@jasy/pdf 1.0.0-alpha.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 +171 -0
- package/dist/api/args.d.ts +10 -0
- package/dist/api/args.js +13 -0
- package/dist/api/color.d.ts +24 -0
- package/dist/api/color.js +215 -0
- package/dist/api/content.d.ts +36 -0
- package/dist/api/content.js +50 -0
- package/dist/api/descriptor.d.ts +25 -0
- package/dist/api/descriptor.js +71 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/index.js +27 -0
- package/dist/api/insets.d.ts +16 -0
- package/dist/api/insets.js +21 -0
- package/dist/api/layout.d.ts +72 -0
- package/dist/api/layout.js +99 -0
- package/dist/api/structure.d.ts +80 -0
- package/dist/api/structure.js +125 -0
- package/dist/api/table.d.ts +37 -0
- package/dist/api/table.js +87 -0
- package/dist/api/text.d.ts +28 -0
- package/dist/api/text.js +61 -0
- package/dist/assets/Courier-Bold.afm +342 -0
- package/dist/assets/Courier-BoldOblique.afm +342 -0
- package/dist/assets/Courier-Oblique.afm +342 -0
- package/dist/assets/Courier.afm +342 -0
- package/dist/assets/Helvetica-Bold.afm +2827 -0
- package/dist/assets/Helvetica-BoldOblique.afm +2827 -0
- package/dist/assets/Helvetica-Oblique.afm +3051 -0
- package/dist/assets/Helvetica.afm +3051 -0
- package/dist/assets/Symbol.afm +213 -0
- package/dist/assets/Times-Bold.afm +2588 -0
- package/dist/assets/Times-BoldItalic.afm +2384 -0
- package/dist/assets/Times-Italic.afm +2667 -0
- package/dist/assets/Times-Roman.afm +2419 -0
- package/dist/assets/ZapfDingbats.afm +225 -0
- package/dist/assets/agl.txt +695 -0
- package/dist/common/color.d.ts +37 -0
- package/dist/common/color.js +62 -0
- package/dist/constants/page-sizes.d.ts +44 -0
- package/dist/constants/page-sizes.js +92 -0
- package/dist/constants/pdf-parts.d.ts +5 -0
- package/dist/constants/pdf-parts.js +8 -0
- package/dist/elements/container-element.d.ts +35 -0
- package/dist/elements/container-element.js +91 -0
- package/dist/elements/image-element.d.ts +56 -0
- package/dist/elements/image-element.js +151 -0
- package/dist/elements/index.d.ts +10 -0
- package/dist/elements/index.js +28 -0
- package/dist/elements/layout/deferred-element.d.ts +19 -0
- package/dist/elements/layout/deferred-element.js +33 -0
- package/dist/elements/layout/expanded-element.d.ts +30 -0
- package/dist/elements/layout/expanded-element.js +68 -0
- package/dist/elements/layout/padding-element.d.ts +29 -0
- package/dist/elements/layout/padding-element.js +76 -0
- package/dist/elements/layout/repeating-header-element.d.ts +29 -0
- package/dist/elements/layout/repeating-header-element.js +60 -0
- package/dist/elements/layout/sized-container-element.d.ts +14 -0
- package/dist/elements/layout/sized-container-element.js +37 -0
- package/dist/elements/line-element.d.ts +31 -0
- package/dist/elements/line-element.js +44 -0
- package/dist/elements/page-element.d.ts +58 -0
- package/dist/elements/page-element.js +90 -0
- package/dist/elements/pdf-document-element.d.ts +13 -0
- package/dist/elements/pdf-document-element.js +22 -0
- package/dist/elements/pdf-element.d.ts +67 -0
- package/dist/elements/pdf-element.js +55 -0
- package/dist/elements/rectangle-element.d.ts +55 -0
- package/dist/elements/rectangle-element.js +120 -0
- package/dist/elements/row-element.d.ts +42 -0
- package/dist/elements/row-element.js +54 -0
- package/dist/elements/text-element.d.ts +59 -0
- package/dist/elements/text-element.js +137 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +21 -0
- package/dist/ir/display-list.d.ts +74 -0
- package/dist/ir/display-list.js +2 -0
- package/dist/layout/box-constraints.d.ts +56 -0
- package/dist/layout/box-constraints.js +73 -0
- package/dist/layout/fragmentation.d.ts +42 -0
- package/dist/layout/fragmentation.js +61 -0
- package/dist/renderer/container-renderer.d.ts +6 -0
- package/dist/renderer/container-renderer.js +30 -0
- package/dist/renderer/deferred-renderer.d.ts +6 -0
- package/dist/renderer/deferred-renderer.js +25 -0
- package/dist/renderer/expanded-renderer.d.ts +6 -0
- package/dist/renderer/expanded-renderer.js +23 -0
- package/dist/renderer/image-renderer.d.ts +6 -0
- package/dist/renderer/image-renderer.js +85 -0
- package/dist/renderer/index.d.ts +10 -0
- package/dist/renderer/index.js +26 -0
- package/dist/renderer/line-renderer.d.ts +6 -0
- package/dist/renderer/line-renderer.js +30 -0
- package/dist/renderer/padding-renderer.d.ts +6 -0
- package/dist/renderer/padding-renderer.js +23 -0
- package/dist/renderer/page-renderer.d.ts +5 -0
- package/dist/renderer/page-renderer.js +81 -0
- package/dist/renderer/pdf-backend.d.ts +45 -0
- package/dist/renderer/pdf-backend.js +184 -0
- package/dist/renderer/pdf-config.d.ts +8 -0
- package/dist/renderer/pdf-config.js +17 -0
- package/dist/renderer/pdf-document-class.d.ts +42 -0
- package/dist/renderer/pdf-document-class.js +118 -0
- package/dist/renderer/pdf-document-renderer.d.ts +20 -0
- package/dist/renderer/pdf-document-renderer.js +104 -0
- package/dist/renderer/pdf-renderer.d.ts +5 -0
- package/dist/renderer/pdf-renderer.js +92 -0
- package/dist/renderer/rectangle-renderer.d.ts +8 -0
- package/dist/renderer/rectangle-renderer.js +61 -0
- package/dist/renderer/repeating-header-renderer.d.ts +6 -0
- package/dist/renderer/repeating-header-renderer.js +28 -0
- package/dist/renderer/row-renderer.d.ts +6 -0
- package/dist/renderer/row-renderer.js +30 -0
- package/dist/renderer/text-renderer.d.ts +9 -0
- package/dist/renderer/text-renderer.js +125 -0
- package/dist/text/line-breaker.d.ts +40 -0
- package/dist/text/line-breaker.js +106 -0
- package/dist/utils/afm-parser.d.ts +12 -0
- package/dist/utils/afm-parser.js +91 -0
- package/dist/utils/flex-layout.d.ts +53 -0
- package/dist/utils/flex-layout.js +119 -0
- package/dist/utils/font-metrics.d.ts +12 -0
- package/dist/utils/font-metrics.js +2 -0
- package/dist/utils/font-path.d.ts +1 -0
- package/dist/utils/font-path.js +19 -0
- package/dist/utils/image-helper.d.ts +43 -0
- package/dist/utils/image-helper.js +206 -0
- package/dist/utils/pdf-object-manager.d.ts +97 -0
- package/dist/utils/pdf-object-manager.js +645 -0
- package/dist/utils/renderer-registry.d.ts +6 -0
- package/dist/utils/renderer-registry.js +19 -0
- package/dist/utils/ttf-parser.d.ts +29 -0
- package/dist/utils/ttf-parser.js +191 -0
- package/dist/utils/ttf-subsetter.d.ts +1 -0
- package/dist/utils/ttf-subsetter.js +161 -0
- package/dist/utils/utf8-to-windows1252-encoder.d.ts +2 -0
- package/dist/utils/utf8-to-windows1252-encoder.js +55 -0
- package/dist/validators/element-validator.d.ts +8 -0
- package/dist/validators/element-validator.js +61 -0
- package/package.json +50 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
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.PageRenderer = void 0;
|
|
13
|
+
const page_sizes_1 = require("../constants/page-sizes");
|
|
14
|
+
const renderer_registry_1 = require("../utils/renderer-registry");
|
|
15
|
+
const pdf_config_1 = require("./pdf-config");
|
|
16
|
+
const pdf_backend_1 = require("./pdf-backend");
|
|
17
|
+
class PageRenderer {
|
|
18
|
+
static render(page, objectManager) {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
var _a;
|
|
21
|
+
const { children, config, header, footer } = page.getProps();
|
|
22
|
+
// Header (top band) and footer (bottom band) sit around the body and repeat on every
|
|
23
|
+
// physical page; they are placed by `PageElement.calculateLayout` / the page driver.
|
|
24
|
+
const renderables = [...(header ? [header] : []), ...children, ...(footer ? [footer] : [])];
|
|
25
|
+
// Page geometry (also the MediaBox below). Needed up front because flipping the
|
|
26
|
+
// display list to PDF coordinates uses the page height. config is fully resolved
|
|
27
|
+
// by the layout pass; fall back to the document default rather than asserting.
|
|
28
|
+
let [width, height] = page_sizes_1.pageFormats[(_a = config === null || config === void 0 ? void 0 : config.pageSize) !== null && _a !== void 0 ? _a : page_sizes_1.PageSize.A4];
|
|
29
|
+
if ((config === null || config === void 0 ? void 0 : config.orientation) === pdf_config_1.Orientation.landscape) {
|
|
30
|
+
[width, height] = [height, width];
|
|
31
|
+
}
|
|
32
|
+
// Collect the whole page as a display list (top-left coordinates), flip it to PDF
|
|
33
|
+
// coordinates at this one seam, then serialize once. Serializing registers the
|
|
34
|
+
// fonts/images used below, so it must run before the resource section.
|
|
35
|
+
const nodes = [];
|
|
36
|
+
for (const element of renderables) {
|
|
37
|
+
const renderer = renderer_registry_1.RendererRegistry.getRenderer(element);
|
|
38
|
+
if (renderer) {
|
|
39
|
+
nodes.push(...(yield renderer(element, objectManager)));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const pageContent = pdf_backend_1.PdfBackend.serialize(pdf_backend_1.PdfBackend.flipY(nodes, height), objectManager);
|
|
43
|
+
// Add the page content as a new object (FlateDecode-compressed when enabled). The /Length is
|
|
44
|
+
// computed inside, with an explicit EOL before `endstream` (PDF/A clause 6.1.7.1).
|
|
45
|
+
const contentObjectNumber = objectManager.addContentStream(pageContent);
|
|
46
|
+
// Get the parent object number dynamically (linked with the page object)
|
|
47
|
+
const parentObjectNumber = objectManager.getParentObjectNumber(); // Get parent object number
|
|
48
|
+
// Page object with MediaBox
|
|
49
|
+
// - Get all fonts and add it to the page (reference)
|
|
50
|
+
objectManager.registerFont("Helvetica");
|
|
51
|
+
const fontReferences = [];
|
|
52
|
+
objectManager.getAllFontsRaw().forEach((value, _key) => {
|
|
53
|
+
const fontRef = `/F${value.fontIndex} ${value.resourceIndex} 0 R`;
|
|
54
|
+
fontReferences.push(fontRef);
|
|
55
|
+
});
|
|
56
|
+
// - Get all images and add it to the page (reference)
|
|
57
|
+
const imageReferences = [];
|
|
58
|
+
objectManager.getAllImagesRaw().forEach((value) => {
|
|
59
|
+
const imageRef = `/IM${value} ${value} 0 R`;
|
|
60
|
+
imageReferences.push(imageRef);
|
|
61
|
+
});
|
|
62
|
+
const imageCode = imageReferences.length > 0
|
|
63
|
+
? "/ProcSet [/PDF /Text /ImageB /ImageC /ImageI] /XObject <<\n" +
|
|
64
|
+
imageReferences.join("\n") +
|
|
65
|
+
"\n>>\n"
|
|
66
|
+
: "";
|
|
67
|
+
// - Transparency (ExtGState) references, registered during serialize above
|
|
68
|
+
const extGStateReferences = [];
|
|
69
|
+
objectManager.getAllExtGStatesRaw().forEach((objectNumber, name) => {
|
|
70
|
+
extGStateReferences.push(`/${name} ${objectNumber} 0 R`);
|
|
71
|
+
});
|
|
72
|
+
const extGStateCode = extGStateReferences.length > 0
|
|
73
|
+
? "/ExtGState <<\n" + extGStateReferences.join("\n") + "\n>>\n"
|
|
74
|
+
: "";
|
|
75
|
+
const pageObject = `<< /Type /Page /Parent ${parentObjectNumber} 0 R /Contents ${contentObjectNumber} 0 R /Resources <<\n/Font <<\n${fontReferences.join("\n")}\n>>\n${imageCode}${extGStateCode}>>\n/MediaBox [0 0 ${width} ${height}] >>`;
|
|
76
|
+
// Add page as new object and return the page number
|
|
77
|
+
return objectManager.addObject(pageObject);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.PageRenderer = PageRenderer;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { IRNode } from "../ir/display-list";
|
|
2
|
+
import { PDFObjectManager } from "../utils/pdf-object-manager";
|
|
3
|
+
/**
|
|
4
|
+
* PDF backend - turns display-list primitives into content-stream operators.
|
|
5
|
+
*
|
|
6
|
+
* This is "the renderer" in the pure sense: it consumes only `IRNode`s and knows
|
|
7
|
+
* nothing about components. Each primitive maps to the exact operators previously
|
|
8
|
+
* emitted inline by the per-element renderers, so output stays byte-identical while
|
|
9
|
+
* the renderers are migrated onto the IR one at a time.
|
|
10
|
+
*/
|
|
11
|
+
export declare class PdfBackend {
|
|
12
|
+
/**
|
|
13
|
+
* Flip a display list from the engine's top-left origin (y grows downward) to PDF's
|
|
14
|
+
* bottom-left origin (y grows upward). This is the ONE place the Y axis is flipped -
|
|
15
|
+
* elements above this seam are coordinate-system-blind. Each primitive flips around
|
|
16
|
+
* its own anchor: a rect/image around its bottom edge, a text baseline / line point
|
|
17
|
+
* directly. (image is migrated in a later slice and still arrives pre-flipped.)
|
|
18
|
+
*/
|
|
19
|
+
static flipY(nodes: IRNode[], pageHeight: number): IRNode[];
|
|
20
|
+
/** Serialize a whole display list into one content stream (page-level entry point). */
|
|
21
|
+
static serialize(nodes: IRNode[], om: PDFObjectManager): string;
|
|
22
|
+
/**
|
|
23
|
+
* Escapes a string for a PDF literal string `( ... )`. The backslash must be doubled
|
|
24
|
+
* first, then the parentheses that delimit the string. Without this, a ")" in the
|
|
25
|
+
* text closes the string early and the remaining characters leak as raw operators.
|
|
26
|
+
*/
|
|
27
|
+
static escapePdfString(text: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Returns the `/GSx gs` operator that selects a transparency state, or `""` when both
|
|
30
|
+
* alphas are fully opaque. Opaque draws emit nothing here, so output stays
|
|
31
|
+
* byte-identical until transparency is actually used.
|
|
32
|
+
*/
|
|
33
|
+
private static alphaPrefix;
|
|
34
|
+
/**
|
|
35
|
+
* Path operators for a rounded rectangle: bottom-left at (x,y), size w×h, corner
|
|
36
|
+
* radius `radius` (clamped to half the smaller side). Corners are 90° Bézier arcs
|
|
37
|
+
* (kappa ≈ 0.5523). Returns m/l/c/h ops WITHOUT the paint operator.
|
|
38
|
+
*/
|
|
39
|
+
private static roundedRectPath;
|
|
40
|
+
/**
|
|
41
|
+
* Serialize a single display-list node to its content-stream operators.
|
|
42
|
+
* `om` is used only by primitives that allocate PDF resources (images, fonts).
|
|
43
|
+
*/
|
|
44
|
+
static serializeNode(node: IRNode, om: PDFObjectManager): string;
|
|
45
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PdfBackend = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* PDF backend - turns display-list primitives into content-stream operators.
|
|
6
|
+
*
|
|
7
|
+
* This is "the renderer" in the pure sense: it consumes only `IRNode`s and knows
|
|
8
|
+
* nothing about components. Each primitive maps to the exact operators previously
|
|
9
|
+
* emitted inline by the per-element renderers, so output stays byte-identical while
|
|
10
|
+
* the renderers are migrated onto the IR one at a time.
|
|
11
|
+
*/
|
|
12
|
+
class PdfBackend {
|
|
13
|
+
/**
|
|
14
|
+
* Flip a display list from the engine's top-left origin (y grows downward) to PDF's
|
|
15
|
+
* bottom-left origin (y grows upward). This is the ONE place the Y axis is flipped -
|
|
16
|
+
* elements above this seam are coordinate-system-blind. Each primitive flips around
|
|
17
|
+
* its own anchor: a rect/image around its bottom edge, a text baseline / line point
|
|
18
|
+
* directly. (image is migrated in a later slice and still arrives pre-flipped.)
|
|
19
|
+
*/
|
|
20
|
+
static flipY(nodes, pageHeight) {
|
|
21
|
+
return nodes.map((node) => {
|
|
22
|
+
switch (node.type) {
|
|
23
|
+
case "rect":
|
|
24
|
+
return Object.assign(Object.assign({}, node), { y: pageHeight - node.y - node.height });
|
|
25
|
+
case "line":
|
|
26
|
+
return Object.assign(Object.assign({}, node), { y1: pageHeight - node.y1, y2: pageHeight - node.y2 });
|
|
27
|
+
case "text":
|
|
28
|
+
// node.y is the baseline measured from the page top; flip it directly.
|
|
29
|
+
return Object.assign(Object.assign({}, node), { y: pageHeight - node.y });
|
|
30
|
+
case "image": {
|
|
31
|
+
// Flip the placement box (and the clip frame, if any) around its bottom edge.
|
|
32
|
+
const flipped = Object.assign(Object.assign({}, node), { y: pageHeight - node.y - node.height });
|
|
33
|
+
if (node.clip) {
|
|
34
|
+
flipped.clip = Object.assign(Object.assign({}, node.clip), { y: pageHeight - node.clip.y - node.clip.height });
|
|
35
|
+
}
|
|
36
|
+
return flipped;
|
|
37
|
+
}
|
|
38
|
+
default: {
|
|
39
|
+
const unknown = node;
|
|
40
|
+
return unknown;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** Serialize a whole display list into one content stream (page-level entry point). */
|
|
46
|
+
static serialize(nodes, om) {
|
|
47
|
+
return nodes.map((node) => PdfBackend.serializeNode(node, om)).join("");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Escapes a string for a PDF literal string `( ... )`. The backslash must be doubled
|
|
51
|
+
* first, then the parentheses that delimit the string. Without this, a ")" in the
|
|
52
|
+
* text closes the string early and the remaining characters leak as raw operators.
|
|
53
|
+
*/
|
|
54
|
+
static escapePdfString(text) {
|
|
55
|
+
return text.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns the `/GSx gs` operator that selects a transparency state, or `""` when both
|
|
59
|
+
* alphas are fully opaque. Opaque draws emit nothing here, so output stays
|
|
60
|
+
* byte-identical until transparency is actually used.
|
|
61
|
+
*/
|
|
62
|
+
static alphaPrefix(om, fillAlpha, strokeAlpha) {
|
|
63
|
+
if (fillAlpha >= 1 && strokeAlpha >= 1)
|
|
64
|
+
return "";
|
|
65
|
+
return `/${om.registerExtGState(fillAlpha, strokeAlpha)} gs\n`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Path operators for a rounded rectangle: bottom-left at (x,y), size w×h, corner
|
|
69
|
+
* radius `radius` (clamped to half the smaller side). Corners are 90° Bézier arcs
|
|
70
|
+
* (kappa ≈ 0.5523). Returns m/l/c/h ops WITHOUT the paint operator.
|
|
71
|
+
*/
|
|
72
|
+
static roundedRectPath(x, y, w, h, radius) {
|
|
73
|
+
const r = Math.min(radius, w / 2, h / 2);
|
|
74
|
+
const c = r * 0.5523; // control-point offset that approximates a quarter circle
|
|
75
|
+
const f = (n) => n.toFixed(3);
|
|
76
|
+
const xr = x + r;
|
|
77
|
+
const xwr = x + w - r;
|
|
78
|
+
const xw = x + w;
|
|
79
|
+
const yr = y + r;
|
|
80
|
+
const yhr = y + h - r;
|
|
81
|
+
const yh = y + h;
|
|
82
|
+
return (`${f(xr)} ${f(y)} m\n` +
|
|
83
|
+
`${f(xwr)} ${f(y)} l\n` +
|
|
84
|
+
`${f(xwr + c)} ${f(y)} ${f(xw)} ${f(yr - c)} ${f(xw)} ${f(yr)} c\n` +
|
|
85
|
+
`${f(xw)} ${f(yhr)} l\n` +
|
|
86
|
+
`${f(xw)} ${f(yhr + c)} ${f(xwr + c)} ${f(yh)} ${f(xwr)} ${f(yh)} c\n` +
|
|
87
|
+
`${f(xr)} ${f(yh)} l\n` +
|
|
88
|
+
`${f(xr - c)} ${f(yh)} ${f(x)} ${f(yhr + c)} ${f(x)} ${f(yhr)} c\n` +
|
|
89
|
+
`${f(x)} ${f(yr)} l\n` +
|
|
90
|
+
`${f(x)} ${f(yr - c)} ${f(xr - c)} ${f(y)} ${f(xr)} ${f(y)} c\n` +
|
|
91
|
+
`h`);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Serialize a single display-list node to its content-stream operators.
|
|
95
|
+
* `om` is used only by primitives that allocate PDF resources (images, fonts).
|
|
96
|
+
*/
|
|
97
|
+
static serializeNode(node, om) {
|
|
98
|
+
var _a, _b, _c, _d, _e;
|
|
99
|
+
switch (node.type) {
|
|
100
|
+
case "line":
|
|
101
|
+
// q/Q isolates the graphics state; "[] 0 d" resets the dash pattern to solid.
|
|
102
|
+
return (`q\n` +
|
|
103
|
+
PdfBackend.alphaPrefix(om, 1, node.stroke.getAlpha()) +
|
|
104
|
+
`${node.strokeWidth} w\n` +
|
|
105
|
+
`${node.stroke.toPDFColorString()} RG\n` +
|
|
106
|
+
`[] 0 d\n` +
|
|
107
|
+
`${node.x1} ${node.y1} m\n` +
|
|
108
|
+
`${node.x2} ${node.y2} l\n` +
|
|
109
|
+
`S\n` +
|
|
110
|
+
`Q\n`);
|
|
111
|
+
case "rect": {
|
|
112
|
+
// Stroke only with a stroke colour AND a positive width - a 0-width border means
|
|
113
|
+
// "no border" (e.g. a filled box with no outline). Nothing to paint at all (no
|
|
114
|
+
// fill, no border) draws nothing. Paint: B = fill+stroke, f = fill, S = stroke.
|
|
115
|
+
const doStroke = !!node.stroke && ((_a = node.strokeWidth) !== null && _a !== void 0 ? _a : 0) > 0;
|
|
116
|
+
if (!node.fill && !doStroke)
|
|
117
|
+
return "";
|
|
118
|
+
let ops = "";
|
|
119
|
+
if (doStroke) {
|
|
120
|
+
ops += `${node.strokeWidth} w\n${node.stroke.toPDFColorString()} RG\n`;
|
|
121
|
+
}
|
|
122
|
+
if (node.fill)
|
|
123
|
+
ops += `${node.fill.toPDFColorString()} rg\n`;
|
|
124
|
+
const paint = node.fill ? (doStroke ? "B" : "f") : "S";
|
|
125
|
+
// Rounded corners emit a Bézier path; sharp corners keep the plain `re`
|
|
126
|
+
// (byte-identical when no radius is set).
|
|
127
|
+
const path = ((_b = node.radius) !== null && _b !== void 0 ? _b : 0) > 0
|
|
128
|
+
? PdfBackend.roundedRectPath(node.x, node.y, node.width, node.height, node.radius)
|
|
129
|
+
: `${node.x} ${node.y} ${node.width} ${node.height} re`;
|
|
130
|
+
const body = ops + `${path} ${paint}\n`;
|
|
131
|
+
// Transparency needs an isolating q/Q so the state does not leak; opaque rects
|
|
132
|
+
// keep their bare operators (byte-identical).
|
|
133
|
+
const gs = PdfBackend.alphaPrefix(om, (_d = (_c = node.fill) === null || _c === void 0 ? void 0 : _c.getAlpha()) !== null && _d !== void 0 ? _d : 1, doStroke ? node.stroke.getAlpha() : 1);
|
|
134
|
+
return gs ? `q\n${gs}${body}Q\n` : body;
|
|
135
|
+
}
|
|
136
|
+
case "text": {
|
|
137
|
+
// One self-contained text block per run. The producer has already resolved
|
|
138
|
+
// absolute position, font and color; the backend only allocates the font
|
|
139
|
+
// resource and emits the operators. The text is escaped for PDF literal-string
|
|
140
|
+
// syntax so parentheses/backslashes can't break out of the string.
|
|
141
|
+
// Embedded (custom) fonts: pick the family variant for this style (falling back to the
|
|
142
|
+
// family's Normal), select its Identity-H Type0 resource and emit hex glyph ids - both
|
|
143
|
+
// from the SAME variant. Standard fonts keep the WinAnsi literal string, byte-identical.
|
|
144
|
+
const isCustom = om.isCustomFont(node.fontFamily, node.fontStyle);
|
|
145
|
+
const font = isCustom
|
|
146
|
+
? om.getCustomFontResource(node.fontFamily, node.fontStyle)
|
|
147
|
+
: om.registerFont(node.fontFamily, node.fontStyle);
|
|
148
|
+
const textOp = isCustom
|
|
149
|
+
? `<${om.encodeCustomText(node.fontFamily, node.text, node.fontStyle)}>`
|
|
150
|
+
: `(${PdfBackend.escapePdfString(node.text)})`;
|
|
151
|
+
const block = `BT\n` +
|
|
152
|
+
`${node.color.toPDFColorString()} rg ` +
|
|
153
|
+
`/F${font.fontIndex} ${node.fontSize} Tf ` +
|
|
154
|
+
`${node.x.toFixed(3)} ${node.y.toFixed(3)} Td ` +
|
|
155
|
+
`${textOp} Tj\n` +
|
|
156
|
+
`ET\n`;
|
|
157
|
+
// Transparent text gets an isolating q/Q + gs; opaque text is byte-identical.
|
|
158
|
+
const gs = PdfBackend.alphaPrefix(om, node.color.getAlpha(), 1);
|
|
159
|
+
return gs ? `q\n${gs}${block}Q\n` : block;
|
|
160
|
+
}
|
|
161
|
+
case "image": {
|
|
162
|
+
// The backend owns PDF resource creation: register the XObject (using the
|
|
163
|
+
// source pixel dimensions) and then place it with a scaling matrix.
|
|
164
|
+
const ref = om.registerImage(node.intrinsicWidth, node.intrinsicHeight, node.imageType, node.data);
|
|
165
|
+
const draw = `q\n${node.width} 0 0 ${node.height} ${node.x} ${node.y} cm\n` + `/IM${ref} Do\nQ\n`;
|
|
166
|
+
if (!node.clip)
|
|
167
|
+
return draw;
|
|
168
|
+
// Clip to the frame (re … W n); rounded when a radius is set. The rectangular
|
|
169
|
+
// path is byte-identical to before.
|
|
170
|
+
const c = node.clip;
|
|
171
|
+
const clipPath = ((_e = node.radius) !== null && _e !== void 0 ? _e : 0) > 0
|
|
172
|
+
? PdfBackend.roundedRectPath(c.x, c.y, c.width, c.height, node.radius)
|
|
173
|
+
: `${c.x} ${c.y} ${c.width} ${c.height} re `;
|
|
174
|
+
return `q\n${clipPath}\nW n \n` + draw + `Q\n`;
|
|
175
|
+
}
|
|
176
|
+
default: {
|
|
177
|
+
// Exhaustiveness guard: if a new IRNode variant is added, this fails to compile.
|
|
178
|
+
const unknown = node;
|
|
179
|
+
throw new Error(`PdfBackend: unhandled IR node ${JSON.stringify(unknown)}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.PdfBackend = PdfBackend;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Leaf module: no imports, so it can never sit inside an import cycle. The page/color
|
|
3
|
+
// enums live here because they are runtime values used across modules that DO form
|
|
4
|
+
// cycles (object manager <-> document <-> elements); importing them from a cyclic
|
|
5
|
+
// module snapshots them as `undefined` under some load orders.
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ColorMode = exports.Orientation = void 0;
|
|
8
|
+
var Orientation;
|
|
9
|
+
(function (Orientation) {
|
|
10
|
+
Orientation["portrait"] = "PORTRAIT";
|
|
11
|
+
Orientation["landscape"] = "LANDSCAPE";
|
|
12
|
+
})(Orientation || (exports.Orientation = Orientation = {}));
|
|
13
|
+
var ColorMode;
|
|
14
|
+
(function (ColorMode) {
|
|
15
|
+
ColorMode["color"] = "COLOR";
|
|
16
|
+
ColorMode["grayscale"] = "GRAYSCALE";
|
|
17
|
+
})(ColorMode || (exports.ColorMode = ColorMode = {}));
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { PageSize } from "../constants/page-sizes";
|
|
2
|
+
import { PDFDocumentElement } from "../elements";
|
|
3
|
+
import { FontStyle, PDFObjectManager } from "../utils/pdf-object-manager";
|
|
4
|
+
import { ColorMode, Orientation } from "./pdf-config";
|
|
5
|
+
export { ColorMode, Orientation } from "./pdf-config";
|
|
6
|
+
export interface Margin {
|
|
7
|
+
left: number;
|
|
8
|
+
top: number;
|
|
9
|
+
right: number;
|
|
10
|
+
bottom: number;
|
|
11
|
+
}
|
|
12
|
+
export interface DefaultFont {
|
|
13
|
+
fontFamily: string;
|
|
14
|
+
fontSize: number;
|
|
15
|
+
fontStyle: FontStyle;
|
|
16
|
+
}
|
|
17
|
+
export interface MetaData {
|
|
18
|
+
title?: string;
|
|
19
|
+
author?: string;
|
|
20
|
+
keywords: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface PDFConfig {
|
|
23
|
+
pageSize?: PageSize;
|
|
24
|
+
orientation?: Orientation;
|
|
25
|
+
margin?: Margin;
|
|
26
|
+
colorMode?: ColorMode;
|
|
27
|
+
defaultFont?: DefaultFont;
|
|
28
|
+
metaData?: MetaData;
|
|
29
|
+
/** Register the 14 standard fonts (default true). Set false for PDF/A, where every font must be
|
|
30
|
+
* embedded - the non-embeddable standard-14 must not appear in the document at all. */
|
|
31
|
+
registerStandardFonts?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare abstract class PDFDocument {
|
|
34
|
+
private _objectManager;
|
|
35
|
+
private child;
|
|
36
|
+
private registerStandardFonts;
|
|
37
|
+
constructor(config?: PDFConfig);
|
|
38
|
+
get objectManager(): PDFObjectManager;
|
|
39
|
+
abstract build(): PDFDocumentElement;
|
|
40
|
+
protected beforeRenderer(): void;
|
|
41
|
+
static render<T extends PDFDocument>(this: new () => T): Promise<string>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
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.PDFDocument = exports.Orientation = exports.ColorMode = void 0;
|
|
13
|
+
const pdf_object_manager_1 = require("../utils/pdf-object-manager");
|
|
14
|
+
const pdf_renderer_1 = require("./pdf-renderer");
|
|
15
|
+
// Re-exported so existing `../renderer` consumers keep importing these from here.
|
|
16
|
+
var pdf_config_1 = require("./pdf-config");
|
|
17
|
+
Object.defineProperty(exports, "ColorMode", { enumerable: true, get: function () { return pdf_config_1.ColorMode; } });
|
|
18
|
+
Object.defineProperty(exports, "Orientation", { enumerable: true, get: function () { return pdf_config_1.Orientation; } });
|
|
19
|
+
class PDFDocument {
|
|
20
|
+
//#region Helper
|
|
21
|
+
// Method to register all standard fonts
|
|
22
|
+
registerStandardFonts(objectManager) {
|
|
23
|
+
const standardFonts = [
|
|
24
|
+
{
|
|
25
|
+
fontName: "Helvetica",
|
|
26
|
+
fontStyle: pdf_object_manager_1.FontStyle.Normal,
|
|
27
|
+
fullName: "Helvetica",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
fontName: "Helvetica",
|
|
31
|
+
fontStyle: pdf_object_manager_1.FontStyle.Bold,
|
|
32
|
+
fullName: "Helvetica-Bold",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
fontName: "Helvetica",
|
|
36
|
+
fontStyle: pdf_object_manager_1.FontStyle.Italic,
|
|
37
|
+
fullName: "Helvetica-Oblique",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
fontName: "Helvetica",
|
|
41
|
+
fontStyle: pdf_object_manager_1.FontStyle.BoldItalic,
|
|
42
|
+
fullName: "Helvetica-BoldOblique",
|
|
43
|
+
},
|
|
44
|
+
{ fontName: "Courier", fontStyle: pdf_object_manager_1.FontStyle.Normal, fullName: "Courier" },
|
|
45
|
+
{
|
|
46
|
+
fontName: "Courier",
|
|
47
|
+
fontStyle: pdf_object_manager_1.FontStyle.Bold,
|
|
48
|
+
fullName: "Courier-Bold",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
fontName: "Courier",
|
|
52
|
+
fontStyle: pdf_object_manager_1.FontStyle.Italic,
|
|
53
|
+
fullName: "Courier-Oblique",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
fontName: "Courier",
|
|
57
|
+
fontStyle: pdf_object_manager_1.FontStyle.BoldItalic,
|
|
58
|
+
fullName: "Courier-BoldOblique",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
fontName: "Times-Roman",
|
|
62
|
+
fontStyle: pdf_object_manager_1.FontStyle.Normal,
|
|
63
|
+
fullName: "Times-Roman",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
fontName: "Times-Roman",
|
|
67
|
+
fontStyle: pdf_object_manager_1.FontStyle.Bold,
|
|
68
|
+
fullName: "Times-Bold",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
fontName: "Times-Roman",
|
|
72
|
+
fontStyle: pdf_object_manager_1.FontStyle.Italic,
|
|
73
|
+
fullName: "Times-Italic",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
fontName: "Times-Roman",
|
|
77
|
+
fontStyle: pdf_object_manager_1.FontStyle.BoldItalic,
|
|
78
|
+
fullName: "Times-BoldItalic",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
fontName: "Symbol",
|
|
82
|
+
fontStyle: pdf_object_manager_1.FontStyle.Normal,
|
|
83
|
+
fullName: "Symbol",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
fontName: "ITC Zapf Dingbats",
|
|
87
|
+
fontStyle: pdf_object_manager_1.FontStyle.Normal,
|
|
88
|
+
fullName: "ZapfDingbats",
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
standardFonts.forEach((font) => objectManager.registerFont(font.fontName, font.fontStyle, font.fullName));
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
constructor(config) {
|
|
95
|
+
// One object manager per document instance - no global singleton. Threaded
|
|
96
|
+
// explicitly into the renderer.
|
|
97
|
+
this._objectManager = new pdf_object_manager_1.PDFObjectManager();
|
|
98
|
+
// Add all standard font families - unless the document is PDF/A, which forbids non-embedded
|
|
99
|
+
// fonts (the caller then supplies embedded fonts for every name it uses).
|
|
100
|
+
if ((config === null || config === void 0 ? void 0 : config.registerStandardFonts) !== false) {
|
|
101
|
+
this.registerStandardFonts(this._objectManager);
|
|
102
|
+
}
|
|
103
|
+
if (config)
|
|
104
|
+
this._objectManager.changePDFConfig(config);
|
|
105
|
+
}
|
|
106
|
+
get objectManager() {
|
|
107
|
+
return this._objectManager;
|
|
108
|
+
}
|
|
109
|
+
beforeRenderer() { }
|
|
110
|
+
static render() {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
const instance = new this();
|
|
113
|
+
instance.child = instance.build();
|
|
114
|
+
return yield pdf_renderer_1.PDFRenderer.render(instance.child, instance._objectManager);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.PDFDocument = PDFDocument;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PDFDocumentElement } from "../elements/pdf-document-element";
|
|
2
|
+
import { LayoutContext } from "../elements/pdf-element";
|
|
3
|
+
import { PDFObjectManager } from "../utils/pdf-object-manager";
|
|
4
|
+
export declare class PDFDocumentRenderer {
|
|
5
|
+
static render(document: PDFDocumentElement, objectManager: PDFObjectManager, ctx: LayoutContext): Promise<number>;
|
|
6
|
+
/**
|
|
7
|
+
* Renders one logical page, paginating it into one or more physical pages.
|
|
8
|
+
*
|
|
9
|
+
* Slice 0 only auto-paginates the common shape - a page whose single child is a
|
|
10
|
+
* fragmentation context (a Container). Anything else renders as a single page on the
|
|
11
|
+
* unchanged path, so non-overflowing documents stay byte-identical to pre-Slice-0.
|
|
12
|
+
*/
|
|
13
|
+
private static renderLogicalPage;
|
|
14
|
+
/**
|
|
15
|
+
* Lays out one fragment on a fresh page of the same geometry and renders it. The
|
|
16
|
+
* header/footer are attached to every physical page so they repeat; `PageElement`
|
|
17
|
+
* re-places them and the body in the band between.
|
|
18
|
+
*/
|
|
19
|
+
private static renderPhysicalPage;
|
|
20
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
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.PDFDocumentRenderer = void 0;
|
|
13
|
+
const page_element_1 = require("../elements/page-element");
|
|
14
|
+
const box_constraints_1 = require("../layout/box-constraints");
|
|
15
|
+
const fragmentation_1 = require("../layout/fragmentation");
|
|
16
|
+
const page_renderer_1 = require("./page-renderer");
|
|
17
|
+
class PDFDocumentRenderer {
|
|
18
|
+
static render(document, objectManager, ctx) {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
const pageNumbers = [];
|
|
21
|
+
// Add the pages object first... we need its object number (resources). The count is
|
|
22
|
+
// a placeholder; it is replaced below with the real (post-pagination) page count.
|
|
23
|
+
const pagesObject = `<< /Type /Pages /Kids [] /Count ${document.getProps().children.length} >>`;
|
|
24
|
+
const pagesObjectNumber = objectManager.addObject(pagesObject);
|
|
25
|
+
// Now set the given object number all its childs
|
|
26
|
+
objectManager.setParentObjectNumber(pagesObjectNumber);
|
|
27
|
+
// The page driver: each logical PageElement may produce SEVERAL physical PDF pages
|
|
28
|
+
// when its content overflows (Slice 0: whole children reflow to the next page).
|
|
29
|
+
for (let page of document.getProps().children) {
|
|
30
|
+
const numbers = yield PDFDocumentRenderer.renderLogicalPage(page, objectManager, ctx);
|
|
31
|
+
pageNumbers.push(...numbers);
|
|
32
|
+
}
|
|
33
|
+
// We must update the pages object with the real physical page numbers and count.
|
|
34
|
+
const updatedPagesObject = `<< /Type /Pages /Kids [${pageNumbers
|
|
35
|
+
.map((num) => `${num} 0 R`)
|
|
36
|
+
.join(" ")}] /Count ${pageNumbers.length} >>`;
|
|
37
|
+
// Now we must replace it in the object manager
|
|
38
|
+
objectManager.replaceObject(pagesObjectNumber, updatedPagesObject);
|
|
39
|
+
return pagesObjectNumber;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Renders one logical page, paginating it into one or more physical pages.
|
|
44
|
+
*
|
|
45
|
+
* Slice 0 only auto-paginates the common shape - a page whose single child is a
|
|
46
|
+
* fragmentation context (a Container). Anything else renders as a single page on the
|
|
47
|
+
* unchanged path, so non-overflowing documents stay byte-identical to pre-Slice-0.
|
|
48
|
+
*/
|
|
49
|
+
static renderLogicalPage(page, objectManager, ctx) {
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
const { children, config, header, footer } = page.getProps();
|
|
52
|
+
if (children.length !== 1 || !(0, fragmentation_1.isFragmentable)(children[0])) {
|
|
53
|
+
return [yield page_renderer_1.PageRenderer.render(page, objectManager)];
|
|
54
|
+
}
|
|
55
|
+
// Header/footer repeat on every physical page, so the body only ever flows into the
|
|
56
|
+
// band between them. Resolve that band once (config is already merged by pass 1).
|
|
57
|
+
const pageCtx = { metrics: ctx.metrics, pageConfig: config };
|
|
58
|
+
const { bodyWidth: width, bodyHeight: height } = (0, page_element_1.layoutPageBands)(config, header, footer, pageCtx);
|
|
59
|
+
const numbers = [];
|
|
60
|
+
let region = children[0];
|
|
61
|
+
let isFirstRegion = true;
|
|
62
|
+
while (region) {
|
|
63
|
+
if (!(0, fragmentation_1.isFragmentable)(region)) {
|
|
64
|
+
// A non-fragmentable remainder is placed whole on its own page.
|
|
65
|
+
numbers.push(yield PDFDocumentRenderer.renderPhysicalPage(config, region, header, footer, objectManager, ctx));
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
const { fitted, remainder } = region.fragment(height, width, ctx);
|
|
69
|
+
// Everything fits on one page: render the ORIGINAL page so output is unchanged.
|
|
70
|
+
// Measuring inside fragment() laid the children out at the origin to size them, so
|
|
71
|
+
// re-run the page layout first to restore their real positions (deterministic).
|
|
72
|
+
if (isFirstRegion && remainder === null) {
|
|
73
|
+
page.calculateLayout(new box_constraints_1.BoxConstraints(), { x: 0, y: 0 }, ctx);
|
|
74
|
+
numbers.push(yield page_renderer_1.PageRenderer.render(page, objectManager));
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
if (fitted) {
|
|
78
|
+
numbers.push(yield PDFDocumentRenderer.renderPhysicalPage(config, fitted, header, footer, objectManager, ctx));
|
|
79
|
+
}
|
|
80
|
+
region = remainder;
|
|
81
|
+
isFirstRegion = false;
|
|
82
|
+
}
|
|
83
|
+
return numbers;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Lays out one fragment on a fresh page of the same geometry and renders it. The
|
|
88
|
+
* header/footer are attached to every physical page so they repeat; `PageElement`
|
|
89
|
+
* re-places them and the body in the band between.
|
|
90
|
+
*/
|
|
91
|
+
static renderPhysicalPage(config, content, header, footer, objectManager, ctx) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const physicalPage = new page_element_1.PageElement({
|
|
94
|
+
config,
|
|
95
|
+
header,
|
|
96
|
+
footer,
|
|
97
|
+
children: [content],
|
|
98
|
+
});
|
|
99
|
+
physicalPage.calculateLayout(new box_constraints_1.BoxConstraints(), { x: 0, y: 0 }, ctx);
|
|
100
|
+
return page_renderer_1.PageRenderer.render(physicalPage, objectManager);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.PDFDocumentRenderer = PDFDocumentRenderer;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { PDFDocumentElement } from "../elements/pdf-document-element";
|
|
2
|
+
import { PDFObjectManager } from "../utils/pdf-object-manager";
|
|
3
|
+
export declare class PDFRenderer {
|
|
4
|
+
static render(document: PDFDocumentElement, objectManager: PDFObjectManager): Promise<string>;
|
|
5
|
+
}
|