@itwin/core-backend
Version:
iTwin.js backend components
135 lines • 5.17 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import * as fs from "fs";
import { FontType } from "@itwin/core-common";
import { _faceProps, _getData, _implementationProhibited, _key } from "./Symbols";
import { compareNumbersOrUndefined } from "@itwin/core-bentley";
import { IModelNative } from "./NativePlatform";
class FontFileImpl {
[_implementationProhibited] = undefined;
faces;
get isEmbeddable() { return true; }
[_key];
[_faceProps];
constructor(faces) {
this.faces = faces.map((face) => {
return {
familyName: face.familyName,
isBold: face.faceName.startsWith("bold"),
isItalic: face.faceName.endsWith("italic"),
};
});
this[_faceProps] = faces;
// Sort the face props in canonical order so we can compare FontFiles for equivalent contents.
this[_faceProps].sort((a, b) => a.familyName.localeCompare(b.familyName) || a.faceName.localeCompare(b.faceName) || compareNumbersOrUndefined(a.subId, b.subId));
// Stringify the face props so that the key properties (and no other properties) appear in a canonical order. for trivial comparisons.
this[_key] = JSON.stringify(this[_faceProps], ["familyName", "faceName", "type", "subId"]);
}
}
/** Points to an OpenType font file on disk. */
class TrueTypeFontFile extends FontFileImpl {
#fileName;
#embeddable;
constructor(fileName, embeddable, faces) {
super(faces);
this.#fileName = fileName;
this.#embeddable = embeddable;
}
get type() { return FontType.TrueType; }
get isEmbeddable() { return this.#embeddable; }
[_getData]() {
return fs.readFileSync(this.#fileName);
}
}
/** Holds the binary representation of a SHX or RSC font in memory. */
export class CadFontFile extends FontFileImpl {
#data;
#type;
constructor(data, type, faces) {
super(faces);
this.#data = data;
this.#type = type;
}
get type() { return this.#type; }
[_getData]() { return this.#data; }
}
/** Points to a font file in any format embedded in an iModel. */
export class EmbeddedFontFile extends FontFileImpl {
// The value of the Id column for the row in the be_Prop table that embeds this font's data.
#db;
#id;
#type;
constructor(db, id, type, faces) {
super(faces);
this.#db = db;
this.#id = id;
this.#type = type;
}
get type() { return this.#type; }
[_getData]() {
const data = this.#db.queryFilePropertyBlob({
namespace: "dgn_Font",
name: "EmbeddedFaceData",
id: this.#id,
});
if (!data) {
throw new Error("Embedded font not found");
}
return data;
}
}
export function shxFontFileFromBlob(args) {
if (args.familyName.length === 0) {
throw new Error("Font family name cannot be empty");
}
// An SHX file with fewer than 40 bytes isn't valid.
const minShxSize = 40;
const shxHeaders = [
"AutoCAD-86 unifont 1.0",
"AutoCAD-86 shapes 1.0",
"AutoCAD-86 shapes 1.1",
].map((shxHeader) => Array.from(shxHeader))
.map((chars) => new Uint8Array(chars.map((ch) => ch.charCodeAt(0))));
// See https://github.com/iTwin/imodel-native/blob/91f509c2175dc49ce1efcf5a906c9a9aa193451d/iModelCore/iModelPlatform/DgnCore/ShxFont.cpp#L44
function validateShx(blob) {
if (blob.length >= minShxSize) {
for (const header of shxHeaders) {
if (header.every((char, index) => char === blob[index])) {
return;
}
}
}
throw new Error("Failed to read font file");
}
validateShx(args.blob);
return new CadFontFile(args.blob, FontType.Shx, [{
faceName: "regular",
familyName: args.familyName,
type: FontType.Shx,
}]);
}
export function rscFontFileFromBlob(args) {
if (args.familyName.length === 0) {
throw new Error("Font family name cannot be empty");
}
if (!IModelNative.platform.isRscFontData(args.blob)) {
throw new Error("Failed to read font file");
}
return new CadFontFile(args.blob, FontType.Rsc, [{
faceName: "regular",
familyName: args.familyName,
type: FontType.Rsc,
encoding: args.encoding,
}]);
}
export function trueTypeFontFileFromFileName(fileName) {
const metadata = IModelNative.platform.getTrueTypeFontMetadata(fileName);
if (metadata.faces.length === 0) {
// The input was almost certainly not a TrueType font file.
throw new Error("Failed to read font file");
}
return new TrueTypeFontFile(fileName, metadata.embeddable, metadata.faces);
}
//# sourceMappingURL=FontFileImpl.js.map