Repository: Hopding/pdf-lib (ts)

Issue:
Unhandled bounds
While testing filling forms I noticed a few cases where the text had really small font size.
It turned out that the issue was a negative bound, which apparently is valid.
So likely we need `Math.abs(bounds.height)` here:
https://github.com/Hopding/pdf-lib/blob/581d87e2a12628df94161df72c9d36e661eb7e2c/src/api/text/layout.ts#L50

Pull request:
Handle negative bounds
Fixes #680.

Patch:
diff --git a/src/api/text/layout.ts b/src/api/text/layout.ts
index 09327e7..48a427c 100644
--- a/src/api/text/layout.ts
+++ b/src/api/text/layout.ts
@@ -47,7 +47,7 @@ const computeFontSize = (
     const height = font.heightAtSize(fontSize);
     const lineHeight = height + height * 0.2;
     const totalHeight = lines.length * lineHeight;
-    if (totalHeight > bounds.height) return fontSize - 1;
+    if (totalHeight > Math.abs(bounds.height)) return fontSize - 1;
 
     fontSize += 1;
   }
diff --git a/src/core/acroform/PDFAcroField.ts b/src/core/acroform/PDFAcroField.ts
index 49868db..716aba0 100644
--- a/src/core/acroform/PDFAcroField.ts
+++ b/src/core/acroform/PDFAcroField.ts
@@ -87,7 +87,13 @@ class PDFAcroField {
   }
 
   getDefaultAppearance(): string | undefined {
-    return this.DA()?.asString() ?? '';
+    const DA = this.DA();
+
+    if (DA instanceof PDFHexString) {
+      return DA.decodeText();
+    }
+
+    return DA?.asString();
   }
 
   getFlags(): number {
diff --git a/src/core/annotation/PDFWidgetAnnotation.ts b/src/core/annotation/PDFWidgetAnnotation.ts
index e91b8d4..e600492 100644
--- a/src/core/annotation/PDFWidgetAnnotation.ts
+++ b/src/core/annotation/PDFWidgetAnnotation.ts
@@ -45,7 +45,13 @@ class PDFWidgetAnnotation extends PDFAnnotation {
   }
 
   getDefaultAppearance(): string | undefined {
-    return this.DA()?.asString() ?? '';
+    const DA = this.DA();
+
+    if (DA instanceof PDFHexString) {
+      return DA.decodeText();
+    }
+
+    return DA?.asString();
   }
 
   getAppearanceCharacteristics(): AppearanceCharacteristics | undefined {


Test patch:
diff --git a/tests/core/acroform/PDFAcroField.spec.ts b/tests/core/acroform/PDFAcroField.spec.ts
new file mode 100644
index 0000000..5ff108b
--- /dev/null
+++ b/tests/core/acroform/PDFAcroField.spec.ts
@@ -0,0 +1,44 @@
+import {
+  PDFAcroTerminal,
+  PDFContext,
+  PDFString,
+  PDFHexString,
+} from 'src/index';
+
+describe(`PDFAcroField`, () => {
+  it(`returns undefined for missing (DAs)`, () => {
+    const context = PDFContext.create();
+
+    const dict = context.obj({
+      DA: null,
+    });
+    const dictRef = context.register(dict);
+    const field = PDFAcroTerminal.fromDict(dict, dictRef);
+
+    expect(field.getDefaultAppearance()).toBe(undefined);
+  });
+
+  it(`returns normal direct appearance strings (DAs)`, () => {
+    const context = PDFContext.create();
+
+    const dict = context.obj({
+      DA: PDFString.of('/ZaDb 10 Tf 0 g'),
+    });
+    const dictRef = context.register(dict);
+    const field = PDFAcroTerminal.fromDict(dict, dictRef);
+
+    expect(field.getDefaultAppearance()).toBe('/ZaDb 10 Tf 0 g');
+  });
+
+  it(`returns hexadecimal (non-standard) direct appearance strings (DAs)`, () => {
+    const context = PDFContext.create();
+
+    const dict = context.obj({
+      DA: PDFHexString.fromText('/ZaDb 10 Tf 0 g'),
+    });
+    const dictRef = context.register(dict);
+    const field = PDFAcroTerminal.fromDict(dict, dictRef);
+
+    expect(field.getDefaultAppearance()).toBe('/ZaDb 10 Tf 0 g');
+  });
+});
diff --git a/tests/core/annotation/PDFWidgetAnnotation.spec.ts b/tests/core/annotation/PDFWidgetAnnotation.spec.ts
new file mode 100644
index 0000000..a9fb66f
--- /dev/null
+++ b/tests/core/annotation/PDFWidgetAnnotation.spec.ts
@@ -0,0 +1,40 @@
+import {
+  PDFWidgetAnnotation,
+  PDFContext,
+  PDFString,
+  PDFHexString,
+  PDFName,
+  PDFNull,
+} from 'src/index';
+
+describe(`PDFWidgetAnnotation`, () => {
+  it(`returns undefined for missing (DAs)`, () => {
+    const context = PDFContext.create();
+
+    const parentRef = context.nextRef();
+    const widget = PDFWidgetAnnotation.create(context, parentRef);
+    widget.dict.set(PDFName.of('DA'), PDFNull);
+
+    expect(widget.getDefaultAppearance()).toBe(undefined);
+  });
+
+  it(`returns normal direct appearance strings (DAs)`, () => {
+    const context = PDFContext.create();
+
+    const parentRef = context.nextRef();
+    const widget = PDFWidgetAnnotation.create(context, parentRef);
+    widget.dict.set(PDFName.of('DA'), PDFString.of('/ZaDb 10 Tf 0 g'));
+
+    expect(widget.getDefaultAppearance()).toBe('/ZaDb 10 Tf 0 g');
+  });
+
+  it(`returns hexadecimal (non-standard) direct appearance strings (DAs)`, () => {
+    const context = PDFContext.create();
+
+    const parentRef = context.nextRef();
+    const widget = PDFWidgetAnnotation.create(context, parentRef);
+    widget.dict.set(PDFName.of('DA'), PDFHexString.fromText('/ZaDb 10 Tf 0 g'));
+
+    expect(widget.getDefaultAppearance()).toBe('/ZaDb 10 Tf 0 g');
+  });
+});