From ae5828c968482fcbac7a7fd0dcf62215bbd8c955 Mon Sep 17 00:00:00 2001
From: Calixte Denizet <calixte.denizet@gmail.com>
Date: Mon, 4 Dec 2023 20:49:15 +0100
Subject: [PATCH] [Editor] Avoid conflicts between new persistent refs and the
 ones created when saving (bug 1865341)

When a pdf as a FreeText without appearance, we use a fake font in order to render it
and that leads to create few new refs for the font.
But then when we're saving, we create some new refs which start at the same number
as the previous created ones.
Consequently, when saving we're using some wrong objects (like a font) to check if
we're able to render the newly added FreeText.
In order to fix this bug, we just remove the persistent refs (which are only used
when rendering/printing) during the saving.
---
 src/core/font_substitutions.js |   4 ++++
 src/core/xref.js               |  20 ++++++++++++++++++++
 test/driver.js                 |   6 ++++++
 test/pdfs/.gitignore           |   1 +
 test/pdfs/bug1865341.pdf       | Bin 0 -> 5618 bytes
 test/test_manifest.json        |  21 +++++++++++++++++++++
 6 files changed, 52 insertions(+)
 create mode 100755 test/pdfs/bug1865341.pdf

diff --git a/src/core/font_substitutions.js b/src/core/font_substitutions.js
index 9ad26b707..c22976ba2 100644
--- a/src/core/font_substitutions.js
+++ b/src/core/font_substitutions.js
@@ -419,6 +419,10 @@ function getFontSubstitution(
   baseFontName,
   standardFontName
 ) {
+  if (baseFontName.startsWith("InvalidPDFjsFont_")) {
+    return null;
+  }
+
   // It's possible to have a font name with spaces, commas or dashes, hence we
   // just replace them by a dash.
   baseFontName = normalizeFontName(baseFontName);
diff --git a/src/core/xref.js b/src/core/xref.js
index 6c32f6ee4..313f18a71 100644
--- a/src/core/xref.js
+++ b/src/core/xref.js
@@ -44,6 +44,7 @@ class XRef {
     this._pendingRefs = new RefSet();
     this._newPersistentRefNum = null;
     this._newTemporaryRefNum = null;
+    this._persistentRefsCache = null;
   }
 
   getNewPersistentRef(obj) {
@@ -63,6 +64,19 @@ class XRef {
     // stream.
     if (this._newTemporaryRefNum === null) {
       this._newTemporaryRefNum = this.entries.length || 1;
+      if (this._newPersistentRefNum) {
+        this._persistentRefsCache = new Map();
+        for (
+          let i = this._newTemporaryRefNum;
+          i < this._newPersistentRefNum;
+          i++
+        ) {
+          // We *temporarily* clear the cache, see `resetNewTemporaryRef` below,
+          // to avoid any conflict with the refs created during saving.
+          this._persistentRefsCache.set(i, this._cacheMap.get(i));
+          this._cacheMap.delete(i);
+        }
+      }
     }
     return Ref.get(this._newTemporaryRefNum++, 0);
   }
@@ -70,6 +84,12 @@ class XRef {
   resetNewTemporaryRef() {
     // Called once saving is finished.
     this._newTemporaryRefNum = null;
+    if (this._persistentRefsCache) {
+      for (const [num, obj] of this._persistentRefsCache) {
+        this._cacheMap.set(num, obj);
+      }
+    }
+    this._persistentRefsCache = null;
   }
 
   setStartXRef(startXRef) {
diff --git a/test/driver.js b/test/driver.js
index 6a0fa4513..6e4ae0393 100644
--- a/test/driver.js
+++ b/test/driver.js
@@ -656,6 +656,12 @@ class Driver {
             if (!task.annotationStorage) {
               throw new Error("Missing `annotationStorage` entry.");
             }
+            if (task.loadAnnotations) {
+              for (let num = 1; num <= doc.numPages; num++) {
+                const page = await doc.getPage(num);
+                await page.getAnnotations({ intent: "display" });
+              }
+            }
             doc.annotationStorage.setAll(task.annotationStorage);
 
             const data = await doc.saveDocument();
diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore
index d63f3d7d9..ce8b9fa1b 100644
--- a/test/pdfs/.gitignore
+++ b/test/pdfs/.gitignore
@@ -619,3 +619,4 @@
 !issue17069.pdf
 !issue17215.pdf
 !bug1863910.pdf
+!bug1865341.pdf
diff --git a/test/pdfs/bug1865341.pdf b/test/pdfs/bug1865341.pdf
new file mode 100755
index 0000000000000000000000000000000000000000..0574cbe010943930f8db5fe842a4dce584d4496f
GIT binary patch
literal 5618
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
P?V|OC1UcMb6Mg>yhzC25

literal 0
HcmV?d00001

diff --git a/test/test_manifest.json b/test/test_manifest.json
index de05cc03e..375ef75d7 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -8381,5 +8381,26 @@
       "link": true,
       "type": "eq",
       "annotations": true
+   },
+   {
+      "id": "bug1865341",
+      "file": "pdfs/bug1865341.pdf",
+      "md5": "e241b6d1dc493df73e77e3eeabc71721",
+      "rounds": 1,
+      "type": "eq",
+      "save": true,
+      "print": true,
+      "loadAnnotations": true,
+      "annotationStorage": {
+         "pdfjs_internal_editor_0": {
+            "annotationType": 3,
+            "color": [255, 0, 0],
+            "fontSize": 10,
+            "value": "Załącznik",
+            "pageIndex": 0,
+            "rect": [ 238, 629, 287, 649],
+            "rotation": 0
+         }
+      }
    }
 ]