@libpdf/core 0.0.1-beta.7 → 0.0.1-beta.8

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.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,3 @@
1
- import { z } from "zod";
2
1
  import * as _google_cloud_kms0 from "@google-cloud/kms";
3
2
  import * as _google_cloud_secret_manager0 from "@google-cloud/secret-manager";
4
3
 
@@ -1931,6 +1930,12 @@ interface Permissions {
1931
1930
  }
1932
1931
  //#endregion
1933
1932
  //#region src/security/schemas.d.ts
1933
+ /**
1934
+ * Zod schemas for PDF encryption dictionary validation.
1935
+ *
1936
+ * These schemas provide type-safe parsing of encryption parameters
1937
+ * with proper validation and TypeScript type inference.
1938
+ */
1934
1939
  /**
1935
1940
  * Valid encryption versions (V entry).
1936
1941
  *
@@ -1940,8 +1945,7 @@ interface Permissions {
1940
1945
  * - 4: AES-128 or RC4 with crypt filters (PDF 1.5)
1941
1946
  * - 5: AES-256 (PDF 2.0)
1942
1947
  */
1943
- declare const VersionSchema: z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>, z.ZodLiteral<5>]>;
1944
- type Version = z.infer<typeof VersionSchema>;
1948
+ type EncryptionVersion = 1 | 2 | 3 | 4 | 5;
1945
1949
  /**
1946
1950
  * Valid encryption revisions (R entry).
1947
1951
  *
@@ -1951,25 +1955,31 @@ type Version = z.infer<typeof VersionSchema>;
1951
1955
  * - 5: V=5, AES-256 (draft, Adobe Extension Level 3)
1952
1956
  * - 6: V=5, AES-256 (final, ISO 32000-2)
1953
1957
  */
1954
- declare const RevisionSchema: z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>, z.ZodLiteral<5>, z.ZodLiteral<6>]>;
1955
- type Revision = z.infer<typeof RevisionSchema>;
1958
+ type EncryptionRevision = 2 | 3 | 4 | 5 | 6;
1959
+ /**
1960
+ * Crypt filter methods (CFM entry).
1961
+ *
1962
+ * - None: Identity (no encryption)
1963
+ * - V2: RC4
1964
+ * - AESV2: AES-128
1965
+ * - AESV3: AES-256
1966
+ */
1967
+ type CryptFilterMethod = "None" | "V2" | "AESV2" | "AESV3";
1968
+ /**
1969
+ * Authentication events (AuthEvent entry).
1970
+ *
1971
+ * - DocOpen: Authentication when document is opened
1972
+ * - EFOpen: Authentication when embedded file is accessed
1973
+ */
1974
+ type AuthEvent = "DocOpen" | "EFOpen";
1956
1975
  /**
1957
1976
  * Complete crypt filter configuration.
1958
1977
  */
1959
- declare const CryptFilterSchema: z.ZodObject<{
1960
- cfm: z.ZodEnum<{
1961
- None: "None";
1962
- V2: "V2";
1963
- AESV2: "AESV2";
1964
- AESV3: "AESV3";
1965
- }>;
1966
- authEvent: z.ZodOptional<z.ZodEnum<{
1967
- DocOpen: "DocOpen";
1968
- EFOpen: "EFOpen";
1969
- }>>;
1970
- length: z.ZodOptional<z.ZodNumber>;
1971
- }, z.core.$strip>;
1972
- type CryptFilter = z.infer<typeof CryptFilterSchema>;
1978
+ interface CryptFilter {
1979
+ cfm: CryptFilterMethod;
1980
+ authEvent?: AuthEvent;
1981
+ length?: number;
1982
+ }
1973
1983
  /**
1974
1984
  * Supported encryption algorithms.
1975
1985
  *
@@ -1977,12 +1987,7 @@ type CryptFilter = z.infer<typeof CryptFilterSchema>;
1977
1987
  * - AES-128: AES with 128-bit key (R4)
1978
1988
  * - AES-256: AES with 256-bit key (R5-R6)
1979
1989
  */
