/** * PDF editor — modify existing PDF documents. * * Supports: * - Adding new pages with free-form content * - Adding text/shapes/images to existing pages (overlay) * - Filling form fields (AcroForm) * - Copying pages from other PDFs (merge) * - Preserving page properties (Rotate, CropBox, etc.) and metadata * * Note: save() rebuilds the PDF from scratch rather than using incremental * updates. This is simpler and more reliable but means object numbers change * and existing digital signatures will be invalidated. * * @example Edit an existing PDF: * ```typescript * import { PdfEditor } from "@cj-tech-master/excelts/pdf"; * * const editor = PdfEditor.load(existingPdfBytes); * editor.getPage(0).drawText("APPROVED", { x: 200, y: 400, fontSize: 48, color: { r: 0, g: 0.5, b: 0 } }); * editor.setFormField("name", "John Doe"); * const result = await editor.save(); * ``` */ import type { PdfContentStream } from "../core/pdf-stream.js"; import type { PdfFormField } from "../reader/form-extractor.js"; import { PdfPageBuilder } from "./document-builder.js"; import type { DrawTextOptions, DrawRectOptions, DrawCircleOptions, DrawEllipseOptions, DrawLineOptions, DrawImageOptions, DrawPathOptions, PathOp, PageOptions, AnnotationOptions, FormFieldOptions, PdfSignatureOptions } from "./document-builder.js"; /** Options for loading a PDF for editing. */ export interface LoadOptions { /** Password for encrypted PDFs. */ password?: string; } /** * Proxy for an existing page that allows overlaying new content. * New content is drawn on top of existing content via a separate content stream. */ export declare class PdfEditorPage { /** Page width in points. */ get width(): number; /** Page height in points. */ get height(): number; /** * Measure the width of a text string in points. */ measureText(text: string, options?: { fontSize?: number; fontFamily?: string; bold?: boolean; italic?: boolean; }): number; /** * Draw text on this existing page (overlaid on top). */ drawText(text: string, options: DrawTextOptions): this; /** * Draw a rectangle on this existing page. */ drawRect(options: DrawRectOptions): this; /** * Draw a circle on this existing page. */ drawCircle(options: DrawCircleOptions): this; /** * Draw an ellipse on this existing page. */ drawEllipse(options: DrawEllipseOptions): this; /** * Draw a line on this existing page. */ drawLine(options: DrawLineOptions): this; /** * Draw an image on this existing page. */ drawImage(options: DrawImageOptions): this; /** * Get the raw overlay content stream. */ getContentStream(): PdfContentStream; /** * Add an annotation to this existing page (Highlight, Text, FreeText, Stamp, etc.). */ addAnnotation(options: AnnotationOptions): this; /** * Add a form field to this existing page. */ addFormField(options: FormFieldOptions): this; /** * Draw an SVG path on this existing page. */ drawSvgPath(d: string, options?: DrawPathOptions): this; /** * Draw a complex path from a list of path operations. */ drawPath(ops: PathOp[], options?: DrawPathOptions): this; } /** * Editor for modifying existing PDF documents. * * Load an existing PDF, overlay content on existing pages, fill form fields, * add new pages, copy pages from other documents, and save. */ export declare class PdfEditor { private _doc; private _password; private _pages; private _newPages; private _fontManager; private _formFieldUpdates; private _copiedPages; private _signaturePlaceholder; private constructor(); /** * Load a PDF for editing. * * @param data - Raw PDF file bytes * @param options - Load options (e.g., password) * @returns A PdfEditor instance */ static load(data: Uint8Array, options?: LoadOptions): PdfEditor; /** Number of existing pages. */ get pageCount(): number; /** * Get an existing page for editing (overlaying content). * * @param index - 0-based page index */ getPage(index: number): PdfEditorPage; /** * Add a new blank page to the end of the document. */ addPage(options?: PageOptions): PdfPageBuilder; /** * Remove a page from the document. * * @param index - 0-based page index (of original pages only) */ removePage(index: number): this; /** * Set the rotation of an existing page. * * @param index - 0-based page index (of original pages only) * @param degrees - Rotation in degrees (must be 0, 90, 180, or 270) */ rotatePage(index: number, degrees: number): this; /** * Split the document: save each page (or a subset) as a separate PDF. * * @param pageIndices - 0-based page indices to extract. Omit to split all pages. * @returns Array of Uint8Array, one per requested page. */ splitPages(pageIndices?: number[]): Promise; /** * Embed a TrueType font for Unicode/CJK support. */ embedFont(fontBytes: Uint8Array): this; /** * Set the value of a form field. * The field is identified by its fully qualified name (e.g., "form.address.city"). * * @param fieldName - Fully qualified field name * @param value - New value to set */ setFormField(fieldName: string, value: string): this; /** * Set multiple form field values at once. * * @param fields - Object mapping field names to values */ setFormFields(fields: Record): this; /** * Get current form fields (before any modifications). */ getFormFields(): PdfFormField[]; /** * Copy pages from another PDF document into this document. * * @param sourcePdf - Raw bytes of the source PDF * @param pageIndices - 0-based page indices to copy. Omit to copy all pages. * @param options - Load options for the source PDF (e.g., password) */ copyPagesFrom(sourcePdf: Uint8Array, pageIndices?: number[], options?: LoadOptions): this; /** * Save the modified PDF. * * Rebuilds the PDF from scratch — content streams, resources, and page * properties are deep-cloned into a new document. Original metadata and * XMP streams are preserved. Digital signatures will be invalidated. * * @returns The modified PDF as Uint8Array */ save(): Promise; /** * Save the modified PDF using incremental update. * * Appends new/modified objects after the original PDF bytes, preserving the * original data byte-for-byte. This is ideal for overlays and form field * updates on existing pages — it preserves digital signatures on unmodified * content and produces smaller output. * * Falls back to {@link save} (full rebuild) if structural changes are * present (new pages, copied pages, or removed pages). * * @returns The modified PDF as Uint8Array */ saveIncremental(): Promise; /** Page-level keys to preserve when rebuilding page dicts. */ private static readonly _PAGE_PRESERVE_KEYS; /** * Sign this PDF with a digital signature. * * Performs a full save with an embedded PKCS#7 signature placeholder, * then fills in the real CMS SignedData. * * @param options - Certificate, private key, and optional signer metadata * @returns The signed PDF as Uint8Array * * @example * ```typescript * const editor = PdfEditor.load(pdfBytes); * const signed = await editor.sign({ * certificate: certDerBytes, * privateKey: pkcs8DerBytes, * name: "Jane Doe", * reason: "Approval" * }); * ``` */ sign(options: PdfSignatureOptions): Promise; }