@embedpdf/engines 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { NoopLogger, PdfTaskHelper, PdfErrorCode, Task, Rotation, PdfAnnotationSubtype, PdfPageObjectType, PdfAnnotationObjectStatus, quadToRect, PDF_FORM_FIELD_TYPE, toIntRect, transformRect, toIntSize, transformSize, PdfActionType, PdfZoomMode, AppearanceMode, MatchFlag } from '@embedpdf/models';
2
1
  import { init } from '@embedpdf/pdfium';
2
+ import { NoopLogger, PdfTaskHelper, PdfErrorCode, Task, Rotation, PdfAnnotationSubtype, stripPdfUnwantedMarkers, dateToPdfDate, PdfPageObjectType, pdfAlphaColorToWebAlphaColor, webAlphaColorToPdfAlphaColor, AppearanceMode, quadToRect, pdfDateToDate, PDF_FORM_FIELD_TYPE, toIntRect, transformRect, toIntSize, transformSize, PdfActionType, PdfZoomMode, MatchFlag, rectToQuad } from '@embedpdf/models';
3
3
 
4
4
  /**
5
5
  * Read string from WASM heap
@@ -915,6 +915,12 @@ class PdfiumEngine {
915
915
  case PdfAnnotationSubtype.STAMP:
916
916
  isSucceed = this.addStampContent(ctx.docPtr, page, pageCtx.pagePtr, annotationPtr, annotation.rect, annotation.contents);
917
917
  break;
918
+ case PdfAnnotationSubtype.UNDERLINE:
919
+ case PdfAnnotationSubtype.STRIKEOUT:
920
+ case PdfAnnotationSubtype.SQUIGGLY:
921
+ case PdfAnnotationSubtype.HIGHLIGHT:
922
+ isSucceed = this.addTextMarkupContent(page, annotationPtr, annotation);
923
+ break;
918
924
  }
919
925
  if (!isSucceed) {
920
926
  this.pdfiumModule.FPDFPage_RemoveAnnot(pageCtx.pagePtr, annotationPtr);
@@ -926,10 +932,101 @@ class PdfiumEngine {
926
932
  });
927
933
  }
928
934
  this.pdfiumModule.FPDFPage_GenerateContent(pageCtx.pagePtr);
935
+ const annotId = this.pdfiumModule.FPDFPage_GetAnnotIndex(pageCtx.pagePtr, annotationPtr);
929
936
  this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
930
937
  pageCtx.release();
931
938
  this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `CreatePageAnnotation`, 'End', `${doc.id}-${page.index}`);
932
- return PdfTaskHelper.resolve(true);
939
+ return annotId >= 0
940
+ ? PdfTaskHelper.resolve(annotId)
941
+ : PdfTaskHelper.reject({
942
+ code: PdfErrorCode.CantCreateAnnot,
943
+ message: 'annotation created but index could not be determined',
944
+ });
945
+ }
946
+ /**
947
+ * Update an existing page annotation in-place
948
+ *
949
+ * • Locates the annot by page-local index (`annotation.id`)
950
+ * • Re-writes its /Rect and type-specific payload
951
+ * • Calls FPDFPage_GenerateContent so the new appearance is rendered
952
+ *
953
+ * @returns PdfTask<boolean> – true on success
954
+ */
955
+ updatePageAnnotation(doc, page, annotation) {
956
+ this.logger.debug(LOG_SOURCE, LOG_CATEGORY, 'updatePageAnnotation', doc, page, annotation);
957
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'UpdatePageAnnotation', 'Begin', `${doc.id}-${page.index}`);
958
+ const ctx = this.cache.getContext(doc.id);
959
+ if (!ctx) {
960
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'UpdatePageAnnotation', 'End', `${doc.id}-${page.index}`);
961
+ return PdfTaskHelper.reject({
962
+ code: PdfErrorCode.DocNotOpen,
963
+ message: 'document does not open',
964
+ });
965
+ }
966
+ const pageCtx = ctx.acquirePage(page.index);
967
+ const annotPtr = this.pdfiumModule.FPDFPage_GetAnnot(pageCtx.pagePtr, annotation.id);
968
+ if (!annotPtr) {
969
+ pageCtx.release();
970
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'UpdatePageAnnotation', 'End', `${doc.id}-${page.index}`);
971
+ return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: 'annotation not found' });
972
+ }
973
+ /* 1 ── (re)set bounding-box ────────────────────────────────────────────── */
974
+ if (!this.setPageAnnoRect(page, pageCtx.pagePtr, annotPtr, annotation.rect)) {
975
+ this.pdfiumModule.FPDFPage_CloseAnnot(annotPtr);
976
+ pageCtx.release();
977
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'UpdatePageAnnotation', 'End', `${doc.id}-${page.index}`);
978
+ return PdfTaskHelper.reject({
979
+ code: PdfErrorCode.CantSetAnnotRect,
980
+ message: 'failed to move annotation',
981
+ });
982
+ }
983
+ /* 2 ── wipe previous payload and rebuild fresh one ─────────────────────── */
984
+ let ok = false;
985
+ switch (annotation.type) {
986
+ /* ── Ink ─────────────────────────────────────────────────────────────── */
987
+ case PdfAnnotationSubtype.INK: {
988
+ /* clear every existing stroke first */
989
+ if (!this.pdfiumModule.FPDFAnnot_RemoveInkList(annotPtr))
990
+ break;
991
+ ok = this.addInkStroke(page, pageCtx.pagePtr, annotPtr, annotation.inkList);
992
+ break;
993
+ }
994
+ /* ── Stamp ───────────────────────────────────────────────────────────── */
995
+ case PdfAnnotationSubtype.STAMP: {
996
+ /* drop every page-object inside the annot */
997
+ for (let i = this.pdfiumModule.FPDFAnnot_GetObjectCount(annotPtr) - 1; i >= 0; i--) {
998
+ this.pdfiumModule.FPDFAnnot_RemoveObject(annotPtr, i);
999
+ }
1000
+ ok = this.addStampContent(ctx.docPtr, page, pageCtx.pagePtr, annotPtr, annotation.rect, annotation.contents);
1001
+ break;
1002
+ }
1003
+ /* ── Text-markup family ──────────────────────────────────────────────── */
1004
+ case PdfAnnotationSubtype.HIGHLIGHT:
1005
+ case PdfAnnotationSubtype.UNDERLINE:
1006
+ case PdfAnnotationSubtype.STRIKEOUT:
1007
+ case PdfAnnotationSubtype.SQUIGGLY: {
1008
+ /* replace quad-points / colour / strings in one go */
1009
+ ok = this.addTextMarkupContent(page, annotPtr, annotation, true);
1010
+ break;
1011
+ }
1012
+ /* ── Unsupported edits – fall through to error ───────────────────────── */
1013
+ default:
1014
+ ok = false;
1015
+ }
1016
+ /* 3 ── regenerate appearance if payload was changed ───────────────────── */
1017
+ if (ok) {
1018
+ this.pdfiumModule.FPDFPage_GenerateContent(pageCtx.pagePtr);
1019
+ }
1020
+ /* 4 ── tidy-up native handles ──────────────────────────────────────────── */
1021
+ this.pdfiumModule.FPDFPage_CloseAnnot(annotPtr);
1022
+ pageCtx.release();
1023
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'UpdatePageAnnotation', 'End', `${doc.id}-${page.index}`);
1024
+ return ok
1025
+ ? PdfTaskHelper.resolve(true)
1026
+ : PdfTaskHelper.reject({
1027
+ code: PdfErrorCode.CantSetAnnotContent,
1028
+ message: 'failed to update annotation',
1029
+ });
933
1030
  }
