-
Notifications
You must be signed in to change notification settings - Fork 503
Implement plugins to support (subset) fonts in standalone CFF1 and Type1 format #704
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
da1ac0b
7c7b80f
0ed7279
cff7316
7b67ce0
8103922
23dded6
e31ebc8
cc7715a
ff4552e
3eef5ed
ea06c9a
78de975
dbcb8bb
3aa9a22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
src/plugins/pdfjs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
// opentype.js | ||
// https://github.com/opentypejs/opentype.js | ||
// (c) 2015 Frederik De Bleser | ||
// opentype.js may be freely distributed under the MIT license. | ||
/*! opentype.js | ||
* https://github.com/opentypejs/opentype.js | ||
* (c) 2015-present Frederik De Bleser and contributors | ||
* opentype.js may be freely distributed under the MIT license. | ||
*/ | ||
|
||
import { tinf_uncompress as inflate } from './[email protected]'; // from code4fukui/tiny-inflate-es | ||
import { isNode } from './util.js'; | ||
import Font from './font.js'; | ||
import Font, { createDefaultNamesInfo } from './font.js'; | ||
import Glyph from './glyph.js'; | ||
import glyphset from './glyphset.js'; | ||
import { CmapEncoding, GlyphNames, addGlyphNames } from './encoding.js'; | ||
import parse from './parse.js'; | ||
import BoundingBox from './bbox.js'; | ||
|
@@ -39,6 +41,9 @@ import meta from './tables/meta.js'; | |
import gasp from './tables/gasp.js'; | ||
import svg from './tables/svg.js'; | ||
import { PaletteManager } from './palettes.js'; | ||
import { sizeOf } from './types.js'; | ||
import { plugins, applyPlugins } from './plugins.mjs'; | ||
|
||
/** | ||
* The opentype library. | ||
* @namespace opentype | ||
|
@@ -261,6 +266,20 @@ function parseBuffer(buffer, opt={}) { | |
} else if (signature === 'wOF2') { | ||
var issue = 'https://github.com/opentypejs/opentype.js/issues/183#issuecomment-1147228025'; | ||
throw new Error('WOFF2 require an external decompressor library, see examples at: ' + issue); | ||
} else if( | ||
applyPlugins( | ||
'parseBuffer_signature', | ||
{ font, opt, cff, CmapEncoding, Glyph, GlyphNames, glyphset, signature, data, createDefaultNamesInfo, parse, sizeOf, tableEntries } | ||
) | ||
) { | ||
numTables = tableEntries.length; | ||
} else if ( | ||
signature.substring(0,2) === '%!' || | ||
(parse.getByte(data, 0) === 0x80 && parse.getByte(data, 1) === 0x01) | ||
) { | ||
throw new Error('PostScript/PS1/T1/Adobe Type 1 fonts are not supported directly, but you can use the plugin "opentypejs.plugin.type1"'); | ||
} else if (data.buffer.byteLength > (3 * sizeOf.Card8() + sizeOf.OffSize()) && parse.getByte(data, 0) === 0x01) { | ||
throw new Error('Standalone CFF1 files are not supported directly, but you can use the plugin "opentypejs.plugin.cff1file"'); | ||
} else { | ||
throw new Error('Unsupported OpenType signature ' + signature); | ||
} | ||
|
@@ -412,9 +431,19 @@ function parseBuffer(buffer, opt={}) { | |
} | ||
} | ||
|
||
const nameTable = uncompressTable(data, nameTableEntry); | ||
font.tables.name = _name.parse(nameTable.data, nameTable.offset, ltagTable); | ||
font.names = font.tables.name; | ||
applyPlugins('parseBuffer_processed', {opentype: this, font, opt, data}); | ||
|
||
if (nameTableEntry) { | ||
const nameTable = uncompressTable(data, nameTableEntry); | ||
font.tables.name = _name.parse(nameTable.data, nameTable.offset, ltagTable); | ||
font.names = font.tables.name; | ||
} else if(!font.names) { | ||
console.error('Font is missing the required table "name"'); | ||
font.names = {}; | ||
font.names.unicode = createDefaultNamesInfo({}); | ||
font.names.macintosh = createDefaultNamesInfo({}); | ||
font.names.windows = createDefaultNamesInfo({}); | ||
} | ||
|
||
if (glyfTableEntry && locaTableEntry) { | ||
const shortVersion = indexToLocFormat === 0; | ||
|
@@ -428,13 +457,27 @@ function parseBuffer(buffer, opt={}) { | |
} else if (cff2TableEntry) { | ||
const cffTable2 = uncompressTable(data, cff2TableEntry); | ||
cff.parse(cffTable2.data, cffTable2.offset, font, opt); | ||
} else { | ||
} else if (!font.nGLyphs && !font.numGlyphs) { | ||
throw new Error('Font doesn\'t contain TrueType, CFF or CFF2 outlines.'); | ||
} | ||
|
||
const hmtxTable = uncompressTable(data, hmtxTableEntry); | ||
hmtx.parse(font, hmtxTable.data, hmtxTable.offset, font.numberOfHMetrics, font.numGlyphs, font.glyphs, opt); | ||
addGlyphNames(font, opt); | ||
|
||
if(hmtxTableEntry) { | ||
const hmtxTable = uncompressTable(data, hmtxTableEntry); | ||
hmtx.parse(font, hmtxTable.data, hmtxTable.offset, font.numberOfHMetrics, font.numGlyphs, font.glyphs, opt); | ||
} else if(!font._hmtxTableData) { | ||
console.error('Font is missing the required table "hmtx"'); | ||
} | ||
|
||
applyPlugins('parseBuffer_before_addGlyphNames', { opentype: this, font, opt, data, createDefaultNamesInfo}); | ||
|
||
if(font.tables.cmap) { | ||
addGlyphNames(font, opt); | ||
} else { | ||
font._IndexToUnicodeMap = font._IndexToUnicodeMap || {}; | ||
font.glyphNames = font.glyphNames || new GlyphNames({}); | ||
console.warn('This font has no "cmap" table'); | ||
} | ||
|
||
if (kernTableEntry) { | ||
const kernTable = uncompressTable(data, kernTableEntry); | ||
|
@@ -523,6 +566,8 @@ function parseBuffer(buffer, opt={}) { | |
|
||
font.palettes = new PaletteManager(font); | ||
|
||
applyPlugins('parseBuffer_parsed', { opentype: this, font, opt, data, tableEntries}); | ||
|
||
return font; | ||
} | ||
|
||
|
@@ -580,13 +625,18 @@ function loadSync(url, opt) { | |
return parseBuffer(require('fs').readFileSync(url), opt); | ||
} | ||
|
||
const { GlyphSet } = glyphset; | ||
|
||
export { | ||
Font, | ||
GlyphSet, | ||
Glyph, | ||
GlyphNames, | ||
Path, | ||
BoundingBox, | ||
parse as _parse, | ||
parseBuffer as parse, | ||
load, | ||
loadSync | ||
loadSync, | ||
plugins | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
export const plugins = []; | ||
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it be cleaner to abstract/hide this (just a suggestion) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about that as well, but then we'd also need methods to unregister a plugin and change the execution order in case two plugins modify the same entry point data. I think everyone should be familiar with modifying an array, so we should be fine leaving this without abstraction. |
||
|
||
/** | ||
* Plugins are registered by pushing them to the opentype.plugins array. | ||
* They need to export any supported entry point names as functions, which will be passed a returnData object | ||
* and different params depending on the entry point, which should be documented. | ||
* | ||
* The returnData object is shared by all plugins handling the same entry point and can be used to return | ||
* data which can be handled by the code following an entry point. Data can also be mutated on any references | ||
* to arrays or objects passed via the params. | ||
* What returnData is expected/supported by each entry point should be documented as well. | ||
* An entry point function returning a falsy value for returnData signals that it has not handled that entry point. | ||
*/ | ||
|
||
/** | ||
* checks if any registered plugin covers the given entry point and data | ||
* @param {string} entryPoint - name of an entry point from where the plugin is called | ||
* @param {Object} params - object of parameters to pass on to the plugin entry point | ||
* @returns {Object|boolean} returnData object if the entry point was handled successfully by at least one plugin, false if not | ||
*/ | ||
export function applyPlugins(entryPoint, params) { | ||
let handled = false; | ||
let returnData = {}; | ||
for(const plugin of plugins) { | ||
if(typeof plugin[entryPoint] !== 'function') continue; | ||
const pluginReturnData = plugin[entryPoint](returnData, params); | ||
if(typeof pluginReturnData === 'object') { | ||
returnData = Object.assign({}, returnData, pluginReturnData); | ||
} | ||
if(!!pluginReturnData) handled = true; | ||
} | ||
return handled ? returnData : false; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you already used the optional chaining
.?
syntax early on,so keep it unified (I have no preference, so it's up to you to chose one :) )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, did I use optional chaining? I thought that would throw an error with the current reify setup... I'll take a look at it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup here:
https://github.com/opentypejs/opentype.js/pull/704/files#diff-cd810153d07820a50b3950a985c42117e1dd7ea75207520e60b7306ce17b8901R396