UNPKG

7.92 kBJavaScriptView Raw
1(function() {
2 var PDFGradient, PDFLinearGradient, PDFRadialGradient, number,
3 extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
4 hasProp = {}.hasOwnProperty;
5
6 number = require('./object').number;
7
8 PDFGradient = (function() {
9 function PDFGradient(doc) {
10 this.doc = doc;
11 this.stops = [];
12 this.embedded = false;
13 this.transform = [1, 0, 0, 1, 0, 0];
14 }
15
16 PDFGradient.prototype.stop = function(pos, color, opacity) {
17 if (opacity == null) {
18 opacity = 1;
19 }
20 color = this.doc._normalizeColor(color);
21 if (this.stops.length === 0) {
22 if (color.length === 3) {
23 this._colorSpace = 'DeviceRGB';
24 } else if (color.length === 4) {
25 this._colorSpace = 'DeviceCMYK';
26 } else if (color.length === 1) {
27 this._colorSpace = 'DeviceGray';
28 } else {
29 throw new Error('Unknown color space');
30 }
31 } else if ((this._colorSpace === 'DeviceRGB' && color.length !== 3) || (this._colorSpace === 'DeviceCMYK' && color.length !== 4) || (this._colorSpace === 'DeviceGray' && color.length !== 1)) {
32 throw new Error('All gradient stops must use the same color space');
33 }
34 opacity = Math.max(0, Math.min(1, opacity));
35 this.stops.push([pos, color, opacity]);
36 return this;
37 };
38
39 PDFGradient.prototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
40 this.transform = [m11, m12, m21, m22, dx, dy];
41 return this;
42 };
43
44 PDFGradient.prototype.embed = function(m) {
45 var bounds, encode, fn, form, grad, gstate, i, j, k, last, len, opacityPattern, pageBBox, pattern, ref, ref1, shader, stop, stops, v;
46 if (this.stops.length === 0) {
47 return;
48 }
49 this.embedded = true;
50 this.matrix = m;
51 last = this.stops[this.stops.length - 1];
52 if (last[0] < 1) {
53 this.stops.push([1, last[1], last[2]]);
54 }
55 bounds = [];
56 encode = [];
57 stops = [];
58 for (i = j = 0, ref = this.stops.length - 1; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
59 encode.push(0, 1);
60 if (i + 2 !== this.stops.length) {
61 bounds.push(this.stops[i + 1][0]);
62 }
63 fn = this.doc.ref({
64 FunctionType: 2,
65 Domain: [0, 1],
66 C0: this.stops[i + 0][1],
67 C1: this.stops[i + 1][1],
68 N: 1
69 });
70 stops.push(fn);
71 fn.end();
72 }
73 if (stops.length === 1) {
74 fn = stops[0];
75 } else {
76 fn = this.doc.ref({
77 FunctionType: 3,
78 Domain: [0, 1],
79 Functions: stops,
80 Bounds: bounds,
81 Encode: encode
82 });
83 fn.end();
84 }
85 this.id = 'Sh' + (++this.doc._gradCount);
86 shader = this.shader(fn);
87 shader.end();
88 pattern = this.doc.ref({
89 Type: 'Pattern',
90 PatternType: 2,
91 Shading: shader,
92 Matrix: (function() {
93 var k, len, ref1, results;
94 ref1 = this.matrix;
95 results = [];
96 for (k = 0, len = ref1.length; k < len; k++) {
97 v = ref1[k];
98 results.push(number(v));
99 }
100 return results;
101 }).call(this)
102 });
103 pattern.end();
104 if (this.stops.some(function(stop) {
105 return stop[2] < 1;
106 })) {
107 grad = this.opacityGradient();
108 grad._colorSpace = 'DeviceGray';
109 ref1 = this.stops;
110 for (k = 0, len = ref1.length; k < len; k++) {
111 stop = ref1[k];
112 grad.stop(stop[0], [stop[2]]);
113 }
114 grad = grad.embed(this.matrix);
115 pageBBox = [0, 0, this.doc.page.width, this.doc.page.height];
116 form = this.doc.ref({
117 Type: 'XObject',
118 Subtype: 'Form',
119 FormType: 1,
120 BBox: pageBBox,
121 Group: {
122 Type: 'Group',
123 S: 'Transparency',
124 CS: 'DeviceGray'
125 },
126 Resources: {
127 ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'],
128 Pattern: {
129 Sh1: grad
130 }
131 }
132 });
133 form.write("/Pattern cs /Sh1 scn");
134 form.end((pageBBox.join(" ")) + " re f");
135 gstate = this.doc.ref({
136 Type: 'ExtGState',
137 SMask: {
138 Type: 'Mask',
139 S: 'Luminosity',
140 G: form
141 }
142 });
143 gstate.end();
144 opacityPattern = this.doc.ref({
145 Type: 'Pattern',
146 PatternType: 1,
147 PaintType: 1,
148 TilingType: 2,
149 BBox: pageBBox,
150 XStep: pageBBox[2],
151 YStep: pageBBox[3],
152 Resources: {
153 ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'],
154 Pattern: {
155 Sh1: pattern
156 },
157 ExtGState: {
158 Gs1: gstate
159 }
160 }
161 });
162 opacityPattern.write("/Gs1 gs /Pattern cs /Sh1 scn");
163 opacityPattern.end((pageBBox.join(" ")) + " re f");
164 this.doc.page.patterns[this.id] = opacityPattern;
165 } else {
166 this.doc.page.patterns[this.id] = pattern;
167 }
168 return pattern;
169 };
170
171 PDFGradient.prototype.apply = function(op) {
172 var dx, dy, m, m0, m1, m11, m12, m2, m21, m22, m3, m4, m5, ref, ref1;
173 ref = this.doc._ctm.slice(), m0 = ref[0], m1 = ref[1], m2 = ref[2], m3 = ref[3], m4 = ref[4], m5 = ref[5];
174 ref1 = this.transform, m11 = ref1[0], m12 = ref1[1], m21 = ref1[2], m22 = ref1[3], dx = ref1[4], dy = ref1[5];
175 m = [m0 * m11 + m2 * m12, m1 * m11 + m3 * m12, m0 * m21 + m2 * m22, m1 * m21 + m3 * m22, m0 * dx + m2 * dy + m4, m1 * dx + m3 * dy + m5];
176 if (!(this.embedded && m.join(" ") === this.matrix.join(" "))) {
177 this.embed(m);
178 }
179 return this.doc.addContent("/" + this.id + " " + op);
180 };
181
182 return PDFGradient;
183
184 })();
185
186 PDFLinearGradient = (function(superClass) {
187 extend(PDFLinearGradient, superClass);
188
189 function PDFLinearGradient(doc, x1, y1, x2, y2) {
190 this.doc = doc;
191 this.x1 = x1;
192 this.y1 = y1;
193 this.x2 = x2;
194 this.y2 = y2;
195 PDFLinearGradient.__super__.constructor.apply(this, arguments);
196 }
197
198 PDFLinearGradient.prototype.shader = function(fn) {
199 return this.doc.ref({
200 ShadingType: 2,
201 ColorSpace: this._colorSpace,
202 Coords: [this.x1, this.y1, this.x2, this.y2],
203 Function: fn,
204 Extend: [true, true]
205 });
206 };
207
208 PDFLinearGradient.prototype.opacityGradient = function() {
209 return new PDFLinearGradient(this.doc, this.x1, this.y1, this.x2, this.y2);
210 };
211
212 return PDFLinearGradient;
213
214 })(PDFGradient);
215
216 PDFRadialGradient = (function(superClass) {
217 extend(PDFRadialGradient, superClass);
218
219 function PDFRadialGradient(doc, x1, y1, r1, x2, y2, r2) {
220 this.doc = doc;
221 this.x1 = x1;
222 this.y1 = y1;
223 this.r1 = r1;
224 this.x2 = x2;
225 this.y2 = y2;
226 this.r2 = r2;
227 PDFRadialGradient.__super__.constructor.apply(this, arguments);
228 }
229
230 PDFRadialGradient.prototype.shader = function(fn) {
231 return this.doc.ref({
232 ShadingType: 3,
233 ColorSpace: this._colorSpace,
234 Coords: [this.x1, this.y1, this.r1, this.x2, this.y2, this.r2],
235 Function: fn,
236 Extend: [true, true]
237 });
238 };
239
240 PDFRadialGradient.prototype.opacityGradient = function() {
241 return new PDFRadialGradient(this.doc, this.x1, this.y1, this.r1, this.x2, this.y2, this.r2);
242 };
243
244 return PDFRadialGradient;
245
246 })(PDFGradient);
247
248 module.exports = {
249 PDFGradient: PDFGradient,
250 PDFLinearGradient: PDFLinearGradient,
251 PDFRadialGradient: PDFRadialGradient
252 };
253
254}).call(this);