3ca03603c2
- For text fields * when printing, we generate a fake font which contains some widths computed thanks to an OffscreenCanvas and its method measureText. In order to avoid to have to layout the glyphs ourselves, we just render all of them in one call in the showText method in using the system sans-serif/monospace fonts. * when saving, we continue to create the appearance streams if the fonts contain the char but when a char is missing, we just set, in the AcroForm dict, the flag /NeedAppearances to true and remove the appearance stream. This way, we let the different readers handle the rendering of the strings. - For FreeText annotations * when printing, we use the same trick as for text fields. * there is no need to save an appearance since Acrobat is able to infer one from the Content entry.
167 lines
4.2 KiB
JavaScript
167 lines
4.2 KiB
JavaScript
/* Copyright 2017 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { NullStream, StringStream } from "../../src/core/stream.js";
|
|
import { Page, PDFDocument } from "../../src/core/document.js";
|
|
import { assert } from "../../src/shared/util.js";
|
|
import { DocStats } from "../../src/core/core_utils.js";
|
|
import { isNodeJS } from "../../src/shared/is_node.js";
|
|
import { Ref } from "../../src/core/primitives.js";
|
|
|
|
const TEST_PDFS_PATH = isNodeJS ? "./test/pdfs/" : "../pdfs/";
|
|
|
|
const CMAP_PARAMS = {
|
|
cMapUrl: isNodeJS ? "./external/bcmaps/" : "../../external/bcmaps/",
|
|
cMapPacked: true,
|
|
};
|
|
|
|
const STANDARD_FONT_DATA_URL = isNodeJS
|
|
? "./external/standard_fonts/"
|
|
: "../../external/standard_fonts/";
|
|
|
|
class DOMFileReaderFactory {
|
|
static async fetch(params) {
|
|
const response = await fetch(params.path);
|
|
if (!response.ok) {
|
|
throw new Error(response.statusText);
|
|
}
|
|
return new Uint8Array(await response.arrayBuffer());
|
|
}
|
|
}
|
|
|
|
class NodeFileReaderFactory {
|
|
static async fetch(params) {
|
|
const fs = require("fs");
|
|
|
|
return new Promise((resolve, reject) => {
|
|
fs.readFile(params.path, (error, data) => {
|
|
if (error || !data) {
|
|
reject(error || new Error(`Empty file for: ${params.path}`));
|
|
return;
|
|
}
|
|
resolve(new Uint8Array(data));
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
const DefaultFileReaderFactory = isNodeJS
|
|
? NodeFileReaderFactory
|
|
: DOMFileReaderFactory;
|
|
|
|
function buildGetDocumentParams(filename, options) {
|
|
const params = Object.create(null);
|
|
params.url = isNodeJS
|
|
? TEST_PDFS_PATH + filename
|
|
: new URL(TEST_PDFS_PATH + filename, window.location).href;
|
|
params.standardFontDataUrl = STANDARD_FONT_DATA_URL;
|
|
|
|
for (const option in options) {
|
|
params[option] = options[option];
|
|
}
|
|
return params;
|
|
}
|
|
|
|
class XRefMock {
|
|
constructor(array) {
|
|
this._map = Object.create(null);
|
|
this.stats = new DocStats({ send: () => {} });
|
|
this._newTemporaryRefNum = null;
|
|
this._newPersistentRefNum = null;
|
|
this.stream = new NullStream();
|
|
|
|
for (const key in array) {
|
|
const obj = array[key];
|
|
this._map[obj.ref.toString()] = obj.data;
|
|
}
|
|
}
|
|
|
|
getNewPersistentRef(obj) {
|
|
if (this._newPersistentRefNum === null) {
|
|
this._newPersistentRefNum = Object.keys(this._map).length || 1;
|
|
}
|
|
const ref = Ref.get(this._newPersistentRefNum++, 0);
|
|
this._map[ref.toString()] = obj;
|
|
return ref;
|
|
}
|
|
|
|
getNewTemporaryRef() {
|
|
if (this._newTemporaryRefNum === null) {
|
|
this._newTemporaryRefNum = Object.keys(this._map).length || 1;
|
|
}
|
|
return Ref.get(this._newTemporaryRefNum++, 0);
|
|
}
|
|
|
|
resetNewTemporaryRef() {
|
|
this._newTemporaryRefNum = null;
|
|
}
|
|
|
|
fetch(ref) {
|
|
return this._map[ref.toString()];
|
|
}
|
|
|
|
async fetchAsync(ref) {
|
|
return this.fetch(ref);
|
|
}
|
|
|
|
fetchIfRef(obj) {
|
|
if (obj instanceof Ref) {
|
|
return this.fetch(obj);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
async fetchIfRefAsync(obj) {
|
|
return this.fetchIfRef(obj);
|
|
}
|
|
}
|
|
|
|
function createIdFactory(pageIndex) {
|
|
const pdfManager = {
|
|
get docId() {
|
|
return "d0";
|
|
},
|
|
};
|
|
const stream = new StringStream("Dummy_PDF_data");
|
|
const pdfDocument = new PDFDocument(pdfManager, stream);
|
|
|
|
const page = new Page({
|
|
pdfManager: pdfDocument.pdfManager,
|
|
xref: pdfDocument.xref,
|
|
pageIndex,
|
|
globalIdFactory: pdfDocument._globalIdFactory,
|
|
});
|
|
return page._localIdFactory;
|
|
}
|
|
|
|
function isEmptyObj(obj) {
|
|
assert(
|
|
typeof obj === "object" && obj !== null,
|
|
"isEmptyObj - invalid argument."
|
|
);
|
|
return Object.keys(obj).length === 0;
|
|
}
|
|
|
|
export {
|
|
buildGetDocumentParams,
|
|
CMAP_PARAMS,
|
|
createIdFactory,
|
|
DefaultFileReaderFactory,
|
|
isEmptyObj,
|
|
STANDARD_FONT_DATA_URL,
|
|
TEST_PDFS_PATH,
|
|
XRefMock,
|
|
};
|