+
+ options
+
+
+
+
+
+Object
+
+
+
+
+
+
+
+
+ <optional>
+
+
+
+
+
+
+
+
+
+
+ The options
+ Properties
+
+
+
+
+
+
+ Name
+
+
+ Type
+
+
+ Attributes
+
+
+
+
+ Description
+
+
+
+
+
+
+
+
+ color
+
+
+
+
+
+string
+|
+
+Array.<number>
+
+
+
+
+
+
+
+
+ <optional>
+
+
+
+
+
+
+
+
+
+
+ HexColor, PercentColor or DecimalColor
+
+
+
+
+
+
+ stroke
+string
+|
+
Array.<number>
@@ -6555,6 +6940,8 @@ Parameters:
+ <optional>
+
@@ -6564,20 +6951,23 @@ Parameters:
- The array of coordinate [[x,y], [m,n]]
+ HexColor, PercentColor or DecimalColor
- options
+ fill
-Object
+string
+|
+
+Array.<number>
@@ -6597,44 +6987,53 @@ Parameters:
- The options
- Properties
+ HexColor, PercentColor or DecimalColor
+
+
+
+
+
+
+ lineWidth
+
+
+
+
+number
-
-
-
-
- Name
-
- Type
+
+
-
- Attributes
-
+
+
+
+ <optional>
+
-
+
- Description
-
-
+
+
+
+
+
+
+ The line width
+
-
- color
+ opacity
-string
-|
-
-Array.<number>
+number
@@ -6654,22 +7053,19 @@ Properties
- HexColor, PercentColor or DecimalColor
+ The opacity
- stroke
+ dash
-string
-|
-
Array.<number>
@@ -6690,23 +7086,20 @@ Properties
- HexColor, PercentColor or DecimalColor
+ The dash pattern [dashSize, gapSize] or [dashAndGapSize]
- fill
+ dashPhase
-string
-|
-
-Array.<number>
+number
@@ -6726,14 +7119,14 @@ Properties
- HexColor, PercentColor or DecimalColor
+ distance into dash pattern at which to start dash (default: 0, immediately)
- lineWidth
+ rotation
@@ -6759,20 +7152,20 @@ Properties
- The line width
+ Accept: +/- 0 through 360. Default: 0
- opacity
+ rotationOrigin
-number
+Array.<number>
@@ -6792,20 +7185,20 @@ Properties
- The opacity
+ [originX, originY] Default: x, y
- dash
+ lineCap
-Array.<number>
+string
@@ -6825,20 +7218,20 @@ Properties
- The dash style [number, number]
+ open line end style, 'butt', 'round', or 'square' (default: 'round')
- rotation
+ lineJoin
-number
+string
@@ -6858,20 +7251,20 @@ Properties
- Accept: +/- 0 through 360. Default: 0
+ joined line end style, 'miter', 'round', or 'bevel' (default: 'round')
- rotationOrigin
+ miterLimit
-Array.<number>
+number
@@ -6891,7 +7284,7 @@ Properties
- [originX, originY] Default: x, y
+ limit at which 'miter' joins are forced to 'bevel' (default: 1.414)
@@ -8043,6 +8436,8 @@ Properties
+ Default
+
Description
@@ -8081,6 +8476,10 @@ Properties
+
+
+
+
Text color (HexColor, PercentColor or DecimalColor)
@@ -8114,6 +8513,12 @@ Properties
+
+
+ 1
+
+
+
opacity
@@ -8147,8 +8552,14 @@ Properties
+
+
+ 0
+
+
+
- Accept: +/- 0 through 360. Default: 0
+ Accept: +/- 0 through 360.
@@ -8180,8 +8591,14 @@ Properties
+
+
+ [x,y]
+
+
+
- [originX, originY] Default: the text coordinate
+ [originX, originY]
@@ -8213,6 +8630,12 @@ Properties
+
+
+ Helvetica
+
+
+
The font. 'Arial', 'Helvetica'...
@@ -8246,8 +8669,14 @@ Properties
+
+
+ 14
+
+
+
- The line width
+ The font size
@@ -8279,6 +8708,12 @@ Properties
+
+
+ 'left top'
+
+
+
The alignment. 'center center'...
@@ -8315,6 +8750,10 @@ Properties
+
+
+
+
Text markup annotation.
@@ -8351,6 +8790,10 @@ Properties
+
+
+
+
Text markup annotation.
@@ -8387,6 +8830,10 @@ Properties
+
+
+
+
Text markup annotation.
@@ -8420,6 +8867,10 @@ Properties
+
+
+
+
Text Box to fit in.
Properties
@@ -8439,6 +8890,8 @@ Properties
+ Default
+
Description
@@ -8474,6 +8927,12 @@ Properties
+
+
+ 100
+
+
+
Text Box width
@@ -8507,6 +8966,10 @@ Properties
+
+
+
+
Text Box fixed height
@@ -8540,6 +9003,12 @@ Properties
+
+
+ 0
+
+
+
Text Box minimum height
@@ -8576,6 +9045,12 @@ Properties
+
+
+ 0
+
+
+
Text Box padding, [top, right, bottom, left]
@@ -8609,12 +9084,57 @@ Properties
+
+
+ 0
+
+
+
Text Box line height
+
+
+ wrap
+
+
+
+
+
+string
+
+
+
+
+
+
+
+
+ <optional>
+
+
+
+
+
+
+
+
+
+
+
+ 'auto'
+
+
+
+
+ Text wrapping mechanism, 'auto', 'clip', 'trim'.
+
+
+
+
textAlign
@@ -8642,8 +9162,15 @@ Properties
+
+
+ 'left top'
+
+
+
- Text alignment inside text box
+ Alignment inside text box, specified as 'horizontal vertical',
+where horizontal is one of: 'left', 'center', 'right', 'justify' and veritical is one of: 'top', 'center', 'bottom'.
@@ -8675,6 +9202,10 @@ Properties
+
+
+
+
Text Box styles
Properties
@@ -8694,6 +9225,8 @@ Properties
+ Default
+
Description
@@ -8729,6 +9262,12 @@ Properties
+
+
+ 2
+
+
+
Text Box border width
@@ -8765,6 +9304,10 @@ Properties
+
+
+
+
Text Box border color (HexColor, PercentColor or DecimalColor)
@@ -8798,6 +9341,12 @@ Properties
+
+
+ []
+
+
+
Text Box border border dash style [number, number]
@@ -8834,6 +9383,10 @@ Properties
+
+
+
+
Text Box border background color (HexColor, PercentColor or DecimalColor)
@@ -8867,6 +9420,12 @@ Properties
+
+
+ 1
+
+
+
Text Box border background opacity
@@ -9062,7 +9621,7 @@ Parameters:
- Documentation generated by JSDoc 3.6.3 on Wed Oct 02 2019 12:00:26 GMT-0700 (Pacific Daylight Time) using the docdash theme.
+ Documentation generated by JSDoc 3.5.5 on Fri Nov 01 2019 20:41:12 GMT-0400 (Eastern Daylight Time) using the docdash theme.
diff --git a/docs/Recipe.js.html b/docs/Recipe.js.html
index dcb496d..dd52fa6 100644
--- a/docs/Recipe.js.html
+++ b/docs/Recipe.js.html
@@ -37,277 +37,277 @@ Recipe.js
- const hummus = require('hummus');
-const path = require('path');
-const fs = require('fs');
-const streams = require('memory-streams');
-/**
- * @name Recipe
- * @desc Create a pdfDoc
- * @namespace
- * @constructor
- * @param {string} src - The file path or Buffer of the src file.
- * @param {string} output - The path of the output file.
- * @param {Object} [options] - The options for pdfDoc
- * @param {number} [options.version] - The pdf version
- * @param {string} [options.author] - The author
- * @param {string} [options.title] - The title
- * @param {string} [options.subject] - The subject
- * @param {string} [options.colorspace] - The default colorspace: rgb, cmyk, gray
- * @param {string[]} [options.keywords] - The array of keywords
- * @param {string} [options.password] - permission password
- * @param {string} [options.userPassword] - this 'view' password also enables encryption
- * @param {string} [options.ownerPassword] - this allows owner to 'edit' file
- * @param {string} [options.userProtectionFlag] - encryption security level (see permissions)
- * @param {string|string[]} [options.fontSrcPath] - directory location(s) of additional fonts
- */
-class Recipe {
- constructor(src, output, options = {}) {
- this.src = src;
- // detect the src is Buffer or not
- this.isBufferSrc = this.src instanceof Buffer;
- this.isNewPDF = (!this.isBufferSrc && src.toLowerCase() === 'new');
- this.encryptOptions = this._getEncryptOptions(options, this.isNewPDF);
- this.options = Object.assign({}, options, this.encryptOptions);
-
- if (this.isBufferSrc) {
- this.outStream = new streams.WritableStream();
- this.output = output;
- } else {
- this.output = output || src;
- if (this.src) {
- this.filename = path.basename(this.src);
- }
- }
- this.hummus = hummus;
- this.logFile = 'hummus-error.log';
-
- this.textMarkupAnnotations = [
- 'Highlight', 'Underline', 'StrikeOut', 'Squiggly'
- ];
-
- this.annotationsToWrite = [];
- this.annotations = [];
- this.vectorsToWrite = [];
-
- this.xObjects = [];
-
- this.needToEncrypt = false;
-
- this.needToInsertPages = false;
-
- this._setParameters(options);
- this._loadFonts(path.join(__dirname, '../fonts'));
- if (options.fontSrcPath) {
- this._loadFonts(options.fontSrcPath);
- }
- this._createWriter();
- }
-
- _createWriter() {
- if (this.isNewPDF) {
- this.writer = hummus.createWriter(this.output,
- Object.assign( {}, this.encryptOptions, {
- version: this._getVersion(this.options.version)
- })
- );
- } else {
- this.read();
- try {
- if (this.isBufferSrc) {
- this.writer = hummus.createWriterToModify(
- new hummus.PDFRStreamForBuffer(this.src),
- new hummus.PDFStreamForResponse(this.outStream),
- Object.assign( {}, this.encryptOptions, {
- log: this.logFile
- })
- );
- } else {
- this.writer = hummus.createWriterToModify(this.src,
- Object.assign( {}, this.encryptOptions, {
- modifiedFilePath: this.output,
- log: this.logFile
- })
- );
- }
- } catch (err) {
- throw new Error(err);
- }
- }
-
- this.info(this.options);
- }
-
- _getVersion(version) {
- const supportedVersions = [
- 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7
- ];
- if (!supportedVersions.includes(version)) {
- version = 1.7;
- }
- version = hummus[`ePDFVersion${version * 10}`];
-
- return version;
- }
-
- get position() {
- const {
- ox,
- oy
- } = this._reverseCoordinate(this._position.x, this._position.y);
- return {
- x: ox,
- y: oy
- };
- }
-
- read(inSrc) {
- const isForExternal = (inSrc) ? true : false;
- try {
- let src = (isForExternal) ? inSrc : this.src;
- if (this.isBufferSrc) {
- src = new hummus.PDFRStreamForBuffer(this.src);
- }
- const pdfReader = hummus.createReader(src, this.encryptOptions);
- const pages = pdfReader.getPagesCount();
- if (pages == 0) {
- // broken or modify password protected
- throw 'HummusJS: Unable to read/edit PDF file (pages=0)';
- }
- const metadata = {
- pages
- };
- for (var i = 0; i < pages; i++) {
- const info = pdfReader.parsePage(i);
- const dimensions = info.getMediaBox();
- const rotate = info.getRotate();
-
- let layout,
- width,
- height,
- pageSize;
- let side1 = Math.abs(dimensions[2] - dimensions[0]);
- let side2 = Math.abs(dimensions[3] - dimensions[1]);
- if (side1 > side2 && rotate % 180 === 0) {
- layout = 'landscape';
- } else
- if (side1 < side2 && rotate % 180 !== 0) {
- layout = 'landscape';
- } else {
- layout = 'portrait';
- }
-
- if (layout === 'landscape') {
- width = (side1 > side2) ? side1 : side2;
- height = (side1 > side2) ? side2 : side1;
- } else {
- width = (side1 > side2) ? side2 : side1;
- height = (side1 > side2) ? side1 : side2;
- }
-
- pageSize = [width, height].sort((a, b) => {
- return (a > b) ? 1 : -1;
- });
-
- const page = {
- pageNumber: i + 1,
- mediaBox: dimensions,
- layout,
- rotate,
- width,
- height,
- size: pageSize,
- // usually 0
- offsetX: dimensions[0],
- offsetY: dimensions[1]
- };
- metadata[page.pageNumber] = page;
- }
- if (!isForExternal) {
- this.pdfReader = pdfReader;
- this.metadata = metadata;
- }
- return metadata;
- } catch (err) {
- throw new Error(err);
- }
- }
-
- /**
- * End the pdfDoc
- * @function
- * @memberof Recipe
- * @param {function} callback - The callback function.
- */
- endPDF(callback) {
- this._writeInfo();
- this.writer.end();
- // This is a temporary work around for copying context will overwrite the current one
- // write annotations at the end.
- if (
- (this.annotations && this.annotations.length > 0) ||
- (this.annotationsToWrite && this.annotationsToWrite.length > 0)
- ) {
- if (this.isBufferSrc) {
- const oldStream = this.outStream;
- this.outStream = new streams.WritableStream();
-
- this.writer = hummus.createWriterToModify(
- new hummus.PDFRStreamForBuffer(oldStream.toBuffer()),
- new hummus.PDFStreamForResponse(this.outStream),
- Object.assign( {}, this.encryptOptions, {
- log: this.logFile
- })
- );
- } else {
- this.writer = hummus.createWriterToModify(this.output,
- Object.assign( {}, this.encryptOptions, {
- modifiedFilePath: this.output,
- log: this.logFile
- })
- );
- }
-
- this._writeAnnotations();
- this._writeInfo();
- this.writer.end();
- }
- if (this.needToInsertPages) {
- if (this.isBufferSrc) {
- // eslint-disable-next-line no-console
- console.log('Feature: Inserting Pages is not supported in Buffer Mode yet.');
- } else {
- this._insertPages();
- }
- }
- if (this.needToEncrypt) {
- if (this.isBufferSrc) {
- // eslint-disable-next-line no-console
- console.log('Feature: Encryption is not supported in Buffer Mode yet.');
- } else {
- this._encrypt();
- }
- }
-
- if (this.isBufferSrc && this.output) {
- fs.writeFileSync(this.output, this.outStream.toBuffer());
- }
-
- if (callback) {
- if (this.isBufferSrc) {
- if (this.output) {
- return callback(this.output);
- } else {
- return callback(this.outStream.toBuffer());
- }
- } else {
- return callback();
- }
- }
- }
-}
-
-module.exports = Recipe;
+ const hummus = require('hummus');
+const path = require('path');
+const fs = require('fs');
+const streams = require('memory-streams');
+/**
+ * @name Recipe
+ * @desc Create a pdfDoc
+ * @namespace
+ * @constructor
+ * @param {string} src - The file path or Buffer of the src file.
+ * @param {string} output - The path of the output file.
+ * @param {Object} [options] - The options for pdfDoc
+ * @param {number} [options.version] - The pdf version
+ * @param {string} [options.author] - The author
+ * @param {string} [options.title] - The title
+ * @param {string} [options.subject] - The subject
+ * @param {string} [options.colorspace] - The default colorspace: rgb, cmyk, gray
+ * @param {string[]} [options.keywords] - The array of keywords
+ * @param {string} [options.password] - permission password
+ * @param {string} [options.userPassword] - this 'view' password also enables encryption
+ * @param {string} [options.ownerPassword] - this allows owner to 'edit' file
+ * @param {string} [options.userProtectionFlag] - encryption security level (see permissions)
+ * @param {string|string[]} [options.fontSrcPath] - directory location(s) of additional fonts
+ */
+class Recipe {
+ constructor(src, output, options = {}) {
+ this.src = src;
+ // detect the src is Buffer or not
+ this.isBufferSrc = this.src instanceof Buffer;
+ this.isNewPDF = (!this.isBufferSrc && src.toLowerCase() === 'new');
+ this.encryptOptions = this._getEncryptOptions(options, this.isNewPDF);
+ this.options = Object.assign({}, options, this.encryptOptions);
+
+ if (this.isBufferSrc) {
+ this.outStream = new streams.WritableStream();
+ this.output = output;
+ } else {
+ this.output = output || src;
+ if (this.src) {
+ this.filename = path.basename(this.src);
+ }
+ }
+ this.hummus = hummus;
+ this.logFile = 'hummus-error.log';
+
+ this.textMarkupAnnotations = [
+ 'Highlight', 'Underline', 'StrikeOut', 'Squiggly'
+ ];
+
+ this.annotationsToWrite = [];
+ this.annotations = [];
+ this.vectorsToWrite = [];
+
+ this.xObjects = [];
+
+ this.needToEncrypt = false;
+
+ this.needToInsertPages = false;
+
+ this._setParameters(options);
+ this._loadFonts(path.join(__dirname, '../fonts'));
+ if (options.fontSrcPath) {
+ this._loadFonts(options.fontSrcPath);
+ }
+ this._createWriter();
+ }
+
+ _createWriter() {
+ if (this.isNewPDF) {
+ this.writer = hummus.createWriter(this.output,
+ Object.assign( {}, this.encryptOptions, {
+ version: this._getVersion(this.options.version)
+ })
+ );
+ } else {
+ this.read();
+ try {
+ if (this.isBufferSrc) {
+ this.writer = hummus.createWriterToModify(
+ new hummus.PDFRStreamForBuffer(this.src),
+ new hummus.PDFStreamForResponse(this.outStream),
+ Object.assign( {}, this.encryptOptions, {
+ log: this.logFile
+ })
+ );
+ } else {
+ this.writer = hummus.createWriterToModify(this.src,
+ Object.assign( {}, this.encryptOptions, {
+ modifiedFilePath: this.output,
+ log: this.logFile
+ })
+ );
+ }
+ } catch (err) {
+ throw new Error(err);
+ }
+ }
+
+ this.info(this.options);
+ }
+
+ _getVersion(version) {
+ const supportedVersions = [
+ 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7
+ ];
+ if (!supportedVersions.includes(version)) {
+ version = 1.7;
+ }
+ version = hummus[`ePDFVersion${version * 10}`];
+
+ return version;
+ }
+
+ get position() {
+ const {
+ ox,
+ oy
+ } = this._reverseCoordinate(this._position.x, this._position.y);
+ return {
+ x: ox,
+ y: oy
+ };
+ }
+
+ read(inSrc) {
+ const isForExternal = (inSrc) ? true : false;
+ try {
+ let src = (isForExternal) ? inSrc : this.src;
+ if (this.isBufferSrc) {
+ src = new hummus.PDFRStreamForBuffer(this.src);
+ }
+ const pdfReader = hummus.createReader(src, this.encryptOptions);
+ const pages = pdfReader.getPagesCount();
+ if (pages == 0) {
+ // broken or modify password protected
+ throw 'HummusJS: Unable to read/edit PDF file (pages=0)';
+ }
+ const metadata = {
+ pages
+ };
+ for (var i = 0; i < pages; i++) {
+ const info = pdfReader.parsePage(i);
+ const dimensions = info.getMediaBox();
+ const rotate = info.getRotate();
+
+ let layout,
+ width,
+ height,
+ pageSize;
+ let side1 = Math.abs(dimensions[2] - dimensions[0]);
+ let side2 = Math.abs(dimensions[3] - dimensions[1]);
+ if (side1 > side2 && rotate % 180 === 0) {
+ layout = 'landscape';
+ } else
+ if (side1 < side2 && rotate % 180 !== 0) {
+ layout = 'landscape';
+ } else {
+ layout = 'portrait';
+ }
+
+ if (layout === 'landscape') {
+ width = (side1 > side2) ? side1 : side2;
+ height = (side1 > side2) ? side2 : side1;
+ } else {
+ width = (side1 > side2) ? side2 : side1;
+ height = (side1 > side2) ? side1 : side2;
+ }
+
+ pageSize = [width, height].sort((a, b) => {
+ return (a > b) ? 1 : -1;
+ });
+
+ const page = {
+ pageNumber: i + 1,
+ mediaBox: dimensions,
+ layout,
+ rotate,
+ width,
+ height,
+ size: pageSize,
+ // usually 0
+ offsetX: dimensions[0],
+ offsetY: dimensions[1]
+ };
+ metadata[page.pageNumber] = page;
+ }
+ if (!isForExternal) {
+ this.pdfReader = pdfReader;
+ this.metadata = metadata;
+ }
+ return metadata;
+ } catch (err) {
+ throw new Error(err);
+ }
+ }
+
+ /**
+ * End the pdfDoc
+ * @function
+ * @memberof Recipe
+ * @param {function} callback - The callback function.
+ */
+ endPDF(callback) {
+ this._writeInfo();
+ this.writer.end();
+ // This is a temporary work around for copying context will overwrite the current one
+ // write annotations at the end.
+ if (
+ (this.annotations && this.annotations.length > 0) ||
+ (this.annotationsToWrite && this.annotationsToWrite.length > 0)
+ ) {
+ if (this.isBufferSrc) {
+ const oldStream = this.outStream;
+ this.outStream = new streams.WritableStream();
+
+ this.writer = hummus.createWriterToModify(
+ new hummus.PDFRStreamForBuffer(oldStream.toBuffer()),
+ new hummus.PDFStreamForResponse(this.outStream),
+ Object.assign( {}, this.encryptOptions, {
+ log: this.logFile
+ })
+ );
+ } else {
+ this.writer = hummus.createWriterToModify(this.output,
+ Object.assign( {}, this.encryptOptions, {
+ modifiedFilePath: this.output,
+ log: this.logFile
+ })
+ );
+ }
+
+ this._writeAnnotations();
+ this._writeInfo();
+ this.writer.end();
+ }
+ if (this.needToInsertPages) {
+ if (this.isBufferSrc) {
+ // eslint-disable-next-line no-console
+ console.log('Feature: Inserting Pages is not supported in Buffer Mode yet.');
+ } else {
+ this._insertPages();
+ }
+ }
+ if (this.needToEncrypt) {
+ if (this.isBufferSrc) {
+ // eslint-disable-next-line no-console
+ console.log('Feature: Encryption is not supported in Buffer Mode yet.');
+ } else {
+ this._encrypt();
+ }
+ }
+
+ if (this.isBufferSrc && this.output) {
+ fs.writeFileSync(this.output, this.outStream.toBuffer());
+ }
+
+ if (callback) {
+ if (this.isBufferSrc) {
+ if (this.output) {
+ return callback(this.output);
+ } else {
+ return callback(this.outStream.toBuffer());
+ }
+ } else {
+ return callback();
+ }
+ }
+ }
+}
+
+module.exports = Recipe;
@@ -320,7 +320,7 @@ Recipe.js
- Documentation generated by JSDoc 3.6.3 on Wed Oct 02 2019 12:00:26 GMT-0700 (Pacific Daylight Time) using the docdash theme.
+ Documentation generated by JSDoc 3.5.5 on Fri Nov 01 2019 20:41:12 GMT-0400 (Eastern Daylight Time) using the docdash theme.
diff --git a/docs/annotation.js.html b/docs/annotation.js.html
index 624c43d..34d8348 100644
--- a/docs/annotation.js.html
+++ b/docs/annotation.js.html
@@ -37,347 +37,347 @@ annotation.js
- /**
- * Create a comment annotation
- * @name comment
- * @function
- * @memberof Recipe
- * @param {string} text - The text content
- * @param {number} x - The coordinate x
- * @param {number} y - The coordinate y
- * @param {Object} [options] - The options
- * @param {string} [options.title] - The title.
- * @param {string} [options.date] - The date.
- * @param {boolean} [options.open=false] - Open the annotation by default?
- * @param {boolean} [options.richText] - Display with rich text format, text will be transformed automatically, or you may pass in your own rich text starts with "<?xml..."
- * @param {'invisible'|'hidden'|'print'|'nozoom'|'norotate'|'noview'|'readonly'|'locked'|'togglenoview'} [options.flag] - The flag property
- */
-exports.comment = function comment(text = '', x, y, options = {}) {
- this.annotationsToWrite.push({
- subtype: 'Text',
- pageNumber: this.pageNumber,
- args: { text, x, y, options: Object.assign({ icon: 'Comment' }, options) }
- });
- return this;
-};
-
-/**
- * Create an annotation
- * @name annot
- * @function
- * @memberof Recipe
- * @todo support for rich texst RC
- * @todo support for opacity CA
- * @param {number} x - The coordinate x
- * @param {number} y - The coordinate y
- * @param {string} subtype - The markup annotation type 'Text'|'FreeText'|'Line'|'Square'|'Circle'|'Polygon'|'PolyLine'|'Highlight'|'Underline'|'Squiggly'|'StrikeOut'|'Stamp'|'Caret'|'Ink'|'FileAttachment'|'Sound'
- * @param {Object} [options] - The options
- * @param {string} [options.title] - The title.
- * @param {boolean} [options.open=false] - Open the annotation by default?
- * @param {'invisible'|'hidden'|'print'|'nozoom'|'norotate'|'noview'|'readonly'|'locked'|'togglenoview'} [options.flag] - The flag property
- * @param {'Comment'|'Key'|'Note'|'Help'|'NewParagraph'|'Paragraph'|'Insert'} [options.icon] - The icon of annotation.
- * @param {number} [options.width] - Width
- * @param {number} [options.height] - Height
- */
-exports.annot = function annot(x, y, subtype, options = { text: '', width: 0, height: 0 }) {
- const { text, width, height } = options;
- this.annotationsToWrite.push({
- subtype,
- args: { text, x, y, width, height, options },
- pageNumber: this.pageNumber
- });
- return this;
-};
-
-// TODO: allow non-markup annots to be associated with markup annotations
-// Link, Popup, Movie, Widget, Screen, PrinterMark, TrapNet, Watermark, 3D
-exports._attachNonMarkupAnnot = function _attachNonMarkupAnnot() {
-
-};
-
-exports._annot = function _annot(subtype, args = {}, pageNumber) {
- const { x, y, width, height, text, options } = args;
- this._startDictionary(pageNumber);
- const { rotate } = this.metadata[pageNumber];
- let { nx, ny } = this._calibrateCoordinateForAnnots(x, y, 0, 0, pageNumber);
-
- let nWidth = width;
- let nHeight = height;
-
- if (!options.followOriginalPageRotation) {
- switch (rotate) {
- case 90:
- nWidth = height;
- nHeight = width;
- nx = nx - nWidth;
- break;
- case 180:
- nx = nx - nWidth;
- ny = ny - nHeight;
- break;
- case 270:
- nWidth = height;
- nHeight = width;
- ny = ny - nHeight;
- break;
- default:
- }
- }
-
- const params = Object.assign({
- title: '',
- subject: '',
- date: new Date(),
- open: false,
- flag: '' // 'readonly'
- }, options);
-
- const ex = (nWidth) ? nWidth : 0;
- const ey = (nHeight) ? nHeight : 0;
- const position = [nx, ny, nx + ex, ny + ey];
-
- this.dictionaryContext
- .writeKey('Type')
- .writeNameValue('Annot')
- .writeKey('Subtype')
- .writeNameValue(subtype)
- .writeKey('L')
- .writeBooleanValue(true)
- .writeKey('Rect')
- .writeRectangleValue(position)
- .writeKey('Subj')
- .writeLiteralStringValue(this.writer.createPDFTextString(params.subject).toBytesArray())
- .writeKey('T')
- .writeLiteralStringValue(this.writer.createPDFTextString(params.title).toBytesArray())
- .writeKey('M')
- .writeLiteralStringValue(this.writer.createPDFDate(params.date).toString())
- .writeKey('Open')
- .writeBooleanValue(params.open)
- .writeKey('F')
- .writeNumberValue(getFlagBitNumberByName(params.flag));
-
- /**
- * Rich Text Strings
- * 12.7.3.4
- */
- if (text && options.richText) {
- const richText = (text.substring(0, 4) !== '<?xml') ? contentToRC(text) : text;
- const richTextContent = this.writer.createPDFTextString(richText).toBytesArray();
- this.dictionaryContext
- .writeKey('RC')
- .writeLiteralStringValue(richTextContent);
- } else
- if (text) {
- const textContent = this.writer.createPDFTextString(text).toBytesArray();
- this.dictionaryContext
- .writeKey('Contents')
- .writeLiteralStringValue(textContent);
- }
-
- let { border, color } = options;
-
- if (this._getTextMarkupAnnotationSubtype(subtype)) {
- this.dictionaryContext.writeKey('QuadPoints');
- const { _textHeight } = options;
- const annotHeight = height;
- const bx = nx;
- const by = ny + ((_textHeight) ? 0 : -annotHeight);
- const coordinates = [
- [bx, by + annotHeight],
- [bx + nWidth, by + annotHeight],
- [bx, by],
- [bx + nWidth, by],
- ];
- this.objectsContext.startArray();
- coordinates.forEach(coord => {
- coord.forEach(point => {
- this.objectsContext.writeNumber(Math.round(point));
- });
- });
- this.objectsContext
- .endArray()
- .endLine();
-
- border = border || 0;
- if (!color) {
- switch (subtype) {
- case 'Highlight':
- color = [255, 255, 0];
- break;
- case 'StrikeOut':
- color = [255, 0, 0];
- break;
- case 'Underline':
- color = [0, 255, 0];
- break;
- case 'Squiggly':
- color = [0, 255, 0];
- break;
- default:
- color = [0, 0, 0];
- break;
- }
- }
- }
-
- if (border != void(0)) {
- this.dictionaryContext.writeKey('Border');
- this.objectsContext
- .startArray()
- .writeNumber(0)
- .writeNumber(0)
- .writeNumber(border)
- .endArray()
- .endLine();
- }
-
- if (color) {
- const rgb = this._colorNumberToRGB(this._transformColor(color));
- this.dictionaryContext.writeKey('C');
- this.objectsContext
- .startArray()
- .writeNumber(rgb.r / 255)
- .writeNumber(rgb.g / 255)
- .writeNumber(rgb.b / 255)
- .endArray()
- .endLine();
- }
-
- /* Display Icon */
- if (params.icon) {
- this.dictionaryContext
- .writeKey('Name')
- .writeNameValue(params.icon);
- }
- this._endDictionary(pageNumber);
-};
-
-exports._writeAnnotations = function _writeAnnotations() {
- this.annotationsToWrite.forEach((annot) => {
- this._annot(annot.subtype, annot.args, annot.pageNumber);
- });
- this.annotations.forEach((pageAnnots, index) => {
- this._writeAnnotation(index);
- });
-};
-
-exports._writeAnnotation = function _writeAnnotation(pageIndex) {
- const pdfWriter = this.writer;
- const copyingContext = pdfWriter.createPDFCopyingContextForModifiedFile();
- const pageID = copyingContext.getSourceDocumentParser().getPageObjectID(pageIndex);
- const pageObject = copyingContext.getSourceDocumentParser().parsePage(pageIndex).getDictionary().toJSObject();
- const objectsContext = pdfWriter.getObjectsContext();
-
- objectsContext.startModifiedIndirectObject(pageID);
- const modifiedPageObject = pdfWriter.getObjectsContext().startDictionary();
- Object.getOwnPropertyNames(pageObject).forEach((element) => {
- const ignore = ['Annots'];
- if (!ignore.includes(element)) {
- modifiedPageObject.writeKey(element);
- copyingContext.copyDirectObjectAsIs(pageObject[element]);
- }
- });
-
- modifiedPageObject.writeKey('Annots');
- objectsContext.startArray();
- if (pageObject['Annots'] && pageObject['Annots'].toJSArray) {
- pageObject['Annots'].toJSArray().forEach((annot) => {
- objectsContext.writeIndirectObjectReference(annot.getObjectID());
- });
- }
- this.annotations[pageIndex].forEach((item) => {
- objectsContext.writeIndirectObjectReference(item);
- });
-
- objectsContext
- .endArray()
- .endLine()
- .endDictionary(modifiedPageObject)
- .endIndirectObject();
-};
-
-exports._startDictionary = function _startDictionary() {
- this.objectsContext = this.writer.getObjectsContext();
- this.dictionaryObject = this.objectsContext.startNewIndirectObject();
- this.dictionaryContext = this.objectsContext.startDictionary();
-};
-
-exports._endDictionary = function _endDictionary(pageNumber) {
- this.objectsContext
- .endDictionary(this.dictionaryContext)
- .endIndirectObject();
- const pageIndex = pageNumber - 1;
- this.annotations[pageIndex] = this.annotations[pageIndex] || [];
- this.annotations[pageIndex].push(this.dictionaryObject);
-};
-
-exports._getTextMarkupAnnotationSubtype = function _getTextMarkupAnnotationSubtype(subtype = '') {
- const matchedSubtype = this.textMarkupAnnotations.find(item => {
- return item.toLowerCase() == subtype.toLowerCase();
- });
- return matchedSubtype;
-};
-
-/**
- * Get Flag Bit by Name
- * @description 12.5.3 Annotation Flags
- * @param {string} name
- */
-function getFlagBitNumberByName(name) {
- switch (name.toLowerCase()) {
- case 'invisible':
- return 1;
- case 'hidden':
- return 2;
- case 'print':
- return 4;
- case 'nozoom':
- return 8;
- case 'norotate':
- return 16;
- case 'noview':
- return 32;
- case 'readonly':
- return 64;
- case 'locked':
- return 128;
- case 'togglenoview':
- return 256;
- // 1.7+
- // case 'lockedcontents':
- // return 512;
- default:
- return 0;
- }
-}
-
-/**
- * Text Strings to Rich Text Strings
- * @todo Fix display issue for ol/ul in richText
- * @param {string} content
- * @description Support XHTML Elements: '<p>' | '<span>' | '<b>' | '<i>'
- * @description Support CSS2 Style: 'text-align' | 'vertical-align' | 'font-size' | 'font-style' | 'font-weight' | 'font-family' | 'font' | 'color' | 'text-decoration' | 'font-stretch'
- */
-function contentToRC(content) {
- content = content.replace(' ', ' ');
- content = content.replace(/\r?\n|\r|\t/g, '');
- let richText =
- '<?xml version="1.0"?>' +
- '<body ' +
- 'xmlns="http://www.w3.org/1999/xhtml"' +
- // 'xmlns:xga=\"http://www.xfa.org/schema/xfa-data/1.0/\" ' +
- // 'xfa:contentType=\"text/html\" ' +
- // 'xfa:APIVersion=\"Acrobat:8.0.0\" ' +
- // 'xfa:spec=\"2.4\" ' +
- '>' +
- content +
- '</body>';
- richText = richText
- .replace(/<li>/g, '<p> • ')
- .replace(/<(\/)li>/g, '</p>')
- .replace(/<(\/)p>/g, '</p><br/>');
- return richText;
-}
+ /**
+ * Create a comment annotation
+ * @name comment
+ * @function
+ * @memberof Recipe
+ * @param {string} text - The text content
+ * @param {number} x - The coordinate x
+ * @param {number} y - The coordinate y
+ * @param {Object} [options] - The options
+ * @param {string} [options.title] - The title.
+ * @param {string} [options.date] - The date.
+ * @param {boolean} [options.open=false] - Open the annotation by default?
+ * @param {boolean} [options.richText] - Display with rich text format, text will be transformed automatically, or you may pass in your own rich text starts with "<?xml..."
+ * @param {'invisible'|'hidden'|'print'|'nozoom'|'norotate'|'noview'|'readonly'|'locked'|'togglenoview'} [options.flag] - The flag property
+ */
+exports.comment = function comment(text = '', x, y, options = {}) {
+ this.annotationsToWrite.push({
+ subtype: 'Text',
+ pageNumber: this.pageNumber,
+ args: { text, x, y, options: Object.assign({ icon: 'Comment' }, options) }
+ });
+ return this;
+};
+
+/**
+ * Create an annotation
+ * @name annot
+ * @function
+ * @memberof Recipe
+ * @todo support for rich texst RC
+ * @todo support for opacity CA
+ * @param {number} x - The coordinate x
+ * @param {number} y - The coordinate y
+ * @param {string} subtype - The markup annotation type 'Text'|'FreeText'|'Line'|'Square'|'Circle'|'Polygon'|'PolyLine'|'Highlight'|'Underline'|'Squiggly'|'StrikeOut'|'Stamp'|'Caret'|'Ink'|'FileAttachment'|'Sound'
+ * @param {Object} [options] - The options
+ * @param {string} [options.title] - The title.
+ * @param {boolean} [options.open=false] - Open the annotation by default?
+ * @param {'invisible'|'hidden'|'print'|'nozoom'|'norotate'|'noview'|'readonly'|'locked'|'togglenoview'} [options.flag] - The flag property
+ * @param {'Comment'|'Key'|'Note'|'Help'|'NewParagraph'|'Paragraph'|'Insert'} [options.icon] - The icon of annotation.
+ * @param {number} [options.width] - Width
+ * @param {number} [options.height] - Height
+ */
+exports.annot = function annot(x, y, subtype, options = { text: '', width: 0, height: 0 }) {
+ const { text, width, height } = options;
+ this.annotationsToWrite.push({
+ subtype,
+ args: { text, x, y, width, height, options },
+ pageNumber: this.pageNumber
+ });
+ return this;
+};
+
+// TODO: allow non-markup annots to be associated with markup annotations
+// Link, Popup, Movie, Widget, Screen, PrinterMark, TrapNet, Watermark, 3D
+exports._attachNonMarkupAnnot = function _attachNonMarkupAnnot() {
+
+};
+
+exports._annot = function _annot(subtype, args = {}, pageNumber) {
+ const { x, y, width, height, text, options } = args;
+ this._startDictionary(pageNumber);
+ const { rotate } = this.metadata[pageNumber];
+ let { nx, ny } = this._calibrateCoordinateForAnnots(x, y, 0, 0, pageNumber);
+
+ let nWidth = width;
+ let nHeight = height;
+
+ if (!options.followOriginalPageRotation) {
+ switch (rotate) {
+ case 90:
+ nWidth = height;
+ nHeight = width;
+ nx = nx - nWidth;
+ break;
+ case 180:
+ nx = nx - nWidth;
+ ny = ny - nHeight;
+ break;
+ case 270:
+ nWidth = height;
+ nHeight = width;
+ ny = ny - nHeight;
+ break;
+ default:
+ }
+ }
+
+ const params = Object.assign({
+ title: '',
+ subject: '',
+ date: new Date(),
+ open: false,
+ flag: '' // 'readonly'
+ }, options);
+
+ const ex = (nWidth) ? nWidth : 0;
+ const ey = (nHeight) ? nHeight : 0;
+ const position = [nx, ny, nx + ex, ny + ey];
+
+ this.dictionaryContext
+ .writeKey('Type')
+ .writeNameValue('Annot')
+ .writeKey('Subtype')
+ .writeNameValue(subtype)
+ .writeKey('L')
+ .writeBooleanValue(true)
+ .writeKey('Rect')
+ .writeRectangleValue(position)
+ .writeKey('Subj')
+ .writeLiteralStringValue(this.writer.createPDFTextString(params.subject).toBytesArray())
+ .writeKey('T')
+ .writeLiteralStringValue(this.writer.createPDFTextString(params.title).toBytesArray())
+ .writeKey('M')
+ .writeLiteralStringValue(this.writer.createPDFDate(params.date).toString())
+ .writeKey('Open')
+ .writeBooleanValue(params.open)
+ .writeKey('F')
+ .writeNumberValue(getFlagBitNumberByName(params.flag));
+
+ /**
+ * Rich Text Strings
+ * 12.7.3.4
+ */
+ if (text && options.richText) {
+ const richText = (text.substring(0, 4) !== '<?xml') ? contentToRC(text) : text;
+ const richTextContent = this.writer.createPDFTextString(richText).toBytesArray();
+ this.dictionaryContext
+ .writeKey('RC')
+ .writeLiteralStringValue(richTextContent);
+ } else
+ if (text) {
+ const textContent = this.writer.createPDFTextString(text).toBytesArray();
+ this.dictionaryContext
+ .writeKey('Contents')
+ .writeLiteralStringValue(textContent);
+ }
+
+ let { border, color } = options;
+
+ if (this._getTextMarkupAnnotationSubtype(subtype)) {
+ this.dictionaryContext.writeKey('QuadPoints');
+ const { _textHeight } = options;
+ const annotHeight = height;
+ const bx = nx;
+ const by = ny + ((_textHeight) ? 0 : -annotHeight);
+ const coordinates = [
+ [bx, by + annotHeight],
+ [bx + nWidth, by + annotHeight],
+ [bx, by],
+ [bx + nWidth, by],
+ ];
+ this.objectsContext.startArray();
+ coordinates.forEach(coord => {
+ coord.forEach(point => {
+ this.objectsContext.writeNumber(Math.round(point));
+ });
+ });
+ this.objectsContext
+ .endArray()
+ .endLine();
+
+ border = border || 0;
+ if (!color) {
+ switch (subtype) {
+ case 'Highlight':
+ color = [255, 255, 0];
+ break;
+ case 'StrikeOut':
+ color = [255, 0, 0];
+ break;
+ case 'Underline':
+ color = [0, 255, 0];
+ break;
+ case 'Squiggly':
+ color = [0, 255, 0];
+ break;
+ default:
+ color = [0, 0, 0];
+ break;
+ }
+ }
+ }
+
+ if (border != void(0)) {
+ this.dictionaryContext.writeKey('Border');
+ this.objectsContext
+ .startArray()
+ .writeNumber(0)
+ .writeNumber(0)
+ .writeNumber(border)
+ .endArray()
+ .endLine();
+ }
+
+ if (color) {
+ const rgb = this._colorNumberToRGB(this._transformColor(color));
+ this.dictionaryContext.writeKey('C');
+ this.objectsContext
+ .startArray()
+ .writeNumber(rgb.r / 255)
+ .writeNumber(rgb.g / 255)
+ .writeNumber(rgb.b / 255)
+ .endArray()
+ .endLine();
+ }
+
+ /* Display Icon */
+ if (params.icon) {
+ this.dictionaryContext
+ .writeKey('Name')
+ .writeNameValue(params.icon);
+ }
+ this._endDictionary(pageNumber);
+};
+
+exports._writeAnnotations = function _writeAnnotations() {
+ this.annotationsToWrite.forEach((annot) => {
+ this._annot(annot.subtype, annot.args, annot.pageNumber);
+ });
+ this.annotations.forEach((pageAnnots, index) => {
+ this._writeAnnotation(index);
+ });
+};
+
+exports._writeAnnotation = function _writeAnnotation(pageIndex) {
+ const pdfWriter = this.writer;
+ const copyingContext = pdfWriter.createPDFCopyingContextForModifiedFile();
+ const pageID = copyingContext.getSourceDocumentParser().getPageObjectID(pageIndex);
+ const pageObject = copyingContext.getSourceDocumentParser().parsePage(pageIndex).getDictionary().toJSObject();
+ const objectsContext = pdfWriter.getObjectsContext();
+
+ objectsContext.startModifiedIndirectObject(pageID);
+ const modifiedPageObject = pdfWriter.getObjectsContext().startDictionary();
+ Object.getOwnPropertyNames(pageObject).forEach((element) => {
+ const ignore = ['Annots'];
+ if (!ignore.includes(element)) {
+ modifiedPageObject.writeKey(element);
+ copyingContext.copyDirectObjectAsIs(pageObject[element]);
+ }
+ });
+
+ modifiedPageObject.writeKey('Annots');
+ objectsContext.startArray();
+ if (pageObject['Annots'] && pageObject['Annots'].toJSArray) {
+ pageObject['Annots'].toJSArray().forEach((annot) => {
+ objectsContext.writeIndirectObjectReference(annot.getObjectID());
+ });
+ }
+ this.annotations[pageIndex].forEach((item) => {
+ objectsContext.writeIndirectObjectReference(item);
+ });
+
+ objectsContext
+ .endArray()
+ .endLine()
+ .endDictionary(modifiedPageObject)
+ .endIndirectObject();
+};
+
+exports._startDictionary = function _startDictionary() {
+ this.objectsContext = this.writer.getObjectsContext();
+ this.dictionaryObject = this.objectsContext.startNewIndirectObject();
+ this.dictionaryContext = this.objectsContext.startDictionary();
+};
+
+exports._endDictionary = function _endDictionary(pageNumber) {
+ this.objectsContext
+ .endDictionary(this.dictionaryContext)
+ .endIndirectObject();
+ const pageIndex = pageNumber - 1;
+ this.annotations[pageIndex] = this.annotations[pageIndex] || [];
+ this.annotations[pageIndex].push(this.dictionaryObject);
+};
+
+exports._getTextMarkupAnnotationSubtype = function _getTextMarkupAnnotationSubtype(subtype = '') {
+ const matchedSubtype = this.textMarkupAnnotations.find(item => {
+ return item.toLowerCase() == subtype.toLowerCase();
+ });
+ return matchedSubtype;
+};
+
+/**
+ * Get Flag Bit by Name
+ * @description 12.5.3 Annotation Flags
+ * @param {string} name
+ */
+function getFlagBitNumberByName(name) {
+ switch (name.toLowerCase()) {
+ case 'invisible':
+ return 1;
+ case 'hidden':
+ return 2;
+ case 'print':
+ return 4;
+ case 'nozoom':
+ return 8;
+ case 'norotate':
+ return 16;
+ case 'noview':
+ return 32;
+ case 'readonly':
+ return 64;
+ case 'locked':
+ return 128;
+ case 'togglenoview':
+ return 256;
+ // 1.7+
+ // case 'lockedcontents':
+ // return 512;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Text Strings to Rich Text Strings
+ * @todo Fix display issue for ol/ul in richText
+ * @param {string} content
+ * @description Support XHTML Elements: '<p>' | '<span>' | '<b>' | '<i>'
+ * @description Support CSS2 Style: 'text-align' | 'vertical-align' | 'font-size' | 'font-style' | 'font-weight' | 'font-family' | 'font' | 'color' | 'text-decoration' | 'font-stretch'
+ */
+function contentToRC(content) {
+ content = content.replace(' ', ' ');
+ content = content.replace(/\r?\n|\r|\t/g, '');
+ let richText =
+ '<?xml version="1.0"?>' +
+ '<body ' +
+ 'xmlns="http://www.w3.org/1999/xhtml"' +
+ // 'xmlns:xga=\"http://www.xfa.org/schema/xfa-data/1.0/\" ' +
+ // 'xfa:contentType=\"text/html\" ' +
+ // 'xfa:APIVersion=\"Acrobat:8.0.0\" ' +
+ // 'xfa:spec=\"2.4\" ' +
+ '>' +
+ content +
+ '</body>';
+ richText = richText
+ .replace(/<li>/g, '<p> • ')
+ .replace(/<(\/)li>/g, '</p>')
+ .replace(/<(\/)p>/g, '</p><br/>');
+ return richText;
+}
@@ -390,7 +390,7 @@ annotation.js
- Documentation generated by JSDoc 3.6.3 on Wed Oct 02 2019 12:00:26 GMT-0700 (Pacific Daylight Time) using the docdash theme.
+ Documentation generated by JSDoc 3.5.5 on Fri Nov 01 2019 20:41:12 GMT-0400 (Eastern Daylight Time) using the docdash theme.
diff --git a/docs/appendPage.js.html b/docs/appendPage.js.html
index 4573d22..bf69b32 100644
--- a/docs/appendPage.js.html
+++ b/docs/appendPage.js.html
@@ -37,51 +37,51 @@ appendPage.js
- const hummus = require('hummus');
-const hummusUtils = require('./utils');
-
-/**
- * Append pages from the other pdf to the current pdf
- * @name appendPage
- * @function
- * @memberof Recipe
- * @param {string} pdfSrc - The path for the other pdf.
- * @param {number|number[]} pages - The page number or the array of page numbers to be appended.
- */
-exports.appendPage = function appendPage(pdfSrc, pages = []) {
- if (!Array.isArray(pages) && !isNaN(pages)) {
- pages = [pages];
- }
- const pdfReader = hummus.createReader(pdfSrc);
- const pageCount = pdfReader.getPagesCount();
- // prevent unmatched pagenumber
- const transformPageNumber = (pageNum) => {
- pageNum = (pageNum > pageCount) ? pageCount : pageNum;
- pageNum = (pageNum < 1) ? 1 : pageNum;
- return (pageNum - 1);
- };
- pages = pages.map((element) => {
- if (Array.isArray(element)) {
- return [
- transformPageNumber(element[0]),
- transformPageNumber(element[1])
- ];
- } else {
- return [
- transformPageNumber(element),
- transformPageNumber(element)
- ];
- }
- });
- if (pages.length > 0) {
- hummusUtils.appendPDFPagesFromPDFWithAnnotations(this.writer, pdfSrc, {
- specificRanges: pages
- });
- } else {
- hummusUtils.appendPDFPagesFromPDFWithAnnotations(this.writer, pdfSrc);
- }
- return this;
-};
+ const hummus = require('hummus');
+const hummusUtils = require('./utils');
+
+/**
+ * Append pages from the other pdf to the current pdf
+ * @name appendPage
+ * @function
+ * @memberof Recipe
+ * @param {string} pdfSrc - The path for the other pdf.
+ * @param {number|number[]} pages - The page number or the array of page numbers to be appended.
+ */
+exports.appendPage = function appendPage(pdfSrc, pages = []) {
+ if (!Array.isArray(pages) && !isNaN(pages)) {
+ pages = [pages];
+ }
+ const pdfReader = hummus.createReader(pdfSrc);
+ const pageCount = pdfReader.getPagesCount();
+ // prevent unmatched pagenumber
+ const transformPageNumber = (pageNum) => {
+ pageNum = (pageNum > pageCount) ? pageCount : pageNum;
+ pageNum = (pageNum < 1) ? 1 : pageNum;
+ return (pageNum - 1);
+ };
+ pages = pages.map((element) => {
+ if (Array.isArray(element)) {
+ return [
+ transformPageNumber(element[0]),
+ transformPageNumber(element[1])
+ ];
+ } else {
+ return [
+ transformPageNumber(element),
+ transformPageNumber(element)
+ ];
+ }
+ });
+ if (pages.length > 0) {
+ hummusUtils.appendPDFPagesFromPDFWithAnnotations(this.writer, pdfSrc, {
+ specificRanges: pages
+ });
+ } else {
+ hummusUtils.appendPDFPagesFromPDFWithAnnotations(this.writer, pdfSrc);
+ }
+ return this;
+};
@@ -94,7 +94,7 @@ appendPage.js
- Documentation generated by JSDoc 3.6.3 on Wed Oct 02 2019 12:00:26 GMT-0700 (Pacific Daylight Time) using the docdash theme.
+ Documentation generated by JSDoc 3.5.5 on Fri Nov 01 2019 20:41:12 GMT-0400 (Eastern Daylight Time) using the docdash theme.
diff --git a/docs/colors.js.html b/docs/colors.js.html
index e84eb22..2297d3a 100644
--- a/docs/colors.js.html
+++ b/docs/colors.js.html
@@ -37,402 +37,402 @@ colors.js
- const hummus = require('hummus');
-const fs = require('fs');
-
-this.knownColors = { // knownColors.colorspace.colorName = value
- rgb: {
- red : 'ff0000',
- green : '00ff00',
- blue : '0000ff',
- },
- cmyk: {
- cyan : 'ff000000',
- magenta: '00ff0000',
- yellow : '0000ff00',
- black : '000000ff'
- },
- gray: {
- white : 'ff',
- black : '00'
- },
- separation : { // meant for printing, so using cmyk values initially
- cyan : 'ff000000',
- magenta: '00ff0000',
- yellow : '0000ff00',
- black : '000000ff',
- nans : '%,35,6,0' // a great PDF collaborator!
- }
-};
-
-/**
- * Associate color values to names
- *
- * The colorspace parameter is optional. When it is missing, the colorspace
- * is automatically determined by the given color value. Note that the special
- * PDF color space called 'separation' may also be used. The color value is then
- * treated as the alternative color when the named 'separation' color is unavailable.
- *
- * If the 'name' parameter is '!load', the second parameter is the name of a JSON
- * formatted file containing a formatted list of defined colors associated with the
- * color spaces rgb, cmyk, gray, or separation (think PANTONE color definitions).
- * This file will be merged with existing set of known colors. The color values
- * must be specified as hex values.
- *
- * For example,
- * {
- * 'rgb': {'purple':'ff00ff', 'red':'#ff0000'},
- * 'cmyk': {'cyan':'ff000000', 'magenta':'%0,100,0,0'},
- * 'gray': {'grey':'#33'}
- * }
- *
- * @name chroma
- * @function
- * @memberof Recipe
- * @param {string} name - the name to be associated to given color value, or '!load'
- * @param {string|number[]} value - the color value (HexColor, DecimalColor, or PercentColor), or name of '!load' file
- * @param {string} colorspace - one of the followning: 'rgb', 'cmyk', 'gray', 'separation';
- */
-exports.chroma = function chroma(name, value, colorspace='') {
- if (name) {
- if (name === '!load') {
- let newColors = JSON.parse(fs.readFileSync(value));
- // Add new colors to existing colorspaces
- for (let cs in newColors) {
- if (this.knownColors[cs]) {
- Object.assign(this.knownColors[cs], newColors[cs]);
- } else {
- throw new Error(`Unrecognized colorspace: ${cs}`);
- }
- }
- } else {
- if (Array.isArray(value)) {
- value = arrayToHex(value);
- } else if (value.startsWith('%')) {
- value = percentToHex(value.replace('%', ''));
- } else {
- value = value.replace('#', '');
- }
-
- // Only deal with valid hex codes from the
- // device colorspaces gray, rgb, and cmyk.
- if (![2,6,8].includes(value.toString().length)) {
- throw new Error('Color value has incorrect size for gray, rgb, or cmyk colorspaces');
- }
-
- // Determine colorspace by length of given input
- // value when colorspace not provided in call.
- if (colorspace === '') {
- const colorSpaces = {2:'gray', 6:'rgb', 8:'cmyk'};
- colorspace = colorSpaces[`${value.length}`];
- } else if (!['rgb','cmyk','gray','separation'].includes(colorspace)) {
- throw new Error(`Unknown colorspace: ${colorspace}.`);
- }
-
- if (colorspace) {
- this.knownColors[colorspace][name] = value;
- }
- }
- }
-
- return this;
-};
-
-function createColorSpaces(self, colorName, color) {
- const deviceCS = {1:'DeviceGray', 3:'DeviceRGB', 4:'DeviceCMYK'};
- const altCS = deviceCS[`${color.length}`];
- this.colorSpaces = this.colorSpaces || {};
- this.colorSpaces[altCS] = this.colorSpaces[altCS] || {};
- let colorSpaceID = this.colorSpaces[altCS][colorName];
-
- if (!colorSpaceID) {
- const transformFunction = tintTransform(self, color);
- self.pauseContext();
- const objCxt = self.writer.getObjectsContext();
- colorSpaceID = objCxt.startNewIndirectObject();
- objCxt
- .startArray()
- .writeName('Separation')
- .writeName(colorName)
- .writeName(altCS)
- .writeIndirectObjectReference(transformFunction)
- .endArray(hummus.eTokenSeparatorEndLine)
- .endIndirectObject();
- self.resumeContext();
- this.colorSpaces[altCS][colorName] = colorSpaceID;
- }
-
- return colorSpaceID;
-}
-
-function tintTransform(self, color) {
- const rangeCount = color.length;
- self.pauseContext();
- const objCxt = self.writer.getObjectsContext();
- const tintFuncID = objCxt.startNewIndirectObject();
- const dict = objCxt.startDictionary();
- dict
- .writeKey('FunctionType')
- .writeNumberValue(2)
- .writeKey('Domain');
- objCxt
- .startArray()
- .writeNumber(0.0)
- .writeNumber(1.0)
- .endArray();
- dict.writeKey('Range');
- objCxt.startArray();
- for (let index = 0; index < rangeCount; index++) {
- objCxt.writeNumber(0.0).writeNumber(1.0);
- }
- objCxt.endArray();
- dict.writeKey('N');
- dict.writeNumberValue(1);
- dict.writeKey('C0');
- objCxt.startArray();
- for (let index = 0; index < rangeCount; index++) {
- objCxt.writeNumber(0.0);
- }
- objCxt.endArray();
-
- dict.writeKey('C1');
- objCxt.startArray();
- for (let index = 0; index < rangeCount; index++) {
- objCxt.writeNumber(color[index]);
- }
- objCxt.endArray();
- objCxt.endDictionary(dict);
- objCxt.endIndirectObject();
- self.resumeContext();
-
- return tintFuncID;
-}
-
-exports._createExtGStates = function _createExtGStates(value) {
- this.extGStates = this.extGStates || {};
- if (this.extGStates[value]) {
- return this.extGStates[value];
- }
-
- const write = (key, value) => {
- this.pauseContext();
- const objCxt = this.writer.getObjectsContext();
- const gsId = objCxt.startNewIndirectObject();
- const dict = objCxt.startDictionary();
- dict.writeKey('type');
- dict.writeNameValue('ExtGState');
- dict.writeKey(key);
- objCxt.writeNumber(value);
- objCxt.endLine();
- objCxt.endDictionary(dict);
- objCxt.endIndirectObject(); // new here [seh]
- this.resumeContext();
- return gsId;
- };
- this.extGStates[value] = {
- stroke: write('CA', value),
- fill: write('ca', value)
- };
- return this.extGStates[value];
-};
-
-function _defaultColor(colorspace='rgb') {
- let defaultColor;
- switch (colorspace) {
- case 'cmyk':
- defaultColor = 'FF000000';
- break;
- case 'gray':
- defaultColor = '00';
- break;
- case 'rgb':
- default:
- defaultColor = '1777d1';
- break;
- }
-
- return defaultColor;
-}
-
-/**
- * Convert given color code int color model object
- *
- * ColorModel consists of: {
- * color: number,
- * colorspace: string {'rgb', 'cmyk', 'gray'},
- * (colorspace == 'rgb') r, g, b
- * (colorspace == 'cmyk') c, m, y, k
- * (colorspace == 'gray') gray
- * }
- *
- * where r,g,b,c,m,y,k,gray are all numbers between 0 and 1
- *
- * @param {string} code the color encoding as HexColor
- * @param {string} colorspace the name of the colorspace of given color code
- * @param {string} colorName the name to be associated with given color code
- * @returns {any} the color model
- */
-function toColorModel(self, code, colorspace, colorName) {
- const cmodel = {};
- let color = hexToArray(code);
-
- cmodel.color = parseInt(code, 16);
-
- // The initial decider of color space is length of given 'code'.
-
- switch (color.length) {
- default:
- color = hexToArray(_defaultColor('rgb'));
- // purposely want to fall through to 'rgb' case below.
- case 3:
- cmodel.colorspace = 'rgb';
- cmodel.r = color[0];
- cmodel.g = color[1];
- cmodel.b = color[2];
- break;
-
- case 4:
- cmodel.colorspace = 'cmyk';
- cmodel.c = color[0];
- cmodel.m = color[1];
- cmodel.y = color[2];
- cmodel.k = color[3];
- break;
-
- case 1:
- cmodel.colorspace = 'gray';
- cmodel.gray = color[0];
- break;
- }
-
- // When creating a separation color space,
- // use the colorspace from above as the
- // alternative color transformation when
- // the named color is unavailable.
- if (colorspace === 'separation' && colorName !== '') {
- cmodel.colorspace = colorspace;
- cmodel.colorName = colorName;
- cmodel.colorspaceId = createColorSpaces(self, colorName, color);
- }
-
- return cmodel;
-}
-
-/**
- * Convert percentage string into hex string (x / 100 * 255)
- *
- * @param {string} code numbers separated by commas with values ranging between 0-100.
- * @returns {string} massaged hexadecimal string that can be used as input to hexToArray.
- */
-function percentToHex(code) {
- return arrayToHex(code.split(',').map( x => Math.round(x * 2.55)) );
-}
-
-/**
- * Transform color code into numeric value or colorModel
- *
- * @param code color specification in form of HexColor (string, begins with '#'),
- * DecimalColor (1, 3, or 4 element array with values between 0-255),
- * PercentColor (string, begins with '%' followed by values separated
- * by commas with values between 0-100)
- */
-exports._transformColor = function _transformColor(code = '', opt = {}) {
- this.knownColors = this.knownColors || {};
- let colorspace = opt.colorspace || 'rgb';
- let wantColorModel = opt.wantColorModel || false;
- let colorName = opt.colorName || '';
- let defaultColor = _defaultColor(colorspace);
- let transformation;
-
- if (Array.isArray(code)) {
- code = arrayToHex(code);
- } else if (code.startsWith('#')){
- code = code.replace('#', '');
- } else if (code.startsWith('%')) {
- code = percentToHex(code.replace('%', ''));
- } else if (code !== '') {
- let color = this.knownColors[colorspace][code]; // assuming code is a color name
- if (!color) {
- color = '';
- code = defaultColor;
- } else {
- colorName = code;
-
- // The following handles known colors in hex form,
- // with or without initial '#' and percent form.
- if (color.startsWith('#')) {
- code = color.replace('#','');
- } else if (color.startsWith('%')) {
- code = percentToHex(color.replace('%', ''));
- } else {
- code = color;
- }
- }
- }
-
- // When colorspace is not explicitly given,
- // use size of value to determine colorspace.
- if (!opt.colorspace) {
- colorspace = {2:'gray', 6:'rgb', 8:'cmyk'}[`${code.length}`] || 'rgb';
- defaultColor = _defaultColor(colorspace);
- }
-
- // Suppply default color:
- // when colorspace is given and given color code does not have appropriate length, or
- // when colorspace is missing, verify allowable hex value sizes for rgb, cmyk, or gray.
- if ( ['rgb','cmyk','gray'].includes(colorspace) && code.length != defaultColor.length ||
- ! [2,6,8].includes(code.toString().length)) {
- code = defaultColor;
- }
-
- if (wantColorModel) {
- transformation = toColorModel(this, code, colorspace, colorName);
- if ( colorName && !this.knownColors[colorspace][colorName]) {
- this.chroma(colorName, code, colorspace);
- }
- } else {
- transformation = parseInt(`0x${code.toUpperCase()}`, 16);
- }
-
- return transformation;
-};
-
-function arrayToHex(color = []) {
- let code = '';
- color.forEach((item) => {
- let hex = item.toString(16);
- hex = (hex.length == 1) ? '0' + hex : hex;
- code += hex;
- });
- return code;
-}
-
-function hexToArray(hex) {
- const result = /^#?([a-f\d]{2})([a-f\d]{2})?([a-f\d]{2})?([a-f\d]{2})?$/i.exec(hex);
- return result
- .reduce((array, item, index) => {
- if (index >= 1 && index <= (hex.length / 2)) {
- array.push(parseInt(item, 16) / 255);
- }
- return array;
- }, []);
-}
-
-exports._colorNumberToRGB = (bigint) => {
- if (!bigint) {
- return {
- r: 0,
- g: 0,
- b: 0
- };
- } else {
- return {
- r: (bigint >> 16) & 255,
- g: (bigint >> 8) & 255,
- b: bigint & 255
- };
- }
-};
+ const hummus = require('hummus');
+const fs = require('fs');
+
+this.knownColors = { // knownColors.colorspace.colorName = value
+ rgb: {
+ red : 'ff0000',
+ green : '00ff00',
+ blue : '0000ff',
+ },
+ cmyk: {
+ cyan : 'ff000000',
+ magenta: '00ff0000',
+ yellow : '0000ff00',
+ black : '000000ff'
+ },
+ gray: {
+ white : 'ff',
+ black : '00'
+ },
+ separation : { // meant for printing, so using cmyk values initially
+ cyan : 'ff000000',
+ magenta: '00ff0000',
+ yellow : '0000ff00',
+ black : '000000ff',
+ nans : '%,35,6,0' // a great PDF collaborator!
+ }
+};
+
+/**
+ * Associate color values to names
+ *
+ * The colorspace parameter is optional. When it is missing, the colorspace
+ * is automatically determined by the given color value. Note that the special
+ * PDF color space called 'separation' may also be used. The color value is then
+ * treated as the alternative color when the named 'separation' color is unavailable.
+ *
+ * If the 'name' parameter is '!load', the second parameter is the name of a JSON
+ * formatted file containing a formatted list of defined colors associated with the
+ * color spaces rgb, cmyk, gray, or separation (think PANTONE color definitions).
+ * This file will be merged with existing set of known colors. The color values
+ * must be specified as hex values.
+ *
+ * For example,
+ * {
+ * 'rgb': {'purple':'ff00ff', 'red':'#ff0000'},
+ * 'cmyk': {'cyan':'ff000000', 'magenta':'%0,100,0,0'},
+ * 'gray': {'grey':'#33'}
+ * }
+ *
+ * @name chroma
+ * @function
+ * @memberof Recipe
+ * @param {string} name - the name to be associated to given color value, or '!load'
+ * @param {string|number[]} value - the color value (HexColor, DecimalColor, or PercentColor), or name of '!load' file
+ * @param {string} colorspace - one of the followning: 'rgb', 'cmyk', 'gray', 'separation';
+ */
+exports.chroma = function chroma(name, value, colorspace='') {
+ if (name) {
+ if (name === '!load') {
+ let newColors = JSON.parse(fs.readFileSync(value));
+ // Add new colors to existing colorspaces
+ for (let cs in newColors) {
+ if (this.knownColors[cs]) {
+ Object.assign(this.knownColors[cs], newColors[cs]);
+ } else {
+ throw new Error(`Unrecognized colorspace: ${cs}`);
+ }
+ }
+ } else {
+ if (Array.isArray(value)) {
+ value = arrayToHex(value);
+ } else if (value.startsWith('%')) {
+ value = percentToHex(value.replace('%', ''));
+ } else {
+ value = value.replace('#', '');
+ }
+
+ // Only deal with valid hex codes from the
+ // device colorspaces gray, rgb, and cmyk.
+ if (![2,6,8].includes(value.toString().length)) {
+ throw new Error('Color value has incorrect size for gray, rgb, or cmyk colorspaces');
+ }
+
+ // Determine colorspace by length of given input
+ // value when colorspace not provided in call.
+ if (colorspace === '') {
+ const colorSpaces = {2:'gray', 6:'rgb', 8:'cmyk'};
+ colorspace = colorSpaces[`${value.length}`];
+ } else if (!['rgb','cmyk','gray','separation'].includes(colorspace)) {
+ throw new Error(`Unknown colorspace: ${colorspace}.`);
+ }
+
+ if (colorspace) {
+ this.knownColors[colorspace][name] = value;
+ }
+ }
+ }
+
+ return this;
+};
+
+function createColorSpaces(self, colorName, color) {
+ const deviceCS = {1:'DeviceGray', 3:'DeviceRGB', 4:'DeviceCMYK'};
+ const altCS = deviceCS[`${color.length}`];
+ this.colorSpaces = this.colorSpaces || {};
+ this.colorSpaces[altCS] = this.colorSpaces[altCS] || {};
+ let colorSpaceID = this.colorSpaces[altCS][colorName];
+
+ if (!colorSpaceID) {
+ const transformFunction = tintTransform(self, color);
+ self.pauseContext();
+ const objCxt = self.writer.getObjectsContext();
+ colorSpaceID = objCxt.startNewIndirectObject();
+ objCxt
+ .startArray()
+ .writeName('Separation')
+ .writeName(colorName)
+ .writeName(altCS)
+ .writeIndirectObjectReference(transformFunction)
+ .endArray(hummus.eTokenSeparatorEndLine)
+ .endIndirectObject();
+ self.resumeContext();
+ this.colorSpaces[altCS][colorName] = colorSpaceID;
+ }
+
+ return colorSpaceID;
+}
+
+function tintTransform(self, color) {
+ const rangeCount = color.length;
+ self.pauseContext();
+ const objCxt = self.writer.getObjectsContext();
+ const tintFuncID = objCxt.startNewIndirectObject();
+ const dict = objCxt.startDictionary();
+ dict
+ .writeKey('FunctionType')
+ .writeNumberValue(2)
+ .writeKey('Domain');
+ objCxt
+ .startArray()
+ .writeNumber(0.0)
+ .writeNumber(1.0)
+ .endArray();
+ dict.writeKey('Range');
+ objCxt.startArray();
+ for (let index = 0; index < rangeCount; index++) {
+ objCxt.writeNumber(0.0).writeNumber(1.0);
+ }
+ objCxt.endArray();
+ dict.writeKey('N');
+ dict.writeNumberValue(1);
+ dict.writeKey('C0');
+ objCxt.startArray();
+ for (let index = 0; index < rangeCount; index++) {
+ objCxt.writeNumber(0.0);
+ }
+ objCxt.endArray();
+
+ dict.writeKey('C1');
+ objCxt.startArray();
+ for (let index = 0; index < rangeCount; index++) {
+ objCxt.writeNumber(color[index]);
+ }
+ objCxt.endArray();
+ objCxt.endDictionary(dict);
+ objCxt.endIndirectObject();
+ self.resumeContext();
+
+ return tintFuncID;
+}
+
+exports._createExtGStates = function _createExtGStates(value) {
+ this.extGStates = this.extGStates || {};
+ if (this.extGStates[value]) {
+ return this.extGStates[value];
+ }
+
+ const write = (key, value) => {
+ this.pauseContext();
+ const objCxt = this.writer.getObjectsContext();
+ const gsId = objCxt.startNewIndirectObject();
+ const dict = objCxt.startDictionary();
+ dict.writeKey('type');
+ dict.writeNameValue('ExtGState');
+ dict.writeKey(key);
+ objCxt.writeNumber(value);
+ objCxt.endLine();
+ objCxt.endDictionary(dict);
+ objCxt.endIndirectObject(); // new here [seh]
+ this.resumeContext();
+ return gsId;
+ };
+ this.extGStates[value] = {
+ stroke: write('CA', value),
+ fill: write('ca', value)
+ };
+ return this.extGStates[value];
+};
+
+function _defaultColor(colorspace='rgb') {
+ let defaultColor;
+ switch (colorspace) {
+ case 'cmyk':
+ defaultColor = 'FF000000';
+ break;
+ case 'gray':
+ defaultColor = '00';
+ break;
+ case 'rgb':
+ default:
+ defaultColor = '1777d1';
+ break;
+ }
+
+ return defaultColor;
+}
+
+/**
+ * Convert given color code int color model object
+ *
+ * ColorModel consists of: {
+ * color: number,
+ * colorspace: string {'rgb', 'cmyk', 'gray'},
+ * (colorspace == 'rgb') r, g, b
+ * (colorspace == 'cmyk') c, m, y, k
+ * (colorspace == 'gray') gray
+ * }
+ *
+ * where r,g,b,c,m,y,k,gray are all numbers between 0 and 1
+ *
+ * @param {string} code the color encoding as HexColor
+ * @param {string} colorspace the name of the colorspace of given color code
+ * @param {string} colorName the name to be associated with given color code
+ * @returns {any} the color model
+ */
+function toColorModel(self, code, colorspace, colorName) {
+ const cmodel = {};
+ let color = hexToArray(code);
+
+ cmodel.color = parseInt(code, 16);
+
+ // The initial decider of color space is length of given 'code'.
+
+ switch (color.length) {
+ default:
+ color = hexToArray(_defaultColor('rgb'));
+ // purposely want to fall through to 'rgb' case below.
+ case 3:
+ cmodel.colorspace = 'rgb';
+ cmodel.r = color[0];
+ cmodel.g = color[1];
+ cmodel.b = color[2];
+ break;
+
+ case 4:
+ cmodel.colorspace = 'cmyk';
+ cmodel.c = color[0];
+ cmodel.m = color[1];
+ cmodel.y = color[2];
+ cmodel.k = color[3];
+ break;
+
+ case 1:
+ cmodel.colorspace = 'gray';
+ cmodel.gray = color[0];
+ break;
+ }
+
+ // When creating a separation color space,
+ // use the colorspace from above as the
+ // alternative color transformation when
+ // the named color is unavailable.
+ if (colorspace === 'separation' && colorName !== '') {
+ cmodel.colorspace = colorspace;
+ cmodel.colorName = colorName;
+ cmodel.colorspaceId = createColorSpaces(self, colorName, color);
+ }
+
+ return cmodel;
+}
+
+/**
+ * Convert percentage string into hex string (x / 100 * 255)
+ *
+ * @param {string} code numbers separated by commas with values ranging between 0-100.
+ * @returns {string} massaged hexadecimal string that can be used as input to hexToArray.
+ */
+function percentToHex(code) {
+ return arrayToHex(code.split(',').map( x => Math.round(x * 2.55)) );
+}
+
+/**
+ * Transform color code into numeric value or colorModel
+ *
+ * @param code color specification in form of HexColor (string, begins with '#'),
+ * DecimalColor (1, 3, or 4 element array with values between 0-255),
+ * PercentColor (string, begins with '%' followed by values separated
+ * by commas with values between 0-100)
+ */
+exports._transformColor = function _transformColor(code = '', opt = {}) {
+ this.knownColors = this.knownColors || {};
+ let colorspace = opt.colorspace || 'rgb';
+ let wantColorModel = opt.wantColorModel || false;
+ let colorName = opt.colorName || '';
+ let defaultColor = _defaultColor(colorspace);
+ let transformation;
+
+ if (Array.isArray(code)) {
+ code = arrayToHex(code);
+ } else if (code.startsWith('#')){
+ code = code.replace('#', '');
+ } else if (code.startsWith('%')) {
+ code = percentToHex(code.replace('%', ''));
+ } else if (code !== '') {
+ let color = this.knownColors[colorspace][code]; // assuming code is a color name
+ if (!color) {
+ color = '';
+ code = defaultColor;
+ } else {
+ colorName = code;
+
+ // The following handles known colors in hex form,
+ // with or without initial '#' and percent form.
+ if (color.startsWith('#')) {
+ code = color.replace('#','');
+ } else if (color.startsWith('%')) {
+ code = percentToHex(color.replace('%', ''));
+ } else {
+ code = color;
+ }
+ }
+ }
+
+ // When colorspace is not explicitly given,
+ // use size of value to determine colorspace.
+ if (!opt.colorspace) {
+ colorspace = {2:'gray', 6:'rgb', 8:'cmyk'}[`${code.length}`] || 'rgb';
+ defaultColor = _defaultColor(colorspace);
+ }
+
+ // Suppply default color:
+ // when colorspace is given and given color code does not have appropriate length, or
+ // when colorspace is missing, verify allowable hex value sizes for rgb, cmyk, or gray.
+ if ( ['rgb','cmyk','gray'].includes(colorspace) && code.length != defaultColor.length ||
+ ! [2,6,8].includes(code.toString().length)) {
+ code = defaultColor;
+ }
+
+ if (wantColorModel) {
+ transformation = toColorModel(this, code, colorspace, colorName);
+ if ( colorName && !this.knownColors[colorspace][colorName]) {
+ this.chroma(colorName, code, colorspace);
+ }
+ } else {
+ transformation = parseInt(`0x${code.toUpperCase()}`, 16);
+ }
+
+ return transformation;
+};
+
+function arrayToHex(color = []) {
+ let code = '';
+ color.forEach((item) => {
+ let hex = item.toString(16);
+ hex = (hex.length == 1) ? '0' + hex : hex;
+ code += hex;
+ });
+ return code;
+}
+
+function hexToArray(hex) {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})?([a-f\d]{2})?([a-f\d]{2})?$/i.exec(hex);
+ return result
+ .reduce((array, item, index) => {
+ if (index >= 1 && index <= (hex.length / 2)) {
+ array.push(parseInt(item, 16) / 255);
+ }
+ return array;
+ }, []);
+}
+
+exports._colorNumberToRGB = (bigint) => {
+ if (!bigint) {
+ return {
+ r: 0,
+ g: 0,
+ b: 0
+ };
+ } else {
+ return {
+ r: (bigint >> 16) & 255,
+ g: (bigint >> 8) & 255,
+ b: bigint & 255
+ };
+ }
+};
@@ -445,7 +445,7 @@ colors.js
- Documentation generated by JSDoc 3.6.3 on Wed Oct 02 2019 12:00:26 GMT-0700 (Pacific Daylight Time) using the docdash theme.
+ Documentation generated by JSDoc 3.5.5 on Fri Nov 01 2019 20:41:12 GMT-0400 (Eastern Daylight Time) using the docdash theme.
diff --git a/docs/encrypt.js.html b/docs/encrypt.js.html
index 1cf79ad..6e3a6ad 100644
--- a/docs/encrypt.js.html
+++ b/docs/encrypt.js.html
@@ -37,112 +37,112 @@ encrypt.js
- const hummus = require('hummus');
-const fs = require('fs');
-
-
-/**
- * Encryption user access permissions
- *
- * This function supplies the numeric value for the encrypt function's 'userProtectionFlag'
- * option. When no argument is given, the default 'print' value is used.
- *
- * @name permission
- * @function
- * @memberof Recipe
- * @param {string} flags from the list print, modify, copy, edit, fillform, extract, assemble, and printbest
- * More than one may be specified by using a comma to separate the names in the input string.
- */
-exports.permission = function permission(flags='print') {
-
- // https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf
-
- const userAccessPermissions = { // see table on page 61 of above document
- print : 1<<2, // allow printing
- modify : 1<<3, // allow template creation, signing, filling form fields
- copy : 1<<4, // allow content copying and copying for accessibility
- edit : 1<<5, // allow commenting
- fillform : 1<<8, // allow filling of form fields
- extract : 1<<9, // allow content copying for accessibility
- assemble : 1<<10, // unused
- printbest: 1<<11 // allow high resolution printing when 'print' is allowed
- };
-
- const perms = flags.split(',').map((x)=>{return x.trim();});
- let access = 0;
- perms.forEach((perm) => {
- if (!userAccessPermissions[perm]) {
- throw new Error(`Unknown user access permission (${perm})`);
- }
- access += userAccessPermissions[perm];
- });
-
- return access;
-};
-
-exports._getEncryptOptions = function _getEncryptOptions(options, addPermissions=true) {
- const encryptOptions = {};
-
- const password = options.password || options.ownerPassword;
- if (password) {
- encryptOptions.password = password;
- encryptOptions.ownerPassword = password;
- }
-
- if (options.userPassword) {
- encryptOptions.userPassword = options.userPassword;
- if (!encryptOptions.password) {
- encryptOptions.password = options.userPassword;
- }
- }
-
- if (addPermissions) {
- if (options.userProtectionFlag) {
- encryptOptions.userProtectionFlag = options.userProtectionFlag;
- }
- }
-
- // Only attach encryption mechanism when attributes
- // have been explicitly given in the incoming options.
- if (Object.keys(encryptOptions) > 0 && !encryptOptions.userPassword) {
- encryptOptions.userPassword = '';
- if (!encryptOptions.userProtectionFlag) {
- encryptOptions.userProtectionFlag = this.permission();
- }
- }
-
- return encryptOptions;
-};
-
-/**
- * Encrypt the pdf
- * @name encrypt
- * @function
- * @memberof Recipe
- * @param {Object} options - The options
- * @param {string} [options.password] - The permission password.
- * @param {string} [options.ownerPassword] - The password for editing.
- * @param {string} [options.userPassword] - The password for viewing & encryption.
- * @param {number} [options.userProtectionFlag] - The flag for the security level.
- */
-exports.encrypt = function encrypt(options = {}) {
- this.needToEncrypt = true;
- this.encryption_ = this._getEncryptOptions(options);
-
- return this;
-};
-
-// http://pdfhummus.com/post/147451287581/hummus-1058-and-pdf-writer-updates-encryption
-exports._encrypt = function _encrypt() {
- if (!this.encryption_) {
- return;
- }
-
- const tmp = this.output + '.tmp.pdf';
- fs.renameSync(this.output, tmp);
- hummus.recrypt(tmp, this.output, this.encryption_);
- fs.unlinkSync(tmp);
-};
+ const hummus = require('hummus');
+const fs = require('fs');
+
+
+/**
+ * Encryption user access permissions
+ *
+ * This function supplies the numeric value for the encrypt function's 'userProtectionFlag'
+ * option. When no argument is given, the default 'print' value is used.
+ *
+ * @name permission
+ * @function
+ * @memberof Recipe
+ * @param {string} flags from the list print, modify, copy, edit, fillform, extract, assemble, and printbest
+ * More than one may be specified by using a comma to separate the names in the input string.
+ */
+exports.permission = function permission(flags='print') {
+
+ // https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf
+
+ const userAccessPermissions = { // see table on page 61 of above document
+ print : 1<<2, // allow printing
+ modify : 1<<3, // allow template creation, signing, filling form fields
+ copy : 1<<4, // allow content copying and copying for accessibility
+ edit : 1<<5, // allow commenting
+ fillform : 1<<8, // allow filling of form fields
+ extract : 1<<9, // allow content copying for accessibility
+ assemble : 1<<10, // unused
+ printbest: 1<<11 // allow high resolution printing when 'print' is allowed
+ };
+
+ const perms = flags.split(',').map((x)=>{return x.trim();});
+ let access = 0;
+ perms.forEach((perm) => {
+ if (!userAccessPermissions[perm]) {
+ throw new Error(`Unknown user access permission (${perm})`);
+ }
+ access += userAccessPermissions[perm];
+ });
+
+ return access;
+};
+
+exports._getEncryptOptions = function _getEncryptOptions(options, addPermissions=true) {
+ const encryptOptions = {};
+
+ const password = options.password || options.ownerPassword;
+ if (password) {
+ encryptOptions.password = password;
+ encryptOptions.ownerPassword = password;
+ }
+
+ if (options.userPassword) {
+ encryptOptions.userPassword = options.userPassword;
+ if (!encryptOptions.password) {
+ encryptOptions.password = options.userPassword;
+ }
+ }
+
+ if (addPermissions) {
+ if (options.userProtectionFlag) {
+ encryptOptions.userProtectionFlag = options.userProtectionFlag;
+ }
+ }
+
+ // Only attach encryption mechanism when attributes
+ // have been explicitly given in the incoming options.
+ if (Object.keys(encryptOptions) > 0 && !encryptOptions.userPassword) {
+ encryptOptions.userPassword = '';
+ if (!encryptOptions.userProtectionFlag) {
+ encryptOptions.userProtectionFlag = this.permission();
+ }
+ }
+
+ return encryptOptions;
+};
+
+/**
+ * Encrypt the pdf
+ * @name encrypt
+ * @function
+ * @memberof Recipe
+ * @param {Object} options - The options
+ * @param {string} [options.password] - The permission password.
+ * @param {string} [options.ownerPassword] - The password for editing.
+ * @param {string} [options.userPassword] - The password for viewing & encryption.
+ * @param {number} [options.userProtectionFlag] - The flag for the security level.
+ */
+exports.encrypt = function encrypt(options = {}) {
+ this.needToEncrypt = true;
+ this.encryption_ = this._getEncryptOptions(options);
+
+ return this;
+};
+
+// http://pdfhummus.com/post/147451287581/hummus-1058-and-pdf-writer-updates-encryption
+exports._encrypt = function _encrypt() {
+ if (!this.encryption_) {
+ return;
+ }
+
+ const tmp = this.output + '.tmp.pdf';
+ fs.renameSync(this.output, tmp);
+ hummus.recrypt(tmp, this.output, this.encryption_);
+ fs.unlinkSync(tmp);
+};
@@ -155,7 +155,7 @@ encrypt.js
- Documentation generated by JSDoc 3.6.3 on Wed Oct 02 2019 12:00:26 GMT-0700 (Pacific Daylight Time) using the docdash theme.
+ Documentation generated by JSDoc 3.5.5 on Fri Nov 01 2019 20:41:12 GMT-0400 (Eastern Daylight Time) using the docdash theme.
diff --git a/docs/font.js.html b/docs/font.js.html
index 8ce9ece..b7c1d65 100644
--- a/docs/font.js.html
+++ b/docs/font.js.html
@@ -37,42 +37,42 @@ font.js
- const fs = require('fs');
-const path = require('path');
-/**
- * Register a custom font
- * @name registerFont
- * @function
- * @memberof Recipe
- * @param {string} fontName - The font name will be used in text
- * @param {string} fontSrcPath - The path to the font file.
- */
-exports.registerFont = function registerFont(fontName = '', fontSrcPath = '') {
- return this._registerFont(fontName, fontSrcPath);
-};
-
-exports._loadFonts = function _loadFonts(fontSrcPath) {
- const fontTypes = ['.ttf', '.ttc', '.otf'];
- const fontPaths = (typeof fontSrcPath === 'string') ? [fontSrcPath] : fontSrcPath;
-
- for (let fpath of fontPaths) {
- fs
- .readdirSync(fpath)
- .filter((file) => {
- return fontTypes.includes(path.extname(file).toLowerCase());
- })
- .forEach((file) => {
- const fontName = path.basename(file, path.extname(file));
- return this._registerFont(fontName, path.join(fpath, file));
- });
- }
-};
-
-exports._registerFont = function _registerFont(fontName, fontSrcPath) {
- this.fonts = this.fonts || {};
- // check fontSrcPath
- this.fonts[fontName.toLowerCase()] = fontSrcPath;
-};
+ const fs = require('fs');
+const path = require('path');
+/**
+ * Register a custom font
+ * @name registerFont
+ * @function
+ * @memberof Recipe
+ * @param {string} fontName - The font name will be used in text
+ * @param {string} fontSrcPath - The path to the font file.
+ */
+exports.registerFont = function registerFont(fontName = '', fontSrcPath = '') {
+ return this._registerFont(fontName, fontSrcPath);
+};
+
+exports._loadFonts = function _loadFonts(fontSrcPath) {
+ const fontTypes = ['.ttf', '.ttc', '.otf'];
+ const fontPaths = (typeof fontSrcPath === 'string') ? [fontSrcPath] : fontSrcPath;
+
+ for (let fpath of fontPaths) {
+ fs
+ .readdirSync(fpath)
+ .filter((file) => {
+ return fontTypes.includes(path.extname(file).toLowerCase());
+ })
+ .forEach((file) => {
+ const fontName = path.basename(file, path.extname(file));
+ return this._registerFont(fontName, path.join(fpath, file));
+ });
+ }
+};
+
+exports._registerFont = function _registerFont(fontName, fontSrcPath) {
+ this.fonts = this.fonts || {};
+ // check fontSrcPath
+ this.fonts[fontName.toLowerCase()] = fontSrcPath;
+};
@@ -85,7 +85,7 @@ font.js
- Documentation generated by JSDoc 3.6.3 on Wed Oct 02 2019 12:00:26 GMT-0700 (Pacific Daylight Time) using the docdash theme.
+ Documentation generated by JSDoc 3.5.5 on Fri Nov 01 2019 20:41:12 GMT-0400 (Eastern Daylight Time) using the docdash theme.
diff --git a/docs/global.html b/docs/global.html
index d9d2a40..cfea0ee 100644
--- a/docs/global.html
+++ b/docs/global.html
@@ -127,7 +127,7 @@