highcharts_rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +106 -0
- data/Rakefile +6 -0
- data/highcharts_rails.gemspec +27 -0
- data/lib/highcharts_rails/version.rb +3 -0
- data/lib/highcharts_rails.rb +8 -0
- data/vendor/assets/javascripts/highcharts-3d.src.js +2139 -0
- data/vendor/assets/javascripts/highcharts-more.src.js +2982 -0
- data/vendor/assets/javascripts/highcharts.src.js +22947 -0
- data/vendor/assets/javascripts/js/highcharts-3d.src.js +2085 -0
- data/vendor/assets/javascripts/js/highcharts-more.src.js +2820 -0
- data/vendor/assets/javascripts/js/highcharts.src.js +20917 -0
- data/vendor/assets/javascripts/js/modules/accessibility.src.js +1072 -0
- data/vendor/assets/javascripts/js/modules/annotations.src.js +408 -0
- data/vendor/assets/javascripts/js/modules/boost.src.js +652 -0
- data/vendor/assets/javascripts/js/modules/broken-axis.src.js +338 -0
- data/vendor/assets/javascripts/js/modules/data.src.js +981 -0
- data/vendor/assets/javascripts/js/modules/drilldown.src.js +756 -0
- data/vendor/assets/javascripts/js/modules/exporting.src.js +953 -0
- data/vendor/assets/javascripts/js/modules/funnel.src.js +290 -0
- data/vendor/assets/javascripts/js/modules/gantt.src.js +791 -0
- data/vendor/assets/javascripts/js/modules/grid-axis.src.js +545 -0
- data/vendor/assets/javascripts/js/modules/heatmap.src.js +798 -0
- data/vendor/assets/javascripts/js/modules/no-data-to-display.src.js +150 -0
- data/vendor/assets/javascripts/js/modules/offline-exporting.src.js +492 -0
- data/vendor/assets/javascripts/js/modules/overlapping-datalabels.src.js +164 -0
- data/vendor/assets/javascripts/js/modules/series-label.src.js +606 -0
- data/vendor/assets/javascripts/js/modules/solid-gauge.src.js +305 -0
- data/vendor/assets/javascripts/js/modules/treemap.src.js +881 -0
- data/vendor/assets/javascripts/js/modules/xrange-series.src.js +254 -0
- data/vendor/assets/javascripts/js/themes/dark-blue.js +317 -0
- data/vendor/assets/javascripts/js/themes/dark-green.js +314 -0
- data/vendor/assets/javascripts/js/themes/dark-unica.js +243 -0
- data/vendor/assets/javascripts/js/themes/gray.js +326 -0
- data/vendor/assets/javascripts/js/themes/grid-light.js +99 -0
- data/vendor/assets/javascripts/js/themes/grid.js +131 -0
- data/vendor/assets/javascripts/js/themes/sand-signika.js +129 -0
- data/vendor/assets/javascripts/js/themes/skies.js +112 -0
- data/vendor/assets/javascripts/lib/canvg.src.js +3073 -0
- data/vendor/assets/javascripts/lib/jspdf.src.js +3031 -0
- data/vendor/assets/javascripts/lib/rgbcolor.src.js +299 -0
- data/vendor/assets/javascripts/lib/svg2pdf.src.js +1451 -0
- data/vendor/assets/javascripts/modules/accessibility.src.js +1072 -0
- data/vendor/assets/javascripts/modules/annotations.src.js +408 -0
- data/vendor/assets/javascripts/modules/boost.src.js +652 -0
- data/vendor/assets/javascripts/modules/broken-axis.src.js +338 -0
- data/vendor/assets/javascripts/modules/data.src.js +981 -0
- data/vendor/assets/javascripts/modules/drilldown.src.js +797 -0
- data/vendor/assets/javascripts/modules/exporting.src.js +882 -0
- data/vendor/assets/javascripts/modules/funnel.src.js +304 -0
- data/vendor/assets/javascripts/modules/gantt.src.js +815 -0
- data/vendor/assets/javascripts/modules/grid-axis.src.js +547 -0
- data/vendor/assets/javascripts/modules/heatmap.src.js +810 -0
- data/vendor/assets/javascripts/modules/no-data-to-display.src.js +161 -0
- data/vendor/assets/javascripts/modules/offline-exporting.src.js +492 -0
- data/vendor/assets/javascripts/modules/overlapping-datalabels.src.js +164 -0
- data/vendor/assets/javascripts/modules/series-label.src.js +606 -0
- data/vendor/assets/javascripts/modules/solid-gauge.src.js +316 -0
- data/vendor/assets/javascripts/modules/treemap.src.js +935 -0
- data/vendor/assets/javascripts/modules/xrange-series.src.js +276 -0
- data/vendor/assets/javascripts/themes/dark-blue.js +317 -0
- data/vendor/assets/javascripts/themes/dark-green.js +314 -0
- data/vendor/assets/javascripts/themes/dark-unica.js +243 -0
- data/vendor/assets/javascripts/themes/gray.js +326 -0
- data/vendor/assets/javascripts/themes/grid-light.js +99 -0
- data/vendor/assets/javascripts/themes/grid.js +131 -0
- data/vendor/assets/javascripts/themes/sand-signika.js +129 -0
- data/vendor/assets/javascripts/themes/skies.js +112 -0
- data/vendor/assets/stylesheets/highcharts.scss +610 -0
- metadata +161 -0
@@ -0,0 +1,3031 @@
|
|
1
|
+
/** @preserve
|
2
|
+
* jsPDF - PDF Document creation from JavaScript
|
3
|
+
* Version ${versionID}
|
4
|
+
* CommitID ${commitID}
|
5
|
+
*
|
6
|
+
* Copyright (c) 2010-2014 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF
|
7
|
+
* 2010 Aaron Spike, https://github.com/acspike
|
8
|
+
* 2012 Willow Systems Corporation, willow-systems.com
|
9
|
+
* 2012 Pablo Hess, https://github.com/pablohess
|
10
|
+
* 2012 Florian Jenett, https://github.com/fjenett
|
11
|
+
* 2013 Warren Weckesser, https://github.com/warrenweckesser
|
12
|
+
* 2013 Youssef Beddad, https://github.com/lifof
|
13
|
+
* 2013 Lee Driscoll, https://github.com/lsdriscoll
|
14
|
+
* 2013 Stefan Slonevskiy, https://github.com/stefslon
|
15
|
+
* 2013 Jeremy Morel, https://github.com/jmorel
|
16
|
+
* 2013 Christoph Hartmann, https://github.com/chris-rock
|
17
|
+
* 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
|
18
|
+
* 2014 James Makes, https://github.com/dollaruw
|
19
|
+
* 2014 Diego Casorran, https://github.com/diegocr
|
20
|
+
* 2014 Steven Spungin, https://github.com/Flamenco
|
21
|
+
* 2014 Kenneth Glassey, https://github.com/Gavvers
|
22
|
+
*
|
23
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
24
|
+
* a copy of this software and associated documentation files (the
|
25
|
+
* "Software"), to deal in the Software without restriction, including
|
26
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
27
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
28
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
29
|
+
* the following conditions:
|
30
|
+
*
|
31
|
+
* The above copyright notice and this permission notice shall be
|
32
|
+
* included in all copies or substantial portions of the Software.
|
33
|
+
*
|
34
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
35
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
36
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
37
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
38
|
+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
39
|
+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
40
|
+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
41
|
+
*
|
42
|
+
* Contributor(s):
|
43
|
+
* siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango,
|
44
|
+
* kim3er, mfo, alnorth, Flamenco
|
45
|
+
*/
|
46
|
+
|
47
|
+
/**
|
48
|
+
* Creates new jsPDF document object instance.
|
49
|
+
*
|
50
|
+
* @class
|
51
|
+
* @param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l")
|
52
|
+
* @param unit Measurement unit to be used when coordinates are specified.
|
53
|
+
* One of "pt" (points), "mm" (Default), "cm", "in"
|
54
|
+
* @param format One of 'pageFormats' as shown below, default: a4
|
55
|
+
* @returns {jsPDF}
|
56
|
+
* @name jsPDF
|
57
|
+
*/
|
58
|
+
var jsPDF = (function(global) {
|
59
|
+
'use strict';
|
60
|
+
var pdfVersion = '1.3',
|
61
|
+
pageFormats = { // Size in pt of various paper formats
|
62
|
+
'a0' : [2383.94, 3370.39], 'a1' : [1683.78, 2383.94],
|
63
|
+
'a2' : [1190.55, 1683.78], 'a3' : [ 841.89, 1190.55],
|
64
|
+
'a4' : [ 595.28, 841.89], 'a5' : [ 419.53, 595.28],
|
65
|
+
'a6' : [ 297.64, 419.53], 'a7' : [ 209.76, 297.64],
|
66
|
+
'a8' : [ 147.40, 209.76], 'a9' : [ 104.88, 147.40],
|
67
|
+
'a10' : [ 73.70, 104.88], 'b0' : [2834.65, 4008.19],
|
68
|
+
'b1' : [2004.09, 2834.65], 'b2' : [1417.32, 2004.09],
|
69
|
+
'b3' : [1000.63, 1417.32], 'b4' : [ 708.66, 1000.63],
|
70
|
+
'b5' : [ 498.90, 708.66], 'b6' : [ 354.33, 498.90],
|
71
|
+
'b7' : [ 249.45, 354.33], 'b8' : [ 175.75, 249.45],
|
72
|
+
'b9' : [ 124.72, 175.75], 'b10' : [ 87.87, 124.72],
|
73
|
+
'c0' : [2599.37, 3676.54], 'c1' : [1836.85, 2599.37],
|
74
|
+
'c2' : [1298.27, 1836.85], 'c3' : [ 918.43, 1298.27],
|
75
|
+
'c4' : [ 649.13, 918.43], 'c5' : [ 459.21, 649.13],
|
76
|
+
'c6' : [ 323.15, 459.21], 'c7' : [ 229.61, 323.15],
|
77
|
+
'c8' : [ 161.57, 229.61], 'c9' : [ 113.39, 161.57],
|
78
|
+
'c10' : [ 79.37, 113.39], 'dl' : [ 311.81, 623.62],
|
79
|
+
'letter' : [612, 792],
|
80
|
+
'government-letter' : [576, 756],
|
81
|
+
'legal' : [612, 1008],
|
82
|
+
'junior-legal' : [576, 360],
|
83
|
+
'ledger' : [1224, 792],
|
84
|
+
'tabloid' : [792, 1224],
|
85
|
+
'credit-card' : [153, 243]
|
86
|
+
};
|
87
|
+
|
88
|
+
/**
|
89
|
+
* jsPDF's Internal PubSub Implementation.
|
90
|
+
* See mrrio.github.io/jsPDF/doc/symbols/PubSub.html
|
91
|
+
* Backward compatible rewritten on 2014 by
|
92
|
+
* Diego Casorran, https://github.com/diegocr
|
93
|
+
*
|
94
|
+
* @class
|
95
|
+
* @name PubSub
|
96
|
+
*/
|
97
|
+
function PubSub(context) {
|
98
|
+
var topics = {};
|
99
|
+
|
100
|
+
this.subscribe = function(topic, callback, once) {
|
101
|
+
if(typeof callback !== 'function') {
|
102
|
+
return false;
|
103
|
+
}
|
104
|
+
|
105
|
+
if(!topics.hasOwnProperty(topic)) {
|
106
|
+
topics[topic] = {};
|
107
|
+
}
|
108
|
+
|
109
|
+
var id = Math.random().toString(35);
|
110
|
+
topics[topic][id] = [callback,!!once];
|
111
|
+
|
112
|
+
return id;
|
113
|
+
};
|
114
|
+
|
115
|
+
this.unsubscribe = function(token) {
|
116
|
+
for(var topic in topics) {
|
117
|
+
if(topics[topic][token]) {
|
118
|
+
delete topics[topic][token];
|
119
|
+
return true;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
return false;
|
123
|
+
};
|
124
|
+
|
125
|
+
this.publish = function(topic) {
|
126
|
+
if(topics.hasOwnProperty(topic)) {
|
127
|
+
var args = Array.prototype.slice.call(arguments, 1), idr = [];
|
128
|
+
|
129
|
+
for(var id in topics[topic]) {
|
130
|
+
var sub = topics[topic][id];
|
131
|
+
try {
|
132
|
+
sub[0].apply(context, args);
|
133
|
+
} catch(ex) {
|
134
|
+
if(global.console) {
|
135
|
+
console.error('jsPDF PubSub Error', ex.message, ex);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
if(sub[1]) idr.push(id);
|
139
|
+
}
|
140
|
+
if(idr.length) idr.forEach(this.unsubscribe);
|
141
|
+
}
|
142
|
+
};
|
143
|
+
}
|
144
|
+
|
145
|
+
/**
|
146
|
+
* @constructor
|
147
|
+
* @private
|
148
|
+
*/
|
149
|
+
function jsPDF(orientation, unit, format, compressPdf) {
|
150
|
+
var options = {};
|
151
|
+
|
152
|
+
if (typeof orientation === 'object') {
|
153
|
+
options = orientation;
|
154
|
+
|
155
|
+
orientation = options.orientation;
|
156
|
+
unit = options.unit || unit;
|
157
|
+
format = options.format || format;
|
158
|
+
compressPdf = options.compress || options.compressPdf || compressPdf;
|
159
|
+
}
|
160
|
+
|
161
|
+
// Default options
|
162
|
+
unit = unit || 'mm';
|
163
|
+
format = format || 'a4';
|
164
|
+
orientation = ('' + (orientation || 'P')).toLowerCase();
|
165
|
+
|
166
|
+
var format_as_string = ('' + format).toLowerCase(),
|
167
|
+
compress = !!compressPdf && typeof Uint8Array === 'function',
|
168
|
+
textColor = options.textColor || '0 g',
|
169
|
+
drawColor = options.drawColor || '0 G',
|
170
|
+
activeFontSize = options.fontSize || 16,
|
171
|
+
lineHeightProportion = options.lineHeight || 1.15,
|
172
|
+
lineWidth = options.lineWidth || 0.200025, // 2mm
|
173
|
+
objectNumber = 2, // 'n' Current object number
|
174
|
+
outToPages = !1, // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content
|
175
|
+
offsets = [], // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
|
176
|
+
fonts = {}, // collection of font objects, where key is fontKey - a dynamically created label for a given font.
|
177
|
+
fontmap = {}, // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
|
178
|
+
activeFontKey, // will be string representing the KEY of the font as combination of fontName + fontStyle
|
179
|
+
|
180
|
+
fontStateStack = [], //
|
181
|
+
|
182
|
+
patterns = {}, // collection of pattern objects
|
183
|
+
patternMap = {}, // see fonts
|
184
|
+
|
185
|
+
gStates = {}, // collection of graphic state objects
|
186
|
+
gStatesMap = {}, // see fonts
|
187
|
+
activeGState = null,
|
188
|
+
|
189
|
+
k, // Scale factor
|
190
|
+
tmp,
|
191
|
+
page = 0,
|
192
|
+
currentPage,
|
193
|
+
pages = [],
|
194
|
+
pagesContext = [], // same index as pages and pagedim
|
195
|
+
pagedim = [],
|
196
|
+
content = [],
|
197
|
+
additionalObjects = [],
|
198
|
+
lineCapID = 0,
|
199
|
+
lineJoinID = 0,
|
200
|
+
content_length = 0,
|
201
|
+
|
202
|
+
renderTargets = {},
|
203
|
+
renderTargetMap = {},
|
204
|
+
renderTargetStack = [],
|
205
|
+
|
206
|
+
pageX, pageY, pageMatrix, // only used for FormObjects
|
207
|
+
pageWidth,
|
208
|
+
pageHeight,
|
209
|
+
pageMode,
|
210
|
+
zoomMode,
|
211
|
+
layoutMode,
|
212
|
+
documentProperties = {
|
213
|
+
'title' : '',
|
214
|
+
'subject' : '',
|
215
|
+
'author' : '',
|
216
|
+
'keywords' : '',
|
217
|
+
'creator' : ''
|
218
|
+
},
|
219
|
+
API = {},
|
220
|
+
events = new PubSub(API),
|
221
|
+
|
222
|
+
/////////////////////
|
223
|
+
// Private functions
|
224
|
+
/////////////////////
|
225
|
+
f2 = function(number) {
|
226
|
+
return number.toFixed(2); // Ie, %.2f
|
227
|
+
},
|
228
|
+
f3 = function(number) {
|
229
|
+
return number.toFixed(3); // Ie, %.3f
|
230
|
+
},
|
231
|
+
padd2 = function(number) {
|
232
|
+
return ('0' + parseInt(number)).slice(-2);
|
233
|
+
},
|
234
|
+
padd2Hex = function (hexString) {
|
235
|
+
var s = "00" + hexString;
|
236
|
+
return s.substr(s.length - 2);
|
237
|
+
},
|
238
|
+
out = function(string) {
|
239
|
+
if (outToPages) {
|
240
|
+
/* set by beginPage */
|
241
|
+
pages[currentPage].push(string);
|
242
|
+
} else {
|
243
|
+
// +1 for '\n' that will be used to join 'content'
|
244
|
+
content_length += string.length + 1;
|
245
|
+
content.push(string);
|
246
|
+
}
|
247
|
+
},
|
248
|
+
newObject = function() {
|
249
|
+
// Begin a new object
|
250
|
+
objectNumber++;
|
251
|
+
offsets[objectNumber] = content_length;
|
252
|
+
out(objectNumber + ' 0 obj');
|
253
|
+
return objectNumber;
|
254
|
+
},
|
255
|
+
// Does not output the object until after the pages have been output.
|
256
|
+
// Returns an object containing the objectId and content.
|
257
|
+
// All pages have been added so the object ID can be estimated to start right after.
|
258
|
+
// This does not modify the current objectNumber; It must be updated after the newObjects are output.
|
259
|
+
newAdditionalObject = function() {
|
260
|
+
var objId = pages.length * 2 + 1;
|
261
|
+
objId += additionalObjects.length;
|
262
|
+
var obj = {objId:objId, content:''};
|
263
|
+
additionalObjects.push(obj);
|
264
|
+
return obj;
|
265
|
+
},
|
266
|
+
// Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data
|
267
|
+
newObjectDeferred = function() {
|
268
|
+
objectNumber++;
|
269
|
+
offsets[objectNumber] = function(){
|
270
|
+
return content_length;
|
271
|
+
};
|
272
|
+
return objectNumber;
|
273
|
+
},
|
274
|
+
newObjectDeferredBegin = function(oid) {
|
275
|
+
offsets[oid] = content_length;
|
276
|
+
},
|
277
|
+
putStream = function(str) {
|
278
|
+
out('stream');
|
279
|
+
out(str);
|
280
|
+
out('endstream');
|
281
|
+
},
|
282
|
+
putPages = function() {
|
283
|
+
var n,p,arr,i,deflater,adler32,adler32cs,wPt,hPt;
|
284
|
+
|
285
|
+
adler32cs = global.adler32cs || jsPDF.adler32cs;
|
286
|
+
if (compress && typeof adler32cs === 'undefined') {
|
287
|
+
compress = false;
|
288
|
+
}
|
289
|
+
|
290
|
+
// outToPages = false as set in endDocument(). out() writes to content.
|
291
|
+
|
292
|
+
for (n = 1; n <= page; n++) {
|
293
|
+
newObject();
|
294
|
+
wPt = (pageWidth = pagedim[n].width) * k;
|
295
|
+
hPt = (pageHeight = pagedim[n].height) * k;
|
296
|
+
out('<</Type /Page');
|
297
|
+
out('/Parent 1 0 R');
|
298
|
+
out('/Resources 2 0 R');
|
299
|
+
out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']');
|
300
|
+
// Added for annotation plugin
|
301
|
+
events.publish('putPage', {pageNumber: n, page: pages[n]});
|
302
|
+
out('/Contents ' + (objectNumber + 1) + ' 0 R');
|
303
|
+
out('>>');
|
304
|
+
out('endobj');
|
305
|
+
|
306
|
+
// Page content
|
307
|
+
p = pages[n].join('\n');
|
308
|
+
|
309
|
+
// prepend global change of basis matrix
|
310
|
+
// (Now, instead of converting every coordinate to the pdf coordinate system, we apply a matrix
|
311
|
+
// that does this job for us (however, texts, images and similar objects must be drawn bottom up))
|
312
|
+
p = new Matrix(k, 0, 0, -k, 0, pageHeight).toString() + " cm\n" + p;
|
313
|
+
|
314
|
+
newObject();
|
315
|
+
if (compress) {
|
316
|
+
arr = [];
|
317
|
+
i = p.length;
|
318
|
+
while(i--) {
|
319
|
+
arr[i] = p.charCodeAt(i);
|
320
|
+
}
|
321
|
+
adler32 = adler32cs.from(p);
|
322
|
+
deflater = new Deflater(6);
|
323
|
+
deflater.append(new Uint8Array(arr));
|
324
|
+
p = deflater.flush();
|
325
|
+
arr = new Uint8Array(p.length + 6);
|
326
|
+
arr.set(new Uint8Array([120, 156]));
|
327
|
+
arr.set(p, 2);
|
328
|
+
arr.set(new Uint8Array([adler32 & 0xFF, (adler32 >> 8) & 0xFF, (adler32 >> 16) & 0xFF, (adler32 >> 24) & 0xFF]), p.length+2);
|
329
|
+
p = String.fromCharCode.apply(null, arr);
|
330
|
+
out('<</Length ' + p.length + ' /Filter [/FlateDecode]>>');
|
331
|
+
} else {
|
332
|
+
out('<</Length ' + p.length + '>>');
|
333
|
+
}
|
334
|
+
putStream(p);
|
335
|
+
out('endobj');
|
336
|
+
}
|
337
|
+
offsets[1] = content_length;
|
338
|
+
out('1 0 obj');
|
339
|
+
out('<</Type /Pages');
|
340
|
+
var kids = '/Kids [';
|
341
|
+
for (i = 0; i < page; i++) {
|
342
|
+
kids += (3 + 2 * i) + ' 0 R ';
|
343
|
+
}
|
344
|
+
out(kids + ']');
|
345
|
+
out('/Count ' + page);
|
346
|
+
out('>>');
|
347
|
+
out('endobj');
|
348
|
+
events.publish('postPutPages');
|
349
|
+
},
|
350
|
+
putFont = function(font) {
|
351
|
+
font.objectNumber = newObject();
|
352
|
+
out('<</BaseFont/' + font.PostScriptName + '/Type/Font');
|
353
|
+
if (typeof font.encoding === 'string') {
|
354
|
+
out('/Encoding/' + font.encoding);
|
355
|
+
}
|
356
|
+
out('/Subtype/Type1>>');
|
357
|
+
out('endobj');
|
358
|
+
},
|
359
|
+
putFonts = function() {
|
360
|
+
for (var fontKey in fonts) {
|
361
|
+
if (fonts.hasOwnProperty(fontKey)) {
|
362
|
+
putFont(fonts[fontKey]);
|
363
|
+
}
|
364
|
+
}
|
365
|
+
},
|
366
|
+
putXObject = function (xObject) {
|
367
|
+
xObject.objectNumber = newObject();
|
368
|
+
out("<<");
|
369
|
+
out("/Type /XObject");
|
370
|
+
out("/Subtype /Form");
|
371
|
+
out("/BBox [" + [
|
372
|
+
f2(xObject.x),
|
373
|
+
f2(xObject.y),
|
374
|
+
f2(xObject.x + xObject.width),
|
375
|
+
f2(xObject.y + xObject.height)
|
376
|
+
].join(" ") + "]");
|
377
|
+
out("/Matrix [" + xObject.matrix.toString() + "]");
|
378
|
+
// TODO: /Resources
|
379
|
+
|
380
|
+
var p = xObject.pages[1].join("\n");
|
381
|
+
out("/Length " + p.length);
|
382
|
+
|
383
|
+
out(">>");
|
384
|
+
putStream(p);
|
385
|
+
out("endobj");
|
386
|
+
},
|
387
|
+
putXObjects = function () {
|
388
|
+
for (var xObjectKey in renderTargets) {
|
389
|
+
if (renderTargets.hasOwnProperty(xObjectKey)) {
|
390
|
+
putXObject(renderTargets[xObjectKey]);
|
391
|
+
}
|
392
|
+
}
|
393
|
+
},
|
394
|
+
|
395
|
+
interpolateAndEncodeRGBStream = function (colors, numberSamples) {
|
396
|
+
var tValues = [];
|
397
|
+
var t;
|
398
|
+
var dT = 1.0 / (numberSamples - 1);
|
399
|
+
for (t = 0.0; t < 1.0; t += dT) {
|
400
|
+
tValues.push(t);
|
401
|
+
}
|
402
|
+
tValues.push(1.0);
|
403
|
+
|
404
|
+
// add first and last control point if not present
|
405
|
+
if (colors[0].offset != 0.0) {
|
406
|
+
var c0 = {
|
407
|
+
offset: 0.0,
|
408
|
+
color: colors[0].color
|
409
|
+
};
|
410
|
+
colors.unshift(c0)
|
411
|
+
}
|
412
|
+
if (colors[colors.length - 1].offset != 1.0) {
|
413
|
+
var c1 = {
|
414
|
+
offset: 1.0,
|
415
|
+
color: colors[colors.length - 1].color
|
416
|
+
};
|
417
|
+
colors.push(c1);
|
418
|
+
}
|
419
|
+
|
420
|
+
var out = "";
|
421
|
+
var index = 0;
|
422
|
+
|
423
|
+
for (var i = 0; i < tValues.length; i++) {
|
424
|
+
t = tValues[i];
|
425
|
+
|
426
|
+
while (t > colors[index + 1].offset)
|
427
|
+
index++;
|
428
|
+
|
429
|
+
var a = colors[index].offset;
|
430
|
+
var b = colors[index + 1].offset;
|
431
|
+
var d = (t - a) / (b - a);
|
432
|
+
|
433
|
+
var aColor = colors[index].color;
|
434
|
+
var bColor = colors[index + 1].color;
|
435
|
+
|
436
|
+
out += padd2Hex((Math.round((1 - d) * aColor[0] + d * bColor[0])).toString(16))
|
437
|
+
+ padd2Hex((Math.round((1 - d) * aColor[1] + d * bColor[1])).toString(16))
|
438
|
+
+ padd2Hex((Math.round((1 - d) * aColor[2] + d * bColor[2])).toString(16));
|
439
|
+
}
|
440
|
+
return out.trim();
|
441
|
+
},
|
442
|
+
putShadingPattern = function (pattern, numberSamples) {
|
443
|
+
/*
|
444
|
+
Axial patterns shade between the two points specified in coords, radial patterns between the inner
|
445
|
+
and outer circle.
|
446
|
+
|
447
|
+
The user can specify an array (colors) that maps t-Values in [0, 1] to RGB colors. These are now
|
448
|
+
interpolated to equidistant samples and written to pdf as a sample (type 0) function.
|
449
|
+
*/
|
450
|
+
|
451
|
+
// The number of color samples that should be used to describe the shading.
|
452
|
+
// The higher, the more accurate the gradient will be.
|
453
|
+
numberSamples || (numberSamples = 21);
|
454
|
+
|
455
|
+
var funcObjectNumber = newObject();
|
456
|
+
var stream = interpolateAndEncodeRGBStream(pattern.colors, numberSamples);
|
457
|
+
out("<< /FunctionType 0");
|
458
|
+
out("/Domain [0.0 1.0]");
|
459
|
+
out("/Size [" + numberSamples + "]");
|
460
|
+
out("/BitsPerSample 8");
|
461
|
+
out("/Range [0.0 1.0 0.0 1.0 0.0 1.0]");
|
462
|
+
out("/Decode [0.0 1.0 0.0 1.0 0.0 1.0]");
|
463
|
+
out("/Length " + stream.length);
|
464
|
+
// The stream is Hex encoded
|
465
|
+
out("/Filter /ASCIIHexDecode");
|
466
|
+
out(">>");
|
467
|
+
putStream(stream);
|
468
|
+
out("endobj");
|
469
|
+
|
470
|
+
pattern.objectNumber = newObject();
|
471
|
+
out("<< /ShadingType " + pattern.type);
|
472
|
+
out("/ColorSpace /DeviceRGB");
|
473
|
+
|
474
|
+
var coords = "/Coords ["
|
475
|
+
+ f3(parseFloat(pattern.coords[0])) + " "// x1
|
476
|
+
+ f3(parseFloat(pattern.coords[1])) + " "; // y1
|
477
|
+
if (pattern.type === 2) {
|
478
|
+
// axial
|
479
|
+
coords += f3(parseFloat(pattern.coords[2])) + " " // x2
|
480
|
+
+ f3(parseFloat(pattern.coords[3])); // y2
|
481
|
+
} else {
|
482
|
+
// radial
|
483
|
+
coords += f3(parseFloat(pattern.coords[2])) + " "// r1
|
484
|
+
+ f3(parseFloat(pattern.coords[3])) + " " // x2
|
485
|
+
+ f3(parseFloat(pattern.coords[4])) + " " // y2
|
486
|
+
+ f3(parseFloat(pattern.coords[5])); // r2
|
487
|
+
}
|
488
|
+
coords += "]";
|
489
|
+
out(coords);
|
490
|
+
|
491
|
+
if (pattern.matrix) {
|
492
|
+
out("/Matrix [" + pattern.matrix.toString() + "]");
|
493
|
+
}
|
494
|
+
|
495
|
+
out("/Function " + funcObjectNumber + " 0 R");
|
496
|
+
out("/Extend [true true]");
|
497
|
+
out(">>");
|
498
|
+
out("endobj");
|
499
|
+
},
|
500
|
+
putTilingPattern = function (pattern) {
|
501
|
+
var resourcesObjectNumber = newObject();
|
502
|
+
out("<<");
|
503
|
+
putResourceDictionary();
|
504
|
+
out(">>");
|
505
|
+
out("endobj");
|
506
|
+
|
507
|
+
pattern.objectNumber = newObject();
|
508
|
+
out("<< /Type /Pattern");
|
509
|
+
out("/PatternType 1"); // tiling pattern
|
510
|
+
out("/PaintType 1"); // colored tiling pattern
|
511
|
+
out("/TilingType 1"); // constant spacing
|
512
|
+
out("/BBox [" + pattern.boundingBox.map(f3).join(" ") + "]");
|
513
|
+
out("/XStep " + f3(pattern.xStep));
|
514
|
+
out("/YStep " + f3(pattern.yStep));
|
515
|
+
out("/Length " + pattern.stream.length);
|
516
|
+
out("/Resources " + resourcesObjectNumber + " 0 R"); // TODO: resources
|
517
|
+
pattern.matrix && out("/Matrix [" + pattern.matrix.toString() + "]");
|
518
|
+
|
519
|
+
out(">>");
|
520
|
+
|
521
|
+
putStream(pattern.stream);
|
522
|
+
|
523
|
+
out("endobj");
|
524
|
+
},
|
525
|
+
putPatterns = function () {
|
526
|
+
var patternKey;
|
527
|
+
for (patternKey in patterns) {
|
528
|
+
if (patterns.hasOwnProperty(patternKey)) {
|
529
|
+
if (patterns[patternKey] instanceof API.ShadingPattern) {
|
530
|
+
putShadingPattern(patterns[patternKey]);
|
531
|
+
} else if (patterns[patternKey] instanceof API.TilingPattern) {
|
532
|
+
putTilingPattern(patterns[patternKey]);
|
533
|
+
}
|
534
|
+
}
|
535
|
+
}
|
536
|
+
},
|
537
|
+
|
538
|
+
putGState = function (gState) {
|
539
|
+
gState.objectNumber = newObject();
|
540
|
+
out("<<");
|
541
|
+
for (var p in gState) {
|
542
|
+
switch (p) {
|
543
|
+
case "opacity":
|
544
|
+
out("/ca " + f2(gState[p]));
|
545
|
+
break;
|
546
|
+
}
|
547
|
+
}
|
548
|
+
out(">>");
|
549
|
+
out("endobj");
|
550
|
+
},
|
551
|
+
putGStates = function () {
|
552
|
+
var gStateKey;
|
553
|
+
for (gStateKey in gStates) {
|
554
|
+
if (gStates.hasOwnProperty(gStateKey)) {
|
555
|
+
putGState(gStates[gStateKey]);
|
556
|
+
}
|
557
|
+
}
|
558
|
+
},
|
559
|
+
|
560
|
+
putXobjectDict = function () {
|
561
|
+
for (var xObjectKey in renderTargets) {
|
562
|
+
if (renderTargets.hasOwnProperty(xObjectKey) && renderTargets[xObjectKey].objectNumber >= 0) {
|
563
|
+
out("/" + xObjectKey + " " + renderTargets[xObjectKey].objectNumber + " 0 R");
|
564
|
+
}
|
565
|
+
}
|
566
|
+
|
567
|
+
events.publish('putXobjectDict');
|
568
|
+
},
|
569
|
+
|
570
|
+
putShadingPatternDict = function () {
|
571
|
+
for (var patternKey in patterns) {
|
572
|
+
if (patterns.hasOwnProperty(patternKey) && patterns[patternKey] instanceof API.ShadingPattern && patterns[patternKey].objectNumber >= 0) {
|
573
|
+
out("/" + patternKey + " " + patterns[patternKey].objectNumber + " 0 R");
|
574
|
+
}
|
575
|
+
}
|
576
|
+
|
577
|
+
events.publish("putShadingPatternDict");
|
578
|
+
},
|
579
|
+
|
580
|
+
putTilingPatternDict = function () {
|
581
|
+
for (var patternKey in patterns) {
|
582
|
+
if (patterns.hasOwnProperty(patternKey) && patterns[patternKey] instanceof API.TilingPattern && patterns[patternKey].objectNumber >= 0) {
|
583
|
+
out("/" + patternKey + " " + patterns[patternKey].objectNumber + " 0 R");
|
584
|
+
}
|
585
|
+
}
|
586
|
+
|
587
|
+
events.publish("putTilingPatternDict");
|
588
|
+
},
|
589
|
+
|
590
|
+
putGStatesDict = function () {
|
591
|
+
var gStateKey;
|
592
|
+
for (gStateKey in gStates) {
|
593
|
+
if (gStates.hasOwnProperty(gStateKey) && gStates[gStateKey].objectNumber >= 0) {
|
594
|
+
out("/" + gStateKey + " " + gStates[gStateKey].objectNumber + " 0 R");
|
595
|
+
}
|
596
|
+
}
|
597
|
+
|
598
|
+
events.publish("putGStateDict");
|
599
|
+
},
|
600
|
+
putResourceDictionary = function() {
|
601
|
+
out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
|
602
|
+
out('/Font <<');
|
603
|
+
// Do this for each font, the '1' bit is the index of the font
|
604
|
+
for (var fontKey in fonts) {
|
605
|
+
if (fonts.hasOwnProperty(fontKey)) {
|
606
|
+
out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R');
|
607
|
+
}
|
608
|
+
}
|
609
|
+
out('>>');
|
610
|
+
|
611
|
+
out("/Shading <<");
|
612
|
+
putShadingPatternDict();
|
613
|
+
out(">>");
|
614
|
+
|
615
|
+
out("/Pattern <<");
|
616
|
+
putTilingPatternDict();
|
617
|
+
out(">>");
|
618
|
+
|
619
|
+
out("/ExtGState <<");
|
620
|
+
putGStatesDict();
|
621
|
+
out('>>');
|
622
|
+
|
623
|
+
out('/XObject <<');
|
624
|
+
putXobjectDict();
|
625
|
+
out('>>');
|
626
|
+
},
|
627
|
+
putResources = function() {
|
628
|
+
putFonts();
|
629
|
+
putGStates();
|
630
|
+
putXObjects();
|
631
|
+
putPatterns();
|
632
|
+
events.publish('putResources');
|
633
|
+
// Resource dictionary
|
634
|
+
offsets[2] = content_length;
|
635
|
+
out('2 0 obj');
|
636
|
+
out('<<');
|
637
|
+
putResourceDictionary();
|
638
|
+
out('>>');
|
639
|
+
out('endobj');
|
640
|
+
events.publish('postPutResources');
|
641
|
+
},
|
642
|
+
putAdditionalObjects = function() {
|
643
|
+
events.publish('putAdditionalObjects');
|
644
|
+
for (var i=0; i<additionalObjects.length; i++){
|
645
|
+
var obj = additionalObjects[i];
|
646
|
+
offsets[obj.objId] = content_length;
|
647
|
+
out( obj.objId + ' 0 obj');
|
648
|
+
out(obj.content);
|
649
|
+
out('endobj');
|
650
|
+
}
|
651
|
+
objectNumber += additionalObjects.length;
|
652
|
+
events.publish('postPutAdditionalObjects');
|
653
|
+
},
|
654
|
+
addToFontDictionary = function(fontKey, fontName, fontStyle) {
|
655
|
+
// this is mapping structure for quick font key lookup.
|
656
|
+
// returns the KEY of the font (ex: "F1") for a given
|
657
|
+
// pair of font name and type (ex: "Arial". "Italic")
|
658
|
+
if (!fontmap.hasOwnProperty(fontName)) {
|
659
|
+
fontmap[fontName] = {};
|
660
|
+
}
|
661
|
+
fontmap[fontName][fontStyle] = fontKey;
|
662
|
+
},
|
663
|
+
/**
|
664
|
+
* FontObject describes a particular font as member of an instnace of jsPDF
|
665
|
+
*
|
666
|
+
* It's a collection of properties like 'id' (to be used in PDF stream),
|
667
|
+
* 'fontName' (font's family name), 'fontStyle' (font's style variant label)
|
668
|
+
*
|
669
|
+
* @public
|
670
|
+
* @property id {String} PDF-document-instance-specific label assinged to the font.
|
671
|
+
* @property PostScriptName {String} PDF specification full name for the font
|
672
|
+
* @property encoding {Object} Encoding_name-to-Font_metrics_object mapping.
|
673
|
+
* @name FontObject
|
674
|
+
*/
|
675
|
+
addFont = function(PostScriptName, fontName, fontStyle, encoding) {
|
676
|
+
var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10),
|
677
|
+
// This is FontObject
|
678
|
+
font = fonts[fontKey] = {
|
679
|
+
'id' : fontKey,
|
680
|
+
'PostScriptName' : PostScriptName,
|
681
|
+
'fontName' : fontName,
|
682
|
+
'fontStyle' : fontStyle,
|
683
|
+
'encoding' : encoding,
|
684
|
+
'metadata' : {}
|
685
|
+
};
|
686
|
+
addToFontDictionary(fontKey, fontName, fontStyle);
|
687
|
+
events.publish('addFont', font);
|
688
|
+
|
689
|
+
return fontKey;
|
690
|
+
},
|
691
|
+
addFonts = function() {
|
692
|
+
|
693
|
+
var HELVETICA = "helvetica",
|
694
|
+
TIMES = "times",
|
695
|
+
COURIER = "courier",
|
696
|
+
NORMAL = "normal",
|
697
|
+
BOLD = "bold",
|
698
|
+
ITALIC = "italic",
|
699
|
+
BOLD_ITALIC = "bolditalic",
|
700
|
+
encoding = 'StandardEncoding',
|
701
|
+
ZAPF = "zapfdingbats",
|
702
|
+
standardFonts = [
|
703
|
+
['Helvetica', HELVETICA, NORMAL],
|
704
|
+
['Helvetica-Bold', HELVETICA, BOLD],
|
705
|
+
['Helvetica-Oblique', HELVETICA, ITALIC],
|
706
|
+
['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC],
|
707
|
+
['Courier', COURIER, NORMAL],
|
708
|
+
['Courier-Bold', COURIER, BOLD],
|
709
|
+
['Courier-Oblique', COURIER, ITALIC],
|
710
|
+
['Courier-BoldOblique', COURIER, BOLD_ITALIC],
|
711
|
+
['Times-Roman', TIMES, NORMAL],
|
712
|
+
['Times-Bold', TIMES, BOLD],
|
713
|
+
['Times-Italic', TIMES, ITALIC],
|
714
|
+
['Times-BoldItalic', TIMES, BOLD_ITALIC],
|
715
|
+
['ZapfDingbats',ZAPF ]
|
716
|
+
];
|
717
|
+
|
718
|
+
for (var i = 0, l = standardFonts.length; i < l; i++) {
|
719
|
+
var fontKey = addFont(
|
720
|
+
standardFonts[i][0],
|
721
|
+
standardFonts[i][1],
|
722
|
+
standardFonts[i][2],
|
723
|
+
encoding);
|
724
|
+
|
725
|
+
// adding aliases for standard fonts, this time matching the capitalization
|
726
|
+
var parts = standardFonts[i][0].split('-');
|
727
|
+
addToFontDictionary(fontKey, parts[0], parts[1] || '');
|
728
|
+
}
|
729
|
+
events.publish('addFonts', { fonts : fonts, dictionary : fontmap });
|
730
|
+
},
|
731
|
+
matrixMult = function (m1, m2) {
|
732
|
+
return new Matrix(
|
733
|
+
m1.a * m2.a + m1.b * m2.c,
|
734
|
+
m1.a * m2.b + m1.b * m2.d,
|
735
|
+
m1.c * m2.a + m1.d * m2.c,
|
736
|
+
m1.c * m2.b + m1.d * m2.d,
|
737
|
+
m1.e * m2.a + m1.f * m2.c + m2.e,
|
738
|
+
m1.e * m2.b + m1.f * m2.d + m2.f
|
739
|
+
);
|
740
|
+
},
|
741
|
+
Matrix = function (a, b, c, d, e, f) {
|
742
|
+
this.a = a;
|
743
|
+
this.b = b;
|
744
|
+
this.c = c;
|
745
|
+
this.d = d;
|
746
|
+
this.e = e;
|
747
|
+
this.f = f;
|
748
|
+
};
|
749
|
+
|
750
|
+
Matrix.prototype = {
|
751
|
+
toString: function () {
|
752
|
+
return [
|
753
|
+
f3(this.a),
|
754
|
+
f3(this.b),
|
755
|
+
f3(this.c),
|
756
|
+
f3(this.d),
|
757
|
+
f3(this.e),
|
758
|
+
f3(this.f)
|
759
|
+
].join(" ");
|
760
|
+
}
|
761
|
+
};
|
762
|
+
|
763
|
+
var unitMatrix = new Matrix(1, 0, 0, 1, 0, 0),
|
764
|
+
|
765
|
+
// Used (1) to save the current stream state to the XObjects stack and (2) to save completed form
|
766
|
+
// objects in the xObjects map.
|
767
|
+
RenderTarget = function () {
|
768
|
+
this.page = page;
|
769
|
+
this.currentPage = currentPage;
|
770
|
+
this.pages = pages.slice(0);
|
771
|
+
this.pagedim = pagedim.slice(0);
|
772
|
+
this.pagesContext = pagesContext.slice(0);
|
773
|
+
this.x = pageX;
|
774
|
+
this.y = pageY;
|
775
|
+
this.matrix = pageMatrix;
|
776
|
+
this.width = pageWidth;
|
777
|
+
this.height = pageHeight;
|
778
|
+
|
779
|
+
this.id = ""; // set by endFormObject()
|
780
|
+
this.objectNumber = -1; // will be set by putXObject()
|
781
|
+
};
|
782
|
+
|
783
|
+
RenderTarget.prototype = {
|
784
|
+
restore: function () {
|
785
|
+
page = this.page;
|
786
|
+
currentPage = this.currentPage;
|
787
|
+
pagesContext = this.pagesContext;
|
788
|
+
pagedim = this.pagedim;
|
789
|
+
pages = this.pages;
|
790
|
+
pageX = this.x;
|
791
|
+
pageY = this.y;
|
792
|
+
pageMatrix = this.matrix;
|
793
|
+
pageWidth = this.width;
|
794
|
+
pageHeight = this.height;
|
795
|
+
}
|
796
|
+
};
|
797
|
+
|
798
|
+
var beginNewRenderTarget = function (x, y, width, height, matrix) {
|
799
|
+
// save current state
|
800
|
+
renderTargetStack.push(new RenderTarget());
|
801
|
+
|
802
|
+
// clear pages
|
803
|
+
page = currentPage = 0;
|
804
|
+
pages = [];
|
805
|
+
pageX = x;
|
806
|
+
pageY = y;
|
807
|
+
|
808
|
+
pageMatrix = matrix;
|
809
|
+
|
810
|
+
beginPage(width, height);
|
811
|
+
},
|
812
|
+
|
813
|
+
endFormObject = function (key) {
|
814
|
+
// only add it if it is not already present (the keys provided by the user must be unique!)
|
815
|
+
if (renderTargetMap[key])
|
816
|
+
return;
|
817
|
+
|
818
|
+
// save the created xObject
|
819
|
+
var newXObject = new RenderTarget();
|
820
|
+
|
821
|
+
var xObjectId = 'Xo' + (Object.keys(renderTargets).length + 1).toString(10);
|
822
|
+
newXObject.id = xObjectId;
|
823
|
+
|
824
|
+
renderTargetMap[key] = xObjectId;
|
825
|
+
renderTargets[xObjectId] = newXObject;
|
826
|
+
|
827
|
+
events.publish('addFormObject', newXObject);
|
828
|
+
|
829
|
+
// restore state from stack
|
830
|
+
renderTargetStack.pop().restore();
|
831
|
+
},
|
832
|
+
|
833
|
+
/**
|
834
|
+
* Adds a new pattern for later use.
|
835
|
+
* @param {String} key The key by it can be referenced later. The keys must be unique!
|
836
|
+
* @param {API.Pattern} pattern The pattern
|
837
|
+
*/
|
838
|
+
addPattern = function (key, pattern) {
|
839
|
+
// only add it if it is not already present (the keys provided by the user must be unique!)
|
840
|
+
if (patternMap[key])
|
841
|
+
return;
|
842
|
+
|
843
|
+
var prefix = pattern instanceof API.ShadingPattern ? "Sh" : "P";
|
844
|
+
var patternKey = prefix + (Object.keys(patterns).length + 1).toString(10);
|
845
|
+
pattern.id = patternKey;
|
846
|
+
|
847
|
+
patternMap[key] = patternKey;
|
848
|
+
patterns[patternKey] = pattern;
|
849
|
+
|
850
|
+
events.publish('addPattern', pattern);
|
851
|
+
},
|
852
|
+
|
853
|
+
/**
|
854
|
+
* Adds a new Graphics State. Duplicates are automatically eliminated.
|
855
|
+
* @param {String} key Might also be null, if no later reference to this gState is needed
|
856
|
+
* @param {Object} gState The gState object
|
857
|
+
*/
|
858
|
+
addGState = function (key, gState) {
|
859
|
+
// only add it if it is not already present (the keys provided by the user must be unique!)
|
860
|
+
if (key && gStatesMap[key])
|
861
|
+
return;
|
862
|
+
|
863
|
+
var duplicate = false;
|
864
|
+
for (var s in gStates) {
|
865
|
+
if (gStates.hasOwnProperty(s)) {
|
866
|
+
if (gStates[s].equals(gState)) {
|
867
|
+
duplicate = true;
|
868
|
+
break;
|
869
|
+
}
|
870
|
+
}
|
871
|
+
}
|
872
|
+
|
873
|
+
if (duplicate) {
|
874
|
+
gState = gStates[s];
|
875
|
+
} else {
|
876
|
+
var gStateKey = 'GS' + (Object.keys(gStates).length + 1).toString(10);
|
877
|
+
gStates[gStateKey] = gState;
|
878
|
+
gState.id = gStateKey;
|
879
|
+
}
|
880
|
+
|
881
|
+
// several user keys may point to the same GState object
|
882
|
+
key && (gStatesMap[key] = gState.id);
|
883
|
+
|
884
|
+
events.publish('addGState', gState);
|
885
|
+
|
886
|
+
return gState;
|
887
|
+
},
|
888
|
+
SAFE = function __safeCall(fn) {
|
889
|
+
fn.foo = function __safeCallWrapper() {
|
890
|
+
try {
|
891
|
+
return fn.apply(this, arguments);
|
892
|
+
} catch (e) {
|
893
|
+
var stack = e.stack || '';
|
894
|
+
if(~stack.indexOf(' at ')) stack = stack.split(" at ")[1];
|
895
|
+
var m = "Error in function " + stack.split("\n")[0].split('<')[0] + ": " + e.message;
|
896
|
+
if(global.console) {
|
897
|
+
global.console.error(m, e);
|
898
|
+
if(global.alert) alert(m);
|
899
|
+
} else {
|
900
|
+
throw new Error(m);
|
901
|
+
}
|
902
|
+
}
|
903
|
+
};
|
904
|
+
fn.foo.bar = fn;
|
905
|
+
return fn.foo;
|
906
|
+
},
|
907
|
+
to8bitStream = function(text, flags) {
|
908
|
+
/**
|
909
|
+
* PDF 1.3 spec:
|
910
|
+
* "For text strings encoded in Unicode, the first two bytes must be 254 followed by
|
911
|
+
* 255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts
|
912
|
+
* with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely
|
913
|
+
* to be a meaningful beginning of a word or phrase.) The remainder of the
|
914
|
+
* string consists of Unicode character codes, according to the UTF-16 encoding
|
915
|
+
* specified in the Unicode standard, version 2.0. Commonly used Unicode values
|
916
|
+
* are represented as 2 bytes per character, with the high-order byte appearing first
|
917
|
+
* in the string."
|
918
|
+
*
|
919
|
+
* In other words, if there are chars in a string with char code above 255, we
|
920
|
+
* recode the string to UCS2 BE - string doubles in length and BOM is prepended.
|
921
|
+
*
|
922
|
+
* HOWEVER!
|
923
|
+
* Actual *content* (body) text (as opposed to strings used in document properties etc)
|
924
|
+
* does NOT expect BOM. There, it is treated as a literal GID (Glyph ID)
|
925
|
+
*
|
926
|
+
* Because of Adobe's focus on "you subset your fonts!" you are not supposed to have
|
927
|
+
* a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could
|
928
|
+
* fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode
|
929
|
+
* code page. There, however, all characters in the stream are treated as GIDs,
|
930
|
+
* including BOM, which is the reason we need to skip BOM in content text (i.e. that
|
931
|
+
* that is tied to a font).
|
932
|
+
*
|
933
|
+
* To signal this "special" PDFEscape / to8bitStream handling mode,
|
934
|
+
* API.text() function sets (unless you overwrite it with manual values
|
935
|
+
* given to API.text(.., flags) )
|
936
|
+
* flags.autoencode = true
|
937
|
+
* flags.noBOM = true
|
938
|
+
*
|
939
|
+
* ===================================================================================
|
940
|
+
* `flags` properties relied upon:
|
941
|
+
* .sourceEncoding = string with encoding label.
|
942
|
+
* "Unicode" by default. = encoding of the incoming text.
|
943
|
+
* pass some non-existing encoding name
|
944
|
+
* (ex: 'Do not touch my strings! I know what I am doing.')
|
945
|
+
* to make encoding code skip the encoding step.
|
946
|
+
* .outputEncoding = Either valid PDF encoding name
|
947
|
+
* (must be supported by jsPDF font metrics, otherwise no encoding)
|
948
|
+
* or a JS object, where key = sourceCharCode, value = outputCharCode
|
949
|
+
* missing keys will be treated as: sourceCharCode === outputCharCode
|
950
|
+
* .noBOM
|
951
|
+
* See comment higher above for explanation for why this is important
|
952
|
+
* .autoencode
|
953
|
+
* See comment higher above for explanation for why this is important
|
954
|
+
*/
|
955
|
+
|
956
|
+
var i,l,sourceEncoding,encodingBlock,outputEncoding,newtext,isUnicode,ch,bch;
|
957
|
+
|
958
|
+
flags = flags || {};
|
959
|
+
sourceEncoding = flags.sourceEncoding || 'Unicode';
|
960
|
+
outputEncoding = flags.outputEncoding;
|
961
|
+
|
962
|
+
// This 'encoding' section relies on font metrics format
|
963
|
+
// attached to font objects by, among others,
|
964
|
+
// "Willow Systems' standard_font_metrics plugin"
|
965
|
+
// see jspdf.plugin.standard_font_metrics.js for format
|
966
|
+
// of the font.metadata.encoding Object.
|
967
|
+
// It should be something like
|
968
|
+
// .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}}
|
969
|
+
// .widths = {0:width, code:width, ..., 'fof':divisor}
|
970
|
+
// .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...}
|
971
|
+
if ((flags.autoencode || outputEncoding) &&
|
972
|
+
fonts[activeFontKey].metadata &&
|
973
|
+
fonts[activeFontKey].metadata[sourceEncoding] &&
|
974
|
+
fonts[activeFontKey].metadata[sourceEncoding].encoding) {
|
975
|
+
encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding;
|
976
|
+
|
977
|
+
// each font has default encoding. Some have it clearly defined.
|
978
|
+
if (!outputEncoding && fonts[activeFontKey].encoding) {
|
979
|
+
outputEncoding = fonts[activeFontKey].encoding;
|
980
|
+
}
|
981
|
+
|
982
|
+
// Hmmm, the above did not work? Let's try again, in different place.
|
983
|
+
if (!outputEncoding && encodingBlock.codePages) {
|
984
|
+
outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default
|
985
|
+
}
|
986
|
+
|
987
|
+
if (typeof outputEncoding === 'string') {
|
988
|
+
outputEncoding = encodingBlock[outputEncoding];
|
989
|
+
}
|
990
|
+
// we want output encoding to be a JS Object, where
|
991
|
+
// key = sourceEncoding's character code and
|
992
|
+
// value = outputEncoding's character code.
|
993
|
+
if (outputEncoding) {
|
994
|
+
isUnicode = false;
|
995
|
+
newtext = [];
|
996
|
+
for (i = 0, l = text.length; i < l; i++) {
|
997
|
+
ch = outputEncoding[text.charCodeAt(i)];
|
998
|
+
if (ch) {
|
999
|
+
newtext.push(
|
1000
|
+
String.fromCharCode(ch));
|
1001
|
+
} else {
|
1002
|
+
newtext.push(
|
1003
|
+
text[i]);
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
// since we are looping over chars anyway, might as well
|
1007
|
+
// check for residual unicodeness
|
1008
|
+
if (newtext[i].charCodeAt(0) >> 8) {
|
1009
|
+
/* more than 255 */
|
1010
|
+
isUnicode = true;
|
1011
|
+
}
|
1012
|
+
}
|
1013
|
+
text = newtext.join('');
|
1014
|
+
}
|
1015
|
+
}
|
1016
|
+
|
1017
|
+
i = text.length;
|
1018
|
+
// isUnicode may be set to false above. Hence the triple-equal to undefined
|
1019
|
+
while (isUnicode === undefined && i !== 0) {
|
1020
|
+
if (text.charCodeAt(i - 1) >> 8) {
|
1021
|
+
/* more than 255 */
|
1022
|
+
isUnicode = true;
|
1023
|
+
}
|
1024
|
+
i--;
|
1025
|
+
}
|
1026
|
+
if (!isUnicode) {
|
1027
|
+
return text;
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
newtext = flags.noBOM ? [] : [254, 255];
|
1031
|
+
for (i = 0, l = text.length; i < l; i++) {
|
1032
|
+
ch = text.charCodeAt(i);
|
1033
|
+
bch = ch >> 8; // divide by 256
|
1034
|
+
if (bch >> 8) {
|
1035
|
+
/* something left after dividing by 256 second time */
|
1036
|
+
throw new Error("Character at position " + i + " of string '"
|
1037
|
+
+ text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE");
|
1038
|
+
}
|
1039
|
+
newtext.push(bch);
|
1040
|
+
newtext.push(ch - (bch << 8));
|
1041
|
+
}
|
1042
|
+
return String.fromCharCode.apply(undefined, newtext);
|
1043
|
+
},
|
1044
|
+
pdfEscape = function(text, flags) {
|
1045
|
+
/**
|
1046
|
+
* Replace '/', '(', and ')' with pdf-safe versions
|
1047
|
+
*
|
1048
|
+
* Doing to8bitStream does NOT make this PDF display unicode text. For that
|
1049
|
+
* we also need to reference a unicode font and embed it - royal pain in the rear.
|
1050
|
+
*
|
1051
|
+
* There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars,
|
1052
|
+
* which JavaScript Strings are happy to provide. So, while we still cannot display
|
1053
|
+
* 2-byte characters property, at least CONDITIONALLY converting (entire string containing)
|
1054
|
+
* 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF
|
1055
|
+
* is still parseable.
|
1056
|
+
* This will allow immediate support for unicode in document properties strings.
|
1057
|
+
*/
|
1058
|
+
return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
1059
|
+
},
|
1060
|
+
putInfo = function() {
|
1061
|
+
out('/Producer (jsPDF ' + jsPDF.version + ')');
|
1062
|
+
for(var key in documentProperties) {
|
1063
|
+
if(documentProperties.hasOwnProperty(key) && documentProperties[key]) {
|
1064
|
+
out('/'+key.substr(0,1).toUpperCase() + key.substr(1)
|
1065
|
+
+' (' + pdfEscape(documentProperties[key]) + ')');
|
1066
|
+
}
|
1067
|
+
}
|
1068
|
+
var created = new Date(),
|
1069
|
+
tzoffset = created.getTimezoneOffset(),
|
1070
|
+
tzsign = tzoffset < 0 ? '+' : '-',
|
1071
|
+
tzhour = Math.floor(Math.abs(tzoffset / 60)),
|
1072
|
+
tzmin = Math.abs(tzoffset % 60),
|
1073
|
+
tzstr = [tzsign, padd2(tzhour), "'", padd2(tzmin), "'"].join('');
|
1074
|
+
out(['/CreationDate (D:',
|
1075
|
+
created.getFullYear(),
|
1076
|
+
padd2(created.getMonth() + 1),
|
1077
|
+
padd2(created.getDate()),
|
1078
|
+
padd2(created.getHours()),
|
1079
|
+
padd2(created.getMinutes()),
|
1080
|
+
padd2(created.getSeconds()), tzstr, ')'].join(''));
|
1081
|
+
},
|
1082
|
+
putCatalog = function() {
|
1083
|
+
out('/Type /Catalog');
|
1084
|
+
out('/Pages 1 0 R');
|
1085
|
+
// PDF13ref Section 7.2.1
|
1086
|
+
if (!zoomMode) zoomMode = 'fullwidth';
|
1087
|
+
switch(zoomMode) {
|
1088
|
+
case 'fullwidth' : out('/OpenAction [3 0 R /FitH null]'); break;
|
1089
|
+
case 'fullheight' : out('/OpenAction [3 0 R /FitV null]'); break;
|
1090
|
+
case 'fullpage' : out('/OpenAction [3 0 R /Fit]'); break;
|
1091
|
+
case 'original' : out('/OpenAction [3 0 R /XYZ null null 1]'); break;
|
1092
|
+
default:
|
1093
|
+
var pcn = '' + zoomMode;
|
1094
|
+
if (pcn.substr(pcn.length-1) === '%')
|
1095
|
+
zoomMode = parseInt(zoomMode) / 100;
|
1096
|
+
if (typeof zoomMode === 'number') {
|
1097
|
+
out('/OpenAction [3 0 R /XYZ null null '+f2(zoomMode)+']');
|
1098
|
+
}
|
1099
|
+
}
|
1100
|
+
if (!layoutMode) layoutMode = 'continuous';
|
1101
|
+
switch(layoutMode) {
|
1102
|
+
case 'continuous' : out('/PageLayout /OneColumn'); break;
|
1103
|
+
case 'single' : out('/PageLayout /SinglePage'); break;
|
1104
|
+
case 'two':
|
1105
|
+
case 'twoleft' : out('/PageLayout /TwoColumnLeft'); break;
|
1106
|
+
case 'tworight' : out('/PageLayout /TwoColumnRight'); break;
|
1107
|
+
}
|
1108
|
+
if (pageMode) {
|
1109
|
+
/**
|
1110
|
+
* A name object specifying how the document should be displayed when opened:
|
1111
|
+
* UseNone : Neither document outline nor thumbnail images visible -- DEFAULT
|
1112
|
+
* UseOutlines : Document outline visible
|
1113
|
+
* UseThumbs : Thumbnail images visible
|
1114
|
+
* FullScreen : Full-screen mode, with no menu bar, window controls, or any other window visible
|
1115
|
+
*/
|
1116
|
+
out('/PageMode /' + pageMode);
|
1117
|
+
}
|
1118
|
+
events.publish('putCatalog');
|
1119
|
+
},
|
1120
|
+
putTrailer = function() {
|
1121
|
+
out('/Size ' + (objectNumber + 1));
|
1122
|
+
out('/Root ' + objectNumber + ' 0 R');
|
1123
|
+
out('/Info ' + (objectNumber - 1) + ' 0 R');
|
1124
|
+
},
|
1125
|
+
beginPage = function(width,height) {
|
1126
|
+
// Dimensions are stored as user units and converted to points on output
|
1127
|
+
var orientation = typeof height === 'string' && height.toLowerCase();
|
1128
|
+
if (typeof width === 'string') {
|
1129
|
+
var format = width.toLowerCase();
|
1130
|
+
if (pageFormats.hasOwnProperty(format)) {
|
1131
|
+
width = pageFormats[format][0] / k;
|
1132
|
+
height = pageFormats[format][1] / k;
|
1133
|
+
}
|
1134
|
+
}
|
1135
|
+
if (Array.isArray(width)) {
|
1136
|
+
height = width[1];
|
1137
|
+
width = width[0];
|
1138
|
+
}
|
1139
|
+
//if (orientation) {
|
1140
|
+
// switch(orientation.substr(0,1)) {
|
1141
|
+
// case 'l': if (height > width ) orientation = 's'; break;
|
1142
|
+
// case 'p': if (width > height ) orientation = 's'; break;
|
1143
|
+
// }
|
1144
|
+
// TODO: What is the reason for this (for me it only seems to raise bugs)?
|
1145
|
+
// if (orientation === 's') { tmp = width; width = height; height = tmp; }
|
1146
|
+
//}
|
1147
|
+
outToPages = true;
|
1148
|
+
pages[++page] = [];
|
1149
|
+
pagedim[page] = {
|
1150
|
+
width : Number(width) || pageWidth,
|
1151
|
+
height : Number(height) || pageHeight
|
1152
|
+
};
|
1153
|
+
pagesContext[page] = {};
|
1154
|
+
_setPage(page);
|
1155
|
+
},
|
1156
|
+
_addPage = function() {
|
1157
|
+
beginPage.apply(this, arguments);
|
1158
|
+
// Set line width
|
1159
|
+
out(f2(lineWidth) + ' w');
|
1160
|
+
// Set draw color
|
1161
|
+
out(drawColor);
|
1162
|
+
// resurrecting non-default line caps, joins
|
1163
|
+
if (lineCapID !== 0) {
|
1164
|
+
out(lineCapID + ' J');
|
1165
|
+
}
|
1166
|
+
if (lineJoinID !== 0) {
|
1167
|
+
out(lineJoinID + ' j');
|
1168
|
+
}
|
1169
|
+
events.publish('addPage', { pageNumber : page });
|
1170
|
+
},
|
1171
|
+
_deletePage = function( n ) {
|
1172
|
+
if (n > 0 && n <= page) {
|
1173
|
+
pages.splice(n, 1);
|
1174
|
+
pagedim.splice(n, 1);
|
1175
|
+
page--;
|
1176
|
+
if (currentPage > page){
|
1177
|
+
currentPage = page;
|
1178
|
+
}
|
1179
|
+
this.setPage(currentPage);
|
1180
|
+
}
|
1181
|
+
},
|
1182
|
+
_setPage = function(n) {
|
1183
|
+
if (n > 0 && n <= page) {
|
1184
|
+
currentPage = n;
|
1185
|
+
pageWidth = pagedim[n].width;
|
1186
|
+
pageHeight = pagedim[n].height;
|
1187
|
+
}
|
1188
|
+
},
|
1189
|
+
/**
|
1190
|
+
* Returns a document-specific font key - a label assigned to a
|
1191
|
+
* font name + font type combination at the time the font was added
|
1192
|
+
* to the font inventory.
|
1193
|
+
*
|
1194
|
+
* Font key is used as label for the desired font for a block of text
|
1195
|
+
* to be added to the PDF document stream.
|
1196
|
+
* @private
|
1197
|
+
* @function
|
1198
|
+
* @param {String} fontName can be undefined on "falthy" to indicate "use current"
|
1199
|
+
* @param {String} fontStyle can be undefined on "falthy" to indicate "use current"
|
1200
|
+
* @returns {String} Font key.
|
1201
|
+
*/
|
1202
|
+
getFont = function(fontName, fontStyle) {
|
1203
|
+
var key;
|
1204
|
+
|
1205
|
+
fontName = fontName !== undefined ? fontName : fonts[activeFontKey].fontName;
|
1206
|
+
fontStyle = fontStyle !== undefined ? fontStyle : fonts[activeFontKey].fontStyle;
|
1207
|
+
|
1208
|
+
if (fontName !== undefined){
|
1209
|
+
fontName = fontName.toLowerCase();
|
1210
|
+
}
|
1211
|
+
switch(fontName){
|
1212
|
+
case 'sans-serif':
|
1213
|
+
case 'verdana':
|
1214
|
+
case 'arial':
|
1215
|
+
case 'helvetica':
|
1216
|
+
fontName = 'helvetica';
|
1217
|
+
break;
|
1218
|
+
case 'fixed':
|
1219
|
+
case 'monospace':
|
1220
|
+
case 'terminal':
|
1221
|
+
case 'courier':
|
1222
|
+
fontName = 'courier';
|
1223
|
+
break;
|
1224
|
+
case 'serif':
|
1225
|
+
case 'cursive':
|
1226
|
+
case 'fantasy':
|
1227
|
+
default:
|
1228
|
+
fontName = 'times';
|
1229
|
+
break;
|
1230
|
+
}
|
1231
|
+
|
1232
|
+
try {
|
1233
|
+
// get a string like 'F3' - the KEY corresponding tot he font + type combination.
|
1234
|
+
key = fontmap[fontName][fontStyle];
|
1235
|
+
} catch (e) {}
|
1236
|
+
|
1237
|
+
if (!key) {
|
1238
|
+
//throw new Error("Unable to look up font label for font '" + fontName + "', '"
|
1239
|
+
//+ fontStyle + "'. Refer to getFontList() for available fonts.");
|
1240
|
+
key = fontmap['times'][fontStyle];
|
1241
|
+
if (key == null){
|
1242
|
+
key = fontmap['times']['normal'];
|
1243
|
+
}
|
1244
|
+
}
|
1245
|
+
return key;
|
1246
|
+
},
|
1247
|
+
buildDocument = function() {
|
1248
|
+
|
1249
|
+
outToPages = false; // switches out() to content
|
1250
|
+
objectNumber = 2;
|
1251
|
+
content = [];
|
1252
|
+
offsets = [];
|
1253
|
+
additionalObjects = [];
|
1254
|
+
|
1255
|
+
// putHeader()
|
1256
|
+
out('%PDF-' + pdfVersion);
|
1257
|
+
|
1258
|
+
putPages();
|
1259
|
+
|
1260
|
+
// Must happen after putPages
|
1261
|
+
// Modifies current object Id
|
1262
|
+
putAdditionalObjects();
|
1263
|
+
|
1264
|
+
putResources();
|
1265
|
+
|
1266
|
+
// Info
|
1267
|
+
newObject();
|
1268
|
+
out('<<');
|
1269
|
+
putInfo();
|
1270
|
+
out('>>');
|
1271
|
+
out('endobj');
|
1272
|
+
|
1273
|
+
// Catalog
|
1274
|
+
newObject();
|
1275
|
+
out('<<');
|
1276
|
+
putCatalog();
|
1277
|
+
out('>>');
|
1278
|
+
out('endobj');
|
1279
|
+
|
1280
|
+
// Cross-ref
|
1281
|
+
var o = content_length, i, p = "0000000000";
|
1282
|
+
out('xref');
|
1283
|
+
out('0 ' + (objectNumber + 1));
|
1284
|
+
out(p+' 65535 f ');
|
1285
|
+
for (i = 1; i <= objectNumber; i++) {
|
1286
|
+
var offset = offsets[i];
|
1287
|
+
if (typeof offset === 'function'){
|
1288
|
+
out((p + offsets[i]()).slice(-10) + ' 00000 n ');
|
1289
|
+
}else{
|
1290
|
+
out((p + offsets[i]).slice(-10) + ' 00000 n ');
|
1291
|
+
}
|
1292
|
+
}
|
1293
|
+
// Trailer
|
1294
|
+
out('trailer');
|
1295
|
+
out('<<');
|
1296
|
+
putTrailer();
|
1297
|
+
out('>>');
|
1298
|
+
out('startxref');
|
1299
|
+
out(o);
|
1300
|
+
out('%%EOF');
|
1301
|
+
|
1302
|
+
outToPages = true;
|
1303
|
+
|
1304
|
+
return content.join('\n');
|
1305
|
+
},
|
1306
|
+
|
1307
|
+
getStyle = function(style) {
|
1308
|
+
// see path-painting operators in PDF spec
|
1309
|
+
var op = 'n'; // none
|
1310
|
+
if (style === "D") {
|
1311
|
+
op = 'S'; // stroke
|
1312
|
+
} else if (style === 'F') {
|
1313
|
+
op = 'f'; // fill
|
1314
|
+
} else if (style === 'FD' || style === 'DF') {
|
1315
|
+
op = 'B'; // both
|
1316
|
+
} else if (style === 'f' || style === 'f*' || style === 'B' || style === 'B*') {
|
1317
|
+
/*
|
1318
|
+
Allow direct use of these PDF path-painting operators:
|
1319
|
+
- f fill using nonzero winding number rule
|
1320
|
+
- f* fill using even-odd rule
|
1321
|
+
- B fill then stroke with fill using non-zero winding number rule
|
1322
|
+
- B* fill then stroke with fill using even-odd rule
|
1323
|
+
*/
|
1324
|
+
op = style;
|
1325
|
+
}
|
1326
|
+
return op;
|
1327
|
+
},
|
1328
|
+
// puts the style for the previously drawn path. If a patternKey is provided, the pattern is used to fill
|
1329
|
+
// the path. Use patternMatrix to transform the pattern to rhe right location.
|
1330
|
+
putStyle = function (style, patternKey, patternData) {
|
1331
|
+
style = getStyle(style);
|
1332
|
+
|
1333
|
+
// stroking / filling / both the path
|
1334
|
+
if (!patternKey) {
|
1335
|
+
out(style);
|
1336
|
+
return;
|
1337
|
+
}
|
1338
|
+
|
1339
|
+
patternData || (patternData = unitMatrix);
|
1340
|
+
|
1341
|
+
var patternId = patternMap[patternKey];
|
1342
|
+
var pattern = patterns[patternId];
|
1343
|
+
|
1344
|
+
if (pattern instanceof API.ShadingPattern) {
|
1345
|
+
out("q");
|
1346
|
+
out("W " + style);
|
1347
|
+
|
1348
|
+
if (pattern.gState) {
|
1349
|
+
API.setGState(pattern.gState);
|
1350
|
+
}
|
1351
|
+
|
1352
|
+
out(patternData.toString() + " cm");
|
1353
|
+
out("/" + patternId + " sh");
|
1354
|
+
out("Q");
|
1355
|
+
} else if (pattern instanceof API.TilingPattern) {
|
1356
|
+
// pdf draws patterns starting at the bottom left corner and they are not affected by the global transformation,
|
1357
|
+
// so we must flip them
|
1358
|
+
var matrix = new Matrix(1, 0, 0, -1, 0, pageHeight);
|
1359
|
+
|
1360
|
+
if (patternData.matrix) {
|
1361
|
+
matrix = matrixMult(patternData.matrix || unitMatrix, matrix);
|
1362
|
+
|
1363
|
+
// we cannot apply a matrix to the pattern on use so we must abuse the pattern matrix and create new instances
|
1364
|
+
// for each use
|
1365
|
+
patternId = pattern.createClone(patternKey, patternData.boundingBox, patternData.xStep, patternData.yStep, matrix).id;
|
1366
|
+
}
|
1367
|
+
|
1368
|
+
out("q");
|
1369
|
+
out("/Pattern cs");
|
1370
|
+
out("/" + patternId + " scn");
|
1371
|
+
|
1372
|
+
if (pattern.gState) {
|
1373
|
+
API.setGState(pattern.gState);
|
1374
|
+
}
|
1375
|
+
|
1376
|
+
out(style);
|
1377
|
+
out("Q");
|
1378
|
+
}
|
1379
|
+
},
|
1380
|
+
|
1381
|
+
getArrayBuffer = function() {
|
1382
|
+
var data = buildDocument(), len = data.length,
|
1383
|
+
ab = new ArrayBuffer(len), u8 = new Uint8Array(ab);
|
1384
|
+
|
1385
|
+
while(len--) u8[len] = data.charCodeAt(len);
|
1386
|
+
return ab;
|
1387
|
+
},
|
1388
|
+
getBlob = function() {
|
1389
|
+
return new Blob([getArrayBuffer()], { type : "application/pdf" });
|
1390
|
+
},
|
1391
|
+
/**
|
1392
|
+
* Generates the PDF document.
|
1393
|
+
*
|
1394
|
+
* If `type` argument is undefined, output is raw body of resulting PDF returned as a string.
|
1395
|
+
*
|
1396
|
+
* @param {String} type A string identifying one of the possible output types.
|
1397
|
+
* @param {Object} options An object providing some additional signalling to PDF generator.
|
1398
|
+
* @function
|
1399
|
+
* @returns {jsPDF}
|
1400
|
+
* @methodOf jsPDF#
|
1401
|
+
* @name output
|
1402
|
+
*/
|
1403
|
+
output = SAFE(function(type, options) {
|
1404
|
+
var datauri = ('' + type).substr(0,6) === 'dataur'
|
1405
|
+
? 'data:application/pdf;base64,'+btoa(buildDocument()):0;
|
1406
|
+
|
1407
|
+
switch (type) {
|
1408
|
+
case undefined:
|
1409
|
+
return buildDocument();
|
1410
|
+
case 'save':
|
1411
|
+
if (navigator.getUserMedia) {
|
1412
|
+
if (global.URL === undefined
|
1413
|
+
|| global.URL.createObjectURL === undefined) {
|
1414
|
+
return API.output('dataurlnewwindow');
|
1415
|
+
}
|
1416
|
+
}
|
1417
|
+
saveAs(getBlob(), options);
|
1418
|
+
if(typeof saveAs.unload === 'function') {
|
1419
|
+
if(global.setTimeout) {
|
1420
|
+
setTimeout(saveAs.unload,911);
|
1421
|
+
}
|
1422
|
+
}
|
1423
|
+
break;
|
1424
|
+
case 'arraybuffer':
|
1425
|
+
return getArrayBuffer();
|
1426
|
+
case 'blob':
|
1427
|
+
return getBlob();
|
1428
|
+
case 'bloburi':
|
1429
|
+
case 'bloburl':
|
1430
|
+
// User is responsible of calling revokeObjectURL
|
1431
|
+
return global.URL && global.URL.createObjectURL(getBlob()) || void 0;
|
1432
|
+
case 'datauristring':
|
1433
|
+
case 'dataurlstring':
|
1434
|
+
return datauri;
|
1435
|
+
case 'dataurlnewwindow':
|
1436
|
+
var nW = global.open(datauri);
|
1437
|
+
if (nW || typeof safari === "undefined") return nW;
|
1438
|
+
/* pass through */
|
1439
|
+
case 'datauri':
|
1440
|
+
case 'dataurl':
|
1441
|
+
return global.document.location.href = datauri;
|
1442
|
+
default:
|
1443
|
+
throw new Error('Output type "' + type + '" is not supported.');
|
1444
|
+
}
|
1445
|
+
// @TODO: Add different output options
|
1446
|
+
});
|
1447
|
+
|
1448
|
+
switch (unit) {
|
1449
|
+
case 'pt': k = 1; break;
|
1450
|
+
case 'mm': k = 72 / 25.4000508; break;
|
1451
|
+
case 'cm': k = 72 / 2.54000508; break;
|
1452
|
+
case 'in': k = 72; break;
|
1453
|
+
case 'px': k = 96 / 72; break;
|
1454
|
+
case 'pc': k = 12; break;
|
1455
|
+
case 'em': k = 12; break;
|
1456
|
+
case 'ex': k = 6; break;
|
1457
|
+
default:
|
1458
|
+
throw ('Invalid unit: ' + unit);
|
1459
|
+
}
|
1460
|
+
|
1461
|
+
//---------------------------------------
|
1462
|
+
// Public API
|
1463
|
+
|
1464
|
+
/**
|
1465
|
+
* Object exposing internal API to plugins
|
1466
|
+
* @public
|
1467
|
+
*/
|
1468
|
+
API.internal = {
|
1469
|
+
'pdfEscape' : pdfEscape,
|
1470
|
+
'getStyle' : getStyle,
|
1471
|
+
/**
|
1472
|
+
* Returns {FontObject} describing a particular font.
|
1473
|
+
* @public
|
1474
|
+
* @function
|
1475
|
+
* @param {String} fontName (Optional) Font's family name
|
1476
|
+
* @param {String} fontStyle (Optional) Font's style variation name (Example:"Italic")
|
1477
|
+
* @returns {FontObject}
|
1478
|
+
*/
|
1479
|
+
'getFont' : function() {
|
1480
|
+
return fonts[getFont.apply(API, arguments)];
|
1481
|
+
},
|
1482
|
+
'getFontSize' : function() {
|
1483
|
+
return activeFontSize;
|
1484
|
+
},
|
1485
|
+
'getLineHeight' : function() {
|
1486
|
+
return activeFontSize * lineHeightProportion;
|
1487
|
+
},
|
1488
|
+
'write' : function(string1 /*, string2, string3, etc */) {
|
1489
|
+
out(arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' '));
|
1490
|
+
},
|
1491
|
+
'getCoordinateString' : function(value) {
|
1492
|
+
return f2(value);
|
1493
|
+
},
|
1494
|
+
'getVerticalCoordinateString' : function(value) {
|
1495
|
+
return f2(value);
|
1496
|
+
},
|
1497
|
+
'collections' : {},
|
1498
|
+
'newObject' : newObject,
|
1499
|
+
'newAdditionalObject' : newAdditionalObject,
|
1500
|
+
'newObjectDeferred' : newObjectDeferred,
|
1501
|
+
'newObjectDeferredBegin' : newObjectDeferredBegin,
|
1502
|
+
'putStream' : putStream,
|
1503
|
+
'events' : events,
|
1504
|
+
// ratio that you use in multiplication of a given "size" number to arrive to 'point'
|
1505
|
+
// units of measurement.
|
1506
|
+
// scaleFactor is set at initialization of the document and calculated against the stated
|
1507
|
+
// default measurement units for the document.
|
1508
|
+
// If default is "mm", k is the number that will turn number in 'mm' into 'points' number.
|
1509
|
+
// through multiplication.
|
1510
|
+
'scaleFactor' : k,
|
1511
|
+
'pageSize' : {
|
1512
|
+
get width() {
|
1513
|
+
return pageWidth
|
1514
|
+
},
|
1515
|
+
get height() {
|
1516
|
+
return pageHeight
|
1517
|
+
}
|
1518
|
+
},
|
1519
|
+
'output' : function(type, options) {
|
1520
|
+
return output(type, options);
|
1521
|
+
},
|
1522
|
+
'getNumberOfPages' : function() {
|
1523
|
+
return pages.length - 1;
|
1524
|
+
},
|
1525
|
+
'pages' : pages,
|
1526
|
+
'out' : out,
|
1527
|
+
'f2' : f2,
|
1528
|
+
'getPageInfo' : function(pageNumberOneBased){
|
1529
|
+
var objId = (pageNumberOneBased - 1) * 2 + 3;
|
1530
|
+
return {objId:objId, pageNumber:pageNumberOneBased, pageContext:pagesContext[pageNumberOneBased]};
|
1531
|
+
},
|
1532
|
+
'getCurrentPageInfo' : function(){
|
1533
|
+
var objId = (currentPage - 1) * 2 + 3;
|
1534
|
+
return {objId:objId, pageNumber:currentPage, pageContext:pagesContext[currentPage]};
|
1535
|
+
},
|
1536
|
+
'getPDFVersion': function () {
|
1537
|
+
return pdfVersion;
|
1538
|
+
}
|
1539
|
+
};
|
1540
|
+
|
1541
|
+
/**
|
1542
|
+
* An object representing a pdf graphics state.
|
1543
|
+
* @param parameters A parameter object that contains all properties this graphics state wants to set.
|
1544
|
+
* Supported are: opacity
|
1545
|
+
* @constructor
|
1546
|
+
*/
|
1547
|
+
API.GState = function (parameters) {
|
1548
|
+
var supported = "opacity";
|
1549
|
+
for (var p in parameters) {
|
1550
|
+
if (parameters.hasOwnProperty(p) && supported.indexOf(p) >= 0) {
|
1551
|
+
this[p] = parameters[p];
|
1552
|
+
}
|
1553
|
+
}
|
1554
|
+
this.id = ""; // set by addGState()
|
1555
|
+
this.objectNumber = -1; // will be set by putGState()
|
1556
|
+
|
1557
|
+
this.equals = function (other) {
|
1558
|
+
var ignore = "id,objectNumber,equals";
|
1559
|
+
if (!other || typeof other !== typeof this)
|
1560
|
+
return false;
|
1561
|
+
var count = 0;
|
1562
|
+
for (var p in this) {
|
1563
|
+
if (ignore.indexOf(p) >= 0)
|
1564
|
+
continue;
|
1565
|
+
if (this.hasOwnProperty(p) && !other.hasOwnProperty(p))
|
1566
|
+
return false;
|
1567
|
+
if (this[p] !== other[p])
|
1568
|
+
return false;
|
1569
|
+
count++;
|
1570
|
+
}
|
1571
|
+
for (var p in other) {
|
1572
|
+
if (other.hasOwnProperty(p) && ignore.indexOf(p) < 0)
|
1573
|
+
count--;
|
1574
|
+
}
|
1575
|
+
return count === 0;
|
1576
|
+
}
|
1577
|
+
};
|
1578
|
+
|
1579
|
+
/**
|
1580
|
+
* Adds a new {@link GState} for later use {@see setGState}.
|
1581
|
+
* @param {String} key
|
1582
|
+
* @param {GState} gState
|
1583
|
+
* @function
|
1584
|
+
* @returns {jsPDF}
|
1585
|
+
* @methodOf jsPDF#
|
1586
|
+
* @name addGState
|
1587
|
+
*/
|
1588
|
+
API.addGState = function (key, gState) {
|
1589
|
+
addGState(key, gState);
|
1590
|
+
return this;
|
1591
|
+
};
|
1592
|
+
|
1593
|
+
/**
|
1594
|
+
* Adds (and transfers the focus to) new page to the PDF document.
|
1595
|
+
* @function
|
1596
|
+
* @returns {jsPDF}
|
1597
|
+
*
|
1598
|
+
* @methodOf jsPDF#
|
1599
|
+
* @name addPage
|
1600
|
+
*/
|
1601
|
+
API.addPage = function() {
|
1602
|
+
_addPage.apply(this, arguments);
|
1603
|
+
return this;
|
1604
|
+
};
|
1605
|
+
API.setPage = function() {
|
1606
|
+
_setPage.apply(this, arguments);
|
1607
|
+
return this;
|
1608
|
+
};
|
1609
|
+
API.insertPage = function(beforePage) {
|
1610
|
+
this.addPage();
|
1611
|
+
this.movePage(currentPage, beforePage);
|
1612
|
+
return this;
|
1613
|
+
};
|
1614
|
+
API.movePage = function(targetPage, beforePage) {
|
1615
|
+
var tmpPagesContext, tmpPagedim, tmpPages, i;
|
1616
|
+
if (targetPage > beforePage){
|
1617
|
+
tmpPages = pages[targetPage];
|
1618
|
+
tmpPagedim = pagedim[targetPage];
|
1619
|
+
tmpPagesContext = pagesContext[targetPage];
|
1620
|
+
for (i = targetPage; i > beforePage; i--){
|
1621
|
+
pages[i] = pages[i-1];
|
1622
|
+
pagedim[i] = pagedim[i-1];
|
1623
|
+
pagesContext[i] = pagesContext[i-1];
|
1624
|
+
}
|
1625
|
+
pages[beforePage] = tmpPages;
|
1626
|
+
pagedim[beforePage] = tmpPagedim;
|
1627
|
+
pagesContext[beforePage] = tmpPagesContext;
|
1628
|
+
this.setPage(beforePage);
|
1629
|
+
} else if (targetPage < beforePage){
|
1630
|
+
tmpPages = pages[targetPage];
|
1631
|
+
tmpPagedim = pagedim[targetPage];
|
1632
|
+
tmpPagesContext = pagesContext[targetPage];
|
1633
|
+
for (i = targetPage; i < beforePage; i++){
|
1634
|
+
pages[i] = pages[i+1];
|
1635
|
+
pagedim[i] = pagedim[i+1];
|
1636
|
+
pagesContext[i] = pagesContext[i+1];
|
1637
|
+
}
|
1638
|
+
pages[beforePage] = tmpPages;
|
1639
|
+
pagedim[beforePage] = tmpPagedim;
|
1640
|
+
pagesContext[beforePage] = tmpPagesContext;
|
1641
|
+
this.setPage(beforePage);
|
1642
|
+
}
|
1643
|
+
return this;
|
1644
|
+
};
|
1645
|
+
|
1646
|
+
API.deletePage = function() {
|
1647
|
+
_deletePage.apply( this, arguments );
|
1648
|
+
return this;
|
1649
|
+
};
|
1650
|
+
API.setDisplayMode = function(zoom, layout, pmode) {
|
1651
|
+
zoomMode = zoom;
|
1652
|
+
layoutMode = layout;
|
1653
|
+
pageMode = pmode;
|
1654
|
+
return this;
|
1655
|
+
};
|
1656
|
+
|
1657
|
+
/**
|
1658
|
+
* Saves the current graphics state ("pushes it on the stack"). It can be restored by {@link restoreGraphicsState}
|
1659
|
+
* later. Here, the general pdf graphics state is meant, also including the current transformation matrix,
|
1660
|
+
* fill and stroke colors etc.
|
1661
|
+
* @function
|
1662
|
+
* @returns {jsPDF}
|
1663
|
+
* @methodOf jsPDF#
|
1664
|
+
* @name saveGraphicsState
|
1665
|
+
*/
|
1666
|
+
API.saveGraphicsState = function () {
|
1667
|
+
out("q");
|
1668
|
+
// as we cannot set font key and size independently we must keep track of both
|
1669
|
+
fontStateStack.push({
|
1670
|
+
key: activeFontKey,
|
1671
|
+
size: activeFontSize
|
1672
|
+
});
|
1673
|
+
return this;
|
1674
|
+
};
|
1675
|
+
|
1676
|
+
/**
|
1677
|
+
* Restores a previously saved graphics state saved by {@link saveGraphicsState} ("pops the stack").
|
1678
|
+
* @function
|
1679
|
+
* @returns {jsPDF}
|
1680
|
+
* @methodOf jsPDF#
|
1681
|
+
* @name restoreGraphicsState
|
1682
|
+
*/
|
1683
|
+
API.restoreGraphicsState = function () {
|
1684
|
+
out("Q");
|
1685
|
+
|
1686
|
+
// restore previous font state
|
1687
|
+
var fontState = fontStateStack.pop();
|
1688
|
+
activeFontKey = fontState.key;
|
1689
|
+
activeFontSize = fontState.size;
|
1690
|
+
|
1691
|
+
return this;
|
1692
|
+
};
|
1693
|
+
|
1694
|
+
/**
|
1695
|
+
* Appends this matrix to the left of all previously applied matrices.
|
1696
|
+
* @param {Matrix} matrix
|
1697
|
+
* @function
|
1698
|
+
* @returns {jsPDF}
|
1699
|
+
* @methodOf jsPDF#
|
1700
|
+
* @name setCurrentTransformationMatrix
|
1701
|
+
*/
|
1702
|
+
API.setCurrentTransformationMatrix = function (matrix) {
|
1703
|
+
out(matrix.toString() + " cm");
|
1704
|
+
return this;
|
1705
|
+
};
|
1706
|
+
|
1707
|
+
/**
|
1708
|
+
* Starts a new pdf form object, which means that all conseequent draw calls target a new independent object
|
1709
|
+
* until {@link endFormObject} is called. The created object can be referenced and drawn later using
|
1710
|
+
* {@link doFormObject}. Nested form objects are possible.
|
1711
|
+
* x, y, width, height set the bounding box that is used to clip the content.
|
1712
|
+
* @param {number} x
|
1713
|
+
* @param {number} y
|
1714
|
+
* @param {number} width
|
1715
|
+
* @param {number} height
|
1716
|
+
* @param {Matrix} matrix The matrix that will be applied to convert the form objects coordinate system to
|
1717
|
+
* the parent's.
|
1718
|
+
* @function
|
1719
|
+
* @returns {jsPDF}
|
1720
|
+
* @methodOf jsPDF#
|
1721
|
+
*/
|
1722
|
+
API.beginFormObject = function (x, y, width, height, matrix) {
|
1723
|
+
// The user can set the output target to a new form object. Nested form objects are possible.
|
1724
|
+
// Currently, they use the resource dictionary of the surrounding stream. This should be changed, as
|
1725
|
+
// the PDF-Spec states:
|
1726
|
+
// "In PDF 1.2 and later versions, form XObjects may be independent of the content streams in which
|
1727
|
+
// they appear, and this is strongly recommended although not requiredIn PDF 1.2 and later versions,
|
1728
|
+
// form XObjects may be independent of the content streams in which they appear, and this is strongly
|
1729
|
+
// recommended although not required"
|
1730
|
+
beginNewRenderTarget(x, y, width, height, matrix);
|
1731
|
+
return this;
|
1732
|
+
};
|
1733
|
+
|
1734
|
+
/**
|
1735
|
+
* Completes and saves the form object.
|
1736
|
+
* @param {String} key The key by which this form object can be referenced.
|
1737
|
+
* @function
|
1738
|
+
* @returns {jsPDF}
|
1739
|
+
* @methodOf jsPDF#
|
1740
|
+
* @name endFormObject
|
1741
|
+
*/
|
1742
|
+
API.endFormObject = function (key) {
|
1743
|
+
endFormObject(key);
|
1744
|
+
return this;
|
1745
|
+
};
|
1746
|
+
|
1747
|
+
/**
|
1748
|
+
* Draws the specified form object by referencing to the respective pdf XObject created with
|
1749
|
+
* {@link API.beginFormObject} and {@link endFormObject}.
|
1750
|
+
* The location is determined by matrix.
|
1751
|
+
* @param {String} key The key to the form object.
|
1752
|
+
* @param {Matrix} matrix The matrix applied before drawing the form object.
|
1753
|
+
* @function
|
1754
|
+
* @returns {jsPDF}
|
1755
|
+
* @methodOf jsPDF#
|
1756
|
+
* @name doFormObject
|
1757
|
+
*/
|
1758
|
+
API.doFormObject = function (key, matrix) {
|
1759
|
+
var xObject = renderTargets[renderTargetMap[key]];
|
1760
|
+
out("q");
|
1761
|
+
out(matrix.toString() + " cm");
|
1762
|
+
out("/" + xObject.id + " Do");
|
1763
|
+
out("Q");
|
1764
|
+
return this;
|
1765
|
+
};
|
1766
|
+
|
1767
|
+
/**
|
1768
|
+
* Returns the form object specified by key.
|
1769
|
+
* @param key {String}
|
1770
|
+
* @returns {{x: number, y: number, width: number, height: number, matrix: Matrix}}
|
1771
|
+
* @function
|
1772
|
+
* @returns {jsPDF}
|
1773
|
+
* @methodOf jsPDF#
|
1774
|
+
* @name getFormObject
|
1775
|
+
*/
|
1776
|
+
API.getFormObject = function (key) {
|
1777
|
+
var xObject = renderTargets[renderTargetMap[key]];
|
1778
|
+
return {
|
1779
|
+
x: xObject.x,
|
1780
|
+
y: xObject.y,
|
1781
|
+
width: xObject.width,
|
1782
|
+
height: xObject.height,
|
1783
|
+
matrix: xObject.matrix
|
1784
|
+
};
|
1785
|
+
};
|
1786
|
+
|
1787
|
+
/**
|
1788
|
+
* A matrix object for 2D homogenous transformations:
|
1789
|
+
* | a b 0 |
|
1790
|
+
* | c d 0 |
|
1791
|
+
* | e f 1 |
|
1792
|
+
* pdf multiplies matrices righthand: v' = v x m1 x m2 x ...
|
1793
|
+
* @param {number} a
|
1794
|
+
* @param {number} b
|
1795
|
+
* @param {number} c
|
1796
|
+
* @param {number} d
|
1797
|
+
* @param {number} e
|
1798
|
+
* @param {number} f
|
1799
|
+
* @constructor
|
1800
|
+
*/
|
1801
|
+
API.Matrix = Matrix;
|
1802
|
+
|
1803
|
+
/**
|
1804
|
+
* Multiplies two matrices. (see {@link Matrix})
|
1805
|
+
* @param {Matrix} m1
|
1806
|
+
* @param {Matrix} m2
|
1807
|
+
*/
|
1808
|
+
API.matrixMult = matrixMult;
|
1809
|
+
|
1810
|
+
/**
|
1811
|
+
* The unit matrix (equal to new Matrix(1, 0, 0, 1, 0, 0).
|
1812
|
+
* @type {Matrix}
|
1813
|
+
*/
|
1814
|
+
API.unitMatrix = unitMatrix;
|
1815
|
+
|
1816
|
+
var Pattern = function (gState, matrix) {
|
1817
|
+
this.gState = gState;
|
1818
|
+
this.matrix = matrix;
|
1819
|
+
|
1820
|
+
this.id = ""; // set by addPattern()
|
1821
|
+
this.objectNumber = -1; // will be set by putPattern()
|
1822
|
+
};
|
1823
|
+
|
1824
|
+
/**
|
1825
|
+
* A pattern describing a shading pattern.
|
1826
|
+
* @param {String} type One of "axial" or "radial"
|
1827
|
+
* @param {Array<Number>} coords Either [x1, y1, x2, y2] for "axial" type describing the two interpolation points
|
1828
|
+
* or [x1, y1, r, x2, y2, r2] for "radial" describing inner and the outer circle.
|
1829
|
+
* @param {Array<Object>} colors An array of objects with the fields "offset" and "color". "offset" describes
|
1830
|
+
* the offset in parameter space [0, 1]. "color" is an array of length 3 describing RGB values in [0, 255].
|
1831
|
+
* @param {GState=} gState An additional graphics state that gets applied to the pattern (optional).
|
1832
|
+
* @param {Matrix=} matrix A matrix that describes the transformation between the pattern coordinate system
|
1833
|
+
* and the use coordinate system (optional).
|
1834
|
+
* @constructor
|
1835
|
+
* @extends API.Pattern
|
1836
|
+
*/
|
1837
|
+
API.ShadingPattern = function (type, coords, colors, gState, matrix) {
|
1838
|
+
// see putPattern() for information how they are realized
|
1839
|
+
this.type = type === "axial" ? 2 : 3;
|
1840
|
+
this.coords = coords;
|
1841
|
+
this.colors = colors;
|
1842
|
+
|
1843
|
+
Pattern.call(this, gState, matrix);
|
1844
|
+
};
|
1845
|
+
|
1846
|
+
/**
|
1847
|
+
* A PDF Tiling pattern.
|
1848
|
+
* @param {Array.<Number>} boundingBox The bounding box at which one pattern cell gets clipped.
|
1849
|
+
* @param {Number} xStep Horizontal spacing between pattern cells.
|
1850
|
+
* @param {Number} yStep Vertical spacing between pattern cells.
|
1851
|
+
* @param {API.GState=} gState An additional graphics state that gets applied to the pattern (optional).
|
1852
|
+
* @param {Matrix=} matrix A matrix that describes the transformation between the pattern coordinate system
|
1853
|
+
* and the use coordinate system (optional).
|
1854
|
+
* @constructor
|
1855
|
+
* @extends API.Pattern
|
1856
|
+
*/
|
1857
|
+
API.TilingPattern = function (boundingBox, xStep, yStep, gState, matrix) {
|
1858
|
+
this.boundingBox = boundingBox;
|
1859
|
+
this.xStep = xStep;
|
1860
|
+
this.yStep = yStep;
|
1861
|
+
|
1862
|
+
this.stream = ""; // set by endTilingPattern();
|
1863
|
+
|
1864
|
+
this.cloneIndex = 0;
|
1865
|
+
|
1866
|
+
Pattern.call(this, gState, matrix);
|
1867
|
+
};
|
1868
|
+
|
1869
|
+
API.TilingPattern.prototype = {
|
1870
|
+
createClone: function (patternKey, boundingBox, xStep, yStep, matrix) {
|
1871
|
+
var clone = new API.TilingPattern(boundingBox || this.boundingBox, xStep || this.xStep, yStep || this.yStep,
|
1872
|
+
this.gState, matrix || this.matrix);
|
1873
|
+
clone.stream = this.stream;
|
1874
|
+
var key = patternKey + "$$" + this.cloneIndex++ + "$$";
|
1875
|
+
addPattern(key, clone);
|
1876
|
+
return clone;
|
1877
|
+
}
|
1878
|
+
};
|
1879
|
+
|
1880
|
+
/**
|
1881
|
+
* Adds a new {@link API.ShadingPattern} for later use.
|
1882
|
+
* @param {String} key
|
1883
|
+
* @param {Pattern} pattern
|
1884
|
+
* @function
|
1885
|
+
* @returns {jsPDF}
|
1886
|
+
* @methodOf jsPDF#
|
1887
|
+
* @name addPattern
|
1888
|
+
*/
|
1889
|
+
API.addShadingPattern = function (key, pattern) {
|
1890
|
+
addPattern(key, pattern);
|
1891
|
+
return this;
|
1892
|
+
};
|
1893
|
+
|
1894
|
+
/**
|
1895
|
+
* Begins a new tiling pattern. All subsequent render calls are drawn to this pattern until {@link API.endTilingPattern}
|
1896
|
+
* gets called.
|
1897
|
+
* @param {API.Pattern} pattern
|
1898
|
+
*/
|
1899
|
+
API.beginTilingPattern = function (pattern) {
|
1900
|
+
beginNewRenderTarget(pattern.boundingBox[0], pattern.boundingBox[1],
|
1901
|
+
pattern.boundingBox[2] - pattern.boundingBox[0], pattern.boundingBox[3] - pattern.boundingBox[1], pattern.matrix);
|
1902
|
+
};
|
1903
|
+
|
1904
|
+
/**
|
1905
|
+
* Ends a tiling pattern and sets the render target to the one active before {@link API.beginTilingPattern} has been called.
|
1906
|
+
* @param {string} key A unique key that is used to reference this pattern at later use.
|
1907
|
+
* @param {API.Pattern} pattern The pattern to end.
|
1908
|
+
*/
|
1909
|
+
API.endTilingPattern = function (key, pattern) {
|
1910
|
+
// retrieve the stream
|
1911
|
+
pattern.stream = pages[currentPage].join("\n");
|
1912
|
+
|
1913
|
+
addPattern(key, pattern);
|
1914
|
+
|
1915
|
+
events.publish("endTilingPattern", pattern);
|
1916
|
+
|
1917
|
+
// restore state from stack
|
1918
|
+
renderTargetStack.pop().restore();
|
1919
|
+
};
|
1920
|
+
|
1921
|
+
/**
|
1922
|
+
* Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.
|
1923
|
+
*
|
1924
|
+
* @function
|
1925
|
+
* @param {String|Array} text String or array of strings to be added to the page. Each line is shifted one line down
|
1926
|
+
* per font, spacing settings declared before this call.
|
1927
|
+
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
1928
|
+
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
1929
|
+
* @param {Object} flags Collection of settings signalling how the text must be encoded. Defaults are sane. If you
|
1930
|
+
* think you want to pass some flags, you likely can read the source.
|
1931
|
+
* @param {number|Matrix} transform If transform is a number the text will be rotated by this value. If it is a Matrix,
|
1932
|
+
* this matrix gets directly applied to the text, which allows shearing effects etc.
|
1933
|
+
* @param align {string}
|
1934
|
+
* @returns {jsPDF}
|
1935
|
+
* @methodOf jsPDF#
|
1936
|
+
*/
|
1937
|
+
API.text = function(text, x, y, flags, transform, align) {
|
1938
|
+
/**
|
1939
|
+
* Inserts something like this into PDF
|
1940
|
+
* BT
|
1941
|
+
* /F1 16 Tf % Font name + size
|
1942
|
+
* 16 TL % How many units down for next line in multiline text
|
1943
|
+
* 0 g % color
|
1944
|
+
* 28.35 813.54 Td % position
|
1945
|
+
* (line one) Tj
|
1946
|
+
* T* (line two) Tj
|
1947
|
+
* T* (line three) Tj
|
1948
|
+
* ET
|
1949
|
+
*/
|
1950
|
+
function ESC(s) {
|
1951
|
+
s = s.split("\t").join(Array(options.TabLen||9).join(" "));
|
1952
|
+
return pdfEscape(s, flags);
|
1953
|
+
}
|
1954
|
+
|
1955
|
+
// Pre-August-2012 the order of arguments was function(x, y, text, flags)
|
1956
|
+
// in effort to make all calls have similar signature like
|
1957
|
+
// function(data, coordinates... , miscellaneous)
|
1958
|
+
// this method had its args flipped.
|
1959
|
+
// code below allows backward compatibility with old arg order.
|
1960
|
+
if (typeof text === 'number') {
|
1961
|
+
var tmp = y;
|
1962
|
+
y = x;
|
1963
|
+
x = text;
|
1964
|
+
text = tmp;
|
1965
|
+
}
|
1966
|
+
|
1967
|
+
// If there are any newlines in text, we assume
|
1968
|
+
// the user wanted to print multiple lines, so break the
|
1969
|
+
// text up into an array. If the text is already an array,
|
1970
|
+
// we assume the user knows what they are doing.
|
1971
|
+
// Convert text into an array anyway to simplify
|
1972
|
+
// later code.
|
1973
|
+
if (typeof text === 'string') {
|
1974
|
+
if(text.match(/[\n\r]/)) {
|
1975
|
+
text = text.split( /\r\n|\r|\n/g);
|
1976
|
+
} else {
|
1977
|
+
text = [text];
|
1978
|
+
}
|
1979
|
+
}
|
1980
|
+
if (typeof transform === 'string') {
|
1981
|
+
align = transform;
|
1982
|
+
transform = null;
|
1983
|
+
}
|
1984
|
+
if (typeof flags === 'string') {
|
1985
|
+
align = flags;
|
1986
|
+
flags = null;
|
1987
|
+
}
|
1988
|
+
if (typeof flags === 'number') {
|
1989
|
+
transform = flags;
|
1990
|
+
flags = null;
|
1991
|
+
}
|
1992
|
+
|
1993
|
+
var todo;
|
1994
|
+
if (transform && typeof transform === "number") {
|
1995
|
+
transform *= (Math.PI / 180);
|
1996
|
+
var c = Math.cos(transform),
|
1997
|
+
s = Math.sin(transform);
|
1998
|
+
transform = new Matrix(c, s , -s, c, 0, 0);
|
1999
|
+
} else if (!transform) {
|
2000
|
+
transform = unitMatrix;
|
2001
|
+
}
|
2002
|
+
|
2003
|
+
flags = flags || {};
|
2004
|
+
if (!('noBOM' in flags))
|
2005
|
+
flags.noBOM = true;
|
2006
|
+
if (!('autoencode' in flags))
|
2007
|
+
flags.autoencode = true;
|
2008
|
+
|
2009
|
+
var strokeOption = '';
|
2010
|
+
var pageContext = this.internal.getCurrentPageInfo().pageContext;
|
2011
|
+
if (true === flags.stroke){
|
2012
|
+
if (pageContext.lastTextWasStroke !== true){
|
2013
|
+
strokeOption = '1 Tr\n';
|
2014
|
+
pageContext.lastTextWasStroke = true;
|
2015
|
+
}
|
2016
|
+
}
|
2017
|
+
else{
|
2018
|
+
if (pageContext.lastTextWasStroke){
|
2019
|
+
strokeOption = '0 Tr\n';
|
2020
|
+
}
|
2021
|
+
pageContext.lastTextWasStroke = false;
|
2022
|
+
}
|
2023
|
+
|
2024
|
+
if (typeof this._runningPageHeight === 'undefined'){
|
2025
|
+
this._runningPageHeight = 0;
|
2026
|
+
}
|
2027
|
+
|
2028
|
+
if (typeof text === 'string') {
|
2029
|
+
text = ESC(text);
|
2030
|
+
} else if (Object.prototype.toString.call(text) === '[object Array]') {
|
2031
|
+
// we don't want to destroy original text array, so cloning it
|
2032
|
+
var sa = text.concat(), da = [], len = sa.length;
|
2033
|
+
// we do array.join('text that must not be PDFescaped")
|
2034
|
+
// thus, pdfEscape each component separately
|
2035
|
+
while (len--) {
|
2036
|
+
da.push(ESC(sa.shift()));
|
2037
|
+
}
|
2038
|
+
var linesLeft = Math.ceil((y - this._runningPageHeight) / (activeFontSize * lineHeightProportion));
|
2039
|
+
if (0 <= linesLeft && linesLeft < da.length + 1) {
|
2040
|
+
//todo = da.splice(linesLeft-1);
|
2041
|
+
}
|
2042
|
+
|
2043
|
+
if( align ) {
|
2044
|
+
var left,
|
2045
|
+
prevX,
|
2046
|
+
maxLineLength,
|
2047
|
+
leading = activeFontSize * lineHeightProportion,
|
2048
|
+
lineWidths = text.map( function( v ) {
|
2049
|
+
return this.getStringUnitWidth( v ) * activeFontSize;
|
2050
|
+
}, this );
|
2051
|
+
maxLineLength = Math.max.apply( Math, lineWidths );
|
2052
|
+
// The first line uses the "main" Td setting,
|
2053
|
+
// and the subsequent lines are offset by the
|
2054
|
+
// previous line's x coordinate.
|
2055
|
+
if( align === "center" ) {
|
2056
|
+
// The passed in x coordinate defines
|
2057
|
+
// the center point.
|
2058
|
+
left = x - maxLineLength / 2;
|
2059
|
+
x -= lineWidths[0] / 2;
|
2060
|
+
} else if ( align === "right" ) {
|
2061
|
+
// The passed in x coordinate defines the
|
2062
|
+
// rightmost point of the text.
|
2063
|
+
left = x - maxLineLength;
|
2064
|
+
x -= lineWidths[0];
|
2065
|
+
} else {
|
2066
|
+
throw new Error('Unrecognized alignment option, use "center" or "right".');
|
2067
|
+
}
|
2068
|
+
prevX = x;
|
2069
|
+
text = da[0] + ") Tj\n";
|
2070
|
+
for ( i = 1, len = da.length ; i < len; i++ ) {
|
2071
|
+
var delta = maxLineLength - lineWidths[i];
|
2072
|
+
if( align === "center" ) delta /= 2;
|
2073
|
+
// T* = x-offset leading Td ( text )
|
2074
|
+
text += ( ( left - prevX ) + delta ) + " -" + leading + " Td (" + da[i];
|
2075
|
+
prevX = left + delta;
|
2076
|
+
if( i < len - 1 ) {
|
2077
|
+
text += ") Tj\n";
|
2078
|
+
}
|
2079
|
+
}
|
2080
|
+
} else {
|
2081
|
+
text = da.join(") Tj\nT* (");
|
2082
|
+
}
|
2083
|
+
} else {
|
2084
|
+
throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.');
|
2085
|
+
}
|
2086
|
+
// Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates
|
2087
|
+
|
2088
|
+
// BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET
|
2089
|
+
// if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations)
|
2090
|
+
// Thus, there is NO useful, *reliable* concept of "default" font for a page.
|
2091
|
+
// The fact that "default" (reuse font used before) font worked before in basic cases is an accident
|
2092
|
+
// - readers dealing smartly with brokenness of jsPDF's markup.
|
2093
|
+
|
2094
|
+
var curY;
|
2095
|
+
|
2096
|
+
if (todo){
|
2097
|
+
//this.addPage();
|
2098
|
+
//this._runningPageHeight += y - (activeFontSize * 1.7);
|
2099
|
+
//curY = f2(activeFontSize * 1.7);
|
2100
|
+
} else {
|
2101
|
+
curY = f2(y);
|
2102
|
+
}
|
2103
|
+
//curY = f2(((y - this._runningPageHeight));
|
2104
|
+
|
2105
|
+
// if (curY < 0){
|
2106
|
+
// console.log('auto page break');
|
2107
|
+
// this.addPage();
|
2108
|
+
// this._runningPageHeight = y - (activeFontSize * 1.7);
|
2109
|
+
// curY = f2(activeFontSize * 1.7);
|
2110
|
+
// }
|
2111
|
+
|
2112
|
+
var translate = new Matrix(1, 0, 0, -1, x, curY);
|
2113
|
+
transform = matrixMult(translate, transform);
|
2114
|
+
var position = transform.toString() + " Tm";
|
2115
|
+
|
2116
|
+
out(
|
2117
|
+
'BT\n' +
|
2118
|
+
(activeFontSize * lineHeightProportion) + ' TL\n' + // line spacing
|
2119
|
+
strokeOption +// stroke option
|
2120
|
+
position + '\n(' +
|
2121
|
+
text +
|
2122
|
+
') Tj\nET');
|
2123
|
+
|
2124
|
+
if (todo) {
|
2125
|
+
//this.text( todo, x, activeFontSize * 1.7);
|
2126
|
+
//this.text( todo, x, this._runningPageHeight + (activeFontSize * 1.7));
|
2127
|
+
this.text( todo, x, y);// + (activeFontSize * 1.7));
|
2128
|
+
}
|
2129
|
+
|
2130
|
+
return this;
|
2131
|
+
};
|
2132
|
+
|
2133
|
+
|
2134
|
+
API.lstext = function(text, x, y, spacing) {
|
2135
|
+
for (var i = 0, len = text.length ; i < len; i++, x += spacing) this.text(text[i], x, y);
|
2136
|
+
};
|
2137
|
+
|
2138
|
+
/**
|
2139
|
+
* Draw a line
|
2140
|
+
* @param {number} x1
|
2141
|
+
* @param {number} y1
|
2142
|
+
* @param {number} x2
|
2143
|
+
* @param {number} y2
|
2144
|
+
* @function
|
2145
|
+
* @returns {jsPDF}
|
2146
|
+
* @methodOf jsPDF#
|
2147
|
+
* @name line
|
2148
|
+
*/
|
2149
|
+
API.line = function(x1, y1, x2, y2) {
|
2150
|
+
return this.lines([[x2 - x1, y2 - y1]], x1, y1, [1, 1], "D");
|
2151
|
+
};
|
2152
|
+
|
2153
|
+
API.clip = function() {
|
2154
|
+
// By patrick-roberts, github.com/MrRio/jsPDF/issues/328
|
2155
|
+
// Call .clip() after calling .rect() with a style argument of null
|
2156
|
+
out('W'); // clip
|
2157
|
+
out('S'); // stroke path; necessary for clip to work
|
2158
|
+
};
|
2159
|
+
|
2160
|
+
|
2161
|
+
/**
|
2162
|
+
* @typedef {Object} PatternData
|
2163
|
+
* {Matrix|undefined} matrix
|
2164
|
+
* {Number|undefined} xStep
|
2165
|
+
* {Number|undefined} yStep
|
2166
|
+
* {Array.<Number>|undefined} boundingBox
|
2167
|
+
*/
|
2168
|
+
|
2169
|
+
/**
|
2170
|
+
* Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates.
|
2171
|
+
* All data points in `lines` are relative to last line origin.
|
2172
|
+
* `x`, `y` become x1,y1 for first line / curve in the set.
|
2173
|
+
* For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point.
|
2174
|
+
* For bezier curves you need to specify [x2,y2,x3,y3,x4,y4] - vectors to control points 1, 2, ending point. All vectors are against the start of the curve - x1,y1.
|
2175
|
+
*
|
2176
|
+
* @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line
|
2177
|
+
* @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves).
|
2178
|
+
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
2179
|
+
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
2180
|
+
* @param {Number} scale (Defaults to [1.0,1.0]) x,y Scaling factor for all vectors. Elements can be any floating number Sub-one makes drawing smaller. Over-one grows the drawing. Negative flips the direction.
|
2181
|
+
* @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
|
2182
|
+
* @param {Boolean} closed If true, the path is closed with a straight line from the end of the last curve to the starting point.
|
2183
|
+
* @param {String} patternKey The pattern key for the pattern that should be used to fill the path.
|
2184
|
+
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
|
2185
|
+
* will modify the pattern on use.
|
2186
|
+
* @function
|
2187
|
+
* @returns {jsPDF}
|
2188
|
+
* @methodOf jsPDF#
|
2189
|
+
* @name lines
|
2190
|
+
*/
|
2191
|
+
API.lines = function(lines, x, y, scale, style, closed, patternKey, patternData) {
|
2192
|
+
var scalex,scaley,i,l,leg,x2,y2,x3,y3,x4,y4;
|
2193
|
+
|
2194
|
+
// Pre-August-2012 the order of arguments was function(x, y, lines, scale, style)
|
2195
|
+
// in effort to make all calls have similar signature like
|
2196
|
+
// function(content, coordinateX, coordinateY , miscellaneous)
|
2197
|
+
// this method had its args flipped.
|
2198
|
+
// code below allows backward compatibility with old arg order.
|
2199
|
+
if (typeof lines === 'number') {
|
2200
|
+
var tmp = y;
|
2201
|
+
y = x;
|
2202
|
+
x = lines;
|
2203
|
+
lines = tmp;
|
2204
|
+
}
|
2205
|
+
|
2206
|
+
scale = scale || [1, 1];
|
2207
|
+
|
2208
|
+
// starting point
|
2209
|
+
out(f3(x) + ' ' + f3(y) + ' m ');
|
2210
|
+
|
2211
|
+
scalex = scale[0];
|
2212
|
+
scaley = scale[1];
|
2213
|
+
l = lines.length;
|
2214
|
+
//, x2, y2 // bezier only. In page default measurement "units", *after* scaling
|
2215
|
+
//, x3, y3 // bezier only. In page default measurement "units", *after* scaling
|
2216
|
+
// ending point for all, lines and bezier. . In page default measurement "units", *after* scaling
|
2217
|
+
x4 = x; // last / ending point = starting point for first item.
|
2218
|
+
y4 = y; // last / ending point = starting point for first item.
|
2219
|
+
|
2220
|
+
for (i = 0; i < l; i++) {
|
2221
|
+
leg = lines[i];
|
2222
|
+
if (leg.length === 2) {
|
2223
|
+
// simple line
|
2224
|
+
x4 = leg[0] * scalex + x4; // here last x4 was prior ending point
|
2225
|
+
y4 = leg[1] * scaley + y4; // here last y4 was prior ending point
|
2226
|
+
out(f3(x4) + ' ' + f3(y4) + ' l');
|
2227
|
+
} else {
|
2228
|
+
// bezier curve
|
2229
|
+
x2 = leg[0] * scalex + x4; // here last x4 is prior ending point
|
2230
|
+
y2 = leg[1] * scaley + y4; // here last y4 is prior ending point
|
2231
|
+
x3 = leg[2] * scalex + x4; // here last x4 is prior ending point
|
2232
|
+
y3 = leg[3] * scaley + y4; // here last y4 is prior ending point
|
2233
|
+
x4 = leg[4] * scalex + x4; // here last x4 was prior ending point
|
2234
|
+
y4 = leg[5] * scaley + y4; // here last y4 was prior ending point
|
2235
|
+
out(
|
2236
|
+
f3(x2) + ' ' +
|
2237
|
+
f3(y2) + ' ' +
|
2238
|
+
f3(x3) + ' ' +
|
2239
|
+
f3(y3) + ' ' +
|
2240
|
+
f3(x4) + ' ' +
|
2241
|
+
f3(y4) + ' c');
|
2242
|
+
}
|
2243
|
+
}
|
2244
|
+
|
2245
|
+
if (closed) {
|
2246
|
+
out('h');
|
2247
|
+
}
|
2248
|
+
|
2249
|
+
putStyle(style, patternKey, patternData);
|
2250
|
+
|
2251
|
+
return this;
|
2252
|
+
};
|
2253
|
+
|
2254
|
+
/**
|
2255
|
+
* Similar to {@link API.lines} but all coordinates are interpreted as absolute coordinates instead of relative.
|
2256
|
+
* @param {Array<Object>} lines An array of {op: operator, c: coordinates} object, where op is one of "m" (move to), "l" (line to)
|
2257
|
+
* "c" (cubic bezier curve) and "h" (close (sub)path)). c is an array of coordinates. "m" and "l" expect two, "c"
|
2258
|
+
* six and "h" an empty array (or undefined).
|
2259
|
+
* @param {String} style The style
|
2260
|
+
* @param {String} patternKey The pattern key for the pattern that should be used to fill the path.
|
2261
|
+
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
|
2262
|
+
* will modify the pattern on use.
|
2263
|
+
* @function
|
2264
|
+
* @returns {jsPDF}
|
2265
|
+
* @methodOf jsPDF#
|
2266
|
+
* @name path
|
2267
|
+
*/
|
2268
|
+
API.path = function (lines, style, patternKey, patternData) {
|
2269
|
+
|
2270
|
+
for (var i = 0; i < lines.length; i++) {
|
2271
|
+
var leg = lines[i];
|
2272
|
+
var coords = leg.c;
|
2273
|
+
switch (leg.op) {
|
2274
|
+
case "m":
|
2275
|
+
// move
|
2276
|
+
out(f3(coords[0]) + ' ' + f3(coords[1]) + ' m');
|
2277
|
+
break;
|
2278
|
+
case "l":
|
2279
|
+
// simple line
|
2280
|
+
out(f3(coords[0]) + ' ' + f3(coords[1]) + ' l');
|
2281
|
+
break;
|
2282
|
+
case "c":
|
2283
|
+
// bezier curve
|
2284
|
+
out([
|
2285
|
+
f3(coords[0]),
|
2286
|
+
f3(coords[1]),
|
2287
|
+
f3(coords[2]),
|
2288
|
+
f3(coords[3]),
|
2289
|
+
f3(coords[4]),
|
2290
|
+
f3(coords[5]),
|
2291
|
+
"c"
|
2292
|
+
].join(" "));
|
2293
|
+
break;
|
2294
|
+
case "h":
|
2295
|
+
// close path
|
2296
|
+
out("h");
|
2297
|
+
}
|
2298
|
+
|
2299
|
+
|
2300
|
+
}
|
2301
|
+
|
2302
|
+
putStyle(style, patternKey, patternData);
|
2303
|
+
|
2304
|
+
return this;
|
2305
|
+
};
|
2306
|
+
|
2307
|
+
/**
|
2308
|
+
* Adds a rectangle to PDF
|
2309
|
+
*
|
2310
|
+
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
2311
|
+
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
2312
|
+
* @param {Number} w Width (in units declared at inception of PDF document)
|
2313
|
+
* @param {Number} h Height (in units declared at inception of PDF document)
|
2314
|
+
* @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
|
2315
|
+
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
|
2316
|
+
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
|
2317
|
+
* will modify the pattern on use.
|
2318
|
+
* @function
|
2319
|
+
* @returns {jsPDF}
|
2320
|
+
* @methodOf jsPDF#
|
2321
|
+
* @name rect
|
2322
|
+
*/
|
2323
|
+
API.rect = function(x, y, w, h, style, patternKey, patternData) {
|
2324
|
+
out([
|
2325
|
+
f2(x),
|
2326
|
+
f2(y),
|
2327
|
+
f2(w),
|
2328
|
+
f2(-h),
|
2329
|
+
're'
|
2330
|
+
].join(' '));
|
2331
|
+
|
2332
|
+
putStyle(style, patternKey, patternData);
|
2333
|
+
|
2334
|
+
return this;
|
2335
|
+
};
|
2336
|
+
|
2337
|
+
/**
|
2338
|
+
* Adds a triangle to PDF
|
2339
|
+
*
|
2340
|
+
* @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page
|
2341
|
+
* @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
2342
|
+
* @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page
|
2343
|
+
* @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
2344
|
+
* @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page
|
2345
|
+
* @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
2346
|
+
* @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
|
2347
|
+
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
|
2348
|
+
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
|
2349
|
+
* will modify the pattern on use.
|
2350
|
+
* @function
|
2351
|
+
* @returns {jsPDF}
|
2352
|
+
* @methodOf jsPDF#
|
2353
|
+
* @name triangle
|
2354
|
+
*/
|
2355
|
+
API.triangle = function(x1, y1, x2, y2, x3, y3, style, patternKey, patternData) {
|
2356
|
+
this.lines(
|
2357
|
+
[
|
2358
|
+
[x2 - x1, y2 - y1], // vector to point 2
|
2359
|
+
[x3 - x2, y3 - y2], // vector to point 3
|
2360
|
+
[x1 - x3, y1 - y3]// closing vector back to point 1
|
2361
|
+
],
|
2362
|
+
x1,
|
2363
|
+
y1, // start of path
|
2364
|
+
[1, 1],
|
2365
|
+
style,
|
2366
|
+
true,
|
2367
|
+
patternKey,
|
2368
|
+
patternData
|
2369
|
+
);
|
2370
|
+
return this;
|
2371
|
+
};
|
2372
|
+
|
2373
|
+
/**
|
2374
|
+
* Adds a rectangle with rounded corners to PDF
|
2375
|
+
*
|
2376
|
+
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
2377
|
+
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
2378
|
+
* @param {Number} w Width (in units declared at inception of PDF document)
|
2379
|
+
* @param {Number} h Height (in units declared at inception of PDF document)
|
2380
|
+
* @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
|
2381
|
+
* @param {Number} ry Radius along y axis (in units declared at inception of PDF document)
|
2382
|
+
* @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
|
2383
|
+
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
|
2384
|
+
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
|
2385
|
+
* will modify the pattern on use.
|
2386
|
+
* @function
|
2387
|
+
* @returns {jsPDF}
|
2388
|
+
* @methodOf jsPDF#
|
2389
|
+
* @name roundedRect
|
2390
|
+
*/
|
2391
|
+
API.roundedRect = function(x, y, w, h, rx, ry, style, patternKey, patternData) {
|
2392
|
+
var MyArc = 4 / 3 * (Math.SQRT2 - 1);
|
2393
|
+
|
2394
|
+
rx = Math.min(rx, w * 0.5);
|
2395
|
+
ry = Math.min(ry, h * 0.5);
|
2396
|
+
|
2397
|
+
this.lines(
|
2398
|
+
[
|
2399
|
+
[(w - 2 * rx), 0],
|
2400
|
+
[(rx * MyArc), 0, rx, ry - (ry * MyArc), rx, ry],
|
2401
|
+
[0, (h - 2 * ry)],
|
2402
|
+
[0, (ry * MyArc), - (rx * MyArc), ry, -rx, ry],
|
2403
|
+
[(-w + 2 * rx), 0],
|
2404
|
+
[ - (rx * MyArc), 0, -rx, - (ry * MyArc), -rx, -ry],
|
2405
|
+
[0, (-h + 2 * ry)],
|
2406
|
+
[0, - (ry * MyArc), (rx * MyArc), -ry, rx, -ry]
|
2407
|
+
],
|
2408
|
+
x + rx,
|
2409
|
+
y, // start of path
|
2410
|
+
[1, 1],
|
2411
|
+
style,
|
2412
|
+
true,
|
2413
|
+
patternKey,
|
2414
|
+
patternData
|
2415
|
+
);
|
2416
|
+
return this;
|
2417
|
+
};
|
2418
|
+
|
2419
|
+
/**
|
2420
|
+
* Adds an ellipse to PDF
|
2421
|
+
*
|
2422
|
+
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
2423
|
+
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
2424
|
+
* @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
|
2425
|
+
* @param {Number} ry Radius along y axis (in units declared at inception of PDF document)
|
2426
|
+
* @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
|
2427
|
+
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
|
2428
|
+
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
|
2429
|
+
* will modify the pattern on use.
|
2430
|
+
* @function
|
2431
|
+
* @returns {jsPDF}
|
2432
|
+
* @methodOf jsPDF#
|
2433
|
+
* @name ellipse
|
2434
|
+
*/
|
2435
|
+
API.ellipse = function(x, y, rx, ry, style, patternKey, patternData) {
|
2436
|
+
var lx = 4 / 3 * (Math.SQRT2 - 1) * rx,
|
2437
|
+
ly = 4 / 3 * (Math.SQRT2 - 1) * ry;
|
2438
|
+
|
2439
|
+
out([
|
2440
|
+
f2(x + rx),
|
2441
|
+
f2(y),
|
2442
|
+
'm',
|
2443
|
+
f2(x + rx),
|
2444
|
+
f2(y - ly),
|
2445
|
+
f2(x + lx),
|
2446
|
+
f2(y - ry),
|
2447
|
+
f2(x),
|
2448
|
+
f2(y - ry),
|
2449
|
+
'c'
|
2450
|
+
].join(' '));
|
2451
|
+
out([
|
2452
|
+
f2(x - lx),
|
2453
|
+
f2(y - ry),
|
2454
|
+
f2(x - rx),
|
2455
|
+
f2(y - ly),
|
2456
|
+
f2(x - rx),
|
2457
|
+
f2(y),
|
2458
|
+
'c'
|
2459
|
+
].join(' '));
|
2460
|
+
out([
|
2461
|
+
f2(x - rx),
|
2462
|
+
f2(y + ly),
|
2463
|
+
f2(x - lx),
|
2464
|
+
f2(y + ry),
|
2465
|
+
f2(x),
|
2466
|
+
f2(y + ry),
|
2467
|
+
'c'
|
2468
|
+
].join(' '));
|
2469
|
+
out([
|
2470
|
+
f2(x + lx),
|
2471
|
+
f2(y + ry),
|
2472
|
+
f2(x + rx),
|
2473
|
+
f2(y + ly),
|
2474
|
+
f2(x + rx),
|
2475
|
+
f2(y),
|
2476
|
+
'c'
|
2477
|
+
].join(' '));
|
2478
|
+
|
2479
|
+
putStyle(style, patternKey, patternData);
|
2480
|
+
|
2481
|
+
return this;
|
2482
|
+
};
|
2483
|
+
|
2484
|
+
/**
|
2485
|
+
* Adds an circle to PDF
|
2486
|
+
*
|
2487
|
+
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
2488
|
+
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
2489
|
+
* @param {Number} r Radius (in units declared at inception of PDF document)
|
2490
|
+
* @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
|
2491
|
+
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
|
2492
|
+
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
|
2493
|
+
* will modify the pattern on use.
|
2494
|
+
* @function
|
2495
|
+
* @returns {jsPDF}
|
2496
|
+
* @methodOf jsPDF#
|
2497
|
+
* @name circle
|
2498
|
+
*/
|
2499
|
+
API.circle = function(x, y, r, style, patternKey, patternData) {
|
2500
|
+
return this.ellipse(x, y, r, r, style, patternKey, patternData);
|
2501
|
+
};
|
2502
|
+
|
2503
|
+
/**
|
2504
|
+
* Adds a properties to the PDF document
|
2505
|
+
*
|
2506
|
+
* @param {Object} properties A property_name-to-property_value object structure.
|
2507
|
+
* @function
|
2508
|
+
* @returns {jsPDF}
|
2509
|
+
* @methodOf jsPDF#
|
2510
|
+
* @name setProperties
|
2511
|
+
*/
|
2512
|
+
API.setProperties = function(properties) {
|
2513
|
+
// copying only those properties we can render.
|
2514
|
+
for (var property in documentProperties) {
|
2515
|
+
if (documentProperties.hasOwnProperty(property) && properties[property]) {
|
2516
|
+
documentProperties[property] = properties[property];
|
2517
|
+
}
|
2518
|
+
}
|
2519
|
+
return this;
|
2520
|
+
};
|
2521
|
+
|
2522
|
+
/**
|
2523
|
+
* Sets font size for upcoming text elements.
|
2524
|
+
*
|
2525
|
+
* @param {Number} size Font size in points.
|
2526
|
+
* @function
|
2527
|
+
* @returns {jsPDF}
|
2528
|
+
* @methodOf jsPDF#
|
2529
|
+
* @name setFontSize
|
2530
|
+
*/
|
2531
|
+
API.setFontSize = function(size) {
|
2532
|
+
activeFontSize = size;
|
2533
|
+
out("/" + activeFontKey + " " + activeFontSize + " Tf");
|
2534
|
+
return this;
|
2535
|
+
};
|
2536
|
+
|
2537
|
+
API.getFontSize = function () {
|
2538
|
+
return activeFontSize;
|
2539
|
+
};
|
2540
|
+
|
2541
|
+
/**
|
2542
|
+
* Sets text font face, variant for upcoming text elements.
|
2543
|
+
* See output of jsPDF.getFontList() for possible font names, styles.
|
2544
|
+
*
|
2545
|
+
* @param {String} fontName Font name or family. Example: "times"
|
2546
|
+
* @param {String} fontStyle Font style or variant. Example: "italic"
|
2547
|
+
* @function
|
2548
|
+
* @returns {jsPDF}
|
2549
|
+
* @methodOf jsPDF#
|
2550
|
+
* @name setFont
|
2551
|
+
*/
|
2552
|
+
API.setFont = function(fontName, fontStyle) {
|
2553
|
+
activeFontKey = getFont(fontName, fontStyle);
|
2554
|
+
// if font is not found, the above line blows up and we never go further
|
2555
|
+
out("/" + activeFontKey + " " + activeFontSize + " Tf");
|
2556
|
+
return this;
|
2557
|
+
};
|
2558
|
+
|
2559
|
+
/**
|
2560
|
+
* Switches font style or variant for upcoming text elements,
|
2561
|
+
* while keeping the font face or family same.
|
2562
|
+
* See output of jsPDF.getFontList() for possible font names, styles.
|
2563
|
+
*
|
2564
|
+
* @param {String} style Font style or variant. Example: "italic"
|
2565
|
+
* @function
|
2566
|
+
* @returns {jsPDF}
|
2567
|
+
* @methodOf jsPDF#
|
2568
|
+
* @name setFontStyle
|
2569
|
+
*/
|
2570
|
+
API.setFontStyle = API.setFontType = function(style) {
|
2571
|
+
activeFontKey = getFont(undefined, style);
|
2572
|
+
// if font is not found, the above line blows up and we never go further
|
2573
|
+
return this;
|
2574
|
+
};
|
2575
|
+
|
2576
|
+
/**
|
2577
|
+
* Returns an object - a tree of fontName to fontStyle relationships available to
|
2578
|
+
* active PDF document.
|
2579
|
+
*
|
2580
|
+
* @public
|
2581
|
+
* @function
|
2582
|
+
* @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... }
|
2583
|
+
* @methodOf jsPDF#
|
2584
|
+
* @name getFontList
|
2585
|
+
*/
|
2586
|
+
API.getFontList = function() {
|
2587
|
+
// TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
|
2588
|
+
var list = {},fontName,fontStyle,tmp;
|
2589
|
+
|
2590
|
+
for (fontName in fontmap) {
|
2591
|
+
if (fontmap.hasOwnProperty(fontName)) {
|
2592
|
+
list[fontName] = tmp = [];
|
2593
|
+
for (fontStyle in fontmap[fontName]) {
|
2594
|
+
if (fontmap[fontName].hasOwnProperty(fontStyle)) {
|
2595
|
+
tmp.push(fontStyle);
|
2596
|
+
}
|
2597
|
+
}
|
2598
|
+
}
|
2599
|
+
}
|
2600
|
+
|
2601
|
+
return list;
|
2602
|
+
};
|
2603
|
+
|
2604
|
+
/**
|
2605
|
+
* Add a custom font.
|
2606
|
+
*
|
2607
|
+
* @param {String} postScriptName name of the Font. Example: "Menlo-Regular"
|
2608
|
+
* @param {String} fontName of font-family from @font-face definition. Example: "Menlo Regular"
|
2609
|
+
* @param {String} fontStyle style. Example: "normal"
|
2610
|
+
* @function
|
2611
|
+
* @returns the {fontKey} (same as the internal method)
|
2612
|
+
* @methodOf jsPDF#
|
2613
|
+
* @name addFont
|
2614
|
+
*/
|
2615
|
+
API.addFont = function(postScriptName, fontName, fontStyle) {
|
2616
|
+
addFont(postScriptName, fontName, fontStyle, 'StandardEncoding');
|
2617
|
+
};
|
2618
|
+
|
2619
|
+
/**
|
2620
|
+
* Sets line width for upcoming lines.
|
2621
|
+
*
|
2622
|
+
* @param {Number} width Line width (in units declared at inception of PDF document)
|
2623
|
+
* @function
|
2624
|
+
* @returns {jsPDF}
|
2625
|
+
* @methodOf jsPDF#
|
2626
|
+
* @name setLineWidth
|
2627
|
+
*/
|
2628
|
+
API.setLineWidth = function(width) {
|
2629
|
+
out(width.toFixed(2) + ' w');
|
2630
|
+
return this;
|
2631
|
+
};
|
2632
|
+
|
2633
|
+
/**
|
2634
|
+
* Sets the stroke color for upcoming elements.
|
2635
|
+
*
|
2636
|
+
* Depending on the number of arguments given, Gray, RGB, or CMYK
|
2637
|
+
* color space is implied.
|
2638
|
+
*
|
2639
|
+
* When only ch1 is given, "Gray" color space is implied and it
|
2640
|
+
* must be a value in the range from 0.00 (solid black) to to 1.00 (white)
|
2641
|
+
* if values are communicated as String types, or in range from 0 (black)
|
2642
|
+
* to 255 (white) if communicated as Number type.
|
2643
|
+
* The RGB-like 0-255 range is provided for backward compatibility.
|
2644
|
+
*
|
2645
|
+
* When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
|
2646
|
+
* value must be in the range from 0.00 (minimum intensity) to to 1.00
|
2647
|
+
* (max intensity) if values are communicated as String types, or
|
2648
|
+
* from 0 (min intensity) to to 255 (max intensity) if values are communicated
|
2649
|
+
* as Number types.
|
2650
|
+
* The RGB-like 0-255 range is provided for backward compatibility.
|
2651
|
+
*
|
2652
|
+
* When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
|
2653
|
+
* value must be a in the range from 0.00 (0% concentration) to to
|
2654
|
+
* 1.00 (100% concentration)
|
2655
|
+
*
|
2656
|
+
* Because JavaScript treats fixed point numbers badly (rounds to
|
2657
|
+
* floating point nearest to binary representation) it is highly advised to
|
2658
|
+
* communicate the fractional numbers as String types, not JavaScript Number type.
|
2659
|
+
*
|
2660
|
+
* @param {Number|String} ch1 Color channel value
|
2661
|
+
* @param {Number|String} ch2 Color channel value
|
2662
|
+
* @param {Number|String} ch3 Color channel value
|
2663
|
+
* @param {Number|String} ch4 Color channel value
|
2664
|
+
*
|
2665
|
+
* @function
|
2666
|
+
* @returns {jsPDF}
|
2667
|
+
* @methodOf jsPDF#
|
2668
|
+
* @name setDrawColor
|
2669
|
+
*/
|
2670
|
+
API.setDrawColor = function(ch1, ch2, ch3, ch4) {
|
2671
|
+
var color;
|
2672
|
+
if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
|
2673
|
+
// Gray color space.
|
2674
|
+
if (typeof ch1 === 'string') {
|
2675
|
+
color = ch1 + ' G';
|
2676
|
+
} else {
|
2677
|
+
color = f2(ch1 / 255) + ' G';
|
2678
|
+
}
|
2679
|
+
} else if (ch4 === undefined) {
|
2680
|
+
// RGB
|
2681
|
+
if (typeof ch1 === 'string') {
|
2682
|
+
color = [ch1, ch2, ch3, 'RG'].join(' ');
|
2683
|
+
} else {
|
2684
|
+
color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(' ');
|
2685
|
+
}
|
2686
|
+
} else {
|
2687
|
+
// CMYK
|
2688
|
+
if (typeof ch1 === 'string') {
|
2689
|
+
color = [ch1, ch2, ch3, ch4, 'K'].join(' ');
|
2690
|
+
} else {
|
2691
|
+
color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' ');
|
2692
|
+
}
|
2693
|
+
}
|
2694
|
+
|
2695
|
+
out(color);
|
2696
|
+
return this;
|
2697
|
+
};
|
2698
|
+
|
2699
|
+
/**
|
2700
|
+
* Sets the fill color for upcoming elements.
|
2701
|
+
*
|
2702
|
+
* Depending on the number of arguments given, Gray, RGB, or CMYK
|
2703
|
+
* color space is implied.
|
2704
|
+
*
|
2705
|
+
* When only ch1 is given, "Gray" color space is implied and it
|
2706
|
+
* must be a value in the range from 0.00 (solid black) to to 1.00 (white)
|
2707
|
+
* if values are communicated as String types, or in range from 0 (black)
|
2708
|
+
* to 255 (white) if communicated as Number type.
|
2709
|
+
* The RGB-like 0-255 range is provided for backward compatibility.
|
2710
|
+
*
|
2711
|
+
* When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
|
2712
|
+
* value must be in the range from 0.00 (minimum intensity) to to 1.00
|
2713
|
+
* (max intensity) if values are communicated as String types, or
|
2714
|
+
* from 0 (min intensity) to to 255 (max intensity) if values are communicated
|
2715
|
+
* as Number types.
|
2716
|
+
* The RGB-like 0-255 range is provided for backward compatibility.
|
2717
|
+
*
|
2718
|
+
* When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
|
2719
|
+
* value must be a in the range from 0.00 (0% concentration) to to
|
2720
|
+
* 1.00 (100% concentration)
|
2721
|
+
*
|
2722
|
+
* Because JavaScript treats fixed point numbers badly (rounds to
|
2723
|
+
* floating point nearest to binary representation) it is highly advised to
|
2724
|
+
* communicate the fractional numbers as String types, not JavaScript Number type.
|
2725
|
+
*
|
2726
|
+
* @param {Number|String} ch1 Color channel value
|
2727
|
+
* @param {Number|String} ch2 Color channel value
|
2728
|
+
* @param {Number|String} ch3 Color channel value
|
2729
|
+
* @param {Number|String} ch4 Color channel value
|
2730
|
+
*
|
2731
|
+
* @function
|
2732
|
+
* @returns {jsPDF}
|
2733
|
+
* @methodOf jsPDF#
|
2734
|
+
* @name setFillColor
|
2735
|
+
*/
|
2736
|
+
API.setFillColor = function(ch1, ch2, ch3, ch4) {
|
2737
|
+
var color;
|
2738
|
+
|
2739
|
+
if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
|
2740
|
+
// Gray color space.
|
2741
|
+
if (typeof ch1 === 'string') {
|
2742
|
+
color = ch1 + ' g';
|
2743
|
+
} else {
|
2744
|
+
color = f2(ch1 / 255) + ' g';
|
2745
|
+
}
|
2746
|
+
} else if (ch4 === undefined || typeof ch4 === 'object') {
|
2747
|
+
// RGB
|
2748
|
+
if (typeof ch1 === 'string') {
|
2749
|
+
color = [ch1, ch2, ch3, 'rg'].join(' ');
|
2750
|
+
} else {
|
2751
|
+
color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' ');
|
2752
|
+
}
|
2753
|
+
if (ch4 && ch4.a === 0){
|
2754
|
+
//TODO Implement transparency.
|
2755
|
+
//WORKAROUND use white for now
|
2756
|
+
color = ['255', '255', '255', 'rg'].join(' ');
|
2757
|
+
}
|
2758
|
+
} else {
|
2759
|
+
// CMYK
|
2760
|
+
if (typeof ch1 === 'string') {
|
2761
|
+
color = [ch1, ch2, ch3, ch4, 'k'].join(' ');
|
2762
|
+
} else {
|
2763
|
+
color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' ');
|
2764
|
+
}
|
2765
|
+
}
|
2766
|
+
|
2767
|
+
out(color);
|
2768
|
+
return this;
|
2769
|
+
};
|
2770
|
+
|
2771
|
+
/**
|
2772
|
+
* Sets the text color for upcoming elements.
|
2773
|
+
* If only one, first argument is given,
|
2774
|
+
* treats the value as gray-scale color value.
|
2775
|
+
*
|
2776
|
+
* @param {Number} r Red channel color value in range 0-255 or {String} r color value in hexadecimal, example: '#FFFFFF'
|
2777
|
+
* @param {Number} g Green channel color value in range 0-255
|
2778
|
+
* @param {Number} b Blue channel color value in range 0-255
|
2779
|
+
* @function
|
2780
|
+
* @returns {jsPDF}
|
2781
|
+
* @methodOf jsPDF#
|
2782
|
+
* @name setTextColor
|
2783
|
+
*/
|
2784
|
+
API.setTextColor = function(r, g, b) {
|
2785
|
+
if ((typeof r === 'string') && /^#[0-9A-Fa-f]{6}$/.test(r)) {
|
2786
|
+
var hex = parseInt(r.substr(1), 16);
|
2787
|
+
r = (hex >> 16) & 255;
|
2788
|
+
g = (hex >> 8) & 255;
|
2789
|
+
b = (hex & 255);
|
2790
|
+
}
|
2791
|
+
|
2792
|
+
if ((r === 0 && g === 0 && b === 0) || (typeof g === 'undefined')) {
|
2793
|
+
textColor = f3(r / 255) + ' g';
|
2794
|
+
} else {
|
2795
|
+
textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
|
2796
|
+
}
|
2797
|
+
|
2798
|
+
out(textColor);
|
2799
|
+
|
2800
|
+
return this;
|
2801
|
+
};
|
2802
|
+
|
2803
|
+
/**
|
2804
|
+
* Sets a either previously added {@link GState} (via {@link addGState}) or a new {@link GState}.
|
2805
|
+
* @param {String|GState} gState If type is string, a previously added GState is used, if type is GState
|
2806
|
+
* it will be added before use.
|
2807
|
+
* @function
|
2808
|
+
* @returns {jsPDF}
|
2809
|
+
* @methodOf jsPDF#
|
2810
|
+
* @name setGState
|
2811
|
+
*/
|
2812
|
+
API.setGState = function (gState) {
|
2813
|
+
if (typeof gState === "string") {
|
2814
|
+
gState = gStates[gStatesMap[gState]];
|
2815
|
+
} else {
|
2816
|
+
gState = addGState(null, gState);
|
2817
|
+
}
|
2818
|
+
|
2819
|
+
if (!gState.equals(activeGState)) {
|
2820
|
+
out("/" + gState.id + " gs");
|
2821
|
+
activeGState = gState;
|
2822
|
+
}
|
2823
|
+
};
|
2824
|
+
|
2825
|
+
/**
|
2826
|
+
* Is an Object providing a mapping from human-readable to
|
2827
|
+
* integer flag values designating the varieties of line cap
|
2828
|
+
* and join styles.
|
2829
|
+
*
|
2830
|
+
* @returns {Object}
|
2831
|
+
* @fieldOf jsPDF#
|
2832
|
+
* @name CapJoinStyles
|
2833
|
+
*/
|
2834
|
+
API.CapJoinStyles = {
|
2835
|
+
0 : 0,
|
2836
|
+
'butt' : 0,
|
2837
|
+
'but' : 0,
|
2838
|
+
'miter' : 0,
|
2839
|
+
1 : 1,
|
2840
|
+
'round' : 1,
|
2841
|
+
'rounded' : 1,
|
2842
|
+
'circle' : 1,
|
2843
|
+
2 : 2,
|
2844
|
+
'projecting' : 2,
|
2845
|
+
'project' : 2,
|
2846
|
+
'square' : 2,
|
2847
|
+
'bevel' : 2
|
2848
|
+
};
|
2849
|
+
|
2850
|
+
/**
|
2851
|
+
* Sets the line cap styles
|
2852
|
+
* See {jsPDF.CapJoinStyles} for variants
|
2853
|
+
*
|
2854
|
+
* @param {String|Number} style A string or number identifying the type of line cap
|
2855
|
+
* @function
|
2856
|
+
* @returns {jsPDF}
|
2857
|
+
* @methodOf jsPDF#
|
2858
|
+
* @name setLineCap
|
2859
|
+
*/
|
2860
|
+
API.setLineCap = function(style) {
|
2861
|
+
var id = this.CapJoinStyles[style];
|
2862
|
+
if (id === undefined) {
|
2863
|
+
throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
|
2864
|
+
}
|
2865
|
+
lineCapID = id;
|
2866
|
+
out(id + ' J');
|
2867
|
+
|
2868
|
+
return this;
|
2869
|
+
};
|
2870
|
+
|
2871
|
+
/**
|
2872
|
+
* Sets the line join styles
|
2873
|
+
* See {jsPDF.CapJoinStyles} for variants
|
2874
|
+
*
|
2875
|
+
* @param {String|Number} style A string or number identifying the type of line join
|
2876
|
+
* @function
|
2877
|
+
* @returns {jsPDF}
|
2878
|
+
* @methodOf jsPDF#
|
2879
|
+
* @name setLineJoin
|
2880
|
+
*/
|
2881
|
+
API.setLineJoin = function(style) {
|
2882
|
+
var id = this.CapJoinStyles[style];
|
2883
|
+
if (id === undefined) {
|
2884
|
+
throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
|
2885
|
+
}
|
2886
|
+
lineJoinID = id;
|
2887
|
+
out(id + ' j');
|
2888
|
+
|
2889
|
+
return this;
|
2890
|
+
};
|
2891
|
+
|
2892
|
+
/**
|
2893
|
+
* Sets the miter limit.
|
2894
|
+
* @param {number} miterLimit
|
2895
|
+
* @function
|
2896
|
+
* @returns {jsPDF}
|
2897
|
+
* @methodOf jsPDF#
|
2898
|
+
* @name setMiterLimit
|
2899
|
+
*/
|
2900
|
+
API.setLineMiterLimit = function (miterLimit) {
|
2901
|
+
out(f2(miterLimit) + " M");
|
2902
|
+
|
2903
|
+
return this;
|
2904
|
+
};
|
2905
|
+
|
2906
|
+
/**
|
2907
|
+
* Sets the line dash pattern.
|
2908
|
+
* @param {Array<number>} array An array containing 0-2 numbers. The first number sets the length of the
|
2909
|
+
* dashes, the second number the length of the gaps. If the second number is missing, the gaps are considered
|
2910
|
+
* to be as long as the dashes. An empty array means solid, unbroken lines.
|
2911
|
+
* @param phase The phase lines start with.
|
2912
|
+
* @function
|
2913
|
+
* @returns {jsPDF}
|
2914
|
+
* @methodOf jsPDF#
|
2915
|
+
* @name setLineDashPattern
|
2916
|
+
*/
|
2917
|
+
API.setLineDashPattern = function (array, phase) {
|
2918
|
+
out([
|
2919
|
+
"[" + (array[0] !== undefined ? array[0] : ""),
|
2920
|
+
(array[1] !== undefined ? array[1] : "" ) + "]",
|
2921
|
+
phase,
|
2922
|
+
"d"
|
2923
|
+
].join(" "));
|
2924
|
+
|
2925
|
+
return this;
|
2926
|
+
};
|
2927
|
+
|
2928
|
+
// Output is both an internal (for plugins) and external function
|
2929
|
+
API.output = output;
|
2930
|
+
|
2931
|
+
/**
|
2932
|
+
* Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf')
|
2933
|
+
* @param {String} filename The filename including extension.
|
2934
|
+
*
|
2935
|
+
* @function
|
2936
|
+
* @returns {jsPDF}
|
2937
|
+
* @methodOf jsPDF#
|
2938
|
+
* @name save
|
2939
|
+
*/
|
2940
|
+
API.save = function(filename) {
|
2941
|
+
API.output('save', filename);
|
2942
|
+
};
|
2943
|
+
|
2944
|
+
// applying plugins (more methods) ON TOP of built-in API.
|
2945
|
+
// this is intentional as we allow plugins to override
|
2946
|
+
// built-ins
|
2947
|
+
for (var plugin in jsPDF.API) {
|
2948
|
+
if (jsPDF.API.hasOwnProperty(plugin)) {
|
2949
|
+
if (plugin === 'events' && jsPDF.API.events.length) {
|
2950
|
+
(function(events, newEvents) {
|
2951
|
+
|
2952
|
+
// jsPDF.API.events is a JS Array of Arrays
|
2953
|
+
// where each Array is a pair of event name, handler
|
2954
|
+
// Events were added by plugins to the jsPDF instantiator.
|
2955
|
+
// These are always added to the new instance and some ran
|
2956
|
+
// during instantiation.
|
2957
|
+
var eventname,handler_and_args,i;
|
2958
|
+
|
2959
|
+
for (i = newEvents.length - 1; i !== -1; i--) {
|
2960
|
+
// subscribe takes 3 args: 'topic', function, runonce_flag
|
2961
|
+
// if undefined, runonce is false.
|
2962
|
+
// users can attach callback directly,
|
2963
|
+
// or they can attach an array with [callback, runonce_flag]
|
2964
|
+
// that's what the "apply" magic is for below.
|
2965
|
+
eventname = newEvents[i][0];
|
2966
|
+
handler_and_args = newEvents[i][1];
|
2967
|
+
events.subscribe.apply(
|
2968
|
+
events,
|
2969
|
+
[eventname].concat(
|
2970
|
+
typeof handler_and_args === 'function' ?
|
2971
|
+
[handler_and_args] : handler_and_args));
|
2972
|
+
}
|
2973
|
+
}(events, jsPDF.API.events));
|
2974
|
+
} else {
|
2975
|
+
API[plugin] = jsPDF.API[plugin];
|
2976
|
+
}
|
2977
|
+
}
|
2978
|
+
}
|
2979
|
+
|
2980
|
+
//////////////////////////////////////////////////////
|
2981
|
+
// continuing initialization of jsPDF Document object
|
2982
|
+
//////////////////////////////////////////////////////
|
2983
|
+
// Add the first page automatically
|
2984
|
+
addFonts();
|
2985
|
+
activeFontKey = 'F1';
|
2986
|
+
_addPage(format, orientation);
|
2987
|
+
|
2988
|
+
events.publish('initialized');
|
2989
|
+
return API;
|
2990
|
+
}
|
2991
|
+
|
2992
|
+
/**
|
2993
|
+
* jsPDF.API is a STATIC property of jsPDF class.
|
2994
|
+
* jsPDF.API is an object you can add methods and properties to.
|
2995
|
+
* The methods / properties you add will show up in new jsPDF objects.
|
2996
|
+
*
|
2997
|
+
* One property is prepopulated. It is the 'events' Object. Plugin authors can add topics,
|
2998
|
+
* callbacks to this object. These will be reassigned to all new instances of jsPDF.
|
2999
|
+
* Examples:
|
3000
|
+
* jsPDF.API.events['initialized'] = function(){ 'this' is API object }
|
3001
|
+
* jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object }
|
3002
|
+
*
|
3003
|
+
* @static
|
3004
|
+
* @public
|
3005
|
+
* @memberOf jsPDF
|
3006
|
+
* @name API
|
3007
|
+
*
|
3008
|
+
* @example
|
3009
|
+
* jsPDF.API.mymethod = function(){
|
3010
|
+
* // 'this' will be ref to internal API object. see jsPDF source
|
3011
|
+
* // , so you can refer to built-in methods like so:
|
3012
|
+
* // this.line(....)
|
3013
|
+
* // this.text(....)
|
3014
|
+
* }
|
3015
|
+
* var pdfdoc = new jsPDF()
|
3016
|
+
* pdfdoc.mymethod() // <- !!!!!!
|
3017
|
+
*/
|
3018
|
+
jsPDF.API = {events:[]};
|
3019
|
+
jsPDF.version = "1.0.0-trunk";
|
3020
|
+
|
3021
|
+
if (typeof define === 'function' && define.amd) {
|
3022
|
+
define('jsPDF', function() {
|
3023
|
+
return jsPDF;
|
3024
|
+
});
|
3025
|
+
} else if (typeof module !== 'undefined' && module.exports) {
|
3026
|
+
module.exports = jsPDF;
|
3027
|
+
} else {
|
3028
|
+
global.jsPDF = jsPDF;
|
3029
|
+
}
|
3030
|
+
return jsPDF;
|
3031
|
+
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this));
|