pdfdancer-client-typescript 1.0.12 → 1.0.13
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/.github/workflows/ci.yml +1 -1
- package/README.md +1 -1
- package/dist/__tests__/e2e/pdf-assertions.d.ts +1 -0
- package/dist/__tests__/e2e/pdf-assertions.d.ts.map +1 -1
- package/dist/__tests__/e2e/pdf-assertions.js +9 -3
- package/dist/__tests__/e2e/pdf-assertions.js.map +1 -1
- package/dist/fingerprint.d.ts +12 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +196 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/image-builder.d.ts +4 -2
- package/dist/image-builder.d.ts.map +1 -1
- package/dist/image-builder.js +12 -3
- package/dist/image-builder.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +75 -8
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +179 -21
- package/dist/models.js.map +1 -1
- package/dist/page-builder.d.ts +24 -0
- package/dist/page-builder.d.ts.map +1 -0
- package/dist/page-builder.js +107 -0
- package/dist/page-builder.js.map +1 -0
- package/dist/paragraph-builder.d.ts +48 -54
- package/dist/paragraph-builder.d.ts.map +1 -1
- package/dist/paragraph-builder.js +408 -135
- package/dist/paragraph-builder.js.map +1 -1
- package/dist/pdfdancer_v1.d.ts +90 -9
- package/dist/pdfdancer_v1.d.ts.map +1 -1
- package/dist/pdfdancer_v1.js +535 -50
- package/dist/pdfdancer_v1.js.map +1 -1
- package/dist/types.d.ts +24 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +117 -2
- package/dist/types.js.map +1 -1
- package/docs/openapi.yml +2076 -0
- package/fixtures/Showcase.pdf +0 -0
- package/package.json +1 -1
- package/src/__tests__/e2e/acroform.test.ts +5 -5
- package/src/__tests__/e2e/context-manager-showcase.test.ts +267 -0
- package/src/__tests__/e2e/form_x_object.test.ts +1 -1
- package/src/__tests__/e2e/image-showcase.test.ts +133 -0
- package/src/__tests__/e2e/image.test.ts +1 -1
- package/src/__tests__/e2e/line-showcase.test.ts +118 -0
- package/src/__tests__/e2e/line.test.ts +1 -16
- package/src/__tests__/e2e/page-showcase.test.ts +154 -0
- package/src/__tests__/e2e/paragraph-showcase.test.ts +523 -0
- package/src/__tests__/e2e/paragraph.test.ts +8 -8
- package/src/__tests__/e2e/pdf-assertions.ts +10 -3
- package/src/__tests__/e2e/pdfdancer-showcase.test.ts +40 -0
- package/src/__tests__/e2e/snapshot-showcase.test.ts +158 -0
- package/src/__tests__/e2e/snapshot.test.ts +296 -0
- package/src/__tests__/fingerprint.test.ts +36 -0
- package/src/fingerprint.ts +169 -0
- package/src/image-builder.ts +13 -6
- package/src/index.ts +6 -1
- package/src/models.ts +208 -24
- package/src/page-builder.ts +130 -0
- package/src/paragraph-builder.ts +517 -159
- package/src/pdfdancer_v1.ts +630 -51
- package/src/types.ts +145 -2
- package/update-api-spec.sh +3 -0
|
@@ -6,36 +6,111 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ParagraphBuilder = void 0;
|
|
7
7
|
const exceptions_1 = require("./exceptions");
|
|
8
8
|
const models_1 = require("./models");
|
|
9
|
+
const DEFAULT_LINE_SPACING_FACTOR = 1.2;
|
|
10
|
+
const DEFAULT_BASE_FONT_SIZE = 12;
|
|
11
|
+
const cloneColor = (color) => {
|
|
12
|
+
if (!color) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return new models_1.Color(color.r, color.g, color.b, color.a);
|
|
16
|
+
};
|
|
17
|
+
const clonePosition = (position) => {
|
|
18
|
+
return position ? position.copy() : undefined;
|
|
19
|
+
};
|
|
20
|
+
const defaultTextColor = () => new models_1.Color(0, 0, 0);
|
|
9
21
|
/**
|
|
10
22
|
* Builder class for constructing Paragraph objects with fluent interface.
|
|
23
|
+
* Aligns with the Python client's ParagraphBuilder behaviour.
|
|
11
24
|
*/
|
|
12
25
|
class ParagraphBuilder {
|
|
13
26
|
constructor(_client, objectRefOrPageIndex) {
|
|
14
27
|
this._client = _client;
|
|
15
|
-
this.
|
|
28
|
+
this._fontExplicitlyChanged = false;
|
|
29
|
+
this._positionChanged = false;
|
|
16
30
|
this._pending = [];
|
|
17
31
|
this._registeringFont = false;
|
|
18
32
|
if (!_client) {
|
|
19
33
|
throw new exceptions_1.ValidationException("Client cannot be null");
|
|
20
34
|
}
|
|
21
|
-
this._pageIndex = objectRefOrPageIndex instanceof models_1.ObjectRef ? objectRefOrPageIndex.position.pageIndex : objectRefOrPageIndex;
|
|
22
35
|
this._paragraph = new models_1.Paragraph();
|
|
23
|
-
// Cast to the internal interface to get access
|
|
24
36
|
this._internals = this._client;
|
|
37
|
+
if (objectRefOrPageIndex instanceof models_1.TextObjectRef) {
|
|
38
|
+
this.target(objectRefOrPageIndex);
|
|
39
|
+
}
|
|
40
|
+
else if (typeof objectRefOrPageIndex === 'number') {
|
|
41
|
+
this._pageIndex = objectRefOrPageIndex;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
static fromObjectRef(client, objectRef) {
|
|
45
|
+
if (!objectRef) {
|
|
46
|
+
throw new exceptions_1.ValidationException("Object reference cannot be null");
|
|
47
|
+
}
|
|
48
|
+
const builder = new ParagraphBuilder(client, objectRef);
|
|
49
|
+
builder.target(objectRef);
|
|
50
|
+
builder.setOriginalParagraphPosition(objectRef.position);
|
|
51
|
+
if (objectRef.lineSpacings) {
|
|
52
|
+
builder._paragraph.setLineSpacings(objectRef.lineSpacings);
|
|
53
|
+
const [firstSpacing] = objectRef.lineSpacings;
|
|
54
|
+
if (firstSpacing !== undefined) {
|
|
55
|
+
builder._paragraph.lineSpacing = firstSpacing;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (objectRef.fontName && objectRef.fontSize) {
|
|
59
|
+
builder._originalFont = new models_1.Font(objectRef.fontName, objectRef.fontSize);
|
|
60
|
+
}
|
|
61
|
+
if (objectRef.color) {
|
|
62
|
+
builder._originalColor = cloneColor(objectRef.color);
|
|
63
|
+
}
|
|
64
|
+
if (objectRef.children && objectRef.children.length > 0) {
|
|
65
|
+
objectRef.children.forEach(child => builder.addTextLine(child));
|
|
66
|
+
}
|
|
67
|
+
else if (objectRef.text) {
|
|
68
|
+
builder._splitText(objectRef.text).forEach(segment => builder.addTextLine(segment));
|
|
69
|
+
}
|
|
70
|
+
return builder;
|
|
71
|
+
}
|
|
72
|
+
setFontExplicitlyChanged(changed) {
|
|
73
|
+
this._fontExplicitlyChanged = !!changed;
|
|
74
|
+
}
|
|
75
|
+
setOriginalParagraphPosition(position) {
|
|
76
|
+
this._originalParagraphPosition = clonePosition(position);
|
|
77
|
+
if (position && !this._paragraph.getPosition()) {
|
|
78
|
+
this._paragraph.setPosition(clonePosition(position));
|
|
79
|
+
}
|
|
80
|
+
if (position?.pageIndex !== undefined) {
|
|
81
|
+
this._pageIndex = position.pageIndex;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
target(objectRef) {
|
|
85
|
+
if (!objectRef) {
|
|
86
|
+
throw new exceptions_1.ValidationException("Object reference cannot be null");
|
|
87
|
+
}
|
|
88
|
+
this._targetObjectRef = objectRef;
|
|
89
|
+
if (objectRef.position) {
|
|
90
|
+
this.setOriginalParagraphPosition(objectRef.position);
|
|
91
|
+
}
|
|
92
|
+
if (objectRef.position?.pageIndex !== undefined) {
|
|
93
|
+
this._pageIndex = objectRef.position.pageIndex;
|
|
94
|
+
}
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
onlyTextChanged() {
|
|
98
|
+
return (this._text !== undefined &&
|
|
99
|
+
this._textColor === undefined &&
|
|
100
|
+
this._ttfSource === undefined &&
|
|
101
|
+
(this._font === undefined || !this._fontExplicitlyChanged) &&
|
|
102
|
+
this._lineSpacingFactor === undefined);
|
|
25
103
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Set the text content for the paragraph.
|
|
28
|
-
*/
|
|
29
104
|
replace(text, color) {
|
|
105
|
+
return this.text(text, color);
|
|
106
|
+
}
|
|
107
|
+
text(text, color) {
|
|
30
108
|
if (text === null || text === undefined) {
|
|
31
109
|
throw new exceptions_1.ValidationException("Text cannot be null");
|
|
32
110
|
}
|
|
33
|
-
if (!text.trim()) {
|
|
34
|
-
throw new exceptions_1.ValidationException("Text cannot be empty");
|
|
35
|
-
}
|
|
36
111
|
this._text = text;
|
|
37
112
|
if (color) {
|
|
38
|
-
this.
|
|
113
|
+
this.color(color);
|
|
39
114
|
}
|
|
40
115
|
return this;
|
|
41
116
|
}
|
|
@@ -52,37 +127,36 @@ class ParagraphBuilder {
|
|
|
52
127
|
}
|
|
53
128
|
this._font = new models_1.Font(fontOrName, fontSize);
|
|
54
129
|
}
|
|
130
|
+
this._fontExplicitlyChanged = true;
|
|
55
131
|
return this;
|
|
56
132
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Set the font for the paragraph using a TTF file.
|
|
59
|
-
*/
|
|
60
133
|
fontFile(ttfFile, fontSize) {
|
|
61
|
-
if (!ttfFile)
|
|
134
|
+
if (!ttfFile) {
|
|
62
135
|
throw new exceptions_1.ValidationException("TTF file cannot be null");
|
|
136
|
+
}
|
|
63
137
|
if (fontSize <= 0) {
|
|
64
138
|
throw new exceptions_1.ValidationException(`Font size must be positive, got ${fontSize}`);
|
|
65
139
|
}
|
|
140
|
+
this._ttfSource = ttfFile;
|
|
66
141
|
this._registeringFont = true;
|
|
67
|
-
const job = this._registerTtf(ttfFile, fontSize)
|
|
142
|
+
const job = this._registerTtf(ttfFile, fontSize)
|
|
143
|
+
.then(font => {
|
|
68
144
|
this._font = font;
|
|
145
|
+
this._fontExplicitlyChanged = true;
|
|
146
|
+
})
|
|
147
|
+
.finally(() => {
|
|
148
|
+
this._registeringFont = false;
|
|
69
149
|
});
|
|
70
150
|
this._pending.push(job);
|
|
71
151
|
return this;
|
|
72
152
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Set the line spacing for the paragraph.
|
|
75
|
-
*/
|
|
76
153
|
lineSpacing(spacing) {
|
|
77
154
|
if (spacing <= 0) {
|
|
78
155
|
throw new exceptions_1.ValidationException(`Line spacing must be positive, got ${spacing}`);
|
|
79
156
|
}
|
|
80
|
-
this.
|
|
157
|
+
this._lineSpacingFactor = spacing;
|
|
81
158
|
return this;
|
|
82
159
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Set the text color for the paragraph.
|
|
85
|
-
*/
|
|
86
160
|
color(color) {
|
|
87
161
|
if (!color) {
|
|
88
162
|
throw new exceptions_1.ValidationException("Color cannot be null");
|
|
@@ -90,154 +164,353 @@ class ParagraphBuilder {
|
|
|
90
164
|
this._textColor = color;
|
|
91
165
|
return this;
|
|
92
166
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Set the position for the paragraph.
|
|
95
|
-
*/
|
|
96
167
|
moveTo(x, y) {
|
|
97
168
|
if (x === null || x === undefined || y === null || y === undefined) {
|
|
98
169
|
throw new exceptions_1.ValidationException("Coordinates cannot be null or undefined");
|
|
99
170
|
}
|
|
100
|
-
|
|
101
|
-
|
|
171
|
+
let position = this._paragraph.getPosition();
|
|
172
|
+
if (!position && this._targetObjectRef?.position) {
|
|
173
|
+
position = clonePosition(this._targetObjectRef.position);
|
|
174
|
+
}
|
|
175
|
+
const pageIndex = position?.pageIndex ?? this._pageIndex;
|
|
176
|
+
if (pageIndex === undefined) {
|
|
177
|
+
throw new exceptions_1.ValidationException("Paragraph position must include a page index to move");
|
|
178
|
+
}
|
|
179
|
+
this._paragraph.setPosition(models_1.Position.atPageCoordinates(pageIndex, x, y));
|
|
180
|
+
this._positionChanged = true;
|
|
102
181
|
return this;
|
|
103
182
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
183
|
+
atPosition(position) {
|
|
184
|
+
if (!position) {
|
|
185
|
+
throw new exceptions_1.ValidationException("Position cannot be null");
|
|
186
|
+
}
|
|
187
|
+
this._paragraph.setPosition(clonePosition(position));
|
|
188
|
+
this._positionChanged = true;
|
|
189
|
+
if (position.pageIndex !== undefined) {
|
|
190
|
+
this._pageIndex = position.pageIndex;
|
|
191
|
+
}
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
at(pageIndexOrX, xOrY, maybeY) {
|
|
195
|
+
if (maybeY === undefined) {
|
|
196
|
+
const pageIndex = this._pageIndex ?? this._paragraph.getPosition()?.pageIndex;
|
|
197
|
+
if (pageIndex === undefined) {
|
|
198
|
+
throw new exceptions_1.ValidationException("Page index must be provided before calling at(x, y)");
|
|
199
|
+
}
|
|
200
|
+
return this._setPosition(pageIndex, pageIndexOrX, xOrY);
|
|
201
|
+
}
|
|
202
|
+
return this._setPosition(pageIndexOrX, xOrY, maybeY);
|
|
203
|
+
}
|
|
204
|
+
_setPosition(pageIndex, x, y) {
|
|
205
|
+
this._pageIndex = pageIndex;
|
|
206
|
+
return this.atPosition(models_1.Position.atPageCoordinates(pageIndex, x, y));
|
|
207
|
+
}
|
|
208
|
+
addTextLine(textLine) {
|
|
209
|
+
this._paragraph.addLine(this._coerceTextLine(textLine));
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
getText() {
|
|
213
|
+
return this._text;
|
|
119
214
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (this.
|
|
123
|
-
|
|
215
|
+
async add() {
|
|
216
|
+
await this._prepareAsync();
|
|
217
|
+
if (this._targetObjectRef) {
|
|
218
|
+
throw new exceptions_1.ValidationException("Target object reference provided; use modify() for updates");
|
|
124
219
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
220
|
+
const paragraph = this._finalizeParagraph();
|
|
221
|
+
return this._internals.addParagraph(paragraph);
|
|
222
|
+
}
|
|
223
|
+
async modify(objectRef) {
|
|
224
|
+
await this._prepareAsync();
|
|
225
|
+
const target = objectRef ?? this._targetObjectRef;
|
|
226
|
+
if (!target) {
|
|
227
|
+
throw new exceptions_1.ValidationException("Object reference must be provided to modify a paragraph");
|
|
129
228
|
}
|
|
130
|
-
|
|
131
|
-
|
|
229
|
+
if (this.onlyTextChanged()) {
|
|
230
|
+
const result = await this._internals.modifyParagraph(target, this._text ?? '');
|
|
231
|
+
return this._withWarning(result);
|
|
132
232
|
}
|
|
233
|
+
const paragraph = this._finalizeParagraph();
|
|
234
|
+
const result = await this._internals.modifyParagraph(target, paragraph);
|
|
235
|
+
return this._withWarning(result);
|
|
133
236
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
237
|
+
async apply() {
|
|
238
|
+
await this._prepareAsync();
|
|
239
|
+
if (this._targetObjectRef) {
|
|
240
|
+
return this.modify(this._targetObjectRef);
|
|
241
|
+
}
|
|
242
|
+
return this.add();
|
|
243
|
+
}
|
|
244
|
+
async _prepareAsync() {
|
|
245
|
+
if (this._pending.length) {
|
|
246
|
+
await Promise.all(this._pending);
|
|
247
|
+
this._pending = [];
|
|
137
248
|
}
|
|
138
|
-
|
|
139
|
-
|
|
249
|
+
if (this._registeringFont) {
|
|
250
|
+
throw new exceptions_1.ValidationException("Font registration is not complete");
|
|
140
251
|
}
|
|
141
|
-
|
|
142
|
-
|
|
252
|
+
}
|
|
253
|
+
_withWarning(result) {
|
|
254
|
+
if (result && result.warning) {
|
|
255
|
+
process.stderr.write(`WARNING: ${result.warning}\n`);
|
|
143
256
|
}
|
|
257
|
+
return result;
|
|
144
258
|
}
|
|
145
|
-
|
|
259
|
+
_finalizeParagraph() {
|
|
260
|
+
const position = this._paragraph.getPosition();
|
|
261
|
+
if (!position) {
|
|
262
|
+
throw new exceptions_1.ValidationException("Paragraph position is null, you need to specify a position for the new paragraph, using .at(x,y)");
|
|
263
|
+
}
|
|
264
|
+
if (!this._targetObjectRef && !this._font && !this._paragraph.font) {
|
|
265
|
+
throw new exceptions_1.ValidationException("Font must be set before building paragraph");
|
|
266
|
+
}
|
|
267
|
+
if (this._text !== undefined) {
|
|
268
|
+
this._finalizeLinesFromText();
|
|
269
|
+
}
|
|
270
|
+
else if (!this._paragraph.textLines || this._paragraph.textLines.length === 0) {
|
|
271
|
+
throw new exceptions_1.ValidationException("Either text must be provided or existing lines supplied");
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
this._finalizeExistingLines();
|
|
275
|
+
}
|
|
276
|
+
this._repositionLines();
|
|
277
|
+
const shouldSkipLines = (this._positionChanged &&
|
|
278
|
+
this._text === undefined &&
|
|
279
|
+
this._textColor === undefined &&
|
|
280
|
+
(this._font === undefined || !this._fontExplicitlyChanged) &&
|
|
281
|
+
this._lineSpacingFactor === undefined);
|
|
282
|
+
if (shouldSkipLines) {
|
|
283
|
+
this._paragraph.textLines = undefined;
|
|
284
|
+
this._paragraph.setLineSpacings(null);
|
|
285
|
+
}
|
|
286
|
+
let finalFont = this._font ?? this._paragraph.font ?? this._originalFont;
|
|
287
|
+
if (!finalFont) {
|
|
288
|
+
finalFont = new models_1.Font(models_1.StandardFonts.HELVETICA, DEFAULT_BASE_FONT_SIZE);
|
|
289
|
+
}
|
|
290
|
+
this._paragraph.font = finalFont;
|
|
291
|
+
let finalColor;
|
|
146
292
|
if (this._textColor) {
|
|
147
|
-
|
|
293
|
+
finalColor = cloneColor(this._textColor);
|
|
148
294
|
}
|
|
149
|
-
else if (
|
|
150
|
-
|
|
295
|
+
else if (this._text !== undefined) {
|
|
296
|
+
finalColor = cloneColor(this._originalColor) ?? defaultTextColor();
|
|
151
297
|
}
|
|
152
298
|
else {
|
|
153
|
-
|
|
299
|
+
finalColor = cloneColor(this._originalColor);
|
|
154
300
|
}
|
|
301
|
+
this._paragraph.color = finalColor;
|
|
302
|
+
return this._paragraph;
|
|
155
303
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return new models_1.Font(fontName, fontSize);
|
|
304
|
+
_finalizeLinesFromText() {
|
|
305
|
+
const baseFont = this._font ?? this._originalFont;
|
|
306
|
+
const baseColor = this._textColor ?? cloneColor(this._originalColor) ?? defaultTextColor();
|
|
307
|
+
let spacing;
|
|
308
|
+
if (this._lineSpacingFactor !== undefined) {
|
|
309
|
+
spacing = this._lineSpacingFactor;
|
|
163
310
|
}
|
|
164
|
-
|
|
165
|
-
|
|
311
|
+
else {
|
|
312
|
+
const existingSpacings = this._paragraph.getLineSpacings();
|
|
313
|
+
if (existingSpacings && existingSpacings.length > 0) {
|
|
314
|
+
spacing = existingSpacings[0];
|
|
315
|
+
}
|
|
316
|
+
else if (this._paragraph.lineSpacing !== undefined && this._paragraph.lineSpacing !== null) {
|
|
317
|
+
spacing = this._paragraph.lineSpacing;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
spacing = DEFAULT_LINE_SPACING_FACTOR;
|
|
321
|
+
}
|
|
166
322
|
}
|
|
323
|
+
this._paragraph.clearLines();
|
|
324
|
+
const lines = [];
|
|
325
|
+
this._splitText(this._text ?? '').forEach((lineText, index) => {
|
|
326
|
+
const linePosition = this._calculateLinePosition(index, spacing);
|
|
327
|
+
lines.push(new models_1.TextLine(linePosition, baseFont, cloneColor(baseColor), spacing, lineText));
|
|
328
|
+
});
|
|
329
|
+
this._paragraph.setLines(lines);
|
|
330
|
+
if (lines.length > 1) {
|
|
331
|
+
this._paragraph.setLineSpacings(Array(lines.length - 1).fill(spacing));
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
this._paragraph.setLineSpacings(null);
|
|
335
|
+
}
|
|
336
|
+
this._paragraph.lineSpacing = spacing;
|
|
167
337
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// Simple implementation - split on newlines
|
|
178
|
-
// In the full version, this would implement proper text layout
|
|
179
|
-
let lines = processedText.split('\n');
|
|
180
|
-
// Remove empty lines at the end but preserve intentional line breaks
|
|
181
|
-
while (lines.length > 0 && !lines[lines.length - 1].trim()) {
|
|
182
|
-
lines.pop();
|
|
183
|
-
}
|
|
184
|
-
// Ensure at least one line
|
|
185
|
-
if (lines.length === 0) {
|
|
186
|
-
lines = [''];
|
|
187
|
-
}
|
|
188
|
-
return lines;
|
|
189
|
-
}
|
|
190
|
-
async apply() {
|
|
191
|
-
// Wait for all deferred operations (e.g., fontFile, images, etc.)
|
|
192
|
-
if (this._pending.length) {
|
|
193
|
-
await Promise.all(this._pending);
|
|
194
|
-
this._pending = []; // reset if builder is reusable
|
|
338
|
+
_finalizeExistingLines() {
|
|
339
|
+
const lines = this._paragraph.getLines();
|
|
340
|
+
const spacingOverride = this._lineSpacingFactor;
|
|
341
|
+
let spacingForCalc = spacingOverride;
|
|
342
|
+
if (spacingForCalc === undefined) {
|
|
343
|
+
const existingSpacings = this._paragraph.getLineSpacings();
|
|
344
|
+
if (existingSpacings && existingSpacings.length > 0) {
|
|
345
|
+
spacingForCalc = existingSpacings[0];
|
|
346
|
+
}
|
|
195
347
|
}
|
|
196
|
-
if (
|
|
197
|
-
|
|
348
|
+
if (spacingForCalc === undefined) {
|
|
349
|
+
spacingForCalc = this._paragraph.lineSpacing ?? DEFAULT_LINE_SPACING_FACTOR;
|
|
198
350
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
this.
|
|
206
|
-
|
|
207
|
-
// Simple text-only modification
|
|
208
|
-
const result = await this._internals.modifyParagraph(originalRef, this._text);
|
|
209
|
-
if (result.warning) {
|
|
210
|
-
process.stderr.write(`WARNING: ${result.warning}\n`);
|
|
351
|
+
const updatedLines = [];
|
|
352
|
+
lines.forEach((line, index) => {
|
|
353
|
+
if (line instanceof models_1.TextLine) {
|
|
354
|
+
if (spacingOverride !== undefined) {
|
|
355
|
+
line.lineSpacing = spacingOverride;
|
|
356
|
+
}
|
|
357
|
+
if (this._textColor) {
|
|
358
|
+
line.color = cloneColor(this._textColor);
|
|
211
359
|
}
|
|
212
|
-
|
|
360
|
+
if (this._font && this._fontExplicitlyChanged) {
|
|
361
|
+
line.font = this._font;
|
|
362
|
+
}
|
|
363
|
+
updatedLines.push(line);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
const linePosition = this._calculateLinePosition(index, spacingForCalc);
|
|
367
|
+
updatedLines.push(new models_1.TextLine(linePosition, this._font ?? this._originalFont, this._textColor ?? cloneColor(this._originalColor) ?? defaultTextColor(), spacingOverride ?? spacingForCalc, String(line)));
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
this._paragraph.setLines(updatedLines);
|
|
371
|
+
if (spacingOverride !== undefined) {
|
|
372
|
+
if (updatedLines.length > 1) {
|
|
373
|
+
this._paragraph.setLineSpacings(Array(updatedLines.length - 1).fill(spacingOverride));
|
|
213
374
|
}
|
|
214
375
|
else {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
376
|
+
this._paragraph.setLineSpacings(null);
|
|
377
|
+
}
|
|
378
|
+
this._paragraph.lineSpacing = spacingOverride;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
_repositionLines() {
|
|
382
|
+
if (this._text !== undefined) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const paragraphPos = this._paragraph.getPosition();
|
|
386
|
+
const lines = this._paragraph.textLines;
|
|
387
|
+
if (!paragraphPos || !lines || lines.length === 0) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
let basePosition = this._originalParagraphPosition;
|
|
391
|
+
if (!basePosition) {
|
|
392
|
+
for (const line of lines) {
|
|
393
|
+
if (line instanceof models_1.TextLine && line.position) {
|
|
394
|
+
basePosition = line.position;
|
|
395
|
+
break;
|
|
225
396
|
}
|
|
226
|
-
return result;
|
|
227
397
|
}
|
|
228
398
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
399
|
+
if (!basePosition) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const targetX = paragraphPos.getX();
|
|
403
|
+
const targetY = paragraphPos.getY();
|
|
404
|
+
const baseX = basePosition.getX();
|
|
405
|
+
const baseY = basePosition.getY();
|
|
406
|
+
if (targetX === undefined || targetY === undefined || baseX === undefined || baseY === undefined) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const dx = targetX - baseX;
|
|
410
|
+
const dy = targetY - baseY;
|
|
411
|
+
if (dx === 0 && dy === 0) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
lines.forEach(line => {
|
|
415
|
+
if (line instanceof models_1.TextLine && line.position) {
|
|
416
|
+
const currentX = line.position.getX();
|
|
417
|
+
const currentY = line.position.getY();
|
|
418
|
+
if (currentX === undefined || currentY === undefined) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const updatedPosition = line.position.copy();
|
|
422
|
+
updatedPosition.atCoordinates({ x: currentX + dx, y: currentY + dy });
|
|
423
|
+
line.setPosition(updatedPosition);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
_coerceTextLine(source) {
|
|
428
|
+
if (source instanceof models_1.TextLine) {
|
|
429
|
+
return source;
|
|
430
|
+
}
|
|
431
|
+
if (source instanceof models_1.TextObjectRef) {
|
|
432
|
+
let font;
|
|
433
|
+
if (source.fontName && source.fontSize) {
|
|
434
|
+
font = new models_1.Font(source.fontName, source.fontSize);
|
|
435
|
+
}
|
|
436
|
+
else if (source.children) {
|
|
437
|
+
for (const child of source.children) {
|
|
438
|
+
if (child.fontName && child.fontSize) {
|
|
439
|
+
font = new models_1.Font(child.fontName, child.fontSize);
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (!font) {
|
|
445
|
+
font = this._originalFont;
|
|
446
|
+
}
|
|
447
|
+
let spacing = this._lineSpacingFactor;
|
|
448
|
+
if (spacing === undefined && source.lineSpacings && source.lineSpacings.length > 0) {
|
|
449
|
+
spacing = source.lineSpacings[0];
|
|
450
|
+
}
|
|
451
|
+
if (spacing === undefined) {
|
|
452
|
+
spacing = this._paragraph.lineSpacing ?? DEFAULT_LINE_SPACING_FACTOR;
|
|
453
|
+
}
|
|
454
|
+
const color = source.color ?? this._originalColor;
|
|
455
|
+
if (!this._originalFont && font) {
|
|
456
|
+
this._originalFont = font;
|
|
457
|
+
}
|
|
458
|
+
if (!this._originalColor && color) {
|
|
459
|
+
this._originalColor = color;
|
|
460
|
+
}
|
|
461
|
+
return new models_1.TextLine(clonePosition(source.position), font, cloneColor(color), spacing, source.text ?? '');
|
|
233
462
|
}
|
|
463
|
+
const currentIndex = this._paragraph.getLines().length;
|
|
464
|
+
const spacing = this._lineSpacingFactor ?? this._paragraph.lineSpacing ?? DEFAULT_LINE_SPACING_FACTOR;
|
|
465
|
+
const linePosition = this._calculateLinePosition(currentIndex, spacing);
|
|
466
|
+
return new models_1.TextLine(linePosition, this._font ?? this._originalFont, this._textColor ?? cloneColor(this._originalColor) ?? defaultTextColor(), spacing, source);
|
|
234
467
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
468
|
+
_splitText(text) {
|
|
469
|
+
const processed = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\\n/g, '\n');
|
|
470
|
+
const parts = processed.split('\n');
|
|
471
|
+
while (parts.length > 0 && parts[parts.length - 1] === '') {
|
|
472
|
+
parts.pop();
|
|
473
|
+
}
|
|
474
|
+
if (parts.length === 0) {
|
|
475
|
+
parts.push('');
|
|
476
|
+
}
|
|
477
|
+
return parts;
|
|
238
478
|
}
|
|
239
|
-
|
|
240
|
-
|
|
479
|
+
_calculateLinePosition(lineIndex, spacingFactor) {
|
|
480
|
+
const paragraphPosition = this._paragraph.getPosition();
|
|
481
|
+
if (!paragraphPosition) {
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
const pageIndex = paragraphPosition.pageIndex;
|
|
485
|
+
const baseX = paragraphPosition.getX();
|
|
486
|
+
const baseY = paragraphPosition.getY();
|
|
487
|
+
if (pageIndex === undefined || baseX === undefined || baseY === undefined) {
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
490
|
+
const offset = lineIndex * this._calculateBaselineDistance(spacingFactor);
|
|
491
|
+
return models_1.Position.atPageCoordinates(pageIndex, baseX, baseY + offset);
|
|
492
|
+
}
|
|
493
|
+
_calculateBaselineDistance(spacingFactor) {
|
|
494
|
+
const factor = spacingFactor > 0 ? spacingFactor : DEFAULT_LINE_SPACING_FACTOR;
|
|
495
|
+
return this._baselineFontSize() * factor;
|
|
496
|
+
}
|
|
497
|
+
_baselineFontSize() {
|
|
498
|
+
if (this._font?.size) {
|
|
499
|
+
return this._font.size;
|
|
500
|
+
}
|
|
501
|
+
if (this._originalFont?.size) {
|
|
502
|
+
return this._originalFont.size;
|
|
503
|
+
}
|
|
504
|
+
return DEFAULT_BASE_FONT_SIZE;
|
|
505
|
+
}
|
|
506
|
+
async _registerTtf(ttfFile, fontSize) {
|
|
507
|
+
try {
|
|
508
|
+
const fontName = await this._client.registerFont(ttfFile);
|
|
509
|
+
return new models_1.Font(fontName, fontSize);
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
throw new exceptions_1.ValidationException(`Failed to register font file: ${error}`);
|
|
513
|
+
}
|
|
241
514
|
}
|
|
242
515
|
}
|
|
243
516
|
exports.ParagraphBuilder = ParagraphBuilder;
|