Apply gradient when stroking text
It fixes #19022. I noticed that the glyph contours weren't correct (for T and x) and because we forgot to close the contour.
This commit is contained in:
parent
bff6738966
commit
79e1f155ac
@ -484,6 +484,7 @@ class CanvasExtraState {
|
|||||||
this.fillColor = "#000000";
|
this.fillColor = "#000000";
|
||||||
this.strokeColor = "#000000";
|
this.strokeColor = "#000000";
|
||||||
this.patternFill = false;
|
this.patternFill = false;
|
||||||
|
this.patternStroke = false;
|
||||||
// Note: fill alpha applies to all non-stroking operations
|
// Note: fill alpha applies to all non-stroking operations
|
||||||
this.fillAlpha = 1;
|
this.fillAlpha = 1;
|
||||||
this.strokeAlpha = 1;
|
this.strokeAlpha = 1;
|
||||||
@ -2001,7 +2002,7 @@ class CanvasGraphics {
|
|||||||
this.moveText(0, this.current.leading);
|
this.moveText(0, this.current.leading);
|
||||||
}
|
}
|
||||||
|
|
||||||
paintChar(character, x, y, patternTransform) {
|
paintChar(character, x, y, patternFillTransform, patternStrokeTransform) {
|
||||||
const ctx = this.ctx;
|
const ctx = this.ctx;
|
||||||
const current = this.current;
|
const current = this.current;
|
||||||
const font = current.font;
|
const font = current.font;
|
||||||
@ -2013,30 +2014,39 @@ class CanvasGraphics {
|
|||||||
textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG
|
textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG
|
||||||
);
|
);
|
||||||
const patternFill = current.patternFill && !font.missingFile;
|
const patternFill = current.patternFill && !font.missingFile;
|
||||||
|
const patternStroke = current.patternStroke && !font.missingFile;
|
||||||
|
|
||||||
let addToPath;
|
let addToPath;
|
||||||
if (font.disableFontFace || isAddToPathSet || patternFill) {
|
if (
|
||||||
|
font.disableFontFace ||
|
||||||
|
isAddToPathSet ||
|
||||||
|
patternFill ||
|
||||||
|
patternStroke
|
||||||
|
) {
|
||||||
addToPath = font.getPathGenerator(this.commonObjs, character);
|
addToPath = font.getPathGenerator(this.commonObjs, character);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (font.disableFontFace || patternFill) {
|
if (font.disableFontFace || patternFill || patternStroke) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(x, y);
|
ctx.translate(x, y);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
addToPath(ctx, fontSize);
|
addToPath(ctx, fontSize);
|
||||||
if (patternTransform) {
|
|
||||||
ctx.setTransform(...patternTransform);
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
fillStrokeMode === TextRenderingMode.FILL ||
|
fillStrokeMode === TextRenderingMode.FILL ||
|
||||||
fillStrokeMode === TextRenderingMode.FILL_STROKE
|
fillStrokeMode === TextRenderingMode.FILL_STROKE
|
||||||
) {
|
) {
|
||||||
|
if (patternFillTransform) {
|
||||||
|
ctx.setTransform(...patternFillTransform);
|
||||||
|
}
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
fillStrokeMode === TextRenderingMode.STROKE ||
|
fillStrokeMode === TextRenderingMode.STROKE ||
|
||||||
fillStrokeMode === TextRenderingMode.FILL_STROKE
|
fillStrokeMode === TextRenderingMode.FILL_STROKE
|
||||||
) {
|
) {
|
||||||
|
if (patternStrokeTransform) {
|
||||||
|
ctx.setTransform(...patternStrokeTransform);
|
||||||
|
}
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
@ -2127,7 +2137,7 @@ class CanvasGraphics {
|
|||||||
ctx.scale(textHScale, 1);
|
ctx.scale(textHScale, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let patternTransform;
|
let patternFillTransform, patternStrokeTransform;
|
||||||
if (current.patternFill) {
|
if (current.patternFill) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
const pattern = current.fillColor.getPattern(
|
const pattern = current.fillColor.getPattern(
|
||||||
@ -2136,11 +2146,24 @@ class CanvasGraphics {
|
|||||||
getCurrentTransformInverse(ctx),
|
getCurrentTransformInverse(ctx),
|
||||||
PathType.FILL
|
PathType.FILL
|
||||||
);
|
);
|
||||||
patternTransform = getCurrentTransform(ctx);
|
patternFillTransform = getCurrentTransform(ctx);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
ctx.fillStyle = pattern;
|
ctx.fillStyle = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current.patternStroke) {
|
||||||
|
ctx.save();
|
||||||
|
const pattern = current.strokeColor.getPattern(
|
||||||
|
ctx,
|
||||||
|
this,
|
||||||
|
getCurrentTransformInverse(ctx),
|
||||||
|
PathType.STROKE
|
||||||
|
);
|
||||||
|
patternStrokeTransform = getCurrentTransform(ctx);
|
||||||
|
ctx.restore();
|
||||||
|
ctx.strokeStyle = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
let lineWidth = current.lineWidth;
|
let lineWidth = current.lineWidth;
|
||||||
const scale = current.textMatrixScale;
|
const scale = current.textMatrixScale;
|
||||||
if (scale === 0 || lineWidth === 0) {
|
if (scale === 0 || lineWidth === 0) {
|
||||||
@ -2233,7 +2256,13 @@ class CanvasGraphics {
|
|||||||
// common case
|
// common case
|
||||||
ctx.fillText(character, scaledX, scaledY);
|
ctx.fillText(character, scaledX, scaledY);
|
||||||
} else {
|
} else {
|
||||||
this.paintChar(character, scaledX, scaledY, patternTransform);
|
this.paintChar(
|
||||||
|
character,
|
||||||
|
scaledX,
|
||||||
|
scaledY,
|
||||||
|
patternFillTransform,
|
||||||
|
patternStrokeTransform
|
||||||
|
);
|
||||||
if (accent) {
|
if (accent) {
|
||||||
const scaledAccentX =
|
const scaledAccentX =
|
||||||
scaledX + (fontSize * accent.offset.x) / fontSizeScale;
|
scaledX + (fontSize * accent.offset.x) / fontSizeScale;
|
||||||
@ -2243,7 +2272,8 @@ class CanvasGraphics {
|
|||||||
accent.fontChar,
|
accent.fontChar,
|
||||||
scaledAccentX,
|
scaledAccentX,
|
||||||
scaledAccentY,
|
scaledAccentY,
|
||||||
patternTransform
|
patternFillTransform,
|
||||||
|
patternStrokeTransform
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2379,6 +2409,7 @@ class CanvasGraphics {
|
|||||||
|
|
||||||
setStrokeColorN() {
|
setStrokeColorN() {
|
||||||
this.current.strokeColor = this.getColorN_Pattern(arguments);
|
this.current.strokeColor = this.getColorN_Pattern(arguments);
|
||||||
|
this.current.patternStroke = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFillColorN() {
|
setFillColorN() {
|
||||||
@ -2392,10 +2423,12 @@ class CanvasGraphics {
|
|||||||
g,
|
g,
|
||||||
b
|
b
|
||||||
);
|
);
|
||||||
|
this.current.patternStroke = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStrokeTransparent() {
|
setStrokeTransparent() {
|
||||||
this.ctx.strokeStyle = this.current.strokeColor = "transparent";
|
this.ctx.strokeStyle = this.current.strokeColor = "transparent";
|
||||||
|
this.current.patternStroke = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFillRGBColor(r, g, b) {
|
setFillRGBColor(r, g, b) {
|
||||||
|
|||||||
@ -498,6 +498,9 @@ class FontFaceObject {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// From https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#paths
|
||||||
|
// All contours must be closed with a lineto operation.
|
||||||
|
commands.push(ctx => ctx.closePath());
|
||||||
|
|
||||||
return (this.compiledGlyphs[character] = function glyphDrawer(ctx, size) {
|
return (this.compiledGlyphs[character] = function glyphDrawer(ctx, size) {
|
||||||
commands[0](ctx);
|
commands[0](ctx);
|
||||||
|
|||||||
1
test/pdfs/issue19022.pdf.link
Normal file
1
test/pdfs/issue19022.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://github.com/user-attachments/files/17703776/linear-gradient-on-rect_text.pdf
|
||||||
@ -10757,5 +10757,13 @@
|
|||||||
"md5": "73e8cd32bd063e42fcc4b270c78549b1",
|
"md5": "73e8cd32bd063e42fcc4b270c78549b1",
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "issue19022",
|
||||||
|
"file": "pdfs/issue19022.pdf",
|
||||||
|
"md5": "7d7a9c45f93a9db269800855ccffe7cd",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"link": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user