934
1031
  /**
935
1032
  * {@inheritDoc @embedpdf/models!PdfEngine.transformPageAnnotation}
@@ -1362,6 +1459,62 @@ class PdfiumEngine {
1362
1459
  this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractText`, 'End', doc.id);
1363
1460
  return PdfTaskHelper.resolve(text);
1364
1461
  }
1462
+ /**
1463
+ * {@inheritDoc @embedpdf/models!PdfEngine.getTextSlices}
1464
+ *
1465
+ * @public
1466
+ */
1467
+ getTextSlices(doc, slices) {
1468
+ this.logger.debug(LOG_SOURCE, LOG_CATEGORY, 'getTextSlices', doc, slices);
1469
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'GetTextSlices', 'Begin', doc.id);
1470
+ /* ⚠︎ 1 — trivial case */
1471
+ if (slices.length === 0) {
1472
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'GetTextSlices', 'End', doc.id);
1473
+ return PdfTaskHelper.resolve([]);
1474
+ }
1475
+ /* ⚠︎ 2 — document must be open */
1476
+ const ctx = this.cache.getContext(doc.id);
1477
+ if (!ctx) {
1478
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'GetTextSlices', 'End', doc.id);
1479
+ return PdfTaskHelper.reject({
1480
+ code: PdfErrorCode.DocNotOpen,
1481
+ message: 'document does not open',
1482
+ });
1483
+ }
1484
+ try {
1485
+ /* keep caller order */
1486
+ const out = new Array(slices.length);
1487
+ /* group → open each page once */
1488
+ const byPage = new Map();
1489
+ slices.forEach((s, i) => {
1490
+ (byPage.get(s.pageIndex) ?? byPage.set(s.pageIndex, []).get(s.pageIndex)).push({
1491
+ slice: s,
1492
+ pos: i,
1493
+ });
1494
+ });
1495
+ for (const [pageIdx, list] of byPage) {
1496
+ const pageCtx = ctx.acquirePage(pageIdx);
1497
+ const textPagePtr = pageCtx.getTextPage();
1498
+ for (const { slice, pos } of list) {
1499
+ const bufPtr = this.malloc(2 * (slice.charCount + 1)); // UTF-16 + NIL
1500
+ this.pdfiumModule.FPDFText_GetText(textPagePtr, slice.charIndex, slice.charCount, bufPtr);
1501
+ out[pos] = stripPdfUnwantedMarkers(this.pdfiumModule.pdfium.UTF16ToString(bufPtr));
1502
+ this.free(bufPtr);
1503
+ }
1504
+ pageCtx.release();
1505
+ }
1506
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'GetTextSlices', 'End', doc.id);
1507
+ return PdfTaskHelper.resolve(out);
1508
+ }
1509
+ catch (e) {
1510
+ this.logger.error(LOG_SOURCE, LOG_CATEGORY, 'getTextSlices error', e);
1511
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'GetTextSlices', 'End', doc.id);
1512
+ return PdfTaskHelper.reject({
1513
+ code: PdfErrorCode.Unknown,
1514
+ message: String(e),
1515
+ });
1516
+ }
1517
+ }
1365
1518
  /**
1366
1519
  * {@inheritDoc @embedpdf/models!PdfEngine.merge}
1367
1520
  *
@@ -1592,6 +1745,36 @@ class PdfiumEngine {
1592
1745
  }
1593
1746
  return true;
1594
1747
  }
1748
+ /**
1749
+ * Add highlight content to annotation
1750
+ * @param page - page info
1751
+ * @param annotationPtr - pointer to highlight annotation
1752
+ * @param annotation - highlight annotation
1753
+ * @returns whether highlight content is added to annotation
1754
+ *
1755
+ * @private
1756
+ */
1757
+ addTextMarkupContent(page, annotationPtr, annotation, shouldClearAP = false) {
1758
+ if (!this.syncQuadPointsAnno(page, annotationPtr, annotation.segmentRects)) {
1759
+ return false;
1760
+ }
1761
+ if (!this.setAnnotString(annotationPtr, 'Contents', annotation.contents ?? '')) {
1762
+ return false;
1763
+ }
1764
+ if (!this.setAnnotString(annotationPtr, 'T', annotation.author || '')) {
1765
+ return false;
1766
+ }
1767
+ if (!this.setAnnotString(annotationPtr, 'M', dateToPdfDate(annotation.modified))) {
1768
+ return false;
1769
+ }
1770
+ if (!this.setAnnotationColor(annotationPtr, {
1771
+ color: annotation.color ?? '#FFFF00',
1772
+ opacity: annotation.opacity ?? 1,
1773
+ }, shouldClearAP, 0)) {
1774
+ return false;
1775
+ }
1776
+ return true;
1777
+ }
1595
1778
  /**
1596
1779
  * Add contents to stamp annotation
1597
1780
  * @param docPtr - pointer to pdf document object
@@ -1891,14 +2074,12 @@ class PdfiumEngine {
1891
2074
  const runs = [];
1892
2075
  let current = null;
1893
2076
  let curObjPtr = null;
2077
+ let bounds = null;
1894
2078
  /** ── main loop ──────────────────────────────────────────── */
