UNPKG

@ironsoftware/ironpdf

Version:

IronPDF for Node

190 lines (179 loc) 6.43 kB
import {ServiceError} from "@grpc/grpc-js"; import {Access} from "../../access"; import {IronPdfServiceClient} from "../../generated_proto/ironpdfengineproto/IronPdfService"; import {EmptyResultP__Output} from "../../generated_proto/ironpdfengineproto/EmptyResultP"; import {PdfiumGetAnnotationsResultP__Output} from "../../generated_proto/ironpdfengineproto/PdfiumGetAnnotationsResultP"; import {PdfiumWrappedPdfAnnotationP__Output} from "../../generated_proto/ironpdfengineproto/PdfiumWrappedPdfAnnotationP"; import {BookmarkDestinations, LinkAnnotation} from "../../../public/annotation"; import {handleEmptyResultP__Output, handleRemoteException} from "../util"; /** * URL prefix used to encode an internal hyperlink (goto-page) target inside the * existing {@code Pdfium_Annotation_AddLinkAnnotation} RPC. Must match the * {@code InternalLinkPrefixes.InternalLinkPrefix} constant in the C# * {@code IronPdf.GrpcLayer.InternalPrefixes} / {@code IronPdfServiceHandler}. * * <p>The {@code x-} prefix follows RFC 6648 conventions for experimental / * private URL schemes and cannot collide with any real URL scheme a user would * construct.</p> */ const INTERNAL_LINK_PREFIX = "x-ironpdf-goto-page:"; /** * Retrieve all heterogeneous annotations (text, free-text, link, ...) on a single page * via the {@code Pdfium_Annotation_GetAnnotations} unary RPC. * * The returned wrapped annotations carry one of the proto sub-types ({@code text}, * {@code freetext}, {@code link}). Callers inspect the {@code annotations} oneof to * dispatch by sub-type. */ export async function getAnnotations( id: string, pageIndex: number ): Promise<PdfiumWrappedPdfAnnotationP__Output[]> { const client: IronPdfServiceClient = await Access.ensureConnection(); return new Promise( ( resolve: (_: PdfiumWrappedPdfAnnotationP__Output[]) => void, reject: (errorMsg: string) => void ) => { client.Pdfium_Annotation_GetAnnotationsRequestP( { document: {documentId: id}, pageIndex: pageIndex, }, ( err: ServiceError | null, value: PdfiumGetAnnotationsResultP__Output | undefined ) => { if (err) { reject(`${err.name}/n${err.message}`); return; } if (!value) { reject("No response from IronPdfEngine for getAnnotations"); return; } if (value.exception) { handleRemoteException(value.exception, reject); return; } resolve(value.result?.annotations ?? []); } ); } ); } /** * Add an internal hyperlink annotation to the document. The link navigates to a * destination page within the same PDF when clicked. * * <p>Reuses the existing {@code Pdfium_Annotation_AddLinkAnnotation} RPC by encoding * the destination parameters into a special URL format: * {@code x-ironpdf-goto-page:{destPage},{destType},{left},{right},{top},{bottom},{zoom},{showBorder}}. * The engine detects this prefix and routes the request to its internal link handler.</p> * * <p>Mirrors {@code IronPdf.Engines.Pdfium.GrpcPdfClient.AddInternalLinkAnnotation} * on the C# side.</p> */ export async function addLinkAnnotation( id: string, link: LinkAnnotation ): Promise<void> { if (link.pageIndex < 0) { throw new Error("Invalid page index when adding link annotation"); } if (link.destinationPageIndex < 0) { throw new Error("Invalid destination page index when adding link annotation"); } const client: IronPdfServiceClient = await Access.ensureConnection(); const destType: BookmarkDestinations = link.destinationType ?? BookmarkDestinations.Page; const destLeft = link.destinationLeft ?? 0; const destRight = link.destinationRight ?? 0; const destTop = link.destinationTop ?? 0; const destBottom = link.destinationBottom ?? 0; const destZoom = link.destinationZoom ?? 0; const showBorderFlag = link.showBorder ? 1 : 0; // Matches the 8-field encoding produced by C# AddInternalLinkAnnotation: // {prefix}{destPageIndex},{destType},{destLeft},{destRight},{destTop},{destBottom},{destZoom},{showBorder} const encodedUrl = `${INTERNAL_LINK_PREFIX}${link.destinationPageIndex},${destType},` + `${destLeft},${destRight},${destTop},${destBottom},${destZoom},${showBorderFlag}`; // Color fallback: the engine parses `color_code` into IronSoftware.Drawing.Color and // throws on an empty string. Match the C# AddLinkAnnotation behavior by always // sending a valid color — default to black when the caller hasn't specified one. const colorCode = link.colorCode && link.colorCode.length > 0 ? link.colorCode : "#000000"; return new Promise( (resolve: () => void, reject: (errorMsg: string) => void) => { client.Pdfium_Annotation_AddLinkAnnotation( { document: {documentId: id}, name: link.title ?? "", url: encodedUrl, pageIndex: link.pageIndex, rectangle: { x: link.x, y: link.y, width: link.width, height: link.height, }, colorCode: colorCode, Hidden: link.hidden ?? false, }, ( err: ServiceError | null, value: EmptyResultP__Output | undefined ) => { if (err) { reject(`${err.name}/n${err.message}`); return; } if (!value) { reject("No response from IronPdfEngine for addLinkAnnotation"); return; } handleEmptyResultP__Output(value, reject); resolve(); } ); } ); } /** * Retrieve the number of annotations contained on the specified page via the * {@code Pdfium_Annotation_GetAnnotationCount} unary RPC. */ export async function getAnnotationCount( id: string, pageIndex: number ): Promise<number> { const client: IronPdfServiceClient = await Access.ensureConnection(); return new Promise( (resolve: (_: number) => void, reject: (errorMsg: string) => void) => { client.Pdfium_Annotation_GetAnnotationCountRequestP( { document: {documentId: id}, pageIndex: pageIndex, }, ( err: ServiceError | null, value: | import("../../generated_proto/ironpdfengineproto/IntResultP").IntResultP__Output | undefined ) => { if (err) { reject(`${err.name}/n${err.message}`); return; } if (!value) { reject("No response from IronPdfEngine for getAnnotationCount"); return; } if (value.exception) { handleRemoteException(value.exception, reject); return; } resolve(value.result ?? 0); } ); } ); }