Move the creation of canvas path fonts to the worker.

This commit is contained in:
Brendan Dahl 2013-08-19 16:33:20 -07:00
parent 81c9eeef4b
commit bb2529de03
16 changed files with 131 additions and 76 deletions

View File

@ -13,7 +13,6 @@
<script type="text/javascript" src="../../src/display/metadata.js"></script>
<script type="text/javascript" src="../../src/display/canvas.js"></script>
<script type="text/javascript" src="../../src/display/font_loader.js"></script>
<script type="text/javascript" src="../../src/display/font_renderer.js"></script>
<script type="text/javascript">
// Specify the main script used to create a new PDF.JS web worker.

View File

@ -13,7 +13,6 @@
<script type="text/javascript" src="../../src/display/metadata.js"></script>
<script type="text/javascript" src="../../src/display/canvas.js"></script>
<script type="text/javascript" src="../../src/display/font_loader.js"></script>
<script type="text/javascript" src="../../src/display/font_renderer.js"></script>
<script type="text/javascript">
// Specify the main script used to create a new PDF.JS web worker.

View File

@ -278,8 +278,7 @@ target.bundle = function(args) {
'display/api.js',
'display/metadata.js',
'display/canvas.js',
'display/font_loader.js',
'display/font_renderer.js'
'display/font_loader.js'
];
var WORKER_SRC_FILES = [
@ -298,6 +297,7 @@ target.bundle = function(args) {
'core/crypto.js',
'core/evaluator.js',
'core/fonts.js',
'core/font_renderer.js',
'core/glyphlist.js',
'core/image.js',
'core/metrics.js',

View File

@ -20,7 +20,7 @@
isStream, isString, JpegStream, Lexer, Metrics, Name, Parser,
Pattern, PDFImage, PDFJS, serifFonts, stdFontMap, symbolsFonts,
TilingPattern, TODO, warn, Util, Promise,
RefSetCache, isRef */
RefSetCache, isRef, TextRenderingMode */
'use strict';
@ -349,6 +349,31 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return loadedName;
},
handleText: function PartialEvaluator_handleText(chars) {
var font = this.state.font.translated;
var glyphs = font.charsToGlyphs(chars);
var isAddToPathSet = !!(this.state.textRenderingMode &
TextRenderingMode.ADD_TO_PATH_FLAG);
if (isAddToPathSet || PDFJS.disableFontFace) {
for (var i = 0; i < glyphs.length; i++) {
if (glyphs[i] === null) {
continue;
}
var fontChar = glyphs[i].fontChar;
if (!font.renderer.hasBuiltPath(fontChar)) {
var path = font.renderer.getPathJs(fontChar);
this.handler.send('commonobj', [
font.loadedName + '_path_' + fontChar,
'FontPath',
path
]);
}
}
}
return glyphs;
},
setGState: function PartialEvaluator_setGState(resources, gState,
operatorList) {
@ -631,19 +656,21 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
this.state = prev;
}
} else if (cmd === 'Tj') { // showText
args[0] = this.state.font.translated.charsToGlyphs(args[0]);
args[0] = this.handleText(args[0]);
} else if (cmd === 'TJ') { // showSpacedText
var arr = args[0];
var arrLength = arr.length;
for (var i = 0; i < arrLength; ++i) {
if (isString(arr[i])) {
arr[i] = this.state.font.translated.charsToGlyphs(arr[i]);
arr[i] = this.handleText(arr[i]);
}
}
} else if (cmd === '\'') { // nextLineShowText
args[0] = this.state.font.translated.charsToGlyphs(args[0]);
args[0] = this.handleText(args[0]);
} else if (cmd === '"') { // nextLineSetSpacingShowText
args[2] = this.state.font.translated.charsToGlyphs(args[2]);
args[2] = this.handleText(args[2]);
} else if (cmd === 'Tr') { // setTextRenderingMode
this.state.textRenderingMode = args[0];
}
switch (fn) {
@ -1529,6 +1556,7 @@ var OperatorList = (function OperatorListClosure() {
var EvalState = (function EvalStateClosure() {
function EvalState() {
this.font = null;
this.textRenderingMode = TextRenderingMode.FILL;
}
EvalState.prototype = {
clone: function CanvasExtraState_clone() {

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals error, Stream, GlyphsUnicode, CFFParser, Encodings */
/* globals error, Stream, GlyphsUnicode, CFFParser, Encodings, Util */
'use strict';
@ -585,18 +585,14 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
parse(code);
}
function TrueTypeCompiled(glyphs, cmap, fontMatrix) {
this.glyphs = glyphs;
this.cmap = cmap;
this.fontMatrix = fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0];
var noop = '';
this.compiledGlyphs = [];
function CompiledFont(fontMatrix) {
this.compiledGlyphs = {};
this.fontMatrix = fontMatrix;
}
var noop = function () {};
TrueTypeCompiled.prototype = {
getPathGenerator: function (unicode) {
CompiledFont.prototype = {
getPathJs: function (unicode) {
var gid = lookupCmap(this.cmap, unicode);
var fn = this.compiledGlyphs[gid];
if (!fn) {
@ -604,6 +600,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
}
return fn;
},
compileGlyph: function (code) {
if (!code || code.length === 0 || code[0] === 14) {
return noop;
@ -614,23 +611,47 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
js.push('c.transform(' + this.fontMatrix.join(',') + ');');
js.push('c.scale(size, -size);');
var stack = [], x = 0, y = 0;
compileGlyf(code, js, this);
this.compileGlyphImpl(code, js);
js.push('c.restore();');
/*jshint -W054 */
return new Function('c', 'size', js.join('\n'));
return js.join('\n');
},
compileGlyphImpl: function () {
error('Children classes should implement this.');
},
hasBuiltPath: function (unicode) {
var gid = lookupCmap(this.cmap, unicode);
return gid in this.compiledGlyphs;
}
};
function TrueTypeCompiled(glyphs, cmap, fontMatrix) {
fontMatrix = fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0];
CompiledFont.call(this, fontMatrix);
this.glyphs = glyphs;
this.cmap = cmap;
this.compiledGlyphs = [];
}
Util.inherit(TrueTypeCompiled, CompiledFont, {
compileGlyphImpl: function (code, js) {
compileGlyf(code, js, this);
}
});
function Type2Compiled(cffInfo, cmap, fontMatrix, glyphNameMap) {
fontMatrix = fontMatrix || [0.001, 0, 0, 0.001, 0, 0];
CompiledFont.call(this, fontMatrix);
this.glyphs = cffInfo.glyphs;
this.gsubrs = cffInfo.gsubrs || [];
this.subrs = cffInfo.subrs || [];
this.cmap = cmap;
this.glyphNameMap = glyphNameMap || GlyphsUnicode;
this.fontMatrix = fontMatrix || [0.001, 0, 0, 0.001, 0, 0];
this.compiledGlyphs = [];
this.gsubrsBias = this.gsubrs.length < 1240 ? 107 :
@ -639,34 +660,12 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
this.subrs.length < 33900 ? 1131 : 32768;
}
Type2Compiled.prototype = {
getPathGenerator: function (unicode) {
var gid = lookupCmap(this.cmap, unicode);
var fn = this.compiledGlyphs[gid];
if (!fn) {
this.compiledGlyphs[gid] = fn = this.compileGlyph(this.glyphs[gid]);
}
return fn;
},
compileGlyph: function (code) {
if (!code || code.length === 0 || code[0] === 14) {
return noop;
}
var js = [];
js.push('c.save();');
js.push('c.transform(' + this.fontMatrix.join(',') + ');');
js.push('c.scale(size, -size);');
var stack = [], x = 0, y = 0;
Util.inherit(Type2Compiled, CompiledFont, {
compileGlyphImpl: function (code, js) {
compileCharString(code, js, this);
js.push('c.restore();');
/*jshint -W054 */
return new Function('c', 'size', js.join('\n'));
}
};
});
return {
create: function FontRendererFactory_create(font) {

View File

@ -18,7 +18,7 @@
ExpertSubsetCharset, FileReaderSync, GlyphsUnicode,
info, isArray, isNum, ISOAdobeCharset, Stream,
stringToBytes, TextDecoder, TODO, warn, Lexer, Util,
FONT_IDENTITY_MATRIX */
FONT_IDENTITY_MATRIX, FontRendererFactory, shadow */
'use strict';
@ -2791,6 +2791,10 @@ var Font = (function FontClosure() {
font: null,
mimetype: null,
encoding: null,
get renderer() {
var renderer = FontRendererFactory.create(this);
return shadow(this, 'renderer', renderer);
},
exportData: function Font_exportData() {
var data = {};

View File

@ -224,6 +224,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
PDFJS.maxImageSize = data.maxImageSize === undefined ?
-1 : data.maxImageSize;
PDFJS.disableFontFace = data.disableFontFace;
getPdfManager(data).then(function pdfManagerReady() {
loadDocument(false).then(onSuccess, function loadFailure(ex) {

View File

@ -29,6 +29,16 @@
*/
PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize;
/**
* By default fonts are converted to OpenType fonts and loaded via font face
* rules. If disabled, the font will be rendered using a built in font renderer
* that constructs the glyphs with primitive path commands.
* @var {Boolean}
*/
PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ?
false :
PDFJS.disableFontFace;
/**
* This is the main entry point for loading a PDF and interacting with it.
* NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
@ -673,6 +683,9 @@ var WorkerTransport = (function WorkerTransportClosure() {
}.bind(this)
);
break;
case 'FontPath':
this.commonObjs.resolve(id, data[2]);
break;
default:
error('Got unknown common object type ' + type);
}
@ -770,7 +783,8 @@ var WorkerTransport = (function WorkerTransportClosure() {
this.messageHandler.send('GetDocRequest', {
source: source,
disableRange: PDFJS.disableRange,
maxImageSize: PDFJS.maxImageSize
maxImageSize: PDFJS.maxImageSize,
disableFontFace: PDFJS.disableFontFace
});
},

View File

@ -16,26 +16,14 @@
*/
/* globals ColorSpace, DeviceCmykCS, DeviceGrayCS, DeviceRgbCS, error,
FONT_IDENTITY_MATRIX, IDENTITY_MATRIX, ImageData, isArray, isNum,
Pattern, TilingPattern, TODO, Util, warn, assert, info */
Pattern, TilingPattern, TODO, Util, warn, assert, info,
TextRenderingMode */
'use strict';
// <canvas> contexts store most of the state we need natively.
// However, PDF needs a bit more state, which we store here.
var TextRenderingMode = {
FILL: 0,
STROKE: 1,
FILL_STROKE: 2,
INVISIBLE: 3,
FILL_ADD_TO_PATH: 4,
STROKE_ADD_TO_PATH: 5,
FILL_STROKE_ADD_TO_PATH: 6,
ADD_TO_PATH: 7,
FILL_STROKE_MASK: 3,
ADD_TO_PATH_FLAG: 4
};
// Minimal font size that would be used during canvas fillText operations.
var MIN_FONT_SIZE = 16;
@ -1007,7 +995,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
var addToPath;
if (font.disableFontFace || isAddToPathSet) {
addToPath = font.renderer.getPathGenerator(character);
addToPath = font.getPathGenerator(this.commonObjs, character);
}
if (font.disableFontFace) {

View File

@ -14,8 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals PDFJS, shadow, isWorker, assert, warn, FontRendererFactory,
bytesToString, globalScope */
/* globals PDFJS, shadow, isWorker, assert, warn, bytesToString, globalScope */
'use strict';
@ -267,6 +266,7 @@ var FontLoader = {
var FontFace = (function FontFaceClosure() {
function FontFace(name, file, properties) {
this.compiledGlyphs = {};
if (arguments.length === 1) {
// importing translated data
var data = arguments[0];
@ -277,10 +277,6 @@ var FontFace = (function FontFaceClosure() {
}
}
FontFace.prototype = {
get renderer() {
var renderer = FontRendererFactory.create(this);
return shadow(this, 'renderer', renderer);
},
bindDOM: function FontFace_bindDOM() {
if (!this.data)
return null;
@ -305,6 +301,14 @@ var FontFace = (function FontFaceClosure() {
globalScope['FontInspector'].fontAdded(this, url);
return rule;
},
getPathGenerator: function (objs, character) {
if (!(character in this.compiledGlyphs)) {
var js = objs.get(this.loadedName + '_path_' + character);
/*jshint -W054 */
this.compiledGlyphs[character] = new Function('c', 'size', js);
}
return this.compiledGlyphs[character];
}
};
return FontFace;

View File

@ -27,6 +27,19 @@ var verbosity = WARNINGS;
var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
var TextRenderingMode = {
FILL: 0,
STROKE: 1,
FILL_STROKE: 2,
INVISIBLE: 3,
FILL_ADD_TO_PATH: 4,
STROKE_ADD_TO_PATH: 5,
FILL_STROKE_ADD_TO_PATH: 6,
ADD_TO_PATH: 7,
FILL_STROKE_MASK: 3,
ADD_TO_PATH_FLAG: 4
};
// The global PDFJS object exposes the API
// In production, it will be declared outside a global wrapper
// In development, it will be declared here

View File

@ -35,6 +35,7 @@ var files = [
'core/crypto.js',
'core/evaluator.js',
'core/fonts.js',
'core/font_renderer.js',
'core/glyphlist.js',
'core/image.js',
'core/metrics.js',

BIN
test/pdfs/issue3584.pdf Normal file

Binary file not shown.

View File

@ -1063,6 +1063,13 @@
"link": true,
"type": "eq"
},
{ "id": "issue3584",
"file": "pdfs/issue3584.pdf",
"md5": "7a00646865a840eefc76f05c588b60ce",
"rounds": 1,
"type": "eq",
"about": "CFF font that is drawn with clipping."
},
{ "id": "annotation-tx",
"file": "pdfs/annotation-tx.pdf",
"md5": "56321ea830be9c4f8437ca17ac535b2d",

View File

@ -28,7 +28,6 @@ limitations under the License.
<script type="text/javascript" src="/src/display/metadata.js"></script>
<script type="text/javascript" src="/src/display/canvas.js"></script>
<script type="text/javascript" src="/src/display/font_loader.js"></script>
<script type="text/javascript" src="/src/display/font_renderer.js"></script>
<script type="text/javascript" src="driver.js"></script>
<script type="text/javascript">

View File

@ -50,7 +50,6 @@ limitations under the License.
<script type="text/javascript" src="../src/display/metadata.js"></script>
<script type="text/javascript" src="../src/display/canvas.js"></script>
<script type="text/javascript" src="../src/display/font_loader.js"></script>
<script type="text/javascript" src="../src/display/font_renderer.js"></script>
<script type="text/javascript">PDFJS.workerSrc = '../src/worker_loader.js';</script>
<!--#endif-->