@jasy/pdf 1.0.0-alpha.8 → 1.0.0-beta.1
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/README.md +29 -10
- package/dist/api/color.js +69 -0
- package/dist/api/content.d.ts +45 -5
- package/dist/api/content.js +96 -7
- package/dist/api/descriptor.js +6 -1
- package/dist/api/dimension.d.ts +57 -0
- package/dist/api/dimension.js +27 -0
- package/dist/api/font-url.d.ts +28 -0
- package/dist/api/font-url.js +135 -0
- package/dist/api/gradient.d.ts +53 -0
- package/dist/api/gradient.js +97 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/insets.d.ts +19 -12
- package/dist/api/insets.js +18 -7
- package/dist/api/layout.d.ts +20 -6
- package/dist/api/layout.js +31 -6
- package/dist/api/page-builder.d.ts +8 -0
- package/dist/api/page-builder.js +12 -0
- package/dist/api/structure.d.ts +12 -0
- package/dist/api/structure.js +54 -7
- package/dist/api/text.d.ts +65 -8
- package/dist/api/text.js +38 -5
- package/dist/canvas/painter.d.ts +102 -0
- package/dist/canvas/painter.js +183 -0
- package/dist/edit/document.d.ts +14 -4
- package/dist/edit/document.js +15 -22
- package/dist/edit/fill.d.ts +19 -2
- package/dist/edit/fill.js +63 -34
- package/dist/edit/flatten.d.ts +13 -1
- package/dist/edit/flatten.js +38 -13
- package/dist/edit/index.d.ts +2 -0
- package/dist/edit/index.js +3 -1
- package/dist/edit/objects.d.ts +5 -0
- package/dist/edit/objects.js +27 -0
- package/dist/edit/writer.d.ts +7 -0
- package/dist/edit/writer.js +23 -1
- package/dist/elements/canvas-element.d.ts +38 -0
- package/dist/elements/canvas-element.js +47 -0
- package/dist/elements/container-element.d.ts +23 -1
- package/dist/elements/container-element.js +31 -10
- package/dist/elements/image-element.d.ts +18 -4
- package/dist/elements/image-element.js +27 -12
- package/dist/elements/index.d.ts +2 -0
- package/dist/elements/index.js +2 -0
- package/dist/elements/layout/expanded-element.d.ts +5 -1
- package/dist/elements/layout/expanded-element.js +16 -3
- package/dist/elements/layout/keep-together-element.d.ts +2 -0
- package/dist/elements/layout/keep-together-element.js +4 -0
- package/dist/elements/layout/padding-element.d.ts +9 -2
- package/dist/elements/layout/padding-element.js +12 -2
- package/dist/elements/layout/page-builder-element.js +2 -0
- package/dist/elements/layout/struct-group.d.ts +2 -0
- package/dist/elements/layout/struct-group.js +4 -0
- package/dist/elements/pdf-element.d.ts +75 -1
- package/dist/elements/pdf-element.js +80 -1
- package/dist/elements/rectangle-element.d.ts +22 -6
- package/dist/elements/rectangle-element.js +31 -12
- package/dist/elements/row-element.d.ts +26 -1
- package/dist/elements/row-element.js +31 -10
- package/dist/elements/svg-element.d.ts +41 -0
- package/dist/elements/svg-element.js +93 -0
- package/dist/elements/text-element.d.ts +92 -1
- package/dist/elements/text-element.js +208 -33
- package/dist/ir/display-list.d.ts +67 -11
- package/dist/ir/display-list.js +8 -1
- package/dist/layout/alignment.d.ts +10 -0
- package/dist/layout/alignment.js +6 -0
- package/dist/layout/box-constraints.d.ts +53 -0
- package/dist/layout/box-constraints.js +72 -0
- package/dist/layout/insets.d.ts +28 -0
- package/dist/layout/insets.js +20 -0
- package/dist/layout/min-intrinsic.d.ts +29 -0
- package/dist/layout/min-intrinsic.js +37 -0
- package/dist/renderer/anchor-renderer.js +1 -1
- package/dist/renderer/bookmark-renderer.js +1 -1
- package/dist/renderer/canvas-renderer.d.ts +14 -0
- package/dist/renderer/canvas-renderer.js +36 -0
- package/dist/renderer/container-renderer.js +1 -3
- package/dist/renderer/default-text-style-renderer.js +1 -1
- package/dist/renderer/deferred-renderer.js +1 -1
- package/dist/renderer/expanded-renderer.js +1 -1
- package/dist/renderer/image-renderer.js +2 -1
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer/index.js +2 -0
- package/dist/renderer/keep-together-renderer.js +1 -1
- package/dist/renderer/link-renderer.js +1 -1
- package/dist/renderer/padding-renderer.js +1 -1
- package/dist/renderer/page-builder-renderer.js +1 -1
- package/dist/renderer/page-renderer.js +1 -3
- package/dist/renderer/pdf-backend.d.ts +21 -4
- package/dist/renderer/pdf-backend.js +198 -61
- package/dist/renderer/pdf-document-renderer.js +8 -1
- package/dist/renderer/pdf-renderer.js +5 -1
- package/dist/renderer/positioned-renderer.js +1 -1
- package/dist/renderer/rectangle-renderer.js +9 -5
- package/dist/renderer/repeating-header-renderer.js +1 -2
- package/dist/renderer/rotated-renderer.js +1 -1
- package/dist/renderer/row-renderer.js +1 -3
- package/dist/renderer/struct-group-renderer.js +1 -1
- package/dist/renderer/svg-renderer.d.ts +11 -0
- package/dist/renderer/svg-renderer.js +27 -0
- package/dist/renderer/text-renderer.d.ts +2 -2
- package/dist/renderer/text-renderer.js +176 -57
- package/dist/svg/css.d.ts +38 -0
- package/dist/svg/css.js +115 -0
- package/dist/svg/errors.d.ts +24 -0
- package/dist/svg/errors.js +30 -0
- package/dist/svg/gradients.d.ts +34 -0
- package/dist/svg/gradients.js +156 -0
- package/dist/svg/index.d.ts +22 -0
- package/dist/svg/index.js +331 -0
- package/dist/svg/shapes.d.ts +6 -0
- package/dist/svg/shapes.js +177 -0
- package/dist/svg/style.d.ts +61 -0
- package/dist/svg/style.js +144 -0
- package/dist/svg/transform.d.ts +13 -0
- package/dist/svg/transform.js +68 -0
- package/dist/svg/units.d.ts +11 -0
- package/dist/svg/units.js +73 -0
- package/dist/svg/xml.d.ts +24 -0
- package/dist/svg/xml.js +158 -0
- package/dist/text/advance.d.ts +3 -0
- package/dist/text/advance.js +8 -4
- package/dist/text/arabic.d.ts +20 -0
- package/dist/text/arabic.js +101 -0
- package/dist/text/bidi.d.ts +33 -0
- package/dist/text/bidi.js +94 -0
- package/dist/text/font-fallback.d.ts +25 -0
- package/dist/text/font-fallback.js +38 -0
- package/dist/text/glyph-coverage.d.ts +15 -0
- package/dist/text/glyph-coverage.js +77 -0
- package/dist/text/line-breaker.d.ts +43 -1
- package/dist/text/line-breaker.js +181 -45
- package/dist/text/orphans-widows.d.ts +27 -0
- package/dist/text/orphans-widows.js +53 -0
- package/dist/text/shape.d.ts +29 -0
- package/dist/text/shape.js +91 -0
- package/dist/text/text-style.d.ts +38 -0
- package/dist/text/text-style.js +50 -1
- package/dist/text/whitespace.d.ts +4 -0
- package/dist/text/whitespace.js +43 -0
- package/dist/text/word-splitting.d.ts +42 -0
- package/dist/text/word-splitting.js +72 -0
- package/dist/utils/flex-layout.d.ts +24 -4
- package/dist/utils/flex-layout.js +218 -7
- package/dist/utils/font-metrics.d.ts +13 -2
- package/dist/utils/gsub.d.ts +36 -0
- package/dist/utils/gsub.js +178 -0
- package/dist/utils/inflate.d.ts +16 -0
- package/dist/utils/inflate.js +52 -0
- package/dist/utils/pdf-object-manager.d.ts +44 -3
- package/dist/utils/pdf-object-manager.js +176 -14
- package/dist/utils/renderer-registry.d.ts +13 -1
- package/dist/utils/renderer-registry.js +22 -1
- package/dist/utils/ttf-parser.d.ts +6 -0
- package/dist/utils/ttf-parser.js +19 -0
- package/dist/utils/utf8-to-windows1252-encoder.d.ts +6 -0
- package/dist/utils/utf8-to-windows1252-encoder.js +10 -0
- package/dist/utils/woff.d.ts +10 -0
- package/dist/utils/woff.js +116 -0
- package/dist/utils/woff2.d.ts +6 -0
- package/dist/utils/woff2.js +651 -0
- package/dist/validators/element-validator.js +9 -2
- package/dist/vector/path.d.ts +33 -0
- package/dist/vector/path.js +81 -0
- package/package.json +6 -2
package/dist/api/text.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { TextElement, TextRole, TextSegment } from "../elements/text-element.js";
|
|
1
|
+
import { TextElement, TextRole, TextSegment, type VerticalTextAlign } from "../elements/text-element.js";
|
|
2
2
|
import { TextOverflow } from "../text/line-breaker.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { Direction } from "../text/bidi.js";
|
|
4
|
+
import { ResolvedTextStyle, type TextTransform } from "../text/text-style.js";
|
|
4
5
|
import { ColorInput } from "./color.js";
|
|
5
|
-
|
|
6
|
+
import type { Hyphenator } from "../text/word-splitting.js";
|
|
7
|
+
export type { TextOverflow, Direction, TextTransform, Hyphenator };
|
|
6
8
|
/** Text styling shared by `Text`, `Paragraph` and `span`. `bold`/`italic` are booleans
|
|
7
9
|
* (locked §7.3) and combine into one engine `FontStyle`. */
|
|
8
10
|
export interface TextStyle {
|
|
9
11
|
size?: number;
|
|
10
|
-
|
|
12
|
+
/** Font family, or a STACK: each code point is drawn by the first family in the list that has a
|
|
13
|
+
* glyph for it, so `["Inter", "NotoSansCJK"]` sets mixed-script text without splitting it by hand. */
|
|
14
|
+
font?: string | string[];
|
|
11
15
|
bold?: boolean;
|
|
12
16
|
italic?: boolean;
|
|
13
17
|
color?: ColorInput;
|
|
@@ -22,6 +26,29 @@ export interface TextStyle {
|
|
|
22
26
|
skipInk?: boolean;
|
|
23
27
|
/** Extra space after every glyph, in points (CSS `letter-spacing`). Negative tightens. Default 0. */
|
|
24
28
|
letterSpacing?: number;
|
|
29
|
+
/** Base writing direction (CSS `direction`): `"rtl"` starts the line on the right, for Hebrew or
|
|
30
|
+
* Arabic. Mixed text is reordered per Unicode UAX #9 either way, so Hebrew inside an `ltr`
|
|
31
|
+
* paragraph already comes out right - this decides where the LINE starts. */
|
|
32
|
+
direction?: Direction;
|
|
33
|
+
/** CSS `text-transform`: recase the text. `capitalize` upper-cases the first letter of each word. */
|
|
34
|
+
textTransform?: TextTransform;
|
|
35
|
+
/** CSS `word-spacing`, in points: extra advance at every space. Negative tightens. */
|
|
36
|
+
wordSpacing?: number;
|
|
37
|
+
/** CSS `text-indent`, in points: how far the FIRST line of a paragraph starts in. */
|
|
38
|
+
textIndent?: number;
|
|
39
|
+
/** Split a word that is wider than its box, anywhere, no hyphen (CSS `overflow-wrap:
|
|
40
|
+
* break-word`). The floor under `hyphenate`: an e-mail, an IBAN or an invoice number has no
|
|
41
|
+
* valid hyphenation point anywhere. Off by default - a too-wide word overflows, as in CSS. */
|
|
42
|
+
breakWord?: boolean;
|
|
43
|
+
/** The ligatures the font's designer drew (`fi`, `fl`, `ffi`). ON by default, like kerning -
|
|
44
|
+
* `false` opts this text out. Embedded fonts only; the standard-14 have no GSUB. */
|
|
45
|
+
ligatures?: boolean;
|
|
46
|
+
/** Language-aware splitting: given a word, return its parts. A hyphen is drawn at the break.
|
|
47
|
+
* Pluggable, so no pattern data ships here: pass `hyphen`, `hyphenopoly`, or your own. */
|
|
48
|
+
hyphenate?: Hyphenator;
|
|
49
|
+
/** CSS `vertical-align` for a SPAN: raises a footnote marker or lowers an index. It shifts the
|
|
50
|
+
* baseline only - pass a smaller `size` too if you want the browser's `<sup>` look. */
|
|
51
|
+
verticalAlign?: VerticalTextAlign;
|
|
25
52
|
/** External URL - makes this run an inline hyperlink (a /Link annotation over its glyphs). On a
|
|
26
53
|
* `span` it links just that run; on a whole `Text` (plain string) it links the whole text. */
|
|
27
54
|
href?: string;
|
|
@@ -29,12 +56,20 @@ export interface TextStyle {
|
|
|
29
56
|
* same-document /GoTo instead of a URL). Use for a clickable table of contents / cross-reference. */
|
|
30
57
|
to?: string;
|
|
31
58
|
}
|
|
32
|
-
export interface TextOptions extends TextStyle {
|
|
59
|
+
export interface TextOptions extends Omit<TextStyle, "verticalAlign"> {
|
|
33
60
|
/** Text-internal alignment (left/center/right) - independent of a parent's `cross` (§5). */
|
|
34
|
-
align?: "left" | "center" | "right";
|
|
61
|
+
align?: "left" | "center" | "right" | "justify";
|
|
35
62
|
/** Cap the number of wrapped lines (default: unlimited - the text grows down as far as it needs,
|
|
36
63
|
* paginating onto the next page). Needs a bounded width (a Column/Expanded/`Box({ width })`). */
|
|
37
64
|
maxLines?: number;
|
|
65
|
+
/**
|
|
66
|
+
* Minimum lines that must stay behind at a page break (CSS `orphans`), and minimum that must carry
|
|
67
|
+
* over (CSS `widows`). Both default to 2, the CSS initial value: a single line stranded at the foot of
|
|
68
|
+
* a page - or alone at the top of the next - reads as a mistake. Set both to 1 to switch the protection
|
|
69
|
+
* off and let a paragraph break wherever the page ends.
|
|
70
|
+
*/
|
|
71
|
+
orphans?: number;
|
|
72
|
+
widows?: number;
|
|
38
73
|
/** What happens past `maxLines`: `"clip"` (default) cuts hard, `"ellipsis"` ends with "…". */
|
|
39
74
|
overflow?: TextOverflow;
|
|
40
75
|
/** Line-height multiplier: each line is `size * lineHeight` tall. Unset means the font's own
|
|
@@ -62,16 +97,38 @@ export declare function Paragraph(content: string | TextSegment[], opts?: TextOp
|
|
|
62
97
|
* Unset fields stay out so they keep inheriting. */
|
|
63
98
|
export interface TextDefaults {
|
|
64
99
|
size?: number;
|
|
65
|
-
|
|
100
|
+
/** Font family, or a STACK: each code point is drawn by the first family in the list that has a
|
|
101
|
+
* glyph for it, so `["Inter", "NotoSansCJK"]` sets mixed-script text without splitting it by hand. */
|
|
102
|
+
font?: string | string[];
|
|
66
103
|
bold?: boolean;
|
|
67
104
|
italic?: boolean;
|
|
68
105
|
color?: ColorInput;
|
|
69
|
-
align?: "left" | "center" | "right";
|
|
106
|
+
align?: "left" | "center" | "right" | "justify";
|
|
70
107
|
lineHeight?: number;
|
|
71
108
|
underline?: boolean;
|
|
72
109
|
strikethrough?: boolean;
|
|
73
110
|
skipInk?: boolean;
|
|
74
111
|
letterSpacing?: number;
|
|
112
|
+
/** Base writing direction (CSS `direction`): `"rtl"` starts the line on the right, for Hebrew or
|
|
113
|
+
* Arabic. Mixed text is reordered per Unicode UAX #9 either way, so Hebrew inside an `ltr`
|
|
114
|
+
* paragraph already comes out right - this decides where the LINE starts. */
|
|
115
|
+
direction?: Direction;
|
|
116
|
+
/** CSS `text-transform`: recase the text. `capitalize` upper-cases the first letter of each word. */
|
|
117
|
+
textTransform?: TextTransform;
|
|
118
|
+
/** CSS `word-spacing`, in points: extra advance at every space. Negative tightens. */
|
|
119
|
+
wordSpacing?: number;
|
|
120
|
+
/** CSS `text-indent`, in points: how far the FIRST line of a paragraph starts in. */
|
|
121
|
+
textIndent?: number;
|
|
122
|
+
/** Split a word that is wider than its box, anywhere, no hyphen (CSS `overflow-wrap:
|
|
123
|
+
* break-word`). The floor under `hyphenate`: an e-mail, an IBAN or an invoice number has no
|
|
124
|
+
* valid hyphenation point anywhere. Off by default - a too-wide word overflows, as in CSS. */
|
|
125
|
+
breakWord?: boolean;
|
|
126
|
+
/** The ligatures the font's designer drew (`fi`, `fl`, `ffi`). ON by default, like kerning -
|
|
127
|
+
* `false` opts this text out. Embedded fonts only; the standard-14 have no GSUB. */
|
|
128
|
+
ligatures?: boolean;
|
|
129
|
+
/** Language-aware splitting: given a word, return its parts. A hyphen is drawn at the break.
|
|
130
|
+
* Pluggable, so no pattern data ships here: pass `hyphen`, `hyphenopoly`, or your own. */
|
|
131
|
+
hyphenate?: Hyphenator;
|
|
75
132
|
}
|
|
76
133
|
/** Maps the `Text`-style option names onto a partial engine `ResolvedTextStyle` (only the set
|
|
77
134
|
* fields), for seeding the inheritance cascade. */
|
package/dist/api/text.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextElement } from "../elements/text-element.js";
|
|
1
|
+
import { TextElement, } from "../elements/text-element.js";
|
|
2
2
|
import { HorizontalAlignment } from "../elements/pdf-element.js";
|
|
3
3
|
import { FontStyle } from "../utils/pdf-object-manager.js";
|
|
4
4
|
import { toColor } from "./color.js";
|
|
@@ -12,10 +12,15 @@ function toFontStyle(bold, italic) {
|
|
|
12
12
|
return FontStyle.Italic;
|
|
13
13
|
return FontStyle.Normal;
|
|
14
14
|
}
|
|
15
|
+
/** The family a run starts in - the first of a stack, or the single name given. */
|
|
16
|
+
const primaryFont = (font) => Array.isArray(font) ? font[0] : font;
|
|
17
|
+
/** The rest of the stack, tried in order for a code point the first family cannot draw. */
|
|
18
|
+
const fallbackFonts = (font) => Array.isArray(font) ? font.slice(1) : undefined;
|
|
15
19
|
const ALIGN = {
|
|
16
20
|
left: HorizontalAlignment.left,
|
|
17
21
|
center: HorizontalAlignment.center,
|
|
18
22
|
right: HorizontalAlignment.right,
|
|
23
|
+
justify: HorizontalAlignment.justify,
|
|
19
24
|
};
|
|
20
25
|
/**
|
|
21
26
|
* Validates a font `size` (a number of points). Rejects anything that would silently become a NaN
|
|
@@ -45,7 +50,8 @@ export function span(text, style = {}) {
|
|
|
45
50
|
return {
|
|
46
51
|
content: text,
|
|
47
52
|
fontSize: toFontSize(style.size),
|
|
48
|
-
fontFamily: style.font,
|
|
53
|
+
fontFamily: primaryFont(style.font),
|
|
54
|
+
fontFallback: fallbackFonts(style.font),
|
|
49
55
|
fontStyle: style.bold !== undefined || style.italic !== undefined
|
|
50
56
|
? toFontStyle(style.bold, style.italic)
|
|
51
57
|
: undefined,
|
|
@@ -53,6 +59,7 @@ export function span(text, style = {}) {
|
|
|
53
59
|
underline: style.underline,
|
|
54
60
|
strikethrough: style.strikethrough,
|
|
55
61
|
letterSpacing: style.letterSpacing,
|
|
62
|
+
verticalAlign: style.verticalAlign,
|
|
56
63
|
href: style.href,
|
|
57
64
|
dest: style.to,
|
|
58
65
|
};
|
|
@@ -73,19 +80,29 @@ export function Text(content, opts = {}) {
|
|
|
73
80
|
return new TextElement({
|
|
74
81
|
content: body,
|
|
75
82
|
fontSize: toFontSize(opts.size),
|
|
76
|
-
fontFamily: opts.font,
|
|
83
|
+
fontFamily: primaryFont(opts.font),
|
|
84
|
+
fontFallback: fallbackFonts(opts.font),
|
|
77
85
|
fontStyle: opts.bold !== undefined || opts.italic !== undefined
|
|
78
86
|
? toFontStyle(opts.bold, opts.italic)
|
|
79
87
|
: undefined,
|
|
80
88
|
color: opts.color !== undefined ? toColor(opts.color) : undefined,
|
|
81
89
|
textAlignment: opts.align ? ALIGN[opts.align] : undefined,
|
|
82
90
|
maxLines: opts.maxLines,
|
|
91
|
+
orphans: opts.orphans,
|
|
92
|
+
widows: opts.widows,
|
|
83
93
|
overflow: opts.overflow,
|
|
84
94
|
lineHeight: opts.lineHeight,
|
|
85
95
|
underline: opts.underline,
|
|
86
96
|
strikethrough: opts.strikethrough,
|
|
87
97
|
skipInk: opts.skipInk,
|
|
88
98
|
letterSpacing: opts.letterSpacing,
|
|
99
|
+
direction: opts.direction,
|
|
100
|
+
textTransform: opts.textTransform,
|
|
101
|
+
wordSpacing: opts.wordSpacing,
|
|
102
|
+
textIndent: opts.textIndent,
|
|
103
|
+
breakWord: opts.breakWord,
|
|
104
|
+
ligatures: opts.ligatures,
|
|
105
|
+
hyphenate: opts.hyphenate,
|
|
89
106
|
role: opts.role,
|
|
90
107
|
});
|
|
91
108
|
}
|
|
@@ -99,8 +116,10 @@ export function toTextStyleOverride(opts) {
|
|
|
99
116
|
const style = {};
|
|
100
117
|
if (opts.size !== undefined)
|
|
101
118
|
style.fontSize = toFontSize(opts.size);
|
|
102
|
-
if (opts.font !== undefined)
|
|
103
|
-
style.fontFamily = opts.font;
|
|
119
|
+
if (opts.font !== undefined) {
|
|
120
|
+
style.fontFamily = primaryFont(opts.font);
|
|
121
|
+
style.fontFallback = fallbackFonts(opts.font);
|
|
122
|
+
}
|
|
104
123
|
if (opts.bold !== undefined || opts.italic !== undefined) {
|
|
105
124
|
style.fontStyle = toFontStyle(opts.bold, opts.italic);
|
|
106
125
|
}
|
|
@@ -118,5 +137,19 @@ export function toTextStyleOverride(opts) {
|
|
|
118
137
|
style.skipInk = opts.skipInk;
|
|
119
138
|
if (opts.letterSpacing !== undefined)
|
|
120
139
|
style.letterSpacing = opts.letterSpacing;
|
|
140
|
+
if (opts.direction !== undefined)
|
|
141
|
+
style.direction = opts.direction;
|
|
142
|
+
if (opts.textTransform !== undefined)
|
|
143
|
+
style.textTransform = opts.textTransform;
|
|
144
|
+
if (opts.wordSpacing !== undefined)
|
|
145
|
+
style.wordSpacing = opts.wordSpacing;
|
|
146
|
+
if (opts.textIndent !== undefined)
|
|
147
|
+
style.textIndent = opts.textIndent;
|
|
148
|
+
if (opts.breakWord !== undefined)
|
|
149
|
+
style.breakWord = opts.breakWord;
|
|
150
|
+
if (opts.ligatures !== undefined)
|
|
151
|
+
style.ligatures = opts.ligatures;
|
|
152
|
+
if (opts.hyphenate !== undefined)
|
|
153
|
+
style.hyphenate = opts.hyphenate;
|
|
121
154
|
return style;
|
|
122
155
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type ColorInput } from "../api/color.js";
|
|
2
|
+
import { type GradientInput } from "../api/gradient.js";
|
|
3
|
+
import type { IRNode, StrokeStyle } from "../ir/display-list.js";
|
|
4
|
+
/**
|
|
5
|
+
* The imperative escape hatch: a box in the layout that you draw into yourself.
|
|
6
|
+
*
|
|
7
|
+
* It is the SECOND producer of the vector layer (SVG is the first), so it needs no new IR and no
|
|
8
|
+
* backend change - everything it draws is a `Path` node like any other, which means it tags, it
|
|
9
|
+
* paginates, and it works in the browser.
|
|
10
|
+
*
|
|
11
|
+
* Three deliberate departures from the PDFKit-shaped painters (react-pdf's included):
|
|
12
|
+
*
|
|
13
|
+
* 1. **Typed to the value.** `cap: "round"` autocompletes and a wrong one is a compile error, where
|
|
14
|
+
* react-pdf's `paint` callback receives `painter: any` - no help on the one thing you use.
|
|
15
|
+
* 2. **No hidden state.** There is no `lineWidth(2)` that a later `stroke()` happens to pick up, and
|
|
16
|
+
* no `save()`/`restore()` pair to forget: a transform or a clip is a SCOPE (`group`, `clipped`)
|
|
17
|
+
* that closes itself.
|
|
18
|
+
* 3. **One coordinate system.** Top-left, y down, points - the same as every other element in jasy
|
|
19
|
+
* and the same as SVG. The origin is the canvas box, so `0,0` is its own corner.
|
|
20
|
+
*/
|
|
21
|
+
/** Where a shape ends up. A gradient resolves against the shape's own bounding box, as in SVG. */
|
|
22
|
+
export type PaintInput = ColorInput | GradientInput;
|
|
23
|
+
export interface StrokeOptions {
|
|
24
|
+
/** Line width in points. Default 1. */
|
|
25
|
+
width?: number;
|
|
26
|
+
cap?: StrokeStyle["cap"];
|
|
27
|
+
join?: StrokeStyle["join"];
|
|
28
|
+
miterLimit?: number;
|
|
29
|
+
/** Dash lengths in points, plus the phase to start at. */
|
|
30
|
+
dash?: number[];
|
|
31
|
+
dashOffset?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface FillOptions {
|
|
34
|
+
/** `evenodd` cuts a hole where subpaths overlap an odd number of times. Default `nonzero`. */
|
|
35
|
+
rule?: "nonzero" | "evenodd";
|
|
36
|
+
}
|
|
37
|
+
/** A transform applied for the duration of a `group`. Composed in this order: translate, rotate, scale. */
|
|
38
|
+
export interface TransformOptions {
|
|
39
|
+
translate?: [number, number];
|
|
40
|
+
/** Degrees, clockwise on the page. */
|
|
41
|
+
rotate?: number;
|
|
42
|
+
/** One number scales both axes. */
|
|
43
|
+
scale?: number | [number, number];
|
|
44
|
+
/** The point `rotate` and `scale` happen around. Default `[0, 0]`. */
|
|
45
|
+
origin?: [number, number];
|
|
46
|
+
}
|
|
47
|
+
/** The size the callback was given, so a drawing can be written relative to its box. */
|
|
48
|
+
export interface CanvasSize {
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
}
|
|
52
|
+
export declare class CanvasPainter {
|
|
53
|
+
readonly size: CanvasSize;
|
|
54
|
+
/** The nodes drawn so far, in order. */
|
|
55
|
+
private readonly nodes;
|
|
56
|
+
/** The path being built. Emptied by every painting call, so a shape cannot leak into the next. */
|
|
57
|
+
private current;
|
|
58
|
+
private cursor;
|
|
59
|
+
private start;
|
|
60
|
+
constructor(size: CanvasSize);
|
|
61
|
+
/** Everything drawn, for the renderer. */
|
|
62
|
+
drawn(): IRNode[];
|
|
63
|
+
move(x: number, y: number): this;
|
|
64
|
+
line(x: number, y: number): this;
|
|
65
|
+
/** A cubic Bézier - the curve PDF draws natively. */
|
|
66
|
+
curve(x1: number, y1: number, x2: number, y2: number, x: number, y: number): this;
|
|
67
|
+
/** A quadratic Bézier. PDF has no operator for one, so it is raised to a cubic exactly. */
|
|
68
|
+
quad(cx: number, cy: number, x: number, y: number): this;
|
|
69
|
+
close(): this;
|
|
70
|
+
rect(x: number, y: number, width: number, height: number): this;
|
|
71
|
+
circle(cx: number, cy: number, r: number): this;
|
|
72
|
+
/** Four cubics; the constant is the standard circle-to-Bézier one. */
|
|
73
|
+
ellipse(cx: number, cy: number, rx: number, ry: number): this;
|
|
74
|
+
polygon(points: readonly (readonly [number, number])[]): this;
|
|
75
|
+
/** SVG path data, through the same reader `Svg()` uses - arcs and shorthands included. */
|
|
76
|
+
path(d: string): this;
|
|
77
|
+
/** Fills the path built so far and starts a new one. Default black, as in SVG and PDF. */
|
|
78
|
+
fill(paint?: PaintInput, options?: FillOptions): this;
|
|
79
|
+
/** Strokes the path built so far and starts a new one. */
|
|
80
|
+
stroke(color?: ColorInput, options?: StrokeOptions): this;
|
|
81
|
+
fillAndStroke(paint: PaintInput, color: ColorInput, options?: StrokeOptions & FillOptions): this;
|
|
82
|
+
/**
|
|
83
|
+
* Draws inside a transform. It closes itself, so there is no `restore()` to forget - and a nested
|
|
84
|
+
* `group` composes, exactly like a `<g transform>` in SVG.
|
|
85
|
+
*/
|
|
86
|
+
group(transform: TransformOptions, draw: (c: CanvasPainter) => void): this;
|
|
87
|
+
/**
|
|
88
|
+
* Draws clipped to a path. `build` draws the clip shape (nothing is painted from it), `draw` draws
|
|
89
|
+
* what the clip applies to.
|
|
90
|
+
*/
|
|
91
|
+
clipped(build: (c: CanvasPainter) => void, draw: (c: CanvasPainter) => void, options?: FillOptions): this;
|
|
92
|
+
private paint;
|
|
93
|
+
/**
|
|
94
|
+
* A path may not cross the edge of a scope. It would be built in one coordinate space and painted
|
|
95
|
+
* in another, and the version that just dropped it made a shape vanish without a word - exactly
|
|
96
|
+
* the silence this codebase refuses everywhere else.
|
|
97
|
+
*/
|
|
98
|
+
private assertNoPendingPath;
|
|
99
|
+
private resolve;
|
|
100
|
+
private strokeStyle;
|
|
101
|
+
private matrixOf;
|
|
102
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { toColor } from "../api/color.js";
|
|
2
|
+
import { isGradientInput, resolveGradient } from "../api/gradient.js";
|
|
3
|
+
import { pathData } from "../svg/shapes.js";
|
|
4
|
+
import { boundsOf, quadToCubic } from "../vector/path.js";
|
|
5
|
+
const DEGREES = Math.PI / 180;
|
|
6
|
+
export class CanvasPainter {
|
|
7
|
+
constructor(size) {
|
|
8
|
+
this.size = size;
|
|
9
|
+
/** The nodes drawn so far, in order. */
|
|
10
|
+
this.nodes = [];
|
|
11
|
+
/** The path being built. Emptied by every painting call, so a shape cannot leak into the next. */
|
|
12
|
+
this.current = [];
|
|
13
|
+
this.cursor = [0, 0];
|
|
14
|
+
this.start = [0, 0];
|
|
15
|
+
}
|
|
16
|
+
/** Everything drawn, for the renderer. */
|
|
17
|
+
drawn() {
|
|
18
|
+
return this.nodes;
|
|
19
|
+
}
|
|
20
|
+
// --- building a path ---------------------------------------------------------------------------
|
|
21
|
+
move(x, y) {
|
|
22
|
+
this.current.push({ op: "m", x, y });
|
|
23
|
+
this.cursor = [x, y];
|
|
24
|
+
this.start = [x, y];
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
line(x, y) {
|
|
28
|
+
this.current.push({ op: "l", x, y });
|
|
29
|
+
this.cursor = [x, y];
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/** A cubic Bézier - the curve PDF draws natively. */
|
|
33
|
+
curve(x1, y1, x2, y2, x, y) {
|
|
34
|
+
this.current.push({ op: "c", x1, y1, x2, y2, x, y });
|
|
35
|
+
this.cursor = [x, y];
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
/** A quadratic Bézier. PDF has no operator for one, so it is raised to a cubic exactly. */
|
|
39
|
+
quad(cx, cy, x, y) {
|
|
40
|
+
this.current.push(quadToCubic(this.cursor[0], this.cursor[1], cx, cy, x, y));
|
|
41
|
+
this.cursor = [x, y];
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
close() {
|
|
45
|
+
this.current.push({ op: "z" });
|
|
46
|
+
this.cursor = [...this.start];
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
rect(x, y, width, height) {
|
|
50
|
+
return this.move(x, y)
|
|
51
|
+
.line(x + width, y)
|
|
52
|
+
.line(x + width, y + height)
|
|
53
|
+
.line(x, y + height)
|
|
54
|
+
.close();
|
|
55
|
+
}
|
|
56
|
+
circle(cx, cy, r) {
|
|
57
|
+
return this.ellipse(cx, cy, r, r);
|
|
58
|
+
}
|
|
59
|
+
/** Four cubics; the constant is the standard circle-to-Bézier one. */
|
|
60
|
+
ellipse(cx, cy, rx, ry) {
|
|
61
|
+
const k = 0.5522847498307936;
|
|
62
|
+
const [ox, oy] = [rx * k, ry * k];
|
|
63
|
+
return this.move(cx - rx, cy)
|
|
64
|
+
.curve(cx - rx, cy - oy, cx - ox, cy - ry, cx, cy - ry)
|
|
65
|
+
.curve(cx + ox, cy - ry, cx + rx, cy - oy, cx + rx, cy)
|
|
66
|
+
.curve(cx + rx, cy + oy, cx + ox, cy + ry, cx, cy + ry)
|
|
67
|
+
.curve(cx - ox, cy + ry, cx - rx, cy + oy, cx - rx, cy)
|
|
68
|
+
.close();
|
|
69
|
+
}
|
|
70
|
+
polygon(points) {
|
|
71
|
+
if (points.length === 0)
|
|
72
|
+
return this;
|
|
73
|
+
this.move(points[0][0], points[0][1]);
|
|
74
|
+
for (const [x, y] of points.slice(1))
|
|
75
|
+
this.line(x, y);
|
|
76
|
+
return this.close();
|
|
77
|
+
}
|
|
78
|
+
/** SVG path data, through the same reader `Svg()` uses - arcs and shorthands included. */
|
|
79
|
+
path(d) {
|
|
80
|
+
const parsed = pathData(d);
|
|
81
|
+
this.current.push(...parsed);
|
|
82
|
+
// Where the pen ends up: after a `z` it is back at that subpath's `m`, not at the last point.
|
|
83
|
+
for (const cmd of parsed) {
|
|
84
|
+
if (cmd.op === "m")
|
|
85
|
+
this.start = [cmd.x, cmd.y];
|
|
86
|
+
this.cursor = cmd.op === "z" ? [...this.start] : [cmd.x, cmd.y];
|
|
87
|
+
}
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
// --- painting it -------------------------------------------------------------------------------
|
|
91
|
+
/** Fills the path built so far and starts a new one. Default black, as in SVG and PDF. */
|
|
92
|
+
fill(paint = "black", options = {}) {
|
|
93
|
+
return this.paint(this.resolve(paint), undefined, options.rule);
|
|
94
|
+
}
|
|
95
|
+
/** Strokes the path built so far and starts a new one. */
|
|
96
|
+
stroke(color = "black", options = {}) {
|
|
97
|
+
return this.paint(undefined, this.strokeStyle(color, options));
|
|
98
|
+
}
|
|
99
|
+
fillAndStroke(paint, color, options = {}) {
|
|
100
|
+
return this.paint(this.resolve(paint), this.strokeStyle(color, options), options.rule);
|
|
101
|
+
}
|
|
102
|
+
// --- scopes, instead of save/restore -----------------------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Draws inside a transform. It closes itself, so there is no `restore()` to forget - and a nested
|
|
105
|
+
* `group` composes, exactly like a `<g transform>` in SVG.
|
|
106
|
+
*/
|
|
107
|
+
group(transform, draw) {
|
|
108
|
+
this.assertNoPendingPath("group");
|
|
109
|
+
const matrix = this.matrixOf(transform);
|
|
110
|
+
this.nodes.push({ type: "transform-push", matrix });
|
|
111
|
+
draw(this);
|
|
112
|
+
this.assertNoPendingPath("group");
|
|
113
|
+
this.nodes.push({ type: "transform-pop" });
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Draws clipped to a path. `build` draws the clip shape (nothing is painted from it), `draw` draws
|
|
118
|
+
* what the clip applies to.
|
|
119
|
+
*/
|
|
120
|
+
clipped(build, draw, options = {}) {
|
|
121
|
+
this.assertNoPendingPath("clipped");
|
|
122
|
+
build(this);
|
|
123
|
+
const commands = this.current;
|
|
124
|
+
this.current = [];
|
|
125
|
+
this.nodes.push({ type: "clip-path-push", commands, fillRule: options.rule });
|
|
126
|
+
draw(this);
|
|
127
|
+
this.assertNoPendingPath("clipped");
|
|
128
|
+
this.nodes.push({ type: "clip-pop" });
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
// --- internals ---------------------------------------------------------------------------------
|
|
132
|
+
paint(fill, stroke, rule) {
|
|
133
|
+
const commands = this.current;
|
|
134
|
+
this.current = [];
|
|
135
|
+
if (commands.length === 0)
|
|
136
|
+
return this;
|
|
137
|
+
this.nodes.push({ type: "path", commands, fill, fillRule: rule, stroke });
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A path may not cross the edge of a scope. It would be built in one coordinate space and painted
|
|
142
|
+
* in another, and the version that just dropped it made a shape vanish without a word - exactly
|
|
143
|
+
* the silence this codebase refuses everywhere else.
|
|
144
|
+
*/
|
|
145
|
+
assertNoPendingPath(scope) {
|
|
146
|
+
if (this.current.length === 0)
|
|
147
|
+
return;
|
|
148
|
+
this.current = [];
|
|
149
|
+
throw new Error(`@jasy/pdf: a Canvas path was built but never filled or stroked around \`${scope}()\`. ` +
|
|
150
|
+
`Paint it before the scope opens, or move the drawing inside it.`);
|
|
151
|
+
}
|
|
152
|
+
resolve(paint) {
|
|
153
|
+
if (isGradientInput(paint)) {
|
|
154
|
+
const box = boundsOf(this.current);
|
|
155
|
+
return resolveGradient(paint, box.x, box.y, box.width, box.height);
|
|
156
|
+
}
|
|
157
|
+
return toColor(paint);
|
|
158
|
+
}
|
|
159
|
+
strokeStyle(color, options) {
|
|
160
|
+
return {
|
|
161
|
+
color: toColor(color),
|
|
162
|
+
width: options.width ?? 1,
|
|
163
|
+
cap: options.cap,
|
|
164
|
+
join: options.join,
|
|
165
|
+
miterLimit: options.miterLimit,
|
|
166
|
+
dash: options.dash,
|
|
167
|
+
dashOffset: options.dashOffset,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
matrixOf(t) {
|
|
171
|
+
const [ox, oy] = t.origin ?? [0, 0];
|
|
172
|
+
const [sx, sy] = Array.isArray(t.scale) ? t.scale : [t.scale ?? 1, t.scale ?? 1];
|
|
173
|
+
const theta = (t.rotate ?? 0) * DEGREES;
|
|
174
|
+
const [cos, sin] = [Math.cos(theta), Math.sin(theta)];
|
|
175
|
+
// T(translate) · T(origin) · R · S · T(-origin), written out as a PDF `cm` operand.
|
|
176
|
+
const [a, b, c, d] = [cos * sx, sin * sx, -sin * sy, cos * sy];
|
|
177
|
+
const [tx, ty] = t.translate ?? [0, 0];
|
|
178
|
+
// `-sin(0)` is -0, which is a surprising value to read back out of a matrix. It writes as
|
|
179
|
+
// "0.000" either way, so normalising costs nothing and keeps the number predictable.
|
|
180
|
+
const zero = (n) => (n === 0 ? 0 : n);
|
|
181
|
+
return [a, b, c, d, tx + ox - (a * ox + c * oy), ty + oy - (b * ox + d * oy)].map(zero);
|
|
182
|
+
}
|
|
183
|
+
}
|
package/dist/edit/document.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { type PdfDict, type PdfObject, type PdfStream } from "./objects.js";
|
|
2
2
|
declare const EMPTY: Uint8Array<ArrayBuffer>;
|
|
3
|
+
/** What `PdfDocument.load` accepts. */
|
|
4
|
+
export interface LoadOptions {
|
|
5
|
+
/** Ceiling on what ONE stream may inflate to, in bytes (default 64 MB). Raise it for a document that
|
|
6
|
+
* is genuinely enormous; a stream past it throws `PdfStreamTooLargeError` rather than being read. */
|
|
7
|
+
maxStreamSize?: number;
|
|
8
|
+
}
|
|
9
|
+
/** What `PdfDocument.open` accepts - everything `load` takes, plus the password. */
|
|
10
|
+
export interface OpenOptions extends LoadOptions {
|
|
11
|
+
password?: string;
|
|
12
|
+
}
|
|
3
13
|
/** Thrown when a file cannot be opened because of its encryption - always saying which of the reasons it
|
|
4
14
|
* is: no password, wrong password, or a revision we do not implement. */
|
|
5
15
|
export declare class PdfEncryptedError extends Error {
|
|
@@ -23,8 +33,10 @@ export declare class PdfDocument {
|
|
|
23
33
|
* update has to follow suit: mixing the two leaves readers that understand only one kind unable to
|
|
24
34
|
* walk the chain back. */
|
|
25
35
|
usesXrefStream: boolean;
|
|
36
|
+
/** Ceiling on what one stream may inflate to; see `inflate.ts`. */
|
|
37
|
+
private maxStreamSize;
|
|
26
38
|
private constructor();
|
|
27
|
-
static load(bytes: Uint8Array): PdfDocument;
|
|
39
|
+
static load(bytes: Uint8Array, opts?: LoadOptions): PdfDocument;
|
|
28
40
|
/**
|
|
29
41
|
* Open a document, decrypting it when it is password-protected.
|
|
30
42
|
*
|
|
@@ -33,9 +45,7 @@ export declare class PdfDocument {
|
|
|
33
45
|
* above them synchronous, which is the same trade the writer makes (`structure.ts` builds the handler
|
|
34
46
|
* before the synchronous constructor).
|
|
35
47
|
*/
|
|
36
|
-
static open(bytes: Uint8Array, opts?:
|
|
37
|
-
password?: string;
|
|
38
|
-
}): Promise<PdfDocument>;
|
|
48
|
+
static open(bytes: Uint8Array, opts?: OpenOptions): Promise<PdfDocument>;
|
|
39
49
|
/**
|
|
40
50
|
* Build the handler for this file's `/Encrypt` dictionary, or say precisely why we cannot.
|
|
41
51
|
*
|
package/dist/edit/document.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { unzlibSync } from "fflate";
|
|
2
1
|
import { Lexer } from "./lexer.js";
|
|
3
|
-
import {
|
|
2
|
+
import { DEFAULT_MAX_STREAM_SIZE, inflateBounded, PdfStreamTooLargeError, } from "../utils/inflate.js";
|
|
3
|
+
import { get, isDict, isRef, isStream, isString, nameOf, numberOf, stringsIn, } from "./objects.js";
|
|
4
4
|
import { StandardAes256 } from "../crypto/security-handler.js";
|
|
5
5
|
import { StandardLegacy } from "../crypto/legacy-handler.js";
|
|
6
6
|
const EMPTY = new Uint8Array(0);
|
|
@@ -13,22 +13,6 @@ export class PdfEncryptedError extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
/** The raw bytes of a `/Encrypt` entry that must be a string (`/U`, `/UE`). */
|
|
15
15
|
const stringBytes = (o) => isString(o) ? o.bytes : undefined;
|
|
16
|
-
/** Every string reachable inside an object, so the whole tree can be decrypted in one walk. */
|
|
17
|
-
function stringsIn(o, out = [], depth = 0) {
|
|
18
|
-
if (o === undefined || o === null || depth > 64)
|
|
19
|
-
return out;
|
|
20
|
-
if (isString(o))
|
|
21
|
-
out.push(o);
|
|
22
|
-
else if (Array.isArray(o))
|
|
23
|
-
for (const e of o)
|
|
24
|
-
stringsIn(e, out, depth + 1);
|
|
25
|
-
else if (isStream(o))
|
|
26
|
-
stringsIn(o.dict, out, depth + 1);
|
|
27
|
-
else if (isDict(o))
|
|
28
|
-
for (const v of o.map.values())
|
|
29
|
-
stringsIn(v, out, depth + 1);
|
|
30
|
-
return out;
|
|
31
|
-
}
|
|
32
16
|
export class PdfDocument {
|
|
33
17
|
constructor(bytes) {
|
|
34
18
|
this.bytes = bytes;
|
|
@@ -46,9 +30,13 @@ export class PdfDocument {
|
|
|
46
30
|
* update has to follow suit: mixing the two leaves readers that understand only one kind unable to
|
|
47
31
|
* walk the chain back. */
|
|
48
32
|
this.usesXrefStream = false;
|
|
33
|
+
/** Ceiling on what one stream may inflate to; see `inflate.ts`. */
|
|
34
|
+
this.maxStreamSize = DEFAULT_MAX_STREAM_SIZE;
|
|
49
35
|
}
|
|
50
|
-
static load(bytes) {
|
|
36
|
+
static load(bytes, opts = {}) {
|
|
51
37
|
const doc = new PdfDocument(bytes);
|
|
38
|
+
if (opts.maxStreamSize !== undefined)
|
|
39
|
+
doc.maxStreamSize = opts.maxStreamSize;
|
|
52
40
|
try {
|
|
53
41
|
doc.readXrefChain();
|
|
54
42
|
}
|
|
@@ -70,7 +58,7 @@ export class PdfDocument {
|
|
|
70
58
|
* before the synchronous constructor).
|
|
71
59
|
*/
|
|
72
60
|
static async open(bytes, opts = {}) {
|
|
73
|
-
const doc = PdfDocument.load(bytes);
|
|
61
|
+
const doc = PdfDocument.load(bytes, opts);
|
|
74
62
|
if (!doc.isEncrypted)
|
|
75
63
|
return doc;
|
|
76
64
|
doc.security = await doc.securityHandlerFor(opts.password);
|
|
@@ -577,9 +565,14 @@ export class PdfDocument {
|
|
|
577
565
|
filters.forEach((filter, i) => {
|
|
578
566
|
if (filter === "FlateDecode") {
|
|
579
567
|
try {
|
|
580
|
-
|
|
568
|
+
// Per pass, because /Filter is a list and [/FlateDecode /FlateDecode] is a legal nested bomb.
|
|
569
|
+
data = inflateBounded(data, this.maxStreamSize);
|
|
581
570
|
}
|
|
582
|
-
catch {
|
|
571
|
+
catch (e) {
|
|
572
|
+
// A stream that blew the ceiling is an attack, not a stream we failed to decode - swallowing
|
|
573
|
+
// it here would hand back silent garbage.
|
|
574
|
+
if (e instanceof PdfStreamTooLargeError)
|
|
575
|
+
throw e;
|
|
583
576
|
return; // not our data to repair; hand back what we have
|
|
584
577
|
}
|
|
585
578
|
data = this.undoPredictor(data, this.resolve(parms[i]));
|
package/dist/edit/fill.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export type FieldValue = string | boolean | string[] | null;
|
|
|
16
16
|
export interface FillOptions {
|
|
17
17
|
/** The user password, for a document that is encrypted. Wrong or missing gives a named error. */
|
|
18
18
|
password?: string;
|
|
19
|
+
/** Ceiling on what ONE stream may inflate to, in bytes (default 64 MB) - the zip-bomb guard. */
|
|
20
|
+
maxStreamSize?: number;
|
|
19
21
|
/**
|
|
20
22
|
* Draw the new value into the field's appearance (default `true`), the same way jasy bakes one when it
|
|
21
23
|
* CREATES a field - so the value is visible in any viewer, not only in one that honours
|
|
@@ -23,10 +25,23 @@ export interface FillOptions {
|
|
|
23
25
|
*/
|
|
24
26
|
fieldAppearances?: boolean;
|
|
25
27
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
+
* Ask the viewer to draw the values itself, by setting `/NeedAppearances true` (default `true`).
|
|
29
|
+
*
|
|
30
|
+
* The two options answer the same question and exactly one of them ends up applying: when we DID
|
|
31
|
+
* bake, the flag is written as `false`, because a form that still asks for regeneration gets it and
|
|
32
|
+
* the viewer throws our drawing away. Turning BOTH off is refused - see `fillForm`.
|
|
28
33
|
*/
|
|
29
34
|
needAppearances?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Flatten the form after filling it (default `false`): the values become ordinary page content and
|
|
37
|
+
* the fields stop being editable. The whole form is flattened, not only the names in `values` - a
|
|
38
|
+
* document where some fields are frozen and others are not is the surprising outcome, and
|
|
39
|
+
* `flattenForm(bytes, { fields })` is there for anyone who wants that on purpose.
|
|
40
|
+
*
|
|
41
|
+
* Filling and flattening in one call produces ONE incremental update rather than two, so the file
|
|
42
|
+
* stays smaller than calling them in sequence - and the result is identical either way.
|
|
43
|
+
*/
|
|
44
|
+
flatten?: boolean;
|
|
30
45
|
}
|
|
31
46
|
export interface FillResult {
|
|
32
47
|
bytes: Uint8Array;
|
|
@@ -34,6 +49,8 @@ export interface FillResult {
|
|
|
34
49
|
warnings: string[];
|
|
35
50
|
/** The names actually written. */
|
|
36
51
|
filled: string[];
|
|
52
|
+
/** The names turned into page content, when `flatten` was asked for. Empty otherwise. */
|
|
53
|
+
flattened: string[];
|
|
37
54
|
}
|
|
38
55
|
declare class FillError extends Error {
|
|
39
56
|
}
|