1895
2079
  for (let i = 0; i < glyphs.length; i++) {
1896
2080
  const g = glyphs[i];
1897
2081
  /* 1 — find the CPDF_TextObject this glyph belongs to */
1898
2082
  const objPtr = this.pdfiumModule.FPDFText_GetTextObject(textPagePtr, i);
1899
- if (g.isEmpty) {
1900
- continue;
1901
- }
1902
2083
  /* 2 — start a new run when the text object changes */
1903
2084
  if (objPtr !== curObjPtr) {
1904
2085
  curObjPtr = objPtr;
@@ -1912,6 +2093,12 @@ class PdfiumEngine {
1912
2093
  charStart: i,
1913
2094
  glyphs: [],
1914
2095
  };
2096
+ bounds = {
2097
+ minX: g.origin.x,
2098
+ minY: g.origin.y,
2099
+ maxX: g.origin.x + g.size.width,
2100
+ maxY: g.origin.y + g.size.height,
2101
+ };
1915
2102
  runs.push(current);
1916
2103
  }
1917
2104
  /* 3 — append the slim glyph record */
@@ -1920,16 +2107,24 @@ class PdfiumEngine {
1920
2107
  y: g.origin.y,
1921
2108
  width: g.size.width,
1922
2109
  height: g.size.height,
1923
- flags: g.isSpace ? 1 : 0,
2110
+ flags: g.isEmpty ? 2 : g.isSpace ? 1 : 0,
1924
2111
  });
1925
2112
  /* 4 — expand the run's bounding rect */
2113
+ if (g.isEmpty) {
2114
+ continue;
2115
+ }
1926
2116
  const right = g.origin.x + g.size.width;
1927
2117
  const bottom = g.origin.y + g.size.height;
1928
- current.rect.width =
1929
- Math.max(current.rect.x + current.rect.width, right) - current.rect.x;
1930
- current.rect.y = Math.min(current.rect.y, g.origin.y);
1931
- current.rect.height =
1932
- Math.max(current.rect.y + current.rect.height, bottom) - current.rect.y;
2118
+ // Update bounds
2119
+ bounds.minX = Math.min(bounds.minX, g.origin.x);
2120
+ bounds.minY = Math.min(bounds.minY, g.origin.y);
2121
+ bounds.maxX = Math.max(bounds.maxX, right);
2122
+ bounds.maxY = Math.max(bounds.maxY, bottom);
2123
+ // Calculate final rect from bounds
2124
+ current.rect.x = bounds.minX;
2125
+ current.rect.y = bounds.minY;
2126
+ current.rect.width = bounds.maxX - bounds.minX;
2127
+ current.rect.height = bounds.maxY - bounds.minY;
1933
2128
  }
1934
2129
  return runs;
1935
2130
  }
@@ -2202,8 +2397,6 @@ class PdfiumEngine {
2202
2397
  annotation = this.readPdfCaretAnno(page, pageCtx.pagePtr, annotationPtr, index);
2203
2398
  }
2204
2399
  break;
2205
- case PdfAnnotationSubtype.POPUP:
2206
- break;
2207
2400
  default:
