|
|
|
@ -44,7 +44,7 @@ function assertWellFormed(cond, msg) {
@@ -44,7 +44,7 @@ function assertWellFormed(cond, msg) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function shadow(obj, prop, value) { |
|
|
|
|
Object.defineProperty(obj, prop, { value: value, enumerable: true }); |
|
|
|
|
Object.defineProperty(obj, prop, { value: value, enumerable: true, configurable: true, writable: false }); |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2923,9 +2923,15 @@ var XRef = (function() {
@@ -2923,9 +2923,15 @@ var XRef = (function() {
|
|
|
|
|
|
|
|
|
|
var Page = (function() { |
|
|
|
|
function constructor(xref, pageNumber, pageDict) { |
|
|
|
|
this.xref = xref; |
|
|
|
|
this.pageNumber = pageNumber; |
|
|
|
|
this.pageDict = pageDict; |
|
|
|
|
this.stats = { |
|
|
|
|
create: Date.now(), |
|
|
|
|
compile: 0.0, |
|
|
|
|
fonts: 0.0, |
|
|
|
|
render: 0.0, |
|
|
|
|
}; |
|
|
|
|
this.xref = xref; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
constructor.prototype = { |
|
|
|
@ -2954,6 +2960,32 @@ var Page = (function() {
@@ -2954,6 +2960,32 @@ var Page = (function() {
|
|
|
|
|
return shadow(this, 'mediaBox', |
|
|
|
|
((IsArray(obj) && obj.length == 4) ? obj : null)); |
|
|
|
|
}, |
|
|
|
|
startRendering: function(canvasCtx, continuation) { |
|
|
|
|
var self = this; |
|
|
|
|
var stats = self.stats; |
|
|
|
|
stats.compile = stats.fonts = stats.render = 0; |
|
|
|
|
|
|
|
|
|
var gfx = new CanvasGraphics(canvasCtx); |
|
|
|
|
var fonts = [ ]; |
|
|
|
|
|
|
|
|
|
this.compile(gfx, fonts); |
|
|
|
|
stats.compile = Date.now(); |
|
|
|
|
|
|
|
|
|
FontLoader.bind( |
|
|
|
|
fonts, |
|
|
|
|
function() { |
|
|
|
|
stats.fonts = Date.now(); |
|
|
|
|
// Always defer call to display() to work around bug in
|
|
|
|
|
// Firefox error reporting from XHR callbacks.
|
|
|
|
|
setTimeout(function () { |
|
|
|
|
self.display(gfx); |
|
|
|
|
stats.render = Date.now(); |
|
|
|
|
continuation(); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
compile: function(gfx, fonts) { |
|
|
|
|
if (this.code) { |
|
|
|
|
// content was compiled
|
|
|
|
@ -3385,18 +3417,13 @@ var Encodings = {
@@ -3385,18 +3417,13 @@ var Encodings = {
|
|
|
|
|
|
|
|
|
|
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; |
|
|
|
|
|
|
|
|
|
// <canvas> contexts store most of the state we need natively.
|
|
|
|
|
// However, PDF needs a bit more state, which we store here.
|
|
|
|
|
var CanvasExtraState = (function() { |
|
|
|
|
var EvalState = (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; |
|
|
|
@ -3413,34 +3440,13 @@ var CanvasExtraState = (function() {
@@ -3413,34 +3440,13 @@ var CanvasExtraState = (function() {
|
|
|
|
|
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(); |
|
|
|
|
var PartialEvaluator = (function() { |
|
|
|
|
function constructor() { |
|
|
|
|
this.state = new EvalState(); |
|
|
|
|
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 = { |
|
|
|
|
map: { |
|
|
|
|
var OP_MAP = { |
|
|
|
|
// Graphics state
|
|
|
|
|
w: 'setLineWidth', |
|
|
|
|
J: 'setLineCap', |
|
|
|
@ -3533,6 +3539,88 @@ var CanvasGraphics = (function() {
@@ -3533,6 +3539,88 @@ var CanvasGraphics = (function() {
|
|
|
|
|
// Compatibility
|
|
|
|
|
BX: 'beginCompat', |
|
|
|
|
EX: 'endCompat' |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
constructor.prototype = { |
|
|
|
|
eval: 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 obj; |
|
|
|
|
while (!IsEOF(obj = parser.getObj())) { |
|
|
|
|
if (IsCmd(obj)) { |
|
|
|
|
var cmd = obj.cmd; |
|
|
|
|
var fn = OP_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.eval(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); }; |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
translateFont: function(fontDict, xref, resources) { |
|
|
|
@ -3697,7 +3785,66 @@ var CanvasGraphics = (function() {
@@ -3697,7 +3785,66 @@ var CanvasGraphics = (function() {
|
|
|
|
|
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) { |
|
|
|
|
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height; |
|
|
|
|
this.ctx.save(); |
|
|
|
@ -3705,6 +3852,11 @@ var CanvasGraphics = (function() {
@@ -3705,6 +3852,11 @@ var CanvasGraphics = (function() {
|
|
|
|
|
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) { |
|
|
|
|
resources = xref.fetchIfRef(resources) || new Dict(); |
|
|
|
|
var savedXref = this.xref, savedRes = this.res, savedXobjs = this.xobjs; |
|
|
|
@ -3719,88 +3871,6 @@ var CanvasGraphics = (function() {
@@ -3719,88 +3871,6 @@ var CanvasGraphics = (function() {
|
|
|
|
|
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() { |
|
|
|
|
this.ctx.restore(); |
|
|
|
|
}, |
|
|
|
@ -4349,8 +4419,8 @@ var CanvasGraphics = (function() {
@@ -4349,8 +4419,8 @@ var CanvasGraphics = (function() {
|
|
|
|
|
// Most likely we will not implement other types of shading
|
|
|
|
|
// unless the browser supports them
|
|
|
|
|
if (!shadingFn) { |
|
|
|
|
TODO("Unknown or NYI type of shading '"+ typeNum +"'"); |
|
|
|
|
return this.makeCssRgb(0, 0, 0); |
|
|
|
|
warn("Unknown or NYI type of shading '"+ typeNum +"'"); |
|
|
|
|
return 'hotpink'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return shadingFn.call(this, shading, cs); |
|
|
|
@ -5310,13 +5380,16 @@ var PDFFunction = (function() {
@@ -5310,13 +5380,16 @@ var PDFFunction = (function() {
|
|
|
|
|
return array; |
|
|
|
|
}, |
|
|
|
|
constructInterpolated: function() { |
|
|
|
|
error('unhandled type of function'); |
|
|
|
|
TODO('unhandled type of function'); |
|
|
|
|
this.func = function () { return [ 255, 105, 180 ]; } |
|
|
|
|
}, |
|
|
|
|
constructStiched: function() { |
|
|
|
|
error('unhandled type of function'); |
|
|
|
|
TODO('unhandled type of function'); |
|
|
|
|
this.func = function () { return [ 255, 105, 180 ]; } |
|
|
|
|
}, |
|
|
|
|
constructPostScript: function() { |
|
|
|
|
error('unhandled type of function'); |
|
|
|
|
TODO('unhandled type of function'); |
|
|
|
|
this.func = function () { return [ 255, 105, 180 ]; } |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|