taro-bluetooth-print 2.3.0 → 2.3.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.
@@ -24,12 +24,12 @@
24
24
  * });
25
25
  * ```
26
26
  */
27
+ import { Logger } from '@/utils/logger';
28
+
27
29
  export class EventEmitter<T> {
28
- // 使用Map存储事件监听器,Set确保每个监听器唯一
29
30
  private listeners: Map<keyof T, Set<(data: T[keyof T]) => void>> = new Map();
30
-
31
- // Debug mode flag
32
31
  private debugMode = false;
32
+ protected readonly logger = Logger.scope('EventEmitter');
33
33
 
34
34
  /**
35
35
  * Subscribe to an event
@@ -45,7 +45,7 @@ export class EventEmitter<T> {
45
45
  this.listeners.get(event)!.add(handler as (data: T[keyof T]) => void);
46
46
 
47
47
  if (this.debugMode) {
48
- console.debug(`EventEmitter: Added listener for "${String(event)}"`, {
48
+ this.logger.debug(`EventEmitter: Added listener for "${String(event)}"`, {
49
49
  listenerCount: this.listenerCount(event),
50
50
  });
51
51
  }
@@ -90,7 +90,7 @@ export class EventEmitter<T> {
90
90
  this.listeners.set(event, newHandlers);
91
91
 
92
92
  if (this.debugMode) {
93
- console.debug(`EventEmitter: Prepend listener for "${String(event)}"`, {
93
+ this.logger.debug(`EventEmitter: Prepend listener for "${String(event)}"`, {
94
94
  listenerCount: this.listenerCount(event),
95
95
  });
96
96
  }
@@ -125,7 +125,7 @@ export class EventEmitter<T> {
125
125
  handlers.delete(handler as (data: T[keyof T]) => void);
126
126
 
127
127
  if (this.debugMode) {
128
- console.debug(`EventEmitter: Removed listener for "${String(event)}"`, {
128
+ this.logger.debug(`EventEmitter: Removed listener for "${String(event)}"`, {
129
129
  listenerCount: handlers.size,
130
130
  });
131
131
  }
@@ -135,7 +135,7 @@ export class EventEmitter<T> {
135
135
  this.listeners.delete(event);
136
136
 
137
137
  if (this.debugMode) {
138
- console.debug(`EventEmitter: Removed event "${String(event)}" (no more listeners)`);
138
+ this.logger.debug(`EventEmitter: Removed event "${String(event)}" (no more listeners)`);
139
139
  }
140
140
  }
141
141
  }
@@ -154,7 +154,7 @@ export class EventEmitter<T> {
154
154
  const data = args[0] as T[K];
155
155
 
156
156
  if (this.debugMode) {
157
- console.debug(`EventEmitter: Emitting "${String(event)}"`, {
157
+ this.logger.debug(`EventEmitter: Emitting "${String(event)}"`, {
158
158
  data,
159
159
  listenerCount: this.listenerCount(event),
160
160
  });
@@ -184,7 +184,7 @@ export class EventEmitter<T> {
184
184
  }
185
185
  } catch (error) {
186
186
  // 捕获并处理事件处理程序中的错误,避免影响其他监听器
187
- console.error(`Error in event handler for "${String(event)}":`, error);
187
+ this.logger.error(`Error in event handler for "${String(event)}":`, error);
188
188
  }
189
189
  }
190
190
  }
@@ -204,7 +204,7 @@ export class EventEmitter<T> {
204
204
  const data = args[0] as T[K];
205
205
 
206
206
  if (this.debugMode) {
207
- console.debug(`EventEmitter: Emitting async "${String(event)}"`, {
207
+ this.logger.debug(`EventEmitter: Emitting async "${String(event)}"`, {
208
208
  data,
209
209
  listenerCount: this.listenerCount(event),
210
210
  });
@@ -238,7 +238,7 @@ export class EventEmitter<T> {
238
238
  }
239
239
  } catch (error) {
240
240
  // 捕获并处理事件处理程序中的错误,避免影响其他监听器
241
- console.error(`Error in event handler for "${String(event)}":`, error);
241
+ this.logger.error(`Error in event handler for "${String(event)}":`, error);
242
242
  }
243
243
  })()
244
244
  );
@@ -247,7 +247,7 @@ export class EventEmitter<T> {
247
247
  await Promise.all(promises);
248
248
 
249
249
  if (this.debugMode) {
250
- console.debug(`EventEmitter: Finished emitting async "${String(event)}"`);
250
+ this.logger.debug(`EventEmitter: Finished emitting async "${String(event)}"`);
251
251
  }
252
252
  }
253
253
 
@@ -262,7 +262,7 @@ export class EventEmitter<T> {
262
262
  this.listeners.delete(event);
263
263
 
264
264
  if (this.debugMode) {
265
- console.debug(
265
+ this.logger.debug(
266
266
  `EventEmitter: Removed all ${listenerCount} listeners for "${String(event)}"`
267
267
  );
268
268
  }
@@ -272,7 +272,7 @@ export class EventEmitter<T> {
272
272
  this.listeners.clear();
273
273
 
274
274
  if (this.debugMode) {
275
- console.debug(`EventEmitter: Removed all ${eventCount} events and their listeners`);
275
+ this.logger.debug(`EventEmitter: Removed all ${eventCount} events and their listeners`);
276
276
  }
277
277
  }
278
278
  }
@@ -327,7 +327,7 @@ export class EventEmitter<T> {
327
327
  */
328
328
  setDebugMode(enabled: boolean): void {
329
329
  this.debugMode = enabled;
330
- console.debug(`EventEmitter: Debug mode ${enabled ? 'enabled' : 'disabled'}`);
330
+ this.logger.debug(`EventEmitter: Debug mode ${enabled ? 'enabled' : 'disabled'}`);
331
331
  }
332
332
 
333
333
  /**
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Core Module - 核心模块
3
+ */
4
+
5
+ export { BluetoothPrinter } from './BluetoothPrinter';
6
+ export type { PrinterEvents } from './BluetoothPrinter';
7
+ export { EventEmitter } from './EventEmitter';
@@ -0,0 +1,549 @@
1
+ /**
2
+ * CPCL Driver
3
+ * PCL commands for Compact Print Language - HP/Honeywell/Toddler printers
4
+ *
5
+ * CPCL is commonly used in mobile/portable thermal printers
6
+ */
7
+
8
+ import { Logger } from '@/utils/logger';
9
+ import { Encoding } from '@/utils/encoding';
10
+
11
+ /**
12
+ * CPCL page size presets
13
+ */
14
+ export type CPCLPageSize = 'A4' | 'A5' | 'LETTER' | '4X6' | '4X2' | '4X4' | '2.25X1.25' | 'CUSTOM';
15
+
16
+ /**
17
+ * CPCL Text options
18
+ */
19
+ export interface CpclTextOptions {
20
+ /** X position in dots */
21
+ x?: number;
22
+ /** Y position in dots */
23
+ y?: number;
24
+ /** Font type: 0-6 (0=OCR, 1=OCR-B, 2=CG Triumvirate, etc.) */
25
+ font?: number;
26
+ /** Horizontal multiplier (1-8) */
27
+ xMulti?: number;
28
+ /** Vertical multiplier (1-8) */
29
+ yMulti?: number;
30
+ /** Rotation: 0, 90, 180, 270 */
31
+ rotation?: 0 | 90 | 180 | 270;
32
+ }
33
+
34
+ /**
35
+ * CPCL Barcode options
36
+ */
37
+ export interface CpclBarcodeOptions {
38
+ /** X position in dots */
39
+ x?: number;
40
+ /** Y position in dots */
41
+ y?: number;
42
+ /** Barcode type */
43
+ type:
44
+ | '128'
45
+ | '39'
46
+ | 'EAN13'
47
+ | 'EAN8'
48
+ | 'UPCA'
49
+ | 'UPCE'
50
+ | 'MSI'
51
+ | 'PLESSEY'
52
+ | 'PDF417'
53
+ | 'DATAMATRIX'
54
+ | 'QR';
55
+ /** Barcode height in dots (default: 50) */
56
+ height?: number;
57
+ /** Wide bar width for 39 (default: 2) */
58
+ wide?: number;
59
+ /** Narrow bar width (default: 1) */
60
+ narrow?: number;
61
+ /** Show human-readable text (default: true) */
62
+ readable?: boolean;
63
+ }
64
+
65
+ /**
66
+ * CPCL QR Code options
67
+ */
68
+ export interface CpclQRCodeOptions {
69
+ /** X position in dots */
70
+ x?: number;
71
+ /** Y position in dots */
72
+ y?: number;
73
+ /** Model (default: 2) */
74
+ model?: 1 | 2;
75
+ /** Error correction level: L, M, Q, H */
76
+ errorCorrection?: 'L' | 'M' | 'Q' | 'H';
77
+ /** Cell size (default: 4) */
78
+ cellSize?: number;
79
+ }
80
+
81
+ /**
82
+ * CPCL Line options
83
+ */
84
+ export interface CpclLineOptions {
85
+ /** Start X position */
86
+ x1: number;
87
+ /** Start Y position */
88
+ y1: number;
89
+ /** End X position */
90
+ x2: number;
91
+ /** End Y position */
92
+ y2: number;
93
+ /** Line width/thickness */
94
+ width?: number;
95
+ }
96
+
97
+ /**
98
+ * CPCL Box options
99
+ */
100
+ export interface CpclBoxOptions {
101
+ /** X position */
102
+ x: number;
103
+ /** Y position */
104
+ y: number;
105
+ /** Width */
106
+ width: number;
107
+ /** Height */
108
+ height: number;
109
+ /** Border thickness */
110
+ thickness?: number;
111
+ }
112
+
113
+ /**
114
+ * CPCL Driver for HP, Honeywell, and Toddler portable printers
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const cpcl = new CpclDriver();
119
+ *
120
+ * const commands = cpcl
121
+ * .pageStart()
122
+ * .text('Hello World!', { x: 50, y: 50 })
123
+ * .barcode('1234567890', { x: 50, y: 150, type: '128' })
124
+ * .qrcode('https://example.com', { x: 300, y: 50 })
125
+ * .pageEnd()
126
+ * .getCommands();
127
+ * ```
128
+ */
129
+ export class CpclDriver {
130
+ private commands: string[] = [];
131
+ private readonly logger = Logger.scope('CpclDriver');
132
+
133
+ // Page dimensions
134
+ private pageWidth = 576; // 4" at 144 DPI (common)
135
+ private pageHeight = 0; // 0 = continuous
136
+
137
+ /**
138
+ * Initialize CPCL driver
139
+ * @param width - Page width in dots
140
+ * @param height - Page height in dots (0 = continuous)
141
+ */
142
+ constructor(width = 576, height = 0) {
143
+ this.pageWidth = width;
144
+ this.pageHeight = height;
145
+ }
146
+
147
+ /**
148
+ * Set page size
149
+ * @param width - Width in dots
150
+ * @param height - Height in dots (0 = continuous)
151
+ */
152
+ setPageSize(width: number, height = 0): this {
153
+ this.pageWidth = width;
154
+ this.pageHeight = height;
155
+ return this;
156
+ }
157
+
158
+ /**
159
+ * Use standard page size
160
+ * @param size - Preset size name
161
+ */
162
+ usePageSize(size: CPCLPageSize): this {
163
+ const sizes: Record<CPCLPageSize, [number, number]> = {
164
+ '4X6': [576, 864],
165
+ '4X4': [576, 576],
166
+ '4X2': [576, 288],
167
+ '2.25X1.25': [324, 180],
168
+ A4: [992, 1406],
169
+ A5: [701, 992],
170
+ LETTER: [1050, 1500],
171
+ CUSTOM: [576, 0],
172
+ };
173
+
174
+ const [width, height] = sizes[size] || [576, 0];
175
+ this.pageWidth = width;
176
+ this.pageHeight = height;
177
+ return this;
178
+ }
179
+
180
+ /**
181
+ * Start a new page
182
+ */
183
+ pageStart(): this {
184
+ this.commands.push(`! U1 SETLP 5 0 30`);
185
+ this.commands.push(`! U1 PAGE WIDTH ${this.pageWidth}`);
186
+ if (this.pageHeight > 0) {
187
+ this.commands.push(`! U1 PAGE HEIGHT ${this.pageHeight}`);
188
+ }
189
+ this.commands.push('START');
190
+ return this;
191
+ }
192
+
193
+ /**
194
+ * End current page
195
+ */
196
+ pageEnd(): this {
197
+ this.commands.push('END');
198
+ return this;
199
+ }
200
+
201
+ /**
202
+ * Form feed to next label
203
+ */
204
+ formFeed(): this {
205
+ this.commands.push('FORM');
206
+ return this;
207
+ }
208
+
209
+ /**
210
+ * Set line print mode
211
+ * @param font - Font number (0-6)
212
+ * @param xMulti - Horizontal multiplier
213
+ * @param yMulti - Vertical multiplier
214
+ */
215
+ setLinePrint(font = 5, xMulti = 0, yMulti = 30): this {
216
+ this.commands.push(`! U1 SETLP ${font} ${xMulti} ${yMulti}`);
217
+ return this;
218
+ }
219
+
220
+ /**
221
+ * Set text font
222
+ * @param font - Font: 0=OCR, 1=OCR-B, 2=CG Triumvirate Bold, 3-6=Various sizes
223
+ * @param xMulti - Horizontal multiplier (1-8)
224
+ * @param yMulti - Vertical multiplier (1-8)
225
+ * @param rotation - Rotation angle
226
+ */
227
+ setFont(font = 3, xMulti = 1, yMulti = 1, rotation: 0 | 90 | 180 | 270 = 0): this {
228
+ const rotationMap: Record<number, string> = { 0: 'N', 90: 'R', 180: 'B', 270: 'I' };
229
+ const rot = rotationMap[rotation] || 'N';
230
+ this.commands.push(`! U1 SETLP ${font} ${xMulti} ${yMulti} ${rot}`);
231
+ return this;
232
+ }
233
+
234
+ /**
235
+ * Add text at current position
236
+ * @param content - Text content
237
+ */
238
+ text(content: string): this {
239
+ this.commands.push(`! U1 TEXT ${this.escapeText(content)}`);
240
+ return this;
241
+ }
242
+
243
+ /**
244
+ * Add positioned text
245
+ * @param content - Text content
246
+ * @param options - Text options
247
+ */
248
+ textAt(content: string, options: CpclTextOptions): this {
249
+ const { x = 0, y = 0, font = 3, xMulti = 1, yMulti = 1, rotation = 0 } = options;
250
+ const rotationMap: Record<number, string> = { 0: 'N', 90: 'R', 180: 'B', 270: 'I' };
251
+ const rot = rotationMap[rotation] || 'N';
252
+
253
+ this.commands.push(`! U1 SETLP ${font} ${xMulti} ${yMulti} ${rot}`);
254
+ this.commands.push(`! U1 ${x} ${y} TEXT ${this.escapeText(content)}`);
255
+ return this;
256
+ }
257
+
258
+ /**
259
+ * Add text using legacy command
260
+ * @param content - Text content
261
+ * @param x - X position
262
+ * @param y - Y position
263
+ * @param font - Font number
264
+ */
265
+ legacyText(content: string, x: number, y: number, font = 3): this {
266
+ this.commands.push(`TEXT ${x} ${y} ${font} "${this.escapeText(content)}"`);
267
+ return this;
268
+ }
269
+
270
+ /**
271
+ * Add barcode at position
272
+ * @param content - Barcode content
273
+ * @param options - Barcode options
274
+ */
275
+ barcode(content: string, options: CpclBarcodeOptions): this {
276
+ const {
277
+ x = 0,
278
+ y = 0,
279
+ type = '128',
280
+ height = 50,
281
+ wide = 2,
282
+ narrow = 1,
283
+ readable = true,
284
+ } = options;
285
+
286
+ const readableFlag = readable ? 'B' : 'N';
287
+
288
+ // CPCL barcode commands
289
+ switch (type) {
290
+ case '128':
291
+ this.commands.push(
292
+ `BARCODE 128 ${x} ${y} ${height} ${readableFlag} 0 ${narrow} ${wide} "${content}"`
293
+ );
294
+ break;
295
+ case '39':
296
+ this.commands.push(
297
+ `BARCODE 39 ${x} ${y} ${height} ${readableFlag} ${wide} ${narrow} "${content}"`
298
+ );
299
+ break;
300
+ case 'EAN13':
301
+ this.commands.push(`BARCODE EAN13 ${x} ${y} ${height} ${readableFlag} "${content}"`);
302
+ break;
303
+ case 'EAN8':
304
+ this.commands.push(`BARCODE EAN8 ${x} ${y} ${height} ${readableFlag} "${content}"`);
305
+ break;
306
+ case 'UPCA':
307
+ this.commands.push(`BARCODE UPCA ${x} ${y} ${height} ${readableFlag} "${content}"`);
308
+ break;
309
+ case 'UPCE':
310
+ this.commands.push(`BARCODE UPCE ${x} ${y} ${height} ${readableFlag} "${content}"`);
311
+ break;
312
+ case 'PDF417':
313
+ this.commands.push(`BARCODE PDF417 ${x} ${y} 6 200 "${content}"`);
314
+ break;
315
+ case 'DATAMATRIX':
316
+ this.commands.push(`BARCODE DATAMATRIX ${x} ${y} 200 "${content}"`);
317
+ break;
318
+ case 'QR':
319
+ // QR uses separate command
320
+ this.qrcode(content, { x, y });
321
+ break;
322
+ default:
323
+ this.commands.push(
324
+ `BARCODE 128 ${x} ${y} ${height} ${readableFlag} 0 ${narrow} ${wide} "${content}"`
325
+ );
326
+ }
327
+
328
+ return this;
329
+ }
330
+
331
+ /**
332
+ * Add Code 128 barcode (most common)
333
+ * @param content - Barcode content
334
+ * @param x - X position
335
+ * @param y - Y position
336
+ * @param height - Barcode height
337
+ */
338
+ code128(content: string, x = 0, y = 0, height = 50): this {
339
+ return this.barcode(content, { x, y, type: '128', height });
340
+ }
341
+
342
+ /**
343
+ * Add Code 39 barcode
344
+ * @param content - Barcode content
345
+ * @param x - X position
346
+ * @param y - Y position
347
+ * @param height - Barcode height
348
+ */
349
+ code39(content: string, x = 0, y = 0, height = 50): this {
350
+ return this.barcode(content, { x, y, type: '39', height, wide: 2, narrow: 1 });
351
+ }
352
+
353
+ /**
354
+ * Add QR code
355
+ * @param content - QR code content
356
+ * @param options - QR options
357
+ */
358
+ qrcode(content: string, options?: CpclQRCodeOptions): this {
359
+ const { x = 0, y = 0, model = 2, errorCorrection = 'M', cellSize = 4 } = options || {};
360
+ this.commands.push(
361
+ `BARCODE QR ${x} ${y} ${model} ${cellSize} A ${errorCorrection} "${content}"`
362
+ );
363
+ return this;
364
+ }
365
+
366
+ /**
367
+ * Add 2D barcode (PDF417 or DataMatrix)
368
+ * @param content - Content
369
+ * @param type - Type: PDF417 or DATAMATRIX
370
+ * @param x - X position
371
+ * @param y - Y position
372
+ * @param height - Height
373
+ */
374
+ twoDBarcode(content: string, type: 'PDF417' | 'DATAMATRIX', x = 0, y = 0, height = 200): this {
375
+ if (type === 'PDF417') {
376
+ this.commands.push(`BARCODE PDF417 ${x} ${y} 6 ${height} "${content}"`);
377
+ } else {
378
+ this.commands.push(`BARCODE DATAMATRIX ${x} ${y} ${height} "${content}"`);
379
+ }
380
+ return this;
381
+ }
382
+
383
+ /**
384
+ * Add line
385
+ * @param options - Line options
386
+ */
387
+ line(options: CpclLineOptions): this {
388
+ const { x1, y1, x2, y2, width = 1 } = options;
389
+ this.commands.push(`LINE ${x1} ${y1} ${x2} ${y2} ${width}`);
390
+ return this;
391
+ }
392
+
393
+ /**
394
+ * Add box/rectangle
395
+ * @param options - Box options
396
+ */
397
+ box(options: CpclBoxOptions): this {
398
+ const { x, y, width, height, thickness = 1 } = options;
399
+ this.commands.push(`BOX ${x} ${y} ${width} ${height} ${thickness}`);
400
+ return this;
401
+ }
402
+
403
+ /**
404
+ * Add inverse area (white on black)
405
+ * @param x - X position
406
+ * @param y - Y position
407
+ * @param width - Width
408
+ * @param height - Height
409
+ */
410
+ inverse(x: number, y: number, width: number, height: number): this {
411
+ this.commands.push(`INVERSE ${width} ${height} ${x} ${y}`);
412
+ return this;
413
+ }
414
+
415
+ /**
416
+ * Set print density
417
+ * @param density - Density value (-50 to +50)
418
+ */
419
+ setDensity(density: number): this {
420
+ const safeDensity = Math.min(50, Math.max(-50, density));
421
+ this.commands.push(`! U1 SETDENSITY ${safeDensity}`);
422
+ return this;
423
+ }
424
+
425
+ /**
426
+ * Set speed
427
+ * @param speed - Speed: 2-6 (2=50%, 3=75%, 4=100%, 5=125%, 6=150%)
428
+ */
429
+ setSpeed(speed: number): this {
430
+ const safeSpeed = Math.min(6, Math.max(2, speed));
431
+ this.commands.push(`! U1 SETSPEED ${safeSpeed}`);
432
+ return this;
433
+ }
434
+
435
+ /**
436
+ * Cut paper (if cutter installed)
437
+ */
438
+ cut(): this {
439
+ this.commands.push('CUT');
440
+ return this;
441
+ }
442
+
443
+ /**
444
+ * Partial cut
445
+ */
446
+ partialCut(): this {
447
+ this.commands.push('PCUT');
448
+ return this;
449
+ }
450
+
451
+ /**
452
+ * Feed and cut
453
+ */
454
+ feedCut(): this {
455
+ this.commands.push('FEED CUT');
456
+ return this;
457
+ }
458
+
459
+ /**
460
+ * Sound beep
461
+ * @param count - Number of beeps
462
+ * @param duration - Duration in 10ms units
463
+ */
464
+ beep(count = 1, duration = 5): this {
465
+ this.commands.push(`BEEP ${count} ${duration}`);
466
+ return this;
467
+ }
468
+
469
+ /**
470
+ * Add logo/image
471
+ * @param x - X position
472
+ * @param y - Y position
473
+ * @param logoName - Stored logo name
474
+ */
475
+ logo(x: number, y: number, logoName: string): this {
476
+ this.commands.push(`LOGO ${x} ${y} "${logoName}"`);
477
+ return this;
478
+ }
479
+
480
+ /**
481
+ * Download logo to printer (store in memory)
482
+ * Note: This is a placeholder for future implementation
483
+ * @param logoName - Name to store logo as
484
+ * @param _bitmap - Logo bitmap data (placeholder)
485
+ */
486
+ downloadLogo(logoName: string, _bitmap: Uint8Array): this {
487
+ this.commands.push(`! DF ${logoName}`);
488
+ // TODO: Encode bitmap to CPCL format
489
+ this.logger.debug('CPCL logo download not fully implemented');
490
+ this.commands.push(`! DF`);
491
+ return this;
492
+ }
493
+
494
+ /**
495
+ * Print stored logo
496
+ * @param logoName - Logo name
497
+ * @param x - X position
498
+ * @param y - Y position
499
+ */
500
+ printLogo(logoName: string, x = 0, y = 0): this {
501
+ this.commands.push(`LOGO ${x} ${y} "${logoName}"`);
502
+ return this;
503
+ }
504
+
505
+ /**
506
+ * Get all commands as string
507
+ */
508
+ getCommands(): string {
509
+ return this.commands.join('\r\n') + '\r\n';
510
+ }
511
+
512
+ /**
513
+ * Get commands as buffer for sending to printer
514
+ */
515
+ getBuffer(): Uint8Array {
516
+ const commandString = this.getCommands();
517
+ this.logger.debug(`CPCL commands:\n${commandString}`);
518
+ return Encoding.encode(commandString, 'ASCII');
519
+ }
520
+
521
+ /**
522
+ * Get raw command list
523
+ */
524
+ getCommandList(): string[] {
525
+ return [...this.commands];
526
+ }
527
+
528
+ /**
529
+ * Clear all commands
530
+ */
531
+ reset(): this {
532
+ this.commands = [];
533
+ return this;
534
+ }
535
+
536
+ /**
537
+ * Get page width
538
+ */
539
+ getPageWidth(): number {
540
+ return this.pageWidth;
541
+ }
542
+
543
+ /**
544
+ * Escape special characters in text
545
+ */
546
+ private escapeText(str: string): string {
547
+ return str.replace(/"/g, '""');
548
+ }
549
+ }