2208
2401
  {
2209
2402
  annotation = this.readPdfAnno(page, pageCtx.pagePtr, subType, annotationPtr, index);
@@ -2350,15 +2543,35 @@ class PdfiumEngine {
2350
2543
  *
2351
2544
  * @param annotationPtr - pointer to an `FPDF_ANNOTATION`
2352
2545
  * @param fallback - colour to use when the PDF stores no tint at all
2353
- * @returns Guaranteed RGBA tuple (never `undefined`)
2546
+ * @returns WebAlphaColor with hex color and opacity (0-1)
2354
2547
  *
2355
2548
  * @private
2356
2549
  */
2357
2550
  resolveAnnotationColor(annotationPtr, fallback = { red: 255, green: 245, blue: 155, alpha: 255 }) {
2358
- return (this.readAnnotationColor(annotationPtr) ?? // 1 – /C entry
2551
+ const pdfColor = this.readAnnotationColor(annotationPtr) ?? // 1 – /C entry
2359
2552
  this.colorFromAppearance(annotationPtr) ?? // 2 – AP stream walk
2360
- fallback // 3 – default
2361
- );
2553
+ fallback; // 3 – default
2554
+ return pdfAlphaColorToWebAlphaColor(pdfColor);
2555
+ }
2556
+ /**
2557
+ * Set the fill/stroke colour for a **Highlight / Underline / StrikeOut / Squiggly** markup annotation.
2558
+ *
2559
+ * @param annotationPtr - pointer to the annotation whose colour is being set
2560
+ * @param webAlphaColor - WebAlphaColor with hex color and opacity (0-1)
2561
+ * @param shouldClearAP - whether to clear the /AP entry
2562
+ * @param which - which colour to set (0 = fill, 1 = stroke)
2563
+ * @returns `true` if the operation was successful
2564
+ *
2565
+ * @private
2566
+ */
2567
+ setAnnotationColor(annotationPtr, webAlphaColor, shouldClearAP = false, which = 0) {
2568
+ const pdfAlphaColor = webAlphaColorToPdfAlphaColor(webAlphaColor);
2569
+ if (shouldClearAP) {
2570
+ // NULL wide-string → remove the /AP entry
2571
+ this.pdfiumModule.FPDFAnnot_SetAP(annotationPtr, AppearanceMode.Normal,
2572
+ /* FPDF_WIDESTRING = */ 0);
2573
+ }
2574
+ return this.pdfiumModule.FPDFAnnot_SetColor(annotationPtr, which, pdfAlphaColor.red & 0xff, pdfAlphaColor.green & 0xff, pdfAlphaColor.blue & 0xff, (pdfAlphaColor.alpha ?? 255) & 0xff);
2362
2575
  }
2363
2576
  /**
2364
2577
  * Read `/QuadPoints` from any annotation and convert each quadrilateral to
@@ -2371,11 +2584,11 @@ class PdfiumEngine {
2371
2584
  *
2372
2585
  * @param page - logical page info object (`PdfPageObject`)
2373
2586
  * @param annotationPtr - pointer to the annotation whose quads are needed
2374
- * @returns Array of `Quad` objects (`[]` if the annotation has no quads)
2587
+ * @returns Array of `Rect` objects (`[]` if the annotation has no quads)
2375
2588
  *
2376
2589
  * @private
2377
2590
  */
2378
- readAnnotationQuads(page, annotationPtr) {
2591
+ getQuadPointsAnno(page, annotationPtr) {
2379
2592
  const quadCount = this.pdfiumModule.FPDFAnnot_CountAttachmentPoints(annotationPtr);
2380
2593
  if (quadCount === 0)
2381
2594
  return [];
@@ -2402,7 +2615,60 @@ class PdfiumEngine {
2402
2615
  }
2403
2616
  this.free(quadPtr);
2404
2617
  }
2405
- return quads;
2618
+ return quads.map(quadToRect);
2619
+ }
2620
+ /**
2621
+ * Set the quadrilaterals for a **Highlight / Underline / StrikeOut / Squiggly** markup annotation.
2622
+ *
2623
+ * @param page - logical page info object (`PdfPageObject`)
2624
+ * @param annotationPtr - pointer to the annotation whose quads are needed
2625
+ * @param rects - array of `Rect` objects (`[]` if the annotation has no quads)
2626
+ * @returns `true` if the operation was successful
2627
+ *
2628
+ * @private
2629
+ */
2630
+ syncQuadPointsAnno(page, annotPtr, rects) {
2631
+ const FS_QUADPOINTSF_SIZE = 8 * 4; // eight floats, 32 bytes
2632
+ const pdf = this.pdfiumModule.pdfium;
2633
+ const count = this.pdfiumModule.FPDFAnnot_CountAttachmentPoints(annotPtr);
2634
+ const buf = this.malloc(FS_QUADPOINTSF_SIZE);
2635
+ /** write one quad into `buf` in annotation space */
2636
+ const writeQuad = (r) => {
2637
+ const q = rectToQuad(r); // TL, TR, BR, BL
2638
+ const p1 = this.convertDevicePointToPagePoint(page, q.p1);
2639
+ const p2 = this.convertDevicePointToPagePoint(page, q.p2);
2640
+ const p3 = this.convertDevicePointToPagePoint(page, q.p3); // BR
2641
+ const p4 = this.convertDevicePointToPagePoint(page, q.p4); // BL
2642
+ // PDF QuadPoints order: BL, BR, TL, TR (bottom-left, bottom-right, top-left, top-right)
2643
+ pdf.setValue(buf + 0, p1.x, 'float'); // BL (bottom-left)
2644
+ pdf.setValue(buf + 4, p1.y, 'float');
2645
+ pdf.setValue(buf + 8, p2.x, 'float'); // BR (bottom-right)
2646
+ pdf.setValue(buf + 12, p2.y, 'float');
2647
+ pdf.setValue(buf + 16, p4.x, 'float'); // TL (top-left)
2648
+ pdf.setValue(buf + 20, p4.y, 'float');
2649
+ pdf.setValue(buf + 24, p3.x, 'float'); // TR (top-right)
2650
+ pdf.setValue(buf + 28, p3.y, 'float');
2651
+ };
2652
+ /* ----------------------------------------------------------------------- */
2653
+ /* 1. overwrite the quads that already exist */
2654
+ const min = Math.min(count, rects.length);
2655
+ for (let i = 0; i < min; i++) {
2656
+ writeQuad(rects[i]);
2657
+ if (!this.pdfiumModule.FPDFAnnot_SetAttachmentPoints(annotPtr, i, buf)) {
2658
+ this.free(buf);
2659
+ return false;
2660
+ }
2661
+ }
2662
+ /* 2. append new quads if rects.length > count */
2663
+ for (let i = count; i < rects.length; i++) {
2664
+ writeQuad(rects[i]);
2665
+ if (!this.pdfiumModule.FPDFAnnot_AppendAttachmentPoints(annotPtr, buf)) {
2666
+ this.free(buf);
2667
+ return false;
2668
+ }
2669
+ }
2670
+ this.free(buf);
2671
+ return true;
2406
2672
  }
2407
2673
  /**
2408
2674
  * Read pdf text annotation
@@ -2415,30 +2681,23 @@ class PdfiumEngine {
2415
2681
  * @private
2416
2682
  */
2417
2683
  readPdfTextAnno(page, pagePtr, annotationPtr, index) {
2418
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2419
2684
  const annoRect = this.readPageAnnoRect(annotationPtr);
2420
2685
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, annoRect);
2421
2686
  const author = this.getAnnotString(annotationPtr, 'T');
2422
2687
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2423
- const modified = this.toIsoDate(modifiedRaw);
2688
+ const modified = pdfDateToDate(modifiedRaw);
2424
2689
  const contents = this.getAnnotString(annotationPtr, 'Contents') || '';
2425
2690
  const state = this.getAnnotString(annotationPtr, 'State');
2426
2691
  const stateModel = this.getAnnotString(annotationPtr, 'StateModel');
2427
- const color = this.resolveAnnotationColor(annotationPtr);
2692
+ const webAlphaColor = this.resolveAnnotationColor(annotationPtr);
2428
2693
  const inReplyToId = this.getInReplyToId(pagePtr, annotationPtr);
2429
- const popup = !inReplyToId
2430
- ? this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index)
2431
- : undefined;
2432
2694
  return {
2433
- status: PdfAnnotationObjectStatus.Committed,
2434
2695
  pageIndex: page.index,
2435
2696
  id: index,
2436
2697
  type: PdfAnnotationSubtype.TEXT,
2437
2698
  contents,
2438
- color,
2699
+ ...webAlphaColor,
2439
2700
  rect,
2440
- popup,
2441
- appearances,
2442
2701
  inReplyToId,
2443
2702
  author,
2444
2703
  modified,
@@ -2457,16 +2716,13 @@ class PdfiumEngine {
2457
2716
  * @private
2458
2717
  */
2459
2718
  readPdfFreeTextAnno(page, pagePtr, annotationPtr, index) {
2460
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2461
2719
  const annoRect = this.readPageAnnoRect(annotationPtr);
2462
2720
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, annoRect);
2463
2721
  const contents = this.getAnnotString(annotationPtr, 'Contents') || '';
2464
2722
  const author = this.getAnnotString(annotationPtr, 'T');
2465
2723
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2466
- const modified = this.toIsoDate(modifiedRaw);
2467
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2724
+ const modified = pdfDateToDate(modifiedRaw);
2468
2725
  return {
2469
- status: PdfAnnotationObjectStatus.Committed,
2470
2726
  pageIndex: page.index,
2471
2727
  id: index,
2472
2728
  type: PdfAnnotationSubtype.FREETEXT,
@@ -2474,8 +2730,6 @@ class PdfiumEngine {
2474
2730
  author,
2475
2731
  modified,
2476
2732
  rect,
2477
- popup,
2478
- appearances,
2479
2733
  };
2480
2734
  }
2481
2735
  /**
@@ -2495,13 +2749,12 @@ class PdfiumEngine {
2495
2749
  if (!linkPtr) {
2496
2750
  return;
2497
2751
  }
2498
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2499
2752
  const annoRect = this.readPageAnnoRect(annotationPtr);
2500
2753
  const { left, top, right, bottom } = annoRect;
2501
2754
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, annoRect);
2502
2755
  const author = this.getAnnotString(annotationPtr, 'T');
2503
2756
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2504
- const modified = this.toIsoDate(modifiedRaw);
2757
+ const modified = pdfDateToDate(modifiedRaw);
2505
2758
  const utf16Length = this.pdfiumModule.FPDFText_GetBoundedText(textPagePtr, left, top, right, bottom, 0, 0);
2506
2759
  const bytesCount = (utf16Length + 1) * 2; // include NIL
2507
2760
  const textBufferPtr = this.malloc(bytesCount);
@@ -2513,17 +2766,13 @@ class PdfiumEngine {
2513
2766
  }, () => {
2514
2767
  return this.pdfiumModule.FPDFLink_GetDest(docPtr, linkPtr);
2515
2768
  });
2516
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2517
2769
  return {
2518
- status: PdfAnnotationObjectStatus.Committed,
2519
2770
  pageIndex: page.index,
2520
2771
  id: index,
2521
2772
  type: PdfAnnotationSubtype.LINK,
2522
2773
  text,
2523
2774
  target,
2524
2775
  rect,
2525
- popup,
2526
- appearances,
2527
2776
  author,
2528
2777
  modified,
2529
2778
  };
@@ -2540,23 +2789,18 @@ class PdfiumEngine {
2540
2789
  * @private
2541
2790
  */
2542
2791
  readPdfWidgetAnno(page, pagePtr, annotationPtr, formHandle, index) {
2543
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2544
2792
  const pageRect = this.readPageAnnoRect(annotationPtr);
2545
2793
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2546
2794
  const author = this.getAnnotString(annotationPtr, 'T');
2547
2795
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2548
- const modified = this.toIsoDate(modifiedRaw);
2549
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2796
+ const modified = pdfDateToDate(modifiedRaw);
2550
2797
  const field = this.readPdfWidgetAnnoField(formHandle, annotationPtr);
2551
2798
  return {
2552
- status: PdfAnnotationObjectStatus.Committed,
2553
2799
  pageIndex: page.index,
2554
2800
  id: index,
2555
2801
  type: PdfAnnotationSubtype.WIDGET,
2556
2802
  rect,
2557
2803
  field,
2558
- popup,
2559
- appearances,
2560
2804
  author,
2561
2805
  modified,
2562
2806
  };
@@ -2572,21 +2816,16 @@ class PdfiumEngine {
2572
2816
  * @private
2573
2817
  */
2574
2818
  readPdfFileAttachmentAnno(page, pagePtr, annotationPtr, index) {
2575
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2576
2819
  const pageRect = this.readPageAnnoRect(annotationPtr);
2577
2820
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2578
2821
  const author = this.getAnnotString(annotationPtr, 'T');
2579
2822
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2580
- const modified = this.toIsoDate(modifiedRaw);
2581
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2823
+ const modified = pdfDateToDate(modifiedRaw);
2582
2824
  return {
2583
- status: PdfAnnotationObjectStatus.Committed,
2584
2825
  pageIndex: page.index,
2585
2826
  id: index,
2586
2827
  type: PdfAnnotationSubtype.FILEATTACHMENT,
2587
2828
  rect,
2588
- popup,
2589
- appearances,
2590
2829
  author,
2591
2830
  modified,
2592
2831
  };
@@ -2602,13 +2841,11 @@ class PdfiumEngine {
2602
2841
  * @private
2603
2842
  */
2604
2843
  readPdfInkAnno(page, pagePtr, annotationPtr, index) {
2605
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2606
2844
  const pageRect = this.readPageAnnoRect(annotationPtr);
2607
2845
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2608
2846
  const author = this.getAnnotString(annotationPtr, 'T');
2609
2847
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2610
- const modified = this.toIsoDate(modifiedRaw);
2611
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2848
+ const modified = pdfDateToDate(modifiedRaw);
2612
2849
  const inkList = [];
2613
2850
  const count = this.pdfiumModule.FPDFAnnot_GetInkListCount(annotationPtr);
2614
2851
  for (let i = 0; i < count; i++) {
@@ -2632,14 +2869,11 @@ class PdfiumEngine {
2632
2869
  inkList.push({ points });
2633
2870
  }
2634
2871
  return {
2635
- status: PdfAnnotationObjectStatus.Committed,
2636
2872
  pageIndex: page.index,
2637
2873
  id: index,
2638
2874
  type: PdfAnnotationSubtype.INK,
2639
2875
  rect,
2640
- popup,
2641
2876
  inkList,
2642
- appearances,
2643
2877
  author,
2644
2878
  modified,
2645
2879
  };
@@ -2655,23 +2889,18 @@ class PdfiumEngine {
2655
2889
  * @private
2656
2890
  */
2657
2891
  readPdfPolygonAnno(page, pagePtr, annotationPtr, index) {
2658
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2659
2892
  const pageRect = this.readPageAnnoRect(annotationPtr);
2660
2893
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2661
2894
  const author = this.getAnnotString(annotationPtr, 'T');
2662
2895
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2663
- const modified = this.toIsoDate(modifiedRaw);
2664
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2896
+ const modified = pdfDateToDate(modifiedRaw);
2665
2897
  const vertices = this.readPdfAnnoVertices(page, pagePtr, annotationPtr);
2666
2898
  return {
2667
- status: PdfAnnotationObjectStatus.Committed,
2668
2899
  pageIndex: page.index,
2669
2900
  id: index,
2670
2901
  type: PdfAnnotationSubtype.POLYGON,
2671
2902
  rect,
2672
- popup,
2673
2903
  vertices,
2674
- appearances,
2675
2904
  author,
2676
2905
  modified,
2677
2906
  };
@@ -2687,23 +2916,18 @@ class PdfiumEngine {
2687
2916
  * @private
2688
2917
  */
2689
2918
  readPdfPolylineAnno(page, pagePtr, annotationPtr, index) {
2690
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2691
2919
  const pageRect = this.readPageAnnoRect(annotationPtr);
2692
2920
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2693
2921
  const author = this.getAnnotString(annotationPtr, 'T');
2694
2922
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2695
- const modified = this.toIsoDate(modifiedRaw);
2696
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2923
+ const modified = pdfDateToDate(modifiedRaw);
2697
2924
  const vertices = this.readPdfAnnoVertices(page, pagePtr, annotationPtr);
2698
2925
  return {
2699
- status: PdfAnnotationObjectStatus.Committed,
2700
2926
  pageIndex: page.index,
2701
2927
  id: index,
2702
2928
  type: PdfAnnotationSubtype.POLYLINE,
2703
2929
  rect,
2704
- popup,
2705
2930
  vertices,
2706
- appearances,
2707
2931
  author,
2708
2932
  modified,
2709
2933
  };
@@ -2719,13 +2943,11 @@ class PdfiumEngine {
2719
2943
  * @private
2720
2944
  */
2721
2945
  readPdfLineAnno(page, pagePtr, annotationPtr, index) {
2722
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2723
2946
  const pageRect = this.readPageAnnoRect(annotationPtr);
2724
2947
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2725
2948
  const author = this.getAnnotString(annotationPtr, 'T');
2726
2949
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2727
- const modified = this.toIsoDate(modifiedRaw);
2728
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2950
+ const modified = pdfDateToDate(modifiedRaw);
2729
2951
  const startPointPtr = this.malloc(8);
2730
2952
  const endPointPtr = this.malloc(8);
2731
2953
  this.pdfiumModule.FPDFAnnot_GetLine(annotationPtr, startPointPtr, endPointPtr);
@@ -2744,15 +2966,12 @@ class PdfiumEngine {
2744
2966
  this.free(startPointPtr);
2745
2967
  this.free(endPointPtr);
2746
2968
  return {
2747
- status: PdfAnnotationObjectStatus.Committed,
2748
2969
  pageIndex: page.index,
2749
2970
  id: index,
2750
2971
  type: PdfAnnotationSubtype.LINE,
2751
2972
  rect,
2752
- popup,
2753
2973
  startPoint,
2754
2974
  endPoint,
2755
- appearances,
2756
2975
  author,
2757
2976
  modified,
2758
2977
  };
@@ -2768,25 +2987,22 @@ class PdfiumEngine {
2768
2987
  * @private
2769
2988
  */
2770
2989
  readPdfHighlightAnno(page, pagePtr, annotationPtr, index) {
2771
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2772
2990
  const pageRect = this.readPageAnnoRect(annotationPtr);
2773
2991
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2774
- const quads = this.readAnnotationQuads(page, annotationPtr);
2775
- const color = this.resolveAnnotationColor(annotationPtr);
2776
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2992
+ const segmentRects = this.getQuadPointsAnno(page, annotationPtr);
2993
+ const webAlphaColor = this.resolveAnnotationColor(annotationPtr);
2777
2994
  const author = this.getAnnotString(annotationPtr, 'T');
2778
2995
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2779
- const modified = this.toIsoDate(modifiedRaw);
2996
+ const modified = pdfDateToDate(modifiedRaw);
2997
+ const contents = this.getAnnotString(annotationPtr, 'Contents') || '';
2780
2998
  return {
2781
- status: PdfAnnotationObjectStatus.Committed,
2782
2999
  pageIndex: page.index,
2783
3000
  id: index,
2784
3001
  type: PdfAnnotationSubtype.HIGHLIGHT,
2785
3002
  rect,
2786
- popup,
2787
- appearances,
2788
- segmentRects: quads.map(quadToRect),
2789
- color,
3003
+ contents,
3004
+ segmentRects,
3005
+ ...webAlphaColor,
2790
3006
  author,
2791
3007
  modified,
2792
3008
  };
@@ -2802,21 +3018,22 @@ class PdfiumEngine {
2802
3018
  * @private
2803
3019
  */
2804
3020
  readPdfUnderlineAnno(page, pagePtr, annotationPtr, index) {
2805
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2806
3021
  const pageRect = this.readPageAnnoRect(annotationPtr);
2807
3022
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2808
3023
  const author = this.getAnnotString(annotationPtr, 'T');
2809
3024
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2810
- const modified = this.toIsoDate(modifiedRaw);
2811
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3025
+ const modified = pdfDateToDate(modifiedRaw);
3026
+ const segmentRects = this.getQuadPointsAnno(page, annotationPtr);
3027
+ const contents = this.getAnnotString(annotationPtr, 'Contents') || '';
3028
+ const webAlphaColor = this.resolveAnnotationColor(annotationPtr);
2812
3029
  return {
2813
- status: PdfAnnotationObjectStatus.Committed,
2814
3030
  pageIndex: page.index,
2815
3031
  id: index,
2816
3032
  type: PdfAnnotationSubtype.UNDERLINE,
2817
3033
  rect,
2818
- popup,
2819
- appearances,
3034
+ contents,
3035
+ segmentRects,
3036
+ ...webAlphaColor,
2820
3037
  author,
2821
3038
  modified,
2822
3039
  };
@@ -2832,21 +3049,22 @@ class PdfiumEngine {
2832
3049
  * @private
2833
3050
  */
2834
3051
  readPdfStrikeOutAnno(page, pagePtr, annotationPtr, index) {
2835
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2836
3052
  const pageRect = this.readPageAnnoRect(annotationPtr);
2837
3053
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2838
3054
  const author = this.getAnnotString(annotationPtr, 'T');
2839
3055
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2840
- const modified = this.toIsoDate(modifiedRaw);
2841
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3056
+ const modified = pdfDateToDate(modifiedRaw);
3057
+ const segmentRects = this.getQuadPointsAnno(page, annotationPtr);
3058
+ const contents = this.getAnnotString(annotationPtr, 'Contents') || '';
3059
+ const webAlphaColor = this.resolveAnnotationColor(annotationPtr);
2842
3060
  return {
2843
- status: PdfAnnotationObjectStatus.Committed,
2844
3061
  pageIndex: page.index,
2845
3062
  id: index,
2846
3063
  type: PdfAnnotationSubtype.STRIKEOUT,
2847
3064
  rect,
2848
- popup,
2849
- appearances,
3065
+ contents,
3066
+ segmentRects,
3067
+ ...webAlphaColor,
2850
3068
  author,
2851
3069
  modified,
2852
3070
  };
@@ -2862,21 +3080,22 @@ class PdfiumEngine {
2862
3080
  * @private
2863
3081
  */
2864
3082
  readPdfSquigglyAnno(page, pagePtr, annotationPtr, index) {
2865
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2866
3083
  const pageRect = this.readPageAnnoRect(annotationPtr);
2867
3084
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2868
3085
  const author = this.getAnnotString(annotationPtr, 'T');
2869
3086
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2870
- const modified = this.toIsoDate(modifiedRaw);
2871
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3087
+ const modified = pdfDateToDate(modifiedRaw);
3088
+ const segmentRects = this.getQuadPointsAnno(page, annotationPtr);
3089
+ const contents = this.getAnnotString(annotationPtr, 'Contents') || '';
3090
+ const webAlphaColor = this.resolveAnnotationColor(annotationPtr);
2872
3091
  return {
2873
- status: PdfAnnotationObjectStatus.Committed,
2874
3092
  pageIndex: page.index,
2875
3093
  id: index,
2876
3094
  type: PdfAnnotationSubtype.SQUIGGLY,
2877
3095
  rect,
2878
- popup,
2879
- appearances,
3096
+ contents,
3097
+ segmentRects,
3098
+ ...webAlphaColor,
2880
3099
  author,
2881
3100
  modified,
2882
3101
  };
@@ -2892,21 +3111,16 @@ class PdfiumEngine {
2892
3111
  * @private
2893
3112
  */
2894
3113
  readPdfCaretAnno(page, pagePtr, annotationPtr, index) {
2895
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2896
3114
  const pageRect = this.readPageAnnoRect(annotationPtr);
2897
3115
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2898
3116
  const author = this.getAnnotString(annotationPtr, 'T');
2899
3117
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2900
- const modified = this.toIsoDate(modifiedRaw);
2901
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3118
+ const modified = pdfDateToDate(modifiedRaw);
2902
3119
  return {
2903
- status: PdfAnnotationObjectStatus.Committed,
2904
3120
  pageIndex: page.index,
2905
3121
  id: index,
2906
3122
  type: PdfAnnotationSubtype.CARET,
2907
3123
  rect,
2908
- popup,
2909
- appearances,
2910
3124
  author,
2911
3125
  modified,
2912
3126
  };
@@ -2923,13 +3137,11 @@ class PdfiumEngine {
2923
3137
  * @private
2924
3138
  */
2925
3139
  readPdfStampAnno(docPtr, page, pagePtr, annotationPtr, index) {
2926
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2927
3140
  const pageRect = this.readPageAnnoRect(annotationPtr);
2928
3141
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2929
3142
  const author = this.getAnnotString(annotationPtr, 'T');
2930
3143
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
2931
- const modified = this.toIsoDate(modifiedRaw);
2932
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3144
+ const modified = pdfDateToDate(modifiedRaw);
2933
3145
  const contents = [];
2934
3146
  const objectCount = this.pdfiumModule.FPDFAnnot_GetObjectCount(annotationPtr);
2935
3147
  for (let i = 0; i < objectCount; i++) {
@@ -2940,14 +3152,11 @@ class PdfiumEngine {
2940
3152
  }
2941
3153
  }
2942
3154
  return {
2943
- status: PdfAnnotationObjectStatus.Committed,
2944
3155
  pageIndex: page.index,
2945
3156
  id: index,
2946
3157
  type: PdfAnnotationSubtype.STAMP,
2947
3158
  rect,
2948
- popup,
2949
3159
  contents,
2950
- appearances,
2951
3160
  author,
2952
3161
  modified,
2953
3162
  };
@@ -3127,21 +3336,16 @@ class PdfiumEngine {
3127
3336
  * @private
3128
3337
  */
3129
3338
  readPdfCircleAnno(page, pagePtr, annotationPtr, index) {
3130
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3131
3339
  const pageRect = this.readPageAnnoRect(annotationPtr);
3132
3340
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3133
3341
  const author = this.getAnnotString(annotationPtr, 'T');
3134
3342
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
3135
- const modified = this.toIsoDate(modifiedRaw);
3136
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3343
+ const modified = pdfDateToDate(modifiedRaw);
3137
3344
  return {
3138
- status: PdfAnnotationObjectStatus.Committed,
3139
3345
  pageIndex: page.index,
3140
3346
  id: index,
3141
3347
  type: PdfAnnotationSubtype.CIRCLE,
3142
3348
  rect,
3143
- popup,
3144
- appearances,
3145
3349
  author,
3146
3350
  modified,
3147
3351
  };
@@ -3157,21 +3361,16 @@ class PdfiumEngine {
3157
3361
  * @private
3158
3362
  */
3159
3363
  readPdfSquareAnno(page, pagePtr, annotationPtr, index) {
3160
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3161
3364
  const pageRect = this.readPageAnnoRect(annotationPtr);
3162
3365
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3163
3366
  const author = this.getAnnotString(annotationPtr, 'T');
3164
3367
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
3165
- const modified = this.toIsoDate(modifiedRaw);
3166
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3368
+ const modified = pdfDateToDate(modifiedRaw);
3167
3369
  return {
3168
- status: PdfAnnotationObjectStatus.Committed,
3169
3370
  pageIndex: page.index,
3170
3371
  id: index,
3171
3372
  type: PdfAnnotationSubtype.SQUARE,
3172
3373
  rect,
3173
- popup,
3174
- appearances,
3175
3374
  author,
3176
3375
  modified,
3177
3376
  };
@@ -3188,21 +3387,16 @@ class PdfiumEngine {
3188
3387
  * @private
3189
3388
  */
3190
3389
  readPdfAnno(page, pagePtr, type, annotationPtr, index) {
3191
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3192
3390
  const pageRect = this.readPageAnnoRect(annotationPtr);
3193
3391
  const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3194
3392
  const author = this.getAnnotString(annotationPtr, 'T');
3195
3393
  const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
3196
- const modified = this.toIsoDate(modifiedRaw);
3197
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3394
+ const modified = pdfDateToDate(modifiedRaw);
3198
3395
  return {
3199
- status: PdfAnnotationObjectStatus.Committed,
3200
3396
  pageIndex: page.index,
3201
3397
  id: index,
3202
3398
  type,
3203
3399
  rect,
3204
- popup,
3205
- appearances,
3206
3400
  author,
3207
3401
  modified,
3208
3402
  };
@@ -3224,25 +3418,6 @@ class PdfiumEngine {
3224
3418
  const idx = this.pdfiumModule.FPDFPage_GetAnnotIndex(pagePtr, parentPtr);
3225
3419
  return idx >= 0 ? idx : undefined;
3226
3420
  }
3227
- /**
3228
- * Parse a PDF date string **D:YYYYMMDDHHmmSSOHH'mm'** to ISO-8601.
3229
- *
3230
- * Returns `undefined` if the input is malformed.
3231
- *
3232
- * @private
3233
- */
3234
- toIsoDate(pdfDate) {
3235
- if (!pdfDate?.startsWith('D:'))
3236
- return;
3237
- // Minimal parse – ignore timezone for brevity
3238
- const y = pdfDate.substring(2, 6);
3239
- const m = pdfDate.substring(6, 8) || '01';
3240
- const d = pdfDate.substring(8, 10) || '01';
3241
- const H = pdfDate.substring(10, 12) || '00';
3242
- const M = pdfDate.substring(12, 14) || '00';
3243
- const S = pdfDate.substring(14, 16) || '00';
3244
- return `${y}-${m}-${d}T${H}:${M}:${S}`;
3245
- }
3246
3421
  /**
3247
3422
  * Fetch a string value (`/T`, `/M`, `/State`, …) from an annotation.
3248
3423
  *
@@ -3262,41 +3437,19 @@ class PdfiumEngine {
3262
3437
  return value || undefined;
3263
3438
  }
3264
3439
  /**
3265
- * Read linked popup of pdf annotation
3266
- * @param page - pdf page infor
3267
- * @param pagePtr - pointer to pdf page object
3268
- * @param annotationPtr - pointer to pdf annotation
3269
- * @param index - index of annotation in the pdf page
3270
- * @returns pdf popup linked to annotation
3440
+ * Set a string value (`/T`, `/M`, `/State`, …) to an annotation.
3441
+ *
3442
+ * @returns `true` if the operation was successful
3271
3443
  *
3272
3444
  * @private
3273
3445
  */
3274
- readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index) {
3275
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3276
- const popupAnnotationPtr = this.pdfiumModule.FPDFAnnot_GetLinkedAnnot(annotationPtr, 'Popup');
3277
- if (!popupAnnotationPtr) {
3278
- return;
3279
- }
3280
- const pageRect = this.readPageAnnoRect(popupAnnotationPtr);
3281
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3282
- const author = this.getAnnotString(annotationPtr, 'T');
3283
- const modifiedRaw = this.getAnnotString(annotationPtr, 'M');
3284
- const modified = this.toIsoDate(modifiedRaw);
3285
- const contents = this.getAnnotString(annotationPtr, 'Contents') || '';
3286
- const open = this.getAnnotString(annotationPtr, 'Open') || 'false';
3287
- this.pdfiumModule.FPDFPage_CloseAnnot(popupAnnotationPtr);
3288
- return {
3289
- status: PdfAnnotationObjectStatus.Committed,
3290
- pageIndex: page.index,
3291
- id: index,
3292
- type: PdfAnnotationSubtype.POPUP,
3293
- rect,
3294
- contents,
3295
- open: open === 'true',
3296
- appearances,
3297
- author,
3298
- modified,
3299
- };
3446
+ setAnnotString(annotationPtr, key, value) {
3447
+ const bytes = 2 * (value.length + 1);
3448
+ const ptr = this.malloc(bytes);
3449
+ this.pdfiumModule.pdfium.stringToUTF16(value, ptr, bytes);
3450
+ const ok = this.pdfiumModule.FPDFAnnot_SetStringValue(annotationPtr, key, ptr);
3451
+ this.free(ptr);
3452
+ return ok;
3300
3453
  }
3301
3454
  /**
3302
3455
  * Read vertices of pdf annotation
@@ -3737,6 +3890,69 @@ class PdfiumEngine {
3737
3890
  this.free(bufferPtr);
3738
3891
  return ap;
3739
3892
  }
3893
+ /**
3894
+ * Change the visible colour (and opacity) of an existing annotation.
3895
+ *
3896
+ * For markup annotations (highlight / underline / strikeout / squiggly) we
3897
+ * first clear the AP dictionary entry, otherwise the stored appearance stream
3898
+ * will override the new tint. For all other sub-types we keep the existing
3899
+ * AP so custom artwork isn't lost.
3900
+ *
3901
+ * @param doc logical document object
3902
+ * @param page logical page object
3903
+ * @param annotation the annotation we want to recolour
3904
+ * @param colour RGBA tuple (0-255 per channel)
3905
+ * @param which 0 = stroke/fill colour (PDFium's "colourType" param)
3906
+ *
3907
+ * @returns `true` when the operation succeeded
3908
+ */
3909
+ updateAnnotationColor(doc, page, annotation, color, which = 0) {
3910
+ this.logger.debug(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor', doc, page, annotation, color, which);
3911
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor', 'Begin', doc.id);
3912
+ const task = PdfTaskHelper.create();
3913
+ try {
3914
+ /* 1 ── sanity & native handles ────────────────────────────────────────── */
3915
+ const ctx = this.cache.getContext(doc.id);
3916
+ if (!ctx) {
3917
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor', 'End', doc.id);
3918
+ this.logger.warn(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor: doc closed');
3919
+ task.resolve(false);
3920
+ return task;
3921
+ }
3922
+ const pageCtx = ctx.acquirePage(page.index);
3923
+ const annotPtr = this.pdfiumModule.FPDFPage_GetAnnot(pageCtx.pagePtr, annotation.id);
3924
+ if (!annotPtr) {
3925
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor', 'End', doc.id);
3926
+ this.logger.warn(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor: annot not found');
3927
+ pageCtx.release();
3928
+ task.resolve(false);
3929
+ return task;
3930
+ }
3931
+ /* 2 ── wipe AP if it's a simple markup annotation ─────────────────────── */
3932
+ const shouldClearAP = annotation.type === PdfAnnotationSubtype.HIGHLIGHT ||
3933
+ annotation.type === PdfAnnotationSubtype.UNDERLINE ||
3934
+ annotation.type === PdfAnnotationSubtype.STRIKEOUT ||
3935
+ annotation.type === PdfAnnotationSubtype.SQUIGGLY;
3936
+ const ok = this.setAnnotationColor(annotPtr, color, shouldClearAP, which);
3937
+ /* 4 ── regenerate appearance & clean-up ───────────────────────────────── */
3938
+ if (ok) {
3939
+ this.pdfiumModule.FPDFPage_GenerateContent(pageCtx.pagePtr);
3940
+ }
3941
+ this.pdfiumModule.FPDFPage_CloseAnnot(annotPtr);
3942
+ pageCtx.release();
3943
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor', 'End', doc.id);
3944
+ task.resolve(!!ok);
3945
+ }
3946
+ catch (error) {
3947
+ this.logger.perf(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor', 'End', doc.id);
3948
+ this.logger.error(LOG_SOURCE, LOG_CATEGORY, 'setAnnotationColor: error', error);
3949
+ task.reject({
3950
+ code: PdfErrorCode.Unknown,
3951
+ message: `Failed to set annotation color: ${error instanceof Error ? error.message : String(error)}`,
3952
+ });
3953
+ }
3954
+ return task;
3955
+ }
3740
3956
  /**
3741
3957
  * Set the rect of specified annotation
3742
3958
  * @param page - page info that the annotation is belonged to
@@ -4024,11 +4240,11 @@ class PdfiumEngine {
4024
4240
  }
4025
4241
  }
4026
4242
 
4027
- async function createPdfiumEngine(wasmUrl) {
4243
+ async function createPdfiumEngine(wasmUrl, logger) {
4028
4244
  const response = await fetch(wasmUrl);
4029
4245
  const wasmBinary = await response.arrayBuffer();
4030
4246
  const wasmModule = await init({ wasmBinary });
4031
- return new PdfiumEngine(wasmModule);
4247
+ return new PdfiumEngine(wasmModule, logger);
4032
4248
  }
4033
4249
 
4034
4250
  export { createPdfiumEngine };