@syncfusion/ej2-pdf
Version:
Feature-rich JavaScript PDF library with built-in support for loading and manipulating PDF document.
3,474 lines • 146 kB
JavaScript
import { PdfPage } from './../pdf-page';
import { _PdfStreamWriter } from './pdf-stream-writer';
import { _PdfBaseStream, _PdfStream } from './../base-stream';
import { _floatToString, _addProcSet, _reverseMapBlendMode, _mapBlendMode, _getNewGuidString, _getBezierArc, _numberToString, _bytesToString, _stringToUnicodeArray, _isNullOrUndefined } from './../utils';
import { _PdfDictionary, _PdfReference, _PdfName } from './../pdf-primitives';
import { PdfCjkStandardFont, PdfFont, PdfFontStyle, PdfStandardFont, PdfTrueTypeFont } from './../fonts/pdf-standard-font';
import { _PdfStringLayouter, _LineType, _StringTokenizer } from './../fonts/string-layouter';
import { PdfTextAlignment, PdfTextDirection, PdfSubSuperScript, PdfBlendMode, PdfLineJoin, PdfLineCap, PdfDashStyle, PdfFillMode, PathPointType, PdfRotationAngle } from './../enumerator';
import { PdfStringFormat, PdfVerticalAlignment } from './../fonts/pdf-string-format';
import { PdfTemplate } from './pdf-template';
import { PdfLayoutFormat } from './pdf-layouter';
import { PdfPath } from './pdf-path';
import { _UnicodeTrueTypeFont } from '../fonts/unicode-true-type-font';
import { _RtlRenderer } from './../graphics/rightToLeft/text-renderer';
import { PdfImage } from './images/pdf-image';
import { initializeTelemetryFeature } from '@syncfusion/ej2-base';
/**
* Represents a graphics from a PDF page.
* ```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();
* ```
*/
var PdfGraphics = /** @class */ (function () {
/**
* Initializes a new instance of the `PdfGraphics` class.
*
* @param {Size} size The graphics client size.
* @param {_PdfContentStream} content Content stream.
* @param {_PdfCrossReference} xref Cross reference.
* @param {PdfPage | PdfTemplate} source Source object of the graphics.
* @private
*/
function PdfGraphics(size, content, xref, source) {
/**
* Resources waiting to be written into the cross-reference when saving.
*
* @private
*/
this._pendingResource = []; // eslint-disable-line
/**
* Indicates whether italic text style is active.
*
* @private
*/
this._isItalic = false;
/**
* Indicates whether the current graphics context is in layouter mode.
*
* @private
*/
this._isLayouter = false;
this._hasResourceReference = false;
if (source instanceof PdfPage) {
this._source = source._pageDictionary;
this._page = source;
}
else if (source instanceof PdfTemplate) {
this._source = source._content.dictionary;
this._template = source;
}
if (this._source) {
var obj = void 0; // eslint-disable-line
if (this._source.has('Resources')) {
obj = this._source.getRaw('Resources');
}
else if (this._source.has('Parent')) {
var parentPage = this._source.get('Parent');
if (parentPage && parentPage.has('Resources')) {
obj = parentPage.getRaw('Resources');
if (obj && obj instanceof _PdfDictionary) {
this._source.update('Resources', obj);
}
}
}
if (obj && obj instanceof _PdfReference) {
this._hasResourceReference = true;
this._resourceObject = xref._fetch(obj);
if (this._resourceObject && this._resourceObject instanceof _PdfStream) {
this._resourceObject = this._resourceObject.dictionary;
}
}
else if (obj && obj instanceof _PdfDictionary) {
this._resourceObject = obj;
}
else {
this._resourceObject = new _PdfDictionary();
this._source.update('Resources', this._resourceObject);
}
}
this._crossReference = xref;
this._sw = new _PdfStreamWriter(content);
this._size = size;
_addProcSet('PDF', this._resourceObject);
this._initialize();
}
Object.defineProperty(PdfGraphics.prototype, "_matrix", {
/**
* Lazily creates and returns the current graphics transformation matrix.
*
* @private
* @returns {_PdfTransformationMatrix} The current transformation matrix.
*/
get: function () {
if (typeof this._m === 'undefined') {
this._m = new _PdfTransformationMatrix();
}
return this._m;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfGraphics.prototype, "_resources", {
/**
* Builds and returns the local resource name map by scanning page/template resources
* Populates transparency cache when available.
*
* @private
* @returns {Map<_PdfReference, _PdfName>} The mapping of references to resource names.
*/
get: function () {
var _this = this;
if (typeof this._resourceMap === 'undefined') {
this._resourceMap = new Map();
if (this._resourceObject && this._resourceObject.has('Font')) {
var fonts = this._resourceObject.get('Font');
if (fonts && fonts.size > 0) {
fonts.forEach(function (key, value) {
if (value !== null && typeof value !== 'undefined' && value instanceof _PdfReference) {
_this._resourceMap.set(value, _PdfName.get(key));
}
});
}
}
if (this._resourceObject.has('XObject')) {
var other = this._resourceObject.get('XObject');
if (other && other.size > 0) {
other.forEach(function (key, value) {
if (value !== null && typeof value !== 'undefined' && value instanceof _PdfReference) {
_this._resourceMap.set(value, _PdfName.get(key));
}
});
}
}
if (this._resourceObject.has('ExtGState')) {
var state = this._resourceObject.get('ExtGState');
if (state && state.size > 0) {
if (!this._transparencies) {
this._transparencies = new Map();
}
state.forEach(function (key, value) {
if (value !== null && typeof value !== 'undefined' && value instanceof _PdfReference) {
_this._setTransparencyData(value, _PdfName.get(key));
}
});
}
}
}
return this._resourceMap;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfGraphics.prototype, "clientSize", {
/**
* Gets the size of the canvas reduced by margins and page templates (Read only).
*
* @returns {Size} The width and height of the client area 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 graphics client size.
* let size: Size = page.graphics.clientSize;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
if (this._page && this._crossReference && this._crossReference._document._hasTemplateContentValue) {
var includeMargins = this._isLayouter ? true : false;
var bounds = this._page._getActualBounds(this._page._pageSettings, includeMargins);
return { width: bounds[2], height: bounds[3] };
}
return { width: this._clipBounds[2], height: this._clipBounds[3] };
},
enumerable: true,
configurable: true
});
/**
* Save the current graphics state.
*
* ```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 font
* let font: PdfFont = document.embedFont(PdfFontFamily.helvetica, 20, PdfFontStyle.regular);
* // Save the graphics
* let state: PdfGraphicsState = graphics.save();
* //Set graphics translate transform.
* graphics.translateTransform({x: 100, y: 100});
* //Draws the String.
* graphics.drawString('Hello world!', font, {x: 10, y: 20, width: 100, height: 200}, new PdfBrush({r: 0, g: 0, b: 255}));
* //Restore the graphics.
* graphics.restore(state);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @returns {PdfGraphicsState} graphics state.
*/
PdfGraphics.prototype.save = function () {
var state = new PdfGraphicsState(this, this._matrix);
state._textRenderingMode = this._textRenderingMode;
state._charSpacing = this._characterSpacing;
state._textScaling = this._textScaling;
state._wordSpacing = this._wordSpacing;
state._currentBrush = this._currentBrush;
state._currentPen = this._currentPen;
state._currentFont = this._currentFont;
this._graphicsState.push(state);
this._sw._saveGraphicsState();
return state;
};
/**
* Restore the graphics state.
*
* ```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 font
* let font: PdfFont = document.embedFont(PdfFontFamily.helvetica, 20, PdfFontStyle.regular);
* // Save the graphics
* let state: PdfGraphicsState = graphics.save();
* //Set graphics translate transform.
* graphics.translateTransform({x: 100, y: 100});
* //Draws the String.
* graphics.drawString('Hello world!', font, {x: 10, y: 20, width: 100, height: 200}, new PdfBrush({r: 0, g: 0, b: 255}));
* //Restore the graphics.
* graphics.restore(state);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {PdfGraphicsState} state graphics state.
* @returns {void} restore of the graphics state.
*/
PdfGraphics.prototype.restore = function (state) {
if (this._graphicsState.length > 0) {
if (typeof state === 'undefined') {
this._doRestore();
}
else {
if (this._graphicsState.length > 0 && this._graphicsState.indexOf(state) !== -1) {
while (this._graphicsState.length > 0) {
if (this._doRestore() === state) {
break;
}
}
}
}
}
};
/**
* Represents a scale transform of the graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the first page
* let page: PdfPage = document.getPage(0);
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a new font
* let font: PdfFont = document.embedFont(PdfFontFamily.helvetica, 20, PdfFontStyle.regular);
* // Save the current graphics state
* let state: PdfGraphicsState = graphics.save();
* // Apply scale transform
* graphics.scaleTransform(0.5, 0.5);
* // Draw a string with the scaled transformation
* graphics.drawString('Hello world!', font, {x: 10, y: 20, width: 100, height: 200}, new PdfBrush({r: 0, g: 0, b: 255}));
* // Restore the graphics to its previous state
* graphics.restore(state);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {number} scaleX Scale factor in the x direction.
* @param {number} scaleY Scale factor in the y direction.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.scaleTransform = function (scaleX, scaleY) {
var matrix = new _PdfTransformationMatrix();
matrix._scale(scaleX, scaleY);
this._sw._modifyCtm(matrix);
this._matrix._multiply(matrix);
};
/**
* Represents a translate transform of the graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the first page
* let page: PdfPage = document.getPage(0);
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a new font
* let font: PdfFont = document.embedFont(PdfFontFamily.helvetica, 20, PdfFontStyle.regular);
* // Save the current graphics state
* let state: PdfGraphicsState = graphics.save();
* // Apply translate transform
* graphics.translateTransform({x: 100, y: 100});
* // Draw a string with the translation applied
* graphics.drawString('Hello world!', font, {x: 10, y: 20, width: 100, height: 200}, new PdfBrush({r: 0, g: 0, b: 255}));
* // Restore the graphics to its previous state
* graphics.restore(state);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {Point} location (x, y) coordinates of the translation.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.translateTransform = function (location) {
var matrix = new _PdfTransformationMatrix();
matrix._translate(location.x, -location.y);
this._sw._modifyCtm(matrix);
this._matrix._multiply(matrix);
};
/**
* Represents a rotate transform of the graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the first page
* let page: PdfPage = document.getPage(0);
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a new font
* let font: PdfFont = document.embedFont(PdfFontFamily.helvetica, 20, PdfFontStyle.regular);
* // Save the current graphics state
* let state: PdfGraphicsState = graphics.save();
* // Apply rotate transform
* graphics.rotateTransform(-90);
* // Draw a string with the rotation applied
* graphics.drawString('Hello world!', font, {x: 10, y: 20, width: 100, height: 200}, new PdfBrush({r: 0, g: 0, b: 255}));
* // Restore the graphics to its previous state
* graphics.restore(state);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {number} angle Angle of rotation in degrees.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.rotateTransform = function (angle) {
var matrix = new _PdfTransformationMatrix();
matrix._rotate(-angle);
this._sw._modifyCtm(matrix);
this._matrix._multiply(matrix);
};
/**
* Represents a clipping region of this graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the first page
* let page: PdfPage = document.getPage(0);
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a new font
* let font: PdfFont = document.embedFont(PdfFontFamily.helvetica, 20, PdfFontStyle.regular);
* // Set clipping region
* graphics.setClip({x: 0, y: 0, width: 50, height: 12}, PdfFillMode.alternate);
* // Draw a string within the clipping region
* graphics.drawString('Hello world!', font, {x: 0, y: 0, width: 100, height: 200}, new PdfBrush({r: 0, g: 0, b: 255}));
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {Rectangle} bounds Rectangle structure that represents the new clip region.
* @param {PdfFillMode} mode Member of the PdfFillMode enumeration that specifies the filling operation to use.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.setClip = function (bounds, mode) {
if (typeof mode === 'undefined') {
mode = PdfFillMode.winding;
}
this._sw._appendRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
this._sw._clipPath(mode === PdfFillMode.alternate);
};
/**
* Sets the transparency for the graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the first page
* let page: PdfPage = document.getPage(0);
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a new font
* let font: PdfFont = document.embedFont(PdfFontFamily.helvetica, 20, PdfFontStyle.regular);
* // Set transparency
* graphics.setTransparency(0.5, 0.5, PdfBlendMode.multiply);
* // Draw the string
* graphics.drawString('Hello world!', font, {x: 0, y: 0, width: 100, height: 200}, new PdfBrush({r: 0, g: 0, b: 255}));
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {number} stroke The transparency value for strokes.
* @param {number} fill The transparency value for fills.
* @param {PdfBlendMode} mode The blend mode to use.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.setTransparency = function (stroke, fill, mode) {
if (typeof fill === 'undefined') {
fill = stroke;
}
if (typeof mode === 'undefined') {
mode = PdfBlendMode.normal;
}
if (typeof this._transparencies === 'undefined') {
this._transparencies = new Map();
}
var transparencyKey = 'CA:' + stroke.toString() + '_ca:' + fill.toString() + '_BM:' + mode.toString();
var transparencyData;
if (this._transparencies.size > 0) {
this._transparencies.forEach(function (value, key) {
if (value === transparencyKey) {
transparencyData = key;
}
});
}
if (!transparencyData) {
transparencyData = new _TransparencyData();
var transparencyDict = new _PdfDictionary();
transparencyDict.update('CA', stroke);
transparencyDict.update('ca', fill);
transparencyDict.update('BM', _reverseMapBlendMode(mode));
transparencyData._dictionary = transparencyDict;
transparencyData._key = transparencyKey;
transparencyData._name = _PdfName.get(_getNewGuidString());
var dictionary = void 0;
var isReference = false;
if (this._resourceObject.has('ExtGState')) {
var obj = this._resourceObject.getRaw('ExtGState'); // eslint-disable-line
if (obj !== null && typeof obj !== 'undefined') {
if (obj instanceof _PdfReference) {
isReference = true;
dictionary = this._crossReference._fetch(obj);
}
else if (obj instanceof _PdfDictionary) {
dictionary = obj;
}
}
}
else {
dictionary = new _PdfDictionary(this._crossReference);
this._resourceObject.update('ExtGState', dictionary);
}
if (this._crossReference) {
var ref = this._crossReference._getNextReference();
this._crossReference._cacheMap.set(ref, transparencyDict);
transparencyData._reference = ref;
dictionary.update(transparencyData._name.name, ref);
}
else {
this._pendingResource.push({ 'resource': transparencyDict, 'key': transparencyData._name, 'source': dictionary });
}
if (isReference) {
this._resourceObject._updated = true;
}
if (this._hasResourceReference) {
this._source._updated = true;
}
}
this._sw._setGraphicsState(transparencyData._name);
};
/**
* Draws a line on the page graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the 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 a 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();
* ```
*
* @param {PdfPen} pen The pen that determines the stroke color, width, and style of the line.
* @param {Point} start The (x, y) coordinates of the starting point of the line.
* @param {Point} end The (x, y) coordinates of the ending point of the line.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.drawLine = function (pen, start, end) {
this._beginMarkContent();
this._stateControl(pen);
this._sw._beginPath(start.x, start.y);
this._sw._appendLineSegment(end.x, end.y);
this._sw._strokePath();
_addProcSet('PDF', this._resourceObject);
this._endMarkContent();
};
PdfGraphics.prototype.drawRectangle = function (bounds, first, second) {
this._beginMarkContent();
var result = this._setPenBrush(first, second);
this._sw._appendRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
this._drawGraphicsPath(result.pen, result.brush);
this._endMarkContent();
};
/**
* Draws a Bezier curve using a specified pen and coordinates for the start point, two control points, and end point.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the 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 a Bezier curve on the page graphics
* graphics.drawBezier({x: 50, y: 100}, {x: 200, y: 50}, {x: 100, y: 150}, {x: 150, y: 100}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {Point} start The (x, y) coordinates of the starting point of the Bezier curve.
* @param {Point} first The (x, y) coordinates of the first control point of the Bezier curve.
* @param {Point} second The (x, y) coordinates of the second control point of the Bezier curve.
* @param {Point} end The (x, y) coordinates of the ending point of the Bezier curve.
* @param {PdfPen} pen The pen that determines the stroke color, width, and style of the Bezier curve.
* @returns {void} Nothing
*/
PdfGraphics.prototype.drawBezier = function (start, first, second, end, pen) {
this._beginMarkContent();
this._stateControl(pen, null, null);
this._sw._beginPath(start.x, start.y);
this._sw._appendBezierSegment(first.x, first.y, second.x, second.y, end.x, end.y);
this._drawGraphicsPath(pen);
this._endMarkContent();
};
PdfGraphics.prototype.drawPie = function (bounds, startAngle, sweepAngle, first, second) {
this._beginMarkContent();
var result = this._setPenBrush(first, second);
this._constructPiePath(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height, startAngle, sweepAngle);
this._sw._appendLineSegment(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
this._drawGraphicsPath(result.pen, result.brush, null, true);
this._endMarkContent();
};
PdfGraphics.prototype.drawPolygon = function (points, first, second) {
var _this = this;
this._beginMarkContent();
if (points.length > 0) {
var result = this._setPenBrush(first, second);
this._sw._beginPath(points[0].x, points[0].y);
points.forEach(function (point, index) {
if (index > 0) {
_this._sw._appendLineSegment(point.x, point.y);
}
});
this._drawGraphicsPath(result.pen, result.brush, PdfFillMode.winding, true);
}
this._endMarkContent();
};
PdfGraphics.prototype.drawEllipse = function (bounds, first, second) {
this._beginMarkContent();
var result = this._setPenBrush(first, second);
this._constructArcPath(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height, 0, 360);
this._drawGraphicsPath(result.pen, result.brush, PdfFillMode.winding, true);
this._endMarkContent();
};
/**
* Draw arc on the page graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the 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 an arc on the page graphics
* graphics.drawArc({x: 10, y: 20, width: 100, height: 200}, 20, 30, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {Rectangle} bounds The bounding rectangle that defines the ellipse from which the arc shape comes.
* @param {number} startAngle Angle measured in degrees clockwise from the x-axis to the first side of the arc shape.
* @param {number} sweepAngle Angle measured in degrees clockwise from the startAngle parameter to the second side of the arc shape.
* @param {PdfPen} pen Pen that determines the stroke color, width, and style of the arc.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.drawArc = function (bounds, startAngle, sweepAngle, pen) {
if (sweepAngle !== 0) {
this._beginMarkContent();
this._stateControl(pen);
this._constructArcPath(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height, startAngle, sweepAngle);
this._drawGraphicsPath(pen, null, PdfFillMode.winding, false);
this._endMarkContent();
}
};
PdfGraphics.prototype.drawImage = function (arg1, arg2) {
initializeTelemetryFeature('ImageToPDF', 'PDFLibrary');
this._beginMarkContent();
if (arg2 && this._isRectangle(arg2)) {
arg1._save();
var matrix = new _PdfTransformationMatrix();
this._getTranslateTransform(arg2.x, (arg2.y + arg2.height), matrix);
this._getScaleTransform(arg2.width, arg2.height, matrix);
this._sw._write('q');
this._sw._modifyCtm(matrix);
var sourceDictionary = void 0;
var keyName = void 0;
var isNew = true;
if (this._resourceObject.has('XObject')) {
var obj = this._resourceObject.getRaw('XObject'); // eslint-disable-line
if (obj instanceof _PdfDictionary) {
sourceDictionary = obj;
}
else if (obj instanceof _PdfReference && this._crossReference) {
sourceDictionary = this._crossReference._fetch(obj);
}
if (sourceDictionary) {
isNew = false;
}
}
if (isNew) {
sourceDictionary = new _PdfDictionary(this._crossReference);
this._resourceObject.update('XObject', sourceDictionary);
}
if (typeof keyName === 'undefined') {
if (!arg1._key) {
arg1._key = _getNewGuidString();
}
keyName = _PdfName.get(arg1._key);
}
if (this._crossReference) {
this._updateImageResource(arg1, keyName, sourceDictionary, this._crossReference);
this._source.update('Resources', this._resourceObject);
this._source._updated = true;
}
else {
this._pendingResource.push({ 'resource': arg1, 'key': keyName, 'source': sourceDictionary });
}
this._sw._executeObject(keyName);
this._sw._write('Q');
this._sw._write('\r\n');
_addProcSet('ImageB', this._resourceObject);
_addProcSet('ImageC', this._resourceObject);
_addProcSet('ImageI', this._resourceObject);
_addProcSet('Text', this._resourceObject);
}
else {
var size = arg1.physicalDimension;
this.drawImage(arg1, { x: arg2.x, y: arg2.y, width: size.width, height: size.height });
}
this._endMarkContent();
};
/**
* Draws a PDF template onto the page graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the first page
* let page: PdfPage = document.getPage(0);
* // Gets the first annotation of the page
* let annotation: PdfRubberStampAnnotation = page.annotations.at(0) as PdfRubberStampAnnotation;
* // Gets the appearance template of the annotation
* let template: PdfTemplate = annotation.createTemplate();
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Draw the template on the page graphics within the specified bounds
* graphics.drawTemplate(template, { x: 10, y: 20, width: template.size.width, height: template.size.height });
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {PdfTemplate} template The PDF template to be drawn.
* @param {Rectangle} bounds The bounds of the template.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.drawTemplate = function (template, bounds) {
var _this = this;
this._beginMarkContent();
var hasPendingTemplate = true;
if (typeof template !== 'undefined') {
if (template._isExported || template._isResourceExport) {
if (this._crossReference) {
template._crossReference = this._crossReference;
template._importStream(true, template._isResourceExport);
}
else {
template._importStream(false, template._isResourceExport);
this._pendingResource.push(template);
hasPendingTemplate = false;
}
}
var scaleX = void 0;
var scaleY = void 0;
if (this._page &&
this._page.rotation &&
template._isSignature &&
this._page._size.width > this._page._size.height &&
this._page.rotation === PdfRotationAngle.angle270 && template._content &&
template._content.dictionary &&
template._content.dictionary.has('Matrix')) {
scaleX = (template && template._size.width > 0) ? bounds.width / template._size.height : 1;
scaleY = (template && template._size.height > 0) ? bounds.height / template._size.width : 1;
}
else {
scaleX = (template && template._size.width > 0) ? bounds.width / template._size.width : 1;
scaleY = (template && template._size.height > 0) ? bounds.height / template._size.height : 1;
}
var needScale = !(Math.trunc(scaleX * 1000) / 1000 === 1 && Math.trunc(scaleY * 1000) / 1000 === 1);
var cropBox = void 0;
var mediaBox = void 0;
if (this._page) {
cropBox = this._page.cropBox;
mediaBox = this._page.mediaBox;
if (this._page._pageDictionary.has('CropBox') && this._page._pageDictionary.has('MediaBox')) {
if (cropBox[0] > 0 && cropBox[1] > 0 && mediaBox[0] < 0 && mediaBox[1] < 0) {
this.translateTransform({ x: cropBox[0], y: -cropBox[1] });
bounds.x = -cropBox[0];
bounds.y = cropBox[1];
}
}
}
var state = this.save();
var matrix = new _PdfTransformationMatrix();
if (this._page) {
var needTransform = (this._page._pageDictionary.has('CropBox') &&
this._page._pageDictionary.has('MediaBox') && cropBox && mediaBox &&
cropBox[0] === mediaBox[0] && cropBox[1] === mediaBox[1] && cropBox[2] === mediaBox[2] && cropBox[3] === mediaBox[3]) ||
(this._page._pageDictionary.has('MediaBox') && mediaBox && mediaBox[3] === 0);
var yAxis = (bounds.y + ((this._page._origin[0] >= 0 || needTransform) ? bounds.height : 0));
if (template && template._isSignature &&
template._content && template._content.dictionary &&
template._content.dictionary.has('BBox')) {
var bbox = template._content.dictionary.get('BBox');
var hasValidBBoxSize = bbox[2] !== 0 && bbox[3] !== 0;
var xTranslate = (bbox[0] > 0 && hasValidBBoxSize) ? 0 : bounds.x;
var yTranslate = (bbox[1] > 0 && hasValidBBoxSize) ? this._page.size.height : yAxis;
matrix._translate(xTranslate, -yTranslate);
}
else {
matrix._translate(bounds.x, -yAxis);
}
}
else {
matrix._translate(bounds.x, -(bounds.y + bounds.height));
}
var scaleApplied = false;
if (template._content && template._content.dictionary) {
var dictionary = template._content.dictionary;
if (dictionary.has('Matrix') && dictionary.has('BBox')) {
var templateMatrix = dictionary.getArray('Matrix');
var templateBox = dictionary.getArray('BBox');
if (templateMatrix && templateBox && templateMatrix.length > 5 && templateBox.length > 3) {
var templateScaleX = Number.parseFloat(_numberToString(-templateMatrix[1]));
var templateScaleY = Number.parseFloat(_numberToString(templateMatrix[2]));
var roundScaleX = Number.parseFloat(_numberToString(scaleX));
var roundScaleY = Number.parseFloat(_numberToString(scaleY));
if (roundScaleX === templateScaleX &&
roundScaleY === templateScaleY &&
templateBox[2] === template._size.width &&
templateBox[3] === template._size.height && template._isAnnotationTemplate
&& template._needScale && needScale) {
matrix = new _PdfTransformationMatrix();
matrix._translate(bounds.x - templateMatrix[4], -(bounds.y + templateMatrix[5]));
matrix._scale(1, 1);
scaleApplied = true;
}
else if (templateBox[0] !== 0 && templateBox[1] !== 0 && templateBox[0] === bounds.x &&
this._page && template._isSignature) {
matrix._translate(bounds.x - templateBox[0], -this._page.size.height);
matrix._scale(scaleX, scaleY);
scaleApplied = true;
}
}
}
}
if (needScale && !scaleApplied) {
matrix._scale(scaleX, scaleY);
}
this._sw._modifyCtm(matrix);
var sourceDictionary = void 0;
var isReference = false;
var keyName_1;
var isNew = true;
var ref_1;
if (this._resourceObject.has('XObject')) {
var obj = this._resourceObject.getRaw('XObject'); // eslint-disable-line
if (obj) {
if (obj instanceof _PdfReference) {
isReference = true;
sourceDictionary = this._crossReference._fetch(obj);
}
else if (obj instanceof _PdfDictionary) {
sourceDictionary = obj;
}
}
if (sourceDictionary) {
isNew = false;
this._resources.forEach(function (value, key) {
if (key && key instanceof _PdfReference) {
var base = _this._crossReference._fetch(key);
if (base && template && base === template._content) {
keyName_1 = value;
ref_1 = key;
}
}
});
}
}
if (isNew) {
sourceDictionary = new _PdfDictionary(this._crossReference);
this._resourceObject.update('XObject', sourceDictionary);
}
if (typeof keyName_1 === 'undefined') {
if (!template._key) {
template._key = _getNewGuidString();
}
keyName_1 = _PdfName.get(template._key);
if (template && template._content.reference) {
ref_1 = template._content.reference;
}
else if (this._crossReference) {
ref_1 = this._crossReference._getNextReference();
}
else {
this._pendingResource.push({ 'resource': template._content, 'key': keyName_1, 'source': sourceDictionary });
if (this._template && hasPendingTemplate) {
this._pendingResource.push(template);
}
}
if (ref_1 && this._crossReference) {
if (!this._crossReference._cacheMap.has(ref_1) && template && template._content) {
this._crossReference._cacheMap.set(ref_1, template._content);
}
sourceDictionary.update(keyName_1.name, ref_1);
this._resources.set(ref_1, keyName_1);
}
this._resourceObject._updated = true;
}
if (template._isNew && this._crossReference) {
template.graphics._processResources(this._crossReference);
}
if (isReference) {
this._resourceObject._updated = true;
}
if (this._hasResourceReference) {
this._source._updated = true;
}
this._sw._executeObject(keyName_1);
this.restore(state);
_addProcSet('ImageB', this._resourceObject);
_addProcSet('ImageC', this._resourceObject);
_addProcSet('ImageI', this._resourceObject);
_addProcSet('Text', this._resourceObject);
}
this._endMarkContent();
};
PdfGraphics.prototype.drawPath = function (path, first, second) {
this._beginMarkContent();
var result = this._setPenBrush(first, second);
if (result.pen || result.brush) {
this._buildUpPath(path._points, path._pathTypes);
this._drawGraphicsPath(result.pen, result.brush, path.fillMode, false);
}
this._endMarkContent();
};
/**
* Draws a rounded rectangle on the page graphics.
*
* ```typescript
* // Load an existing PDF document
* let document: PdfDocument = new PdfDocument(data, password);
* // Access the 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);
* // Create a new brush
* let brush: PdfBrush = new PdfBrush({r: 0, g: 0, b: 255});
* // Draw a rounded rectangle on the page graphics
* graphics.drawRoundedRectangle({x: 10, y: 20, width: 100, height: 200}, 5, pen, brush);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*
* @param {Rectangle} bounds The bounding rectangle of the rounded rectangle.
* @param {number} radius The radius of the rounded corners of the rectangle.
* @param {PdfPen} pen The pen that determines the stroke color, width, and style of the rectangle.
* @param {PdfBrush} brush The brush that determines the fill color and texture of the rectangle.
* @returns {void} Nothing.
*/
PdfGraphics.prototype.drawRoundedRectangle = function (bounds, radius, pen, brush) {
if (pen === null || typeof pen === 'undefined') {
throw new Error('Pen cannot be null or undefined');
}
if (brush === null || typeof brush === 'undefined') {
throw new Error('Brush cannot be null or undefined');
}
var diameter = radius * 2;
var size = [diameter, diameter];
var arc = [bounds.x, bounds.y, size[0], size[1]];
var path = new PdfPath();
if (radius === 0) {
path.addRectangle(bounds);
this.drawPath(path, pen, brush);
}
else {
path._isRoundedRectangle = true;
path.addArc({ x: arc[0], y: arc[1], width: arc[2], height: arc[3] }, 180, 90);
arc[0] = (bounds.x + bounds.width) - diameter;
path.addArc({ x: arc[0], y: arc[1], width: arc[2], height: arc[3] }, 270, 90);
arc[1] = (bounds.y + bounds.height) - diameter;
path.addArc({ x: arc[0], y: arc[1], width: arc[2], height: arc[3] }, 0, 90);
arc[0] = bounds.x;
path.addArc({ x: arc[0], y: arc[1], width: arc[2], height: arc[3] }, 90, 90);
path.closeFigure();
this.drawPath(path, pen, brush);
}
};
PdfGraphics.prototype.drawString = function (value, font, bounds, arg1, arg2, arg3) {
var pen;
var brush;
var format;
if (arg1 instanceof PdfPen) {
pen = arg1;
if (arg2 instanceof PdfBrush) {
brush = arg2;
if (arg3 instanceof PdfStringFormat) {
format = arg3;
}
}
else if (arg2 instanceof PdfStringFormat) {
format = arg2;
}
}
else if (arg1 instanceof PdfBrush) {
brush = arg1;
if (arg2 instanceof PdfStringFormat) {
format = arg2;
}
}
else if (arg2 instanceof PdfBrush) {
brush = arg2;
}
if (arg2 && arg2 instanceof PdfStringFormat) {
format = arg2;
}
if (arg3 && arg3 instanceof PdfStringFormat) {
format = arg3;
}
this._beginMarkContent();
if (font && font._document && font._document._crossReference) {
this._crossReference = font._document._crossReference;
}
var layouter = this._stringLayouter;
if (typeof layouter === 'undefined') {
layouter = new _PdfStringLayouter();
this._stringLayouter = layouter;
}
if (!format) {
format = new PdfStringFormat();
}
if (value) {
value = this._normalizeText(font, value);
}
if (this._isRectangle(bounds)) {
var result = layouter._layout(value, font, format, [bounds.width, bounds.height]);
if (!result._empty) {
var rect = this._checkCorrectLayoutRectangle([result._actualSize.width, result._actualSize.height], bounds.x, bounds.y, format);
if (bounds.width <= 0) {
bounds.x = rect[0];
bounds.width = rect[2];
}
if (bounds.height <= 0) {
bounds.y = rect[1];
bounds.height = rect[3];
}
this._drawStringLayoutResult(result, font, pen, brush, [bounds.x, bounds.y, bounds.width, bounds.height], format);
}
}
_addProcSet('Text', this._resourceObject);
this._endMarkContent();
};
PdfGraphics.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._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 defaultBrush = new PdfBrush({ r: 0, g: 0, b: 0 });
var brushToUse = (typeof element.brush === 'undefined' || element.brush === null) ? defaultBrush : element.brush;
this.drawString(element.text, element.font, bounds, element.pen, brushToUse, element.stringFormat);
};
/**
* Pops and restores a graphics state from the stack and emits the 'Q' operator.
*
* @private
* @returns {PdfGraphicsState} The restored graphics state.
*/
PdfGraphics.prototype._doRestore = function () {
var state = this._graphicsState.pop();
this._m = state._transformationMatrix;
this._currentBrush = state._currentBrush;
this._currentPen = state._currentPen;
this._currentFont = state._currentFont;
this._characterSpacing = state._charSpacing;
this._wordSpacing = state._wordSpacing;
this._textScaling = state._textScaling;
this._textRenderingMode = state._textRenderingMode;
this._sw._restoreGraphicsState();
return state;
};
PdfGraphics.prototype._beginMarkContent = function () {
if (this._layer) {
this._layer._beginLayer(this);
}
};
PdfGraphics.prototype._endMarkContent = function () {
if (this._layer) {
if (this._layer._isEndState && this._layer._parentLayer.length !== 0) {
for (var i = 0; i < this._layer._parentLayer.length; i++) {
this._sw._write('EMC');
}
}
if (this._layer._isEndState) {
this._sw._write('EMC');
}
}
};
/**
* Resolves and writes all pending resources images, fonts, templates, streams
* into the cross-reference table and updates resource dictionaries.
*
* @private
* @param {_PdfCrossReference} crossReference The target cross-reference table.
* @returns {void}
*/
PdfGraphics.prototype._processResources = function (crossReference) {
var _this = this;
this._crossReference = crossReference;
if (this._pendingResource.length > 0) {
this._pendingResource.forEach(function (entry, index) {
if (entry instanceof PdfTemplate) {
entry._crossReference = crossReference;
if (entry._isNew) {
entry.graphics._processResources(crossReference);
}
else {
entry._updatePendingResource(crossReference);
}
}
else if (entry.resource instanceof _PdfBaseStream || entry.resource instanceof _PdfDictionary) {
var reference = void 0;
if (entry.resource._reference) {
reference = entry.resource._reference;
}
else {
reference = crossReference._getNextReference();
entry.resource._reference = reference;
}
if (!crossReference._cacheMap.has(reference) && entry.resource) {
crossReference._cacheMap.set(reference, entry.resource);
}
entry.source.update(entry.key.name, reference);
_this._resources.set(reference, entry.key);
}
else if (entry.resource instanceof PdfImage) {
_this._updateImageResource(entry.resource, entry.key, entry.source, crossReference);
}
else if (entry.resource instanceof PdfFont) {
_this._updateFontResource(entry.resource, entry.key, entry.source, crossReference);
}
_this._source.update('Resources', _this._resourceObject);
_this._source._updated = true;
});
this._pendingResource = [];
}
};
/**
* Registers an image and optional soft mask into the resource dictionary and
* caches the streams in the cross-reference.
*
* @private
* @param {PdfImage} image The image to register.
* @param {_PdfName} keyName The resource name to use under `XObject`.
* @param {_PdfDictionary} source The `XObject` dictionary to update.
* @param {_PdfCrossReference} crossReference The cross-reference to populate.
* @returns {void}
*/
PdfGraphics.prototype._updateImageResource = function (image, keyName, source, crossReference) {
var reference;
if (image._reference) {
reference = image._reference;
}
else {
reference = crossReference._getNextReference();
image._reference = reference;
}
if (!crossReference._cacheMap.has(reference)) {
if (image && image._imageStream && image._imageStream.dictionary) {
crossReference._cacheMap.set(reference, image._imageStream);
image._imageStream.dictionary._updated = true;
if (image._maskStream && image._maskStream.dictionary) {
var ref = void 0;
if (image._maskReference) {
ref = image._maskReference;
}
else {
ref = crossReference._getNextReference();
image._maskReference = ref;
}
crossReference._cacheMap.set(ref, image._maskStream);
image._maskStream.dictionary._updated = true;
image._imageStream.dictionary.set('SMask', ref);
}
}
}
source.update(keyName.name, reference);
this._resources.set(reference, keyName);
this._resourceObject._updated = true;
};
/**
* Registers a font resource standard or TrueType and ensures its dictionary is
* cached and referenced from the page/template resources.
*
* @private
* @param {PdfFont} font The font to register.
* @param {_PdfName} keyName The resource name to use under `Font`.
* @param {_PdfDictionary} source The `Font` dictionary to update.
* @param {_PdfCrossReference} crossReference The cross-reference to populate.
* @returns {void}
*/
PdfGraphics.prototype._updateFontResource = function (font, keyName, source, crossReference) {
var reference;
if (font._reference) {
reference = font._reference;
}
else {
reference = crossReference._getNextReference();
font._reference = reference;
}
if (!crossReference._cacheMap.has(reference)) {
if (font._dictionary) {
crossReference._cacheMap.set(reference, font._dictionary);
source.update(keyName.name, reference);
this._resources.set(reference, keyName);
}
else if (font instanceof PdfTrueTypeFont) {
var internal = font._fontInternal;
if (internal && internal._fontDictionary) {
crossReference._cacheMap.set(reference, internal._fontDictionary);
}
source.update(keyName.name, reference);
this._resources.set(reference, keyName);
}
}
};
/**
* Emits a Bezier-approximated elliptical arc path into the content stream.
*
* @private
* @param {number} x1 Left of bounding box.
* @param {number} y1 Top of bounding box.
* @param {number} x2 Right of bounding box.
* @param {number} y2 Bottom of bounding box.
* @param {number} start Start angle in degrees (clockwise from +X).
* @param {number} sweep Sweep angle in degrees (clockwise).
* @returns {void}
*/
PdfGraphics.prototype._constructArcPath = function (x1, y1, x2, y2, start, sweep) {
var points = _getBezierArc(x1, y1, x2, y2, start, sweep);
if (points.length === 0) {
return;
}
var point = [points[0], points[1], points[2], points[3], points[4], points[5], points[6], points[7]];
this._sw._beginPath(point[0], point[1]);
for (var i = 0; i < points.length; i = i + 8) {
point = [points[i],
points[i + 1],
points[i + 2],
points[i + 3],
points[i + 4],
points[i + 5],
points[i + 6],
points[i + 7]];
this._sw._appendBezierSegment(point[2], point[3], point[4], point[5], point[6], point[7]);
}
};
/**
* Emits a Bezier-approximated pie-arc path without closing to center for pie slices.
*
* @private
* @param {number} x1 Left of bounding box.
* @param {number} y1 Top of bounding box.
* @param {number} x2 Right of bounding box.
* @param {number} y2 Bottom of bounding box.
* @param {number} start Start angle in degrees (clockwise from +X).
* @param {number} sweep Sweep angle in degrees (clockwise).
* @returns {void}
*/
PdfGraphics.prototype._constructPiePath = function (x1, y1, x2, y2, start, sweep) {
var points = _getBezierArc(x1, y1, x2, y2, start, sweep);
if (points.length === 8) {
var point = [points[0], points[1], points[2], points[3], points[4], points[5], points[6], points[7]];
this._sw._beginPath(point[0], point[1]);
for (var i = 0; i < points.length; i = i + 8) {
point = [points[i],
points[i + 1],
points[i + 2],
points[i + 3],
points[i + 4],
points[i + 5],
points[i + 6],
points[i + 7]];
this._sw._appendBezierSegment(point[2], point[3], point[4], point[5], point[6], point[7]);
}
}
};
/**
* Applies pen stroke properties dash, width, join, cap, miter, color to the stream.
*
* @private
* @param {PdfPen} pen The pen to apply.
* @returns {void}
*/
PdfGraphics.prototype._writePen = function (pen) {
var lineWidth = pen._width;
var pattern = pen._dashPattern;
this._sw._setLineDashPattern(pattern, pen._dashOffset * lineWidth);
this._sw._setLineWidth(pen._width);
this._sw._setLineJoin(pen._lineJoin);
this._sw._setLineCap(pen._lineCap);
if (pen._miterLimit > 0) {
this._sw._setMiterLimit(pen._miterLimit);
}
this._sw._setColor([pen._color.r, pen._color.g, pen._color.b], true);
};
/**
* Type guard that determines if the given bounds represent a rectangle.
*
* @private
* @param {Rectangle | Point} bounds A point or rectangle.
* @returns {Rectangle} True if `bounds` is a rectangle.
*/
PdfGraphics.prototype._isRectangle = function (bounds) {
return 'width' in bounds && 'height' in bounds;
};
/**
* Normalizes text for non-Unicode standard fonts by removing unsupported code points
* returning a printable subset.
*
* @private
* @param {PdfFont} font The target font.
* @param {string} value The input text.
* @returns {string} The normalized text.
*/
PdfGraphics.prototype._normalizeText = function (font, value) {
var resultantValue = '';
if (font instanceof PdfStandardFont) {
var result = [];
if (value !== null && typeof value !== 'undefined' && value.length > 0) {
for (var i = 0; i < value.length; i++) {
var charCode = value.charCodeAt(i);
if (charCode >= 0x4E00 && charCode <= 0x9FFF) {
continue;
}
else {
result.push(charCode);
}
}
}
if (result && result.length > 0) {
for (var i = 0; i < result.length; ++i) {
resultantValue += String.fromCharCode(result[Number.parseInt(i.toString(), 10)]);
}
}
}
else {
resultantValue = value;
}
return resultantValue;
};
/**
* Builds a vector path from points and point types, emitting move/line/bezier segments.
*
* @private
* @param {Point[]} points The path points.
* @param {PathPointType[]} types The corresponding point types.
* @returns {void}
* @throws {Error} If path formation is incorrect.
*/
PdfGraphics.prototype._buildUpPath = function (points, types) {
for (var i = 0; i < points.length; i++) {
var point = points[i];
var type = types[i];
switch (type & 0xf) {
case PathPointType.start:
this._sw._beginPath(point.x, point.y);
break;
case PathPointType.bezier:
var result = this._getBezierPoint(points, types, i); // eslint-disable-line
i = result.index;
var first = result.point; // eslint-disable-line
result = this._getBezierPoint(points, types, i);
i = result.index;
var second = result.point; // eslint-disable-line
this._sw._appendBezierSegment(point.x, point.y, first.x, first.y, second.x, second.y);
break;
case PathPointType.line:
this._sw._appendLineSegment(point.x, point.y);
break;
default:
throw new Error('Incorrect path formation.');
}
type = types[Number.parseInt(i.toString(), 10)];
if ((type & PathPointType.closePath) === PathPointType.closePath) {
this._sw._closePath();
}
}
};
/**
* Reads the next Bezier control point and advances the index.
*
* @private
* @param {Point[]} points The path points.
* @param {PathPointType[]} types The point types.
* @param {number} index The current index (at a Bezier marker).
* @returns {{ index: number, point: Point }} The updated index and point.
* @throws {Error} If the current type is not `bezier`.
*/
PdfGraphics.prototype._getBezierPoint = function (points, types, index) {
if (types[index] !== PathPointType.bezier) {
throw new Error('Malforming path.');
}
index++;
return { 'index': index, 'point': points[index] };
};
/**
* Initializes internal graphics defaults state stack, color space flag, CTM caches, etc.
*
* @private
* @returns {void}
*/
PdfGraphics.prototype._initialize = function () {
this._mediaBoxUpperRightBound = 0;
this._characterSpacing = -1;
this._wordSpacing = -1;
this._textScaling = -100;
this._textRenderingMode = -1;
this._graphicsState = [];
this._clipBounds = [0, 0, this._size.width, this._size.height];
this._colorSpaceInitialized = false;
this._startCutIndex = -1;
};
/**
* Ensures both stroking and non-stroking color spaces are set to `DeviceRGB` once.
*
* @private
* @returns {void}
*/
PdfGraphics.prototype._initializeCurrentColorSpace = function () {
if (!this._colorSpaceInitialized) {
this._sw._setColorSpace('DeviceRGB', true);
this._sw._setColorSpace('DeviceRGB', false);
this._colorSpaceInitialized = true;
}
};
/**
* Applies the brush non-stroking color and caches it as the current brush.
*
* @private
* @param {PdfBrush} brush The brush to apply.
* @returns {void}
*/
PdfGraphics.prototype._brushControl = function (brush) {
this._sw._setColor([brush._color.r, brush._color.g, brush._color.b], false);
this._currentBrush = brush;
};
/**
* Applies the pen stroking attributes and caches it as the current pen.
*
* @private
* @param {PdfPen} pen The pen to apply.
* @returns {void}
*/
PdfGraphics.prototype._penControl = function (pen) {
this._currentPen = pen;
this._writePen(pen);
this._currentPen = pen;
};
/**
* Registers/selects the font in resources if needed, sets size, and updates the text state.
*
* @private
* @param {PdfFont} font The font to select.
* @param {PdfStringFormat} format The text format (used for size resolution).
* @returns {void}
*/
PdfGraphics.prototype._fontControl = function (font, format) {
var _this = this;
var size = font._getSize(format);
this._currentFont = font;
var sourceDictionary;
var isReference = false;
var keyName;
var isNew = true;
var ref;
var hasResource = false;
if (this._resourceObject.has('Font')) {
var obj = this._resourceObject.getRaw('Font'); // eslint-disable-line
if (obj !== null && typeof obj !== 'undefined') {
if (obj instanceof _PdfReference) {
isReference = true;
sourceDictionary = this._crossReference._fetch(obj);
}
else if (obj instanceof _PdfDictionary) {
sourceDictionary = obj;
}
}
if (typeof sourceDictionary !== 'undefined' && sourceDictionary !== null) {
isNew = false;
this._resources.forEach(function (value, key) {
if (_this._crossReference) {
if (key !== null && typeof key !== 'undefined') {
var dictionary = _this._crossReference._fetch(key);
if (dictionary && ((font instanceof PdfStandardFont && dictionary === font._dictionary) ||
(font instanceof PdfTrueTypeFont && dictionary === font._fontInternal._fontDictionary) ||
(font instanceof PdfCjkStandardFont && dictionary === font._dictionary))) {
keyName = value;
ref = key;
hasResource = true;
}
}
}
else if (font._reference && font._reference === key) {
keyName = value;
ref = key;
hasResource = true;
}
});
}
}
if (isNew) {
sourceDictionary = new _PdfDictionary(this._crossReference);
this._resourceObject.update('Font', sourceDictionary);
}
if (typeof keyName === 'undefined') {
if (!font._key) {
font._key = _getNewGuidString();
}
keyName = _PdfName.get(font._key);
if (!ref) {
if (font._reference) {
ref = font._reference;
sourceDictionary.update(keyName.name, ref);
}
else if (this._crossReference) {
ref = this._crossReference._getNextReference();
}
else {
this._pendingResource.push({ 'resource': font, 'key': keyName, 'source': sourceDictionary });
}
}
if (ref && this._crossReference) {
if (!font._reference) {
font._reference = ref;
}
if (font._dictionary) {
this._crossReference._cacheMap.set(ref, font._dictionary);
sourceDictionary.update(keyName.name, ref);
}
else if (font instanceof PdfTrueTypeFont) {
var internal = font._fontInternal;
if (internal && internal._fontDictionary && !internal._fontDictionary._currentObj) {
this._crossReference._cacheMap.set(ref, internal._fontDictionary);
}
sourceDictionary.update(keyName.name, ref);
}
}
if (!hasResource && ref) {
this._resources.set(ref, keyName);
}
}
if (isReference) {
this._resourceObject._updated = true;
}
if (this._hasResourceReference) {
this._source._updated = true;
}
this._sw._setFont(keyName.name, size);
};
/**
* Resolves pen/brush overloads to a structured `{pen, brush}` result and applies state.
*
* @private
* @param {PdfPen | PdfBrush} [first] Pen or brush.
* @param {PdfBrush} [second] Optional brush.
* @returns {{pen: PdfPen, brush: PdfBrush}} The resolved pen/brush.
*/
PdfGraphics.prototype._setPenBrush = function (first, second) {
var pen;
var brush;
if (first) {
if (first instanceof PdfPen) {
pen = first;
}
else {
brush = first;
}
}
if (second && second instanceof PdfBrush) {
brush = second;
}
this._stateControl(pen, brush, null);
return { pen: pen, brush: brush };
};
/**
* Applies pen/brush/font state and initializes color spaces when needed.
*
* @private
* @param {PdfPen} [pen] Optional pen to apply.
* @param {PdfBrush} [brush] Optional brush to apply.
* @param {PdfFont} [font] Optional font to select.
* @param {PdfStringFormat} [format] Optional text format for font selection.
* @returns {void}
*/
PdfGraphics.prototype._stateControl = function (pen, brush, font, format) {
if (pen || brush) {
this._initializeCurrentColorSpace();
}
if (pen) {
this._penControl(pen);
}
if (brush) {
this._brushControl(brush);
}
if (font) {
this._fontControl(font, format);
}
};
/**
* Renders a laid-out text result into the content stream with alignment, line spacing,
* clipping, italic simulation, and resource finalization.
*
* @private
* @param {_PdfStringLayoutResult} result The layout computation result.
* @param {PdfFont} font The font to use.
* @param {PdfPen} pen Optional pen for stroke text.
* @param {PdfBrush} brush Optional brush for fill text.
* @param {number[]} layoutRectangle [x, y, width, height] in user units.
* @param {PdfStringFormat} format Text format settings.
* @returns {void}
*/
PdfGraphics.prototype._drawStringLayoutResult = function (result, font, pen, brush, layoutRectangle, format) {
var _this = this;
if (!result._empty) {
var allowPartialLines = (format && typeof format.lineLimit !== 'undefined' && !format.lineLimit);
var shouldClip = (typeof format === 'undefined' || (format && typeof format.noClip !== 'undefined'
&& !format.noClip));
var clipRegion = allowPartialLines && shouldClip;
var state = void 0;
if (clipRegion) {
state = this.save();
var clipBounds = [layoutRectangle[0], layoutRectangle[1], result._actualSize.width, result._actualSize.height];
if (layoutRectangle[2] > 0) {
clipBounds[2] = layoutRectangle[2];
}
if (format.lineAlignment === PdfVerticalAlignment.middle) {
clipBounds[1] += (layoutRectangle[3] - clipBounds[3]) / 2;
}
else if (format.lineAlignment === PdfVerticalAlignment.bottom) {
clipBounds[1] += (layoutRectangle[3] - clipBounds[3]);
}
this.setClip({ x: clipBounds[0], y: clipBounds[1], width: clipBounds[2], height: clipBounds[3] });
}
if (font && font instanceof PdfTrueTypeFont && font._fontInternal &&
font._fontInternal instanceof _UnicodeTrueTypeFont && font.isItalic) {
if (!font._fontInternal._ttfMetrics._isItalic) {
state = this.save();
this._isItalic = true;
}
}
this._applyStringSettings(font, pen, brush, format);
var textScaling = (typeof format !== 'undefined' && format !== null) ? format.horizontalScalingFactor : 100.0;
if (textScaling !== this._textScaling) {
this._sw._setTextScaling(textScaling);
this._textScaling = textScaling;
}
var verticalAlignShift = this._getTextVerticalAlignShift(result._actualSize.height, layoutRectangle[3], format);
var height = (typeof format === 'undefined' || format === null || format.lineSpacing === 0) ?
font._getHeight(format) :
format.lineSpacing + font._getHeight(format);
var script = (format !== null && typeof format !== 'undefined' &&
format.subSuperScript === PdfSubSuperScript.subScript);
var shift = 0;
shift = (script) ? height - (font.height + font._getDescent(format)) : (height - font._getAscent(format));
if (format && format.lineAlignment === PdfVerticalAlignment.bottom) {
var layoutStr = _numberToString(layoutRectangle[3]);
var fontHeightStr = _numberToString(font._getHeight(format));
if (layoutRectangle[3] - result._actualSize.height !== 0 &&
(layoutRectangle[3] - result._actualSize.height) < (font._size / 2) - 1) {
var layoutNum = +layoutStr;
var fontHeightNum = +fontHeightStr;
if (layoutNum <= fontHeightNum) {
shift = -(height / font._size);
}
}
}
var matrix = new _PdfTransformationMatrix();
if (this._isItalic) {
this.translateTransform({ x: layoutRectangle[0] + font.size / 5, y: layoutRectangle[1] - shift + verticalAlignShift });
this._skewTransform(0, -11);
}
else {
matrix._translate(layoutRectangle[0], (-(layoutRectangle[1] + font._getHeight(format)) -
(font._getDescent(format) > 0 ? -font._getDescent(format) : font._getDescent(format))) -
verticalAlignShift);
this._sw._modifyTM(matrix);
}
if (layoutRectangle[3] < font._size) {
if ((result._actualSize.height - layoutRectangle[3]) < (font._size / 2) - 1) {
verticalAlignShift = 0;
}
}
if (verticalAlignShift !== 0) {
if (format !== null && format.lineAlignment === PdfVerticalAlignment.bottom) {
if (layoutRectangle[3] - result._actualSize.height !== 0 &&
(layoutRectangle[3] - result._actualSize.height) > (font._size / 2) - 1) {
verticalAlignShift -= (shift - (height - font._size)) / 2;
}
}
}
if (this._isItalic) {
this._sw._startNextLine(0, 0);
this._sw._setLeading(+height);
}
this._drawLayoutResult(result, font, format, layoutRectangle);
var internal_1 = this._currentFont._fontInternal;
if ((font instanceof PdfTrueTypeFont) && font.isUnicode &&
internal_1 && internal_1._fontDictionary && internal_1._fontDictionary._currentObj) {
this._resourceMap.forEach(function (value, key) {
if (_this._crossReference && _this._crossReference._cacheMap
&& !_this._crossReference._cacheMap.has(key)) {
internal_1._fontDictionary._currentObj._beginSave();
_this._crossReference._writeFontDictionary(internal_1._fontDictionary);
_this._crossReference._cacheMap.set(key, internal_1._fontDictionary);
}
});
}
if (verticalAlignShift !== 0) {
this._sw._startNextLine(0, -(verticalAlignShift - result._lineHeight));
}
_addProcSet('Text', this._resourceObject);
this._sw._endText();
if (this._isItalic) {
this.restore(state);
}
this._underlineStrikeoutText(brush, result, font, layoutRectangle, format);
if (clipRegion) {
this.restore(state);
}
}
};
/**
* Returns the next page if any, or creates and returns a new page at the end.
*
* @private
* @returns {PdfPage} The next or newly created page.
*/
PdfGraphics.prototype._getNextPage = function () {
var page;
var pageCount = this._crossReference._document.pageCount;
if (this._page._pageIndex <= pageCount - 2) {
page = this._crossReference._document.getPage(this._page._pageIndex + 1);
}
else {
page = this._crossReference._document.addPage();
}
return page;
};
/**
* Begins text object, applies text rendering mode, spacing, optional bold
* emulation line width, and selects brush/pen/font as needed.
*
* @private
* @param {PdfFont} font The font.
* @param {PdfPen} pen Optional pen for stroke text.
* @param {PdfBrush} brush Optional brush for fill text.
* @param {PdfStringFormat} format Text format.
* @returns {void}
*/
PdfGraphics.prototype._applyStringSettings = function (font, pen, brush, format) {
var tm = _TextRenderingMode.fill;
var setLineWidth = false;
if (pen && brush) {
tm = _TextRenderingMode.fillStroke;
}
else if (pen) {
tm = _TextRenderingMode.stroke;
}
else if (brush) {
tm = _TextRenderingMode.fill;
}
if (font && font instanceof PdfTrueTypeFont && (font.isUnicode || (font._style & PdfFontStyle.bold) !== 0)) {
var fontName = font._fontInternal._metrics._postScriptName;
var isBoldFont = false;
if (fontName && fontName.toLocaleLowerCase().includes('bold')) {
isBoldFont = true;
}
if (font._fontInternal && font._fontInternal._metrics && font._fontInternal._metrics._isBold !==
font.isBold && font.isBold === true && !isBoldFont) {
if (!pen && brush) {
pen = new PdfPen(brush._color, 1);
}
tm = _TextRenderingMode.fillStroke;
setLineWidth = true;
}
}
if (format && format.clipPath) {
tm |= _TextRenderingMode.clipFlag;
}
this._sw._beginText();
this._stateControl(pen, brush, font, format);
if (tm !== this._textRenderingMode) {
this._sw._setTextRenderingMode(tm);
this._textRenderingMode = tm;
}
var cs = (typeof format !== 'undefined' && format !== null) ? format.characterSpacing : 0;
if (cs !== this._characterSpacing) {
this._sw._setCharacterSpacing(cs);
this._characterSpacing = cs;
}
var ws = (typeof format !== 'undefined' && format !== null) ? format.wordSpacing : 0;
if (ws !== this._wordSpacing) {
this._sw._setWordSpacing(ws);
this._wordSpacing = ws;
}
if (font && setLineWidth) {
this._sw._setLineWidth(font.size / 30);
}
};
/**
* Iterates through laid-out lines, applies horizontal alignment/indents, and writes
* text runs with the appropriate encoding pipeline.
*
* @private
* @param {_PdfStringLayoutResult} result The layout result.
* @param {PdfFont} font The font in use.
* @param {PdfStringFormat} format Text format.
* @param {number[]} layoutRectangle [x, y, width, height] area for the text.
* @returns {void}
*/
PdfGraphics.prototype._drawLayoutResult = function (result, font, format, layoutRectangle) {
var height = (typeof format === 'undefined' || format === null || format.lineSpacing === 0) ?
font._getHeight(format) :
format.lineSpacing + font._getHeight(format);
var lines = result._lines;
var ttfFont = font;
var unicode = (ttfFont !== null && ttfFont.isUnicode);
for (var i = 0, len = lines.length; (i < len && i !== this._startCutIndex); i++) {
var lineInfo = lines[i];
var lineWidth = lineInfo._width;
var hAlignShift = this._getHorizontalAlignShift(lineWidth, layoutRectangle[2], format) +
this._getLineIndent(lineInfo, format, layoutRectangle[2], (i === 0));
if (hAlignShift !== 0) {
this._sw._startNextLine(hAlignShift, 0);
}
if (font instanceof PdfCjkStandardFont) {
this._drawCjkString(lineInfo, layoutRectangle, font, format);
}
else if (unicode) {
this._drawUnicodeLine(lineInfo, layoutRectangle[2], font, format);
}
else {
this._drawAsciiLine(lineInfo, layoutRectangle[2], format, font);
}
if (i + 1 !== len) {
var vAlignShift = this._getTextVerticalAlignShift(result._actualSize.height, layoutRectangle[3], format);
var matrix = new _PdfTransformationMatrix();
var baseline = ((-(layoutRectangle[1] + font._getHeight(format)) -
font._getDescent(format)) -
vAlignShift) -
(height * (i + 1));
matrix._translate(layoutRectangle[0], baseline);
this._sw._modifyTM(matrix);
}
}
};
/**
* Encodes and writes a CJK line, applying justification spacing when required.
*
* @private
* @param {_LineInfo} lineInfo The line to render.
* @param {number[]} layoutRectangle [x, y, width, height].
* @param {PdfFont} font The CJK font.
* @param {PdfStringFormat} format Text format.
* @returns {void}
*/
PdfGraphics.prototype._drawCjkString = function (lineInfo, layoutRectangle, font, format) {
if (font) {
this._justifyLine(lineInfo, layoutRectangle[2], format, font);
var line = lineInfo._text;
var lines = this._getCjkString(line);
var value = _bytesToString(lines);
this._sw._showNextLineText('(' + value + ')', false);
}
};
/**
* Converts a string to a UTF 16BE byte array and escapes PDF literal string symbols.
*
* @private
* @param {string} line The input text.
* @returns {Uint8Array} The escaped byte array.
* @throws {Error} If the input is null or undefined.
*/
PdfGraphics.prototype._getCjkString = function (line) {
if (line === null || typeof line === 'undefined') {
throw new Error('line cannot be null');
}
var value = _stringToUnicodeArray(line);
value = this._escapeSymbols(value);
return value;
};
/**
* Escapes '(', ')', '\\', and CR for safe inclusion in a PDF literal string.
*
* @private
* @param {Uint8Array} data The raw bytes.
* @returns {Uint8Array} The escaped bytes.
* @throws {Error} If `data` is null.
*/
PdfGraphics.prototype._escapeSymbols = function (data) {
if (data === null) {
throw new Error('data cannot be null');
}
var escaped = [];
for (var i = 0, len = data.length; i < len; i++) {
var bt = data[i]; // eslint-disable-line
switch (bt) {
case 40:
case 41:
case 92:
escaped.push(92);
escaped.push(bt);
break;
case 13:
escaped.push(92);
escaped.push(114);
break;
default:
escaped.push(bt);
break;
}
}
return new Uint8Array(escaped);
};
/**
* Draws a Unicode line, handling BiDi/RTL, Arabic shaping, word-space justification,
* and per-word encoding when required.
*
* @private
* @param {_LineInfo} lineInfo The line info.
* @param {number} width Available width for justification.
* @param {PdfFont} font A Unicode TrueType font.
* @param {PdfStringFormat} format Text format.
* @returns {void}
*/
PdfGraphics.prototype._drawUnicodeLine = function (lineInfo, width, font, format) {
var line = lineInfo._text;
var rtl = (format !== null && typeof format !== 'undefined' && format.rightToLeft);
var useWordSpace = (format !== null && typeof format !== 'undefined' && format.wordSpacing > 0);
var ttfFont = font;
var wordSpacing = this._justifyLine(lineInfo, width, format, ttfFont);
var rtlRender = new _RtlRenderer();
if (rtl || (format !== null && typeof format !== 'undefined' && format.textDirection !== PdfTextDirection.none)) {
var blocks = [];
var rightAlign = (format !== null && typeof format !== 'undefined' && format.alignment === PdfTextAlignment.right);
if (format !== null && typeof format !== 'undefined' && format.textDirection !== PdfTextDirection.none) {
blocks = rtlRender._layout(line, ttfFont, (format.textDirection === PdfTextDirection.rightToLeft) ? true : false, useWordSpace, format);
}
else {
blocks = rtlRender._layout(line, ttfFont, rightAlign, useWordSpace, format);
}
var words = [];
if (blocks.length > 1) {
if (format !== null && typeof format !== 'undefined' && format.textDirection !== PdfTextDirection.none) {
words = rtlRender._splitLayout(line, ttfFont, (format.textDirection === PdfTextDirection.rightToLeft) ? true : false, useWordSpace, format);
}
}
else {
words = [line];
}
this._drawUnicodeBlocks(blocks, words, ttfFont, format, wordSpacing);
}
else {
if (useWordSpace) {
var result = this._breakUnicodeLine(line, ttfFont, null);
var blocks = result.tokens;
var words = result.words;
this._drawUnicodeBlocks(blocks, words, ttfFont, format, wordSpacing);
}
else {
var token = this._convertToUnicode(line, ttfFont);
this._sw._showNextLineText(token, true);
}
}
};
/**
* Renders tokenized Unicode runs with explicit positioning to account for
* word spacing and character spacing.
*
* @private
* @param {string[]} blocks Encoded tokens matching `words`.
* @param {string[]} words Original word tokens (visual order).
* @param {PdfTrueTypeFont} font The TrueType font.
* @param {PdfStringFormat} format Text format.
* @param {number} wordSpacing Computed justification word spacing (extra).
* @returns {void}
*/
PdfGraphics.prototype._drawUnicodeBlocks = function (blocks, words, font, format, wordSpacing) {
if (blocks !== null && typeof blocks !== 'undefined' && blocks.length > 0 && words !== null && typeof words !== 'undefined' &&
words.length > 0 && font !== null && typeof font !== 'undefined') {
this._sw._startNextLine();
var x = 0;
var xShift = 0;
var firstLineIndent = 0;
var paragraphIndent = 0;
try {
if (format !== null && typeof format !== 'undefined') {
firstLineIndent = format.firstLineIndent;
paragraphIndent = format.paragraphIndent;
format.firstLineIndent = 0;
format.paragraphIndent = 0;
}
var spaceWidth = font._getCharacterWidth(_StringTokenizer._whiteSpace, format) + wordSpacing;
var characterSpacing = (format !== null) ? format.characterSpacing : 0;
var wordSpace = (format !== null && typeof format !== 'undefined' && wordSpacing === 0) ? format.wordSpacing : 0;
spaceWidth += characterSpacing + wordSpace;
for (var i = 0; i < blocks.length; i++) {
var token = blocks[i]; //eslint-disable-line
var word = words[i]; //eslint-disable-line
var tokenWidth = 0;
if (x !== 0) {
this._sw._startNextLine(x, 0);
}
if (word.length > 0) {
tokenWidth += font.measureString(word, format).width;
tokenWidth += characterSpacing;
this._sw._showText(token);
}
if (i !== blocks.length - 1) {
x = tokenWidth + spaceWidth;
xShift += x;
}
}
if (xShift > 0) {
this._sw._startNextLine(-xShift, 0);
}
}
finally {
if (format !== null && typeof format !== 'undefined') {
format.firstLineIndent = firstLineIndent;
format.paragraphIndent = paragraphIndent;
}
}
}
};
/**
* Splits a Unicode line into words, converts each word via the font reader,
* and returns both the encoded tokens and original words.
*
* @private
* @param {string} line The line to split.
* @param {PdfTrueTypeFont} ttfFont The TrueType font.
* @param {string[]} words Output word array (ignored on input).
* @returns {{tokens: string[], words: string[]}} The encoded tokens and raw words.
*/
PdfGraphics.prototype._breakUnicodeLine = function (line, ttfFont, words) {
var tokens = [];
if (line !== null && typeof line !== 'undefined' && line.length > 0) {
words = line.split(null);
for (var i = 0; i < words.length; i++) {
var word = words[i];
var token = this._convertToUnicode(word, ttfFont);
tokens.push(token);
}
}
return { tokens: tokens, words: words };
};
/**
* Converts a string using the font's TrueType reader and returns a PDF-safe
* UTF-16BE literal string.
*
* @private
* @param {string} text The text to convert.
* @param {PdfTrueTypeFont} ttfFont The font used for conversion.
* @returns {string} The converted PDF string (literal).
*/
PdfGraphics.prototype._convertToUnicode = function (text, ttfFont) {
var token = null;
if (text !== null && typeof text !== 'undefined' && ttfFont !== null && typeof ttfFont !== 'undefined' &&
ttfFont._fontInternal instanceof _UnicodeTrueTypeFont) {
var ttfReader = ttfFont._fontInternal._ttfReader;
ttfFont._setSymbols(text);
token = ttfReader._convertString(text);
var bytes = _stringToUnicodeArray(token);
token = _bytesToString(bytes);
}
return token;
};
/**
* Computes the vertical offset needed to achieve the requested vertical alignment.
*
* @private
* @param {number} textHeight Height of laid-out text.
* @param {number} boundsHeight Height of layout rectangle.
* @param {PdfStringFormat} format Text format.
* @returns {number} The vertical shift in user units.
*/
PdfGraphics.prototype._getTextVerticalAlignShift = function (textHeight, boundsHeight, format) {
var shift = 0;
if (boundsHeight >= 0 && (typeof format !== 'undefined' && format !== null) && format.lineAlignment !== PdfVerticalAlignment.top) {
switch (format.lineAlignment) {
case PdfVerticalAlignment.middle:
shift = (boundsHeight - textHeight) / 2;
break;
case PdfVerticalAlignment.bottom:
shift = boundsHeight - textHeight;
break;
}
}
return shift;
};
/**
* Computes the horizontal offset needed to achieve the requested horizontal alignment.
*
* @private
* @param {number} lineWidth The width of the rendered line.
* @param {number} boundsWidth The available width.
* @param {PdfStringFormat} format Text format.
* @returns {number} The horizontal shift in user units.
*/
PdfGraphics.prototype._getHorizontalAlignShift = function (lineWidth, boundsWidth, format) {
var shift = 0;
if (boundsWidth >= 0 && (typeof format !== 'undefined' && format !== null) && format.alignment !== PdfTextAlignment.left) {
switch (format.alignment) {
case PdfTextAlignment.center:
shift = (boundsWidth - lineWidth) / 2;
break;
case PdfTextAlignment.right:
shift = boundsWidth - lineWidth;
break;
}
}
return shift;
};
/**
* Resolves paragraph/first line indents for the given line respecting bounds width.
*
* @private
* @param {_LineInfo} lineInfo The line info.
* @param {PdfStringFormat} format Text format.
* @param {number} width The line width bound.
* @param {boolean} firstLine True if this is the first line of the paragraph.
* @returns {number} The indent in user units.
*/
PdfGraphics.prototype._getLineIndent = function (lineInfo, format, width, firstLine) {
var lineIndent = 0;
var firstParagraphLine = ((lineInfo._lineType & _LineType.firstParagraphLine) > 0);
if (format && firstParagraphLine) {
lineIndent = (firstLine) ? format.firstLineIndent : format.paragraphIndent;
lineIndent = (width > 0) ? Math.min(width, lineIndent) : lineIndent;
}
return lineIndent;
};
/**
* Writes an ASCII line as a PDF literal string, escaping parentheses,
* and applying justification if required.
*
* @private
* @param {_LineInfo} lineInfo The line to draw.
* @param {number} width Available width for justification.
* @param {PdfStringFormat} format Text format.
* @param {PdfFont} font The font used to measure.
* @returns {void}
*/
PdfGraphics.prototype._drawAsciiLine = function (lineInfo, width, format, font) {
this._justifyLine(lineInfo, width, format, font);
var value = '';
if (lineInfo._text.indexOf('(') !== -1 || lineInfo._text.indexOf(')') !== -1 ||
lineInfo._text.indexOf('\\') !== -1 || lineInfo._text.indexOf('\r') !== -1) {
var text = lineInfo._text;
for (var i = 0, len = text.length; i < len; i++) {
var char = text[i];
if (char === '(') {
value += '\\(';
}
else if (char === ')') {
value += '\\)';
}
else if (char === '\\') {
value += '\\\\';
}
else if (char === '\r') {
value += '\\r';
}
else {
value += char;
}
}
}
if (value === '') {
value = lineInfo._text;
}
this._sw._showNextLineText('(' + value + ')');
};
/**
* Applies word spacing justification to the current line if conditions are met.
*
* @private
* @param {_LineInfo} lineInfo The line info.
* @param {number} boundsWidth The line width bound.
* @param {PdfStringFormat} format Text format.
* @param {PdfFont} font Font for measuring spaces.
* @returns {number} The extra word spacing applied per whitespace.
*/
PdfGraphics.prototype._justifyLine = function (lineInfo, boundsWidth, format, font) {
var line = lineInfo._text;
var lineWidth = lineInfo._width;
var shouldJustify = this._shouldJustify(lineInfo, boundsWidth, format, font);
var hasWordSpacing = (format && format.wordSpacing !== 0);
var whitespacesCount = font._getCharacterCount(line, [' ', '\t']);
var wordSpace = 0;
if (shouldJustify) {
if (hasWordSpacing) {
lineWidth -= (whitespacesCount * format.wordSpacing);
}
wordSpace = (boundsWidth - lineWidth) / whitespacesCount;
this._sw._setWordSpacing(wordSpace);
}
else if (format && format.alignment === PdfTextAlignment.justify) {
this._sw._setWordSpacing(0);
}
return wordSpace;
};
/**
* Determines whether the current line should be justified based on alignment,
* width, whitespace presence, and line break type.
*
* @private
* @param {_LineInfo} lineInfo The line info.
* @param {number} boundsWidth The width bound.
* @param {PdfStringFormat} format Text format.
* @param {PdfFont} font Font for character counting.
* @returns {boolean} True if justification should be applied.
*/
PdfGraphics.prototype._shouldJustify = function (lineInfo, boundsWidth, format, font) {
var line = lineInfo._text;
var lineWidth = lineInfo._width;
var justifyStyle = (format && format.alignment === PdfTextAlignment.justify);
var goodWidth = (boundsWidth >= 0 && lineWidth < boundsWidth);
var whitespacesCount = font._getCharacterCount(line, [' ', '\t']);
var hasSpaces = (whitespacesCount > 0 && line[0] !== ' ');
var goodLineBreakStyle = ((lineInfo._lineType & _LineType.layoutBreak) > 0);
return (justifyStyle && goodWidth && hasSpaces && goodLineBreakStyle);
};
/**
* Draws underline and/or strikeout lines over the rendered text according to font flags.
*
* @private
* @param {PdfBrush} brush The brush used to color the decoration lines.
* @param {_PdfStringLayoutResult} result The layout result.
* @param {PdfFont} font The font used.
* @param {number[]} layoutRectangle [x, y, width, height] text bounds.
* @param {PdfStringFormat} format Text format.
* @returns {void}
*/
PdfGraphics.prototype._underlineStrikeoutText = function (brush, result, font, layoutRectangle, format) {
if (font.isUnderline || font.isStrikeout) {
var linePen = this._createUnderlineStrikeoutPen(brush, font);
if (typeof linePen !== 'undefined' && linePen !== null) {
var shift = this._getTextVerticalAlignShift(result._actualSize.height, layoutRectangle[3], format);
var underlineYOffset = layoutRectangle[1] + shift + font._getAscent(format) + 1.5 * linePen._width;
var strikeoutYOffset = layoutRectangle[1] + shift + font._getHeight(format) / 2 + 1.5 * linePen._width;
var lines = result._lines;
for (var i = 0, len = lines.length; i < len; i++) {
var lineInfo = lines[i];
var lineWidth = lineInfo._width;
var hShift = this._getHorizontalAlignShift(lineWidth, layoutRectangle[2], format);
var lineIndent = this._getLineIndent(lineInfo, format, layoutRectangle[2], (i === 0));
var x1 = layoutRectangle[0] + hShift;
var x2 = (!this._shouldJustify(lineInfo, layoutRectangle[2], format, font)) ?
x1 + lineWidth - lineIndent :
x1 + layoutRectangle[2] - lineIndent;
if (font.isUnderline) {
this.drawLine(linePen, { x: x1, y: underlineYOffset }, { x: x2, y: underlineYOffset });
underlineYOffset += result._lineHeight;
}
if (font.isStrikeout) {
this.drawLine(linePen, { x: x1, y: strikeoutYOffset }, { x: x2, y: strikeoutYOffset });
strikeoutYOffset += result._lineHeight;
}
}
}
}
};
/**
* Creates a pen for underline/strikeout based on the brush color and font size.
*
* @private
* @param {PdfBrush} brush The text fill brush.
* @param {PdfFont} font The font used .
* @returns {PdfPen} The decoration pen.
*/
PdfGraphics.prototype._createUnderlineStrikeoutPen = function (brush, font) {
var brushColor = brush ? brush._color : undefined;
return new PdfPen(brushColor, font._size / 20);
};
/**
* Computes the top-left anchor for the text rectangle based on alignment against (x, y).
*
* @private
* @param {number[]} textSize The measured text size as [width, height].
* @param {number} x Anchor X.
* @param {number} y Anchor Y.
* @param {PdfStringFormat} format Text format alignment / line alignment.
* @returns {number[]} The adjusted rectangle [x, y, width, height].
*/
PdfGraphics.prototype._checkCorrectLayoutRectangle = function (textSize, x, y, format) {
var layoutedRectangle = [x, y, textSize[0], textSize[0]];
if (format) {
switch (format.alignment) {
case PdfTextAlignment.center:
layoutedRectangle[0] = layoutedRectangle[0] - layoutedRectangle[2] / 2;
break;
case PdfTextAlignment.right:
layoutedRectangle[0] = layoutedRectangle[0] - layoutedRectangle[2];
break;
}
switch (format.lineAlignment) {
case PdfVerticalAlignment.middle:
layoutedRectangle[1] = layoutedRectangle[1] - layoutedRectangle[3] / 2;
break;
case PdfVerticalAlignment.bottom:
layoutedRectangle[1] = layoutedRectangle[1] - layoutedRectangle[3];
break;
}
}
return layoutedRectangle;
};
/**
* Finishes the current path by stroking/filling/closing based on pen/brush and fill mode.
*
* @private
* @param {PdfPen} [pen] Optional stroke pen.
* @param {PdfBrush} [brush] Optional fill brush.
* @param {PdfFillMode} [fillMode=PdfFillMode.winding] Fill rule to use.
* @param {boolean} [needClosing=false] Whether to close the path before painting.
* @returns {void}
*/
PdfGraphics.prototype._drawGraphicsPath = function (pen, brush, fillMode, needClosing) {
if (typeof fillMode === 'undefined') {
fillMode = PdfFillMode.winding;
}
var isBrush = (typeof brush !== 'undefined' && brush !== null);
var isPen = (typeof pen !== 'undefined' && pen !== null);
var isEvenOdd = fillMode === PdfFillMode.alternate;
if (isPen && isBrush) {
if (needClosing) {
this._sw._closeFillStrokePath(isEvenOdd);
}
else {
this._sw._fillStrokePath(isEvenOdd);
}
}
else if (!isPen && !isBrush) {
this._sw._endPath();
}
else if (isPen) {
if (needClosing) {
this._sw._closeStrokePath();
}
else {
this._sw._strokePath();
}
}
else {
if (needClosing) {
this._sw._closeFillPath(isEvenOdd);
}
else {
this._sw._fillPath(isEvenOdd);
}
}
};
/**
* Adjusts the coordinate system for page/template rendering, translating to a
* top-left origin if needed and honoring CropBox/MediaBox combinations.
*
* @private
* @param {PdfPage} [page] Optional page context.
* @returns {void}
*/
PdfGraphics.prototype._initializeCoordinates = function (page) {
var cbox;
if (page) {
var location_1 = [0, 0];
var needTransformation = false;
if (page._pageDictionary.has('CropBox') && page._pageDictionary.has('MediaBox')) {
cbox = page._pageDictionary.getArray('CropBox');
var mbox = page._pageDictionary.getArray('MediaBox');
if (cbox[0] === mbox[0] && cbox[1] === mbox[1] && cbox[2] === mbox[2] && cbox[3] === mbox[3]) {
needTransformation = true;
}
if (cbox[0] > 0 && cbox[3] > 0 && mbox[0] < 0 && mbox[1] < 0) {
this.translateTransform({ x: cbox[0], y: -cbox[3] });
location_1[0] = -cbox[0];
location_1[1] = cbox[3];
}
else if (!page._pageDictionary.has('CropBox')) {
needTransformation = true;
}
if (needTransformation) {
this._sw._writeComment('Change co-ordinate system to left/top.');
if (this._cropBox) {
this.translateTransform({ x: this._cropBox[0], y: -this._cropBox[3] });
}
else {
if (-(page._origin[1]) < this._mediaBoxUpperRightBound || this._mediaBoxUpperRightBound === 0) {
this.translateTransform({ x: 0, y: -this._size.height });
}
else {
this.translateTransform({ x: 0, y: -this._mediaBoxUpperRightBound });
}
}
}
}
}
else {
this._sw._writeComment('Change co-ordinate system to left/top.');
if (this._mediaBoxUpperRightBound !== (-this._size.height)) {
if (this._cropBox) {
cbox = this._cropBox;
if (cbox[0] > 0 || cbox[1] > 0 || this._size.width === cbox[2] || this._size.height === cbox[3]) {
this.translateTransform({ x: cbox[0], y: -cbox[3] });
}
else {
if (this._mediaBoxUpperRightBound === this._size.height || this._mediaBoxUpperRightBound === 0) {
this.translateTransform({ x: 0, y: -this._size.height });
}
else {
this.translateTransform({ x: 0, y: -this._mediaBoxUpperRightBound });
}
}
}
else {
if (this._mediaBoxUpperRightBound === this._size.height || this._mediaBoxUpperRightBound === 0) {
this.translateTransform({ x: 0, y: -this._size.height });
}
else {
this.translateTransform({ x: 0, y: -this._mediaBoxUpperRightBound });
}
}
}
}
};
/**
* Caches an existing transparency `ExtGState` entry in the internal map and
* reconstructs its composite key.
*
* @private
* @param {_PdfReference} ref The graphics state reference.
* @param {_PdfName} name The resource name.
* @returns {void}
*/
PdfGraphics.prototype._setTransparencyData = function (ref, name) {
this._resourceMap.set(ref, name);
var dictionary = this._crossReference._fetch(ref);
var stroke = 0;
var fill = 0;
var mode = 0;
if (dictionary) {
if (dictionary.has('CA')) {
stroke = dictionary.get('CA');
}
if (dictionary.has('ca')) {
fill = dictionary.get('ca');
}
if (dictionary.has('ca')) {
fill = dictionary.get('ca');
}
if (dictionary.has('BM')) {
mode = _mapBlendMode(dictionary.get('BM'));
}
}
var tkey = 'CA:' + stroke.toString() + '_ca:' + fill.toString() + '_BM:' + mode.toString();
var tdata = new _TransparencyData();
tdata._dictionary = dictionary;
tdata._key = tkey;
tdata._name = name;
tdata._reference = ref;
this._transparencies.set(tdata, tkey);
};
/**
* Applies a translation x, -y to the provided matrix and returns it.
*
* @private
* @param {number} x Translation along X.
* @param {number} y Translation along Y.
* @param {_PdfTransformationMatrix} input The matrix to mutate.
* @returns {_PdfTransformationMatrix} The mutated matrix.
*/
PdfGraphics.prototype._getTranslateTransform = function (x, y, input) {
input._translate(x, -y);
return input;
};
/**
* Applies a scale to the provided matrix and returns it.
*
* @private
* @param {number} x Scale along X.
* @param {number} y Scale along Y.
* @param {_PdfTransformationMatrix} input The matrix to mutate (or create).
* @returns {_PdfTransformationMatrix} The mutated matrix.
*/
PdfGraphics.prototype._getScaleTransform = function (x, y, input) {
if (input === null || typeof input === 'undefined') {
input = new _PdfTransformationMatrix();
}
input._scale(x, y);
return input;
};
/**
* Clips to the specified bounds and translates the coordinate system to the
* new top left origin.
*
* @private
* @param {number[]} clipBounds [x, y, width, height] clipping rectangle.
* @returns {void}
*/
PdfGraphics.prototype._clipTranslateMargins = function (clipBounds) {
this._clipBounds = clipBounds;
this._sw._writeComment('Clip margins.');
this._sw._appendRectangle(clipBounds[0], clipBounds[1], clipBounds[2], clipBounds[3]);
this._sw._closePath();
this._sw._clipPath(false);
this._sw._writeComment('Translate co-ordinate system.');
this.translateTransform({ x: clipBounds[0], y: clipBounds[1] });
};
/**
* Applies a clipping rectangle and translates the graphics coordinate system
* based on the specified margin and template bounds.
*
* @private
* @param {number[]} clipBounds The bounds used to compute clipping and translation,
* specified as an array containing margin and template offsets.
* @returns {void} Nothing.
*/
PdfGraphics.prototype._clipTranslateMarginsWithBounds = function (clipBounds) {
var bounds = [clipBounds[2], clipBounds[3], this._size.width - clipBounds[2] - clipBounds[4],
this._size.height - clipBounds[3] - clipBounds[4]];
this._clipBounds = bounds;
this._sw._writeComment('Clip margins.');
this._sw._appendRectangle(bounds[0], bounds[1], bounds[2], bounds[3]);
this._sw._closePath();
this._sw._clipPath(false);
this._sw._writeComment('Translate co-ordinate system.');
this.translateTransform({ x: clipBounds[0], y: clipBounds[1] });
};
/**
* Applies a skew to the current CTM by composing a skew matrix into the stream
* and accumulating it into the local CTM.
*
* @private
* @param {number} angleX Skew angle along X (degrees).
* @param {number} angleY Skew angle along Y (degrees).
* @returns {void}
*/
PdfGraphics.prototype._skewTransform = function (angleX, angleY) {
var matrix = new _PdfTransformationMatrix();
this._getSkewTransform(angleX, angleY, matrix);
this._sw._modifyCtm(matrix);
matrix._multiply(matrix);
};
/**
* Skews the provided matrix by -angleX, -angleY and returns it.
*
* @private
* @param {number} angleX Skew angle X in degrees.
* @param {number} angleY Skew angle Y in degrees.
* @param {_PdfTransformationMatrix} input The matrix to mutate.
* @returns {_PdfTransformationMatrix} The mutated matrix.
*/
PdfGraphics.prototype._getSkewTransform = function (angleX, angleY, input) {
input._skew(-angleX, -angleY);
return input;
};
return PdfGraphics;
}());
export { PdfGraphics };
/**
* Represents an internal affine transformation matrix used for PDF graphics
* operations such as translate, scale, rotate, skew, and matrix multiplication.
*
* @private
*/
var _PdfTransformationMatrix = /** @class */ (function () {
/**
* Initializes a new identity transformation matrix (1 0 0 1 0 0).
*
* @private
*/
function _PdfTransformationMatrix() {
this._matrix = new _Matrix(1, 0, 0, 1, 0, 0);
}
/**
* Applies a translation to the matrix.
*
* @param {number} x Horizontal translation.
* @param {number} y Vertical translation.
* @returns {void} nothing.
*
* @private
*/
_PdfTransformationMatrix.prototype._translate = function (x, y) {
this._matrix._translate(x, y);
};
/**
* Replaces the scale components of the transformation matrix.
*
* @param {number} x Scale value along X.
* @param {number} y Scale value along Y.
* @returns {void} nothing.
*
* @private
*/
_PdfTransformationMatrix.prototype._scale = function (x, y) {
this._matrix._elements[0] = x;
this._matrix._elements[3] = y;
};
/**
* Rotates the matrix by the specified angle in degrees.
*
* @param {number} angle Rotation angle in degrees.
* @returns {void} nothing.
*
* @private
*/
_PdfTransformationMatrix.prototype._rotate = function (angle) {
angle = (angle * Math.PI) / 180;
this._matrix._elements[0] = Math.cos(angle);
this._matrix._elements[1] = Math.sin(angle);
this._matrix._elements[2] = -Math.sin(angle);
this._matrix._elements[3] = Math.cos(angle);
};
/**
* Multiplies this transformation matrix with another transformation matrix.
*
* @param {_PdfTransformationMatrix} matrix The matrix to multiply with.
* @returns {void} nothing.
*
* @private
*/
_PdfTransformationMatrix.prototype._multiply = function (matrix) {
this._matrix._multiply(matrix._matrix);
};
/**
* Converts the matrix into a space-separated string representation suitable
* for writing into a PDF content stream.
*
* @returns {string} String representation of the matrix.
*
* @private
*/
_PdfTransformationMatrix.prototype._toString = function () {
var builder = '';
this._matrix._elements.forEach(function (element) {
builder += _floatToString(element) + ' ';
});
return builder;
};
/**
* Applies a skew transform using tangent based shear operations.
*
* @param {number} angleX Skew angle along X axis in degrees.
* @param {number} angleY Skew angle along Y axis in degrees.
* @returns {void} nothing.
*
* @private
*/
_PdfTransformationMatrix.prototype._skew = function (angleX, angleY) {
var tanA = Math.tan(this._degreeToRadians(angleX));
var tanB = Math.tan(this._degreeToRadians(angleY));
var skew = new _Matrix(1, tanA, tanB, 1, 0, 0);
this._matrix._multiply(skew);
};
/**
* Converts degrees to radians.
*
* @param {number} degreesX Angle in degrees.
* @returns {number} Equivalent angle in radians.
*
* @private
*/
_PdfTransformationMatrix.prototype._degreeToRadians = function (degreesX) {
var degreeRadFactor = Math.PI / 180;
return degreeRadFactor * degreesX;
};
return _PdfTransformationMatrix;
}());
export { _PdfTransformationMatrix };
/**
* Internal low level 2D affine matrix representing the six element PDF transformation matrix.
*
* @private
*/
var _Matrix = /** @class */ (function () {
function _Matrix(arg1, arg2, arg3, arg4, arg5, arg6) {
if (typeof arg1 === 'undefined') {
this._elements = [];
}
else if (typeof arg1 === 'number') {
this._elements = [arg1, arg2, arg3, arg4, arg5, arg6];
}
else {
this._elements = arg1;
}
}
Object.defineProperty(_Matrix.prototype, "_offsetX", {
/**
* Gets the translation dx component of the matrix.
*
* @returns {number} The X offset.
*
* @private
*/
get: function () {
return this._elements[4];
},
enumerable: true,
configurable: true
});
Object.defineProperty(_Matrix.prototype, "_offsetY", {
/**
* Gets the translation dy component of the matrix.
*
* @returns {number} The Y offset.
*
* @private
*/
get: function () {
return this._elements[5];
},
enumerable: true,
configurable: true
});
/**
* Creates a deep copy of the matrix.
*
* @returns {_Matrix} A new matrix with identical values.
*
* @private
*/
_Matrix.prototype._clone = function () {
return new _Matrix(this._elements.slice());
};
/**
* Overrides the translation components of the matrix.
*
* @param {number} x X translation.
* @param {number} y Y translation.
* @returns {void} nothing.
*
* @private
*/
_Matrix.prototype._translate = function (x, y) {
this._elements[4] = x;
this._elements[5] = y;
};
/**
* Transforms a point using this matrix.
*
* @param {Point} points The point to transform.
* @returns {Point} The transformed point.
*
* @private
*/
_Matrix.prototype._transform = function (points) {
var x = points.x;
var y = points.y;
var x2 = x * this._elements[0] + y * this._elements[2] + this._offsetX;
var y2 = x * this._elements[1] + y * this._elements[3] + this._offsetY;
return { x: x2, y: y2 };
};
/**
* Multiplies this matrix with another matrix using affine matrix multiplication rules.
*
* @param {_Matrix} matrix The matrix to multiply with.
* @returns {void} nothing.
*
* @private
*/
_Matrix.prototype._multiply = function (matrix) {
this._elements = [(this._elements[0] * matrix._elements[0] + this._elements[1] * matrix._elements[2]),
(this._elements[0] * matrix._elements[1] + this._elements[1] * matrix._elements[3]),
(this._elements[2] * matrix._elements[0] + this._elements[3] * matrix._elements[2]),
(this._elements[2] * matrix._elements[1] + this._elements[3] * matrix._elements[3]),
(this._offsetX * matrix._elements[0] + this._offsetY * matrix._elements[2] + matrix._offsetX),
(this._offsetX * matrix._elements[1] + this._offsetY * matrix._elements[3] + matrix._offsetY)];
};
return _Matrix;
}());
export { _Matrix };
/**
* Represents a state of the graphics from a PDF page.
*
* ```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 font
* let font: PdfFont = document.embedFont(PdfFontFamily.helvetica, 20, PdfFontStyle.regular);
* // Save the graphics state
* let state: PdfGraphicsState = graphics.save();
* // Set graphics translate transform
* graphics.translateTransform({x: 100, y: 100});
* // Draw the string
* graphics.drawString('Hello world!', font, {x: 10, y: 20, width: 100, height: 200}, new PdfBrush({r: 0, g: 0, b: 255}));
* // Restore the graphics state
* graphics.restore(state);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
var PdfGraphicsState = /** @class */ (function () {
/**
* Initializes a new instance of the `PdfGraphicsState` class.
*
* @private
* @param {PdfGraphics} graphics Graphics.
* @param {_PdfTransformationMatrix} matrix Matrix.
*
*/
function PdfGraphicsState(graphics, matrix) {
if (graphics) {
this._g = graphics;
this._transformationMatrix = matrix;
}
this._charSpacing = 0;
this._wordSpacing = 0;
this._textScaling = 100;
this._textRenderingMode = _TextRenderingMode.fill;
}
return PdfGraphicsState;
}());
export { PdfGraphicsState };
/**
* Internal container for an ExtGState transparency object, including its
* dictionary, name, reference, and unique cache key.
*
* @private
*/
var _TransparencyData = /** @class */ (function () {
function _TransparencyData() {
}
return _TransparencyData;
}());
export var _TextRenderingMode;
(function (_TextRenderingMode) {
_TextRenderingMode[_TextRenderingMode["fill"] = 0] = "fill";
_TextRenderingMode[_TextRenderingMode["stroke"] = 1] = "stroke";
_TextRenderingMode[_TextRenderingMode["fillStroke"] = 2] = "fillStroke";
_TextRenderingMode[_TextRenderingMode["none"] = 3] = "none";
_TextRenderingMode[_TextRenderingMode["clipFlag"] = 4] = "clipFlag";
_TextRenderingMode[_TextRenderingMode["clipFill"] = 4] = "clipFill";
_TextRenderingMode[_TextRenderingMode["clipStroke"] = 5] = "clipStroke";
_TextRenderingMode[_TextRenderingMode["clipFillStroke"] = 6] = "clipFillStroke";
_TextRenderingMode[_TextRenderingMode["clip"] = 7] = "clip";
})(_TextRenderingMode || (_TextRenderingMode = {}));
/**
* Represents a brush for the PDF page.
*
* ```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 brush
* let brush: PdfBrush = new PdfBrush({r: 0, g: 255, b: 255});
* // Draw a rectangle using brush
* graphics.drawRectangle({x: 10, y: 10, width: 100, height: 100}, brush);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
var PdfBrush = /** @class */ (function () {
function PdfBrush(color) {
this._color = typeof color !== 'undefined' ? color : { r: 0, g: 0, b: 0 };
}
return PdfBrush;
}());
export { PdfBrush };
/**
* Represents a pen for the PDF page.
*
* ```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 a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
var PdfPen = /** @class */ (function () {
function PdfPen(color, width, properties) {
this._color = color;
this._width = width;
this._dashOffset = 0;
this._dashPattern = [];
this._dashStyle = PdfDashStyle.solid;
this._miterLimit = 0;
this._lineCap = PdfLineCap.flat;
this._lineJoin = PdfLineJoin.miter;
if (properties) {
if (_isNullOrUndefined(properties.dashOffset)) {
this._dashOffset = properties.dashOffset;
}
if (_isNullOrUndefined(properties.dashPattern) && Array.isArray(properties.dashPattern)) {
this._dashPattern = properties.dashPattern;
}
if (_isNullOrUndefined(properties.dashStyle)) {
this._dashStyle = properties.dashStyle;
}
if (_isNullOrUndefined(properties.miterLimit)) {
this._miterLimit = properties.miterLimit;
}
if (_isNullOrUndefined(properties.lineCap)) {
this._lineCap = properties.lineCap;
}
if (_isNullOrUndefined(properties.lineJoin)) {
this._lineJoin = properties.lineJoin;
}
}
}
Object.defineProperty(PdfPen.prototype, "color", {
/**
* Gets the pen color used for drawing.
*
* @returns {PdfColor} The current pen color.
* ```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}, 4, {dashOffset: 0.5, dashPattern: [4,2,1,3], dashStyle: PdfDashStyle.custom, miterLimit:2, lineCap: PdfLineCap.round, lineJoin: PdfLineJoin.bevel});
* // Gets the pen color used for drawing
* const color: PdfColor = pen.color;
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._color;
},
/**
* Sets the pen color used for drawing.
*
* @param {PdfColor} value - The color to use for the pen.
* ```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}, 4);
* // Set the pen color for drawing
* pen.color = {r: 255, g: 0, b: 0};
* // Draw using the pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (_isNullOrUndefined(value)) {
this._color = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPen.prototype, "width", {
/**
* Gets the width of the pen.
*
* @returns {number} The pen width in points.
* ```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}, 4, {dashOffset: 0.5, dashPattern: [4,2,1,3], dashStyle: PdfDashStyle.custom, miterLimit:2, lineCap: PdfLineCap.round, lineJoin: PdfLineJoin.bevel});
* // Gets the width of the pen used for drawing
* const w: number = pen.width;
* // Draw a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._width;
},
/**
* Sets the width of the pen.
*
* @param {number} value - The pen width in points.
* ```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);
* // Sets the pen width for drawing
* pen.width = 2;
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (!Number.isNaN(value) && value > 0) {
this._width = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPen.prototype, "dashOffset", {
/**
* Gets the dash phase offset that shifts where the dash pattern begins. Measured in points.
*
* @returns {number} The dash offset in points.
* ```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}, 4, {dashOffset: 0.5, dashPattern: [4,2,1,3], dashStyle: PdfDashStyle.custom, miterLimit:2, lineCap: PdfLineCap.round, lineJoin: PdfLineJoin.bevel});
* const offset = pen.dashOffset;
* // Draw a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._dashOffset;
},
/**
* Sets the dash phase offset that determines where the dash pattern begins. Measured in points.
*
* @param {number} value - The dash offset in points.
* ```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}, 4);
* //Sets the dashOffset value for drawing
* pen.dashOffset = 0.5;
* // Draw a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (!Number.isNaN(value) && value > 0) {
this._dashOffset = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPen.prototype, "dashPattern", {
/**
* Gets the dash pattern array specifying alternating dash and gap lengths in points.
*
* @returns {number[]} The dash/gap lengths array.
* ```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}, 4, {dashOffset: 0.5, dashPattern: [4,2,1,3], dashStyle: PdfDashStyle.custom});
* // Gets the dash pattern used for drawing
* const pattern: number[] = pen.dashPattern;
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._dashPattern;
},
/**
* Sets the dash pattern array specifying alternating dash and gap lengths in points.
*
* @remarks
* The dash pattern cannot be set when the pen's `dashStyle` is `PdfDashStyle.Solid`.
*
* @param {number[]} value - Array of numbers representing dash and gap lengths.
* ```typescript
* // Create a new PDF document
* let document: PdfDocument = new PdfDocument();
* // Add a new page
* let page: PdfPage = document.pages.add();
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a pen with width 2
* let pen: PdfPen = new PdfPen({r:0, g:0, b:0}, 2);
* //Sets the dashPattern value for drawing
* pen.dashPattern = [3, 1, 3, 1];
* // Draw a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (Array.isArray(value) && value.length > 0) {
this._dashPattern = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPen.prototype, "dashStyle", {
/**
* Gets the dash style of the pen.
*
* @returns {PdfDashStyle} The dash style.
* ```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}, 4, {dashOffset: 0.5, dashPattern: [4,2,1,3], dashStyle: PdfDashStyle.custom, miterLimit:2, lineCap: PdfLineCap.round, lineJoin: PdfLineJoin.bevel});
* // Gets pen dash style used for drawing
* const style: PdfDashStyle = pen.dashStyle;
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._dashStyle;
},
/**
* Sets the dash style of the pen.
*
* @param {PdfDashStyle} value - The dash style to apply.
* ```typescript
* // Create a new PDF document
* let document: PdfDocument = new PdfDocument();
* // Add a new page
* let page: PdfPage = document.pages.add();
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a pen and set a dash style
* let pen: PdfPen = new PdfPen({r:0, g:0, b:0}, 2);
* // Sets the dashStyle value for drawing
* pen.dashStyle = PdfDashStyle.custom;
* // Sets the dash pattern for custom style
* pen.dashPattern = [4, 2, 1, 3];
* //Sets the dashStyle value for drawing
* // For custom style, set the pattern as well
* pen.dashStyle = PdfDashStyle.custom;
* pen.dashPattern = [4,2,1,3];
* // Draw a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (!Number.isNaN(value) && value >= PdfDashStyle.solid && value <= PdfDashStyle.custom) {
this._dashStyle = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPen.prototype, "miterLimit", {
/**
* Gets the miter limit value, used when lineJoin is Miter.
*
* @returns {number} The miter limit value.
* ```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}, 4);
* // Gets miter limit used for drawing
* const m: number = pen.miterLimit;
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._miterLimit;
},
/**
* Sets the miter limit for mitered line joins.
*
* @param {number} value - The miter limit value.
* ```typescript
* // Create a new PDF document
* let document: PdfDocument = new PdfDocument();
* // Add a new page
* let page: PdfPage = document.pages.add();
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a pen and set miter limit
* let pen: PdfPen = new PdfPen({r:0, g:0, b:0}, 2);
* // Sets the line join type as miter.
* pen.lineJoin = PdfLineJoin.miter;
* // Sets the miter limit value for drawing
* pen.miterLimit = 4;
* // Draw a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (!Number.isNaN(value) && _isNullOrUndefined(value)) {
this._miterLimit = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPen.prototype, "lineCap", {
/**
* Gets the line cap style applied to the ends of lines.
*
* @returns {PdfLineCap} The line cap style.
* ```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}, 4);
* // Gets line cap style used for drawing
* const cap: PdfLineCap = pen.lineCap; // PdfLineCap.flat | round | square
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._lineCap;
},
/**
* Sets the line cap style applied to the ends of lines.
*
* @param {PdfLineCap} value - The line cap style.
* ```typescript
* // Create a new PDF document
* let document: PdfDocument = new PdfDocument();
* // Add a new page
* let page: PdfPage = document.pages.add();
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a pen and set line cap
* let pen: PdfPen = new PdfPen({r:0, g:0, b:0}, 2);
* // Sets the line cap value used for drawing
* pen.lineCap = PdfLineCap.round;
* // Draw a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (!Number.isNaN(value) && value >= PdfLineCap.flat && value <= PdfLineCap.square) {
this._lineCap = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(PdfPen.prototype, "lineJoin", {
/**
* Gets the line join style used at intersections between line segments.
*
* @returns {PdfLineJoin} The line join style.
* ```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}, 4);
* // Gets the line join style used for drawing
* const join: PdfLineJoin = pen.lineJoin; // PdfLineJoin.miter | round | bevel
* // Destroy the document
* document.destroy();
* ```
*/
get: function () {
return this._lineJoin;
},
/**
* Sets the line join style used at intersections between line segments.
*
* @param {PdfLineJoin} value - The line join style to set.
* ```typescript
* // Create a new PDF document
* let document: PdfDocument = new PdfDocument();
* // Add a new page
* let page: PdfPage = document.pages.add();
* // Gets the graphics of the PDF page
* let graphics: PdfGraphics = page.graphics;
* // Create a pen and set line join
* let pen: PdfPen = new PdfPen({r:0, g:0, b:0}, 2);
* // Sets the line join type for drawing
* pen.lineJoin = PdfLineJoin.bevel;
* // Draw a rectangle using pen
* graphics.drawRectangle({x: 150, y: 50, width: 50, height: 50}, pen);
* // Save the document
* document.save('output.pdf');
* // Destroy the document
* document.destroy();
* ```
*/
set: function (value) {
if (!Number.isNaN(value) && value >= PdfLineJoin.miter && value <= PdfLineJoin.bevel) {
this._lineJoin = value;
}
},
enumerable: true,
configurable: true
});
return PdfPen;
}());
export { PdfPen };
/**
* Provides internal unit conversion between various measurement units used
* in PDF graphics pixels, points, inches, centimeters, etc.
*
* @private
*/
var _PdfUnitConvertor = /** @class */ (function () {
/**
* Initializes a new unit converter using the default horizontal resolution.
*
* @private
*/
function _PdfUnitConvertor() {
/**
* Horizontal pixel resolution used for unit conversions.
*
* @private
*/
this._horizontalResolution = 96;
this._proportions = this._updateProportions(this._horizontalResolution);
}
/**
* Computes and returns the proportional conversion values based on the
* specified pixel resolution.
*
* @param {number} pixel The horizontal pixel resolution.
* @returns {number[]} The array of proportional constants.
*
* @private
*/
_PdfUnitConvertor.prototype._updateProportions = function (pixel) {
return [pixel / 2.54, pixel / 6.0, 1, pixel / 72.0, pixel, pixel / 300.0, pixel / 25.4];
};
/**
* Converts a value from one unit type to another.
*
* @param {number} value The value to convert.
* @param {_PdfGraphicsUnit} from The source unit.
* @param {_PdfGraphicsUnit} to The destination unit.
* @returns {number} The converted value.
*
* @private
*/
_PdfUnitConvertor.prototype._convertUnits = function (value, from, to) {
return this._convertFromPixels(this._convertToPixels(value, from), to);
};
/**
* Converts a pixel value into the specified target unit.
*
* @param {number} value Pixel value.
* @param {_PdfGraphicsUnit} to Target unit.
* @returns {number} Converted value.
*
* @private
*/
_PdfUnitConvertor.prototype._convertFromPixels = function (value, to) {
var index = to;
return (value / this._proportions[index]);
};
/**
* Converts a value from the specified unit into pixels.
*
* @param {number} value The value to convert.
* @param {_PdfGraphicsUnit} from The source unit.
* @returns {number} Converted pixel value.
*
* @private
*/
_PdfUnitConvertor.prototype._convertToPixels = function (value, from) {
var index = from;
return (value * this._proportions[index]);
};
return _PdfUnitConvertor;
}());
export { _PdfUnitConvertor };