"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const colors_1 = require("./colors");
const operations_1 = require("./operations");
const operators_1 = require("./operators");
const PDFDocument_1 = tslib_1.__importDefault(require("./PDFDocument"));
const PDFEmbeddedPage_1 = tslib_1.__importDefault(require("./PDFEmbeddedPage"));
const PDFFont_1 = tslib_1.__importDefault(require("./PDFFont"));
const PDFImage_1 = tslib_1.__importDefault(require("./PDFImage"));
const PDFPageOptions_1 = require("./PDFPageOptions");
const rotations_1 = require("./rotations");
const StandardFonts_1 = require("./StandardFonts");
const core_1 = require("../core");
const utils_1 = require("../utils");
const svg_1 = require("./svg");
/**
 * Represents a single page of a [[PDFDocument]].
 */
class PDFPage {
    constructor(leafNode, ref, doc) {
        this.fontSize = 24;
        this.fontColor = (0, colors_1.rgb)(0, 0, 0);
        this.lineHeight = 24;
        this.x = 0;
        this.y = 0;
        (0, utils_1.assertIs)(leafNode, 'leafNode', [[core_1.PDFPageLeaf, 'PDFPageLeaf']]);
        (0, utils_1.assertIs)(ref, 'ref', [[core_1.PDFRef, 'PDFRef']]);
        (0, utils_1.assertIs)(doc, 'doc', [[PDFDocument_1.default, 'PDFDocument']]);
        this.node = leafNode;
        this.ref = ref;
        this.doc = doc;
    }
    /**
     * Rotate this page by a multiple of 90 degrees. For example:
     * ```js
     * import { degrees } from 'pdf-lib'
     *
     * page.setRotation(degrees(-90))
     * page.setRotation(degrees(0))
     * page.setRotation(degrees(90))
     * page.setRotation(degrees(180))
     * page.setRotation(degrees(270))
     * ```
     * @param angle The angle to rotate this page.
     */
    setRotation(angle) {
        const degreesAngle = (0, rotations_1.toDegrees)(angle);
        (0, utils_1.assertMultiple)(degreesAngle, 'degreesAngle', 90);
        this.node.set(core_1.PDFName.of('Rotate'), this.doc.context.obj(degreesAngle));
    }
    /**
     * Get this page's rotation angle in degrees. For example:
     * ```js
     * const rotationAngle = page.getRotation().angle;
     * ```
     * @returns The rotation angle of the page in degrees (always a multiple of
     *          90 degrees).
     */
    getRotation() {
        const Rotate = this.node.Rotate();
        return (0, rotations_1.degrees)(Rotate ? Rotate.asNumber() : 0);
    }
    /**
     * Resize this page by increasing or decreasing its width and height. For
     * example:
     * ```js
     * page.setSize(250, 500)
     * page.setSize(page.getWidth() + 50, page.getHeight() + 100)
     * page.setSize(page.getWidth() - 50, page.getHeight() - 100)
     * ```
     *
     * Note that the PDF specification does not allow for pages to have explicit
     * widths and heights. Instead it defines the "size" of a page in terms of
     * five rectangles: the MediaBox, CropBox, BleedBox, TrimBox, and ArtBox. As a
     * result, this method cannot directly change the width and height of a page.
     * Instead, it works by adjusting these five boxes.
     *
     * This method performs the following steps:
     *   1. Set width & height of MediaBox.
     *   2. Set width & height of CropBox, if it has same dimensions as MediaBox.
     *   3. Set width & height of BleedBox, if it has same dimensions as MediaBox.
     *   4. Set width & height of TrimBox, if it has same dimensions as MediaBox.
     *   5. Set width & height of ArtBox, if it has same dimensions as MediaBox.
     *
     * This approach works well for most PDF documents as all PDF pages must
     * have a MediaBox, but relatively few have a CropBox, BleedBox, TrimBox, or
     * ArtBox. And when they do have these additional boxes, they often have the
     * same dimensions as the MediaBox. However, if you find this method does not
     * work for your document, consider setting the boxes directly:
     *   * [[PDFPage.setMediaBox]]
     *   * [[PDFPage.setCropBox]]
     *   * [[PDFPage.setBleedBox]]
     *   * [[PDFPage.setTrimBox]]
     *   * [[PDFPage.setArtBox]]
     *
     * @param width The new width of the page.
     * @param height The new height of the page.
     */
    setSize(width, height) {
        (0, utils_1.assertIs)(width, 'width', ['number']);
        (0, utils_1.assertIs)(height, 'height', ['number']);
        const mediaBox = this.getMediaBox();
        this.setMediaBox(mediaBox.x, mediaBox.y, width, height);
        const cropBox = this.getCropBox();
        const bleedBox = this.getBleedBox();
        const trimBox = this.getTrimBox();
        const artBox = this.getArtBox();
        const hasCropBox = this.node.CropBox();
        const hasBleedBox = this.node.BleedBox();
        const hasTrimBox = this.node.TrimBox();
        const hasArtBox = this.node.ArtBox();
        if (hasCropBox && (0, utils_1.rectanglesAreEqual)(cropBox, mediaBox)) {
            this.setCropBox(mediaBox.x, mediaBox.y, width, height);
        }
        if (hasBleedBox && (0, utils_1.rectanglesAreEqual)(bleedBox, mediaBox)) {
            this.setBleedBox(mediaBox.x, mediaBox.y, width, height);
        }
        if (hasTrimBox && (0, utils_1.rectanglesAreEqual)(trimBox, mediaBox)) {
            this.setTrimBox(mediaBox.x, mediaBox.y, width, height);
        }
        if (hasArtBox && (0, utils_1.rectanglesAreEqual)(artBox, mediaBox)) {
            this.setArtBox(mediaBox.x, mediaBox.y, width, height);
        }
    }
    /**
     * Resize this page by increasing or decreasing its width. For example:
     * ```js
     * page.setWidth(250)
     * page.setWidth(page.getWidth() + 50)
     * page.setWidth(page.getWidth() - 50)
     * ```
     *
     * This method uses [[PDFPage.setSize]] to set the page's width.
     *
     * @param width The new width of the page.
     */
    setWidth(width) {
        (0, utils_1.assertIs)(width, 'width', ['number']);
        this.setSize(width, this.getSize().height);
    }
    /**
     * Resize this page by increasing or decreasing its height. For example:
     * ```js
     * page.setHeight(500)
     * page.setHeight(page.getWidth() + 100)
     * page.setHeight(page.getWidth() - 100)
     * ```
     *
     * This method uses [[PDFPage.setSize]] to set the page's height.
     *
     * @param height The new height of the page.
     */
    setHeight(height) {
        (0, utils_1.assertIs)(height, 'height', ['number']);
        this.setSize(this.getSize().width, height);
    }
    /**
     * Set the MediaBox of this page. For example:
     * ```js
     * const mediaBox = page.getMediaBox()
     *
     * page.setMediaBox(0, 0, 250, 500)
     * page.setMediaBox(mediaBox.x, mediaBox.y, 50, 100)
     * page.setMediaBox(15, 5, mediaBox.width - 50, mediaBox.height - 100)
     * ```
     *
     * See [[PDFPage.getMediaBox]] for details about what the MediaBox represents.
     *
     * @param x The x coordinate of the lower left corner of the new MediaBox.
     * @param y The y coordinate of the lower left corner of the new MediaBox.
     * @param width The width of the new MediaBox.
     * @param height The height of the new MediaBox.
     */
    setMediaBox(x, y, width, height) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        (0, utils_1.assertIs)(width, 'width', ['number']);
        (0, utils_1.assertIs)(height, 'height', ['number']);
        const mediaBox = this.doc.context.obj([x, y, x + width, y + height]);
        this.node.set(core_1.PDFName.MediaBox, mediaBox);
    }
    /**
     * Set the CropBox of this page. For example:
     * ```js
     * const cropBox = page.getCropBox()
     *
     * page.setCropBox(0, 0, 250, 500)
     * page.setCropBox(cropBox.x, cropBox.y, 50, 100)
     * page.setCropBox(15, 5, cropBox.width - 50, cropBox.height - 100)
     * ```
     *
     * See [[PDFPage.getCropBox]] for details about what the CropBox represents.
     *
     * @param x The x coordinate of the lower left corner of the new CropBox.
     * @param y The y coordinate of the lower left corner of the new CropBox.
     * @param width The width of the new CropBox.
     * @param height The height of the new CropBox.
     */
    setCropBox(x, y, width, height) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        (0, utils_1.assertIs)(width, 'width', ['number']);
        (0, utils_1.assertIs)(height, 'height', ['number']);
        const cropBox = this.doc.context.obj([x, y, x + width, y + height]);
        this.node.set(core_1.PDFName.CropBox, cropBox);
    }
    /**
     * Set the BleedBox of this page. For example:
     * ```js
     * const bleedBox = page.getBleedBox()
     *
     * page.setBleedBox(0, 0, 250, 500)
     * page.setBleedBox(bleedBox.x, bleedBox.y, 50, 100)
     * page.setBleedBox(15, 5, bleedBox.width - 50, bleedBox.height - 100)
     * ```
     *
     * See [[PDFPage.getBleedBox]] for details about what the BleedBox represents.
     *
     * @param x The x coordinate of the lower left corner of the new BleedBox.
     * @param y The y coordinate of the lower left corner of the new BleedBox.
     * @param width The width of the new BleedBox.
     * @param height The height of the new BleedBox.
     */
    setBleedBox(x, y, width, height) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        (0, utils_1.assertIs)(width, 'width', ['number']);
        (0, utils_1.assertIs)(height, 'height', ['number']);
        const bleedBox = this.doc.context.obj([x, y, x + width, y + height]);
        this.node.set(core_1.PDFName.BleedBox, bleedBox);
    }
    /**
     * Set the TrimBox of this page. For example:
     * ```js
     * const trimBox = page.getTrimBox()
     *
     * page.setTrimBox(0, 0, 250, 500)
     * page.setTrimBox(trimBox.x, trimBox.y, 50, 100)
     * page.setTrimBox(15, 5, trimBox.width - 50, trimBox.height - 100)
     * ```
     *
     * See [[PDFPage.getTrimBox]] for details about what the TrimBox represents.
     *
     * @param x The x coordinate of the lower left corner of the new TrimBox.
     * @param y The y coordinate of the lower left corner of the new TrimBox.
     * @param width The width of the new TrimBox.
     * @param height The height of the new TrimBox.
     */
    setTrimBox(x, y, width, height) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        (0, utils_1.assertIs)(width, 'width', ['number']);
        (0, utils_1.assertIs)(height, 'height', ['number']);
        const trimBox = this.doc.context.obj([x, y, x + width, y + height]);
        this.node.set(core_1.PDFName.TrimBox, trimBox);
    }
    /**
     * Set the ArtBox of this page. For example:
     * ```js
     * const artBox = page.getArtBox()
     *
     * page.setArtBox(0, 0, 250, 500)
     * page.setArtBox(artBox.x, artBox.y, 50, 100)
     * page.setArtBox(15, 5, artBox.width - 50, artBox.height - 100)
     * ```
     *
     * See [[PDFPage.getArtBox]] for details about what the ArtBox represents.
     *
     * @param x The x coordinate of the lower left corner of the new ArtBox.
     * @param y The y coordinate of the lower left corner of the new ArtBox.
     * @param width The width of the new ArtBox.
     * @param height The height of the new ArtBox.
     */
    setArtBox(x, y, width, height) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        (0, utils_1.assertIs)(width, 'width', ['number']);
        (0, utils_1.assertIs)(height, 'height', ['number']);
        const artBox = this.doc.context.obj([x, y, x + width, y + height]);
        this.node.set(core_1.PDFName.ArtBox, artBox);
    }
    /**
     * Get this page's width and height. For example:
     * ```js
     * const { width, height } = page.getSize()
     * ```
     *
     * This method uses [[PDFPage.getMediaBox]] to obtain the page's
     * width and height.
     *
     * @returns The width and height of the page.
     */
    getSize() {
        const { width, height } = this.getMediaBox();
        return { width, height };
    }
    /**
     * Get this page's width. For example:
     * ```js
     * const width = page.getWidth()
     * ```
     *
     * This method uses [[PDFPage.getSize]] to obtain the page's size.
     *
     * @returns The width of the page.
     */
    getWidth() {
        return this.getSize().width;
    }
    /**
     * Get this page's height. For example:
     * ```js
     * const height = page.getHeight()
     * ```
     *
     * This method uses [[PDFPage.getSize]] to obtain the page's size.
     *
     * @returns The height of the page.
     */
    getHeight() {
        return this.getSize().height;
    }
    /**
     * Get the rectangle defining this page's MediaBox. For example:
     * ```js
     * const { x, y, width, height } = page.getMediaBox()
     * ```
     *
     * The MediaBox of a page defines the boundaries of the physical medium on
     * which the page is to be displayed/printed. It may include extended area
     * surrounding the page content for bleed marks, printing marks, etc...
     * It may also include areas close to the edges of the medium that cannot be
     * marked because of physical limitations of the output device. Content
     * falling outside this boundary may safely be discarded without affecting
     * the meaning of the PDF file.
     *
     * @returns An object defining the lower left corner of the MediaBox and its
     *          width & height.
     */
    getMediaBox() {
        const mediaBox = this.node.MediaBox();
        return mediaBox.asRectangle();
    }
    /**
     * Get the rectangle defining this page's CropBox. For example:
     * ```js
     * const { x, y, width, height } = page.getCropBox()
     * ```
     *
     * The CropBox of a page defines the region to which the contents of the page
     * shall be clipped when displayed or printed. Unlike the other boxes, the
     * CropBox does not necessarily represent the physical page geometry. It
     * merely imposes clipping on the page contents.
     *
     * The CropBox's default value is the page's MediaBox.
     *
     * @returns An object defining the lower left corner of the CropBox and its
     *          width & height.
     */
    getCropBox() {
        var _a;
        const cropBox = this.node.CropBox();
        return (_a = cropBox === null || cropBox === void 0 ? void 0 : cropBox.asRectangle()) !== null && _a !== void 0 ? _a : this.getMediaBox();
    }
    /**
     * Get the rectangle defining this page's BleedBox. For example:
     * ```js
     * const { x, y, width, height } = page.getBleedBox()
     * ```
     *
     * The BleedBox of a page defines the region to which the contents of the
     * page shall be clipped when output in a production environment. This may
     * include any extra bleed area needed to accommodate the physical
     * limitations of cutting, folding, and trimming equipment. The actual
     * printed page may include printing marks that fall outside the BleedBox.
     *
     * The BleedBox's default value is the page's CropBox.
     *
     * @returns An object defining the lower left corner of the BleedBox and its
     *          width & height.
     */
    getBleedBox() {
        var _a;
        const bleedBox = this.node.BleedBox();
        return (_a = bleedBox === null || bleedBox === void 0 ? void 0 : bleedBox.asRectangle()) !== null && _a !== void 0 ? _a : this.getCropBox();
    }
    /**
     * Get the rectangle defining this page's TrimBox. For example:
     * ```js
     * const { x, y, width, height } = page.getTrimBox()
     * ```
     *
     * The TrimBox of a page defines the intended dimensions of the finished
     * page after trimming. It may be smaller than the MediaBox to allow for
     * production-related content, such as printing instructions, cut marks, or
     * color bars.
     *
     * The TrimBox's default value is the page's CropBox.
     *
     * @returns An object defining the lower left corner of the TrimBox and its
     *          width & height.
     */
    getTrimBox() {
        var _a;
        const trimBox = this.node.TrimBox();
        return (_a = trimBox === null || trimBox === void 0 ? void 0 : trimBox.asRectangle()) !== null && _a !== void 0 ? _a : this.getCropBox();
    }
    /**
     * Get the rectangle defining this page's ArtBox. For example:
     * ```js
     * const { x, y, width, height } = page.getArtBox()
     * ```
     *
     * The ArtBox of a page defines the extent of the page's meaningful content
     * (including potential white space).
     *
     * The ArtBox's default value is the page's CropBox.
     *
     * @returns An object defining the lower left corner of the ArtBox and its
     *          width & height.
     */
    getArtBox() {
        var _a;
        const artBox = this.node.ArtBox();
        return (_a = artBox === null || artBox === void 0 ? void 0 : artBox.asRectangle()) !== null && _a !== void 0 ? _a : this.getCropBox();
    }
    /**
     * Translate this page's content to a new location on the page. This operation
     * is often useful after resizing the page with [[setSize]]. For example:
     * ```js
     * // Add 50 units of whitespace to the top and right of the page
     * page.setSize(page.getWidth() + 50, page.getHeight() + 50)
     *
     * // Move the page's content from the lower-left corner of the page
     * // to the top-right corner.
     * page.translateContent(50, 50)
     *
     * // Now there are 50 units of whitespace to the left and bottom of the page
     * ```
     * See also: [[resetPosition]]
     * @param x The new position on the x-axis for this page's content.
     * @param y The new position on the y-axis for this page's content.
     */
    translateContent(x, y) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        this.node.normalize();
        this.getContentStream();
        const start = this.createContentStream((0, operators_1.pushGraphicsState)(), (0, operators_1.translate)(x, y));
        const startRef = this.doc.context.register(start);
        const end = this.createContentStream((0, operators_1.popGraphicsState)());
        const endRef = this.doc.context.register(end);
        this.node.wrapContentStreams(startRef, endRef);
    }
    /**
     * Translate this page's content and annotations. In some scenarios, the position of annotations
     * matter (e.g. form filled with FreeText annotations). If the [[translateContent]] function is used in these cases,
     * the position of the annotations relative to the text will change. To take this into account, you can use [translate] function.
     * For example:
     * ```js
     * // Move the page's content and annotations from the lower-left corner of the page
     * // to the top-right corner.
     * page.translate(50, 50)
     * ```
     * @param x The new position on the x-axis for this page's content.
     * @param y The new position on the y-axis for this page's content.
     */
    translate(x, y) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        this.translateContent(x, y);
        const annots = this.node.Annots();
        if (!annots)
            return;
        for (let idx = 0; idx < annots.size(); idx++) {
            const annot = annots.lookup(idx);
            if (annot instanceof core_1.PDFDict)
                this.translateAnnot(annot, x, y);
        }
    }
    /**
     * Scale the size, content, and annotations of a page.
     *
     * For example:
     * ```js
     * page.scale(0.5, 0.5);
     * ```
     *
     * @param x The factor by which the width for the page should be scaled
     *          (e.g. `0.5` is 50%).
     * @param y The factor by which the height for the page should be scaled
     *          (e.g. `2.0` is 200%).
     */
    scale(x, y) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        this.setSize(this.getWidth() * x, this.getHeight() * y);
        this.scaleContent(x, y);
        this.scaleAnnotations(x, y);
    }
    /**
     * Scale the content of a page. This is useful after resizing an existing
     * page. This scales only the content, not the annotations.
     *
     * For example:
     * ```js
     * // Bisect the size of the page
     * page.setSize(page.getWidth() / 2, page.getHeight() / 2);
     *
     * // Scale the content of the page down by 50% in x and y
     * page.scaleContent(0.5, 0.5);
     * ```
     * See also: [[scaleAnnotations]]
     * @param x The factor by which the x-axis for the content should be scaled
     *          (e.g. `0.5` is 50%).
     * @param y The factor by which the y-axis for the content should be scaled
     *          (e.g. `2.0` is 200%).
     */
    scaleContent(x, y) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        this.node.normalize();
        this.getContentStream();
        const start = this.createContentStream((0, operators_1.pushGraphicsState)(), (0, operators_1.scale)(x, y));
        const startRef = this.doc.context.register(start);
        const end = this.createContentStream((0, operators_1.popGraphicsState)());
        const endRef = this.doc.context.register(end);
        this.node.wrapContentStreams(startRef, endRef);
    }
    /**
     * Scale the annotations of a page. This is useful if you want to scale a
     * page with comments or other annotations.
     * ```js
     * // Scale the content of the page down by 50% in x and y
     * page.scaleContent(0.5, 0.5);
     *
     * // Scale the content of the page down by 50% in x and y
     * page.scaleAnnotations(0.5, 0.5);
     * ```
     * See also: [[scaleContent]]
     * @param x The factor by which the x-axis for the annotations should be
     *          scaled (e.g. `0.5` is 50%).
     * @param y The factor by which the y-axis for the annotations should be
     *          scaled (e.g. `2.0` is 200%).
     */
    scaleAnnotations(x, y) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        const annots = this.node.Annots();
        if (!annots)
            return;
        for (let idx = 0; idx < annots.size(); idx++) {
            const annot = annots.lookup(idx);
            if (annot instanceof core_1.PDFDict)
                this.scaleAnnot(annot, x, y);
        }
    }
    /**
     * Reset the x and y coordinates of this page to `(0, 0)`. This operation is
     * often useful after calling [[translateContent]]. For example:
     * ```js
     * // Shift the page's contents up and to the right by 50 units
     * page.translateContent(50, 50)
     *
     * // This text will shifted - it will be drawn at (50, 50)
     * page.drawText('I am shifted')
     *
     * // Move back to (0, 0)
     * page.resetPosition()
     *
     * // This text will not be shifted - it will be drawn at (0, 0)
     * page.drawText('I am not shifted')
     * ```
     */
    resetPosition() {
        this.getContentStream(false);
        this.x = 0;
        this.y = 0;
    }
    /**
     * Choose a default font for this page. The default font will be used whenever
     * text is drawn on this page and no font is specified. For example:
     * ```js
     * import { StandardFonts } from 'pdf-lib'
     *
     * const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman)
     * const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica)
     * const courierFont = await pdfDoc.embedFont(StandardFonts.Courier)
     *
     * const page = pdfDoc.addPage()
     *
     * page.setFont(helveticaFont)
     * page.drawText('I will be drawn in Helvetica')
     *
     * page.setFont(timesRomanFont)
     * page.drawText('I will be drawn in Courier', { font: courierFont })
     * ```
     * @param font The default font to be used when drawing text on this page.
     */
    setFont(font) {
        // TODO: Reuse image Font name if we've already added this image to Resources.Fonts
        (0, utils_1.assertIs)(font, 'font', [[PDFFont_1.default, 'PDFFont']]);
        this.font = font;
        this.fontKey = this.node.newFontDictionary(this.font.name, this.font.ref);
    }
    /**
     * Choose a default font size for this page. The default font size will be
     * used whenever text is drawn on this page and no font size is specified.
     * For example:
     * ```js
     * page.setFontSize(12)
     * page.drawText('I will be drawn in size 12')
     *
     * page.setFontSize(36)
     * page.drawText('I will be drawn in size 24', { fontSize: 24 })
     * ```
     * @param fontSize The default font size to be used when drawing text on this
     *                 page.
     */
    setFontSize(fontSize) {
        (0, utils_1.assertIs)(fontSize, 'fontSize', ['number']);
        this.fontSize = fontSize;
    }
    /**
     * Choose a default font color for this page. The default font color will be
     * used whenever text is drawn on this page and no font color is specified.
     * For example:
     * ```js
     * import { rgb, cmyk, grayscale } from 'pdf-lib'
     *
     * page.setFontColor(rgb(0.97, 0.02, 0.97))
     * page.drawText('I will be drawn in pink')
     *
     * page.setFontColor(cmyk(0.4, 0.7, 0.39, 0.15))
     * page.drawText('I will be drawn in gray', { color: grayscale(0.5) })
     * ```
     * @param fontColor The default font color to be used when drawing text on
     *                  this page.
     */
    setFontColor(fontColor) {
        (0, utils_1.assertIs)(fontColor, 'fontColor', [[Object, 'Color']]);
        this.fontColor = fontColor;
    }
    /**
     * Choose a default line height for this page. The default line height will be
     * used whenever text is drawn on this page and no line height is specified.
     * For example:
     * ```js
     * page.setLineHeight(12);
     * page.drawText('These lines will be vertically \n separated by 12 units')
     *
     * page.setLineHeight(36);
     * page.drawText('These lines will be vertically \n separated by 24 units', {
     *   lineHeight: 24
     * })
     * ```
     * @param lineHeight The default line height to be used when drawing text on
     *                   this page.
     */
    setLineHeight(lineHeight) {
        (0, utils_1.assertIs)(lineHeight, 'lineHeight', ['number']);
        this.lineHeight = lineHeight;
    }
    /**
     * Get the default position of this page. For example:
     * ```js
     * const { x, y } = page.getPosition()
     * ```
     * @returns The default position of the page.
     */
    getPosition() {
        return { x: this.x, y: this.y };
    }
    /**
     * Get the default x coordinate of this page. For example:
     * ```js
     * const x = page.getX()
     * ```
     * @returns The default x coordinate of the page.
     */
    getX() {
        return this.x;
    }
    /**
     * Get the default y coordinate of this page. For example:
     * ```js
     * const y = page.getY()
     * ```
     * @returns The default y coordinate of the page.
     */
    getY() {
        return this.y;
    }
    /**
     * Change the default position of this page. For example:
     * ```js
     * page.moveTo(0, 0)
     * page.drawText('I will be drawn at the origin')
     *
     * page.moveTo(0, 25)
     * page.drawText('I will be drawn 25 units up')
     *
     * page.moveTo(25, 25)
     * page.drawText('I will be drawn 25 units up and 25 units to the right')
     * ```
     * @param x The new default position on the x-axis for this page.
     * @param y The new default position on the y-axis for this page.
     */
    moveTo(x, y) {
        (0, utils_1.assertIs)(x, 'x', ['number']);
        (0, utils_1.assertIs)(y, 'y', ['number']);
        this.x = x;
        this.y = y;
    }
    /**
     * Change the default position of this page to be further down the y-axis.
     * For example:
     * ```js
     * page.moveTo(50, 50)
     * page.drawText('I will be drawn at (50, 50)')
     *
     * page.moveDown(10)
     * page.drawText('I will be drawn at (50, 40)')
     * ```
     * @param yDecrease The amount by which the page's default position along the
     *                  y-axis should be decreased.
     */
    moveDown(yDecrease) {
        (0, utils_1.assertIs)(yDecrease, 'yDecrease', ['number']);
        this.y -= yDecrease;
    }
    /**
     * Change the default position of this page to be further up the y-axis.
     * For example:
     * ```js
     * page.moveTo(50, 50)
     * page.drawText('I will be drawn at (50, 50)')
     *
     * page.moveUp(10)
     * page.drawText('I will be drawn at (50, 60)')
     * ```
     * @param yIncrease The amount by which the page's default position along the
     *                  y-axis should be increased.
     */
    moveUp(yIncrease) {
        (0, utils_1.assertIs)(yIncrease, 'yIncrease', ['number']);
        this.y += yIncrease;
    }
    /**
     * Change the default position of this page to be further left on the x-axis.
     * For example:
     * ```js
     * page.moveTo(50, 50)
     * page.drawText('I will be drawn at (50, 50)')
     *
     * page.moveLeft(10)
     * page.drawText('I will be drawn at (40, 50)')
     * ```
     * @param xDecrease The amount by which the page's default position along the
     *                  x-axis should be decreased.
     */
    moveLeft(xDecrease) {
        (0, utils_1.assertIs)(xDecrease, 'xDecrease', ['number']);
        this.x -= xDecrease;
    }
    /**
     * Change the default position of this page to be further right on the y-axis.
     * For example:
     * ```js
     * page.moveTo(50, 50)
     * page.drawText('I will be drawn at (50, 50)')
     *
     * page.moveRight(10)
     * page.drawText('I will be drawn at (60, 50)')
     * ```
     * @param xIncrease The amount by which the page's default position along the
     *                  x-axis should be increased.
     */
    moveRight(xIncrease) {
        (0, utils_1.assertIs)(xIncrease, 'xIncrease', ['number']);
        this.x += xIncrease;
    }
    /**
     * Push one or more operators to the end of this page's current content
     * stream. For example:
     * ```js
     * import {
     *   pushGraphicsState,
     *   moveTo,
     *   lineTo,
     *   closePath,
     *   setFillingColor,
     *   rgb,
     *   fill,
     *   popGraphicsState,
     * } from 'pdf-lib'
     *
     * // Draw a green triangle in the lower-left corner of the page
     * page.pushOperators(
     *   pushGraphicsState(),
     *   moveTo(0, 0),
     *   lineTo(100, 0),
     *   lineTo(50, 100),
     *   closePath(),
     *   setFillingColor(rgb(0.0, 1.0, 0.0)),
     *   fill(),
     *   popGraphicsState(),
     * )
     * ```
     * @param operator The operators to be pushed.
     */
    pushOperators(...operator) {
        (0, utils_1.assertEachIs)(operator, 'operator', [[core_1.PDFOperator, 'PDFOperator']]);
        const contentStream = this.getContentStream();
        contentStream.push(...operator);
    }
    /**
     * Draw one or more lines of text on this page. For example:
     * ```js
     * import { StandardFonts, rgb } from 'pdf-lib'
     *
     * const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica)
     * const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman)
     *
     * const page = pdfDoc.addPage()
     *
     * page.setFont(helveticaFont)
     *
     * page.moveTo(5, 200)
     * page.drawText('The Life of an Egg', { size: 36 })
     *
     * page.moveDown(36)
     * page.drawText('An Epic Tale of Woe', { size: 30 })
     *
     * page.drawText(
     *   `Humpty Dumpty sat on a wall \n` +
     *   `Humpty Dumpty had a great fall; \n` +
     *   `All the king's horses and all the king's men \n` +
     *   `Couldn't put Humpty together again. \n`,
     *   {
     *     x: 25,
     *     y: 100,
     *     font: timesRomanFont,
     *     size: 24,
     *     color: rgb(1, 0, 0),
     *     lineHeight: 24,
     *     opacity: 0.75,
     *   },
     * )
     * ```
     * @param text The text to be drawn.
     * @param options The options to be used when drawing the text.
     */
    drawText(text, options = {}) {
        var _a, _b, _c, _d, _e, _f, _g;
        (0, utils_1.assertIs)(text, 'text', ['string']);
        (0, utils_1.assertOrUndefined)(options.color, 'options.color', [[Object, 'Color']]);
        (0, utils_1.assertRangeOrUndefined)(options.opacity, 'opacity.opacity', 0, 1);
        (0, utils_1.assertOrUndefined)(options.font, 'options.font', [[PDFFont_1.default, 'PDFFont']]);
        (0, utils_1.assertOrUndefined)(options.size, 'options.size', ['number']);
        (0, utils_1.assertOrUndefined)(options.rotate, 'options.rotate', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.xSkew, 'options.xSkew', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.ySkew, 'options.ySkew', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.x, 'options.x', ['number']);
        (0, utils_1.assertOrUndefined)(options.y, 'options.y', ['number']);
        (0, utils_1.assertOrUndefined)(options.lineHeight, 'options.lineHeight', ['number']);
        (0, utils_1.assertOrUndefined)(options.maxWidth, 'options.maxWidth', ['number']);
        (0, utils_1.assertOrUndefined)(options.wordBreaks, 'options.wordBreaks', [Array]);
        (0, utils_1.assertIsOneOfOrUndefined)(options.blendMode, 'options.blendMode', PDFPageOptions_1.BlendMode);
        const { oldFont, newFont, newFontKey } = this.setOrEmbedFont(options.font);
        const fontSize = options.size || this.fontSize;
        const wordBreaks = options.wordBreaks || this.doc.defaultWordBreaks;
        const textWidth = (t) => newFont.widthOfTextAtSize(t, fontSize);
        const lines = options.maxWidth === undefined
            ? (0, utils_1.lineSplit)((0, utils_1.cleanText)(text))
            : (0, utils_1.breakTextIntoLines)(text, wordBreaks, options.maxWidth, textWidth);
        const encodedLines = new Array(lines.length);
        for (let idx = 0, len = lines.length; idx < len; idx++) {
            encodedLines[idx] = newFont.encodeText(lines[idx]);
        }
        const graphicsStateKey = this.maybeEmbedGraphicsState({
            opacity: options.opacity,
            blendMode: options.blendMode,
        });
        const contentStream = this.getContentStream();
        contentStream.push(...(0, operations_1.drawLinesOfText)(encodedLines, {
            color: (_a = options.color) !== null && _a !== void 0 ? _a : this.fontColor,
            font: newFontKey,
            size: fontSize,
            rotate: (_b = options.rotate) !== null && _b !== void 0 ? _b : (0, rotations_1.degrees)(0),
            xSkew: (_c = options.xSkew) !== null && _c !== void 0 ? _c : (0, rotations_1.degrees)(0),
            ySkew: (_d = options.ySkew) !== null && _d !== void 0 ? _d : (0, rotations_1.degrees)(0),
            x: (_e = options.x) !== null && _e !== void 0 ? _e : this.x,
            y: (_f = options.y) !== null && _f !== void 0 ? _f : this.y,
            lineHeight: (_g = options.lineHeight) !== null && _g !== void 0 ? _g : this.lineHeight,
            graphicsState: graphicsStateKey,
            matrix: options.matrix,
            clipSpaces: options.clipSpaces,
        }));
        if (options.font) {
            if (oldFont)
                this.setFont(oldFont);
            else
                this.resetFont();
        }
    }
    /**
     * Draw an image on this page. For example:
     * ```js
     * import { degrees } from 'pdf-lib'
     *
     * const jpgUrl = 'https://pdf-lib.js.org/assets/cat_riding_unicorn.jpg'
     * const jpgImageBytes = await fetch(jpgUrl).then((res) => res.arrayBuffer())
     *
     * const jpgImage = await pdfDoc.embedJpg(jpgImageBytes)
     * const jpgDims = jpgImage.scale(0.5)
     *
     * const page = pdfDoc.addPage()
     *
     * page.drawImage(jpgImage, {
     *   x: 25,
     *   y: 25,
     *   width: jpgDims.width,
     *   height: jpgDims.height,
     *   rotate: degrees(30),
     *   opacity: 0.75,
     * })
     * ```
     * @param image The image to be drawn.
     * @param options The options to be used when drawing the image.
     */
    drawImage(image, options = {}) {
        var _a, _b, _c, _d, _e, _f, _g;
        // TODO: Reuse image XObject name if we've already added this image to Resources.XObjects
        (0, utils_1.assertIs)(image, 'image', [[PDFImage_1.default, 'PDFImage']]);
        (0, utils_1.assertOrUndefined)(options.x, 'options.x', ['number']);
        (0, utils_1.assertOrUndefined)(options.y, 'options.y', ['number']);
        (0, utils_1.assertOrUndefined)(options.width, 'options.width', ['number']);
        (0, utils_1.assertOrUndefined)(options.height, 'options.height', ['number']);
        (0, utils_1.assertOrUndefined)(options.rotate, 'options.rotate', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.xSkew, 'options.xSkew', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.ySkew, 'options.ySkew', [[Object, 'Rotation']]);
        (0, utils_1.assertRangeOrUndefined)(options.opacity, 'opacity.opacity', 0, 1);
        (0, utils_1.assertIsOneOfOrUndefined)(options.blendMode, 'options.blendMode', PDFPageOptions_1.BlendMode);
        const xObjectKey = this.node.newXObject('Image', image.ref);
        const graphicsStateKey = this.maybeEmbedGraphicsState({
            opacity: options.opacity,
            blendMode: options.blendMode,
        });
        const contentStream = this.getContentStream();
        contentStream.push(...(0, operations_1.drawImage)(xObjectKey, {
            x: (_a = options.x) !== null && _a !== void 0 ? _a : this.x,
            y: (_b = options.y) !== null && _b !== void 0 ? _b : this.y,
            width: (_c = options.width) !== null && _c !== void 0 ? _c : image.size().width,
            height: (_d = options.height) !== null && _d !== void 0 ? _d : image.size().height,
            rotate: (_e = options.rotate) !== null && _e !== void 0 ? _e : (0, rotations_1.degrees)(0),
            xSkew: (_f = options.xSkew) !== null && _f !== void 0 ? _f : (0, rotations_1.degrees)(0),
            ySkew: (_g = options.ySkew) !== null && _g !== void 0 ? _g : (0, rotations_1.degrees)(0),
            graphicsState: graphicsStateKey,
            matrix: options.matrix,
            clipSpaces: options.clipSpaces,
        }));
    }
    /**
     * Draw an embedded PDF page on this page. For example:
     * ```js
     * import { degrees } from 'pdf-lib'
     *
     * const pdfDoc = await PDFDocument.create()
     * const page = pdfDoc.addPage()
     *
     * const sourcePdfUrl = 'https://pdf-lib.js.org/assets/with_large_page_count.pdf'
     * const sourcePdf = await fetch(sourcePdfUrl).then((res) => res.arrayBuffer())
     *
     * // Embed page 74 from the PDF
     * const [embeddedPage] = await pdfDoc.embedPdf(sourcePdf, 73)
     *
     * page.drawPage(embeddedPage, {
     *   x: 250,
     *   y: 200,
     *   xScale: 0.5,
     *   yScale: 0.5,
     *   rotate: degrees(30),
     *   opacity: 0.75,
     * })
     * ```
     *
     * The `options` argument accepts both `width`/`height` and `xScale`/`yScale`
     * as options. Since each of these options defines the size of the drawn page,
     * if both options are given, `width` and `height` take precedence and the
     * corresponding scale variants are ignored.
     *
     * @param embeddedPage The embedded page to be drawn.
     * @param options The options to be used when drawing the embedded page.
     */
    drawPage(embeddedPage, options = {}) {
        var _a, _b, _c, _d, _e;
        // TODO: Reuse embeddedPage XObject name if we've already added this embeddedPage to Resources.XObjects
        (0, utils_1.assertIs)(embeddedPage, 'embeddedPage', [
            [PDFEmbeddedPage_1.default, 'PDFEmbeddedPage'],
        ]);
        (0, utils_1.assertOrUndefined)(options.x, 'options.x', ['number']);
        (0, utils_1.assertOrUndefined)(options.y, 'options.y', ['number']);
        (0, utils_1.assertOrUndefined)(options.xScale, 'options.xScale', ['number']);
        (0, utils_1.assertOrUndefined)(options.yScale, 'options.yScale', ['number']);
        (0, utils_1.assertOrUndefined)(options.width, 'options.width', ['number']);
        (0, utils_1.assertOrUndefined)(options.height, 'options.height', ['number']);
        (0, utils_1.assertOrUndefined)(options.rotate, 'options.rotate', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.xSkew, 'options.xSkew', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.ySkew, 'options.ySkew', [[Object, 'Rotation']]);
        (0, utils_1.assertRangeOrUndefined)(options.opacity, 'opacity.opacity', 0, 1);
        (0, utils_1.assertIsOneOfOrUndefined)(options.blendMode, 'options.blendMode', PDFPageOptions_1.BlendMode);
        const xObjectKey = this.node.newXObject('EmbeddedPdfPage', embeddedPage.ref);
        const graphicsStateKey = this.maybeEmbedGraphicsState({
            opacity: options.opacity,
            blendMode: options.blendMode,
        });
        // prettier-ignore
        const xScale = (options.width !== undefined ? options.width / embeddedPage.width
            : options.xScale !== undefined ? options.xScale
                : 1);
        // prettier-ignore
        const yScale = (options.height !== undefined ? options.height / embeddedPage.height
            : options.yScale !== undefined ? options.yScale
                : 1);
        const contentStream = this.getContentStream();
        contentStream.push(...(0, operations_1.drawPage)(xObjectKey, {
            x: (_a = options.x) !== null && _a !== void 0 ? _a : this.x,
            y: (_b = options.y) !== null && _b !== void 0 ? _b : this.y,
            xScale,
            yScale,
            rotate: (_c = options.rotate) !== null && _c !== void 0 ? _c : (0, rotations_1.degrees)(0),
            xSkew: (_d = options.xSkew) !== null && _d !== void 0 ? _d : (0, rotations_1.degrees)(0),
            ySkew: (_e = options.ySkew) !== null && _e !== void 0 ? _e : (0, rotations_1.degrees)(0),
            graphicsState: graphicsStateKey,
        }));
    }
    /**
     * Draw an SVG path on this page. For example:
     * ```js
     * import { rgb } from 'pdf-lib'
     *
     * const svgPath = 'M 0,20 L 100,160 Q 130,200 150,120 C 190,-40 200,200 300,150 L 400,90'
     *
     * // Draw path as black line
     * page.drawSvgPath(svgPath, { x: 25, y: 75 })
     *
     * // Change border style and opacity
     * page.drawSvgPath(svgPath, {
     *   x: 25,
     *   y: 275,
     *   borderColor: rgb(0.5, 0.5, 0.5),
     *   borderWidth: 2,
     *   borderOpacity: 0.75,
     * })
     *
     * // Set fill color and opacity
     * page.drawSvgPath(svgPath, {
     *   x: 25,
     *   y: 475,
     *   color: rgb(1.0, 0, 0),
     *   opacity: 0.75,
     * })
     *
     * // Draw 50% of original size
     * page.drawSvgPath(svgPath, {
     *   x: 25,
     *   y: 675,
     *   scale: 0.5,
     * })
     * ```
     * @param path The SVG path to be drawn.
     * @param options The options to be used when drawing the SVG path.
     */
    drawSvgPath(path, options = {}) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j;
        (0, utils_1.assertIs)(path, 'path', ['string']);
        (0, utils_1.assertOrUndefined)(options.x, 'options.x', ['number']);
        (0, utils_1.assertOrUndefined)(options.y, 'options.y', ['number']);
        (0, utils_1.assertOrUndefined)(options.scale, 'options.scale', ['number']);
        (0, utils_1.assertOrUndefined)(options.rotate, 'options.rotate', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.borderWidth, 'options.borderWidth', ['number']);
        (0, utils_1.assertOrUndefined)(options.color, 'options.color', [[Object, 'Color']]);
        (0, utils_1.assertRangeOrUndefined)(options.opacity, 'opacity.opacity', 0, 1);
        (0, utils_1.assertOrUndefined)(options.borderColor, 'options.borderColor', [
            [Object, 'Color'],
        ]);
        (0, utils_1.assertOrUndefined)(options.borderDashArray, 'options.borderDashArray', [
            Array,
        ]);
        (0, utils_1.assertOrUndefined)(options.borderDashPhase, 'options.borderDashPhase', [
            'number',
        ]);
        (0, utils_1.assertIsOneOfOrUndefined)(options.borderLineCap, 'options.borderLineCap', operators_1.LineCapStyle);
        (0, utils_1.assertRangeOrUndefined)(options.borderOpacity, 'options.borderOpacity', 0, 1);
        (0, utils_1.assertIsOneOfOrUndefined)(options.blendMode, 'options.blendMode', PDFPageOptions_1.BlendMode);
        (0, utils_1.assertIsOneOfOrUndefined)(options.fillRule, 'options.fillRule', operators_1.FillRule);
        const graphicsStateKey = this.maybeEmbedGraphicsState({
            opacity: options.opacity,
            borderOpacity: options.borderOpacity,
            blendMode: options.blendMode,
        });
        if (!('color' in options) && !('borderColor' in options)) {
            options.borderColor = (0, colors_1.rgb)(0, 0, 0);
        }
        const contentStream = this.getContentStream();
        contentStream.push(...(0, operations_1.drawSvgPath)(path, {
            x: (_a = options.x) !== null && _a !== void 0 ? _a : this.x,
            y: (_b = options.y) !== null && _b !== void 0 ? _b : this.y,
            scale: options.scale,
            rotate: (_c = options.rotate) !== null && _c !== void 0 ? _c : (0, rotations_1.degrees)(0),
            color: (_d = options.color) !== null && _d !== void 0 ? _d : undefined,
            borderColor: (_e = options.borderColor) !== null && _e !== void 0 ? _e : undefined,
            borderWidth: (_f = options.borderWidth) !== null && _f !== void 0 ? _f : 0,
            borderDashArray: (_g = options.borderDashArray) !== null && _g !== void 0 ? _g : undefined,
            borderDashPhase: (_h = options.borderDashPhase) !== null && _h !== void 0 ? _h : undefined,
            borderLineCap: (_j = options.borderLineCap) !== null && _j !== void 0 ? _j : undefined,
            graphicsState: graphicsStateKey,
            fillRule: options.fillRule,
            matrix: options.matrix,
            clipSpaces: options.clipSpaces,
        }));
    }
    /**
     * Draw a line on this page. For example:
     * ```js
     * import { rgb } from 'pdf-lib'
     *
     * page.drawLine({
     *   start: { x: 25, y: 75 },
     *   end: { x: 125, y: 175 },
     *   thickness: 2,
     *   color: rgb(0.75, 0.2, 0.2),
     *   opacity: 0.75,
     * })
     * ```
     * @param options The options to be used when drawing the line.
     */
    drawLine(options) {
        var _a, _b, _c, _d, _e;
        (0, utils_1.assertIs)(options.start, 'options.start', [
            [Object, '{ x: number, y: number }'],
        ]);
        (0, utils_1.assertIs)(options.end, 'options.end', [
            [Object, '{ x: number, y: number }'],
        ]);
        (0, utils_1.assertIs)(options.start.x, 'options.start.x', ['number']);
        (0, utils_1.assertIs)(options.start.y, 'options.start.y', ['number']);
        (0, utils_1.assertIs)(options.end.x, 'options.end.x', ['number']);
        (0, utils_1.assertIs)(options.end.y, 'options.end.y', ['number']);
        (0, utils_1.assertOrUndefined)(options.thickness, 'options.thickness', ['number']);
        (0, utils_1.assertOrUndefined)(options.color, 'options.color', [[Object, 'Color']]);
        (0, utils_1.assertOrUndefined)(options.dashArray, 'options.dashArray', [Array]);
        (0, utils_1.assertOrUndefined)(options.dashPhase, 'options.dashPhase', ['number']);
        (0, utils_1.assertIsOneOfOrUndefined)(options.lineCap, 'options.lineCap', operators_1.LineCapStyle);
        (0, utils_1.assertRangeOrUndefined)(options.opacity, 'opacity.opacity', 0, 1);
        (0, utils_1.assertIsOneOfOrUndefined)(options.blendMode, 'options.blendMode', PDFPageOptions_1.BlendMode);
        const graphicsStateKey = this.maybeEmbedGraphicsState({
            borderOpacity: options.opacity,
            blendMode: options.blendMode,
        });
        if (!('color' in options)) {
            options.color = (0, colors_1.rgb)(0, 0, 0);
        }
        const contentStream = this.getContentStream();
        contentStream.push(...(0, operations_1.drawLine)({
            start: options.start,
            end: options.end,
            thickness: (_a = options.thickness) !== null && _a !== void 0 ? _a : 1,
            color: (_b = options.color) !== null && _b !== void 0 ? _b : undefined,
            dashArray: (_c = options.dashArray) !== null && _c !== void 0 ? _c : undefined,
            dashPhase: (_d = options.dashPhase) !== null && _d !== void 0 ? _d : undefined,
            lineCap: (_e = options.lineCap) !== null && _e !== void 0 ? _e : undefined,
            graphicsState: graphicsStateKey,
            matrix: options.matrix,
            clipSpaces: options.clipSpaces,
        }));
    }
    /**
     * Draw a rectangle on this page. For example:
     * ```js
     * import { degrees, grayscale, rgb } from 'pdf-lib'
     *
     * page.drawRectangle({
     *   x: 25,
     *   y: 75,
     *   width: 250,
     *   height: 75,
     *   rotate: degrees(-15),
     *   borderWidth: 5,
     *   borderColor: grayscale(0.5),
     *   color: rgb(0.75, 0.2, 0.2),
     *   opacity: 0.5,
     *   borderOpacity: 0.75,
     * })
     * ```
     * @param options The options to be used when drawing the rectangle.
     */
    drawRectangle(options = {}) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
        (0, utils_1.assertOrUndefined)(options.x, 'options.x', ['number']);
        (0, utils_1.assertOrUndefined)(options.y, 'options.y', ['number']);
        (0, utils_1.assertOrUndefined)(options.width, 'options.width', ['number']);
        (0, utils_1.assertOrUndefined)(options.height, 'options.height', ['number']);
        (0, utils_1.assertOrUndefined)(options.rotate, 'options.rotate', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.xSkew, 'options.xSkew', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.ySkew, 'options.ySkew', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.borderWidth, 'options.borderWidth', ['number']);
        (0, utils_1.assertOrUndefined)(options.color, 'options.color', [[Object, 'Color']]);
        (0, utils_1.assertRangeOrUndefined)(options.opacity, 'opacity.opacity', 0, 1);
        (0, utils_1.assertOrUndefined)(options.borderColor, 'options.borderColor', [
            [Object, 'Color'],
        ]);
        (0, utils_1.assertOrUndefined)(options.borderDashArray, 'options.borderDashArray', [
            Array,
        ]);
        (0, utils_1.assertOrUndefined)(options.borderDashPhase, 'options.borderDashPhase', [
            'number',
        ]);
        (0, utils_1.assertIsOneOfOrUndefined)(options.borderLineCap, 'options.borderLineCap', operators_1.LineCapStyle);
        (0, utils_1.assertRangeOrUndefined)(options.borderOpacity, 'options.borderOpacity', 0, 1);
        (0, utils_1.assertIsOneOfOrUndefined)(options.blendMode, 'options.blendMode', PDFPageOptions_1.BlendMode);
        const graphicsStateKey = this.maybeEmbedGraphicsState({
            opacity: options.opacity,
            borderOpacity: options.borderOpacity,
            blendMode: options.blendMode,
        });
        if (!('color' in options) && !('borderColor' in options)) {
            options.color = (0, colors_1.rgb)(0, 0, 0);
        }
        const contentStream = this.getContentStream();
        contentStream.push(...(0, operations_1.drawRectangle)({
            x: (_a = options.x) !== null && _a !== void 0 ? _a : this.x,
            y: (_b = options.y) !== null && _b !== void 0 ? _b : this.y,
            width: (_c = options.width) !== null && _c !== void 0 ? _c : 150,
            height: (_d = options.height) !== null && _d !== void 0 ? _d : 100,
            rotate: (_e = options.rotate) !== null && _e !== void 0 ? _e : (0, rotations_1.degrees)(0),
            xSkew: (_f = options.xSkew) !== null && _f !== void 0 ? _f : (0, rotations_1.degrees)(0),
            ySkew: (_g = options.ySkew) !== null && _g !== void 0 ? _g : (0, rotations_1.degrees)(0),
            borderWidth: (_h = options.borderWidth) !== null && _h !== void 0 ? _h : 0,
            color: (_j = options.color) !== null && _j !== void 0 ? _j : undefined,
            borderColor: (_k = options.borderColor) !== null && _k !== void 0 ? _k : undefined,
            borderDashArray: (_l = options.borderDashArray) !== null && _l !== void 0 ? _l : undefined,
            borderDashPhase: (_m = options.borderDashPhase) !== null && _m !== void 0 ? _m : undefined,
            graphicsState: graphicsStateKey,
            borderLineCap: (_o = options.borderLineCap) !== null && _o !== void 0 ? _o : undefined,
            matrix: options.matrix,
            clipSpaces: options.clipSpaces,
        }));
    }
    /**
     * Draw a square on this page. For example:
     * ```js
     * import { degrees, grayscale, rgb } from 'pdf-lib'
     *
     * page.drawSquare({
     *   x: 25,
     *   y: 75,
     *   size: 100,
     *   rotate: degrees(-15),
     *   borderWidth: 5,
     *   borderColor: grayscale(0.5),
     *   color: rgb(0.75, 0.2, 0.2),
     *   opacity: 0.5,
     *   borderOpacity: 0.75,
     * })
     * ```
     * @param options The options to be used when drawing the square.
     */
    drawSquare(options = {}) {
        const { size } = options;
        (0, utils_1.assertOrUndefined)(size, 'size', ['number']);
        this.drawRectangle({ ...options, width: size, height: size });
    }
    /**
     * Draw an ellipse on this page. For example:
     * ```js
     * import { grayscale, rgb } from 'pdf-lib'
     *
     * page.drawEllipse({
     *   x: 200,
     *   y: 75,
     *   xScale: 100,
     *   yScale: 50,
     *   borderWidth: 5,
     *   borderColor: grayscale(0.5),
     *   color: rgb(0.75, 0.2, 0.2),
     *   opacity: 0.5,
     *   borderOpacity: 0.75,
     * })
     * ```
     * @param options The options to be used when drawing the ellipse.
     */
    drawEllipse(options = {}) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
        (0, utils_1.assertOrUndefined)(options.x, 'options.x', ['number']);
        (0, utils_1.assertOrUndefined)(options.y, 'options.y', ['number']);
        (0, utils_1.assertOrUndefined)(options.xScale, 'options.xScale', ['number']);
        (0, utils_1.assertOrUndefined)(options.yScale, 'options.yScale', ['number']);
        (0, utils_1.assertOrUndefined)(options.rotate, 'options.rotate', [[Object, 'Rotation']]);
        (0, utils_1.assertOrUndefined)(options.color, 'options.color', [[Object, 'Color']]);
        (0, utils_1.assertRangeOrUndefined)(options.opacity, 'opacity.opacity', 0, 1);
        (0, utils_1.assertOrUndefined)(options.borderColor, 'options.borderColor', [
            [Object, 'Color'],
        ]);
        (0, utils_1.assertRangeOrUndefined)(options.borderOpacity, 'options.borderOpacity', 0, 1);
        (0, utils_1.assertOrUndefined)(options.borderWidth, 'options.borderWidth', ['number']);
        (0, utils_1.assertOrUndefined)(options.borderDashArray, 'options.borderDashArray', [
            Array,
        ]);
        (0, utils_1.assertOrUndefined)(options.borderDashPhase, 'options.borderDashPhase', [
            'number',
        ]);
        (0, utils_1.assertIsOneOfOrUndefined)(options.borderLineCap, 'options.borderLineCap', operators_1.LineCapStyle);
        (0, utils_1.assertIsOneOfOrUndefined)(options.blendMode, 'options.blendMode', PDFPageOptions_1.BlendMode);
        const graphicsStateKey = this.maybeEmbedGraphicsState({
            opacity: options.opacity,
            borderOpacity: options.borderOpacity,
            blendMode: options.blendMode,
        });
        if (!('color' in options) && !('borderColor' in options)) {
            options.color = (0, colors_1.rgb)(0, 0, 0);
        }
        const contentStream = this.getContentStream();
        contentStream.push(...(0, operations_1.drawEllipse)({
            x: (_a = options.x) !== null && _a !== void 0 ? _a : this.x,
            y: (_b = options.y) !== null && _b !== void 0 ? _b : this.y,
            xScale: (_c = options.xScale) !== null && _c !== void 0 ? _c : 100,
            yScale: (_d = options.yScale) !== null && _d !== void 0 ? _d : 100,
            rotate: (_e = options.rotate) !== null && _e !== void 0 ? _e : undefined,
            color: (_f = options.color) !== null && _f !== void 0 ? _f : undefined,
            borderColor: (_g = options.borderColor) !== null && _g !== void 0 ? _g : undefined,
            borderWidth: (_h = options.borderWidth) !== null && _h !== void 0 ? _h : 0,
            borderDashArray: (_j = options.borderDashArray) !== null && _j !== void 0 ? _j : undefined,
            borderDashPhase: (_k = options.borderDashPhase) !== null && _k !== void 0 ? _k : undefined,
            borderLineCap: (_l = options.borderLineCap) !== null && _l !== void 0 ? _l : undefined,
            graphicsState: graphicsStateKey,
            matrix: options.matrix,
            clipSpaces: options.clipSpaces,
        }));
    }
    /**
     * Draw a circle on this page. For example:
     * ```js
     * import { grayscale, rgb } from 'pdf-lib'
     *
     * page.drawCircle({
     *   x: 200,
     *   y: 150,
     *   size: 100,
     *   borderWidth: 5,
     *   borderColor: grayscale(0.5),
     *   color: rgb(0.75, 0.2, 0.2),
     *   opacity: 0.5,
     *   borderOpacity: 0.75,
     * })
     * ```
     * @param options The options to be used when drawing the ellipse.
     */
    drawCircle(options = {}) {
        const { size = 100 } = options;
        (0, utils_1.assertOrUndefined)(size, 'size', ['number']);
        this.drawEllipse({ ...options, xScale: size, yScale: size });
    }
    setOrEmbedFont(font) {
        const oldFont = this.font;
        const oldFontKey = this.fontKey;
        if (font)
            this.setFont(font);
        else
            this.getFont();
        const newFont = this.font;
        const newFontKey = this.fontKey;
        return { oldFont, oldFontKey, newFont, newFontKey };
    }
    /**
     * Draw an SVG on this page. For example:
     * ```js
     * const svg = '<svg><path d="M 0,20 L 100,160 Q 130,200 150,120 C 190,-40 200,200 300,150 L 400,90"></path></svg>'
     *
     * // Draw svg
     * page.drawSvg(svg, { x: 25, y: 75 })
     * ```
     * @param svg The SVG to be drawn.
     * @param options The options to be used when drawing the SVG.
     */
    async drawSvg(svg, options = {}) {
        var _a, _b;
        (0, utils_1.assertIs)(svg, 'svg', ['string']);
        (0, utils_1.assertOrUndefined)(options.x, 'options.x', ['number']);
        (0, utils_1.assertOrUndefined)(options.y, 'options.y', ['number']);
        (0, utils_1.assertOrUndefined)(options.width, 'options.width', ['number']);
        (0, utils_1.assertOrUndefined)(options.height, 'options.height', ['number']);
        await (0, svg_1.drawSvg)(this, svg, {
            x: (_a = options.x) !== null && _a !== void 0 ? _a : this.x,
            y: (_b = options.y) !== null && _b !== void 0 ? _b : this.y,
            fonts: options.fonts,
            width: options.width,
            height: options.height,
        });
    }
    getFont() {
        if (!this.font || !this.fontKey) {
            const font = this.doc.embedStandardFont(StandardFonts_1.StandardFonts.Helvetica);
            this.setFont(font);
        }
        return [this.font, this.fontKey];
    }
    resetFont() {
        this.font = undefined;
        this.fontKey = undefined;
    }
    getContentStream(useExisting = true) {
        if (useExisting && this.contentStream)
            return this.contentStream;
        this.contentStream = this.createContentStream();
        this.contentStreamRef = this.doc.context.register(this.contentStream);
        this.node.addContentStream(this.contentStreamRef);
        return this.contentStream;
    }
    createContentStream(...operators) {
        const dict = this.doc.context.obj({});
        const contentStream = core_1.PDFContentStream.of(dict, operators);
        return contentStream;
    }
    maybeEmbedGraphicsState(options) {
        const { opacity, borderOpacity, blendMode } = options;
        if (opacity === undefined &&
            borderOpacity === undefined &&
            blendMode === undefined) {
            return undefined;
        }
        const graphicsState = this.doc.context.obj({
            Type: 'ExtGState',
            ca: opacity,
            CA: borderOpacity,
            BM: blendMode,
        });
        const key = this.node.newExtGState('GS', graphicsState);
        return key;
    }
    scaleAnnot(annot, x, y) {
        const selectors = ['RD', 'CL', 'Vertices', 'QuadPoints', 'L', 'Rect'];
        for (let idx = 0, len = selectors.length; idx < len; idx++) {
            const list = annot.lookup(core_1.PDFName.of(selectors[idx]));
            if (list instanceof core_1.PDFArray)
                list.scalePDFNumbers(x, y);
        }
        const inkLists = annot.lookup(core_1.PDFName.of('InkList'));
        if (inkLists instanceof core_1.PDFArray) {
            for (let idx = 0, len = inkLists.size(); idx < len; idx++) {
                const arr = inkLists.lookup(idx);
                if (arr instanceof core_1.PDFArray)
                    arr.scalePDFNumbers(x, y);
            }
        }
    }
    /**
     * move annotations
     * @param annot annot
     * @param x The new position on the x-axis for this page's content.
     * @param y The new position on the y-axis for this page's content.
     */
    translateAnnot(annot, x, y) {
        const selectors = ['RD', 'CL', 'Vertices', 'QuadPoints', 'L', 'Rect'];
        for (let idx = 0, len = selectors.length; idx < len; idx++) {
            const list = annot.lookup(core_1.PDFName.of(selectors[idx]));
            if (list instanceof core_1.PDFArray)
                list.translatePDFNumbers(x, y);
        }
        const inkLists = annot.lookup(core_1.PDFName.of('InkList'));
        if (inkLists instanceof core_1.PDFArray) {
            for (let idx = 0, len = inkLists.size(); idx < len; idx++) {
                const arr = inkLists.lookup(idx);
                if (arr instanceof core_1.PDFArray)
                    arr.translatePDFNumbers(x, y);
            }
        }
    }
}
exports.default = PDFPage;
/**
 * > **NOTE:** You probably don't want to call this method directly. Instead,
 * > consider using the [[PDFDocument.addPage]] and [[PDFDocument.insertPage]]
 * > methods, which can create instances of [[PDFPage]] for you.
 *
 * Create an instance of [[PDFPage]] from an existing leaf node.
 *
 * @param leafNode The leaf node to be wrapped.
 * @param ref The unique reference for the page.
 * @param doc The document to which the page will belong.
 */
PDFPage.of = (leafNode, ref, doc) => new PDFPage(leafNode, ref, doc);
/**
 * > **NOTE:** You probably don't want to call this method directly. Instead,
 * > consider using the [[PDFDocument.addPage]] and [[PDFDocument.insertPage]]
 * > methods, which can create instances of [[PDFPage]] for you.
 *
 * Create an instance of [[PDFPage]].
 *
 * @param doc The document to which the page will belong.
 */
PDFPage.create = (doc) => {
    (0, utils_1.assertIs)(doc, 'doc', [[PDFDocument_1.default, 'PDFDocument']]);
    const dummyRef = core_1.PDFRef.of(-1);
    const pageLeaf = core_1.PDFPageLeaf.withContextAndParent(doc.context, dummyRef);
    const pageRef = doc.context.register(pageLeaf);
    return new PDFPage(pageLeaf, pageRef, doc);
};
//# sourceMappingURL=PDFPage.js.map