| @@ -0,0 +1,306 @@ | ||
| /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
| /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | ||
|
|
||
| 'use strict'; | ||
|
|
||
| var PDFFunction = (function() { | ||
| var CONSTRUCT_SAMPLED = 0; | ||
| var CONSTRUCT_INTERPOLATED = 2; | ||
| var CONSTRUCT_STICHED = 3; | ||
| var CONSTRUCT_POSTSCRIPT = 4; | ||
|
|
||
| return { | ||
| getSampleArray: function(size, outputSize, bps, str) { | ||
| var length = 1; | ||
| for (var i = 0; i < size.length; i++) | ||
| length *= size[i]; | ||
| length *= outputSize; | ||
|
|
||
| var array = []; | ||
| var codeSize = 0; | ||
| var codeBuf = 0; | ||
|
|
||
| var strBytes = str.getBytes((length * bps + 7) / 8); | ||
| var strIdx = 0; | ||
| for (var i = 0; i < length; i++) { | ||
| while (codeSize < bps) { | ||
| codeBuf <<= 8; | ||
| codeBuf |= strBytes[strIdx++]; | ||
| codeSize += 8; | ||
| } | ||
| codeSize -= bps; | ||
| array.push(codeBuf >> codeSize); | ||
| codeBuf &= (1 << codeSize) - 1; | ||
| } | ||
| return array; | ||
| }, | ||
|
|
||
| getIR: function(xref, fn) { | ||
| var dict = fn.dict; | ||
| if (!dict) | ||
| dict = fn; | ||
|
|
||
| var types = [this.constructSampled, | ||
| null, | ||
| this.constructInterpolated, | ||
| this.constructStiched, | ||
| this.constructPostScript]; | ||
|
|
||
| var typeNum = dict.get('FunctionType'); | ||
| var typeFn = types[typeNum]; | ||
| if (!typeFn) | ||
| error('Unknown type of function'); | ||
|
|
||
| return typeFn.call(this, fn, dict, xref); | ||
| }, | ||
|
|
||
| fromIR: function(IR) { | ||
| var type = IR[0]; | ||
| switch (type) { | ||
| case CONSTRUCT_SAMPLED: | ||
| return this.constructSampledFromIR(IR); | ||
| case CONSTRUCT_INTERPOLATED: | ||
| return this.constructInterpolatedFromIR(IR); | ||
| case CONSTRUCT_STICHED: | ||
| return this.constructStichedFromIR(IR); | ||
| case CONSTRUCT_POSTSCRIPT: | ||
| default: | ||
| return this.constructPostScriptFromIR(IR); | ||
| } | ||
| }, | ||
|
|
||
| parse: function(xref, fn) { | ||
| var IR = this.getIR(xref, fn); | ||
| return this.fromIR(IR); | ||
| }, | ||
|
|
||
| constructSampled: function(str, dict) { | ||
| var domain = dict.get('Domain'); | ||
| var range = dict.get('Range'); | ||
|
|
||
| if (!domain || !range) | ||
| error('No domain or range'); | ||
|
|
||
| var inputSize = domain.length / 2; | ||
| var outputSize = range.length / 2; | ||
|
|
||
| if (inputSize != 1) | ||
| error('No support for multi-variable inputs to functions: ' + | ||
| inputSize); | ||
|
|
||
| var size = dict.get('Size'); | ||
| var bps = dict.get('BitsPerSample'); | ||
| var order = dict.get('Order'); | ||
| if (!order) | ||
| order = 1; | ||
| if (order !== 1) | ||
| error('No support for cubic spline interpolation: ' + order); | ||
|
|
||
| var encode = dict.get('Encode'); | ||
| if (!encode) { | ||
| encode = []; | ||
| for (var i = 0; i < inputSize; ++i) { | ||
| encode.push(0); | ||
| encode.push(size[i] - 1); | ||
| } | ||
| } | ||
| var decode = dict.get('Decode'); | ||
| if (!decode) | ||
| decode = range; | ||
|
|
||
| var samples = this.getSampleArray(size, outputSize, bps, str); | ||
|
|
||
| return [ | ||
| CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size, | ||
| outputSize, bps, range | ||
| ]; | ||
| }, | ||
|
|
||
| constructSampledFromIR: function(IR) { | ||
| var inputSize = IR[1]; | ||
| var domain = IR[2]; | ||
| var encode = IR[3]; | ||
| var decode = IR[4]; | ||
| var samples = IR[5]; | ||
| var size = IR[6]; | ||
| var outputSize = IR[7]; | ||
| var bps = IR[8]; | ||
| var range = IR[9]; | ||
|
|
||
| return function(args) { | ||
| var clip = function(v, min, max) { | ||
| if (v > max) | ||
| v = max; | ||
| else if (v < min) | ||
| v = min; | ||
| return v; | ||
| }; | ||
|
|
||
| if (inputSize != args.length) | ||
| error('Incorrect number of arguments: ' + inputSize + ' != ' + | ||
| args.length); | ||
|
|
||
| for (var i = 0; i < inputSize; i++) { | ||
| var i2 = i * 2; | ||
|
|
||
| // clip to the domain | ||
| var v = clip(args[i], domain[i2], domain[i2 + 1]); | ||
|
|
||
| // encode | ||
| v = encode[i2] + ((v - domain[i2]) * | ||
| (encode[i2 + 1] - encode[i2]) / | ||
| (domain[i2 + 1] - domain[i2])); | ||
|
|
||
| // clip to the size | ||
| args[i] = clip(v, 0, size[i] - 1); | ||
| } | ||
|
|
||
| // interpolate to table | ||
| TODO('Multi-dimensional interpolation'); | ||
| var floor = Math.floor(args[0]); | ||
| var ceil = Math.ceil(args[0]); | ||
| var scale = args[0] - floor; | ||
|
|
||
| floor *= outputSize; | ||
| ceil *= outputSize; | ||
|
|
||
| var output = [], v = 0; | ||
| for (var i = 0; i < outputSize; ++i) { | ||
| if (ceil == floor) { | ||
| v = samples[ceil + i]; | ||
| } else { | ||
| var low = samples[floor + i]; | ||
| var high = samples[ceil + i]; | ||
| v = low * scale + high * (1 - scale); | ||
| } | ||
|
|
||
| var i2 = i * 2; | ||
| // decode | ||
| v = decode[i2] + (v * (decode[i2 + 1] - decode[i2]) / | ||
| ((1 << bps) - 1)); | ||
|
|
||
| // clip to the domain | ||
| output.push(clip(v, range[i2], range[i2 + 1])); | ||
| } | ||
|
|
||
| return output; | ||
| } | ||
| }, | ||
|
|
||
| constructInterpolated: | ||
| function pdfFunctionConstructInterpolated(str, dict) { | ||
| var c0 = dict.get('C0') || [0]; | ||
| var c1 = dict.get('C1') || [1]; | ||
| var n = dict.get('N'); | ||
|
|
||
| if (!isArray(c0) || !isArray(c1)) | ||
| error('Illegal dictionary for interpolated function'); | ||
|
|
||
| var length = c0.length; | ||
| var diff = []; | ||
| for (var i = 0; i < length; ++i) | ||
| diff.push(c1[i] - c0[i]); | ||
|
|
||
| return [CONSTRUCT_INTERPOLATED, c0, diff, n]; | ||
| }, | ||
|
|
||
| constructInterpolatedFromIR: | ||
| function pdfFunctionconstructInterpolatedFromIR(IR) { | ||
| var c0 = IR[1]; | ||
| var diff = IR[2]; | ||
| var n = IR[3]; | ||
|
|
||
| var length = diff.length; | ||
|
|
||
| return function(args) { | ||
| var x = n == 1 ? args[0] : Math.pow(args[0], n); | ||
|
|
||
| var out = []; | ||
| for (var j = 0; j < length; ++j) | ||
| out.push(c0[j] + (x * diff[j])); | ||
|
|
||
| return out; | ||
|
|
||
| } | ||
| }, | ||
|
|
||
| constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) { | ||
| var domain = dict.get('Domain'); | ||
| var range = dict.get('Range'); | ||
|
|
||
| if (!domain) | ||
| error('No domain'); | ||
|
|
||
| var inputSize = domain.length / 2; | ||
| if (inputSize != 1) | ||
| error('Bad domain for stiched function'); | ||
|
|
||
| var fnRefs = dict.get('Functions'); | ||
| var fns = []; | ||
| for (var i = 0, ii = fnRefs.length; i < ii; ++i) | ||
| fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i]))); | ||
|
|
||
| var bounds = dict.get('Bounds'); | ||
| var encode = dict.get('Encode'); | ||
|
|
||
| return [CONSTRUCT_STICHED, domain, bounds, encode, fns]; | ||
| }, | ||
|
|
||
| constructStichedFromIR: function pdfFunctionConstructStichedFromIR(IR) { | ||
| var domain = IR[1]; | ||
| var bounds = IR[2]; | ||
| var encode = IR[3]; | ||
| var fnsIR = IR[4]; | ||
| var fns = []; | ||
|
|
||
| for (var i = 0; i < fnsIR.length; i++) { | ||
| fns.push(PDFFunction.fromIR(fnsIR[i])); | ||
| } | ||
|
|
||
| return function(args) { | ||
| var clip = function(v, min, max) { | ||
| if (v > max) | ||
| v = max; | ||
| else if (v < min) | ||
| v = min; | ||
| return v; | ||
| }; | ||
|
|
||
| // clip to domain | ||
| var v = clip(args[0], domain[0], domain[1]); | ||
| // calulate which bound the value is in | ||
| for (var i = 0, ii = bounds.length; i < ii; ++i) { | ||
| if (v < bounds[i]) | ||
| break; | ||
| } | ||
|
|
||
| // encode value into domain of function | ||
| var dmin = domain[0]; | ||
| if (i > 0) | ||
| dmin = bounds[i - 1]; | ||
| var dmax = domain[1]; | ||
| if (i < bounds.length) | ||
| dmax = bounds[i]; | ||
|
|
||
| var rmin = encode[2 * i]; | ||
| var rmax = encode[2 * i + 1]; | ||
|
|
||
| var v2 = rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin); | ||
|
|
||
| // call the appropropriate function | ||
| return fns[i]([v2]); | ||
| }; | ||
| }, | ||
|
|
||
| constructPostScript: function pdfFunctionConstructPostScript() { | ||
| return [CONSTRUCT_POSTSCRIPT]; | ||
| }, | ||
|
|
||
| constructPostScriptFromIR: function pdfFunctionConstructPostScriptFromIR() { | ||
| TODO('unhandled type of function'); | ||
| return function() { | ||
| return [255, 105, 180]; | ||
| }; | ||
| } | ||
| }; | ||
| })(); |
| @@ -0,0 +1,256 @@ | ||
| /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
| /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | ||
|
|
||
| 'use strict'; | ||
|
|
||
| var PDFImage = (function pdfImage() { | ||
| function constructor(xref, res, image, inline) { | ||
| this.image = image; | ||
| if (image.getParams) { | ||
| // JPX/JPEG2000 streams directly contain bits per component | ||
| // and color space mode information. | ||
| TODO('get params from actual stream'); | ||
| // var bits = ... | ||
| // var colorspace = ... | ||
| } | ||
| // TODO cache rendered images? | ||
|
|
||
| var dict = image.dict; | ||
| this.width = dict.get('Width', 'W'); | ||
| this.height = dict.get('Height', 'H'); | ||
|
|
||
| if (this.width < 1 || this.height < 1) | ||
| error('Invalid image width: ' + this.width + ' or height: ' + | ||
| this.height); | ||
|
|
||
| this.interpolate = dict.get('Interpolate', 'I') || false; | ||
| this.imageMask = dict.get('ImageMask', 'IM') || false; | ||
|
|
||
| var bitsPerComponent = image.bitsPerComponent; | ||
| if (!bitsPerComponent) { | ||
| bitsPerComponent = dict.get('BitsPerComponent', 'BPC'); | ||
| if (!bitsPerComponent) { | ||
| if (this.imageMask) | ||
| bitsPerComponent = 1; | ||
| else | ||
| error('Bits per component missing in image: ' + this.imageMask); | ||
| } | ||
| } | ||
| this.bpc = bitsPerComponent; | ||
|
|
||
| if (!this.imageMask) { | ||
| var colorSpace = dict.get('ColorSpace', 'CS'); | ||
| if (!colorSpace) { | ||
| TODO('JPX images (which don"t require color spaces'); | ||
| colorSpace = new Name('DeviceRGB'); | ||
| } | ||
| this.colorSpace = ColorSpace.parse(colorSpace, xref, res); | ||
| this.numComps = this.colorSpace.numComps; | ||
| } | ||
|
|
||
| this.decode = dict.get('Decode', 'D'); | ||
|
|
||
| var mask = xref.fetchIfRef(dict.get('Mask')); | ||
| var smask = xref.fetchIfRef(dict.get('SMask')); | ||
|
|
||
| if (mask) { | ||
| TODO('masked images'); | ||
| } else if (smask) { | ||
| this.smask = new PDFImage(xref, res, smask); | ||
| } | ||
| } | ||
|
|
||
| constructor.prototype = { | ||
| getComponents: function getComponents(buffer, decodeMap) { | ||
| var bpc = this.bpc; | ||
| if (bpc == 8) | ||
| return buffer; | ||
|
|
||
| var width = this.width; | ||
| var height = this.height; | ||
| var numComps = this.numComps; | ||
|
|
||
| var length = width * height; | ||
| var bufferPos = 0; | ||
| var output = bpc <= 8 ? new Uint8Array(length) : | ||
| bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length); | ||
| var rowComps = width * numComps; | ||
|
|
||
| if (bpc == 1) { | ||
| var valueZero = 0, valueOne = 1; | ||
| if (decodeMap) { | ||
| valueZero = decodeMap[0] ? 1 : 0; | ||
| valueOne = decodeMap[1] ? 1 : 0; | ||
| } | ||
| var mask = 0; | ||
| var buf = 0; | ||
|
|
||
| for (var i = 0, ii = length; i < ii; ++i) { | ||
| if (i % rowComps == 0) { | ||
| mask = 0; | ||
| buf = 0; | ||
| } else { | ||
| mask >>= 1; | ||
| } | ||
|
|
||
| if (mask <= 0) { | ||
| buf = buffer[bufferPos++]; | ||
| mask = 128; | ||
| } | ||
|
|
||
| output[i] = !(buf & mask) ? valueZero : valueOne; | ||
| } | ||
| } else { | ||
| if (decodeMap != null) | ||
| TODO('interpolate component values'); | ||
| var bits = 0, buf = 0; | ||
| for (var i = 0, ii = length; i < ii; ++i) { | ||
| if (i % rowComps == 0) { | ||
| buf = 0; | ||
| bits = 0; | ||
| } | ||
|
|
||
| while (bits < bpc) { | ||
| buf = (buf << 8) | buffer[bufferPos++]; | ||
| bits += 8; | ||
| } | ||
|
|
||
| var remainingBits = bits - bpc; | ||
| output[i] = buf >> remainingBits; | ||
| buf = buf & ((1 << remainingBits) - 1); | ||
| bits = remainingBits; | ||
| } | ||
| } | ||
| return output; | ||
| }, | ||
| getOpacity: function getOpacity() { | ||
| var smask = this.smask; | ||
| var width = this.width; | ||
| var height = this.height; | ||
| var buf = new Uint8Array(width * height); | ||
|
|
||
| if (smask) { | ||
| if (smask.image.getImage) { | ||
| // smask is a DOM image | ||
| var tempCanvas = new ScratchCanvas(width, height); | ||
| var tempCtx = tempCanvas.getContext('2d'); | ||
| var domImage = smask.image.getImage(); | ||
| tempCtx.drawImage(domImage, 0, 0, domImage.width, domImage.height, | ||
| 0, 0, width, height); | ||
| var data = tempCtx.getImageData(0, 0, width, height).data; | ||
| for (var i = 0, j = 0, ii = width * height; i < ii; ++i, j += 4) | ||
| buf[i] = data[j]; // getting first component value | ||
| return buf; | ||
| } | ||
| var sw = smask.width; | ||
| var sh = smask.height; | ||
| if (sw != this.width || sh != this.height) | ||
| error('smask dimensions do not match image dimensions: ' + sw + | ||
| ' != ' + this.width + ', ' + sh + ' != ' + this.height); | ||
|
|
||
| smask.fillGrayBuffer(buf); | ||
| return buf; | ||
| } else { | ||
| for (var i = 0, ii = width * height; i < ii; ++i) | ||
| buf[i] = 255; | ||
| } | ||
| return buf; | ||
| }, | ||
| applyStencilMask: function applyStencilMask(buffer, inverseDecode) { | ||
| var width = this.width, height = this.height; | ||
| var bitStrideLength = (width + 7) >> 3; | ||
| this.image.reset(); | ||
| var imgArray = this.image.getBytes(bitStrideLength * height); | ||
| var imgArrayPos = 0; | ||
| var i, j, mask, buf; | ||
| // removing making non-masked pixels transparent | ||
| var bufferPos = 3; // alpha component offset | ||
| for (i = 0; i < height; i++) { | ||
| mask = 0; | ||
| for (j = 0; j < width; j++) { | ||
| if (!mask) { | ||
| buf = imgArray[imgArrayPos++]; | ||
| mask = 128; | ||
| } | ||
| if (!(buf & mask) == inverseDecode) { | ||
| buffer[bufferPos] = 0; | ||
| } | ||
| bufferPos += 4; | ||
| mask >>= 1; | ||
| } | ||
| } | ||
| }, | ||
| fillRgbaBuffer: function fillRgbaBuffer(buffer, decodeMap) { | ||
| var numComps = this.numComps; | ||
| var width = this.width; | ||
| var height = this.height; | ||
| var bpc = this.bpc; | ||
|
|
||
| // rows start at byte boundary; | ||
| var rowBytes = (width * numComps * bpc + 7) >> 3; | ||
| this.image.reset(); | ||
| var imgArray = this.image.getBytes(height * rowBytes); | ||
|
|
||
| var comps = this.colorSpace.getRgbBuffer( | ||
| this.getComponents(imgArray, decodeMap), bpc); | ||
| var compsPos = 0; | ||
| var opacity = this.getOpacity(); | ||
| var opacityPos = 0; | ||
| var length = width * height * 4; | ||
|
|
||
| for (var i = 0; i < length; i += 4) { | ||
| buffer[i] = comps[compsPos++]; | ||
| buffer[i + 1] = comps[compsPos++]; | ||
| buffer[i + 2] = comps[compsPos++]; | ||
| buffer[i + 3] = opacity[opacityPos++]; | ||
| } | ||
| }, | ||
| fillGrayBuffer: function fillGrayBuffer(buffer) { | ||
| var numComps = this.numComps; | ||
| if (numComps != 1) | ||
| error('Reading gray scale from a color image: ' + numComps); | ||
|
|
||
| var width = this.width; | ||
| var height = this.height; | ||
| var bpc = this.bpc; | ||
|
|
||
| // rows start at byte boundary; | ||
| var rowBytes = (width * numComps * bpc + 7) >> 3; | ||
| this.image.reset(); | ||
| var imgArray = this.image.getBytes(height * rowBytes); | ||
|
|
||
| var comps = this.getComponents(imgArray); | ||
| var length = width * height; | ||
|
|
||
| for (var i = 0; i < length; ++i) | ||
| buffer[i] = comps[i]; | ||
| } | ||
| }; | ||
| return constructor; | ||
| })(); | ||
|
|
||
| var JpegImage = (function() { | ||
| function JpegImage(objId, imageData, objs) { | ||
| var src = 'data:image/jpeg;base64,' + window.btoa(imageData); | ||
|
|
||
| var img = new Image(); | ||
| img.onload = (function() { | ||
| this.loaded = true; | ||
|
|
||
| objs.resolve(objId, this); | ||
|
|
||
| if (this.onLoad) | ||
| this.onLoad(); | ||
| }).bind(this); | ||
| img.src = src; | ||
| this.domImage = img; | ||
| } | ||
|
|
||
| JpegImage.prototype = { | ||
| getImage: function() { | ||
| return this.domImage; | ||
| } | ||
| }; | ||
|
|
||
| return JpegImage; | ||
| })(); |
| @@ -0,0 +1,289 @@ | ||
| /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
| /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | ||
|
|
||
| 'use strict'; | ||
|
|
||
| var Pattern = (function patternPattern() { | ||
| // Constructor should define this.getPattern | ||
| function constructor() { | ||
| error('should not call Pattern constructor'); | ||
| } | ||
|
|
||
| constructor.prototype = { | ||
| // Input: current Canvas context | ||
| // Output: the appropriate fillStyle or strokeStyle | ||
| getPattern: function pattern_getStyle(ctx) { | ||
| error('Should not call Pattern.getStyle: ' + ctx); | ||
| } | ||
| }; | ||
|
|
||
| constructor.shadingFromIR = function pattern_shadingFromIR(ctx, raw) { | ||
| return Shadings[raw[0]].fromIR(ctx, raw); | ||
| } | ||
|
|
||
| constructor.parseShading = function pattern_shading(shading, matrix, | ||
| xref, res, ctx) { | ||
|
|
||
| var dict = isStream(shading) ? shading.dict : shading; | ||
| var type = dict.get('ShadingType'); | ||
|
|
||
| switch (type) { | ||
| case 2: | ||
| case 3: | ||
| // both radial and axial shadings are handled by RadialAxial shading | ||
| return new Shadings.RadialAxial(dict, matrix, xref, res, ctx); | ||
| default: | ||
| return new Shadings.Dummy(); | ||
| } | ||
| }; | ||
| return constructor; | ||
| })(); | ||
|
|
||
| var Shadings = {}; | ||
|
|
||
| // Radial and axial shading have very similar implementations | ||
| // If needed, the implementations can be broken into two classes | ||
| Shadings.RadialAxial = (function radialAxialShading() { | ||
| function constructor(dict, matrix, xref, res, ctx) { | ||
| this.matrix = matrix; | ||
| this.coordsArr = dict.get('Coords'); | ||
| this.shadingType = dict.get('ShadingType'); | ||
| this.type = 'Pattern'; | ||
|
|
||
| this.ctx = ctx; | ||
| var cs = dict.get('ColorSpace', 'CS'); | ||
| cs = ColorSpace.parse(cs, xref, res); | ||
| this.cs = cs; | ||
|
|
||
| var t0 = 0.0, t1 = 1.0; | ||
| if (dict.has('Domain')) { | ||
| var domainArr = dict.get('Domain'); | ||
| t0 = domainArr[0]; | ||
| t1 = domainArr[1]; | ||
| } | ||
|
|
||
| var extendStart = false, extendEnd = false; | ||
| if (dict.has('Extend')) { | ||
| var extendArr = dict.get('Extend'); | ||
| extendStart = extendArr[0]; | ||
| extendEnd = extendArr[1]; | ||
| TODO('Support extend'); | ||
| } | ||
|
|
||
| this.extendStart = extendStart; | ||
| this.extendEnd = extendEnd; | ||
|
|
||
| var fnObj = dict.get('Function'); | ||
| fnObj = xref.fetchIfRef(fnObj); | ||
| if (isArray(fnObj)) | ||
| error('No support for array of functions'); | ||
| else if (!isPDFFunction(fnObj)) | ||
| error('Invalid function'); | ||
| var fn = PDFFunction.parse(xref, fnObj); | ||
|
|
||
| // 10 samples seems good enough for now, but probably won't work | ||
| // if there are sharp color changes. Ideally, we would implement | ||
| // the spec faithfully and add lossless optimizations. | ||
| var step = (t1 - t0) / 10; | ||
| var diff = t1 - t0; | ||
|
|
||
| var colorStops = []; | ||
| for (var i = t0; i <= t1; i += step) { | ||
| var color = fn([i]); | ||
| var rgbColor = Util.makeCssRgb.apply(this, cs.getRgb(color)); | ||
| colorStops.push([(i - t0) / diff, rgbColor]); | ||
| } | ||
|
|
||
| this.colorStops = colorStops; | ||
| } | ||
|
|
||
| constructor.fromIR = function(ctx, raw) { | ||
| var type = raw[1]; | ||
| var colorStops = raw[2]; | ||
| var p0 = raw[3]; | ||
| var p1 = raw[4]; | ||
| var r0 = raw[5]; | ||
| var r1 = raw[6]; | ||
|
|
||
| var curMatrix = ctx.mozCurrentTransform; | ||
| if (curMatrix) { | ||
| var userMatrix = ctx.mozCurrentTransformInverse; | ||
|
|
||
| p0 = Util.applyTransform(p0, curMatrix); | ||
| p0 = Util.applyTransform(p0, userMatrix); | ||
|
|
||
| p1 = Util.applyTransform(p1, curMatrix); | ||
| p1 = Util.applyTransform(p1, userMatrix); | ||
| } | ||
|
|
||
| if (type == 2) | ||
| var grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]); | ||
| else if (type == 3) | ||
| var grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1); | ||
|
|
||
| for (var i = 0, ii = colorStops.length; i < ii; ++i) { | ||
| var c = colorStops[i]; | ||
| grad.addColorStop(c[0], c[1]); | ||
| } | ||
| return grad; | ||
| } | ||
|
|
||
| constructor.prototype = { | ||
| getIR: function RadialAxialShading_getIR() { | ||
| var coordsArr = this.coordsArr; | ||
| var type = this.shadingType; | ||
| if (type == 2) { | ||
| var p0 = [coordsArr[0], coordsArr[1]]; | ||
| var p1 = [coordsArr[2], coordsArr[3]]; | ||
| var r0 = null; | ||
| var r1 = null; | ||
| } else if (type == 3) { | ||
| var p0 = [coordsArr[0], coordsArr[1]]; | ||
| var p1 = [coordsArr[3], coordsArr[4]]; | ||
| var r0 = coordsArr[2]; | ||
| var r1 = coordsArr[5]; | ||
| } else { | ||
| error('getPattern type unknown: ' + type); | ||
| } | ||
|
|
||
| var matrix = this.matrix; | ||
| if (matrix) { | ||
| p0 = Util.applyTransform(p0, matrix); | ||
| p1 = Util.applyTransform(p1, matrix); | ||
| } | ||
|
|
||
| return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1]; | ||
| } | ||
| }; | ||
|
|
||
| return constructor; | ||
| })(); | ||
|
|
||
| Shadings.Dummy = (function dummyShading() { | ||
| function constructor() { | ||
| this.type = 'Pattern'; | ||
| } | ||
|
|
||
| constructor.fromIR = function() { | ||
| return 'hotpink'; | ||
| } | ||
|
|
||
| constructor.prototype = { | ||
| getIR: function dummpy_getir() { | ||
| return ['Dummy']; | ||
| } | ||
| }; | ||
| return constructor; | ||
| })(); | ||
|
|
||
| var TilingPattern = (function tilingPattern() { | ||
| var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2; | ||
|
|
||
| function TilingPattern(IR, color, ctx, objs) { | ||
| var IRQueue = IR[2]; | ||
| this.matrix = IR[3]; | ||
| var bbox = IR[4]; | ||
| var xstep = IR[5]; | ||
| var ystep = IR[6]; | ||
| var paintType = IR[7]; | ||
|
|
||
| TODO('TilingType'); | ||
|
|
||
| this.curMatrix = ctx.mozCurrentTransform; | ||
| this.invMatrix = ctx.mozCurrentTransformInverse; | ||
| this.ctx = ctx; | ||
| this.type = 'Pattern'; | ||
|
|
||
| var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; | ||
|
|
||
| var topLeft = [x0, y0]; | ||
| // we want the canvas to be as large as the step size | ||
| var botRight = [x0 + xstep, y0 + ystep]; | ||
|
|
||
| var width = botRight[0] - topLeft[0]; | ||
| var height = botRight[1] - topLeft[1]; | ||
|
|
||
| // TODO: hack to avoid OOM, we would idealy compute the tiling | ||
| // pattern to be only as large as the acual size in device space | ||
| // This could be computed with .mozCurrentTransform, but still | ||
| // needs to be implemented | ||
| while (Math.abs(width) > 512 || Math.abs(height) > 512) { | ||
| width = 512; | ||
| height = 512; | ||
| } | ||
|
|
||
| var tmpCanvas = new ScratchCanvas(width, height); | ||
|
|
||
| // set the new canvas element context as the graphics context | ||
| var tmpCtx = tmpCanvas.getContext('2d'); | ||
| var graphics = new CanvasGraphics(tmpCtx, objs); | ||
|
|
||
| switch (paintType) { | ||
| case PAINT_TYPE_COLORED: | ||
| tmpCtx.fillStyle = ctx.fillStyle; | ||
| tmpCtx.strokeStyle = ctx.strokeStyle; | ||
| break; | ||
| case PAINT_TYPE_UNCOLORED: | ||
| color = Util.makeCssRgb.apply(this, color); | ||
| tmpCtx.fillStyle = color; | ||
| tmpCtx.strokeStyle = color; | ||
| break; | ||
| default: | ||
| error('Unsupported paint type: ' + paintType); | ||
| } | ||
|
|
||
| var scale = [width / xstep, height / ystep]; | ||
| this.scale = scale; | ||
|
|
||
| // transform coordinates to pattern space | ||
| var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]]; | ||
| var tmpScale = [scale[0], 0, 0, scale[1], 0, 0]; | ||
| graphics.transform.apply(graphics, tmpScale); | ||
| graphics.transform.apply(graphics, tmpTranslate); | ||
|
|
||
| if (bbox && isArray(bbox) && 4 == bbox.length) { | ||
| var bboxWidth = bbox[2] - bbox[0]; | ||
| var bboxHeight = bbox[3] - bbox[1]; | ||
| graphics.rectangle(bbox[0], bbox[1], bboxWidth, bboxHeight); | ||
| graphics.clip(); | ||
| graphics.endPath(); | ||
| } | ||
|
|
||
| graphics.executeIRQueue(IRQueue); | ||
|
|
||
| this.canvas = tmpCanvas; | ||
| } | ||
|
|
||
| TilingPattern.getIR = function tiling_getIR(codeIR, dict, args) { | ||
| var matrix = dict.get('Matrix'); | ||
| var bbox = dict.get('BBox'); | ||
| var xstep = dict.get('XStep'); | ||
| var ystep = dict.get('YStep'); | ||
| var paintType = dict.get('PaintType'); | ||
|
|
||
| return [ | ||
| 'TilingPattern', args, codeIR, matrix, bbox, xstep, ystep, paintType | ||
| ]; | ||
| } | ||
|
|
||
| TilingPattern.prototype = { | ||
| getPattern: function tiling_getPattern() { | ||
| var matrix = this.matrix; | ||
| var curMatrix = this.curMatrix; | ||
| var ctx = this.ctx; | ||
|
|
||
| if (curMatrix) | ||
| ctx.setTransform.apply(ctx, curMatrix); | ||
|
|
||
| if (matrix) | ||
| ctx.transform.apply(ctx, matrix); | ||
|
|
||
| var scale = this.scale; | ||
| ctx.scale(1 / scale[0], 1 / scale[1]); | ||
|
|
||
| return ctx.createPattern(this.canvas, 'repeat'); | ||
| } | ||
| }; | ||
|
|
||
| return TilingPattern; | ||
| })(); |
| @@ -0,0 +1,14 @@ | ||
| /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
| /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | ||
|
|
||
| var PDFJS = {}; | ||
|
|
||
| (function pdfjsWrapper() { | ||
|
|
||
| // Use strict in our context only - users might not want it | ||
| 'use strict'; | ||
|
|
||
| // Files are inserted below - see Makefile | ||
| /* PDFJSSCRIPT_INCLUDE_ALL */ | ||
|
|
||
| })(); |
| @@ -0,0 +1,285 @@ | ||
| /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
| /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | ||
|
|
||
| 'use strict'; | ||
|
|
||
| function log(msg) { | ||
| if (console && console.log) | ||
| console.log(msg); | ||
| else if (print) | ||
| print(msg); | ||
| } | ||
|
|
||
| function warn(msg) { | ||
| if (verbosity >= WARNINGS) | ||
| log('Warning: ' + msg); | ||
| } | ||
|
|
||
| function backtrace() { | ||
| var stackStr; | ||
| try { | ||
| throw new Error(); | ||
| } catch (e) { | ||
| stackStr = e.stack; | ||
| } | ||
| return stackStr.split('\n').slice(1).join('\n'); | ||
| } | ||
|
|
||
| function error(msg) { | ||
| log(backtrace()); | ||
| throw new Error(msg); | ||
| } | ||
|
|
||
| function TODO(what) { | ||
| if (verbosity >= TODOS) | ||
| log('TODO: ' + what); | ||
| } | ||
|
|
||
| function malformed(msg) { | ||
| error('Malformed PDF: ' + msg); | ||
| } | ||
|
|
||
| function assert(cond, msg) { | ||
| if (!cond) | ||
| error(msg); | ||
| } | ||
|
|
||
| // In a well-formed PDF, |cond| holds. If it doesn't, subsequent | ||
| // behavior is undefined. | ||
| function assertWellFormed(cond, msg) { | ||
| if (!cond) | ||
| malformed(msg); | ||
| } | ||
|
|
||
| function shadow(obj, prop, value) { | ||
| Object.defineProperty(obj, prop, { value: value, | ||
| enumerable: true, | ||
| configurable: true, | ||
| writable: false }); | ||
| return value; | ||
| } | ||
|
|
||
| function bytesToString(bytes) { | ||
| var str = ''; | ||
| var length = bytes.length; | ||
| for (var n = 0; n < length; ++n) | ||
| str += String.fromCharCode(bytes[n]); | ||
| return str; | ||
| } | ||
|
|
||
| function stringToBytes(str) { | ||
| var length = str.length; | ||
| var bytes = new Uint8Array(length); | ||
| for (var n = 0; n < length; ++n) | ||
| bytes[n] = str.charCodeAt(n) & 0xFF; | ||
| return bytes; | ||
| } | ||
|
|
||
| var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; | ||
|
|
||
| var Util = (function utilUtil() { | ||
| function constructor() {} | ||
| constructor.makeCssRgb = function makergb(r, g, b) { | ||
| var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0; | ||
| return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; | ||
| }; | ||
| constructor.makeCssCmyk = function makecmyk(c, m, y, k) { | ||
| c = (new DeviceCmykCS()).getRgb([c, m, y, k]); | ||
| var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0; | ||
| return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; | ||
| }; | ||
| constructor.applyTransform = function apply(p, m) { | ||
| var xt = p[0] * m[0] + p[1] * m[2] + m[4]; | ||
| var yt = p[0] * m[1] + p[1] * m[3] + m[5]; | ||
| return [xt, yt]; | ||
| }; | ||
|
|
||
| return constructor; | ||
| })(); | ||
|
|
||
| var PDFStringTranslateTable = [ | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, | ||
| 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C, | ||
| 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160, | ||
| 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC | ||
| ]; | ||
|
|
||
| function stringToPDFString(str) { | ||
| var i, n = str.length, str2 = ''; | ||
| if (str[0] === '\xFE' && str[1] === '\xFF') { | ||
| // UTF16BE BOM | ||
| for (i = 2; i < n; i += 2) | ||
| str2 += String.fromCharCode( | ||
| (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)); | ||
| } else { | ||
| for (i = 0; i < n; ++i) { | ||
| var code = PDFStringTranslateTable[str.charCodeAt(i)]; | ||
| str2 += code ? String.fromCharCode(code) : str.charAt(i); | ||
| } | ||
| } | ||
| return str2; | ||
| } | ||
|
|
||
| function isBool(v) { | ||
| return typeof v == 'boolean'; | ||
| } | ||
|
|
||
| function isInt(v) { | ||
| return typeof v == 'number' && ((v | 0) == v); | ||
| } | ||
|
|
||
| function isNum(v) { | ||
| return typeof v == 'number'; | ||
| } | ||
|
|
||
| function isString(v) { | ||
| return typeof v == 'string'; | ||
| } | ||
|
|
||
| function isNull(v) { | ||
| return v === null; | ||
| } | ||
|
|
||
| function isName(v) { | ||
| return v instanceof Name; | ||
| } | ||
|
|
||
| function isCmd(v, cmd) { | ||
| return v instanceof Cmd && (!cmd || v.cmd == cmd); | ||
| } | ||
|
|
||
| function isDict(v, type) { | ||
| return v instanceof Dict && (!type || v.get('Type').name == type); | ||
| } | ||
|
|
||
| function isArray(v) { | ||
| return v instanceof Array; | ||
| } | ||
|
|
||
| function isStream(v) { | ||
| return typeof v == 'object' && v != null && ('getChar' in v); | ||
| } | ||
|
|
||
| function isArrayBuffer(v) { | ||
| return typeof v == 'object' && v != null && ('byteLength' in v); | ||
| } | ||
|
|
||
| function isRef(v) { | ||
| return v instanceof Ref; | ||
| } | ||
|
|
||
| function isPDFFunction(v) { | ||
| var fnDict; | ||
| if (typeof v != 'object') | ||
| return false; | ||
| else if (isDict(v)) | ||
| fnDict = v; | ||
| else if (isStream(v)) | ||
| fnDict = v.dict; | ||
| else | ||
| return false; | ||
| return fnDict.has('FunctionType'); | ||
| } | ||
|
|
||
| /** | ||
| * 'Promise' object. | ||
| * Each object that is stored in PDFObjects is based on a Promise object that | ||
| * contains the status of the object and the data. There migth be situations, | ||
| * where a function want to use the value of an object, but it isn't ready at | ||
| * that time. To get a notification, once the object is ready to be used, s.o. | ||
| * can add a callback using the `then` method on the promise that then calls | ||
| * the callback once the object gets resolved. | ||
| * A promise can get resolved only once and only once the data of the promise | ||
| * can be set. If any of these happens twice or the data is required before | ||
| * it was set, an exception is throw. | ||
| */ | ||
| var Promise = (function() { | ||
| var EMPTY_PROMISE = {}; | ||
|
|
||
| /** | ||
| * If `data` is passed in this constructor, the promise is created resolved. | ||
| * If there isn't data, it isn't resolved at the beginning. | ||
| */ | ||
| function Promise(name, data) { | ||
| this.name = name; | ||
| // If you build a promise and pass in some data it's already resolved. | ||
| if (data != null) { | ||
| this.isResolved = true; | ||
| this._data = data; | ||
| this.hasData = true; | ||
| } else { | ||
| this.isResolved = false; | ||
| this._data = EMPTY_PROMISE; | ||
| } | ||
| this.callbacks = []; | ||
| }; | ||
|
|
||
| Promise.prototype = { | ||
| hasData: false, | ||
|
|
||
| set data(data) { | ||
| if (data === undefined) { | ||
| return; | ||
| } | ||
| if (this._data !== EMPTY_PROMISE) { | ||
| throw 'Promise ' + this.name + | ||
| ': Cannot set the data of a promise twice'; | ||
| } | ||
| this._data = data; | ||
| this.hasData = true; | ||
|
|
||
| if (this.onDataCallback) { | ||
| this.onDataCallback(data); | ||
| } | ||
| }, | ||
|
|
||
| get data() { | ||
| if (this._data === EMPTY_PROMISE) { | ||
| throw 'Promise ' + this.name + ': Cannot get data that isn\'t set'; | ||
| } | ||
| return this._data; | ||
| }, | ||
|
|
||
| onData: function(callback) { | ||
| if (this._data !== EMPTY_PROMISE) { | ||
| callback(this._data); | ||
| } else { | ||
| this.onDataCallback = callback; | ||
| } | ||
| }, | ||
|
|
||
| resolve: function(data) { | ||
| if (this.isResolved) { | ||
| throw 'A Promise can be resolved only once ' + this.name; | ||
| } | ||
|
|
||
| this.isResolved = true; | ||
| this.data = data; | ||
| var callbacks = this.callbacks; | ||
|
|
||
| for (var i = 0; i < callbacks.length; i++) { | ||
| callbacks[i].call(null, data); | ||
| } | ||
| }, | ||
|
|
||
| then: function(callback) { | ||
| if (!callback) { | ||
| throw 'Requiring callback' + this.name; | ||
| } | ||
|
|
||
| // If the promise is already resolved, call the callback directly. | ||
| if (this.isResolved) { | ||
| var data = this.data; | ||
| callback.call(null, data); | ||
| } else { | ||
| this.callbacks.push(callback); | ||
| } | ||
| } | ||
| }; | ||
| return Promise; | ||
| })(); |
| @@ -0,0 +1,23 @@ | ||
| /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
| /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | ||
|
|
||
| 'use strict'; | ||
|
|
||
| importScripts('../src/core.js'); | ||
| importScripts('../src/util.js'); | ||
| importScripts('../src/canvas.js'); | ||
| importScripts('../src/obj.js'); | ||
| importScripts('../src/function.js'); | ||
| importScripts('../src/charsets.js'); | ||
| importScripts('../src/cidmaps.js'); | ||
| importScripts('../src/colorspace.js'); | ||
| importScripts('../src/crypto.js'); | ||
| importScripts('../src/evaluator.js'); | ||
| importScripts('../src/fonts.js'); | ||
| importScripts('../src/glyphlist.js'); | ||
| importScripts('../src/image.js'); | ||
| importScripts('../src/metrics.js'); | ||
| importScripts('../src/parser.js'); | ||
| importScripts('../src/pattern.js'); | ||
| importScripts('../src/stream.js'); | ||
| importScripts('../src/worker.js'); |
| @@ -0,0 +1 @@ | ||
| viewer-production.html |
| @@ -0,0 +1,2 @@ | ||
| <!-- This snippet is used in production, see Makefile --> | ||
| <script type="text/javascript" src="../build/pdf.js"></script> |