highcharts_rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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));
|