pdfdancer-client-typescript 1.0.15 → 1.0.17
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 +6 -3
- package/dist/image-builder.d.ts +1 -0
- package/dist/image-builder.d.ts.map +1 -1
- package/dist/image-builder.js +4 -0
- package/dist/image-builder.js.map +1 -1
- package/dist/pdfdancer_v1.d.ts +6 -0
- package/dist/pdfdancer_v1.d.ts.map +1 -1
- package/dist/pdfdancer_v1.js +52 -3
- package/dist/pdfdancer_v1.js.map +1 -1
- package/package.json +18 -2
- package/.claude/commands/discuss.md +0 -4
- package/.eslintrc.js +0 -27
- package/.github/workflows/ci.yml +0 -86
- package/.github/workflows/daily-tests.yml +0 -54
- package/NOTICE +0 -8
- package/docs/openapi.yml +0 -2640
- package/fixtures/DancingScript-Regular.ttf +0 -0
- package/fixtures/Empty.pdf +0 -0
- package/fixtures/JetBrainsMono-Regular.ttf +0 -0
- package/fixtures/ObviouslyAwesome.pdf +0 -0
- package/fixtures/README.md +0 -23
- package/fixtures/Showcase.pdf +0 -0
- package/fixtures/basic-paths.pdf +0 -0
- package/fixtures/form-xobject-example.pdf +0 -0
- package/fixtures/logo-80.png +0 -0
- package/fixtures/mixed-form-types.pdf +0 -0
- package/jest.config.js +0 -27
- package/scripts/release.js +0 -91
- package/scripts/test-release.js +0 -59
- package/src/__tests__/assertions.ts +0 -12
- package/src/__tests__/client-v1.test.ts +0 -70
- package/src/__tests__/e2e/acroform.test.ts +0 -166
- package/src/__tests__/e2e/context-manager-showcase.test.ts +0 -267
- package/src/__tests__/e2e/create-new.test.ts +0 -133
- package/src/__tests__/e2e/form_x_object.test.ts +0 -87
- package/src/__tests__/e2e/image-showcase.test.ts +0 -133
- package/src/__tests__/e2e/image.test.ts +0 -147
- package/src/__tests__/e2e/line-showcase.test.ts +0 -113
- package/src/__tests__/e2e/line.test.ts +0 -189
- package/src/__tests__/e2e/page-showcase.test.ts +0 -154
- package/src/__tests__/e2e/page.test.ts +0 -47
- package/src/__tests__/e2e/paragraph-showcase.test.ts +0 -515
- package/src/__tests__/e2e/paragraph.test.ts +0 -683
- package/src/__tests__/e2e/path.test.ts +0 -108
- package/src/__tests__/e2e/pdf-assertions.ts +0 -248
- package/src/__tests__/e2e/pdfdancer-showcase.test.ts +0 -40
- package/src/__tests__/e2e/snapshot-showcase.test.ts +0 -158
- package/src/__tests__/e2e/snapshot.test.ts +0 -296
- package/src/__tests__/e2e/test-helpers.ts +0 -152
- package/src/__tests__/e2e/token_from_env.test.ts +0 -80
- package/src/__tests__/fingerprint.test.ts +0 -36
- package/src/__tests__/retry-mechanism.test.ts +0 -420
- package/src/__tests__/standard-fonts.test.ts +0 -87
- package/src/__tests__/url-builder.test.ts +0 -44
- package/src/exceptions.ts +0 -63
- package/src/fingerprint.ts +0 -182
- package/src/image-builder.ts +0 -59
- package/src/index.ts +0 -49
- package/src/models.ts +0 -1051
- package/src/page-builder.ts +0 -130
- package/src/paragraph-builder.ts +0 -644
- package/src/pdfdancer_v1.ts +0 -2224
- package/src/types.ts +0 -407
- package/tsconfig.json +0 -20
- package/update-api-spec.sh +0 -3
package/src/paragraph-builder.ts
DELETED
|
@@ -1,644 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ParagraphBuilder for the PDFDancer TypeScript client.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {ValidationException} from './exceptions';
|
|
6
|
-
import {
|
|
7
|
-
Color,
|
|
8
|
-
CommandResult,
|
|
9
|
-
Font,
|
|
10
|
-
ObjectRef,
|
|
11
|
-
Paragraph,
|
|
12
|
-
Position,
|
|
13
|
-
StandardFonts,
|
|
14
|
-
TextLine,
|
|
15
|
-
TextObjectRef
|
|
16
|
-
} from './models';
|
|
17
|
-
import {PDFDancer} from './pdfdancer_v1';
|
|
18
|
-
|
|
19
|
-
const DEFAULT_LINE_SPACING_FACTOR = 1.2;
|
|
20
|
-
const DEFAULT_BASE_FONT_SIZE = 12;
|
|
21
|
-
|
|
22
|
-
// 👇 Internal view of PDFDancer methods, not exported
|
|
23
|
-
interface PDFDancerInternals {
|
|
24
|
-
modifyParagraph(objectRef: ObjectRef, update: Paragraph | string | null): Promise<CommandResult>;
|
|
25
|
-
|
|
26
|
-
addParagraph(paragraph: Paragraph): Promise<boolean>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const cloneColor = (color?: Color | null): Color | undefined => {
|
|
30
|
-
if (!color) {
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
return new Color(color.r, color.g, color.b, color.a);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const clonePosition = (position?: Position): Position | undefined => {
|
|
37
|
-
return position ? position.copy() : undefined;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const defaultTextColor = (): Color => new Color(0, 0, 0);
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Builder class for constructing Paragraph objects with fluent interface.
|
|
44
|
-
* Aligns with the Python client's ParagraphBuilder behaviour.
|
|
45
|
-
*/
|
|
46
|
-
export class ParagraphBuilder {
|
|
47
|
-
private readonly _paragraph: Paragraph;
|
|
48
|
-
private readonly _internals: PDFDancerInternals;
|
|
49
|
-
|
|
50
|
-
private _lineSpacingFactor?: number;
|
|
51
|
-
private _textColor?: Color;
|
|
52
|
-
private _text?: string;
|
|
53
|
-
private _ttfSource?: Uint8Array | File | string;
|
|
54
|
-
private _font?: Font;
|
|
55
|
-
private _fontExplicitlyChanged = false;
|
|
56
|
-
private _originalParagraphPosition?: Position;
|
|
57
|
-
private _targetObjectRef?: TextObjectRef;
|
|
58
|
-
private _originalFont?: Font;
|
|
59
|
-
private _originalColor?: Color;
|
|
60
|
-
private _positionChanged = false;
|
|
61
|
-
private _pageIndex?: number;
|
|
62
|
-
|
|
63
|
-
private _pending: Promise<unknown>[] = [];
|
|
64
|
-
private _registeringFont = false;
|
|
65
|
-
|
|
66
|
-
constructor(private readonly _client: PDFDancer, objectRefOrPageIndex?: TextObjectRef | number) {
|
|
67
|
-
if (!_client) {
|
|
68
|
-
throw new ValidationException("Client cannot be null");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
this._paragraph = new Paragraph();
|
|
72
|
-
this._internals = this._client as unknown as PDFDancerInternals;
|
|
73
|
-
|
|
74
|
-
if (objectRefOrPageIndex instanceof TextObjectRef) {
|
|
75
|
-
this.target(objectRefOrPageIndex);
|
|
76
|
-
} else if (typeof objectRefOrPageIndex === 'number') {
|
|
77
|
-
this._pageIndex = objectRefOrPageIndex;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
static fromObjectRef(client: PDFDancer, objectRef: TextObjectRef): ParagraphBuilder {
|
|
82
|
-
if (!objectRef) {
|
|
83
|
-
throw new ValidationException("Object reference cannot be null");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const builder = new ParagraphBuilder(client, objectRef);
|
|
87
|
-
builder.target(objectRef);
|
|
88
|
-
builder.setOriginalParagraphPosition(objectRef.position);
|
|
89
|
-
|
|
90
|
-
if (objectRef.lineSpacings) {
|
|
91
|
-
builder._paragraph.setLineSpacings(objectRef.lineSpacings);
|
|
92
|
-
const [firstSpacing] = objectRef.lineSpacings;
|
|
93
|
-
if (firstSpacing !== undefined) {
|
|
94
|
-
builder._paragraph.lineSpacing = firstSpacing;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (objectRef.fontName && objectRef.fontSize) {
|
|
99
|
-
builder._originalFont = new Font(objectRef.fontName, objectRef.fontSize);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (objectRef.color) {
|
|
103
|
-
builder._originalColor = cloneColor(objectRef.color);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (objectRef.children && objectRef.children.length > 0) {
|
|
107
|
-
objectRef.children.forEach(child => builder.addTextLine(child));
|
|
108
|
-
} else if (objectRef.text) {
|
|
109
|
-
builder._splitText(objectRef.text).forEach(segment => builder.addTextLine(segment));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return builder;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
setFontExplicitlyChanged(changed: boolean): void {
|
|
116
|
-
this._fontExplicitlyChanged = !!changed;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
setOriginalParagraphPosition(position?: Position): void {
|
|
120
|
-
this._originalParagraphPosition = clonePosition(position);
|
|
121
|
-
if (position && !this._paragraph.getPosition()) {
|
|
122
|
-
this._paragraph.setPosition(clonePosition(position)!);
|
|
123
|
-
}
|
|
124
|
-
if (position?.pageIndex !== undefined) {
|
|
125
|
-
this._pageIndex = position.pageIndex;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
target(objectRef: ObjectRef): this {
|
|
130
|
-
if (!objectRef) {
|
|
131
|
-
throw new ValidationException("Object reference cannot be null");
|
|
132
|
-
}
|
|
133
|
-
this._targetObjectRef = objectRef as TextObjectRef;
|
|
134
|
-
if (objectRef.position) {
|
|
135
|
-
this.setOriginalParagraphPosition(objectRef.position);
|
|
136
|
-
}
|
|
137
|
-
if (objectRef.position?.pageIndex !== undefined) {
|
|
138
|
-
this._pageIndex = objectRef.position.pageIndex;
|
|
139
|
-
}
|
|
140
|
-
return this;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
onlyTextChanged(): boolean {
|
|
144
|
-
return (
|
|
145
|
-
this._text !== undefined &&
|
|
146
|
-
this._textColor === undefined &&
|
|
147
|
-
this._ttfSource === undefined &&
|
|
148
|
-
(this._font === undefined || !this._fontExplicitlyChanged) &&
|
|
149
|
-
this._lineSpacingFactor === undefined
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
replace(text: string, color?: Color): this {
|
|
154
|
-
return this.text(text, color);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
text(text: string, color?: Color): this {
|
|
158
|
-
if (text === null || text === undefined) {
|
|
159
|
-
throw new ValidationException("Text cannot be null");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
this._text = text;
|
|
163
|
-
if (color) {
|
|
164
|
-
this.color(color);
|
|
165
|
-
}
|
|
166
|
-
return this;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
font(font: Font): this;
|
|
170
|
-
font(fontName: string, fontSize: number): this;
|
|
171
|
-
font(fontOrName: Font | string, fontSize?: number): this {
|
|
172
|
-
if (fontOrName instanceof Font) {
|
|
173
|
-
this._font = fontOrName;
|
|
174
|
-
} else {
|
|
175
|
-
if (!fontOrName) {
|
|
176
|
-
throw new ValidationException("Font name cannot be null");
|
|
177
|
-
}
|
|
178
|
-
if (fontSize == null) {
|
|
179
|
-
throw new ValidationException("Font size cannot be null");
|
|
180
|
-
}
|
|
181
|
-
this._font = new Font(fontOrName, fontSize);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
this._fontExplicitlyChanged = true;
|
|
185
|
-
return this;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
fontFile(ttfFile: Uint8Array | File | string, fontSize: number): this {
|
|
189
|
-
if (!ttfFile) {
|
|
190
|
-
throw new ValidationException("TTF file cannot be null");
|
|
191
|
-
}
|
|
192
|
-
if (fontSize <= 0) {
|
|
193
|
-
throw new ValidationException(`Font size must be positive, got ${fontSize}`);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
this._ttfSource = ttfFile;
|
|
197
|
-
this._registeringFont = true;
|
|
198
|
-
const job = this._registerTtf(ttfFile, fontSize)
|
|
199
|
-
.then(font => {
|
|
200
|
-
this._font = font;
|
|
201
|
-
this._fontExplicitlyChanged = true;
|
|
202
|
-
})
|
|
203
|
-
.finally(() => {
|
|
204
|
-
this._registeringFont = false;
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
this._pending.push(job);
|
|
208
|
-
return this;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
lineSpacing(spacing: number): this {
|
|
212
|
-
if (spacing <= 0) {
|
|
213
|
-
throw new ValidationException(`Line spacing must be positive, got ${spacing}`);
|
|
214
|
-
}
|
|
215
|
-
this._lineSpacingFactor = spacing;
|
|
216
|
-
return this;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
color(color: Color): this {
|
|
220
|
-
if (!color) {
|
|
221
|
-
throw new ValidationException("Color cannot be null");
|
|
222
|
-
}
|
|
223
|
-
this._textColor = color;
|
|
224
|
-
return this;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
moveTo(x: number, y: number): this {
|
|
228
|
-
if (x === null || x === undefined || y === null || y === undefined) {
|
|
229
|
-
throw new ValidationException("Coordinates cannot be null or undefined");
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
let position = this._paragraph.getPosition();
|
|
233
|
-
if (!position && this._targetObjectRef?.position) {
|
|
234
|
-
position = clonePosition(this._targetObjectRef.position);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const pageIndex = position?.pageIndex ?? this._pageIndex;
|
|
238
|
-
if (pageIndex === undefined) {
|
|
239
|
-
throw new ValidationException("Paragraph position must include a page index to move");
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
this._paragraph.setPosition(Position.atPageCoordinates(pageIndex, x, y));
|
|
243
|
-
this._positionChanged = true;
|
|
244
|
-
return this;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
atPosition(position: Position): this {
|
|
248
|
-
if (!position) {
|
|
249
|
-
throw new ValidationException("Position cannot be null");
|
|
250
|
-
}
|
|
251
|
-
this._paragraph.setPosition(clonePosition(position)!);
|
|
252
|
-
this._positionChanged = true;
|
|
253
|
-
if (position.pageIndex !== undefined) {
|
|
254
|
-
this._pageIndex = position.pageIndex;
|
|
255
|
-
}
|
|
256
|
-
return this;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
at(x: number, y: number): this;
|
|
260
|
-
at(pageIndex: number, x: number, y: number): this;
|
|
261
|
-
at(pageIndexOrX: number, xOrY: number, maybeY?: number): this {
|
|
262
|
-
if (maybeY === undefined) {
|
|
263
|
-
const pageIndex = this._pageIndex ?? this._paragraph.getPosition()?.pageIndex;
|
|
264
|
-
if (pageIndex === undefined) {
|
|
265
|
-
throw new ValidationException("Page index must be provided before calling at(x, y)");
|
|
266
|
-
}
|
|
267
|
-
return this._setPosition(pageIndex, pageIndexOrX, xOrY);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return this._setPosition(pageIndexOrX, xOrY, maybeY);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
private _setPosition(pageIndex: number, x: number, y: number): this {
|
|
274
|
-
this._pageIndex = pageIndex;
|
|
275
|
-
return this.atPosition(Position.atPageCoordinates(pageIndex, x, y));
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
addTextLine(textLine: TextLine | TextObjectRef | string): this {
|
|
279
|
-
this._paragraph.addLine(this._coerceTextLine(textLine));
|
|
280
|
-
return this;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
getText(): string | undefined {
|
|
284
|
-
return this._text;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async add(): Promise<boolean> {
|
|
288
|
-
await this._prepareAsync();
|
|
289
|
-
if (this._targetObjectRef) {
|
|
290
|
-
throw new ValidationException("Target object reference provided; use modify() for updates");
|
|
291
|
-
}
|
|
292
|
-
const paragraph = this._finalizeParagraph();
|
|
293
|
-
return this._internals.addParagraph(paragraph);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
async modify(objectRef?: ObjectRef): Promise<CommandResult> {
|
|
297
|
-
await this._prepareAsync();
|
|
298
|
-
const target = (objectRef as TextObjectRef) ?? this._targetObjectRef;
|
|
299
|
-
if (!target) {
|
|
300
|
-
throw new ValidationException("Object reference must be provided to modify a paragraph");
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (this.onlyTextChanged()) {
|
|
304
|
-
const result = await this._internals.modifyParagraph(target, this._text ?? '');
|
|
305
|
-
return this._withWarning(result);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const paragraph = this._finalizeParagraph();
|
|
309
|
-
const result = await this._internals.modifyParagraph(target, paragraph);
|
|
310
|
-
return this._withWarning(result);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
async apply(): Promise<boolean | CommandResult> {
|
|
314
|
-
await this._prepareAsync();
|
|
315
|
-
if (this._targetObjectRef) {
|
|
316
|
-
return this.modify(this._targetObjectRef);
|
|
317
|
-
}
|
|
318
|
-
return this.add();
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
private async _prepareAsync(): Promise<void> {
|
|
322
|
-
if (this._pending.length) {
|
|
323
|
-
await Promise.all(this._pending);
|
|
324
|
-
this._pending = [];
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (this._registeringFont) {
|
|
328
|
-
throw new ValidationException("Font registration is not complete");
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
private _withWarning(result: CommandResult): CommandResult {
|
|
333
|
-
if (result && result.warning) {
|
|
334
|
-
process.stderr.write(`WARNING: ${result.warning}\n`);
|
|
335
|
-
}
|
|
336
|
-
return result;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
private _finalizeParagraph(): Paragraph {
|
|
340
|
-
const position = this._paragraph.getPosition();
|
|
341
|
-
if (!position) {
|
|
342
|
-
throw new ValidationException("Paragraph position is null, you need to specify a position for the new paragraph, using .at(x,y)");
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (!this._targetObjectRef && !this._font && !this._paragraph.font) {
|
|
346
|
-
throw new ValidationException("Font must be set before building paragraph");
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (this._text !== undefined) {
|
|
350
|
-
this._finalizeLinesFromText();
|
|
351
|
-
} else if (!this._paragraph.textLines || this._paragraph.textLines.length === 0) {
|
|
352
|
-
throw new ValidationException("Either text must be provided or existing lines supplied");
|
|
353
|
-
} else {
|
|
354
|
-
this._finalizeExistingLines();
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
this._repositionLines();
|
|
358
|
-
|
|
359
|
-
const shouldSkipLines = (
|
|
360
|
-
this._positionChanged &&
|
|
361
|
-
this._text === undefined &&
|
|
362
|
-
this._textColor === undefined &&
|
|
363
|
-
(this._font === undefined || !this._fontExplicitlyChanged) &&
|
|
364
|
-
this._lineSpacingFactor === undefined
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
if (shouldSkipLines) {
|
|
368
|
-
this._paragraph.textLines = undefined;
|
|
369
|
-
this._paragraph.setLineSpacings(null);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
let finalFont = this._font ?? this._paragraph.font ?? this._originalFont;
|
|
373
|
-
if (!finalFont) {
|
|
374
|
-
finalFont = new Font(StandardFonts.HELVETICA, DEFAULT_BASE_FONT_SIZE);
|
|
375
|
-
}
|
|
376
|
-
this._paragraph.font = finalFont;
|
|
377
|
-
|
|
378
|
-
let finalColor: Color | undefined;
|
|
379
|
-
if (this._textColor) {
|
|
380
|
-
finalColor = cloneColor(this._textColor);
|
|
381
|
-
} else if (this._text !== undefined) {
|
|
382
|
-
finalColor = cloneColor(this._originalColor) ?? defaultTextColor();
|
|
383
|
-
} else {
|
|
384
|
-
finalColor = cloneColor(this._originalColor);
|
|
385
|
-
}
|
|
386
|
-
this._paragraph.color = finalColor;
|
|
387
|
-
|
|
388
|
-
return this._paragraph;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
private _finalizeLinesFromText(): void {
|
|
392
|
-
const baseFont = this._font ?? this._originalFont;
|
|
393
|
-
const baseColor = this._textColor ?? cloneColor(this._originalColor) ?? defaultTextColor();
|
|
394
|
-
|
|
395
|
-
let spacing: number;
|
|
396
|
-
if (this._lineSpacingFactor !== undefined) {
|
|
397
|
-
spacing = this._lineSpacingFactor;
|
|
398
|
-
} else {
|
|
399
|
-
const existingSpacings = this._paragraph.getLineSpacings();
|
|
400
|
-
if (existingSpacings && existingSpacings.length > 0) {
|
|
401
|
-
spacing = existingSpacings[0];
|
|
402
|
-
} else if (this._paragraph.lineSpacing !== undefined && this._paragraph.lineSpacing !== null) {
|
|
403
|
-
spacing = this._paragraph.lineSpacing;
|
|
404
|
-
} else {
|
|
405
|
-
spacing = DEFAULT_LINE_SPACING_FACTOR;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
this._paragraph.clearLines();
|
|
410
|
-
const lines: TextLine[] = [];
|
|
411
|
-
|
|
412
|
-
this._splitText(this._text ?? '').forEach((lineText, index) => {
|
|
413
|
-
const linePosition = this._calculateLinePosition(index, spacing);
|
|
414
|
-
lines.push(new TextLine(linePosition, baseFont, cloneColor(baseColor), spacing, lineText));
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
this._paragraph.setLines(lines);
|
|
418
|
-
if (lines.length > 1) {
|
|
419
|
-
this._paragraph.setLineSpacings(Array(lines.length - 1).fill(spacing));
|
|
420
|
-
} else {
|
|
421
|
-
this._paragraph.setLineSpacings(null);
|
|
422
|
-
}
|
|
423
|
-
this._paragraph.lineSpacing = spacing;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
private _finalizeExistingLines(): void {
|
|
427
|
-
const lines = this._paragraph.getLines();
|
|
428
|
-
const spacingOverride = this._lineSpacingFactor;
|
|
429
|
-
let spacingForCalc = spacingOverride;
|
|
430
|
-
|
|
431
|
-
if (spacingForCalc === undefined) {
|
|
432
|
-
const existingSpacings = this._paragraph.getLineSpacings();
|
|
433
|
-
if (existingSpacings && existingSpacings.length > 0) {
|
|
434
|
-
spacingForCalc = existingSpacings[0];
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
if (spacingForCalc === undefined) {
|
|
438
|
-
spacingForCalc = this._paragraph.lineSpacing ?? DEFAULT_LINE_SPACING_FACTOR;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
const updatedLines: TextLine[] = [];
|
|
442
|
-
lines.forEach((line, index) => {
|
|
443
|
-
if (line instanceof TextLine) {
|
|
444
|
-
if (spacingOverride !== undefined) {
|
|
445
|
-
line.lineSpacing = spacingOverride;
|
|
446
|
-
}
|
|
447
|
-
if (this._textColor) {
|
|
448
|
-
line.color = cloneColor(this._textColor);
|
|
449
|
-
}
|
|
450
|
-
if (this._font && this._fontExplicitlyChanged) {
|
|
451
|
-
line.font = this._font;
|
|
452
|
-
}
|
|
453
|
-
updatedLines.push(line);
|
|
454
|
-
} else {
|
|
455
|
-
const linePosition = this._calculateLinePosition(index, spacingForCalc!);
|
|
456
|
-
updatedLines.push(new TextLine(
|
|
457
|
-
linePosition,
|
|
458
|
-
this._font ?? this._originalFont,
|
|
459
|
-
this._textColor ?? cloneColor(this._originalColor) ?? defaultTextColor(),
|
|
460
|
-
spacingOverride ?? spacingForCalc!,
|
|
461
|
-
String(line)
|
|
462
|
-
));
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
this._paragraph.setLines(updatedLines);
|
|
467
|
-
|
|
468
|
-
if (spacingOverride !== undefined) {
|
|
469
|
-
if (updatedLines.length > 1) {
|
|
470
|
-
this._paragraph.setLineSpacings(Array(updatedLines.length - 1).fill(spacingOverride));
|
|
471
|
-
} else {
|
|
472
|
-
this._paragraph.setLineSpacings(null);
|
|
473
|
-
}
|
|
474
|
-
this._paragraph.lineSpacing = spacingOverride;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
private _repositionLines(): void {
|
|
479
|
-
if (this._text !== undefined) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const paragraphPos = this._paragraph.getPosition();
|
|
484
|
-
const lines = this._paragraph.textLines;
|
|
485
|
-
if (!paragraphPos || !lines || lines.length === 0) {
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
let basePosition = this._originalParagraphPosition;
|
|
490
|
-
if (!basePosition) {
|
|
491
|
-
for (const line of lines) {
|
|
492
|
-
if (line instanceof TextLine && line.position) {
|
|
493
|
-
basePosition = line.position;
|
|
494
|
-
break;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
if (!basePosition) {
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
const targetX = paragraphPos.getX();
|
|
504
|
-
const targetY = paragraphPos.getY();
|
|
505
|
-
const baseX = basePosition.getX();
|
|
506
|
-
const baseY = basePosition.getY();
|
|
507
|
-
|
|
508
|
-
if (targetX === undefined || targetY === undefined || baseX === undefined || baseY === undefined) {
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const dx = targetX - baseX;
|
|
513
|
-
const dy = targetY - baseY;
|
|
514
|
-
if (dx === 0 && dy === 0) {
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
lines.forEach(line => {
|
|
519
|
-
if (line instanceof TextLine && line.position) {
|
|
520
|
-
const currentX = line.position.getX();
|
|
521
|
-
const currentY = line.position.getY();
|
|
522
|
-
if (currentX === undefined || currentY === undefined) {
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
const updatedPosition = line.position.copy();
|
|
526
|
-
updatedPosition.atCoordinates({x: currentX + dx, y: currentY + dy});
|
|
527
|
-
line.setPosition(updatedPosition);
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
private _coerceTextLine(source: TextLine | TextObjectRef | string): TextLine {
|
|
533
|
-
if (source instanceof TextLine) {
|
|
534
|
-
return source;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (source instanceof TextObjectRef) {
|
|
538
|
-
let font: Font | undefined;
|
|
539
|
-
if (source.fontName && source.fontSize) {
|
|
540
|
-
font = new Font(source.fontName, source.fontSize);
|
|
541
|
-
} else if (source.children) {
|
|
542
|
-
for (const child of source.children) {
|
|
543
|
-
if (child.fontName && child.fontSize) {
|
|
544
|
-
font = new Font(child.fontName, child.fontSize);
|
|
545
|
-
break;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
if (!font) {
|
|
550
|
-
font = this._originalFont;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
let spacing = this._lineSpacingFactor;
|
|
554
|
-
if (spacing === undefined && source.lineSpacings && source.lineSpacings.length > 0) {
|
|
555
|
-
spacing = source.lineSpacings[0];
|
|
556
|
-
}
|
|
557
|
-
if (spacing === undefined) {
|
|
558
|
-
spacing = this._paragraph.lineSpacing ?? DEFAULT_LINE_SPACING_FACTOR;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
const color = source.color ?? this._originalColor;
|
|
562
|
-
|
|
563
|
-
if (!this._originalFont && font) {
|
|
564
|
-
this._originalFont = font;
|
|
565
|
-
}
|
|
566
|
-
if (!this._originalColor && color) {
|
|
567
|
-
this._originalColor = color;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
return new TextLine(
|
|
571
|
-
clonePosition(source.position),
|
|
572
|
-
font,
|
|
573
|
-
cloneColor(color),
|
|
574
|
-
spacing,
|
|
575
|
-
source.text ?? ''
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const currentIndex = this._paragraph.getLines().length;
|
|
580
|
-
const spacing = this._lineSpacingFactor ?? this._paragraph.lineSpacing ?? DEFAULT_LINE_SPACING_FACTOR;
|
|
581
|
-
const linePosition = this._calculateLinePosition(currentIndex, spacing);
|
|
582
|
-
|
|
583
|
-
return new TextLine(
|
|
584
|
-
linePosition,
|
|
585
|
-
this._font ?? this._originalFont,
|
|
586
|
-
this._textColor ?? cloneColor(this._originalColor) ?? defaultTextColor(),
|
|
587
|
-
spacing,
|
|
588
|
-
source
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
private _splitText(text: string): string[] {
|
|
593
|
-
const processed = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\\n/g, '\n');
|
|
594
|
-
const parts = processed.split('\n');
|
|
595
|
-
while (parts.length > 0 && parts[parts.length - 1] === '') {
|
|
596
|
-
parts.pop();
|
|
597
|
-
}
|
|
598
|
-
if (parts.length === 0) {
|
|
599
|
-
parts.push('');
|
|
600
|
-
}
|
|
601
|
-
return parts;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
private _calculateLinePosition(lineIndex: number, spacingFactor: number): Position | undefined {
|
|
605
|
-
const paragraphPosition = this._paragraph.getPosition();
|
|
606
|
-
if (!paragraphPosition) {
|
|
607
|
-
return undefined;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
const pageIndex = paragraphPosition.pageIndex;
|
|
611
|
-
const baseX = paragraphPosition.getX();
|
|
612
|
-
const baseY = paragraphPosition.getY();
|
|
613
|
-
if (pageIndex === undefined || baseX === undefined || baseY === undefined) {
|
|
614
|
-
return undefined;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
const offset = lineIndex * this._calculateBaselineDistance(spacingFactor);
|
|
618
|
-
return Position.atPageCoordinates(pageIndex, baseX, baseY + offset);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
private _calculateBaselineDistance(spacingFactor: number): number {
|
|
622
|
-
const factor = spacingFactor > 0 ? spacingFactor : DEFAULT_LINE_SPACING_FACTOR;
|
|
623
|
-
return this._baselineFontSize() * factor;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
private _baselineFontSize(): number {
|
|
627
|
-
if (this._font?.size) {
|
|
628
|
-
return this._font.size;
|
|
629
|
-
}
|
|
630
|
-
if (this._originalFont?.size) {
|
|
631
|
-
return this._originalFont.size;
|
|
632
|
-
}
|
|
633
|
-
return DEFAULT_BASE_FONT_SIZE;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
private async _registerTtf(ttfFile: Uint8Array | File | string, fontSize: number): Promise<Font> {
|
|
637
|
-
try {
|
|
638
|
-
const fontName = await this._client.registerFont(ttfFile);
|
|
639
|
-
return new Font(fontName, fontSize);
|
|
640
|
-
} catch (error: any) {
|
|
641
|
-
throw new ValidationException(`Failed to register font file: ${error}`);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|