Skip to content

Commit

Permalink
Add support for spot colors #756
Browse files Browse the repository at this point in the history
  • Loading branch information
liborm85 committed Dec 26, 2024
1 parent eecfb4c commit 3ccf3b8
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Update fontkit to 2.0
- Update linebreak to 1.1
- Add support for spot colors
- Fix measuring text when OpenType features are passed in to .text()

### [v0.15.2] - 2024-12-15
Expand Down
10 changes: 8 additions & 2 deletions docs/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ and some other properties. Here is a list of the available annotation methods:
* `fileAnnotation(x, y, width, height, file, options)`

Many of the annotations have a `color` option that you can specify. You can
use an array of RGB values, a hex color, or a named CSS color value for that
option.
use an array of RGB values, a hex color, a named CSS color value, or a named
spot color value for that option.

If you are adding an annotation to a piece of text, such as a link or
underline, you will need to know the width and height of the text in order to
Expand Down Expand Up @@ -58,6 +58,12 @@ Here is an example that uses a few of the annotation types.
.highlight(20, doc.y, doc.widthOfString('This text is highlighted!'), height)
.text('This text is highlighted!');

// Create text with a spot color
doc.addSpotColor('PANTONE185C', 0, 100, 78, 9)
doc.moveDown()
.fillColor('PANTONE185C')
.text('This text uses spot color!');

// Create the crossed out text
doc.moveDown()
.strike(20, doc.y, doc.widthOfString('STRIKE!'), height)
Expand Down
6 changes: 6 additions & 0 deletions examples/kitchen-sink.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,10 @@ doc
.font('fonts/Chalkboard.ttc', 'Chalkboard', 16)
.list(['One', 'Two', 'Three'], 100, 150);

// Create text with a spot color
doc.addSpotColor('PANTONE185C', 0, 100, 78, 9)
doc.moveDown()
.fillColor('PANTONE185C')
.text('This text uses spot color!');

doc.end();
Binary file modified examples/kitchen-sink.pdf
Binary file not shown.
22 changes: 20 additions & 2 deletions lib/mixins/color.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import Gradient from '../gradient';
import pattern from '../pattern';
import SpotColor from '../spotcolor';

const { PDFGradient, PDFLinearGradient, PDFRadialGradient } = Gradient;
const { PDFTilingPattern } = pattern;

export default {
initColor() {
this.spotColors = {};
// The opacity dictionaries
this._opacityRegistry = {};
this._opacityCount = 0;
Expand All @@ -26,6 +28,8 @@ export default {
color = [hex >> 16, (hex >> 8) & 0xff, hex & 0xff];
} else if (namedColors[color]) {
color = namedColors[color];
} else if (this.spotColors[color]) {
return this.spotColors[color];
}
}

Expand Down Expand Up @@ -66,8 +70,12 @@ export default {
const space = this._getColorSpace(color);
this._setColorSpace(space, stroke);

color = color.join(' ');
this.addContent(`${color} ${op}`);
if (color instanceof SpotColor) {
this.page.colorSpaces[color.id] = color.ref;
this.addContent(`1 ${op}`);
} else {
this.addContent(`${color.join(' ')} ${op}`);
}

return true;
},
Expand All @@ -78,6 +86,10 @@ export default {
},

_getColorSpace(color) {
if (color instanceof SpotColor) {
return color.id;
}

return color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB';
},

Expand Down Expand Up @@ -163,6 +175,12 @@ export default {

pattern(bbox, xStep, yStep, stream) {
return new PDFTilingPattern(this, bbox, xStep, yStep, stream);
},

addSpotColor(name, C, M, Y, K) {
const color = new SpotColor(this, name, C, M, Y, K);
this.spotColors[name] = color;
return this;
}
};

Expand Down
4 changes: 3 additions & 1 deletion lib/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ By Devon Govett

import PDFAbstractReference from './abstract_reference';
import PDFTree from './tree';
import SpotColor from './spotcolor';

const pad = (str, length) => (Array(length + 1).join('0') + str).slice(-length);

Expand Down Expand Up @@ -79,7 +80,8 @@ class PDFObject {
return `<${object.toString('hex')}>`;
} else if (
object instanceof PDFAbstractReference ||
object instanceof PDFTree
object instanceof PDFTree ||
object instanceof SpotColor
) {
return object.toString();
} else if (object instanceof Date) {
Expand Down
4 changes: 4 additions & 0 deletions lib/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ class PDFPage {

end() {
this.dictionary.end();
this.resources.data.ColorSpace = this.resources.data.ColorSpace || {};
for (let color of Object.values(this.document.spotColors)) {
this.resources.data.ColorSpace[color.id] = color;
}
this.resources.end();
return this.content.end();
}
Expand Down
25 changes: 25 additions & 0 deletions lib/spotcolor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export default class SpotColor {
constructor(doc, name, C, M, Y, K) {
this.id = 'CS' + Object.keys(doc.spotColors).length;
this.name = name;
this.values = [C, M, Y, K];
this.ref = doc.ref([
'Separation',
this.name,
'DeviceCMYK',
{
Range: [0, 1, 0, 1, 0, 1, 0, 1],
C0: [0, 0, 0, 0],
C1: this.values.map(value => value / 100),
FunctionType: 2,
Domain: [0, 1],
N: 1
}
]);
this.ref.end();
}

toString() {
return `${this.ref.id} 0 R`;
}
}
32 changes: 32 additions & 0 deletions tests/unit/color.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import PDFDocument from '../../lib/document';
import { logData, joinTokens } from './helpers';

Check failure on line 2 in tests/unit/color.spec.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'joinTokens' is defined but never used

Check failure on line 2 in tests/unit/color.spec.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'joinTokens' is defined but never used

Check failure on line 2 in tests/unit/color.spec.js

View workflow job for this annotation

GitHub Actions / build (22.x)

'joinTokens' is defined but never used

describe('color', function() {
test('normalize', function() {
Expand Down Expand Up @@ -35,4 +36,35 @@ describe('color', function() {
1
]);
});

test('normalize with spot color', function() {
const doc = new PDFDocument();
doc.addSpotColor('PANTONE 123 C', 0.1, 0.2, 0.3, 0.4);

const color = doc._normalizeColor('PANTONE 123 C');
expect(color.id).toEqual('CS0');
expect(color.values).toEqual([0.1, 0.2, 0.3, 0.4]);
});

test('spot color', function() {
const doc = new PDFDocument();
const data = logData(doc);
doc.addSpotColor('PANTONE185C', 0, 100, 78, 9)
doc.fillColor('PANTONE185C')
.text('This text uses spot color!');
doc.end();
console.log(data);
expect(data).toContainChunk([
`6 0 obj`,
'<<\n' +
'/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n' +
'/ColorSpace <<\n' +
'/CS0 8 0 R\n' +
'>>\n' +
'/Font <<\n' +
'/F1 9 0 R\n' +
'>>\n' +
'>>',
]);
});
});

0 comments on commit 3ccf3b8

Please sign in to comment.