@polotno/pdf-export 0.1.38 → 0.1.39
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 +61 -8
- package/lib/index.d.ts +66 -8
- package/lib/index.js +25 -145
- package/package.json +17 -18
- package/lib/compare-render.d.ts +0 -1
- package/lib/compare-render.js +0 -185
- package/lib/figure.d.ts +0 -10
- package/lib/figure.js +0 -54
- package/lib/filters.d.ts +0 -2
- package/lib/filters.js +0 -163
- package/lib/ghostscript.d.ts +0 -21
- package/lib/ghostscript.js +0 -132
- package/lib/group.d.ts +0 -5
- package/lib/group.js +0 -5
- package/lib/image.d.ts +0 -38
- package/lib/image.js +0 -279
- package/lib/line.d.ts +0 -10
- package/lib/line.js +0 -66
- package/lib/pdf-import/coordinate-transform.d.ts +0 -51
- package/lib/pdf-import/coordinate-transform.js +0 -99
- package/lib/pdf-import/element-builder.d.ts +0 -21
- package/lib/pdf-import/element-builder.js +0 -163
- package/lib/pdf-import/font-mapper.d.ts +0 -17
- package/lib/pdf-import/font-mapper.js +0 -142
- package/lib/pdf-import/index.d.ts +0 -35
- package/lib/pdf-import/index.js +0 -105
- package/lib/pdf-import/parser.d.ts +0 -29
- package/lib/pdf-import/parser.js +0 -285
- package/lib/pdf-import/text-analysis.d.ts +0 -17
- package/lib/pdf-import/text-analysis.js +0 -186
- package/lib/pdf-import/types.d.ts +0 -101
- package/lib/pdf-import/types.js +0 -1
- package/lib/scripts/compare-json.d.ts +0 -1
- package/lib/scripts/compare-json.js +0 -141
- package/lib/spot-colors.d.ts +0 -38
- package/lib/spot-colors.js +0 -141
- package/lib/svg-render.d.ts +0 -9
- package/lib/svg-render.js +0 -63
- package/lib/svg.d.ts +0 -12
- package/lib/svg.js +0 -224
- package/lib/text/fonts.d.ts +0 -16
- package/lib/text/fonts.js +0 -113
- package/lib/text/index.d.ts +0 -8
- package/lib/text/index.js +0 -42
- package/lib/text/layout.d.ts +0 -22
- package/lib/text/layout.js +0 -522
- package/lib/text/parser.d.ts +0 -46
- package/lib/text/parser.js +0 -415
- package/lib/text/render.d.ts +0 -8
- package/lib/text/render.js +0 -237
- package/lib/text/types.d.ts +0 -91
- package/lib/text/types.js +0 -1
- package/lib/text.d.ts +0 -39
- package/lib/text.js +0 -576
- package/lib/utils.d.ts +0 -16
- package/lib/utils.js +0 -124
package/lib/svg.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ImageCache } from './utils.js';
|
|
2
|
-
export declare function urlToBase64(url: string, cache?: ImageCache | null): Promise<string>;
|
|
3
|
-
export declare function urlToString(url: string, cache?: ImageCache | null): Promise<string>;
|
|
4
|
-
export declare function getColors(svgString: string): string[];
|
|
5
|
-
export declare function svgToURL(s: string): string;
|
|
6
|
-
export declare function getSvgSize(url: string): Promise<{
|
|
7
|
-
width: number;
|
|
8
|
-
height: number;
|
|
9
|
-
}>;
|
|
10
|
-
export declare function fixSize(svgString: string): string;
|
|
11
|
-
export declare const sameColors: (color1: any, color2: any) => boolean;
|
|
12
|
-
export declare function replaceColors(svgString: string, replaceMap: Map<string, string>): string;
|
package/lib/svg.js
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { Util } from 'konva/lib/Util.js';
|
|
2
|
-
import { DOMParser, XMLSerializer } from 'xmldom';
|
|
3
|
-
import { fetchWithTimeout } from './utils.js';
|
|
4
|
-
function isInsideDef(element) {
|
|
5
|
-
while (element.parentNode) {
|
|
6
|
-
if (element.nodeName === 'defs') {
|
|
7
|
-
return true;
|
|
8
|
-
}
|
|
9
|
-
element = element.parentNode;
|
|
10
|
-
}
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
function parseStyleAttribute(style) {
|
|
14
|
-
if (!style) {
|
|
15
|
-
return {};
|
|
16
|
-
}
|
|
17
|
-
const styles = style.split(';');
|
|
18
|
-
const result = {};
|
|
19
|
-
styles.forEach((style) => {
|
|
20
|
-
const [key, value] = style.split(':');
|
|
21
|
-
if (key && value) {
|
|
22
|
-
result[key] = value;
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
return result;
|
|
26
|
-
}
|
|
27
|
-
function buildStyleAttribute(styleObject) {
|
|
28
|
-
return Object.keys(styleObject)
|
|
29
|
-
.map((key) => `${key}:${styleObject[key]}`)
|
|
30
|
-
.join(';');
|
|
31
|
-
}
|
|
32
|
-
function getElementColors(e) {
|
|
33
|
-
const style = parseStyleAttribute(e.getAttribute('style'));
|
|
34
|
-
const colors = {
|
|
35
|
-
fill: '',
|
|
36
|
-
stroke: '',
|
|
37
|
-
};
|
|
38
|
-
if (e.getAttribute('fill') && e.getAttribute('fill') !== 'none') {
|
|
39
|
-
colors.fill = e.getAttribute('fill');
|
|
40
|
-
}
|
|
41
|
-
if (!colors.fill && style && style.fill && style.fill !== 'none') {
|
|
42
|
-
colors.fill = style.fill;
|
|
43
|
-
}
|
|
44
|
-
if (e.getAttribute('stroke')) {
|
|
45
|
-
colors.stroke = e.getAttribute('stroke');
|
|
46
|
-
}
|
|
47
|
-
if (!colors.stroke && style && style.stroke) {
|
|
48
|
-
colors.stroke = style.stroke;
|
|
49
|
-
}
|
|
50
|
-
if (!colors.stroke && !colors.fill) {
|
|
51
|
-
colors.fill = 'black';
|
|
52
|
-
}
|
|
53
|
-
return colors;
|
|
54
|
-
}
|
|
55
|
-
const SVG_SHAPES = ['path', 'rect', 'circle'];
|
|
56
|
-
function getAllElementsWithColor(doc) {
|
|
57
|
-
var matchingElements = [];
|
|
58
|
-
var allElements = doc.getElementsByTagName('*');
|
|
59
|
-
for (var i = 0, n = allElements.length; i < n; i++) {
|
|
60
|
-
const element = allElements[i];
|
|
61
|
-
if (isInsideDef(element)) {
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
if (element.getAttribute('fill') !== null) {
|
|
65
|
-
matchingElements.push(element);
|
|
66
|
-
}
|
|
67
|
-
const style = element.getAttribute('style');
|
|
68
|
-
if (style != null && style.indexOf('fill') >= 0) {
|
|
69
|
-
matchingElements.push(element);
|
|
70
|
-
}
|
|
71
|
-
if (element.getAttribute('stroke') !== null) {
|
|
72
|
-
matchingElements.push(element);
|
|
73
|
-
}
|
|
74
|
-
else if (element.style && element.style['fill']) {
|
|
75
|
-
matchingElements.push(element);
|
|
76
|
-
}
|
|
77
|
-
else if (SVG_SHAPES.indexOf(element.nodeName) >= 0) {
|
|
78
|
-
matchingElements.push(element);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return matchingElements;
|
|
82
|
-
}
|
|
83
|
-
export async function urlToBase64(url, cache = null) {
|
|
84
|
-
// Check cache first
|
|
85
|
-
if (cache && cache.buffers.has(url)) {
|
|
86
|
-
return cache.buffers.get(url);
|
|
87
|
-
}
|
|
88
|
-
const req = await fetchWithTimeout(url);
|
|
89
|
-
let result;
|
|
90
|
-
if (req.arrayBuffer) {
|
|
91
|
-
const buffer = Buffer.from(await req.arrayBuffer());
|
|
92
|
-
result = `data:image/svg+xml;base64,${buffer.toString('base64')}`;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
const svgString = await req.text();
|
|
96
|
-
result = svgToURL(svgString);
|
|
97
|
-
}
|
|
98
|
-
// Store in cache
|
|
99
|
-
if (cache) {
|
|
100
|
-
cache.buffers.set(url, result);
|
|
101
|
-
}
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
export async function urlToString(url, cache = null) {
|
|
105
|
-
// Check cache first
|
|
106
|
-
if (cache && cache.buffers.has(url)) {
|
|
107
|
-
const cached = cache.buffers.get(url);
|
|
108
|
-
// If cached value is a base64 data URL, decode it
|
|
109
|
-
if (cached.startsWith('data:')) {
|
|
110
|
-
return Buffer.from(cached.split('base64,')[1], 'base64').toString();
|
|
111
|
-
}
|
|
112
|
-
return cached;
|
|
113
|
-
}
|
|
114
|
-
let svgString;
|
|
115
|
-
if (url.startsWith('data:')) {
|
|
116
|
-
svgString = Buffer.from(url.split('base64,')[1], 'base64').toString();
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
const req = await fetchWithTimeout(url);
|
|
120
|
-
svgString = await req.text();
|
|
121
|
-
}
|
|
122
|
-
// Store in cache
|
|
123
|
-
if (cache) {
|
|
124
|
-
cache.buffers.set(url, svgString);
|
|
125
|
-
}
|
|
126
|
-
return svgString;
|
|
127
|
-
}
|
|
128
|
-
export function getColors(svgString) {
|
|
129
|
-
var parser = new DOMParser();
|
|
130
|
-
var doc = parser.parseFromString(svgString, 'text/xml');
|
|
131
|
-
const elements = getAllElementsWithColor(doc);
|
|
132
|
-
const colors = [];
|
|
133
|
-
elements.forEach((e) => {
|
|
134
|
-
const { fill, stroke } = getElementColors(e);
|
|
135
|
-
const results = [fill, stroke];
|
|
136
|
-
results.forEach((color) => {
|
|
137
|
-
if (!color) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const rgba = Util.colorToRGBA(color);
|
|
141
|
-
if (!rgba) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
if (colors.indexOf(color) === -1) {
|
|
145
|
-
colors.push(color);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
return colors;
|
|
150
|
-
}
|
|
151
|
-
export function svgToURL(s) {
|
|
152
|
-
const uri = Buffer.from(unescape(encodeURIComponent(s))).toString('base64');
|
|
153
|
-
return 'data:image/svg+xml;base64,' + uri;
|
|
154
|
-
}
|
|
155
|
-
export async function getSvgSize(url) {
|
|
156
|
-
const svgString = await urlToString(url);
|
|
157
|
-
var parser = new DOMParser();
|
|
158
|
-
var doc = parser.parseFromString(svgString, 'image/svg+xml');
|
|
159
|
-
const viewBox = doc.documentElement.getAttribute('viewBox');
|
|
160
|
-
const [x, y, width, height] = viewBox?.split(' ') || [];
|
|
161
|
-
return { width: parseFloat(width), height: parseFloat(height) };
|
|
162
|
-
}
|
|
163
|
-
export function fixSize(svgString) {
|
|
164
|
-
var parser = new DOMParser();
|
|
165
|
-
var doc = parser.parseFromString(svgString, 'image/svg+xml');
|
|
166
|
-
const viewBox = doc.documentElement.getAttribute('viewBox');
|
|
167
|
-
const [x, y, width, height] = viewBox?.split(' ') || [];
|
|
168
|
-
if (!doc.documentElement.getAttribute('width')) {
|
|
169
|
-
doc.documentElement.setAttribute('width', width + 'px');
|
|
170
|
-
}
|
|
171
|
-
if (!doc.documentElement.getAttribute('height')) {
|
|
172
|
-
doc.documentElement.setAttribute('height', height + 'px');
|
|
173
|
-
}
|
|
174
|
-
var xmlSerializer = new XMLSerializer();
|
|
175
|
-
const str = xmlSerializer.serializeToString(doc);
|
|
176
|
-
return str;
|
|
177
|
-
}
|
|
178
|
-
export const sameColors = (color1, color2) => {
|
|
179
|
-
if (!color1 || !color2) {
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
if (color2 === 'currentColor' && color1 === 'black') {
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
const c1 = Util.colorToRGBA(color1);
|
|
186
|
-
const c2 = Util.colorToRGBA(color2);
|
|
187
|
-
if (!c1 || !c2) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
return c1.r === c2.r && c1.g === c2.g && c1.b === c2.b && c1.a === c2.a;
|
|
191
|
-
};
|
|
192
|
-
export function replaceColors(svgString, replaceMap) {
|
|
193
|
-
var parser = new DOMParser();
|
|
194
|
-
var doc = parser.parseFromString(svgString, 'text/xml');
|
|
195
|
-
const elements = getAllElementsWithColor(doc);
|
|
196
|
-
const oldColors = Array.from(replaceMap.keys());
|
|
197
|
-
elements.forEach((el) => {
|
|
198
|
-
const { fill, stroke } = getElementColors(el);
|
|
199
|
-
const colors = [
|
|
200
|
-
{ prop: 'fill', color: fill },
|
|
201
|
-
{ prop: 'stroke', color: stroke },
|
|
202
|
-
];
|
|
203
|
-
colors.forEach(({ prop, color }) => {
|
|
204
|
-
// find matched oldColor
|
|
205
|
-
const marchedOldValue = oldColors.find((oldColor) => {
|
|
206
|
-
return sameColors(oldColor, color);
|
|
207
|
-
});
|
|
208
|
-
if (!marchedOldValue) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
el.setAttribute(prop, replaceMap.get(marchedOldValue));
|
|
213
|
-
const style = parseStyleAttribute(el.getAttribute('style'));
|
|
214
|
-
if (style && style[prop]) {
|
|
215
|
-
style[prop] = replaceMap.get(marchedOldValue);
|
|
216
|
-
el.setAttribute('style', buildStyleAttribute(style));
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
var xmlSerializer = new XMLSerializer();
|
|
222
|
-
const str = xmlSerializer.serializeToString(doc);
|
|
223
|
-
return svgToURL(str);
|
|
224
|
-
}
|
package/lib/text/fonts.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { TextElement, TextSegment } from './types.js';
|
|
2
|
-
export declare function registerFontUrl(fontFamily: string, url: string): void;
|
|
3
|
-
/**
|
|
4
|
-
* Get font weight string based on bold/italic state
|
|
5
|
-
*/
|
|
6
|
-
export declare function getFontWeight(bold: boolean, italic: boolean, baseFontWeight?: string): string;
|
|
7
|
-
/**
|
|
8
|
-
* Get font key for caching
|
|
9
|
-
*/
|
|
10
|
-
export declare function getFontKey(fontFamily: string, bold: boolean, italic: boolean, baseFontWeight?: string): string;
|
|
11
|
-
export declare function getGoogleFontPath(fontFamily: string, fontWeight?: string, italic?: boolean): Promise<string>;
|
|
12
|
-
/**
|
|
13
|
-
* Load font for a text element or segment
|
|
14
|
-
*/
|
|
15
|
-
export declare function loadFontForSegment(doc: any, segment: TextSegment | null, element: TextElement, fonts: Record<string, boolean>): Promise<string>;
|
|
16
|
-
export declare function loadFontIfNeeded(doc: any, element: TextElement, fonts: Record<string, boolean>): Promise<string>;
|
package/lib/text/fonts.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { srcToBuffer } from '../utils.js';
|
|
2
|
-
import getUrls from 'get-urls';
|
|
3
|
-
import fetch from 'node-fetch';
|
|
4
|
-
const fontUrlRegistry = {};
|
|
5
|
-
const patchedPrototypes = new WeakSet();
|
|
6
|
-
/**
|
|
7
|
-
* Patch fontkit's TTFGlyph._getCBox to handle zero-length glyphs.
|
|
8
|
-
* Some fonts (e.g. "Cute Font") have glyphs with equal start/end offsets
|
|
9
|
-
* in the loca table (no outline data), but fontkit still tries to read
|
|
10
|
-
* from the glyf table at that offset, causing a DataView out-of-bounds error.
|
|
11
|
-
*/
|
|
12
|
-
function patchFontkitGlyphs(doc) {
|
|
13
|
-
const fkFont = doc._font?.font;
|
|
14
|
-
if (!fkFont || typeof fkFont.getGlyph !== 'function')
|
|
15
|
-
return;
|
|
16
|
-
const glyph = fkFont.getGlyph(0);
|
|
17
|
-
const proto = Object.getPrototypeOf(glyph);
|
|
18
|
-
if (!proto._getCBox || patchedPrototypes.has(proto))
|
|
19
|
-
return;
|
|
20
|
-
const origGetCBox = proto._getCBox;
|
|
21
|
-
proto._getCBox = function (internal) {
|
|
22
|
-
const loca = this._font.loca;
|
|
23
|
-
if (loca) {
|
|
24
|
-
const start = loca.offsets[this.id];
|
|
25
|
-
const end = loca.offsets[this.id + 1];
|
|
26
|
-
if (start === end) {
|
|
27
|
-
return Object.freeze({ minX: 0, minY: 0, maxX: 0, maxY: 0 });
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return origGetCBox.call(this, internal);
|
|
31
|
-
};
|
|
32
|
-
patchedPrototypes.add(proto);
|
|
33
|
-
}
|
|
34
|
-
export function registerFontUrl(fontFamily, url) {
|
|
35
|
-
fontUrlRegistry[fontFamily] = url;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Get font weight string based on bold/italic state
|
|
39
|
-
*/
|
|
40
|
-
export function getFontWeight(bold, italic, baseFontWeight) {
|
|
41
|
-
if (bold) {
|
|
42
|
-
return 'bold';
|
|
43
|
-
}
|
|
44
|
-
return baseFontWeight || 'normal';
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Get font key for caching
|
|
48
|
-
*/
|
|
49
|
-
export function getFontKey(fontFamily, bold, italic, baseFontWeight) {
|
|
50
|
-
const weight = getFontWeight(bold, italic, baseFontWeight);
|
|
51
|
-
const style = italic ? 'italic' : 'normal';
|
|
52
|
-
return `${fontFamily}-${weight}-${style}`;
|
|
53
|
-
}
|
|
54
|
-
export async function getGoogleFontPath(fontFamily, fontWeight = 'normal', italic = false) {
|
|
55
|
-
const weight = fontWeight === 'bold' ? '700' : '400';
|
|
56
|
-
const italicParam = italic ? 'italic' : '';
|
|
57
|
-
const url = `https://fonts.googleapis.com/css?family=${fontFamily}:${italicParam}${weight}`;
|
|
58
|
-
const req = await fetch(url);
|
|
59
|
-
if (!req.ok) {
|
|
60
|
-
if (weight !== '400' || italic) {
|
|
61
|
-
// Fallback: try normal weight without italic
|
|
62
|
-
return getGoogleFontPath(fontFamily, 'normal', false);
|
|
63
|
-
}
|
|
64
|
-
throw new Error(`Failed to fetch Google font: ${fontFamily}`);
|
|
65
|
-
}
|
|
66
|
-
const text = await req.text();
|
|
67
|
-
const urls = getUrls(text);
|
|
68
|
-
return urls.values().next().value;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Load font for a text element or segment
|
|
72
|
-
*/
|
|
73
|
-
export async function loadFontForSegment(doc, segment, element, fonts) {
|
|
74
|
-
const fontFamily = element.fontFamily;
|
|
75
|
-
// Determine bold/italic from segment or element
|
|
76
|
-
const bold = segment
|
|
77
|
-
? segment.bold || element.fontWeight == 'bold' || false
|
|
78
|
-
: element.fontWeight == 'bold';
|
|
79
|
-
const italic = segment
|
|
80
|
-
? segment.italic || element.fontStyle?.indexOf('italic') >= 0 || false
|
|
81
|
-
: element.fontStyle?.indexOf('italic') >= 0 || false;
|
|
82
|
-
// Check if universal font is already defined
|
|
83
|
-
if (fonts[fontFamily]) {
|
|
84
|
-
doc.font(fontFamily);
|
|
85
|
-
patchFontkitGlyphs(doc);
|
|
86
|
-
return fontFamily;
|
|
87
|
-
}
|
|
88
|
-
const fontKey = getFontKey(fontFamily, bold, italic, element.fontWeight);
|
|
89
|
-
if (!fonts[fontKey]) {
|
|
90
|
-
let src;
|
|
91
|
-
if (fontUrlRegistry[fontFamily]) {
|
|
92
|
-
src = fontUrlRegistry[fontFamily];
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
const weight = getFontWeight(bold, italic, element.fontWeight);
|
|
96
|
-
src = await getGoogleFontPath(fontFamily, weight, italic);
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
doc.registerFont(fontKey, await srcToBuffer(src));
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
throw new Error(`Failed to load font "${fontFamily}" from ${src}: ${error.message}`);
|
|
103
|
-
}
|
|
104
|
-
fonts[fontKey] = true;
|
|
105
|
-
}
|
|
106
|
-
doc.font(fontKey);
|
|
107
|
-
patchFontkitGlyphs(doc);
|
|
108
|
-
return fontKey;
|
|
109
|
-
}
|
|
110
|
-
// Alias for backward compatibility
|
|
111
|
-
export async function loadFontIfNeeded(doc, element, fonts) {
|
|
112
|
-
return loadFontForSegment(doc, null, element, fonts);
|
|
113
|
-
}
|
package/lib/text/index.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { TextElement, RenderAttrs } from './types.js';
|
|
2
|
-
export * from './types.js';
|
|
3
|
-
export { loadFontIfNeeded } from './fonts.js';
|
|
4
|
-
export { normalizeRichText, parseHTMLToSegments } from './parser.js';
|
|
5
|
-
/**
|
|
6
|
-
* Main text rendering function
|
|
7
|
-
*/
|
|
8
|
-
export declare function renderText(doc: PDFKit.PDFDocument, element: TextElement, fonts: Record<string, boolean>, attrs?: RenderAttrs): Promise<void>;
|
package/lib/text/index.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { normalizeRichText } from './parser.js';
|
|
2
|
-
import { calculateTextMetrics, calculateVerticalAlignment, fitTextToHeight, } from './layout.js';
|
|
3
|
-
import { renderTextBackground, renderPDFX1aStroke, renderStandardStroke, renderTextFill, } from './render.js';
|
|
4
|
-
export * from './types.js';
|
|
5
|
-
export { loadFontIfNeeded } from './fonts.js';
|
|
6
|
-
export { normalizeRichText, parseHTMLToSegments } from './parser.js';
|
|
7
|
-
/**
|
|
8
|
-
* Main text rendering function
|
|
9
|
-
*/
|
|
10
|
-
export async function renderText(doc, element, fonts, attrs = {}) {
|
|
11
|
-
let elementToRender = element;
|
|
12
|
-
if (typeof element.text === 'string') {
|
|
13
|
-
const normalizedText = normalizeRichText(element.text);
|
|
14
|
-
if (normalizedText !== element.text) {
|
|
15
|
-
elementToRender = { ...element, text: normalizedText };
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
doc.fontSize(elementToRender.fontSize);
|
|
19
|
-
const hasStroke = elementToRender.strokeWidth > 0;
|
|
20
|
-
const isPDFX1a = attrs.pdfx1a;
|
|
21
|
-
// Calculate text metrics and line positioning
|
|
22
|
-
const metrics = calculateTextMetrics(doc, elementToRender);
|
|
23
|
-
const verticalAlignmentOffset = calculateVerticalAlignment(doc, elementToRender, metrics.textOptions);
|
|
24
|
-
// Fit text to element height if needed
|
|
25
|
-
fitTextToHeight(doc, elementToRender, metrics.textOptions);
|
|
26
|
-
// Calculate final vertical offset
|
|
27
|
-
const finalYOffset = verticalAlignmentOffset + metrics.baselineOffset;
|
|
28
|
-
// Render background if enabled
|
|
29
|
-
renderTextBackground(doc, elementToRender, verticalAlignmentOffset, metrics.textOptions);
|
|
30
|
-
// Render text based on stroke and PDF/X-1a requirements
|
|
31
|
-
if (hasStroke && isPDFX1a) {
|
|
32
|
-
// PDF/X-1a mode: simulate stroke with offset fills
|
|
33
|
-
await renderPDFX1aStroke(doc, elementToRender, metrics.textLines, finalYOffset, metrics.lineHeightPx, metrics.textOptions, fonts);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
// Standard rendering: stroke first, then fill
|
|
37
|
-
if (hasStroke) {
|
|
38
|
-
await renderStandardStroke(doc, elementToRender, metrics.textLines, finalYOffset, metrics.lineHeightPx, metrics.textOptions, fonts);
|
|
39
|
-
}
|
|
40
|
-
await renderTextFill(doc, elementToRender, metrics.textLines, finalYOffset, metrics.lineHeightPx, metrics.textOptions, fonts);
|
|
41
|
-
}
|
|
42
|
-
}
|
package/lib/text/layout.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { TextElement, TextLine, TextMetrics, RenderSegment, TabExpansionSegment } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Expand tabs in text with word spacing adjustment for accurate visual alignment.
|
|
4
|
-
*/
|
|
5
|
-
export declare function expandTabsWithWordSpacing(text: string, doc: PDFKit.PDFDocument, textOptions: PDFKit.Mixins.TextOptions, tabSizeInSpaces?: number, currentWidth?: number): {
|
|
6
|
-
segments: TabExpansionSegment[];
|
|
7
|
-
finalWidth: number;
|
|
8
|
-
};
|
|
9
|
-
export declare function buildRenderSegmentsForLine(doc: PDFKit.PDFDocument, element: TextElement, lineText: string, textOptions: PDFKit.Mixins.TextOptions, fonts: Record<string, boolean>): Promise<RenderSegment[]>;
|
|
10
|
-
export declare function splitTextIntoLines(doc: PDFKit.PDFDocument, element: TextElement, props: PDFKit.Mixins.TextOptions): TextLine[];
|
|
11
|
-
export declare function calculateTextMetrics(doc: PDFKit.PDFDocument, element: TextElement): TextMetrics;
|
|
12
|
-
export declare function calculateVerticalAlignment(doc: PDFKit.PDFDocument, element: TextElement, textOptions: PDFKit.Mixins.TextOptions): number;
|
|
13
|
-
export declare function fitTextToHeight(doc: PDFKit.PDFDocument, element: TextElement, textOptions: PDFKit.Mixins.TextOptions): void;
|
|
14
|
-
/**
|
|
15
|
-
* Calculate X offset for list markers (not used for text content positioning)
|
|
16
|
-
*/
|
|
17
|
-
export declare function calculateLineXOffset(element: TextElement, line: TextLine): number;
|
|
18
|
-
export declare function calculateTextContentXOffset(element: TextElement, line: TextLine): number;
|
|
19
|
-
/**
|
|
20
|
-
* Calculate effective width for text rendering, considering justify and underline constraints
|
|
21
|
-
*/
|
|
22
|
-
export declare function calculateEffectiveWidth(element: TextElement, line: TextLine, widthOption: number | undefined, hasUnderline: boolean): number | undefined;
|