@cj-tech-master/excelts 9.4.1 → 9.4.2
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/dist/browser/modules/pdf/builder/document-builder.js +4 -23
- package/dist/browser/modules/pdf/core/pdf-stream.d.ts +15 -0
- package/dist/browser/modules/pdf/core/pdf-stream.js +47 -3
- package/dist/browser/modules/pdf/font/font-manager.d.ts +37 -6
- package/dist/browser/modules/pdf/font/font-manager.js +129 -17
- package/dist/browser/modules/pdf/font/system-fonts.d.ts +41 -0
- package/dist/browser/modules/pdf/font/system-fonts.js +188 -0
- package/dist/browser/modules/pdf/font/ttf-parser.js +29 -1
- package/dist/browser/modules/pdf/font/type3-font.d.ts +35 -0
- package/dist/browser/modules/pdf/font/type3-font.js +228 -0
- package/dist/browser/modules/pdf/font/type3-glyphs-extended.d.ts +33 -0
- package/dist/browser/modules/pdf/font/type3-glyphs-extended.js +4164 -0
- package/dist/browser/modules/pdf/font/type3-glyphs-extended2.d.ts +16 -0
- package/dist/browser/modules/pdf/font/type3-glyphs-extended2.js +9649 -0
- package/dist/browser/modules/pdf/font/type3-glyphs-fill.d.ts +17 -0
- package/dist/browser/modules/pdf/font/type3-glyphs-fill.js +5438 -0
- package/dist/browser/modules/pdf/font/type3-glyphs-quality.d.ts +28 -0
- package/dist/browser/modules/pdf/font/type3-glyphs-quality.js +5345 -0
- package/dist/browser/modules/pdf/font/type3-glyphs.d.ts +79 -0
- package/dist/browser/modules/pdf/font/type3-glyphs.js +2567 -0
- package/dist/browser/modules/pdf/render/layout-engine.js +36 -23
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +9 -0
- package/dist/browser/modules/pdf/render/page-renderer.js +110 -78
- package/dist/browser/modules/pdf/render/pdf-exporter.js +73 -5
- package/dist/cjs/modules/pdf/builder/document-builder.js +3 -22
- package/dist/cjs/modules/pdf/core/pdf-stream.js +49 -3
- package/dist/cjs/modules/pdf/font/font-manager.js +129 -17
- package/dist/cjs/modules/pdf/font/system-fonts.js +194 -0
- package/dist/cjs/modules/pdf/font/ttf-parser.js +29 -1
- package/dist/cjs/modules/pdf/font/type3-font.js +231 -0
- package/dist/cjs/modules/pdf/font/type3-glyphs-extended.js +4167 -0
- package/dist/cjs/modules/pdf/font/type3-glyphs-extended2.js +9652 -0
- package/dist/cjs/modules/pdf/font/type3-glyphs-fill.js +5441 -0
- package/dist/cjs/modules/pdf/font/type3-glyphs-quality.js +5348 -0
- package/dist/cjs/modules/pdf/font/type3-glyphs.js +2573 -0
- package/dist/cjs/modules/pdf/render/layout-engine.js +36 -23
- package/dist/cjs/modules/pdf/render/page-renderer.js +111 -78
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +71 -3
- package/dist/esm/modules/pdf/builder/document-builder.js +4 -23
- package/dist/esm/modules/pdf/core/pdf-stream.js +47 -3
- package/dist/esm/modules/pdf/font/font-manager.js +129 -17
- package/dist/esm/modules/pdf/font/system-fonts.js +188 -0
- package/dist/esm/modules/pdf/font/ttf-parser.js +29 -1
- package/dist/esm/modules/pdf/font/type3-font.js +228 -0
- package/dist/esm/modules/pdf/font/type3-glyphs-extended.js +4164 -0
- package/dist/esm/modules/pdf/font/type3-glyphs-extended2.js +9649 -0
- package/dist/esm/modules/pdf/font/type3-glyphs-fill.js +5438 -0
- package/dist/esm/modules/pdf/font/type3-glyphs-quality.js +5345 -0
- package/dist/esm/modules/pdf/font/type3-glyphs.js +2567 -0
- package/dist/esm/modules/pdf/render/layout-engine.js +36 -23
- package/dist/esm/modules/pdf/render/page-renderer.js +110 -78
- package/dist/esm/modules/pdf/render/pdf-exporter.js +73 -5
- package/dist/iife/excelts.iife.js +25445 -344
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +48 -46
- package/dist/types/modules/pdf/core/pdf-stream.d.ts +15 -0
- package/dist/types/modules/pdf/font/font-manager.d.ts +37 -6
- package/dist/types/modules/pdf/font/system-fonts.d.ts +41 -0
- package/dist/types/modules/pdf/font/type3-font.d.ts +35 -0
- package/dist/types/modules/pdf/font/type3-glyphs-extended.d.ts +33 -0
- package/dist/types/modules/pdf/font/type3-glyphs-extended2.d.ts +16 -0
- package/dist/types/modules/pdf/font/type3-glyphs-fill.d.ts +17 -0
- package/dist/types/modules/pdf/font/type3-glyphs-quality.d.ts +28 -0
- package/dist/types/modules/pdf/font/type3-glyphs.d.ts +79 -0
- package/dist/types/modules/pdf/render/page-renderer.d.ts +9 -0
- package/package.json +1 -1
|
@@ -25,7 +25,7 @@ import { PdfWriter } from "../core/pdf-writer.js";
|
|
|
25
25
|
import { writePdfAMetadata, writePdfAOutputIntent } from "../core/pdfa.js";
|
|
26
26
|
import { FontManager } from "../font/font-manager.js";
|
|
27
27
|
import { parseTtf } from "../font/ttf-parser.js";
|
|
28
|
-
import { wrapTextLines } from "../render/page-renderer.js";
|
|
28
|
+
import { wrapTextLines, emitTextWithMatrix } from "../render/page-renderer.js";
|
|
29
29
|
import { writeImageXObject } from "./image-utils.js";
|
|
30
30
|
// =============================================================================
|
|
31
31
|
// Constants
|
|
@@ -87,8 +87,8 @@ export class PdfPageBuilder {
|
|
|
87
87
|
const fontFamily = options.fontFamily ?? "Helvetica";
|
|
88
88
|
// Resolve font
|
|
89
89
|
const resourceName = this._fontManager.resolveFont(fontFamily, bold, italic);
|
|
90
|
-
const encodedText = this._fontManager.encodeText(text, resourceName);
|
|
91
90
|
this._fontManager.trackText(text);
|
|
91
|
+
const useType3 = this._fontManager.hasType3Fonts() && !this._fontManager.hasEmbeddedFont();
|
|
92
92
|
if (options.maxWidth) {
|
|
93
93
|
// Word-wrap (reuses the shared wrapTextLines from page-renderer)
|
|
94
94
|
const measure = (s) => this._fontManager.measureText(s, resourceName, fontSize);
|
|
@@ -96,36 +96,17 @@ export class PdfPageBuilder {
|
|
|
96
96
|
const leading = fontSize * lineHeightFactor;
|
|
97
97
|
this._stream.save();
|
|
98
98
|
this._stream.setFillColor(color);
|
|
99
|
-
this._stream.beginText();
|
|
100
|
-
this._stream.setFont(resourceName, fontSize);
|
|
101
99
|
for (let i = 0; i < lines.length; i++) {
|
|
102
100
|
const lineY = options.y - i * leading;
|
|
103
|
-
this._stream
|
|
104
|
-
const lineEncoded = this._fontManager.encodeText(lines[i], resourceName);
|
|
105
|
-
if (lineEncoded) {
|
|
106
|
-
this._stream.showTextHex(lineEncoded);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
this._stream.showText(lines[i]);
|
|
110
|
-
}
|
|
101
|
+
emitTextWithMatrix(this._stream, lines[i], 1, 0, 0, 1, options.x, lineY, resourceName, fontSize, this._fontManager, useType3);
|
|
111
102
|
}
|
|
112
|
-
this._stream.endText();
|
|
113
103
|
this._stream.restore();
|
|
114
104
|
}
|
|
115
105
|
else {
|
|
116
106
|
// Single line
|
|
117
107
|
this._stream.save();
|
|
118
108
|
this._stream.setFillColor(color);
|
|
119
|
-
this._stream.
|
|
120
|
-
this._stream.setFont(resourceName, fontSize);
|
|
121
|
-
this._stream.setTextMatrix(1, 0, 0, 1, options.x, options.y);
|
|
122
|
-
if (encodedText) {
|
|
123
|
-
this._stream.showTextHex(encodedText);
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
this._stream.showText(text);
|
|
127
|
-
}
|
|
128
|
-
this._stream.endText();
|
|
109
|
+
emitTextWithMatrix(this._stream, text, 1, 0, 0, 1, options.x, options.y, resourceName, fontSize, this._fontManager, useType3);
|
|
129
110
|
this._stream.restore();
|
|
130
111
|
}
|
|
131
112
|
return this;
|
|
@@ -20,6 +20,11 @@ import type { PdfColor } from "../types.js";
|
|
|
20
20
|
*/
|
|
21
21
|
export declare class PdfContentStream {
|
|
22
22
|
private parts;
|
|
23
|
+
/**
|
|
24
|
+
* Append a raw PDF operator string to the content stream.
|
|
25
|
+
* Use this for operators not covered by the typed API (e.g. `d1` for Type3 glyphs).
|
|
26
|
+
*/
|
|
27
|
+
raw(operator: string): this;
|
|
23
28
|
/**
|
|
24
29
|
* Save the current graphics state (push onto state stack).
|
|
25
30
|
* Must be balanced with a corresponding restore().
|
|
@@ -225,3 +230,13 @@ export declare class PdfContentStream {
|
|
|
225
230
|
*/
|
|
226
231
|
toUint8Array(): Uint8Array;
|
|
227
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Check whether a single code point is representable in WinAnsi encoding.
|
|
235
|
+
*/
|
|
236
|
+
export declare function isWinAnsiCodePoint(cp: number): boolean;
|
|
237
|
+
/**
|
|
238
|
+
* Check whether a string contains characters outside the WinAnsi repertoire.
|
|
239
|
+
* When true, standard Type1 fonts cannot render those characters and an
|
|
240
|
+
* embedded TrueType font is required for correct output.
|
|
241
|
+
*/
|
|
242
|
+
export declare function hasNonWinAnsiChars(text: string): boolean;
|
|
@@ -26,6 +26,17 @@ export class PdfContentStream {
|
|
|
26
26
|
this.parts = [];
|
|
27
27
|
}
|
|
28
28
|
// ===========================================================================
|
|
29
|
+
// Raw Operator
|
|
30
|
+
// ===========================================================================
|
|
31
|
+
/**
|
|
32
|
+
* Append a raw PDF operator string to the content stream.
|
|
33
|
+
* Use this for operators not covered by the typed API (e.g. `d1` for Type3 glyphs).
|
|
34
|
+
*/
|
|
35
|
+
raw(operator) {
|
|
36
|
+
this.parts.push(operator);
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
// ===========================================================================
|
|
29
40
|
// Graphics State
|
|
30
41
|
// ===========================================================================
|
|
31
42
|
/**
|
|
@@ -502,7 +513,9 @@ const UNICODE_TO_WINANSI = new Map([
|
|
|
502
513
|
]);
|
|
503
514
|
/**
|
|
504
515
|
* Convert a Unicode code point to a WinAnsi byte value.
|
|
505
|
-
* Returns
|
|
516
|
+
* Returns 0x20 (space) for unmappable characters — standard Type1 fonts
|
|
517
|
+
* do not contain glyphs outside the WinAnsi repertoire, so a space is
|
|
518
|
+
* less misleading than a literal '?'.
|
|
506
519
|
*/
|
|
507
520
|
function unicodeToWinAnsi(cp) {
|
|
508
521
|
// Direct mapping for Latin-1 range (0x00-0xFF), excluding 0x80-0x9F
|
|
@@ -517,6 +530,37 @@ function unicodeToWinAnsi(cp) {
|
|
|
517
530
|
if (mapped !== undefined) {
|
|
518
531
|
return mapped;
|
|
519
532
|
}
|
|
520
|
-
// Unmappable — use '?'
|
|
521
|
-
|
|
533
|
+
// Unmappable — use space instead of '?' to avoid misleading output.
|
|
534
|
+
// Full Unicode support requires an embedded TrueType font (the `font`
|
|
535
|
+
// option in PdfExportOptions).
|
|
536
|
+
return 0x20;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Check whether a single code point is representable in WinAnsi encoding.
|
|
540
|
+
*/
|
|
541
|
+
export function isWinAnsiCodePoint(cp) {
|
|
542
|
+
if (cp < 0x80) {
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
if (cp >= 0xa0 && cp <= 0xff) {
|
|
546
|
+
return true;
|
|
547
|
+
}
|
|
548
|
+
return UNICODE_TO_WINANSI.has(cp);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Check whether a string contains characters outside the WinAnsi repertoire.
|
|
552
|
+
* When true, standard Type1 fonts cannot render those characters and an
|
|
553
|
+
* embedded TrueType font is required for correct output.
|
|
554
|
+
*/
|
|
555
|
+
export function hasNonWinAnsiChars(text) {
|
|
556
|
+
for (let i = 0; i < text.length; i++) {
|
|
557
|
+
const cp = text.codePointAt(i);
|
|
558
|
+
if (cp > 0xffff) {
|
|
559
|
+
i++;
|
|
560
|
+
}
|
|
561
|
+
if (!isWinAnsiCodePoint(cp)) {
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return false;
|
|
522
566
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Font manager for PDF generation.
|
|
3
3
|
*
|
|
4
|
-
* Manages
|
|
4
|
+
* Manages three kinds of fonts:
|
|
5
5
|
* 1. **Standard Type1 fonts** (Helvetica, Times, Courier) — always available,
|
|
6
|
-
* used
|
|
6
|
+
* used for Latin text (WinAnsi repertoire) when no embedded font is provided.
|
|
7
7
|
* 2. **Embedded TrueType fonts** — user-provided .ttf files for full
|
|
8
8
|
* Unicode support (CJK, Arabic, Hindi, etc.)
|
|
9
|
+
* 3. **Type3 fallback fonts** — auto-generated vector-drawn glyphs for
|
|
10
|
+
* Unicode characters outside WinAnsi when no embedded font is provided.
|
|
9
11
|
*
|
|
10
12
|
* When an embedded font is registered, ALL text uses the embedded font.
|
|
11
|
-
* When no embedded font is provided, the system
|
|
12
|
-
*
|
|
13
|
+
* When no embedded font is provided, the system uses Type1 for WinAnsi
|
|
14
|
+
* characters and Type3 for everything else.
|
|
13
15
|
*
|
|
14
16
|
* The manager tracks which Unicode code points are used so the font embedder
|
|
15
|
-
* can create
|
|
17
|
+
* and Type3 builder can create minimal subsets when writing the PDF.
|
|
16
18
|
*/
|
|
17
19
|
import type { PdfWriter } from "../core/pdf-writer.js";
|
|
18
20
|
import { type EmbeddedFont } from "./font-embedder.js";
|
|
@@ -23,7 +25,8 @@ import type { TtfFont } from "./ttf-parser.js";
|
|
|
23
25
|
export declare function resolvePdfFontName(fontFamily: string, bold: boolean, italic: boolean): string;
|
|
24
26
|
/**
|
|
25
27
|
* Manages PDF font resources for a document.
|
|
26
|
-
* Supports
|
|
28
|
+
* Supports standard Type1 fonts, embedded TrueType fonts, and auto-generated
|
|
29
|
+
* Type3 fallback fonts for non-WinAnsi Unicode characters.
|
|
27
30
|
*/
|
|
28
31
|
export declare class FontManager {
|
|
29
32
|
private type1Map;
|
|
@@ -33,6 +36,8 @@ export declare class FontManager {
|
|
|
33
36
|
private embeddedResourceName;
|
|
34
37
|
private usedCodePoints;
|
|
35
38
|
private nextEmbeddedId;
|
|
39
|
+
private type3CodePoints;
|
|
40
|
+
private _type3Result;
|
|
36
41
|
/**
|
|
37
42
|
* Register an embedded TrueType font for use.
|
|
38
43
|
* When set, all text rendering uses this font instead of standard fonts.
|
|
@@ -65,8 +70,25 @@ export declare class FontManager {
|
|
|
65
70
|
* Get the PDF font name for a given resource name.
|
|
66
71
|
*/
|
|
67
72
|
getPdfFontName(resourceName: string): string;
|
|
73
|
+
/**
|
|
74
|
+
* Check if Type3 fallback fonts are available (after writeFontResources).
|
|
75
|
+
*/
|
|
76
|
+
hasType3Fonts(): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Resolve the Type3 font resource name and char code for a code point.
|
|
79
|
+
* Returns null if the code point is not in the Type3 encoding.
|
|
80
|
+
*/
|
|
81
|
+
resolveType3(codePoint: number): {
|
|
82
|
+
resourceName: string;
|
|
83
|
+
charCode: number;
|
|
84
|
+
} | null;
|
|
85
|
+
/**
|
|
86
|
+
* Check if a code point needs Type3 rendering (non-WinAnsi, no embedded font).
|
|
87
|
+
*/
|
|
88
|
+
needsType3(codePoint: number): boolean;
|
|
68
89
|
/**
|
|
69
90
|
* Measure text width using the correct font metrics.
|
|
91
|
+
* For mixed Type1/Type3 text, measures each character with the right font.
|
|
70
92
|
*/
|
|
71
93
|
measureText(text: string, resourceName: string, fontSize: number): number;
|
|
72
94
|
/**
|
|
@@ -85,6 +107,10 @@ export declare class FontManager {
|
|
|
85
107
|
* Check if a resource name refers to an embedded font.
|
|
86
108
|
*/
|
|
87
109
|
isEmbeddedFont(resourceName: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Check if a resource name refers to a Type3 fallback font.
|
|
112
|
+
*/
|
|
113
|
+
isType3Resource(resourceName: string): boolean;
|
|
88
114
|
/**
|
|
89
115
|
* Encode text for the given font resource.
|
|
90
116
|
* For embedded fonts, returns a hex string `<0012003A...>`.
|
|
@@ -94,6 +120,11 @@ export declare class FontManager {
|
|
|
94
120
|
* subset and produces the unicodeToCid mapping.
|
|
95
121
|
*/
|
|
96
122
|
encodeText(text: string, resourceName: string): string | null;
|
|
123
|
+
/**
|
|
124
|
+
* Encode a single character for a Type3 font.
|
|
125
|
+
* Returns a hex string `<XX>` suitable for the Tj operator.
|
|
126
|
+
*/
|
|
127
|
+
encodeType3Char(codePoint: number): string | null;
|
|
97
128
|
/**
|
|
98
129
|
* Write all font resource objects to the PDF.
|
|
99
130
|
* Returns a map from resource name → object number.
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Font manager for PDF generation.
|
|
3
3
|
*
|
|
4
|
-
* Manages
|
|
4
|
+
* Manages three kinds of fonts:
|
|
5
5
|
* 1. **Standard Type1 fonts** (Helvetica, Times, Courier) — always available,
|
|
6
|
-
* used
|
|
6
|
+
* used for Latin text (WinAnsi repertoire) when no embedded font is provided.
|
|
7
7
|
* 2. **Embedded TrueType fonts** — user-provided .ttf files for full
|
|
8
8
|
* Unicode support (CJK, Arabic, Hindi, etc.)
|
|
9
|
+
* 3. **Type3 fallback fonts** — auto-generated vector-drawn glyphs for
|
|
10
|
+
* Unicode characters outside WinAnsi when no embedded font is provided.
|
|
9
11
|
*
|
|
10
12
|
* When an embedded font is registered, ALL text uses the embedded font.
|
|
11
|
-
* When no embedded font is provided, the system
|
|
12
|
-
*
|
|
13
|
+
* When no embedded font is provided, the system uses Type1 for WinAnsi
|
|
14
|
+
* characters and Type3 for everything else.
|
|
13
15
|
*
|
|
14
16
|
* The manager tracks which Unicode code points are used so the font embedder
|
|
15
|
-
* can create
|
|
17
|
+
* and Type3 builder can create minimal subsets when writing the PDF.
|
|
16
18
|
*/
|
|
17
19
|
import { PdfDict, pdfName, pdfRef } from "../core/pdf-object.js";
|
|
20
|
+
import { hasNonWinAnsiChars, isWinAnsiCodePoint } from "../core/pdf-stream.js";
|
|
18
21
|
import { PdfFontError } from "../errors.js";
|
|
19
22
|
import { embedTtfFont } from "./font-embedder.js";
|
|
20
23
|
import { measureText as measureType1Text, getFontAscent as getType1Ascent, getFontDescent as getType1Descent, getLineHeight as getType1LineHeight } from "./metrics.js";
|
|
24
|
+
import { writeType3Fonts } from "./type3-font.js";
|
|
21
25
|
// =============================================================================
|
|
22
26
|
// Font Name Mapping (Type1 fallback)
|
|
23
27
|
// =============================================================================
|
|
@@ -104,7 +108,8 @@ export function resolvePdfFontName(fontFamily, bold, italic) {
|
|
|
104
108
|
// =============================================================================
|
|
105
109
|
/**
|
|
106
110
|
* Manages PDF font resources for a document.
|
|
107
|
-
* Supports
|
|
111
|
+
* Supports standard Type1 fonts, embedded TrueType fonts, and auto-generated
|
|
112
|
+
* Type3 fallback fonts for non-WinAnsi Unicode characters.
|
|
108
113
|
*/
|
|
109
114
|
export class FontManager {
|
|
110
115
|
constructor() {
|
|
@@ -117,6 +122,9 @@ export class FontManager {
|
|
|
117
122
|
this.embeddedResourceName = "";
|
|
118
123
|
this.usedCodePoints = new Set();
|
|
119
124
|
this.nextEmbeddedId = 1;
|
|
125
|
+
// --- Type3 fallback font tracking ---
|
|
126
|
+
this.type3CodePoints = new Set();
|
|
127
|
+
this._type3Result = null;
|
|
120
128
|
/** Stored after writeFontResources is called */
|
|
121
129
|
this._embeddedResult = null;
|
|
122
130
|
}
|
|
@@ -149,14 +157,25 @@ export class FontManager {
|
|
|
149
157
|
* Must be called for every text string before writing the PDF.
|
|
150
158
|
*/
|
|
151
159
|
trackText(text) {
|
|
152
|
-
if (
|
|
153
|
-
|
|
160
|
+
if (this.embeddedFont) {
|
|
161
|
+
for (let i = 0; i < text.length; i++) {
|
|
162
|
+
const cp = text.codePointAt(i);
|
|
163
|
+
this.usedCodePoints.add(cp);
|
|
164
|
+
if (cp > 0xffff) {
|
|
165
|
+
i++; // skip low surrogate
|
|
166
|
+
}
|
|
167
|
+
}
|
|
154
168
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
169
|
+
else {
|
|
170
|
+
// No embedded font — track non-WinAnsi chars for Type3 fallback
|
|
171
|
+
for (let i = 0; i < text.length; i++) {
|
|
172
|
+
const cp = text.codePointAt(i);
|
|
173
|
+
if (cp > 0xffff) {
|
|
174
|
+
i++;
|
|
175
|
+
}
|
|
176
|
+
if (!isWinAnsiCodePoint(cp)) {
|
|
177
|
+
this.type3CodePoints.add(cp);
|
|
178
|
+
}
|
|
160
179
|
}
|
|
161
180
|
}
|
|
162
181
|
}
|
|
@@ -194,17 +213,72 @@ export class FontManager {
|
|
|
194
213
|
return this.resourceToType1.get(resourceName) ?? "Helvetica";
|
|
195
214
|
}
|
|
196
215
|
// ==========================================================================
|
|
216
|
+
// Type3 Fallback Font
|
|
217
|
+
// ==========================================================================
|
|
218
|
+
/**
|
|
219
|
+
* Check if Type3 fallback fonts are available (after writeFontResources).
|
|
220
|
+
*/
|
|
221
|
+
hasType3Fonts() {
|
|
222
|
+
return this._type3Result !== null && this._type3Result.fontObjects.size > 0;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Resolve the Type3 font resource name and char code for a code point.
|
|
226
|
+
* Returns null if the code point is not in the Type3 encoding.
|
|
227
|
+
*/
|
|
228
|
+
resolveType3(codePoint) {
|
|
229
|
+
if (!this._type3Result) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
return this._type3Result.encoding.get(codePoint) ?? null;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Check if a code point needs Type3 rendering (non-WinAnsi, no embedded font).
|
|
236
|
+
*/
|
|
237
|
+
needsType3(codePoint) {
|
|
238
|
+
return !this.embeddedFont && !isWinAnsiCodePoint(codePoint);
|
|
239
|
+
}
|
|
240
|
+
// ==========================================================================
|
|
197
241
|
// Text Measurement
|
|
198
242
|
// ==========================================================================
|
|
199
243
|
/**
|
|
200
244
|
* Measure text width using the correct font metrics.
|
|
245
|
+
* For mixed Type1/Type3 text, measures each character with the right font.
|
|
201
246
|
*/
|
|
202
247
|
measureText(text, resourceName, fontSize) {
|
|
203
248
|
if (this.embeddedFont && resourceName === this.embeddedResourceName) {
|
|
204
249
|
return measureEmbeddedText(text, this.embeddedFont, fontSize);
|
|
205
250
|
}
|
|
251
|
+
// If no Type3 fonts or text has no non-WinAnsi chars, use Type1 directly
|
|
252
|
+
if (!this._type3Result || !hasNonWinAnsiChars(text)) {
|
|
253
|
+
const pdfFontName = this.getPdfFontName(resourceName);
|
|
254
|
+
return measureType1Text(text, pdfFontName, fontSize);
|
|
255
|
+
}
|
|
256
|
+
// Mixed text: measure char by char
|
|
257
|
+
let totalWidth = 0;
|
|
206
258
|
const pdfFontName = this.getPdfFontName(resourceName);
|
|
207
|
-
|
|
259
|
+
for (let i = 0; i < text.length; i++) {
|
|
260
|
+
const cp = text.codePointAt(i);
|
|
261
|
+
if (cp > 0xffff) {
|
|
262
|
+
i++;
|
|
263
|
+
}
|
|
264
|
+
if (isWinAnsiCodePoint(cp)) {
|
|
265
|
+
totalWidth += measureType1Text(String.fromCodePoint(cp), pdfFontName, fontSize);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
// Type3 character width
|
|
269
|
+
const t3 = this._type3Result.encoding.get(cp);
|
|
270
|
+
if (t3) {
|
|
271
|
+
const widthMap = this._type3Result.widths.get(t3.resourceName);
|
|
272
|
+
const glyphWidth = widthMap?.get(t3.charCode) ?? 600;
|
|
273
|
+
totalWidth += (glyphWidth / 1000) * fontSize;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
// Notdef width
|
|
277
|
+
totalWidth += (600 / 1000) * fontSize;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return totalWidth;
|
|
208
282
|
}
|
|
209
283
|
/**
|
|
210
284
|
* Get the font ascent in points.
|
|
@@ -213,7 +287,11 @@ export class FontManager {
|
|
|
213
287
|
if (this.embeddedFont && resourceName === this.embeddedResourceName) {
|
|
214
288
|
return (this.embeddedFont.ascent / this.embeddedFont.unitsPerEm) * fontSize;
|
|
215
289
|
}
|
|
216
|
-
|
|
290
|
+
// Type3 fonts use the same metrics as the base Type1 font
|
|
291
|
+
const base = this.isType3Resource(resourceName)
|
|
292
|
+
? "Helvetica"
|
|
293
|
+
: this.getPdfFontName(resourceName);
|
|
294
|
+
return getType1Ascent(base, fontSize);
|
|
217
295
|
}
|
|
218
296
|
/**
|
|
219
297
|
* Get the font descent in points (negative value).
|
|
@@ -222,7 +300,10 @@ export class FontManager {
|
|
|
222
300
|
if (this.embeddedFont && resourceName === this.embeddedResourceName) {
|
|
223
301
|
return (this.embeddedFont.descent / this.embeddedFont.unitsPerEm) * fontSize;
|
|
224
302
|
}
|
|
225
|
-
|
|
303
|
+
const base = this.isType3Resource(resourceName)
|
|
304
|
+
? "Helvetica"
|
|
305
|
+
: this.getPdfFontName(resourceName);
|
|
306
|
+
return getType1Descent(base, fontSize);
|
|
226
307
|
}
|
|
227
308
|
/**
|
|
228
309
|
* Get the line height in points.
|
|
@@ -232,7 +313,10 @@ export class FontManager {
|
|
|
232
313
|
const f = this.embeddedFont;
|
|
233
314
|
return ((f.ascent - f.descent) / f.unitsPerEm) * fontSize;
|
|
234
315
|
}
|
|
235
|
-
|
|
316
|
+
const base = this.isType3Resource(resourceName)
|
|
317
|
+
? "Helvetica"
|
|
318
|
+
: this.getPdfFontName(resourceName);
|
|
319
|
+
return getType1LineHeight(base, fontSize);
|
|
236
320
|
}
|
|
237
321
|
// ==========================================================================
|
|
238
322
|
// Text Encoding
|
|
@@ -243,6 +327,12 @@ export class FontManager {
|
|
|
243
327
|
isEmbeddedFont(resourceName) {
|
|
244
328
|
return this.embeddedFont !== null && resourceName === this.embeddedResourceName;
|
|
245
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Check if a resource name refers to a Type3 fallback font.
|
|
332
|
+
*/
|
|
333
|
+
isType3Resource(resourceName) {
|
|
334
|
+
return this._type3Result?.fontObjects.has(resourceName) ?? false;
|
|
335
|
+
}
|
|
246
336
|
/**
|
|
247
337
|
* Encode text for the given font resource.
|
|
248
338
|
* For embedded fonts, returns a hex string `<0012003A...>`.
|
|
@@ -263,6 +353,21 @@ export class FontManager {
|
|
|
263
353
|
// writeFontResources not called yet — this is a programming error
|
|
264
354
|
throw new PdfFontError("encodeText called before writeFontResources — subset mapping not available");
|
|
265
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Encode a single character for a Type3 font.
|
|
358
|
+
* Returns a hex string `<XX>` suitable for the Tj operator.
|
|
359
|
+
*/
|
|
360
|
+
encodeType3Char(codePoint) {
|
|
361
|
+
if (!this._type3Result) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
const entry = this._type3Result.encoding.get(codePoint);
|
|
365
|
+
if (!entry) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
const hex = entry.charCode.toString(16).toUpperCase().padStart(2, "0");
|
|
369
|
+
return `<${hex}>`;
|
|
370
|
+
}
|
|
266
371
|
// ==========================================================================
|
|
267
372
|
// PDF Object Writing
|
|
268
373
|
// ==========================================================================
|
|
@@ -290,6 +395,13 @@ export class FontManager {
|
|
|
290
395
|
// Store the embedding result for text re-encoding
|
|
291
396
|
this._embeddedResult = embedded;
|
|
292
397
|
}
|
|
398
|
+
// Write Type3 fallback fonts (only when no embedded font)
|
|
399
|
+
if (!this.embeddedFont && this.type3CodePoints.size > 0) {
|
|
400
|
+
this._type3Result = writeType3Fonts(writer, this.type3CodePoints);
|
|
401
|
+
for (const [resourceName, objNum] of this._type3Result.fontObjects) {
|
|
402
|
+
fontObjectMap.set(resourceName, objNum);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
293
405
|
return fontObjectMap;
|
|
294
406
|
}
|
|
295
407
|
/**
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System font discovery for PDF generation.
|
|
3
|
+
*
|
|
4
|
+
* When no embedded font is provided and the document contains non-WinAnsi
|
|
5
|
+
* characters, this module searches standard system font directories for a
|
|
6
|
+
* TrueType font (.ttf or .ttc) with broad Unicode coverage.
|
|
7
|
+
*
|
|
8
|
+
* This is a Node.js-only feature — browser environments do not have
|
|
9
|
+
* file system access and must always provide fonts explicitly.
|
|
10
|
+
*
|
|
11
|
+
* .ttc (TrueType Collection) files are supported — parseTtf() extracts
|
|
12
|
+
* the first font from the collection automatically.
|
|
13
|
+
*
|
|
14
|
+
* Results are cached: the filesystem search runs only once per process.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Return all discoverable system font candidates, ordered by preference.
|
|
18
|
+
*
|
|
19
|
+
* Each entry is the raw font file bytes of a `.ttf` or `.ttc` file.
|
|
20
|
+
* The caller decides which candidate to use (e.g. by checking cmap coverage).
|
|
21
|
+
*
|
|
22
|
+
* Results are cached — the filesystem scan runs only once per process.
|
|
23
|
+
*/
|
|
24
|
+
export declare function discoverSystemFontCandidates(): Uint8Array[];
|
|
25
|
+
/**
|
|
26
|
+
* Search for a system font suitable for Unicode rendering.
|
|
27
|
+
*
|
|
28
|
+
* Returns the raw font file bytes of the highest-priority candidate,
|
|
29
|
+
* or `null` if no font was found. This is a convenience wrapper around
|
|
30
|
+
* {@link discoverSystemFontCandidates}.
|
|
31
|
+
*/
|
|
32
|
+
export declare function discoverSystemFont(): Uint8Array | null;
|
|
33
|
+
/**
|
|
34
|
+
* Reset the cached font discovery result (for testing).
|
|
35
|
+
*/
|
|
36
|
+
export declare function resetFontDiscoveryCache(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Override the cached candidates with a custom list (for testing).
|
|
39
|
+
* Call {@link resetFontDiscoveryCache} to clear the override.
|
|
40
|
+
*/
|
|
41
|
+
export declare function _setCandidatesForTest(candidates: Uint8Array[]): void;
|