fix: documents failing to seal (#1821)

During our field rework that makes fields appear
more accurately between signing and the completed pdf we swapped to
using text fields. Unfortunately as part of that we dropped using the
Noto font for the text field causing ANSI encoding issues when
encountering certain characters.

This change restores the font and handles a nasty issue we had with our
form flattening reverting our selected font.
This commit is contained in:
Lucas Smith
2025-06-04 23:29:36 +10:00
committed by GitHub
parent 93aece9644
commit 695ed418e2
4 changed files with 48 additions and 47 deletions

View File

@ -40,43 +40,6 @@ services:
entrypoint: sh
command: -c 'mkdir -p /data/documenso && minio server /data --console-address ":9001" --address ":9002"'
triggerdotdev:
image: ghcr.io/triggerdotdev/trigger.dev:latest
container_name: triggerdotdev
environment:
- LOGIN_ORIGIN=http://localhost:3030
- APP_ORIGIN=http://localhost:3030
- PORT=3030
- REMIX_APP_PORT=3030
- MAGIC_LINK_SECRET=secret
- SESSION_SECRET=secret
- ENCRYPTION_KEY=deadbeefcafefeed
- DATABASE_URL=postgresql://trigger:password@triggerdotdev_database:5432/trigger
- DIRECT_URL=postgresql://trigger:password@triggerdotdev_database:5432/trigger
- RUNTIME_PLATFORM=docker-compose
ports:
- 3030:3030
depends_on:
- triggerdotdev_database
triggerdotdev_database:
container_name: triggerdotdev_database
image: postgres:15
volumes:
- triggerdotdev_database:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER}']
interval: 10s
timeout: 5s
retries: 5
environment:
- POSTGRES_USER=trigger
- POSTGRES_PASSWORD=password
- POSTGRES_DB=trigger
ports:
- 54321:5432
volumes:
minio:
documenso_database:
triggerdotdev_database:

View File

@ -128,7 +128,7 @@ export const sealDocument = async ({
// Normalize and flatten layers that could cause issues with the signature
normalizeSignatureAppearances(doc);
flattenForm(doc);
await flattenForm(doc);
flattenAnnotations(doc);
// Add rejection stamp if the document is rejected
@ -153,7 +153,7 @@ export const sealDocument = async ({
}
// Re-flatten post-insertion to handle fields that create arcoFields
flattenForm(doc);
await flattenForm(doc);
const pdfBytes = await doc.save();

View File

@ -1,3 +1,4 @@
import fontkit from '@pdf-lib/fontkit';
import type { PDFField, PDFWidgetAnnotation } from 'pdf-lib';
import {
PDFCheckBox,
@ -13,6 +14,8 @@ import {
translate,
} from 'pdf-lib';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
export const removeOptionalContentGroups = (document: PDFDocument) => {
const context = document.context;
const catalog = context.lookup(context.trailerInfo.Root);
@ -21,12 +24,20 @@ export const removeOptionalContentGroups = (document: PDFDocument) => {
}
};
export const flattenForm = (document: PDFDocument) => {
export const flattenForm = async (document: PDFDocument) => {
removeOptionalContentGroups(document);
const form = document.getForm();
form.updateFieldAppearances();
const fontNoto = await fetch(`${NEXT_PUBLIC_WEBAPP_URL()}/fonts/noto-sans.ttf`).then(
async (res) => res.arrayBuffer(),
);
document.registerFontkit(fontkit);
const font = await document.embedFont(fontNoto);
form.updateFieldAppearances(font);
for (const field of form.getFields()) {
for (const widget of field.acroField.getWidgets()) {

View File

@ -1,8 +1,15 @@
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
import fontkit from '@pdf-lib/fontkit';
import { FieldType } from '@prisma/client';
import type { PDFDocument, PDFFont } from 'pdf-lib';
import { RotationTypes, TextAlignment, degrees, radiansToDegrees, rgb } from 'pdf-lib';
import type { PDFDocument, PDFFont, PDFTextField } from 'pdf-lib';
import {
RotationTypes,
TextAlignment,
degrees,
radiansToDegrees,
rgb,
setFontAndSize,
} from 'pdf-lib';
import { P, match } from 'ts-pattern';
import {
@ -442,6 +449,10 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
adjustedFieldY = adjustedPosition.yPos;
}
// Set properties for the text field
setTextFieldFontSize(textField, font, fontSize);
textField.setText(textToInsert);
// Set the position and size of the text field
textField.addToPage(page, {
x: adjustedFieldX,
@ -450,6 +461,8 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
height: adjustedFieldHeight,
rotate: degrees(pageRotationInDegrees),
font,
// Hide borders.
borderWidth: 0,
borderColor: undefined,
@ -457,10 +470,6 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
...(isDebugMode ? { borderWidth: 1, borderColor: rgb(0, 0, 1) } : {}),
});
// Set properties for the text field
textField.setFontSize(fontSize);
textField.setText(textToInsert);
});
return pdf;
@ -629,3 +638,21 @@ function breakLongString(text: string, maxWidth: number, font: PDFFont, fontSize
return lines.join('\n');
}
const setTextFieldFontSize = (textField: PDFTextField, font: PDFFont, fontSize: number) => {
textField.defaultUpdateAppearances(font);
textField.updateAppearances(font);
try {
textField.setFontSize(fontSize);
} catch (err) {
let da = textField.acroField.getDefaultAppearance() ?? '';
da += `\n ${setFontAndSize(font.name, fontSize)}`;
textField.acroField.setDefaultAppearance(da);
}
textField.defaultUpdateAppearances(font);
textField.updateAppearances(font);
};