@@ -17,6 +17,8 @@ import {
1717 PdfBookmarkObject ,
1818 PdfDocumentObject ,
1919 PdfPageObject ,
20+ PdfPageBoxes ,
21+ Box ,
2022 PdfActionType ,
2123 Rotation ,
2224 PDF_FORM_FIELD_FLAG ,
@@ -359,6 +361,8 @@ export class PdfiumNative implements IPdfiumExecutor {
359361
360362 const pages : PdfPageObject [ ] = [ ] ;
361363 const sizePtr = this . memoryManager . malloc ( 8 ) ;
364+ // FS_RECTF is { float left, top, right, bottom } = 16 bytes.
365+ const boxPtr = this . memoryManager . malloc ( 16 ) ;
362366 for ( let index = 0 ; index < pageCount ; index ++ ) {
363367 // Use normalized size function when normalizeRotation is enabled
364368 const result = normalizeRotation
@@ -373,6 +377,7 @@ export class PdfiumNative implements IPdfiumExecutor {
373377 `${ normalizeRotation ? 'EPDF_GetPageSizeByIndexNormalized' : 'FPDF_GetPageSizeByIndexF' } failed with ${ lastError } ` ,
374378 ) ;
375379 this . memoryManager . free ( sizePtr ) ;
380+ this . memoryManager . free ( boxPtr ) ;
376381 this . pdfiumModule . FPDF_CloseDocument ( docPtr ) ;
377382 this . memoryManager . free ( filePtr ) ;
378383 this . logger . perf ( LOG_SOURCE , LOG_CATEGORY , `OpenDocumentBuffer` , 'End' , file . id ) ;
@@ -384,6 +389,7 @@ export class PdfiumNative implements IPdfiumExecutor {
384389
385390 const rotation = this . pdfiumModule . EPDF_GetPageRotationByIndex ( docPtr , index ) as Rotation ;
386391 const objectNumber = this . pdfiumModule . EPDFDoc_GetPageObjectNumberByIndex ( docPtr , index ) ;
392+ const boxes = this . readPageBoxes ( docPtr , index , boxPtr ) ;
387393
388394 const page = {
389395 index,
@@ -393,11 +399,13 @@ export class PdfiumNative implements IPdfiumExecutor {
393399 } ,
394400 rotation,
395401 objectNumber,
402+ boxes,
396403 } ;
397404
398405 pages . push ( page ) ;
399406 }
400407 this . memoryManager . free ( sizePtr ) ;
408+ this . memoryManager . free ( boxPtr ) ;
401409
402410 // Query security state
403411 const isEncrypted = this . pdfiumModule . EPDF_IsEncrypted ( docPtr ) ;
@@ -10432,6 +10440,75 @@ export class PdfiumNative implements IPdfiumExecutor {
1043210440 } ;
1043310441 }
1043410442
10443+ /**
10444+ * Read the page boundary boxes (Media/Crop/Bleed/Trim/Art) for a page without
10445+ * loading it. Reuses a caller-owned 16-byte FS_RECTF scratch buffer.
10446+ * @param docPtr - pointer to the pdf document
10447+ * @param index - page index
10448+ * @param boxPtr - pointer to a 16-byte FS_RECTF scratch buffer
10449+ * @returns the page boxes, or undefined when Media/Crop cannot be resolved
10450+ *
10451+ * @private
10452+ */
10453+ private readPageBoxes (
10454+ docPtr : number ,
10455+ index : number ,
10456+ boxPtr : number ,
10457+ ) : PdfPageBoxes | undefined {
10458+ const readBox = ( boxType : number ) : Box | undefined => {
10459+ const ok = this . pdfiumModule . EPDF_GetPageBoxByIndex ( docPtr , index , boxType , boxPtr ) ;
10460+ if ( ! ok ) {
10461+ return undefined ;
10462+ }
10463+ // FS_RECTF layout: { float left, top, right, bottom }.
10464+ return {
10465+ left : this . pdfiumModule . pdfium . getValue ( boxPtr , 'float' ) ,
10466+ top : this . pdfiumModule . pdfium . getValue ( boxPtr + 4 , 'float' ) ,
10467+ right : this . pdfiumModule . pdfium . getValue ( boxPtr + 8 , 'float' ) ,
10468+ bottom : this . pdfiumModule . pdfium . getValue ( boxPtr + 12 , 'float' ) ,
10469+ } ;
10470+ } ;
10471+
10472+ const media = readBox ( 0 ) ; // EPDF_PAGE_BOX_MEDIA
10473+ const crop = readBox ( 1 ) ; // EPDF_PAGE_BOX_CROP (falls back to media)
10474+ if ( ! media || ! crop ) {
10475+ return undefined ;
10476+ }
10477+
10478+ const boxes : PdfPageBoxes = { media, crop } ;
10479+ const bleed = readBox ( 2 ) ; // EPDF_PAGE_BOX_BLEED
10480+ if ( bleed ) {
10481+ boxes . bleed = bleed ;
10482+ }
10483+ const trim = readBox ( 3 ) ; // EPDF_PAGE_BOX_TRIM
10484+ if ( trim ) {
10485+ boxes . trim = trim ;
10486+ }
10487+ const art = readBox ( 4 ) ; // EPDF_PAGE_BOX_ART
10488+ if ( art ) {
10489+ boxes . art = art ;
10490+ }
10491+ return boxes ;
10492+ }
10493+
10494+ /**
10495+ * The effective page origin in PDF user space: the lower-left corner of the
10496+ * crop box (which PDFium also uses for the page size). Used to offset
10497+ * coordinates for PDFs whose MediaBox/CropBox origin is not `(0, 0)`.
10498+ * Returns `(0, 0)` when the page has no box information.
10499+ * @param page - pdf page info
10500+ * @returns the page origin
10501+ *
10502+ * @private
10503+ */
10504+ private getPageOrigin ( page : PdfPageObject ) : Position {
10505+ const crop = page . boxes ?. crop ;
10506+ if ( ! crop ) {
10507+ return { x : 0 , y : 0 } ;
10508+ }
10509+ return { x : crop . left , y : crop . bottom } ;
10510+ }
10511+
1043510512 /**
1043610513 * Convert coordinate of point from device coordinate to page coordinate
1043710514 * @param doc - pdf document object
@@ -10452,24 +10529,28 @@ export class PdfiumNative implements IPdfiumExecutor {
1045210529 // so we must also use 0° for coordinate transformations
1045310530 const r = doc . normalizedRotation ? 0 : page . rotation & 3 ;
1045410531
10532+ // Recover the point relative to the page box origin, then shift back into
10533+ // PDF user space so non-zero MediaBox/CropBox origins round-trip correctly.
10534+ const origin = this . getPageOrigin ( page ) ;
10535+
10536+ let point : Position ;
1045510537 if ( r === 0 ) {
1045610538 // 0°
10457- return { x : position . x , y : DH - position . y } ;
10458- }
10459- if ( r === 1 ) {
10539+ point = { x : position . x , y : DH - position . y } ;
10540+ } else if ( r === 1 ) {
1046010541 // 90° CW
1046110542 // x_d = sx*y, y_d = sy*x => x = y_d/sy, y = x_d/sx
10462- return { x : position . y , y : position . x } ;
10463- }
10464- if ( r === 2 ) {
10543+ point = { x : position . y , y : position . x } ;
10544+ } else if ( r === 2 ) {
1046510545 // 180°
10466- return { x : DW - position . x , y : position . y } ;
10467- }
10468- {
10546+ point = { x : DW - position . x , y : position . y } ;
10547+ } else {
1046910548 // 270° CW
1047010549 // x_d = DW - sx*y, y_d = DH - sy*x
10471- return { x : DH - position . y , y : DW - position . x } ;
10550+ point = { x : DH - position . y , y : DW - position . x } ;
1047210551 }
10552+
10553+ return { x : point . x + origin . x , y : point . y + origin . y } ;
1047310554 }
1047410555
1047510556 /**
@@ -10492,21 +10573,27 @@ export class PdfiumNative implements IPdfiumExecutor {
1049210573 // so we must also use 0° for coordinate transformations
1049310574 const r = doc . normalizedRotation ? 0 : page . rotation & 3 ;
1049410575
10576+ // Shift the PDF user-space point so it is relative to the page box origin
10577+ // before applying rotation, supporting non-zero MediaBox/CropBox origins.
10578+ const origin = this . getPageOrigin ( page ) ;
10579+ const px = position . x - origin . x ;
10580+ const py = position . y - origin . y ;
10581+
1049510582 if ( r === 0 ) {
1049610583 // 0°
10497- return { x : position . x , y : DH - position . y } ;
10584+ return { x : px , y : DH - py } ;
1049810585 }
1049910586 if ( r === 1 ) {
1050010587 // 90° CW
10501- return { x : position . y , y : position . x } ;
10588+ return { x : py , y : px } ;
1050210589 }
1050310590 if ( r === 2 ) {
1050410591 // 180°
10505- return { x : DW - position . x , y : position . y } ;
10592+ return { x : DW - px , y : py } ;
1050610593 }
1050710594 {
1050810595 // 270° CW
10509- return { x : DW - position . y , y : DH - position . x } ;
10596+ return { x : DW - py , y : DH - px } ;
1051010597 }
1051110598 }
1051210599
0 commit comments