|
|
'use strict';
const { querySelector, closestByName, detachNodeFromParent, } = require('../lib/xast.js'); const { collectStylesheet, computeStyle } = require('../lib/style.js'); const { parsePathData } = require('../lib/path.js');
exports.name = 'removeHiddenElems'; exports.type = 'visitor'; exports.active = true; exports.description = 'removes hidden elements (zero sized, with absent attributes)';
/** * Remove hidden elements with disabled rendering: * - display="none" * - opacity="0" * - circle with zero radius * - ellipse with zero x-axis or y-axis radius * - rectangle with zero width or height * - pattern with zero width or height * - image with zero width or height * - path with empty data * - polyline with empty points * - polygon with empty points * * @author Kir Belevich * * @type {import('../lib/types').Plugin<{ * isHidden: boolean, * displayNone: boolean, * opacity0: boolean, * circleR0: boolean, * ellipseRX0: boolean, * ellipseRY0: boolean, * rectWidth0: boolean, * rectHeight0: boolean, * patternWidth0: boolean, * patternHeight0: boolean, * imageWidth0: boolean, * imageHeight0: boolean, * pathEmptyD: boolean, * polylineEmptyPoints: boolean, * polygonEmptyPoints: boolean, * }>} */ exports.fn = (root, params) => { const { isHidden = true, displayNone = true, opacity0 = true, circleR0 = true, ellipseRX0 = true, ellipseRY0 = true, rectWidth0 = true, rectHeight0 = true, patternWidth0 = true, patternHeight0 = true, imageWidth0 = true, imageHeight0 = true, pathEmptyD = true, polylineEmptyPoints = true, polygonEmptyPoints = true, } = params; const stylesheet = collectStylesheet(root);
return { element: { enter: (node, parentNode) => { // Removes hidden elements
// https://www.w3schools.com/cssref/pr_class_visibility.asp
const computedStyle = computeStyle(stylesheet, node); if ( isHidden && computedStyle.visibility && computedStyle.visibility.type === 'static' && computedStyle.visibility.value === 'hidden' && // keep if any descendant enables visibility
querySelector(node, '[visibility=visible]') == null ) { detachNodeFromParent(node, parentNode); return; }
// display="none"
//
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty
// "A value of display: none indicates that the given element
// and its children shall not be rendered directly"
if ( displayNone && computedStyle.display && computedStyle.display.type === 'static' && computedStyle.display.value === 'none' && // markers with display: none still rendered
node.name !== 'marker' ) { detachNodeFromParent(node, parentNode); return; }
// opacity="0"
//
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties
if ( opacity0 && computedStyle.opacity && computedStyle.opacity.type === 'static' && computedStyle.opacity.value === '0' && // transparent element inside clipPath still affect clipped elements
closestByName(node, 'clipPath') == null ) { detachNodeFromParent(node, parentNode); return; }
// Circles with zero radius
//
// https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute
// "A value of zero disables rendering of the element"
//
// <circle r="0">
if ( circleR0 && node.name === 'circle' && node.children.length === 0 && node.attributes.r === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Ellipse with zero x-axis radius
//
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute
// "A value of zero disables rendering of the element"
//
// <ellipse rx="0">
if ( ellipseRX0 && node.name === 'ellipse' && node.children.length === 0 && node.attributes.rx === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Ellipse with zero y-axis radius
//
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute
// "A value of zero disables rendering of the element"
//
// <ellipse ry="0">
if ( ellipseRY0 && node.name === 'ellipse' && node.children.length === 0 && node.attributes.ry === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Rectangle with zero width
//
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute
// "A value of zero disables rendering of the element"
//
// <rect width="0">
if ( rectWidth0 && node.name === 'rect' && node.children.length === 0 && node.attributes.width === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Rectangle with zero height
//
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute
// "A value of zero disables rendering of the element"
//
// <rect height="0">
if ( rectHeight0 && rectWidth0 && node.name === 'rect' && node.children.length === 0 && node.attributes.height === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Pattern with zero width
//
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
//
// <pattern width="0">
if ( patternWidth0 && node.name === 'pattern' && node.attributes.width === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Pattern with zero height
//
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
//
// <pattern height="0">
if ( patternHeight0 && node.name === 'pattern' && node.attributes.height === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Image with zero width
//
// https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute
// "A value of zero disables rendering of the element"
//
// <image width="0">
if ( imageWidth0 && node.name === 'image' && node.attributes.width === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Image with zero height
//
// https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute
// "A value of zero disables rendering of the element"
//
// <image height="0">
if ( imageHeight0 && node.name === 'image' && node.attributes.height === '0' ) { detachNodeFromParent(node, parentNode); return; }
// Path with empty data
//
// https://www.w3.org/TR/SVG11/paths.html#DAttribute
//
// <path d=""/>
if (pathEmptyD && node.name === 'path') { if (node.attributes.d == null) { detachNodeFromParent(node, parentNode); return; } const pathData = parsePathData(node.attributes.d); if (pathData.length === 0) { detachNodeFromParent(node, parentNode); return; } // keep single point paths for markers
if ( pathData.length === 1 && computedStyle['marker-start'] == null && computedStyle['marker-end'] == null ) { detachNodeFromParent(node, parentNode); return; } return; }
// Polyline with empty points
//
// https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute
//
// <polyline points="">
if ( polylineEmptyPoints && node.name === 'polyline' && node.attributes.points == null ) { detachNodeFromParent(node, parentNode); return; }
// Polygon with empty points
//
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
//
// <polygon points="">
if ( polygonEmptyPoints && node.name === 'polygon' && node.attributes.points == null ) { detachNodeFromParent(node, parentNode); return; } }, }, }; };
|