-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDemos.ts
More file actions
142 lines (125 loc) · 5.1 KB
/
Copy pathDemos.ts
File metadata and controls
142 lines (125 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import {
connectDsPdf,
disconnectDsPdf,
DsPdf,
DsPdfConfig,
Font,
PdfDocument,
StandardPdfFont,
withObjectManager,
} from "@mescius/ds-pdf";
// The 14 standard PDF fonts, offered as replacement-font choices. The value is
// the StandardPdfFont enum member passed to Font.getPdfFont().
export const STANDARD_FONTS: ReadonlyArray<{ label: string; value: StandardPdfFont }> = [
{ label: "Helvetica", value: StandardPdfFont.Helvetica },
{ label: "Helvetica Bold", value: StandardPdfFont.HelveticaBold },
{ label: "Helvetica Italic", value: StandardPdfFont.HelveticaItalic },
{ label: "Helvetica Bold Italic", value: StandardPdfFont.HelveticaBoldItalic },
{ label: "Times", value: StandardPdfFont.Times },
{ label: "Times Bold", value: StandardPdfFont.TimesBold },
{ label: "Times Italic", value: StandardPdfFont.TimesItalic },
{ label: "Times Bold Italic", value: StandardPdfFont.TimesBoldItalic },
{ label: "Courier", value: StandardPdfFont.Courier },
{ label: "Courier Bold", value: StandardPdfFont.CourierBold },
{ label: "Courier Italic", value: StandardPdfFont.CourierItalic },
{ label: "Courier Bold Italic", value: StandardPdfFont.CourierBoldItalic },
{ label: "Symbol", value: StandardPdfFont.Symbol },
{ label: "Zapf Dingbats", value: StandardPdfFont.ZapfDingbats },
];
const STANDARD_FONT_VALUES = new Set<number>(STANDARD_FONTS.map((f) => f.value));
export type AnalysisResult = {
occurrences: number;
previewPngs: Uint8Array[];
pageCount: number;
};
export type ReplaceResult = {
pdf: Uint8Array;
previewPngs: Uint8Array[];
replacedCount: number; // occurrences of the search term before replacing
remainingOldCount: number; // occurrences of the search term still found afterwards (expected: 0)
newCount: number; // occurrences of the replacement text found afterwards
pageCount: number;
};
const PREVIEW_ZOOM = 1.35;
export class Demos {
static get apiVersion(): string {
return DsPdf.instance?.version ?? "Unknown";
}
static async connect(): Promise<boolean> {
if (!DsPdf.instance?.isConnected) {
DsPdfConfig.wasmUrl = "/DsPdf.wasm";
return await connectDsPdf();
}
return true;
}
static disconnect(): void {
if (DsPdf.instance?.isConnected) {
disconnectDsPdf();
}
}
static async loadSample(): Promise<Uint8Array> {
const response = await fetch("text-replace-sample.pdf", { cache: "no-cache" });
if (!response.ok) {
throw new Error(`Unable to load the sample PDF: ${response.statusText}`);
}
return new Uint8Array(await response.arrayBuffer());
}
private static countMatches(doc: PdfDocument, text: string, matchCase: boolean): number {
if (!text) return 0;
return doc.findText({ text, matchCase })?.length ?? 0;
}
private static renderPages(doc: PdfDocument): Uint8Array[] {
const previews: Uint8Array[] = [];
for (let pageIndex = 0; pageIndex < doc.pages.count; pageIndex++) {
previews.push(doc.pages.getAt(pageIndex).saveAsPng({ zoom: PREVIEW_ZOOM }));
}
return previews;
}
@withObjectManager
static async analyze(pdfBytes: Uint8Array, searchTerm: string, matchCase: boolean): Promise<AnalysisResult> {
const doc = PdfDocument.load(pdfBytes);
return {
occurrences: this.countMatches(doc, searchTerm, matchCase),
previewPngs: this.renderPages(doc),
pageCount: doc.pages.count,
};
}
@withObjectManager
static async count(pdfBytes: Uint8Array, searchTerm: string, matchCase: boolean): Promise<number> {
const doc = PdfDocument.load(pdfBytes);
return this.countMatches(doc, searchTerm, matchCase);
}
@withObjectManager
static async replace(
pdfBytes: Uint8Array,
searchTerm: string,
newText: string,
matchCase: boolean,
fontSize: number | null,
fontId: StandardPdfFont | null,
): Promise<ReplaceResult> {
const doc = PdfDocument.load(pdfBytes);
const replacedCount = this.countMatches(doc, searchTerm, matchCase);
// A chosen standard font is resolved with the current ObjectManager (set by
// @withObjectManager); null means keep the text's current font.
const font = fontId !== null && STANDARD_FONT_VALUES.has(fontId) ? Font.getPdfFont(fontId) : null;
// Edit the actual content stream. `font: null` keeps the current font;
// `fontSize: null` keeps the current size.
//
// replaceText() locates text using the document's current recognition
// algorithm (PdfDocument.recognitionAlgorithm). On complex PDFs the result
// can depend on how the text is stored in the content stream, so a run that
// reads naturally to a human is not always a single replaceable unit. The
// bundled sample PDF uses simple, plain text so this behaves predictably;
// when replacing text in arbitrary PDFs, review the output.
doc.replaceText({ text: searchTerm, matchCase }, newText, undefined, font, fontSize);
return {
pdf: doc.savePdf(),
previewPngs: this.renderPages(doc),
replacedCount,
remainingOldCount: this.countMatches(doc, searchTerm, matchCase),
newCount: this.countMatches(doc, newText, matchCase),
pageCount: doc.pages.count,
};
}
}