ngx-extended-pdf-viewer
Version:
Embedding PDF files in your Angular application. Highly configurable viewer including the toolbar, sidebar, and all the features you're used to.
340 lines (339 loc) • 17.8 kB
TypeScript
import { RendererFactory2 } from '@angular/core';
import { AnnotationMode, EditorAnnotation } from './options/editor-annotations';
import { PdfLayer } from './options/optional_content_config';
import { PDFPrintRange } from './options/pdf-print-range';
import { PDFNotificationService } from './pdf-notification-service';
import * as i0 from "@angular/core";
export interface FindOptions {
highlightAll?: boolean;
matchCase?: boolean;
wholeWords?: boolean;
matchDiacritics?: boolean;
dontScrollIntoView?: boolean;
findMultiple?: boolean;
regexp?: boolean;
useSecondaryFindcontroller?: boolean;
}
export interface PDFExportScaleFactor {
width?: number;
height?: number;
scale?: number;
}
/**
* A rectangular region of a page, expressed in normalized coordinates on the
* page's *un-rotated* frame. Every value is a fraction between 0 and 1, measured
* from the top-left corner of the un-rotated page (x grows to the right, y grows
* downwards).
*
* To screenshot a single annotation, use the editor's rotation-independent
* rectangle `event.source.normalizedPageRect` (from the `annotationEditorEvent`),
* which is already in this coordinate system. Note that the editor's raw
* `x`/`y`/`width`/`height` are **not** suitable here: they are stored in whatever
* rotation the page had when the annotation was added (axes swapped for 90°/270°),
* so they only match this frame when the annotation was added un-rotated.
*
* Example: the upper-left quarter of a page is `{ x: 0, y: 0, width: 0.5, height: 0.5 }`.
*/
export interface PdfPageCropBox {
/** Distance of the left edge from the left of the page, as a fraction (0..1). */
x: number;
/** Distance of the top edge from the top of the page, as a fraction (0..1). */
y: number;
/** Width of the region, as a fraction of the page width (0..1). */
width: number;
/** Height of the region, as a fraction of the page height (0..1). */
height: number;
}
/**
* Rotation (in degrees, clockwise) to render a page at, e.g. when exporting it
* via {@link NgxExtendedPdfViewerService.getPageAsCanvas} /
* {@link NgxExtendedPdfViewerService.getPageAsImage}. Overrides the rotation the
* user applied in the viewer. `0` gives the page in its authored orientation
* regardless of how the user rotated it; `90`/`180`/`270` force that rotation.
* Omit it to follow the user's current on-screen rotation.
*/
export type PdfPageRotation = 0 | 90 | 180 | 270;
type DirectionType = 'ltr' | 'rtl' | 'both' | undefined;
export interface PdfImageParameters {
urlOrDataUrl: string;
page?: number;
left?: number | string;
bottom?: number | string;
right?: number | string;
top?: number | string;
rotation?: 0 | 90 | 180 | 270;
}
export interface Line {
x: number;
y: number;
width: number;
height: number;
direction: DirectionType;
text: string;
}
export interface Section {
x: number;
y: number;
width: number;
height: number;
direction: DirectionType;
lines: Array<Line>;
}
export declare class NgxExtendedPdfViewerService {
private readonly rendererFactory;
/**
* Tracks the most recently mounted `<ngx-extended-pdf-viewer>` instance's `openPDF()`
* completion. Public for backward compatibility — historically used as the "is the viewer
* ready?" gate when the library only supported a single instance at a time.
*
* Today this flag is **not** the source of truth for the service's own `find()` /
* `findNext()` / `findPrevious()` methods; those gate on the live `findController`
* directly. Component-internal effect guards have moved to a per-instance flag.
*
* In a future multi-viewer world this field will likely become "any viewer is mounted".
* Prefer querying viewer state via a viewer reference (see future API) instead of via
* this singleton flag.
*/
ngxExtendedPdfViewerInitialized: boolean;
secondaryMenuIsEmpty: import("@angular/core").WritableSignal<boolean>;
private readonly renderer;
private PDFViewerApplication?;
constructor(rendererFactory: RendererFactory2, notificationService: PDFNotificationService);
find(text: string | string[] | RegExp, options?: FindOptions): Array<Promise<number>> | undefined;
findNext(useSecondaryFindcontroller?: boolean): boolean;
findPrevious(useSecondaryFindcontroller?: boolean): boolean;
print(printRange?: PDFPrintRange): void;
removePrintRange(): void;
setPrintRange(printRange: PDFPrintRange): void;
filteredPageCount(pageCount: number, range: PDFPrintRange): number;
isInPDFPrintRange(pageIndex: number, printRange: PDFPrintRange): boolean;
getPageAsLines(pageNumber: number): Promise<Array<Line>>;
getPageAsText(pageNumber: number): Promise<string>;
private convertTextInfoToText;
/**
* Renders a single page to an off-screen `<canvas>`.
*
* @param pageNumber 1-based page number to render.
* @param scale How large the rendered page should be. Provide exactly one of
* `width`, `height` (both in pixels) or `scale` (a zoom factor, e.g. `2`).
* @param background Optional CSS color painted behind the page (e.g. `'rgba(255,0,0,0.3)'`).
* @param backgroundColorToReplace The page background color that is replaced by `background`. Defaults to white.
* @param annotationMode Which annotations to render. Defaults to
* `AnnotationMode.ENABLE_STORAGE`, so the screenshot includes what you
* see on screen: saved annotations, current form-field values, and
* annotations you just added with the editor (e.g. an image stamp) that
* haven't been saved into the PDF yet. Pass `AnnotationMode.ENABLE` to
* get only the annotations already baked into the document, or
* `AnnotationMode.DISABLE` for none.
* @param cropBox Optional region to crop to, in normalized 0..1 coordinates on
* the page's *un-rotated* frame (top-left origin). When set, only that
* part of the page is returned; the crop is rotated to follow the
* rendered rotation for you. To screenshot a single annotation, pass the
* editor's rotation-independent rectangle: `event.source.normalizedPageRect`
* (do **not** pass the raw `x`/`y`/`width`/`height`, which are stored in
* the rotation the annotation was added at). See {@link PdfPageCropBox}.
* @param rotation Optional rotation override (`0`, `90`, `180` or `270`). By
* default the screenshot follows the rotation the user applied in the
* viewer. Pass `0` to always get the page in its authored orientation
* regardless of the user's rotation, or `90`/`180`/`270` to force a
* specific rotation. See {@link PdfPageRotation}.
* @returns The rendered (and optionally cropped) canvas, or `undefined` if no document is loaded.
*/
getPageAsCanvas(pageNumber: number, scale: PDFExportScaleFactor, background?: string, backgroundColorToReplace?: string, annotationMode?: AnnotationMode, cropBox?: PdfPageCropBox, rotation?: PdfPageRotation): Promise<HTMLCanvasElement | undefined>;
/**
* Renders a single page and returns it as a PNG data URL.
*
* Same parameters as {@link NgxExtendedPdfViewerService.getPageAsCanvas}; in
* particular `cropBox` lets you export just a sub-region (e.g. a single
* annotation) using normalized 0..1 page-relative coordinates.
*
* @returns A `data:image/png;base64,...` string, or `undefined` if no document is loaded.
*/
getPageAsImage(pageNumber: number, scale: PDFExportScaleFactor, background?: string, backgroundColorToReplace?: string, annotationMode?: AnnotationMode, cropBox?: PdfPageCropBox, rotation?: PdfPageRotation): Promise<string | undefined>;
private draw;
/**
* Rotates a normalized (0..1, top-left origin) cropBox from the page's
* un-rotated coordinate space into the space of a canvas rendered at
* `rotation` degrees clockwise. For 90°/270° the width and height swap axes.
*/
private rotateCropBox;
/**
* Returns a new canvas containing only the region described by `cropBox`
* (normalized 0..1 page-relative coordinates, top-left origin) of `source`.
*/
private cropCanvas;
private getPageDrawContext;
getCurrentDocumentAsBlob(): Promise<Blob | undefined>;
getFormData(currentFormValues?: boolean): Promise<Array<Object>>;
/**
* Adds a page to the rendering queue
* @param {number} pageIndex Index of the page to render
* @returns {boolean} false, if the page has already been rendered,
* if it's out of range or if the viewer hasn't been initialized yet
*/
addPageToRenderQueue(pageIndex: number): boolean;
isRenderQueueEmpty(): boolean;
hasPageBeenRendered(pageIndex: number): boolean;
private sleep;
renderPage(pageIndex: number): Promise<void>;
currentlyRenderedPages(): Array<number>;
numberOfPages(): number;
getCurrentlyVisiblePageNumbers(): Array<number>;
listLayers(): Promise<Array<PdfLayer> | undefined>;
toggleLayer(layerId: string): Promise<void>;
scrollPageIntoView(pageNumber: number, pageSpot?: {
top?: number | string;
left?: number | string;
}): void;
/**
* Returns all editor annotations (drawings, text, images, highlights) in serialized format.
*
* **Two kinds of identifier:**
* - `id` — a **temporary** identifier (`pdfjs_internal_editor_N`) regenerated
* every session. Useful for live event tracking via `annotationEditorEvent`,
* but it is **not** stable, so do not persist it.
* - `customId` — a **stable**, developer-supplied identifier (e.g. a UUID).
* It is only present if you set it (see below). Unlike `id`, it survives the
* `getSerializedAnnotations()` → store → `addEditorAnnotation()` round-trip,
* so it is the field to rely on when correlating saved annotations.
*
* **Example - Stable IDs across sessions (#3225):**
* ```typescript
* // First save: assign your own stable id to each annotation.
* const annotations = pdfService.getSerializedAnnotations() ?? [];
* const toStore = annotations.map((a) => ({ ...a, customId: a.customId ?? crypto.randomUUID() }));
* localStorage.setItem('annotations', JSON.stringify(toStore));
*
* // Restore: customId is preserved (the temporary `id` is regenerated).
* const stored = JSON.parse(localStorage.getItem('annotations')!);
* await pdfService.addEditorAnnotation(stored);
* // getSerializedAnnotations() now returns the same customId values.
* ```
*
* @returns Array of serialized annotations (each with a temporary `id`, plus a
* stable `customId` if you assigned one), or null if none exist
*/
getSerializedAnnotations(): EditorAnnotation[] | null | undefined;
/**
* Returns a single editor annotation by its identifier.
*
* Matches against **both** identifiers, so you can look an annotation up by
* either the temporary `id` (e.g. the one carried by an `annotationEditorEvent`)
* or the stable `customId` you assigned (see {@link getSerializedAnnotations}).
*
* @param id The annotation's temporary `id` or its stable `customId`
* @returns The serialized annotation matching the identifier, or null if not found
*/
getSerializedAnnotation(id: string): EditorAnnotation | null | undefined;
/**
* Programmatically adds one or more editor annotations to the PDF.
*
* **ID Behavior:**
* - The temporary `id` field is **ignored** - a fresh internal id is always
* assigned, so no id conflicts can occur.
* - The stable `customId` field, if present, is **preserved** (#3225). Set it
* to your own value (e.g. a UUID) before storing, and the restored
* annotation keeps it - `getSerializedAnnotations()` will return it again.
* Uniqueness of `customId` values is your responsibility.
*
* **Supported Annotation Types:**
* - Ink (drawings)
* - FreeText (text boxes)
* - Stamp (images)
* - Highlight
* - Popup (comments)
*
* **Timing:** an annotation is added to a page's annotation _editor_ layer,
* which is created only after that page has been rendered (slightly after the
* display annotation layer). Wait for the page's `(annotationEditorLayerRendered)`
* event before calling this method - **not** `(annotationLayerRendered)`, which
* fires too early. Calling it before the editor layer exists logs
* `paste: "Cannot read properties of undefined (reading 'deserialize')"` and adds
* nothing (see issue #2656).
*
* @param serializedAnnotation A single annotation object, array of annotations, or JSON string
* @returns Promise that resolves when the annotation(s) have been added
*
* @example
* // Add a single annotation (with or without ID - both work the same)
* await pdfService.addEditorAnnotation({
* annotationType: 3,
* color: [255, 0, 0],
* value: 'Hello',
* pageIndex: 0,
* rect: [100, 100, 200, 150],
* rotation: 0,
* // `id` is ignored; set `customId` if you want a stable id that survives the round-trip
* customId: 'a3f1c2e0-...'
* });
*/
addEditorAnnotation(serializedAnnotation: string | EditorAnnotation): Promise<void>;
removeEditorAnnotations(filter?: (serialized: object) => boolean): void;
private loadImageAsDataURL;
/**
* Adds an image to a page as a stamp (editor) annotation. Since this is a
* viewer rather than an editor, the image is placed on the annotation editor
* layer; in most cases the result is indistinguishable from a real stamp.
*
* The `left`, `bottom`, `right`, and `top` coordinates accept percentages
* (e.g. `'50%'`), pixels (e.g. `'100px'`), or PDF coordinates (e.g. `100`).
* Omitted coordinates default to the logical origin (`left`/`bottom` to `0`,
* `right`/`top` to `'100%'`). If `page` is omitted, the current page is used.
*
* **Timing:** wait for the target page's `(annotationEditorLayerRendered)`
* event before calling this - **not** `(annotationLayerRendered)`. The editor
* layer is created only after the page is rendered, so calling it too early
* logs `paste: "Cannot read properties of undefined (reading 'deserialize')"`
* and adds nothing (see issue #2656). On large, lazily-rendered documents,
* add each page's image from its own `(annotationEditorLayerRendered)` handler.
*
* @param parameters The image source, optional target page, position, and rotation
* @returns Promise that resolves once the image has been added
*/
addImageToAnnotationLayer({ urlOrDataUrl, page, left, bottom, right, top, rotation }: PdfImageParameters): Promise<void>;
/**
* Adds a highlight (editor) annotation to a page.
*
* The `left`, `bottom`, `right`, and `top` coordinates accept percentages
* (e.g. `'50%'`), pixels (e.g. `'100px'`), or PDF coordinates (e.g. `100`).
* If `page` is omitted (`undefined`), the current page is used.
*
* **Timing:** like {@link addImageToAnnotationLayer}, wait for the target
* page's `(annotationEditorLayerRendered)` event before calling this - **not**
* `(annotationLayerRendered)`. Calling it before the editor layer exists logs
* `paste: "Cannot read properties of undefined (reading 'deserialize')"` and
* adds nothing (see issue #2656).
*
* @param color RGB color as a 0-255 triple, e.g. `[255, 255, 0]` for yellow
* @param page Zero-based page index, or `undefined` for the current page
* @param left Left edge (percentage, pixels, or PDF coordinate)
* @param bottom Bottom edge (percentage, pixels, or PDF coordinate)
* @param right Right edge (percentage, pixels, or PDF coordinate)
* @param top Top edge (percentage, pixels, or PDF coordinate)
* @param thickness Highlighter thickness; defaults to `12`
* @param rotation Rotation in degrees (`0`, `90`, `180`, or `270`); defaults to `0`
* @param opacity Opacity between `0` and `1`; defaults to `0.5`
* @returns Promise that resolves once the highlight has been added
*/
addHighlightToAnnotationLayer(color: number[], page: number | undefined, left: number | string, bottom: number | string, right: number | string, top: number | string, thickness?: number, rotation?: 0 | 90 | 180 | 270, opacity?: number): Promise<void>;
currentPageIndex(): number | undefined;
private convertToPDFCoordinates;
switchAnnotationEdtorMode(mode: number): void;
set editorFontSize(size: number);
set editorFontColor(color: string);
set editorInkColor(color: string);
set editorInkOpacity(opacity: number);
set editorInkThickness(thickness: number);
set editorHighlightColor(color: string);
/** @deprecated This feature was never wired up in pdf.js. Use editorHighlightColor instead. */
set editorHighlightDefaultColor(color: string);
set editorHighlightShowAll(showAll: boolean);
set editorHighlightThickness(thickness: number);
setEditorProperty(editorPropertyType: number, value: any): void;
getCurrentPage(): number;
getPageCount(): number;
movePage(fromIndex: number, toIndex: number): void;
static ɵfac: i0.ɵɵFactoryDeclaration<NgxExtendedPdfViewerService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<NgxExtendedPdfViewerService>;
}
export {};