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);
|