mirror of
https://github.com/documenso/documenso.git
synced 2026-07-22 16:03:39 +10:00
perf: upgrade @libpdf/core to 0.3.3 and deduplicate font registration (#2598)
Upgrade @libpdf/core from 0.2.12 to 0.3.3, which includes: - WebCrypto SHA-256 replacing pure-JS @noble/hashes (10x signing speedup) - Iterative collectReachableRefs (fixes stack overflow on large PDFs) - Iterative Math.max helpers in xref writer (fixes remaining stack overflow) Extract duplicated FontLibrary.use() calls from render-certificate, render-audit-logs, and insert-field-in-pdf-v2 into a shared ensureFontLibrary() helper with has() guards so fonts are only registered once per process.
This commit is contained in:
Generated
+50
-4
@@ -15,7 +15,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/google-vertex": "3.0.81",
|
"@ai-sdk/google-vertex": "3.0.81",
|
||||||
"@documenso/prisma": "*",
|
"@documenso/prisma": "*",
|
||||||
"@libpdf/core": "^0.2.12",
|
"@libpdf/core": "^0.3.3",
|
||||||
"@lingui/conf": "^5.6.0",
|
"@lingui/conf": "^5.6.0",
|
||||||
"@lingui/core": "^5.6.0",
|
"@lingui/core": "^5.6.0",
|
||||||
"@prisma/extension-read-replicas": "^0.4.1",
|
"@prisma/extension-read-replicas": "^0.4.1",
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^20.1.0",
|
"@commitlint/cli": "^20.1.0",
|
||||||
"@commitlint/config-conventional": "^20.0.0",
|
"@commitlint/config-conventional": "^20.0.0",
|
||||||
|
"@datadog/pprof": "^5.13.5",
|
||||||
"@lingui/cli": "^5.6.0",
|
"@lingui/cli": "^5.6.0",
|
||||||
"@prisma/client": "^6.19.0",
|
"@prisma/client": "^6.19.0",
|
||||||
"@trpc/client": "11.8.1",
|
"@trpc/client": "11.8.1",
|
||||||
@@ -2852,6 +2853,32 @@
|
|||||||
"node": ">=v18"
|
"node": ">=v18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@datadog/pprof": {
|
||||||
|
"version": "5.13.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@datadog/pprof/-/pprof-5.13.5.tgz",
|
||||||
|
"integrity": "sha512-W0dvo91ff2EMQI9Vhv8PNM+w1ZWuClm1pBVdLB6y0bMB3+E+wEGg0VD1iNJxsuPbwDt5+yV0u3e4WkqK12Lzlg==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"node-gyp-build": "<4.0",
|
||||||
|
"pprof-format": "^2.2.1",
|
||||||
|
"source-map": "^0.7.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@datadog/pprof/node_modules/source-map": {
|
||||||
|
"version": "0.7.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
|
||||||
|
"integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@documenso/api": {
|
"node_modules/@documenso/api": {
|
||||||
"resolved": "packages/api",
|
"resolved": "packages/api",
|
||||||
"link": true
|
"link": true
|
||||||
@@ -4645,9 +4672,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@libpdf/core": {
|
"node_modules/@libpdf/core": {
|
||||||
"version": "0.2.12",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@libpdf/core/-/core-0.2.12.tgz",
|
"resolved": "https://registry.npmjs.org/@libpdf/core/-/core-0.3.3.tgz",
|
||||||
"integrity": "sha512-z22SyNEXa8YsCJarJBkgQv4SvvDn0Opw21cNQOQ0Xax9Ys1qjpAyVTSjlGExYVI8bT9b02VNy+nsOcJ79SzsQg==",
|
"integrity": "sha512-MoyjZ00RPJ1sDgFooerCw3WqXzaaufHFkBYZv6v8qKUaIljdS2MYm1OYvcyV+V1qplo+o8qc0X+0p/JipzJ/Jw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/ciphers": "^2.1.1",
|
"@noble/ciphers": "^2.1.1",
|
||||||
@@ -29555,6 +29582,18 @@
|
|||||||
"integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
|
"integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/node-gyp-build": {
|
||||||
|
"version": "3.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz",
|
||||||
|
"integrity": "sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"node-gyp-build": "bin.js",
|
||||||
|
"node-gyp-build-optional": "optional.js",
|
||||||
|
"node-gyp-build-test": "build-test.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-html-better-parser": {
|
"node_modules/node-html-better-parser": {
|
||||||
"version": "1.5.8",
|
"version": "1.5.8",
|
||||||
"resolved": "https://registry.npmjs.org/node-html-better-parser/-/node-html-better-parser-1.5.8.tgz",
|
"resolved": "https://registry.npmjs.org/node-html-better-parser/-/node-html-better-parser-1.5.8.tgz",
|
||||||
@@ -31215,6 +31254,13 @@
|
|||||||
"node": ">=15.0.0"
|
"node": ">=15.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pprof-format": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pprof-format/-/pprof-format-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-p4tVN7iK19ccDqQv8heyobzUmbHyds4N2FI6aBMcXz6y99MglTWDxIyhFkNaLeEXs6IFUEzT0zya0icbSLLY0g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/preact": {
|
"node_modules/preact": {
|
||||||
"version": "10.28.2",
|
"version": "10.28.2",
|
||||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz",
|
"resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz",
|
||||||
|
|||||||
+2
-1
@@ -49,6 +49,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^20.1.0",
|
"@commitlint/cli": "^20.1.0",
|
||||||
"@commitlint/config-conventional": "^20.0.0",
|
"@commitlint/config-conventional": "^20.0.0",
|
||||||
|
"@datadog/pprof": "^5.13.5",
|
||||||
"@lingui/cli": "^5.6.0",
|
"@lingui/cli": "^5.6.0",
|
||||||
"@prisma/client": "^6.19.0",
|
"@prisma/client": "^6.19.0",
|
||||||
"@trpc/client": "11.8.1",
|
"@trpc/client": "11.8.1",
|
||||||
@@ -86,7 +87,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/google-vertex": "3.0.81",
|
"@ai-sdk/google-vertex": "3.0.81",
|
||||||
"@documenso/prisma": "*",
|
"@documenso/prisma": "*",
|
||||||
"@libpdf/core": "^0.2.12",
|
"@libpdf/core": "^0.3.3",
|
||||||
"@lingui/conf": "^5.6.0",
|
"@lingui/conf": "^5.6.0",
|
||||||
"@lingui/core": "^5.6.0",
|
"@lingui/core": "^5.6.0",
|
||||||
"@prisma/extension-read-replicas": "^0.4.1",
|
"@prisma/extension-read-replicas": "^0.4.1",
|
||||||
|
|||||||
@@ -1,9 +1,45 @@
|
|||||||
import { FieldType } from '@prisma/client';
|
import { FieldType } from '@prisma/client';
|
||||||
import type { Recipient } from '@prisma/client';
|
import type { Recipient } from '@prisma/client';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { FontLibrary } from 'skia-canvas';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure all required fonts are registered in the skia-canvas FontLibrary.
|
||||||
|
*
|
||||||
|
* Fonts are registered once per process and retained — calling this multiple
|
||||||
|
* times is a no-op after the first invocation.
|
||||||
|
*/
|
||||||
|
export const ensureFontLibrary = () => {
|
||||||
|
const fontPath = path.join(process.cwd(), 'public/fonts');
|
||||||
|
|
||||||
|
if (!FontLibrary.has('Caveat')) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
FontLibrary.use({
|
||||||
|
['Caveat']: [path.join(fontPath, 'caveat.ttf')],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FontLibrary.has('Inter')) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
FontLibrary.use({
|
||||||
|
['Inter']: [path.join(fontPath, 'inter-variablefont_opsz,wght.ttf')],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FontLibrary.has('Noto Sans')) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
FontLibrary.use({
|
||||||
|
['Noto Sans']: [path.join(fontPath, 'noto-sans.ttf')],
|
||||||
|
['Noto Sans Japanese']: [path.join(fontPath, 'noto-sans-japanese.ttf')],
|
||||||
|
['Noto Sans Chinese']: [path.join(fontPath, 'noto-sans-chinese.ttf')],
|
||||||
|
['Noto Sans Korean']: [path.join(fontPath, 'noto-sans-korean.ttf')],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
type RecipientPlaceholderInfo = {
|
type RecipientPlaceholderInfo = {
|
||||||
email: string;
|
email: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
import '../konva/skia-backend';
|
import '../konva/skia-backend';
|
||||||
|
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import path from 'node:path';
|
|
||||||
import type { Canvas } from 'skia-canvas';
|
import type { Canvas } from 'skia-canvas';
|
||||||
import { FontLibrary } from 'skia-canvas';
|
|
||||||
|
|
||||||
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
||||||
|
|
||||||
import { renderField } from '../../universal/field-renderer/render-field';
|
import { renderField } from '../../universal/field-renderer/render-field';
|
||||||
|
import { ensureFontLibrary } from './helpers';
|
||||||
|
|
||||||
type InsertFieldInPDFV2Options = {
|
type InsertFieldInPDFV2Options = {
|
||||||
pageWidth: number;
|
pageWidth: number;
|
||||||
@@ -21,16 +20,7 @@ export const insertFieldInPDFV2 = async ({
|
|||||||
pageHeight,
|
pageHeight,
|
||||||
fields,
|
fields,
|
||||||
}: InsertFieldInPDFV2Options) => {
|
}: InsertFieldInPDFV2Options) => {
|
||||||
const fontPath = path.join(process.cwd(), 'public/fonts');
|
ensureFontLibrary();
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
FontLibrary.use({
|
|
||||||
['Caveat']: [path.join(fontPath, 'caveat.ttf')],
|
|
||||||
['Noto Sans']: [path.join(fontPath, 'noto-sans.ttf')],
|
|
||||||
['Noto Sans Japanese']: [path.join(fontPath, 'noto-sans-japanese.ttf')],
|
|
||||||
['Noto Sans Chinese']: [path.join(fontPath, 'noto-sans-chinese.ttf')],
|
|
||||||
['Noto Sans Korean']: [path.join(fontPath, 'noto-sans-korean.ttf')],
|
|
||||||
});
|
|
||||||
|
|
||||||
let stage: Konva.Stage | null = new Konva.Stage({ width: pageWidth, height: pageHeight });
|
let stage: Konva.Stage | null = new Konva.Stage({ width: pageWidth, height: pageHeight });
|
||||||
let layer: Konva.Layer | null = new Konva.Layer();
|
let layer: Konva.Layer | null = new Konva.Layer();
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { DateTime } from 'luxon';
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import type { Canvas } from 'skia-canvas';
|
import type { Canvas } from 'skia-canvas';
|
||||||
import { FontLibrary } from 'skia-canvas';
|
|
||||||
import { Image as SkiaImage } from 'skia-canvas';
|
import { Image as SkiaImage } from 'skia-canvas';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
import { P } from 'ts-pattern';
|
import { P } from 'ts-pattern';
|
||||||
@@ -21,6 +20,7 @@ import { RECIPIENT_ROLES_DESCRIPTION } from '../../constants/recipient-roles';
|
|||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
import type { TDocumentAuditLog } from '../../types/document-audit-logs';
|
import type { TDocumentAuditLog } from '../../types/document-audit-logs';
|
||||||
import { formatDocumentAuditLogAction } from '../../utils/document-audit-logs';
|
import { formatDocumentAuditLogAction } from '../../utils/document-audit-logs';
|
||||||
|
import { ensureFontLibrary } from './helpers';
|
||||||
|
|
||||||
export type AuditLogRecipient = {
|
export type AuditLogRecipient = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -575,13 +575,7 @@ export async function renderAuditLogs({
|
|||||||
i18n,
|
i18n,
|
||||||
hidePoweredBy,
|
hidePoweredBy,
|
||||||
}: GenerateAuditLogsOptions) {
|
}: GenerateAuditLogsOptions) {
|
||||||
const fontPath = path.join(process.cwd(), 'public/fonts');
|
ensureFontLibrary();
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
FontLibrary.use({
|
|
||||||
['Caveat']: [path.join(fontPath, 'caveat.ttf')],
|
|
||||||
['Inter']: [path.join(fontPath, 'inter-variablefont_opsz,wght.ttf')],
|
|
||||||
});
|
|
||||||
|
|
||||||
const minimumMargin = 10;
|
const minimumMargin = 10;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { DateTime } from 'luxon';
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import type { Canvas } from 'skia-canvas';
|
import type { Canvas } from 'skia-canvas';
|
||||||
import { FontLibrary } from 'skia-canvas';
|
|
||||||
import { Image as SkiaImage } from 'skia-canvas';
|
import { Image as SkiaImage } from 'skia-canvas';
|
||||||
import { UAParser } from 'ua-parser-js';
|
import { UAParser } from 'ua-parser-js';
|
||||||
import { renderSVG } from 'uqr';
|
import { renderSVG } from 'uqr';
|
||||||
@@ -22,6 +21,7 @@ import {
|
|||||||
} from '../../constants/recipient-roles';
|
} from '../../constants/recipient-roles';
|
||||||
import type { TDocumentAuditLogBaseSchema } from '../../types/document-audit-logs';
|
import type { TDocumentAuditLogBaseSchema } from '../../types/document-audit-logs';
|
||||||
import { svgToPng } from '../../utils/images/svg-to-png';
|
import { svgToPng } from '../../utils/images/svg-to-png';
|
||||||
|
import { ensureFontLibrary } from './helpers';
|
||||||
|
|
||||||
type ColumnWidths = [number, number, number];
|
type ColumnWidths = [number, number, number];
|
||||||
|
|
||||||
@@ -724,13 +724,7 @@ export async function renderCertificate({
|
|||||||
pageWidth,
|
pageWidth,
|
||||||
pageHeight,
|
pageHeight,
|
||||||
}: GenerateCertificateOptions) {
|
}: GenerateCertificateOptions) {
|
||||||
const fontPath = path.join(process.cwd(), 'public/fonts');
|
ensureFontLibrary();
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
FontLibrary.use({
|
|
||||||
['Caveat']: [path.join(fontPath, 'caveat.ttf')],
|
|
||||||
['Inter']: [path.join(fontPath, 'inter-variablefont_opsz,wght.ttf')],
|
|
||||||
});
|
|
||||||
|
|
||||||
const minimumMargin = 10;
|
const minimumMargin = 10;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user