@syncfusion/ej2-pdf
Version:
Feature-rich JavaScript PDF library with built-in support for loading and manipulating PDF document.
1,980 lines • 83 kB
JavaScript
import { _PdfDictionary, _PdfReference, _PdfName } from './pdf-primitives';
import { _areArrayEqual, _checkRotation, _getInheritableProperty, _getPageIndex, _isNullOrUndefined, _stringToBytes } from './utils';
import { PdfAnnotationCollection } from './annotations/annotation-collection';
import { PdfGraphics, PdfBrush } from './graphics/pdf-graphics';
import { _PdfBaseStream, _PdfContentStream } from './base-stream';
import { PdfRotationAngle, PdfDestinationMode, PdfFormFieldsTabOrder, PdfPageOrientation, PdfLayoutBreakType, PdfLayoutType } from './enumerator';
import { PdfTemplate } from './graphics/pdf-template';
import { PdfLayoutResult, _PdfLayoutParameters, PdfLayoutFormat } from './graphics/pdf-layouter';
import { PdfStringFormat } from './fonts/pdf-string-format';
import { _PdfStringLayouter } from './fonts/string-layouter';
/**
* Represents a page loaded from the PDF document.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
var PdfPage = /** @class */ (function () {
/**
* Represents a loaded page of the PDF document.
*
* @private
* @param {_PdfCrossReference} crossReference Cross reference object.
* @param {number} pageIndex page index.
* @param {_PdfDictionary} dictionary page Dictionary.
* @param {_PdfReference} reference page reference.
*/
function PdfPage(crossReference, pageIndex, dictionary, reference) {
/**
* Indicates whether annotations have been parsed.
*
* @private
*/
this._isAnnotationParsed = false;
/**
* Indicates whether this page is newly created.
*
* @private
*/
this._isNew = false;
/**
* Indicates whether this page is a duplicate of another.
*
* @private
*/
this._isDuplicate = false;
/**
* Indicates whether the current operation pertains to a line annotation.
*
* @private
*/
this._isLineAnnotation = false;
/**
* Indicates whether the page content was accessed before template rendering started.
*
* @private
*/
this._accessedBeforeTemplate = false;
/**
* Indicates whether all templates have been rendered for the document or section.
*
* @private
*/
this._templatesRendered = false;
this._pageIndex = pageIndex;
this._pageDictionary = dictionary;
this._crossReference = crossReference;
this._ref = reference;
}
Object.defineProperty(PdfPage.prototype, "annotations", {
/**
* Gets the collection of the page's annotations (Read only).
*
* @returns {PdfAnnotationCollection} Annotation collection.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Gets the annotation collection
* let annotations: PdfAnnotationCollection = page.annotations;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
if (typeof this._annotations === 'undefined') {
if (this._pageDictionary && this._pageDictionary.has('Annots')) {
var annots = this._getProperty('Annots');
if (_isNullOrUndefined(annots) && Array.isArray(annots)) {
var widgets_1;
if (this._crossReference._document._catalog._catalogDictionary.has('AcroForm')) {
widgets_1 = this._crossReference._document.form._parseWidgetReferences();
}
if (widgets_1 && widgets_1.length > 0) {
var validAnnotations_1 = [];
annots.forEach(function (entry) {
if (widgets_1.indexOf(entry) === -1) {
validAnnotations_1.push(entry);
}
});
this._annotations = new PdfAnnotationCollection(validAnnotations_1, this._crossReference, this);
}
else {
this._annotations = new PdfAnnotationCollection(annots, this._crossReference, this);
}
}
}
if (typeof this._annotations === 'undefined') {
this._annotations = new PdfAnnotationCollection([], this._crossReference, this);
}
this._annotations._getAnnotations();
}
return this._annotations;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPage.prototype, "size", {
/**
* Gets the size of the page (Read only).
*
* @returns {Size} The size of the PDF page.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Gets the width and height of the PDF page as number array
* let size: Size = page.size;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
if (typeof this._size === 'undefined' || typeof this._size.width === 'undefined' || typeof this._size.height === 'undefined') {
var mBox = _getInheritableProperty(this._pageDictionary, 'MediaBox', false, true, 'Parent', 'P');
var cBox = _getInheritableProperty(this._pageDictionary, 'CropBox', false, true, 'Parent', 'P');
var width = 0;
var height = 0;
var rotate = this._pageDictionary && this._pageDictionary.has('Rotate')
? _getInheritableProperty(this._pageDictionary, 'Rotate', false, true, 'Parent')
: 0;
cBox = this._parseBoxValues(cBox, 'CropBox');
mBox = this._parseBoxValues(mBox, 'MediaBox');
if (cBox && rotate !== null && typeof rotate !== 'undefined') {
width = cBox[2] - cBox[0];
height = cBox[3] - cBox[1];
var isValidCropBox = !(mBox && (mBox[2] - mBox[0]) < width);
if (!(((rotate === 0 || rotate === 180) && (width < height)) ||
((rotate === 90 || rotate === 270) && (width > height) || isValidCropBox)) && (rotate === 0 && mBox)) {
width = mBox[2] - mBox[0];
height = mBox[3] !== 0 ? mBox[3] - mBox[1] : mBox[1];
}
}
else if (mBox) {
width = mBox[2] - mBox[0];
height = mBox[3] !== 0 ? mBox[3] - mBox[1] : mBox[1];
}
else {
this._pageDictionary.update('MediaBox', [0, 0, 612, 792]);
width = 612;
height = 792;
}
this._size = { width: Math.abs(width), height: Math.abs(height) };
}
return this._size;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPage.prototype, "rotation", {
/**
* Gets the rotation angle of the page (Read only).
*
* @returns {PdfRotationAngle} Page rotation angle.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Gets the rotation angle of the page
* let rotation: PdfRotationAngle = page.rotation;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
var angle = 0;
if (typeof this._rotation === 'undefined') {
angle = _getInheritableProperty(this._pageDictionary, 'Rotate', false, true, 'Parent');
if (angle < 0) {
angle += 360;
}
this._rotation = (typeof angle !== 'undefined') ? ((angle / 90) % 4) : PdfRotationAngle.angle0;
}
return this._rotation;
},
/**
* Sets the rotation angle of the PDF page
*
* @param {PdfRotationAngle} value rotation angle.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Sets the rotation angle of the PDF page
* page.rotate = PdfRotationAngle.angle90;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (!this._isNew) {
this._rotation = value;
var rotate = Math.floor(this._rotation) * 90;
if (rotate >= 360) {
rotate = rotate % 360;
}
this._pageDictionary.update('Rotate', rotate);
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPage.prototype, "tabOrder", {
/**
* Gets the tab order of a PDF form field.
*
* @returns {PdfFormFieldsTabOrder} tab order.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Gets the tab order of a PDF form field.
* let tabOrder: PdfFormFieldsTabOrder = page.tabOrder;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._obtainTabOrder();
},
/**
* Sets the tab order of a PDF form field.
*
* @param {PdfFormFieldsTabOrder} value tab order.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Sets the tab order of a PDF form field.
* page.tabOrder = PdfFormFieldsTabOrder.row;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
this._tabOrder = value;
var tabs = '';
if (this._tabOrder !== PdfFormFieldsTabOrder.none) {
if (this._tabOrder === PdfFormFieldsTabOrder.row) {
tabs = 'R';
}
else if (this._tabOrder === PdfFormFieldsTabOrder.column) {
tabs = 'C';
}
else if (this._tabOrder === PdfFormFieldsTabOrder.structure) {
tabs = 'S';
}
}
this._pageDictionary.update('Tabs', _PdfName.get(tabs));
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPage.prototype, "cropBox", {
/**
* Gets the bounds that define the area intended for display or printing in the PDF viewer application (Read only).
*
* @returns {number[]} Page size as number array.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Gets the cropBox of the PDF page as number array
* let cropBox: number[] = page.cropBox;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
if (typeof this._cBox === 'undefined') {
this._cBox = _getInheritableProperty(this._pageDictionary, 'CropBox', false, true, 'Parent', 'P');
}
if (typeof this._cBox === 'undefined') {
this._cBox = [0, 0, 0, 0];
}
return this._cBox;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPage.prototype, "mediaBox", {
/**
* Gets the size that specify the width and height of the page (Read only).
*
* @returns {number[]} Page size as number array.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Gets the mediaBox of the PDF page as number array
* let mediaBox: number[] = page.mediaBox;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
if (typeof this._mBox === 'undefined') {
this._mBox = _getInheritableProperty(this._pageDictionary, 'MediaBox', false, true, 'Parent', 'P');
}
if (typeof this._mBox === 'undefined') {
this._mBox = [0, 0, 0, 0];
}
return this._mBox;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPage.prototype, "orientation", {
/**
* Gets the orientation of the page (Read only).
*
* @returns {PdfPageOrientation} Page orientation.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Gets the orientation of the PDF page
* let orientation: number[] = page.orientation;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
if (typeof this._orientation === 'undefined') {
if (typeof this.size !== 'undefined') {
var size = this.size;
if (size.width > size.height) {
this._orientation = PdfPageOrientation.landscape;
}
else {
this._orientation = PdfPageOrientation.portrait;
}
}
}
return this._orientation;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPage.prototype, "_origin", {
/**
* Gets the origin coordinates derived from the MediaBox.
*
* @returns {number[]} Origin as a two-element array [x, y].
*/
get: function () {
if (typeof this._o === 'undefined' || (this._o[0] === 0 && this._o[1] === 0)) {
this._o = [this.mediaBox[0], this._mBox[1]];
}
return this._o;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPage.prototype, "graphics", {
/**
* Gets the graphics of the page (Read only).
*
* @returns {PdfGraphics} Page graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access first page
* let page: PdfPage = document.getPage(0);
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* //Create a new pen.
* let pen: PdfPen = new PdfPen({r: 0, g: 0, b: 0}, 1);
* //Draw line on the page graphics.
* graphics.drawLine(pen, {x: 10, y: 10}, {x: 100, y: 100});
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
if (typeof this._g === 'undefined' || this._needInitializeGraphics) {
this._parseGraphics();
}
else {
if (this._crossReference && this._crossReference._document &&
this._crossReference._document._hasTemplateContentValue && !this._templatesRendered &&
!this._crossReference._document._templateRenderingStarted) {
this._accessedBeforeTemplate = true;
}
}
return this._g;
},
enumerable: true,
configurable: true
});
PdfPage.prototype.drawTextElement = function (element, locationOrBounds) {
if (typeof element === 'undefined' || element === null) {
throw new Error('PdfTextElement cannot be null or undefined');
}
if (typeof element.text !== 'string' || element.text.length === 0) {
throw new Error('PdfTextElement.text must be a non-empty string');
}
if (typeof element.font === 'undefined' || element.font === null) {
throw new Error('PdfTextElement.font is required');
}
if (typeof element.layoutFormat !== 'undefined' && element.layoutFormat !== null && !(element.layoutFormat instanceof PdfLayoutFormat)) {
throw new Error('PdfTextElement.layoutFormat must be an instance of PdfLayoutFormat');
}
var bounds = this.graphics._isRectangle(locationOrBounds) ?
{ x: locationOrBounds.x, y: locationOrBounds.y, width: locationOrBounds.width, height: locationOrBounds.height } :
{ x: locationOrBounds.x, y: locationOrBounds.y, width: 0, height: 0 };
var brush = element.brush ? element.brush : new PdfBrush({ r: 0, g: 0, b: 0 });
if (element.layoutFormat || (typeof bounds.width === 'number' && typeof bounds.height === 'number' && (bounds.width > 0 || bounds.height > 0))) {
var params = new _PdfLayoutParameters();
var actualBounds = this._getActualBounds(this._pageSettings);
if (bounds.y < 0) {
bounds.y = 0;
}
if (bounds.height === 0) {
bounds.height = actualBounds[3] - bounds.y;
}
else {
var maxHeight = actualBounds[3] - bounds.y;
if (bounds.height > maxHeight) {
bounds.height = maxHeight;
}
}
params._page = this;
params._bounds = [bounds.x, bounds.y, bounds.width, bounds.height];
params._format = element.layoutFormat ? element.layoutFormat : new PdfLayoutFormat();
params._graphics = this.graphics;
return this._layoutTextElement(params, element);
}
this.graphics.drawString(element.text, element.font, bounds, element.pen, brush, element.stringFormat);
return new PdfLayoutResult(this, bounds);
};
/**
* Adds a widget annotation reference to the page's Annots array.
*
* @private
* @param {_PdfReference} reference Widget annotation reference to add.
* @returns {void} nothing.
*/
PdfPage.prototype._addWidget = function (reference) {
var annots;
if (this._pageDictionary.has('Annots')) {
var annotsRef = this._pageDictionary.getRaw('Annots'); // eslint-disable-line
annots = this._getProperty('Annots');
if (annotsRef && annotsRef instanceof _PdfReference) {
delete this._pageDictionary._map.Annots;
this._pageDictionary.update('Annots', annots);
}
}
if (annots && Array.isArray(annots)) {
annots.push(reference);
}
else {
this._pageDictionary.update('Annots', [reference]);
}
this._pageDictionary._updated = true;
};
/**
* Resolves an inheritable page property from the page tree.
*
* @private
* @param {string} key The dictionary key to fetch.
* @param {boolean} [getArray=false] Whether to return an array value as-is.
* @returns {any} Resolved value or merged dictionary.
*/
PdfPage.prototype._getProperty = function (key, getArray) {
if (getArray === void 0) { getArray = false; }
var value = _getInheritableProperty(this._pageDictionary, key, getArray, false); // eslint-disable-line
if (!Array.isArray(value)) {
return value;
}
if (value.length === 1 || !(value[0] instanceof _PdfDictionary)) {
return value[0];
}
return _PdfDictionary.merge(this._crossReference, value);
};
/**
* Initializes content streams and graphics for drawing operations.
*
* @private
* @returns {void} nothing.
*/
PdfPage.prototype._parseGraphics = function () {
this._loadContents();
var saveStream = new _PdfContentStream([32, 113, 32, 10]);
var saveReference = this._crossReference._getNextReference();
this._crossReference._cacheMap.set(saveReference, saveStream);
this._contents.splice(0, 0, saveReference);
var restoreStream = new _PdfContentStream([32, 81, 32, 10]);
var restoreReference = this._crossReference._getNextReference();
this._crossReference._cacheMap.set(restoreReference, restoreStream);
this._contents.push(restoreReference);
var contentStream = new _PdfContentStream([]);
var contentReference = this._crossReference._getNextReference();
this._crossReference._cacheMap.set(contentReference, contentStream);
this._contents.push(contentReference);
this._pageDictionary.set('Contents', this._contents);
this._pageDictionary._updated = true;
this._initializeGraphics(contentStream);
};
/**
* Loads the page's Contents entry into the internal reference list.
*
* @private
* @returns {void} nothing.
*/
PdfPage.prototype._loadContents = function () {
var contents = this._pageDictionary.getRaw('Contents'); // eslint-disable-line
var ref;
if (contents !== null && typeof contents !== 'undefined' && contents instanceof _PdfReference) {
ref = contents;
contents = this._crossReference._fetch(ref);
}
if (contents && contents instanceof _PdfBaseStream) {
this._contents = [ref];
}
else if (contents && Array.isArray(contents)) {
this._contents = contents;
}
else {
this._contents = [];
}
};
/**
* Creates the graphics context and applies initial transforms and rotation.
*
* @private
* @param {_PdfContentStream} stream Target content stream to draw into.
* @returns {void} nothing.
*/
PdfPage.prototype._initializeGraphics = function (stream) {
var isInvalidCase = false;
var llx = 0;
var lly = 0;
var urx = 0;
var ury = 0;
var size = this.size;
var mbox = this.mediaBox;
if (mbox && mbox.length >= 4) {
llx = mbox[0];
lly = mbox[1];
urx = mbox[2];
ury = mbox[3];
}
var cbox;
if (this._pageDictionary.has('CropBox')) {
cbox = this.cropBox;
if (cbox && cbox.length >= 4) {
var cx = cbox[0];
var cy = cbox[1];
var crx = cbox[2];
var cry = cbox[3];
var isValid = (cx < 0 || cy < 0 || crx < 0 || cry < 0) &&
(Math.floor(Math.abs(cy)) === Math.floor(Math.abs(size.height))) &&
(Math.floor(Math.abs(cx)) === Math.floor(Math.abs(size.width)));
if (isValid) {
this._g = new PdfGraphics({ width: Math.max(cx, crx), height: Math.max(cy, cry) }, stream, this._crossReference, this);
}
else {
this._g = new PdfGraphics(size, stream, this._crossReference, this);
this._g._cropBox = cbox;
}
}
else {
this._g = new PdfGraphics(size, stream, this._crossReference, this);
}
}
else if ((llx < 0 || lly < 0 || urx < 0 || ury < 0) &&
(Math.floor(Math.abs(lly)) === Math.floor(Math.abs(size.height))) &&
(Math.floor(Math.abs(urx)) === Math.floor(Math.abs(size.width)))) {
var width = Math.max(llx, urx);
var height = Math.max(lly, ury);
if (width <= 0 || height <= 0) {
isInvalidCase = true;
if (llx < 0) {
llx = -llx;
}
if (lly < 0) {
lly = -lly;
}
if (urx < 0) {
urx = -urx;
}
if (ury < 0) {
ury = -ury;
}
width = Math.max(llx, urx);
height = Math.max(lly, ury);
}
this._g = new PdfGraphics({ width: width, height: height }, stream, this._crossReference, this);
}
else {
this._g = new PdfGraphics(size, stream, this._crossReference, this);
}
if (this._pageDictionary.has('MediaBox')) {
this._g._mediaBoxUpperRightBound = isInvalidCase ? -lly : ury;
}
this._graphicsState = this._g.save();
var origin = this._origin;
if ((origin[0] >= 0 && origin[1] >= 0) || Math.sign(origin[0]) !== Math.sign(origin[1])) {
this._g._initializeCoordinates();
}
else {
this._g._initializeCoordinates(this);
}
if (!this._isNew) {
var rotation = this.rotation;
if (!Number.isNaN(rotation) && (rotation !== PdfRotationAngle.angle0 || this._pageDictionary.has('Rotate'))) {
var rotate = void 0;
if (this._pageDictionary.has('Rotate')) {
rotate = this._pageDictionary.get('Rotate');
}
else {
rotate = rotation * 90;
}
var clip = this._g._clipBounds;
if (rotate === 90) {
this._g.translateTransform({ x: 0, y: size.height });
this._g.rotateTransform(-90);
this._g._clipBounds = [clip[0], clip[1], size.width, size.height];
}
else if (rotate === 180) {
this._g.translateTransform({ x: size.width, y: size.height });
this._g.rotateTransform(-180);
}
else if (rotate === 270) {
this._g.translateTransform({ x: size.width, y: 0 });
this._g.rotateTransform(-270);
this._g._clipBounds = [clip[0], clip[1], size.height, size.width];
}
}
}
if (this._isNew && this._pageSettings && !this._isLineAnnotation) {
var clipBounds = this._getActualBounds(this._pageSettings);
if (!this._crossReference._document._hasTemplateContentValue) {
this._g._clipTranslateMargins(clipBounds);
}
else {
var bounds = [
clipBounds[0],
clipBounds[1],
this._pageSettings.margins._left,
this._pageSettings.margins._top,
this._pageSettings.margins._right,
this._pageSettings.margins._bottom
];
this._g._clipTranslateMarginsWithBounds(bounds);
}
}
this._needInitializeGraphics = false;
};
/**
* Computes the effective drawable content bounds of the page by excluding
* both page margins and the space reserved for document templates on all sides.
*
* @private
* @param {PdfPageSettings} pageSettings - The page settings that define the page size and margins.
* @param {boolean} [includeMargins] - Specifies whether the calculation should use the full page size
* (including margins) or the already adjusted content size.
* @returns {number[]} An array representing the computed bounds in the format:
* [x, y, width, height], where:
* - x: left offset including margin and left template space
* - y: top offset including margin and top template space
* - width: usable width after excluding left and right template spaces
* - height: usable height after excluding top and bottom template spaces.
*/
PdfPage.prototype._getActualBounds = function (pageSettings, includeMargins) {
var actualSize = includeMargins ? [pageSettings.size.width, pageSettings.size.height] : pageSettings._getActualSize();
var templateReserved = this._getTemplateReservedSpace(includeMargins);
return [
pageSettings.margins.left + templateReserved[3],
pageSettings.margins.top + templateReserved[0],
actualSize[0] - templateReserved[3] - templateReserved[1],
actualSize[1] - templateReserved[0] - templateReserved[2]
];
};
/**
* Calculates the effective template bounds of the page by excluding space
* reserved for other document templates on each side.
*
* @private
* @param {PdfPageSettings} pageSettings - The page settings that determine the actual page size.
* @param {boolean} [includeMargins] - Specifies whether page margins should be included in the reserved space calculation.
* @returns {number[]} An array representing the effective bounds in the format: * [x, y, width, height], where:
* - x: left offset
* - y: top offset
* - width: available width after excluding left and right reserved space
* - height: available height after excluding top and bottom reserved space
*/
PdfPage.prototype._getActualTemplateBounds = function (pageSettings, includeMargins) {
var actualSize = pageSettings._getActualSize();
var templateReserved = this._getTemplateReservedSpace(includeMargins);
return [
templateReserved[3],
templateReserved[0],
actualSize[0] - templateReserved[3] - templateReserved[1],
actualSize[1] - templateReserved[0] - templateReserved[2]
];
};
/**
* Calculates the space reserved by document templates on all four edges.
*
* @private
* @param {boolean} includeMargins - Indicates whether page margins should be
* included along with template space in the calculation.
* @returns {number[]} An array representing the reserved space in the order: [top, right, bottom, left], in page units.
*/
PdfPage.prototype._getTemplateReservedSpace = function (includeMargins) {
var margin = includeMargins ? true : false;
var top = this._crossReference._document._getTopIndentHeight(this, margin);
var right = this._crossReference._document._getRightIndentWidth(this, margin);
var bottom = this._crossReference._document._getBottomIndentHeight(this, margin);
var left = this._crossReference._document._getLeftIndentWidth(this, margin);
return [top, right, bottom, left];
};
/**
* Fetches or creates the resources dictionary for the page.
*
* @private
* @returns {_PdfDictionary} Resources dictionary.
*/
PdfPage.prototype._fetchResources = function () {
if (typeof this._resourceObject === 'undefined') {
if (this._pageDictionary && this._pageDictionary.has('Resources')) {
var obj = this._pageDictionary.getRaw('Resources'); // eslint-disable-line
if (obj !== null && typeof obj !== 'undefined' && obj instanceof _PdfReference) {
this._hasResourceReference = true;
this._resourceObject = this._crossReference._fetch(obj);
}
else if (obj && obj instanceof _PdfDictionary) {
this._resourceObject = obj;
}
}
else {
this._resourceObject = new _PdfDictionary(this._crossReference);
this._pageDictionary.update('Resources', this._resourceObject);
}
}
return this._resourceObject;
};
/**
* Returns the CropBox or MediaBox of the page, preferring CropBox when available.
*
* @private
* @returns {number[]} The selected box array.
*/
PdfPage.prototype._getCropOrMediaBox = function () {
var box;
if (this._pageDictionary) {
if (this._pageDictionary.has('CropBox')) {
box = this._pageDictionary.getArray('CropBox');
}
else if (this._pageDictionary.has('MediaBox')) {
box = this._pageDictionary.getArray('MediaBox');
}
}
return box;
};
/**
* Finalizes the graphics state and marks that graphics need reinitialization on next access.
*
* @private
* @returns {void}
*/
PdfPage.prototype._beginSave = function () {
if (typeof this._graphicsState !== 'undefined') {
this.graphics.restore(this._graphicsState);
this._graphicsState = null;
this._needInitializeGraphics = true;
}
};
/**
* Releases page resources and cached values.
*
* @private
* @returns {void}
*/
PdfPage.prototype._destroy = function () {
this._pageDictionary = undefined;
this._size = undefined;
this._mBox = undefined;
this._cBox = undefined;
this._o = undefined;
this._g = undefined;
this._graphicsState = undefined;
this._contents = undefined;
};
/**
* Resolves the current tab order from the page dictionary.
*
* @private
* @returns {PdfFormFieldsTabOrder} The resolved tab order.
*/
PdfPage.prototype._obtainTabOrder = function () {
if (this._pageDictionary && this._pageDictionary.has('Tabs')) {
var tabOrder = this._pageDictionary.get('Tabs');
if (tabOrder === _PdfName.get('R')) {
this._tabOrder = PdfFormFieldsTabOrder.row;
}
else if (tabOrder === _PdfName.get('C')) {
this._tabOrder = PdfFormFieldsTabOrder.column;
}
else if (tabOrder === _PdfName.get('S')) {
this._tabOrder = PdfFormFieldsTabOrder.structure;
}
else if (tabOrder === _PdfName.get('W')) {
this._tabOrder = PdfFormFieldsTabOrder.widget;
}
}
if (this._tabOrder === null || typeof this._tabOrder === 'undefined') {
this._tabOrder = PdfFormFieldsTabOrder.none;
}
return this._tabOrder;
};
/**
* Removes the specified annotation reference from the page's Annots array.
*
* @private
* @param {_PdfReference} reference Annotation reference to remove.
* @returns {void} nothing.
*/
PdfPage.prototype._removeAnnotation = function (reference) {
if (this._pageDictionary && this._pageDictionary.has('Annots')) {
var annots = this._getProperty('Annots');
if (_isNullOrUndefined(annots) && Array.isArray(annots)) {
annots = annots.filter(function (item) { return item !== reference; });
this._pageDictionary.set('Annots', annots);
this._pageDictionary._updated = true;
}
}
};
Object.defineProperty(PdfPage.prototype, "_contentTemplate", {
/**
* Gets the page's combined content as a reusable template.
*
* @returns {PdfTemplate} Generated template containing the page content.
*/
get: function () {
this._fetchResources();
var targetArray = this._combineContent();
var targetStream = new _PdfContentStream(Array.from(targetArray));
var template = new PdfTemplate(targetStream, this._crossReference);
template._content.dictionary.set('Resources', this._resourceObject);
if (this.cropBox[0] > 0 || this.cropBox[1] > 0) {
template._content.dictionary.set('BBox', this.cropBox);
if (_areArrayEqual(this.cropBox, this.mediaBox)) {
template._size = { width: this.cropBox[0], height: this.cropBox[1] };
}
else {
template._size = { width: this.cropBox[2], height: this.cropBox[3] };
}
}
else if (this.mediaBox[0] > 0 || this.mediaBox[1] > 0) {
template._content.dictionary.set('BBox', this.mediaBox);
template._size = { width: this.mediaBox[0], height: this.mediaBox[1] };
}
else {
template._content.dictionary.set('BBox', [0, 0, this.size.width, this.size.height]);
template._size = { width: this.size.width, height: this.size.height };
}
return template;
},
enumerable: true,
configurable: true
});
PdfPage.prototype._combineIntoSingleArray = function (arrays) {
var totalLength = arrays.reduce(function (length, arr) { return length + arr.length; }, 0);
var targetArray = new Uint8Array(totalLength);
var offset = 0;
arrays.forEach(function (sourceArray) {
targetArray.set(sourceArray, offset);
offset += sourceArray.length;
});
return targetArray;
};
/**
* Concatenates multiple byte arrays into a single array.
*
* @private
* @returns {Uint8Array} Combined array.
*/
PdfPage.prototype._combineContent = function () {
var _this = this;
var list = [];
var array;
this._loadContents();
list.push(new Uint8Array([32, 113, 32, 10]));
var contents = this._contents;
contents.forEach(function (reference) {
var base = _this._crossReference._fetch(reference); // eslint-disable-line
if (base) {
if (base instanceof _PdfContentStream) {
array = new Uint8Array(base._bytes);
}
else if (base instanceof _PdfBaseStream) {
array = base.getBytes();
}
if (array) {
list.push(array);
list.push(new Uint8Array([13, 10]));
}
}
});
list.push(new Uint8Array([32, 81, 32, 10]));
list.push(new Uint8Array([13, 10]));
var targetArray = this._combineIntoSingleArray(list);
return targetArray;
};
/**
* Lays out and renders text within layout bounds, supporting column flow and pagination.
*
* @private
* @param {_PdfLayoutParameters} params Layout parameters defining page context, bounds, and layout behavior.
* @param {PdfTextElement} element Text element containing content, font, brush, and formatting information.
* @returns {PdfLayoutResult} The layout result containing the final page, bounds, and any remaining text.
*/
PdfPage.prototype._layoutTextElement = function (params, element) {
var format = params._format;
var text = element.text ? element.text : '';
var page = params._page;
var initialBounds = params._bounds.slice();
var paginateBounds = format && format.paginateBounds;
var usePaginateBounds = format && format.usePaginateBounds && paginateBounds;
var result;
var isFirstPage = true;
var hasDrawnAfterFirstPage = false;
while (text.length > 0) {
var bounds = void 0;
if (isFirstPage) {
bounds = initialBounds.slice();
}
else if (usePaginateBounds) {
bounds = [paginateBounds.x, paginateBounds.y, paginateBounds.width, paginateBounds.height];
}
else {
bounds = [initialBounds[0], 0, initialBounds[2], initialBounds[3]];
}
var previousText = text;
result = this._layoutOnPage(text, page, bounds, params, element);
text = result.remainingText ? result.remainingText : '';
if (format.layout === PdfLayoutType.onePage &&
format.break === PdfLayoutBreakType.fitElement) {
return result;
}
if ((format.break === PdfLayoutBreakType.fitElement
|| format.break === PdfLayoutBreakType.fitPage) &&
isFirstPage && text === previousText) {
var document_1 = page._crossReference._document;
page = page._pageIndex + 1 < document_1.pageCount
? document_1.getPage(page._pageIndex + 1)
: document_1.addPage();
isFirstPage = false;
continue;
}
if (result._hasRenderedContent && !isFirstPage) {
hasDrawnAfterFirstPage = true;
}
var breakType = format.break;
if (format.layout === PdfLayoutType.paginate &&
breakType === PdfLayoutBreakType.fitElement &&
hasDrawnAfterFirstPage) {
return result;
}
if (text === previousText) {
break;
}
if (!text || text.length === 0) {
break;
}
if (format.layout === PdfLayoutType.onePage) {
break;
}
var document_2 = page._crossReference._document;
page = page._pageIndex + 1 < document_2.pageCount
? document_2.getPage(page._pageIndex + 1)
: document_2.addPage();
isFirstPage = false;
}
return result;
};
/**
* Parses a PDF box array and updates _PdfReference entries to numeric values.
*
* @param {any[]} boxValues - Array containing box coordinates.
* @param {string} key - Contains box name.
* @returns {number[]} Parsed box values as a number array.
*/
PdfPage.prototype._parseBoxValues = function (boxValues, key) {
if (!Array.isArray(boxValues) || boxValues.length !== 4) {
return boxValues;
}
var value = new Array(4);
for (var i = 0; i < 4; i++) {
var ref = boxValues[parseInt(i.toString(), 10)]; // eslint-disable-line
if (!(ref instanceof _PdfReference)) {
return boxValues;
}
value[parseInt(i.toString(), 10)] = Number(this._crossReference._fetch(ref));
}
this._pageDictionary.update(key, value);
return value;
};
/**
* Lays out and renders text within the specified bounds on a page, supporting multi-column flow and FitElement behavior, and returns layout details including remaining text.
*
* @private
* @param {string} text The input text content to be processed and rendered.
* @param {PdfPage} page The target page on which the text is laid out and drawn.
* @param {number[]} bounds The layout bounds as [x, y, width, height].
* @param {_PdfLayoutParameters} params Layout parameters providing page context and formatting options.
* @param {PdfTextElement} element The text element containing font, brush, and formatting settings.
* @returns {PdfLayoutResult} The result containing the rendered bounds, last line info, and any remaining text.
*/
PdfPage.prototype._layoutOnPage = function (text, page, bounds, params, element) {
var font = element.font;
var brush = element.brush ? element.brush : new PdfBrush({ r: 0, g: 0, b: 0 });
var format = params._format;
var stringFormat = element.stringFormat ? element.stringFormat : new PdfStringFormat();
var layouter = new _PdfStringLayouter();
var clientHeight = page.graphics.clientSize.height;
var availableHeight = clientHeight - bounds[1];
var columns = format && format._columns ? Math.max(1, format._columns) : 1;
var gutter = format && format._columnGutter ? format._columnGutter : 0;
var totalWidth = bounds[2];
var columnWidth = (totalWidth - (columns - 1) * gutter) / columns;
var tempText = text;
for (var col = 0; col < columns && tempText.length > 0; col++) {
var lr = layouter._layout(tempText, font, stringFormat, [columnWidth, availableHeight]);
if (lr._empty) {
return new PdfLayoutResult(page, { x: bounds[0], y: bounds[1], width: 0, height: 0 }, undefined, tempText);
}
tempText = lr._remainder ? lr._remainder : '';
}
var textFinished = !tempText || tempText.length === 0;
var isFirstPage = page === params._page;
if (format.break === PdfLayoutBreakType.fitElement && isFirstPage &&
!textFinished) {
return new PdfLayoutResult(page, { x: bounds[0], y: bounds[1], width: 0, height: 0 }, undefined, text);
}
var remainingText = text;
var lastBounds = { x: bounds[0], y: bounds[1], width: 0, height: 0 };
var lastLineBounds = lastBounds;
var didDraw = false;
for (var col = 0; col < columns && remainingText.length > 0; col++) {
var x = bounds[0] + col * (columnWidth + gutter);
var y = bounds[1];
var lr = layouter._layout(remainingText, font, stringFormat, [columnWidth, availableHeight]);
if (lr._empty) {
return new PdfLayoutResult(page, lastBounds, lastLineBounds, remainingText);
}
var remainder = lr._remainder ? lr._remainder : '';
if (remainder === remainingText) {
break;
}
var consumedLength = remainingText.length - remainder.length;
var columnText = remainingText.substring(0, consumedLength);
page.graphics.drawString(columnText, font, { x: x, y: y, width: columnWidth,
height: lr._actualSize.height }, brush, stringFormat);
didDraw = true;
lastBounds = { x: x, y: y, width: lr._actualSize.width, height: lr._actualSize.height };
lastLineBounds = { x: x, y: y + Math.max(0, lr._actualSize.height - lr._lineHeight),
width: lr._actualSize.width, height: lr._lineHeight };
remainingText = remainder;
}
var result = new PdfLayoutResult(page, lastBounds, lastLineBounds, remainingText);
result._hasRenderedContent = didDraw;
return result;
};
PdfPage.prototype._getSectionIndex = function () {
var parent = this._pageDictionary.getRaw('Parent');
if (!parent) {
return -1;
}
var document = this._crossReference._document;
return document._getSectionIndexByPage(parent);
};
return PdfPage;
}());
export { PdfPage };
/**
* `PdfDestination` class represents the PDF destination.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annotation: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Initializes a new instance of the `PdfDestination` class.
* let destination: PdfDestination = new PdfDestination();
* // Sets the zoom factor.
* destination.zoom = 20;
* // Sets the page where the destination is situated.
* destination.page = page;
* // Sets the mode of the destination.
* destination.mode = PdfDestinationMode.fitToPage;
* // Sets the location of the destination.
* destination.location = {x: 20, y: 20};
* // Sets the bounds of the destination.
* destination.destinationBounds = {x: 20, y: 20, width: 100, height: 50};
* // Sets destination to document link annotation.
* annotation.destination = destination;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
var PdfDestination = /** @class */ (function () {
function PdfDestination(arg1, arg2, arg3) {
/**
* @private
*/
this._location = { x: 0, y: 0 };
/**
* @private
*/
this._destinationMode = PdfDestinationMode.location;
/**
* @private
*/
this._zoom = 0;
/**
* @private
*/
this._isValid = true;
/**
* @private
*/
this._destinationBounds = { x: 0, y: 0, width: 0, height: 0 };
/**
* @private
*/
this._array = Array(); // eslint-disable-line
if (typeof arg1 !== 'undefined' && arg1 !== null) {
if (arg1.rotation === PdfRotationAngle.angle180) {
this._location = { x: arg1.graphics._size.width, y: this._location.y };
}
else if (arg1.rotation === PdfRotationAngle.angle90) {
this._location = { x: 0, y: 0 };
}
else if (arg1.rotation === PdfRotationAngle.angle270) {
this._location = { x: arg1.graphics._size.width, y: 0 };
}
else {
this._location = { x: 0, y: this._location.y };
}
this._page = arg1;
this._index = arg1._pageIndex;
}
if (arg2 !== null && typeof arg2 !== 'undefined') {
this._location = { x: arg2.x, y: arg2.y };
if ('width' in arg2 && 'height' in arg2 && typeof arg2.width === 'number' && typeof arg2.height === 'number') {
this._destinationBounds = arg2;
}
}
if (arg3 !== null && typeof arg3 !== 'undefined') {
if ('mode' in arg3 && arg3.mode !== null && typeof arg3.mode !== 'undefined') {
this.mode = arg3.mode;
}
if ('zoom' in arg3 && arg3.zoom !== null && typeof arg3.zoom !== 'undefined') {
this.zoom = arg3.zoom;
}
}
}
Object.defineProperty(PdfDestination.prototype, "zoom", {
/**
* Gets the zoom factor.
*
* @returns {number} zoom.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* //Access the annotation at index 0
* let annot: PdfDocumentLinkAnnotation = page.annotations.at(0) as PdfDocumentLinkAnnotation;
* // Gets the zoom factor of the destination.
* let zoom: number = annot.destination.zoom;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._zoom;
},
/**
* Sets the zoom factor.
*
* @param {number} value zoom.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annotation: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Initializes a new instance of the `PdfDestination` class.
* let destination: PdfDestination = new PdfDestination();
* // Sets the zoom factor.
* destination.zoom = 20;
* // Sets the page where the destination is situated.
* destination.page = page;
* // Sets the mode of the destination.
* destination.mode = PdfDestinationMode.fitToPage;
* // Sets the location of the destination.
* destination.location = {x: 20, y: 20};
* // Sets the bounds of the destination.
* destination.destinationBounds = {x: 20, y: 20, width: 100, height: 50};
* // Sets destination to document link annotation.
* annotation.destination = destination;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (value !== this._zoom) {
this._zoom = value;
this._initializePrimitive();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfDestination.prototype, "page", {
/**
* Gets the page where the destination is situated.
*
* @returns {PdfPage} page.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* //Access the annotation at index 0
* let annot: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Gets the page of the destination.
* let page: PdfPage = annot.destination.page;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._page;
},
/**
* Sets the page where the destination is situated.
*
* @param {PdfPage} value page.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annotation: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Initializes a new instance of the `PdfDestination` class.
* let destination: PdfDestination = new PdfDestination();
* // Sets the zoom factor.
* destination.zoom = 20;
* // Sets the page where the destination is situated.
* destination.page = page;
* // Sets the mode of the destination.
* destination.mode = PdfDestinationMode.fitToPage;
* // Sets the location of the destination.
* destination.location = {x: 20, y: 20};
* // Sets the bounds of the destination.
* destination.destinationBounds = {x: 20, y: 20, width: 100, height: 50};
* // Sets destination to document link annotation.
* annotation.destination = destination;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (value !== this._page) {
this._page = value;
this._initializePrimitive();
this._index = value._pageIndex;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfDestination.prototype, "pageIndex", {
/**
* Gets the page index of bookmark destination (Read only).
*
* @returns {number} index.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* //Access the annotation at index 0
* let annot: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Gets the page index of the destination.
* let pageIndex: number = annot.destination.pageIndex;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._index;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfDestination.prototype, "mode", {
/**
* Gets the mode of the destination.
*
* @returns {PdfDestinationMode} page.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* //Access the annotation at index 0
* let annot: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Gets the mode of the destination.
* let mode: PdfDestinationMode = annot.destination.mode;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._destinationMode;
},
/**
* Sets the mode of the destination.
*
* @param {PdfDestinationMode} value page.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annotation: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Initializes a new instance of the `PdfDestination` class.
* let destination: PdfDestination = new PdfDestination();
* // Sets the zoom factor.
* destination.zoom = 20;
* // Sets the page where the destination is situated.
* destination.page = page;
* // Sets the mode of the destination.
* destination.mode = PdfDestinationMode.fitToPage;
* // Sets the location of the destination.
* destination.location = {x: 20, y: 20};
* // Sets the bounds of the destination.
* destination.destinationBounds = {x: 20, y: 20, width: 100, height: 50};
* // Sets destination to document link annotation.
* annotation.destination = destination;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (value !== this._destinationMode) {
this._destinationMode = value;
this._initializePrimitive();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfDestination.prototype, "location", {
/**
* Gets the location of the destination.
*
* @returns {Point} page.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annot: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Gets the location of the destination.
* let location: Point = annot.destination.location;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._location;
},
/**
* Sets the location of the destination.
*
* @param {Point} value page.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annotation: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Initializes a new instance of the `PdfDestination` class.
* let destination: PdfDestination = new PdfDestination();
* // Sets the zoom factor.
* destination.zoom = 20;
* // Sets the page where the destination is situated.
* destination.page = page;
* // Sets the mode of the destination.
* destination.mode = PdfDestinationMode.fitToPage;
* // Sets the location of the destination.
* destination.location = {x: 20, y: 20};
* // Sets the bounds of the destination.
* destination.destinationBounds = {x: 20, y: 20, width: 100, height: 50};
* // Sets destination to document link annotation.
* annotation.destination = destination;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (value !== this._location) {
this._location = value;
this._initializePrimitive();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfDestination.prototype, "destinationBounds", {
/**
* Gets the bounds of the destination.
*
* @returns {Rectangle} bounds.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annot: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Gets the bounds of the destination.
* let destinationBounds: Rectangle = annot.destination.destinationBounds;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._destinationBounds;
},
/**
* Sets the bounds of the destination.
*
* @param {Rectangle} value bounds.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annotation: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Initializes a new instance of the `PdfDestination` class.
* let destination: PdfDestination = new PdfDestination();
* // Sets the zoom factor.
* destination.zoom = 20;
* // Sets the page where the destination is situated.
* destination.page = page;
* // Sets the mode of the destination.
* destination.mode = PdfDestinationMode.fitToPage;
* // Sets the location of the destination.
* destination.location = {x: 20, y: 20};
* // Sets the bounds of the destination.
* destination.destinationBounds = {x: 20, y: 20, width: 100, height: 50};
* // Sets destination to document link annotation.
* annotation.destination = destination;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (value !== this._destinationBounds) {
this._destinationBounds = value;
this._initializePrimitive();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfDestination.prototype, "isValid", {
/**
* Gets a value indicating whether this instance is valid (Read only).
*
* @returns {boolean} value indicating whether this instance is valid.
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the annotation at index 0
* let annot: PdfDocumentLinkAnnotation = document.getPage(0).annotations.at(0) as PdfDocumentLinkAnnotation;
* // Gets a value indicating whether this instance is valid.
* let isValid: boolean = annot.destination.isValid;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._isValid;
},
enumerable: true,
configurable: true
});
/**
* Sets the internal validation flag for the destination.
*
* @private
* @param {boolean} value True to mark as valid; otherwise false.
* @returns {void} nothing.
*/
PdfDestination.prototype._setValidation = function (value) {
this._isValid = value;
};
/**
* Builds the internal PDF array representation and updates the parent dictionary.
*
* @private
* @returns {void}
*/
PdfDestination.prototype._initializePrimitive = function () {
this._array = [];
var page = this._page;
if (page && page._pageDictionary) {
var element = page._pageDictionary;
if (typeof element !== 'undefined' && element !== null) {
this._array.push(this._page._ref);
}
switch (this._destinationMode) {
case PdfDestinationMode.location:
this._array.push(_PdfName.get('XYZ'));
this._array.push(this._location.x);
this._array.push(this._page.graphics._size.height - this._location.y);
this._array.push(this._zoom);
break;
case PdfDestinationMode.fitToPage:
this._array.push(_PdfName.get('Fit'));
break;
case PdfDestinationMode.fitR:
this._array.push(_PdfName.get('FitR'));
this._array.push(this._destinationBounds.x);
this._array.push(this._destinationBounds.y);
this._array.push(this._destinationBounds.width);
this._array.push(this._destinationBounds.height);
break;
case PdfDestinationMode.fitH:
this._array.push(_PdfName.get('FitH'));
this._array.push((typeof page !== 'undefined' && page !== null) ? page.size.height - this._location.y : 0);
break;
}
if (this._parent) {
this._parent._dictionary.set(this._isBookmark ? 'Dest' : 'D', this._array);
this._parent._dictionary._updated = true;
}
}
};
return PdfDestination;
}());
export { PdfDestination };
/**
* Provides utilities to resolve and parse destination arrays from dictionaries and Names trees.
*
* @private
*/
var _PdfDestinationHelper = /** @class */ (function () {
function _PdfDestinationHelper(dictionary, value) {
if (dictionary && typeof value === 'string') {
this._dictionary = dictionary;
this._key = value;
}
}
/**
* Obtains the `PdfDestination` from the source dictionary or Names tree.
*
* @private
* @returns {PdfDestination} Resolved destination instance, if any.
*/
_PdfDestinationHelper.prototype._obtainDestination = function () {
var destination;
var page;
var loadedDocument;
if (!this._dictionary || (!this._dictionary.has(this._key) && !this._dictionary.has('D'))) {
return undefined;
}
else if (this._dictionary && this._dictionary._crossReference && this._dictionary._crossReference._document) {
loadedDocument = this._dictionary._crossReference._document;
}
if (this._dictionary.has('D')) {
this._key = 'D';
}
var destinationArray = this._dictionary.getArray(this._key); // eslint-disable-line
if ((typeof destinationArray === 'string' || (destinationArray instanceof _PdfName && typeof destinationArray.name === 'string')) && loadedDocument) {
destinationArray = this._getDestination(destinationArray, loadedDocument);
}
var value; // eslint-disable-line
if (Array.isArray(destinationArray) && destinationArray.length > 0) {
value = destinationArray[0];
}
var mode;
var left;
var top;
var bottom;
var right;
var zoom;
var index;
var topValue;
var leftValue;
if (typeof value === 'number') {
index = value;
}
else if (value instanceof _PdfDictionary) {
index = _getPageIndex(loadedDocument, value);
}
else if (value instanceof _PdfReference) {
var pageDictionary = loadedDocument._crossReference._fetch(value);
if (pageDictionary && pageDictionary instanceof _PdfDictionary) {
index = _getPageIndex(loadedDocument, pageDictionary);
}
}
if (!page && typeof index === 'number' && (index >= 0 && index < loadedDocument.pageCount)) {
page = loadedDocument.getPage(index);
}
if (Array.isArray(destinationArray) && destinationArray.length > 0) {
mode = destinationArray[1];
}
if (mode && page && destinationArray) {
switch (mode.name) {
case 'XYZ':
left = destinationArray[2];
top = destinationArray[3];
zoom = destinationArray[4];
topValue = typeof top === 'number' ? (page.size.height - top) : 0;
leftValue = typeof left === 'number' ? left : 0;
if (page.rotation !== PdfRotationAngle.angle0) {
topValue = _checkRotation(page, top, left);
}
destination = new PdfDestination(page, { x: leftValue, y: topValue });
destination._index = page._pageIndex;
destination.zoom = (typeof zoom !== 'undefined' && zoom !== null) ? zoom : 0;
if (left === null || top === null || zoom === null || typeof left === 'undefined' ||
typeof top === 'undefined' || typeof zoom === 'undefined') {
destination._setValidation(false);
}
break;
case 'FitR':
if (destinationArray.length > 2) {
left = destinationArray[2];
}
if (destinationArray.length > 3) {
bottom = destinationArray[3];
}
if (destinationArray.length > 4) {
right = destinationArray[4];
}
if (destinationArray.length > 5) {
top = destinationArray[5];
}
left = (typeof left !== 'undefined' && left !== null) ? left : 0;
bottom = (typeof bottom !== 'undefined' && bottom !== null) ? bottom : 0;
right = (typeof right !== 'undefined' && right !== null) ? right : 0;
top = (typeof top !== 'undefined' && top !== null) ? top : 0;
destination = new PdfDestination(page, { x: left, y: bottom, width: right, height: top });
destination._index = page._pageIndex;
destination.mode = PdfDestinationMode.fitR;
break;
case 'FitH':
case 'FitBH':
if (destinationArray.length > 2) {
top = destinationArray[2];
}
topValue = typeof top === 'number' ? (page.size.height - top) : 0;
destination = new PdfDestination(page, { x: 0, y: topValue });
destination._index = page._pageIndex;
destination.mode = PdfDestinationMode.fitH;
if (top === null || typeof top === 'undefined') {
destination._setValidation(false);
}
break;
case 'Fit':
destination = new PdfDestination(page);
destination._index = page._pageIndex;
destination.mode = PdfDestinationMode.fitToPage;
break;
}
}
else if (Array.isArray(destinationArray)) {
destination = new PdfDestination();
if (destinationArray.length > 4) {
zoom = destinationArray[4];
}
if (destinationArray.length > 1) {
mode = destinationArray[1];
}
if (typeof zoom === 'number') {
destination.zoom = zoom;
}
if (mode) {
if (mode.name === 'Fit') {
destination.mode = PdfDestinationMode.fitToPage;
}
else if (mode.name === 'XYZ') {
if (destinationArray.length > 2) {
left = destinationArray[2];
}
if (destinationArray.length > 3) {
topValue = destinationArray[3];
}
if ((typeof left === 'undefined' || left === null) || (typeof topValue === 'undefined' || topValue === null)
|| (typeof zoom === 'undefined' || zoom === null)) {
destination._setValidation(false);
}
}
}
if (typeof index === 'number' && (index >= 0 && index < loadedDocument.pageCount)) {
destination._index = index;
}
}
return destination;
};
/**
* Looks up a destination array by name from the document.
*
* @private
* @param {_PdfName | string} name Named destination identifier.
* @param {PdfDocument} document Document to search.
* @returns {any[]} The resolved destination array if found.
*/
_PdfDestinationHelper.prototype._getDestination = function (name, document) {
var destinationArray; // eslint-disable-line
if (document) {
destinationArray = this._getNamedDestination(document, name);
}
return destinationArray;
};
/**
* Resolves a named destination from the Names tree or Dests dictionary.
*
* @private
* @param {PdfDocument} document Source document.
* @param {_PdfName | string} result Name key to resolve.
* @returns {any[]} Destination array or undefined.
*/
_PdfDestinationHelper.prototype._getNamedDestination = function (document, result) {
var destination; // eslint-disable-line
var catalog = document._catalog;
if (catalog && catalog._catalogDictionary) {
if (result && typeof result === 'string') {
if (catalog._catalogDictionary.has('Names')) {
var names = catalog._catalogDictionary.get('Names');
if (names && names.has('Dests')) {
var kids = names.get('Dests');
if (kids) {
var ref = this._getNamedObjectFromTree(kids, result);
destination = this._extractDestination(ref, document);
}
}
}
}
else if (result && result instanceof _PdfName) {
var destinations = catalog._catalogDictionary.get('Dests');
if (destinations) {
destination = destinations.get(result.name);
}
}
}
return destination;
};
/**
* Extracts a destination array from a referenced dictionary or array.
*
* @private
* @param {any} ref Reference or array pointing to a destination.
* @param {PdfDocument} document Document to use for dereferencing.
* @returns {any[]} The destination array if available.
*/
_PdfDestinationHelper.prototype._extractDestination = function (ref, document) {
var dict; // eslint-disable-line
var destinationArray; // eslint-disable-line
if (ref && ref instanceof _PdfReference) {
dict = document._crossReference._fetch(ref);
}
if (dict) {
if (dict instanceof _PdfDictionary && dict.has('D')) {
destinationArray = dict.getRaw('D');
}
else if (Array.isArray(dict)) {
destinationArray = dict;
}
}
return destinationArray ? destinationArray : ref;
};
/**
* Traverses the Names tree to find a named object reference.
*
* @private
* @param {_PdfDictionary} kids Current node in the Names tree.
* @param {string} name Name to locate.
* @returns {_PdfReference} Reference to the matching named object.
*/
_PdfDestinationHelper.prototype._getNamedObjectFromTree = function (kids, name) {
var found = false;
var currentDictionary = kids;
var reference;
while (!found && currentDictionary) {
if (currentDictionary && currentDictionary.has('Kids')) {
currentDictionary = this._getProperKid(currentDictionary, name);
}
else if (currentDictionary && currentDictionary.has('Names')) {
reference = this._findName(currentDictionary, name);
found = true;
}
}
return reference;
};
/**
* Performs a binary search in a Names array for the given name.
*
* @private
* @param {_PdfDictionary} current Dictionary containing a 'Names' array.
* @param {string} target Name to search for.
* @returns {_PdfReference} Reference associated with the found name.
*/
_PdfDestinationHelper.prototype._findName = function (current, target) {
var reference;
var names = current.get('Names'); // eslint-disable-line
if (!Array.isArray(names) || names.length === 0) {
return reference;
}
for (var i = 0; i < names.length; i += 2) {
var key = names[i]; // eslint-disable-line
if (key instanceof _PdfReference) {
key = current._crossReference._fetch(key);
}
if (this._stringCompare(target, key) === 0) {
reference = names[i + 1];
return reference;
}
}
return reference;
};
/**
* Selects the child dictionary whose Limits bracket the specified name.
*
* @private
* @param {_PdfDictionary} kids Parent dictionary with Kids array.
* @param {string} name Name to bracket.
* @returns {_PdfDictionary} The child dictionary likely containing the name.
*/
_PdfDestinationHelper.prototype._getProperKid = function (kids, name) {
var kidsArray; // eslint-disable-line
var kid;
if (kids && kids.has('Kids')) {
kidsArray = kids.getRaw('Kids');
}
if (kidsArray && Array.isArray(kidsArray) && kidsArray.length !== 0) {
kidsArray = kids.getArray('Kids');
for (var i = kidsArray.length - 1; i >= 0; i--) {
kid = kidsArray[Number.parseInt(i.toString(), 10)];
if (this._checkLimits(kid, name)) {
break;
}
}
}
return kid;
};
/**
* Checks whether the given name falls within the Limits of the node.
*
* @private
* @param {_PdfDictionary} kid Node to test.
* @param {string} result Name to compare.
* @returns {boolean} True if within limits; otherwise false.
*/
_PdfDestinationHelper.prototype._checkLimits = function (kid, result) {
var found = false;
if (kid && kid.has('Limits')) {
var limits = kid.get('Limits'); // eslint-disable-line
var lowerLimit = limits[0];
var higherLimit = limits[1];
var lowCompare = this._stringCompare(lowerLimit, result);
var highCompare = this._stringCompare(higherLimit, result);
found = (lowCompare === 0 || highCompare === 0 || (lowCompare < 0 && highCompare > 0));
}
return found;
};
/**
* Compares two strings using byte-wise comparison.
*
* @private
* @param {string} limits First string to compare.
* @param {string} result Second string to compare.
* @returns {number} Negative if limits < result, positive if limits > result, zero if equal.
*/
_PdfDestinationHelper.prototype._stringCompare = function (limits, result) {
var byteArray = _stringToBytes(limits);
var byteArray1 = _stringToBytes(result);
var commonSize = Math.min(byteArray.length, byteArray1.length);
var resultValue = 0;
for (var i = 0; i < commonSize; i++) {
var byte = byteArray[Number.parseInt(i.toString(), 10)];
var byte1 = byteArray1[Number.parseInt(i.toString(), 10)];
resultValue = byte - byte1;
if (resultValue !== 0) {
break;
}
}
if (resultValue === 0) {
resultValue = byteArray.length - byteArray1.length;
}
return resultValue;
};
return _PdfDestinationHelper;
}());
export { _PdfDestinationHelper };