yWorks/svg2pdf.js

Force stroke='none' if 'stroke-width' === 0

sz5000 opened this issue · 3 comments

If stroke-width is 0, some pdf readers still strokes texts, as you can see with this example. Chrome for instance does this. Adobe Reader does not stroke if stroke-width is 0.

Here is my test SVG file. As you can see, stroke is set to rgb(253,8,8) and stroke-width to 0. I expect, that that the text is not stroked, but only filled. This is not the case in chrome. You can test this here:
https://raw.githack.com/yWorks/svg2pdf.js/master/index.html

Open the URL in Chrome and paste the following SVG file:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="595pt" height="842pt" viewBox="0 0 1152 1630" xml:space="preserve">
<g transform="matrix(1 0 0 1 576 373.1)">
		<text xml:space="preserve" font-family="times" font-size="180" font-style="normal" font-weight="normal" style="stroke: rgb(253,8,8); stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1; white-space: pre;" ><tspan x="-33.04" y="12.57" >Text</tspan></text>
</g>
</svg>

You can see that the text is stroked in read.

This problem can be fixed easily, just change change the following code:

if (stroke === 'none') {
      context.attributeState.stroke = null;
}

to:

if (stroke === 'none' || context.attributeState.strokeWidth === 0) {
      context.attributeState.stroke = null;
}

This fixes the problem.

Here is an example image where you can see that the text is stroked red even if stroke-width is 0 if you open the PDF in Chrome.

text-stroked-red

yGuy commented

I guess this is a short-coming in Chrome? Anyhow, if the SVG spec says that there should not be a stroke, even if one is specified with a zero width, then we should not write it to the PDF and then the proposed fix sounds like a simple solution.

Can someone confirm that there should never be a stroke visible with a stroke width of 0 in SVG? Maybe the SVG implementation is wrong, already?

The SVG 2.0 spec states:

This property specifies the width of the stroke on the current object. A zero value causes no stroke to be painted.

https://svgwg.org/svg2-draft/painting.html#StrokeWidth

The PDF 1.7 spec states:

A line width of 0 shall denote the thinnest line that can be rendered at device resolution [...]

So, yes we shouldn't draw zero-width strokes. The proposal looks good to me.

Actually, I think we should rather change

export function getTextRenderingMode(attributeState: AttributeState): TextRenderingMode {
let renderingMode: TextRenderingMode = 'invisible'
if (attributeState.fill && attributeState.stroke) {
renderingMode = 'fillThenStroke'
} else if (attributeState.fill) {
renderingMode = 'fill'
} else if (attributeState.stroke) {
renderingMode = 'stroke'
}
return renderingMode
}

to make it consistent with

protected async fillOrStroke(context: Context): Promise<void> {
if (context.withinClipPath) {
return
}
const fill = context.attributeState.fill
const stroke = context.attributeState.stroke && context.attributeState.strokeWidth !== 0
const fillData = fill ? await fill.getFillData(this, context) : undefined
const isNodeFillRuleEvenOdd =
getAttribute(this.element, context.styleSheets, 'fill-rule') === 'evenodd'
// This is a workaround for symbols that are used multiple times with different
// fill/stroke attributes. All paths within symbols are both filled and stroked
// and we set the fill/stroke to transparent if the use element has
// fill/stroke="none".
if ((fill && stroke) || context.withinUse) {
if (isNodeFillRuleEvenOdd) {
context.pdf.fillStrokeEvenOdd(fillData)
} else {
context.pdf.fillStroke(fillData)
}
} else if (fill) {
if (isNodeFillRuleEvenOdd) {
context.pdf.fillEvenOdd(fillData)
} else {
context.pdf.fill(fillData)
}
} else if (stroke) {
context.pdf.stroke()
} else {
context.pdf.discardPath()
}
}