pdf-lite 1.4.0 → 1.5.0
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/acroform/acroform.d.ts +3 -3
- package/dist/acroform/acroform.js +2 -2
- package/dist/acroform/appearance/index.d.ts +4 -4
- package/dist/acroform/appearance/index.js +4 -4
- package/dist/acroform/appearance/{PdfAppearanceStream.d.ts → pdf-appearance-stream.d.ts} +9 -3
- package/dist/acroform/appearance/{PdfAppearanceStream.js → pdf-appearance-stream.js} +14 -5
- package/dist/acroform/appearance/{PdfButtonAppearanceStream.d.ts → pdf-button-appearance-stream.d.ts} +3 -2
- package/dist/acroform/appearance/pdf-button-appearance-stream.js +54 -0
- package/dist/acroform/appearance/pdf-choice-appearance-stream.d.ts +22 -0
- package/dist/acroform/appearance/pdf-choice-appearance-stream.js +75 -0
- package/dist/acroform/appearance/pdf-graphics.d.ts +51 -0
- package/dist/acroform/appearance/pdf-graphics.js +239 -0
- package/dist/acroform/appearance/{PdfTextAppearanceStream.d.ts → pdf-text-appearance-stream.d.ts} +7 -2
- package/dist/acroform/appearance/pdf-text-appearance-stream.js +104 -0
- package/dist/acroform/fields/index.d.ts +7 -7
- package/dist/acroform/fields/index.js +7 -7
- package/dist/acroform/fields/pdf-button-form-field.d.ts +23 -0
- package/dist/acroform/fields/pdf-button-form-field.js +102 -0
- package/dist/acroform/fields/pdf-choice-form-field.d.ts +18 -0
- package/dist/acroform/fields/pdf-choice-form-field.js +131 -0
- package/dist/acroform/fields/{PdfFormFieldFlags.d.ts → pdf-form-field-flags.d.ts} +5 -6
- package/dist/acroform/fields/{PdfFormFieldFlags.js → pdf-form-field-flags.js} +12 -18
- package/dist/acroform/fields/{PdfFormField.d.ts → pdf-form-field.d.ts} +15 -11
- package/dist/acroform/fields/{PdfFormField.js → pdf-form-field.js} +68 -134
- package/dist/acroform/fields/{PdfSignatureFormField.d.ts → pdf-signature-form-field.d.ts} +1 -1
- package/dist/acroform/fields/{PdfSignatureFormField.js → pdf-signature-form-field.js} +1 -1
- package/dist/acroform/fields/{PdfTextFormField.d.ts → pdf-text-form-field.d.ts} +1 -1
- package/dist/acroform/fields/pdf-text-form-field.js +77 -0
- package/dist/acroform/fields/types.d.ts +5 -0
- package/dist/acroform/index.d.ts +2 -2
- package/dist/acroform/index.js +2 -2
- package/dist/acroform/manager.d.ts +7 -3
- package/dist/acroform/manager.js +14 -8
- package/dist/acroform/{PdfAcroForm.d.ts → pdf-acro-form.d.ts} +12 -6
- package/dist/acroform/{PdfAcroForm.js → pdf-acro-form.js} +43 -29
- package/dist/acroform/{PdfFontEncodingCache.d.ts → pdf-font-encoding-cache.d.ts} +11 -0
- package/dist/acroform/pdf-font-encoding-cache.js +188 -0
- package/dist/acroform/xfa/index.d.ts +3 -3
- package/dist/acroform/xfa/index.js +2 -2
- package/dist/acroform/xfa/{PdfXfaForm.d.ts → pdf-xfa-form.d.ts} +1 -2
- package/dist/acroform/xfa/{PdfXfaForm.js → pdf-xfa-form.js} +2 -10
- package/dist/annotations/index.d.ts +4 -4
- package/dist/annotations/index.js +4 -4
- package/dist/annotations/{PdfAnnotationFlags.d.ts → pdf-annotation-flags.d.ts} +3 -4
- package/dist/annotations/{PdfAnnotationFlags.js → pdf-annotation-flags.js} +5 -6
- package/dist/annotations/{PdfAnnotation.d.ts → pdf-annotation.d.ts} +25 -4
- package/dist/annotations/{PdfAnnotation.js → pdf-annotation.js} +4 -3
- package/dist/annotations/{PdfWidgetAnnotation.d.ts → pdf-widget-annotation.d.ts} +1 -1
- package/dist/annotations/{PdfWidgetAnnotation.js → pdf-widget-annotation.js} +1 -1
- package/dist/core/objects/pdf-stream.d.ts +1 -0
- package/dist/core/objects/pdf-stream.js +6 -2
- package/dist/core/parser/incremental-parser.d.ts +0 -13
- package/dist/core/parser/incremental-parser.js +1 -18
- package/dist/core/streams/object-stream.d.ts +1 -1
- package/dist/core/streams/object-stream.js +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +24 -0
- package/dist/fonts/index.d.ts +1 -1
- package/dist/fonts/index.js +1 -1
- package/dist/fonts/pdf-font.d.ts +64 -7
- package/dist/fonts/pdf-font.js +188 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/pdf/index.d.ts +0 -1
- package/dist/pdf/index.js +0 -1
- package/dist/pdf/pdf-document.d.ts +2 -1
- package/dist/pdf/pdf-document.js +15 -11
- package/dist/utils/encodePdfText.d.ts +17 -0
- package/dist/utils/encodePdfText.js +61 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/package.json +1 -1
- package/dist/acroform/PdfFontEncodingCache.js +0 -75
- package/dist/acroform/appearance/PdfButtonAppearanceStream.js +0 -54
- package/dist/acroform/appearance/PdfChoiceAppearanceStream.d.ts +0 -15
- package/dist/acroform/appearance/PdfChoiceAppearanceStream.js +0 -48
- package/dist/acroform/appearance/PdfTextAppearanceStream.js +0 -75
- package/dist/acroform/fields/PdfButtonFormField.d.ts +0 -9
- package/dist/acroform/fields/PdfButtonFormField.js +0 -35
- package/dist/acroform/fields/PdfChoiceFormField.d.ts +0 -9
- package/dist/acroform/fields/PdfChoiceFormField.js +0 -46
- package/dist/acroform/fields/PdfTextFormField.js +0 -52
- package/dist/pdf/errors.d.ts +0 -6
- package/dist/pdf/errors.js +0 -6
- /package/dist/acroform/fields/{PdfDefaultAppearance.d.ts → pdf-default-appearance.d.ts} +0 -0
- /package/dist/acroform/fields/{PdfDefaultAppearance.js → pdf-default-appearance.js} +0 -0
- /package/dist/acroform/xfa/{PdfXfaData.d.ts → pdf-xfa-data.d.ts} +0 -0
- /package/dist/acroform/xfa/{PdfXfaData.js → pdf-xfa-data.js} +0 -0
- /package/dist/annotations/{PdfAnnotationWriter.d.ts → pdf-annotation-writer.d.ts} +0 -0
- /package/dist/annotations/{PdfAnnotationWriter.js → pdf-annotation-writer.js} +0 -0
- /package/dist/fonts/{font-manager.d.ts → manager.d.ts} +0 -0
- /package/dist/fonts/{font-manager.js → manager.js} +0 -0
- /package/dist/utils/{IterableReadableStream.d.ts → iterable-readable-stream.d.ts} +0 -0
- /package/dist/utils/{IterableReadableStream.js → iterable-readable-stream.js} +0 -0
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Backward-compatible re-export shim.
|
|
3
3
|
* All classes have been moved to dedicated modules.
|
|
4
4
|
*/
|
|
5
|
-
export { PdfFormField as PdfAcroFormField } from './fields/
|
|
6
|
-
export { PdfAcroForm } from './
|
|
7
|
-
export type { PdfDefaultResourcesDictionary } from './
|
|
5
|
+
export { PdfFormField as PdfAcroFormField } from './fields/pdf-form-field.js';
|
|
6
|
+
export { PdfAcroForm } from './pdf-acro-form.js';
|
|
7
|
+
export type { PdfDefaultResourcesDictionary } from './pdf-acro-form.js';
|
|
8
8
|
export { PdfFieldType } from './fields/types.js';
|
|
9
9
|
export type { PdfAppearanceStreamDictionary } from '../annotations/index.js';
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Backward-compatible re-export shim.
|
|
3
3
|
* All classes have been moved to dedicated modules.
|
|
4
4
|
*/
|
|
5
|
-
export { PdfFormField as PdfAcroFormField } from './fields/
|
|
6
|
-
export { PdfAcroForm } from './
|
|
5
|
+
export { PdfFormField as PdfAcroFormField } from './fields/pdf-form-field.js';
|
|
6
|
+
export { PdfAcroForm } from './pdf-acro-form.js';
|
|
7
7
|
export { PdfFieldType } from './fields/types.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { PdfAppearanceStream } from './
|
|
2
|
-
export { PdfTextAppearanceStream } from './
|
|
3
|
-
export { PdfButtonAppearanceStream } from './
|
|
4
|
-
export { PdfChoiceAppearanceStream } from './
|
|
1
|
+
export { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
2
|
+
export { PdfTextAppearanceStream } from './pdf-text-appearance-stream.js';
|
|
3
|
+
export { PdfButtonAppearanceStream } from './pdf-button-appearance-stream.js';
|
|
4
|
+
export { PdfChoiceAppearanceStream } from './pdf-choice-appearance-stream.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { PdfAppearanceStream } from './
|
|
2
|
-
export { PdfTextAppearanceStream } from './
|
|
3
|
-
export { PdfButtonAppearanceStream } from './
|
|
4
|
-
export { PdfChoiceAppearanceStream } from './
|
|
1
|
+
export { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
2
|
+
export { PdfTextAppearanceStream } from './pdf-text-appearance-stream.js';
|
|
3
|
+
export { PdfButtonAppearanceStream } from './pdf-button-appearance-stream.js';
|
|
4
|
+
export { PdfChoiceAppearanceStream } from './pdf-choice-appearance-stream.js';
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
|
|
2
2
|
import { PdfStream } from '../../core/objects/pdf-stream.js';
|
|
3
3
|
import { PdfIndirectObject } from '../../core/objects/pdf-indirect-object.js';
|
|
4
|
+
import { PdfGraphics } from './pdf-graphics.js';
|
|
4
5
|
/**
|
|
5
6
|
* Base class for PDF appearance streams (Form XObjects).
|
|
6
7
|
* Wraps a PdfStream as a PdfIndirectObject so it can be added directly to a document.
|
|
7
8
|
*/
|
|
8
9
|
export declare class PdfAppearanceStream extends PdfIndirectObject<PdfStream> {
|
|
9
10
|
constructor(options: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
x?: number;
|
|
12
|
+
y?: number;
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
contentStream?: string;
|
|
13
16
|
resources?: PdfDictionary;
|
|
14
17
|
});
|
|
18
|
+
get contentStream(): string;
|
|
19
|
+
set contentStream(newContent: string);
|
|
20
|
+
set graphics(g: PdfGraphics);
|
|
15
21
|
}
|
|
@@ -15,18 +15,27 @@ export class PdfAppearanceStream extends PdfIndirectObject {
|
|
|
15
15
|
appearanceDict.set('Subtype', new PdfName('Form'));
|
|
16
16
|
appearanceDict.set('FormType', new PdfNumber(1));
|
|
17
17
|
appearanceDict.set('BBox', new PdfArray([
|
|
18
|
-
new PdfNumber(0),
|
|
19
|
-
new PdfNumber(0),
|
|
20
|
-
new PdfNumber(options.width),
|
|
21
|
-
new PdfNumber(options.height),
|
|
18
|
+
new PdfNumber(options.x ?? 0),
|
|
19
|
+
new PdfNumber(options.y ?? 0),
|
|
20
|
+
new PdfNumber(options.width ?? 100),
|
|
21
|
+
new PdfNumber(options.height ?? 100),
|
|
22
22
|
]));
|
|
23
23
|
if (options.resources) {
|
|
24
24
|
appearanceDict.set('Resources', options.resources);
|
|
25
25
|
}
|
|
26
26
|
const stream = new PdfStream({
|
|
27
27
|
header: appearanceDict,
|
|
28
|
-
original: options.contentStream,
|
|
28
|
+
original: options.contentStream ?? '',
|
|
29
29
|
});
|
|
30
30
|
super({ content: stream });
|
|
31
31
|
}
|
|
32
|
+
get contentStream() {
|
|
33
|
+
return this.content.rawAsString;
|
|
34
|
+
}
|
|
35
|
+
set contentStream(newContent) {
|
|
36
|
+
this.content.rawAsString = newContent;
|
|
37
|
+
}
|
|
38
|
+
set graphics(g) {
|
|
39
|
+
this.contentStream = g.build();
|
|
40
|
+
}
|
|
32
41
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { PdfAppearanceStream } from './
|
|
1
|
+
import { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
2
|
+
import { PdfFormFieldFlags } from '../fields/pdf-form-field-flags.js';
|
|
2
3
|
/**
|
|
3
4
|
* Appearance stream for button fields (checkboxes, radio buttons).
|
|
4
5
|
*/
|
|
@@ -8,5 +9,5 @@ export declare class PdfButtonAppearanceStream extends PdfAppearanceStream {
|
|
|
8
9
|
height: number;
|
|
9
10
|
contentStream: string;
|
|
10
11
|
});
|
|
11
|
-
static buildYesContent(width: number, height: number, flags: number): string;
|
|
12
|
+
static buildYesContent(width: number, height: number, flags: number | PdfFormFieldFlags): string;
|
|
12
13
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
|
|
2
|
+
import { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
3
|
+
import { PdfGraphics } from './pdf-graphics.js';
|
|
4
|
+
import { PdfFont } from '../../fonts/pdf-font.js';
|
|
5
|
+
import { PdfFormFieldFlags } from '../fields/pdf-form-field-flags.js';
|
|
6
|
+
/**
|
|
7
|
+
* Appearance stream for button fields (checkboxes, radio buttons).
|
|
8
|
+
*/
|
|
9
|
+
export class PdfButtonAppearanceStream extends PdfAppearanceStream {
|
|
10
|
+
constructor(ctx) {
|
|
11
|
+
const resources = new PdfDictionary();
|
|
12
|
+
const fonts = new PdfDictionary();
|
|
13
|
+
fonts.set('ZaDb', PdfFont.ZAPF_DINGBATS);
|
|
14
|
+
resources.set('Font', fonts);
|
|
15
|
+
super({
|
|
16
|
+
width: ctx.width,
|
|
17
|
+
height: ctx.height,
|
|
18
|
+
contentStream: ctx.contentStream,
|
|
19
|
+
resources,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
static buildYesContent(width, height, flags) {
|
|
23
|
+
const size = Math.min(width, height);
|
|
24
|
+
const isRadio = new PdfFormFieldFlags(flags).radio;
|
|
25
|
+
const g = new PdfGraphics();
|
|
26
|
+
if (isRadio) {
|
|
27
|
+
const center = size / 2;
|
|
28
|
+
const radius = size * 0.35;
|
|
29
|
+
const k = 0.5522847498;
|
|
30
|
+
const kRadius = k * radius;
|
|
31
|
+
g.save();
|
|
32
|
+
g.setFillRGB(0, 0, 0);
|
|
33
|
+
g.movePath(center, center + radius);
|
|
34
|
+
g.curveTo(center + kRadius, center + radius, center + radius, center + kRadius, center + radius, center);
|
|
35
|
+
g.curveTo(center + radius, center - kRadius, center + kRadius, center - radius, center, center - radius);
|
|
36
|
+
g.curveTo(center - kRadius, center - radius, center - radius, center - kRadius, center - radius, center);
|
|
37
|
+
g.curveTo(center - radius, center + kRadius, center - kRadius, center + radius, center, center + radius);
|
|
38
|
+
g.fill();
|
|
39
|
+
g.restore();
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const checkSize = size * 0.8;
|
|
43
|
+
const offset = (size - checkSize) / 2;
|
|
44
|
+
g.save();
|
|
45
|
+
g.beginText();
|
|
46
|
+
g.setFont('ZaDb', checkSize);
|
|
47
|
+
g.moveTo(offset, offset);
|
|
48
|
+
g.showLiteralText('4'); // Checkmark character in Zapf Dingbats
|
|
49
|
+
g.endText();
|
|
50
|
+
g.restore();
|
|
51
|
+
}
|
|
52
|
+
return g.build();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PdfDefaultAppearance } from '../fields/pdf-default-appearance.js';
|
|
2
|
+
import { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
3
|
+
import type { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
|
|
4
|
+
import type { PdfFont } from '../../fonts/pdf-font.js';
|
|
5
|
+
import { PdfFormFieldFlags } from '../fields/pdf-form-field-flags.js';
|
|
6
|
+
/**
|
|
7
|
+
* Appearance stream for choice fields (dropdowns, list boxes).
|
|
8
|
+
*/
|
|
9
|
+
export declare class PdfChoiceAppearanceStream extends PdfAppearanceStream {
|
|
10
|
+
constructor(ctx: {
|
|
11
|
+
rect: [number, number, number, number];
|
|
12
|
+
value: string;
|
|
13
|
+
da: PdfDefaultAppearance;
|
|
14
|
+
flags: number | PdfFormFieldFlags;
|
|
15
|
+
fontResources?: PdfDictionary;
|
|
16
|
+
resolvedFonts?: Map<string, PdfFont>;
|
|
17
|
+
isUnicode?: boolean;
|
|
18
|
+
reverseEncodingMap?: Map<string, number>;
|
|
19
|
+
displayOptions?: string[];
|
|
20
|
+
selectedIndex?: number;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
2
|
+
import { PdfGraphics } from './pdf-graphics.js';
|
|
3
|
+
import { PdfFormFieldFlags } from '../fields/pdf-form-field-flags.js';
|
|
4
|
+
/**
|
|
5
|
+
* Appearance stream for choice fields (dropdowns, list boxes).
|
|
6
|
+
*/
|
|
7
|
+
export class PdfChoiceAppearanceStream extends PdfAppearanceStream {
|
|
8
|
+
constructor(ctx) {
|
|
9
|
+
const [x1, y1, x2, y2] = ctx.rect;
|
|
10
|
+
const width = x2 - x1;
|
|
11
|
+
const height = y2 - y1;
|
|
12
|
+
super({
|
|
13
|
+
width,
|
|
14
|
+
height,
|
|
15
|
+
resources: ctx.fontResources,
|
|
16
|
+
});
|
|
17
|
+
const isUnicode = ctx.isUnicode ?? false;
|
|
18
|
+
const reverseEncodingMap = ctx.reverseEncodingMap;
|
|
19
|
+
const padding = 2;
|
|
20
|
+
const isCombo = new PdfFormFieldFlags(ctx.flags).combo;
|
|
21
|
+
const g = new PdfGraphics({ resolvedFonts: ctx.resolvedFonts });
|
|
22
|
+
g.beginMarkedContent();
|
|
23
|
+
g.save();
|
|
24
|
+
if (!isCombo && ctx.displayOptions && ctx.displayOptions.length > 0) {
|
|
25
|
+
// Listbox: render all items, highlight the selected one
|
|
26
|
+
const lineHeight = ctx.da.fontSize + 4;
|
|
27
|
+
const selectedIndex = ctx.selectedIndex ?? -1;
|
|
28
|
+
for (let i = 0; i < ctx.displayOptions.length; i++) {
|
|
29
|
+
const itemY = height - (i + 1) * lineHeight;
|
|
30
|
+
// Stop rendering if we've gone below the visible area
|
|
31
|
+
if (itemY + lineHeight < 0)
|
|
32
|
+
break;
|
|
33
|
+
if (i === selectedIndex) {
|
|
34
|
+
// Draw highlight background using the PDF re (rectangle) operator
|
|
35
|
+
g.save();
|
|
36
|
+
g.setFillRGB(0.376, 0.62, 0.671);
|
|
37
|
+
g.raw(`0 ${itemY} ${width} ${lineHeight} re`);
|
|
38
|
+
g.fill();
|
|
39
|
+
g.restore();
|
|
40
|
+
}
|
|
41
|
+
const textY = itemY + lineHeight * 0.25;
|
|
42
|
+
g.beginText();
|
|
43
|
+
g.setDefaultAppearance(ctx.da);
|
|
44
|
+
g.moveTo(padding, textY);
|
|
45
|
+
g.showText(ctx.displayOptions[i], isUnicode, reverseEncodingMap);
|
|
46
|
+
g.endText();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Combo (dropdown) or no options: show selected value only
|
|
51
|
+
const textY = (height - ctx.da.fontSize) / 2 + ctx.da.fontSize * 0.2;
|
|
52
|
+
g.beginText();
|
|
53
|
+
g.setDefaultAppearance(ctx.da);
|
|
54
|
+
g.moveTo(padding, textY);
|
|
55
|
+
g.showText(ctx.value, isUnicode, reverseEncodingMap);
|
|
56
|
+
g.endText();
|
|
57
|
+
if (isCombo) {
|
|
58
|
+
const arrowWidth = height * 0.8;
|
|
59
|
+
const arrowX = width - arrowWidth - 2;
|
|
60
|
+
const arrowY = height / 2;
|
|
61
|
+
const arrowSize = height * 0.3;
|
|
62
|
+
g.save();
|
|
63
|
+
g.setFillGray(0.5);
|
|
64
|
+
g.movePath(arrowX + arrowWidth / 2, arrowY - arrowSize / 3);
|
|
65
|
+
g.lineTo(arrowX + arrowWidth / 2 - arrowSize / 2, arrowY + arrowSize / 3);
|
|
66
|
+
g.lineTo(arrowX + arrowWidth / 2 + arrowSize / 2, arrowY + arrowSize / 3);
|
|
67
|
+
g.fill();
|
|
68
|
+
g.restore();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
g.restore();
|
|
72
|
+
g.endMarkedContent();
|
|
73
|
+
this.graphics = g;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { PdfDefaultAppearance } from '../fields/pdf-default-appearance.js';
|
|
2
|
+
import { PdfFont } from '../../fonts/pdf-font.js';
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight builder for PDF content streams.
|
|
5
|
+
* Chains PDF operators via a fluent API and emits the final stream with build().
|
|
6
|
+
* Enhanced with text measurement capabilities for layout calculations.
|
|
7
|
+
*/
|
|
8
|
+
export declare class PdfGraphics {
|
|
9
|
+
private lines;
|
|
10
|
+
private resolvedFonts?;
|
|
11
|
+
private defaultAppearance?;
|
|
12
|
+
constructor(options?: {
|
|
13
|
+
resolvedFonts?: Map<string, PdfFont>;
|
|
14
|
+
defaultAppearance?: PdfDefaultAppearance;
|
|
15
|
+
});
|
|
16
|
+
save(): this;
|
|
17
|
+
restore(): this;
|
|
18
|
+
beginText(): this;
|
|
19
|
+
endText(): this;
|
|
20
|
+
setDefaultAppearance(da: PdfDefaultAppearance): this;
|
|
21
|
+
moveTo(x: number, y: number): this;
|
|
22
|
+
showText(text: string, isUnicode: boolean, reverseEncodingMap?: Map<string, number>): this;
|
|
23
|
+
showLiteralText(text: string): this;
|
|
24
|
+
beginMarkedContent(): this;
|
|
25
|
+
endMarkedContent(): this;
|
|
26
|
+
raw(op: string): this;
|
|
27
|
+
setFillRGB(r: number, g: number, b: number): this;
|
|
28
|
+
setFillGray(v: number): this;
|
|
29
|
+
setFont(name: string, size: number): this;
|
|
30
|
+
movePath(x: number, y: number): this;
|
|
31
|
+
lineTo(x: number, y: number): this;
|
|
32
|
+
curveTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): this;
|
|
33
|
+
fill(): this;
|
|
34
|
+
stroke(): this;
|
|
35
|
+
closePath(): this;
|
|
36
|
+
build(): string;
|
|
37
|
+
private get currentFont();
|
|
38
|
+
/**
|
|
39
|
+
* Calculate the width of text using the current font and size.
|
|
40
|
+
*/
|
|
41
|
+
measureTextWidth(text: string, fontSize?: number): number;
|
|
42
|
+
/**
|
|
43
|
+
* Wrap text to fit within the specified width, breaking at word boundaries.
|
|
44
|
+
*/
|
|
45
|
+
wrapTextToLines(text: string, maxWidth: number, fontSize?: number): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Calculate the minimum font size needed to fit text within given constraints.
|
|
48
|
+
*/
|
|
49
|
+
calculateFittingFontSize(text: string, maxWidth: number, maxHeight?: number, lineHeight?: number): number;
|
|
50
|
+
private breakLongWord;
|
|
51
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { encodePdfText } from '../../utils/encodePdfText.js';
|
|
2
|
+
import { PdfFont } from '../../fonts/pdf-font.js';
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight builder for PDF content streams.
|
|
5
|
+
* Chains PDF operators via a fluent API and emits the final stream with build().
|
|
6
|
+
* Enhanced with text measurement capabilities for layout calculations.
|
|
7
|
+
*/
|
|
8
|
+
export class PdfGraphics {
|
|
9
|
+
lines = [];
|
|
10
|
+
resolvedFonts;
|
|
11
|
+
defaultAppearance;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.resolvedFonts = options?.resolvedFonts;
|
|
14
|
+
this.defaultAppearance = options?.defaultAppearance;
|
|
15
|
+
}
|
|
16
|
+
save() {
|
|
17
|
+
this.lines.push('q');
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
restore() {
|
|
21
|
+
this.lines.push('Q');
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
beginText() {
|
|
25
|
+
this.lines.push('BT');
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
endText() {
|
|
29
|
+
this.lines.push('ET');
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
setDefaultAppearance(da) {
|
|
33
|
+
this.lines.push(da.toString());
|
|
34
|
+
this.defaultAppearance = da;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
moveTo(x, y) {
|
|
38
|
+
this.lines.push(`${x} ${y} Td`);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
showText(text, isUnicode, reverseEncodingMap) {
|
|
42
|
+
this.lines.push(`${encodePdfText(text, isUnicode, reverseEncodingMap)} Tj`);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
showLiteralText(text) {
|
|
46
|
+
this.lines.push(`(${text}) Tj`);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
beginMarkedContent() {
|
|
50
|
+
this.lines.push('/Tx BMC');
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
endMarkedContent() {
|
|
54
|
+
this.lines.push('EMC');
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
raw(op) {
|
|
58
|
+
this.lines.push(op);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
setFillRGB(r, g, b) {
|
|
62
|
+
this.lines.push(`${r} ${g} ${b} rg`);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
setFillGray(v) {
|
|
66
|
+
this.lines.push(`${v} g`);
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
setFont(name, size) {
|
|
70
|
+
this.lines.push(`/${name} ${size} Tf`);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
movePath(x, y) {
|
|
74
|
+
this.lines.push(`${x} ${y} m`);
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
lineTo(x, y) {
|
|
78
|
+
this.lines.push(`${x} ${y} l`);
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
curveTo(x1, y1, x2, y2, x3, y3) {
|
|
82
|
+
this.lines.push(`${x1} ${y1} ${x2} ${y2} ${x3} ${y3} c`);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
fill() {
|
|
86
|
+
this.lines.push('f');
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
stroke() {
|
|
90
|
+
this.lines.push('S');
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
closePath() {
|
|
94
|
+
this.lines.push('h');
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
build() {
|
|
98
|
+
return this.lines.join('\n') + '\n';
|
|
99
|
+
}
|
|
100
|
+
get currentFont() {
|
|
101
|
+
if (!this.defaultAppearance) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const fontName = this.defaultAppearance.fontName;
|
|
105
|
+
const size = this.defaultAppearance.fontSize;
|
|
106
|
+
const fontObject = this.resolvedFonts?.get(fontName);
|
|
107
|
+
return { fontObject, size };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Calculate the width of text using the current font and size.
|
|
111
|
+
*/
|
|
112
|
+
measureTextWidth(text, fontSize) {
|
|
113
|
+
if (!this.currentFont) {
|
|
114
|
+
throw new Error('No font set - call setDefaultAppearance() first');
|
|
115
|
+
}
|
|
116
|
+
const { fontObject, size: currentSize } = this.currentFont;
|
|
117
|
+
const size = fontSize ?? currentSize;
|
|
118
|
+
const fontName = this.defaultAppearance.fontName;
|
|
119
|
+
const effectiveFontObject = fontObject ?? PdfFont.getStandardFont(fontName);
|
|
120
|
+
if (effectiveFontObject?.widths &&
|
|
121
|
+
effectiveFontObject.firstChar !== undefined) {
|
|
122
|
+
let width = 0;
|
|
123
|
+
for (const char of text) {
|
|
124
|
+
const charCode = char.charCodeAt(0);
|
|
125
|
+
const charWidth = effectiveFontObject.getCharacterWidth(charCode, size);
|
|
126
|
+
if (charWidth !== null) {
|
|
127
|
+
width += charWidth;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
width += size * 0.6;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return width;
|
|
134
|
+
}
|
|
135
|
+
// Unknown font — use Helvetica as best-effort approximation
|
|
136
|
+
let width = 0;
|
|
137
|
+
for (const char of text) {
|
|
138
|
+
const charWidth = PdfFont.HELVETICA.getCharacterWidth(char.charCodeAt(0), size);
|
|
139
|
+
width += charWidth !== null ? charWidth : size * 0.6;
|
|
140
|
+
}
|
|
141
|
+
return width;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Wrap text to fit within the specified width, breaking at word boundaries.
|
|
145
|
+
*/
|
|
146
|
+
wrapTextToLines(text, maxWidth, fontSize) {
|
|
147
|
+
if (!this.currentFont) {
|
|
148
|
+
throw new Error('No font set - call setDefaultAppearance() first');
|
|
149
|
+
}
|
|
150
|
+
// Handle explicit line breaks first
|
|
151
|
+
const paragraphs = text.split('\n');
|
|
152
|
+
const wrappedLines = [];
|
|
153
|
+
for (const paragraph of paragraphs) {
|
|
154
|
+
if (this.measureTextWidth(paragraph, fontSize) <= maxWidth) {
|
|
155
|
+
wrappedLines.push(paragraph);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
// Word wrapping needed
|
|
159
|
+
const words = paragraph.split(' ');
|
|
160
|
+
let currentLine = '';
|
|
161
|
+
for (const word of words) {
|
|
162
|
+
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
163
|
+
if (this.measureTextWidth(testLine, fontSize) <= maxWidth) {
|
|
164
|
+
currentLine = testLine;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
if (currentLine) {
|
|
168
|
+
wrappedLines.push(currentLine);
|
|
169
|
+
currentLine = word;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// Single word is too long, break it
|
|
173
|
+
const brokenLines = this.breakLongWord(word, maxWidth, fontSize);
|
|
174
|
+
wrappedLines.push(...brokenLines.slice(0, -1));
|
|
175
|
+
currentLine = brokenLines[brokenLines.length - 1];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (currentLine) {
|
|
180
|
+
wrappedLines.push(currentLine);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return wrappedLines;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Calculate the minimum font size needed to fit text within given constraints.
|
|
187
|
+
*/
|
|
188
|
+
calculateFittingFontSize(text, maxWidth, maxHeight, lineHeight = 1.2) {
|
|
189
|
+
if (!this.currentFont) {
|
|
190
|
+
throw new Error('No font set - call setDefaultAppearance() first');
|
|
191
|
+
}
|
|
192
|
+
const startSize = this.currentFont.size;
|
|
193
|
+
const minSize = 0.5;
|
|
194
|
+
const fits = (size) => {
|
|
195
|
+
if (this.measureTextWidth(text, size) > maxWidth)
|
|
196
|
+
return false;
|
|
197
|
+
if (maxHeight !== undefined) {
|
|
198
|
+
const lines = this.wrapTextToLines(text, maxWidth, size);
|
|
199
|
+
return lines.length * size * lineHeight <= maxHeight;
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
};
|
|
203
|
+
if (fits(startSize))
|
|
204
|
+
return startSize;
|
|
205
|
+
if (!fits(minSize))
|
|
206
|
+
return minSize;
|
|
207
|
+
// Binary search on 0.5pt grid: lo fits, hi does not
|
|
208
|
+
let lo = minSize;
|
|
209
|
+
let hi = startSize;
|
|
210
|
+
while (hi - lo > 0.5) {
|
|
211
|
+
const mid = Math.round((lo + hi) / 2 / 0.5) * 0.5;
|
|
212
|
+
if (fits(mid))
|
|
213
|
+
lo = mid;
|
|
214
|
+
else
|
|
215
|
+
hi = mid;
|
|
216
|
+
}
|
|
217
|
+
return lo;
|
|
218
|
+
}
|
|
219
|
+
breakLongWord(word, maxWidth, fontSize) {
|
|
220
|
+
const lines = [];
|
|
221
|
+
let currentLine = '';
|
|
222
|
+
for (const char of word) {
|
|
223
|
+
const testLine = currentLine + char;
|
|
224
|
+
if (this.measureTextWidth(testLine, fontSize) <= maxWidth) {
|
|
225
|
+
currentLine = testLine;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
if (currentLine) {
|
|
229
|
+
lines.push(currentLine);
|
|
230
|
+
}
|
|
231
|
+
currentLine = char;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (currentLine) {
|
|
235
|
+
lines.push(currentLine);
|
|
236
|
+
}
|
|
237
|
+
return lines.length > 0 ? lines : [word];
|
|
238
|
+
}
|
|
239
|
+
}
|
package/dist/acroform/appearance/{PdfTextAppearanceStream.d.ts → pdf-text-appearance-stream.d.ts}
RENAMED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { PdfDefaultAppearance } from '../fields/
|
|
2
|
-
import { PdfAppearanceStream } from './
|
|
1
|
+
import { PdfDefaultAppearance } from '../fields/pdf-default-appearance.js';
|
|
2
|
+
import { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
3
3
|
import type { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
|
|
4
|
+
import type { PdfFont } from '../../fonts/pdf-font.js';
|
|
4
5
|
/**
|
|
5
6
|
* Appearance stream for text fields (single-line, multiline, comb).
|
|
7
|
+
* Enhanced with word wrapping and automatic font scaling.
|
|
6
8
|
*/
|
|
7
9
|
export declare class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
8
10
|
constructor(ctx: {
|
|
@@ -13,5 +15,8 @@ export declare class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
|
13
15
|
comb: boolean;
|
|
14
16
|
maxLen: number | null;
|
|
15
17
|
fontResources?: PdfDictionary;
|
|
18
|
+
resolvedFonts?: Map<string, PdfFont>;
|
|
19
|
+
isUnicode?: boolean;
|
|
20
|
+
reverseEncodingMap?: Map<string, number>;
|
|
16
21
|
});
|
|
17
22
|
}
|