/base/000_base/node_modules/highcharts/lib/jspdf.src.js |
@@ -0,0 +1,3031 @@ |
/** @preserve |
* jsPDF - PDF Document creation from JavaScript |
* Version ${versionID} |
* CommitID ${commitID} |
* |
* Copyright (c) 2010-2014 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF |
* 2010 Aaron Spike, https://github.com/acspike |
* 2012 Willow Systems Corporation, willow-systems.com |
* 2012 Pablo Hess, https://github.com/pablohess |
* 2012 Florian Jenett, https://github.com/fjenett |
* 2013 Warren Weckesser, https://github.com/warrenweckesser |
* 2013 Youssef Beddad, https://github.com/lifof |
* 2013 Lee Driscoll, https://github.com/lsdriscoll |
* 2013 Stefan Slonevskiy, https://github.com/stefslon |
* 2013 Jeremy Morel, https://github.com/jmorel |
* 2013 Christoph Hartmann, https://github.com/chris-rock |
* 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria |
* 2014 James Makes, https://github.com/dollaruw |
* 2014 Diego Casorran, https://github.com/diegocr |
* 2014 Steven Spungin, https://github.com/Flamenco |
* 2014 Kenneth Glassey, https://github.com/Gavvers |
* |
* Permission is hereby granted, free of charge, to any person obtaining |
* a copy of this software and associated documentation files (the |
* "Software"), to deal in the Software without restriction, including |
* without limitation the rights to use, copy, modify, merge, publish, |
* distribute, sublicense, and/or sell copies of the Software, and to |
* permit persons to whom the Software is furnished to do so, subject to |
* the following conditions: |
* |
* The above copyright notice and this permission notice shall be |
* included in all copies or substantial portions of the Software. |
* |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
* |
* Contributor(s): |
* siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango, |
* kim3er, mfo, alnorth, Flamenco |
*/ |
|
/** |
* Creates new jsPDF document object instance. |
* |
* @class |
* @param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l") |
* @param unit Measurement unit to be used when coordinates are specified. |
* One of "pt" (points), "mm" (Default), "cm", "in" |
* @param format One of 'pageFormats' as shown below, default: a4 |
* @returns {jsPDF} |
* @name jsPDF |
*/ |
var jsPDF = (function(global) { |
'use strict'; |
var pdfVersion = '1.3', |
pageFormats = { // Size in pt of various paper formats |
'a0' : [2383.94, 3370.39], 'a1' : [1683.78, 2383.94], |
'a2' : [1190.55, 1683.78], 'a3' : [ 841.89, 1190.55], |
'a4' : [ 595.28, 841.89], 'a5' : [ 419.53, 595.28], |
'a6' : [ 297.64, 419.53], 'a7' : [ 209.76, 297.64], |
'a8' : [ 147.40, 209.76], 'a9' : [ 104.88, 147.40], |
'a10' : [ 73.70, 104.88], 'b0' : [2834.65, 4008.19], |
'b1' : [2004.09, 2834.65], 'b2' : [1417.32, 2004.09], |
'b3' : [1000.63, 1417.32], 'b4' : [ 708.66, 1000.63], |
'b5' : [ 498.90, 708.66], 'b6' : [ 354.33, 498.90], |
'b7' : [ 249.45, 354.33], 'b8' : [ 175.75, 249.45], |
'b9' : [ 124.72, 175.75], 'b10' : [ 87.87, 124.72], |
'c0' : [2599.37, 3676.54], 'c1' : [1836.85, 2599.37], |
'c2' : [1298.27, 1836.85], 'c3' : [ 918.43, 1298.27], |
'c4' : [ 649.13, 918.43], 'c5' : [ 459.21, 649.13], |
'c6' : [ 323.15, 459.21], 'c7' : [ 229.61, 323.15], |
'c8' : [ 161.57, 229.61], 'c9' : [ 113.39, 161.57], |
'c10' : [ 79.37, 113.39], 'dl' : [ 311.81, 623.62], |
'letter' : [612, 792], |
'government-letter' : [576, 756], |
'legal' : [612, 1008], |
'junior-legal' : [576, 360], |
'ledger' : [1224, 792], |
'tabloid' : [792, 1224], |
'credit-card' : [153, 243] |
}; |
|
/** |
* jsPDF's Internal PubSub Implementation. |
* See mrrio.github.io/jsPDF/doc/symbols/PubSub.html |
* Backward compatible rewritten on 2014 by |
* Diego Casorran, https://github.com/diegocr |
* |
* @class |
* @name PubSub |
*/ |
function PubSub(context) { |
var topics = {}; |
|
this.subscribe = function(topic, callback, once) { |
if(typeof callback !== 'function') { |
return false; |
} |
|
if(!topics.hasOwnProperty(topic)) { |
topics[topic] = {}; |
} |
|
var id = Math.random().toString(35); |
topics[topic][id] = [callback,!!once]; |
|
return id; |
}; |
|
this.unsubscribe = function(token) { |
for(var topic in topics) { |
if(topics[topic][token]) { |
delete topics[topic][token]; |
return true; |
} |
} |
return false; |
}; |
|
this.publish = function(topic) { |
if(topics.hasOwnProperty(topic)) { |
var args = Array.prototype.slice.call(arguments, 1), idr = []; |
|
for(var id in topics[topic]) { |
var sub = topics[topic][id]; |
try { |
sub[0].apply(context, args); |
} catch(ex) { |
if(global.console) { |
console.error('jsPDF PubSub Error', ex.message, ex); |
} |
} |
if(sub[1]) idr.push(id); |
} |
if(idr.length) idr.forEach(this.unsubscribe); |
} |
}; |
} |
|
/** |
* @constructor |
* @private |
*/ |
function jsPDF(orientation, unit, format, compressPdf) { |
var options = {}; |
|
if (typeof orientation === 'object') { |
options = orientation; |
|
orientation = options.orientation; |
unit = options.unit || unit; |
format = options.format || format; |
compressPdf = options.compress || options.compressPdf || compressPdf; |
} |
|
// Default options |
unit = unit || 'mm'; |
format = format || 'a4'; |
orientation = ('' + (orientation || 'P')).toLowerCase(); |
|
var format_as_string = ('' + format).toLowerCase(), |
compress = !!compressPdf && typeof Uint8Array === 'function', |
textColor = options.textColor || '0 g', |
drawColor = options.drawColor || '0 G', |
activeFontSize = options.fontSize || 16, |
lineHeightProportion = options.lineHeight || 1.15, |
lineWidth = options.lineWidth || 0.200025, // 2mm |
objectNumber = 2, // 'n' Current object number |
outToPages = !1, // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content |
offsets = [], // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes. |
fonts = {}, // collection of font objects, where key is fontKey - a dynamically created label for a given font. |
fontmap = {}, // mapping structure fontName > fontStyle > font key - performance layer. See addFont() |
activeFontKey, // will be string representing the KEY of the font as combination of fontName + fontStyle |
|
fontStateStack = [], // |
|
patterns = {}, // collection of pattern objects |
patternMap = {}, // see fonts |
|
gStates = {}, // collection of graphic state objects |
gStatesMap = {}, // see fonts |
activeGState = null, |
|
k, // Scale factor |
tmp, |
page = 0, |
currentPage, |
pages = [], |
pagesContext = [], // same index as pages and pagedim |
pagedim = [], |
content = [], |
additionalObjects = [], |
lineCapID = 0, |
lineJoinID = 0, |
content_length = 0, |
|
renderTargets = {}, |
renderTargetMap = {}, |
renderTargetStack = [], |
|
pageX, pageY, pageMatrix, // only used for FormObjects |
pageWidth, |
pageHeight, |
pageMode, |
zoomMode, |
layoutMode, |
documentProperties = { |
'title' : '', |
'subject' : '', |
'author' : '', |
'keywords' : '', |
'creator' : '' |
}, |
API = {}, |
events = new PubSub(API), |
|
///////////////////// |
// Private functions |
///////////////////// |
f2 = function(number) { |
return number.toFixed(2); // Ie, %.2f |
}, |
f3 = function(number) { |
return number.toFixed(3); // Ie, %.3f |
}, |
padd2 = function(number) { |
return ('0' + parseInt(number)).slice(-2); |
}, |
padd2Hex = function (hexString) { |
var s = "00" + hexString; |
return s.substr(s.length - 2); |
}, |
out = function(string) { |
if (outToPages) { |
/* set by beginPage */ |
pages[currentPage].push(string); |
} else { |
// +1 for '\n' that will be used to join 'content' |
content_length += string.length + 1; |
content.push(string); |
} |
}, |
newObject = function() { |
// Begin a new object |
objectNumber++; |
offsets[objectNumber] = content_length; |
out(objectNumber + ' 0 obj'); |
return objectNumber; |
}, |
// Does not output the object until after the pages have been output. |
// Returns an object containing the objectId and content. |
// All pages have been added so the object ID can be estimated to start right after. |
// This does not modify the current objectNumber; It must be updated after the newObjects are output. |
newAdditionalObject = function() { |
var objId = pages.length * 2 + 1; |
objId += additionalObjects.length; |
var obj = {objId:objId, content:''}; |
additionalObjects.push(obj); |
return obj; |
}, |
// Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data |
newObjectDeferred = function() { |
objectNumber++; |
offsets[objectNumber] = function(){ |
return content_length; |
}; |
return objectNumber; |
}, |
newObjectDeferredBegin = function(oid) { |
offsets[oid] = content_length; |
}, |
putStream = function(str) { |
out('stream'); |
out(str); |
out('endstream'); |
}, |
putPages = function() { |
var n,p,arr,i,deflater,adler32,adler32cs,wPt,hPt; |
|
adler32cs = global.adler32cs || jsPDF.adler32cs; |
if (compress && typeof adler32cs === 'undefined') { |
compress = false; |
} |
|
// outToPages = false as set in endDocument(). out() writes to content. |
|
for (n = 1; n <= page; n++) { |
newObject(); |
wPt = (pageWidth = pagedim[n].width) * k; |
hPt = (pageHeight = pagedim[n].height) * k; |
out('<</Type /Page'); |
out('/Parent 1 0 R'); |
out('/Resources 2 0 R'); |
out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']'); |
// Added for annotation plugin |
events.publish('putPage', {pageNumber: n, page: pages[n]}); |
out('/Contents ' + (objectNumber + 1) + ' 0 R'); |
out('>>'); |
out('endobj'); |
|
// Page content |
p = pages[n].join('\n'); |
|
// prepend global change of basis matrix |
// (Now, instead of converting every coordinate to the pdf coordinate system, we apply a matrix |
// that does this job for us (however, texts, images and similar objects must be drawn bottom up)) |
p = new Matrix(k, 0, 0, -k, 0, pageHeight).toString() + " cm\n" + p; |
|
newObject(); |
if (compress) { |
arr = []; |
i = p.length; |
while(i--) { |
arr[i] = p.charCodeAt(i); |
} |
adler32 = adler32cs.from(p); |
deflater = new Deflater(6); |
deflater.append(new Uint8Array(arr)); |
p = deflater.flush(); |
arr = new Uint8Array(p.length + 6); |
arr.set(new Uint8Array([120, 156])); |
arr.set(p, 2); |
arr.set(new Uint8Array([adler32 & 0xFF, (adler32 >> 8) & 0xFF, (adler32 >> 16) & 0xFF, (adler32 >> 24) & 0xFF]), p.length+2); |
p = String.fromCharCode.apply(null, arr); |
out('<</Length ' + p.length + ' /Filter [/FlateDecode]>>'); |
} else { |
out('<</Length ' + p.length + '>>'); |
} |
putStream(p); |
out('endobj'); |
} |
offsets[1] = content_length; |
out('1 0 obj'); |
out('<</Type /Pages'); |
var kids = '/Kids ['; |
for (i = 0; i < page; i++) { |
kids += (3 + 2 * i) + ' 0 R '; |
} |
out(kids + ']'); |
out('/Count ' + page); |
out('>>'); |
out('endobj'); |
events.publish('postPutPages'); |
}, |
putFont = function(font) { |
font.objectNumber = newObject(); |
out('<</BaseFont/' + font.PostScriptName + '/Type/Font'); |
if (typeof font.encoding === 'string') { |
out('/Encoding/' + font.encoding); |
} |
out('/Subtype/Type1>>'); |
out('endobj'); |
}, |
putFonts = function() { |
for (var fontKey in fonts) { |
if (fonts.hasOwnProperty(fontKey)) { |
putFont(fonts[fontKey]); |
} |
} |
}, |
putXObject = function (xObject) { |
xObject.objectNumber = newObject(); |
out("<<"); |
out("/Type /XObject"); |
out("/Subtype /Form"); |
out("/BBox [" + [ |
f2(xObject.x), |
f2(xObject.y), |
f2(xObject.x + xObject.width), |
f2(xObject.y + xObject.height) |
].join(" ") + "]"); |
out("/Matrix [" + xObject.matrix.toString() + "]"); |
// TODO: /Resources |
|
var p = xObject.pages[1].join("\n"); |
out("/Length " + p.length); |
|
out(">>"); |
putStream(p); |
out("endobj"); |
}, |
putXObjects = function () { |
for (var xObjectKey in renderTargets) { |
if (renderTargets.hasOwnProperty(xObjectKey)) { |
putXObject(renderTargets[xObjectKey]); |
} |
} |
}, |
|
interpolateAndEncodeRGBStream = function (colors, numberSamples) { |
var tValues = []; |
var t; |
var dT = 1.0 / (numberSamples - 1); |
for (t = 0.0; t < 1.0; t += dT) { |
tValues.push(t); |
} |
tValues.push(1.0); |
|
// add first and last control point if not present |
if (colors[0].offset != 0.0) { |
var c0 = { |
offset: 0.0, |
color: colors[0].color |
}; |
colors.unshift(c0) |
} |
if (colors[colors.length - 1].offset != 1.0) { |
var c1 = { |
offset: 1.0, |
color: colors[colors.length - 1].color |
}; |
colors.push(c1); |
} |
|
var out = ""; |
var index = 0; |
|
for (var i = 0; i < tValues.length; i++) { |
t = tValues[i]; |
|
while (t > colors[index + 1].offset) |
index++; |
|
var a = colors[index].offset; |
var b = colors[index + 1].offset; |
var d = (t - a) / (b - a); |
|
var aColor = colors[index].color; |
var bColor = colors[index + 1].color; |
|
out += padd2Hex((Math.round((1 - d) * aColor[0] + d * bColor[0])).toString(16)) |
+ padd2Hex((Math.round((1 - d) * aColor[1] + d * bColor[1])).toString(16)) |
+ padd2Hex((Math.round((1 - d) * aColor[2] + d * bColor[2])).toString(16)); |
} |
return out.trim(); |
}, |
putShadingPattern = function (pattern, numberSamples) { |
/* |
Axial patterns shade between the two points specified in coords, radial patterns between the inner |
and outer circle. |
|
The user can specify an array (colors) that maps t-Values in [0, 1] to RGB colors. These are now |
interpolated to equidistant samples and written to pdf as a sample (type 0) function. |
*/ |
|
// The number of color samples that should be used to describe the shading. |
// The higher, the more accurate the gradient will be. |
numberSamples || (numberSamples = 21); |
|
var funcObjectNumber = newObject(); |
var stream = interpolateAndEncodeRGBStream(pattern.colors, numberSamples); |
out("<< /FunctionType 0"); |
out("/Domain [0.0 1.0]"); |
out("/Size [" + numberSamples + "]"); |
out("/BitsPerSample 8"); |
out("/Range [0.0 1.0 0.0 1.0 0.0 1.0]"); |
out("/Decode [0.0 1.0 0.0 1.0 0.0 1.0]"); |
out("/Length " + stream.length); |
// The stream is Hex encoded |
out("/Filter /ASCIIHexDecode"); |
out(">>"); |
putStream(stream); |
out("endobj"); |
|
pattern.objectNumber = newObject(); |
out("<< /ShadingType " + pattern.type); |
out("/ColorSpace /DeviceRGB"); |
|
var coords = "/Coords [" |
+ f3(parseFloat(pattern.coords[0])) + " "// x1 |
+ f3(parseFloat(pattern.coords[1])) + " "; // y1 |
if (pattern.type === 2) { |
// axial |
coords += f3(parseFloat(pattern.coords[2])) + " " // x2 |
+ f3(parseFloat(pattern.coords[3])); // y2 |
} else { |
// radial |
coords += f3(parseFloat(pattern.coords[2])) + " "// r1 |
+ f3(parseFloat(pattern.coords[3])) + " " // x2 |
+ f3(parseFloat(pattern.coords[4])) + " " // y2 |
+ f3(parseFloat(pattern.coords[5])); // r2 |
} |
coords += "]"; |
out(coords); |
|
if (pattern.matrix) { |
out("/Matrix [" + pattern.matrix.toString() + "]"); |
} |
|
out("/Function " + funcObjectNumber + " 0 R"); |
out("/Extend [true true]"); |
out(">>"); |
out("endobj"); |
}, |
putTilingPattern = function (pattern) { |
var resourcesObjectNumber = newObject(); |
out("<<"); |
putResourceDictionary(); |
out(">>"); |
out("endobj"); |
|
pattern.objectNumber = newObject(); |
out("<< /Type /Pattern"); |
out("/PatternType 1"); // tiling pattern |
out("/PaintType 1"); // colored tiling pattern |
out("/TilingType 1"); // constant spacing |
out("/BBox [" + pattern.boundingBox.map(f3).join(" ") + "]"); |
out("/XStep " + f3(pattern.xStep)); |
out("/YStep " + f3(pattern.yStep)); |
out("/Length " + pattern.stream.length); |
out("/Resources " + resourcesObjectNumber + " 0 R"); // TODO: resources |
pattern.matrix && out("/Matrix [" + pattern.matrix.toString() + "]"); |
|
out(">>"); |
|
putStream(pattern.stream); |
|
out("endobj"); |
}, |
putPatterns = function () { |
var patternKey; |
for (patternKey in patterns) { |
if (patterns.hasOwnProperty(patternKey)) { |
if (patterns[patternKey] instanceof API.ShadingPattern) { |
putShadingPattern(patterns[patternKey]); |
} else if (patterns[patternKey] instanceof API.TilingPattern) { |
putTilingPattern(patterns[patternKey]); |
} |
} |
} |
}, |
|
putGState = function (gState) { |
gState.objectNumber = newObject(); |
out("<<"); |
for (var p in gState) { |
switch (p) { |
case "opacity": |
out("/ca " + f2(gState[p])); |
break; |
} |
} |
out(">>"); |
out("endobj"); |
}, |
putGStates = function () { |
var gStateKey; |
for (gStateKey in gStates) { |
if (gStates.hasOwnProperty(gStateKey)) { |
putGState(gStates[gStateKey]); |
} |
} |
}, |
|
putXobjectDict = function () { |
for (var xObjectKey in renderTargets) { |
if (renderTargets.hasOwnProperty(xObjectKey) && renderTargets[xObjectKey].objectNumber >= 0) { |
out("/" + xObjectKey + " " + renderTargets[xObjectKey].objectNumber + " 0 R"); |
} |
} |
|
events.publish('putXobjectDict'); |
}, |
|
putShadingPatternDict = function () { |
for (var patternKey in patterns) { |
if (patterns.hasOwnProperty(patternKey) && patterns[patternKey] instanceof API.ShadingPattern && patterns[patternKey].objectNumber >= 0) { |
out("/" + patternKey + " " + patterns[patternKey].objectNumber + " 0 R"); |
} |
} |
|
events.publish("putShadingPatternDict"); |
}, |
|
putTilingPatternDict = function () { |
for (var patternKey in patterns) { |
if (patterns.hasOwnProperty(patternKey) && patterns[patternKey] instanceof API.TilingPattern && patterns[patternKey].objectNumber >= 0) { |
out("/" + patternKey + " " + patterns[patternKey].objectNumber + " 0 R"); |
} |
} |
|
events.publish("putTilingPatternDict"); |
}, |
|
putGStatesDict = function () { |
var gStateKey; |
for (gStateKey in gStates) { |
if (gStates.hasOwnProperty(gStateKey) && gStates[gStateKey].objectNumber >= 0) { |
out("/" + gStateKey + " " + gStates[gStateKey].objectNumber + " 0 R"); |
} |
} |
|
events.publish("putGStateDict"); |
}, |
putResourceDictionary = function() { |
out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); |
out('/Font <<'); |
// Do this for each font, the '1' bit is the index of the font |
for (var fontKey in fonts) { |
if (fonts.hasOwnProperty(fontKey)) { |
out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R'); |
} |
} |
out('>>'); |
|
out("/Shading <<"); |
putShadingPatternDict(); |
out(">>"); |
|
out("/Pattern <<"); |
putTilingPatternDict(); |
out(">>"); |
|
out("/ExtGState <<"); |
putGStatesDict(); |
out('>>'); |
|
out('/XObject <<'); |
putXobjectDict(); |
out('>>'); |
}, |
putResources = function() { |
putFonts(); |
putGStates(); |
putXObjects(); |
putPatterns(); |
events.publish('putResources'); |
// Resource dictionary |
offsets[2] = content_length; |
out('2 0 obj'); |
out('<<'); |
putResourceDictionary(); |
out('>>'); |
out('endobj'); |
events.publish('postPutResources'); |
}, |
putAdditionalObjects = function() { |
events.publish('putAdditionalObjects'); |
for (var i=0; i<additionalObjects.length; i++){ |
var obj = additionalObjects[i]; |
offsets[obj.objId] = content_length; |
out( obj.objId + ' 0 obj'); |
out(obj.content); |
out('endobj'); |
} |
objectNumber += additionalObjects.length; |
events.publish('postPutAdditionalObjects'); |
}, |
addToFontDictionary = function(fontKey, fontName, fontStyle) { |
// this is mapping structure for quick font key lookup. |
// returns the KEY of the font (ex: "F1") for a given |
// pair of font name and type (ex: "Arial". "Italic") |
if (!fontmap.hasOwnProperty(fontName)) { |
fontmap[fontName] = {}; |
} |
fontmap[fontName][fontStyle] = fontKey; |
}, |
/** |
* FontObject describes a particular font as member of an instnace of jsPDF |
* |
* It's a collection of properties like 'id' (to be used in PDF stream), |
* 'fontName' (font's family name), 'fontStyle' (font's style variant label) |
* |
* @public |
* @property id {String} PDF-document-instance-specific label assinged to the font. |
* @property PostScriptName {String} PDF specification full name for the font |
* @property encoding {Object} Encoding_name-to-Font_metrics_object mapping. |
* @name FontObject |
*/ |
addFont = function(PostScriptName, fontName, fontStyle, encoding) { |
var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10), |
// This is FontObject |
font = fonts[fontKey] = { |
'id' : fontKey, |
'PostScriptName' : PostScriptName, |
'fontName' : fontName, |
'fontStyle' : fontStyle, |
'encoding' : encoding, |
'metadata' : {} |
}; |
addToFontDictionary(fontKey, fontName, fontStyle); |
events.publish('addFont', font); |
|
return fontKey; |
}, |
addFonts = function() { |
|
var HELVETICA = "helvetica", |
TIMES = "times", |
COURIER = "courier", |
NORMAL = "normal", |
BOLD = "bold", |
ITALIC = "italic", |
BOLD_ITALIC = "bolditalic", |
encoding = 'StandardEncoding', |
ZAPF = "zapfdingbats", |
standardFonts = [ |
['Helvetica', HELVETICA, NORMAL], |
['Helvetica-Bold', HELVETICA, BOLD], |
['Helvetica-Oblique', HELVETICA, ITALIC], |
['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC], |
['Courier', COURIER, NORMAL], |
['Courier-Bold', COURIER, BOLD], |
['Courier-Oblique', COURIER, ITALIC], |
['Courier-BoldOblique', COURIER, BOLD_ITALIC], |
['Times-Roman', TIMES, NORMAL], |
['Times-Bold', TIMES, BOLD], |
['Times-Italic', TIMES, ITALIC], |
['Times-BoldItalic', TIMES, BOLD_ITALIC], |
['ZapfDingbats',ZAPF ] |
]; |
|
for (var i = 0, l = standardFonts.length; i < l; i++) { |
var fontKey = addFont( |
standardFonts[i][0], |
standardFonts[i][1], |
standardFonts[i][2], |
encoding); |
|
// adding aliases for standard fonts, this time matching the capitalization |
var parts = standardFonts[i][0].split('-'); |
addToFontDictionary(fontKey, parts[0], parts[1] || ''); |
} |
events.publish('addFonts', { fonts : fonts, dictionary : fontmap }); |
}, |
matrixMult = function (m1, m2) { |
return new Matrix( |
m1.a * m2.a + m1.b * m2.c, |
m1.a * m2.b + m1.b * m2.d, |
m1.c * m2.a + m1.d * m2.c, |
m1.c * m2.b + m1.d * m2.d, |
m1.e * m2.a + m1.f * m2.c + m2.e, |
m1.e * m2.b + m1.f * m2.d + m2.f |
); |
}, |
Matrix = function (a, b, c, d, e, f) { |
this.a = a; |
this.b = b; |
this.c = c; |
this.d = d; |
this.e = e; |
this.f = f; |
}; |
|
Matrix.prototype = { |
toString: function () { |
return [ |
f3(this.a), |
f3(this.b), |
f3(this.c), |
f3(this.d), |
f3(this.e), |
f3(this.f) |
].join(" "); |
} |
}; |
|
var unitMatrix = new Matrix(1, 0, 0, 1, 0, 0), |
|
// Used (1) to save the current stream state to the XObjects stack and (2) to save completed form |
// objects in the xObjects map. |
RenderTarget = function () { |
this.page = page; |
this.currentPage = currentPage; |
this.pages = pages.slice(0); |
this.pagedim = pagedim.slice(0); |
this.pagesContext = pagesContext.slice(0); |
this.x = pageX; |
this.y = pageY; |
this.matrix = pageMatrix; |
this.width = pageWidth; |
this.height = pageHeight; |
|
this.id = ""; // set by endFormObject() |
this.objectNumber = -1; // will be set by putXObject() |
}; |
|
RenderTarget.prototype = { |
restore: function () { |
page = this.page; |
currentPage = this.currentPage; |
pagesContext = this.pagesContext; |
pagedim = this.pagedim; |
pages = this.pages; |
pageX = this.x; |
pageY = this.y; |
pageMatrix = this.matrix; |
pageWidth = this.width; |
pageHeight = this.height; |
} |
}; |
|
var beginNewRenderTarget = function (x, y, width, height, matrix) { |
// save current state |
renderTargetStack.push(new RenderTarget()); |
|
// clear pages |
page = currentPage = 0; |
pages = []; |
pageX = x; |
pageY = y; |
|
pageMatrix = matrix; |
|
beginPage(width, height); |
}, |
|
endFormObject = function (key) { |
// only add it if it is not already present (the keys provided by the user must be unique!) |
if (renderTargetMap[key]) |
return; |
|
// save the created xObject |
var newXObject = new RenderTarget(); |
|
var xObjectId = 'Xo' + (Object.keys(renderTargets).length + 1).toString(10); |
newXObject.id = xObjectId; |
|
renderTargetMap[key] = xObjectId; |
renderTargets[xObjectId] = newXObject; |
|
events.publish('addFormObject', newXObject); |
|
// restore state from stack |
renderTargetStack.pop().restore(); |
}, |
|
/** |
* Adds a new pattern for later use. |
* @param {String} key The key by it can be referenced later. The keys must be unique! |
* @param {API.Pattern} pattern The pattern |
*/ |
addPattern = function (key, pattern) { |
// only add it if it is not already present (the keys provided by the user must be unique!) |
if (patternMap[key]) |
return; |
|
var prefix = pattern instanceof API.ShadingPattern ? "Sh" : "P"; |
var patternKey = prefix + (Object.keys(patterns).length + 1).toString(10); |
pattern.id = patternKey; |
|
patternMap[key] = patternKey; |
patterns[patternKey] = pattern; |
|
events.publish('addPattern', pattern); |
}, |
|
/** |
* Adds a new Graphics State. Duplicates are automatically eliminated. |
* @param {String} key Might also be null, if no later reference to this gState is needed |
* @param {Object} gState The gState object |
*/ |
addGState = function (key, gState) { |
// only add it if it is not already present (the keys provided by the user must be unique!) |
if (key && gStatesMap[key]) |
return; |
|
var duplicate = false; |
for (var s in gStates) { |
if (gStates.hasOwnProperty(s)) { |
if (gStates[s].equals(gState)) { |
duplicate = true; |
break; |
} |
} |
} |
|
if (duplicate) { |
gState = gStates[s]; |
} else { |
var gStateKey = 'GS' + (Object.keys(gStates).length + 1).toString(10); |
gStates[gStateKey] = gState; |
gState.id = gStateKey; |
} |
|
// several user keys may point to the same GState object |
key && (gStatesMap[key] = gState.id); |
|
events.publish('addGState', gState); |
|
return gState; |
}, |
SAFE = function __safeCall(fn) { |
fn.foo = function __safeCallWrapper() { |
try { |
return fn.apply(this, arguments); |
} catch (e) { |
var stack = e.stack || ''; |
if(~stack.indexOf(' at ')) stack = stack.split(" at ")[1]; |
var m = "Error in function " + stack.split("\n")[0].split('<')[0] + ": " + e.message; |
if(global.console) { |
global.console.error(m, e); |
if(global.alert) alert(m); |
} else { |
throw new Error(m); |
} |
} |
}; |
fn.foo.bar = fn; |
return fn.foo; |
}, |
to8bitStream = function(text, flags) { |
/** |
* PDF 1.3 spec: |
* "For text strings encoded in Unicode, the first two bytes must be 254 followed by |
* 255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts |
* with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely |
* to be a meaningful beginning of a word or phrase.) The remainder of the |
* string consists of Unicode character codes, according to the UTF-16 encoding |
* specified in the Unicode standard, version 2.0. Commonly used Unicode values |
* are represented as 2 bytes per character, with the high-order byte appearing first |
* in the string." |
* |
* In other words, if there are chars in a string with char code above 255, we |
* recode the string to UCS2 BE - string doubles in length and BOM is prepended. |
* |
* HOWEVER! |
* Actual *content* (body) text (as opposed to strings used in document properties etc) |
* does NOT expect BOM. There, it is treated as a literal GID (Glyph ID) |
* |
* Because of Adobe's focus on "you subset your fonts!" you are not supposed to have |
* a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could |
* fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode |
* code page. There, however, all characters in the stream are treated as GIDs, |
* including BOM, which is the reason we need to skip BOM in content text (i.e. that |
* that is tied to a font). |
* |
* To signal this "special" PDFEscape / to8bitStream handling mode, |
* API.text() function sets (unless you overwrite it with manual values |
* given to API.text(.., flags) ) |
* flags.autoencode = true |
* flags.noBOM = true |
* |
* =================================================================================== |
* `flags` properties relied upon: |
* .sourceEncoding = string with encoding label. |
* "Unicode" by default. = encoding of the incoming text. |
* pass some non-existing encoding name |
* (ex: 'Do not touch my strings! I know what I am doing.') |
* to make encoding code skip the encoding step. |
* .outputEncoding = Either valid PDF encoding name |
* (must be supported by jsPDF font metrics, otherwise no encoding) |
* or a JS object, where key = sourceCharCode, value = outputCharCode |
* missing keys will be treated as: sourceCharCode === outputCharCode |
* .noBOM |
* See comment higher above for explanation for why this is important |
* .autoencode |
* See comment higher above for explanation for why this is important |
*/ |
|
var i,l,sourceEncoding,encodingBlock,outputEncoding,newtext,isUnicode,ch,bch; |
|
flags = flags || {}; |
sourceEncoding = flags.sourceEncoding || 'Unicode'; |
outputEncoding = flags.outputEncoding; |
|
// This 'encoding' section relies on font metrics format |
// attached to font objects by, among others, |
// "Willow Systems' standard_font_metrics plugin" |
// see jspdf.plugin.standard_font_metrics.js for format |
// of the font.metadata.encoding Object. |
// It should be something like |
// .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}} |
// .widths = {0:width, code:width, ..., 'fof':divisor} |
// .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...} |
if ((flags.autoencode || outputEncoding) && |
fonts[activeFontKey].metadata && |
fonts[activeFontKey].metadata[sourceEncoding] && |
fonts[activeFontKey].metadata[sourceEncoding].encoding) { |
encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding; |
|
// each font has default encoding. Some have it clearly defined. |
if (!outputEncoding && fonts[activeFontKey].encoding) { |
outputEncoding = fonts[activeFontKey].encoding; |
} |
|
// Hmmm, the above did not work? Let's try again, in different place. |
if (!outputEncoding && encodingBlock.codePages) { |
outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default |
} |
|
if (typeof outputEncoding === 'string') { |
outputEncoding = encodingBlock[outputEncoding]; |
} |
// we want output encoding to be a JS Object, where |
// key = sourceEncoding's character code and |
// value = outputEncoding's character code. |
if (outputEncoding) { |
isUnicode = false; |
newtext = []; |
for (i = 0, l = text.length; i < l; i++) { |
ch = outputEncoding[text.charCodeAt(i)]; |
if (ch) { |
newtext.push( |
String.fromCharCode(ch)); |
} else { |
newtext.push( |
text[i]); |
} |
|
// since we are looping over chars anyway, might as well |
// check for residual unicodeness |
if (newtext[i].charCodeAt(0) >> 8) { |
/* more than 255 */ |
isUnicode = true; |
} |
} |
text = newtext.join(''); |
} |
} |
|
i = text.length; |
// isUnicode may be set to false above. Hence the triple-equal to undefined |
while (isUnicode === undefined && i !== 0) { |
if (text.charCodeAt(i - 1) >> 8) { |
/* more than 255 */ |
isUnicode = true; |
} |
i--; |
} |
if (!isUnicode) { |
return text; |
} |
|
newtext = flags.noBOM ? [] : [254, 255]; |
for (i = 0, l = text.length; i < l; i++) { |
ch = text.charCodeAt(i); |
bch = ch >> 8; // divide by 256 |
if (bch >> 8) { |
/* something left after dividing by 256 second time */ |
throw new Error("Character at position " + i + " of string '" |
+ text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE"); |
} |
newtext.push(bch); |
newtext.push(ch - (bch << 8)); |
} |
return String.fromCharCode.apply(undefined, newtext); |
}, |
pdfEscape = function(text, flags) { |
/** |
* Replace '/', '(', and ')' with pdf-safe versions |
* |
* Doing to8bitStream does NOT make this PDF display unicode text. For that |
* we also need to reference a unicode font and embed it - royal pain in the rear. |
* |
* There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars, |
* which JavaScript Strings are happy to provide. So, while we still cannot display |
* 2-byte characters property, at least CONDITIONALLY converting (entire string containing) |
* 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF |
* is still parseable. |
* This will allow immediate support for unicode in document properties strings. |
*/ |
return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); |
}, |
putInfo = function() { |
out('/Producer (jsPDF ' + jsPDF.version + ')'); |
for(var key in documentProperties) { |
if(documentProperties.hasOwnProperty(key) && documentProperties[key]) { |
out('/'+key.substr(0,1).toUpperCase() + key.substr(1) |
+' (' + pdfEscape(documentProperties[key]) + ')'); |
} |
} |
var created = new Date(), |
tzoffset = created.getTimezoneOffset(), |
tzsign = tzoffset < 0 ? '+' : '-', |
tzhour = Math.floor(Math.abs(tzoffset / 60)), |
tzmin = Math.abs(tzoffset % 60), |
tzstr = [tzsign, padd2(tzhour), "'", padd2(tzmin), "'"].join(''); |
out(['/CreationDate (D:', |
created.getFullYear(), |
padd2(created.getMonth() + 1), |
padd2(created.getDate()), |
padd2(created.getHours()), |
padd2(created.getMinutes()), |
padd2(created.getSeconds()), tzstr, ')'].join('')); |
}, |
putCatalog = function() { |
out('/Type /Catalog'); |
out('/Pages 1 0 R'); |
// PDF13ref Section 7.2.1 |
if (!zoomMode) zoomMode = 'fullwidth'; |
switch(zoomMode) { |
case 'fullwidth' : out('/OpenAction [3 0 R /FitH null]'); break; |
case 'fullheight' : out('/OpenAction [3 0 R /FitV null]'); break; |
case 'fullpage' : out('/OpenAction [3 0 R /Fit]'); break; |
case 'original' : out('/OpenAction [3 0 R /XYZ null null 1]'); break; |
default: |
var pcn = '' + zoomMode; |
if (pcn.substr(pcn.length-1) === '%') |
zoomMode = parseInt(zoomMode) / 100; |
if (typeof zoomMode === 'number') { |
out('/OpenAction [3 0 R /XYZ null null '+f2(zoomMode)+']'); |
} |
} |
if (!layoutMode) layoutMode = 'continuous'; |
switch(layoutMode) { |
case 'continuous' : out('/PageLayout /OneColumn'); break; |
case 'single' : out('/PageLayout /SinglePage'); break; |
case 'two': |
case 'twoleft' : out('/PageLayout /TwoColumnLeft'); break; |
case 'tworight' : out('/PageLayout /TwoColumnRight'); break; |
} |
if (pageMode) { |
/** |
* A name object specifying how the document should be displayed when opened: |
* UseNone : Neither document outline nor thumbnail images visible -- DEFAULT |
* UseOutlines : Document outline visible |
* UseThumbs : Thumbnail images visible |
* FullScreen : Full-screen mode, with no menu bar, window controls, or any other window visible |
*/ |
out('/PageMode /' + pageMode); |
} |
events.publish('putCatalog'); |
}, |
putTrailer = function() { |
out('/Size ' + (objectNumber + 1)); |
out('/Root ' + objectNumber + ' 0 R'); |
out('/Info ' + (objectNumber - 1) + ' 0 R'); |
}, |
beginPage = function(width,height) { |
// Dimensions are stored as user units and converted to points on output |
var orientation = typeof height === 'string' && height.toLowerCase(); |
if (typeof width === 'string') { |
var format = width.toLowerCase(); |
if (pageFormats.hasOwnProperty(format)) { |
width = pageFormats[format][0] / k; |
height = pageFormats[format][1] / k; |
} |
} |
if (Array.isArray(width)) { |
height = width[1]; |
width = width[0]; |
} |
//if (orientation) { |
// switch(orientation.substr(0,1)) { |
// case 'l': if (height > width ) orientation = 's'; break; |
// case 'p': if (width > height ) orientation = 's'; break; |
// } |
// TODO: What is the reason for this (for me it only seems to raise bugs)? |
// if (orientation === 's') { tmp = width; width = height; height = tmp; } |
//} |
outToPages = true; |
pages[++page] = []; |
pagedim[page] = { |
width : Number(width) || pageWidth, |
height : Number(height) || pageHeight |
}; |
pagesContext[page] = {}; |
_setPage(page); |
}, |
_addPage = function() { |
beginPage.apply(this, arguments); |
// Set line width |
out(f2(lineWidth) + ' w'); |
// Set draw color |
out(drawColor); |
// resurrecting non-default line caps, joins |
if (lineCapID !== 0) { |
out(lineCapID + ' J'); |
} |
if (lineJoinID !== 0) { |
out(lineJoinID + ' j'); |
} |
events.publish('addPage', { pageNumber : page }); |
}, |
_deletePage = function( n ) { |
if (n > 0 && n <= page) { |
pages.splice(n, 1); |
pagedim.splice(n, 1); |
page--; |
if (currentPage > page){ |
currentPage = page; |
} |
this.setPage(currentPage); |
} |
}, |
_setPage = function(n) { |
if (n > 0 && n <= page) { |
currentPage = n; |
pageWidth = pagedim[n].width; |
pageHeight = pagedim[n].height; |
} |
}, |
/** |
* Returns a document-specific font key - a label assigned to a |
* font name + font type combination at the time the font was added |
* to the font inventory. |
* |
* Font key is used as label for the desired font for a block of text |
* to be added to the PDF document stream. |
* @private |
* @function |
* @param {String} fontName can be undefined on "falthy" to indicate "use current" |
* @param {String} fontStyle can be undefined on "falthy" to indicate "use current" |
* @returns {String} Font key. |
*/ |
getFont = function(fontName, fontStyle) { |
var key; |
|
fontName = fontName !== undefined ? fontName : fonts[activeFontKey].fontName; |
fontStyle = fontStyle !== undefined ? fontStyle : fonts[activeFontKey].fontStyle; |
|
if (fontName !== undefined){ |
fontName = fontName.toLowerCase(); |
} |
switch(fontName){ |
case 'sans-serif': |
case 'verdana': |
case 'arial': |
case 'helvetica': |
fontName = 'helvetica'; |
break; |
case 'fixed': |
case 'monospace': |
case 'terminal': |
case 'courier': |
fontName = 'courier'; |
break; |
case 'serif': |
case 'cursive': |
case 'fantasy': |
default: |
fontName = 'times'; |
break; |
} |
|
try { |
// get a string like 'F3' - the KEY corresponding tot he font + type combination. |
key = fontmap[fontName][fontStyle]; |
} catch (e) {} |
|
if (!key) { |
//throw new Error("Unable to look up font label for font '" + fontName + "', '" |
//+ fontStyle + "'. Refer to getFontList() for available fonts."); |
key = fontmap['times'][fontStyle]; |
if (key == null){ |
key = fontmap['times']['normal']; |
} |
} |
return key; |
}, |
buildDocument = function() { |
|
outToPages = false; // switches out() to content |
objectNumber = 2; |
content = []; |
offsets = []; |
additionalObjects = []; |
|
// putHeader() |
out('%PDF-' + pdfVersion); |
|
putPages(); |
|
// Must happen after putPages |
// Modifies current object Id |
putAdditionalObjects(); |
|
putResources(); |
|
// Info |
newObject(); |
out('<<'); |
putInfo(); |
out('>>'); |
out('endobj'); |
|
// Catalog |
newObject(); |
out('<<'); |
putCatalog(); |
out('>>'); |
out('endobj'); |
|
// Cross-ref |
var o = content_length, i, p = "0000000000"; |
out('xref'); |
out('0 ' + (objectNumber + 1)); |
out(p+' 65535 f '); |
for (i = 1; i <= objectNumber; i++) { |
var offset = offsets[i]; |
if (typeof offset === 'function'){ |
out((p + offsets[i]()).slice(-10) + ' 00000 n '); |
}else{ |
out((p + offsets[i]).slice(-10) + ' 00000 n '); |
} |
} |
// Trailer |
out('trailer'); |
out('<<'); |
putTrailer(); |
out('>>'); |
out('startxref'); |
out(o); |
out('%%EOF'); |
|
outToPages = true; |
|
return content.join('\n'); |
}, |
|
getStyle = function(style) { |
// see path-painting operators in PDF spec |
var op = 'n'; // none |
if (style === "D") { |
op = 'S'; // stroke |
} else if (style === 'F') { |
op = 'f'; // fill |
} else if (style === 'FD' || style === 'DF') { |
op = 'B'; // both |
} else if (style === 'f' || style === 'f*' || style === 'B' || style === 'B*') { |
/* |
Allow direct use of these PDF path-painting operators: |
- f fill using nonzero winding number rule |
- f* fill using even-odd rule |
- B fill then stroke with fill using non-zero winding number rule |
- B* fill then stroke with fill using even-odd rule |
*/ |
op = style; |
} |
return op; |
}, |
// puts the style for the previously drawn path. If a patternKey is provided, the pattern is used to fill |
// the path. Use patternMatrix to transform the pattern to rhe right location. |
putStyle = function (style, patternKey, patternData) { |
style = getStyle(style); |
|
// stroking / filling / both the path |
if (!patternKey) { |
out(style); |
return; |
} |
|
patternData || (patternData = unitMatrix); |
|
var patternId = patternMap[patternKey]; |
var pattern = patterns[patternId]; |
|
if (pattern instanceof API.ShadingPattern) { |
out("q"); |
out("W " + style); |
|
if (pattern.gState) { |
API.setGState(pattern.gState); |
} |
|
out(patternData.toString() + " cm"); |
out("/" + patternId + " sh"); |
out("Q"); |
} else if (pattern instanceof API.TilingPattern) { |
// pdf draws patterns starting at the bottom left corner and they are not affected by the global transformation, |
// so we must flip them |
var matrix = new Matrix(1, 0, 0, -1, 0, pageHeight); |
|
if (patternData.matrix) { |
matrix = matrixMult(patternData.matrix || unitMatrix, matrix); |
|
// we cannot apply a matrix to the pattern on use so we must abuse the pattern matrix and create new instances |
// for each use |
patternId = pattern.createClone(patternKey, patternData.boundingBox, patternData.xStep, patternData.yStep, matrix).id; |
} |
|
out("q"); |
out("/Pattern cs"); |
out("/" + patternId + " scn"); |
|
if (pattern.gState) { |
API.setGState(pattern.gState); |
} |
|
out(style); |
out("Q"); |
} |
}, |
|
getArrayBuffer = function() { |
var data = buildDocument(), len = data.length, |
ab = new ArrayBuffer(len), u8 = new Uint8Array(ab); |
|
while(len--) u8[len] = data.charCodeAt(len); |
return ab; |
}, |
getBlob = function() { |
return new Blob([getArrayBuffer()], { type : "application/pdf" }); |
}, |
/** |
* Generates the PDF document. |
* |
* If `type` argument is undefined, output is raw body of resulting PDF returned as a string. |
* |
* @param {String} type A string identifying one of the possible output types. |
* @param {Object} options An object providing some additional signalling to PDF generator. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name output |
*/ |
output = SAFE(function(type, options) { |
var datauri = ('' + type).substr(0,6) === 'dataur' |
? 'data:application/pdf;base64,'+btoa(buildDocument()):0; |
|
switch (type) { |
case undefined: |
return buildDocument(); |
case 'save': |
if (navigator.getUserMedia) { |
if (global.URL === undefined |
|| global.URL.createObjectURL === undefined) { |
return API.output('dataurlnewwindow'); |
} |
} |
saveAs(getBlob(), options); |
if(typeof saveAs.unload === 'function') { |
if(global.setTimeout) { |
setTimeout(saveAs.unload,911); |
} |
} |
break; |
case 'arraybuffer': |
return getArrayBuffer(); |
case 'blob': |
return getBlob(); |
case 'bloburi': |
case 'bloburl': |
// User is responsible of calling revokeObjectURL |
return global.URL && global.URL.createObjectURL(getBlob()) || void 0; |
case 'datauristring': |
case 'dataurlstring': |
return datauri; |
case 'dataurlnewwindow': |
var nW = global.open(datauri); |
if (nW || typeof safari === "undefined") return nW; |
/* pass through */ |
case 'datauri': |
case 'dataurl': |
return global.document.location.href = datauri; |
default: |
throw new Error('Output type "' + type + '" is not supported.'); |
} |
// @TODO: Add different output options |
}); |
|
switch (unit) { |
case 'pt': k = 1; break; |
case 'mm': k = 72 / 25.4000508; break; |
case 'cm': k = 72 / 2.54000508; break; |
case 'in': k = 72; break; |
case 'px': k = 96 / 72; break; |
case 'pc': k = 12; break; |
case 'em': k = 12; break; |
case 'ex': k = 6; break; |
default: |
throw ('Invalid unit: ' + unit); |
} |
|
//--------------------------------------- |
// Public API |
|
/** |
* Object exposing internal API to plugins |
* @public |
*/ |
API.internal = { |
'pdfEscape' : pdfEscape, |
'getStyle' : getStyle, |
/** |
* Returns {FontObject} describing a particular font. |
* @public |
* @function |
* @param {String} fontName (Optional) Font's family name |
* @param {String} fontStyle (Optional) Font's style variation name (Example:"Italic") |
* @returns {FontObject} |
*/ |
'getFont' : function() { |
return fonts[getFont.apply(API, arguments)]; |
}, |
'getFontSize' : function() { |
return activeFontSize; |
}, |
'getLineHeight' : function() { |
return activeFontSize * lineHeightProportion; |
}, |
'write' : function(string1 /*, string2, string3, etc */) { |
out(arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' ')); |
}, |
'getCoordinateString' : function(value) { |
return f2(value); |
}, |
'getVerticalCoordinateString' : function(value) { |
return f2(value); |
}, |
'collections' : {}, |
'newObject' : newObject, |
'newAdditionalObject' : newAdditionalObject, |
'newObjectDeferred' : newObjectDeferred, |
'newObjectDeferredBegin' : newObjectDeferredBegin, |
'putStream' : putStream, |
'events' : events, |
// ratio that you use in multiplication of a given "size" number to arrive to 'point' |
// units of measurement. |
// scaleFactor is set at initialization of the document and calculated against the stated |
// default measurement units for the document. |
// If default is "mm", k is the number that will turn number in 'mm' into 'points' number. |
// through multiplication. |
'scaleFactor' : k, |
'pageSize' : { |
get width() { |
return pageWidth |
}, |
get height() { |
return pageHeight |
} |
}, |
'output' : function(type, options) { |
return output(type, options); |
}, |
'getNumberOfPages' : function() { |
return pages.length - 1; |
}, |
'pages' : pages, |
'out' : out, |
'f2' : f2, |
'getPageInfo' : function(pageNumberOneBased){ |
var objId = (pageNumberOneBased - 1) * 2 + 3; |
return {objId:objId, pageNumber:pageNumberOneBased, pageContext:pagesContext[pageNumberOneBased]}; |
}, |
'getCurrentPageInfo' : function(){ |
var objId = (currentPage - 1) * 2 + 3; |
return {objId:objId, pageNumber:currentPage, pageContext:pagesContext[currentPage]}; |
}, |
'getPDFVersion': function () { |
return pdfVersion; |
} |
}; |
|
/** |
* An object representing a pdf graphics state. |
* @param parameters A parameter object that contains all properties this graphics state wants to set. |
* Supported are: opacity |
* @constructor |
*/ |
API.GState = function (parameters) { |
var supported = "opacity"; |
for (var p in parameters) { |
if (parameters.hasOwnProperty(p) && supported.indexOf(p) >= 0) { |
this[p] = parameters[p]; |
} |
} |
this.id = ""; // set by addGState() |
this.objectNumber = -1; // will be set by putGState() |
|
this.equals = function (other) { |
var ignore = "id,objectNumber,equals"; |
if (!other || typeof other !== typeof this) |
return false; |
var count = 0; |
for (var p in this) { |
if (ignore.indexOf(p) >= 0) |
continue; |
if (this.hasOwnProperty(p) && !other.hasOwnProperty(p)) |
return false; |
if (this[p] !== other[p]) |
return false; |
count++; |
} |
for (var p in other) { |
if (other.hasOwnProperty(p) && ignore.indexOf(p) < 0) |
count--; |
} |
return count === 0; |
} |
}; |
|
/** |
* Adds a new {@link GState} for later use {@see setGState}. |
* @param {String} key |
* @param {GState} gState |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name addGState |
*/ |
API.addGState = function (key, gState) { |
addGState(key, gState); |
return this; |
}; |
|
/** |
* Adds (and transfers the focus to) new page to the PDF document. |
* @function |
* @returns {jsPDF} |
* |
* @methodOf jsPDF# |
* @name addPage |
*/ |
API.addPage = function() { |
_addPage.apply(this, arguments); |
return this; |
}; |
API.setPage = function() { |
_setPage.apply(this, arguments); |
return this; |
}; |
API.insertPage = function(beforePage) { |
this.addPage(); |
this.movePage(currentPage, beforePage); |
return this; |
}; |
API.movePage = function(targetPage, beforePage) { |
var tmpPagesContext, tmpPagedim, tmpPages, i; |
if (targetPage > beforePage){ |
tmpPages = pages[targetPage]; |
tmpPagedim = pagedim[targetPage]; |
tmpPagesContext = pagesContext[targetPage]; |
for (i = targetPage; i > beforePage; i--){ |
pages[i] = pages[i-1]; |
pagedim[i] = pagedim[i-1]; |
pagesContext[i] = pagesContext[i-1]; |
} |
pages[beforePage] = tmpPages; |
pagedim[beforePage] = tmpPagedim; |
pagesContext[beforePage] = tmpPagesContext; |
this.setPage(beforePage); |
} else if (targetPage < beforePage){ |
tmpPages = pages[targetPage]; |
tmpPagedim = pagedim[targetPage]; |
tmpPagesContext = pagesContext[targetPage]; |
for (i = targetPage; i < beforePage; i++){ |
pages[i] = pages[i+1]; |
pagedim[i] = pagedim[i+1]; |
pagesContext[i] = pagesContext[i+1]; |
} |
pages[beforePage] = tmpPages; |
pagedim[beforePage] = tmpPagedim; |
pagesContext[beforePage] = tmpPagesContext; |
this.setPage(beforePage); |
} |
return this; |
}; |
|
API.deletePage = function() { |
_deletePage.apply( this, arguments ); |
return this; |
}; |
API.setDisplayMode = function(zoom, layout, pmode) { |
zoomMode = zoom; |
layoutMode = layout; |
pageMode = pmode; |
return this; |
}; |
|
/** |
* Saves the current graphics state ("pushes it on the stack"). It can be restored by {@link restoreGraphicsState} |
* later. Here, the general pdf graphics state is meant, also including the current transformation matrix, |
* fill and stroke colors etc. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name saveGraphicsState |
*/ |
API.saveGraphicsState = function () { |
out("q"); |
// as we cannot set font key and size independently we must keep track of both |
fontStateStack.push({ |
key: activeFontKey, |
size: activeFontSize |
}); |
return this; |
}; |
|
/** |
* Restores a previously saved graphics state saved by {@link saveGraphicsState} ("pops the stack"). |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name restoreGraphicsState |
*/ |
API.restoreGraphicsState = function () { |
out("Q"); |
|
// restore previous font state |
var fontState = fontStateStack.pop(); |
activeFontKey = fontState.key; |
activeFontSize = fontState.size; |
|
return this; |
}; |
|
/** |
* Appends this matrix to the left of all previously applied matrices. |
* @param {Matrix} matrix |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setCurrentTransformationMatrix |
*/ |
API.setCurrentTransformationMatrix = function (matrix) { |
out(matrix.toString() + " cm"); |
return this; |
}; |
|
/** |
* Starts a new pdf form object, which means that all conseequent draw calls target a new independent object |
* until {@link endFormObject} is called. The created object can be referenced and drawn later using |
* {@link doFormObject}. Nested form objects are possible. |
* x, y, width, height set the bounding box that is used to clip the content. |
* @param {number} x |
* @param {number} y |
* @param {number} width |
* @param {number} height |
* @param {Matrix} matrix The matrix that will be applied to convert the form objects coordinate system to |
* the parent's. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
*/ |
API.beginFormObject = function (x, y, width, height, matrix) { |
// The user can set the output target to a new form object. Nested form objects are possible. |
// Currently, they use the resource dictionary of the surrounding stream. This should be changed, as |
// the PDF-Spec states: |
// "In PDF 1.2 and later versions, form XObjects may be independent of the content streams in which |
// they appear, and this is strongly recommended although not requiredIn PDF 1.2 and later versions, |
// form XObjects may be independent of the content streams in which they appear, and this is strongly |
// recommended although not required" |
beginNewRenderTarget(x, y, width, height, matrix); |
return this; |
}; |
|
/** |
* Completes and saves the form object. |
* @param {String} key The key by which this form object can be referenced. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name endFormObject |
*/ |
API.endFormObject = function (key) { |
endFormObject(key); |
return this; |
}; |
|
/** |
* Draws the specified form object by referencing to the respective pdf XObject created with |
* {@link API.beginFormObject} and {@link endFormObject}. |
* The location is determined by matrix. |
* @param {String} key The key to the form object. |
* @param {Matrix} matrix The matrix applied before drawing the form object. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name doFormObject |
*/ |
API.doFormObject = function (key, matrix) { |
var xObject = renderTargets[renderTargetMap[key]]; |
out("q"); |
out(matrix.toString() + " cm"); |
out("/" + xObject.id + " Do"); |
out("Q"); |
return this; |
}; |
|
/** |
* Returns the form object specified by key. |
* @param key {String} |
* @returns {{x: number, y: number, width: number, height: number, matrix: Matrix}} |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name getFormObject |
*/ |
API.getFormObject = function (key) { |
var xObject = renderTargets[renderTargetMap[key]]; |
return { |
x: xObject.x, |
y: xObject.y, |
width: xObject.width, |
height: xObject.height, |
matrix: xObject.matrix |
}; |
}; |
|
/** |
* A matrix object for 2D homogenous transformations: |
* | a b 0 | |
* | c d 0 | |
* | e f 1 | |
* pdf multiplies matrices righthand: v' = v x m1 x m2 x ... |
* @param {number} a |
* @param {number} b |
* @param {number} c |
* @param {number} d |
* @param {number} e |
* @param {number} f |
* @constructor |
*/ |
API.Matrix = Matrix; |
|
/** |
* Multiplies two matrices. (see {@link Matrix}) |
* @param {Matrix} m1 |
* @param {Matrix} m2 |
*/ |
API.matrixMult = matrixMult; |
|
/** |
* The unit matrix (equal to new Matrix(1, 0, 0, 1, 0, 0). |
* @type {Matrix} |
*/ |
API.unitMatrix = unitMatrix; |
|
var Pattern = function (gState, matrix) { |
this.gState = gState; |
this.matrix = matrix; |
|
this.id = ""; // set by addPattern() |
this.objectNumber = -1; // will be set by putPattern() |
}; |
|
/** |
* A pattern describing a shading pattern. |
* @param {String} type One of "axial" or "radial" |
* @param {Array<Number>} coords Either [x1, y1, x2, y2] for "axial" type describing the two interpolation points |
* or [x1, y1, r, x2, y2, r2] for "radial" describing inner and the outer circle. |
* @param {Array<Object>} colors An array of objects with the fields "offset" and "color". "offset" describes |
* the offset in parameter space [0, 1]. "color" is an array of length 3 describing RGB values in [0, 255]. |
* @param {GState=} gState An additional graphics state that gets applied to the pattern (optional). |
* @param {Matrix=} matrix A matrix that describes the transformation between the pattern coordinate system |
* and the use coordinate system (optional). |
* @constructor |
* @extends API.Pattern |
*/ |
API.ShadingPattern = function (type, coords, colors, gState, matrix) { |
// see putPattern() for information how they are realized |
this.type = type === "axial" ? 2 : 3; |
this.coords = coords; |
this.colors = colors; |
|
Pattern.call(this, gState, matrix); |
}; |
|
/** |
* A PDF Tiling pattern. |
* @param {Array.<Number>} boundingBox The bounding box at which one pattern cell gets clipped. |
* @param {Number} xStep Horizontal spacing between pattern cells. |
* @param {Number} yStep Vertical spacing between pattern cells. |
* @param {API.GState=} gState An additional graphics state that gets applied to the pattern (optional). |
* @param {Matrix=} matrix A matrix that describes the transformation between the pattern coordinate system |
* and the use coordinate system (optional). |
* @constructor |
* @extends API.Pattern |
*/ |
API.TilingPattern = function (boundingBox, xStep, yStep, gState, matrix) { |
this.boundingBox = boundingBox; |
this.xStep = xStep; |
this.yStep = yStep; |
|
this.stream = ""; // set by endTilingPattern(); |
|
this.cloneIndex = 0; |
|
Pattern.call(this, gState, matrix); |
}; |
|
API.TilingPattern.prototype = { |
createClone: function (patternKey, boundingBox, xStep, yStep, matrix) { |
var clone = new API.TilingPattern(boundingBox || this.boundingBox, xStep || this.xStep, yStep || this.yStep, |
this.gState, matrix || this.matrix); |
clone.stream = this.stream; |
var key = patternKey + "$$" + this.cloneIndex++ + "$$"; |
addPattern(key, clone); |
return clone; |
} |
}; |
|
/** |
* Adds a new {@link API.ShadingPattern} for later use. |
* @param {String} key |
* @param {Pattern} pattern |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name addPattern |
*/ |
API.addShadingPattern = function (key, pattern) { |
addPattern(key, pattern); |
return this; |
}; |
|
/** |
* Begins a new tiling pattern. All subsequent render calls are drawn to this pattern until {@link API.endTilingPattern} |
* gets called. |
* @param {API.Pattern} pattern |
*/ |
API.beginTilingPattern = function (pattern) { |
beginNewRenderTarget(pattern.boundingBox[0], pattern.boundingBox[1], |
pattern.boundingBox[2] - pattern.boundingBox[0], pattern.boundingBox[3] - pattern.boundingBox[1], pattern.matrix); |
}; |
|
/** |
* Ends a tiling pattern and sets the render target to the one active before {@link API.beginTilingPattern} has been called. |
* @param {string} key A unique key that is used to reference this pattern at later use. |
* @param {API.Pattern} pattern The pattern to end. |
*/ |
API.endTilingPattern = function (key, pattern) { |
// retrieve the stream |
pattern.stream = pages[currentPage].join("\n"); |
|
addPattern(key, pattern); |
|
events.publish("endTilingPattern", pattern); |
|
// restore state from stack |
renderTargetStack.pop().restore(); |
}; |
|
/** |
* Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings. |
* |
* @function |
* @param {String|Array} text String or array of strings to be added to the page. Each line is shifted one line down |
* per font, spacing settings declared before this call. |
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @param {Object} flags Collection of settings signalling how the text must be encoded. Defaults are sane. If you |
* think you want to pass some flags, you likely can read the source. |
* @param {number|Matrix} transform If transform is a number the text will be rotated by this value. If it is a Matrix, |
* this matrix gets directly applied to the text, which allows shearing effects etc. |
* @param align {string} |
* @returns {jsPDF} |
* @methodOf jsPDF# |
*/ |
API.text = function(text, x, y, flags, transform, align) { |
/** |
* Inserts something like this into PDF |
* BT |
* /F1 16 Tf % Font name + size |
* 16 TL % How many units down for next line in multiline text |
* 0 g % color |
* 28.35 813.54 Td % position |
* (line one) Tj |
* T* (line two) Tj |
* T* (line three) Tj |
* ET |
*/ |
function ESC(s) { |
s = s.split("\t").join(Array(options.TabLen||9).join(" ")); |
return pdfEscape(s, flags); |
} |
|
// Pre-August-2012 the order of arguments was function(x, y, text, flags) |
// in effort to make all calls have similar signature like |
// function(data, coordinates... , miscellaneous) |
// this method had its args flipped. |
// code below allows backward compatibility with old arg order. |
if (typeof text === 'number') { |
var tmp = y; |
y = x; |
x = text; |
text = tmp; |
} |
|
// If there are any newlines in text, we assume |
// the user wanted to print multiple lines, so break the |
// text up into an array. If the text is already an array, |
// we assume the user knows what they are doing. |
// Convert text into an array anyway to simplify |
// later code. |
if (typeof text === 'string') { |
if(text.match(/[\n\r]/)) { |
text = text.split( /\r\n|\r|\n/g); |
} else { |
text = [text]; |
} |
} |
if (typeof transform === 'string') { |
align = transform; |
transform = null; |
} |
if (typeof flags === 'string') { |
align = flags; |
flags = null; |
} |
if (typeof flags === 'number') { |
transform = flags; |
flags = null; |
} |
|
var todo; |
if (transform && typeof transform === "number") { |
transform *= (Math.PI / 180); |
var c = Math.cos(transform), |
s = Math.sin(transform); |
transform = new Matrix(c, s , -s, c, 0, 0); |
} else if (!transform) { |
transform = unitMatrix; |
} |
|
flags = flags || {}; |
if (!('noBOM' in flags)) |
flags.noBOM = true; |
if (!('autoencode' in flags)) |
flags.autoencode = true; |
|
var strokeOption = ''; |
var pageContext = this.internal.getCurrentPageInfo().pageContext; |
if (true === flags.stroke){ |
if (pageContext.lastTextWasStroke !== true){ |
strokeOption = '1 Tr\n'; |
pageContext.lastTextWasStroke = true; |
} |
} |
else{ |
if (pageContext.lastTextWasStroke){ |
strokeOption = '0 Tr\n'; |
} |
pageContext.lastTextWasStroke = false; |
} |
|
if (typeof this._runningPageHeight === 'undefined'){ |
this._runningPageHeight = 0; |
} |
|
if (typeof text === 'string') { |
text = ESC(text); |
} else if (Object.prototype.toString.call(text) === '[object Array]') { |
// we don't want to destroy original text array, so cloning it |
var sa = text.concat(), da = [], len = sa.length; |
// we do array.join('text that must not be PDFescaped") |
// thus, pdfEscape each component separately |
while (len--) { |
da.push(ESC(sa.shift())); |
} |
var linesLeft = Math.ceil((y - this._runningPageHeight) / (activeFontSize * lineHeightProportion)); |
if (0 <= linesLeft && linesLeft < da.length + 1) { |
//todo = da.splice(linesLeft-1); |
} |
|
if( align ) { |
var left, |
prevX, |
maxLineLength, |
leading = activeFontSize * lineHeightProportion, |
lineWidths = text.map( function( v ) { |
return this.getStringUnitWidth( v ) * activeFontSize; |
}, this ); |
maxLineLength = Math.max.apply( Math, lineWidths ); |
// The first line uses the "main" Td setting, |
// and the subsequent lines are offset by the |
// previous line's x coordinate. |
if( align === "center" ) { |
// The passed in x coordinate defines |
// the center point. |
left = x - maxLineLength / 2; |
x -= lineWidths[0] / 2; |
} else if ( align === "right" ) { |
// The passed in x coordinate defines the |
// rightmost point of the text. |
left = x - maxLineLength; |
x -= lineWidths[0]; |
} else { |
throw new Error('Unrecognized alignment option, use "center" or "right".'); |
} |
prevX = x; |
text = da[0] + ") Tj\n"; |
for ( i = 1, len = da.length ; i < len; i++ ) { |
var delta = maxLineLength - lineWidths[i]; |
if( align === "center" ) delta /= 2; |
// T* = x-offset leading Td ( text ) |
text += ( ( left - prevX ) + delta ) + " -" + leading + " Td (" + da[i]; |
prevX = left + delta; |
if( i < len - 1 ) { |
text += ") Tj\n"; |
} |
} |
} else { |
text = da.join(") Tj\nT* ("); |
} |
} else { |
throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.'); |
} |
// Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates |
|
// BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET |
// if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations) |
// Thus, there is NO useful, *reliable* concept of "default" font for a page. |
// The fact that "default" (reuse font used before) font worked before in basic cases is an accident |
// - readers dealing smartly with brokenness of jsPDF's markup. |
|
var curY; |
|
if (todo){ |
//this.addPage(); |
//this._runningPageHeight += y - (activeFontSize * 1.7); |
//curY = f2(activeFontSize * 1.7); |
} else { |
curY = f2(y); |
} |
//curY = f2(((y - this._runningPageHeight)); |
|
// if (curY < 0){ |
// console.log('auto page break'); |
// this.addPage(); |
// this._runningPageHeight = y - (activeFontSize * 1.7); |
// curY = f2(activeFontSize * 1.7); |
// } |
|
var translate = new Matrix(1, 0, 0, -1, x, curY); |
transform = matrixMult(translate, transform); |
var position = transform.toString() + " Tm"; |
|
out( |
'BT\n' + |
(activeFontSize * lineHeightProportion) + ' TL\n' + // line spacing |
strokeOption +// stroke option |
position + '\n(' + |
text + |
') Tj\nET'); |
|
if (todo) { |
//this.text( todo, x, activeFontSize * 1.7); |
//this.text( todo, x, this._runningPageHeight + (activeFontSize * 1.7)); |
this.text( todo, x, y);// + (activeFontSize * 1.7)); |
} |
|
return this; |
}; |
|
|
API.lstext = function(text, x, y, spacing) { |
for (var i = 0, len = text.length ; i < len; i++, x += spacing) this.text(text[i], x, y); |
}; |
|
/** |
* Draw a line |
* @param {number} x1 |
* @param {number} y1 |
* @param {number} x2 |
* @param {number} y2 |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name line |
*/ |
API.line = function(x1, y1, x2, y2) { |
return this.lines([[x2 - x1, y2 - y1]], x1, y1, [1, 1], "D"); |
}; |
|
API.clip = function() { |
// By patrick-roberts, github.com/MrRio/jsPDF/issues/328 |
// Call .clip() after calling .rect() with a style argument of null |
out('W'); // clip |
out('S'); // stroke path; necessary for clip to work |
}; |
|
|
/** |
* @typedef {Object} PatternData |
* {Matrix|undefined} matrix |
* {Number|undefined} xStep |
* {Number|undefined} yStep |
* {Array.<Number>|undefined} boundingBox |
*/ |
|
/** |
* Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates. |
* All data points in `lines` are relative to last line origin. |
* `x`, `y` become x1,y1 for first line / curve in the set. |
* For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point. |
* 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. |
* |
* @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line |
* @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves). |
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @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. |
* @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. |
* @param {Boolean} closed If true, the path is closed with a straight line from the end of the last curve to the starting point. |
* @param {String} patternKey The pattern key for the pattern that should be used to fill the path. |
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that |
* will modify the pattern on use. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name lines |
*/ |
API.lines = function(lines, x, y, scale, style, closed, patternKey, patternData) { |
var scalex,scaley,i,l,leg,x2,y2,x3,y3,x4,y4; |
|
// Pre-August-2012 the order of arguments was function(x, y, lines, scale, style) |
// in effort to make all calls have similar signature like |
// function(content, coordinateX, coordinateY , miscellaneous) |
// this method had its args flipped. |
// code below allows backward compatibility with old arg order. |
if (typeof lines === 'number') { |
var tmp = y; |
y = x; |
x = lines; |
lines = tmp; |
} |
|
scale = scale || [1, 1]; |
|
// starting point |
out(f3(x) + ' ' + f3(y) + ' m '); |
|
scalex = scale[0]; |
scaley = scale[1]; |
l = lines.length; |
//, x2, y2 // bezier only. In page default measurement "units", *after* scaling |
//, x3, y3 // bezier only. In page default measurement "units", *after* scaling |
// ending point for all, lines and bezier. . In page default measurement "units", *after* scaling |
x4 = x; // last / ending point = starting point for first item. |
y4 = y; // last / ending point = starting point for first item. |
|
for (i = 0; i < l; i++) { |
leg = lines[i]; |
if (leg.length === 2) { |
// simple line |
x4 = leg[0] * scalex + x4; // here last x4 was prior ending point |
y4 = leg[1] * scaley + y4; // here last y4 was prior ending point |
out(f3(x4) + ' ' + f3(y4) + ' l'); |
} else { |
// bezier curve |
x2 = leg[0] * scalex + x4; // here last x4 is prior ending point |
y2 = leg[1] * scaley + y4; // here last y4 is prior ending point |
x3 = leg[2] * scalex + x4; // here last x4 is prior ending point |
y3 = leg[3] * scaley + y4; // here last y4 is prior ending point |
x4 = leg[4] * scalex + x4; // here last x4 was prior ending point |
y4 = leg[5] * scaley + y4; // here last y4 was prior ending point |
out( |
f3(x2) + ' ' + |
f3(y2) + ' ' + |
f3(x3) + ' ' + |
f3(y3) + ' ' + |
f3(x4) + ' ' + |
f3(y4) + ' c'); |
} |
} |
|
if (closed) { |
out('h'); |
} |
|
putStyle(style, patternKey, patternData); |
|
return this; |
}; |
|
/** |
* Similar to {@link API.lines} but all coordinates are interpreted as absolute coordinates instead of relative. |
* @param {Array<Object>} lines An array of {op: operator, c: coordinates} object, where op is one of "m" (move to), "l" (line to) |
* "c" (cubic bezier curve) and "h" (close (sub)path)). c is an array of coordinates. "m" and "l" expect two, "c" |
* six and "h" an empty array (or undefined). |
* @param {String} style The style |
* @param {String} patternKey The pattern key for the pattern that should be used to fill the path. |
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that |
* will modify the pattern on use. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name path |
*/ |
API.path = function (lines, style, patternKey, patternData) { |
|
for (var i = 0; i < lines.length; i++) { |
var leg = lines[i]; |
var coords = leg.c; |
switch (leg.op) { |
case "m": |
// move |
out(f3(coords[0]) + ' ' + f3(coords[1]) + ' m'); |
break; |
case "l": |
// simple line |
out(f3(coords[0]) + ' ' + f3(coords[1]) + ' l'); |
break; |
case "c": |
// bezier curve |
out([ |
f3(coords[0]), |
f3(coords[1]), |
f3(coords[2]), |
f3(coords[3]), |
f3(coords[4]), |
f3(coords[5]), |
"c" |
].join(" ")); |
break; |
case "h": |
// close path |
out("h"); |
} |
|
|
} |
|
putStyle(style, patternKey, patternData); |
|
return this; |
}; |
|
/** |
* Adds a rectangle to PDF |
* |
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @param {Number} w Width (in units declared at inception of PDF document) |
* @param {Number} h Height (in units declared at inception of PDF document) |
* @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. |
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive. |
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that |
* will modify the pattern on use. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name rect |
*/ |
API.rect = function(x, y, w, h, style, patternKey, patternData) { |
out([ |
f2(x), |
f2(y), |
f2(w), |
f2(-h), |
're' |
].join(' ')); |
|
putStyle(style, patternKey, patternData); |
|
return this; |
}; |
|
/** |
* Adds a triangle to PDF |
* |
* @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @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. |
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive. |
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that |
* will modify the pattern on use. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name triangle |
*/ |
API.triangle = function(x1, y1, x2, y2, x3, y3, style, patternKey, patternData) { |
this.lines( |
[ |
[x2 - x1, y2 - y1], // vector to point 2 |
[x3 - x2, y3 - y2], // vector to point 3 |
[x1 - x3, y1 - y3]// closing vector back to point 1 |
], |
x1, |
y1, // start of path |
[1, 1], |
style, |
true, |
patternKey, |
patternData |
); |
return this; |
}; |
|
/** |
* Adds a rectangle with rounded corners to PDF |
* |
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @param {Number} w Width (in units declared at inception of PDF document) |
* @param {Number} h Height (in units declared at inception of PDF document) |
* @param {Number} rx Radius along x axis (in units declared at inception of PDF document) |
* @param {Number} ry Radius along y axis (in units declared at inception of PDF document) |
* @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. |
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive. |
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that |
* will modify the pattern on use. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name roundedRect |
*/ |
API.roundedRect = function(x, y, w, h, rx, ry, style, patternKey, patternData) { |
var MyArc = 4 / 3 * (Math.SQRT2 - 1); |
|
rx = Math.min(rx, w * 0.5); |
ry = Math.min(ry, h * 0.5); |
|
this.lines( |
[ |
[(w - 2 * rx), 0], |
[(rx * MyArc), 0, rx, ry - (ry * MyArc), rx, ry], |
[0, (h - 2 * ry)], |
[0, (ry * MyArc), - (rx * MyArc), ry, -rx, ry], |
[(-w + 2 * rx), 0], |
[ - (rx * MyArc), 0, -rx, - (ry * MyArc), -rx, -ry], |
[0, (-h + 2 * ry)], |
[0, - (ry * MyArc), (rx * MyArc), -ry, rx, -ry] |
], |
x + rx, |
y, // start of path |
[1, 1], |
style, |
true, |
patternKey, |
patternData |
); |
return this; |
}; |
|
/** |
* Adds an ellipse to PDF |
* |
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @param {Number} rx Radius along x axis (in units declared at inception of PDF document) |
* @param {Number} ry Radius along y axis (in units declared at inception of PDF document) |
* @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. |
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive. |
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that |
* will modify the pattern on use. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name ellipse |
*/ |
API.ellipse = function(x, y, rx, ry, style, patternKey, patternData) { |
var lx = 4 / 3 * (Math.SQRT2 - 1) * rx, |
ly = 4 / 3 * (Math.SQRT2 - 1) * ry; |
|
out([ |
f2(x + rx), |
f2(y), |
'm', |
f2(x + rx), |
f2(y - ly), |
f2(x + lx), |
f2(y - ry), |
f2(x), |
f2(y - ry), |
'c' |
].join(' ')); |
out([ |
f2(x - lx), |
f2(y - ry), |
f2(x - rx), |
f2(y - ly), |
f2(x - rx), |
f2(y), |
'c' |
].join(' ')); |
out([ |
f2(x - rx), |
f2(y + ly), |
f2(x - lx), |
f2(y + ry), |
f2(x), |
f2(y + ry), |
'c' |
].join(' ')); |
out([ |
f2(x + lx), |
f2(y + ry), |
f2(x + rx), |
f2(y + ly), |
f2(x + rx), |
f2(y), |
'c' |
].join(' ')); |
|
putStyle(style, patternKey, patternData); |
|
return this; |
}; |
|
/** |
* Adds an circle to PDF |
* |
* @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page |
* @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page |
* @param {Number} r Radius (in units declared at inception of PDF document) |
* @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. |
* @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive. |
* @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that |
* will modify the pattern on use. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name circle |
*/ |
API.circle = function(x, y, r, style, patternKey, patternData) { |
return this.ellipse(x, y, r, r, style, patternKey, patternData); |
}; |
|
/** |
* Adds a properties to the PDF document |
* |
* @param {Object} properties A property_name-to-property_value object structure. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setProperties |
*/ |
API.setProperties = function(properties) { |
// copying only those properties we can render. |
for (var property in documentProperties) { |
if (documentProperties.hasOwnProperty(property) && properties[property]) { |
documentProperties[property] = properties[property]; |
} |
} |
return this; |
}; |
|
/** |
* Sets font size for upcoming text elements. |
* |
* @param {Number} size Font size in points. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setFontSize |
*/ |
API.setFontSize = function(size) { |
activeFontSize = size; |
out("/" + activeFontKey + " " + activeFontSize + " Tf"); |
return this; |
}; |
|
API.getFontSize = function () { |
return activeFontSize; |
}; |
|
/** |
* Sets text font face, variant for upcoming text elements. |
* See output of jsPDF.getFontList() for possible font names, styles. |
* |
* @param {String} fontName Font name or family. Example: "times" |
* @param {String} fontStyle Font style or variant. Example: "italic" |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setFont |
*/ |
API.setFont = function(fontName, fontStyle) { |
activeFontKey = getFont(fontName, fontStyle); |
// if font is not found, the above line blows up and we never go further |
out("/" + activeFontKey + " " + activeFontSize + " Tf"); |
return this; |
}; |
|
/** |
* Switches font style or variant for upcoming text elements, |
* while keeping the font face or family same. |
* See output of jsPDF.getFontList() for possible font names, styles. |
* |
* @param {String} style Font style or variant. Example: "italic" |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setFontStyle |
*/ |
API.setFontStyle = API.setFontType = function(style) { |
activeFontKey = getFont(undefined, style); |
// if font is not found, the above line blows up and we never go further |
return this; |
}; |
|
/** |
* Returns an object - a tree of fontName to fontStyle relationships available to |
* active PDF document. |
* |
* @public |
* @function |
* @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... } |
* @methodOf jsPDF# |
* @name getFontList |
*/ |
API.getFontList = function() { |
// TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added. |
var list = {},fontName,fontStyle,tmp; |
|
for (fontName in fontmap) { |
if (fontmap.hasOwnProperty(fontName)) { |
list[fontName] = tmp = []; |
for (fontStyle in fontmap[fontName]) { |
if (fontmap[fontName].hasOwnProperty(fontStyle)) { |
tmp.push(fontStyle); |
} |
} |
} |
} |
|
return list; |
}; |
|
/** |
* Add a custom font. |
* |
* @param {String} postScriptName name of the Font. Example: "Menlo-Regular" |
* @param {String} fontName of font-family from @font-face definition. Example: "Menlo Regular" |
* @param {String} fontStyle style. Example: "normal" |
* @function |
* @returns the {fontKey} (same as the internal method) |
* @methodOf jsPDF# |
* @name addFont |
*/ |
API.addFont = function(postScriptName, fontName, fontStyle) { |
addFont(postScriptName, fontName, fontStyle, 'StandardEncoding'); |
}; |
|
/** |
* Sets line width for upcoming lines. |
* |
* @param {Number} width Line width (in units declared at inception of PDF document) |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setLineWidth |
*/ |
API.setLineWidth = function(width) { |
out(width.toFixed(2) + ' w'); |
return this; |
}; |
|
/** |
* Sets the stroke color for upcoming elements. |
* |
* Depending on the number of arguments given, Gray, RGB, or CMYK |
* color space is implied. |
* |
* When only ch1 is given, "Gray" color space is implied and it |
* must be a value in the range from 0.00 (solid black) to to 1.00 (white) |
* if values are communicated as String types, or in range from 0 (black) |
* to 255 (white) if communicated as Number type. |
* The RGB-like 0-255 range is provided for backward compatibility. |
* |
* When only ch1,ch2,ch3 are given, "RGB" color space is implied and each |
* value must be in the range from 0.00 (minimum intensity) to to 1.00 |
* (max intensity) if values are communicated as String types, or |
* from 0 (min intensity) to to 255 (max intensity) if values are communicated |
* as Number types. |
* The RGB-like 0-255 range is provided for backward compatibility. |
* |
* When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each |
* value must be a in the range from 0.00 (0% concentration) to to |
* 1.00 (100% concentration) |
* |
* Because JavaScript treats fixed point numbers badly (rounds to |
* floating point nearest to binary representation) it is highly advised to |
* communicate the fractional numbers as String types, not JavaScript Number type. |
* |
* @param {Number|String} ch1 Color channel value |
* @param {Number|String} ch2 Color channel value |
* @param {Number|String} ch3 Color channel value |
* @param {Number|String} ch4 Color channel value |
* |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setDrawColor |
*/ |
API.setDrawColor = function(ch1, ch2, ch3, ch4) { |
var color; |
if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) { |
// Gray color space. |
if (typeof ch1 === 'string') { |
color = ch1 + ' G'; |
} else { |
color = f2(ch1 / 255) + ' G'; |
} |
} else if (ch4 === undefined) { |
// RGB |
if (typeof ch1 === 'string') { |
color = [ch1, ch2, ch3, 'RG'].join(' '); |
} else { |
color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(' '); |
} |
} else { |
// CMYK |
if (typeof ch1 === 'string') { |
color = [ch1, ch2, ch3, ch4, 'K'].join(' '); |
} else { |
color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' '); |
} |
} |
|
out(color); |
return this; |
}; |
|
/** |
* Sets the fill color for upcoming elements. |
* |
* Depending on the number of arguments given, Gray, RGB, or CMYK |
* color space is implied. |
* |
* When only ch1 is given, "Gray" color space is implied and it |
* must be a value in the range from 0.00 (solid black) to to 1.00 (white) |
* if values are communicated as String types, or in range from 0 (black) |
* to 255 (white) if communicated as Number type. |
* The RGB-like 0-255 range is provided for backward compatibility. |
* |
* When only ch1,ch2,ch3 are given, "RGB" color space is implied and each |
* value must be in the range from 0.00 (minimum intensity) to to 1.00 |
* (max intensity) if values are communicated as String types, or |
* from 0 (min intensity) to to 255 (max intensity) if values are communicated |
* as Number types. |
* The RGB-like 0-255 range is provided for backward compatibility. |
* |
* When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each |
* value must be a in the range from 0.00 (0% concentration) to to |
* 1.00 (100% concentration) |
* |
* Because JavaScript treats fixed point numbers badly (rounds to |
* floating point nearest to binary representation) it is highly advised to |
* communicate the fractional numbers as String types, not JavaScript Number type. |
* |
* @param {Number|String} ch1 Color channel value |
* @param {Number|String} ch2 Color channel value |
* @param {Number|String} ch3 Color channel value |
* @param {Number|String} ch4 Color channel value |
* |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setFillColor |
*/ |
API.setFillColor = function(ch1, ch2, ch3, ch4) { |
var color; |
|
if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) { |
// Gray color space. |
if (typeof ch1 === 'string') { |
color = ch1 + ' g'; |
} else { |
color = f2(ch1 / 255) + ' g'; |
} |
} else if (ch4 === undefined || typeof ch4 === 'object') { |
// RGB |
if (typeof ch1 === 'string') { |
color = [ch1, ch2, ch3, 'rg'].join(' '); |
} else { |
color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' '); |
} |
if (ch4 && ch4.a === 0){ |
//TODO Implement transparency. |
//WORKAROUND use white for now |
color = ['255', '255', '255', 'rg'].join(' '); |
} |
} else { |
// CMYK |
if (typeof ch1 === 'string') { |
color = [ch1, ch2, ch3, ch4, 'k'].join(' '); |
} else { |
color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' '); |
} |
} |
|
out(color); |
return this; |
}; |
|
/** |
* Sets the text color for upcoming elements. |
* If only one, first argument is given, |
* treats the value as gray-scale color value. |
* |
* @param {Number} r Red channel color value in range 0-255 or {String} r color value in hexadecimal, example: '#FFFFFF' |
* @param {Number} g Green channel color value in range 0-255 |
* @param {Number} b Blue channel color value in range 0-255 |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setTextColor |
*/ |
API.setTextColor = function(r, g, b) { |
if ((typeof r === 'string') && /^#[0-9A-Fa-f]{6}$/.test(r)) { |
var hex = parseInt(r.substr(1), 16); |
r = (hex >> 16) & 255; |
g = (hex >> 8) & 255; |
b = (hex & 255); |
} |
|
if ((r === 0 && g === 0 && b === 0) || (typeof g === 'undefined')) { |
textColor = f3(r / 255) + ' g'; |
} else { |
textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' '); |
} |
|
out(textColor); |
|
return this; |
}; |
|
/** |
* Sets a either previously added {@link GState} (via {@link addGState}) or a new {@link GState}. |
* @param {String|GState} gState If type is string, a previously added GState is used, if type is GState |
* it will be added before use. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setGState |
*/ |
API.setGState = function (gState) { |
if (typeof gState === "string") { |
gState = gStates[gStatesMap[gState]]; |
} else { |
gState = addGState(null, gState); |
} |
|
if (!gState.equals(activeGState)) { |
out("/" + gState.id + " gs"); |
activeGState = gState; |
} |
}; |
|
/** |
* Is an Object providing a mapping from human-readable to |
* integer flag values designating the varieties of line cap |
* and join styles. |
* |
* @returns {Object} |
* @fieldOf jsPDF# |
* @name CapJoinStyles |
*/ |
API.CapJoinStyles = { |
0 : 0, |
'butt' : 0, |
'but' : 0, |
'miter' : 0, |
1 : 1, |
'round' : 1, |
'rounded' : 1, |
'circle' : 1, |
2 : 2, |
'projecting' : 2, |
'project' : 2, |
'square' : 2, |
'bevel' : 2 |
}; |
|
/** |
* Sets the line cap styles |
* See {jsPDF.CapJoinStyles} for variants |
* |
* @param {String|Number} style A string or number identifying the type of line cap |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setLineCap |
*/ |
API.setLineCap = function(style) { |
var id = this.CapJoinStyles[style]; |
if (id === undefined) { |
throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles"); |
} |
lineCapID = id; |
out(id + ' J'); |
|
return this; |
}; |
|
/** |
* Sets the line join styles |
* See {jsPDF.CapJoinStyles} for variants |
* |
* @param {String|Number} style A string or number identifying the type of line join |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setLineJoin |
*/ |
API.setLineJoin = function(style) { |
var id = this.CapJoinStyles[style]; |
if (id === undefined) { |
throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles"); |
} |
lineJoinID = id; |
out(id + ' j'); |
|
return this; |
}; |
|
/** |
* Sets the miter limit. |
* @param {number} miterLimit |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setMiterLimit |
*/ |
API.setLineMiterLimit = function (miterLimit) { |
out(f2(miterLimit) + " M"); |
|
return this; |
}; |
|
/** |
* Sets the line dash pattern. |
* @param {Array<number>} array An array containing 0-2 numbers. The first number sets the length of the |
* dashes, the second number the length of the gaps. If the second number is missing, the gaps are considered |
* to be as long as the dashes. An empty array means solid, unbroken lines. |
* @param phase The phase lines start with. |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name setLineDashPattern |
*/ |
API.setLineDashPattern = function (array, phase) { |
out([ |
"[" + (array[0] !== undefined ? array[0] : ""), |
(array[1] !== undefined ? array[1] : "" ) + "]", |
phase, |
"d" |
].join(" ")); |
|
return this; |
}; |
|
// Output is both an internal (for plugins) and external function |
API.output = output; |
|
/** |
* Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf') |
* @param {String} filename The filename including extension. |
* |
* @function |
* @returns {jsPDF} |
* @methodOf jsPDF# |
* @name save |
*/ |
API.save = function(filename) { |
API.output('save', filename); |
}; |
|
// applying plugins (more methods) ON TOP of built-in API. |
// this is intentional as we allow plugins to override |
// built-ins |
for (var plugin in jsPDF.API) { |
if (jsPDF.API.hasOwnProperty(plugin)) { |
if (plugin === 'events' && jsPDF.API.events.length) { |
(function(events, newEvents) { |
|
// jsPDF.API.events is a JS Array of Arrays |
// where each Array is a pair of event name, handler |
// Events were added by plugins to the jsPDF instantiator. |
// These are always added to the new instance and some ran |
// during instantiation. |
var eventname,handler_and_args,i; |
|
for (i = newEvents.length - 1; i !== -1; i--) { |
// subscribe takes 3 args: 'topic', function, runonce_flag |
// if undefined, runonce is false. |
// users can attach callback directly, |
// or they can attach an array with [callback, runonce_flag] |
// that's what the "apply" magic is for below. |
eventname = newEvents[i][0]; |
handler_and_args = newEvents[i][1]; |
events.subscribe.apply( |
events, |
[eventname].concat( |
typeof handler_and_args === 'function' ? |
[handler_and_args] : handler_and_args)); |
} |
}(events, jsPDF.API.events)); |
} else { |
API[plugin] = jsPDF.API[plugin]; |
} |
} |
} |
|
////////////////////////////////////////////////////// |
// continuing initialization of jsPDF Document object |
////////////////////////////////////////////////////// |
// Add the first page automatically |
addFonts(); |
activeFontKey = 'F1'; |
_addPage(format, orientation); |
|
events.publish('initialized'); |
return API; |
} |
|
/** |
* jsPDF.API is a STATIC property of jsPDF class. |
* jsPDF.API is an object you can add methods and properties to. |
* The methods / properties you add will show up in new jsPDF objects. |
* |
* One property is prepopulated. It is the 'events' Object. Plugin authors can add topics, |
* callbacks to this object. These will be reassigned to all new instances of jsPDF. |
* Examples: |
* jsPDF.API.events['initialized'] = function(){ 'this' is API object } |
* jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object } |
* |
* @static |
* @public |
* @memberOf jsPDF |
* @name API |
* |
* @example |
* jsPDF.API.mymethod = function(){ |
* // 'this' will be ref to internal API object. see jsPDF source |
* // , so you can refer to built-in methods like so: |
* // this.line(....) |
* // this.text(....) |
* } |
* var pdfdoc = new jsPDF() |
* pdfdoc.mymethod() // <- !!!!!! |
*/ |
jsPDF.API = {events:[]}; |
jsPDF.version = "1.0.0-trunk"; |
|
if (typeof define === 'function' && define.amd) { |
define('jsPDF', function() { |
return jsPDF; |
}); |
} else if (typeof module !== 'undefined' && module.exports) { |
module.exports = jsPDF; |
} else { |
global.jsPDF = jsPDF; |
} |
return jsPDF; |
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this)); |