Skip to content

Commit

Permalink
Added Nimbus Mono font
Browse files Browse the repository at this point in the history
  • Loading branch information
Balearica committed Nov 2, 2024
1 parent 154576d commit e0e1ba9
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 13 deletions.
9 changes: 9 additions & 0 deletions js/containers/fontContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ export class FontCont {

static sansDefaultName = 'NimbusSans';

/**
* If `false`, 'Courier' will not be cleaned to Nimbus Mono.
* This setting is useful because Tesseract sometimes misidentifies fonts as Courier, and when not the document default, Nimbus Mono is almost always incorrect.
* Even with this setting `false`, Nimbus Mono will still be used when the font is exactly 'NimbusMono' and Nimbus Mono can still be the document default font.
*/
static enableCleanToNimbusMono = false;

/** @type {?('latin'|'all')} */
static glyphSet = null;

Expand Down Expand Up @@ -337,6 +344,8 @@ export class FontCont {
family = 'Carlito';
} else if (/Calibri/i.test(family)) {
family = 'Carlito';
} else if (/Courier/i.test(family) && FontCont.enableCleanToNimbusMono) {
family = 'NimbusMono';
}
}

Expand Down
5 changes: 3 additions & 2 deletions js/export/writePdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,10 +534,11 @@ async function ocrPageToPDFStream(pageObj, outputDims, pdfFonts, textMode, angle
const wordSpaceNextAdj = (wordNext.bbox.left - wordJ.bbox.right) / cosAngle;
// const wordSpaceNextAdj = wordNext.bbox.left - wordBox.right;

const wordGlyphMetrics = wordFontOpentype.charToGlyph(charArr.at(-1)).getMetrics();
const wordGlyph = wordFontOpentype.charToGlyph(charArr.at(-1));
const wordGlyphMetrics = wordGlyph.getMetrics();
const wordNextGlyphMetrics = wordFontOpentype.charToGlyph(wordNext.text.substr(0, 1)).getMetrics();

const wordRightBearing = wordJ.visualCoords ? wordGlyphMetrics.rightSideBearing * (wordFontSize / wordFontOpentype.unitsPerEm) : 0;
const wordRightBearing = wordJ.visualCoords ? (wordGlyph.advanceWidth - wordGlyphMetrics.xMax) * (wordFontSize / wordFontOpentype.unitsPerEm) : 0;

const wordNextLeftBearing = wordNext.visualCoords ? wordNextGlyphMetrics.xMin * (wordFontSize / wordFontOpentype.unitsPerEm) : 0;

Expand Down
19 changes: 17 additions & 2 deletions js/fontContainerMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export async function loadBuiltInFontsRaw(glyphSet = 'latin') {
let /** @type {Promise<ArrayBuffer>} */nimbusSansNormal;
let /** @type {Promise<ArrayBuffer>} */nimbusSansItalic;
let /** @type {Promise<ArrayBuffer>} */nimbusSansBold;
let /** @type {Promise<ArrayBuffer>} */nimbusMonoNormal;
let /** @type {Promise<ArrayBuffer>} */nimbusMonoItalic;
let /** @type {Promise<ArrayBuffer>} */nimbusMonoBold;
if (typeof process === 'undefined') {
if (glyphSet === 'latin') {
carlitoNormal = fetch(new URL('../fonts/latin/Carlito-Regular.woff', import.meta.url)).then((res) => res.arrayBuffer());
Expand All @@ -59,6 +62,9 @@ export async function loadBuiltInFontsRaw(glyphSet = 'latin') {
nimbusSansNormal = fetch(new URL('../fonts/latin/NimbusSans-Regular.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusSansItalic = fetch(new URL('../fonts/latin/NimbusSans-Italic.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusSansBold = fetch(new URL('../fonts/latin/NimbusSans-Bold.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusMonoNormal = fetch(new URL('../fonts/latin/NimbusMonoPS-Regular.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusMonoItalic = fetch(new URL('../fonts/latin/NimbusMonoPS-Italic.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusMonoBold = fetch(new URL('../fonts/latin/NimbusMonoPS-Bold.woff', import.meta.url)).then((res) => res.arrayBuffer());
} else {
carlitoNormal = fetch(new URL('../fonts/all/Carlito-Regular.woff', import.meta.url)).then((res) => res.arrayBuffer());
carlitoItalic = fetch(new URL('../fonts/all/Carlito-Italic.woff', import.meta.url)).then((res) => res.arrayBuffer());
Expand All @@ -78,6 +84,9 @@ export async function loadBuiltInFontsRaw(glyphSet = 'latin') {
nimbusSansNormal = fetch(new URL('../fonts/all/NimbusSans-Regular.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusSansItalic = fetch(new URL('../fonts/all/NimbusSans-Italic.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusSansBold = fetch(new URL('../fonts/all/NimbusSans-Bold.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusMonoNormal = fetch(new URL('../fonts/all/NimbusMonoPS-Regular.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusMonoItalic = fetch(new URL('../fonts/all/NimbusMonoPS-Italic.woff', import.meta.url)).then((res) => res.arrayBuffer());
nimbusMonoBold = fetch(new URL('../fonts/all/NimbusMonoPS-Bold.woff', import.meta.url)).then((res) => res.arrayBuffer());
}
} else {
const { readFile } = await import('fs/promises');
Expand All @@ -99,6 +108,9 @@ export async function loadBuiltInFontsRaw(glyphSet = 'latin') {
nimbusSansNormal = readFile(new URL('../fonts/all_ttf/NimbusSans-Regular.ttf', import.meta.url)).then((res) => res.buffer);
nimbusSansItalic = readFile(new URL('../fonts/all_ttf/NimbusSans-Italic.ttf', import.meta.url)).then((res) => res.buffer);
nimbusSansBold = readFile(new URL('../fonts/all_ttf/NimbusSans-Bold.ttf', import.meta.url)).then((res) => res.buffer);
nimbusMonoNormal = readFile(new URL('../fonts/all_ttf/NimbusMonoPS-Regular.ttf', import.meta.url)).then((res) => res.buffer);
nimbusMonoItalic = readFile(new URL('../fonts/all_ttf/NimbusMonoPS-Italic.ttf', import.meta.url)).then((res) => res.buffer);
nimbusMonoBold = readFile(new URL('../fonts/all_ttf/NimbusMonoPS-Bold.ttf', import.meta.url)).then((res) => res.buffer);
}

const srcObj = {
Expand All @@ -108,6 +120,7 @@ export async function loadBuiltInFontsRaw(glyphSet = 'latin') {
Palatino: { normal: await palatinoNormal, italic: await palatinoItalic, bold: await palatinoBold },
NimbusRomNo9L: { normal: await nimbusRomNo9LNormal, italic: await nimbusRomNo9LItalic, bold: await nimbusRomNo9LBold },
NimbusSans: { normal: await nimbusSansNormal, italic: await nimbusSansItalic, bold: await nimbusSansBold },
NimbusMono: { normal: await nimbusMonoNormal, italic: await nimbusMonoItalic, bold: await nimbusMonoBold },
};

FontCont.raw = await /** @type {FontContainer} */(/** @type {any} */(loadFontsFromSource(srcObj)));
Expand Down Expand Up @@ -256,7 +269,7 @@ export async function setUploadFontsWorker(scheduler) {
/** @type {Object<string, fontSrcBuiltIn|fontSrcUpload>} */
const fontsUpload = {};
for (const [key, value] of Object.entries(FontCont.active)) {
if (!['Carlito', 'Century', 'Garamond', 'Palatino', 'NimbusRomNo9L', 'NimbusSans'].includes(key)) {
if (!['Carlito', 'Century', 'Garamond', 'Palatino', 'NimbusRomNo9L', 'NimbusSans', 'NimbusMono'].includes(key)) {
fontsUpload[key] = {
normal: value?.normal?.src, italic: value?.italic?.src, bold: value?.bold?.src,
};
Expand Down Expand Up @@ -381,8 +394,9 @@ export async function optimizeFontContainerAll(fontPrivate, fontMetricsObj) {
const palatinoPromise = optimizeFontContainerFamily(fontPrivate.Palatino, fontMetricsObj);
const nimbusRomNo9LPromise = optimizeFontContainerFamily(fontPrivate.NimbusRomNo9L, fontMetricsObj);
const nimbusSansPromise = optimizeFontContainerFamily(fontPrivate.NimbusSans, fontMetricsObj);
const nimbusMonoPromise = optimizeFontContainerFamily(fontPrivate.NimbusMono, fontMetricsObj);

const results = await Promise.all([carlitoPromise, centuryPromise, garamondPromise, palatinoPromise, nimbusRomNo9LPromise, nimbusSansPromise]);
const results = await Promise.all([carlitoPromise, centuryPromise, garamondPromise, palatinoPromise, nimbusRomNo9LPromise, nimbusSansPromise, nimbusMonoPromise]);

if (results.every((x) => x === null)) return null;

Expand All @@ -393,5 +407,6 @@ export async function optimizeFontContainerAll(fontPrivate, fontMetricsObj) {
Palatino: results[3],
NimbusRomNo9L: results[4],
NimbusSans: results[5],
NimbusMono: results[6],
};
}
11 changes: 8 additions & 3 deletions js/fontEval.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export async function evaluateFonts(pageArr, opt) {
const evalPalatino = !!(opt ? FontCont.opt?.Palatino : FontCont.raw?.Palatino);
const evalGaramond = !!(opt ? FontCont.opt?.Garamond : FontCont.raw?.Garamond);
const evalNimbusRomNo9L = !!(opt ? FontCont.opt?.NimbusRomNo9L : FontCont.raw?.NimbusRomNo9L);
const evalNimbusMono = !!(opt ? FontCont.opt?.NimbusMono : FontCont.raw?.NimbusMono);

// The browser version runs in parallel using workers, however the Node.js version runs sequentially,
// as the canvas package does not support workers, and trying to run in parallel causes problems.
Expand All @@ -63,6 +64,7 @@ export async function evaluateFonts(pageArr, opt) {
palatino: evalPalatino ? evalPagesFont('Palatino', pageArr, opt) : null,
garamond: evalGaramond ? evalPagesFont('Garamond', pageArr, opt) : null,
nimbusRomNo9L: evalNimbusRomNo9L ? evalPagesFont('NimbusRomNo9L', pageArr, opt) : null,
nimbusMono: evalNimbusMono ? evalPagesFont('NimbusMono', pageArr, opt) : null,
};

fontMetricsTmp = {
Expand All @@ -72,6 +74,7 @@ export async function evaluateFonts(pageArr, opt) {
palatino: await fontMetricsPromises.palatino,
garamond: await fontMetricsPromises.garamond,
nimbusRomNo9L: await fontMetricsPromises.nimbusRomNo9L,
nimbusMono: await fontMetricsPromises.nimbusMono,
};
} else {
fontMetricsTmp = {
Expand All @@ -81,6 +84,7 @@ export async function evaluateFonts(pageArr, opt) {
palatino: evalPalatino ? await evalPagesFont('Palatino', pageArr, opt) : null,
garamond: evalGaramond ? await evalPagesFont('Garamond', pageArr, opt) : null,
nimbusRomNo9L: evalNimbusRomNo9L ? await evalPagesFont('NimbusRomNo9L', pageArr, opt) : null,
nimbusMono: evalNimbusMono ? await evalPagesFont('NimbusMono', pageArr, opt) : null,
};
}

Expand All @@ -91,6 +95,7 @@ export async function evaluateFonts(pageArr, opt) {
Palatino: fontMetricsTmp.palatino ? fontMetricsTmp.palatino.metricTotal / fontMetricsTmp.palatino.wordsTotal : null,
Garamond: fontMetricsTmp.garamond ? fontMetricsTmp.garamond.metricTotal / fontMetricsTmp.garamond.wordsTotal : null,
NimbusRomNo9L: fontMetricsTmp.nimbusRomNo9L ? fontMetricsTmp.nimbusRomNo9L.metricTotal / fontMetricsTmp.nimbusRomNo9L.wordsTotal : null,
NimbusMono: fontMetricsTmp.nimbusMono ? fontMetricsTmp.nimbusMono.metricTotal / fontMetricsTmp.nimbusMono.wordsTotal : null,
};

return fontMetrics;
Expand All @@ -106,7 +111,7 @@ const calcBestFonts = (fontMetrics) => {

for (const [key, value] of Object.entries(fontMetrics)) {
if (!['Carlito', 'NimbusSans'].includes(key)) continue;
if (value < minValueSans) {
if (value && value < minValueSans) {
minValueSans = value;
minKeySans = key;
}
Expand All @@ -116,8 +121,8 @@ const calcBestFonts = (fontMetrics) => {
let minValueSerif = Number.MAX_VALUE;

for (const [key, value] of Object.entries(fontMetrics)) {
if (!['Century', 'Palatino', 'Garamond', 'NimbusRomNo9L'].includes(key)) continue;
if (value < minValueSerif) {
if (!['Century', 'Palatino', 'Garamond', 'NimbusRomNo9L', 'NimbusMono'].includes(key)) continue;
if (value && value < minValueSerif) {
minValueSerif = value;
minKeySerif = key;
}
Expand Down
1 change: 1 addition & 0 deletions js/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ declare global {
Palatino: FontContainerFamilyBuiltIn;
NimbusRomNo9L: FontContainerFamilyBuiltIn;
NimbusSans: FontContainerFamilyBuiltIn;
NimbusMono: FontContainerFamilyBuiltIn;
[key: string]: FontContainerFamily;
};

Expand Down
1 change: 1 addition & 0 deletions js/import/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ export async function importFiles(files, options = {}) {
await extractInternalPDFText({
setActive: true, extractPDFTextNative, extractPDFTextOCR, extractPDFTextImage,
});
if (ImageCache.pdfType === 'text') FontCont.enableCleanToNimbusMono = true;
if (opt.calcSuppFontInfo) await calcSuppFontInfo(ocrAll.pdf);
}
}
Expand Down
5 changes: 3 additions & 2 deletions js/utils/fontUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,9 @@ export function calcWordMetrics(word, angle = 0) {
const wordLastGlyphMetrics = fontOpentype.charToGlyph(charArr2.at(-1)).getMetrics();
const wordFirstGlyphMetrics = fontOpentype.charToGlyph(charArr2[0]).getMetrics();

let wordLeftBearing = wordFirstGlyphMetrics.leftSideBearing || 0;
let wordRightBearing = wordLastGlyphMetrics.rightSideBearing || 0;
// The `leftSideBearing`/`rightSideBearing`/ numbers reported by Opentype.js are not accurate for mono-spaced fonts.
let wordLeftBearing = wordFirstGlyphMetrics.xMin || 0;
let wordRightBearing = advanceArr[advanceArr.length - 1] - wordLastGlyphMetrics.xMax || 0;
if (word.smallCaps && charArr2[0] !== charArr[0]) wordLeftBearing *= fontI.smallCapsMult;
if (word.smallCaps && charArr2[charArr2.length - 1] !== charArr[charArr2.length - 1]) wordRightBearing *= fontI.smallCapsMult;

Expand Down
2 changes: 1 addition & 1 deletion js/utils/miscUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ export function replaceObjectProperties(obj, obj2 = {}) {
// Fonts that should not be added (both Sans and Serif variants):
// DejaVu
const serifFonts = ['SerifDefault', 'Baskerville', 'Bookman', 'C059', 'Calibri', 'Cambria', 'Century', 'Courier', 'Garamond', 'Georgia',
'LucidaBright', 'Minion', 'Optima', 'P052', 'Palatino', 'Times'];
'LucidaBright', 'Minion', 'NimbusMono', 'Optima', 'P052', 'Palatino', 'Times'];
const sansFonts = ['SansDefault', 'Avenir', 'Arial', 'Calibri', 'Candara', 'Carlito', 'Comic', 'Franklin', 'Futura', 'Gotham',
'Helvetica', 'Impact', 'Interstate', 'Myriad', 'Tahoma', 'Trebuchet', 'Univers', 'Verdana'];

Expand Down
9 changes: 6 additions & 3 deletions js/worker/optimizeFontModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,11 @@ const calculateKerningPairs = (font, fontMetricsObj, xHeight, style) => {
const indexFirst = font.charToGlyphIndex(charFirst);
const indexSecond = font.charToGlyphIndex(charSecond);

const metricsFirst = font.glyphs.glyphs[indexFirst].getMetrics();
const metricsSecond = font.glyphs.glyphs[indexSecond].getMetrics();
const glyphFirst = font.glyphs.glyphs[indexFirst];
const glyphSecond = font.glyphs.glyphs[indexSecond];

const metricsFirst = glyphFirst.getMetrics();
const metricsSecond = glyphSecond.getMetrics();

const fontKern1 = Math.round(value * xHeight);
let spaceTarget = fontKern1;
Expand All @@ -119,7 +122,7 @@ const calculateKerningPairs = (font, fontMetricsObj, xHeight, style) => {
}

// Calculate current space between these 2 glyphs (without kerning adjustments)
const spaceCurrent = metricsFirst.rightSideBearing + metricsSecond.leftSideBearing;
const spaceCurrent = (glyphFirst.advanceWidth - metricsFirst.xMax) + metricsSecond.xMin;

// Calculate kerning adjustment needed
let fontKern = spaceTarget - spaceCurrent;
Expand Down

0 comments on commit e0e1ba9

Please sign in to comment.