1980
- declare const EncryptionAlgorithmSchema: z.ZodEnum<{
1981
- RC4: "RC4";
1982
- "AES-128": "AES-128";
1983
- "AES-256": "AES-256";
1984
- }>;
1985
- type EncryptionAlgorithm = z.infer<typeof EncryptionAlgorithmSchema>;
1990
+ type EncryptionAlgorithm = "RC4" | "AES-128" | "AES-256";
1986
1991
  //#endregion
1987
1992
  //#region src/security/errors.d.ts
1988
1993
  /**
@@ -2021,9 +2026,9 @@ interface EncryptionDict {
2021
2026
  /** Security handler filter (always "Standard" for this implementation) */
2022
2027
  filter: "Standard";
2023
2028
  /** Algorithm version: 1, 2, 3, 4, or 5 */
2024
- version: Version;
2029
+ version: EncryptionVersion;
2025
2030
  /** Standard security handler revision: 2, 3, 4, 5, or 6 */
2026
- revision: Revision;
2031
+ revision: EncryptionRevision;
2027
2032
  /** Key length in bits (40-256) */
2028
2033
  keyLengthBits: number;
2029
2034
  /** Owner password verification value (32 bytes for R2-R4, 48 bytes for R5-R6) */
@@ -2120,11 +2125,11 @@ declare class StandardSecurityHandler {
2120
2125
  /**
2121
2126
  * Get the encryption version.
2122
2127
  */
2123
- get version(): Version;
2128
+ get version(): EncryptionVersion;
2124
2129
  /**
2125
2130
  * Get the encryption revision.
2126
2131
  */
2127
- get revision(): Revision;
2132
+ get revision(): EncryptionRevision;
2128
2133
  /**
2129
2134
  * Get document permissions.
2130
2135
  */
@@ -6492,6 +6497,16 @@ interface FlattenOptions {
6492
6497
  font?: FormFont;
6493
6498
  /** Font size to use (0 = auto) */
6494
6499
  fontSize?: number;
6500
+ /**
6501
+ * Skip signature fields during flattening.
6502
+ *
6503
+ * When true, signature fields are preserved as interactive fields while
6504
+ * all other fields are flattened. This is useful when you want to flatten
6505
+ * filled form data but keep signature fields available for signing.
6506
+ *
6507
+ * @default false
6508
+ */
6509
+ skipSignatures?: boolean;
6495
6510
  }
6496
6511
  //#endregion
6497
6512
  //#region src/document/forms/acro-form.d.ts
@@ -7245,6 +7260,13 @@ declare class PDFForm {
7245
7260
  * await pdf.form.flatten();
7246
7261
  * const bytes = await pdf.save();
7247
7262
  * ```
7263
+ *
7264
+ * @example Flatten while preserving signature fields
7265
+ * ```typescript
7266
+ * await pdf.form.fill({ name: "John Doe" });
7267
+ * await pdf.form.flatten({ skipSignatures: true });
7268
+ * // Signature fields remain interactive for signing
7269
+ * ```
7248
7270
  */
7249
7271
  flatten(options?: FlattenOptions): void;
7250
7272
  /**
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import pako, { deflate, inflate } from "pako";
2
- import { z } from "zod";
3
2
  import { cbc, ecb } from "@noble/ciphers/aes.js";
4
3
  import { randomBytes } from "@noble/ciphers/utils.js";
5
4
  import { md5, sha1 } from "@noble/hashes/legacy.js";
@@ -9,7 +8,7 @@ import * as pkijs from "pkijs";
9
8
  import { base64 } from "@scure/base";
10
9
 
11
10
  //#region package.json
12
- var version = "0.0.1-beta.7";
11
+ var version = "0.0.1-beta.8";
13
12
 
14
13
  //#endregion
15
14
  //#region src/objects/pdf-array.ts
@@ -3923,85 +3922,23 @@ function encodePermissions(permissions) {
3923
3922
 
3924
3923
  //#endregion
3925
3924
  //#region src/security/schemas.ts
3926
- /**
3927
- * Zod schemas for PDF encryption dictionary validation.
3928
- *
3929
- * These schemas provide type-safe parsing of encryption parameters
3930
- * with proper validation and TypeScript type inference.
3931
- */
3932
- /**
3933
- * Valid encryption versions (V entry).
3934
- *
3935
- * - 1: 40-bit RC4 (PDF 1.1)
3936
- * - 2: 40-128 bit RC4 (PDF 1.4)
3937
- * - 3: Unpublished (rare)
3938
- * - 4: AES-128 or RC4 with crypt filters (PDF 1.5)
3939
- * - 5: AES-256 (PDF 2.0)
3940
- */
3941
- const VersionSchema = z.union([
3942
- z.literal(1),
3943
- z.literal(2),
3944
- z.literal(3),
3945
- z.literal(4),
3946
- z.literal(5)
3947
- ]);
3948
- /**
3949
- * Valid encryption revisions (R entry).
3950
- *
3951
- * - 2: V=1, 40-bit RC4
3952
- * - 3: V=2, 40-128 bit RC4
3953
- * - 4: V=4, AES-128 or RC4
3954
- * - 5: V=5, AES-256 (draft, Adobe Extension Level 3)
3955
- * - 6: V=5, AES-256 (final, ISO 32000-2)
3956
- */
3957
- const RevisionSchema = z.union([
3958
- z.literal(2),
3959
- z.literal(3),
3960
- z.literal(4),
3961
- z.literal(5),
3962
- z.literal(6)
3963
- ]);
3964
- /**
3965
- * Crypt filter methods (CFM entry).
3966
- *
3967
- * - None: Identity (no encryption)
3968
- * - V2: RC4
3969
- * - AESV2: AES-128
3970
- * - AESV3: AES-256
3971
- */
3972
- const CryptFilterMethodSchema = z.enum([
3973
- "None",
3974
- "V2",
3975
- "AESV2",
3976
- "AESV3"
3977
- ]);
3978
- /**
3979
- * Authentication events (AuthEvent entry).
3980
- *
3981
- * - DocOpen: Authentication when document is opened
3982
- * - EFOpen: Authentication when embedded file is accessed
3983
- */
3984
- const AuthEventSchema = z.enum(["DocOpen", "EFOpen"]);
3985
- /**
3986
- * Complete crypt filter configuration.
3987
- */
3988
- const CryptFilterSchema = z.object({
3989
- cfm: CryptFilterMethodSchema,
3990
- authEvent: AuthEventSchema.optional(),
3991
- length: z.number().optional()
3992
- });
3993
- /**
3994
- * Supported encryption algorithms.
3995
- *
3996
- * - RC4: Legacy stream cipher (R2-R4)
3997
- * - AES-128: AES with 128-bit key (R4)
3998
- * - AES-256: AES with 256-bit key (R5-R6)
3999
- */
4000
- const EncryptionAlgorithmSchema = z.enum([
4001
- "RC4",
4002
- "AES-128",
4003
- "AES-256"
4004
- ]);
3925
+ const isEncryptionVersion = (version$1) => {
3926
+ return typeof version$1 === "number" && version$1 >= 1 && version$1 <= 5;
3927
+ };
3928
+ const isEncryptionRevision = (revision) => {
3929
+ return typeof revision === "number" && revision >= 2 && revision <= 6;
3930
+ };
3931
+ const isCryptFilterMethod = (method) => {
3932
+ return typeof method === "string" && [
3933
+ "None",
3934
+ "V2",
3935
+ "AESV2",
3936
+ "AESV3"
3937
+ ].includes(method);
3938
+ };
3939
+ const isAuthEvent = (event) => {
3940
+ return typeof event === "string" && ["DocOpen", "EFOpen"].includes(event);
3941
+ };
4005
3942
 
4006
3943
  //#endregion
4007
3944
  //#region src/security/encryption-dict.ts
@@ -4014,22 +3951,16 @@ const EncryptionAlgorithmSchema = z.enum([
4014
3951
  * @see PDF 2.0 Specification, Section 7.6.2 (Standard encryption dictionary)
4015
3952
  */
4016
3953
  /**
4017
- * Parse a crypt filter dictionary with Zod validation.
3954
+ * Parse a crypt filter dictionary.
4018
3955
  */
4019
3956
  function parseCryptFilter(dict) {
4020
- const cfmRaw = dict.getName("CFM")?.value ?? "None";
4021
- const authEventRaw = dict.getName("AuthEvent")?.value;
3957
+ const cfm = dict.getName("CFM")?.value ?? "None";
3958
+ const authEvent = dict.getName("AuthEvent")?.value;
4022
3959
  const length = dict.getNumber("Length")?.value;
4023
- const cfmResult = CryptFilterMethodSchema.safeParse(cfmRaw);
4024
- if (!cfmResult.success) throw new EncryptionDictError(`Invalid crypt filter method: ${cfmRaw}`);
4025
- let authEvent;
4026
- if (authEventRaw !== void 0) {
4027
- const authEventResult = AuthEventSchema.safeParse(authEventRaw);
4028
- if (!authEventResult.success) throw new EncryptionDictError(`Invalid auth event: ${authEventRaw}`);
4029
- authEvent = authEventResult.data;
4030
- }
3960
+ if (!isCryptFilterMethod(cfm)) throw new EncryptionDictError(`Invalid crypt filter method: ${cfm}`);
3961
+ if (authEvent !== void 0 && !isAuthEvent(authEvent)) throw new EncryptionDictError(`Invalid auth event: ${authEvent}`);
4031
3962
  return {
4032
- cfm: cfmResult.data,
3963
+ cfm,
4033
3964
  authEvent,
4034
3965
  length
4035
3966
  };
@@ -4055,21 +3986,19 @@ function determineAlgorithm(version$1, revision, cryptFilters, streamFilter) {
4055
3986
  * Parse and validate the encryption version.
4056
3987
  */
4057
3988
  function parseVersion(dict) {
4058
- const versionRaw = dict.getNumber("V")?.value;
4059
- if (versionRaw === void 0) throw new EncryptionDictError("Missing /V (version) in encryption dictionary");
4060
- const result = VersionSchema.safeParse(versionRaw);
4061
- if (!result.success) throw new EncryptionDictError(`Unsupported encryption version: ${versionRaw}`);
4062
- return result.data;
3989
+ const version$1 = dict.getNumber("V")?.value;
3990
+ if (version$1 === void 0) throw new EncryptionDictError("Missing /V (version) in encryption dictionary");
3991
+ if (!isEncryptionVersion(version$1)) throw new EncryptionDictError(`Unsupported encryption version: ${version$1}`);
3992
+ return version$1;
4063
3993
  }
4064
3994
  /**
4065
3995
  * Parse and validate the encryption revision.
4066
3996
  */
4067
3997
  function parseRevision(dict) {
4068
- const revisionRaw = dict.getNumber("R")?.value;
4069
- if (revisionRaw === void 0) throw new EncryptionDictError("Missing /R (revision) in encryption dictionary");
4070
- const result = RevisionSchema.safeParse(revisionRaw);
4071
- if (!result.success) throw new EncryptionDictError(`Unsupported encryption revision: ${revisionRaw}`);
4072
- return result.data;
3998
+ const revision = dict.getNumber("R")?.value;
3999
+ if (revision === void 0) throw new EncryptionDictError("Missing /R (revision) in encryption dictionary");
4000
+ if (!isEncryptionRevision(revision)) throw new EncryptionDictError(`Unsupported encryption revision: ${revision}`);
4001
+ return revision;
4073
4002
  }
4074
4003
  /**
4075
4004
  * Parse an encryption dictionary from a PDF dictionary.
@@ -26236,30 +26165,36 @@ var FormFlattener = class {
26236
26165
  * Flatten all form fields into static page content.
26237
26166
  */
26238
26167
  flatten(options = {}) {
26239
- if (options.font || options.fontSize !== void 0) {
26240
- const fields = this.form.getFields();
26241
- for (const field of fields) {
26242
- if (field.isReadOnly()) continue;
26243
- if (options.font) field.setFont(options.font);
26244
- if (options.fontSize !== void 0) field.setFontSize(options.fontSize);
26245
- }
26168
+ const allFields = this.form.getFields();
26169
+ const skipSignatures = options.skipSignatures ?? false;
26170
+ const signatureFields = skipSignatures ? allFields.filter((f) => f instanceof SignatureField) : [];
26171
+ const fieldsToFlatten = skipSignatures ? allFields.filter((f) => !(f instanceof SignatureField)) : allFields;
26172
+ if (options.font || options.fontSize !== void 0) for (const field of fieldsToFlatten) {
26173
+ if (field.isReadOnly()) continue;
26174
+ if (options.font) field.setFont(options.font);
26175
+ if (options.fontSize !== void 0) field.setFontSize(options.fontSize);
26246
26176
  }
26247
26177
  if (!options.skipAppearanceUpdate) this.form.updateAppearances({ forceRegenerate: options.regenerateAppearances });
26248
- const pageWidgets = this.collectWidgetsByPage();
26178
+ const pageWidgets = this.collectWidgetsByPage(fieldsToFlatten);
26249
26179
  for (const { pageRef, widgets } of pageWidgets.values()) this.flattenWidgetsOnPage(pageRef, widgets);
26250
26180
  const dict = this.form.getDict();
26251
- dict.set("Fields", new PdfArray([]));
26181
+ if (signatureFields.length > 0) {
26182
+ const sigRefs = signatureFields.map((f) => f.getRef()).filter((ref) => ref !== null);
26183
+ dict.set("Fields", PdfArray.of(...sigRefs));
26184
+ } else dict.set("Fields", new PdfArray([]));
26252
26185
  dict.delete("NeedAppearances");
26253
26186
  dict.delete("XFA");
26254
- if (!this.form.hasSignatures) dict.delete("SigFlags");
26187
+ if (!this.form.hasSignatures && signatureFields.length === 0) dict.delete("SigFlags");
26255
26188
  }
26256
26189
  /**
26257
26190
  * Collect all widgets grouped by their containing page.
26191
+ *
26192
+ * @param fields Optional list of fields to collect from. If not provided, uses all form fields.
26258
26193
  */
26259
- collectWidgetsByPage() {
26194
+ collectWidgetsByPage(fields) {
26260
26195
  const result = /* @__PURE__ */ new Map();
26261
- const fields = this.form.getFields();
26262
- for (const field of fields) for (const widget of field.getWidgets()) {
26196
+ const fieldsToProcess = fields ?? this.form.getFields();
26197
+ for (const field of fieldsToProcess) for (const widget of field.getWidgets()) {
26263
26198
  let pageRef = widget.pageRef;
26264
26199
  if (!pageRef) pageRef = this.findPageForWidget(widget);
26265
26200
  if (!pageRef) {
@@ -27838,9 +27773,22 @@ var PDFForm = class PDFForm {
27838
27773
  * await pdf.form.flatten();
27839
27774
  * const bytes = await pdf.save();
27840
27775
  * ```
27776
+ *
27777
+ * @example Flatten while preserving signature fields
27778
+ * ```typescript
27779
+ * await pdf.form.fill({ name: "John Doe" });
27780
+ * await pdf.form.flatten({ skipSignatures: true });
27781
+ * // Signature fields remain interactive for signing
27782
+ * ```
27841
27783
  */
27842
27784
  flatten(options = {}) {
27843
27785
  this._acroForm.flatten(options);
27786
+ if (options.skipSignatures) {
27787
+ const sigFields = this.allFields.filter((f) => f instanceof SignatureField);
27788
+ this.allFields = sigFields;
27789
+ this.fieldsByName = new Map(sigFields.map((f) => [f.name, f]));
27790
+ return;
27791
+ }
27844
27792
  this._ctx.catalog.removeAcroForm();
27845
27793
  this.allFields = [];
27846
27794
  this.fieldsByName.clear();