Browse Source

Merge pull request #241 from cgjones/workers

#123, part 1: create PartialEvaluator
Andreas Gal 14 years ago
parent
commit
47c29974c4
  1. 444
      pdf.js

444
pdf.js

@ -3385,18 +3385,13 @@ var Encodings = {
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
// <canvas> contexts store most of the state we need natively. var EvalState = (function() {
// However, PDF needs a bit more state, which we store here.
var CanvasExtraState = (function() {
function constructor() { function constructor() {
// Are soft masks and alpha values shapes or opacities? // Are soft masks and alpha values shapes or opacities?
this.alphaIsShape = false; this.alphaIsShape = false;
this.fontSize = 0; this.fontSize = 0;
this.textMatrix = IDENTITY_MATRIX; this.textMatrix = IDENTITY_MATRIX;
this.leading = 0; this.leading = 0;
// Current point (in user coordinates)
this.x = 0;
this.y = 0;
// Start of text line (in text coordinates) // Start of text line (in text coordinates)
this.lineX = 0; this.lineX = 0;
this.lineY = 0; this.lineY = 0;
@ -3413,126 +3408,187 @@ var CanvasExtraState = (function() {
return constructor; return constructor;
})(); })();
function ScratchCanvas(width, height) { var PartialEvaluator = (function() {
var canvas = document.createElement('canvas'); function constructor() {
canvas.width = width; this.state = new EvalState();
canvas.height = height; this.stateStack = [ ];
return canvas;
}
var CanvasGraphics = (function() {
function constructor(canvasCtx, imageCanvas) {
this.ctx = canvasCtx;
this.current = new CanvasExtraState();
this.stateStack = [];
this.pendingClip = null;
this.res = null;
this.xobjs = null;
this.ScratchCanvas = imageCanvas || ScratchCanvas;
} }
var LINE_CAP_STYLES = ['butt', 'round', 'square']; var OP_MAP = {
var LINE_JOIN_STYLES = ['miter', 'round', 'bevel']; // Graphics state
var NORMAL_CLIP = {}; w: 'setLineWidth',
var EO_CLIP = {}; J: 'setLineCap',
j: 'setLineJoin',
M: 'setMiterLimit',
d: 'setDash',
ri: 'setRenderingIntent',
i: 'setFlatness',
gs: 'setGState',
q: 'save',
Q: 'restore',
cm: 'transform',
// Used for tiling patterns // Path
var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2; m: 'moveTo',
l: 'lineTo',
c: 'curveTo',
v: 'curveTo2',
y: 'curveTo3',
h: 'closePath',
re: 'rectangle',
S: 'stroke',
s: 'closeStroke',
f: 'fill',
F: 'fill',
'f*': 'eoFill',
B: 'fillStroke',
'B*': 'eoFillStroke',
b: 'closeFillStroke',
'b*': 'closeEOFillStroke',
n: 'endPath',
// Clipping
W: 'clip',
'W*': 'eoClip',
// Text
BT: 'beginText',
ET: 'endText',
Tc: 'setCharSpacing',
Tw: 'setWordSpacing',
Tz: 'setHScale',
TL: 'setLeading',
Tf: 'setFont',
Tr: 'setTextRenderingMode',
Ts: 'setTextRise',
Td: 'moveText',
TD: 'setLeadingMoveText',
Tm: 'setTextMatrix',
'T*': 'nextLine',
Tj: 'showText',
TJ: 'showSpacedText',
"'": 'nextLineShowText',
'"': 'nextLineSetSpacingShowText',
// Type3 fonts
d0: 'setCharWidth',
d1: 'setCharWidthAndBounds',
// Color
CS: 'setStrokeColorSpace',
cs: 'setFillColorSpace',
SC: 'setStrokeColor',
SCN: 'setStrokeColorN',
sc: 'setFillColor',
scn: 'setFillColorN',
G: 'setStrokeGray',
g: 'setFillGray',
RG: 'setStrokeRGBColor',
rg: 'setFillRGBColor',
K: 'setStrokeCMYKColor',
k: 'setFillCMYKColor',
// Shading
sh: 'shadingFill',
// Images
BI: 'beginInlineImage',
// XObjects
Do: 'paintXObject',
// Marked content
MP: 'markPoint',
DP: 'markPointProps',
BMC: 'beginMarkedContent',
BDC: 'beginMarkedContentProps',
EMC: 'endMarkedContent',
// Compatibility
BX: 'beginCompat',
EX: 'endCompat'
};
constructor.prototype = { constructor.prototype = {
map: { eval: function(stream, xref, resources, fonts) {
// Graphics state resources = xref.fetchIfRef(resources) || new Dict();
w: 'setLineWidth', var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
J: 'setLineCap',
j: 'setLineJoin', var parser = new Parser(new Lexer(stream), false);
M: 'setMiterLimit', var objpool = [];
d: 'setDash',
ri: 'setRenderingIntent', function emitArg(arg) {
i: 'setFlatness', if (typeof arg == 'object' || typeof arg == 'string') {
gs: 'setGState', var index = objpool.length;
q: 'save', objpool[index] = arg;
Q: 'restore', return 'objpool[' + index + ']';
cm: 'transform', }
return arg;
// Path }
m: 'moveTo',
l: 'lineTo', var src = '';
c: 'curveTo',
v: 'curveTo2', var args = [];
y: 'curveTo3', var obj;
h: 'closePath', while (!IsEOF(obj = parser.getObj())) {
re: 'rectangle', if (IsCmd(obj)) {
S: 'stroke', var cmd = obj.cmd;
s: 'closeStroke', var fn = OP_MAP[cmd];
f: 'fill', assertWellFormed(fn, "Unknown command '" + cmd + "'");
F: 'fill', // TODO figure out how to type-check vararg functions
'f*': 'eoFill',
B: 'fillStroke', if (cmd == 'Do' && !args[0].code) { // eagerly compile XForm objects
'B*': 'eoFillStroke', var name = args[0].name;
b: 'closeFillStroke', var xobj = xobjs.get(name);
'b*': 'closeEOFillStroke', if (xobj) {
n: 'endPath', xobj = xref.fetchIfRef(xobj);
assertWellFormed(IsStream(xobj), 'XObject should be a stream');
// Clipping
W: 'clip', var type = xobj.dict.get('Subtype');
'W*': 'eoClip', assertWellFormed(
IsName(type),
// Text 'XObject should have a Name subtype'
BT: 'beginText', );
ET: 'endText',
Tc: 'setCharSpacing', if ('Form' == type.name) {
Tw: 'setWordSpacing', args[0].code = this.eval(xobj,
Tz: 'setHScale', xref,
TL: 'setLeading', xobj.dict.get('Resources'),
Tf: 'setFont', fonts);
Tr: 'setTextRenderingMode', }
Ts: 'setTextRise', }
Td: 'moveText', } else if (cmd == 'Tf') { // eagerly collect all fonts
TD: 'setLeadingMoveText', var fontRes = resources.get('Font');
Tm: 'setTextMatrix', if (fontRes) {
'T*': 'nextLine', fontRes = xref.fetchIfRef(fontRes);
Tj: 'showText', var font = xref.fetchIfRef(fontRes.get(args[0].name));
TJ: 'showSpacedText', assertWellFormed(IsDict(font));
"'": 'nextLineShowText', if (!font.translated) {
'"': 'nextLineSetSpacingShowText', font.translated = this.translateFont(font, xref, resources);
if (fonts && font.translated) {
// Type3 fonts // keep track of each font we translated so the caller can
d0: 'setCharWidth', // load them asynchronously before calling display on a page
d1: 'setCharWidthAndBounds', fonts.push(font.translated);
}
// Color }
CS: 'setStrokeColorSpace', }
cs: 'setFillColorSpace', }
SC: 'setStrokeColor',
SCN: 'setStrokeColorN', src += 'this.';
sc: 'setFillColor', src += fn;
scn: 'setFillColorN', src += '(';
G: 'setStrokeGray', src += args.map(emitArg).join(',');
g: 'setFillGray', src += ');\n';
RG: 'setStrokeRGBColor',
rg: 'setFillRGBColor', args.length = 0;
K: 'setStrokeCMYKColor', } else {
k: 'setFillCMYKColor', assertWellFormed(args.length <= 33, 'Too many arguments');
args.push(obj);
// Shading }
sh: 'shadingFill', }
// Images var fn = Function('objpool', src);
BI: 'beginInlineImage', return function(gfx) { fn.call(gfx, objpool); };
// XObjects
Do: 'paintXObject',
// Marked content
MP: 'markPoint',
DP: 'markPointProps',
BMC: 'beginMarkedContent',
BDC: 'beginMarkedContentProps',
EMC: 'endMarkedContent',
// Compatibility
BX: 'beginCompat',
EX: 'endCompat'
}, },
translateFont: function(fontDict, xref, resources) { translateFont: function(fontDict, xref, resources) {
@ -3697,7 +3753,66 @@ var CanvasGraphics = (function() {
properties: properties properties: properties
}; };
}, },
};
return constructor;
})();
// <canvas> contexts store most of the state we need natively.
// However, PDF needs a bit more state, which we store here.
var CanvasExtraState = (function() {
function constructor() {
// Are soft masks and alpha values shapes or opacities?
this.alphaIsShape = false;
this.fontSize = 0;
this.textMatrix = IDENTITY_MATRIX;
this.leading = 0;
// Current point (in user coordinates)
this.x = 0;
this.y = 0;
// Start of text line (in text coordinates)
this.lineX = 0;
this.lineY = 0;
// Character and word spacing
this.charSpace = 0;
this.wordSpace = 0;
this.textHScale = 100;
// Color spaces
this.fillColorSpace = null;
this.strokeColorSpace = null;
}
constructor.prototype = {
};
return constructor;
})();
function ScratchCanvas(width, height) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
}
var CanvasGraphics = (function() {
function constructor(canvasCtx, imageCanvas) {
this.ctx = canvasCtx;
this.current = new CanvasExtraState();
this.stateStack = [];
this.pendingClip = null;
this.res = null;
this.xobjs = null;
this.ScratchCanvas = imageCanvas || ScratchCanvas;
}
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
var NORMAL_CLIP = {};
var EO_CLIP = {};
// Used for tiling patterns
var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
constructor.prototype = {
beginDrawing: function(mediaBox) { beginDrawing: function(mediaBox) {
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height; var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
this.ctx.save(); this.ctx.save();
@ -3705,6 +3820,11 @@ var CanvasGraphics = (function() {
this.ctx.translate(0, -mediaBox.height); this.ctx.translate(0, -mediaBox.height);
}, },
compile: function(stream, xref, resources, fonts) {
var pe = new PartialEvaluator();
return pe.eval(stream, xref, resources, fonts);
},
execute: function(code, xref, resources) { execute: function(code, xref, resources) {
resources = xref.fetchIfRef(resources) || new Dict(); resources = xref.fetchIfRef(resources) || new Dict();
var savedXref = this.xref, savedRes = this.res, savedXobjs = this.xobjs; var savedXref = this.xref, savedRes = this.res, savedXobjs = this.xobjs;
@ -3719,88 +3839,6 @@ var CanvasGraphics = (function() {
this.xref = savedXref; this.xref = savedXref;
}, },
compile: function(stream, xref, resources, fonts) {
resources = xref.fetchIfRef(resources) || new Dict();
var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
var parser = new Parser(new Lexer(stream), false);
var objpool = [];
function emitArg(arg) {
if (typeof arg == 'object' || typeof arg == 'string') {
var index = objpool.length;
objpool[index] = arg;
return 'objpool[' + index + ']';
}
return arg;
}
var src = '';
var args = [];
var map = this.map;
var obj;
while (!IsEOF(obj = parser.getObj())) {
if (IsCmd(obj)) {
var cmd = obj.cmd;
var fn = map[cmd];
assertWellFormed(fn, "Unknown command '" + cmd + "'");
// TODO figure out how to type-check vararg functions
if (cmd == 'Do' && !args[0].code) { // eagerly compile XForm objects
var name = args[0].name;
var xobj = xobjs.get(name);
if (xobj) {
xobj = xref.fetchIfRef(xobj);
assertWellFormed(IsStream(xobj), 'XObject should be a stream');
var type = xobj.dict.get('Subtype');
assertWellFormed(
IsName(type),
'XObject should have a Name subtype'
);
if ('Form' == type.name) {
args[0].code = this.compile(xobj,
xref,
xobj.dict.get('Resources'),
fonts);
}
}
} else if (cmd == 'Tf') { // eagerly collect all fonts
var fontRes = resources.get('Font');
if (fontRes) {
fontRes = xref.fetchIfRef(fontRes);
var font = xref.fetchIfRef(fontRes.get(args[0].name));
assertWellFormed(IsDict(font));
if (!font.translated) {
font.translated = this.translateFont(font, xref, resources);
if (fonts && font.translated) {
// keep track of each font we translated so the caller can
// load them asynchronously before calling display on a page
fonts.push(font.translated);
}
}
}
}
src += 'this.';
src += fn;
src += '(';
src += args.map(emitArg).join(',');
src += ');\n';
args.length = 0;
} else {
assertWellFormed(args.length <= 33, 'Too many arguments');
args.push(obj);
}
}
var fn = Function('objpool', src);
return function(gfx) { fn.call(gfx, objpool); };
},
endDrawing: function() { endDrawing: function() {
this.ctx.restore(); this.ctx.restore();
}, },

Loading…
Cancel
Save