diff --git a/packages/placeholder-pdf-lib/dist/pdflibAddPlaceholder.js b/packages/placeholder-pdf-lib/dist/pdflibAddPlaceholder.js index b608c51..ebbdf5d 100644 --- a/packages/placeholder-pdf-lib/dist/pdflibAddPlaceholder.js +++ b/packages/placeholder-pdf-lib/dist/pdflibAddPlaceholder.js @@ -95,11 +95,11 @@ const pdflibAddPlaceholder = ({ SubFilter: subFilter, ByteRange: byteRange, Contents: placeholder, - Reason: _pdfLib.PDFString.of(reason), + Reason: _pdfLib.PDFHexString.fromText(reason), M: _pdfLib.PDFString.fromDate(signingTime !== null && signingTime !== void 0 ? signingTime : new Date()), - ContactInfo: _pdfLib.PDFString.of(contactInfo), - Name: _pdfLib.PDFString.of(name), - Location: _pdfLib.PDFString.of(location), + ContactInfo: _pdfLib.PDFHexString.fromText(contactInfo), + Name: _pdfLib.PDFHexString.fromText(name), + Location: _pdfLib.PDFHexString.fromText(location), Prop_Build: { Filter: { Name: 'Adobe.PPKLite' diff --git a/packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.js b/packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.js index 99e267c..c62a079 100644 --- a/packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.js +++ b/packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.js @@ -98,11 +98,11 @@ export const pdflibAddPlaceholder = ({ SubFilter: subFilter, ByteRange: byteRange, Contents: placeholder, - Reason: PDFString.of(reason), + Reason: PDFHexString.fromText(reason), M: PDFString.fromDate(signingTime ?? new Date()), - ContactInfo: PDFString.of(contactInfo), - Name: PDFString.of(name), - Location: PDFString.of(location), + ContactInfo: PDFHexString.fromText(contactInfo), + Name: PDFHexString.fromText(name), + Location: PDFHexString.fromText(location), Prop_Build: { Filter: {Name: 'Adobe.PPKLite'}, ...appBuild, diff --git a/packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.test.js b/packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.test.js index e9e46fb..e5ef988 100644 --- a/packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.test.js +++ b/packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.test.js @@ -1,5 +1,5 @@ import { - PDFArray, PDFDict, PDFDocument, PDFName, PDFObjectParser, PDFStream, PDFString, + PDFArray, PDFDict, PDFDocument, PDFHexString, PDFName, PDFObjectParser, PDFStream, PDFString, } from 'pdf-lib'; import {readTestResource} from '@signpdf/internal-utils'; import {DEFAULT_BYTE_RANGE_PLACEHOLDER, SUBFILTER_ETSI_CADES_DETACHED, SignPdfError} from '@signpdf/utils'; @@ -116,10 +116,10 @@ describe(pdflibAddPlaceholder, () => { const widgetData = parseObject(pdfDoc, widget.lookup(PDFName.of('V'))); expect(widget.get(PDFName.of('Subtype'))).toEqual(PDFName.of('Widget')); - expect(widgetData.get(PDFName.of('Reason'))).toEqual(PDFString.of(defaults.reason)); - expect(widgetData.get(PDFName.of('ContactInfo'))).toEqual(PDFString.of(defaults.contactInfo)); - expect(widgetData.get(PDFName.of('Location'))).toEqual(PDFString.of(defaults.location)); - expect(widgetData.get(PDFName.of('Name'))).toEqual(PDFString.of(defaults.name)); + expect(widgetData.get(PDFName.of('Reason'))).toEqual(PDFHexString.fromText(defaults.reason)); + expect(widgetData.get(PDFName.of('ContactInfo'))).toEqual(PDFHexString.fromText(defaults.contactInfo)); + expect(widgetData.get(PDFName.of('Location'))).toEqual(PDFHexString.fromText(defaults.location)); + expect(widgetData.get(PDFName.of('Name'))).toEqual(PDFHexString.fromText(defaults.name)); }); it('allows defining signing time', async () => { @@ -383,4 +383,57 @@ describe(pdflibAddPlaceholder, () => { expect(fields).toBeInstanceOf(PDFArray); expect(fields.size()).toBe(1); }); + + it('handles Japanese characters in signature info fields', async () => { + const input = readTestResource('w3dummy.pdf'); + const pdfDoc = await PDFDocument.load(input); + + const japaneseDefaults = { + reason: '日本語の理由', // Japanese reason + contactInfo: '連絡先@example.com', // Japanese contact info + name: '田中太郎', // Japanese name + location: '東京、日本', // Japanese location (Tokyo, Japan) + }; + + pdflibAddPlaceholder({ + pdfDoc, + ...japaneseDefaults, + }); + + /** + * @type {PDFArray} + */ + const annots = pdfDoc.getPage(0).node.lookup(PDFName.of('Annots')); + + /** + * @type {PDFDict} + */ + const widget = annots.lookup(annots.size() - 1, PDFDict); + + /** + * @type {PDFDict} + */ + const widgetData = parseObject(pdfDoc, widget.lookup(PDFName.of('V'))); + + expect(widget.get(PDFName.of('Subtype'))).toEqual(PDFName.of('Widget')); + + // The signature fields should be properly encoded as hex strings + // to preserve Japanese characters + const reason = widgetData.get(PDFName.of('Reason')); + const contactInfo = widgetData.get(PDFName.of('ContactInfo')); + const name = widgetData.get(PDFName.of('Name')); + const location = widgetData.get(PDFName.of('Location')); + + // These should be PDFHexString instances, not PDFString + expect(reason.constructor.name).toBe('PDFHexString'); + expect(contactInfo.constructor.name).toBe('PDFHexString'); + expect(name.constructor.name).toBe('PDFHexString'); + expect(location.constructor.name).toBe('PDFHexString'); + + // The decoded text should match the original Japanese text + expect(reason.decodeText()).toBe(japaneseDefaults.reason); + expect(contactInfo.decodeText()).toBe(japaneseDefaults.contactInfo); + expect(name.decodeText()).toBe(japaneseDefaults.name); + expect(location.decodeText()).toBe(japaneseDefaults.location); + }); });