@jasy/pdf 1.0.0-alpha.1 → 1.0.0-alpha.3
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 +3 -3
- package/dist/api/args.d.ts +1 -1
- package/dist/api/args.js +2 -5
- package/dist/api/color.d.ts +4 -4
- package/dist/api/color.js +11 -17
- package/dist/api/content.d.ts +8 -8
- package/dist/api/content.js +23 -24
- package/dist/api/descriptor.d.ts +2 -2
- package/dist/api/descriptor.js +75 -31
- package/dist/api/index.d.ts +8 -8
- package/dist/api/index.js +8 -24
- package/dist/api/insets.js +4 -8
- package/dist/api/layout.d.ts +27 -12
- package/dist/api/layout.js +46 -45
- package/dist/api/structure.d.ts +60 -13
- package/dist/api/structure.js +132 -88
- package/dist/api/table.d.ts +5 -5
- package/dist/api/table.js +28 -24
- package/dist/api/text.d.ts +27 -2
- package/dist/api/text.js +45 -27
- package/dist/assets/font-data.d.ts +2 -0
- package/dist/assets/font-data.js +6 -0
- package/dist/assets/font-data.ts +7 -0
- package/dist/common/color.js +1 -5
- package/dist/constants/page-sizes.js +3 -6
- package/dist/constants/pdf-parts.js +1 -4
- package/dist/elements/container-element.d.ts +4 -4
- package/dist/elements/container-element.js +9 -13
- package/dist/elements/image-element.d.ts +18 -2
- package/dist/elements/image-element.js +81 -105
- package/dist/elements/index.d.ts +12 -10
- package/dist/elements/index.js +12 -28
- package/dist/elements/layout/default-text-style-element.d.ts +30 -0
- package/dist/elements/layout/default-text-style-element.js +47 -0
- package/dist/elements/layout/deferred-element.d.ts +3 -3
- package/dist/elements/layout/deferred-element.js +4 -8
- package/dist/elements/layout/expanded-element.d.ts +3 -3
- package/dist/elements/layout/expanded-element.js +10 -14
- package/dist/elements/layout/padding-element.d.ts +3 -3
- package/dist/elements/layout/padding-element.js +9 -14
- package/dist/elements/layout/positioned-element.d.ts +44 -0
- package/dist/elements/layout/positioned-element.js +61 -0
- package/dist/elements/layout/repeating-header-element.d.ts +3 -3
- package/dist/elements/layout/repeating-header-element.js +8 -12
- package/dist/elements/layout/sized-container-element.d.ts +2 -2
- package/dist/elements/layout/sized-container-element.js +6 -11
- package/dist/elements/line-element.d.ts +3 -3
- package/dist/elements/line-element.js +5 -10
- package/dist/elements/page-element.d.ts +8 -6
- package/dist/elements/page-element.js +31 -23
- package/dist/elements/pdf-document-element.d.ts +10 -4
- package/dist/elements/pdf-document-element.js +11 -10
- package/dist/elements/pdf-element.d.ts +28 -3
- package/dist/elements/pdf-element.js +10 -19
- package/dist/elements/rectangle-element.d.ts +14 -6
- package/dist/elements/rectangle-element.js +44 -21
- package/dist/elements/row-element.d.ts +3 -3
- package/dist/elements/row-element.js +7 -11
- package/dist/elements/text-element.d.ts +37 -11
- package/dist/elements/text-element.js +64 -39
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -19
- package/dist/ir/display-list.d.ts +22 -3
- package/dist/ir/display-list.js +1 -2
- package/dist/layout/box-constraints.js +2 -6
- package/dist/layout/fragmentation.d.ts +8 -1
- package/dist/layout/fragmentation.js +22 -10
- package/dist/platform/browser-fs.d.ts +2 -0
- package/dist/platform/browser-fs.js +9 -0
- package/dist/platform/browser-image.d.ts +5 -0
- package/dist/platform/browser-image.js +13 -0
- package/dist/platform/node-fs.d.ts +2 -0
- package/dist/platform/node-fs.js +10 -0
- package/dist/platform/node-image.d.ts +5 -0
- package/dist/platform/node-image.js +9 -0
- package/dist/renderer/container-renderer.d.ts +3 -3
- package/dist/renderer/container-renderer.js +12 -27
- package/dist/renderer/default-text-style-renderer.d.ts +6 -0
- package/dist/renderer/default-text-style-renderer.js +10 -0
- package/dist/renderer/deferred-renderer.d.ts +3 -3
- package/dist/renderer/deferred-renderer.js +8 -23
- package/dist/renderer/expanded-renderer.d.ts +3 -3
- package/dist/renderer/expanded-renderer.js +6 -21
- package/dist/renderer/image-renderer.d.ts +3 -3
- package/dist/renderer/image-renderer.js +77 -75
- package/dist/renderer/index.d.ts +10 -10
- package/dist/renderer/index.js +10 -26
- package/dist/renderer/line-renderer.d.ts +3 -3
- package/dist/renderer/line-renderer.js +13 -28
- package/dist/renderer/padding-renderer.d.ts +3 -3
- package/dist/renderer/padding-renderer.js +6 -21
- package/dist/renderer/page-renderer.d.ts +2 -2
- package/dist/renderer/page-renderer.js +61 -77
- package/dist/renderer/pdf-backend.d.ts +2 -2
- package/dist/renderer/pdf-backend.js +34 -17
- package/dist/renderer/pdf-config.js +4 -7
- package/dist/renderer/pdf-document-class.d.ts +5 -5
- package/dist/renderer/pdf-document-class.js +24 -41
- package/dist/renderer/pdf-document-renderer.d.ts +3 -3
- package/dist/renderer/pdf-document-renderer.js +71 -85
- package/dist/renderer/pdf-renderer.d.ts +2 -2
- package/dist/renderer/pdf-renderer.js +83 -90
- package/dist/renderer/positioned-renderer.d.ts +6 -0
- package/dist/renderer/positioned-renderer.js +10 -0
- package/dist/renderer/rectangle-renderer.d.ts +3 -3
- package/dist/renderer/rectangle-renderer.js +45 -44
- package/dist/renderer/repeating-header-renderer.d.ts +3 -3
- package/dist/renderer/repeating-header-renderer.js +11 -26
- package/dist/renderer/row-renderer.d.ts +3 -3
- package/dist/renderer/row-renderer.js +12 -27
- package/dist/renderer/text-renderer.d.ts +6 -5
- package/dist/renderer/text-renderer.js +33 -42
- package/dist/text/line-breaker.d.ts +8 -5
- package/dist/text/line-breaker.js +67 -16
- package/dist/text/text-style.d.ts +25 -0
- package/dist/text/text-style.js +29 -0
- package/dist/utils/afm-parser.js +3 -13
- package/dist/utils/bytes.d.ts +24 -0
- package/dist/utils/bytes.js +76 -0
- package/dist/utils/flex-layout.d.ts +2 -2
- package/dist/utils/flex-layout.js +15 -20
- package/dist/utils/font-metrics.d.ts +1 -1
- package/dist/utils/font-metrics.js +1 -2
- package/dist/utils/font-path.js +3 -6
- package/dist/utils/image-helper.d.ts +6 -5
- package/dist/utils/image-helper.js +101 -111
- package/dist/utils/md5.d.ts +4 -0
- package/dist/utils/md5.js +80 -0
- package/dist/utils/pdf-object-manager.d.ts +10 -6
- package/dist/utils/pdf-object-manager.js +89 -94
- package/dist/utils/renderer-registry.js +1 -5
- package/dist/utils/ttf-parser.d.ts +2 -2
- package/dist/utils/ttf-parser.js +32 -36
- package/dist/utils/ttf-subsetter.d.ts +1 -1
- package/dist/utils/ttf-subsetter.js +40 -42
- package/dist/utils/utf8-to-windows1252-encoder.js +1 -4
- package/dist/validators/element-validator.d.ts +2 -2
- package/dist/validators/element-validator.js +17 -23
- package/package.json +14 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { TextElement, TextSegment } from "../elements/text-element";
|
|
2
|
-
import { FontStyle, PDFObjectManager } from "../utils/pdf-object-manager";
|
|
3
|
-
import type { FontMetrics } from "../utils/font-metrics";
|
|
4
|
-
import { IRNode } from "../ir/display-list";
|
|
1
|
+
import { TextElement, TextSegment } from "../elements/text-element.js";
|
|
2
|
+
import { FontStyle, PDFObjectManager } from "../utils/pdf-object-manager.js";
|
|
3
|
+
import type { FontMetrics } from "../utils/font-metrics.js";
|
|
4
|
+
import { IRNode } from "../ir/display-list.js";
|
|
5
|
+
import { TextOverflow } from "../text/line-breaker.js";
|
|
5
6
|
export declare class TextRenderer {
|
|
6
|
-
static calculateTextHeight(content: string | TextSegment[], fontSize: number, fontFamily: string, fontStyle: FontStyle, objectManager: FontMetrics, maxWidth: number): number;
|
|
7
|
+
static calculateTextHeight(content: string | TextSegment[], fontSize: number, fontFamily: string, fontStyle: FontStyle, objectManager: FontMetrics, maxWidth: number, maxLines?: number, overflow?: TextOverflow, lineHeight?: number): number;
|
|
7
8
|
static render(textElement: TextElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
|
|
8
9
|
private static _buildRuns;
|
|
9
10
|
}
|
|
@@ -1,52 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.TextRenderer = void 0;
|
|
13
|
-
const pdf_element_1 = require("../elements/pdf-element");
|
|
14
|
-
const line_breaker_1 = require("../text/line-breaker");
|
|
1
|
+
import { HorizontalAlignment } from "../elements/pdf-element.js";
|
|
2
|
+
import { wrapStringIntoLines, breakSegmentsIntoLines, } from "../text/line-breaker.js";
|
|
15
3
|
// Distance from the top of a line down to its baseline, as a fraction of the font
|
|
16
4
|
// size. ~0.683 is the standard-14 ascent ratio used to seat the first baseline.
|
|
17
5
|
const BASELINE_RATIO = 683 / 1000;
|
|
18
|
-
class TextRenderer {
|
|
6
|
+
export class TextRenderer {
|
|
19
7
|
// Measuring only needs metrics, not the full object manager. (The render pass below
|
|
20
8
|
// still receives the manager because it also registers fonts/images.)
|
|
21
|
-
static calculateTextHeight(content, fontSize, fontFamily, fontStyle, objectManager, maxWidth) {
|
|
22
|
-
// Plain string: one line
|
|
9
|
+
static calculateTextHeight(content, fontSize, fontFamily, fontStyle, objectManager, maxWidth, maxLines, overflow, lineHeight = 1) {
|
|
10
|
+
// Plain string: one line box (fontSize * lineHeight) per wrapped line.
|
|
23
11
|
if (typeof content === "string") {
|
|
24
|
-
const lines =
|
|
25
|
-
return lines.length * fontSize;
|
|
12
|
+
const lines = wrapStringIntoLines(content, fontFamily, fontSize, fontStyle, maxWidth, objectManager, maxLines, overflow);
|
|
13
|
+
return lines.length * fontSize * lineHeight;
|
|
26
14
|
}
|
|
27
|
-
// Segments: each line contributes its own (tallest-on-line) leading.
|
|
28
|
-
const lines =
|
|
29
|
-
return lines.reduce((total, line) => total + line.maxFontSize, 0);
|
|
15
|
+
// Segments: each line contributes its own (tallest-on-line) leading, scaled by lineHeight.
|
|
16
|
+
const lines = breakSegmentsIntoLines(content, { fontFamily, fontSize, fontStyle }, maxWidth, objectManager, maxLines, overflow);
|
|
17
|
+
return lines.reduce((total, line) => total + line.maxFontSize * lineHeight, 0);
|
|
30
18
|
}
|
|
31
|
-
static render(textElement, objectManager) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return TextRenderer._buildRuns(content, fontSize, fontFamily, fontStyle, objectManager, width !== null && width !== void 0 ? width : Number.NaN, textAlignment, color, x, y);
|
|
38
|
-
});
|
|
19
|
+
static async render(textElement, objectManager) {
|
|
20
|
+
const { x, y, width, fontSize, color, content, fontFamily, fontStyle, textAlignment, maxLines, overflow, lineHeight, } = textElement.getProps();
|
|
21
|
+
// Component -> display list. Wrapping and positioning stay here; the backend
|
|
22
|
+
// turns each run into BT/Tf/Td/Tj/ET. The wrapping algorithm is unchanged from
|
|
23
|
+
// the original renderer - unifying it into the engine is Phase 3.
|
|
24
|
+
return TextRenderer._buildRuns(content, fontSize, fontFamily, fontStyle, objectManager, width ?? Number.NaN, textAlignment, color, x, y, maxLines, overflow, lineHeight);
|
|
39
25
|
}
|
|
40
26
|
// Lay the content out into absolutely-positioned text runs. Glyph positions match
|
|
41
27
|
// the previous hand-written operators exactly (verified by pixel-identical render);
|
|
42
28
|
// only the output form changed from PDF strings to `TextRun`s.
|
|
43
|
-
static _buildRuns(content, fontSize, fontFamily, fontStyle, objectManager, maxWidth, textAlignment, color, initialX, yPosition) {
|
|
29
|
+
static _buildRuns(content, fontSize, fontFamily, fontStyle, objectManager, maxWidth, textAlignment, color, initialX, yPosition, maxLines, overflow, lineHeight = 1) {
|
|
44
30
|
const runs = [];
|
|
45
31
|
// Horizontal offset of a line of the given width under the current alignment.
|
|
46
32
|
const alignmentOffset = (lineWidth) => {
|
|
47
|
-
if (textAlignment ===
|
|
33
|
+
if (textAlignment === HorizontalAlignment.center)
|
|
48
34
|
return (maxWidth - lineWidth) / 2;
|
|
49
|
-
if (textAlignment ===
|
|
35
|
+
if (textAlignment === HorizontalAlignment.right)
|
|
50
36
|
return maxWidth - lineWidth;
|
|
51
37
|
return 0;
|
|
52
38
|
};
|
|
@@ -62,16 +48,19 @@ class TextRenderer {
|
|
|
62
48
|
};
|
|
63
49
|
// --- Plain string: one run per wrapped line. ---
|
|
64
50
|
if (typeof content === "string") {
|
|
65
|
-
const lines =
|
|
66
|
-
// yPosition is the top of the text box (top-left); seat line 0's baseline below
|
|
67
|
-
//
|
|
68
|
-
|
|
51
|
+
const lines = wrapStringIntoLines(content, fontFamily, fontSize, fontStyle, maxWidth, objectManager, maxLines, overflow);
|
|
52
|
+
// yPosition is the top of the text box (top-left); seat line 0's baseline below it, then step
|
|
53
|
+
// DOWN by one line box (fontSize * lineHeight) per line. The lineHeight EXTRA leading is split
|
|
54
|
+
// half above / half below (CSS/Flutter "half-leading"), so the text sits centered in its line
|
|
55
|
+
// box instead of clinging to the top. At lineHeight 1 the half-leading is 0 -> byte-identical.
|
|
56
|
+
const halfLeading = (fontSize * (lineHeight - 1)) / 2;
|
|
57
|
+
const baseline = yPosition + halfLeading + fontSize * BASELINE_RATIO;
|
|
69
58
|
lines.forEach((line, index) => {
|
|
70
59
|
const lineWidth = objectManager.getStringWidth(line, fontFamily, fontSize, fontStyle);
|
|
71
60
|
runs.push({
|
|
72
61
|
type: "text",
|
|
73
62
|
x: initialX + alignmentOffset(lineWidth),
|
|
74
|
-
y: baseline + fontSize * index,
|
|
63
|
+
y: baseline + fontSize * lineHeight * index,
|
|
75
64
|
text: line,
|
|
76
65
|
fontFamily,
|
|
77
66
|
fontStyle,
|
|
@@ -115,11 +104,13 @@ class TextRenderer {
|
|
|
115
104
|
overallMaxFont = size;
|
|
116
105
|
}
|
|
117
106
|
let lineY = yPosition + overallMaxFont * BASELINE_RATIO;
|
|
118
|
-
for (const line of
|
|
119
|
-
|
|
120
|
-
|
|
107
|
+
for (const line of breakSegmentsIntoLines(content, { fontFamily, fontSize, fontStyle }, maxWidth, objectManager, maxLines, overflow)) {
|
|
108
|
+
// Half-leading: shift this line's baseline down by half its own extra leading, so the line
|
|
109
|
+
// sits centered in its box (CSS/Flutter). At lineHeight 1 the shift is 0 -> byte-identical.
|
|
110
|
+
const halfLeading = (line.maxFontSize * (lineHeight - 1)) / 2;
|
|
111
|
+
pushLine(line, lineY + halfLeading);
|
|
112
|
+
lineY += line.maxFontSize * lineHeight;
|
|
121
113
|
}
|
|
122
114
|
return runs;
|
|
123
115
|
}
|
|
124
116
|
}
|
|
125
|
-
exports.TextRenderer = TextRenderer;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import type { FontStyle } from "../utils/pdf-object-manager";
|
|
2
|
-
import type { FontMetrics } from "../utils/font-metrics";
|
|
3
|
-
import type { TextSegment } from "../elements/text-element";
|
|
1
|
+
import type { FontStyle } from "../utils/pdf-object-manager.js";
|
|
2
|
+
import type { FontMetrics } from "../utils/font-metrics.js";
|
|
3
|
+
import type { TextSegment } from "../elements/text-element.js";
|
|
4
4
|
/** Default font for segments that don't override it. */
|
|
5
5
|
export interface SegmentDefaults {
|
|
6
6
|
fontFamily: string;
|
|
7
7
|
fontSize: number;
|
|
8
8
|
fontStyle: FontStyle;
|
|
9
9
|
}
|
|
10
|
+
/** What happens to text beyond `maxLines`: `"clip"` drops it, `"ellipsis"` ends the last kept line
|
|
11
|
+
* with an ellipsis. Mirrors Flutter's `TextOverflow`. */
|
|
12
|
+
export type TextOverflow = "clip" | "ellipsis";
|
|
10
13
|
/** One laid-out line of segments. `maxFontSize` is the tallest font ON THIS LINE - its
|
|
11
14
|
* leading - matching how real engines (and this lib's plain-string path) space lines. */
|
|
12
15
|
export interface SegmentLine {
|
|
@@ -22,14 +25,14 @@ export interface SegmentLine {
|
|
|
22
25
|
* rendering call this, so they can never disagree. Depends only on `FontMetrics`,
|
|
23
26
|
* not the PDF byte writer - the future fragmentation pass can reuse it.
|
|
24
27
|
*/
|
|
25
|
-
export declare function wrapStringIntoLines(text: string, fontFamily: string, fontSize: number, fontStyle: FontStyle, maxWidth: number, metrics: FontMetrics): string[];
|
|
28
|
+
export declare function wrapStringIntoLines(text: string, fontFamily: string, fontSize: number, fontStyle: FontStyle, maxWidth: number, metrics: FontMetrics, maxLines?: number, overflow?: TextOverflow): string[];
|
|
26
29
|
/**
|
|
27
30
|
* Break styled segments into lines that fit within `maxWidth`. Same greedy
|
|
28
31
|
* word-splitting as the string breaker, but each line records the tallest font on
|
|
29
32
|
* THAT line as its leading (per-line, not a paragraph-global maximum). Single source
|
|
30
33
|
* of truth: both height measurement and rendering call this.
|
|
31
34
|
*/
|
|
32
|
-
export declare function breakSegmentsIntoLines(segments: TextSegment[], defaults: SegmentDefaults, maxWidth: number, metrics: FontMetrics): SegmentLine[];
|
|
35
|
+
export declare function breakSegmentsIntoLines(segments: TextSegment[], defaults: SegmentDefaults, maxWidth: number, metrics: FontMetrics, maxLines?: number, overflow?: TextOverflow): SegmentLine[];
|
|
33
36
|
/**
|
|
34
37
|
* Inverse of `breakSegmentsIntoLines`: flatten broken lines back into a `TextSegment[]`
|
|
35
38
|
* that re-wraps to exactly those lines. The wrap consumed the space at each line break,
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
exports.breakSegmentsIntoLines = breakSegmentsIntoLines;
|
|
5
|
-
exports.segmentLinesToSegments = segmentLinesToSegments;
|
|
1
|
+
/** Three ASCII dots, NOT the "…" glyph (U+2026): plain dots encode in every font - standard-14
|
|
2
|
+
* (WinAnsi) and any embedded TTF - whereas U+2026 needs a glyph the font may not carry. */
|
|
3
|
+
const ELLIPSIS = "...";
|
|
6
4
|
/**
|
|
7
5
|
* Break a plain string into lines that each fit within `maxWidth`, splitting on
|
|
8
6
|
* spaces (greedy: a word stays on the current line unless it would overflow).
|
|
@@ -11,7 +9,7 @@ exports.segmentLinesToSegments = segmentLinesToSegments;
|
|
|
11
9
|
* rendering call this, so they can never disagree. Depends only on `FontMetrics`,
|
|
12
10
|
* not the PDF byte writer - the future fragmentation pass can reuse it.
|
|
13
11
|
*/
|
|
14
|
-
function wrapStringIntoLines(text, fontFamily, fontSize, fontStyle, maxWidth, metrics) {
|
|
12
|
+
export function wrapStringIntoLines(text, fontFamily, fontSize, fontStyle, maxWidth, metrics, maxLines, overflow) {
|
|
15
13
|
let currentLine = "";
|
|
16
14
|
let currentWidth = 0;
|
|
17
15
|
const lines = [];
|
|
@@ -19,7 +17,10 @@ function wrapStringIntoLines(text, fontFamily, fontSize, fontStyle, maxWidth, me
|
|
|
19
17
|
words.forEach((word, index) => {
|
|
20
18
|
const wordWidth = metrics.getStringWidth(word, fontFamily, fontSize, fontStyle);
|
|
21
19
|
const spaceWidth = metrics.getCharWidth(" ", fontSize, undefined, fontFamily, fontStyle);
|
|
22
|
-
|
|
20
|
+
// Break before a word that won't fit - but only once the line has content. A single word wider
|
|
21
|
+
// than maxWidth must sit on its (empty) line and overflow, not push a phantom empty line before
|
|
22
|
+
// it (which would over-count the height by a line and shift the text down at render).
|
|
23
|
+
if (currentWidth + wordWidth > maxWidth && currentLine !== "") {
|
|
23
24
|
lines.push(currentLine.trim());
|
|
24
25
|
currentLine = word;
|
|
25
26
|
currentWidth = wordWidth;
|
|
@@ -31,7 +32,15 @@ function wrapStringIntoLines(text, fontFamily, fontSize, fontStyle, maxWidth, me
|
|
|
31
32
|
});
|
|
32
33
|
if (currentLine)
|
|
33
34
|
lines.push(currentLine.trim());
|
|
34
|
-
|
|
35
|
+
// Open-end by default; cap only when maxLines is set (the others get undefined → untouched).
|
|
36
|
+
if (maxLines == null || lines.length <= maxLines)
|
|
37
|
+
return lines;
|
|
38
|
+
const kept = lines.slice(0, maxLines);
|
|
39
|
+
if (overflow === "ellipsis") {
|
|
40
|
+
const last = kept.length - 1;
|
|
41
|
+
kept[last] = ellipsize(kept[last], fontFamily, fontSize, fontStyle, maxWidth, metrics);
|
|
42
|
+
}
|
|
43
|
+
return kept;
|
|
35
44
|
}
|
|
36
45
|
/**
|
|
37
46
|
* Break styled segments into lines that fit within `maxWidth`. Same greedy
|
|
@@ -39,7 +48,7 @@ function wrapStringIntoLines(text, fontFamily, fontSize, fontStyle, maxWidth, me
|
|
|
39
48
|
* THAT line as its leading (per-line, not a paragraph-global maximum). Single source
|
|
40
49
|
* of truth: both height measurement and rendering call this.
|
|
41
50
|
*/
|
|
42
|
-
function breakSegmentsIntoLines(segments, defaults, maxWidth, metrics) {
|
|
51
|
+
export function breakSegmentsIntoLines(segments, defaults, maxWidth, metrics, maxLines, overflow) {
|
|
43
52
|
const lines = [];
|
|
44
53
|
let width = 0;
|
|
45
54
|
let maxFontSize = 0; // per line: starts at 0, grows to the tallest font on the line
|
|
@@ -54,13 +63,15 @@ function breakSegmentsIntoLines(segments, defaults, maxWidth, metrics) {
|
|
|
54
63
|
// Start this segment's piece empty; its content is filled word-by-word below. (Not
|
|
55
64
|
// the original content - otherwise a segment whose FIRST word overflows would carry
|
|
56
65
|
// its whole text into the line that just closed.)
|
|
57
|
-
lineSegments.push(
|
|
66
|
+
lineSegments.push({ ...segment, fontFamily: family, content: "" });
|
|
58
67
|
combined = "";
|
|
59
68
|
if (maxFontSize < size)
|
|
60
69
|
maxFontSize = size;
|
|
61
70
|
words.forEach((word, wordIndex) => {
|
|
62
71
|
const wordWidth = metrics.getStringWidth(word, family, size, style);
|
|
63
|
-
|
|
72
|
+
// Same guard as the string path: don't open a phantom empty line for an over-wide first word -
|
|
73
|
+
// place it (overflowing) on the current empty line instead.
|
|
74
|
+
if (width + wordWidth > maxWidth && width > 0) {
|
|
64
75
|
lines.push({ segments: lineSegments, width, maxFontSize });
|
|
65
76
|
// Start the next line; its leading resets to the wrapping segment's size.
|
|
66
77
|
width = 0;
|
|
@@ -68,13 +79,13 @@ function breakSegmentsIntoLines(segments, defaults, maxWidth, metrics) {
|
|
|
68
79
|
lineSegments = [];
|
|
69
80
|
combined = word;
|
|
70
81
|
width += wordWidth + spaceWidth;
|
|
71
|
-
lineSegments.push(
|
|
82
|
+
lineSegments.push({ ...segment, content: combined });
|
|
72
83
|
}
|
|
73
84
|
else {
|
|
74
85
|
combined += wordIndex === 0 ? word : " " + word;
|
|
75
86
|
width += wordWidth + spaceWidth;
|
|
76
87
|
if (lineSegments.length === 0) {
|
|
77
|
-
lineSegments.push(
|
|
88
|
+
lineSegments.push({ ...segment, fontFamily: family, content: combined });
|
|
78
89
|
}
|
|
79
90
|
lineSegments[lineSegments.length - 1].content = combined;
|
|
80
91
|
}
|
|
@@ -83,7 +94,13 @@ function breakSegmentsIntoLines(segments, defaults, maxWidth, metrics) {
|
|
|
83
94
|
if (lineSegments.length > 0) {
|
|
84
95
|
lines.push({ segments: lineSegments, width, maxFontSize });
|
|
85
96
|
}
|
|
86
|
-
|
|
97
|
+
// Open-end by default; cap only when maxLines is set.
|
|
98
|
+
if (maxLines == null || lines.length <= maxLines)
|
|
99
|
+
return lines;
|
|
100
|
+
const kept = lines.slice(0, maxLines);
|
|
101
|
+
if (overflow === "ellipsis")
|
|
102
|
+
ellipsizeSegmentLine(kept[kept.length - 1], defaults, maxWidth, metrics);
|
|
103
|
+
return kept;
|
|
87
104
|
}
|
|
88
105
|
/**
|
|
89
106
|
* Inverse of `breakSegmentsIntoLines`: flatten broken lines back into a `TextSegment[]`
|
|
@@ -92,10 +109,10 @@ function breakSegmentsIntoLines(segments, defaults, maxWidth, metrics) {
|
|
|
92
109
|
* last word of a line and the first of the next would fuse ("a b" + "c d" -> "a bc d").
|
|
93
110
|
* Used by text fragmentation to rebuild the fitted/remainder halves of a split paragraph.
|
|
94
111
|
*/
|
|
95
|
-
function segmentLinesToSegments(lines) {
|
|
112
|
+
export function segmentLinesToSegments(lines) {
|
|
96
113
|
const result = [];
|
|
97
114
|
lines.forEach((line, lineIndex) => {
|
|
98
|
-
line.segments.forEach((segment) => result.push(
|
|
115
|
+
line.segments.forEach((segment) => result.push({ ...segment }));
|
|
99
116
|
if (lineIndex < lines.length - 1) {
|
|
100
117
|
const last = result[result.length - 1];
|
|
101
118
|
if (last && !last.content.endsWith(" "))
|
|
@@ -104,3 +121,37 @@ function segmentLinesToSegments(lines) {
|
|
|
104
121
|
});
|
|
105
122
|
return result;
|
|
106
123
|
}
|
|
124
|
+
/** Appends "…" to a single line, dropping trailing words (then characters) until the line plus the
|
|
125
|
+
* ellipsis fits `maxWidth`. Falls back to a bare "…" if not even one character fits. */
|
|
126
|
+
function ellipsize(line, fontFamily, fontSize, fontStyle, maxWidth, metrics) {
|
|
127
|
+
const fits = (s) => metrics.getStringWidth(s + ELLIPSIS, fontFamily, fontSize, fontStyle) <= maxWidth;
|
|
128
|
+
if (fits(line))
|
|
129
|
+
return line + ELLIPSIS;
|
|
130
|
+
const words = line.split(" ");
|
|
131
|
+
while (words.length > 1) {
|
|
132
|
+
words.pop();
|
|
133
|
+
if (fits(words.join(" ")))
|
|
134
|
+
return words.join(" ") + ELLIPSIS;
|
|
135
|
+
}
|
|
136
|
+
let single = words[0] ?? "";
|
|
137
|
+
while (single.length > 1) {
|
|
138
|
+
single = single.slice(0, -1);
|
|
139
|
+
if (fits(single))
|
|
140
|
+
return single + ELLIPSIS;
|
|
141
|
+
}
|
|
142
|
+
return ELLIPSIS;
|
|
143
|
+
}
|
|
144
|
+
/** Ellipsizes the LAST segment of a truncated segment line in place (within the width left by the
|
|
145
|
+
* segments before it) and recomputes the line width. */
|
|
146
|
+
function ellipsizeSegmentLine(line, defaults, maxWidth, metrics) {
|
|
147
|
+
const segs = line.segments;
|
|
148
|
+
if (segs.length === 0)
|
|
149
|
+
return;
|
|
150
|
+
const widthOf = (seg) => metrics.getStringWidth(seg.content, seg.fontFamily || defaults.fontFamily, seg.fontSize || defaults.fontSize, seg.fontStyle || defaults.fontStyle);
|
|
151
|
+
let prefix = 0;
|
|
152
|
+
for (let i = 0; i < segs.length - 1; i++)
|
|
153
|
+
prefix += widthOf(segs[i]);
|
|
154
|
+
const last = segs[segs.length - 1];
|
|
155
|
+
last.content = ellipsize(last.content, last.fontFamily || defaults.fontFamily, last.fontSize || defaults.fontSize, last.fontStyle || defaults.fontStyle, maxWidth - prefix, metrics);
|
|
156
|
+
line.width = prefix + widthOf(last);
|
|
157
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Color } from "../common/color.js";
|
|
2
|
+
import { FontStyle } from "../utils/pdf-object-manager.js";
|
|
3
|
+
import { HorizontalAlignment } from "../elements/pdf-element.js";
|
|
4
|
+
/**
|
|
5
|
+
* The inheritable text properties - the same set CSS and Flutter cascade. A `Text` resolves each of
|
|
6
|
+
* its own (possibly unset) properties against the nearest cascaded style: explicit > inherited >
|
|
7
|
+
* built-in default. Box/layout properties (padding, border, width, ...) are deliberately NOT here -
|
|
8
|
+
* they never inherit, exactly as in CSS.
|
|
9
|
+
*/
|
|
10
|
+
export interface ResolvedTextStyle {
|
|
11
|
+
fontSize: number;
|
|
12
|
+
fontFamily: string;
|
|
13
|
+
fontStyle: FontStyle;
|
|
14
|
+
color: Color;
|
|
15
|
+
textAlignment: HorizontalAlignment;
|
|
16
|
+
lineHeight: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The root of the cascade: what a `Text` falls back to when neither it nor any ancestor sets a
|
|
20
|
+
* property. These MUST match the historical `Text` defaults so a document that sets nothing renders
|
|
21
|
+
* byte-for-byte as before.
|
|
22
|
+
*/
|
|
23
|
+
export declare const DEFAULT_TEXT_STYLE: ResolvedTextStyle;
|
|
24
|
+
/** Layers a partial override onto a complete style; an unset (undefined) field keeps the base. */
|
|
25
|
+
export declare function mergeTextStyle(base: ResolvedTextStyle, override?: Partial<ResolvedTextStyle>): ResolvedTextStyle;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Color } from "../common/color.js";
|
|
2
|
+
import { FontStyle } from "../utils/pdf-object-manager.js";
|
|
3
|
+
import { HorizontalAlignment } from "../elements/pdf-element.js";
|
|
4
|
+
/**
|
|
5
|
+
* The root of the cascade: what a `Text` falls back to when neither it nor any ancestor sets a
|
|
6
|
+
* property. These MUST match the historical `Text` defaults so a document that sets nothing renders
|
|
7
|
+
* byte-for-byte as before.
|
|
8
|
+
*/
|
|
9
|
+
export const DEFAULT_TEXT_STYLE = {
|
|
10
|
+
fontSize: 12,
|
|
11
|
+
fontFamily: "Helvetica",
|
|
12
|
+
fontStyle: FontStyle.Normal,
|
|
13
|
+
color: new Color(0, 0, 0),
|
|
14
|
+
textAlignment: HorizontalAlignment.left,
|
|
15
|
+
lineHeight: 1,
|
|
16
|
+
};
|
|
17
|
+
/** Layers a partial override onto a complete style; an unset (undefined) field keeps the base. */
|
|
18
|
+
export function mergeTextStyle(base, override) {
|
|
19
|
+
if (!override)
|
|
20
|
+
return base;
|
|
21
|
+
return {
|
|
22
|
+
fontSize: override.fontSize ?? base.fontSize,
|
|
23
|
+
fontFamily: override.fontFamily ?? base.fontFamily,
|
|
24
|
+
fontStyle: override.fontStyle ?? base.fontStyle,
|
|
25
|
+
color: override.color ?? base.color,
|
|
26
|
+
textAlignment: override.textAlignment ?? base.textAlignment,
|
|
27
|
+
lineHeight: override.lineHeight ?? base.lineHeight,
|
|
28
|
+
};
|
|
29
|
+
}
|
package/dist/utils/afm-parser.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.AFMParser = void 0;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
class AFMParser {
|
|
1
|
+
import { AGL } from "../assets/font-data.js";
|
|
2
|
+
export class AFMParser {
|
|
10
3
|
constructor(afmData) {
|
|
11
4
|
this.advanceWidths = {};
|
|
12
5
|
this.kerningPairs = {};
|
|
@@ -15,9 +8,7 @@ class AFMParser {
|
|
|
15
8
|
this.loadGlyphList();
|
|
16
9
|
}
|
|
17
10
|
loadGlyphList() {
|
|
18
|
-
const
|
|
19
|
-
const fileContent = fs_1.default.readFileSync(afmFilePath, "utf-8");
|
|
20
|
-
const lines = fileContent.split("\n");
|
|
11
|
+
const lines = AGL.split("\n");
|
|
21
12
|
for (const line of lines) {
|
|
22
13
|
const parts = line.trim().split(";");
|
|
23
14
|
if (parts.length >= 2) {
|
|
@@ -88,4 +79,3 @@ class AFMParser {
|
|
|
88
79
|
return this.kerningPairs[pair] || 0;
|
|
89
80
|
}
|
|
90
81
|
}
|
|
91
|
-
exports.AFMParser = AFMParser;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `Uint8Array` → a latin1 (ISO-8859-1) string: each byte 0x00-0xFF maps 1:1 to the same code point.
|
|
3
|
+
* This matches `Buffer.toString("latin1")` exactly. NOTE: do NOT use `TextDecoder("latin1")` here - per
|
|
4
|
+
* the encoding spec that label is really windows-1252, which mangles 0x80-0x9F; we need a true 1:1 pass
|
|
5
|
+
* so arbitrary binary (e.g. a compressed stream) rides through unchanged. The PDF body is assembled as
|
|
6
|
+
* such a binary string and the final encoder passes 0x00-0xFF through.
|
|
7
|
+
*/
|
|
8
|
+
export declare function latin1FromBytes(bytes: Uint8Array): string;
|
|
9
|
+
/**
|
|
10
|
+
* A latin1 (ISO-8859-1) string → a `Uint8Array`: each char's low byte. Matches `Buffer.from(str,
|
|
11
|
+
* "latin1")` / `"binary"`. Use for the binary strings the engine assembles char-by-char (CMaps, object
|
|
12
|
+
* bodies) before they become a stream.
|
|
13
|
+
*/
|
|
14
|
+
export declare function bytesFromLatin1(str: string): Uint8Array;
|
|
15
|
+
export declare function u8(b: Uint8Array, o: number): number;
|
|
16
|
+
export declare function u16(b: Uint8Array, o: number): number;
|
|
17
|
+
export declare function u32(b: Uint8Array, o: number): number;
|
|
18
|
+
export declare function i16(b: Uint8Array, o: number): number;
|
|
19
|
+
export declare function wu16(b: Uint8Array, value: number, offset: number): void;
|
|
20
|
+
export declare function wu32(b: Uint8Array, value: number, offset: number): void;
|
|
21
|
+
export declare function wi16(b: Uint8Array, value: number, offset: number): void;
|
|
22
|
+
export declare function writeLatin1(b: Uint8Array, str: string, offset: number): void;
|
|
23
|
+
/** Concatenate Uint8Arrays into one (mirrors Buffer.concat). */
|
|
24
|
+
export declare function concatBytes(parts: Uint8Array[]): Uint8Array;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Byte helpers for an isomorphic engine (Node + browser). Kept tiny + dependency-free; as the engine
|
|
2
|
+
// moves off Node-only APIs, the Node `Buffer` conveniences are replaced by these.
|
|
3
|
+
/**
|
|
4
|
+
* A `Uint8Array` → a latin1 (ISO-8859-1) string: each byte 0x00-0xFF maps 1:1 to the same code point.
|
|
5
|
+
* This matches `Buffer.toString("latin1")` exactly. NOTE: do NOT use `TextDecoder("latin1")` here - per
|
|
6
|
+
* the encoding spec that label is really windows-1252, which mangles 0x80-0x9F; we need a true 1:1 pass
|
|
7
|
+
* so arbitrary binary (e.g. a compressed stream) rides through unchanged. The PDF body is assembled as
|
|
8
|
+
* such a binary string and the final encoder passes 0x00-0xFF through.
|
|
9
|
+
*/
|
|
10
|
+
export function latin1FromBytes(bytes) {
|
|
11
|
+
let out = "";
|
|
12
|
+
const CHUNK = 0x8000; // chunk the apply() to stay under the argument-count limit on big streams
|
|
13
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
14
|
+
out += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A latin1 (ISO-8859-1) string → a `Uint8Array`: each char's low byte. Matches `Buffer.from(str,
|
|
20
|
+
* "latin1")` / `"binary"`. Use for the binary strings the engine assembles char-by-char (CMaps, object
|
|
21
|
+
* bodies) before they become a stream.
|
|
22
|
+
*/
|
|
23
|
+
export function bytesFromLatin1(str) {
|
|
24
|
+
const u8a = new Uint8Array(str.length);
|
|
25
|
+
for (let i = 0; i < str.length; i++)
|
|
26
|
+
u8a[i] = str.charCodeAt(i) & 0xff;
|
|
27
|
+
return u8a;
|
|
28
|
+
}
|
|
29
|
+
// Big-endian integer reads from a Uint8Array (TrueType + PDF are big-endian), mirroring
|
|
30
|
+
// Buffer.readUInt8 / readUInt16BE / readUInt32BE / readInt16BE without needing a Buffer.
|
|
31
|
+
export function u8(b, o) {
|
|
32
|
+
return b[o];
|
|
33
|
+
}
|
|
34
|
+
export function u16(b, o) {
|
|
35
|
+
return (b[o] << 8) | b[o + 1];
|
|
36
|
+
}
|
|
37
|
+
export function u32(b, o) {
|
|
38
|
+
// Multiply the top byte (a left shift by 24 would go negative in 32-bit signed math).
|
|
39
|
+
return b[o] * 0x1000000 + ((b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]);
|
|
40
|
+
}
|
|
41
|
+
export function i16(b, o) {
|
|
42
|
+
const v = (b[o] << 8) | b[o + 1];
|
|
43
|
+
return v & 0x8000 ? v - 0x10000 : v;
|
|
44
|
+
}
|
|
45
|
+
// Big-endian integer + latin1-string writes into a Uint8Array, mirroring Buffer.writeUInt16BE /
|
|
46
|
+
// writeUInt32BE / writeInt16BE / write(str, off, len, "latin1"). Arg order matches Buffer: (value, offset).
|
|
47
|
+
export function wu16(b, value, offset) {
|
|
48
|
+
b[offset] = (value >>> 8) & 0xff;
|
|
49
|
+
b[offset + 1] = value & 0xff;
|
|
50
|
+
}
|
|
51
|
+
export function wu32(b, value, offset) {
|
|
52
|
+
b[offset] = (value >>> 24) & 0xff;
|
|
53
|
+
b[offset + 1] = (value >>> 16) & 0xff;
|
|
54
|
+
b[offset + 2] = (value >>> 8) & 0xff;
|
|
55
|
+
b[offset + 3] = value & 0xff;
|
|
56
|
+
}
|
|
57
|
+
export function wi16(b, value, offset) {
|
|
58
|
+
wu16(b, value & 0xffff, offset);
|
|
59
|
+
}
|
|
60
|
+
export function writeLatin1(b, str, offset) {
|
|
61
|
+
for (let i = 0; i < str.length; i++)
|
|
62
|
+
b[offset + i] = str.charCodeAt(i) & 0xff;
|
|
63
|
+
}
|
|
64
|
+
/** Concatenate Uint8Arrays into one (mirrors Buffer.concat). */
|
|
65
|
+
export function concatBytes(parts) {
|
|
66
|
+
let len = 0;
|
|
67
|
+
for (const p of parts)
|
|
68
|
+
len += p.length;
|
|
69
|
+
const out = new Uint8Array(len);
|
|
70
|
+
let off = 0;
|
|
71
|
+
for (const p of parts) {
|
|
72
|
+
out.set(p, off);
|
|
73
|
+
off += p.length;
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PDFElement, LayoutContext } from "../elements/pdf-element";
|
|
2
|
-
import { BoxConstraints, Offset, Size } from "../layout/box-constraints";
|
|
1
|
+
import { PDFElement, LayoutContext } from "../elements/pdf-element.js";
|
|
2
|
+
import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
|
|
3
3
|
/** Distribution of the children ALONG the stacking (main) axis when there is leftover
|
|
4
4
|
* space and no flex child to absorb it. */
|
|
5
5
|
export type MainAlign = "start" | "center" | "end" | "between" | "around";
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const pdf_element_1 = require("../elements/pdf-element");
|
|
5
|
-
const box_constraints_1 = require("../layout/box-constraints");
|
|
6
|
-
exports.VERTICAL_AXIS = {
|
|
1
|
+
import { FlexiblePDFElement } from "../elements/pdf-element.js";
|
|
2
|
+
import { BoxConstraints } from "../layout/box-constraints.js";
|
|
3
|
+
export const VERTICAL_AXIS = {
|
|
7
4
|
mainOf: (s) => s.height,
|
|
8
5
|
crossOf: (s) => s.width,
|
|
9
|
-
measureConstraints: (crossAvail) =>
|
|
10
|
-
flexConstraints: (mainExtent, crossAvail) =>
|
|
6
|
+
measureConstraints: (crossAvail) => BoxConstraints.loose(crossAvail, Infinity),
|
|
7
|
+
flexConstraints: (mainExtent, crossAvail) => BoxConstraints.loose(crossAvail, mainExtent),
|
|
11
8
|
offsetAt: (mainPos, crossPos) => ({ x: crossPos, y: mainPos }),
|
|
12
9
|
};
|
|
13
|
-
|
|
10
|
+
export const HORIZONTAL_AXIS = {
|
|
14
11
|
mainOf: (s) => s.width,
|
|
15
12
|
crossOf: (s) => s.height,
|
|
16
|
-
measureConstraints: (crossAvail) =>
|
|
17
|
-
flexConstraints: (mainExtent, crossAvail) =>
|
|
13
|
+
measureConstraints: (crossAvail) => BoxConstraints.loose(Infinity, crossAvail),
|
|
14
|
+
flexConstraints: (mainExtent, crossAvail) => BoxConstraints.loose(mainExtent, crossAvail),
|
|
18
15
|
offsetAt: (mainPos, crossPos) => ({ x: mainPos, y: crossPos }),
|
|
19
16
|
};
|
|
20
17
|
/** Cross-axis offset of a child of size `childCross` within `crossExtent`. */
|
|
@@ -25,7 +22,7 @@ function crossOffset(align, crossExtent, childCross) {
|
|
|
25
22
|
return Math.max(0, crossExtent - childCross);
|
|
26
23
|
return 0; // start, stretch (stretch fills, so no offset)
|
|
27
24
|
}
|
|
28
|
-
class FlexLayoutHelper {
|
|
25
|
+
export class FlexLayoutHelper {
|
|
29
26
|
/**
|
|
30
27
|
* Lays out a flex line along `axis`, IN SOURCE ORDER, and places every child.
|
|
31
28
|
* Fixed children take their natural main extent; flex (`ExpandedElement`) children
|
|
@@ -36,10 +33,9 @@ class FlexLayoutHelper {
|
|
|
36
33
|
* previous Column layout exactly.
|
|
37
34
|
*/
|
|
38
35
|
static layout(children, axis, mainAvail, crossAvail, mainStart, crossOrigin, options, ctx) {
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const cross = (_c = options.cross) !== null && _c !== void 0 ? _c : "stretch";
|
|
36
|
+
const gap = options.gap ?? 0;
|
|
37
|
+
const main = options.main ?? "start";
|
|
38
|
+
const cross = options.cross ?? "stretch";
|
|
43
39
|
const count = children.length;
|
|
44
40
|
// Pass 1: measure the fixed children (main extent + cross size) and total the flex.
|
|
45
41
|
let fixedMain = 0;
|
|
@@ -47,7 +43,7 @@ class FlexLayoutHelper {
|
|
|
47
43
|
let crossUsed = 0;
|
|
48
44
|
const fixedSize = new Map();
|
|
49
45
|
for (const child of children) {
|
|
50
|
-
if (child instanceof
|
|
46
|
+
if (child instanceof FlexiblePDFElement) {
|
|
51
47
|
totalFlex += child.getFlex();
|
|
52
48
|
}
|
|
53
49
|
else {
|
|
@@ -80,7 +76,7 @@ class FlexLayoutHelper {
|
|
|
80
76
|
// toward the line's cross extent - they're placed in pass 2, but crossExtent needs them now.
|
|
81
77
|
if (totalFlex > 0) {
|
|
82
78
|
for (const child of children) {
|
|
83
|
-
if (child instanceof
|
|
79
|
+
if (child instanceof FlexiblePDFElement) {
|
|
84
80
|
const mainExtent = (child.getFlex() / totalFlex) * remaining;
|
|
85
81
|
const size = child.calculateLayout(axis.flexConstraints(mainExtent, crossAvail), axis.offsetAt(mainStart, crossOrigin), ctx);
|
|
86
82
|
crossUsed = Math.max(crossUsed, axis.crossOf(size));
|
|
@@ -98,7 +94,7 @@ class FlexLayoutHelper {
|
|
|
98
94
|
let placedCross = 0;
|
|
99
95
|
children.forEach((child, index) => {
|
|
100
96
|
let mainExtent;
|
|
101
|
-
if (child instanceof
|
|
97
|
+
if (child instanceof FlexiblePDFElement) {
|
|
102
98
|
mainExtent = (child.getFlex() / totalFlex) * remaining;
|
|
103
99
|
const size = child.calculateLayout(axis.flexConstraints(mainExtent, stretch ? crossExtent : crossAvail), axis.offsetAt(mainPos, crossOrigin), ctx);
|
|
104
100
|
placedCross = Math.max(placedCross, axis.crossOf(size));
|
|
@@ -116,4 +112,3 @@ class FlexLayoutHelper {
|
|
|
116
112
|
return { mainUsed: mainPos - mainStart, crossUsed: placedCross };
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
|
-
exports.FlexLayoutHelper = FlexLayoutHelper;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FontStyle } from "./pdf-object-manager";
|
|
1
|
+
import type { FontStyle } from "./pdf-object-manager.js";
|
|
2
2
|
/**
|
|
3
3
|
* Read-only font measurement - the slice of the object manager that the layout pass
|
|
4
4
|
* needs. Keeping layout (and, later, fragmentation) behind this interface means those
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/utils/font-path.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const os = require("os");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
function getFontPath(fontName) {
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function getFontPath(fontName) {
|
|
7
4
|
const platform = os.platform();
|
|
8
5
|
let fontPath = "";
|
|
9
6
|
if (platform === "win32") {
|