Skip to content

Commit 8d1994c

Browse files
fix masks
1 parent deb8f93 commit 8d1994c

3 files changed

Lines changed: 66 additions & 35 deletions

File tree

lib/mixins/color.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,17 @@ export default {
9595
}
9696
return profile.label;
9797
}
98-
return color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
98+
99+
switch (color.length) {
100+
case 1:
101+
return 'DeviceGray';
102+
case 3:
103+
return 'DeviceRGB';
104+
case 4:
105+
return 'DeviceCMYK';
106+
default:
107+
throw Error('Unsupported color channels.');
108+
}
99109
},
100110

101111
fillColor(color, opacity) {

lib/write_svg.js

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -635,16 +635,7 @@ export default function (doc, svg, x, y, options) {
635635
luminosity: 'Luminosity',
636636
};
637637

638-
const maskBackdrop = [0, 0, 0];
639-
if (doc._activeColorProfile) {
640-
let i = doc._activeColorProfile.channels - 3;
641-
while (i > 0) {
642-
maskBackdrop.push(0);
643-
i--;
644-
}
645-
}
646-
647-
function docBeginGroup(bbox) {
638+
function docBeginGroup(bbox, groupColorSpace) {
648639
let group = new (function PDFGroup() {})();
649640
group.name = 'G' + (doc._groupCount = (doc._groupCount || 0) + 1);
650641
group.resources = doc.ref();
@@ -653,7 +644,7 @@ export default function (doc, svg, x, y, options) {
653644
Subtype: 'Form',
654645
FormType: 1,
655646
BBox: bbox,
656-
Group: { S: 'Transparency', CS: colorSpace, I: true, K: false },
647+
Group: { S: 'Transparency', CS: groupColorSpace || colorSpace, I: true, K: false },
657648
Resources: group.resources,
658649
});
659650
group.xobj.write('');
@@ -703,14 +694,14 @@ export default function (doc, svg, x, y, options) {
703694
let name = 'M' + (doc._maskCount = (doc._maskCount || 0) + 1);
704695
let gstate = doc.ref({
705696
Type: 'ExtGState',
706-
CA: 1,
707697
ca: 1,
698+
CA: 1,
708699
BM: 'Normal',
709700
SMask: {
710-
// This should ideally be an Alpha mask for clipping but when we set it to Alpha the firefox pdf viewer doesn't render masks correctly
711701
S: 'Luminosity',
712702
G: group.xobj,
713-
BC: maskBackdrop,
703+
CS: 'DeviceRGB',
704+
BC: [0, 0, 0],
714705
},
715706
});
716707
gstate.end();
@@ -853,22 +844,22 @@ export default function (doc, svg, x, y, options) {
853844
function docEndText() {
854845
doc.addContent('ET');
855846
}
856-
function docFillColor(color, isClip) {
857-
if (isClip) {
858-
// always write clips in RGB colorspace
847+
function docFillColor(color, maskOrClip) {
848+
if (color[0].constructor.name === 'PDFPattern') {
849+
doc.fillOpacity(color[1]);
850+
docUsePattern(color[0], false);
851+
return;
852+
}
853+
if (maskOrClip) {
854+
// always write clips/masks in RGB colorspace
859855
const oldColorProfile = doc._activeColorProfile;
860856
doc._activeColorProfile = null;
861-
doc.fillColor(RGBDefaultColors.white[0], RGBDefaultColors.white[1]);
857+
doc.fillColor(color[0], color[1]);
862858
doc._activeColorProfile = oldColorProfile;
863859
return;
864860
}
865861

866-
if (color[0].constructor.name === 'PDFPattern') {
867-
doc.fillOpacity(color[1]);
868-
docUsePattern(color[0], false);
869-
} else {
870-
doc.fillColor(color[0], color[1]);
871-
}
862+
doc.fillColor(color[0], color[1]);
872863
}
873864
function docStrokeColor(color) {
874865
if (color[0].constructor.name === 'PDFPattern') {
@@ -1083,6 +1074,10 @@ export default function (doc, svg, x, y, options) {
10831074
let newColor = color[0].slice(),
10841075
newOpacity = color[1] * opacity;
10851076
if (isMask) {
1077+
// For mask rendering, use DeviceRGB (premultiplied).
1078+
if (newColor.length === 4) {
1079+
newColor = cmykToRgb(newColor);
1080+
}
10861081
for (let i = 0; i < newColor.length; i++) {
10871082
newColor[i] *= newOpacity;
10881083
}
@@ -1091,6 +1086,27 @@ export default function (doc, svg, x, y, options) {
10911086
return [newColor, newOpacity];
10921087
}
10931088
}
1089+
function cmykToRgb(cmyk) {
1090+
let c = cmyk[0],
1091+
m = cmyk[1],
1092+
y = cmyk[2],
1093+
k = cmyk[3];
1094+
if (c > 1 || m > 1 || y > 1 || k > 1) {
1095+
c /= 100;
1096+
m /= 100;
1097+
y /= 100;
1098+
k /= 100;
1099+
}
1100+
c = Math.max(0, Math.min(1, c));
1101+
m = Math.max(0, Math.min(1, m));
1102+
y = Math.max(0, Math.min(1, y));
1103+
k = Math.max(0, Math.min(1, k));
1104+
return [
1105+
Math.round(255 * (1 - c) * (1 - k)),
1106+
Math.round(255 * (1 - m) * (1 - k)),
1107+
Math.round(255 * (1 - y) * (1 - k)),
1108+
];
1109+
}
10941110
function multiplyMatrix() {
10951111
function multiply(a, b) {
10961112
return [
@@ -2458,7 +2474,7 @@ export default function (doc, svg, x, y, options) {
24582474
fill = this.get('fill'),
24592475
fillOpacity = this.get('fill-opacity');
24602476
if (isClip) {
2461-
return DefaultColors.white;
2477+
return RGBDefaultColors.white;
24622478
}
24632479
if (fill !== 'none' && opacity && fillOpacity) {
24642480
if (fill instanceof SvgElemGradient || fill instanceof SvgElemPattern) {
@@ -2759,7 +2775,7 @@ export default function (doc, svg, x, y, options) {
27592775
doc.image(image, 0, 0);
27602776
} else {
27612777
doc.rect(0, 0, image.width, image.height);
2762-
docFillColor(DefaultColors.white, true);
2778+
docFillColor(RGBDefaultColors.white, true);
27632779
}
27642780
doc.restore();
27652781
};
@@ -3036,7 +3052,7 @@ export default function (doc, svg, x, y, options) {
30363052
lineCap = this.get('stroke-linecap');
30373053
if (fill || stroke) {
30383054
if (fill) {
3039-
docFillColor(fill);
3055+
docFillColor(fill, isMask);
30403056
}
30413057
if (stroke) {
30423058
for (let j = 0; j < subPaths.length; j++) {
@@ -3045,7 +3061,7 @@ export default function (doc, svg, x, y, options) {
30453061
if (subPaths[j].startPoint && subPaths[j].startPoint.length > 1) {
30463062
let x = subPaths[j].startPoint[0],
30473063
y = subPaths[j].startPoint[1];
3048-
docFillColor(stroke);
3064+
docFillColor(stroke, isMask);
30493065
if (lineCap === 'square') {
30503066
doc.rect(x - 0.5 * lineWidth, y - 0.5 * lineWidth, lineWidth, lineWidth);
30513067
} else if (lineCap === 'round') {
@@ -3111,7 +3127,7 @@ export default function (doc, svg, x, y, options) {
31113127
}
31123128
} else {
31133129
this.shape.insertInDocument();
3114-
docFillColor(DefaultColors.white, true);
3130+
docFillColor(RGBDefaultColors.white, true);
31153131
doc.fill(this.get('clip-rule'));
31163132
}
31173133
doc.restore();
@@ -3314,7 +3330,7 @@ export default function (doc, svg, x, y, options) {
33143330
var SvgElemClipPath = function (obj, inherits) {
33153331
SvgElemHasChildren.call(this, obj, inherits);
33163332
this.useMask = function (bBox) {
3317-
let group = docBeginGroup(getPageBBox());
3333+
let group = docBeginGroup(getPageBBox(), 'DeviceRGB');
33183334
doc.save();
33193335
doc.transform.apply(doc, this.get('transform'));
33203336
if (this.attr('clipPathUnits') === 'objectBoundingBox') {
@@ -3331,7 +3347,10 @@ export default function (doc, svg, x, y, options) {
33313347
var SvgElemMask = function (obj, inherits) {
33323348
SvgElemHasChildren.call(this, obj, inherits);
33333349
this.useMask = function (bBox) {
3334-
let group = docBeginGroup(getPageBBox());
3350+
let group = docBeginGroup(getPageBBox(), 'DeviceRGB');
3351+
3352+
const savedColorProfile = doc._activeColorProfile;
3353+
doc._activeColorProfile = null;
33353354
doc.save();
33363355
/* let x, y, w, h; */
33373356
if (this.attr('maskUnits') === 'userSpaceOnUse') {
@@ -3349,8 +3368,10 @@ export default function (doc, svg, x, y, options) {
33493368
doc.transform(bBox[2] - bBox[0], 0, 0, bBox[3] - bBox[1], bBox[0], bBox[1]);
33503369
}
33513370
this.clip();
3371+
// treat all masks as luminosity mask
33523372
this.drawChildren(false, true);
33533373
doc.restore();
3374+
doc._activeColorProfile = savedColorProfile;
33543375
docEndGroup(group);
33553376
docApplyMask(group);
33563377
};
@@ -3424,7 +3445,7 @@ export default function (doc, svg, x, y, options) {
34243445
}
34253446
if (fill || stroke || isClip) {
34263447
if (fill) {
3427-
docFillColor(fill, isClip);
3448+
docFillColor(fill, isClip || isMask);
34283449
}
34293450
if (stroke && strokeWidth) {
34303451
docStrokeColor(stroke);
@@ -3458,7 +3479,7 @@ export default function (doc, svg, x, y, options) {
34583479
let fill = this.getFill(isClip, isMask),
34593480
stroke = this.getStroke(isClip, isMask);
34603481
if (fill) {
3461-
docFillColor(fill, isClip);
3482+
docFillColor(fill, isClip || isMask);
34623483
}
34633484
if (stroke) {
34643485
docStrokeColor(stroke);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"document",
1010
"vector"
1111
],
12-
"version": "0.17.54",
12+
"version": "0.17.55",
1313
"homepage": "http://pdfkit.org/",
1414
"author": {
1515
"name": "Devon Govett",

0 commit comments

Comments
 (0)