1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
3 /* Copyright 2012 Mozilla Foundation
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 /*jshint globalstrict: false */
20 // Initializing PDFJS global object (if still undefined)
21 if (typeof PDFJS === 'undefined') {
22 (typeof window !== 'undefined' ? window : this).PDFJS = {};
25 PDFJS.version = '1.1.159';
26 PDFJS.build = '82536f8';
28 (function pdfjsWrapper() {
29 // Use strict in our context only - users might not want it
32 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
33 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
34 /* Copyright 2012 Mozilla Foundation
36 * Licensed under the Apache License, Version 2.0 (the "License");
37 * you may not use this file except in compliance with the License.
38 * You may obtain a copy of the License at
40 * http://www.apache.org/licenses/LICENSE-2.0
42 * Unless required by applicable law or agreed to in writing, software
43 * distributed under the License is distributed on an "AS IS" BASIS,
44 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45 * See the License for the specific language governing permissions and
46 * limitations under the License.
48 /* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL,
53 var globalScope = (typeof window === 'undefined') ? this : window;
55 var isWorker = (typeof window === 'undefined');
57 var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
59 var TextRenderingMode = {
65 STROKE_ADD_TO_PATH: 5,
66 FILL_STROKE_ADD_TO_PATH: 6,
78 var AnnotationType = {
111 // The global PDFJS object exposes the API
112 // In production, it will be declared outside a global wrapper
113 // In development, it will be declared here
114 if (!globalScope.PDFJS) {
115 globalScope.PDFJS = {};
118 globalScope.PDFJS.pdfBug = false;
120 PDFJS.VERBOSITY_LEVELS = {
126 // All the possible operations for an operator list.
127 var OPS = PDFJS.OPS = {
128 // Intentionally start from 1 so it is easy to spot bad operators that will be
136 setRenderingIntent: 7,
156 closeEOFillStroke: 27,
167 setTextRenderingMode: 38,
170 setLeadingMoveText: 41,
175 nextLineShowText: 46,
176 nextLineSetSpacingShowText: 47,
178 setCharWidthAndBounds: 49,
179 setStrokeColorSpace: 50,
180 setFillColorSpace: 51,
187 setStrokeRGBColor: 58,
189 setStrokeCMYKColor: 60,
190 setFillCMYKColor: 61,
192 beginInlineImage: 63,
198 beginMarkedContent: 69,
199 beginMarkedContentProps: 70,
200 endMarkedContent: 71,
203 paintFormXObjectBegin: 74,
204 paintFormXObjectEnd: 75,
207 beginAnnotations: 78,
211 paintJpegXObject: 82,
212 paintImageMaskXObject: 83,
213 paintImageMaskXObjectGroup: 84,
214 paintImageXObject: 85,
215 paintInlineImageXObject: 86,
216 paintInlineImageXObjectGroup: 87,
217 paintImageXObjectRepeat: 88,
218 paintImageMaskXObjectRepeat: 89,
219 paintSolidColorImageMask: 90,
223 // A notice for devs. These are good for things that are helpful to devs, such
224 // as warning that Workers were disabled, which is important to devs but not
227 if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.infos) {
228 console.log('Info: ' + msg);
232 // Non-fatal warnings.
234 if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.warnings) {
235 console.log('Warning: ' + msg);
239 // Fatal errors that should trigger the fallback UI and halt execution by
240 // throwing an exception.
241 function error(msg) {
242 if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.errors) {
243 console.log('Error: ' + msg);
244 console.log(backtrace());
246 UnsupportedManager.notify(UNSUPPORTED_FEATURES.unknown);
247 throw new Error(msg);
250 function backtrace() {
254 return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
258 function assert(cond, msg) {
264 var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = {
267 javaScript: 'javaScript',
269 shadingPattern: 'shadingPattern',
273 var UnsupportedManager = PDFJS.UnsupportedManager =
274 (function UnsupportedManagerClosure() {
277 listen: function (cb) {
280 notify: function (featureId) {
281 warn('Unsupported feature "' + featureId + '"');
282 for (var i = 0, ii = listeners.length; i < ii; i++) {
283 listeners[i](featureId);
289 // Combines two URLs. The baseUrl shall be absolute URL. If the url is an
290 // absolute URL, it will be returned as is.
291 function combineUrl(baseUrl, url) {
295 if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) {
299 if (url.charAt(0) === '/') {
301 i = baseUrl.indexOf('://');
302 if (url.charAt(1) === '/') {
305 i = baseUrl.indexOf('/', i + 3);
307 return baseUrl.substring(0, i) + url;
310 var pathLength = baseUrl.length;
311 i = baseUrl.lastIndexOf('#');
312 pathLength = i >= 0 ? i : pathLength;
313 i = baseUrl.lastIndexOf('?', pathLength);
314 pathLength = i >= 0 ? i : pathLength;
315 var prefixLength = baseUrl.lastIndexOf('/', pathLength);
316 return baseUrl.substring(0, prefixLength + 1) + url;
320 // Validates if URL is safe and allowed, e.g. to avoid XSS.
321 function isValidUrl(url, allowRelative) {
325 // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1)
326 // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
327 var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url);
329 return allowRelative;
331 protocol = protocol[0].toLowerCase();
343 PDFJS.isValidUrl = isValidUrl;
345 function shadow(obj, prop, value) {
346 Object.defineProperty(obj, prop, { value: value,
352 PDFJS.shadow = shadow;
354 var PasswordResponses = PDFJS.PasswordResponses = {
356 INCORRECT_PASSWORD: 2
359 var PasswordException = (function PasswordExceptionClosure() {
360 function PasswordException(msg, code) {
361 this.name = 'PasswordException';
366 PasswordException.prototype = new Error();
367 PasswordException.constructor = PasswordException;
369 return PasswordException;
371 PDFJS.PasswordException = PasswordException;
373 var UnknownErrorException = (function UnknownErrorExceptionClosure() {
374 function UnknownErrorException(msg, details) {
375 this.name = 'UnknownErrorException';
377 this.details = details;
380 UnknownErrorException.prototype = new Error();
381 UnknownErrorException.constructor = UnknownErrorException;
383 return UnknownErrorException;
385 PDFJS.UnknownErrorException = UnknownErrorException;
387 var InvalidPDFException = (function InvalidPDFExceptionClosure() {
388 function InvalidPDFException(msg) {
389 this.name = 'InvalidPDFException';
393 InvalidPDFException.prototype = new Error();
394 InvalidPDFException.constructor = InvalidPDFException;
396 return InvalidPDFException;
398 PDFJS.InvalidPDFException = InvalidPDFException;
400 var MissingPDFException = (function MissingPDFExceptionClosure() {
401 function MissingPDFException(msg) {
402 this.name = 'MissingPDFException';
406 MissingPDFException.prototype = new Error();
407 MissingPDFException.constructor = MissingPDFException;
409 return MissingPDFException;
411 PDFJS.MissingPDFException = MissingPDFException;
413 var UnexpectedResponseException =
414 (function UnexpectedResponseExceptionClosure() {
415 function UnexpectedResponseException(msg, status) {
416 this.name = 'UnexpectedResponseException';
418 this.status = status;
421 UnexpectedResponseException.prototype = new Error();
422 UnexpectedResponseException.constructor = UnexpectedResponseException;
424 return UnexpectedResponseException;
426 PDFJS.UnexpectedResponseException = UnexpectedResponseException;
428 var NotImplementedException = (function NotImplementedExceptionClosure() {
429 function NotImplementedException(msg) {
433 NotImplementedException.prototype = new Error();
434 NotImplementedException.prototype.name = 'NotImplementedException';
435 NotImplementedException.constructor = NotImplementedException;
437 return NotImplementedException;
440 var MissingDataException = (function MissingDataExceptionClosure() {
441 function MissingDataException(begin, end) {
444 this.message = 'Missing data [' + begin + ', ' + end + ')';
447 MissingDataException.prototype = new Error();
448 MissingDataException.prototype.name = 'MissingDataException';
449 MissingDataException.constructor = MissingDataException;
451 return MissingDataException;
454 var XRefParseException = (function XRefParseExceptionClosure() {
455 function XRefParseException(msg) {
459 XRefParseException.prototype = new Error();
460 XRefParseException.prototype.name = 'XRefParseException';
461 XRefParseException.constructor = XRefParseException;
463 return XRefParseException;
467 function bytesToString(bytes) {
468 assert(bytes !== null && typeof bytes === 'object' &&
469 bytes.length !== undefined, 'Invalid argument for bytesToString');
470 var length = bytes.length;
471 var MAX_ARGUMENT_COUNT = 8192;
472 if (length < MAX_ARGUMENT_COUNT) {
473 return String.fromCharCode.apply(null, bytes);
476 for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
477 var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
478 var chunk = bytes.subarray(i, chunkEnd);
479 strBuf.push(String.fromCharCode.apply(null, chunk));
481 return strBuf.join('');
484 function stringToBytes(str) {
485 assert(typeof str === 'string', 'Invalid argument for stringToBytes');
486 var length = str.length;
487 var bytes = new Uint8Array(length);
488 for (var i = 0; i < length; ++i) {
489 bytes[i] = str.charCodeAt(i) & 0xFF;
494 function string32(value) {
495 return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff,
496 (value >> 8) & 0xff, value & 0xff);
508 function readInt8(data, start) {
509 return (data[start] << 24) >> 24;
512 function readUint16(data, offset) {
513 return (data[offset] << 8) | data[offset + 1];
516 function readUint32(data, offset) {
517 return ((data[offset] << 24) | (data[offset + 1] << 16) |
518 (data[offset + 2] << 8) | data[offset + 3]) >>> 0;
521 // Lazy test the endianness of the platform
522 // NOTE: This will be 'true' for simulated TypedArrays
523 function isLittleEndian() {
524 var buffer8 = new Uint8Array(2);
526 var buffer16 = new Uint16Array(buffer8.buffer);
527 return (buffer16[0] === 1);
530 Object.defineProperty(PDFJS, 'isLittleEndian', {
532 get: function PDFJS_isLittleEndian() {
533 return shadow(PDFJS, 'isLittleEndian', isLittleEndian());
537 // Lazy test if the userAgant support CanvasTypedArrays
538 function hasCanvasTypedArrays() {
539 var canvas = document.createElement('canvas');
540 canvas.width = canvas.height = 1;
541 var ctx = canvas.getContext('2d');
542 var imageData = ctx.createImageData(1, 1);
543 return (typeof imageData.data.buffer !== 'undefined');
546 Object.defineProperty(PDFJS, 'hasCanvasTypedArrays', {
548 get: function PDFJS_hasCanvasTypedArrays() {
549 return shadow(PDFJS, 'hasCanvasTypedArrays', hasCanvasTypedArrays());
553 var Uint32ArrayView = (function Uint32ArrayViewClosure() {
555 function Uint32ArrayView(buffer, length) {
556 this.buffer = buffer;
557 this.byteLength = buffer.length;
558 this.length = length === undefined ? (this.byteLength >> 2) : length;
559 ensureUint32ArrayViewProps(this.length);
561 Uint32ArrayView.prototype = Object.create(null);
563 var uint32ArrayViewSetters = 0;
564 function createUint32ArrayProp(index) {
567 var buffer = this.buffer, offset = index << 2;
568 return (buffer[offset] | (buffer[offset + 1] << 8) |
569 (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)) >>> 0;
571 set: function (value) {
572 var buffer = this.buffer, offset = index << 2;
573 buffer[offset] = value & 255;
574 buffer[offset + 1] = (value >> 8) & 255;
575 buffer[offset + 2] = (value >> 16) & 255;
576 buffer[offset + 3] = (value >>> 24) & 255;
581 function ensureUint32ArrayViewProps(length) {
582 while (uint32ArrayViewSetters < length) {
583 Object.defineProperty(Uint32ArrayView.prototype,
584 uint32ArrayViewSetters,
585 createUint32ArrayProp(uint32ArrayViewSetters));
586 uint32ArrayViewSetters++;
590 return Uint32ArrayView;
593 var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
595 var Util = PDFJS.Util = (function UtilClosure() {
598 var rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')'];
600 // makeCssRgb() can be called thousands of times. Using |rgbBuf| avoids
601 // creating many intermediate strings.
602 Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
606 return rgbBuf.join('');
609 // Concatenates two transformation matrices together and returns the result.
610 Util.transform = function Util_transform(m1, m2) {
612 m1[0] * m2[0] + m1[2] * m2[1],
613 m1[1] * m2[0] + m1[3] * m2[1],
614 m1[0] * m2[2] + m1[2] * m2[3],
615 m1[1] * m2[2] + m1[3] * m2[3],
616 m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
617 m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
621 // For 2d affine transforms
622 Util.applyTransform = function Util_applyTransform(p, m) {
623 var xt = p[0] * m[0] + p[1] * m[2] + m[4];
624 var yt = p[0] * m[1] + p[1] * m[3] + m[5];
628 Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
629 var d = m[0] * m[3] - m[1] * m[2];
630 var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
631 var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
635 // Applies the transform to the rectangle and finds the minimum axially
636 // aligned bounding box.
637 Util.getAxialAlignedBoundingBox =
638 function Util_getAxialAlignedBoundingBox(r, m) {
640 var p1 = Util.applyTransform(r, m);
641 var p2 = Util.applyTransform(r.slice(2, 4), m);
642 var p3 = Util.applyTransform([r[0], r[3]], m);
643 var p4 = Util.applyTransform([r[2], r[1]], m);
645 Math.min(p1[0], p2[0], p3[0], p4[0]),
646 Math.min(p1[1], p2[1], p3[1], p4[1]),
647 Math.max(p1[0], p2[0], p3[0], p4[0]),
648 Math.max(p1[1], p2[1], p3[1], p4[1])
652 Util.inverseTransform = function Util_inverseTransform(m) {
653 var d = m[0] * m[3] - m[1] * m[2];
654 return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d,
655 (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
658 // Apply a generic 3d matrix M on a 3-vector v:
662 // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
664 Util.apply3dTransform = function Util_apply3dTransform(m, v) {
666 m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
667 m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
668 m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
672 // This calculation uses Singular Value Decomposition.
673 // The SVD can be represented with formula A = USV. We are interested in the
674 // matrix S here because it represents the scale values.
675 Util.singularValueDecompose2dScale =
676 function Util_singularValueDecompose2dScale(m) {
678 var transpose = [m[0], m[2], m[1], m[3]];
680 // Multiply matrix m with its transpose.
681 var a = m[0] * transpose[0] + m[1] * transpose[2];
682 var b = m[0] * transpose[1] + m[1] * transpose[3];
683 var c = m[2] * transpose[0] + m[3] * transpose[2];
684 var d = m[2] * transpose[1] + m[3] * transpose[3];
686 // Solve the second degree polynomial to get roots.
687 var first = (a + d) / 2;
688 var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
689 var sx = first + second || 1;
690 var sy = first - second || 1;
692 // Scale values are the square roots of the eigenvalues.
693 return [Math.sqrt(sx), Math.sqrt(sy)];
696 // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
697 // For coordinate systems whose origin lies in the bottom-left, this
698 // means normalization to (BL,TR) ordering. For systems with origin in the
699 // top-left, this means (TL,BR) ordering.
700 Util.normalizeRect = function Util_normalizeRect(rect) {
701 var r = rect.slice(0); // clone rect
702 if (rect[0] > rect[2]) {
706 if (rect[1] > rect[3]) {
713 // Returns a rectangle [x1, y1, x2, y2] corresponding to the
714 // intersection of rect1 and rect2. If no intersection, returns 'false'
715 // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
716 Util.intersect = function Util_intersect(rect1, rect2) {
717 function compare(a, b) {
721 // Order points along the axes
722 var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
723 orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
726 rect1 = Util.normalizeRect(rect1);
727 rect2 = Util.normalizeRect(rect2);
729 // X: first and second points belong to different rectangles?
730 if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
731 (orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) {
732 // Intersection must be between second and third points
733 result[0] = orderedX[1];
734 result[2] = orderedX[2];
739 // Y: first and second points belong to different rectangles?
740 if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
741 (orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) {
742 // Intersection must be between second and third points
743 result[1] = orderedY[1];
744 result[3] = orderedY[2];
752 Util.sign = function Util_sign(num) {
753 return num < 0 ? -1 : 1;
756 Util.appendToArray = function Util_appendToArray(arr1, arr2) {
757 Array.prototype.push.apply(arr1, arr2);
760 Util.prependToArray = function Util_prependToArray(arr1, arr2) {
761 Array.prototype.unshift.apply(arr1, arr2);
764 Util.extendObj = function extendObj(obj1, obj2) {
765 for (var key in obj2) {
766 obj1[key] = obj2[key];
770 Util.getInheritableProperty = function Util_getInheritableProperty(dict,
772 while (dict && !dict.has(name)) {
773 dict = dict.get('Parent');
778 return dict.get(name);
781 Util.inherit = function Util_inherit(sub, base, prototype) {
782 sub.prototype = Object.create(base.prototype);
783 sub.prototype.constructor = sub;
784 for (var prop in prototype) {
785 sub.prototype[prop] = prototype[prop];
789 Util.loadScript = function Util_loadScript(src, callback) {
790 var script = document.createElement('script');
792 script.setAttribute('src', src);
794 script.onload = function() {
801 document.getElementsByTagName('head')[0].appendChild(script);
808 * PDF page viewport created based on scale, rotation and offset.
810 * @alias PDFJS.PageViewport
812 var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
816 * @param viewBox {Array} xMin, yMin, xMax and yMax coordinates.
817 * @param scale {number} scale of the viewport.
818 * @param rotation {number} rotations of the viewport in degrees.
819 * @param offsetX {number} offset X
820 * @param offsetY {number} offset Y
821 * @param dontFlip {boolean} if true, axis Y will not be flipped.
823 function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
824 this.viewBox = viewBox;
826 this.rotation = rotation;
827 this.offsetX = offsetX;
828 this.offsetY = offsetY;
830 // creating transform to convert pdf coordinate system to the normal
831 // canvas like coordinates taking in account scale and rotation
832 var centerX = (viewBox[2] + viewBox[0]) / 2;
833 var centerY = (viewBox[3] + viewBox[1]) / 2;
834 var rotateA, rotateB, rotateC, rotateD;
835 rotation = rotation % 360;
836 rotation = rotation < 0 ? rotation + 360 : rotation;
839 rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
842 rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
845 rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0;
849 rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1;
854 rotateC = -rotateC; rotateD = -rotateD;
857 var offsetCanvasX, offsetCanvasY;
860 offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
861 offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
862 width = Math.abs(viewBox[3] - viewBox[1]) * scale;
863 height = Math.abs(viewBox[2] - viewBox[0]) * scale;
865 offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
866 offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
867 width = Math.abs(viewBox[2] - viewBox[0]) * scale;
868 height = Math.abs(viewBox[3] - viewBox[1]) * scale;
870 // creating transform for the following operations:
871 // translate(-centerX, -centerY), rotate and flip vertically,
872 // scale, and translate(offsetCanvasX, offsetCanvasY)
878 offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
879 offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
883 this.height = height;
884 this.fontScale = scale;
886 PageViewport.prototype = /** @lends PDFJS.PageViewport.prototype */ {
888 * Clones viewport with additional properties.
889 * @param args {Object} (optional) If specified, may contain the 'scale' or
890 * 'rotation' properties to override the corresponding properties in
891 * the cloned viewport.
892 * @returns {PDFJS.PageViewport} Cloned viewport.
894 clone: function PageViewPort_clone(args) {
896 var scale = 'scale' in args ? args.scale : this.scale;
897 var rotation = 'rotation' in args ? args.rotation : this.rotation;
898 return new PageViewport(this.viewBox.slice(), scale, rotation,
899 this.offsetX, this.offsetY, args.dontFlip);
902 * Converts PDF point to the viewport coordinates. For examples, useful for
903 * converting PDF location into canvas pixel coordinates.
904 * @param x {number} X coordinate.
905 * @param y {number} Y coordinate.
906 * @returns {Object} Object that contains 'x' and 'y' properties of the
907 * point in the viewport coordinate space.
908 * @see {@link convertToPdfPoint}
909 * @see {@link convertToViewportRectangle}
911 convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
912 return Util.applyTransform([x, y], this.transform);
915 * Converts PDF rectangle to the viewport coordinates.
916 * @param rect {Array} xMin, yMin, xMax and yMax coordinates.
917 * @returns {Array} Contains corresponding coordinates of the rectangle
918 * in the viewport coordinate space.
919 * @see {@link convertToViewportPoint}
921 convertToViewportRectangle:
922 function PageViewport_convertToViewportRectangle(rect) {
923 var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
924 var br = Util.applyTransform([rect[2], rect[3]], this.transform);
925 return [tl[0], tl[1], br[0], br[1]];
928 * Converts viewport coordinates to the PDF location. For examples, useful
929 * for converting canvas pixel location into PDF one.
930 * @param x {number} X coordinate.
931 * @param y {number} Y coordinate.
932 * @returns {Object} Object that contains 'x' and 'y' properties of the
933 * point in the PDF coordinate space.
934 * @see {@link convertToViewportPoint}
936 convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
937 return Util.applyInverseTransform([x, y], this.transform);
943 var PDFStringTranslateTable = [
944 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
945 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0,
946 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,
947 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,
948 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,
949 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014,
950 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C,
951 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160,
952 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC
955 function stringToPDFString(str) {
956 var i, n = str.length, strBuf = [];
957 if (str[0] === '\xFE' && str[1] === '\xFF') {
959 for (i = 2; i < n; i += 2) {
960 strBuf.push(String.fromCharCode(
961 (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)));
964 for (i = 0; i < n; ++i) {
965 var code = PDFStringTranslateTable[str.charCodeAt(i)];
966 strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
969 return strBuf.join('');
972 function stringToUTF8String(str) {
973 return decodeURIComponent(escape(str));
976 function utf8StringToString(str) {
977 return unescape(encodeURIComponent(str));
980 function isEmptyObj(obj) {
981 for (var key in obj) {
988 return typeof v === 'boolean';
992 return typeof v === 'number' && ((v | 0) === v);
996 return typeof v === 'number';
999 function isString(v) {
1000 return typeof v === 'string';
1003 function isName(v) {
1004 return v instanceof Name;
1007 function isCmd(v, cmd) {
1008 return v instanceof Cmd && (cmd === undefined || v.cmd === cmd);
1011 function isDict(v, type) {
1012 if (!(v instanceof Dict)) {
1018 var dictType = v.get('Type');
1019 return isName(dictType) && dictType.name === type;
1022 function isArray(v) {
1023 return v instanceof Array;
1026 function isStream(v) {
1027 return typeof v === 'object' && v !== null && v.getBytes !== undefined;
1030 function isArrayBuffer(v) {
1031 return typeof v === 'object' && v !== null && v.byteLength !== undefined;
1035 return v instanceof Ref;
1039 * Promise Capability object.
1041 * @typedef {Object} PromiseCapability
1042 * @property {Promise} promise - A promise object.
1043 * @property {function} resolve - Fullfills the promise.
1044 * @property {function} reject - Rejects the promise.
1048 * Creates a promise capability object.
1049 * @alias PDFJS.createPromiseCapability
1051 * @return {PromiseCapability} A capability object contains:
1052 * - a Promise, resolve and reject methods.
1054 function createPromiseCapability() {
1055 var capability = {};
1056 capability.promise = new Promise(function (resolve, reject) {
1057 capability.resolve = resolve;
1058 capability.reject = reject;
1063 PDFJS.createPromiseCapability = createPromiseCapability;
1066 * Polyfill for Promises:
1067 * The following promise implementation tries to generally implement the
1068 * Promise/A+ spec. Some notable differences from other promise libaries are:
1069 * - There currently isn't a seperate deferred and promise object.
1070 * - Unhandled rejections eventually show an error if they aren't handled.
1072 * Based off of the work in:
1073 * https://bugzilla.mozilla.org/show_bug.cgi?id=810490
1075 (function PromiseClosure() {
1076 if (globalScope.Promise) {
1077 // Promises existing in the DOM/Worker, checking presence of all/resolve
1078 if (typeof globalScope.Promise.all !== 'function') {
1079 globalScope.Promise.all = function (iterable) {
1080 var count = 0, results = [], resolve, reject;
1081 var promise = new globalScope.Promise(function (resolve_, reject_) {
1085 iterable.forEach(function (p, i) {
1087 p.then(function (result) {
1088 results[i] = result;
1101 if (typeof globalScope.Promise.resolve !== 'function') {
1102 globalScope.Promise.resolve = function (value) {
1103 return new globalScope.Promise(function (resolve) { resolve(value); });
1106 if (typeof globalScope.Promise.reject !== 'function') {
1107 globalScope.Promise.reject = function (reason) {
1108 return new globalScope.Promise(function (resolve, reject) {
1113 if (typeof globalScope.Promise.prototype.catch !== 'function') {
1114 globalScope.Promise.prototype.catch = function (onReject) {
1115 return globalScope.Promise.prototype.then(undefined, onReject);
1120 var STATUS_PENDING = 0;
1121 var STATUS_RESOLVED = 1;
1122 var STATUS_REJECTED = 2;
1124 // In an attempt to avoid silent exceptions, unhandled rejections are
1125 // tracked and if they aren't handled in a certain amount of time an
1127 var REJECTION_TIMEOUT = 500;
1129 var HandlerManager = {
1132 unhandledRejections: [],
1133 pendingRejectionCheck: false,
1135 scheduleHandlers: function scheduleHandlers(promise) {
1136 if (promise._status === STATUS_PENDING) {
1140 this.handlers = this.handlers.concat(promise._handlers);
1141 promise._handlers = [];
1146 this.running = true;
1148 setTimeout(this.runHandlers.bind(this), 0);
1151 runHandlers: function runHandlers() {
1152 var RUN_TIMEOUT = 1; // ms
1153 var timeoutAt = Date.now() + RUN_TIMEOUT;
1154 while (this.handlers.length > 0) {
1155 var handler = this.handlers.shift();
1157 var nextStatus = handler.thisPromise._status;
1158 var nextValue = handler.thisPromise._value;
1161 if (nextStatus === STATUS_RESOLVED) {
1162 if (typeof handler.onResolve === 'function') {
1163 nextValue = handler.onResolve(nextValue);
1165 } else if (typeof handler.onReject === 'function') {
1166 nextValue = handler.onReject(nextValue);
1167 nextStatus = STATUS_RESOLVED;
1169 if (handler.thisPromise._unhandledRejection) {
1170 this.removeUnhandeledRejection(handler.thisPromise);
1174 nextStatus = STATUS_REJECTED;
1178 handler.nextPromise._updateStatus(nextStatus, nextValue);
1179 if (Date.now() >= timeoutAt) {
1184 if (this.handlers.length > 0) {
1185 setTimeout(this.runHandlers.bind(this), 0);
1189 this.running = false;
1192 addUnhandledRejection: function addUnhandledRejection(promise) {
1193 this.unhandledRejections.push({
1197 this.scheduleRejectionCheck();
1200 removeUnhandeledRejection: function removeUnhandeledRejection(promise) {
1201 promise._unhandledRejection = false;
1202 for (var i = 0; i < this.unhandledRejections.length; i++) {
1203 if (this.unhandledRejections[i].promise === promise) {
1204 this.unhandledRejections.splice(i);
1210 scheduleRejectionCheck: function scheduleRejectionCheck() {
1211 if (this.pendingRejectionCheck) {
1214 this.pendingRejectionCheck = true;
1215 setTimeout(function rejectionCheck() {
1216 this.pendingRejectionCheck = false;
1217 var now = Date.now();
1218 for (var i = 0; i < this.unhandledRejections.length; i++) {
1219 if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) {
1220 var unhandled = this.unhandledRejections[i].promise._value;
1221 var msg = 'Unhandled rejection: ' + unhandled;
1222 if (unhandled.stack) {
1223 msg += '\n' + unhandled.stack;
1226 this.unhandledRejections.splice(i);
1230 if (this.unhandledRejections.length) {
1231 this.scheduleRejectionCheck();
1233 }.bind(this), REJECTION_TIMEOUT);
1237 function Promise(resolver) {
1238 this._status = STATUS_PENDING;
1239 this._handlers = [];
1241 resolver.call(this, this._resolve.bind(this), this._reject.bind(this));
1247 * Builds a promise that is resolved when all the passed in promises are
1249 * @param {array} array of data and/or promises to wait for.
1250 * @return {Promise} New dependant promise.
1252 Promise.all = function Promise_all(promises) {
1253 var resolveAll, rejectAll;
1254 var deferred = new Promise(function (resolve, reject) {
1255 resolveAll = resolve;
1258 var unresolved = promises.length;
1260 if (unresolved === 0) {
1261 resolveAll(results);
1264 function reject(reason) {
1265 if (deferred._status === STATUS_REJECTED) {
1271 for (var i = 0, ii = promises.length; i < ii; ++i) {
1272 var promise = promises[i];
1273 var resolve = (function(i) {
1274 return function(value) {
1275 if (deferred._status === STATUS_REJECTED) {
1280 if (unresolved === 0) {
1281 resolveAll(results);
1285 if (Promise.isPromise(promise)) {
1286 promise.then(resolve, reject);
1295 * Checks if the value is likely a promise (has a 'then' function).
1296 * @return {boolean} true if value is thenable
1298 Promise.isPromise = function Promise_isPromise(value) {
1299 return value && typeof value.then === 'function';
1303 * Creates resolved promise
1304 * @param value resolve value
1305 * @returns {Promise}
1307 Promise.resolve = function Promise_resolve(value) {
1308 return new Promise(function (resolve) { resolve(value); });
1312 * Creates rejected promise
1313 * @param reason rejection value
1314 * @returns {Promise}
1316 Promise.reject = function Promise_reject(reason) {
1317 return new Promise(function (resolve, reject) { reject(reason); });
1320 Promise.prototype = {
1324 _unhandledRejection: null,
1326 _updateStatus: function Promise__updateStatus(status, value) {
1327 if (this._status === STATUS_RESOLVED ||
1328 this._status === STATUS_REJECTED) {
1332 if (status === STATUS_RESOLVED &&
1333 Promise.isPromise(value)) {
1334 value.then(this._updateStatus.bind(this, STATUS_RESOLVED),
1335 this._updateStatus.bind(this, STATUS_REJECTED));
1339 this._status = status;
1340 this._value = value;
1342 if (status === STATUS_REJECTED && this._handlers.length === 0) {
1343 this._unhandledRejection = true;
1344 HandlerManager.addUnhandledRejection(this);
1347 HandlerManager.scheduleHandlers(this);
1350 _resolve: function Promise_resolve(value) {
1351 this._updateStatus(STATUS_RESOLVED, value);
1354 _reject: function Promise_reject(reason) {
1355 this._updateStatus(STATUS_REJECTED, reason);
1358 then: function Promise_then(onResolve, onReject) {
1359 var nextPromise = new Promise(function (resolve, reject) {
1360 this.resolve = resolve;
1361 this.reject = reject;
1363 this._handlers.push({
1365 onResolve: onResolve,
1367 nextPromise: nextPromise
1369 HandlerManager.scheduleHandlers(this);
1373 catch: function Promise_catch(onReject) {
1374 return this.then(undefined, onReject);
1378 globalScope.Promise = Promise;
1381 var StatTimer = (function StatTimerClosure() {
1382 function rpad(str, pad, length) {
1383 while (str.length < length) {
1388 function StatTimer() {
1391 this.enabled = true;
1393 StatTimer.prototype = {
1394 time: function StatTimer_time(name) {
1395 if (!this.enabled) {
1398 if (name in this.started) {
1399 warn('Timer is already running for ' + name);
1401 this.started[name] = Date.now();
1403 timeEnd: function StatTimer_timeEnd(name) {
1404 if (!this.enabled) {
1407 if (!(name in this.started)) {
1408 warn('Timer has not been started for ' + name);
1412 'start': this.started[name],
1415 // Remove timer from started so it can be called again.
1416 delete this.started[name];
1418 toString: function StatTimer_toString() {
1420 var times = this.times;
1422 // Find the longest name for padding purposes.
1424 for (i = 0, ii = times.length; i < ii; ++i) {
1425 var name = times[i]['name'];
1426 if (name.length > longest) {
1427 longest = name.length;
1430 for (i = 0, ii = times.length; i < ii; ++i) {
1431 var span = times[i];
1432 var duration = span.end - span.start;
1433 out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
1441 PDFJS.createBlob = function createBlob(data, contentType) {
1442 if (typeof Blob !== 'undefined') {
1443 return new Blob([data], { type: contentType });
1445 // Blob builder is deprecated in FF14 and removed in FF18.
1446 var bb = new MozBlobBuilder();
1448 return bb.getBlob(contentType);
1451 PDFJS.createObjectURL = (function createObjectURLClosure() {
1452 // Blob/createObjectURL is not available, falling back to data schema.
1454 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
1456 return function createObjectURL(data, contentType) {
1457 if (!PDFJS.disableCreateObjectURL &&
1458 typeof URL !== 'undefined' && URL.createObjectURL) {
1459 var blob = PDFJS.createBlob(data, contentType);
1460 return URL.createObjectURL(blob);
1463 var buffer = 'data:' + contentType + ';base64,';
1464 for (var i = 0, ii = data.length; i < ii; i += 3) {
1465 var b1 = data[i] & 0xFF;
1466 var b2 = data[i + 1] & 0xFF;
1467 var b3 = data[i + 2] & 0xFF;
1468 var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
1469 var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
1470 var d4 = i + 2 < ii ? (b3 & 0x3F) : 64;
1471 buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
1477 function MessageHandler(name, comObj) {
1479 this.comObj = comObj;
1480 this.callbackIndex = 1;
1481 this.postMessageTransfers = true;
1482 var callbacksCapabilities = this.callbacksCapabilities = {};
1483 var ah = this.actionHandler = {};
1485 ah['console_log'] = [function ahConsoleLog(data) {
1486 console.log.apply(console, data);
1488 ah['console_error'] = [function ahConsoleError(data) {
1489 console.error.apply(console, data);
1491 ah['_unsupported_feature'] = [function ah_unsupportedFeature(data) {
1492 UnsupportedManager.notify(data);
1495 comObj.onmessage = function messageHandlerComObjOnMessage(event) {
1496 var data = event.data;
1498 var callbackId = data.callbackId;
1499 if (data.callbackId in callbacksCapabilities) {
1500 var callback = callbacksCapabilities[callbackId];
1501 delete callbacksCapabilities[callbackId];
1502 if ('error' in data) {
1503 callback.reject(data.error);
1505 callback.resolve(data.data);
1508 error('Cannot resolve callback ' + callbackId);
1510 } else if (data.action in ah) {
1511 var action = ah[data.action];
1512 if (data.callbackId) {
1513 Promise.resolve().then(function () {
1514 return action[0].call(action[1], data.data);
1515 }).then(function (result) {
1516 comObj.postMessage({
1518 callbackId: data.callbackId,
1521 }, function (reason) {
1522 comObj.postMessage({
1524 callbackId: data.callbackId,
1529 action[0].call(action[1], data.data);
1532 error('Unknown action from worker: ' + data.action);
1537 MessageHandler.prototype = {
1538 on: function messageHandlerOn(actionName, handler, scope) {
1539 var ah = this.actionHandler;
1540 if (ah[actionName]) {
1541 error('There is already an actionName called "' + actionName + '"');
1543 ah[actionName] = [handler, scope];
1546 * Sends a message to the comObj to invoke the action with the supplied data.
1547 * @param {String} actionName Action to call.
1548 * @param {JSON} data JSON data to send.
1549 * @param {Array} [transfers] Optional list of transfers/ArrayBuffers
1551 send: function messageHandlerSend(actionName, data, transfers) {
1556 this.postMessage(message, transfers);
1559 * Sends a message to the comObj to invoke the action with the supplied data.
1560 * Expects that other side will callback with the response.
1561 * @param {String} actionName Action to call.
1562 * @param {JSON} data JSON data to send.
1563 * @param {Array} [transfers] Optional list of transfers/ArrayBuffers.
1564 * @returns {Promise} Promise to be resolved with response data.
1567 function messageHandlerSendWithPromise(actionName, data, transfers) {
1568 var callbackId = this.callbackIndex++;
1572 callbackId: callbackId
1574 var capability = createPromiseCapability();
1575 this.callbacksCapabilities[callbackId] = capability;
1577 this.postMessage(message, transfers);
1579 capability.reject(e);
1581 return capability.promise;
1584 * Sends raw message to the comObj.
1586 * @param message {Object} Raw message.
1587 * @param transfers List of transfers/ArrayBuffers, or undefined.
1589 postMessage: function (message, transfers) {
1590 if (transfers && this.postMessageTransfers) {
1591 this.comObj.postMessage(message, transfers);
1593 this.comObj.postMessage(message);
1598 function loadJpegStream(id, imageUrl, objs) {
1599 var img = new Image();
1600 img.onload = (function loadJpegStream_onloadClosure() {
1601 objs.resolve(id, img);
1603 img.onerror = (function loadJpegStream_onerrorClosure() {
1604 objs.resolve(id, null);
1605 warn('Error during JPEG image loading');
1613 var NetworkManager = (function NetworkManagerClosure() {
1615 var OK_RESPONSE = 200;
1616 var PARTIAL_CONTENT_RESPONSE = 206;
1618 function NetworkManager(url, args) {
1621 this.isHttp = /^https?:/i.test(url);
1622 this.httpHeaders = (this.isHttp && args.httpHeaders) || {};
1623 this.withCredentials = args.withCredentials || false;
1624 this.getXhr = args.getXhr ||
1625 function NetworkManager_getXhr() {
1626 return new XMLHttpRequest();
1630 this.pendingRequests = {};
1631 this.loadedRequests = {};
1634 function getArrayBuffer(xhr) {
1635 var data = xhr.response;
1636 if (typeof data !== 'string') {
1639 var length = data.length;
1640 var array = new Uint8Array(length);
1641 for (var i = 0; i < length; i++) {
1642 array[i] = data.charCodeAt(i) & 0xFF;
1644 return array.buffer;
1647 NetworkManager.prototype = {
1648 requestRange: function NetworkManager_requestRange(begin, end, listeners) {
1653 for (var prop in listeners) {
1654 args[prop] = listeners[prop];
1656 return this.request(args);
1659 requestFull: function NetworkManager_requestFull(listeners) {
1660 return this.request(listeners);
1663 request: function NetworkManager_request(args) {
1664 var xhr = this.getXhr();
1665 var xhrId = this.currXhrId++;
1666 var pendingRequest = this.pendingRequests[xhrId] = {
1670 xhr.open('GET', this.url);
1671 xhr.withCredentials = this.withCredentials;
1672 for (var property in this.httpHeaders) {
1673 var value = this.httpHeaders[property];
1674 if (typeof value === 'undefined') {
1677 xhr.setRequestHeader(property, value);
1679 if (this.isHttp && 'begin' in args && 'end' in args) {
1680 var rangeStr = args.begin + '-' + (args.end - 1);
1681 xhr.setRequestHeader('Range', 'bytes=' + rangeStr);
1682 pendingRequest.expectedStatus = 206;
1684 pendingRequest.expectedStatus = 200;
1687 if (args.onProgressiveData) {
1688 // Some legacy browsers might throw an exception.
1690 xhr.responseType = 'moz-chunked-arraybuffer';
1692 if (xhr.responseType === 'moz-chunked-arraybuffer') {
1693 pendingRequest.onProgressiveData = args.onProgressiveData;
1694 pendingRequest.mozChunked = true;
1696 xhr.responseType = 'arraybuffer';
1699 xhr.responseType = 'arraybuffer';
1703 xhr.onerror = function(evt) {
1704 args.onError(xhr.status);
1707 xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
1708 xhr.onprogress = this.onProgress.bind(this, xhrId);
1710 pendingRequest.onHeadersReceived = args.onHeadersReceived;
1711 pendingRequest.onDone = args.onDone;
1712 pendingRequest.onError = args.onError;
1713 pendingRequest.onProgress = args.onProgress;
1720 onProgress: function NetworkManager_onProgress(xhrId, evt) {
1721 var pendingRequest = this.pendingRequests[xhrId];
1722 if (!pendingRequest) {
1723 // Maybe abortRequest was called...
1727 if (pendingRequest.mozChunked) {
1728 var chunk = getArrayBuffer(pendingRequest.xhr);
1729 pendingRequest.onProgressiveData(chunk);
1732 var onProgress = pendingRequest.onProgress;
1738 onStateChange: function NetworkManager_onStateChange(xhrId, evt) {
1739 var pendingRequest = this.pendingRequests[xhrId];
1740 if (!pendingRequest) {
1741 // Maybe abortRequest was called...
1745 var xhr = pendingRequest.xhr;
1746 if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
1747 pendingRequest.onHeadersReceived();
1748 delete pendingRequest.onHeadersReceived;
1751 if (xhr.readyState !== 4) {
1755 if (!(xhrId in this.pendingRequests)) {
1756 // The XHR request might have been aborted in onHeadersReceived()
1757 // callback, in which case we should abort request
1761 delete this.pendingRequests[xhrId];
1763 // success status == 0 can be on ftp, file and other protocols
1764 if (xhr.status === 0 && this.isHttp) {
1765 if (pendingRequest.onError) {
1766 pendingRequest.onError(xhr.status);
1770 var xhrStatus = xhr.status || OK_RESPONSE;
1772 // From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2:
1773 // "A server MAY ignore the Range header". This means it's possible to
1774 // get a 200 rather than a 206 response from a range request.
1775 var ok_response_on_range_request =
1776 xhrStatus === OK_RESPONSE &&
1777 pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;
1779 if (!ok_response_on_range_request &&
1780 xhrStatus !== pendingRequest.expectedStatus) {
1781 if (pendingRequest.onError) {
1782 pendingRequest.onError(xhr.status);
1787 this.loadedRequests[xhrId] = true;
1789 var chunk = getArrayBuffer(xhr);
1790 if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
1791 var rangeHeader = xhr.getResponseHeader('Content-Range');
1792 var matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader);
1793 var begin = parseInt(matches[1], 10);
1794 pendingRequest.onDone({
1798 } else if (pendingRequest.onProgressiveData) {
1799 pendingRequest.onDone(null);
1801 pendingRequest.onDone({
1808 hasPendingRequests: function NetworkManager_hasPendingRequests() {
1809 for (var xhrId in this.pendingRequests) {
1815 getRequestXhr: function NetworkManager_getXhr(xhrId) {
1816 return this.pendingRequests[xhrId].xhr;
1819 isStreamingRequest: function NetworkManager_isStreamingRequest(xhrId) {
1820 return !!(this.pendingRequests[xhrId].onProgressiveData);
1823 isPendingRequest: function NetworkManager_isPendingRequest(xhrId) {
1824 return xhrId in this.pendingRequests;
1827 isLoadedRequest: function NetworkManager_isLoadedRequest(xhrId) {
1828 return xhrId in this.loadedRequests;
1831 abortAllRequests: function NetworkManager_abortAllRequests() {
1832 for (var xhrId in this.pendingRequests) {
1833 this.abortRequest(xhrId | 0);
1837 abortRequest: function NetworkManager_abortRequest(xhrId) {
1838 var xhr = this.pendingRequests[xhrId].xhr;
1839 delete this.pendingRequests[xhrId];
1844 return NetworkManager;
1848 var ChunkedStream = (function ChunkedStreamClosure() {
1849 function ChunkedStream(length, chunkSize, manager) {
1850 this.bytes = new Uint8Array(length);
1854 this.chunkSize = chunkSize;
1855 this.loadedChunks = [];
1856 this.numChunksLoaded = 0;
1857 this.numChunks = Math.ceil(length / chunkSize);
1858 this.manager = manager;
1859 this.progressiveDataLength = 0;
1860 this.lastSuccessfulEnsureByteChunk = -1; // a single-entry cache
1863 // required methods for a stream. if a particular stream does not
1864 // implement these, an error should be thrown
1865 ChunkedStream.prototype = {
1867 getMissingChunks: function ChunkedStream_getMissingChunks() {
1869 for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) {
1870 if (!this.loadedChunks[chunk]) {
1877 getBaseStreams: function ChunkedStream_getBaseStreams() {
1881 allChunksLoaded: function ChunkedStream_allChunksLoaded() {
1882 return this.numChunksLoaded === this.numChunks;
1885 onReceiveData: function ChunkedStream_onReceiveData(begin, chunk) {
1886 var end = begin + chunk.byteLength;
1888 assert(begin % this.chunkSize === 0, 'Bad begin offset: ' + begin);
1889 // Using this.length is inaccurate here since this.start can be moved
1890 // See ChunkedStream.moveStart()
1891 var length = this.bytes.length;
1892 assert(end % this.chunkSize === 0 || end === length,
1893 'Bad end offset: ' + end);
1895 this.bytes.set(new Uint8Array(chunk), begin);
1896 var chunkSize = this.chunkSize;
1897 var beginChunk = Math.floor(begin / chunkSize);
1898 var endChunk = Math.floor((end - 1) / chunkSize) + 1;
1901 for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
1902 if (!this.loadedChunks[curChunk]) {
1903 this.loadedChunks[curChunk] = true;
1904 ++this.numChunksLoaded;
1909 onReceiveProgressiveData:
1910 function ChunkedStream_onReceiveProgressiveData(data) {
1911 var position = this.progressiveDataLength;
1912 var beginChunk = Math.floor(position / this.chunkSize);
1914 this.bytes.set(new Uint8Array(data), position);
1915 position += data.byteLength;
1916 this.progressiveDataLength = position;
1917 var endChunk = position >= this.end ? this.numChunks :
1918 Math.floor(position / this.chunkSize);
1920 for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
1921 if (!this.loadedChunks[curChunk]) {
1922 this.loadedChunks[curChunk] = true;
1923 ++this.numChunksLoaded;
1928 ensureByte: function ChunkedStream_ensureByte(pos) {
1929 var chunk = Math.floor(pos / this.chunkSize);
1930 if (chunk === this.lastSuccessfulEnsureByteChunk) {
1934 if (!this.loadedChunks[chunk]) {
1935 throw new MissingDataException(pos, pos + 1);
1937 this.lastSuccessfulEnsureByteChunk = chunk;
1940 ensureRange: function ChunkedStream_ensureRange(begin, end) {
1945 if (end <= this.progressiveDataLength) {
1949 var chunkSize = this.chunkSize;
1950 var beginChunk = Math.floor(begin / chunkSize);
1951 var endChunk = Math.floor((end - 1) / chunkSize) + 1;
1952 for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
1953 if (!this.loadedChunks[chunk]) {
1954 throw new MissingDataException(begin, end);
1959 nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) {
1961 for (chunk = beginChunk, n = this.numChunks; chunk < n; ++chunk) {
1962 if (!this.loadedChunks[chunk]) {
1966 // Wrap around to beginning
1967 for (chunk = 0; chunk < beginChunk; ++chunk) {
1968 if (!this.loadedChunks[chunk]) {
1975 hasChunk: function ChunkedStream_hasChunk(chunk) {
1976 return !!this.loadedChunks[chunk];
1980 return this.end - this.start;
1984 return this.length === 0;
1987 getByte: function ChunkedStream_getByte() {
1989 if (pos >= this.end) {
1992 this.ensureByte(pos);
1993 return this.bytes[this.pos++];
1996 getUint16: function ChunkedStream_getUint16() {
1997 var b0 = this.getByte();
1998 var b1 = this.getByte();
1999 if (b0 === -1 || b1 === -1) {
2002 return (b0 << 8) + b1;
2005 getInt32: function ChunkedStream_getInt32() {
2006 var b0 = this.getByte();
2007 var b1 = this.getByte();
2008 var b2 = this.getByte();
2009 var b3 = this.getByte();
2010 return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
2013 // returns subarray of original buffer
2014 // should only be read
2015 getBytes: function ChunkedStream_getBytes(length) {
2016 var bytes = this.bytes;
2018 var strEnd = this.end;
2021 this.ensureRange(pos, strEnd);
2022 return bytes.subarray(pos, strEnd);
2025 var end = pos + length;
2029 this.ensureRange(pos, end);
2032 return bytes.subarray(pos, end);
2035 peekByte: function ChunkedStream_peekByte() {
2036 var peekedByte = this.getByte();
2041 peekBytes: function ChunkedStream_peekBytes(length) {
2042 var bytes = this.getBytes(length);
2043 this.pos -= bytes.length;
2047 getByteRange: function ChunkedStream_getBytes(begin, end) {
2048 this.ensureRange(begin, end);
2049 return this.bytes.subarray(begin, end);
2052 skip: function ChunkedStream_skip(n) {
2059 reset: function ChunkedStream_reset() {
2060 this.pos = this.start;
2063 moveStart: function ChunkedStream_moveStart() {
2064 this.start = this.pos;
2067 makeSubStream: function ChunkedStream_makeSubStream(start, length, dict) {
2068 this.ensureRange(start, start + length);
2070 function ChunkedStreamSubstream() {}
2071 ChunkedStreamSubstream.prototype = Object.create(this);
2072 ChunkedStreamSubstream.prototype.getMissingChunks = function() {
2073 var chunkSize = this.chunkSize;
2074 var beginChunk = Math.floor(this.start / chunkSize);
2075 var endChunk = Math.floor((this.end - 1) / chunkSize) + 1;
2076 var missingChunks = [];
2077 for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
2078 if (!this.loadedChunks[chunk]) {
2079 missingChunks.push(chunk);
2082 return missingChunks;
2084 var subStream = new ChunkedStreamSubstream();
2085 subStream.pos = subStream.start = start;
2086 subStream.end = start + length || this.end;
2087 subStream.dict = dict;
2094 return ChunkedStream;
2097 var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
2099 function ChunkedStreamManager(length, chunkSize, url, args) {
2100 this.stream = new ChunkedStream(length, chunkSize, this);
2101 this.length = length;
2102 this.chunkSize = chunkSize;
2104 this.disableAutoFetch = args.disableAutoFetch;
2105 var msgHandler = this.msgHandler = args.msgHandler;
2107 if (args.chunkedViewerLoading) {
2108 msgHandler.on('OnDataRange', this.onReceiveData.bind(this));
2109 msgHandler.on('OnDataProgress', this.onProgress.bind(this));
2110 this.sendRequest = function ChunkedStreamManager_sendRequest(begin, end) {
2111 msgHandler.send('RequestDataRange', { begin: begin, end: end });
2115 var getXhr = function getXhr() {
2116 return new XMLHttpRequest();
2118 this.networkManager = new NetworkManager(this.url, {
2120 httpHeaders: args.httpHeaders,
2121 withCredentials: args.withCredentials
2123 this.sendRequest = function ChunkedStreamManager_sendRequest(begin, end) {
2124 this.networkManager.requestRange(begin, end, {
2125 onDone: this.onReceiveData.bind(this),
2126 onProgress: this.onProgress.bind(this)
2131 this.currRequestId = 0;
2133 this.chunksNeededByRequest = {};
2134 this.requestsByChunk = {};
2135 this.callbacksByRequest = {};
2136 this.progressiveDataLength = 0;
2138 this._loadedStreamCapability = createPromiseCapability();
2140 if (args.initialData) {
2141 this.onReceiveData({chunk: args.initialData});
2145 ChunkedStreamManager.prototype = {
2146 onLoadedStream: function ChunkedStreamManager_getLoadedStream() {
2147 return this._loadedStreamCapability.promise;
2150 // Get all the chunks that are not yet loaded and groups them into
2151 // contiguous ranges to load in as few requests as possible
2152 requestAllChunks: function ChunkedStreamManager_requestAllChunks() {
2153 var missingChunks = this.stream.getMissingChunks();
2154 this.requestChunks(missingChunks);
2155 return this._loadedStreamCapability.promise;
2158 requestChunks: function ChunkedStreamManager_requestChunks(chunks,
2160 var requestId = this.currRequestId++;
2164 this.chunksNeededByRequest[requestId] = chunksNeeded = {};
2165 for (i = 0, ii = chunks.length; i < ii; i++) {
2166 if (!this.stream.hasChunk(chunks[i])) {
2167 chunksNeeded[chunks[i]] = true;
2171 if (isEmptyObj(chunksNeeded)) {
2178 this.callbacksByRequest[requestId] = callback;
2180 var chunksToRequest = [];
2181 for (var chunk in chunksNeeded) {
2183 if (!(chunk in this.requestsByChunk)) {
2184 this.requestsByChunk[chunk] = [];
2185 chunksToRequest.push(chunk);
2187 this.requestsByChunk[chunk].push(requestId);
2190 if (!chunksToRequest.length) {
2194 var groupedChunksToRequest = this.groupChunks(chunksToRequest);
2196 for (i = 0; i < groupedChunksToRequest.length; ++i) {
2197 var groupedChunk = groupedChunksToRequest[i];
2198 var begin = groupedChunk.beginChunk * this.chunkSize;
2199 var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length);
2200 this.sendRequest(begin, end);
2204 getStream: function ChunkedStreamManager_getStream() {
2208 // Loads any chunks in the requested range that are not yet loaded
2209 requestRange: function ChunkedStreamManager_requestRange(
2210 begin, end, callback) {
2212 end = Math.min(end, this.length);
2214 var beginChunk = this.getBeginChunk(begin);
2215 var endChunk = this.getEndChunk(end);
2218 for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
2222 this.requestChunks(chunks, callback);
2225 requestRanges: function ChunkedStreamManager_requestRanges(ranges,
2227 ranges = ranges || [];
2228 var chunksToRequest = [];
2230 for (var i = 0; i < ranges.length; i++) {
2231 var beginChunk = this.getBeginChunk(ranges[i].begin);
2232 var endChunk = this.getEndChunk(ranges[i].end);
2233 for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
2234 if (chunksToRequest.indexOf(chunk) < 0) {
2235 chunksToRequest.push(chunk);
2240 chunksToRequest.sort(function(a, b) { return a - b; });
2241 this.requestChunks(chunksToRequest, callback);
2244 // Groups a sorted array of chunks into as few continguous larger
2245 // chunks as possible
2246 groupChunks: function ChunkedStreamManager_groupChunks(chunks) {
2247 var groupedChunks = [];
2248 var beginChunk = -1;
2250 for (var i = 0; i < chunks.length; ++i) {
2251 var chunk = chunks[i];
2253 if (beginChunk < 0) {
2257 if (prevChunk >= 0 && prevChunk + 1 !== chunk) {
2258 groupedChunks.push({ beginChunk: beginChunk,
2259 endChunk: prevChunk + 1 });
2262 if (i + 1 === chunks.length) {
2263 groupedChunks.push({ beginChunk: beginChunk,
2264 endChunk: chunk + 1 });
2269 return groupedChunks;
2272 onProgress: function ChunkedStreamManager_onProgress(args) {
2273 var bytesLoaded = (this.stream.numChunksLoaded * this.chunkSize +
2275 this.msgHandler.send('DocProgress', {
2276 loaded: bytesLoaded,
2281 onReceiveData: function ChunkedStreamManager_onReceiveData(args) {
2282 var chunk = args.chunk;
2283 var isProgressive = args.begin === undefined;
2284 var begin = isProgressive ? this.progressiveDataLength : args.begin;
2285 var end = begin + chunk.byteLength;
2287 var beginChunk = Math.floor(begin / this.chunkSize);
2288 var endChunk = end < this.length ? Math.floor(end / this.chunkSize) :
2289 Math.ceil(end / this.chunkSize);
2291 if (isProgressive) {
2292 this.stream.onReceiveProgressiveData(chunk);
2293 this.progressiveDataLength = end;
2295 this.stream.onReceiveData(begin, chunk);
2298 if (this.stream.allChunksLoaded()) {
2299 this._loadedStreamCapability.resolve(this.stream);
2302 var loadedRequests = [];
2304 for (chunk = beginChunk; chunk < endChunk; ++chunk) {
2305 // The server might return more chunks than requested
2306 var requestIds = this.requestsByChunk[chunk] || [];
2307 delete this.requestsByChunk[chunk];
2309 for (i = 0; i < requestIds.length; ++i) {
2310 requestId = requestIds[i];
2311 var chunksNeeded = this.chunksNeededByRequest[requestId];
2312 if (chunk in chunksNeeded) {
2313 delete chunksNeeded[chunk];
2316 if (!isEmptyObj(chunksNeeded)) {
2320 loadedRequests.push(requestId);
2324 // If there are no pending requests, automatically fetch the next
2325 // unfetched chunk of the PDF
2326 if (!this.disableAutoFetch && isEmptyObj(this.requestsByChunk)) {
2328 if (this.stream.numChunksLoaded === 1) {
2329 // This is a special optimization so that after fetching the first
2330 // chunk, rather than fetching the second chunk, we fetch the last
2332 var lastChunk = this.stream.numChunks - 1;
2333 if (!this.stream.hasChunk(lastChunk)) {
2334 nextEmptyChunk = lastChunk;
2337 nextEmptyChunk = this.stream.nextEmptyChunk(endChunk);
2339 if (isInt(nextEmptyChunk)) {
2340 this.requestChunks([nextEmptyChunk]);
2344 for (i = 0; i < loadedRequests.length; ++i) {
2345 requestId = loadedRequests[i];
2346 var callback = this.callbacksByRequest[requestId];
2347 delete this.callbacksByRequest[requestId];
2353 this.msgHandler.send('DocProgress', {
2354 loaded: this.stream.numChunksLoaded * this.chunkSize,
2359 onError: function ChunkedStreamManager_onError(err) {
2360 this._loadedStreamCapability.reject(err);
2363 getBeginChunk: function ChunkedStreamManager_getBeginChunk(begin) {
2364 var chunk = Math.floor(begin / this.chunkSize);
2368 getEndChunk: function ChunkedStreamManager_getEndChunk(end) {
2369 if (end % this.chunkSize === 0) {
2370 return end / this.chunkSize;
2378 var chunk = Math.floor((end - 1) / this.chunkSize) + 1;
2383 return ChunkedStreamManager;
2387 // The maximum number of bytes fetched per range request
2388 var RANGE_CHUNK_SIZE = 65536;
2390 // TODO(mack): Make use of PDFJS.Util.inherit() when it becomes available
2391 var BasePdfManager = (function BasePdfManagerClosure() {
2392 function BasePdfManager() {
2393 throw new Error('Cannot initialize BaseManagerManager');
2396 BasePdfManager.prototype = {
2397 onLoadedStream: function BasePdfManager_onLoadedStream() {
2398 throw new NotImplementedException();
2401 ensureDoc: function BasePdfManager_ensureDoc(prop, args) {
2402 return this.ensure(this.pdfDocument, prop, args);
2405 ensureXRef: function BasePdfManager_ensureXRef(prop, args) {
2406 return this.ensure(this.pdfDocument.xref, prop, args);
2409 ensureCatalog: function BasePdfManager_ensureCatalog(prop, args) {
2410 return this.ensure(this.pdfDocument.catalog, prop, args);
2413 getPage: function BasePdfManager_pagePage(pageIndex) {
2414 return this.pdfDocument.getPage(pageIndex);
2417 cleanup: function BasePdfManager_cleanup() {
2418 return this.pdfDocument.cleanup();
2421 ensure: function BasePdfManager_ensure(obj, prop, args) {
2422 return new NotImplementedException();
2425 requestRange: function BasePdfManager_ensure(begin, end) {
2426 return new NotImplementedException();
2429 requestLoadedStream: function BasePdfManager_requestLoadedStream() {
2430 return new NotImplementedException();
2433 sendProgressiveData: function BasePdfManager_sendProgressiveData(chunk) {
2434 return new NotImplementedException();
2437 updatePassword: function BasePdfManager_updatePassword(password) {
2438 this.pdfDocument.xref.password = this.password = password;
2439 if (this._passwordChangedCapability) {
2440 this._passwordChangedCapability.resolve();
2444 passwordChanged: function BasePdfManager_passwordChanged() {
2445 this._passwordChangedCapability = createPromiseCapability();
2446 return this._passwordChangedCapability.promise;
2449 terminate: function BasePdfManager_terminate() {
2450 return new NotImplementedException();
2454 return BasePdfManager;
2457 var LocalPdfManager = (function LocalPdfManagerClosure() {
2458 function LocalPdfManager(data, password) {
2459 var stream = new Stream(data);
2460 this.pdfDocument = new PDFDocument(this, stream, password);
2461 this._loadedStreamCapability = createPromiseCapability();
2462 this._loadedStreamCapability.resolve(stream);
2465 LocalPdfManager.prototype = Object.create(BasePdfManager.prototype);
2466 LocalPdfManager.prototype.constructor = LocalPdfManager;
2468 LocalPdfManager.prototype.ensure =
2469 function LocalPdfManager_ensure(obj, prop, args) {
2470 return new Promise(function (resolve, reject) {
2472 var value = obj[prop];
2474 if (typeof value === 'function') {
2475 result = value.apply(obj, args);
2486 LocalPdfManager.prototype.requestRange =
2487 function LocalPdfManager_requestRange(begin, end) {
2488 return Promise.resolve();
2491 LocalPdfManager.prototype.requestLoadedStream =
2492 function LocalPdfManager_requestLoadedStream() {
2495 LocalPdfManager.prototype.onLoadedStream =
2496 function LocalPdfManager_getLoadedStream() {
2497 return this._loadedStreamCapability.promise;
2500 LocalPdfManager.prototype.terminate =
2501 function LocalPdfManager_terminate() {
2505 return LocalPdfManager;
2508 var NetworkPdfManager = (function NetworkPdfManagerClosure() {
2509 function NetworkPdfManager(args, msgHandler) {
2511 this.msgHandler = msgHandler;
2514 msgHandler: msgHandler,
2515 httpHeaders: args.httpHeaders,
2516 withCredentials: args.withCredentials,
2517 chunkedViewerLoading: args.chunkedViewerLoading,
2518 disableAutoFetch: args.disableAutoFetch,
2519 initialData: args.initialData
2521 this.streamManager = new ChunkedStreamManager(args.length, RANGE_CHUNK_SIZE,
2524 this.pdfDocument = new PDFDocument(this, this.streamManager.getStream(),
2528 NetworkPdfManager.prototype = Object.create(BasePdfManager.prototype);
2529 NetworkPdfManager.prototype.constructor = NetworkPdfManager;
2531 NetworkPdfManager.prototype.ensure =
2532 function NetworkPdfManager_ensure(obj, prop, args) {
2533 var pdfManager = this;
2535 return new Promise(function (resolve, reject) {
2536 function ensureHelper() {
2539 var value = obj[prop];
2540 if (typeof value === 'function') {
2541 result = value.apply(obj, args);
2547 if (!(e instanceof MissingDataException)) {
2551 pdfManager.streamManager.requestRange(e.begin, e.end, ensureHelper);
2559 NetworkPdfManager.prototype.requestRange =
2560 function NetworkPdfManager_requestRange(begin, end) {
2561 return new Promise(function (resolve) {
2562 this.streamManager.requestRange(begin, end, function() {
2568 NetworkPdfManager.prototype.requestLoadedStream =
2569 function NetworkPdfManager_requestLoadedStream() {
2570 this.streamManager.requestAllChunks();
2573 NetworkPdfManager.prototype.sendProgressiveData =
2574 function NetworkPdfManager_sendProgressiveData(chunk) {
2575 this.streamManager.onReceiveData({ chunk: chunk });
2578 NetworkPdfManager.prototype.onLoadedStream =
2579 function NetworkPdfManager_getLoadedStream() {
2580 return this.streamManager.onLoadedStream();
2583 NetworkPdfManager.prototype.terminate =
2584 function NetworkPdfManager_terminate() {
2585 this.streamManager.networkManager.abortAllRequests();
2588 return NetworkPdfManager;
2592 var Page = (function PageClosure() {
2594 var LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
2596 function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache) {
2597 this.pdfManager = pdfManager;
2598 this.pageIndex = pageIndex;
2599 this.pageDict = pageDict;
2602 this.fontCache = fontCache;
2606 this.resourcesPromise = null;
2610 getPageProp: function Page_getPageProp(key) {
2611 return this.pageDict.get(key);
2614 getInheritedPageProp: function Page_inheritPageProp(key) {
2615 var dict = this.pageDict;
2616 var value = dict.get(key);
2617 while (value === undefined) {
2618 dict = dict.get('Parent');
2622 value = dict.get(key);
2628 return this.getPageProp('Contents');
2632 var value = this.getInheritedPageProp('Resources');
2633 // For robustness: The spec states that a \Resources entry has to be
2634 // present, but can be empty. Some document omit it still. In this case
2635 // return an empty dictionary:
2636 if (value === undefined) {
2639 return shadow(this, 'resources', value);
2643 var obj = this.getInheritedPageProp('MediaBox');
2644 // Reset invalid media box to letter size.
2645 if (!isArray(obj) || obj.length !== 4) {
2646 obj = LETTER_SIZE_MEDIABOX;
2648 return shadow(this, 'mediaBox', obj);
2652 var mediaBox = this.mediaBox;
2653 var cropBox = this.getInheritedPageProp('CropBox');
2654 if (!isArray(cropBox) || cropBox.length !== 4) {
2655 return shadow(this, 'view', mediaBox);
2658 // From the spec, 6th ed., p.963:
2659 // "The crop, bleed, trim, and art boxes should not ordinarily
2660 // extend beyond the boundaries of the media box. If they do, they are
2661 // effectively reduced to their intersection with the media box."
2662 cropBox = Util.intersect(cropBox, mediaBox);
2664 return shadow(this, 'view', mediaBox);
2666 return shadow(this, 'view', cropBox);
2669 get annotationRefs() {
2670 return shadow(this, 'annotationRefs',
2671 this.getInheritedPageProp('Annots'));
2675 var rotate = this.getInheritedPageProp('Rotate') || 0;
2676 // Normalize rotation so it's a multiple of 90 and between 0 and 270
2677 if (rotate % 90 !== 0) {
2679 } else if (rotate >= 360) {
2680 rotate = rotate % 360;
2681 } else if (rotate < 0) {
2682 // The spec doesn't cover negatives, assume its counterclockwise
2683 // rotation. The following is the other implementation of modulo.
2684 rotate = ((rotate % 360) + 360) % 360;
2686 return shadow(this, 'rotate', rotate);
2689 getContentStream: function Page_getContentStream() {
2690 var content = this.content;
2692 if (isArray(content)) {
2694 var xref = this.xref;
2695 var i, n = content.length;
2697 for (i = 0; i < n; ++i) {
2698 streams.push(xref.fetchIfRef(content[i]));
2700 stream = new StreamsSequenceStream(streams);
2701 } else if (isStream(content)) {
2704 // replacing non-existent page content with empty one
2705 stream = new NullStream();
2710 loadResources: function Page_loadResources(keys) {
2711 if (!this.resourcesPromise) {
2712 // TODO: add async getInheritedPageProp and remove this.
2713 this.resourcesPromise = this.pdfManager.ensure(this, 'resources');
2715 return this.resourcesPromise.then(function resourceSuccess() {
2716 var objectLoader = new ObjectLoader(this.resources.map,
2719 return objectLoader.load();
2723 getOperatorList: function Page_getOperatorList(handler, intent) {
2726 var pdfManager = this.pdfManager;
2727 var contentStreamPromise = pdfManager.ensure(this, 'getContentStream',
2729 var resourcesPromise = this.loadResources([
2740 var partialEvaluator = new PartialEvaluator(pdfManager, this.xref,
2741 handler, this.pageIndex,
2742 'p' + this.pageIndex + '_',
2746 var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
2747 var pageListPromise = dataPromises.then(function(data) {
2748 var contentStream = data[0];
2749 var opList = new OperatorList(intent, handler, self.pageIndex);
2751 handler.send('StartRenderPage', {
2752 transparency: partialEvaluator.hasBlendModes(self.resources),
2753 pageIndex: self.pageIndex,
2756 return partialEvaluator.getOperatorList(contentStream, self.resources,
2757 opList).then(function () {
2762 var annotationsPromise = pdfManager.ensure(this, 'annotations');
2763 return Promise.all([pageListPromise, annotationsPromise]).then(
2765 var pageOpList = datas[0];
2766 var annotations = datas[1];
2768 if (annotations.length === 0) {
2769 pageOpList.flush(true);
2773 var annotationsReadyPromise = Annotation.appendToOperatorList(
2774 annotations, pageOpList, pdfManager, partialEvaluator, intent);
2775 return annotationsReadyPromise.then(function () {
2776 pageOpList.flush(true);
2782 extractTextContent: function Page_extractTextContent() {
2784 on: function nullHandlerOn() {},
2785 send: function nullHandlerSend() {}
2790 var pdfManager = this.pdfManager;
2791 var contentStreamPromise = pdfManager.ensure(this, 'getContentStream',
2794 var resourcesPromise = this.loadResources([
2800 var dataPromises = Promise.all([contentStreamPromise,
2802 return dataPromises.then(function(data) {
2803 var contentStream = data[0];
2804 var partialEvaluator = new PartialEvaluator(pdfManager, self.xref,
2805 handler, self.pageIndex,
2806 'p' + self.pageIndex + '_',
2810 return partialEvaluator.getTextContent(contentStream,
2815 getAnnotationsData: function Page_getAnnotationsData() {
2816 var annotations = this.annotations;
2817 var annotationsData = [];
2818 for (var i = 0, n = annotations.length; i < n; ++i) {
2819 annotationsData.push(annotations[i].getData());
2821 return annotationsData;
2825 var annotations = [];
2826 var annotationRefs = (this.annotationRefs || []);
2827 for (var i = 0, n = annotationRefs.length; i < n; ++i) {
2828 var annotationRef = annotationRefs[i];
2829 var annotation = Annotation.fromRef(this.xref, annotationRef);
2831 annotations.push(annotation);
2834 return shadow(this, 'annotations', annotations);
2842 * The `PDFDocument` holds all the data of the PDF file. Compared to the
2843 * `PDFDoc`, this one doesn't have any job management code.
2844 * Right now there exists one PDFDocument on the main thread + one object
2845 * for each worker. If there is no worker support enabled, there are two
2846 * `PDFDocument` objects on the main thread created.
2848 var PDFDocument = (function PDFDocumentClosure() {
2849 var FINGERPRINT_FIRST_BYTES = 1024;
2850 var EMPTY_FINGERPRINT = '\x00\x00\x00\x00\x00\x00\x00' +
2851 '\x00\x00\x00\x00\x00\x00\x00\x00\x00';
2853 function PDFDocument(pdfManager, arg, password) {
2854 if (isStream(arg)) {
2855 init.call(this, pdfManager, arg, password);
2856 } else if (isArrayBuffer(arg)) {
2857 init.call(this, pdfManager, new Stream(arg), password);
2859 error('PDFDocument: Unknown argument type');
2863 function init(pdfManager, stream, password) {
2864 assert(stream.length > 0, 'stream must have data');
2865 this.pdfManager = pdfManager;
2866 this.stream = stream;
2867 var xref = new XRef(this.stream, password, pdfManager);
2871 function find(stream, needle, limit, backwards) {
2872 var pos = stream.pos;
2873 var end = stream.end;
2875 if (pos + limit > end) {
2878 for (var n = 0; n < limit; ++n) {
2879 strBuf.push(String.fromCharCode(stream.getByte()));
2881 var str = strBuf.join('');
2883 var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle);
2885 return false; /* not found */
2887 stream.pos += index;
2888 return true; /* found */
2891 var DocumentInfoValidators = {
2893 // Lazily build this since all the validation functions below are not
2894 // defined until after this file loads.
2895 return shadow(this, 'entries', {
2902 CreationDate: isString,
2909 PDFDocument.prototype = {
2910 parse: function PDFDocument_parse(recoveryMode) {
2911 this.setup(recoveryMode);
2913 // checking if AcroForm is present
2914 this.acroForm = this.catalog.catDict.get('AcroForm');
2915 if (this.acroForm) {
2916 this.xfa = this.acroForm.get('XFA');
2917 var fields = this.acroForm.get('Fields');
2918 if ((!fields || !isArray(fields) || fields.length === 0) &&
2920 // no fields and no XFA -- not a form (?)
2921 this.acroForm = null;
2925 info('Something wrong with AcroForm entry');
2926 this.acroForm = null;
2930 get linearization() {
2931 var linearization = null;
2932 if (this.stream.length) {
2934 linearization = Linearization.create(this.stream);
2936 if (err instanceof MissingDataException) {
2942 // shadow the prototype getter with a data property
2943 return shadow(this, 'linearization', linearization);
2946 var stream = this.stream;
2948 var linearization = this.linearization;
2949 if (linearization) {
2950 // Find end of first obj.
2952 if (find(stream, 'endobj', 1024)) {
2953 startXRef = stream.pos + 6;
2956 // Find startxref by jumping backward from the end of the file.
2958 var found = false, pos = stream.end;
2959 while (!found && pos > 0) {
2960 pos -= step - 'startxref'.length;
2965 found = find(stream, 'startxref', step, true);
2971 ch = stream.getByte();
2972 } while (Lexer.isSpace(ch));
2974 while (ch >= 0x20 && ch <= 0x39) { // < '9'
2975 str += String.fromCharCode(ch);
2976 ch = stream.getByte();
2978 startXRef = parseInt(str, 10);
2979 if (isNaN(startXRef)) {
2984 // shadow the prototype getter with a data property
2985 return shadow(this, 'startXRef', startXRef);
2987 get mainXRefEntriesOffset() {
2988 var mainXRefEntriesOffset = 0;
2989 var linearization = this.linearization;
2990 if (linearization) {
2991 mainXRefEntriesOffset = linearization.mainXRefEntriesOffset;
2993 // shadow the prototype getter with a data property
2994 return shadow(this, 'mainXRefEntriesOffset', mainXRefEntriesOffset);
2996 // Find the header, remove leading garbage and setup the stream
2997 // starting from the header.
2998 checkHeader: function PDFDocument_checkHeader() {
2999 var stream = this.stream;
3001 if (find(stream, '%PDF-', 1024)) {
3002 // Found the header, trim off any garbage before it.
3004 // Reading file format version
3005 var MAX_VERSION_LENGTH = 12;
3006 var version = '', ch;
3007 while ((ch = stream.getByte()) > 0x20) { // SPACE
3008 if (version.length >= MAX_VERSION_LENGTH) {
3011 version += String.fromCharCode(ch);
3013 // removing "%PDF-"-prefix
3014 this.pdfFormatVersion = version.substring(5);
3017 // May not be a PDF file, continue anyway.
3019 parseStartXRef: function PDFDocument_parseStartXRef() {
3020 var startXRef = this.startXRef;
3021 this.xref.setStartXRef(startXRef);
3023 setup: function PDFDocument_setup(recoveryMode) {
3024 this.xref.parse(recoveryMode);
3025 this.catalog = new Catalog(this.pdfManager, this.xref);
3028 var linearization = this.linearization;
3029 var num = linearization ? linearization.numPages : this.catalog.numPages;
3030 // shadow the prototype getter
3031 return shadow(this, 'numPages', num);
3033 get documentInfo() {
3035 PDFFormatVersion: this.pdfFormatVersion,
3036 IsAcroFormPresent: !!this.acroForm,
3037 IsXFAPresent: !!this.xfa
3041 infoDict = this.xref.trailer.get('Info');
3043 info('The document information dictionary is invalid.');
3046 var validEntries = DocumentInfoValidators.entries;
3047 // Only fill the document info with valid entries from the spec.
3048 for (var key in validEntries) {
3049 if (infoDict.has(key)) {
3050 var value = infoDict.get(key);
3051 // Make sure the value conforms to the spec.
3052 if (validEntries[key](value)) {
3053 docInfo[key] = (typeof value !== 'string' ?
3054 value : stringToPDFString(value));
3056 info('Bad value in document info for "' + key + '"');
3061 return shadow(this, 'documentInfo', docInfo);
3064 var xref = this.xref, idArray, hash, fileID = '';
3066 if (xref.trailer.has('ID')) {
3067 idArray = xref.trailer.get('ID');
3069 if (idArray && isArray(idArray) && idArray[0] !== EMPTY_FINGERPRINT) {
3070 hash = stringToBytes(idArray[0]);
3072 if (this.stream.ensureRange) {
3073 this.stream.ensureRange(0,
3074 Math.min(FINGERPRINT_FIRST_BYTES, this.stream.end));
3076 hash = calculateMD5(this.stream.bytes.subarray(0,
3077 FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES);
3080 for (var i = 0, n = hash.length; i < n; i++) {
3081 var hex = hash[i].toString(16);
3082 fileID += hex.length === 1 ? '0' + hex : hex;
3085 return shadow(this, 'fingerprint', fileID);
3088 getPage: function PDFDocument_getPage(pageIndex) {
3089 return this.catalog.getPage(pageIndex);
3092 cleanup: function PDFDocument_cleanup() {
3093 return this.catalog.cleanup();
3101 var Name = (function NameClosure() {
3102 function Name(name) {
3106 Name.prototype = {};
3110 Name.get = function Name_get(name) {
3111 var nameValue = nameCache[name];
3112 return (nameValue ? nameValue : (nameCache[name] = new Name(name)));
3118 var Cmd = (function CmdClosure() {
3127 Cmd.get = function Cmd_get(cmd) {
3128 var cmdValue = cmdCache[cmd];
3129 return (cmdValue ? cmdValue : (cmdCache[cmd] = new Cmd(cmd)));
3135 var Dict = (function DictClosure() {
3136 var nonSerializable = function nonSerializableClosure() {
3137 return nonSerializable; // creating closure on some variable
3140 var GETALL_DICTIONARY_TYPES_WHITELIST = {
3150 function isRecursionAllowedFor(dict) {
3151 if (!isName(dict.Type)) {
3154 var dictType = dict.Type.name;
3155 return GETALL_DICTIONARY_TYPES_WHITELIST[dictType] === true;
3159 function Dict(xref) {
3160 // Map should only be used internally, use functions below to access.
3161 this.map = Object.create(null);
3164 this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict
3168 assignXref: function Dict_assignXref(newXref) {
3169 this.xref = newXref;
3172 // automatically dereferences Ref objects
3173 get: function Dict_get(key1, key2, key3) {
3175 var xref = this.xref;
3176 if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map ||
3177 typeof key2 === 'undefined') {
3178 return xref ? xref.fetchIfRef(value) : value;
3180 if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map ||
3181 typeof key3 === 'undefined') {
3182 return xref ? xref.fetchIfRef(value) : value;
3184 value = this.map[key3] || null;
3185 return xref ? xref.fetchIfRef(value) : value;
3188 // Same as get(), but returns a promise and uses fetchIfRefAsync().
3189 getAsync: function Dict_getAsync(key1, key2, key3) {
3191 var xref = this.xref;
3192 if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map ||
3193 typeof key2 === 'undefined') {
3195 return xref.fetchIfRefAsync(value);
3197 return Promise.resolve(value);
3199 if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map ||
3200 typeof key3 === 'undefined') {
3202 return xref.fetchIfRefAsync(value);
3204 return Promise.resolve(value);
3206 value = this.map[key3] || null;
3208 return xref.fetchIfRefAsync(value);
3210 return Promise.resolve(value);
3214 getRaw: function Dict_getRaw(key) {
3215 return this.map[key];
3218 // creates new map and dereferences all Refs
3219 getAll: function Dict_getAll() {
3220 var all = Object.create(null);
3223 for (key in this.map) {
3224 obj = this.get(key);
3225 if (obj instanceof Dict) {
3226 if (isRecursionAllowedFor(obj)) {
3227 (queue || (queue = [])).push({target: all, key: key, obj: obj});
3229 all[key] = this.getRaw(key);
3239 // trying to take cyclic references into the account
3240 var processed = Object.create(null);
3241 while (queue.length > 0) {
3242 var item = queue.shift();
3243 var itemObj = item.obj;
3244 var objId = itemObj.objId;
3245 if (objId && objId in processed) {
3246 item.target[item.key] = processed[objId];
3249 var dereferenced = Object.create(null);
3250 for (key in itemObj.map) {
3251 obj = itemObj.get(key);
3252 if (obj instanceof Dict) {
3253 if (isRecursionAllowedFor(obj)) {
3254 queue.push({target: dereferenced, key: key, obj: obj});
3256 dereferenced[key] = itemObj.getRaw(key);
3259 dereferenced[key] = obj;
3263 processed[objId] = dereferenced;
3265 item.target[item.key] = dereferenced;
3270 getKeys: function Dict_getKeys() {
3271 return Object.keys(this.map);
3274 set: function Dict_set(key, value) {
3275 this.map[key] = value;
3278 has: function Dict_has(key) {
3279 return key in this.map;
3282 forEach: function Dict_forEach(callback) {
3283 for (var key in this.map) {
3284 callback(key, this.get(key));
3289 Dict.empty = new Dict(null);
3294 var Ref = (function RefClosure() {
3295 function Ref(num, gen) {
3301 toString: function Ref_toString() {
3302 // This function is hot, so we make the string as compact as possible.
3303 // |this.gen| is almost always zero, so we treat that case specially.
3304 var str = this.num + 'R';
3305 if (this.gen !== 0) {
3315 // The reference is identified by number and generation.
3316 // This structure stores only one instance of the reference.
3317 var RefSet = (function RefSetClosure() {
3322 RefSet.prototype = {
3323 has: function RefSet_has(ref) {
3324 return ref.toString() in this.dict;
3327 put: function RefSet_put(ref) {
3328 this.dict[ref.toString()] = true;
3331 remove: function RefSet_remove(ref) {
3332 delete this.dict[ref.toString()];
3339 var RefSetCache = (function RefSetCacheClosure() {
3340 function RefSetCache() {
3341 this.dict = Object.create(null);
3344 RefSetCache.prototype = {
3345 get: function RefSetCache_get(ref) {
3346 return this.dict[ref.toString()];
3349 has: function RefSetCache_has(ref) {
3350 return ref.toString() in this.dict;
3353 put: function RefSetCache_put(ref, obj) {
3354 this.dict[ref.toString()] = obj;
3357 putAlias: function RefSetCache_putAlias(ref, aliasRef) {
3358 this.dict[ref.toString()] = this.get(aliasRef);
3361 forEach: function RefSetCache_forEach(fn, thisArg) {
3362 for (var i in this.dict) {
3363 fn.call(thisArg, this.dict[i]);
3367 clear: function RefSetCache_clear() {
3368 this.dict = Object.create(null);
3375 var Catalog = (function CatalogClosure() {
3376 function Catalog(pdfManager, xref) {
3377 this.pdfManager = pdfManager;
3379 this.catDict = xref.getCatalogObj();
3380 this.fontCache = new RefSetCache();
3381 assert(isDict(this.catDict),
3382 'catalog object is not a dictionary');
3384 this.pagePromises = [];
3387 Catalog.prototype = {
3389 var streamRef = this.catDict.getRaw('Metadata');
3390 if (!isRef(streamRef)) {
3391 return shadow(this, 'metadata', null);
3394 var encryptMetadata = (!this.xref.encrypt ? false :
3395 this.xref.encrypt.encryptMetadata);
3397 var stream = this.xref.fetch(streamRef, !encryptMetadata);
3399 if (stream && isDict(stream.dict)) {
3400 var type = stream.dict.get('Type');
3401 var subtype = stream.dict.get('Subtype');
3403 if (isName(type) && isName(subtype) &&
3404 type.name === 'Metadata' && subtype.name === 'XML') {
3405 // XXX: This should examine the charset the XML document defines,
3406 // however since there are currently no real means to decode
3407 // arbitrary charsets, let's just hope that the author of the PDF
3408 // was reasonable enough to stick with the XML default charset,
3411 metadata = stringToUTF8String(bytesToString(stream.getBytes()));
3413 info('Skipping invalid metadata.');
3418 return shadow(this, 'metadata', metadata);
3420 get toplevelPagesDict() {
3421 var pagesObj = this.catDict.get('Pages');
3422 assert(isDict(pagesObj), 'invalid top-level pages dictionary');
3423 // shadow the prototype getter
3424 return shadow(this, 'toplevelPagesDict', pagesObj);
3426 get documentOutline() {
3429 obj = this.readDocumentOutline();
3431 if (ex instanceof MissingDataException) {
3434 warn('Unable to read document outline');
3436 return shadow(this, 'documentOutline', obj);
3438 readDocumentOutline: function Catalog_readDocumentOutline() {
3439 var xref = this.xref;
3440 var obj = this.catDict.get('Outlines');
3441 var root = { items: [] };
3443 obj = obj.getRaw('First');
3444 var processed = new RefSet();
3446 var queue = [{obj: obj, parent: root}];
3447 // to avoid recursion keeping track of the items
3448 // in the processed dictionary
3450 while (queue.length > 0) {
3451 var i = queue.shift();
3452 var outlineDict = xref.fetchIfRef(i.obj);
3453 if (outlineDict === null) {
3456 if (!outlineDict.has('Title')) {
3457 error('Invalid outline item');
3459 var dest = outlineDict.get('A');
3461 dest = dest.get('D');
3462 } else if (outlineDict.has('Dest')) {
3463 dest = outlineDict.getRaw('Dest');
3468 var title = outlineDict.get('Title');
3471 title: stringToPDFString(title),
3472 color: outlineDict.get('C') || [0, 0, 0],
3473 count: outlineDict.get('Count'),
3474 bold: !!(outlineDict.get('F') & 2),
3475 italic: !!(outlineDict.get('F') & 1),
3478 i.parent.items.push(outlineItem);
3479 obj = outlineDict.getRaw('First');
3480 if (isRef(obj) && !processed.has(obj)) {
3481 queue.push({obj: obj, parent: outlineItem});
3484 obj = outlineDict.getRaw('Next');
3485 if (isRef(obj) && !processed.has(obj)) {
3486 queue.push({obj: obj, parent: i.parent});
3492 return (root.items.length > 0 ? root.items : null);
3495 var obj = this.toplevelPagesDict.get('Count');
3498 'page count in top level pages object is not an integer'
3500 // shadow the prototype getter
3501 return shadow(this, 'num', obj);
3503 get destinations() {
3504 function fetchDestination(dest) {
3505 return isDict(dest) ? dest.get('D') : dest;
3508 var xref = this.xref;
3509 var dests = {}, nameTreeRef, nameDictionaryRef;
3510 var obj = this.catDict.get('Names');
3511 if (obj && obj.has('Dests')) {
3512 nameTreeRef = obj.getRaw('Dests');
3513 } else if (this.catDict.has('Dests')) {
3514 nameDictionaryRef = this.catDict.get('Dests');
3517 if (nameDictionaryRef) {
3518 // reading simple destination dictionary
3519 obj = nameDictionaryRef;
3520 obj.forEach(function catalogForEach(key, value) {
3524 dests[key] = fetchDestination(value);
3528 var nameTree = new NameTree(nameTreeRef, xref);
3529 var names = nameTree.getAll();
3530 for (var name in names) {
3531 if (!names.hasOwnProperty(name)) {
3534 dests[name] = fetchDestination(names[name]);
3537 return shadow(this, 'destinations', dests);
3539 getDestination: function Catalog_getDestination(destinationId) {
3540 function fetchDestination(dest) {
3541 return isDict(dest) ? dest.get('D') : dest;
3544 var xref = this.xref;
3545 var dest, nameTreeRef, nameDictionaryRef;
3546 var obj = this.catDict.get('Names');
3547 if (obj && obj.has('Dests')) {
3548 nameTreeRef = obj.getRaw('Dests');
3549 } else if (this.catDict.has('Dests')) {
3550 nameDictionaryRef = this.catDict.get('Dests');
3553 if (nameDictionaryRef) {
3554 // reading simple destination dictionary
3555 obj = nameDictionaryRef;
3556 obj.forEach(function catalogForEach(key, value) {
3560 if (key === destinationId) {
3561 dest = fetchDestination(value);
3566 var nameTree = new NameTree(nameTreeRef, xref);
3567 dest = fetchDestination(nameTree.get(destinationId));
3572 var xref = this.xref;
3573 var attachments = null, nameTreeRef;
3574 var obj = this.catDict.get('Names');
3576 nameTreeRef = obj.getRaw('EmbeddedFiles');
3580 var nameTree = new NameTree(nameTreeRef, xref);
3581 var names = nameTree.getAll();
3582 for (var name in names) {
3583 if (!names.hasOwnProperty(name)) {
3586 var fs = new FileSpec(names[name], xref);
3590 attachments[stringToPDFString(name)] = fs.serializable;
3593 return shadow(this, 'attachments', attachments);
3596 var xref = this.xref;
3597 var obj = this.catDict.get('Names');
3599 var javaScript = [];
3600 if (obj && obj.has('JavaScript')) {
3601 var nameTree = new NameTree(obj.getRaw('JavaScript'), xref);
3602 var names = nameTree.getAll();
3603 for (var name in names) {
3604 if (!names.hasOwnProperty(name)) {
3607 // We don't really use the JavaScript right now. This code is
3608 // defensive so we don't cause errors on document load.
3609 var jsDict = names[name];
3610 if (!isDict(jsDict)) {
3613 var type = jsDict.get('S');
3614 if (!isName(type) || type.name !== 'JavaScript') {
3617 var js = jsDict.get('JS');
3618 if (!isString(js) && !isStream(js)) {
3622 js = bytesToString(js.getBytes());
3624 javaScript.push(stringToPDFString(js));
3628 // Append OpenAction actions to javaScript array
3629 var openactionDict = this.catDict.get('OpenAction');
3630 if (isDict(openactionDict)) {
3631 var objType = openactionDict.get('Type');
3632 var actionType = openactionDict.get('S');
3633 var action = openactionDict.get('N');
3634 var isPrintAction = (isName(objType) && objType.name === 'Action' &&
3635 isName(actionType) && actionType.name === 'Named' &&
3636 isName(action) && action.name === 'Print');
3638 if (isPrintAction) {
3639 javaScript.push('print(true);');
3643 return shadow(this, 'javaScript', javaScript);
3646 cleanup: function Catalog_cleanup() {
3648 this.fontCache.forEach(function (promise) {
3649 promises.push(promise);
3651 return Promise.all(promises).then(function (translatedFonts) {
3652 for (var i = 0, ii = translatedFonts.length; i < ii; i++) {
3653 var font = translatedFonts[i].dict;
3654 delete font.translated;
3656 this.fontCache.clear();
3660 getPage: function Catalog_getPage(pageIndex) {
3661 if (!(pageIndex in this.pagePromises)) {
3662 this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(
3666 return new Page(this.pdfManager, this.xref, pageIndex, dict, ref,
3671 return this.pagePromises[pageIndex];
3674 getPageDict: function Catalog_getPageDict(pageIndex) {
3675 var capability = createPromiseCapability();
3676 var nodesToVisit = [this.catDict.getRaw('Pages')];
3677 var currentPageIndex = 0;
3678 var xref = this.xref;
3679 var checkAllKids = false;
3682 while (nodesToVisit.length) {
3683 var currentNode = nodesToVisit.pop();
3685 if (isRef(currentNode)) {
3686 xref.fetchAsync(currentNode).then(function (obj) {
3687 if (isDict(obj, 'Page') || (isDict(obj) && !obj.has('Kids'))) {
3688 if (pageIndex === currentPageIndex) {
3689 capability.resolve([obj, currentNode]);
3696 nodesToVisit.push(obj);
3698 }, capability.reject);
3702 // Must be a child page dictionary.
3704 isDict(currentNode),
3705 'page dictionary kid reference points to wrong type of object'
3707 var count = currentNode.get('Count');
3708 // If the current node doesn't have any children, avoid getting stuck
3709 // in an empty node further down in the tree (see issue5644.pdf).
3711 checkAllKids = true;
3713 // Skip nodes where the page can't be.
3714 if (currentPageIndex + count <= pageIndex) {
3715 currentPageIndex += count;
3719 var kids = currentNode.get('Kids');
3720 assert(isArray(kids), 'page dictionary kids object is not an array');
3721 if (!checkAllKids && count === kids.length) {
3722 // Nodes that don't have the page have been skipped and this is the
3723 // bottom of the tree which means the page requested must be a
3724 // descendant of this pages node. Ideally we would just resolve the
3725 // promise with the page ref here, but there is the case where more
3726 // pages nodes could link to single a page (see issue 3666 pdf). To
3727 // handle this push it back on the queue so if it is a pages node it
3728 // will be descended into.
3729 nodesToVisit = [kids[pageIndex - currentPageIndex]];
3730 currentPageIndex = pageIndex;
3733 for (var last = kids.length - 1; last >= 0; last--) {
3734 nodesToVisit.push(kids[last]);
3738 capability.reject('Page index ' + pageIndex + ' not found.');
3741 return capability.promise;
3744 getPageIndex: function Catalog_getPageIndex(ref) {
3745 // The page tree nodes have the count of all the leaves below them. To get
3746 // how many pages are before we just have to walk up the tree and keep
3747 // adding the count of siblings to the left of the node.
3748 var xref = this.xref;
3749 function pagesBeforeRef(kidRef) {
3752 return xref.fetchAsync(kidRef).then(function (node) {
3756 parentRef = node.getRaw('Parent');
3757 return node.getAsync('Parent');
3758 }).then(function (parent) {
3762 return parent.getAsync('Kids');
3763 }).then(function (kids) {
3767 var kidPromises = [];
3769 for (var i = 0; i < kids.length; i++) {
3771 assert(isRef(kid), 'kids must be a ref');
3772 if (kid.num === kidRef.num) {
3776 kidPromises.push(xref.fetchAsync(kid).then(function (kid) {
3777 if (kid.has('Count')) {
3778 var count = kid.get('Count');
3780 } else { // page leaf node
3786 error('kid ref not found in parents kids');
3788 return Promise.all(kidPromises).then(function () {
3789 return [total, parentRef];
3795 function next(ref) {
3796 return pagesBeforeRef(ref).then(function (args) {
3800 var count = args[0];
3801 var parentRef = args[1];
3803 return next(parentRef);
3814 var XRef = (function XRefClosure() {
3815 function XRef(stream, password) {
3816 this.stream = stream;
3819 // prepare the XRef cache
3821 this.password = password;
3829 setStartXRef: function XRef_setStartXRef(startXRef) {
3830 // Store the starting positions of xref tables as we process them
3831 // so we can recover from missing data errors
3832 this.startXRefQueue = [startXRef];
3835 parse: function XRef_parse(recoveryMode) {
3837 if (!recoveryMode) {
3838 trailerDict = this.readXRef();
3840 warn('Indexing all PDF objects');
3841 trailerDict = this.indexObjects();
3843 trailerDict.assignXref(this);
3844 this.trailer = trailerDict;
3845 var encrypt = trailerDict.get('Encrypt');
3847 var ids = trailerDict.get('ID');
3848 var fileId = (ids && ids.length) ? ids[0] : '';
3849 this.encrypt = new CipherTransformFactory(encrypt, fileId,
3853 // get the root dictionary (catalog) object
3854 if (!(this.root = trailerDict.get('Root'))) {
3855 error('Invalid root reference');
3859 processXRefTable: function XRef_processXRefTable(parser) {
3860 if (!('tableState' in this)) {
3861 // Stores state of the table as we process it so we can resume
3862 // from middle of table in case of missing data error
3865 streamPos: parser.lexer.stream.pos,
3866 parserBuf1: parser.buf1,
3867 parserBuf2: parser.buf2
3871 var obj = this.readXRefTable(parser);
3874 if (!isCmd(obj, 'trailer')) {
3875 error('Invalid XRef table: could not find trailer dictionary');
3877 // Read trailer dictionary, e.g.
3882 // /ID [ <81b14aafa313db63dbd6f981e49f94f4> ]
3884 // The parser goes through the entire stream << ... >> and provides
3885 // a getter interface for the key-value table
3886 var dict = parser.getObj();
3888 // The pdflib PDF generator can generate a nested trailer dictionary
3889 if (!isDict(dict) && dict.dict) {
3892 if (!isDict(dict)) {
3893 error('Invalid XRef table: could not parse trailer dictionary');
3895 delete this.tableState;
3900 readXRefTable: function XRef_readXRefTable(parser) {
3901 // Example of cross-reference table:
3903 // 0 1 <-- subsection header (first obj #, obj count)
3904 // 0000000000 65535 f <-- actual object (offset, generation #, f/n)
3905 // 23 2 <-- subsection header ... and so on ...
3906 // 0000025518 00002 n
3907 // 0000025635 00000 n
3911 var stream = parser.lexer.stream;
3912 var tableState = this.tableState;
3913 stream.pos = tableState.streamPos;
3914 parser.buf1 = tableState.parserBuf1;
3915 parser.buf2 = tableState.parserBuf2;
3917 // Outer loop is over subsection headers
3921 if (!('firstEntryNum' in tableState) || !('entryCount' in tableState)) {
3922 if (isCmd(obj = parser.getObj(), 'trailer')) {
3925 tableState.firstEntryNum = obj;
3926 tableState.entryCount = parser.getObj();
3929 var first = tableState.firstEntryNum;
3930 var count = tableState.entryCount;
3931 if (!isInt(first) || !isInt(count)) {
3932 error('Invalid XRef table: wrong types in subsection header');
3934 // Inner loop is over objects themselves
3935 for (var i = tableState.entryNum; i < count; i++) {
3936 tableState.streamPos = stream.pos;
3937 tableState.entryNum = i;
3938 tableState.parserBuf1 = parser.buf1;
3939 tableState.parserBuf2 = parser.buf2;
3942 entry.offset = parser.getObj();
3943 entry.gen = parser.getObj();
3944 var type = parser.getObj();
3946 if (isCmd(type, 'f')) {
3948 } else if (isCmd(type, 'n')) {
3949 entry.uncompressed = true;
3952 // Validate entry obj
3953 if (!isInt(entry.offset) || !isInt(entry.gen) ||
3954 !(entry.free || entry.uncompressed)) {
3955 error('Invalid entry in XRef subsection: ' + first + ', ' + count);
3958 if (!this.entries[i + first]) {
3959 this.entries[i + first] = entry;
3963 tableState.entryNum = 0;
3964 tableState.streamPos = stream.pos;
3965 tableState.parserBuf1 = parser.buf1;
3966 tableState.parserBuf2 = parser.buf2;
3967 delete tableState.firstEntryNum;
3968 delete tableState.entryCount;
3971 // Per issue 3248: hp scanners generate bad XRef
3972 if (first === 1 && this.entries[1] && this.entries[1].free) {
3973 // shifting the entries
3974 this.entries.shift();
3977 // Sanity check: as per spec, first object must be free
3978 if (this.entries[0] && !this.entries[0].free) {
3979 error('Invalid XRef table: unexpected first object');
3984 processXRefStream: function XRef_processXRefStream(stream) {
3985 if (!('streamState' in this)) {
3986 // Stores state of the stream as we process it so we can resume
3987 // from middle of stream in case of missing data error
3988 var streamParameters = stream.dict;
3989 var byteWidths = streamParameters.get('W');
3990 var range = streamParameters.get('Index');
3992 range = [0, streamParameters.get('Size')];
3995 this.streamState = {
3997 byteWidths: byteWidths,
3999 streamPos: stream.pos
4002 this.readXRefStream(stream);
4003 delete this.streamState;
4008 readXRefStream: function XRef_readXRefStream(stream) {
4010 var streamState = this.streamState;
4011 stream.pos = streamState.streamPos;
4013 var byteWidths = streamState.byteWidths;
4014 var typeFieldWidth = byteWidths[0];
4015 var offsetFieldWidth = byteWidths[1];
4016 var generationFieldWidth = byteWidths[2];
4018 var entryRanges = streamState.entryRanges;
4019 while (entryRanges.length > 0) {
4020 var first = entryRanges[0];
4021 var n = entryRanges[1];
4023 if (!isInt(first) || !isInt(n)) {
4024 error('Invalid XRef range fields: ' + first + ', ' + n);
4026 if (!isInt(typeFieldWidth) || !isInt(offsetFieldWidth) ||
4027 !isInt(generationFieldWidth)) {
4028 error('Invalid XRef entry fields length: ' + first + ', ' + n);
4030 for (i = streamState.entryNum; i < n; ++i) {
4031 streamState.entryNum = i;
4032 streamState.streamPos = stream.pos;
4034 var type = 0, offset = 0, generation = 0;
4035 for (j = 0; j < typeFieldWidth; ++j) {
4036 type = (type << 8) | stream.getByte();
4038 // if type field is absent, its default value is 1
4039 if (typeFieldWidth === 0) {
4042 for (j = 0; j < offsetFieldWidth; ++j) {
4043 offset = (offset << 8) | stream.getByte();
4045 for (j = 0; j < generationFieldWidth; ++j) {
4046 generation = (generation << 8) | stream.getByte();
4049 entry.offset = offset;
4050 entry.gen = generation;
4056 entry.uncompressed = true;
4061 error('Invalid XRef entry type: ' + type);
4063 if (!this.entries[first + i]) {
4064 this.entries[first + i] = entry;
4068 streamState.entryNum = 0;
4069 streamState.streamPos = stream.pos;
4070 entryRanges.splice(0, 2);
4074 indexObjects: function XRef_indexObjects() {
4075 // Simple scan through the PDF content to find objects,
4076 // trailers and XRef streams.
4077 function readToken(data, offset) {
4078 var token = '', ch = data[offset];
4079 while (ch !== 13 && ch !== 10) {
4080 if (++offset >= data.length) {
4083 token += String.fromCharCode(ch);
4088 function skipUntil(data, offset, what) {
4089 var length = what.length, dataLength = data.length;
4091 // finding byte sequence
4092 while (offset < dataLength) {
4094 while (i < length && data[offset + i] === what[i]) {
4098 break; // sequence found
4105 var trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);
4106 var startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114,
4108 var endobjBytes = new Uint8Array([101, 110, 100, 111, 98, 106]);
4109 var xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
4111 var stream = this.stream;
4113 var buffer = stream.getBytes();
4114 var position = stream.start, length = buffer.length;
4115 var trailers = [], xrefStms = [];
4116 while (position < length) {
4117 var ch = buffer[position];
4118 if (ch === 32 || ch === 9 || ch === 13 || ch === 10) {
4122 if (ch === 37) { // %-comment
4125 if (position >= length) {
4128 ch = buffer[position];
4129 } while (ch !== 13 && ch !== 10);
4132 var token = readToken(buffer, position);
4134 if (token === 'xref') {
4135 position += skipUntil(buffer, position, trailerBytes);
4136 trailers.push(position);
4137 position += skipUntil(buffer, position, startxrefBytes);
4138 } else if ((m = /^(\d+)\s+(\d+)\s+obj\b/.exec(token))) {
4139 if (typeof this.entries[m[1]] === 'undefined') {
4140 this.entries[m[1]] = {
4146 var contentLength = skipUntil(buffer, position, endobjBytes) + 7;
4147 var content = buffer.subarray(position, position + contentLength);
4149 // checking XRef stream suspect
4150 // (it shall have '/XRef' and next char is not a letter)
4151 var xrefTagOffset = skipUntil(content, 0, xrefBytes);
4152 if (xrefTagOffset < contentLength &&
4153 content[xrefTagOffset + 5] < 64) {
4154 xrefStms.push(position);
4155 this.xrefstms[position] = 1; // don't read it recursively
4158 position += contentLength;
4160 position += token.length + 1;
4163 // reading XRef streams
4165 for (i = 0, ii = xrefStms.length; i < ii; ++i) {
4166 this.startXRefQueue.push(xrefStms[i]);
4167 this.readXRef(/* recoveryMode */ true);
4169 // finding main trailer
4171 for (i = 0, ii = trailers.length; i < ii; ++i) {
4172 stream.pos = trailers[i];
4173 var parser = new Parser(new Lexer(stream), true, this);
4174 var obj = parser.getObj();
4175 if (!isCmd(obj, 'trailer')) {
4178 // read the trailer dictionary
4179 if (!isDict(dict = parser.getObj())) {
4182 // taking the first one with 'ID'
4183 if (dict.has('ID')) {
4187 // no tailer with 'ID', taking last one (if exists)
4192 // calling error() would reject worker with an UnknownErrorException.
4193 throw new InvalidPDFException('Invalid PDF structure');
4196 readXRef: function XRef_readXRef(recoveryMode) {
4197 var stream = this.stream;
4200 while (this.startXRefQueue.length) {
4201 var startXRef = this.startXRefQueue[0];
4203 stream.pos = startXRef + stream.start;
4205 var parser = new Parser(new Lexer(stream), true, this);
4206 var obj = parser.getObj();
4210 if (isCmd(obj, 'xref')) {
4211 // Parse end-of-file XRef
4212 dict = this.processXRefTable(parser);
4213 if (!this.topDict) {
4214 this.topDict = dict;
4217 // Recursively get other XRefs 'XRefStm', if any
4218 obj = dict.get('XRefStm');
4221 // ignore previously loaded xref streams
4222 // (possible infinite recursion)
4223 if (!(pos in this.xrefstms)) {
4224 this.xrefstms[pos] = 1;
4225 this.startXRefQueue.push(pos);
4228 } else if (isInt(obj)) {
4229 // Parse in-stream XRef
4230 if (!isInt(parser.getObj()) ||
4231 !isCmd(parser.getObj(), 'obj') ||
4232 !isStream(obj = parser.getObj())) {
4233 error('Invalid XRef stream');
4235 dict = this.processXRefStream(obj);
4236 if (!this.topDict) {
4237 this.topDict = dict;
4240 error('Failed to read XRef stream');
4243 error('Invalid XRef stream header');
4246 // Recursively get previous dictionary, if any
4247 obj = dict.get('Prev');
4249 this.startXRefQueue.push(obj);
4250 } else if (isRef(obj)) {
4251 // The spec says Prev must not be a reference, i.e. "/Prev NNN"
4252 // This is a fallback for non-compliant PDFs, i.e. "/Prev NNN 0 R"
4253 this.startXRefQueue.push(obj.num);
4256 this.startXRefQueue.shift();
4259 return this.topDict;
4261 if (e instanceof MissingDataException) {
4264 info('(while reading XRef): ' + e);
4270 throw new XRefParseException();
4273 getEntry: function XRef_getEntry(i) {
4274 var xrefEntry = this.entries[i];
4275 if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {
4281 fetchIfRef: function XRef_fetchIfRef(obj) {
4285 return this.fetch(obj);
4288 fetch: function XRef_fetch(ref, suppressEncryption) {
4289 assert(isRef(ref), 'ref object is not a reference');
4291 if (num in this.cache) {
4292 var cacheEntry = this.cache[num];
4296 var xrefEntry = this.getEntry(num);
4298 // the referenced entry can be free
4299 if (xrefEntry === null) {
4300 return (this.cache[num] = null);
4303 if (xrefEntry.uncompressed) {
4304 xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
4306 xrefEntry = this.fetchCompressed(xrefEntry, suppressEncryption);
4308 if (isDict(xrefEntry)){
4309 xrefEntry.objId = ref.toString();
4310 } else if (isStream(xrefEntry)) {
4311 xrefEntry.dict.objId = ref.toString();
4316 fetchUncompressed: function XRef_fetchUncompressed(ref, xrefEntry,
4317 suppressEncryption) {
4320 if (xrefEntry.gen !== gen) {
4321 error('inconsistent generation in XRef');
4323 var stream = this.stream.makeSubStream(xrefEntry.offset +
4325 var parser = new Parser(new Lexer(stream), true, this);
4326 var obj1 = parser.getObj();
4327 var obj2 = parser.getObj();
4328 var obj3 = parser.getObj();
4329 if (!isInt(obj1) || parseInt(obj1, 10) !== num ||
4330 !isInt(obj2) || parseInt(obj2, 10) !== gen ||
4332 error('bad XRef entry');
4334 if (!isCmd(obj3, 'obj')) {
4335 // some bad PDFs use "obj1234" and really mean 1234
4336 if (obj3.cmd.indexOf('obj') === 0) {
4337 num = parseInt(obj3.cmd.substring(3), 10);
4342 error('bad XRef entry');
4344 if (this.encrypt && !suppressEncryption) {
4345 xrefEntry = parser.getObj(this.encrypt.createCipherTransform(num, gen));
4347 xrefEntry = parser.getObj();
4349 if (!isStream(xrefEntry)) {
4350 this.cache[num] = xrefEntry;
4355 fetchCompressed: function XRef_fetchCompressed(xrefEntry,
4356 suppressEncryption) {
4357 var tableOffset = xrefEntry.offset;
4358 var stream = this.fetch(new Ref(tableOffset, 0));
4359 if (!isStream(stream)) {
4360 error('bad ObjStm stream');
4362 var first = stream.dict.get('First');
4363 var n = stream.dict.get('N');
4364 if (!isInt(first) || !isInt(n)) {
4365 error('invalid first and n parameters for ObjStm stream');
4367 var parser = new Parser(new Lexer(stream), false, this);
4368 parser.allowStreams = true;
4369 var i, entries = [], num, nums = [];
4370 // read the object numbers to populate cache
4371 for (i = 0; i < n; ++i) {
4372 num = parser.getObj();
4374 error('invalid object number in the ObjStm stream: ' + num);
4377 var offset = parser.getObj();
4378 if (!isInt(offset)) {
4379 error('invalid object offset in the ObjStm stream: ' + offset);
4382 // read stream objects for cache
4383 for (i = 0; i < n; ++i) {
4384 entries.push(parser.getObj());
4386 var entry = this.entries[num];
4387 if (entry && entry.offset === tableOffset && entry.gen === i) {
4388 this.cache[num] = entries[i];
4391 xrefEntry = entries[xrefEntry.gen];
4392 if (xrefEntry === undefined) {
4393 error('bad XRef entry for compressed object');
4398 fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) {
4400 return Promise.resolve(obj);
4402 return this.fetchAsync(obj);
4405 fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) {
4406 var streamManager = this.stream.manager;
4408 return new Promise(function tryFetch(resolve, reject) {
4410 resolve(xref.fetch(ref, suppressEncryption));
4412 if (e instanceof MissingDataException) {
4413 streamManager.requestRange(e.begin, e.end, function () {
4414 tryFetch(resolve, reject);
4423 getCatalogObj: function XRef_getCatalogObj() {
4432 * A NameTree is like a Dict but has some advantageous properties, see the
4433 * spec (7.9.6) for more details.
4434 * TODO: implement all the Dict functions and make this more efficent.
4436 var NameTree = (function NameTreeClosure() {
4437 function NameTree(root, xref) {
4442 NameTree.prototype = {
4443 getAll: function NameTree_getAll() {
4448 var xref = this.xref;
4449 // reading name tree
4450 var processed = new RefSet();
4451 processed.put(this.root);
4452 var queue = [this.root];
4453 while (queue.length > 0) {
4455 var obj = xref.fetchIfRef(queue.shift());
4459 if (obj.has('Kids')) {
4460 var kids = obj.get('Kids');
4461 for (i = 0, n = kids.length; i < n; i++) {
4463 if (processed.has(kid)) {
4464 error('invalid destinations');
4471 var names = obj.get('Names');
4473 for (i = 0, n = names.length; i < n; i += 2) {
4474 dict[names[i]] = xref.fetchIfRef(names[i + 1]);
4481 get: function NameTree_get(destinationId) {
4486 var xref = this.xref;
4487 var kidsOrNames = xref.fetchIfRef(this.root);
4489 var MAX_NAMES_LEVELS = 10;
4492 // Perform a binary search to quickly find the entry that
4493 // contains the named destination we are looking for.
4494 while (kidsOrNames.has('Kids')) {
4496 if (loopCount > MAX_NAMES_LEVELS) {
4497 warn('Search depth limit for named destionations has been reached.');
4501 var kids = kidsOrNames.get('Kids');
4502 if (!isArray(kids)) {
4507 r = kids.length - 1;
4510 var kid = xref.fetchIfRef(kids[m]);
4511 var limits = kid.get('Limits');
4513 if (destinationId < limits[0]) {
4515 } else if (destinationId > limits[1]) {
4518 kidsOrNames = xref.fetchIfRef(kids[m]);
4527 // If we get here, then we have found the right entry. Now
4528 // go through the named destinations in the Named dictionary
4529 // until we find the exact destination we're looking for.
4530 var names = kidsOrNames.get('Names');
4531 if (isArray(names)) {
4532 // Perform a binary search to reduce the lookup time.
4534 r = names.length - 2;
4536 // Check only even indices (0, 2, 4, ...) because the
4537 // odd indices contain the actual D array.
4539 if (destinationId < names[m]) {
4541 } else if (destinationId > names[m]) {
4544 return xref.fetchIfRef(names[m + 1]);
4555 * "A PDF file can refer to the contents of another file by using a File
4556 * Specification (PDF 1.1)", see the spec (7.11) for more details.
4557 * NOTE: Only embedded files are supported (as part of the attachments support)
4558 * TODO: support the 'URL' file system (with caching if !/V), portable
4559 * collections attributes and related files (/RF)
4561 var FileSpec = (function FileSpecClosure() {
4562 function FileSpec(root, xref) {
4563 if (!root || !isDict(root)) {
4568 if (root.has('FS')) {
4569 this.fs = root.get('FS');
4571 this.description = root.has('Desc') ?
4572 stringToPDFString(root.get('Desc')) :
4574 if (root.has('RF')) {
4575 warn('Related file specifications are not supported');
4577 this.contentAvailable = true;
4578 if (!root.has('EF')) {
4579 this.contentAvailable = false;
4580 warn('Non-embedded file specifications are not supported');
4584 function pickPlatformItem(dict) {
4585 // Look for the filename in this order:
4586 // UF, F, Unix, Mac, DOS
4587 if (dict.has('UF')) {
4588 return dict.get('UF');
4589 } else if (dict.has('F')) {
4590 return dict.get('F');
4591 } else if (dict.has('Unix')) {
4592 return dict.get('Unix');
4593 } else if (dict.has('Mac')) {
4594 return dict.get('Mac');
4595 } else if (dict.has('DOS')) {
4596 return dict.get('DOS');
4602 FileSpec.prototype = {
4604 if (!this._filename && this.root) {
4605 var filename = pickPlatformItem(this.root) || 'unnamed';
4606 this._filename = stringToPDFString(filename).
4607 replace(/\\\\/g, '\\').
4608 replace(/\\\//g, '/').
4609 replace(/\\/g, '/');
4611 return this._filename;
4614 if (!this.contentAvailable) {
4617 if (!this.contentRef && this.root) {
4618 this.contentRef = pickPlatformItem(this.root.get('EF'));
4621 if (this.contentRef) {
4622 var xref = this.xref;
4623 var fileObj = xref.fetchIfRef(this.contentRef);
4624 if (fileObj && isStream(fileObj)) {
4625 content = fileObj.getBytes();
4627 warn('Embedded file specification points to non-existing/invalid ' +
4631 warn('Embedded file specification does not have a content');
4635 get serializable() {
4637 filename: this.filename,
4638 content: this.content
4646 * A helper for loading missing data in object graphs. It traverses the graph
4647 * depth first and queues up any objects that have missing data. Once it has
4648 * has traversed as many objects that are available it attempts to bundle the
4649 * missing data requests and then resume from the nodes that weren't ready.
4651 * NOTE: It provides protection from circular references by keeping track of
4652 * of loaded references. However, you must be careful not to load any graphs
4653 * that have references to the catalog or other pages since that will cause the
4654 * entire PDF document object graph to be traversed.
4656 var ObjectLoader = (function() {
4657 function mayHaveChildren(value) {
4658 return isRef(value) || isDict(value) || isArray(value) || isStream(value);
4661 function addChildren(node, nodesToVisit) {
4663 if (isDict(node) || isStream(node)) {
4668 map = node.dict.map;
4670 for (var key in map) {
4672 if (mayHaveChildren(value)) {
4673 nodesToVisit.push(value);
4676 } else if (isArray(node)) {
4677 for (var i = 0, ii = node.length; i < ii; i++) {
4679 if (mayHaveChildren(value)) {
4680 nodesToVisit.push(value);
4686 function ObjectLoader(obj, keys, xref) {
4693 ObjectLoader.prototype = {
4694 load: function ObjectLoader_load() {
4695 var keys = this.keys;
4696 this.capability = createPromiseCapability();
4697 // Don't walk the graph if all the data is already loaded.
4698 if (!(this.xref.stream instanceof ChunkedStream) ||
4699 this.xref.stream.getMissingChunks().length === 0) {
4700 this.capability.resolve();
4701 return this.capability.promise;
4704 this.refSet = new RefSet();
4705 // Setup the initial nodes to visit.
4706 var nodesToVisit = [];
4707 for (var i = 0; i < keys.length; i++) {
4708 nodesToVisit.push(this.obj[keys[i]]);
4711 this.walk(nodesToVisit);
4712 return this.capability.promise;
4715 walk: function ObjectLoader_walk(nodesToVisit) {
4716 var nodesToRevisit = [];
4717 var pendingRequests = [];
4718 // DFS walk of the object graph.
4719 while (nodesToVisit.length) {
4720 var currentNode = nodesToVisit.pop();
4722 // Only references or chunked streams can cause missing data exceptions.
4723 if (isRef(currentNode)) {
4724 // Skip nodes that have already been visited.
4725 if (this.refSet.has(currentNode)) {
4729 var ref = currentNode;
4730 this.refSet.put(ref);
4731 currentNode = this.xref.fetch(currentNode);
4733 if (!(e instanceof MissingDataException)) {
4736 nodesToRevisit.push(currentNode);
4737 pendingRequests.push({ begin: e.begin, end: e.end });
4740 if (currentNode && currentNode.getBaseStreams) {
4741 var baseStreams = currentNode.getBaseStreams();
4742 var foundMissingData = false;
4743 for (var i = 0; i < baseStreams.length; i++) {
4744 var stream = baseStreams[i];
4745 if (stream.getMissingChunks && stream.getMissingChunks().length) {
4746 foundMissingData = true;
4747 pendingRequests.push({
4748 begin: stream.start,
4753 if (foundMissingData) {
4754 nodesToRevisit.push(currentNode);
4758 addChildren(currentNode, nodesToVisit);
4761 if (pendingRequests.length) {
4762 this.xref.stream.manager.requestRanges(pendingRequests,
4763 function pendingRequestCallback() {
4764 nodesToVisit = nodesToRevisit;
4765 for (var i = 0; i < nodesToRevisit.length; i++) {
4766 var node = nodesToRevisit[i];
4767 // Remove any reference nodes from the currrent refset so they
4768 // aren't skipped when we revist them.
4770 this.refSet.remove(node);
4773 this.walk(nodesToVisit);
4777 // Everything is loaded.
4779 this.capability.resolve();
4783 return ObjectLoader;
4787 var ISOAdobeCharset = [
4788 '.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar',
4789 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
4790 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero',
4791 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
4792 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question',
4793 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
4794 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
4795 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
4796 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
4797 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
4798 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent',
4799 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency',
4800 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft',
4801 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl',
4802 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
4803 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis',
4804 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde',
4805 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla',
4806 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine',
4807 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash',
4808 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu',
4809 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter',
4810 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior',
4811 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright',
4812 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde',
4813 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute',
4814 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex',
4815 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex',
4816 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute',
4817 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
4818 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
4819 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
4820 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis',
4821 'ugrave', 'yacute', 'ydieresis', 'zcaron'
4824 var ExpertCharset = [
4825 '.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle',
4826 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior',
4827 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma',
4828 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle',
4829 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle',
4830 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle',
4831 'colon', 'semicolon', 'commasuperior', 'threequartersemdash',
4832 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior',
4833 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
4834 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
4835 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior',
4836 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
4837 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall',
4838 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall',
4839 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall',
4840 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary',
4841 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle',
4842 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall',
4843 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
4844 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall',
4845 'Cedillasmall', 'onequarter', 'onehalf', 'threequarters',
4846 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths',
4847 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior',
4848 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
4849 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
4850 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
4851 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
4852 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
4853 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall',
4854 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
4855 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall',
4856 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall',
4857 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall',
4858 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall',
4859 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall',
4860 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall',
4864 var ExpertSubsetCharset = [
4865 '.notdef', 'space', 'dollaroldstyle', 'dollarsuperior',
4866 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader',
4867 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction',
4868 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle',
4869 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle',
4870 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior',
4871 'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior',
4872 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
4873 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
4874 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior',
4875 'parenrightinferior', 'hyphensuperior', 'colonmonetary', 'onefitted',
4876 'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior', 'onequarter',
4877 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths',
4878 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior',
4879 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
4880 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
4881 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
4882 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
4883 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
4884 'periodinferior', 'commainferior'
4888 var DEFAULT_ICON_SIZE = 22; // px
4889 var SUPPORTED_TYPES = ['Link', 'Text', 'Widget'];
4891 var Annotation = (function AnnotationClosure() {
4892 // 12.5.5: Algorithm: Appearance streams
4893 function getTransformMatrix(rect, bbox, matrix) {
4894 var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix);
4895 var minX = bounds[0];
4896 var minY = bounds[1];
4897 var maxX = bounds[2];
4898 var maxY = bounds[3];
4900 if (minX === maxX || minY === maxY) {
4901 // From real-life file, bbox was [0, 0, 0, 0]. In this case,
4902 // just apply the transform for rect
4903 return [1, 0, 0, 1, rect[0], rect[1]];
4906 var xRatio = (rect[2] - rect[0]) / (maxX - minX);
4907 var yRatio = (rect[3] - rect[1]) / (maxY - minY);
4913 rect[0] - minX * xRatio,
4914 rect[1] - minY * yRatio
4918 function getDefaultAppearance(dict) {
4919 var appearanceState = dict.get('AP');
4920 if (!isDict(appearanceState)) {
4925 var appearances = appearanceState.get('N');
4926 if (isDict(appearances)) {
4927 var as = dict.get('AS');
4928 if (as && appearances.has(as.name)) {
4929 appearance = appearances.get(as.name);
4932 appearance = appearances;
4937 function Annotation(params) {
4938 var dict = params.dict;
4939 var data = this.data = {};
4941 data.subtype = dict.get('Subtype').name;
4942 var rect = dict.get('Rect') || [0, 0, 0, 0];
4943 data.rect = Util.normalizeRect(rect);
4944 data.annotationFlags = dict.get('F');
4946 var color = dict.get('C');
4948 // The PDF spec does not mention how a missing color array is interpreted.
4949 // Adobe Reader seems to default to black in this case.
4950 data.color = [0, 0, 0];
4951 } else if (isArray(color)) {
4952 switch (color.length) {
4954 // Empty array denotes transparent border.
4958 // TODO: implement DeviceGray
4964 // TODO: implement DeviceCMYK
4969 // Some types of annotations have border style dict which has more
4970 // info than the border array
4971 if (dict.has('BS')) {
4972 var borderStyle = dict.get('BS');
4973 data.borderWidth = borderStyle.has('W') ? borderStyle.get('W') : 1;
4975 var borderArray = dict.get('Border') || [0, 0, 1];
4976 data.borderWidth = borderArray[2] || 0;
4978 // TODO: implement proper support for annotations with line dash patterns.
4979 var dashArray = borderArray[3];
4980 if (data.borderWidth > 0 && dashArray) {
4981 if (!isArray(dashArray)) {
4982 // Ignore the border if dashArray is not actually an array,
4983 // this is consistent with the behaviour in Adobe Reader.
4984 data.borderWidth = 0;
4986 var dashArrayLength = dashArray.length;
4987 if (dashArrayLength > 0) {
4988 // According to the PDF specification: the elements in a dashArray
4989 // shall be numbers that are nonnegative and not all equal to zero.
4990 var isInvalid = false;
4991 var numPositive = 0;
4992 for (var i = 0; i < dashArrayLength; i++) {
4993 var validNumber = (+dashArray[i] >= 0);
4997 } else if (dashArray[i] > 0) {
5001 if (isInvalid || numPositive === 0) {
5002 data.borderWidth = 0;
5009 this.appearance = getDefaultAppearance(dict);
5010 data.hasAppearance = !!this.appearance;
5011 data.id = params.ref.num;
5014 Annotation.prototype = {
5016 getData: function Annotation_getData() {
5020 isInvisible: function Annotation_isInvisible() {
5021 var data = this.data;
5022 if (data && SUPPORTED_TYPES.indexOf(data.subtype) !== -1) {
5026 data.annotationFlags && // Default: not invisible
5027 data.annotationFlags & 0x1); // Invisible
5031 isViewable: function Annotation_isViewable() {
5032 var data = this.data;
5033 return !!(!this.isInvisible() &&
5035 (!data.annotationFlags ||
5036 !(data.annotationFlags & 0x22)) && // Hidden or NoView
5037 data.rect); // rectangle is necessary
5040 isPrintable: function Annotation_isPrintable() {
5041 var data = this.data;
5042 return !!(!this.isInvisible() &&
5044 data.annotationFlags && // Default: not printable
5045 data.annotationFlags & 0x4 && // Print
5046 !(data.annotationFlags & 0x2) && // Hidden
5047 data.rect); // rectangle is necessary
5050 loadResources: function Annotation_loadResources(keys) {
5051 return new Promise(function (resolve, reject) {
5052 this.appearance.dict.getAsync('Resources').then(function (resources) {
5057 var objectLoader = new ObjectLoader(resources.map,
5060 objectLoader.load().then(function() {
5067 getOperatorList: function Annotation_getOperatorList(evaluator) {
5069 if (!this.appearance) {
5070 return Promise.resolve(new OperatorList());
5073 var data = this.data;
5075 var appearanceDict = this.appearance.dict;
5076 var resourcesPromise = this.loadResources([
5086 var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
5087 var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
5088 var transform = getTransformMatrix(data.rect, bbox, matrix);
5091 return resourcesPromise.then(function(resources) {
5092 var opList = new OperatorList();
5093 opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
5094 return evaluator.getOperatorList(self.appearance, resources, opList).
5096 opList.addOp(OPS.endAnnotation, []);
5097 self.appearance.reset();
5104 Annotation.getConstructor =
5105 function Annotation_getConstructor(subtype, fieldType) {
5111 // TODO(mack): Implement FreeText annotations
5112 if (subtype === 'Link') {
5113 return LinkAnnotation;
5114 } else if (subtype === 'Text') {
5115 return TextAnnotation;
5116 } else if (subtype === 'Widget') {
5121 if (fieldType === 'Tx') {
5122 return TextWidgetAnnotation;
5124 return WidgetAnnotation;
5131 Annotation.fromRef = function Annotation_fromRef(xref, ref) {
5133 var dict = xref.fetchIfRef(ref);
5134 if (!isDict(dict)) {
5138 var subtype = dict.get('Subtype');
5139 subtype = isName(subtype) ? subtype.name : '';
5144 var fieldType = Util.getInheritableProperty(dict, 'FT');
5145 fieldType = isName(fieldType) ? fieldType.name : '';
5147 var Constructor = Annotation.getConstructor(subtype, fieldType);
5157 var annotation = new Constructor(params);
5159 if (annotation.isViewable() || annotation.isPrintable()) {
5162 if (SUPPORTED_TYPES.indexOf(subtype) === -1) {
5163 warn('unimplemented annotation type: ' + subtype);
5168 Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
5169 annotations, opList, pdfManager, partialEvaluator, intent) {
5171 function reject(e) {
5172 annotationsReadyCapability.reject(e);
5175 var annotationsReadyCapability = createPromiseCapability();
5177 var annotationPromises = [];
5178 for (var i = 0, n = annotations.length; i < n; ++i) {
5179 if (intent === 'display' && annotations[i].isViewable() ||
5180 intent === 'print' && annotations[i].isPrintable()) {
5181 annotationPromises.push(
5182 annotations[i].getOperatorList(partialEvaluator));
5185 Promise.all(annotationPromises).then(function(datas) {
5186 opList.addOp(OPS.beginAnnotations, []);
5187 for (var i = 0, n = datas.length; i < n; ++i) {
5188 var annotOpList = datas[i];
5189 opList.addOpList(annotOpList);
5191 opList.addOp(OPS.endAnnotations, []);
5192 annotationsReadyCapability.resolve();
5195 return annotationsReadyCapability.promise;
5201 var WidgetAnnotation = (function WidgetAnnotationClosure() {
5203 function WidgetAnnotation(params) {
5204 Annotation.call(this, params);
5206 var dict = params.dict;
5207 var data = this.data;
5209 data.fieldValue = stringToPDFString(
5210 Util.getInheritableProperty(dict, 'V') || '');
5211 data.alternativeText = stringToPDFString(dict.get('TU') || '');
5212 data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
5213 var fieldType = Util.getInheritableProperty(dict, 'FT');
5214 data.fieldType = isName(fieldType) ? fieldType.name : '';
5215 data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;
5216 this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty;
5218 // Building the full field name by collecting the field and
5219 // its ancestors 'T' data and joining them using '.'.
5221 var namedItem = dict;
5222 var ref = params.ref;
5224 var parent = namedItem.get('Parent');
5225 var parentRef = namedItem.getRaw('Parent');
5226 var name = namedItem.get('T');
5228 fieldName.unshift(stringToPDFString(name));
5229 } else if (parent && ref) {
5230 // The field name is absent, that means more than one field
5231 // with the same name may exist. Replacing the empty name
5232 // with the '`' plus index in the parent's 'Kids' array.
5233 // This is not in the PDF spec but necessary to id the
5234 // the input controls.
5235 var kids = parent.get('Kids');
5237 for (j = 0, jj = kids.length; j < jj; j++) {
5238 var kidRef = kids[j];
5239 if (kidRef.num === ref.num && kidRef.gen === ref.gen) {
5243 fieldName.unshift('`' + j);
5248 data.fullName = fieldName.join('.');
5251 var parent = Annotation.prototype;
5252 Util.inherit(WidgetAnnotation, Annotation, {
5253 isViewable: function WidgetAnnotation_isViewable() {
5254 if (this.data.fieldType === 'Sig') {
5255 warn('unimplemented annotation type: Widget signature');
5259 return parent.isViewable.call(this);
5263 return WidgetAnnotation;
5266 var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
5267 function TextWidgetAnnotation(params) {
5268 WidgetAnnotation.call(this, params);
5270 this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q');
5271 this.data.annotationType = AnnotationType.WIDGET;
5272 this.data.hasHtml = !this.data.hasAppearance && !!this.data.fieldValue;
5275 Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
5276 getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
5277 if (this.appearance) {
5278 return Annotation.prototype.getOperatorList.call(this, evaluator);
5281 var opList = new OperatorList();
5282 var data = this.data;
5284 // Even if there is an appearance stream, ignore it. This is the
5285 // behaviour used by Adobe Reader.
5286 if (!data.defaultAppearance) {
5287 return Promise.resolve(opList);
5290 var stream = new Stream(stringToBytes(data.defaultAppearance));
5291 return evaluator.getOperatorList(stream, this.fieldResources, opList).
5298 return TextWidgetAnnotation;
5301 var InteractiveAnnotation = (function InteractiveAnnotationClosure() {
5302 function InteractiveAnnotation(params) {
5303 Annotation.call(this, params);
5305 this.data.hasHtml = true;
5308 Util.inherit(InteractiveAnnotation, Annotation, { });
5310 return InteractiveAnnotation;
5313 var TextAnnotation = (function TextAnnotationClosure() {
5314 function TextAnnotation(params) {
5315 InteractiveAnnotation.call(this, params);
5317 var dict = params.dict;
5318 var data = this.data;
5320 var content = dict.get('Contents');
5321 var title = dict.get('T');
5322 data.annotationType = AnnotationType.TEXT;
5323 data.content = stringToPDFString(content || '');
5324 data.title = stringToPDFString(title || '');
5326 if (data.hasAppearance) {
5327 data.name = 'NoIcon';
5329 data.rect[1] = data.rect[3] - DEFAULT_ICON_SIZE;
5330 data.rect[2] = data.rect[0] + DEFAULT_ICON_SIZE;
5331 data.name = dict.has('Name') ? dict.get('Name').name : 'Note';
5334 if (dict.has('C')) {
5335 data.hasBgColor = true;
5339 Util.inherit(TextAnnotation, InteractiveAnnotation, { });
5341 return TextAnnotation;
5344 var LinkAnnotation = (function LinkAnnotationClosure() {
5345 function LinkAnnotation(params) {
5346 InteractiveAnnotation.call(this, params);
5348 var dict = params.dict;
5349 var data = this.data;
5350 data.annotationType = AnnotationType.LINK;
5352 var action = dict.get('A');
5353 if (action && isDict(action)) {
5354 var linkType = action.get('S').name;
5355 if (linkType === 'URI') {
5356 var url = action.get('URI');
5358 // Some bad PDFs do not put parentheses around relative URLs.
5359 url = '/' + url.name;
5361 url = addDefaultProtocolToUrl(url);
5363 // TODO: pdf spec mentions urls can be relative to a Base
5364 // entry in the dictionary.
5365 if (!isValidUrl(url, false)) {
5368 // According to ISO 32000-1:2008, section 12.6.4.7,
5369 // URI should to be encoded in 7-bit ASCII.
5370 // Some bad PDFs may have URIs in UTF-8 encoding, see Bugzilla 1122280.
5372 data.url = stringToUTF8String(url);
5374 // Fall back to a simple copy.
5377 } else if (linkType === 'GoTo') {
5378 data.dest = action.get('D');
5379 } else if (linkType === 'GoToR') {
5380 var urlDict = action.get('F');
5381 if (isDict(urlDict)) {
5382 // We assume that the 'url' is a Filspec dictionary
5383 // and fetch the url without checking any further
5384 url = urlDict.get('F') || '';
5387 // TODO: pdf reference says that GoToR
5388 // can also have 'NewWindow' attribute
5389 if (!isValidUrl(url, false)) {
5393 data.dest = action.get('D');
5394 } else if (linkType === 'Named') {
5395 data.action = action.get('N').name;
5397 warn('unrecognized link type: ' + linkType);
5399 } else if (dict.has('Dest')) {
5400 // simple destination link
5401 var dest = dict.get('Dest');
5402 data.dest = isName(dest) ? dest.name : dest;
5406 // Lets URLs beginning with 'www.' default to using the 'http://' protocol.
5407 function addDefaultProtocolToUrl(url) {
5408 if (url && url.indexOf('www.') === 0) {
5409 return ('http://' + url);
5414 Util.inherit(LinkAnnotation, InteractiveAnnotation, { });
5416 return LinkAnnotation;
5420 var PDFFunction = (function PDFFunctionClosure() {
5421 var CONSTRUCT_SAMPLED = 0;
5422 var CONSTRUCT_INTERPOLATED = 2;
5423 var CONSTRUCT_STICHED = 3;
5424 var CONSTRUCT_POSTSCRIPT = 4;
5427 getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps,
5431 for (i = 0, ii = size.length; i < ii; i++) {
5434 length *= outputSize;
5436 var array = new Array(length);
5439 // 32 is a valid bps so shifting won't work
5440 var sampleMul = 1.0 / (Math.pow(2.0, bps) - 1);
5442 var strBytes = str.getBytes((length * bps + 7) / 8);
5444 for (i = 0; i < length; i++) {
5445 while (codeSize < bps) {
5447 codeBuf |= strBytes[strIdx++];
5451 array[i] = (codeBuf >> codeSize) * sampleMul;
5452 codeBuf &= (1 << codeSize) - 1;
5457 getIR: function PDFFunction_getIR(xref, fn) {
5463 var types = [this.constructSampled,
5465 this.constructInterpolated,
5466 this.constructStiched,
5467 this.constructPostScript];
5469 var typeNum = dict.get('FunctionType');
5470 var typeFn = types[typeNum];
5472 error('Unknown type of function');
5475 return typeFn.call(this, fn, dict, xref);
5478 fromIR: function PDFFunction_fromIR(IR) {
5481 case CONSTRUCT_SAMPLED:
5482 return this.constructSampledFromIR(IR);
5483 case CONSTRUCT_INTERPOLATED:
5484 return this.constructInterpolatedFromIR(IR);
5485 case CONSTRUCT_STICHED:
5486 return this.constructStichedFromIR(IR);
5487 //case CONSTRUCT_POSTSCRIPT:
5489 return this.constructPostScriptFromIR(IR);
5493 parse: function PDFFunction_parse(xref, fn) {
5494 var IR = this.getIR(xref, fn);
5495 return this.fromIR(IR);
5498 parseArray: function PDFFunction_parseArray(xref, fnObj) {
5499 if (!isArray(fnObj)) {
5500 // not an array -- parsing as regular function
5501 return this.parse(xref, fnObj);
5505 for (var j = 0, jj = fnObj.length; j < jj; j++) {
5506 var obj = xref.fetchIfRef(fnObj[j]);
5507 fnArray.push(PDFFunction.parse(xref, obj));
5509 return function (src, srcOffset, dest, destOffset) {
5510 for (var i = 0, ii = fnArray.length; i < ii; i++) {
5511 fnArray[i](src, srcOffset, dest, destOffset + i);
5516 constructSampled: function PDFFunction_constructSampled(str, dict) {
5517 function toMultiArray(arr) {
5518 var inputLength = arr.length;
5521 for (var i = 0; i < inputLength; i += 2) {
5522 out[index] = [arr[i], arr[i + 1]];
5527 var domain = dict.get('Domain');
5528 var range = dict.get('Range');
5530 if (!domain || !range) {
5531 error('No domain or range');
5534 var inputSize = domain.length / 2;
5535 var outputSize = range.length / 2;
5537 domain = toMultiArray(domain);
5538 range = toMultiArray(range);
5540 var size = dict.get('Size');
5541 var bps = dict.get('BitsPerSample');
5542 var order = dict.get('Order') || 1;
5544 // No description how cubic spline interpolation works in PDF32000:2008
5545 // As in poppler, ignoring order, linear interpolation may work as good
5546 info('No support for cubic spline interpolation: ' + order);
5549 var encode = dict.get('Encode');
5552 for (var i = 0; i < inputSize; ++i) {
5554 encode.push(size[i] - 1);
5557 encode = toMultiArray(encode);
5559 var decode = dict.get('Decode');
5563 decode = toMultiArray(decode);
5566 var samples = this.getSampleArray(size, outputSize, bps, str);
5569 CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size,
5570 outputSize, Math.pow(2, bps) - 1, range
5574 constructSampledFromIR: function PDFFunction_constructSampledFromIR(IR) {
5575 // See chapter 3, page 109 of the PDF reference
5576 function interpolate(x, xmin, xmax, ymin, ymax) {
5577 return ymin + ((x - xmin) * ((ymax - ymin) / (xmax - xmin)));
5580 return function constructSampledFromIRResult(src, srcOffset,
5582 // See chapter 3, page 110 of the PDF reference.
5587 var samples = IR[5];
5593 // Building the cube vertices: its part and sample index
5594 // http://rjwagner49.com/Mathematics/Interpolation.pdf
5595 var cubeVertices = 1 << m;
5596 var cubeN = new Float64Array(cubeVertices);
5597 var cubeVertex = new Uint32Array(cubeVertices);
5599 for (j = 0; j < cubeVertices; j++) {
5604 // Map x_i to y_j for 0 <= i < m using the sampled function.
5605 for (i = 0; i < m; ++i) {
5606 // x_i' = min(max(x_i, Domain_2i), Domain_2i+1)
5607 var domain_2i = domain[i][0];
5608 var domain_2i_1 = domain[i][1];
5609 var xi = Math.min(Math.max(src[srcOffset +i], domain_2i),
5612 // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1,
5613 // Encode_2i, Encode_2i+1)
5614 var e = interpolate(xi, domain_2i, domain_2i_1,
5615 encode[i][0], encode[i][1]);
5617 // e_i' = min(max(e_i, 0), Size_i - 1)
5618 var size_i = size[i];
5619 e = Math.min(Math.max(e, 0), size_i - 1);
5621 // Adjusting the cube: N and vertex sample index
5622 var e0 = e < size_i - 1 ? Math.floor(e) : e - 1; // e1 = e0 + 1;
5623 var n0 = e0 + 1 - e; // (e1 - e) / (e1 - e0);
5624 var n1 = e - e0; // (e - e0) / (e1 - e0);
5625 var offset0 = e0 * k;
5626 var offset1 = offset0 + k; // e1 * k
5627 for (j = 0; j < cubeVertices; j++) {
5630 cubeVertex[j] += offset1;
5633 cubeVertex[j] += offset0;
5641 for (j = 0; j < n; ++j) {
5642 // Sum all cube vertices' samples portions
5644 for (i = 0; i < cubeVertices; i++) {
5645 rj += samples[cubeVertex[i] + j] * cubeN[i];
5648 // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1,
5649 // Decode_2j, Decode_2j+1)
5650 rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
5652 // y_j = min(max(r_j, range_2j), range_2j+1)
5653 dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]),
5659 constructInterpolated: function PDFFunction_constructInterpolated(str,
5661 var c0 = dict.get('C0') || [0];
5662 var c1 = dict.get('C1') || [1];
5663 var n = dict.get('N');
5665 if (!isArray(c0) || !isArray(c1)) {
5666 error('Illegal dictionary for interpolated function');
5669 var length = c0.length;
5671 for (var i = 0; i < length; ++i) {
5672 diff.push(c1[i] - c0[i]);
5675 return [CONSTRUCT_INTERPOLATED, c0, diff, n];
5678 constructInterpolatedFromIR:
5679 function PDFFunction_constructInterpolatedFromIR(IR) {
5684 var length = diff.length;
5686 return function constructInterpolatedFromIRResult(src, srcOffset,
5688 var x = n === 1 ? src[srcOffset] : Math.pow(src[srcOffset], n);
5690 for (var j = 0; j < length; ++j) {
5691 dest[destOffset + j] = c0[j] + (x * diff[j]);
5696 constructStiched: function PDFFunction_constructStiched(fn, dict, xref) {
5697 var domain = dict.get('Domain');
5703 var inputSize = domain.length / 2;
5704 if (inputSize !== 1) {
5705 error('Bad domain for stiched function');
5708 var fnRefs = dict.get('Functions');
5710 for (var i = 0, ii = fnRefs.length; i < ii; ++i) {
5711 fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
5714 var bounds = dict.get('Bounds');
5715 var encode = dict.get('Encode');
5717 return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
5720 constructStichedFromIR: function PDFFunction_constructStichedFromIR(IR) {
5726 var tmpBuf = new Float32Array(1);
5728 for (var i = 0, ii = fnsIR.length; i < ii; i++) {
5729 fns.push(PDFFunction.fromIR(fnsIR[i]));
5732 return function constructStichedFromIRResult(src, srcOffset,
5734 var clip = function constructStichedFromIRClip(v, min, max) {
5737 } else if (v < min) {
5744 var v = clip(src[srcOffset], domain[0], domain[1]);
5745 // calulate which bound the value is in
5746 for (var i = 0, ii = bounds.length; i < ii; ++i) {
5747 if (v < bounds[i]) {
5752 // encode value into domain of function
5753 var dmin = domain[0];
5755 dmin = bounds[i - 1];
5757 var dmax = domain[1];
5758 if (i < bounds.length) {
5762 var rmin = encode[2 * i];
5763 var rmax = encode[2 * i + 1];
5765 tmpBuf[0] = rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
5767 // call the appropriate function
5768 fns[i](tmpBuf, 0, dest, destOffset);
5772 constructPostScript: function PDFFunction_constructPostScript(fn, dict,
5774 var domain = dict.get('Domain');
5775 var range = dict.get('Range');
5778 error('No domain.');
5785 var lexer = new PostScriptLexer(fn);
5786 var parser = new PostScriptParser(lexer);
5787 var code = parser.parse();
5789 return [CONSTRUCT_POSTSCRIPT, domain, range, code];
5792 constructPostScriptFromIR: function PDFFunction_constructPostScriptFromIR(
5798 var compiled = (new PostScriptCompiler()).compile(code, domain, range);
5800 // Compiled function consists of simple expressions such as addition,
5801 // subtraction, Math.max, and also contains 'var' and 'return'
5802 // statements. See the generation in the PostScriptCompiler below.
5804 return new Function('src', 'srcOffset', 'dest', 'destOffset', compiled);
5807 info('Unable to compile PS function');
5809 var numOutputs = range.length >> 1;
5810 var numInputs = domain.length >> 1;
5811 var evaluator = new PostScriptEvaluator(code);
5812 // Cache the values for a big speed up, the cache size is limited though
5813 // since the number of possible values can be huge from a PS function.
5815 // The MAX_CACHE_SIZE is set to ~4x the maximum number of distinct values
5816 // seen in our tests.
5817 var MAX_CACHE_SIZE = 2048 * 4;
5818 var cache_available = MAX_CACHE_SIZE;
5819 var tmpBuf = new Float32Array(numInputs);
5821 return function constructPostScriptFromIRResult(src, srcOffset,
5826 for (i = 0; i < numInputs; i++) {
5827 value = src[srcOffset + i];
5832 var cachedValue = cache[key];
5833 if (cachedValue !== undefined) {
5834 dest.set(cachedValue, destOffset);
5838 var output = new Float32Array(numOutputs);
5839 var stack = evaluator.execute(input);
5840 var stackIndex = stack.length - numOutputs;
5841 for (i = 0; i < numOutputs; i++) {
5842 value = stack[stackIndex + i];
5843 var bound = range[i * 2];
5844 if (value < bound) {
5847 bound = range[i * 2 +1];
5848 if (value > bound) {
5854 if (cache_available > 0) {
5856 cache[key] = output;
5858 dest.set(output, destOffset);
5864 function isPDFFunction(v) {
5866 if (typeof v !== 'object') {
5868 } else if (isDict(v)) {
5870 } else if (isStream(v)) {
5875 return fnDict.has('FunctionType');
5878 var PostScriptStack = (function PostScriptStackClosure() {
5879 var MAX_STACK_SIZE = 100;
5880 function PostScriptStack(initialStack) {
5881 this.stack = !initialStack ? [] :
5882 Array.prototype.slice.call(initialStack, 0);
5885 PostScriptStack.prototype = {
5886 push: function PostScriptStack_push(value) {
5887 if (this.stack.length >= MAX_STACK_SIZE) {
5888 error('PostScript function stack overflow.');
5890 this.stack.push(value);
5892 pop: function PostScriptStack_pop() {
5893 if (this.stack.length <= 0) {
5894 error('PostScript function stack underflow.');
5896 return this.stack.pop();
5898 copy: function PostScriptStack_copy(n) {
5899 if (this.stack.length + n >= MAX_STACK_SIZE) {
5900 error('PostScript function stack overflow.');
5902 var stack = this.stack;
5903 for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
5904 stack.push(stack[i]);
5907 index: function PostScriptStack_index(n) {
5908 this.push(this.stack[this.stack.length - n - 1]);
5910 // rotate the last n stack elements p times
5911 roll: function PostScriptStack_roll(n, p) {
5912 var stack = this.stack;
5913 var l = stack.length - n;
5914 var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t;
5915 for (i = l, j = r; i < j; i++, j--) {
5916 t = stack[i]; stack[i] = stack[j]; stack[j] = t;
5918 for (i = l, j = c - 1; i < j; i++, j--) {
5919 t = stack[i]; stack[i] = stack[j]; stack[j] = t;
5921 for (i = c, j = r; i < j; i++, j--) {
5922 t = stack[i]; stack[i] = stack[j]; stack[j] = t;
5926 return PostScriptStack;
5928 var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
5929 function PostScriptEvaluator(operators) {
5930 this.operators = operators;
5932 PostScriptEvaluator.prototype = {
5933 execute: function PostScriptEvaluator_execute(initialStack) {
5934 var stack = new PostScriptStack(initialStack);
5936 var operators = this.operators;
5937 var length = operators.length;
5939 while (counter < length) {
5940 operator = operators[counter++];
5941 if (typeof operator === 'number') {
5942 // Operator is really an operand and should be pushed to the stack.
5943 stack.push(operator);
5947 // non standard ps operators
5948 case 'jz': // jump if false
5960 // all ps operators in alphabetical order (excluding if/ifelse)
5963 stack.push(Math.abs(a));
5973 if (isBool(a) && isBool(b)) {
5981 stack.push(Math.atan(a));
5994 stack.push(Math.ceil(a));
6002 stack.push(Math.cos(a));
6005 a = stack.pop() | 0;
6022 stack.push(a === b);
6030 stack.push(Math.pow(a, b));
6037 stack.push(Math.floor(a));
6052 stack.push((a / b) | 0);
6065 stack.push(Math.log(a));
6069 stack.push(Math.log(a) / Math.LN10);
6089 stack.push(a !== b);
6106 if (isBool(a) && isBool(b)) {
6122 stack.push(Math.round(a));
6126 stack.push(Math.sin(a));
6130 stack.push(Math.sqrt(a));
6142 a = a < 0 ? Math.ceil(a) : Math.floor(a);
6148 if (isBool(a) && isBool(b)) {
6149 stack.push(a !== b);
6155 error('Unknown operator ' + operator);
6162 return PostScriptEvaluator;
6165 // Most of the PDFs functions consist of simple operations such as:
6166 // roll, exch, sub, cvr, pop, index, dup, mul, if, gt, add.
6168 // We can compile most of such programs, and at the same moment, we can
6169 // optimize some expressions using basic math properties. Keeping track of
6170 // min/max values will allow us to avoid extra Math.min/Math.max calls.
6171 var PostScriptCompiler = (function PostScriptCompilerClosure() {
6172 function AstNode(type) {
6175 AstNode.prototype.visit = function (visitor) {
6176 throw new Error('abstract method');
6179 function AstArgument(index, min, max) {
6180 AstNode.call(this, 'args');
6185 AstArgument.prototype = Object.create(AstNode.prototype);
6186 AstArgument.prototype.visit = function (visitor) {
6187 visitor.visitArgument(this);
6190 function AstLiteral(number) {
6191 AstNode.call(this, 'literal');
6192 this.number = number;
6196 AstLiteral.prototype = Object.create(AstNode.prototype);
6197 AstLiteral.prototype.visit = function (visitor) {
6198 visitor.visitLiteral(this);
6201 function AstBinaryOperation(op, arg1, arg2, min, max) {
6202 AstNode.call(this, 'binary');
6209 AstBinaryOperation.prototype = Object.create(AstNode.prototype);
6210 AstBinaryOperation.prototype.visit = function (visitor) {
6211 visitor.visitBinaryOperation(this);
6214 function AstMin(arg, max) {
6215 AstNode.call(this, 'max');
6220 AstMin.prototype = Object.create(AstNode.prototype);
6221 AstMin.prototype.visit = function (visitor) {
6222 visitor.visitMin(this);
6225 function AstVariable(index, min, max) {
6226 AstNode.call(this, 'var');
6231 AstVariable.prototype = Object.create(AstNode.prototype);
6232 AstVariable.prototype.visit = function (visitor) {
6233 visitor.visitVariable(this);
6236 function AstVariableDefinition(variable, arg) {
6237 AstNode.call(this, 'definition');
6238 this.variable = variable;
6241 AstVariableDefinition.prototype = Object.create(AstNode.prototype);
6242 AstVariableDefinition.prototype.visit = function (visitor) {
6243 visitor.visitVariableDefinition(this);
6246 function ExpressionBuilderVisitor() {
6249 ExpressionBuilderVisitor.prototype = {
6250 visitArgument: function (arg) {
6251 this.parts.push('Math.max(', arg.min, ', Math.min(',
6252 arg.max, ', src[srcOffset + ', arg.index, ']))');
6254 visitVariable: function (variable) {
6255 this.parts.push('v', variable.index);
6257 visitLiteral: function (literal) {
6258 this.parts.push(literal.number);
6260 visitBinaryOperation: function (operation) {
6261 this.parts.push('(');
6262 operation.arg1.visit(this);
6263 this.parts.push(' ', operation.op, ' ');
6264 operation.arg2.visit(this);
6265 this.parts.push(')');
6267 visitVariableDefinition: function (definition) {
6268 this.parts.push('var ');
6269 definition.variable.visit(this);
6270 this.parts.push(' = ');
6271 definition.arg.visit(this);
6272 this.parts.push(';');
6274 visitMin: function (max) {
6275 this.parts.push('Math.min(');
6276 max.arg.visit(this);
6277 this.parts.push(', ', max.max, ')');
6279 toString: function () {
6280 return this.parts.join('');
6284 function buildAddOperation(num1, num2) {
6285 if (num2.type === 'literal' && num2.number === 0) {
6286 // optimization: second operand is 0
6289 if (num1.type === 'literal' && num1.number === 0) {
6290 // optimization: first operand is 0
6293 if (num2.type === 'literal' && num1.type === 'literal') {
6294 // optimization: operands operand are literals
6295 return new AstLiteral(num1.number + num2.number);
6297 return new AstBinaryOperation('+', num1, num2,
6298 num1.min + num2.min, num1.max + num2.max);
6301 function buildMulOperation(num1, num2) {
6302 if (num2.type === 'literal') {
6303 // optimization: second operands is a literal...
6304 if (num2.number === 0) {
6305 return new AstLiteral(0); // and it's 0
6306 } else if (num2.number === 1) {
6307 return num1; // and it's 1
6308 } else if (num1.type === 'literal') {
6309 // ... and first operands is a literal too
6310 return new AstLiteral(num1.number * num2.number);
6313 if (num1.type === 'literal') {
6314 // optimization: first operands is a literal...
6315 if (num1.number === 0) {
6316 return new AstLiteral(0); // and it's 0
6317 } else if (num1.number === 1) {
6318 return num2; // and it's 1
6321 var min = Math.min(num1.min * num2.min, num1.min * num2.max,
6322 num1.max * num2.min, num1.max * num2.max);
6323 var max = Math.max(num1.min * num2.min, num1.min * num2.max,
6324 num1.max * num2.min, num1.max * num2.max);
6325 return new AstBinaryOperation('*', num1, num2, min, max);
6328 function buildSubOperation(num1, num2) {
6329 if (num2.type === 'literal') {
6330 // optimization: second operands is a literal...
6331 if (num2.number === 0) {
6332 return num1; // ... and it's 0
6333 } else if (num1.type === 'literal') {
6334 // ... and first operands is a literal too
6335 return new AstLiteral(num1.number - num2.number);
6338 if (num2.type === 'binary' && num2.op === '-' &&
6339 num1.type === 'literal' && num1.number === 1 &&
6340 num2.arg1.type === 'literal' && num2.arg1.number === 1) {
6341 // optimization for case: 1 - (1 - x)
6344 return new AstBinaryOperation('-', num1, num2,
6345 num1.min - num2.max, num1.max - num2.min);
6348 function buildMinOperation(num1, max) {
6349 if (num1.min >= max) {
6350 // optimization: num1 min value is not less than required max
6351 return new AstLiteral(max); // just returning max
6352 } else if (num1.max <= max) {
6353 // optimization: num1 max value is not greater than required max
6354 return num1; // just returning an argument
6356 return new AstMin(num1, max);
6359 function PostScriptCompiler() {}
6360 PostScriptCompiler.prototype = {
6361 compile: function PostScriptCompiler_compile(code, domain, range) {
6364 var instructions = [];
6365 var inputSize = domain.length >> 1, outputSize = range.length >> 1;
6366 var lastRegister = 0;
6368 var num1, num2, ast1, ast2, tmpVar, item;
6369 for (i = 0; i < inputSize; i++) {
6370 stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1]));
6373 for (i = 0, ii = code.length; i < ii; i++) {
6375 if (typeof item === 'number') {
6376 stack.push(new AstLiteral(item));
6382 if (stack.length < 2) {
6387 stack.push(buildAddOperation(num1, num2));
6390 if (stack.length < 1) {
6395 if (stack.length < 2) {
6400 stack.push(buildMulOperation(num1, num2));
6403 if (stack.length < 2) {
6408 stack.push(buildSubOperation(num1, num2));
6411 if (stack.length < 2) {
6414 ast1 = stack.pop(); ast2 = stack.pop();
6415 stack.push(ast1, ast2);
6418 if (stack.length < 1) {
6424 if (stack.length < 1) {
6428 if (num1.type !== 'literal') {
6432 if (n < 0 || (n|0) !== n || stack.length < n) {
6435 ast1 = stack[stack.length - n - 1];
6436 if (ast1.type === 'literal' || ast1.type === 'var') {
6440 tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
6441 stack[stack.length - n - 1] = tmpVar;
6443 instructions.push(new AstVariableDefinition(tmpVar, ast1));
6446 if (stack.length < 1) {
6449 if (typeof code[i + 1] === 'number' && code[i + 2] === 'gt' &&
6450 code[i + 3] === i + 7 && code[i + 4] === 'jz' &&
6451 code[i + 5] === 'pop' && code[i + 6] === code[i + 1]) {
6452 // special case of the commands sequence for the min operation
6454 stack.push(buildMinOperation(num1, code[i + 1]));
6458 ast1 = stack[stack.length - 1];
6459 if (ast1.type === 'literal' || ast1.type === 'var') {
6460 // we don't have to save into intermediate variable a literal or
6465 tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
6466 stack[stack.length - 1] = tmpVar;
6468 instructions.push(new AstVariableDefinition(tmpVar, ast1));
6471 if (stack.length < 2) {
6476 if (num2.type !== 'literal' || num1.type !== 'literal') {
6477 // both roll operands must be numbers
6482 if (n <= 0 || (n|0) !== n || (j|0) !== j || stack.length < n) {
6486 j = ((j % n) + n) % n;
6488 break; // just skipping -- there are nothing to rotate
6490 Array.prototype.push.apply(stack,
6491 stack.splice(stack.length - n, n - j));
6494 return null; // unsupported operator
6498 if (stack.length !== outputSize) {
6503 instructions.forEach(function (instruction) {
6504 var statementBuilder = new ExpressionBuilderVisitor();
6505 instruction.visit(statementBuilder);
6506 result.push(statementBuilder.toString());
6508 stack.forEach(function (expr, i) {
6509 var statementBuilder = new ExpressionBuilderVisitor();
6510 expr.visit(statementBuilder);
6511 var min = range[i * 2], max = range[i * 2 + 1];
6512 var out = [statementBuilder.toString()];
6513 if (min > expr.min) {
6514 out.unshift('Math.max(', min, ', ');
6517 if (max < expr.max) {
6518 out.unshift('Math.min(', max, ', ');
6521 out.unshift('dest[destOffset + ', i, '] = ');
6523 result.push(out.join(''));
6525 return result.join('\n');
6529 return PostScriptCompiler;
6533 var ColorSpace = (function ColorSpaceClosure() {
6534 // Constructor should define this.numComps, this.defaultColor, this.name
6535 function ColorSpace() {
6536 error('should not call ColorSpace constructor');
6539 ColorSpace.prototype = {
6541 * Converts the color value to the RGB color. The color components are
6542 * located in the src array starting from the srcOffset. Returns the array
6543 * of the rgb components, each value ranging from [0,255].
6545 getRgb: function ColorSpace_getRgb(src, srcOffset) {
6546 var rgb = new Uint8Array(3);
6547 this.getRgbItem(src, srcOffset, rgb, 0);
6551 * Converts the color value to the RGB color, similar to the getRgb method.
6552 * The result placed into the dest array starting from the destOffset.
6554 getRgbItem: function ColorSpace_getRgbItem(src, srcOffset,
6556 error('Should not call ColorSpace.getRgbItem');
6559 * Converts the specified number of the color values to the RGB colors.
6560 * The colors are located in the src array starting from the srcOffset.
6561 * The result is placed into the dest array starting from the destOffset.
6562 * The src array items shall be in [0,2^bits) range, the dest array items
6563 * will be in [0,255] range. alpha01 indicates how many alpha components
6564 * there are in the dest array; it will be either 0 (RGB array) or 1 (RGBA
6567 getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count,
6568 dest, destOffset, bits,
6570 error('Should not call ColorSpace.getRgbBuffer');
6573 * Determines the number of bytes required to store the result of the
6574 * conversion done by the getRgbBuffer method. As in getRgbBuffer,
6575 * |alpha01| is either 0 (RGB output) or 1 (RGBA output).
6577 getOutputLength: function ColorSpace_getOutputLength(inputLength,
6579 error('Should not call ColorSpace.getOutputLength');
6582 * Returns true if source data will be equal the result/output data.
6584 isPassthrough: function ColorSpace_isPassthrough(bits) {
6588 * Fills in the RGB colors in the destination buffer. alpha01 indicates
6589 * how many alpha components there are in the dest array; it will be either
6590 * 0 (RGB array) or 1 (RGBA array).
6592 fillRgb: function ColorSpace_fillRgb(dest, originalWidth,
6593 originalHeight, width, height,
6594 actualHeight, bpc, comps, alpha01) {
6595 var count = originalWidth * originalHeight;
6597 var numComponentColors = 1 << bpc;
6598 var needsResizing = originalHeight !== height || originalWidth !== width;
6601 if (this.isPassthrough(bpc)) {
6603 } else if (this.numComps === 1 && count > numComponentColors &&
6604 this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') {
6605 // Optimization: create a color map when there is just one component and
6606 // we are converting more colors than the size of the color map. We
6607 // don't build the map if the colorspace is gray or rgb since those
6608 // methods are faster than building a map. This mainly offers big speed
6609 // ups for indexed and alternate colorspaces.
6611 // TODO it may be worth while to cache the color map. While running
6612 // testing I never hit a cache so I will leave that out for now (perhaps
6613 // we are reparsing colorspaces too much?).
6614 var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) :
6615 new Uint16Array(numComponentColors);
6617 for (i = 0; i < numComponentColors; i++) {
6620 var colorMap = new Uint8Array(numComponentColors * 3);
6621 this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc,
6624 var destPos, rgbPos;
6625 if (!needsResizing) {
6626 // Fill in the RGB values directly into |dest|.
6628 for (i = 0; i < count; ++i) {
6630 dest[destPos++] = colorMap[key];
6631 dest[destPos++] = colorMap[key + 1];
6632 dest[destPos++] = colorMap[key + 2];
6636 rgbBuf = new Uint8Array(count * 3);
6638 for (i = 0; i < count; ++i) {
6640 rgbBuf[rgbPos++] = colorMap[key];
6641 rgbBuf[rgbPos++] = colorMap[key + 1];
6642 rgbBuf[rgbPos++] = colorMap[key + 2];
6646 if (!needsResizing) {
6647 // Fill in the RGB values directly into |dest|.
6648 this.getRgbBuffer(comps, 0, width * actualHeight, dest, 0, bpc,
6651 rgbBuf = new Uint8Array(count * 3);
6652 this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc,
6658 if (needsResizing) {
6659 PDFImage.resize(rgbBuf, bpc, 3, originalWidth, originalHeight, width,
6660 height, dest, alpha01);
6664 for (i = 0, ii = width * actualHeight; i < ii; i++) {
6665 dest[destPos++] = rgbBuf[rgbPos++];
6666 dest[destPos++] = rgbBuf[rgbPos++];
6667 dest[destPos++] = rgbBuf[rgbPos++];
6674 * True if the colorspace has components in the default range of [0, 1].
6675 * This should be true for all colorspaces except for lab color spaces
6676 * which are [0,100], [-128, 127], [-128, 127].
6678 usesZeroToOneRange: true
6681 ColorSpace.parse = function ColorSpace_parse(cs, xref, res) {
6682 var IR = ColorSpace.parseToIR(cs, xref, res);
6683 if (IR instanceof AlternateCS) {
6686 return ColorSpace.fromIR(IR);
6689 ColorSpace.fromIR = function ColorSpace_fromIR(IR) {
6690 var name = isArray(IR) ? IR[0] : IR;
6691 var whitePoint, blackPoint, gamma;
6694 case 'DeviceGrayCS':
6695 return this.singletons.gray;
6697 return this.singletons.rgb;
6698 case 'DeviceCmykCS':
6699 return this.singletons.cmyk;
6701 whitePoint = IR[1].WhitePoint;
6702 blackPoint = IR[1].BlackPoint;
6703 gamma = IR[1].Gamma;
6704 return new CalGrayCS(whitePoint, blackPoint, gamma);
6706 whitePoint = IR[1].WhitePoint;
6707 blackPoint = IR[1].BlackPoint;
6708 gamma = IR[1].Gamma;
6709 var matrix = IR[1].Matrix;
6710 return new CalRGBCS(whitePoint, blackPoint, gamma, matrix);
6712 var basePatternCS = IR[1];
6713 if (basePatternCS) {
6714 basePatternCS = ColorSpace.fromIR(basePatternCS);
6716 return new PatternCS(basePatternCS);
6718 var baseIndexedCS = IR[1];
6721 return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup);
6723 var numComps = IR[1];
6725 var tintFnIR = IR[3];
6727 return new AlternateCS(numComps, ColorSpace.fromIR(alt),
6728 PDFFunction.fromIR(tintFnIR));
6730 whitePoint = IR[1].WhitePoint;
6731 blackPoint = IR[1].BlackPoint;
6732 var range = IR[1].Range;
6733 return new LabCS(whitePoint, blackPoint, range);
6735 error('Unknown name ' + name);
6740 ColorSpace.parseToIR = function ColorSpace_parseToIR(cs, xref, res) {
6742 var colorSpaces = res.get('ColorSpace');
6743 if (isDict(colorSpaces)) {
6744 var refcs = colorSpaces.get(cs.name);
6751 cs = xref.fetchIfRef(cs);
6761 return 'DeviceGrayCS';
6764 return 'DeviceRgbCS';
6767 return 'DeviceCmykCS';
6769 return ['PatternCS', null];
6771 error('unrecognized colorspace ' + mode);
6773 } else if (isArray(cs)) {
6776 var numComps, params;
6781 return 'DeviceGrayCS';
6784 return 'DeviceRgbCS';
6787 return 'DeviceCmykCS';
6789 params = xref.fetchIfRef(cs[1]).getAll();
6790 return ['CalGrayCS', params];
6792 params = xref.fetchIfRef(cs[1]).getAll();
6793 return ['CalRGBCS', params];
6795 var stream = xref.fetchIfRef(cs[1]);
6796 var dict = stream.dict;
6797 numComps = dict.get('N');
6798 if (numComps === 1) {
6799 return 'DeviceGrayCS';
6800 } else if (numComps === 3) {
6801 return 'DeviceRgbCS';
6802 } else if (numComps === 4) {
6803 return 'DeviceCmykCS';
6807 var basePatternCS = cs[1];
6808 if (basePatternCS) {
6809 basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
6811 return ['PatternCS', basePatternCS];
6814 var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
6815 var hiVal = cs[2] + 1;
6816 var lookup = xref.fetchIfRef(cs[3]);
6817 if (isStream(lookup)) {
6818 lookup = lookup.getBytes();
6820 return ['IndexedCS', baseIndexedCS, hiVal, lookup];
6827 } else if (isArray(name)) {
6828 numComps = name.length;
6830 var alt = ColorSpace.parseToIR(cs[2], xref, res);
6831 var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
6832 return ['AlternateCS', numComps, alt, tintFnIR];
6834 params = cs[1].getAll();
6835 return ['LabCS', params];
6837 error('unimplemented color space object "' + mode + '"');
6840 error('unrecognized color space object: "' + cs + '"');
6845 * Checks if a decode map matches the default decode map for a color space.
6846 * This handles the general decode maps where there are two values per
6847 * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color.
6848 * This does not handle Lab, Indexed, or Pattern decode maps since they are
6849 * slightly different.
6850 * @param {Array} decode Decode map (usually from an image).
6851 * @param {Number} n Number of components the color space has.
6853 ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) {
6858 if (n * 2 !== decode.length) {
6859 warn('The decode map is not the correct length');
6862 for (var i = 0, ii = decode.length; i < ii; i += 2) {
6863 if (decode[i] !== 0 || decode[i + 1] !== 1) {
6870 ColorSpace.singletons = {
6872 return shadow(this, 'gray', new DeviceGrayCS());
6875 return shadow(this, 'rgb', new DeviceRgbCS());
6878 return shadow(this, 'cmyk', new DeviceCmykCS());
6886 * Alternate color space handles both Separation and DeviceN color spaces. A
6887 * Separation color space is actually just a DeviceN with one color component.
6888 * Both color spaces use a tinting function to convert colors to a base color
6891 var AlternateCS = (function AlternateCSClosure() {
6892 function AlternateCS(numComps, base, tintFn) {
6893 this.name = 'Alternate';
6894 this.numComps = numComps;
6895 this.defaultColor = new Float32Array(numComps);
6896 for (var i = 0; i < numComps; ++i) {
6897 this.defaultColor[i] = 1;
6900 this.tintFn = tintFn;
6901 this.tmpBuf = new Float32Array(base.numComps);
6904 AlternateCS.prototype = {
6905 getRgb: ColorSpace.prototype.getRgb,
6906 getRgbItem: function AlternateCS_getRgbItem(src, srcOffset,
6908 var tmpBuf = this.tmpBuf;
6909 this.tintFn(src, srcOffset, tmpBuf, 0);
6910 this.base.getRgbItem(tmpBuf, 0, dest, destOffset);
6912 getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count,
6913 dest, destOffset, bits,
6915 var tintFn = this.tintFn;
6916 var base = this.base;
6917 var scale = 1 / ((1 << bits) - 1);
6918 var baseNumComps = base.numComps;
6919 var usesZeroToOneRange = base.usesZeroToOneRange;
6920 var isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) &&
6922 var pos = isPassthrough ? destOffset : 0;
6923 var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count);
6924 var numComps = this.numComps;
6926 var scaled = new Float32Array(numComps);
6927 var tinted = new Float32Array(baseNumComps);
6929 if (usesZeroToOneRange) {
6930 for (i = 0; i < count; i++) {
6931 for (j = 0; j < numComps; j++) {
6932 scaled[j] = src[srcOffset++] * scale;
6934 tintFn(scaled, 0, tinted, 0);
6935 for (j = 0; j < baseNumComps; j++) {
6936 baseBuf[pos++] = tinted[j] * 255;
6940 for (i = 0; i < count; i++) {
6941 for (j = 0; j < numComps; j++) {
6942 scaled[j] = src[srcOffset++] * scale;
6944 tintFn(scaled, 0, tinted, 0);
6945 base.getRgbItem(tinted, 0, baseBuf, pos);
6946 pos += baseNumComps;
6949 if (!isPassthrough) {
6950 base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01);
6953 getOutputLength: function AlternateCS_getOutputLength(inputLength,
6955 return this.base.getOutputLength(inputLength *
6956 this.base.numComps / this.numComps,
6959 isPassthrough: ColorSpace.prototype.isPassthrough,
6960 fillRgb: ColorSpace.prototype.fillRgb,
6961 isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) {
6962 return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
6964 usesZeroToOneRange: true
6970 var PatternCS = (function PatternCSClosure() {
6971 function PatternCS(baseCS) {
6972 this.name = 'Pattern';
6975 PatternCS.prototype = {};
6980 var IndexedCS = (function IndexedCSClosure() {
6981 function IndexedCS(base, highVal, lookup) {
6982 this.name = 'Indexed';
6984 this.defaultColor = new Uint8Array([0]);
6986 this.highVal = highVal;
6988 var baseNumComps = base.numComps;
6989 var length = baseNumComps * highVal;
6992 if (isStream(lookup)) {
6993 lookupArray = new Uint8Array(length);
6994 var bytes = lookup.getBytes(length);
6995 lookupArray.set(bytes);
6996 } else if (isString(lookup)) {
6997 lookupArray = new Uint8Array(length);
6998 for (var i = 0; i < length; ++i) {
6999 lookupArray[i] = lookup.charCodeAt(i);
7001 } else if (lookup instanceof Uint8Array || lookup instanceof Array) {
7002 lookupArray = lookup;
7004 error('Unrecognized lookup table: ' + lookup);
7006 this.lookup = lookupArray;
7009 IndexedCS.prototype = {
7010 getRgb: ColorSpace.prototype.getRgb,
7011 getRgbItem: function IndexedCS_getRgbItem(src, srcOffset,
7013 var numComps = this.base.numComps;
7014 var start = src[srcOffset] * numComps;
7015 this.base.getRgbItem(this.lookup, start, dest, destOffset);
7017 getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count,
7018 dest, destOffset, bits,
7020 var base = this.base;
7021 var numComps = base.numComps;
7022 var outputDelta = base.getOutputLength(numComps, alpha01);
7023 var lookup = this.lookup;
7025 for (var i = 0; i < count; ++i) {
7026 var lookupPos = src[srcOffset++] * numComps;
7027 base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01);
7028 destOffset += outputDelta;
7031 getOutputLength: function IndexedCS_getOutputLength(inputLength, alpha01) {
7032 return this.base.getOutputLength(inputLength * this.base.numComps,
7035 isPassthrough: ColorSpace.prototype.isPassthrough,
7036 fillRgb: ColorSpace.prototype.fillRgb,
7037 isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) {
7038 // indexed color maps shouldn't be changed
7041 usesZeroToOneRange: true
7046 var DeviceGrayCS = (function DeviceGrayCSClosure() {
7047 function DeviceGrayCS() {
7048 this.name = 'DeviceGray';
7050 this.defaultColor = new Float32Array([0]);
7053 DeviceGrayCS.prototype = {
7054 getRgb: ColorSpace.prototype.getRgb,
7055 getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset,
7057 var c = (src[srcOffset] * 255) | 0;
7058 c = c < 0 ? 0 : c > 255 ? 255 : c;
7059 dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;
7061 getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count,
7062 dest, destOffset, bits,
7064 var scale = 255 / ((1 << bits) - 1);
7065 var j = srcOffset, q = destOffset;
7066 for (var i = 0; i < count; ++i) {
7067 var c = (scale * src[j++]) | 0;
7074 getOutputLength: function DeviceGrayCS_getOutputLength(inputLength,
7076 return inputLength * (3 + alpha01);
7078 isPassthrough: ColorSpace.prototype.isPassthrough,
7079 fillRgb: ColorSpace.prototype.fillRgb,
7080 isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) {
7081 return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
7083 usesZeroToOneRange: true
7085 return DeviceGrayCS;
7088 var DeviceRgbCS = (function DeviceRgbCSClosure() {
7089 function DeviceRgbCS() {
7090 this.name = 'DeviceRGB';
7092 this.defaultColor = new Float32Array([0, 0, 0]);
7094 DeviceRgbCS.prototype = {
7095 getRgb: ColorSpace.prototype.getRgb,
7096 getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset,
7098 var r = (src[srcOffset] * 255) | 0;
7099 var g = (src[srcOffset + 1] * 255) | 0;
7100 var b = (src[srcOffset + 2] * 255) | 0;
7101 dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r;
7102 dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g;
7103 dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b;
7105 getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count,
7106 dest, destOffset, bits,
7108 if (bits === 8 && alpha01 === 0) {
7109 dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset);
7112 var scale = 255 / ((1 << bits) - 1);
7113 var j = srcOffset, q = destOffset;
7114 for (var i = 0; i < count; ++i) {
7115 dest[q++] = (scale * src[j++]) | 0;
7116 dest[q++] = (scale * src[j++]) | 0;
7117 dest[q++] = (scale * src[j++]) | 0;
7121 getOutputLength: function DeviceRgbCS_getOutputLength(inputLength,
7123 return (inputLength * (3 + alpha01) / 3) | 0;
7125 isPassthrough: function DeviceRgbCS_isPassthrough(bits) {
7128 fillRgb: ColorSpace.prototype.fillRgb,
7129 isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) {
7130 return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
7132 usesZeroToOneRange: true
7137 var DeviceCmykCS = (function DeviceCmykCSClosure() {
7138 // The coefficients below was found using numerical analysis: the method of
7139 // steepest descent for the sum((f_i - color_value_i)^2) for r/g/b colors,
7140 // where color_value is the tabular value from the table of sampled RGB colors
7141 // from CMYK US Web Coated (SWOP) colorspace, and f_i is the corresponding
7142 // CMYK color conversion using the estimation below:
7143 // f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255
7144 function convertToRgb(src, srcOffset, srcScale, dest, destOffset) {
7145 var c = src[srcOffset + 0] * srcScale;
7146 var m = src[srcOffset + 1] * srcScale;
7147 var y = src[srcOffset + 2] * srcScale;
7148 var k = src[srcOffset + 3] * srcScale;
7151 (c * (-4.387332384609988 * c + 54.48615194189176 * m +
7152 18.82290502165302 * y + 212.25662451639585 * k +
7153 -285.2331026137004) +
7154 m * (1.7149763477362134 * m - 5.6096736904047315 * y +
7155 -17.873870861415444 * k - 5.497006427196366) +
7156 y * (-2.5217340131683033 * y - 21.248923337353073 * k +
7158 k * (-21.86122147463605 * k - 189.48180835922747) + 255) | 0;
7160 (c * (8.841041422036149 * c + 60.118027045597366 * m +
7161 6.871425592049007 * y + 31.159100130055922 * k +
7162 -79.2970844816548) +
7163 m * (-15.310361306967817 * m + 17.575251261109482 * y +
7164 131.35250912493976 * k - 190.9453302588951) +
7165 y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
7166 k * (-20.737325471181034 * k - 187.80453709719578) + 255) | 0;
7168 (c * (0.8842522430003296 * c + 8.078677503112928 * m +
7169 30.89978309703729 * y - 0.23883238689178934 * k +
7170 -14.183576799673286) +
7171 m * (10.49593273432072 * m + 63.02378494754052 * y +
7172 50.606957656360734 * k - 112.23884253719248) +
7173 y * (0.03296041114873217 * y + 115.60384449646641 * k +
7174 -193.58209356861505) +
7175 k * (-22.33816807309886 * k - 180.12613974708367) + 255) | 0;
7177 dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r;
7178 dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
7179 dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
7182 function DeviceCmykCS() {
7183 this.name = 'DeviceCMYK';
7185 this.defaultColor = new Float32Array([0, 0, 0, 1]);
7187 DeviceCmykCS.prototype = {
7188 getRgb: ColorSpace.prototype.getRgb,
7189 getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset,
7191 convertToRgb(src, srcOffset, 1, dest, destOffset);
7193 getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count,
7194 dest, destOffset, bits,
7196 var scale = 1 / ((1 << bits) - 1);
7197 for (var i = 0; i < count; i++) {
7198 convertToRgb(src, srcOffset, scale, dest, destOffset);
7200 destOffset += 3 + alpha01;
7203 getOutputLength: function DeviceCmykCS_getOutputLength(inputLength,
7205 return (inputLength / 4 * (3 + alpha01)) | 0;
7207 isPassthrough: ColorSpace.prototype.isPassthrough,
7208 fillRgb: ColorSpace.prototype.fillRgb,
7209 isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) {
7210 return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
7212 usesZeroToOneRange: true
7215 return DeviceCmykCS;
7219 // CalGrayCS: Based on "PDF Reference, Sixth Ed", p.245
7221 var CalGrayCS = (function CalGrayCSClosure() {
7222 function CalGrayCS(whitePoint, blackPoint, gamma) {
7223 this.name = 'CalGray';
7225 this.defaultColor = new Float32Array([0]);
7228 error('WhitePoint missing - required for color space CalGray');
7230 blackPoint = blackPoint || [0, 0, 0];
7233 // Translate arguments to spec variables.
7234 this.XW = whitePoint[0];
7235 this.YW = whitePoint[1];
7236 this.ZW = whitePoint[2];
7238 this.XB = blackPoint[0];
7239 this.YB = blackPoint[1];
7240 this.ZB = blackPoint[2];
7244 // Validate variables as per spec.
7245 if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {
7246 error('Invalid WhitePoint components for ' + this.name +
7247 ', no fallback available');
7250 if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
7251 info('Invalid BlackPoint for ' + this.name + ', falling back to default');
7252 this.XB = this.YB = this.ZB = 0;
7255 if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) {
7256 warn(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB +
7257 ', ZB: ' + this.ZB + ', only default values are supported.');
7261 info('Invalid Gamma: ' + this.G + ' for ' + this.name +
7262 ', falling back to default');
7267 function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) {
7268 // A represents a gray component of a calibrated gray space.
7269 // A <---> AG in the spec
7270 var A = src[srcOffset] * scale;
7271 var AG = Math.pow(A, cs.G);
7273 // Computes L as per spec. ( = cs.YW * AG )
7274 // Except if other than default BlackPoint values are used.
7276 // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html, Ch 4.
7277 // Convert values to rgb range [0, 255].
7278 var val = Math.max(295.8 * Math.pow(L, 0.333333333333333333) - 40.8, 0) | 0;
7279 dest[destOffset] = val;
7280 dest[destOffset + 1] = val;
7281 dest[destOffset + 2] = val;
7284 CalGrayCS.prototype = {
7285 getRgb: ColorSpace.prototype.getRgb,
7286 getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset,
7288 convertToRgb(this, src, srcOffset, dest, destOffset, 1);
7290 getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count,
7291 dest, destOffset, bits,
7293 var scale = 1 / ((1 << bits) - 1);
7295 for (var i = 0; i < count; ++i) {
7296 convertToRgb(this, src, srcOffset, dest, destOffset, scale);
7298 destOffset += 3 + alpha01;
7301 getOutputLength: function CalGrayCS_getOutputLength(inputLength, alpha01) {
7302 return inputLength * (3 + alpha01);
7304 isPassthrough: ColorSpace.prototype.isPassthrough,
7305 fillRgb: ColorSpace.prototype.fillRgb,
7306 isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) {
7307 return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
7309 usesZeroToOneRange: true
7315 // CalRGBCS: Based on "PDF Reference, Sixth Ed", p.247
7317 var CalRGBCS = (function CalRGBCSClosure() {
7319 // See http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html for these
7321 var BRADFORD_SCALE_MATRIX = new Float32Array([
7322 0.8951, 0.2664, -0.1614,
7323 -0.7502, 1.7135, 0.0367,
7324 0.0389, -0.0685, 1.0296]);
7326 var BRADFORD_SCALE_INVERSE_MATRIX = new Float32Array([
7327 0.9869929, -0.1470543, 0.1599627,
7328 0.4323053, 0.5183603, 0.0492912,
7329 -0.0085287, 0.0400428, 0.9684867]);
7331 // See http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html.
7332 var SRGB_D65_XYZ_TO_RGB_MATRIX = new Float32Array([
7333 3.2404542, -1.5371385, -0.4985314,
7334 -0.9692660, 1.8760108, 0.0415560,
7335 0.0556434, -0.2040259, 1.0572252]);
7337 var FLAT_WHITEPOINT_MATRIX = new Float32Array([1, 1, 1]);
7339 var tempNormalizeMatrix = new Float32Array(3);
7340 var tempConvertMatrix1 = new Float32Array(3);
7341 var tempConvertMatrix2 = new Float32Array(3);
7343 var DECODE_L_CONSTANT = Math.pow(((8 + 16) / 116), 3) / 8.0;
7345 function CalRGBCS(whitePoint, blackPoint, gamma, matrix) {
7346 this.name = 'CalRGB';
7348 this.defaultColor = new Float32Array(3);
7351 error('WhitePoint missing - required for color space CalRGB');
7353 blackPoint = blackPoint || new Float32Array(3);
7354 gamma = gamma || new Float32Array([1, 1, 1]);
7355 matrix = matrix || new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]);
7357 // Translate arguments to spec variables.
7358 var XW = whitePoint[0];
7359 var YW = whitePoint[1];
7360 var ZW = whitePoint[2];
7361 this.whitePoint = whitePoint;
7363 var XB = blackPoint[0];
7364 var YB = blackPoint[1];
7365 var ZB = blackPoint[2];
7366 this.blackPoint = blackPoint;
7372 this.MXA = matrix[0];
7373 this.MYA = matrix[1];
7374 this.MZA = matrix[2];
7375 this.MXB = matrix[3];
7376 this.MYB = matrix[4];
7377 this.MZB = matrix[5];
7378 this.MXC = matrix[6];
7379 this.MYC = matrix[7];
7380 this.MZC = matrix[8];
7382 // Validate variables as per spec.
7383 if (XW < 0 || ZW < 0 || YW !== 1) {
7384 error('Invalid WhitePoint components for ' + this.name +
7385 ', no fallback available');
7388 if (XB < 0 || YB < 0 || ZB < 0) {
7389 info('Invalid BlackPoint for ' + this.name + ' [' + XB + ', ' + YB +
7390 ', ' + ZB + '], falling back to default');
7391 this.blackPoint = new Float32Array(3);
7394 if (this.GR < 0 || this.GG < 0 || this.GB < 0) {
7395 info('Invalid Gamma [' + this.GR + ', ' + this.GG + ', ' + this.GB +
7396 '] for ' + this.name + ', falling back to default');
7397 this.GR = this.GG = this.GB = 1;
7400 if (this.MXA < 0 || this.MYA < 0 || this.MZA < 0 ||
7401 this.MXB < 0 || this.MYB < 0 || this.MZB < 0 ||
7402 this.MXC < 0 || this.MYC < 0 || this.MZC < 0) {
7403 info('Invalid Matrix for ' + this.name + ' [' +
7404 this.MXA + ', ' + this.MYA + ', ' + this.MZA +
7405 this.MXB + ', ' + this.MYB + ', ' + this.MZB +
7406 this.MXC + ', ' + this.MYC + ', ' + this.MZC +
7407 '], falling back to default');
7408 this.MXA = this.MYB = this.MZC = 1;
7409 this.MXB = this.MYA = this.MZA = this.MXC = this.MYC = this.MZB = 0;
7413 function matrixProduct(a, b, result) {
7414 result[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
7415 result[1] = a[3] * b[0] + a[4] * b[1] + a[5] * b[2];
7416 result[2] = a[6] * b[0] + a[7] * b[1] + a[8] * b[2];
7419 function convertToFlat(sourceWhitePoint, LMS, result) {
7420 result[0] = LMS[0] * 1 / sourceWhitePoint[0];
7421 result[1] = LMS[1] * 1 / sourceWhitePoint[1];
7422 result[2] = LMS[2] * 1 / sourceWhitePoint[2];
7425 function convertToD65(sourceWhitePoint, LMS, result) {
7430 result[0] = LMS[0] * D65X / sourceWhitePoint[0];
7431 result[1] = LMS[1] * D65Y / sourceWhitePoint[1];
7432 result[2] = LMS[2] * D65Z / sourceWhitePoint[2];
7435 function sRGBTransferFunction(color) {
7436 // See http://en.wikipedia.org/wiki/SRGB.
7437 if (color <= 0.0031308){
7438 return adjustToRange(0, 1, 12.92 * color);
7441 return adjustToRange(0, 1, (1 + 0.055) * Math.pow(color, 1 / 2.4) - 0.055);
7444 function adjustToRange(min, max, value) {
7445 return Math.max(min, Math.min(max, value));
7448 function decodeL(L) {
7450 return -decodeL(-L);
7454 return Math.pow(((L + 16) / 116), 3);
7457 return L * DECODE_L_CONSTANT;
7460 function compensateBlackPoint(sourceBlackPoint, XYZ_Flat, result) {
7462 // In case the blackPoint is already the default blackPoint then there is
7463 // no need to do compensation.
7464 if (sourceBlackPoint[0] === 0 &&
7465 sourceBlackPoint[1] === 0 &&
7466 sourceBlackPoint[2] === 0) {
7467 result[0] = XYZ_Flat[0];
7468 result[1] = XYZ_Flat[1];
7469 result[2] = XYZ_Flat[2];
7473 // For the blackPoint calculation details, please see
7474 // http://www.adobe.com/content/dam/Adobe/en/devnet/photoshop/sdk/
7476 // The destination blackPoint is the default blackPoint [0, 0, 0].
7477 var zeroDecodeL = decodeL(0);
7479 var X_DST = zeroDecodeL;
7480 var X_SRC = decodeL(sourceBlackPoint[0]);
7482 var Y_DST = zeroDecodeL;
7483 var Y_SRC = decodeL(sourceBlackPoint[1]);
7485 var Z_DST = zeroDecodeL;
7486 var Z_SRC = decodeL(sourceBlackPoint[2]);
7488 var X_Scale = (1 - X_DST) / (1 - X_SRC);
7489 var X_Offset = 1 - X_Scale;
7491 var Y_Scale = (1 - Y_DST) / (1 - Y_SRC);
7492 var Y_Offset = 1 - Y_Scale;
7494 var Z_Scale = (1 - Z_DST) / (1 - Z_SRC);
7495 var Z_Offset = 1 - Z_Scale;
7497 result[0] = XYZ_Flat[0] * X_Scale + X_Offset;
7498 result[1] = XYZ_Flat[1] * Y_Scale + Y_Offset;
7499 result[2] = XYZ_Flat[2] * Z_Scale + Z_Offset;
7502 function normalizeWhitePointToFlat(sourceWhitePoint, XYZ_In, result) {
7504 // In case the whitePoint is already flat then there is no need to do
7506 if (sourceWhitePoint[0] === 1 && sourceWhitePoint[2] === 1) {
7507 result[0] = XYZ_In[0];
7508 result[1] = XYZ_In[1];
7509 result[2] = XYZ_In[2];
7514 matrixProduct(BRADFORD_SCALE_MATRIX, XYZ_In, LMS);
7516 var LMS_Flat = tempNormalizeMatrix;
7517 convertToFlat(sourceWhitePoint, LMS, LMS_Flat);
7519 matrixProduct(BRADFORD_SCALE_INVERSE_MATRIX, LMS_Flat, result);
7522 function normalizeWhitePointToD65(sourceWhitePoint, XYZ_In, result) {
7525 matrixProduct(BRADFORD_SCALE_MATRIX, XYZ_In, LMS);
7527 var LMS_D65 = tempNormalizeMatrix;
7528 convertToD65(sourceWhitePoint, LMS, LMS_D65);
7530 matrixProduct(BRADFORD_SCALE_INVERSE_MATRIX, LMS_D65, result);
7533 function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) {
7534 // A, B and C represent a red, green and blue components of a calibrated
7536 var A = adjustToRange(0, 1, src[srcOffset] * scale);
7537 var B = adjustToRange(0, 1, src[srcOffset + 1] * scale);
7538 var C = adjustToRange(0, 1, src[srcOffset + 2] * scale);
7540 // A <---> AGR in the spec
7541 // B <---> BGG in the spec
7542 // C <---> CGB in the spec
7543 var AGR = Math.pow(A, cs.GR);
7544 var BGG = Math.pow(B, cs.GG);
7545 var CGB = Math.pow(C, cs.GB);
7547 // Computes intermediate variables L, M, N as per spec.
7548 // To decode X, Y, Z values map L, M, N directly to them.
7549 var X = cs.MXA * AGR + cs.MXB * BGG + cs.MXC * CGB;
7550 var Y = cs.MYA * AGR + cs.MYB * BGG + cs.MYC * CGB;
7551 var Z = cs.MZA * AGR + cs.MZB * BGG + cs.MZC * CGB;
7553 // The following calculations are based on this document:
7554 // http://www.adobe.com/content/dam/Adobe/en/devnet/photoshop/sdk/
7556 var XYZ = tempConvertMatrix1;
7560 var XYZ_Flat = tempConvertMatrix2;
7562 normalizeWhitePointToFlat(cs.whitePoint, XYZ, XYZ_Flat);
7564 var XYZ_Black = tempConvertMatrix1;
7565 compensateBlackPoint(cs.blackPoint, XYZ_Flat, XYZ_Black);
7567 var XYZ_D65 = tempConvertMatrix2;
7568 normalizeWhitePointToD65(FLAT_WHITEPOINT_MATRIX, XYZ_Black, XYZ_D65);
7570 var SRGB = tempConvertMatrix1;
7571 matrixProduct(SRGB_D65_XYZ_TO_RGB_MATRIX, XYZ_D65, SRGB);
7573 var sR = sRGBTransferFunction(SRGB[0]);
7574 var sG = sRGBTransferFunction(SRGB[1]);
7575 var sB = sRGBTransferFunction(SRGB[2]);
7577 // Convert the values to rgb range [0, 255].
7578 dest[destOffset] = Math.round(sR * 255);
7579 dest[destOffset + 1] = Math.round(sG * 255);
7580 dest[destOffset + 2] = Math.round(sB * 255);
7583 CalRGBCS.prototype = {
7584 getRgb: function CalRGBCS_getRgb(src, srcOffset) {
7585 var rgb = new Uint8Array(3);
7586 this.getRgbItem(src, srcOffset, rgb, 0);
7589 getRgbItem: function CalRGBCS_getRgbItem(src, srcOffset,
7591 convertToRgb(this, src, srcOffset, dest, destOffset, 1);
7593 getRgbBuffer: function CalRGBCS_getRgbBuffer(src, srcOffset, count,
7594 dest, destOffset, bits,
7596 var scale = 1 / ((1 << bits) - 1);
7598 for (var i = 0; i < count; ++i) {
7599 convertToRgb(this, src, srcOffset, dest, destOffset, scale);
7601 destOffset += 3 + alpha01;
7604 getOutputLength: function CalRGBCS_getOutputLength(inputLength, alpha01) {
7605 return (inputLength * (3 + alpha01) / 3) | 0;
7607 isPassthrough: ColorSpace.prototype.isPassthrough,
7608 fillRgb: ColorSpace.prototype.fillRgb,
7609 isDefaultDecode: function CalRGBCS_isDefaultDecode(decodeMap) {
7610 return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
7612 usesZeroToOneRange: true
7618 // LabCS: Based on "PDF Reference, Sixth Ed", p.250
7620 var LabCS = (function LabCSClosure() {
7621 function LabCS(whitePoint, blackPoint, range) {
7624 this.defaultColor = new Float32Array([0, 0, 0]);
7627 error('WhitePoint missing - required for color space Lab');
7629 blackPoint = blackPoint || [0, 0, 0];
7630 range = range || [-100, 100, -100, 100];
7632 // Translate args to spec variables
7633 this.XW = whitePoint[0];
7634 this.YW = whitePoint[1];
7635 this.ZW = whitePoint[2];
7636 this.amin = range[0];
7637 this.amax = range[1];
7638 this.bmin = range[2];
7639 this.bmax = range[3];
7641 // These are here just for completeness - the spec doesn't offer any
7642 // formulas that use BlackPoint in Lab
7643 this.XB = blackPoint[0];
7644 this.YB = blackPoint[1];
7645 this.ZB = blackPoint[2];
7647 // Validate vars as per spec
7648 if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {
7649 error('Invalid WhitePoint components, no fallback available');
7652 if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
7653 info('Invalid BlackPoint, falling back to default');
7654 this.XB = this.YB = this.ZB = 0;
7657 if (this.amin > this.amax || this.bmin > this.bmax) {
7658 info('Invalid Range, falling back to defaults');
7666 // Function g(x) from spec
7671 return (108 / 841) * (x - 4 / 29);
7675 function decode(value, high1, low2, high2) {
7676 return low2 + (value) * (high2 - low2) / (high1);
7679 // If decoding is needed maxVal should be 2^bits per component - 1.
7680 function convertToRgb(cs, src, srcOffset, maxVal, dest, destOffset) {
7681 // XXX: Lab input is in the range of [0, 100], [amin, amax], [bmin, bmax]
7682 // not the usual [0, 1]. If a command like setFillColor is used the src
7683 // values will already be within the correct range. However, if we are
7684 // converting an image we have to map the values to the correct range given
7686 // Ls,as,bs <---> L*,a*,b* in the spec
7687 var Ls = src[srcOffset];
7688 var as = src[srcOffset + 1];
7689 var bs = src[srcOffset + 2];
7690 if (maxVal !== false) {
7691 Ls = decode(Ls, maxVal, 0, 100);
7692 as = decode(as, maxVal, cs.amin, cs.amax);
7693 bs = decode(bs, maxVal, cs.bmin, cs.bmax);
7696 // Adjust limits of 'as' and 'bs'
7697 as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as;
7698 bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs;
7700 // Computes intermediate variables X,Y,Z as per spec
7701 var M = (Ls + 16) / 116;
7702 var L = M + (as / 500);
7703 var N = M - (bs / 200);
7705 var X = cs.XW * fn_g(L);
7706 var Y = cs.YW * fn_g(M);
7707 var Z = cs.ZW * fn_g(N);
7710 // Using different conversions for D50 and D65 white points,
7711 // per http://www.color.org/srgb.pdf
7713 // Assuming D50 (X=0.9642, Y=1.00, Z=0.8249)
7714 r = X * 3.1339 + Y * -1.6170 + Z * -0.4906;
7715 g = X * -0.9785 + Y * 1.9160 + Z * 0.0333;
7716 b = X * 0.0720 + Y * -0.2290 + Z * 1.4057;
7718 // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888)
7719 r = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
7720 g = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
7721 b = X * 0.0557 + Y * -0.2040 + Z * 1.0570;
7723 // clamp color values to [0,1] range then convert to [0,255] range.
7724 dest[destOffset] = r <= 0 ? 0 : r >= 1 ? 255 : Math.sqrt(r) * 255 | 0;
7725 dest[destOffset + 1] = g <= 0 ? 0 : g >= 1 ? 255 : Math.sqrt(g) * 255 | 0;
7726 dest[destOffset + 2] = b <= 0 ? 0 : b >= 1 ? 255 : Math.sqrt(b) * 255 | 0;
7730 getRgb: ColorSpace.prototype.getRgb,
7731 getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) {
7732 convertToRgb(this, src, srcOffset, false, dest, destOffset);
7734 getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count,
7735 dest, destOffset, bits,
7737 var maxVal = (1 << bits) - 1;
7738 for (var i = 0; i < count; i++) {
7739 convertToRgb(this, src, srcOffset, maxVal, dest, destOffset);
7741 destOffset += 3 + alpha01;
7744 getOutputLength: function LabCS_getOutputLength(inputLength, alpha01) {
7745 return (inputLength * (3 + alpha01) / 3) | 0;
7747 isPassthrough: ColorSpace.prototype.isPassthrough,
7748 fillRgb: ColorSpace.prototype.fillRgb,
7749 isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) {
7750 // XXX: Decoding is handled with the lab conversion because of the strange
7751 // ranges that are used.
7754 usesZeroToOneRange: false
7760 var ARCFourCipher = (function ARCFourCipherClosure() {
7761 function ARCFourCipher(key) {
7764 var s = new Uint8Array(256);
7765 var i, j = 0, tmp, keyLength = key.length;
7766 for (i = 0; i < 256; ++i) {
7769 for (i = 0; i < 256; ++i) {
7771 j = (j + tmp + key[i % keyLength]) & 0xFF;
7778 ARCFourCipher.prototype = {
7779 encryptBlock: function ARCFourCipher_encryptBlock(data) {
7780 var i, n = data.length, tmp, tmp2;
7781 var a = this.a, b = this.b, s = this.s;
7782 var output = new Uint8Array(n);
7783 for (i = 0; i < n; ++i) {
7786 b = (b + tmp) & 0xFF;
7790 output[i] = data[i] ^ s[(tmp + tmp2) & 0xFF];
7797 ARCFourCipher.prototype.decryptBlock = ARCFourCipher.prototype.encryptBlock;
7799 return ARCFourCipher;
7802 var calculateMD5 = (function calculateMD5Closure() {
7803 var r = new Uint8Array([
7804 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
7805 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
7806 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
7807 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]);
7809 var k = new Int32Array([
7810 -680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426,
7811 -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162,
7812 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632,
7813 643717713, -373897302, -701558691, 38016083, -660478335, -405537848,
7814 568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784,
7815 1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556,
7816 -1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222,
7817 -722521979, 76029189, -640364487, -421815835, 530742520, -995338651,
7818 -198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606,
7819 -1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649,
7820 -145523070, -1120210379, 718787259, -343485551]);
7822 function hash(data, offset, length) {
7823 var h0 = 1732584193, h1 = -271733879, h2 = -1732584194, h3 = 271733878;
7825 var paddedLength = (length + 72) & ~63; // data + 9 extra bytes
7826 var padded = new Uint8Array(paddedLength);
7828 for (i = 0; i < length; ++i) {
7829 padded[i] = data[offset++];
7832 n = paddedLength - 8;
7836 padded[i++] = (length << 3) & 0xFF;
7837 padded[i++] = (length >> 5) & 0xFF;
7838 padded[i++] = (length >> 13) & 0xFF;
7839 padded[i++] = (length >> 21) & 0xFF;
7840 padded[i++] = (length >>> 29) & 0xFF;
7844 var w = new Int32Array(16);
7845 for (i = 0; i < paddedLength;) {
7846 for (j = 0; j < 16; ++j, i += 4) {
7847 w[j] = (padded[i] | (padded[i + 1] << 8) |
7848 (padded[i + 2] << 16) | (padded[i + 3] << 24));
7850 var a = h0, b = h1, c = h2, d = h3, f, g;
7851 for (j = 0; j < 64; ++j) {
7853 f = (b & c) | ((~b) & d);
7855 } else if (j < 32) {
7856 f = (d & b) | ((~d) & c);
7857 g = (5 * j + 1) & 15;
7858 } else if (j < 48) {
7860 g = (3 * j + 5) & 15;
7865 var tmp = d, rotateArg = (a + f + k[j] + w[g]) | 0, rotate = r[j];
7868 b = (b + ((rotateArg << rotate) | (rotateArg >>> (32 - rotate)))) | 0;
7876 return new Uint8Array([
7877 h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >>> 24) & 0xFF,
7878 h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >>> 24) & 0xFF,
7879 h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >>> 24) & 0xFF,
7880 h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >>> 24) & 0xFF
7886 var Word64 = (function Word64Closure() {
7887 function Word64(highInteger, lowInteger) {
7888 this.high = highInteger | 0;
7889 this.low = lowInteger | 0;
7891 Word64.prototype = {
7892 and: function Word64_and(word) {
7893 this.high &= word.high;
7894 this.low &= word.low;
7896 xor: function Word64_xor(word) {
7897 this.high ^= word.high;
7898 this.low ^= word.low;
7901 or: function Word64_or(word) {
7902 this.high |= word.high;
7903 this.low |= word.low;
7906 shiftRight: function Word64_shiftRight(places) {
7908 this.low = (this.high >>> (places - 32)) | 0;
7911 this.low = (this.low >>> places) | (this.high << (32 - places));
7912 this.high = (this.high >>> places) | 0;
7916 shiftLeft: function Word64_shiftLeft(places) {
7918 this.high = this.low << (places - 32);
7921 this.high = (this.high << places) | (this.low >>> (32 - places));
7922 this.low = this.low << places;
7926 rotateRight: function Word64_rotateRight(places) {
7936 this.low = (low >>> places) | (high << (32 - places));
7937 this.high = (high >>> places) | (low << (32 - places));
7940 not: function Word64_not() {
7941 this.high = ~this.high;
7942 this.low = ~this.low;
7945 add: function Word64_add(word) {
7946 var lowAdd = (this.low >>> 0) + (word.low >>> 0);
7947 var highAdd = (this.high >>> 0) + (word.high >>> 0);
7948 if (lowAdd > 0xFFFFFFFF) {
7951 this.low = lowAdd | 0;
7952 this.high = highAdd | 0;
7955 copyTo: function Word64_copyTo(bytes, offset) {
7956 bytes[offset] = (this.high >>> 24) & 0xFF;
7957 bytes[offset + 1] = (this.high >> 16) & 0xFF;
7958 bytes[offset + 2] = (this.high >> 8) & 0xFF;
7959 bytes[offset + 3] = this.high & 0xFF;
7960 bytes[offset + 4] = (this.low >>> 24) & 0xFF;
7961 bytes[offset + 5] = (this.low >> 16) & 0xFF;
7962 bytes[offset + 6] = (this.low >> 8) & 0xFF;
7963 bytes[offset + 7] = this.low & 0xFF;
7966 assign: function Word64_assign(word) {
7967 this.high = word.high;
7968 this.low = word.low;
7974 var calculateSHA256 = (function calculateSHA256Closure() {
7975 function rotr(x, n) {
7976 return (x >>> n) | (x << 32 - n);
7979 function ch(x, y, z) {
7980 return (x & y) ^ (~x & z);
7983 function maj(x, y, z) {
7984 return (x & y) ^ (x & z) ^ (y & z);
7988 return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
7991 function sigmaPrime(x) {
7992 return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
7995 function littleSigma(x) {
7996 return rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3;
7999 function littleSigmaPrime(x) {
8000 return rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10;
8003 var k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
8004 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
8005 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
8006 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
8007 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
8008 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
8009 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
8010 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
8011 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
8012 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
8013 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
8014 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
8015 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
8016 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
8017 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
8018 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
8020 function hash(data, offset, length) {
8021 // initial hash values
8022 var h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372,
8023 h3 = 0xa54ff53a, h4 = 0x510e527f, h5 = 0x9b05688c,
8024 h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
8026 var paddedLength = Math.ceil((length + 9) / 64) * 64;
8027 var padded = new Uint8Array(paddedLength);
8029 for (i = 0; i < length; ++i) {
8030 padded[i] = data[offset++];
8033 n = paddedLength - 8;
8040 padded[i++] = (length >>> 29) & 0xFF;
8041 padded[i++] = (length >> 21) & 0xFF;
8042 padded[i++] = (length >> 13) & 0xFF;
8043 padded[i++] = (length >> 5) & 0xFF;
8044 padded[i++] = (length << 3) & 0xFF;
8045 var w = new Uint32Array(64);
8046 // for each 512 bit block
8047 for (i = 0; i < paddedLength;) {
8048 for (j = 0; j < 16; ++j) {
8049 w[j] = (padded[i] << 24 | (padded[i + 1] << 16) |
8050 (padded[i + 2] << 8) | (padded[i + 3]));
8054 for (j = 16; j < 64; ++j) {
8055 w[j] = littleSigmaPrime(w[j - 2]) + w[j - 7] +
8056 littleSigma(w[j - 15]) + w[j - 16] | 0;
8058 var a = h0, b = h1, c = h2, d = h3, e = h4,
8059 f = h5, g = h6, h = h7, t1, t2;
8060 for (j = 0; j < 64; ++j) {
8061 t1 = h + sigmaPrime(e) + ch(e, f, g) + k[j] + w[j];
8062 t2 = sigma(a) + maj(a, b, c);
8081 return new Uint8Array([
8082 (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, (h0) & 0xFF,
8083 (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, (h1) & 0xFF,
8084 (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, (h2) & 0xFF,
8085 (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, (h3) & 0xFF,
8086 (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, (h4) & 0xFF,
8087 (h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, (h5) & 0xFF,
8088 (h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, (h6) & 0xFF,
8089 (h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, (h7) & 0xFF
8096 var calculateSHA512 = (function calculateSHA512Closure() {
8097 function ch(result, x, y, z, tmp) {
8106 function maj(result, x, y, z, tmp) {
8117 function sigma(result, x, tmp) {
8119 result.rotateRight(28);
8121 tmp.rotateRight(34);
8124 tmp.rotateRight(39);
8128 function sigmaPrime(result, x, tmp) {
8130 result.rotateRight(14);
8132 tmp.rotateRight(18);
8135 tmp.rotateRight(41);
8139 function littleSigma(result, x, tmp) {
8141 result.rotateRight(1);
8150 function littleSigmaPrime(result, x, tmp) {
8152 result.rotateRight(19);
8154 tmp.rotateRight(61);
8162 new Word64(0x428a2f98, 0xd728ae22), new Word64(0x71374491, 0x23ef65cd),
8163 new Word64(0xb5c0fbcf, 0xec4d3b2f), new Word64(0xe9b5dba5, 0x8189dbbc),
8164 new Word64(0x3956c25b, 0xf348b538), new Word64(0x59f111f1, 0xb605d019),
8165 new Word64(0x923f82a4, 0xaf194f9b), new Word64(0xab1c5ed5, 0xda6d8118),
8166 new Word64(0xd807aa98, 0xa3030242), new Word64(0x12835b01, 0x45706fbe),
8167 new Word64(0x243185be, 0x4ee4b28c), new Word64(0x550c7dc3, 0xd5ffb4e2),
8168 new Word64(0x72be5d74, 0xf27b896f), new Word64(0x80deb1fe, 0x3b1696b1),
8169 new Word64(0x9bdc06a7, 0x25c71235), new Word64(0xc19bf174, 0xcf692694),
8170 new Word64(0xe49b69c1, 0x9ef14ad2), new Word64(0xefbe4786, 0x384f25e3),
8171 new Word64(0x0fc19dc6, 0x8b8cd5b5), new Word64(0x240ca1cc, 0x77ac9c65),
8172 new Word64(0x2de92c6f, 0x592b0275), new Word64(0x4a7484aa, 0x6ea6e483),
8173 new Word64(0x5cb0a9dc, 0xbd41fbd4), new Word64(0x76f988da, 0x831153b5),
8174 new Word64(0x983e5152, 0xee66dfab), new Word64(0xa831c66d, 0x2db43210),
8175 new Word64(0xb00327c8, 0x98fb213f), new Word64(0xbf597fc7, 0xbeef0ee4),
8176 new Word64(0xc6e00bf3, 0x3da88fc2), new Word64(0xd5a79147, 0x930aa725),
8177 new Word64(0x06ca6351, 0xe003826f), new Word64(0x14292967, 0x0a0e6e70),
8178 new Word64(0x27b70a85, 0x46d22ffc), new Word64(0x2e1b2138, 0x5c26c926),
8179 new Word64(0x4d2c6dfc, 0x5ac42aed), new Word64(0x53380d13, 0x9d95b3df),
8180 new Word64(0x650a7354, 0x8baf63de), new Word64(0x766a0abb, 0x3c77b2a8),
8181 new Word64(0x81c2c92e, 0x47edaee6), new Word64(0x92722c85, 0x1482353b),
8182 new Word64(0xa2bfe8a1, 0x4cf10364), new Word64(0xa81a664b, 0xbc423001),
8183 new Word64(0xc24b8b70, 0xd0f89791), new Word64(0xc76c51a3, 0x0654be30),
8184 new Word64(0xd192e819, 0xd6ef5218), new Word64(0xd6990624, 0x5565a910),
8185 new Word64(0xf40e3585, 0x5771202a), new Word64(0x106aa070, 0x32bbd1b8),
8186 new Word64(0x19a4c116, 0xb8d2d0c8), new Word64(0x1e376c08, 0x5141ab53),
8187 new Word64(0x2748774c, 0xdf8eeb99), new Word64(0x34b0bcb5, 0xe19b48a8),
8188 new Word64(0x391c0cb3, 0xc5c95a63), new Word64(0x4ed8aa4a, 0xe3418acb),
8189 new Word64(0x5b9cca4f, 0x7763e373), new Word64(0x682e6ff3, 0xd6b2b8a3),
8190 new Word64(0x748f82ee, 0x5defb2fc), new Word64(0x78a5636f, 0x43172f60),
8191 new Word64(0x84c87814, 0xa1f0ab72), new Word64(0x8cc70208, 0x1a6439ec),
8192 new Word64(0x90befffa, 0x23631e28), new Word64(0xa4506ceb, 0xde82bde9),
8193 new Word64(0xbef9a3f7, 0xb2c67915), new Word64(0xc67178f2, 0xe372532b),
8194 new Word64(0xca273ece, 0xea26619c), new Word64(0xd186b8c7, 0x21c0c207),
8195 new Word64(0xeada7dd6, 0xcde0eb1e), new Word64(0xf57d4f7f, 0xee6ed178),
8196 new Word64(0x06f067aa, 0x72176fba), new Word64(0x0a637dc5, 0xa2c898a6),
8197 new Word64(0x113f9804, 0xbef90dae), new Word64(0x1b710b35, 0x131c471b),
8198 new Word64(0x28db77f5, 0x23047d84), new Word64(0x32caab7b, 0x40c72493),
8199 new Word64(0x3c9ebe0a, 0x15c9bebc), new Word64(0x431d67c4, 0x9c100d4c),
8200 new Word64(0x4cc5d4be, 0xcb3e42b6), new Word64(0x597f299c, 0xfc657e2a),
8201 new Word64(0x5fcb6fab, 0x3ad6faec), new Word64(0x6c44198c, 0x4a475817)];
8203 function hash(data, offset, length, mode384) {
8204 mode384 = !!mode384;
8205 // initial hash values
8206 var h0, h1, h2, h3, h4, h5, h6, h7;
8208 h0 = new Word64(0x6a09e667, 0xf3bcc908);
8209 h1 = new Word64(0xbb67ae85, 0x84caa73b);
8210 h2 = new Word64(0x3c6ef372, 0xfe94f82b);
8211 h3 = new Word64(0xa54ff53a, 0x5f1d36f1);
8212 h4 = new Word64(0x510e527f, 0xade682d1);
8213 h5 = new Word64(0x9b05688c, 0x2b3e6c1f);
8214 h6 = new Word64(0x1f83d9ab, 0xfb41bd6b);
8215 h7 = new Word64(0x5be0cd19, 0x137e2179);
8218 // SHA384 is exactly the same
8219 // except with different starting values and a trimmed result
8220 h0 = new Word64(0xcbbb9d5d, 0xc1059ed8);
8221 h1 = new Word64(0x629a292a, 0x367cd507);
8222 h2 = new Word64(0x9159015a, 0x3070dd17);
8223 h3 = new Word64(0x152fecd8, 0xf70e5939);
8224 h4 = new Word64(0x67332667, 0xffc00b31);
8225 h5 = new Word64(0x8eb44a87, 0x68581511);
8226 h6 = new Word64(0xdb0c2e0d, 0x64f98fa7);
8227 h7 = new Word64(0x47b5481d, 0xbefa4fa4);
8231 var paddedLength = Math.ceil((length + 17) / 128) * 128;
8232 var padded = new Uint8Array(paddedLength);
8234 for (i = 0; i < length; ++i) {
8235 padded[i] = data[offset++];
8238 n = paddedLength - 16;
8253 padded[i++] = (length >>> 29) & 0xFF;
8254 padded[i++] = (length >> 21) & 0xFF;
8255 padded[i++] = (length >> 13) & 0xFF;
8256 padded[i++] = (length >> 5) & 0xFF;
8257 padded[i++] = (length << 3) & 0xFF;
8259 var w = new Array(80);
8260 for (i = 0; i < 80; i++) {
8261 w[i] = new Word64(0, 0);
8263 var a = new Word64(0, 0), b = new Word64(0, 0), c = new Word64(0, 0);
8264 var d = new Word64(0, 0), e = new Word64(0, 0), f = new Word64(0, 0);
8265 var g = new Word64(0, 0), h = new Word64(0, 0);
8266 var t1 = new Word64(0, 0), t2 = new Word64(0, 0);
8267 var tmp1 = new Word64(0, 0), tmp2 = new Word64(0, 0), tmp3;
8269 // for each 1024 bit block
8270 for (i = 0; i < paddedLength;) {
8271 for (j = 0; j < 16; ++j) {
8272 w[j].high = (padded[i] << 24) | (padded[i + 1] << 16) |
8273 (padded[i + 2] << 8) | (padded[i + 3]);
8274 w[j].low = (padded[i + 4]) << 24 | (padded[i + 5]) << 16 |
8275 (padded[i + 6]) << 8 | (padded[i + 7]);
8278 for (j = 16; j < 80; ++j) {
8280 littleSigmaPrime(tmp3, w[j - 2], tmp2);
8282 littleSigma(tmp1, w[j - 15], tmp2);
8284 tmp3.add(w[j - 16]);
8287 a.assign(h0); b.assign(h1); c.assign(h2); d.assign(h3);
8288 e.assign(h4); f.assign(h5); g.assign(h6); h.assign(h7);
8289 for (j = 0; j < 80; ++j) {
8291 sigmaPrime(tmp1, e, tmp2);
8293 ch(tmp1, e, f, g, tmp2);
8299 maj(tmp1, a, b, c, tmp2);
8327 result = new Uint8Array(64);
8328 h0.copyTo(result,0);
8329 h1.copyTo(result,8);
8330 h2.copyTo(result,16);
8331 h3.copyTo(result,24);
8332 h4.copyTo(result,32);
8333 h5.copyTo(result,40);
8334 h6.copyTo(result,48);
8335 h7.copyTo(result,56);
8338 result = new Uint8Array(48);
8339 h0.copyTo(result,0);
8340 h1.copyTo(result,8);
8341 h2.copyTo(result,16);
8342 h3.copyTo(result,24);
8343 h4.copyTo(result,32);
8344 h5.copyTo(result,40);
8351 var calculateSHA384 = (function calculateSHA384Closure() {
8352 function hash(data, offset, length) {
8353 return calculateSHA512(data, offset, length, true);
8358 var NullCipher = (function NullCipherClosure() {
8359 function NullCipher() {
8362 NullCipher.prototype = {
8363 decryptBlock: function NullCipher_decryptBlock(data) {
8371 var AES128Cipher = (function AES128CipherClosure() {
8372 var rcon = new Uint8Array([
8373 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
8374 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
8375 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
8376 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
8377 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
8378 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6,
8379 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72,
8380 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
8381 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10,
8382 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e,
8383 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,
8384 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
8385 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02,
8386 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,
8387 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,
8388 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
8389 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb,
8390 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
8391 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
8392 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
8393 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
8394 0x74, 0xe8, 0xcb, 0x8d]);
8396 var s = new Uint8Array([
8397 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
8398 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
8399 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
8400 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
8401 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
8402 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
8403 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
8404 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
8405 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
8406 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
8407 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
8408 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
8409 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
8410 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
8411 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
8412 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
8413 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
8414 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
8415 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
8416 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
8417 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
8418 0xb0, 0x54, 0xbb, 0x16]);
8420 var inv_s = new Uint8Array([
8421 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
8422 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
8423 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
8424 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
8425 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
8426 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
8427 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
8428 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
8429 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
8430 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
8431 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
8432 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
8433 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
8434 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
8435 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
8436 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
8437 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
8438 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
8439 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
8440 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
8441 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
8442 0x55, 0x21, 0x0c, 0x7d]);
8443 var mixCol = new Uint8Array(256);
8444 for (var i = 0; i < 256; i++) {
8448 mixCol[i] = (i << 1) ^ 0x1b;
8451 var mix = new Uint32Array([
8452 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927,
8453 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45,
8454 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb,
8455 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381,
8456 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf,
8457 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66,
8458 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28,
8459 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012,
8460 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec,
8461 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e,
8462 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd,
8463 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7,
8464 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89,
8465 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b,
8466 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815,
8467 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f,
8468 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa,
8469 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8,
8470 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36,
8471 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c,
8472 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742,
8473 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea,
8474 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4,
8475 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e,
8476 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360,
8477 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502,
8478 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87,
8479 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd,
8480 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3,
8481 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621,
8482 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f,
8483 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55,
8484 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26,
8485 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844,
8486 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba,
8487 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480,
8488 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce,
8489 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67,
8490 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929,
8491 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713,
8492 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed,
8493 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f,
8494 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]);
8496 function expandKey128(cipherKey) {
8497 var b = 176, result = new Uint8Array(b);
8498 result.set(cipherKey);
8499 for (var j = 16, i = 1; j < b; ++i) {
8501 var t1 = result[j - 3], t2 = result[j - 2],
8502 t3 = result[j - 1], t4 = result[j - 4];
8510 for (var n = 0; n < 4; ++n) {
8511 result[j] = (t1 ^= result[j - 16]);
8513 result[j] = (t2 ^= result[j - 16]);
8515 result[j] = (t3 ^= result[j - 16]);
8517 result[j] = (t4 ^= result[j - 16]);
8524 function decrypt128(input, key) {
8525 var state = new Uint8Array(16);
8530 for (j = 0, k = 160; j < 16; ++j, ++k) {
8533 for (i = 9; i >= 1; --i) {
8536 state[13] = state[9];
8537 state[9] = state[5];
8538 state[5] = state[1];
8542 state[14] = state[6];
8543 state[10] = state[2];
8549 state[15] = state[3];
8554 for (j = 0; j < 16; ++j) {
8555 state[j] = inv_s[state[j]];
8558 for (j = 0, k = i * 16; j < 16; ++j, ++k) {
8562 for (j = 0; j < 16; j += 4) {
8563 var s0 = mix[state[j]], s1 = mix[state[j + 1]],
8564 s2 = mix[state[j + 2]], s3 = mix[state[j + 3]];
8565 t = (s0 ^ (s1 >>> 8) ^ (s1 << 24) ^ (s2 >>> 16) ^ (s2 << 16) ^
8566 (s3 >>> 24) ^ (s3 << 8));
8567 state[j] = (t >>> 24) & 0xFF;
8568 state[j + 1] = (t >> 16) & 0xFF;
8569 state[j + 2] = (t >> 8) & 0xFF;
8570 state[j + 3] = t & 0xFF;
8575 state[13] = state[9];
8576 state[9] = state[5];
8577 state[5] = state[1];
8581 state[14] = state[6];
8582 state[10] = state[2];
8588 state[15] = state[3];
8592 for (j = 0; j < 16; ++j) {
8594 state[j] = inv_s[state[j]];
8601 function encrypt128(input, key) {
8603 var state = new Uint8Array(16);
8605 for (j = 0; j < 16; ++j) {
8610 for (i = 1; i < 10; i++) {
8612 for (j = 0; j < 16; ++j) {
8613 state[j] = s[state[j]];
8617 state[1] = state[5];
8618 state[5] = state[9];
8619 state[9] = state[13];
8623 state[2] = state[10];
8624 state[6] = state[14];
8630 state[3] = state[15];
8635 for (var j = 0; j < 16; j += 4) {
8636 var s0 = state[j + 0], s1 = state[j + 1];
8637 var s2 = state[j + 2], s3 = state[j + 3];
8638 t = s0 ^ s1 ^ s2 ^ s3;
8639 state[j + 0] ^= t ^ mixCol[s0 ^ s1];
8640 state[j + 1] ^= t ^ mixCol[s1 ^ s2];
8641 state[j + 2] ^= t ^ mixCol[s2 ^ s3];
8642 state[j + 3] ^= t ^ mixCol[s3 ^ s0];
8645 for (j = 0, k = i * 16; j < 16; ++j, ++k) {
8651 for (j = 0; j < 16; ++j) {
8652 state[j] = s[state[j]];
8656 state[1] = state[5];
8657 state[5] = state[9];
8658 state[9] = state[13];
8662 state[2] = state[10];
8663 state[6] = state[14];
8669 state[3] = state[15];
8674 for (j = 0, k = 160; j < 16; ++j, ++k) {
8680 function AES128Cipher(key) {
8681 this.key = expandKey128(key);
8682 this.buffer = new Uint8Array(16);
8683 this.bufferPosition = 0;
8686 function decryptBlock2(data, finalize) {
8687 var i, j, ii, sourceLength = data.length,
8688 buffer = this.buffer, bufferLength = this.bufferPosition,
8689 result = [], iv = this.iv;
8690 for (i = 0; i < sourceLength; ++i) {
8691 buffer[bufferLength] = data[i];
8693 if (bufferLength < 16) {
8696 // buffer is full, decrypting
8697 var plain = decrypt128(buffer, this.key);
8698 // xor-ing the IV vector to get plain text
8699 for (j = 0; j < 16; ++j) {
8704 buffer = new Uint8Array(16);
8707 // saving incomplete buffer
8708 this.buffer = buffer;
8709 this.bufferLength = bufferLength;
8711 if (result.length === 0) {
8712 return new Uint8Array([]);
8714 // combining plain text blocks into one
8715 var outputLength = 16 * result.length;
8717 // undo a padding that is described in RFC 2898
8718 var lastBlock = result[result.length - 1];
8719 var psLen = lastBlock[15];
8721 for (i = 15, ii = 16 - psLen; i >= ii; --i) {
8722 if (lastBlock[i] !== psLen) {
8723 // Invalid padding, assume that the block has no padding.
8728 outputLength -= psLen;
8729 result[result.length - 1] = lastBlock.subarray(0, 16 - psLen);
8732 var output = new Uint8Array(outputLength);
8733 for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
8734 output.set(result[i], j);
8739 AES128Cipher.prototype = {
8740 decryptBlock: function AES128Cipher_decryptBlock(data, finalize) {
8741 var i, sourceLength = data.length;
8742 var buffer = this.buffer, bufferLength = this.bufferPosition;
8743 // waiting for IV values -- they are at the start of the stream
8744 for (i = 0; bufferLength < 16 && i < sourceLength; ++i, ++bufferLength) {
8745 buffer[bufferLength] = data[i];
8747 if (bufferLength < 16) {
8749 this.bufferLength = bufferLength;
8750 return new Uint8Array([]);
8753 this.buffer = new Uint8Array(16);
8754 this.bufferLength = 0;
8755 // starting decryption
8756 this.decryptBlock = decryptBlock2;
8757 return this.decryptBlock(data.subarray(16), finalize);
8759 encrypt: function AES128Cipher_encrypt(data, iv) {
8760 var i, j, ii, sourceLength = data.length,
8761 buffer = this.buffer, bufferLength = this.bufferPosition,
8764 iv = new Uint8Array(16);
8766 for (i = 0; i < sourceLength; ++i) {
8767 buffer[bufferLength] = data[i];
8769 if (bufferLength < 16) {
8772 for (j = 0; j < 16; ++j) {
8776 // buffer is full, encrypting
8777 var cipher = encrypt128(buffer, this.key);
8779 result.push(cipher);
8780 buffer = new Uint8Array(16);
8783 // saving incomplete buffer
8784 this.buffer = buffer;
8785 this.bufferLength = bufferLength;
8787 if (result.length === 0) {
8788 return new Uint8Array([]);
8790 // combining plain text blocks into one
8791 var outputLength = 16 * result.length;
8792 var output = new Uint8Array(outputLength);
8793 for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
8794 output.set(result[i], j);
8800 return AES128Cipher;
8803 var AES256Cipher = (function AES256CipherClosure() {
8804 var rcon = new Uint8Array([
8805 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
8806 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
8807 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
8808 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
8809 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
8810 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6,
8811 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72,
8812 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
8813 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10,
8814 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e,
8815 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,
8816 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
8817 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02,
8818 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,
8819 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,
8820 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
8821 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb,
8822 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
8823 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
8824 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
8825 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
8826 0x74, 0xe8, 0xcb, 0x8d]);
8828 var s = new Uint8Array([
8829 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
8830 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
8831 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
8832 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
8833 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
8834 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
8835 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
8836 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
8837 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
8838 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
8839 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
8840 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
8841 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
8842 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
8843 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
8844 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
8845 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
8846 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
8847 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
8848 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
8849 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
8850 0xb0, 0x54, 0xbb, 0x16]);
8852 var inv_s = new Uint8Array([
8853 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
8854 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
8855 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
8856 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
8857 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
8858 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
8859 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
8860 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
8861 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
8862 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
8863 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
8864 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
8865 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
8866 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
8867 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
8868 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
8869 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
8870 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
8871 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
8872 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
8873 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
8874 0x55, 0x21, 0x0c, 0x7d]);
8876 var mixCol = new Uint8Array(256);
8877 for (var i = 0; i < 256; i++) {
8881 mixCol[i] = (i << 1) ^ 0x1b;
8884 var mix = new Uint32Array([
8885 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927,
8886 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45,
8887 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb,
8888 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381,
8889 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf,
8890 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66,
8891 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28,
8892 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012,
8893 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec,
8894 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e,
8895 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd,
8896 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7,
8897 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89,
8898 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b,
8899 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815,
8900 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f,
8901 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa,
8902 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8,
8903 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36,
8904 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c,
8905 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742,
8906 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea,
8907 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4,
8908 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e,
8909 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360,
8910 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502,
8911 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87,
8912 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd,
8913 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3,
8914 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621,
8915 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f,
8916 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55,
8917 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26,
8918 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844,
8919 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba,
8920 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480,
8921 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce,
8922 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67,
8923 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929,
8924 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713,
8925 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed,
8926 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f,
8927 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]);
8929 function expandKey256(cipherKey) {
8930 var b = 240, result = new Uint8Array(b);
8933 result.set(cipherKey);
8934 for (var j = 32, i = 1; j < b; ++i) {
8935 if (j % 32 === 16) {
8940 } else if (j % 32 === 0) {
8942 var t1 = result[j - 3], t2 = result[j - 2],
8943 t3 = result[j - 1], t4 = result[j - 4];
8951 if ((r <<= 1) >= 256) {
8952 r = (r ^ 0x1b) & 0xFF;
8956 for (var n = 0; n < 4; ++n) {
8957 result[j] = (t1 ^= result[j - 32]);
8959 result[j] = (t2 ^= result[j - 32]);
8961 result[j] = (t3 ^= result[j - 32]);
8963 result[j] = (t4 ^= result[j - 32]);
8970 function decrypt256(input, key) {
8971 var state = new Uint8Array(16);
8976 for (j = 0, k = 224; j < 16; ++j, ++k) {
8979 for (i = 13; i >= 1; --i) {
8982 state[13] = state[9];
8983 state[9] = state[5];
8984 state[5] = state[1];
8988 state[14] = state[6];
8989 state[10] = state[2];
8995 state[15] = state[3];
9000 for (j = 0; j < 16; ++j) {
9001 state[j] = inv_s[state[j]];
9004 for (j = 0, k = i * 16; j < 16; ++j, ++k) {
9008 for (j = 0; j < 16; j += 4) {
9009 var s0 = mix[state[j]], s1 = mix[state[j + 1]],
9010 s2 = mix[state[j + 2]], s3 = mix[state[j + 3]];
9011 t = (s0 ^ (s1 >>> 8) ^ (s1 << 24) ^ (s2 >>> 16) ^ (s2 << 16) ^
9012 (s3 >>> 24) ^ (s3 << 8));
9013 state[j] = (t >>> 24) & 0xFF;
9014 state[j + 1] = (t >> 16) & 0xFF;
9015 state[j + 2] = (t >> 8) & 0xFF;
9016 state[j + 3] = t & 0xFF;
9021 state[13] = state[9];
9022 state[9] = state[5];
9023 state[5] = state[1];
9027 state[14] = state[6];
9028 state[10] = state[2];
9034 state[15] = state[3];
9038 for (j = 0; j < 16; ++j) {
9040 state[j] = inv_s[state[j]];
9047 function encrypt256(input, key) {
9049 var state = new Uint8Array(16);
9051 for (j = 0; j < 16; ++j) {
9056 for (i = 1; i < 14; i++) {
9058 for (j = 0; j < 16; ++j) {
9059 state[j] = s[state[j]];
9063 state[1] = state[5];
9064 state[5] = state[9];
9065 state[9] = state[13];
9069 state[2] = state[10];
9070 state[6] = state[14];
9076 state[3] = state[15];
9081 for (var j = 0; j < 16; j += 4) {
9082 var s0 = state[j + 0], s1 = state[j + 1];
9083 var s2 = state[j + 2], s3 = state[j + 3];
9084 t = s0 ^ s1 ^ s2 ^ s3;
9085 state[j + 0] ^= t ^ mixCol[s0 ^ s1];
9086 state[j + 1] ^= t ^ mixCol[s1 ^ s2];
9087 state[j + 2] ^= t ^ mixCol[s2 ^ s3];
9088 state[j + 3] ^= t ^ mixCol[s3 ^ s0];
9091 for (j = 0, k = i * 16; j < 16; ++j, ++k) {
9097 for (j = 0; j < 16; ++j) {
9098 state[j] = s[state[j]];
9102 state[1] = state[5];
9103 state[5] = state[9];
9104 state[9] = state[13];
9108 state[2] = state[10];
9109 state[6] = state[14];
9115 state[3] = state[15];
9120 for (j = 0, k = 224; j < 16; ++j, ++k) {
9128 function AES256Cipher(key) {
9129 this.key = expandKey256(key);
9130 this.buffer = new Uint8Array(16);
9131 this.bufferPosition = 0;
9134 function decryptBlock2(data, finalize) {
9135 var i, j, ii, sourceLength = data.length,
9136 buffer = this.buffer, bufferLength = this.bufferPosition,
9137 result = [], iv = this.iv;
9139 for (i = 0; i < sourceLength; ++i) {
9140 buffer[bufferLength] = data[i];
9142 if (bufferLength < 16) {
9145 // buffer is full, decrypting
9146 var plain = decrypt256(buffer, this.key);
9147 // xor-ing the IV vector to get plain text
9148 for (j = 0; j < 16; ++j) {
9153 buffer = new Uint8Array(16);
9156 // saving incomplete buffer
9157 this.buffer = buffer;
9158 this.bufferLength = bufferLength;
9160 if (result.length === 0) {
9161 return new Uint8Array([]);
9163 // combining plain text blocks into one
9164 var outputLength = 16 * result.length;
9166 // undo a padding that is described in RFC 2898
9167 var lastBlock = result[result.length - 1];
9168 var psLen = lastBlock[15];
9170 for (i = 15, ii = 16 - psLen; i >= ii; --i) {
9171 if (lastBlock[i] !== psLen) {
9172 // Invalid padding, assume that the block has no padding.
9177 outputLength -= psLen;
9178 result[result.length - 1] = lastBlock.subarray(0, 16 - psLen);
9181 var output = new Uint8Array(outputLength);
9182 for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
9183 output.set(result[i], j);
9189 AES256Cipher.prototype = {
9190 decryptBlock: function AES256Cipher_decryptBlock(data, finalize, iv) {
9191 var i, sourceLength = data.length;
9192 var buffer = this.buffer, bufferLength = this.bufferPosition;
9193 // if not supplied an IV wait for IV values
9194 // they are at the start of the stream
9198 for (i = 0; bufferLength < 16 &&
9199 i < sourceLength; ++i, ++bufferLength) {
9200 buffer[bufferLength] = data[i];
9202 if (bufferLength < 16) {
9204 this.bufferLength = bufferLength;
9205 return new Uint8Array([]);
9208 data = data.subarray(16);
9210 this.buffer = new Uint8Array(16);
9211 this.bufferLength = 0;
9212 // starting decryption
9213 this.decryptBlock = decryptBlock2;
9214 return this.decryptBlock(data, finalize);
9216 encrypt: function AES256Cipher_encrypt(data, iv) {
9217 var i, j, ii, sourceLength = data.length,
9218 buffer = this.buffer, bufferLength = this.bufferPosition,
9221 iv = new Uint8Array(16);
9223 for (i = 0; i < sourceLength; ++i) {
9224 buffer[bufferLength] = data[i];
9226 if (bufferLength < 16) {
9229 for (j = 0; j < 16; ++j) {
9233 // buffer is full, encrypting
9234 var cipher = encrypt256(buffer, this.key);
9236 result.push(cipher);
9237 buffer = new Uint8Array(16);
9240 // saving incomplete buffer
9241 this.buffer = buffer;
9242 this.bufferLength = bufferLength;
9244 if (result.length === 0) {
9245 return new Uint8Array([]);
9247 // combining plain text blocks into one
9248 var outputLength = 16 * result.length;
9249 var output = new Uint8Array(outputLength);
9250 for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
9251 output.set(result[i], j);
9257 return AES256Cipher;
9260 var PDF17 = (function PDF17Closure() {
9262 function compareByteArrays(array1, array2) {
9263 if (array1.length !== array2.length) {
9266 for (var i = 0; i < array1.length; i++) {
9267 if (array1[i] !== array2[i]) {
9278 checkOwnerPassword: function PDF17_checkOwnerPassword(password,
9279 ownerValidationSalt,
9282 var hashData = new Uint8Array(password.length + 56);
9283 hashData.set(password, 0);
9284 hashData.set(ownerValidationSalt, password.length);
9285 hashData.set(userBytes, password.length + ownerValidationSalt.length);
9286 var result = calculateSHA256(hashData, 0, hashData.length);
9287 return compareByteArrays(result, ownerPassword);
9289 checkUserPassword: function PDF17_checkUserPassword(password,
9292 var hashData = new Uint8Array(password.length + 8);
9293 hashData.set(password, 0);
9294 hashData.set(userValidationSalt, password.length);
9295 var result = calculateSHA256(hashData, 0, hashData.length);
9296 return compareByteArrays(result, userPassword);
9298 getOwnerKey: function PDF17_getOwnerKey(password, ownerKeySalt, userBytes,
9300 var hashData = new Uint8Array(password.length + 56);
9301 hashData.set(password, 0);
9302 hashData.set(ownerKeySalt, password.length);
9303 hashData.set(userBytes, password.length + ownerKeySalt.length);
9304 var key = calculateSHA256(hashData, 0, hashData.length);
9305 var cipher = new AES256Cipher(key);
9306 return cipher.decryptBlock(ownerEncryption,
9308 new Uint8Array(16));
9311 getUserKey: function PDF17_getUserKey(password, userKeySalt,
9313 var hashData = new Uint8Array(password.length + 8);
9314 hashData.set(password, 0);
9315 hashData.set(userKeySalt, password.length);
9316 //key is the decryption key for the UE string
9317 var key = calculateSHA256(hashData, 0, hashData.length);
9318 var cipher = new AES256Cipher(key);
9319 return cipher.decryptBlock(userEncryption,
9321 new Uint8Array(16));
9327 var PDF20 = (function PDF20Closure() {
9329 function concatArrays(array1, array2) {
9330 var t = new Uint8Array(array1.length + array2.length);
9332 t.set(array2, array1.length);
9336 function calculatePDF20Hash(password, input, userBytes) {
9337 //This refers to Algorithm 2.B as defined in ISO 32000-2
9338 var k = calculateSHA256(input, 0, input.length).subarray(0, 32);
9341 while (i < 64 || e[e.length - 1] > i - 32) {
9342 var arrayLength = password.length + k.length + userBytes.length;
9344 var k1 = new Uint8Array(arrayLength * 64);
9345 var array = concatArrays(password, k);
9346 array = concatArrays(array, userBytes);
9347 for (var j = 0, pos = 0; j < 64; j++, pos += arrayLength) {
9350 //AES128 CBC NO PADDING with
9351 //first 16 bytes of k as the key and the second 16 as the iv.
9352 var cipher = new AES128Cipher(k.subarray(0, 16));
9353 e = cipher.encrypt(k1, k.subarray(16, 32));
9354 //Now we have to take the first 16 bytes of an unsigned
9355 //big endian integer... and compute the remainder
9356 //modulo 3.... That is a fairly large number and
9357 //JavaScript isn't going to handle that well...
9358 //So we're using a trick that allows us to perform
9359 //modulo math byte by byte
9361 for (var z = 0; z < 16; z++) {
9362 remainder *= (256 % 3);
9364 remainder += ((e[z] >>> 0) % 3);
9367 if (remainder === 0) {
9368 k = calculateSHA256(e, 0, e.length);
9370 else if (remainder === 1) {
9371 k = calculateSHA384(e, 0, e.length);
9373 else if (remainder === 2) {
9374 k = calculateSHA512(e, 0, e.length);
9378 return k.subarray(0, 32);
9384 function compareByteArrays(array1, array2) {
9385 if (array1.length !== array2.length) {
9388 for (var i = 0; i < array1.length; i++) {
9389 if (array1[i] !== array2[i]) {
9397 hash: function PDF20_hash(password, concatBytes, userBytes) {
9398 return calculatePDF20Hash(password, concatBytes, userBytes);
9400 checkOwnerPassword: function PDF20_checkOwnerPassword(password,
9401 ownerValidationSalt,
9404 var hashData = new Uint8Array(password.length + 56);
9405 hashData.set(password, 0);
9406 hashData.set(ownerValidationSalt, password.length);
9407 hashData.set(userBytes, password.length + ownerValidationSalt.length);
9408 var result = calculatePDF20Hash(password, hashData, userBytes);
9409 return compareByteArrays(result, ownerPassword);
9411 checkUserPassword: function PDF20_checkUserPassword(password,
9414 var hashData = new Uint8Array(password.length + 8);
9415 hashData.set(password, 0);
9416 hashData.set(userValidationSalt, password.length);
9417 var result = calculatePDF20Hash(password, hashData, []);
9418 return compareByteArrays(result, userPassword);
9420 getOwnerKey: function PDF20_getOwnerKey(password, ownerKeySalt, userBytes,
9422 var hashData = new Uint8Array(password.length + 56);
9423 hashData.set(password, 0);
9424 hashData.set(ownerKeySalt, password.length);
9425 hashData.set(userBytes, password.length + ownerKeySalt.length);
9426 var key = calculatePDF20Hash(password, hashData, userBytes);
9427 var cipher = new AES256Cipher(key);
9428 return cipher.decryptBlock(ownerEncryption,
9430 new Uint8Array(16));
9433 getUserKey: function PDF20_getUserKey(password, userKeySalt,
9435 var hashData = new Uint8Array(password.length + 8);
9436 hashData.set(password, 0);
9437 hashData.set(userKeySalt, password.length);
9438 //key is the decryption key for the UE string
9439 var key = calculatePDF20Hash(password, hashData, []);
9440 var cipher = new AES256Cipher(key);
9441 return cipher.decryptBlock(userEncryption,
9443 new Uint8Array(16));
9449 var CipherTransform = (function CipherTransformClosure() {
9450 function CipherTransform(stringCipherConstructor, streamCipherConstructor) {
9451 this.stringCipherConstructor = stringCipherConstructor;
9452 this.streamCipherConstructor = streamCipherConstructor;
9455 CipherTransform.prototype = {
9456 createStream: function CipherTransform_createStream(stream, length) {
9457 var cipher = new this.streamCipherConstructor();
9458 return new DecryptStream(stream, length,
9459 function cipherTransformDecryptStream(data, finalize) {
9460 return cipher.decryptBlock(data, finalize);
9464 decryptString: function CipherTransform_decryptString(s) {
9465 var cipher = new this.stringCipherConstructor();
9466 var data = stringToBytes(s);
9467 data = cipher.decryptBlock(data, true);
9468 return bytesToString(data);
9471 return CipherTransform;
9474 var CipherTransformFactory = (function CipherTransformFactoryClosure() {
9475 var defaultPasswordBytes = new Uint8Array([
9476 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
9477 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
9478 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
9479 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
9481 function createEncryptionKey20(revision, password, ownerPassword,
9482 ownerValidationSalt, ownerKeySalt, uBytes,
9483 userPassword, userValidationSalt, userKeySalt,
9484 ownerEncryption, userEncryption, perms) {
9486 var passwordLength = Math.min(127, password.length);
9487 password = password.subarray(0, passwordLength);
9492 if (revision === 6) {
9493 pdfAlgorithm = new PDF20();
9495 pdfAlgorithm = new PDF17();
9499 if (pdfAlgorithm.checkUserPassword(password, userValidationSalt,
9501 return pdfAlgorithm.getUserKey(password, userKeySalt, userEncryption);
9502 } else if (pdfAlgorithm.checkOwnerPassword(password, ownerValidationSalt,
9505 return pdfAlgorithm.getOwnerKey(password, ownerKeySalt, uBytes,
9513 function prepareKeyData(fileId, password, ownerPassword, userPassword,
9514 flags, revision, keyLength, encryptMetadata) {
9515 var hashDataSize = 40 + ownerPassword.length + fileId.length;
9516 var hashData = new Uint8Array(hashDataSize), i = 0, j, n;
9518 n = Math.min(32, password.length);
9519 for (; i < n; ++i) {
9520 hashData[i] = password[i];
9525 hashData[i++] = defaultPasswordBytes[j++];
9527 // as now the padded password in the hashData[0..i]
9528 for (j = 0, n = ownerPassword.length; j < n; ++j) {
9529 hashData[i++] = ownerPassword[j];
9531 hashData[i++] = flags & 0xFF;
9532 hashData[i++] = (flags >> 8) & 0xFF;
9533 hashData[i++] = (flags >> 16) & 0xFF;
9534 hashData[i++] = (flags >>> 24) & 0xFF;
9535 for (j = 0, n = fileId.length; j < n; ++j) {
9536 hashData[i++] = fileId[j];
9538 if (revision >= 4 && !encryptMetadata) {
9539 hashData[i++] = 0xFF;
9540 hashData[i++] = 0xFF;
9541 hashData[i++] = 0xFF;
9542 hashData[i++] = 0xFF;
9544 var hash = calculateMD5(hashData, 0, i);
9545 var keyLengthInBytes = keyLength >> 3;
9546 if (revision >= 3) {
9547 for (j = 0; j < 50; ++j) {
9548 hash = calculateMD5(hash, 0, keyLengthInBytes);
9551 var encryptionKey = hash.subarray(0, keyLengthInBytes);
9552 var cipher, checkData;
9554 if (revision >= 3) {
9555 for (i = 0; i < 32; ++i) {
9556 hashData[i] = defaultPasswordBytes[i];
9558 for (j = 0, n = fileId.length; j < n; ++j) {
9559 hashData[i++] = fileId[j];
9561 cipher = new ARCFourCipher(encryptionKey);
9562 checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i));
9563 n = encryptionKey.length;
9564 var derivedKey = new Uint8Array(n), k;
9565 for (j = 1; j <= 19; ++j) {
9566 for (k = 0; k < n; ++k) {
9567 derivedKey[k] = encryptionKey[k] ^ j;
9569 cipher = new ARCFourCipher(derivedKey);
9570 checkData = cipher.encryptBlock(checkData);
9572 for (j = 0, n = checkData.length; j < n; ++j) {
9573 if (userPassword[j] !== checkData[j]) {
9578 cipher = new ARCFourCipher(encryptionKey);
9579 checkData = cipher.encryptBlock(defaultPasswordBytes);
9580 for (j = 0, n = checkData.length; j < n; ++j) {
9581 if (userPassword[j] !== checkData[j]) {
9586 return encryptionKey;
9589 function decodeUserPassword(password, ownerPassword, revision, keyLength) {
9590 var hashData = new Uint8Array(32), i = 0, j, n;
9591 n = Math.min(32, password.length);
9592 for (; i < n; ++i) {
9593 hashData[i] = password[i];
9597 hashData[i++] = defaultPasswordBytes[j++];
9599 var hash = calculateMD5(hashData, 0, i);
9600 var keyLengthInBytes = keyLength >> 3;
9601 if (revision >= 3) {
9602 for (j = 0; j < 50; ++j) {
9603 hash = calculateMD5(hash, 0, hash.length);
9607 var cipher, userPassword;
9608 if (revision >= 3) {
9609 userPassword = ownerPassword;
9610 var derivedKey = new Uint8Array(keyLengthInBytes), k;
9611 for (j = 19; j >= 0; j--) {
9612 for (k = 0; k < keyLengthInBytes; ++k) {
9613 derivedKey[k] = hash[k] ^ j;
9615 cipher = new ARCFourCipher(derivedKey);
9616 userPassword = cipher.encryptBlock(userPassword);
9619 cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes));
9620 userPassword = cipher.encryptBlock(ownerPassword);
9622 return userPassword;
9625 var identityName = Name.get('Identity');
9627 function CipherTransformFactory(dict, fileId, password) {
9628 var filter = dict.get('Filter');
9629 if (!isName(filter) || filter.name !== 'Standard') {
9630 error('unknown encryption method');
9633 var algorithm = dict.get('V');
9634 if (!isInt(algorithm) ||
9635 (algorithm !== 1 && algorithm !== 2 && algorithm !== 4 &&
9637 error('unsupported encryption algorithm');
9639 this.algorithm = algorithm;
9640 var keyLength = dict.get('Length') || 40;
9641 if (!isInt(keyLength) ||
9642 keyLength < 40 || (keyLength % 8) !== 0) {
9643 error('invalid key length');
9647 var ownerPassword = stringToBytes(dict.get('O')).subarray(0, 32);
9648 var userPassword = stringToBytes(dict.get('U')).subarray(0, 32);
9649 var flags = dict.get('P');
9650 var revision = dict.get('R');
9651 // meaningful when V is 4 or 5
9652 var encryptMetadata = ((algorithm === 4 || algorithm === 5) &&
9653 dict.get('EncryptMetadata') !== false);
9654 this.encryptMetadata = encryptMetadata;
9656 var fileIdBytes = stringToBytes(fileId);
9659 if (revision === 6) {
9661 password = utf8StringToString(password);
9663 warn('CipherTransformFactory: ' +
9664 'Unable to convert UTF8 encoded password.');
9667 passwordBytes = stringToBytes(password);
9671 if (algorithm !== 5) {
9672 encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
9673 ownerPassword, userPassword, flags,
9674 revision, keyLength, encryptMetadata);
9677 var ownerValidationSalt = stringToBytes(dict.get('O')).subarray(32, 40);
9678 var ownerKeySalt = stringToBytes(dict.get('O')).subarray(40, 48);
9679 var uBytes = stringToBytes(dict.get('U')).subarray(0, 48);
9680 var userValidationSalt = stringToBytes(dict.get('U')).subarray(32, 40);
9681 var userKeySalt = stringToBytes(dict.get('U')).subarray(40, 48);
9682 var ownerEncryption = stringToBytes(dict.get('OE'));
9683 var userEncryption = stringToBytes(dict.get('UE'));
9684 var perms = stringToBytes(dict.get('Perms'));
9686 createEncryptionKey20(revision, passwordBytes,
9687 ownerPassword, ownerValidationSalt,
9688 ownerKeySalt, uBytes,
9689 userPassword, userValidationSalt,
9690 userKeySalt, ownerEncryption,
9691 userEncryption, perms);
9693 if (!encryptionKey && !password) {
9694 throw new PasswordException('No password given',
9695 PasswordResponses.NEED_PASSWORD);
9696 } else if (!encryptionKey && password) {
9697 // Attempting use the password as an owner password
9698 var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword,
9699 revision, keyLength);
9700 encryptionKey = prepareKeyData(fileIdBytes, decodedPassword,
9701 ownerPassword, userPassword, flags,
9702 revision, keyLength, encryptMetadata);
9705 if (!encryptionKey) {
9706 throw new PasswordException('Incorrect Password',
9707 PasswordResponses.INCORRECT_PASSWORD);
9710 this.encryptionKey = encryptionKey;
9712 if (algorithm >= 4) {
9713 this.cf = dict.get('CF');
9714 this.stmf = dict.get('StmF') || identityName;
9715 this.strf = dict.get('StrF') || identityName;
9716 this.eff = dict.get('EFF') || this.stmf;
9720 function buildObjectKey(num, gen, encryptionKey, isAes) {
9721 var key = new Uint8Array(encryptionKey.length + 9), i, n;
9722 for (i = 0, n = encryptionKey.length; i < n; ++i) {
9723 key[i] = encryptionKey[i];
9725 key[i++] = num & 0xFF;
9726 key[i++] = (num >> 8) & 0xFF;
9727 key[i++] = (num >> 16) & 0xFF;
9728 key[i++] = gen & 0xFF;
9729 key[i++] = (gen >> 8) & 0xFF;
9736 var hash = calculateMD5(key, 0, i);
9737 return hash.subarray(0, Math.min(encryptionKey.length + 5, 16));
9740 function buildCipherConstructor(cf, name, num, gen, key) {
9741 var cryptFilter = cf.get(name.name);
9743 if (cryptFilter !== null && cryptFilter !== undefined) {
9744 cfm = cryptFilter.get('CFM');
9746 if (!cfm || cfm.name === 'None') {
9747 return function cipherTransformFactoryBuildCipherConstructorNone() {
9748 return new NullCipher();
9751 if ('V2' === cfm.name) {
9752 return function cipherTransformFactoryBuildCipherConstructorV2() {
9753 return new ARCFourCipher(buildObjectKey(num, gen, key, false));
9756 if ('AESV2' === cfm.name) {
9757 return function cipherTransformFactoryBuildCipherConstructorAESV2() {
9758 return new AES128Cipher(buildObjectKey(num, gen, key, true));
9761 if ('AESV3' === cfm.name) {
9762 return function cipherTransformFactoryBuildCipherConstructorAESV3() {
9763 return new AES256Cipher(key);
9766 error('Unknown crypto method');
9769 CipherTransformFactory.prototype = {
9770 createCipherTransform:
9771 function CipherTransformFactory_createCipherTransform(num, gen) {
9772 if (this.algorithm === 4 || this.algorithm === 5) {
9773 return new CipherTransform(
9774 buildCipherConstructor(this.cf, this.stmf,
9775 num, gen, this.encryptionKey),
9776 buildCipherConstructor(this.cf, this.strf,
9777 num, gen, this.encryptionKey));
9779 // algorithms 1 and 2
9780 var key = buildObjectKey(num, gen, this.encryptionKey, false);
9781 var cipherConstructor = function buildCipherCipherConstructor() {
9782 return new ARCFourCipher(key);
9784 return new CipherTransform(cipherConstructor, cipherConstructor);
9788 return CipherTransformFactory;
9797 LATTICE_FORM_MESH: 5,
9798 COONS_PATCH_MESH: 6,
9799 TENSOR_PATCH_MESH: 7
9802 var Pattern = (function PatternClosure() {
9803 // Constructor should define this.getPattern
9804 function Pattern() {
9805 error('should not call Pattern constructor');
9808 Pattern.prototype = {
9809 // Input: current Canvas context
9810 // Output: the appropriate fillStyle or strokeStyle
9811 getPattern: function Pattern_getPattern(ctx) {
9812 error('Should not call Pattern.getStyle: ' + ctx);
9816 Pattern.parseShading = function Pattern_parseShading(shading, matrix, xref,
9819 var dict = isStream(shading) ? shading.dict : shading;
9820 var type = dict.get('ShadingType');
9824 case PatternType.AXIAL:
9825 case PatternType.RADIAL:
9826 // Both radial and axial shadings are handled by RadialAxial shading.
9827 return new Shadings.RadialAxial(dict, matrix, xref, res);
9828 case PatternType.FREE_FORM_MESH:
9829 case PatternType.LATTICE_FORM_MESH:
9830 case PatternType.COONS_PATCH_MESH:
9831 case PatternType.TENSOR_PATCH_MESH:
9832 return new Shadings.Mesh(shading, matrix, xref, res);
9834 throw new Error('Unknown PatternType: ' + type);
9837 if (ex instanceof MissingDataException) {
9840 UnsupportedManager.notify(UNSUPPORTED_FEATURES.shadingPattern);
9842 return new Shadings.Dummy();
9850 // A small number to offset the first/last color stops so we can insert ones to
9851 // support extend. Number.MIN_VALUE appears to be too small and breaks the
9852 // extend. 1e-7 works in FF but chrome seems to use an even smaller sized number
9853 // internally so we have to go bigger.
9854 Shadings.SMALL_NUMBER = 1e-2;
9856 // Radial and axial shading have very similar implementations
9857 // If needed, the implementations can be broken into two classes
9858 Shadings.RadialAxial = (function RadialAxialClosure() {
9859 function RadialAxial(dict, matrix, xref, res) {
9860 this.matrix = matrix;
9861 this.coordsArr = dict.get('Coords');
9862 this.shadingType = dict.get('ShadingType');
9863 this.type = 'Pattern';
9864 var cs = dict.get('ColorSpace', 'CS');
9865 cs = ColorSpace.parse(cs, xref, res);
9868 var t0 = 0.0, t1 = 1.0;
9869 if (dict.has('Domain')) {
9870 var domainArr = dict.get('Domain');
9875 var extendStart = false, extendEnd = false;
9876 if (dict.has('Extend')) {
9877 var extendArr = dict.get('Extend');
9878 extendStart = extendArr[0];
9879 extendEnd = extendArr[1];
9882 if (this.shadingType === PatternType.RADIAL &&
9883 (!extendStart || !extendEnd)) {
9884 // Radial gradient only currently works if either circle is fully within
9885 // the other circle.
9886 var x1 = this.coordsArr[0];
9887 var y1 = this.coordsArr[1];
9888 var r1 = this.coordsArr[2];
9889 var x2 = this.coordsArr[3];
9890 var y2 = this.coordsArr[4];
9891 var r2 = this.coordsArr[5];
9892 var distance = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
9893 if (r1 <= r2 + distance &&
9894 r2 <= r1 + distance) {
9895 warn('Unsupported radial gradient.');
9899 this.extendStart = extendStart;
9900 this.extendEnd = extendEnd;
9902 var fnObj = dict.get('Function');
9903 var fn = PDFFunction.parseArray(xref, fnObj);
9905 // 10 samples seems good enough for now, but probably won't work
9906 // if there are sharp color changes. Ideally, we would implement
9907 // the spec faithfully and add lossless optimizations.
9909 var step = diff / 10;
9911 var colorStops = this.colorStops = [];
9913 // Protect against bad domains so we don't end up in an infinte loop below.
9914 if (t0 >= t1 || step <= 0) {
9915 // Acrobat doesn't seem to handle these cases so we'll ignore for
9917 info('Bad shading domain.');
9921 var color = new Float32Array(cs.numComps), ratio = new Float32Array(1);
9923 for (var i = t0; i <= t1; i += step) {
9925 fn(ratio, 0, color, 0);
9926 rgbColor = cs.getRgb(color, 0);
9927 var cssColor = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
9928 colorStops.push([(i - t0) / diff, cssColor]);
9931 var background = 'transparent';
9932 if (dict.has('Background')) {
9933 rgbColor = cs.getRgb(dict.get('Background'), 0);
9934 background = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
9938 // Insert a color stop at the front and offset the first real color stop
9939 // so it doesn't conflict with the one we insert.
9940 colorStops.unshift([0, background]);
9941 colorStops[1][0] += Shadings.SMALL_NUMBER;
9944 // Same idea as above in extendStart but for the end.
9945 colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER;
9946 colorStops.push([1, background]);
9949 this.colorStops = colorStops;
9952 RadialAxial.prototype = {
9953 getIR: function RadialAxial_getIR() {
9954 var coordsArr = this.coordsArr;
9955 var shadingType = this.shadingType;
9956 var type, p0, p1, r0, r1;
9957 if (shadingType === PatternType.AXIAL) {
9958 p0 = [coordsArr[0], coordsArr[1]];
9959 p1 = [coordsArr[2], coordsArr[3]];
9963 } else if (shadingType === PatternType.RADIAL) {
9964 p0 = [coordsArr[0], coordsArr[1]];
9965 p1 = [coordsArr[3], coordsArr[4]];
9970 error('getPattern type unknown: ' + shadingType);
9973 var matrix = this.matrix;
9975 p0 = Util.applyTransform(p0, matrix);
9976 p1 = Util.applyTransform(p1, matrix);
9979 return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1];
9986 // All mesh shading. For now, they will be presented as set of the triangles
9987 // to be drawn on the canvas and rgb color for each vertex.
9988 Shadings.Mesh = (function MeshClosure() {
9989 function MeshStreamReader(stream, context) {
9990 this.stream = stream;
9991 this.context = context;
9993 this.bufferLength = 0;
9995 var numComps = context.numComps;
9996 this.tmpCompsBuf = new Float32Array(numComps);
9997 var csNumComps = context.colorSpace;
9998 this.tmpCsCompsBuf = context.colorFn ? new Float32Array(csNumComps) :
10001 MeshStreamReader.prototype = {
10003 if (this.stream.end) {
10004 return this.stream.pos < this.stream.end;
10006 if (this.bufferLength > 0) {
10009 var nextByte = this.stream.getByte();
10010 if (nextByte < 0) {
10013 this.buffer = nextByte;
10014 this.bufferLength = 8;
10017 readBits: function MeshStreamReader_readBits(n) {
10018 var buffer = this.buffer;
10019 var bufferLength = this.bufferLength;
10021 if (bufferLength === 0) {
10022 return ((this.stream.getByte() << 24) |
10023 (this.stream.getByte() << 16) | (this.stream.getByte() << 8) |
10024 this.stream.getByte()) >>> 0;
10026 buffer = (buffer << 24) | (this.stream.getByte() << 16) |
10027 (this.stream.getByte() << 8) | this.stream.getByte();
10028 var nextByte = this.stream.getByte();
10029 this.buffer = nextByte & ((1 << bufferLength) - 1);
10030 return ((buffer << (8 - bufferLength)) |
10031 ((nextByte & 0xFF) >> bufferLength)) >>> 0;
10033 if (n === 8 && bufferLength === 0) {
10034 return this.stream.getByte();
10036 while (bufferLength < n) {
10037 buffer = (buffer << 8) | this.stream.getByte();
10041 this.bufferLength = bufferLength;
10042 this.buffer = buffer & ((1 << bufferLength) - 1);
10043 return buffer >> bufferLength;
10045 align: function MeshStreamReader_align() {
10047 this.bufferLength = 0;
10049 readFlag: function MeshStreamReader_readFlag() {
10050 return this.readBits(this.context.bitsPerFlag);
10052 readCoordinate: function MeshStreamReader_readCoordinate() {
10053 var bitsPerCoordinate = this.context.bitsPerCoordinate;
10054 var xi = this.readBits(bitsPerCoordinate);
10055 var yi = this.readBits(bitsPerCoordinate);
10056 var decode = this.context.decode;
10057 var scale = bitsPerCoordinate < 32 ? 1 / ((1 << bitsPerCoordinate) - 1) :
10058 2.3283064365386963e-10; // 2 ^ -32
10060 xi * scale * (decode[1] - decode[0]) + decode[0],
10061 yi * scale * (decode[3] - decode[2]) + decode[2]
10064 readComponents: function MeshStreamReader_readComponents() {
10065 var numComps = this.context.numComps;
10066 var bitsPerComponent = this.context.bitsPerComponent;
10067 var scale = bitsPerComponent < 32 ? 1 / ((1 << bitsPerComponent) - 1) :
10068 2.3283064365386963e-10; // 2 ^ -32
10069 var decode = this.context.decode;
10070 var components = this.tmpCompsBuf;
10071 for (var i = 0, j = 4; i < numComps; i++, j += 2) {
10072 var ci = this.readBits(bitsPerComponent);
10073 components[i] = ci * scale * (decode[j + 1] - decode[j]) + decode[j];
10075 var color = this.tmpCsCompsBuf;
10076 if (this.context.colorFn) {
10077 this.context.colorFn(components, 0, color, 0);
10079 return this.context.colorSpace.getRgb(color, 0);
10083 function decodeType4Shading(mesh, reader) {
10084 var coords = mesh.coords;
10085 var colors = mesh.colors;
10086 var operators = [];
10087 var ps = []; // not maintaining cs since that will match ps
10088 var verticesLeft = 0; // assuming we have all data to start a new triangle
10089 while (reader.hasData) {
10090 var f = reader.readFlag();
10091 var coord = reader.readCoordinate();
10092 var color = reader.readComponents();
10093 if (verticesLeft === 0) { // ignoring flags if we started a triangle
10094 assert(0 <= f && f <= 2, 'Unknown type4 flag');
10100 ps.push(ps[ps.length - 2], ps[ps.length - 1]);
10104 ps.push(ps[ps.length - 3], ps[ps.length - 1]);
10110 ps.push(coords.length);
10111 coords.push(coord);
10112 colors.push(color);
10118 var psPacked = new Int32Array(ps);
10120 mesh.figures.push({
10127 function decodeType5Shading(mesh, reader, verticesPerRow) {
10128 var coords = mesh.coords;
10129 var colors = mesh.colors;
10130 var ps = []; // not maintaining cs since that will match ps
10131 while (reader.hasData) {
10132 var coord = reader.readCoordinate();
10133 var color = reader.readComponents();
10134 ps.push(coords.length);
10135 coords.push(coord);
10136 colors.push(color);
10139 var psPacked = new Int32Array(ps);
10141 mesh.figures.push({
10145 verticesPerRow: verticesPerRow
10149 var MIN_SPLIT_PATCH_CHUNKS_AMOUNT = 3;
10150 var MAX_SPLIT_PATCH_CHUNKS_AMOUNT = 20;
10152 var TRIANGLE_DENSITY = 20; // count of triangles per entire mesh bounds
10154 var getB = (function getBClosure() {
10155 function buildB(count) {
10157 for (var i = 0; i <= count; i++) {
10158 var t = i / count, t_ = 1 - t;
10159 lut.push(new Float32Array([t_ * t_ * t_, 3 * t * t_ * t_,
10160 3 * t * t * t_, t * t * t]));
10165 return function getB(count) {
10166 if (!cache[count]) {
10167 cache[count] = buildB(count);
10169 return cache[count];
10173 function buildFigureFromPatch(mesh, index) {
10174 var figure = mesh.figures[index];
10175 assert(figure.type === 'patch', 'Unexpected patch mesh figure');
10177 var coords = mesh.coords, colors = mesh.colors;
10178 var pi = figure.coords;
10179 var ci = figure.colors;
10181 var figureMinX = Math.min(coords[pi[0]][0], coords[pi[3]][0],
10182 coords[pi[12]][0], coords[pi[15]][0]);
10183 var figureMinY = Math.min(coords[pi[0]][1], coords[pi[3]][1],
10184 coords[pi[12]][1], coords[pi[15]][1]);
10185 var figureMaxX = Math.max(coords[pi[0]][0], coords[pi[3]][0],
10186 coords[pi[12]][0], coords[pi[15]][0]);
10187 var figureMaxY = Math.max(coords[pi[0]][1], coords[pi[3]][1],
10188 coords[pi[12]][1], coords[pi[15]][1]);
10189 var splitXBy = Math.ceil((figureMaxX - figureMinX) * TRIANGLE_DENSITY /
10190 (mesh.bounds[2] - mesh.bounds[0]));
10191 splitXBy = Math.max(MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
10192 Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy));
10193 var splitYBy = Math.ceil((figureMaxY - figureMinY) * TRIANGLE_DENSITY /
10194 (mesh.bounds[3] - mesh.bounds[1]));
10195 splitYBy = Math.max(MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
10196 Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy));
10198 var verticesPerRow = splitXBy + 1;
10199 var figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);
10200 var figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);
10202 var cl = new Uint8Array(3), cr = new Uint8Array(3);
10203 var c0 = colors[ci[0]], c1 = colors[ci[1]],
10204 c2 = colors[ci[2]], c3 = colors[ci[3]];
10205 var bRow = getB(splitYBy), bCol = getB(splitXBy);
10206 for (var row = 0; row <= splitYBy; row++) {
10207 cl[0] = ((c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy) | 0;
10208 cl[1] = ((c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy) | 0;
10209 cl[2] = ((c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy) | 0;
10211 cr[0] = ((c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy) | 0;
10212 cr[1] = ((c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy) | 0;
10213 cr[2] = ((c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy) | 0;
10215 for (var col = 0; col <= splitXBy; col++, k++) {
10216 if ((row === 0 || row === splitYBy) &&
10217 (col === 0 || col === splitXBy)) {
10222 for (var i = 0; i <= 3; i++) {
10223 for (var j = 0; j <= 3; j++, q++) {
10224 var m = bRow[row][i] * bCol[col][j];
10225 x += coords[pi[q]][0] * m;
10226 y += coords[pi[q]][1] * m;
10229 figureCoords[k] = coords.length;
10230 coords.push([x, y]);
10231 figureColors[k] = colors.length;
10232 var newColor = new Uint8Array(3);
10233 newColor[0] = ((cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy) | 0;
10234 newColor[1] = ((cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy) | 0;
10235 newColor[2] = ((cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy) | 0;
10236 colors.push(newColor);
10239 figureCoords[0] = pi[0];
10240 figureColors[0] = ci[0];
10241 figureCoords[splitXBy] = pi[3];
10242 figureColors[splitXBy] = ci[1];
10243 figureCoords[verticesPerRow * splitYBy] = pi[12];
10244 figureColors[verticesPerRow * splitYBy] = ci[2];
10245 figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];
10246 figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];
10248 mesh.figures[index] = {
10250 coords: figureCoords,
10251 colors: figureColors,
10252 verticesPerRow: verticesPerRow
10256 function decodeType6Shading(mesh, reader) {
10257 // A special case of Type 7. The p11, p12, p21, p22 automatically filled
10258 var coords = mesh.coords;
10259 var colors = mesh.colors;
10260 var ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
10261 var cs = new Int32Array(4); // c00, c30, c03, c33
10262 while (reader.hasData) {
10263 var f = reader.readFlag();
10264 assert(0 <= f && f <= 3, 'Unknown type6 flag');
10266 var pi = coords.length;
10267 for (i = 0, ii = (f !== 0 ? 8 : 12); i < ii; i++) {
10268 coords.push(reader.readCoordinate());
10270 var ci = colors.length;
10271 for (i = 0, ii = (f !== 0 ? 2 : 4); i < ii; i++) {
10272 colors.push(reader.readComponents());
10274 var tmp1, tmp2, tmp3, tmp4;
10277 ps[12] = pi + 3; ps[13] = pi + 4; ps[14] = pi + 5; ps[15] = pi + 6;
10278 ps[ 8] = pi + 2; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 7;
10279 ps[ 4] = pi + 1; /* calculated below */ ps[ 7] = pi + 8;
10280 ps[ 0] = pi; ps[ 1] = pi + 11; ps[ 2] = pi + 10; ps[ 3] = pi + 9;
10281 cs[2] = ci + 1; cs[3] = ci + 2;
10282 cs[0] = ci; cs[1] = ci + 3;
10285 tmp1 = ps[12]; tmp2 = ps[13]; tmp3 = ps[14]; tmp4 = ps[15];
10286 ps[12] = pi + 5; ps[13] = pi + 4; ps[14] = pi + 3; ps[15] = pi + 2;
10287 ps[ 8] = pi + 6; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 1;
10288 ps[ 4] = pi + 7; /* calculated below */ ps[ 7] = pi;
10289 ps[ 0] = tmp1; ps[ 1] = tmp2; ps[ 2] = tmp3; ps[ 3] = tmp4;
10290 tmp1 = cs[2]; tmp2 = cs[3];
10291 cs[2] = ci + 1; cs[3] = ci;
10292 cs[0] = tmp1; cs[1] = tmp2;
10295 ps[12] = ps[15]; ps[13] = pi + 7; ps[14] = pi + 6; ps[15] = pi + 5;
10296 ps[ 8] = ps[11]; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 4;
10297 ps[ 4] = ps[7]; /* calculated below */ ps[ 7] = pi + 3;
10298 ps[ 0] = ps[3]; ps[ 1] = pi; ps[ 2] = pi + 1; ps[ 3] = pi + 2;
10299 cs[2] = cs[3]; cs[3] = ci + 1;
10300 cs[0] = cs[1]; cs[1] = ci;
10303 ps[12] = ps[0]; ps[13] = ps[1]; ps[14] = ps[2]; ps[15] = ps[3];
10304 ps[ 8] = pi; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 7;
10305 ps[ 4] = pi + 1; /* calculated below */ ps[ 7] = pi + 6;
10306 ps[ 0] = pi + 2; ps[ 1] = pi + 3; ps[ 2] = pi + 4; ps[ 3] = pi + 5;
10307 cs[2] = cs[0]; cs[3] = cs[1];
10308 cs[0] = ci; cs[1] = ci + 1;
10311 // set p11, p12, p21, p22
10312 ps[5] = coords.length;
10314 (-4 * coords[ps[0]][0] - coords[ps[15]][0] +
10315 6 * (coords[ps[4]][0] + coords[ps[1]][0]) -
10316 2 * (coords[ps[12]][0] + coords[ps[3]][0]) +
10317 3 * (coords[ps[13]][0] + coords[ps[7]][0])) / 9,
10318 (-4 * coords[ps[0]][1] - coords[ps[15]][1] +
10319 6 * (coords[ps[4]][1] + coords[ps[1]][1]) -
10320 2 * (coords[ps[12]][1] + coords[ps[3]][1]) +
10321 3 * (coords[ps[13]][1] + coords[ps[7]][1])) / 9
10323 ps[6] = coords.length;
10325 (-4 * coords[ps[3]][0] - coords[ps[12]][0] +
10326 6 * (coords[ps[2]][0] + coords[ps[7]][0]) -
10327 2 * (coords[ps[0]][0] + coords[ps[15]][0]) +
10328 3 * (coords[ps[4]][0] + coords[ps[14]][0])) / 9,
10329 (-4 * coords[ps[3]][1] - coords[ps[12]][1] +
10330 6 * (coords[ps[2]][1] + coords[ps[7]][1]) -
10331 2 * (coords[ps[0]][1] + coords[ps[15]][1]) +
10332 3 * (coords[ps[4]][1] + coords[ps[14]][1])) / 9
10334 ps[9] = coords.length;
10336 (-4 * coords[ps[12]][0] - coords[ps[3]][0] +
10337 6 * (coords[ps[8]][0] + coords[ps[13]][0]) -
10338 2 * (coords[ps[0]][0] + coords[ps[15]][0]) +
10339 3 * (coords[ps[11]][0] + coords[ps[1]][0])) / 9,
10340 (-4 * coords[ps[12]][1] - coords[ps[3]][1] +
10341 6 * (coords[ps[8]][1] + coords[ps[13]][1]) -
10342 2 * (coords[ps[0]][1] + coords[ps[15]][1]) +
10343 3 * (coords[ps[11]][1] + coords[ps[1]][1])) / 9
10345 ps[10] = coords.length;
10347 (-4 * coords[ps[15]][0] - coords[ps[0]][0] +
10348 6 * (coords[ps[11]][0] + coords[ps[14]][0]) -
10349 2 * (coords[ps[12]][0] + coords[ps[3]][0]) +
10350 3 * (coords[ps[2]][0] + coords[ps[8]][0])) / 9,
10351 (-4 * coords[ps[15]][1] - coords[ps[0]][1] +
10352 6 * (coords[ps[11]][1] + coords[ps[14]][1]) -
10353 2 * (coords[ps[12]][1] + coords[ps[3]][1]) +
10354 3 * (coords[ps[2]][1] + coords[ps[8]][1])) / 9
10356 mesh.figures.push({
10358 coords: new Int32Array(ps), // making copies of ps and cs
10359 colors: new Int32Array(cs)
10364 function decodeType7Shading(mesh, reader) {
10365 var coords = mesh.coords;
10366 var colors = mesh.colors;
10367 var ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
10368 var cs = new Int32Array(4); // c00, c30, c03, c33
10369 while (reader.hasData) {
10370 var f = reader.readFlag();
10371 assert(0 <= f && f <= 3, 'Unknown type7 flag');
10373 var pi = coords.length;
10374 for (i = 0, ii = (f !== 0 ? 12 : 16); i < ii; i++) {
10375 coords.push(reader.readCoordinate());
10377 var ci = colors.length;
10378 for (i = 0, ii = (f !== 0 ? 2 : 4); i < ii; i++) {
10379 colors.push(reader.readComponents());
10381 var tmp1, tmp2, tmp3, tmp4;
10384 ps[12] = pi + 3; ps[13] = pi + 4; ps[14] = pi + 5; ps[15] = pi + 6;
10385 ps[ 8] = pi + 2; ps[ 9] = pi + 13; ps[10] = pi + 14; ps[11] = pi + 7;
10386 ps[ 4] = pi + 1; ps[ 5] = pi + 12; ps[ 6] = pi + 15; ps[ 7] = pi + 8;
10387 ps[ 0] = pi; ps[ 1] = pi + 11; ps[ 2] = pi + 10; ps[ 3] = pi + 9;
10388 cs[2] = ci + 1; cs[3] = ci + 2;
10389 cs[0] = ci; cs[1] = ci + 3;
10392 tmp1 = ps[12]; tmp2 = ps[13]; tmp3 = ps[14]; tmp4 = ps[15];
10393 ps[12] = pi + 5; ps[13] = pi + 4; ps[14] = pi + 3; ps[15] = pi + 2;
10394 ps[ 8] = pi + 6; ps[ 9] = pi + 11; ps[10] = pi + 10; ps[11] = pi + 1;
10395 ps[ 4] = pi + 7; ps[ 5] = pi + 8; ps[ 6] = pi + 9; ps[ 7] = pi;
10396 ps[ 0] = tmp1; ps[ 1] = tmp2; ps[ 2] = tmp3; ps[ 3] = tmp4;
10397 tmp1 = cs[2]; tmp2 = cs[3];
10398 cs[2] = ci + 1; cs[3] = ci;
10399 cs[0] = tmp1; cs[1] = tmp2;
10402 ps[12] = ps[15]; ps[13] = pi + 7; ps[14] = pi + 6; ps[15] = pi + 5;
10403 ps[ 8] = ps[11]; ps[ 9] = pi + 8; ps[10] = pi + 11; ps[11] = pi + 4;
10404 ps[ 4] = ps[7]; ps[ 5] = pi + 9; ps[ 6] = pi + 10; ps[ 7] = pi + 3;
10405 ps[ 0] = ps[3]; ps[ 1] = pi; ps[ 2] = pi + 1; ps[ 3] = pi + 2;
10406 cs[2] = cs[3]; cs[3] = ci + 1;
10407 cs[0] = cs[1]; cs[1] = ci;
10410 ps[12] = ps[0]; ps[13] = ps[1]; ps[14] = ps[2]; ps[15] = ps[3];
10411 ps[ 8] = pi; ps[ 9] = pi + 9; ps[10] = pi + 8; ps[11] = pi + 7;
10412 ps[ 4] = pi + 1; ps[ 5] = pi + 10; ps[ 6] = pi + 11; ps[ 7] = pi + 6;
10413 ps[ 0] = pi + 2; ps[ 1] = pi + 3; ps[ 2] = pi + 4; ps[ 3] = pi + 5;
10414 cs[2] = cs[0]; cs[3] = cs[1];
10415 cs[0] = ci; cs[1] = ci + 1;
10418 mesh.figures.push({
10420 coords: new Int32Array(ps), // making copies of ps and cs
10421 colors: new Int32Array(cs)
10426 function updateBounds(mesh) {
10427 var minX = mesh.coords[0][0], minY = mesh.coords[0][1],
10428 maxX = minX, maxY = minY;
10429 for (var i = 1, ii = mesh.coords.length; i < ii; i++) {
10430 var x = mesh.coords[i][0], y = mesh.coords[i][1];
10431 minX = minX > x ? x : minX;
10432 minY = minY > y ? y : minY;
10433 maxX = maxX < x ? x : maxX;
10434 maxY = maxY < y ? y : maxY;
10436 mesh.bounds = [minX, minY, maxX, maxY];
10439 function packData(mesh) {
10442 var coords = mesh.coords;
10443 var coordsPacked = new Float32Array(coords.length * 2);
10444 for (i = 0, j = 0, ii = coords.length; i < ii; i++) {
10445 var xy = coords[i];
10446 coordsPacked[j++] = xy[0];
10447 coordsPacked[j++] = xy[1];
10449 mesh.coords = coordsPacked;
10451 var colors = mesh.colors;
10452 var colorsPacked = new Uint8Array(colors.length * 3);
10453 for (i = 0, j = 0, ii = colors.length; i < ii; i++) {
10455 colorsPacked[j++] = c[0];
10456 colorsPacked[j++] = c[1];
10457 colorsPacked[j++] = c[2];
10459 mesh.colors = colorsPacked;
10461 var figures = mesh.figures;
10462 for (i = 0, ii = figures.length; i < ii; i++) {
10463 var figure = figures[i], ps = figure.coords, cs = figure.colors;
10464 for (j = 0, jj = ps.length; j < jj; j++) {
10471 function Mesh(stream, matrix, xref, res) {
10472 assert(isStream(stream), 'Mesh data is not a stream');
10473 var dict = stream.dict;
10474 this.matrix = matrix;
10475 this.shadingType = dict.get('ShadingType');
10476 this.type = 'Pattern';
10477 this.bbox = dict.get('BBox');
10478 var cs = dict.get('ColorSpace', 'CS');
10479 cs = ColorSpace.parse(cs, xref, res);
10481 this.background = dict.has('Background') ?
10482 cs.getRgb(dict.get('Background'), 0) : null;
10484 var fnObj = dict.get('Function');
10485 var fn = fnObj ? PDFFunction.parseArray(xref, fnObj) : null;
10491 var decodeContext = {
10492 bitsPerCoordinate: dict.get('BitsPerCoordinate'),
10493 bitsPerComponent: dict.get('BitsPerComponent'),
10494 bitsPerFlag: dict.get('BitsPerFlag'),
10495 decode: dict.get('Decode'),
10498 numComps: fn ? 1 : cs.numComps
10500 var reader = new MeshStreamReader(stream, decodeContext);
10502 var patchMesh = false;
10503 switch (this.shadingType) {
10504 case PatternType.FREE_FORM_MESH:
10505 decodeType4Shading(this, reader);
10507 case PatternType.LATTICE_FORM_MESH:
10508 var verticesPerRow = dict.get('VerticesPerRow') | 0;
10509 assert(verticesPerRow >= 2, 'Invalid VerticesPerRow');
10510 decodeType5Shading(this, reader, verticesPerRow);
10512 case PatternType.COONS_PATCH_MESH:
10513 decodeType6Shading(this, reader);
10516 case PatternType.TENSOR_PATCH_MESH:
10517 decodeType7Shading(this, reader);
10521 error('Unsupported mesh type.');
10526 // dirty bounds calculation for determining, how dense shall be triangles
10527 updateBounds(this);
10528 for (var i = 0, ii = this.figures.length; i < ii; i++) {
10529 buildFigureFromPatch(this, i);
10532 // calculate bounds
10533 updateBounds(this);
10539 getIR: function Mesh_getIR() {
10540 return ['Mesh', this.shadingType, this.coords, this.colors, this.figures,
10541 this.bounds, this.matrix, this.bbox, this.background];
10548 Shadings.Dummy = (function DummyClosure() {
10550 this.type = 'Pattern';
10553 Dummy.prototype = {
10554 getIR: function Dummy_getIR() {
10561 function getTilingPatternIR(operatorList, dict, args) {
10562 var matrix = dict.get('Matrix');
10563 var bbox = dict.get('BBox');
10564 var xstep = dict.get('XStep');
10565 var ystep = dict.get('YStep');
10566 var paintType = dict.get('PaintType');
10567 var tilingType = dict.get('TilingType');
10570 'TilingPattern', args, operatorList, matrix, bbox, xstep, ystep,
10571 paintType, tilingType
10576 var PartialEvaluator = (function PartialEvaluatorClosure() {
10577 function PartialEvaluator(pdfManager, xref, handler, pageIndex,
10578 uniquePrefix, idCounters, fontCache) {
10579 this.pdfManager = pdfManager;
10581 this.handler = handler;
10582 this.pageIndex = pageIndex;
10583 this.uniquePrefix = uniquePrefix;
10584 this.idCounters = idCounters;
10585 this.fontCache = fontCache;
10588 // Trying to minimize Date.now() usage and check every 100 time
10589 var TIME_SLOT_DURATION_MS = 20;
10590 var CHECK_TIME_EVERY = 100;
10591 function TimeSlotManager() {
10594 TimeSlotManager.prototype = {
10595 check: function TimeSlotManager_check() {
10596 if (++this.checked < CHECK_TIME_EVERY) {
10600 return this.endTime <= Date.now();
10602 reset: function TimeSlotManager_reset() {
10603 this.endTime = Date.now() + TIME_SLOT_DURATION_MS;
10608 var deferred = Promise.resolve();
10610 var TILING_PATTERN = 1, SHADING_PATTERN = 2;
10612 PartialEvaluator.prototype = {
10613 hasBlendModes: function PartialEvaluator_hasBlendModes(resources) {
10614 if (!isDict(resources)) {
10618 var processed = Object.create(null);
10619 if (resources.objId) {
10620 processed[resources.objId] = true;
10623 var nodes = [resources];
10624 while (nodes.length) {
10626 var node = nodes.shift();
10627 // First check the current resources for blend modes.
10628 var graphicStates = node.get('ExtGState');
10629 if (isDict(graphicStates)) {
10630 graphicStates = graphicStates.getAll();
10631 for (key in graphicStates) {
10632 var graphicState = graphicStates[key];
10633 var bm = graphicState['BM'];
10634 if (isName(bm) && bm.name !== 'Normal') {
10639 // Descend into the XObjects to look for more resources and blend modes.
10640 var xObjects = node.get('XObject');
10641 if (!isDict(xObjects)) {
10644 xObjects = xObjects.getAll();
10645 for (key in xObjects) {
10646 var xObject = xObjects[key];
10647 if (!isStream(xObject)) {
10650 if (xObject.dict.objId) {
10651 if (processed[xObject.dict.objId]) {
10652 // stream has objId and is processed already
10655 processed[xObject.dict.objId] = true;
10657 var xResources = xObject.dict.get('Resources');
10658 // Checking objId to detect an infinite loop.
10659 if (isDict(xResources) &&
10660 (!xResources.objId || !processed[xResources.objId])) {
10661 nodes.push(xResources);
10662 if (xResources.objId) {
10663 processed[xResources.objId] = true;
10671 buildFormXObject: function PartialEvaluator_buildFormXObject(resources,
10675 var matrix = xobj.dict.get('Matrix');
10676 var bbox = xobj.dict.get('BBox');
10677 var group = xobj.dict.get('Group');
10679 var groupOptions = {
10687 var groupSubtype = group.get('S');
10689 if (isName(groupSubtype) && groupSubtype.name === 'Transparency') {
10690 groupOptions.isolated = (group.get('I') || false);
10691 groupOptions.knockout = (group.get('K') || false);
10692 colorSpace = (group.has('CS') ?
10693 ColorSpace.parse(group.get('CS'), this.xref, resources) : null);
10696 if (smask && smask.backdrop) {
10697 colorSpace = colorSpace || ColorSpace.singletons.rgb;
10698 smask.backdrop = colorSpace.getRgb(smask.backdrop, 0);
10701 operatorList.addOp(OPS.beginGroup, [groupOptions]);
10704 operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]);
10706 return this.getOperatorList(xobj,
10707 (xobj.dict.get('Resources') || resources), operatorList, initialState).
10709 operatorList.addOp(OPS.paintFormXObjectEnd, []);
10712 operatorList.addOp(OPS.endGroup, [groupOptions]);
10717 buildPaintImageXObject:
10718 function PartialEvaluator_buildPaintImageXObject(resources, image,
10719 inline, operatorList,
10720 cacheKey, imageCache) {
10722 var dict = image.dict;
10723 var w = dict.get('Width', 'W');
10724 var h = dict.get('Height', 'H');
10726 if (!(w && isNum(w)) || !(h && isNum(h))) {
10727 warn('Image dimensions are missing, or not numbers.');
10730 if (PDFJS.maxImageSize !== -1 && w * h > PDFJS.maxImageSize) {
10731 warn('Image exceeded maximum allowed size and was removed.');
10735 var imageMask = (dict.get('ImageMask', 'IM') || false);
10738 // This depends on a tmpCanvas being filled with the
10739 // current fillStyle, such that processing the pixel
10740 // data can't be done here. Instead of creating a
10741 // complete PDFImage, only read the information needed
10744 var width = dict.get('Width', 'W');
10745 var height = dict.get('Height', 'H');
10746 var bitStrideLength = (width + 7) >> 3;
10747 var imgArray = image.getBytes(bitStrideLength * height);
10748 var decode = dict.get('Decode', 'D');
10749 var inverseDecode = (!!decode && decode[0] > 0);
10751 imgData = PDFImage.createMask(imgArray, width, height,
10752 image instanceof DecodeStream,
10754 imgData.cached = true;
10756 operatorList.addOp(OPS.paintImageMaskXObject, args);
10758 imageCache[cacheKey] = {
10759 fn: OPS.paintImageMaskXObject,
10766 var softMask = (dict.get('SMask', 'SM') || false);
10767 var mask = (dict.get('Mask') || false);
10769 var SMALL_IMAGE_DIMENSIONS = 200;
10770 // Inlining small images into the queue as RGB data
10771 if (inline && !softMask && !mask && !(image instanceof JpegStream) &&
10772 (w + h) < SMALL_IMAGE_DIMENSIONS) {
10773 var imageObj = new PDFImage(this.xref, resources, image,
10774 inline, null, null);
10775 // We force the use of RGBA_32BPP images here, because we can't handle
10777 imgData = imageObj.createImageData(/* forceRGBA = */ true);
10778 operatorList.addOp(OPS.paintInlineImageXObject, [imgData]);
10782 // If there is no imageMask, create the PDFImage and a lot
10783 // of image processing can be done here.
10784 var uniquePrefix = (this.uniquePrefix || '');
10785 var objId = 'img_' + uniquePrefix + (++this.idCounters.obj);
10786 operatorList.addDependency(objId);
10787 args = [objId, w, h];
10789 if (!softMask && !mask && image instanceof JpegStream &&
10790 image.isNativelySupported(this.xref, resources)) {
10791 // These JPEGs don't need any more processing so we can just send it.
10792 operatorList.addOp(OPS.paintJpegXObject, args);
10793 this.handler.send('obj',
10794 [objId, this.pageIndex, 'JpegStream', image.getIR()]);
10798 PDFImage.buildImage(self.handler, self.xref, resources, image, inline).
10799 then(function(imageObj) {
10800 var imgData = imageObj.createImageData(/* forceRGBA = */ false);
10801 self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData],
10802 [imgData.data.buffer]);
10803 }).then(undefined, function (reason) {
10804 warn('Unable to decode image: ' + reason);
10805 self.handler.send('obj', [objId, self.pageIndex, 'Image', null]);
10808 operatorList.addOp(OPS.paintImageXObject, args);
10810 imageCache[cacheKey] = {
10811 fn: OPS.paintImageXObject,
10817 handleSMask: function PartialEvaluator_handleSmask(smask, resources,
10820 var smaskContent = smask.get('G');
10821 var smaskOptions = {
10822 subtype: smask.get('S').name,
10823 backdrop: smask.get('BC')
10825 return this.buildFormXObject(resources, smaskContent, smaskOptions,
10826 operatorList, stateManager.state.clone());
10830 function PartialEvaluator_handleTilingType(fn, args, resources,
10831 pattern, patternDict,
10833 // Create an IR of the pattern code.
10834 var tilingOpList = new OperatorList();
10835 return this.getOperatorList(pattern,
10836 (patternDict.get('Resources') || resources), tilingOpList).
10838 // Add the dependencies to the parent operator list so they are
10839 // resolved before sub operator list is executed synchronously.
10840 operatorList.addDependencies(tilingOpList.dependencies);
10841 operatorList.addOp(fn, getTilingPatternIR({
10842 fnArray: tilingOpList.fnArray,
10843 argsArray: tilingOpList.argsArray
10844 }, patternDict, args));
10849 function PartialEvaluator_handleSetFont(resources, fontArgs, fontRef,
10850 operatorList, state) {
10851 // TODO(mack): Not needed?
10854 fontArgs = fontArgs.slice();
10855 fontName = fontArgs[0].name;
10859 return this.loadFont(fontName, fontRef, this.xref, resources).then(
10860 function (translated) {
10861 if (!translated.font.isType3Font) {
10864 return translated.loadType3Data(self, resources, operatorList).then(
10868 }).then(function (translated) {
10869 state.font = translated.font;
10870 translated.send(self.handler);
10871 return translated.loadedName;
10875 handleText: function PartialEvaluator_handleText(chars, state) {
10876 var font = state.font;
10877 var glyphs = font.charsToGlyphs(chars);
10878 var isAddToPathSet = !!(state.textRenderingMode &
10879 TextRenderingMode.ADD_TO_PATH_FLAG);
10880 if (font.data && (isAddToPathSet || PDFJS.disableFontFace)) {
10881 var buildPath = function (fontChar) {
10882 if (!font.renderer.hasBuiltPath(fontChar)) {
10883 var path = font.renderer.getPathJs(fontChar);
10884 this.handler.send('commonobj', [
10885 font.loadedName + '_path_' + fontChar,
10892 for (var i = 0, ii = glyphs.length; i < ii; i++) {
10893 var glyph = glyphs[i];
10894 if (glyph === null) {
10897 buildPath(glyph.fontChar);
10899 // If the glyph has an accent we need to build a path for its
10900 // fontChar too, otherwise CanvasGraphics_paintChar will fail.
10901 var accent = glyph.accent;
10902 if (accent && accent.fontChar) {
10903 buildPath(accent.fontChar);
10911 setGState: function PartialEvaluator_setGState(resources, gState,
10912 operatorList, xref,
10914 // This array holds the converted/processed state data.
10915 var gStateObj = [];
10916 var gStateMap = gState.map;
10918 var promise = Promise.resolve();
10919 for (var key in gStateMap) {
10920 var value = gStateMap[key];
10933 gStateObj.push([key, value]);
10936 promise = promise.then(function () {
10937 return self.handleSetFont(resources, null, value[0],
10938 operatorList, stateManager.state).
10939 then(function (loadedName) {
10940 operatorList.addDependency(loadedName);
10941 gStateObj.push([key, [loadedName, value[1]]]);
10946 gStateObj.push([key, value]);
10949 if (isName(value) && value.name === 'None') {
10950 gStateObj.push([key, false]);
10953 var dict = xref.fetchIfRef(value);
10954 if (isDict(dict)) {
10955 promise = promise.then(function () {
10956 return self.handleSMask(dict, resources, operatorList,
10959 gStateObj.push([key, true]);
10961 warn('Unsupported SMask type');
10965 // Only generate info log messages for the following since
10966 // they are unlikely to have a big impact on the rendering.
10981 // TODO implement these operators.
10982 info('graphic state operator ' + key);
10985 info('Unknown graphic state operator ' + key);
10989 return promise.then(function () {
10990 if (gStateObj.length >= 0) {
10991 operatorList.addOp(OPS.setGState, [gStateObj]);
10996 loadFont: function PartialEvaluator_loadFont(fontName, font, xref,
10999 function errorFont() {
11000 return Promise.resolve(new TranslatedFont('g_font_error',
11001 new ErrorFont('Font ' + fontName + ' is not available'), font));
11004 if (font) { // Loading by ref.
11005 assert(isRef(font));
11007 } else { // Loading by name.
11008 var fontRes = resources.get('Font');
11010 fontRef = fontRes.getRaw(fontName);
11012 warn('fontRes not available');
11013 return errorFont();
11017 warn('fontRef not available');
11018 return errorFont();
11021 if (this.fontCache.has(fontRef)) {
11022 return this.fontCache.get(fontRef);
11025 font = xref.fetchIfRef(fontRef);
11026 if (!isDict(font)) {
11027 return errorFont();
11030 // We are holding font.translated references just for fontRef that are not
11031 // dictionaries (Dict). See explanation below.
11032 if (font.translated) {
11033 return font.translated;
11036 var fontCapability = createPromiseCapability();
11038 var preEvaluatedFont = this.preEvaluateFont(font, xref);
11039 var descriptor = preEvaluatedFont.descriptor;
11040 var fontID = fontRef.num + '_' + fontRef.gen;
11041 if (isDict(descriptor)) {
11042 if (!descriptor.fontAliases) {
11043 descriptor.fontAliases = Object.create(null);
11046 var fontAliases = descriptor.fontAliases;
11047 var hash = preEvaluatedFont.hash;
11048 if (fontAliases[hash]) {
11049 var aliasFontRef = fontAliases[hash].aliasRef;
11050 if (aliasFontRef && this.fontCache.has(aliasFontRef)) {
11051 this.fontCache.putAlias(fontRef, aliasFontRef);
11052 return this.fontCache.get(fontRef);
11056 if (!fontAliases[hash]) {
11057 fontAliases[hash] = {
11058 fontID: Font.getFontID()
11062 fontAliases[hash].aliasRef = fontRef;
11063 fontID = fontAliases[hash].fontID;
11066 // Workaround for bad PDF generators that don't reference fonts
11067 // properly, i.e. by not using an object identifier.
11068 // Check if the fontRef is a Dict (as opposed to a standard object),
11069 // in which case we don't cache the font and instead reference it by
11070 // fontName in font.loadedName below.
11071 var fontRefIsDict = isDict(fontRef);
11072 if (!fontRefIsDict) {
11073 this.fontCache.put(fontRef, fontCapability.promise);
11076 // Keep track of each font we translated so the caller can
11077 // load them asynchronously before calling display on a page.
11078 font.loadedName = 'g_font_' + (fontRefIsDict ?
11079 fontName.replace(/\W/g, '') : fontID);
11081 font.translated = fontCapability.promise;
11083 // TODO move promises into translate font
11084 var translatedPromise;
11086 translatedPromise = Promise.resolve(
11087 this.translateFont(preEvaluatedFont, xref));
11089 translatedPromise = Promise.reject(e);
11092 translatedPromise.then(function (translatedFont) {
11093 if (translatedFont.fontType !== undefined) {
11094 var xrefFontStats = xref.stats.fontTypes;
11095 xrefFontStats[translatedFont.fontType] = true;
11098 fontCapability.resolve(new TranslatedFont(font.loadedName,
11099 translatedFont, font));
11100 }, function (reason) {
11101 // TODO fontCapability.reject?
11102 UnsupportedManager.notify(UNSUPPORTED_FEATURES.font);
11105 // error, but it's still nice to have font type reported
11106 var descriptor = preEvaluatedFont.descriptor;
11107 var fontFile3 = descriptor && descriptor.get('FontFile3');
11108 var subtype = fontFile3 && fontFile3.get('Subtype');
11109 var fontType = getFontType(preEvaluatedFont.type,
11110 subtype && subtype.name);
11111 var xrefFontStats = xref.stats.fontTypes;
11112 xrefFontStats[fontType] = true;
11115 fontCapability.resolve(new TranslatedFont(font.loadedName,
11116 new ErrorFont(reason instanceof Error ? reason.message : reason),
11119 return fontCapability.promise;
11122 buildPath: function PartialEvaluator_buildPath(operatorList, fn, args) {
11123 var lastIndex = operatorList.length - 1;
11127 if (lastIndex < 0 ||
11128 operatorList.fnArray[lastIndex] !== OPS.constructPath) {
11129 operatorList.addOp(OPS.constructPath, [[fn], args]);
11131 var opArgs = operatorList.argsArray[lastIndex];
11132 opArgs[0].push(fn);
11133 Array.prototype.push.apply(opArgs[1], args);
11137 handleColorN: function PartialEvaluator_handleColorN(operatorList, fn, args,
11138 cs, patterns, resources, xref) {
11139 // compile tiling patterns
11140 var patternName = args[args.length - 1];
11141 // SCN/scn applies patterns along with normal colors
11143 if (isName(patternName) &&
11144 (pattern = patterns.get(patternName.name))) {
11145 var dict = (isStream(pattern) ? pattern.dict : pattern);
11146 var typeNum = dict.get('PatternType');
11148 if (typeNum === TILING_PATTERN) {
11149 var color = cs.base ? cs.base.getRgb(args, 0) : null;
11150 return this.handleTilingType(fn, color, resources, pattern,
11151 dict, operatorList);
11152 } else if (typeNum === SHADING_PATTERN) {
11153 var shading = dict.get('Shading');
11154 var matrix = dict.get('Matrix');
11155 pattern = Pattern.parseShading(shading, matrix, xref, resources);
11156 operatorList.addOp(fn, pattern.getIR());
11157 return Promise.resolve();
11159 return Promise.reject('Unknown PatternType: ' + typeNum);
11162 // TODO shall we fail here?
11163 operatorList.addOp(fn, args);
11164 return Promise.resolve();
11167 getOperatorList: function PartialEvaluator_getOperatorList(stream,
11173 var xref = this.xref;
11174 var imageCache = {};
11176 assert(operatorList);
11178 resources = (resources || Dict.empty);
11179 var xobjs = (resources.get('XObject') || Dict.empty);
11180 var patterns = (resources.get('Pattern') || Dict.empty);
11181 var stateManager = new StateManager(initialState || new EvalState());
11182 var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
11183 var timeSlotManager = new TimeSlotManager();
11185 return new Promise(function next(resolve, reject) {
11186 timeSlotManager.reset();
11187 var stop, operation = {}, i, ii, cs;
11188 while (!(stop = timeSlotManager.check())) {
11189 // The arguments parsed by read() are used beyond this loop, so we
11190 // cannot reuse the same array on each iteration. Therefore we pass
11191 // in |null| as the initial value (see the comment on
11192 // EvaluatorPreprocessor_read() for why).
11193 operation.args = null;
11194 if (!(preprocessor.read(operation))) {
11197 var args = operation.args;
11198 var fn = operation.fn;
11201 case OPS.paintXObject:
11202 if (args[0].code) {
11205 // eagerly compile XForm objects
11206 var name = args[0].name;
11207 if (imageCache[name] !== undefined) {
11208 operatorList.addOp(imageCache[name].fn, imageCache[name].args);
11213 var xobj = xobjs.get(name);
11215 assert(isStream(xobj), 'XObject should be a stream');
11217 var type = xobj.dict.get('Subtype');
11218 assert(isName(type),
11219 'XObject should have a Name subtype');
11221 if (type.name === 'Form') {
11222 stateManager.save();
11223 return self.buildFormXObject(resources, xobj, null,
11225 stateManager.state.clone()).
11227 stateManager.restore();
11228 next(resolve, reject);
11230 } else if (type.name === 'Image') {
11231 self.buildPaintImageXObject(resources, xobj, false,
11232 operatorList, name, imageCache);
11235 } else if (type.name === 'PS') {
11236 // PostScript XObjects are unused when viewing documents.
11237 // See section 4.7.1 of Adobe's PDF reference.
11238 info('Ignored XObject subtype PS');
11241 error('Unhandled XObject subtype ' + type.name);
11246 var fontSize = args[1];
11247 // eagerly collect all fonts
11248 return self.handleSetFont(resources, args, null,
11249 operatorList, stateManager.state).
11250 then(function (loadedName) {
11251 operatorList.addDependency(loadedName);
11252 operatorList.addOp(OPS.setFont, [loadedName, fontSize]);
11253 next(resolve, reject);
11255 case OPS.endInlineImage:
11256 var cacheKey = args[0].cacheKey;
11258 var cacheEntry = imageCache[cacheKey];
11259 if (cacheEntry !== undefined) {
11260 operatorList.addOp(cacheEntry.fn, cacheEntry.args);
11265 self.buildPaintImageXObject(resources, args[0], true,
11266 operatorList, cacheKey, imageCache);
11270 args[0] = self.handleText(args[0], stateManager.state);
11272 case OPS.showSpacedText:
11274 var combinedGlyphs = [];
11275 var arrLength = arr.length;
11276 for (i = 0; i < arrLength; ++i) {
11277 var arrItem = arr[i];
11278 if (isString(arrItem)) {
11279 Array.prototype.push.apply(combinedGlyphs,
11280 self.handleText(arrItem, stateManager.state));
11281 } else if (isNum(arrItem)) {
11282 combinedGlyphs.push(arrItem);
11285 args[0] = combinedGlyphs;
11288 case OPS.nextLineShowText:
11289 operatorList.addOp(OPS.nextLine);
11290 args[0] = self.handleText(args[0], stateManager.state);
11293 case OPS.nextLineSetSpacingShowText:
11294 operatorList.addOp(OPS.nextLine);
11295 operatorList.addOp(OPS.setWordSpacing, [args.shift()]);
11296 operatorList.addOp(OPS.setCharSpacing, [args.shift()]);
11297 args[0] = self.handleText(args[0], stateManager.state);
11300 case OPS.setTextRenderingMode:
11301 stateManager.state.textRenderingMode = args[0];
11304 case OPS.setFillColorSpace:
11305 stateManager.state.fillColorSpace =
11306 ColorSpace.parse(args[0], xref, resources);
11308 case OPS.setStrokeColorSpace:
11309 stateManager.state.strokeColorSpace =
11310 ColorSpace.parse(args[0], xref, resources);
11312 case OPS.setFillColor:
11313 cs = stateManager.state.fillColorSpace;
11314 args = cs.getRgb(args, 0);
11315 fn = OPS.setFillRGBColor;
11317 case OPS.setStrokeColor:
11318 cs = stateManager.state.strokeColorSpace;
11319 args = cs.getRgb(args, 0);
11320 fn = OPS.setStrokeRGBColor;
11322 case OPS.setFillGray:
11323 stateManager.state.fillColorSpace = ColorSpace.singletons.gray;
11324 args = ColorSpace.singletons.gray.getRgb(args, 0);
11325 fn = OPS.setFillRGBColor;
11327 case OPS.setStrokeGray:
11328 stateManager.state.strokeColorSpace = ColorSpace.singletons.gray;
11329 args = ColorSpace.singletons.gray.getRgb(args, 0);
11330 fn = OPS.setStrokeRGBColor;
11332 case OPS.setFillCMYKColor:
11333 stateManager.state.fillColorSpace = ColorSpace.singletons.cmyk;
11334 args = ColorSpace.singletons.cmyk.getRgb(args, 0);
11335 fn = OPS.setFillRGBColor;
11337 case OPS.setStrokeCMYKColor:
11338 stateManager.state.strokeColorSpace = ColorSpace.singletons.cmyk;
11339 args = ColorSpace.singletons.cmyk.getRgb(args, 0);
11340 fn = OPS.setStrokeRGBColor;
11342 case OPS.setFillRGBColor:
11343 stateManager.state.fillColorSpace = ColorSpace.singletons.rgb;
11344 args = ColorSpace.singletons.rgb.getRgb(args, 0);
11346 case OPS.setStrokeRGBColor:
11347 stateManager.state.strokeColorSpace = ColorSpace.singletons.rgb;
11348 args = ColorSpace.singletons.rgb.getRgb(args, 0);
11350 case OPS.setFillColorN:
11351 cs = stateManager.state.fillColorSpace;
11352 if (cs.name === 'Pattern') {
11353 return self.handleColorN(operatorList, OPS.setFillColorN,
11354 args, cs, patterns, resources, xref).then(function() {
11355 next(resolve, reject);
11358 args = cs.getRgb(args, 0);
11359 fn = OPS.setFillRGBColor;
11361 case OPS.setStrokeColorN:
11362 cs = stateManager.state.strokeColorSpace;
11363 if (cs.name === 'Pattern') {
11364 return self.handleColorN(operatorList, OPS.setStrokeColorN,
11365 args, cs, patterns, resources, xref).then(function() {
11366 next(resolve, reject);
11369 args = cs.getRgb(args, 0);
11370 fn = OPS.setStrokeRGBColor;
11373 case OPS.shadingFill:
11374 var shadingRes = resources.get('Shading');
11376 error('No shading resource found');
11379 var shading = shadingRes.get(args[0].name);
11381 error('No shading object found');
11384 var shadingFill = Pattern.parseShading(shading, null, xref,
11386 var patternIR = shadingFill.getIR();
11387 args = [patternIR];
11388 fn = OPS.shadingFill;
11390 case OPS.setGState:
11391 var dictName = args[0];
11392 var extGState = resources.get('ExtGState');
11394 if (!isDict(extGState) || !extGState.has(dictName.name)) {
11398 var gState = extGState.get(dictName.name);
11399 return self.setGState(resources, gState, operatorList, xref,
11400 stateManager).then(function() {
11401 next(resolve, reject);
11408 case OPS.closePath:
11409 self.buildPath(operatorList, fn, args);
11411 case OPS.rectangle:
11412 self.buildPath(operatorList, fn, args);
11415 operatorList.addOp(fn, args);
11418 deferred.then(function () {
11419 next(resolve, reject);
11423 // Some PDFs don't close all restores inside object/form.
11424 // Closing those for them.
11425 for (i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) {
11426 operatorList.addOp(OPS.restore, []);
11432 getTextContent: function PartialEvaluator_getTextContent(stream, resources,
11435 stateManager = (stateManager || new StateManager(new TextState()));
11437 var textContent = {
11439 styles: Object.create(null)
11441 var bidiTexts = textContent.items;
11442 var SPACE_FACTOR = 0.3;
11443 var MULTI_SPACE_FACTOR = 1.5;
11446 var xref = this.xref;
11448 resources = (xref.fetchIfRef(resources) || Dict.empty);
11450 // The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd.
11452 var xobjsCache = {};
11454 var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
11458 function newTextChunk() {
11459 var font = textState.font;
11460 if (!(font.loadedName in textContent.styles)) {
11461 textContent.styles[font.loadedName] = {
11462 fontFamily: font.fallbackName,
11463 ascent: font.ascent,
11464 descent: font.descent,
11465 vertical: font.vertical
11469 // |str| is initially an array which we push individual chars to, and
11470 // then runBidi() overwrites it with the final string.
11476 fontName: font.loadedName
11480 function runBidi(textChunk) {
11481 var str = textChunk.str.join('');
11482 var bidiResult = PDFJS.bidi(str, -1, textState.font.vertical);
11483 textChunk.str = bidiResult.str;
11484 textChunk.dir = bidiResult.dir;
11488 function handleSetFont(fontName, fontRef) {
11489 return self.loadFont(fontName, fontRef, xref, resources).
11490 then(function (translated) {
11491 textState.font = translated.font;
11492 textState.fontMatrix = translated.font.fontMatrix ||
11493 FONT_IDENTITY_MATRIX;
11497 function buildTextGeometry(chars, textChunk) {
11498 var font = textState.font;
11499 textChunk = textChunk || newTextChunk();
11500 if (!textChunk.transform) {
11501 // 9.4.4 Text Space Details
11502 var tsm = [textState.fontSize * textState.textHScale, 0,
11503 0, textState.fontSize,
11504 0, textState.textRise];
11506 if (font.isType3Font &&
11507 textState.fontMatrix !== FONT_IDENTITY_MATRIX &&
11508 textState.fontSize === 1) {
11509 var glyphHeight = font.bbox[3] - font.bbox[1];
11510 if (glyphHeight > 0) {
11511 glyphHeight = glyphHeight * textState.fontMatrix[3];
11512 tsm[3] *= glyphHeight;
11516 var trm = textChunk.transform = Util.transform(textState.ctm,
11517 Util.transform(textState.textMatrix, tsm));
11518 if (!font.vertical) {
11519 textChunk.height = Math.sqrt(trm[2] * trm[2] + trm[3] * trm[3]);
11521 textChunk.width = Math.sqrt(trm[0] * trm[0] + trm[1] * trm[1]);
11526 var glyphs = font.charsToGlyphs(chars);
11527 var defaultVMetrics = font.defaultVMetrics;
11528 for (var i = 0; i < glyphs.length; i++) {
11529 var glyph = glyphs[i];
11530 if (!glyph) { // Previous glyph was a space.
11531 width += textState.wordSpacing * textState.textHScale;
11534 var vMetricX = null;
11535 var vMetricY = null;
11536 var glyphWidth = null;
11537 if (font.vertical) {
11538 if (glyph.vmetric) {
11539 glyphWidth = glyph.vmetric[0];
11540 vMetricX = glyph.vmetric[1];
11541 vMetricY = glyph.vmetric[2];
11543 glyphWidth = glyph.width;
11544 vMetricX = glyph.width * 0.5;
11545 vMetricY = defaultVMetrics[2];
11548 glyphWidth = glyph.width;
11551 var glyphUnicode = glyph.unicode;
11552 if (NormalizedUnicodes[glyphUnicode] !== undefined) {
11553 glyphUnicode = NormalizedUnicodes[glyphUnicode];
11555 glyphUnicode = reverseIfRtl(glyphUnicode);
11557 // The following will calculate the x and y of the individual glyphs.
11558 // if (font.vertical) {
11559 // tsm[4] -= vMetricX * Math.abs(textState.fontSize) *
11560 // textState.fontMatrix[0];
11561 // tsm[5] -= vMetricY * textState.fontSize *
11562 // textState.fontMatrix[0];
11564 // var trm = Util.transform(textState.textMatrix, tsm);
11565 // var pt = Util.applyTransform([trm[4], trm[5]], textState.ctm);
11569 var charSpacing = 0;
11570 if (textChunk.str.length > 0) {
11571 // Apply char spacing only when there are chars.
11572 // As a result there is only spacing between glyphs.
11573 charSpacing = textState.charSpacing;
11578 if (!font.vertical) {
11579 var w0 = glyphWidth * textState.fontMatrix[0];
11580 tx = (w0 * textState.fontSize + charSpacing) *
11581 textState.textHScale;
11584 var w1 = glyphWidth * textState.fontMatrix[0];
11585 ty = w1 * textState.fontSize + charSpacing;
11588 textState.translateTextMatrix(tx, ty);
11590 textChunk.str.push(glyphUnicode);
11593 var a = textState.textLineMatrix[0];
11594 var b = textState.textLineMatrix[1];
11595 var scaleLineX = Math.sqrt(a * a + b * b);
11596 a = textState.ctm[0];
11597 b = textState.ctm[1];
11598 var scaleCtmX = Math.sqrt(a * a + b * b);
11599 if (!font.vertical) {
11600 textChunk.width += width * scaleCtmX * scaleLineX;
11602 textChunk.height += Math.abs(height * scaleCtmX * scaleLineX);
11607 var timeSlotManager = new TimeSlotManager();
11609 return new Promise(function next(resolve, reject) {
11610 timeSlotManager.reset();
11611 var stop, operation = {}, args = [];
11612 while (!(stop = timeSlotManager.check())) {
11613 // The arguments parsed by read() are not used beyond this loop, so
11614 // we can reuse the same array on every iteration, thus avoiding
11615 // unnecessary allocations.
11617 operation.args = args;
11618 if (!(preprocessor.read(operation))) {
11621 textState = stateManager.state;
11622 var fn = operation.fn;
11623 args = operation.args;
11627 textState.fontSize = args[1];
11628 return handleSetFont(args[0].name).then(function() {
11629 next(resolve, reject);
11631 case OPS.setTextRise:
11632 textState.textRise = args[0];
11634 case OPS.setHScale:
11635 textState.textHScale = args[0] / 100;
11637 case OPS.setLeading:
11638 textState.leading = args[0];
11641 textState.translateTextLineMatrix(args[0], args[1]);
11642 textState.textMatrix = textState.textLineMatrix.slice();
11644 case OPS.setLeadingMoveText:
11645 textState.leading = -args[1];
11646 textState.translateTextLineMatrix(args[0], args[1]);
11647 textState.textMatrix = textState.textLineMatrix.slice();
11650 textState.carriageReturn();
11652 case OPS.setTextMatrix:
11653 textState.setTextMatrix(args[0], args[1], args[2], args[3],
11655 textState.setTextLineMatrix(args[0], args[1], args[2], args[3],
11658 case OPS.setCharSpacing:
11659 textState.charSpacing = args[0];
11661 case OPS.setWordSpacing:
11662 textState.wordSpacing = args[0];
11664 case OPS.beginText:
11665 textState.textMatrix = IDENTITY_MATRIX.slice();
11666 textState.textLineMatrix = IDENTITY_MATRIX.slice();
11668 case OPS.showSpacedText:
11669 var items = args[0];
11670 var textChunk = newTextChunk();
11672 for (var j = 0, jj = items.length; j < jj; j++) {
11673 if (typeof items[j] === 'string') {
11674 buildTextGeometry(items[j], textChunk);
11676 var val = items[j] / 1000;
11677 if (!textState.font.vertical) {
11678 offset = -val * textState.fontSize * textState.textHScale *
11679 textState.textMatrix[0];
11680 textState.translateTextMatrix(offset, 0);
11681 textChunk.width += offset;
11683 offset = -val * textState.fontSize *
11684 textState.textMatrix[3];
11685 textState.translateTextMatrix(0, offset);
11686 textChunk.height += offset;
11688 if (items[j] < 0 && textState.font.spaceWidth > 0) {
11689 var fakeSpaces = -items[j] / textState.font.spaceWidth;
11690 if (fakeSpaces > MULTI_SPACE_FACTOR) {
11691 fakeSpaces = Math.round(fakeSpaces);
11692 while (fakeSpaces--) {
11693 textChunk.str.push(' ');
11695 } else if (fakeSpaces > SPACE_FACTOR) {
11696 textChunk.str.push(' ');
11701 bidiTexts.push(runBidi(textChunk));
11704 bidiTexts.push(runBidi(buildTextGeometry(args[0])));
11706 case OPS.nextLineShowText:
11707 textState.carriageReturn();
11708 bidiTexts.push(runBidi(buildTextGeometry(args[0])));
11710 case OPS.nextLineSetSpacingShowText:
11711 textState.wordSpacing = args[0];
11712 textState.charSpacing = args[1];
11713 textState.carriageReturn();
11714 bidiTexts.push(runBidi(buildTextGeometry(args[2])));
11716 case OPS.paintXObject:
11717 if (args[0].code) {
11722 xobjs = (resources.get('XObject') || Dict.empty);
11725 var name = args[0].name;
11726 if (xobjsCache.key === name) {
11727 if (xobjsCache.texts) {
11728 Util.appendToArray(bidiTexts, xobjsCache.texts.items);
11729 Util.extendObj(textContent.styles, xobjsCache.texts.styles);
11734 var xobj = xobjs.get(name);
11738 assert(isStream(xobj), 'XObject should be a stream');
11740 var type = xobj.dict.get('Subtype');
11741 assert(isName(type),
11742 'XObject should have a Name subtype');
11744 if ('Form' !== type.name) {
11745 xobjsCache.key = name;
11746 xobjsCache.texts = null;
11750 stateManager.save();
11751 var matrix = xobj.dict.get('Matrix');
11752 if (isArray(matrix) && matrix.length === 6) {
11753 stateManager.transform(matrix);
11756 return self.getTextContent(xobj,
11757 xobj.dict.get('Resources') || resources, stateManager).
11758 then(function (formTextContent) {
11759 Util.appendToArray(bidiTexts, formTextContent.items);
11760 Util.extendObj(textContent.styles, formTextContent.styles);
11761 stateManager.restore();
11763 xobjsCache.key = name;
11764 xobjsCache.texts = formTextContent;
11766 next(resolve, reject);
11768 case OPS.setGState:
11769 var dictName = args[0];
11770 var extGState = resources.get('ExtGState');
11772 if (!isDict(extGState) || !extGState.has(dictName.name)) {
11776 var gsStateMap = extGState.get(dictName.name);
11777 var gsStateFont = null;
11778 for (var key in gsStateMap) {
11779 if (key === 'Font') {
11780 assert(!gsStateFont);
11781 gsStateFont = gsStateMap[key];
11785 textState.fontSize = gsStateFont[1];
11786 return handleSetFont(gsStateFont[0]).then(function() {
11787 next(resolve, reject);
11794 deferred.then(function () {
11795 next(resolve, reject);
11799 resolve(textContent);
11803 extractDataStructures: function
11804 partialEvaluatorExtractDataStructures(dict, baseDict,
11805 xref, properties) {
11807 var toUnicode = (dict.get('ToUnicode') || baseDict.get('ToUnicode'));
11809 properties.toUnicode = this.readToUnicode(toUnicode);
11811 if (properties.composite) {
11812 // CIDSystemInfo helps to match CID to glyphs
11813 var cidSystemInfo = dict.get('CIDSystemInfo');
11814 if (isDict(cidSystemInfo)) {
11815 properties.cidSystemInfo = {
11816 registry: cidSystemInfo.get('Registry'),
11817 ordering: cidSystemInfo.get('Ordering'),
11818 supplement: cidSystemInfo.get('Supplement')
11822 var cidToGidMap = dict.get('CIDToGIDMap');
11823 if (isStream(cidToGidMap)) {
11824 properties.cidToGidMap = this.readCidToGidMap(cidToGidMap);
11828 // Based on 9.6.6 of the spec the encoding can come from multiple places
11829 // and depends on the font type. The base encoding and differences are
11830 // read here, but the encoding that is actually used is chosen during
11831 // glyph mapping in the font.
11832 // TODO: Loading the built in encoding in the font would allow the
11833 // differences to be merged in here not require us to hold on to it.
11834 var differences = [];
11835 var baseEncodingName = null;
11837 if (dict.has('Encoding')) {
11838 encoding = dict.get('Encoding');
11839 if (isDict(encoding)) {
11840 baseEncodingName = encoding.get('BaseEncoding');
11841 baseEncodingName = (isName(baseEncodingName) ?
11842 baseEncodingName.name : null);
11843 // Load the differences between the base and original
11844 if (encoding.has('Differences')) {
11845 var diffEncoding = encoding.get('Differences');
11847 for (var j = 0, jj = diffEncoding.length; j < jj; j++) {
11848 var data = diffEncoding[j];
11851 } else if (isName(data)) {
11852 differences[index++] = data.name;
11853 } else if (isRef(data)) {
11854 diffEncoding[j--] = xref.fetch(data);
11857 error('Invalid entry in \'Differences\' array: ' + data);
11861 } else if (isName(encoding)) {
11862 baseEncodingName = encoding.name;
11864 error('Encoding is not a Name nor a Dict');
11866 // According to table 114 if the encoding is a named encoding it must be
11867 // one of these predefined encodings.
11868 if ((baseEncodingName !== 'MacRomanEncoding' &&
11869 baseEncodingName !== 'MacExpertEncoding' &&
11870 baseEncodingName !== 'WinAnsiEncoding')) {
11871 baseEncodingName = null;
11875 if (baseEncodingName) {
11876 properties.defaultEncoding = Encodings[baseEncodingName].slice();
11878 encoding = (properties.type === 'TrueType' ?
11879 Encodings.WinAnsiEncoding : Encodings.StandardEncoding);
11880 // The Symbolic attribute can be misused for regular fonts
11881 // Heuristic: we have to check if the font is a standard one also
11882 if (!!(properties.flags & FontFlags.Symbolic)) {
11883 encoding = Encodings.MacRomanEncoding;
11884 if (!properties.file) {
11885 if (/Symbol/i.test(properties.name)) {
11886 encoding = Encodings.SymbolSetEncoding;
11887 } else if (/Dingbats/i.test(properties.name)) {
11888 encoding = Encodings.ZapfDingbatsEncoding;
11892 properties.defaultEncoding = encoding;
11895 properties.differences = differences;
11896 properties.baseEncodingName = baseEncodingName;
11897 properties.dict = dict;
11900 readToUnicode: function PartialEvaluator_readToUnicode(toUnicode) {
11901 var cmap, cmapObj = toUnicode;
11902 if (isName(cmapObj)) {
11903 cmap = CMapFactory.create(cmapObj,
11904 { url: PDFJS.cMapUrl, packed: PDFJS.cMapPacked }, null);
11905 if (cmap instanceof IdentityCMap) {
11906 return new IdentityToUnicodeMap(0, 0xFFFF);
11908 return new ToUnicodeMap(cmap.getMap());
11909 } else if (isStream(cmapObj)) {
11910 cmap = CMapFactory.create(cmapObj,
11911 { url: PDFJS.cMapUrl, packed: PDFJS.cMapPacked }, null);
11912 if (cmap instanceof IdentityCMap) {
11913 return new IdentityToUnicodeMap(0, 0xFFFF);
11915 cmap = cmap.getMap();
11916 // Convert UTF-16BE
11917 // NOTE: cmap can be a sparse array, so use forEach instead of for(;;)
11918 // to iterate over all keys.
11919 cmap.forEach(function(token, i) {
11921 for (var k = 0; k < token.length; k += 2) {
11922 var w1 = (token.charCodeAt(k) << 8) | token.charCodeAt(k + 1);
11923 if ((w1 & 0xF800) !== 0xD800) { // w1 < 0xD800 || w1 > 0xDFFF
11928 var w2 = (token.charCodeAt(k) << 8) | token.charCodeAt(k + 1);
11929 str.push(((w1 & 0x3ff) << 10) + (w2 & 0x3ff) + 0x10000);
11931 cmap[i] = String.fromCharCode.apply(String, str);
11933 return new ToUnicodeMap(cmap);
11938 readCidToGidMap: function PartialEvaluator_readCidToGidMap(cidToGidStream) {
11939 // Extract the encoding from the CIDToGIDMap
11940 var glyphsData = cidToGidStream.getBytes();
11942 // Set encoding 0 to later verify the font has an encoding
11944 for (var j = 0, jj = glyphsData.length; j < jj; j++) {
11945 var glyphID = (glyphsData[j++] << 8) | glyphsData[j];
11946 if (glyphID === 0) {
11950 result[code] = glyphID;
11955 extractWidths: function PartialEvaluator_extractWidths(dict, xref,
11958 var glyphsWidths = [];
11959 var defaultWidth = 0;
11960 var glyphsVMetrics = [];
11961 var defaultVMetrics;
11962 var i, ii, j, jj, start, code, widths;
11963 if (properties.composite) {
11964 defaultWidth = dict.get('DW') || 1000;
11966 widths = dict.get('W');
11968 for (i = 0, ii = widths.length; i < ii; i++) {
11969 start = widths[i++];
11970 code = xref.fetchIfRef(widths[i]);
11971 if (isArray(code)) {
11972 for (j = 0, jj = code.length; j < jj; j++) {
11973 glyphsWidths[start++] = code[j];
11976 var width = widths[++i];
11977 for (j = start; j <= code; j++) {
11978 glyphsWidths[j] = width;
11984 if (properties.vertical) {
11985 var vmetrics = (dict.get('DW2') || [880, -1000]);
11986 defaultVMetrics = [vmetrics[1], defaultWidth * 0.5, vmetrics[0]];
11987 vmetrics = dict.get('W2');
11989 for (i = 0, ii = vmetrics.length; i < ii; i++) {
11990 start = vmetrics[i++];
11991 code = xref.fetchIfRef(vmetrics[i]);
11992 if (isArray(code)) {
11993 for (j = 0, jj = code.length; j < jj; j++) {
11994 glyphsVMetrics[start++] = [code[j++], code[j++], code[j]];
11997 var vmetric = [vmetrics[++i], vmetrics[++i], vmetrics[++i]];
11998 for (j = start; j <= code; j++) {
11999 glyphsVMetrics[j] = vmetric;
12006 var firstChar = properties.firstChar;
12007 widths = dict.get('Widths');
12010 for (i = 0, ii = widths.length; i < ii; i++) {
12011 glyphsWidths[j++] = widths[i];
12013 defaultWidth = (parseFloat(descriptor.get('MissingWidth')) || 0);
12015 // Trying get the BaseFont metrics (see comment above).
12016 var baseFontName = dict.get('BaseFont');
12017 if (isName(baseFontName)) {
12018 var metrics = this.getBaseFontMetrics(baseFontName.name);
12020 glyphsWidths = this.buildCharCodeToWidth(metrics.widths,
12022 defaultWidth = metrics.defaultWidth;
12027 // Heuristic: detection of monospace font by checking all non-zero widths
12028 var isMonospace = true;
12029 var firstWidth = defaultWidth;
12030 for (var glyph in glyphsWidths) {
12031 var glyphWidth = glyphsWidths[glyph];
12036 firstWidth = glyphWidth;
12039 if (firstWidth !== glyphWidth) {
12040 isMonospace = false;
12045 properties.flags |= FontFlags.FixedPitch;
12048 properties.defaultWidth = defaultWidth;
12049 properties.widths = glyphsWidths;
12050 properties.defaultVMetrics = defaultVMetrics;
12051 properties.vmetrics = glyphsVMetrics;
12054 isSerifFont: function PartialEvaluator_isSerifFont(baseFontName) {
12055 // Simulating descriptor flags attribute
12056 var fontNameWoStyle = baseFontName.split('-')[0];
12057 return (fontNameWoStyle in serifFonts) ||
12058 (fontNameWoStyle.search(/serif/gi) !== -1);
12061 getBaseFontMetrics: function PartialEvaluator_getBaseFontMetrics(name) {
12062 var defaultWidth = 0;
12064 var monospace = false;
12065 var lookupName = (stdFontMap[name] || name);
12067 if (!(lookupName in Metrics)) {
12068 // Use default fonts for looking up font metrics if the passed
12069 // font is not a base font
12070 if (this.isSerifFont(name)) {
12071 lookupName = 'Times-Roman';
12073 lookupName = 'Helvetica';
12076 var glyphWidths = Metrics[lookupName];
12078 if (isNum(glyphWidths)) {
12079 defaultWidth = glyphWidths;
12082 widths = glyphWidths;
12086 defaultWidth: defaultWidth,
12087 monospace: monospace,
12092 buildCharCodeToWidth:
12093 function PartialEvaluator_bulildCharCodeToWidth(widthsByGlyphName,
12095 var widths = Object.create(null);
12096 var differences = properties.differences;
12097 var encoding = properties.defaultEncoding;
12098 for (var charCode = 0; charCode < 256; charCode++) {
12099 if (charCode in differences &&
12100 widthsByGlyphName[differences[charCode]]) {
12101 widths[charCode] = widthsByGlyphName[differences[charCode]];
12104 if (charCode in encoding && widthsByGlyphName[encoding[charCode]]) {
12105 widths[charCode] = widthsByGlyphName[encoding[charCode]];
12112 preEvaluateFont: function PartialEvaluator_preEvaluateFont(dict, xref) {
12113 var baseDict = dict;
12114 var type = dict.get('Subtype');
12115 assert(isName(type), 'invalid font Subtype');
12117 var composite = false;
12119 if (type.name === 'Type0') {
12120 // If font is a composite
12121 // - get the descendant font
12122 // - set the type according to the descendant font
12123 // - get the FontDescriptor from the descendant font
12124 var df = dict.get('DescendantFonts');
12126 error('Descendant fonts are not specified');
12128 dict = (isArray(df) ? xref.fetchIfRef(df[0]) : df);
12130 type = dict.get('Subtype');
12131 assert(isName(type), 'invalid font Subtype');
12135 var descriptor = dict.get('FontDescriptor');
12137 var hash = new MurmurHash3_64();
12138 var encoding = baseDict.getRaw('Encoding');
12139 if (isName(encoding)) {
12140 hash.update(encoding.name);
12141 } else if (isRef(encoding)) {
12142 hash.update(encoding.num + '_' + encoding.gen);
12143 } else if (isDict(encoding)) {
12144 var keys = encoding.getKeys();
12145 for (var i = 0, ii = keys.length; i < ii; i++) {
12146 var entry = encoding.getRaw(keys[i]);
12147 if (isName(entry)) {
12148 hash.update(entry.name);
12149 } else if (isRef(entry)) {
12150 hash.update(entry.num + '_' + entry.gen);
12151 } else if (isArray(entry)) { // 'Differences' entry.
12152 // Ideally we should check the contents of the array, but to avoid
12153 // parsing it here and then again in |extractDataStructures|,
12154 // we only use the array length for now (fixes bug1157493.pdf).
12155 hash.update(entry.length.toString());
12160 var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode');
12161 if (isStream(toUnicode)) {
12162 var stream = toUnicode.str || toUnicode;
12163 uint8array = stream.buffer ?
12164 new Uint8Array(stream.buffer.buffer, 0, stream.bufferLength) :
12165 new Uint8Array(stream.bytes.buffer,
12166 stream.start, stream.end - stream.start);
12167 hash.update(uint8array);
12169 } else if (isName(toUnicode)) {
12170 hash.update(toUnicode.name);
12173 var widths = dict.get('Widths') || baseDict.get('Widths');
12175 uint8array = new Uint8Array(new Uint32Array(widths).buffer);
12176 hash.update(uint8array);
12181 descriptor: descriptor,
12183 baseDict: baseDict,
12184 composite: composite,
12186 hash: hash ? hash.hexdigest() : ''
12190 translateFont: function PartialEvaluator_translateFont(preEvaluatedFont,
12192 var baseDict = preEvaluatedFont.baseDict;
12193 var dict = preEvaluatedFont.dict;
12194 var composite = preEvaluatedFont.composite;
12195 var descriptor = preEvaluatedFont.descriptor;
12196 var type = preEvaluatedFont.type;
12197 var maxCharIndex = (composite ? 0xFFFF : 0xFF);
12201 if (type === 'Type3') {
12202 // FontDescriptor is only required for Type3 fonts when the document
12203 // is a tagged pdf. Create a barbebones one to get by.
12204 descriptor = new Dict(null);
12205 descriptor.set('FontName', Name.get(type));
12206 descriptor.set('FontBBox', dict.get('FontBBox'));
12208 // Before PDF 1.5 if the font was one of the base 14 fonts, having a
12209 // FontDescriptor was not required.
12210 // This case is here for compatibility.
12211 var baseFontName = dict.get('BaseFont');
12212 if (!isName(baseFontName)) {
12213 error('Base font is not specified');
12216 // Using base font name as a font name.
12217 baseFontName = baseFontName.name.replace(/[,_]/g, '-');
12218 var metrics = this.getBaseFontMetrics(baseFontName);
12220 // Simulating descriptor flags attribute
12221 var fontNameWoStyle = baseFontName.split('-')[0];
12223 (this.isSerifFont(fontNameWoStyle) ? FontFlags.Serif : 0) |
12224 (metrics.monospace ? FontFlags.FixedPitch : 0) |
12225 (symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic :
12226 FontFlags.Nonsymbolic);
12230 name: baseFontName,
12231 widths: metrics.widths,
12232 defaultWidth: metrics.defaultWidth,
12235 lastChar: maxCharIndex
12237 this.extractDataStructures(dict, dict, xref, properties);
12238 properties.widths = this.buildCharCodeToWidth(metrics.widths,
12240 return new Font(baseFontName, null, properties);
12244 // According to the spec if 'FontDescriptor' is declared, 'FirstChar',
12245 // 'LastChar' and 'Widths' should exist too, but some PDF encoders seem
12246 // to ignore this rule when a variant of a standart font is used.
12247 // TODO Fill the width array depending on which of the base font this is
12249 var firstChar = (dict.get('FirstChar') || 0);
12250 var lastChar = (dict.get('LastChar') || maxCharIndex);
12252 var fontName = descriptor.get('FontName');
12253 var baseFont = dict.get('BaseFont');
12254 // Some bad PDFs have a string as the font name.
12255 if (isString(fontName)) {
12256 fontName = Name.get(fontName);
12258 if (isString(baseFont)) {
12259 baseFont = Name.get(baseFont);
12262 if (type !== 'Type3') {
12263 var fontNameStr = fontName && fontName.name;
12264 var baseFontStr = baseFont && baseFont.name;
12265 if (fontNameStr !== baseFontStr) {
12266 info('The FontDescriptor\'s FontName is "' + fontNameStr +
12267 '" but should be the same as the Font\'s BaseFont "' +
12268 baseFontStr + '"');
12269 // Workaround for cases where e.g. fontNameStr = 'Arial' and
12270 // baseFontStr = 'Arial,Bold' (needed when no font file is embedded).
12271 if (fontNameStr && baseFontStr &&
12272 baseFontStr.indexOf(fontNameStr) === 0) {
12273 fontName = baseFont;
12277 fontName = (fontName || baseFont);
12279 assert(isName(fontName), 'invalid font name');
12281 var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3');
12283 if (fontFile.dict) {
12284 var subtype = fontFile.dict.get('Subtype');
12286 subtype = subtype.name;
12288 var length1 = fontFile.dict.get('Length1');
12289 var length2 = fontFile.dict.get('Length2');
12295 name: fontName.name,
12300 loadedName: baseDict.loadedName,
12301 composite: composite,
12302 wideChars: composite,
12304 fontMatrix: (dict.get('FontMatrix') || FONT_IDENTITY_MATRIX),
12305 firstChar: firstChar || 0,
12306 lastChar: (lastChar || maxCharIndex),
12307 bbox: descriptor.get('FontBBox'),
12308 ascent: descriptor.get('Ascent'),
12309 descent: descriptor.get('Descent'),
12310 xHeight: descriptor.get('XHeight'),
12311 capHeight: descriptor.get('CapHeight'),
12312 flags: descriptor.get('Flags'),
12313 italicAngle: descriptor.get('ItalicAngle'),
12318 var cidEncoding = baseDict.get('Encoding');
12319 if (isName(cidEncoding)) {
12320 properties.cidEncoding = cidEncoding.name;
12322 properties.cMap = CMapFactory.create(cidEncoding,
12323 { url: PDFJS.cMapUrl, packed: PDFJS.cMapPacked }, null);
12324 properties.vertical = properties.cMap.vertical;
12326 this.extractDataStructures(dict, baseDict, xref, properties);
12327 this.extractWidths(dict, xref, descriptor, properties);
12329 if (type === 'Type3') {
12330 properties.isType3Font = true;
12333 return new Font(fontName.name, fontFile, properties);
12337 return PartialEvaluator;
12340 var TranslatedFont = (function TranslatedFontClosure() {
12341 function TranslatedFont(loadedName, font, dict) {
12342 this.loadedName = loadedName;
12345 this.type3Loaded = null;
12348 TranslatedFont.prototype = {
12349 send: function (handler) {
12353 var fontData = this.font.exportData();
12354 handler.send('commonobj', [
12361 loadType3Data: function (evaluator, resources, parentOperatorList) {
12362 assert(this.font.isType3Font);
12364 if (this.type3Loaded) {
12365 return this.type3Loaded;
12368 var translatedFont = this.font;
12369 var loadCharProcsPromise = Promise.resolve();
12370 var charProcs = this.dict.get('CharProcs').getAll();
12371 var fontResources = this.dict.get('Resources') || resources;
12372 var charProcKeys = Object.keys(charProcs);
12373 var charProcOperatorList = {};
12374 for (var i = 0, n = charProcKeys.length; i < n; ++i) {
12375 loadCharProcsPromise = loadCharProcsPromise.then(function (key) {
12376 var glyphStream = charProcs[key];
12377 var operatorList = new OperatorList();
12378 return evaluator.getOperatorList(glyphStream, fontResources,
12379 operatorList).then(function () {
12380 charProcOperatorList[key] = operatorList.getIR();
12382 // Add the dependencies to the parent operator list so they are
12383 // resolved before sub operator list is executed synchronously.
12384 parentOperatorList.addDependencies(operatorList.dependencies);
12385 }, function (reason) {
12386 warn('Type3 font resource \"' + key + '\" is not available');
12387 var operatorList = new OperatorList();
12388 charProcOperatorList[key] = operatorList.getIR();
12390 }.bind(this, charProcKeys[i]));
12392 this.type3Loaded = loadCharProcsPromise.then(function () {
12393 translatedFont.charProcOperatorList = charProcOperatorList;
12395 return this.type3Loaded;
12398 return TranslatedFont;
12401 var OperatorList = (function OperatorListClosure() {
12402 var CHUNK_SIZE = 1000;
12403 var CHUNK_SIZE_ABOUT = CHUNK_SIZE - 5; // close to chunk size
12405 function getTransfers(queue) {
12406 var transfers = [];
12407 var fnArray = queue.fnArray, argsArray = queue.argsArray;
12408 for (var i = 0, ii = queue.length; i < ii; i++) {
12409 switch (fnArray[i]) {
12410 case OPS.paintInlineImageXObject:
12411 case OPS.paintInlineImageXObjectGroup:
12412 case OPS.paintImageMaskXObject:
12413 var arg = argsArray[i][0]; // first param in imgData
12415 transfers.push(arg.data.buffer);
12423 function OperatorList(intent, messageHandler, pageIndex) {
12424 this.messageHandler = messageHandler;
12426 this.argsArray = [];
12427 this.dependencies = {};
12428 this.pageIndex = pageIndex;
12429 this.intent = intent;
12432 OperatorList.prototype = {
12434 return this.argsArray.length;
12437 addOp: function(fn, args) {
12438 this.fnArray.push(fn);
12439 this.argsArray.push(args);
12440 if (this.messageHandler) {
12441 if (this.fnArray.length >= CHUNK_SIZE) {
12443 } else if (this.fnArray.length >= CHUNK_SIZE_ABOUT &&
12444 (fn === OPS.restore || fn === OPS.endText)) {
12445 // heuristic to flush on boundary of restore or endText
12451 addDependency: function(dependency) {
12452 if (dependency in this.dependencies) {
12455 this.dependencies[dependency] = true;
12456 this.addOp(OPS.dependency, [dependency]);
12459 addDependencies: function(dependencies) {
12460 for (var key in dependencies) {
12461 this.addDependency(key);
12465 addOpList: function(opList) {
12466 Util.extendObj(this.dependencies, opList.dependencies);
12467 for (var i = 0, ii = opList.length; i < ii; i++) {
12468 this.addOp(opList.fnArray[i], opList.argsArray[i]);
12472 getIR: function() {
12474 fnArray: this.fnArray,
12475 argsArray: this.argsArray,
12476 length: this.length
12480 flush: function(lastChunk) {
12481 if (this.intent !== 'oplist') {
12482 new QueueOptimizer().optimize(this);
12484 var transfers = getTransfers(this);
12485 this.messageHandler.send('RenderPageChunk', {
12487 fnArray: this.fnArray,
12488 argsArray: this.argsArray,
12489 lastChunk: lastChunk,
12490 length: this.length
12492 pageIndex: this.pageIndex,
12493 intent: this.intent
12495 this.dependencies = {};
12496 this.fnArray.length = 0;
12497 this.argsArray.length = 0;
12501 return OperatorList;
12504 var StateManager = (function StateManagerClosure() {
12505 function StateManager(initialState) {
12506 this.state = initialState;
12507 this.stateStack = [];
12509 StateManager.prototype = {
12510 save: function () {
12511 var old = this.state;
12512 this.stateStack.push(this.state);
12513 this.state = old.clone();
12515 restore: function () {
12516 var prev = this.stateStack.pop();
12521 transform: function (args) {
12522 this.state.ctm = Util.transform(this.state.ctm, args);
12525 return StateManager;
12528 var TextState = (function TextStateClosure() {
12529 function TextState() {
12530 this.ctm = new Float32Array(IDENTITY_MATRIX);
12533 this.fontMatrix = FONT_IDENTITY_MATRIX;
12534 this.textMatrix = IDENTITY_MATRIX.slice();
12535 this.textLineMatrix = IDENTITY_MATRIX.slice();
12536 this.charSpacing = 0;
12537 this.wordSpacing = 0;
12539 this.textHScale = 1;
12543 TextState.prototype = {
12544 setTextMatrix: function TextState_setTextMatrix(a, b, c, d, e, f) {
12545 var m = this.textMatrix;
12546 m[0] = a; m[1] = b; m[2] = c; m[3] = d; m[4] = e; m[5] = f;
12548 setTextLineMatrix: function TextState_setTextMatrix(a, b, c, d, e, f) {
12549 var m = this.textLineMatrix;
12550 m[0] = a; m[1] = b; m[2] = c; m[3] = d; m[4] = e; m[5] = f;
12552 translateTextMatrix: function TextState_translateTextMatrix(x, y) {
12553 var m = this.textMatrix;
12554 m[4] = m[0] * x + m[2] * y + m[4];
12555 m[5] = m[1] * x + m[3] * y + m[5];
12557 translateTextLineMatrix: function TextState_translateTextMatrix(x, y) {
12558 var m = this.textLineMatrix;
12559 m[4] = m[0] * x + m[2] * y + m[4];
12560 m[5] = m[1] * x + m[3] * y + m[5];
12562 calcRenderMatrix: function TextState_calcRendeMatrix(ctm) {
12563 // 9.4.4 Text Space Details
12564 var tsm = [this.fontSize * this.textHScale, 0,
12567 return Util.transform(ctm, Util.transform(this.textMatrix, tsm));
12569 carriageReturn: function TextState_carriageReturn() {
12570 this.translateTextLineMatrix(0, -this.leading);
12571 this.textMatrix = this.textLineMatrix.slice();
12573 clone: function TextState_clone() {
12574 var clone = Object.create(this);
12575 clone.textMatrix = this.textMatrix.slice();
12576 clone.textLineMatrix = this.textLineMatrix.slice();
12577 clone.fontMatrix = this.fontMatrix.slice();
12584 var EvalState = (function EvalStateClosure() {
12585 function EvalState() {
12586 this.ctm = new Float32Array(IDENTITY_MATRIX);
12588 this.textRenderingMode = TextRenderingMode.FILL;
12589 this.fillColorSpace = ColorSpace.singletons.gray;
12590 this.strokeColorSpace = ColorSpace.singletons.gray;
12592 EvalState.prototype = {
12593 clone: function CanvasExtraState_clone() {
12594 return Object.create(this);
12600 var EvaluatorPreprocessor = (function EvaluatorPreprocessorClosure() {
12601 // Specifies properties for each command
12603 // If variableArgs === true: [0, `numArgs`] expected
12604 // If variableArgs === false: exactly `numArgs` expected
12607 w: { id: OPS.setLineWidth, numArgs: 1, variableArgs: false },
12608 J: { id: OPS.setLineCap, numArgs: 1, variableArgs: false },
12609 j: { id: OPS.setLineJoin, numArgs: 1, variableArgs: false },
12610 M: { id: OPS.setMiterLimit, numArgs: 1, variableArgs: false },
12611 d: { id: OPS.setDash, numArgs: 2, variableArgs: false },
12612 ri: { id: OPS.setRenderingIntent, numArgs: 1, variableArgs: false },
12613 i: { id: OPS.setFlatness, numArgs: 1, variableArgs: false },
12614 gs: { id: OPS.setGState, numArgs: 1, variableArgs: false },
12615 q: { id: OPS.save, numArgs: 0, variableArgs: false },
12616 Q: { id: OPS.restore, numArgs: 0, variableArgs: false },
12617 cm: { id: OPS.transform, numArgs: 6, variableArgs: false },
12620 m: { id: OPS.moveTo, numArgs: 2, variableArgs: false },
12621 l: { id: OPS.lineTo, numArgs: 2, variableArgs: false },
12622 c: { id: OPS.curveTo, numArgs: 6, variableArgs: false },
12623 v: { id: OPS.curveTo2, numArgs: 4, variableArgs: false },
12624 y: { id: OPS.curveTo3, numArgs: 4, variableArgs: false },
12625 h: { id: OPS.closePath, numArgs: 0, variableArgs: false },
12626 re: { id: OPS.rectangle, numArgs: 4, variableArgs: false },
12627 S: { id: OPS.stroke, numArgs: 0, variableArgs: false },
12628 s: { id: OPS.closeStroke, numArgs: 0, variableArgs: false },
12629 f: { id: OPS.fill, numArgs: 0, variableArgs: false },
12630 F: { id: OPS.fill, numArgs: 0, variableArgs: false },
12631 'f*': { id: OPS.eoFill, numArgs: 0, variableArgs: false },
12632 B: { id: OPS.fillStroke, numArgs: 0, variableArgs: false },
12633 'B*': { id: OPS.eoFillStroke, numArgs: 0, variableArgs: false },
12634 b: { id: OPS.closeFillStroke, numArgs: 0, variableArgs: false },
12635 'b*': { id: OPS.closeEOFillStroke, numArgs: 0, variableArgs: false },
12636 n: { id: OPS.endPath, numArgs: 0, variableArgs: false },
12639 W: { id: OPS.clip, numArgs: 0, variableArgs: false },
12640 'W*': { id: OPS.eoClip, numArgs: 0, variableArgs: false },
12643 BT: { id: OPS.beginText, numArgs: 0, variableArgs: false },
12644 ET: { id: OPS.endText, numArgs: 0, variableArgs: false },
12645 Tc: { id: OPS.setCharSpacing, numArgs: 1, variableArgs: false },
12646 Tw: { id: OPS.setWordSpacing, numArgs: 1, variableArgs: false },
12647 Tz: { id: OPS.setHScale, numArgs: 1, variableArgs: false },
12648 TL: { id: OPS.setLeading, numArgs: 1, variableArgs: false },
12649 Tf: { id: OPS.setFont, numArgs: 2, variableArgs: false },
12650 Tr: { id: OPS.setTextRenderingMode, numArgs: 1, variableArgs: false },
12651 Ts: { id: OPS.setTextRise, numArgs: 1, variableArgs: false },
12652 Td: { id: OPS.moveText, numArgs: 2, variableArgs: false },
12653 TD: { id: OPS.setLeadingMoveText, numArgs: 2, variableArgs: false },
12654 Tm: { id: OPS.setTextMatrix, numArgs: 6, variableArgs: false },
12655 'T*': { id: OPS.nextLine, numArgs: 0, variableArgs: false },
12656 Tj: { id: OPS.showText, numArgs: 1, variableArgs: false },
12657 TJ: { id: OPS.showSpacedText, numArgs: 1, variableArgs: false },
12658 '\'': { id: OPS.nextLineShowText, numArgs: 1, variableArgs: false },
12659 '"': { id: OPS.nextLineSetSpacingShowText, numArgs: 3,
12660 variableArgs: false },
12663 d0: { id: OPS.setCharWidth, numArgs: 2, variableArgs: false },
12664 d1: { id: OPS.setCharWidthAndBounds, numArgs: 6, variableArgs: false },
12667 CS: { id: OPS.setStrokeColorSpace, numArgs: 1, variableArgs: false },
12668 cs: { id: OPS.setFillColorSpace, numArgs: 1, variableArgs: false },
12669 SC: { id: OPS.setStrokeColor, numArgs: 4, variableArgs: true },
12670 SCN: { id: OPS.setStrokeColorN, numArgs: 33, variableArgs: true },
12671 sc: { id: OPS.setFillColor, numArgs: 4, variableArgs: true },
12672 scn: { id: OPS.setFillColorN, numArgs: 33, variableArgs: true },
12673 G: { id: OPS.setStrokeGray, numArgs: 1, variableArgs: false },
12674 g: { id: OPS.setFillGray, numArgs: 1, variableArgs: false },
12675 RG: { id: OPS.setStrokeRGBColor, numArgs: 3, variableArgs: false },
12676 rg: { id: OPS.setFillRGBColor, numArgs: 3, variableArgs: false },
12677 K: { id: OPS.setStrokeCMYKColor, numArgs: 4, variableArgs: false },
12678 k: { id: OPS.setFillCMYKColor, numArgs: 4, variableArgs: false },
12681 sh: { id: OPS.shadingFill, numArgs: 1, variableArgs: false },
12684 BI: { id: OPS.beginInlineImage, numArgs: 0, variableArgs: false },
12685 ID: { id: OPS.beginImageData, numArgs: 0, variableArgs: false },
12686 EI: { id: OPS.endInlineImage, numArgs: 1, variableArgs: false },
12689 Do: { id: OPS.paintXObject, numArgs: 1, variableArgs: false },
12690 MP: { id: OPS.markPoint, numArgs: 1, variableArgs: false },
12691 DP: { id: OPS.markPointProps, numArgs: 2, variableArgs: false },
12692 BMC: { id: OPS.beginMarkedContent, numArgs: 1, variableArgs: false },
12693 BDC: { id: OPS.beginMarkedContentProps, numArgs: 2,
12694 variableArgs: false },
12695 EMC: { id: OPS.endMarkedContent, numArgs: 0, variableArgs: false },
12698 BX: { id: OPS.beginCompat, numArgs: 0, variableArgs: false },
12699 EX: { id: OPS.endCompat, numArgs: 0, variableArgs: false },
12701 // (reserved partial commands for the lexer)
12714 function EvaluatorPreprocessor(stream, xref, stateManager) {
12715 // TODO(mduan): pass array of knownCommands rather than OP_MAP
12717 this.parser = new Parser(new Lexer(stream, OP_MAP), false, xref);
12718 this.stateManager = stateManager;
12719 this.nonProcessedArgs = [];
12722 EvaluatorPreprocessor.prototype = {
12723 get savedStatesDepth() {
12724 return this.stateManager.stateStack.length;
12727 // |operation| is an object with two fields:
12729 // - |fn| is an out param.
12731 // - |args| is an inout param. On entry, it should have one of two values.
12733 // - An empty array. This indicates that the caller is providing the
12734 // array in which the args will be stored in. The caller should use
12735 // this value if it can reuse a single array for each call to read().
12737 // - |null|. This indicates that the caller needs this function to create
12738 // the array in which any args are stored in. If there are zero args,
12739 // this function will leave |operation.args| as |null| (thus avoiding
12740 // allocations that would occur if we used an empty array to represent
12741 // zero arguments). Otherwise, it will replace |null| with a new array
12742 // containing the arguments. The caller should use this value if it
12743 // cannot reuse an array for each call to read().
12745 // These two modes are present because this function is very hot and so
12746 // avoiding allocations where possible is worthwhile.
12748 read: function EvaluatorPreprocessor_read(operation) {
12749 var args = operation.args;
12751 var obj = this.parser.getObj();
12754 // Check that the command is valid
12755 var opSpec = OP_MAP[cmd];
12757 warn('Unknown command "' + cmd + '"');
12761 var fn = opSpec.id;
12762 var numArgs = opSpec.numArgs;
12763 var argsLength = args !== null ? args.length : 0;
12765 if (!opSpec.variableArgs) {
12766 // Postscript commands can be nested, e.g. /F2 /GS2 gs 5.711 Tf
12767 if (argsLength !== numArgs) {
12768 var nonProcessedArgs = this.nonProcessedArgs;
12769 while (argsLength > numArgs) {
12770 nonProcessedArgs.push(args.shift());
12773 while (argsLength < numArgs && nonProcessedArgs.length !== 0) {
12777 args.unshift(nonProcessedArgs.pop());
12782 if (argsLength < numArgs) {
12783 // If we receive too few args, it's not possible to possible
12784 // to execute the command, so skip the command
12785 info('Command ' + fn + ': because expected ' +
12786 numArgs + ' args, but received ' + argsLength +
12787 ' args; skipping');
12791 } else if (argsLength > numArgs) {
12792 info('Command ' + fn + ': expected [0,' + numArgs +
12793 '] args, but received ' + argsLength + ' args');
12796 // TODO figure out how to type-check vararg functions
12797 this.preprocessCommand(fn, args);
12800 operation.args = args;
12804 return false; // no more commands
12807 if (obj !== null) {
12811 args.push((obj instanceof Dict ? obj.getAll() : obj));
12812 assert(args.length <= 33, 'Too many arguments');
12819 function EvaluatorPreprocessor_preprocessCommand(fn, args) {
12822 this.stateManager.save();
12825 this.stateManager.restore();
12827 case OPS.transform:
12828 this.stateManager.transform(args);
12833 return EvaluatorPreprocessor;
12836 var QueueOptimizer = (function QueueOptimizerClosure() {
12837 function addState(parentState, pattern, fn) {
12838 var state = parentState;
12839 for (var i = 0, ii = pattern.length - 1; i < ii; i++) {
12840 var item = pattern[i];
12841 state = (state[item] || (state[item] = []));
12843 state[pattern[pattern.length - 1]] = fn;
12846 function handlePaintSolidColorImageMask(iFirstSave, count, fnArray,
12848 // Handles special case of mainly LaTeX documents which use image masks to
12849 // draw lines with the current fill style.
12850 // 'count' groups of (save, transform, paintImageMaskXObject, restore)+
12851 // have been found at iFirstSave.
12852 var iFirstPIMXO = iFirstSave + 2;
12853 for (var i = 0; i < count; i++) {
12854 var arg = argsArray[iFirstPIMXO + 4 * i];
12855 var imageMask = arg.length === 1 && arg[0];
12856 if (imageMask && imageMask.width === 1 && imageMask.height === 1 &&
12857 (!imageMask.data.length ||
12858 (imageMask.data.length === 1 && imageMask.data[0] === 0))) {
12859 fnArray[iFirstPIMXO + 4 * i] = OPS.paintSolidColorImageMask;
12867 var InitialState = [];
12869 // This replaces (save, transform, paintInlineImageXObject, restore)+
12870 // sequences with one |paintInlineImageXObjectGroup| operation.
12871 addState(InitialState,
12872 [OPS.save, OPS.transform, OPS.paintInlineImageXObject, OPS.restore],
12873 function foundInlineImageGroup(context) {
12874 var MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;
12875 var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
12876 var MAX_WIDTH = 1000;
12877 var IMAGE_PADDING = 1;
12879 var fnArray = context.fnArray, argsArray = context.argsArray;
12880 var curr = context.iCurr;
12881 var iFirstSave = curr - 3;
12882 var iFirstTransform = curr - 2;
12883 var iFirstPIIXO = curr - 1;
12885 // Look for the quartets.
12886 var i = iFirstSave + 4;
12887 var ii = fnArray.length;
12888 while (i + 3 < ii) {
12889 if (fnArray[i] !== OPS.save ||
12890 fnArray[i + 1] !== OPS.transform ||
12891 fnArray[i + 2] !== OPS.paintInlineImageXObject ||
12892 fnArray[i + 3] !== OPS.restore) {
12893 break; // ops don't match
12898 // At this point, i is the index of the first op past the last valid
12900 var count = Math.min((i - iFirstSave) / 4,
12901 MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);
12902 if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) {
12906 // assuming that heights of those image is too small (~1 pixel)
12907 // packing as much as possible by lines
12909 var map = [], maxLineHeight = 0;
12910 var currentX = IMAGE_PADDING, currentY = IMAGE_PADDING;
12912 for (q = 0; q < count; q++) {
12913 var transform = argsArray[iFirstTransform + (q << 2)];
12914 var img = argsArray[iFirstPIIXO + (q << 2)][0];
12915 if (currentX + img.width > MAX_WIDTH) {
12916 // starting new line
12917 maxX = Math.max(maxX, currentX);
12918 currentY += maxLineHeight + 2 * IMAGE_PADDING;
12923 transform: transform,
12924 x: currentX, y: currentY,
12925 w: img.width, h: img.height
12927 currentX += img.width + 2 * IMAGE_PADDING;
12928 maxLineHeight = Math.max(maxLineHeight, img.height);
12930 var imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING;
12931 var imgHeight = currentY + maxLineHeight + IMAGE_PADDING;
12932 var imgData = new Uint8Array(imgWidth * imgHeight * 4);
12933 var imgRowSize = imgWidth << 2;
12934 for (q = 0; q < count; q++) {
12935 var data = argsArray[iFirstPIIXO + (q << 2)][0].data;
12936 // Copy image by lines and extends pixels into padding.
12937 var rowSize = map[q].w << 2;
12938 var dataOffset = 0;
12939 var offset = (map[q].x + map[q].y * imgWidth) << 2;
12940 imgData.set(data.subarray(0, rowSize), offset - imgRowSize);
12941 for (var k = 0, kk = map[q].h; k < kk; k++) {
12942 imgData.set(data.subarray(dataOffset, dataOffset + rowSize), offset);
12943 dataOffset += rowSize;
12944 offset += imgRowSize;
12946 imgData.set(data.subarray(dataOffset - rowSize, dataOffset), offset);
12947 while (offset >= 0) {
12948 data[offset - 4] = data[offset];
12949 data[offset - 3] = data[offset + 1];
12950 data[offset - 2] = data[offset + 2];
12951 data[offset - 1] = data[offset + 3];
12952 data[offset + rowSize] = data[offset + rowSize - 4];
12953 data[offset + rowSize + 1] = data[offset + rowSize - 3];
12954 data[offset + rowSize + 2] = data[offset + rowSize - 2];
12955 data[offset + rowSize + 3] = data[offset + rowSize - 1];
12956 offset -= imgRowSize;
12960 // Replace queue items.
12961 fnArray.splice(iFirstSave, count * 4, OPS.paintInlineImageXObjectGroup);
12962 argsArray.splice(iFirstSave, count * 4,
12963 [{ width: imgWidth, height: imgHeight, kind: ImageKind.RGBA_32BPP,
12964 data: imgData }, map]);
12966 return iFirstSave + 1;
12969 // This replaces (save, transform, paintImageMaskXObject, restore)+
12970 // sequences with one |paintImageMaskXObjectGroup| or one
12971 // |paintImageMaskXObjectRepeat| operation.
12972 addState(InitialState,
12973 [OPS.save, OPS.transform, OPS.paintImageMaskXObject, OPS.restore],
12974 function foundImageMaskGroup(context) {
12975 var MIN_IMAGES_IN_MASKS_BLOCK = 10;
12976 var MAX_IMAGES_IN_MASKS_BLOCK = 100;
12977 var MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;
12979 var fnArray = context.fnArray, argsArray = context.argsArray;
12980 var curr = context.iCurr;
12981 var iFirstSave = curr - 3;
12982 var iFirstTransform = curr - 2;
12983 var iFirstPIMXO = curr - 1;
12985 // Look for the quartets.
12986 var i = iFirstSave + 4;
12987 var ii = fnArray.length;
12988 while (i + 3 < ii) {
12989 if (fnArray[i] !== OPS.save ||
12990 fnArray[i + 1] !== OPS.transform ||
12991 fnArray[i + 2] !== OPS.paintImageMaskXObject ||
12992 fnArray[i + 3] !== OPS.restore) {
12993 break; // ops don't match
12998 // At this point, i is the index of the first op past the last valid
13000 var count = (i - iFirstSave) / 4;
13001 count = handlePaintSolidColorImageMask(iFirstSave, count, fnArray,
13003 if (count < MIN_IMAGES_IN_MASKS_BLOCK) {
13008 var isSameImage = false;
13009 var iTransform, transformArgs;
13010 var firstPIMXOArg0 = argsArray[iFirstPIMXO][0];
13011 if (argsArray[iFirstTransform][1] === 0 &&
13012 argsArray[iFirstTransform][2] === 0) {
13013 isSameImage = true;
13014 var firstTransformArg0 = argsArray[iFirstTransform][0];
13015 var firstTransformArg3 = argsArray[iFirstTransform][3];
13016 iTransform = iFirstTransform + 4;
13017 var iPIMXO = iFirstPIMXO + 4;
13018 for (q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) {
13019 transformArgs = argsArray[iTransform];
13020 if (argsArray[iPIMXO][0] !== firstPIMXOArg0 ||
13021 transformArgs[0] !== firstTransformArg0 ||
13022 transformArgs[1] !== 0 ||
13023 transformArgs[2] !== 0 ||
13024 transformArgs[3] !== firstTransformArg3) {
13025 if (q < MIN_IMAGES_IN_MASKS_BLOCK) {
13026 isSameImage = false;
13030 break; // different image or transform
13036 count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK);
13037 var positions = new Float32Array(count * 2);
13038 iTransform = iFirstTransform;
13039 for (q = 0; q < count; q++, iTransform += 4) {
13040 transformArgs = argsArray[iTransform];
13041 positions[(q << 1)] = transformArgs[4];
13042 positions[(q << 1) + 1] = transformArgs[5];
13045 // Replace queue items.
13046 fnArray.splice(iFirstSave, count * 4, OPS.paintImageMaskXObjectRepeat);
13047 argsArray.splice(iFirstSave, count * 4,
13048 [firstPIMXOArg0, firstTransformArg0, firstTransformArg3, positions]);
13050 count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK);
13052 for (q = 0; q < count; q++) {
13053 transformArgs = argsArray[iFirstTransform + (q << 2)];
13054 var maskParams = argsArray[iFirstPIMXO + (q << 2)][0];
13055 images.push({ data: maskParams.data, width: maskParams.width,
13056 height: maskParams.height,
13057 transform: transformArgs });
13060 // Replace queue items.
13061 fnArray.splice(iFirstSave, count * 4, OPS.paintImageMaskXObjectGroup);
13062 argsArray.splice(iFirstSave, count * 4, [images]);
13065 return iFirstSave + 1;
13068 // This replaces (save, transform, paintImageXObject, restore)+ sequences
13069 // with one paintImageXObjectRepeat operation, if the |transform| and
13070 // |paintImageXObjectRepeat| ops are appropriate.
13071 addState(InitialState,
13072 [OPS.save, OPS.transform, OPS.paintImageXObject, OPS.restore],
13073 function (context) {
13074 var MIN_IMAGES_IN_BLOCK = 3;
13075 var MAX_IMAGES_IN_BLOCK = 1000;
13077 var fnArray = context.fnArray, argsArray = context.argsArray;
13078 var curr = context.iCurr;
13079 var iFirstSave = curr - 3;
13080 var iFirstTransform = curr - 2;
13081 var iFirstPIXO = curr - 1;
13082 var iFirstRestore = curr;
13084 if (argsArray[iFirstTransform][1] !== 0 ||
13085 argsArray[iFirstTransform][2] !== 0) {
13086 return iFirstRestore + 1; // transform has the wrong form
13089 // Look for the quartets.
13090 var firstPIXOArg0 = argsArray[iFirstPIXO][0];
13091 var firstTransformArg0 = argsArray[iFirstTransform][0];
13092 var firstTransformArg3 = argsArray[iFirstTransform][3];
13093 var i = iFirstSave + 4;
13094 var ii = fnArray.length;
13095 while (i + 3 < ii) {
13096 if (fnArray[i] !== OPS.save ||
13097 fnArray[i + 1] !== OPS.transform ||
13098 fnArray[i + 2] !== OPS.paintImageXObject ||
13099 fnArray[i + 3] !== OPS.restore) {
13100 break; // ops don't match
13102 if (argsArray[i + 1][0] !== firstTransformArg0 ||
13103 argsArray[i + 1][1] !== 0 ||
13104 argsArray[i + 1][2] !== 0 ||
13105 argsArray[i + 1][3] !== firstTransformArg3) {
13106 break; // transforms don't match
13108 if (argsArray[i + 2][0] !== firstPIXOArg0) {
13109 break; // images don't match
13114 // At this point, i is the index of the first op past the last valid
13116 var count = Math.min((i - iFirstSave) / 4, MAX_IMAGES_IN_BLOCK);
13117 if (count < MIN_IMAGES_IN_BLOCK) {
13121 // Extract the (x,y) positions from all of the matching transforms.
13122 var positions = new Float32Array(count * 2);
13123 var iTransform = iFirstTransform;
13124 for (var q = 0; q < count; q++, iTransform += 4) {
13125 var transformArgs = argsArray[iTransform];
13126 positions[(q << 1)] = transformArgs[4];
13127 positions[(q << 1) + 1] = transformArgs[5];
13130 // Replace queue items.
13131 var args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3,
13133 fnArray.splice(iFirstSave, count * 4, OPS.paintImageXObjectRepeat);
13134 argsArray.splice(iFirstSave, count * 4, args);
13136 return iFirstSave + 1;
13139 // This replaces (beginText, setFont, setTextMatrix, showText, endText)+
13140 // sequences with (beginText, setFont, (setTextMatrix, showText)+, endText)+
13141 // sequences, if the font for each one is the same.
13142 addState(InitialState,
13143 [OPS.beginText, OPS.setFont, OPS.setTextMatrix, OPS.showText, OPS.endText],
13144 function (context) {
13145 var MIN_CHARS_IN_BLOCK = 3;
13146 var MAX_CHARS_IN_BLOCK = 1000;
13148 var fnArray = context.fnArray, argsArray = context.argsArray;
13149 var curr = context.iCurr;
13150 var iFirstBeginText = curr - 4;
13151 var iFirstSetFont = curr - 3;
13152 var iFirstSetTextMatrix = curr - 2;
13153 var iFirstShowText = curr - 1;
13154 var iFirstEndText = curr;
13156 // Look for the quintets.
13157 var firstSetFontArg0 = argsArray[iFirstSetFont][0];
13158 var firstSetFontArg1 = argsArray[iFirstSetFont][1];
13159 var i = iFirstBeginText + 5;
13160 var ii = fnArray.length;
13161 while (i + 4 < ii) {
13162 if (fnArray[i] !== OPS.beginText ||
13163 fnArray[i + 1] !== OPS.setFont ||
13164 fnArray[i + 2] !== OPS.setTextMatrix ||
13165 fnArray[i + 3] !== OPS.showText ||
13166 fnArray[i + 4] !== OPS.endText) {
13167 break; // ops don't match
13169 if (argsArray[i + 1][0] !== firstSetFontArg0 ||
13170 argsArray[i + 1][1] !== firstSetFontArg1) {
13171 break; // fonts don't match
13176 // At this point, i is the index of the first op past the last valid
13178 var count = Math.min(((i - iFirstBeginText) / 5), MAX_CHARS_IN_BLOCK);
13179 if (count < MIN_CHARS_IN_BLOCK) {
13183 // If the preceding quintet is (<something>, setFont, setTextMatrix,
13184 // showText, endText), include that as well. (E.g. <something> might be
13186 var iFirst = iFirstBeginText;
13187 if (iFirstBeginText >= 4 &&
13188 fnArray[iFirstBeginText - 4] === fnArray[iFirstSetFont] &&
13189 fnArray[iFirstBeginText - 3] === fnArray[iFirstSetTextMatrix] &&
13190 fnArray[iFirstBeginText - 2] === fnArray[iFirstShowText] &&
13191 fnArray[iFirstBeginText - 1] === fnArray[iFirstEndText] &&
13192 argsArray[iFirstBeginText - 4][0] === firstSetFontArg0 &&
13193 argsArray[iFirstBeginText - 4][1] === firstSetFontArg1) {
13198 // Remove (endText, beginText, setFont) trios.
13199 var iEndText = iFirst + 4;
13200 for (var q = 1; q < count; q++) {
13201 fnArray.splice(iEndText, 3);
13202 argsArray.splice(iEndText, 3);
13206 return iEndText + 1;
13209 function QueueOptimizer() {}
13211 QueueOptimizer.prototype = {
13212 optimize: function QueueOptimizer_optimize(queue) {
13213 var fnArray = queue.fnArray, argsArray = queue.argsArray;
13217 argsArray: argsArray
13220 var i = 0, ii = fnArray.length;
13222 state = (state || InitialState)[fnArray[i]];
13223 if (typeof state === 'function') { // we found some handler
13225 // state() returns the index of the first non-matching op (if we
13226 // didn't match) or the first op past the modified ops (if we did
13227 // match and replace).
13228 i = state(context);
13229 state = undefined; // reset the state machine
13230 ii = context.fnArray.length;
13237 return QueueOptimizer;
13241 var BUILT_IN_CMAPS = [
13242 // << Start unicode maps.
13245 'Adobe-Japan1-UCS2',
13246 'Adobe-Korea1-UCS2',
13247 // >> End unicode maps.
13380 'UniJIS-UCS2-HW-H',
13381 'UniJIS-UCS2-HW-V',
13389 'UniJIS2004-UTF16-H',
13390 'UniJIS2004-UTF16-V',
13391 'UniJIS2004-UTF32-H',
13392 'UniJIS2004-UTF32-V',
13393 'UniJIS2004-UTF8-H',
13394 'UniJIS2004-UTF8-V',
13395 'UniJISPro-UCS2-HW-V',
13396 'UniJISPro-UCS2-V',
13397 'UniJISPro-UTF8-V',
13398 'UniJISX0213-UTF32-H',
13399 'UniJISX0213-UTF32-V',
13400 'UniJISX02132004-UTF32-H',
13401 'UniJISX02132004-UTF32-V',
13413 // CMap, not to be confused with TrueType's cmap.
13414 var CMap = (function CMapClosure() {
13415 function CMap(builtInCMap) {
13416 // Codespace ranges are stored as follows:
13417 // [[1BytePairs], [2BytePairs], [3BytePairs], [4BytePairs]]
13418 // where nBytePairs are ranges e.g. [low1, high1, low2, high2, ...]
13419 this.codespaceRanges = [[], [], [], []];
13420 this.numCodespaceRanges = 0;
13421 // Map entries have one of two forms.
13422 // - cid chars are 16-bit unsigned integers, stored as integers.
13423 // - bf chars are variable-length byte sequences, stored as strings, with
13424 // one byte per character.
13427 this.vertical = false;
13428 this.useCMap = null;
13429 this.builtInCMap = builtInCMap;
13432 addCodespaceRange: function(n, low, high) {
13433 this.codespaceRanges[n - 1].push(low, high);
13434 this.numCodespaceRanges++;
13437 mapCidRange: function(low, high, dstLow) {
13438 while (low <= high) {
13439 this._map[low++] = dstLow++;
13443 mapBfRange: function(low, high, dstLow) {
13444 var lastByte = dstLow.length - 1;
13445 while (low <= high) {
13446 this._map[low++] = dstLow;
13447 // Only the last byte has to be incremented.
13448 dstLow = dstLow.substr(0, lastByte) +
13449 String.fromCharCode(dstLow.charCodeAt(lastByte) + 1);
13453 mapBfRangeToArray: function(low, high, array) {
13454 var i = 0, ii = array.length;
13455 while (low <= high && i < ii) {
13456 this._map[low] = array[i++];
13461 // This is used for both bf and cid chars.
13462 mapOne: function(src, dst) {
13463 this._map[src] = dst;
13466 lookup: function(code) {
13467 return this._map[code];
13470 contains: function(code) {
13471 return this._map[code] !== undefined;
13474 forEach: function(callback) {
13475 // Most maps have fewer than 65536 entries, and for those we use normal
13476 // array iteration. But really sparse tables are possible -- e.g. with
13477 // indices in the *billions*. For such tables we use for..in, which isn't
13478 // ideal because it stringifies the indices for all present elements, but
13479 // it does avoid iterating over every undefined entry.
13480 var map = this._map;
13481 var length = map.length;
13483 if (length <= 0x10000) {
13484 for (i = 0; i < length; i++) {
13485 if (map[i] !== undefined) {
13486 callback(i, map[i]);
13490 for (i in this._map) {
13491 callback(i, map[i]);
13496 charCodeOf: function(value) {
13497 return this._map.indexOf(value);
13500 getMap: function() {
13504 readCharCode: function(str, offset, out) {
13506 var codespaceRanges = this.codespaceRanges;
13507 var codespaceRangesLen = this.codespaceRanges.length;
13508 // 9.7.6.2 CMap Mapping
13509 // The code length is at most 4.
13510 for (var n = 0; n < codespaceRangesLen; n++) {
13511 c = ((c << 8) | str.charCodeAt(offset + n)) >>> 0;
13512 // Check each codespace range to see if it falls within.
13513 var codespaceRange = codespaceRanges[n];
13514 for (var k = 0, kk = codespaceRange.length; k < kk;) {
13515 var low = codespaceRange[k++];
13516 var high = codespaceRange[k++];
13517 if (c >= low && c <= high) {
13519 out.length = n + 1;
13528 get isIdentityCMap() {
13529 if (!(this.name === 'Identity-H' || this.name === 'Identity-V')) {
13532 if (this._map.length !== 0x10000) {
13535 for (var i = 0; i < 0x10000; i++) {
13536 if (this._map[i] !== i) {
13546 // A special case of CMap, where the _map array implicitly has a length of
13547 // 65536 and each element is equal to its index.
13548 var IdentityCMap = (function IdentityCMapClosure() {
13549 function IdentityCMap(vertical, n) {
13551 this.vertical = vertical;
13552 this.addCodespaceRange(n, 0, 0xffff);
13554 Util.inherit(IdentityCMap, CMap, {});
13556 IdentityCMap.prototype = {
13557 addCodespaceRange: CMap.prototype.addCodespaceRange,
13559 mapCidRange: function(low, high, dstLow) {
13560 error('should not call mapCidRange');
13563 mapBfRange: function(low, high, dstLow) {
13564 error('should not call mapBfRange');
13567 mapBfRangeToArray: function(low, high, array) {
13568 error('should not call mapBfRangeToArray');
13571 mapOne: function(src, dst) {
13572 error('should not call mapCidOne');
13575 lookup: function(code) {
13576 return (isInt(code) && code <= 0xffff) ? code : undefined;
13579 contains: function(code) {
13580 return isInt(code) && code <= 0xffff;
13583 forEach: function(callback) {
13584 for (var i = 0; i <= 0xffff; i++) {
13589 charCodeOf: function(value) {
13590 return (isInt(value) && value <= 0xffff) ? value : -1;
13593 getMap: function() {
13594 // Sometimes identity maps must be instantiated, but it's rare.
13595 var map = new Array(0x10000);
13596 for (var i = 0; i <= 0xffff; i++) {
13602 readCharCode: CMap.prototype.readCharCode,
13604 get isIdentityCMap() {
13605 error('should not access .isIdentityCMap');
13609 return IdentityCMap;
13612 var BinaryCMapReader = (function BinaryCMapReaderClosure() {
13613 function fetchBinaryData(url) {
13614 var nonBinaryRequest = PDFJS.disableWorker;
13615 var request = new XMLHttpRequest();
13616 request.open('GET', url, false);
13617 if (!nonBinaryRequest) {
13619 request.responseType = 'arraybuffer';
13620 nonBinaryRequest = request.responseType !== 'arraybuffer';
13622 nonBinaryRequest = true;
13625 if (nonBinaryRequest && request.overrideMimeType) {
13626 request.overrideMimeType('text/plain; charset=x-user-defined');
13628 request.send(null);
13629 if (nonBinaryRequest ? !request.responseText : !request.response) {
13630 error('Unable to get binary cMap at: ' + url);
13632 if (nonBinaryRequest) {
13633 var data = Array.prototype.map.call(request.responseText, function (ch) {
13634 return ch.charCodeAt(0) & 255;
13636 return new Uint8Array(data);
13638 return new Uint8Array(request.response);
13641 function hexToInt(a, size) {
13643 for (var i = 0; i <= size; i++) {
13644 n = (n << 8) | a[i];
13649 function hexToStr(a, size) {
13650 // This code is hot. Special-case some common values to avoid creating an
13651 // object with subarray().
13653 return String.fromCharCode(a[0], a[1]);
13656 return String.fromCharCode(a[0], a[1], a[2], a[3]);
13658 return String.fromCharCode.apply(null, a.subarray(0, size + 1));
13661 function addHex(a, b, size) {
13663 for (var i = size; i >= 0; i--) {
13670 function incHex(a, size) {
13672 for (var i = size; i >= 0 && c > 0; i--) {
13679 var MAX_NUM_SIZE = 16;
13680 var MAX_ENCODED_NUM_SIZE = 19; // ceil(MAX_NUM_SIZE * 7 / 8)
13682 function BinaryCMapStream(data) {
13683 this.buffer = data;
13685 this.end = data.length;
13686 this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE);
13689 BinaryCMapStream.prototype = {
13690 readByte: function () {
13691 if (this.pos >= this.end) {
13694 return this.buffer[this.pos++];
13696 readNumber: function () {
13700 var b = this.readByte();
13702 error('unexpected EOF in bcmap');
13704 last = !(b & 0x80);
13705 n = (n << 7) | (b & 0x7F);
13709 readSigned: function () {
13710 var n = this.readNumber();
13711 return (n & 1) ? ~(n >>> 1) : n >>> 1;
13713 readHex: function (num, size) {
13714 num.set(this.buffer.subarray(this.pos,
13715 this.pos + size + 1));
13716 this.pos += size + 1;
13718 readHexNumber: function (num, size) {
13720 var stack = this.tmpBuf, sp = 0;
13722 var b = this.readByte();
13724 error('unexpected EOF in bcmap');
13726 last = !(b & 0x80);
13727 stack[sp++] = b & 0x7F;
13729 var i = size, buffer = 0, bufferSize = 0;
13731 while (bufferSize < 8 && stack.length > 0) {
13732 buffer = (stack[--sp] << bufferSize) | buffer;
13735 num[i] = buffer & 255;
13741 readHexSigned: function (num, size) {
13742 this.readHexNumber(num, size);
13743 var sign = num[size] & 1 ? 255 : 0;
13745 for (var i = 0; i <= size; i++) {
13746 c = ((c & 1) << 8) | num[i];
13747 num[i] = (c >> 1) ^ sign;
13750 readString: function () {
13751 var len = this.readNumber();
13753 for (var i = 0; i < len; i++) {
13754 s += String.fromCharCode(this.readNumber());
13760 function processBinaryCMap(url, cMap, extend) {
13761 var data = fetchBinaryData(url);
13762 var stream = new BinaryCMapStream(data);
13764 var header = stream.readByte();
13765 cMap.vertical = !!(header & 1);
13767 var useCMap = null;
13768 var start = new Uint8Array(MAX_NUM_SIZE);
13769 var end = new Uint8Array(MAX_NUM_SIZE);
13770 var char = new Uint8Array(MAX_NUM_SIZE);
13771 var charCode = new Uint8Array(MAX_NUM_SIZE);
13772 var tmp = new Uint8Array(MAX_NUM_SIZE);
13776 while ((b = stream.readByte()) >= 0) {
13778 if (type === 7) { // metadata, e.g. comment or usecmap
13779 switch (b & 0x1F) {
13781 stream.readString(); // skipping comment
13784 useCMap = stream.readString();
13789 var sequence = !!(b & 0x10);
13790 var dataSize = b & 15;
13792 assert(dataSize + 1 <= MAX_NUM_SIZE);
13794 var ucs2DataSize = 1;
13795 var subitemsCount = stream.readNumber();
13798 case 0: // codespacerange
13799 stream.readHex(start, dataSize);
13800 stream.readHexNumber(end, dataSize);
13801 addHex(end, start, dataSize);
13802 cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize),
13803 hexToInt(end, dataSize));
13804 for (i = 1; i < subitemsCount; i++) {
13805 incHex(end, dataSize);
13806 stream.readHexNumber(start, dataSize);
13807 addHex(start, end, dataSize);
13808 stream.readHexNumber(end, dataSize);
13809 addHex(end, start, dataSize);
13810 cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize),
13811 hexToInt(end, dataSize));
13814 case 1: // notdefrange
13815 stream.readHex(start, dataSize);
13816 stream.readHexNumber(end, dataSize);
13817 addHex(end, start, dataSize);
13818 code = stream.readNumber();
13819 // undefined range, skipping
13820 for (i = 1; i < subitemsCount; i++) {
13821 incHex(end, dataSize);
13822 stream.readHexNumber(start, dataSize);
13823 addHex(start, end, dataSize);
13824 stream.readHexNumber(end, dataSize);
13825 addHex(end, start, dataSize);
13826 code = stream.readNumber();
13831 stream.readHex(char, dataSize);
13832 code = stream.readNumber();
13833 cMap.mapOne(hexToInt(char, dataSize), code);
13834 for (i = 1; i < subitemsCount; i++) {
13835 incHex(char, dataSize);
13837 stream.readHexNumber(tmp, dataSize);
13838 addHex(char, tmp, dataSize);
13840 code = stream.readSigned() + (code + 1);
13841 cMap.mapOne(hexToInt(char, dataSize), code);
13844 case 3: // cidrange
13845 stream.readHex(start, dataSize);
13846 stream.readHexNumber(end, dataSize);
13847 addHex(end, start, dataSize);
13848 code = stream.readNumber();
13849 cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize),
13851 for (i = 1; i < subitemsCount; i++) {
13852 incHex(end, dataSize);
13854 stream.readHexNumber(start, dataSize);
13855 addHex(start, end, dataSize);
13859 stream.readHexNumber(end, dataSize);
13860 addHex(end, start, dataSize);
13861 code = stream.readNumber();
13862 cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize),
13867 stream.readHex(char, ucs2DataSize);
13868 stream.readHex(charCode, dataSize);
13869 cMap.mapOne(hexToInt(char, ucs2DataSize),
13870 hexToStr(charCode, dataSize));
13871 for (i = 1; i < subitemsCount; i++) {
13872 incHex(char, ucs2DataSize);
13874 stream.readHexNumber(tmp, ucs2DataSize);
13875 addHex(char, tmp, ucs2DataSize);
13877 incHex(charCode, dataSize);
13878 stream.readHexSigned(tmp, dataSize);
13879 addHex(charCode, tmp, dataSize);
13880 cMap.mapOne(hexToInt(char, ucs2DataSize),
13881 hexToStr(charCode, dataSize));
13885 stream.readHex(start, ucs2DataSize);
13886 stream.readHexNumber(end, ucs2DataSize);
13887 addHex(end, start, ucs2DataSize);
13888 stream.readHex(charCode, dataSize);
13889 cMap.mapBfRange(hexToInt(start, ucs2DataSize),
13890 hexToInt(end, ucs2DataSize),
13891 hexToStr(charCode, dataSize));
13892 for (i = 1; i < subitemsCount; i++) {
13893 incHex(end, ucs2DataSize);
13895 stream.readHexNumber(start, ucs2DataSize);
13896 addHex(start, end, ucs2DataSize);
13900 stream.readHexNumber(end, ucs2DataSize);
13901 addHex(end, start, ucs2DataSize);
13902 stream.readHex(charCode, dataSize);
13903 cMap.mapBfRange(hexToInt(start, ucs2DataSize),
13904 hexToInt(end, ucs2DataSize),
13905 hexToStr(charCode, dataSize));
13909 error('Unknown type: ' + type);
13920 function BinaryCMapReader() {}
13922 BinaryCMapReader.prototype = {
13923 read: processBinaryCMap
13926 return BinaryCMapReader;
13929 var CMapFactory = (function CMapFactoryClosure() {
13930 function strToInt(str) {
13932 for (var i = 0; i < str.length; i++) {
13933 a = (a << 8) | str.charCodeAt(i);
13938 function expectString(obj) {
13939 if (!isString(obj)) {
13940 error('Malformed CMap: expected string.');
13944 function expectInt(obj) {
13946 error('Malformed CMap: expected int.');
13950 function parseBfChar(cMap, lexer) {
13952 var obj = lexer.getObj();
13956 if (isCmd(obj, 'endbfchar')) {
13960 var src = strToInt(obj);
13961 obj = lexer.getObj();
13962 // TODO are /dstName used?
13965 cMap.mapOne(src, dst);
13969 function parseBfRange(cMap, lexer) {
13971 var obj = lexer.getObj();
13975 if (isCmd(obj, 'endbfrange')) {
13979 var low = strToInt(obj);
13980 obj = lexer.getObj();
13982 var high = strToInt(obj);
13983 obj = lexer.getObj();
13984 if (isInt(obj) || isString(obj)) {
13985 var dstLow = isInt(obj) ? String.fromCharCode(obj) : obj;
13986 cMap.mapBfRange(low, high, dstLow);
13987 } else if (isCmd(obj, '[')) {
13988 obj = lexer.getObj();
13990 while (!isCmd(obj, ']') && !isEOF(obj)) {
13992 obj = lexer.getObj();
13994 cMap.mapBfRangeToArray(low, high, array);
13999 error('Invalid bf range.');
14002 function parseCidChar(cMap, lexer) {
14004 var obj = lexer.getObj();
14008 if (isCmd(obj, 'endcidchar')) {
14012 var src = strToInt(obj);
14013 obj = lexer.getObj();
14016 cMap.mapOne(src, dst);
14020 function parseCidRange(cMap, lexer) {
14022 var obj = lexer.getObj();
14026 if (isCmd(obj, 'endcidrange')) {
14030 var low = strToInt(obj);
14031 obj = lexer.getObj();
14033 var high = strToInt(obj);
14034 obj = lexer.getObj();
14037 cMap.mapCidRange(low, high, dstLow);
14041 function parseCodespaceRange(cMap, lexer) {
14043 var obj = lexer.getObj();
14047 if (isCmd(obj, 'endcodespacerange')) {
14050 if (!isString(obj)) {
14053 var low = strToInt(obj);
14054 obj = lexer.getObj();
14055 if (!isString(obj)) {
14058 var high = strToInt(obj);
14059 cMap.addCodespaceRange(obj.length, low, high);
14061 error('Invalid codespace range.');
14064 function parseWMode(cMap, lexer) {
14065 var obj = lexer.getObj();
14067 cMap.vertical = !!obj;
14071 function parseCMapName(cMap, lexer) {
14072 var obj = lexer.getObj();
14073 if (isName(obj) && isString(obj.name)) {
14074 cMap.name = obj.name;
14078 function parseCMap(cMap, lexer, builtInCMapParams, useCMap) {
14080 var embededUseCMap;
14081 objLoop: while (true) {
14082 var obj = lexer.getObj();
14085 } else if (isName(obj)) {
14086 if (obj.name === 'WMode') {
14087 parseWMode(cMap, lexer);
14088 } else if (obj.name === 'CMapName') {
14089 parseCMapName(cMap, lexer);
14092 } else if (isCmd(obj)) {
14097 if (isName(previous)) {
14098 embededUseCMap = previous.name;
14101 case 'begincodespacerange':
14102 parseCodespaceRange(cMap, lexer);
14104 case 'beginbfchar':
14105 parseBfChar(cMap, lexer);
14107 case 'begincidchar':
14108 parseCidChar(cMap, lexer);
14110 case 'beginbfrange':
14111 parseBfRange(cMap, lexer);
14113 case 'begincidrange':
14114 parseCidRange(cMap, lexer);
14120 if (!useCMap && embededUseCMap) {
14121 // Load the usecmap definition from the file only if there wasn't one
14123 useCMap = embededUseCMap;
14126 extendCMap(cMap, builtInCMapParams, useCMap);
14130 function extendCMap(cMap, builtInCMapParams, useCMap) {
14131 cMap.useCMap = createBuiltInCMap(useCMap, builtInCMapParams);
14132 // If there aren't any code space ranges defined clone all the parent ones
14134 if (cMap.numCodespaceRanges === 0) {
14135 var useCodespaceRanges = cMap.useCMap.codespaceRanges;
14136 for (var i = 0; i < useCodespaceRanges.length; i++) {
14137 cMap.codespaceRanges[i] = useCodespaceRanges[i].slice();
14139 cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges;
14141 // Merge the map into the current one, making sure not to override
14142 // any previously defined entries.
14143 cMap.useCMap.forEach(function(key, value) {
14144 if (!cMap.contains(key)) {
14145 cMap.mapOne(key, cMap.useCMap.lookup(key));
14150 function parseBinaryCMap(name, builtInCMapParams) {
14151 var url = builtInCMapParams.url + name + '.bcmap';
14152 var cMap = new CMap(true);
14153 new BinaryCMapReader().read(url, cMap, function (useCMap) {
14154 extendCMap(cMap, builtInCMapParams, useCMap);
14159 function createBuiltInCMap(name, builtInCMapParams) {
14160 if (name === 'Identity-H') {
14161 return new IdentityCMap(false, 2);
14162 } else if (name === 'Identity-V') {
14163 return new IdentityCMap(true, 2);
14165 if (BUILT_IN_CMAPS.indexOf(name) === -1) {
14166 error('Unknown cMap name: ' + name);
14168 assert(builtInCMapParams, 'built-in cMap parameters are not provided');
14170 if (builtInCMapParams.packed) {
14171 return parseBinaryCMap(name, builtInCMapParams);
14174 var request = new XMLHttpRequest();
14175 var url = builtInCMapParams.url + name;
14176 request.open('GET', url, false);
14177 request.send(null);
14178 if (!request.responseText) {
14179 error('Unable to get cMap at: ' + url);
14181 var cMap = new CMap(true);
14182 var lexer = new Lexer(new StringStream(request.responseText));
14183 parseCMap(cMap, lexer, builtInCMapParams, null);
14188 create: function (encoding, builtInCMapParams, useCMap) {
14189 if (isName(encoding)) {
14190 return createBuiltInCMap(encoding.name, builtInCMapParams);
14191 } else if (isStream(encoding)) {
14192 var cMap = new CMap();
14193 var lexer = new Lexer(encoding);
14195 parseCMap(cMap, lexer, builtInCMapParams, useCMap);
14197 warn('Invalid CMap data. ' + e);
14199 if (cMap.isIdentityCMap) {
14200 return createBuiltInCMap(cMap.name, builtInCMapParams);
14204 error('Encoding required.');
14210 // Unicode Private Use Area
14211 var PRIVATE_USE_OFFSET_START = 0xE000;
14212 var PRIVATE_USE_OFFSET_END = 0xF8FF;
14213 var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false;
14215 // PDF Glyph Space Units are one Thousandth of a TextSpace Unit
14216 // except for Type 3 fonts
14217 var PDF_GLYPH_SPACE_UNITS = 1000;
14219 // Hinting is currently disabled due to unknown problems on windows
14220 // in tracemonkey and various other pdfs with type1 fonts.
14221 var HINTING_ENABLED = false;
14223 // Accented charactars are not displayed properly on windows, using this flag
14224 // to control analysis of seac charstrings.
14225 var SEAC_ANALYSIS_ENABLED = false;
14240 ExpertEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14241 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14242 'space', 'exclamsmall', 'Hungarumlautsmall', '', 'dollaroldstyle',
14243 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior',
14244 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma',
14245 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle',
14246 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle',
14247 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon',
14248 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior',
14249 'questionsmall', '', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
14250 'esuperior', '', '', 'isuperior', '', '', 'lsuperior', 'msuperior',
14251 'nsuperior', 'osuperior', '', '', 'rsuperior', 'ssuperior', 'tsuperior',
14252 '', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', '',
14253 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
14254 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall',
14255 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall',
14256 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall',
14257 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary',
14258 'onefitted', 'rupiah', 'Tildesmall', '', '', '', '', '', '', '', '', '',
14259 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14260 '', '', '', '', '', '', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall',
14261 '', '', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall',
14262 'Caronsmall', '', 'Dotaccentsmall', '', '', 'Macronsmall', '', '',
14263 'figuredash', 'hypheninferior', '', '', 'Ogoneksmall', 'Ringsmall',
14264 'Cedillasmall', '', '', '', 'onequarter', 'onehalf', 'threequarters',
14265 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths',
14266 'seveneighths', 'onethird', 'twothirds', '', '', 'zerosuperior',
14267 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior',
14268 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior',
14269 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior',
14270 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior',
14271 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
14272 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
14273 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall',
14274 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall',
14275 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall',
14276 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall',
14277 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall',
14278 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall',
14279 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall',
14281 MacExpertEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '',
14282 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14283 'space', 'exclamsmall', 'Hungarumlautsmall', 'centoldstyle',
14284 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
14285 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader',
14286 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle',
14287 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
14288 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
14289 'nineoldstyle', 'colon', 'semicolon', '', 'threequartersemdash', '',
14290 'questionsmall', '', '', '', '', 'Ethsmall', '', '', 'onequarter',
14291 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths',
14292 'seveneighths', 'onethird', 'twothirds', '', '', '', '', '', '', 'ff',
14293 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', '', 'parenrightinferior',
14294 'Circumflexsmall', 'hypheninferior', 'Gravesmall', 'Asmall', 'Bsmall',
14295 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall',
14296 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
14297 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall',
14298 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah',
14299 'Tildesmall', '', '', 'asuperior', 'centsuperior', '', '', '', '',
14300 'Aacutesmall', 'Agravesmall', 'Acircumflexsmall', 'Adieresissmall',
14301 'Atildesmall', 'Aringsmall', 'Ccedillasmall', 'Eacutesmall', 'Egravesmall',
14302 'Ecircumflexsmall', 'Edieresissmall', 'Iacutesmall', 'Igravesmall',
14303 'Icircumflexsmall', 'Idieresissmall', 'Ntildesmall', 'Oacutesmall',
14304 'Ogravesmall', 'Ocircumflexsmall', 'Odieresissmall', 'Otildesmall',
14305 'Uacutesmall', 'Ugravesmall', 'Ucircumflexsmall', 'Udieresissmall', '',
14306 'eightsuperior', 'fourinferior', 'threeinferior', 'sixinferior',
14307 'eightinferior', 'seveninferior', 'Scaronsmall', '', 'centinferior',
14308 'twoinferior', '', 'Dieresissmall', '', 'Caronsmall', 'osuperior',
14309 'fiveinferior', '', 'commainferior', 'periodinferior', 'Yacutesmall', '',
14310 'dollarinferior', '', 'Thornsmall', '', 'nineinferior', 'zeroinferior',
14311 'Zcaronsmall', 'AEsmall', 'Oslashsmall', 'questiondownsmall',
14312 'oneinferior', 'Lslashsmall', '', '', '', '', '', '', 'Cedillasmall', '',
14313 '', '', '', '', 'OEsmall', 'figuredash', 'hyphensuperior', '', '', '', '',
14314 'exclamdownsmall', '', 'Ydieresissmall', '', 'onesuperior', 'twosuperior',
14315 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior',
14316 'sevensuperior', 'ninesuperior', 'zerosuperior', '', 'esuperior',
14317 'rsuperior', 'tsuperior', '', '', 'isuperior', 'ssuperior', 'dsuperior',
14318 '', '', '', '', '', 'lsuperior', 'Ogoneksmall', 'Brevesmall',
14319 'Macronsmall', 'bsuperior', 'nsuperior', 'msuperior', 'commasuperior',
14320 'periodsuperior', 'Dotaccentsmall', 'Ringsmall'],
14321 MacRomanEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14322 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14323 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
14324 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus',
14325 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three',
14326 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon',
14327 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F',
14328 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
14329 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright',
14330 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
14331 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
14332 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', '',
14333 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis',
14334 'Udieresis', 'aacute', 'agrave', 'acircumflex', 'adieresis', 'atilde',
14335 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis',
14336 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute',
14337 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave',
14338 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling',
14339 'section', 'bullet', 'paragraph', 'germandbls', 'registered', 'copyright',
14340 'trademark', 'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity',
14341 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff',
14342 'summation', 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine',
14343 'Omega', 'ae', 'oslash', 'questiondown', 'exclamdown', 'logicalnot',
14344 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft',
14345 'guillemotright', 'ellipsis', 'space', 'Agrave', 'Atilde', 'Otilde', 'OE',
14346 'oe', 'endash', 'emdash', 'quotedblleft', 'quotedblright', 'quoteleft',
14347 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction',
14348 'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl',
14349 'periodcentered', 'quotesinglbase', 'quotedblbase', 'perthousand',
14350 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute',
14351 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple',
14352 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi', 'circumflex',
14353 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut',
14354 'ogonek', 'caron'],
14355 StandardEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14356 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14357 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
14358 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus',
14359 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three',
14360 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon',
14361 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F',
14362 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
14363 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright',
14364 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f',
14365 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
14366 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde',
14367 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14368 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'exclamdown',
14369 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency',
14370 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft',
14371 'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger', 'daggerdbl',
14372 'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase',
14373 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis',
14374 'perthousand', '', 'questiondown', '', 'grave', 'acute', 'circumflex',
14375 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', '', 'ring', 'cedilla',
14376 '', 'hungarumlaut', 'ogonek', 'caron', 'emdash', '', '', '', '', '', '',
14377 '', '', '', '', '', '', '', '', '', '', 'AE', '', 'ordfeminine', '', '',
14378 '', '', 'Lslash', 'Oslash', 'OE', 'ordmasculine', '', '', '', '', '', 'ae',
14379 '', '', '', 'dotlessi', '', '', 'lslash', 'oslash', 'oe', 'germandbls'],
14380 WinAnsiEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14381 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14382 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
14383 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus',
14384 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three',
14385 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon',
14386 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F',
14387 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
14388 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright',
14389 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
14390 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
14391 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde',
14392 'bullet', 'Euro', 'bullet', 'quotesinglbase', 'florin', 'quotedblbase',
14393 'ellipsis', 'dagger', 'daggerdbl', 'circumflex', 'perthousand', 'Scaron',
14394 'guilsinglleft', 'OE', 'bullet', 'Zcaron', 'bullet', 'bullet', 'quoteleft',
14395 'quoteright', 'quotedblleft', 'quotedblright', 'bullet', 'endash',
14396 'emdash', 'tilde', 'trademark', 'scaron', 'guilsinglright', 'oe', 'bullet',
14397 'zcaron', 'Ydieresis', 'space', 'exclamdown', 'cent', 'sterling',
14398 'currency', 'yen', 'brokenbar', 'section', 'dieresis', 'copyright',
14399 'ordfeminine', 'guillemotleft', 'logicalnot', 'hyphen', 'registered',
14400 'macron', 'degree', 'plusminus', 'twosuperior', 'threesuperior', 'acute',
14401 'mu', 'paragraph', 'periodcentered', 'cedilla', 'onesuperior',
14402 'ordmasculine', 'guillemotright', 'onequarter', 'onehalf', 'threequarters',
14403 'questiondown', 'Agrave', 'Aacute', 'Acircumflex', 'Atilde', 'Adieresis',
14404 'Aring', 'AE', 'Ccedilla', 'Egrave', 'Eacute', 'Ecircumflex', 'Edieresis',
14405 'Igrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Eth', 'Ntilde', 'Ograve',
14406 'Oacute', 'Ocircumflex', 'Otilde', 'Odieresis', 'multiply', 'Oslash',
14407 'Ugrave', 'Uacute', 'Ucircumflex', 'Udieresis', 'Yacute', 'Thorn',
14408 'germandbls', 'agrave', 'aacute', 'acircumflex', 'atilde', 'adieresis',
14409 'aring', 'ae', 'ccedilla', 'egrave', 'eacute', 'ecircumflex', 'edieresis',
14410 'igrave', 'iacute', 'icircumflex', 'idieresis', 'eth', 'ntilde', 'ograve',
14411 'oacute', 'ocircumflex', 'otilde', 'odieresis', 'divide', 'oslash',
14412 'ugrave', 'uacute', 'ucircumflex', 'udieresis', 'yacute', 'thorn',
14414 SymbolSetEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '',
14415 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14416 'space', 'exclam', 'universal', 'numbersign', 'existential', 'percent',
14417 'ampersand', 'suchthat', 'parenleft', 'parenright', 'asteriskmath', 'plus',
14418 'comma', 'minus', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four',
14419 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less',
14420 'equal', 'greater', 'question', 'congruent', 'Alpha', 'Beta', 'Chi',
14421 'Delta', 'Epsilon', 'Phi', 'Gamma', 'Eta', 'Iota', 'theta1', 'Kappa',
14422 'Lambda', 'Mu', 'Nu', 'Omicron', 'Pi', 'Theta', 'Rho', 'Sigma', 'Tau',
14423 'Upsilon', 'sigma1', 'Omega', 'Xi', 'Psi', 'Zeta', 'bracketleft',
14424 'therefore', 'bracketright', 'perpendicular', 'underscore', 'radicalex',
14425 'alpha', 'beta', 'chi', 'delta', 'epsilon', 'phi', 'gamma', 'eta', 'iota',
14426 'phi1', 'kappa', 'lambda', 'mu', 'nu', 'omicron', 'pi', 'theta', 'rho',
14427 'sigma', 'tau', 'upsilon', 'omega1', 'omega', 'xi', 'psi', 'zeta',
14428 'braceleft', 'bar', 'braceright', 'similar', '', '', '', '', '', '', '',
14429 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14430 '', '', '', '', '', '', '', 'Euro', 'Upsilon1', 'minute', 'lessequal',
14431 'fraction', 'infinity', 'florin', 'club', 'diamond', 'heart', 'spade',
14432 'arrowboth', 'arrowleft', 'arrowup', 'arrowright', 'arrowdown', 'degree',
14433 'plusminus', 'second', 'greaterequal', 'multiply', 'proportional',
14434 'partialdiff', 'bullet', 'divide', 'notequal', 'equivalence',
14435 'approxequal', 'ellipsis', 'arrowvertex', 'arrowhorizex', 'carriagereturn',
14436 'aleph', 'Ifraktur', 'Rfraktur', 'weierstrass', 'circlemultiply',
14437 'circleplus', 'emptyset', 'intersection', 'union', 'propersuperset',
14438 'reflexsuperset', 'notsubset', 'propersubset', 'reflexsubset', 'element',
14439 'notelement', 'angle', 'gradient', 'registerserif', 'copyrightserif',
14440 'trademarkserif', 'product', 'radical', 'dotmath', 'logicalnot',
14441 'logicaland', 'logicalor', 'arrowdblboth', 'arrowdblleft', 'arrowdblup',
14442 'arrowdblright', 'arrowdbldown', 'lozenge', 'angleleft', 'registersans',
14443 'copyrightsans', 'trademarksans', 'summation', 'parenlefttp',
14444 'parenleftex', 'parenleftbt', 'bracketlefttp', 'bracketleftex',
14445 'bracketleftbt', 'bracelefttp', 'braceleftmid', 'braceleftbt', 'braceex',
14446 '', 'angleright', 'integral', 'integraltp', 'integralex', 'integralbt',
14447 'parenrighttp', 'parenrightex', 'parenrightbt', 'bracketrighttp',
14448 'bracketrightex', 'bracketrightbt', 'bracerighttp', 'bracerightmid',
14450 ZapfDingbatsEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '',
14451 '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
14452 'space', 'a1', 'a2', 'a202', 'a3', 'a4', 'a5', 'a119', 'a118', 'a117',
14453 'a11', 'a12', 'a13', 'a14', 'a15', 'a16', 'a105', 'a17', 'a18', 'a19',
14454 'a20', 'a21', 'a22', 'a23', 'a24', 'a25', 'a26', 'a27', 'a28', 'a6', 'a7',
14455 'a8', 'a9', 'a10', 'a29', 'a30', 'a31', 'a32', 'a33', 'a34', 'a35', 'a36',
14456 'a37', 'a38', 'a39', 'a40', 'a41', 'a42', 'a43', 'a44', 'a45', 'a46',
14457 'a47', 'a48', 'a49', 'a50', 'a51', 'a52', 'a53', 'a54', 'a55', 'a56',
14458 'a57', 'a58', 'a59', 'a60', 'a61', 'a62', 'a63', 'a64', 'a65', 'a66',
14459 'a67', 'a68', 'a69', 'a70', 'a71', 'a72', 'a73', 'a74', 'a203', 'a75',
14460 'a204', 'a76', 'a77', 'a78', 'a79', 'a81', 'a82', 'a83', 'a84', 'a97',
14461 'a98', 'a99', 'a100', '', 'a89', 'a90', 'a93', 'a94', 'a91', 'a92', 'a205',
14462 'a85', 'a206', 'a86', 'a87', 'a88', 'a95', 'a96', '', '', '', '', '', '',
14463 '', '', '', '', '', '', '', '', '', '', '', '', '', 'a101', 'a102', 'a103',
14464 'a104', 'a106', 'a107', 'a108', 'a112', 'a111', 'a110', 'a109', 'a120',
14465 'a121', 'a122', 'a123', 'a124', 'a125', 'a126', 'a127', 'a128', 'a129',
14466 'a130', 'a131', 'a132', 'a133', 'a134', 'a135', 'a136', 'a137', 'a138',
14467 'a139', 'a140', 'a141', 'a142', 'a143', 'a144', 'a145', 'a146', 'a147',
14468 'a148', 'a149', 'a150', 'a151', 'a152', 'a153', 'a154', 'a155', 'a156',
14469 'a157', 'a158', 'a159', 'a160', 'a161', 'a163', 'a164', 'a196', 'a165',
14470 'a192', 'a166', 'a167', 'a168', 'a169', 'a170', 'a171', 'a172', 'a173',
14471 'a162', 'a174', 'a175', 'a176', 'a177', 'a178', 'a179', 'a193', 'a180',
14472 'a199', 'a181', 'a200', 'a182', '', 'a201', 'a183', 'a184', 'a197', 'a185',
14473 'a194', 'a198', 'a186', 'a195', 'a187', 'a188', 'a189', 'a190', 'a191']
14477 * Hold a map of decoded fonts and of the standard fourteen Type1
14478 * fonts and their acronyms.
14481 'ArialNarrow': 'Helvetica',
14482 'ArialNarrow-Bold': 'Helvetica-Bold',
14483 'ArialNarrow-BoldItalic': 'Helvetica-BoldOblique',
14484 'ArialNarrow-Italic': 'Helvetica-Oblique',
14485 'ArialBlack': 'Helvetica',
14486 'ArialBlack-Bold': 'Helvetica-Bold',
14487 'ArialBlack-BoldItalic': 'Helvetica-BoldOblique',
14488 'ArialBlack-Italic': 'Helvetica-Oblique',
14489 'Arial': 'Helvetica',
14490 'Arial-Bold': 'Helvetica-Bold',
14491 'Arial-BoldItalic': 'Helvetica-BoldOblique',
14492 'Arial-Italic': 'Helvetica-Oblique',
14493 'Arial-BoldItalicMT': 'Helvetica-BoldOblique',
14494 'Arial-BoldMT': 'Helvetica-Bold',
14495 'Arial-ItalicMT': 'Helvetica-Oblique',
14496 'ArialMT': 'Helvetica',
14497 'Courier-Bold': 'Courier-Bold',
14498 'Courier-BoldItalic': 'Courier-BoldOblique',
14499 'Courier-Italic': 'Courier-Oblique',
14500 'CourierNew': 'Courier',
14501 'CourierNew-Bold': 'Courier-Bold',
14502 'CourierNew-BoldItalic': 'Courier-BoldOblique',
14503 'CourierNew-Italic': 'Courier-Oblique',
14504 'CourierNewPS-BoldItalicMT': 'Courier-BoldOblique',
14505 'CourierNewPS-BoldMT': 'Courier-Bold',
14506 'CourierNewPS-ItalicMT': 'Courier-Oblique',
14507 'CourierNewPSMT': 'Courier',
14508 'Helvetica': 'Helvetica',
14509 'Helvetica-Bold': 'Helvetica-Bold',
14510 'Helvetica-BoldItalic': 'Helvetica-BoldOblique',
14511 'Helvetica-BoldOblique': 'Helvetica-BoldOblique',
14512 'Helvetica-Italic': 'Helvetica-Oblique',
14513 'Helvetica-Oblique':'Helvetica-Oblique',
14514 'Symbol-Bold': 'Symbol',
14515 'Symbol-BoldItalic': 'Symbol',
14516 'Symbol-Italic': 'Symbol',
14517 'TimesNewRoman': 'Times-Roman',
14518 'TimesNewRoman-Bold': 'Times-Bold',
14519 'TimesNewRoman-BoldItalic': 'Times-BoldItalic',
14520 'TimesNewRoman-Italic': 'Times-Italic',
14521 'TimesNewRomanPS': 'Times-Roman',
14522 'TimesNewRomanPS-Bold': 'Times-Bold',
14523 'TimesNewRomanPS-BoldItalic': 'Times-BoldItalic',
14524 'TimesNewRomanPS-BoldItalicMT': 'Times-BoldItalic',
14525 'TimesNewRomanPS-BoldMT': 'Times-Bold',
14526 'TimesNewRomanPS-Italic': 'Times-Italic',
14527 'TimesNewRomanPS-ItalicMT': 'Times-Italic',
14528 'TimesNewRomanPSMT': 'Times-Roman',
14529 'TimesNewRomanPSMT-Bold': 'Times-Bold',
14530 'TimesNewRomanPSMT-BoldItalic': 'Times-BoldItalic',
14531 'TimesNewRomanPSMT-Italic': 'Times-Italic'
14535 * Holds the map of the non-standard fonts that might be included as a standard
14536 * fonts without glyph data.
14538 var nonStdFontMap = {
14539 'CenturyGothic': 'Helvetica',
14540 'CenturyGothic-Bold': 'Helvetica-Bold',
14541 'CenturyGothic-BoldItalic': 'Helvetica-BoldOblique',
14542 'CenturyGothic-Italic': 'Helvetica-Oblique',
14543 'ComicSansMS': 'Comic Sans MS',
14544 'ComicSansMS-Bold': 'Comic Sans MS-Bold',
14545 'ComicSansMS-BoldItalic': 'Comic Sans MS-BoldItalic',
14546 'ComicSansMS-Italic': 'Comic Sans MS-Italic',
14547 'LucidaConsole': 'Courier',
14548 'LucidaConsole-Bold': 'Courier-Bold',
14549 'LucidaConsole-BoldItalic': 'Courier-BoldOblique',
14550 'LucidaConsole-Italic': 'Courier-Oblique',
14551 'MS-Gothic': 'MS Gothic',
14552 'MS-Gothic-Bold': 'MS Gothic-Bold',
14553 'MS-Gothic-BoldItalic': 'MS Gothic-BoldItalic',
14554 'MS-Gothic-Italic': 'MS Gothic-Italic',
14555 'MS-Mincho': 'MS Mincho',
14556 'MS-Mincho-Bold': 'MS Mincho-Bold',
14557 'MS-Mincho-BoldItalic': 'MS Mincho-BoldItalic',
14558 'MS-Mincho-Italic': 'MS Mincho-Italic',
14559 'MS-PGothic': 'MS PGothic',
14560 'MS-PGothic-Bold': 'MS PGothic-Bold',
14561 'MS-PGothic-BoldItalic': 'MS PGothic-BoldItalic',
14562 'MS-PGothic-Italic': 'MS PGothic-Italic',
14563 'MS-PMincho': 'MS PMincho',
14564 'MS-PMincho-Bold': 'MS PMincho-Bold',
14565 'MS-PMincho-BoldItalic': 'MS PMincho-BoldItalic',
14566 'MS-PMincho-Italic': 'MS PMincho-Italic',
14567 'Wingdings': 'ZapfDingbats'
14571 'Adobe Jenson': true, 'Adobe Text': true, 'Albertus': true,
14572 'Aldus': true, 'Alexandria': true, 'Algerian': true,
14573 'American Typewriter': true, 'Antiqua': true, 'Apex': true,
14574 'Arno': true, 'Aster': true, 'Aurora': true,
14575 'Baskerville': true, 'Bell': true, 'Bembo': true,
14576 'Bembo Schoolbook': true, 'Benguiat': true, 'Berkeley Old Style': true,
14577 'Bernhard Modern': true, 'Berthold City': true, 'Bodoni': true,
14578 'Bauer Bodoni': true, 'Book Antiqua': true, 'Bookman': true,
14579 'Bordeaux Roman': true, 'Californian FB': true, 'Calisto': true,
14580 'Calvert': true, 'Capitals': true, 'Cambria': true,
14581 'Cartier': true, 'Caslon': true, 'Catull': true,
14582 'Centaur': true, 'Century Old Style': true, 'Century Schoolbook': true,
14583 'Chaparral': true, 'Charis SIL': true, 'Cheltenham': true,
14584 'Cholla Slab': true, 'Clarendon': true, 'Clearface': true,
14585 'Cochin': true, 'Colonna': true, 'Computer Modern': true,
14586 'Concrete Roman': true, 'Constantia': true, 'Cooper Black': true,
14587 'Corona': true, 'Ecotype': true, 'Egyptienne': true,
14588 'Elephant': true, 'Excelsior': true, 'Fairfield': true,
14589 'FF Scala': true, 'Folkard': true, 'Footlight': true,
14590 'FreeSerif': true, 'Friz Quadrata': true, 'Garamond': true,
14591 'Gentium': true, 'Georgia': true, 'Gloucester': true,
14592 'Goudy Old Style': true, 'Goudy Schoolbook': true, 'Goudy Pro Font': true,
14593 'Granjon': true, 'Guardian Egyptian': true, 'Heather': true,
14594 'Hercules': true, 'High Tower Text': true, 'Hiroshige': true,
14595 'Hoefler Text': true, 'Humana Serif': true, 'Imprint': true,
14596 'Ionic No. 5': true, 'Janson': true, 'Joanna': true,
14597 'Korinna': true, 'Lexicon': true, 'Liberation Serif': true,
14598 'Linux Libertine': true, 'Literaturnaya': true, 'Lucida': true,
14599 'Lucida Bright': true, 'Melior': true, 'Memphis': true,
14600 'Miller': true, 'Minion': true, 'Modern': true,
14601 'Mona Lisa': true, 'Mrs Eaves': true, 'MS Serif': true,
14602 'Museo Slab': true, 'New York': true, 'Nimbus Roman': true,
14603 'NPS Rawlinson Roadway': true, 'Palatino': true, 'Perpetua': true,
14604 'Plantin': true, 'Plantin Schoolbook': true, 'Playbill': true,
14605 'Poor Richard': true, 'Rawlinson Roadway': true, 'Renault': true,
14606 'Requiem': true, 'Rockwell': true, 'Roman': true,
14607 'Rotis Serif': true, 'Sabon': true, 'Scala': true,
14608 'Seagull': true, 'Sistina': true, 'Souvenir': true,
14609 'STIX': true, 'Stone Informal': true, 'Stone Serif': true,
14610 'Sylfaen': true, 'Times': true, 'Trajan': true,
14611 'Trinité': true, 'Trump Mediaeval': true, 'Utopia': true,
14612 'Vale Type': true, 'Bitstream Vera': true, 'Vera Serif': true,
14613 'Versailles': true, 'Wanted': true, 'Weiss': true,
14614 'Wide Latin': true, 'Windsor': true, 'XITS': true
14617 var symbolsFonts = {
14618 'Dingbats': true, 'Symbol': true, 'ZapfDingbats': true
14621 // Glyph map for well-known standard fonts. Sometimes Ghostscript uses CID fonts
14622 // but does not embed the CID to GID mapping. The mapping is incomplete for all
14623 // glyphs, but common for some set of the standard fonts.
14624 var GlyphMapForStandardFonts = {
14625 '2': 10, '3': 32, '4': 33, '5': 34, '6': 35, '7': 36, '8': 37, '9': 38,
14626 '10': 39, '11': 40, '12': 41, '13': 42, '14': 43, '15': 44, '16': 45,
14627 '17': 46, '18': 47, '19': 48, '20': 49, '21': 50, '22': 51, '23': 52,
14628 '24': 53, '25': 54, '26': 55, '27': 56, '28': 57, '29': 58, '30': 894,
14629 '31': 60, '32': 61, '33': 62, '34': 63, '35': 64, '36': 65, '37': 66,
14630 '38': 67, '39': 68, '40': 69, '41': 70, '42': 71, '43': 72, '44': 73,
14631 '45': 74, '46': 75, '47': 76, '48': 77, '49': 78, '50': 79, '51': 80,
14632 '52': 81, '53': 82, '54': 83, '55': 84, '56': 85, '57': 86, '58': 87,
14633 '59': 88, '60': 89, '61': 90, '62': 91, '63': 92, '64': 93, '65': 94,
14634 '66': 95, '67': 96, '68': 97, '69': 98, '70': 99, '71': 100, '72': 101,
14635 '73': 102, '74': 103, '75': 104, '76': 105, '77': 106, '78': 107, '79': 108,
14636 '80': 109, '81': 110, '82': 111, '83': 112, '84': 113, '85': 114, '86': 115,
14637 '87': 116, '88': 117, '89': 118, '90': 119, '91': 120, '92': 121, '93': 122,
14638 '94': 123, '95': 124, '96': 125, '97': 126, '98': 196, '99': 197, '100': 199,
14639 '101': 201, '102': 209, '103': 214, '104': 220, '105': 225, '106': 224,
14640 '107': 226, '108': 228, '109': 227, '110': 229, '111': 231, '112': 233,
14641 '113': 232, '114': 234, '115': 235, '116': 237, '117': 236, '118': 238,
14642 '119': 239, '120': 241, '121': 243, '122': 242, '123': 244, '124': 246,
14643 '125': 245, '126': 250, '127': 249, '128': 251, '129': 252, '130': 8224,
14644 '131': 176, '132': 162, '133': 163, '134': 167, '135': 8226, '136': 182,
14645 '137': 223, '138': 174, '139': 169, '140': 8482, '141': 180, '142': 168,
14646 '143': 8800, '144': 198, '145': 216, '146': 8734, '147': 177, '148': 8804,
14647 '149': 8805, '150': 165, '151': 181, '152': 8706, '153': 8721, '154': 8719,
14648 '156': 8747, '157': 170, '158': 186, '159': 8486, '160': 230, '161': 248,
14649 '162': 191, '163': 161, '164': 172, '165': 8730, '166': 402, '167': 8776,
14650 '168': 8710, '169': 171, '170': 187, '171': 8230, '210': 218, '223': 711,
14651 '224': 321, '225': 322, '227': 353, '229': 382, '234': 253, '252': 263,
14652 '253': 268, '254': 269, '258': 258, '260': 260, '261': 261, '265': 280,
14653 '266': 281, '268': 283, '269': 313, '275': 323, '276': 324, '278': 328,
14654 '284': 345, '285': 346, '286': 347, '292': 367, '295': 377, '296': 378,
14655 '298': 380, '305': 963,
14656 '306': 964, '307': 966, '308': 8215, '309': 8252, '310': 8319, '311': 8359,
14657 '312': 8592, '313': 8593, '337': 9552, '493': 1039, '494': 1040, '705': 1524,
14658 '706': 8362, '710': 64288, '711': 64298, '759': 1617, '761': 1776,
14659 '763': 1778, '775': 1652, '777': 1764, '778': 1780, '779': 1781, '780': 1782,
14660 '782': 771, '783': 64726, '786': 8363, '788': 8532, '790': 768, '791': 769,
14661 '792': 768, '795': 803, '797': 64336, '798': 64337, '799': 64342,
14662 '800': 64343, '801': 64344, '802': 64345, '803': 64362, '804': 64363,
14663 '805': 64364, '2424': 7821, '2425': 7822, '2426': 7823, '2427': 7824,
14664 '2428': 7825, '2429': 7826, '2430': 7827, '2433': 7682, '2678': 8045,
14665 '2679': 8046, '2830': 1552, '2838': 686, '2840': 751, '2842': 753,
14666 '2843': 754, '2844': 755, '2846': 757, '2856': 767, '2857': 848, '2858': 849,
14667 '2862': 853, '2863': 854, '2864': 855, '2865': 861, '2866': 862, '2906': 7460,
14668 '2908': 7462, '2909': 7463, '2910': 7464, '2912': 7466, '2913': 7467,
14669 '2914': 7468, '2916': 7470, '2917': 7471, '2918': 7472, '2920': 7474,
14670 '2921': 7475, '2922': 7476, '2924': 7478, '2925': 7479, '2926': 7480,
14671 '2928': 7482, '2929': 7483, '2930': 7484, '2932': 7486, '2933': 7487,
14672 '2934': 7488, '2936': 7490, '2937': 7491, '2938': 7492, '2940': 7494,
14673 '2941': 7495, '2942': 7496, '2944': 7498, '2946': 7500, '2948': 7502,
14674 '2950': 7504, '2951': 7505, '2952': 7506, '2954': 7508, '2955': 7509,
14675 '2956': 7510, '2958': 7512, '2959': 7513, '2960': 7514, '2962': 7516,
14676 '2963': 7517, '2964': 7518, '2966': 7520, '2967': 7521, '2968': 7522,
14677 '2970': 7524, '2971': 7525, '2972': 7526, '2974': 7528, '2975': 7529,
14678 '2976': 7530, '2978': 1537, '2979': 1538, '2980': 1539, '2982': 1549,
14679 '2983': 1551, '2984': 1552, '2986': 1554, '2987': 1555, '2988': 1556,
14680 '2990': 1623, '2991': 1624, '2995': 1775, '2999': 1791, '3002': 64290,
14681 '3003': 64291, '3004': 64292, '3006': 64294, '3007': 64295, '3008': 64296,
14682 '3011': 1900, '3014': 8223, '3015': 8244, '3017': 7532, '3018': 7533,
14683 '3019': 7534, '3075': 7590, '3076': 7591, '3079': 7594, '3080': 7595,
14684 '3083': 7598, '3084': 7599, '3087': 7602, '3088': 7603, '3091': 7606,
14685 '3092': 7607, '3095': 7610, '3096': 7611, '3099': 7614, '3100': 7615,
14686 '3103': 7618, '3104': 7619, '3107': 8337, '3108': 8338, '3116': 1884,
14687 '3119': 1885, '3120': 1885, '3123': 1886, '3124': 1886, '3127': 1887,
14688 '3128': 1887, '3131': 1888, '3132': 1888, '3135': 1889, '3136': 1889,
14689 '3139': 1890, '3140': 1890, '3143': 1891, '3144': 1891, '3147': 1892,
14690 '3148': 1892, '3153': 580, '3154': 581, '3157': 584, '3158': 585, '3161': 588,
14691 '3162': 589, '3165': 891, '3166': 892, '3169': 1274, '3170': 1275,
14692 '3173': 1278, '3174': 1279, '3181': 7622, '3182': 7623, '3282': 11799,
14693 '3316': 578, '3379': 42785, '3393': 1159, '3416': 8377
14696 // Some characters, e.g. copyrightserif, are mapped to the private use area and
14697 // might not be displayed using standard fonts. Mapping/hacking well-known chars
14698 // to the similar equivalents in the normal characters range.
14699 var SpecialPUASymbols = {
14700 '63721': 0x00A9, // copyrightsans (0xF8E9) => copyright
14701 '63193': 0x00A9, // copyrightserif (0xF6D9) => copyright
14702 '63720': 0x00AE, // registersans (0xF8E8) => registered
14703 '63194': 0x00AE, // registerserif (0xF6DA) => registered
14704 '63722': 0x2122, // trademarksans (0xF8EA) => trademark
14705 '63195': 0x2122, // trademarkserif (0xF6DB) => trademark
14706 '63729': 0x23A7, // bracelefttp (0xF8F1)
14707 '63730': 0x23A8, // braceleftmid (0xF8F2)
14708 '63731': 0x23A9, // braceleftbt (0xF8F3)
14709 '63740': 0x23AB, // bracerighttp (0xF8FC)
14710 '63741': 0x23AC, // bracerightmid (0xF8FD)
14711 '63742': 0x23AD, // bracerightbt (0xF8FE)
14712 '63726': 0x23A1, // bracketlefttp (0xF8EE)
14713 '63727': 0x23A2, // bracketleftex (0xF8EF)
14714 '63728': 0x23A3, // bracketleftbt (0xF8F0)
14715 '63737': 0x23A4, // bracketrighttp (0xF8F9)
14716 '63738': 0x23A5, // bracketrightex (0xF8FA)
14717 '63739': 0x23A6, // bracketrightbt (0xF8FB)
14718 '63723': 0x239B, // parenlefttp (0xF8EB)
14719 '63724': 0x239C, // parenleftex (0xF8EC)
14720 '63725': 0x239D, // parenleftbt (0xF8ED)
14721 '63734': 0x239E, // parenrighttp (0xF8F6)
14722 '63735': 0x239F, // parenrightex (0xF8F7)
14723 '63736': 0x23A0, // parenrightbt (0xF8F8)
14725 function mapSpecialUnicodeValues(code) {
14726 if (code >= 0xFFF0 && code <= 0xFFFF) { // Specials unicode block.
14728 } else if (code >= 0xF600 && code <= 0xF8FF) {
14729 return (SpecialPUASymbols[code] || code);
14734 var UnicodeRanges = [
14735 { 'begin': 0x0000, 'end': 0x007F }, // Basic Latin
14736 { 'begin': 0x0080, 'end': 0x00FF }, // Latin-1 Supplement
14737 { 'begin': 0x0100, 'end': 0x017F }, // Latin Extended-A
14738 { 'begin': 0x0180, 'end': 0x024F }, // Latin Extended-B
14739 { 'begin': 0x0250, 'end': 0x02AF }, // IPA Extensions
14740 { 'begin': 0x02B0, 'end': 0x02FF }, // Spacing Modifier Letters
14741 { 'begin': 0x0300, 'end': 0x036F }, // Combining Diacritical Marks
14742 { 'begin': 0x0370, 'end': 0x03FF }, // Greek and Coptic
14743 { 'begin': 0x2C80, 'end': 0x2CFF }, // Coptic
14744 { 'begin': 0x0400, 'end': 0x04FF }, // Cyrillic
14745 { 'begin': 0x0530, 'end': 0x058F }, // Armenian
14746 { 'begin': 0x0590, 'end': 0x05FF }, // Hebrew
14747 { 'begin': 0xA500, 'end': 0xA63F }, // Vai
14748 { 'begin': 0x0600, 'end': 0x06FF }, // Arabic
14749 { 'begin': 0x07C0, 'end': 0x07FF }, // NKo
14750 { 'begin': 0x0900, 'end': 0x097F }, // Devanagari
14751 { 'begin': 0x0980, 'end': 0x09FF }, // Bengali
14752 { 'begin': 0x0A00, 'end': 0x0A7F }, // Gurmukhi
14753 { 'begin': 0x0A80, 'end': 0x0AFF }, // Gujarati
14754 { 'begin': 0x0B00, 'end': 0x0B7F }, // Oriya
14755 { 'begin': 0x0B80, 'end': 0x0BFF }, // Tamil
14756 { 'begin': 0x0C00, 'end': 0x0C7F }, // Telugu
14757 { 'begin': 0x0C80, 'end': 0x0CFF }, // Kannada
14758 { 'begin': 0x0D00, 'end': 0x0D7F }, // Malayalam
14759 { 'begin': 0x0E00, 'end': 0x0E7F }, // Thai
14760 { 'begin': 0x0E80, 'end': 0x0EFF }, // Lao
14761 { 'begin': 0x10A0, 'end': 0x10FF }, // Georgian
14762 { 'begin': 0x1B00, 'end': 0x1B7F }, // Balinese
14763 { 'begin': 0x1100, 'end': 0x11FF }, // Hangul Jamo
14764 { 'begin': 0x1E00, 'end': 0x1EFF }, // Latin Extended Additional
14765 { 'begin': 0x1F00, 'end': 0x1FFF }, // Greek Extended
14766 { 'begin': 0x2000, 'end': 0x206F }, // General Punctuation
14767 { 'begin': 0x2070, 'end': 0x209F }, // Superscripts And Subscripts
14768 { 'begin': 0x20A0, 'end': 0x20CF }, // Currency Symbol
14769 { 'begin': 0x20D0, 'end': 0x20FF }, // Combining Diacritical Marks For Symbols
14770 { 'begin': 0x2100, 'end': 0x214F }, // Letterlike Symbols
14771 { 'begin': 0x2150, 'end': 0x218F }, // Number Forms
14772 { 'begin': 0x2190, 'end': 0x21FF }, // Arrows
14773 { 'begin': 0x2200, 'end': 0x22FF }, // Mathematical Operators
14774 { 'begin': 0x2300, 'end': 0x23FF }, // Miscellaneous Technical
14775 { 'begin': 0x2400, 'end': 0x243F }, // Control Pictures
14776 { 'begin': 0x2440, 'end': 0x245F }, // Optical Character Recognition
14777 { 'begin': 0x2460, 'end': 0x24FF }, // Enclosed Alphanumerics
14778 { 'begin': 0x2500, 'end': 0x257F }, // Box Drawing
14779 { 'begin': 0x2580, 'end': 0x259F }, // Block Elements
14780 { 'begin': 0x25A0, 'end': 0x25FF }, // Geometric Shapes
14781 { 'begin': 0x2600, 'end': 0x26FF }, // Miscellaneous Symbols
14782 { 'begin': 0x2700, 'end': 0x27BF }, // Dingbats
14783 { 'begin': 0x3000, 'end': 0x303F }, // CJK Symbols And Punctuation
14784 { 'begin': 0x3040, 'end': 0x309F }, // Hiragana
14785 { 'begin': 0x30A0, 'end': 0x30FF }, // Katakana
14786 { 'begin': 0x3100, 'end': 0x312F }, // Bopomofo
14787 { 'begin': 0x3130, 'end': 0x318F }, // Hangul Compatibility Jamo
14788 { 'begin': 0xA840, 'end': 0xA87F }, // Phags-pa
14789 { 'begin': 0x3200, 'end': 0x32FF }, // Enclosed CJK Letters And Months
14790 { 'begin': 0x3300, 'end': 0x33FF }, // CJK Compatibility
14791 { 'begin': 0xAC00, 'end': 0xD7AF }, // Hangul Syllables
14792 { 'begin': 0xD800, 'end': 0xDFFF }, // Non-Plane 0 *
14793 { 'begin': 0x10900, 'end': 0x1091F }, // Phoenicia
14794 { 'begin': 0x4E00, 'end': 0x9FFF }, // CJK Unified Ideographs
14795 { 'begin': 0xE000, 'end': 0xF8FF }, // Private Use Area (plane 0)
14796 { 'begin': 0x31C0, 'end': 0x31EF }, // CJK Strokes
14797 { 'begin': 0xFB00, 'end': 0xFB4F }, // Alphabetic Presentation Forms
14798 { 'begin': 0xFB50, 'end': 0xFDFF }, // Arabic Presentation Forms-A
14799 { 'begin': 0xFE20, 'end': 0xFE2F }, // Combining Half Marks
14800 { 'begin': 0xFE10, 'end': 0xFE1F }, // Vertical Forms
14801 { 'begin': 0xFE50, 'end': 0xFE6F }, // Small Form Variants
14802 { 'begin': 0xFE70, 'end': 0xFEFF }, // Arabic Presentation Forms-B
14803 { 'begin': 0xFF00, 'end': 0xFFEF }, // Halfwidth And Fullwidth Forms
14804 { 'begin': 0xFFF0, 'end': 0xFFFF }, // Specials
14805 { 'begin': 0x0F00, 'end': 0x0FFF }, // Tibetan
14806 { 'begin': 0x0700, 'end': 0x074F }, // Syriac
14807 { 'begin': 0x0780, 'end': 0x07BF }, // Thaana
14808 { 'begin': 0x0D80, 'end': 0x0DFF }, // Sinhala
14809 { 'begin': 0x1000, 'end': 0x109F }, // Myanmar
14810 { 'begin': 0x1200, 'end': 0x137F }, // Ethiopic
14811 { 'begin': 0x13A0, 'end': 0x13FF }, // Cherokee
14812 { 'begin': 0x1400, 'end': 0x167F }, // Unified Canadian Aboriginal Syllabics
14813 { 'begin': 0x1680, 'end': 0x169F }, // Ogham
14814 { 'begin': 0x16A0, 'end': 0x16FF }, // Runic
14815 { 'begin': 0x1780, 'end': 0x17FF }, // Khmer
14816 { 'begin': 0x1800, 'end': 0x18AF }, // Mongolian
14817 { 'begin': 0x2800, 'end': 0x28FF }, // Braille Patterns
14818 { 'begin': 0xA000, 'end': 0xA48F }, // Yi Syllables
14819 { 'begin': 0x1700, 'end': 0x171F }, // Tagalog
14820 { 'begin': 0x10300, 'end': 0x1032F }, // Old Italic
14821 { 'begin': 0x10330, 'end': 0x1034F }, // Gothic
14822 { 'begin': 0x10400, 'end': 0x1044F }, // Deseret
14823 { 'begin': 0x1D000, 'end': 0x1D0FF }, // Byzantine Musical Symbols
14824 { 'begin': 0x1D400, 'end': 0x1D7FF }, // Mathematical Alphanumeric Symbols
14825 { 'begin': 0xFF000, 'end': 0xFFFFD }, // Private Use (plane 15)
14826 { 'begin': 0xFE00, 'end': 0xFE0F }, // Variation Selectors
14827 { 'begin': 0xE0000, 'end': 0xE007F }, // Tags
14828 { 'begin': 0x1900, 'end': 0x194F }, // Limbu
14829 { 'begin': 0x1950, 'end': 0x197F }, // Tai Le
14830 { 'begin': 0x1980, 'end': 0x19DF }, // New Tai Lue
14831 { 'begin': 0x1A00, 'end': 0x1A1F }, // Buginese
14832 { 'begin': 0x2C00, 'end': 0x2C5F }, // Glagolitic
14833 { 'begin': 0x2D30, 'end': 0x2D7F }, // Tifinagh
14834 { 'begin': 0x4DC0, 'end': 0x4DFF }, // Yijing Hexagram Symbols
14835 { 'begin': 0xA800, 'end': 0xA82F }, // Syloti Nagri
14836 { 'begin': 0x10000, 'end': 0x1007F }, // Linear B Syllabary
14837 { 'begin': 0x10140, 'end': 0x1018F }, // Ancient Greek Numbers
14838 { 'begin': 0x10380, 'end': 0x1039F }, // Ugaritic
14839 { 'begin': 0x103A0, 'end': 0x103DF }, // Old Persian
14840 { 'begin': 0x10450, 'end': 0x1047F }, // Shavian
14841 { 'begin': 0x10480, 'end': 0x104AF }, // Osmanya
14842 { 'begin': 0x10800, 'end': 0x1083F }, // Cypriot Syllabary
14843 { 'begin': 0x10A00, 'end': 0x10A5F }, // Kharoshthi
14844 { 'begin': 0x1D300, 'end': 0x1D35F }, // Tai Xuan Jing Symbols
14845 { 'begin': 0x12000, 'end': 0x123FF }, // Cuneiform
14846 { 'begin': 0x1D360, 'end': 0x1D37F }, // Counting Rod Numerals
14847 { 'begin': 0x1B80, 'end': 0x1BBF }, // Sundanese
14848 { 'begin': 0x1C00, 'end': 0x1C4F }, // Lepcha
14849 { 'begin': 0x1C50, 'end': 0x1C7F }, // Ol Chiki
14850 { 'begin': 0xA880, 'end': 0xA8DF }, // Saurashtra
14851 { 'begin': 0xA900, 'end': 0xA92F }, // Kayah Li
14852 { 'begin': 0xA930, 'end': 0xA95F }, // Rejang
14853 { 'begin': 0xAA00, 'end': 0xAA5F }, // Cham
14854 { 'begin': 0x10190, 'end': 0x101CF }, // Ancient Symbols
14855 { 'begin': 0x101D0, 'end': 0x101FF }, // Phaistos Disc
14856 { 'begin': 0x102A0, 'end': 0x102DF }, // Carian
14857 { 'begin': 0x1F030, 'end': 0x1F09F } // Domino Tiles
14860 var MacStandardGlyphOrdering = [
14861 '.notdef', '.null', 'nonmarkingreturn', 'space', 'exclam', 'quotedbl',
14862 'numbersign', 'dollar', 'percent', 'ampersand', 'quotesingle', 'parenleft',
14863 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash',
14864 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
14865 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at',
14866 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
14867 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft',
14868 'backslash', 'bracketright', 'asciicircum', 'underscore', 'grave', 'a', 'b',
14869 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
14870 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
14871 'asciitilde', 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde',
14872 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex', 'adieresis',
14873 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis',
14874 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve',
14875 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave', 'ucircumflex',
14876 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section', 'bullet',
14877 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'acute',
14878 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus', 'lessequal',
14879 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation', 'product', 'pi',
14880 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash',
14881 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin',
14882 'approxequal', 'Delta', 'guillemotleft', 'guillemotright', 'ellipsis',
14883 'nonbreakingspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash',
14884 'emdash', 'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright',
14885 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction', 'currency',
14886 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered',
14887 'quotesinglbase', 'quotedblbase', 'perthousand', 'Acircumflex',
14888 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex',
14889 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', 'Ograve', 'Uacute',
14890 'Ucircumflex', 'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron',
14891 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
14892 'Lslash', 'lslash', 'Scaron', 'scaron', 'Zcaron', 'zcaron', 'brokenbar',
14893 'Eth', 'eth', 'Yacute', 'yacute', 'Thorn', 'thorn', 'minus', 'multiply',
14894 'onesuperior', 'twosuperior', 'threesuperior', 'onehalf', 'onequarter',
14895 'threequarters', 'franc', 'Gbreve', 'gbreve', 'Idotaccent', 'Scedilla',
14896 'scedilla', 'Cacute', 'cacute', 'Ccaron', 'ccaron', 'dcroat'];
14898 function getUnicodeRangeFor(value) {
14899 for (var i = 0, ii = UnicodeRanges.length; i < ii; i++) {
14900 var range = UnicodeRanges[i];
14901 if (value >= range.begin && value < range.end) {
14908 function isRTLRangeFor(value) {
14909 var range = UnicodeRanges[13];
14910 if (value >= range.begin && value < range.end) {
14913 range = UnicodeRanges[11];
14914 if (value >= range.begin && value < range.end) {
14920 // The normalization table is obtained by filtering the Unicode characters
14921 // database with <compat> entries.
14922 var NormalizedUnicodes = {
14923 '\u00A8': '\u0020\u0308',
14924 '\u00AF': '\u0020\u0304',
14925 '\u00B4': '\u0020\u0301',
14926 '\u00B5': '\u03BC',
14927 '\u00B8': '\u0020\u0327',
14928 '\u0132': '\u0049\u004A',
14929 '\u0133': '\u0069\u006A',
14930 '\u013F': '\u004C\u00B7',
14931 '\u0140': '\u006C\u00B7',
14932 '\u0149': '\u02BC\u006E',
14933 '\u017F': '\u0073',
14934 '\u01C4': '\u0044\u017D',
14935 '\u01C5': '\u0044\u017E',
14936 '\u01C6': '\u0064\u017E',
14937 '\u01C7': '\u004C\u004A',
14938 '\u01C8': '\u004C\u006A',
14939 '\u01C9': '\u006C\u006A',
14940 '\u01CA': '\u004E\u004A',
14941 '\u01CB': '\u004E\u006A',
14942 '\u01CC': '\u006E\u006A',
14943 '\u01F1': '\u0044\u005A',
14944 '\u01F2': '\u0044\u007A',
14945 '\u01F3': '\u0064\u007A',
14946 '\u02D8': '\u0020\u0306',
14947 '\u02D9': '\u0020\u0307',
14948 '\u02DA': '\u0020\u030A',
14949 '\u02DB': '\u0020\u0328',
14950 '\u02DC': '\u0020\u0303',
14951 '\u02DD': '\u0020\u030B',
14952 '\u037A': '\u0020\u0345',
14953 '\u0384': '\u0020\u0301',
14954 '\u03D0': '\u03B2',
14955 '\u03D1': '\u03B8',
14956 '\u03D2': '\u03A5',
14957 '\u03D5': '\u03C6',
14958 '\u03D6': '\u03C0',
14959 '\u03F0': '\u03BA',
14960 '\u03F1': '\u03C1',
14961 '\u03F2': '\u03C2',
14962 '\u03F4': '\u0398',
14963 '\u03F5': '\u03B5',
14964 '\u03F9': '\u03A3',
14965 '\u0587': '\u0565\u0582',
14966 '\u0675': '\u0627\u0674',
14967 '\u0676': '\u0648\u0674',
14968 '\u0677': '\u06C7\u0674',
14969 '\u0678': '\u064A\u0674',
14970 '\u0E33': '\u0E4D\u0E32',
14971 '\u0EB3': '\u0ECD\u0EB2',
14972 '\u0EDC': '\u0EAB\u0E99',
14973 '\u0EDD': '\u0EAB\u0EA1',
14974 '\u0F77': '\u0FB2\u0F81',
14975 '\u0F79': '\u0FB3\u0F81',
14976 '\u1E9A': '\u0061\u02BE',
14977 '\u1FBD': '\u0020\u0313',
14978 '\u1FBF': '\u0020\u0313',
14979 '\u1FC0': '\u0020\u0342',
14980 '\u1FFE': '\u0020\u0314',
14981 '\u2002': '\u0020',
14982 '\u2003': '\u0020',
14983 '\u2004': '\u0020',
14984 '\u2005': '\u0020',
14985 '\u2006': '\u0020',
14986 '\u2008': '\u0020',
14987 '\u2009': '\u0020',
14988 '\u200A': '\u0020',
14989 '\u2017': '\u0020\u0333',
14990 '\u2024': '\u002E',
14991 '\u2025': '\u002E\u002E',
14992 '\u2026': '\u002E\u002E\u002E',
14993 '\u2033': '\u2032\u2032',
14994 '\u2034': '\u2032\u2032\u2032',
14995 '\u2036': '\u2035\u2035',
14996 '\u2037': '\u2035\u2035\u2035',
14997 '\u203C': '\u0021\u0021',
14998 '\u203E': '\u0020\u0305',
14999 '\u2047': '\u003F\u003F',
15000 '\u2048': '\u003F\u0021',
15001 '\u2049': '\u0021\u003F',
15002 '\u2057': '\u2032\u2032\u2032\u2032',
15003 '\u205F': '\u0020',
15004 '\u20A8': '\u0052\u0073',
15005 '\u2100': '\u0061\u002F\u0063',
15006 '\u2101': '\u0061\u002F\u0073',
15007 '\u2103': '\u00B0\u0043',
15008 '\u2105': '\u0063\u002F\u006F',
15009 '\u2106': '\u0063\u002F\u0075',
15010 '\u2107': '\u0190',
15011 '\u2109': '\u00B0\u0046',
15012 '\u2116': '\u004E\u006F',
15013 '\u2121': '\u0054\u0045\u004C',
15014 '\u2135': '\u05D0',
15015 '\u2136': '\u05D1',
15016 '\u2137': '\u05D2',
15017 '\u2138': '\u05D3',
15018 '\u213B': '\u0046\u0041\u0058',
15019 '\u2160': '\u0049',
15020 '\u2161': '\u0049\u0049',
15021 '\u2162': '\u0049\u0049\u0049',
15022 '\u2163': '\u0049\u0056',
15023 '\u2164': '\u0056',
15024 '\u2165': '\u0056\u0049',
15025 '\u2166': '\u0056\u0049\u0049',
15026 '\u2167': '\u0056\u0049\u0049\u0049',
15027 '\u2168': '\u0049\u0058',
15028 '\u2169': '\u0058',
15029 '\u216A': '\u0058\u0049',
15030 '\u216B': '\u0058\u0049\u0049',
15031 '\u216C': '\u004C',
15032 '\u216D': '\u0043',
15033 '\u216E': '\u0044',
15034 '\u216F': '\u004D',
15035 '\u2170': '\u0069',
15036 '\u2171': '\u0069\u0069',
15037 '\u2172': '\u0069\u0069\u0069',
15038 '\u2173': '\u0069\u0076',
15039 '\u2174': '\u0076',
15040 '\u2175': '\u0076\u0069',
15041 '\u2176': '\u0076\u0069\u0069',
15042 '\u2177': '\u0076\u0069\u0069\u0069',
15043 '\u2178': '\u0069\u0078',
15044 '\u2179': '\u0078',
15045 '\u217A': '\u0078\u0069',
15046 '\u217B': '\u0078\u0069\u0069',
15047 '\u217C': '\u006C',
15048 '\u217D': '\u0063',
15049 '\u217E': '\u0064',
15050 '\u217F': '\u006D',
15051 '\u222C': '\u222B\u222B',
15052 '\u222D': '\u222B\u222B\u222B',
15053 '\u222F': '\u222E\u222E',
15054 '\u2230': '\u222E\u222E\u222E',
15055 '\u2474': '\u0028\u0031\u0029',
15056 '\u2475': '\u0028\u0032\u0029',
15057 '\u2476': '\u0028\u0033\u0029',
15058 '\u2477': '\u0028\u0034\u0029',
15059 '\u2478': '\u0028\u0035\u0029',
15060 '\u2479': '\u0028\u0036\u0029',
15061 '\u247A': '\u0028\u0037\u0029',
15062 '\u247B': '\u0028\u0038\u0029',
15063 '\u247C': '\u0028\u0039\u0029',
15064 '\u247D': '\u0028\u0031\u0030\u0029',
15065 '\u247E': '\u0028\u0031\u0031\u0029',
15066 '\u247F': '\u0028\u0031\u0032\u0029',
15067 '\u2480': '\u0028\u0031\u0033\u0029',
15068 '\u2481': '\u0028\u0031\u0034\u0029',
15069 '\u2482': '\u0028\u0031\u0035\u0029',
15070 '\u2483': '\u0028\u0031\u0036\u0029',
15071 '\u2484': '\u0028\u0031\u0037\u0029',
15072 '\u2485': '\u0028\u0031\u0038\u0029',
15073 '\u2486': '\u0028\u0031\u0039\u0029',
15074 '\u2487': '\u0028\u0032\u0030\u0029',
15075 '\u2488': '\u0031\u002E',
15076 '\u2489': '\u0032\u002E',
15077 '\u248A': '\u0033\u002E',
15078 '\u248B': '\u0034\u002E',
15079 '\u248C': '\u0035\u002E',
15080 '\u248D': '\u0036\u002E',
15081 '\u248E': '\u0037\u002E',
15082 '\u248F': '\u0038\u002E',
15083 '\u2490': '\u0039\u002E',
15084 '\u2491': '\u0031\u0030\u002E',
15085 '\u2492': '\u0031\u0031\u002E',
15086 '\u2493': '\u0031\u0032\u002E',
15087 '\u2494': '\u0031\u0033\u002E',
15088 '\u2495': '\u0031\u0034\u002E',
15089 '\u2496': '\u0031\u0035\u002E',
15090 '\u2497': '\u0031\u0036\u002E',
15091 '\u2498': '\u0031\u0037\u002E',
15092 '\u2499': '\u0031\u0038\u002E',
15093 '\u249A': '\u0031\u0039\u002E',
15094 '\u249B': '\u0032\u0030\u002E',
15095 '\u249C': '\u0028\u0061\u0029',
15096 '\u249D': '\u0028\u0062\u0029',
15097 '\u249E': '\u0028\u0063\u0029',
15098 '\u249F': '\u0028\u0064\u0029',
15099 '\u24A0': '\u0028\u0065\u0029',
15100 '\u24A1': '\u0028\u0066\u0029',
15101 '\u24A2': '\u0028\u0067\u0029',
15102 '\u24A3': '\u0028\u0068\u0029',
15103 '\u24A4': '\u0028\u0069\u0029',
15104 '\u24A5': '\u0028\u006A\u0029',
15105 '\u24A6': '\u0028\u006B\u0029',
15106 '\u24A7': '\u0028\u006C\u0029',
15107 '\u24A8': '\u0028\u006D\u0029',
15108 '\u24A9': '\u0028\u006E\u0029',
15109 '\u24AA': '\u0028\u006F\u0029',
15110 '\u24AB': '\u0028\u0070\u0029',
15111 '\u24AC': '\u0028\u0071\u0029',
15112 '\u24AD': '\u0028\u0072\u0029',
15113 '\u24AE': '\u0028\u0073\u0029',
15114 '\u24AF': '\u0028\u0074\u0029',
15115 '\u24B0': '\u0028\u0075\u0029',
15116 '\u24B1': '\u0028\u0076\u0029',
15117 '\u24B2': '\u0028\u0077\u0029',
15118 '\u24B3': '\u0028\u0078\u0029',
15119 '\u24B4': '\u0028\u0079\u0029',
15120 '\u24B5': '\u0028\u007A\u0029',
15121 '\u2A0C': '\u222B\u222B\u222B\u222B',
15122 '\u2A74': '\u003A\u003A\u003D',
15123 '\u2A75': '\u003D\u003D',
15124 '\u2A76': '\u003D\u003D\u003D',
15125 '\u2E9F': '\u6BCD',
15126 '\u2EF3': '\u9F9F',
15127 '\u2F00': '\u4E00',
15128 '\u2F01': '\u4E28',
15129 '\u2F02': '\u4E36',
15130 '\u2F03': '\u4E3F',
15131 '\u2F04': '\u4E59',
15132 '\u2F05': '\u4E85',
15133 '\u2F06': '\u4E8C',
15134 '\u2F07': '\u4EA0',
15135 '\u2F08': '\u4EBA',
15136 '\u2F09': '\u513F',
15137 '\u2F0A': '\u5165',
15138 '\u2F0B': '\u516B',
15139 '\u2F0C': '\u5182',
15140 '\u2F0D': '\u5196',
15141 '\u2F0E': '\u51AB',
15142 '\u2F0F': '\u51E0',
15143 '\u2F10': '\u51F5',
15144 '\u2F11': '\u5200',
15145 '\u2F12': '\u529B',
15146 '\u2F13': '\u52F9',
15147 '\u2F14': '\u5315',
15148 '\u2F15': '\u531A',
15149 '\u2F16': '\u5338',
15150 '\u2F17': '\u5341',
15151 '\u2F18': '\u535C',
15152 '\u2F19': '\u5369',
15153 '\u2F1A': '\u5382',
15154 '\u2F1B': '\u53B6',
15155 '\u2F1C': '\u53C8',
15156 '\u2F1D': '\u53E3',
15157 '\u2F1E': '\u56D7',
15158 '\u2F1F': '\u571F',
15159 '\u2F20': '\u58EB',
15160 '\u2F21': '\u5902',
15161 '\u2F22': '\u590A',
15162 '\u2F23': '\u5915',
15163 '\u2F24': '\u5927',
15164 '\u2F25': '\u5973',
15165 '\u2F26': '\u5B50',
15166 '\u2F27': '\u5B80',
15167 '\u2F28': '\u5BF8',
15168 '\u2F29': '\u5C0F',
15169 '\u2F2A': '\u5C22',
15170 '\u2F2B': '\u5C38',
15171 '\u2F2C': '\u5C6E',
15172 '\u2F2D': '\u5C71',
15173 '\u2F2E': '\u5DDB',
15174 '\u2F2F': '\u5DE5',
15175 '\u2F30': '\u5DF1',
15176 '\u2F31': '\u5DFE',
15177 '\u2F32': '\u5E72',
15178 '\u2F33': '\u5E7A',
15179 '\u2F34': '\u5E7F',
15180 '\u2F35': '\u5EF4',
15181 '\u2F36': '\u5EFE',
15182 '\u2F37': '\u5F0B',
15183 '\u2F38': '\u5F13',
15184 '\u2F39': '\u5F50',
15185 '\u2F3A': '\u5F61',
15186 '\u2F3B': '\u5F73',
15187 '\u2F3C': '\u5FC3',
15188 '\u2F3D': '\u6208',
15189 '\u2F3E': '\u6236',
15190 '\u2F3F': '\u624B',
15191 '\u2F40': '\u652F',
15192 '\u2F41': '\u6534',
15193 '\u2F42': '\u6587',
15194 '\u2F43': '\u6597',
15195 '\u2F44': '\u65A4',
15196 '\u2F45': '\u65B9',
15197 '\u2F46': '\u65E0',
15198 '\u2F47': '\u65E5',
15199 '\u2F48': '\u66F0',
15200 '\u2F49': '\u6708',
15201 '\u2F4A': '\u6728',
15202 '\u2F4B': '\u6B20',
15203 '\u2F4C': '\u6B62',
15204 '\u2F4D': '\u6B79',
15205 '\u2F4E': '\u6BB3',
15206 '\u2F4F': '\u6BCB',
15207 '\u2F50': '\u6BD4',
15208 '\u2F51': '\u6BDB',
15209 '\u2F52': '\u6C0F',
15210 '\u2F53': '\u6C14',
15211 '\u2F54': '\u6C34',
15212 '\u2F55': '\u706B',
15213 '\u2F56': '\u722A',
15214 '\u2F57': '\u7236',
15215 '\u2F58': '\u723B',
15216 '\u2F59': '\u723F',
15217 '\u2F5A': '\u7247',
15218 '\u2F5B': '\u7259',
15219 '\u2F5C': '\u725B',
15220 '\u2F5D': '\u72AC',
15221 '\u2F5E': '\u7384',
15222 '\u2F5F': '\u7389',
15223 '\u2F60': '\u74DC',
15224 '\u2F61': '\u74E6',
15225 '\u2F62': '\u7518',
15226 '\u2F63': '\u751F',
15227 '\u2F64': '\u7528',
15228 '\u2F65': '\u7530',
15229 '\u2F66': '\u758B',
15230 '\u2F67': '\u7592',
15231 '\u2F68': '\u7676',
15232 '\u2F69': '\u767D',
15233 '\u2F6A': '\u76AE',
15234 '\u2F6B': '\u76BF',
15235 '\u2F6C': '\u76EE',
15236 '\u2F6D': '\u77DB',
15237 '\u2F6E': '\u77E2',
15238 '\u2F6F': '\u77F3',
15239 '\u2F70': '\u793A',
15240 '\u2F71': '\u79B8',
15241 '\u2F72': '\u79BE',
15242 '\u2F73': '\u7A74',
15243 '\u2F74': '\u7ACB',
15244 '\u2F75': '\u7AF9',
15245 '\u2F76': '\u7C73',
15246 '\u2F77': '\u7CF8',
15247 '\u2F78': '\u7F36',
15248 '\u2F79': '\u7F51',
15249 '\u2F7A': '\u7F8A',
15250 '\u2F7B': '\u7FBD',
15251 '\u2F7C': '\u8001',
15252 '\u2F7D': '\u800C',
15253 '\u2F7E': '\u8012',
15254 '\u2F7F': '\u8033',
15255 '\u2F80': '\u807F',
15256 '\u2F81': '\u8089',
15257 '\u2F82': '\u81E3',
15258 '\u2F83': '\u81EA',
15259 '\u2F84': '\u81F3',
15260 '\u2F85': '\u81FC',
15261 '\u2F86': '\u820C',
15262 '\u2F87': '\u821B',
15263 '\u2F88': '\u821F',
15264 '\u2F89': '\u826E',
15265 '\u2F8A': '\u8272',
15266 '\u2F8B': '\u8278',
15267 '\u2F8C': '\u864D',
15268 '\u2F8D': '\u866B',
15269 '\u2F8E': '\u8840',
15270 '\u2F8F': '\u884C',
15271 '\u2F90': '\u8863',
15272 '\u2F91': '\u897E',
15273 '\u2F92': '\u898B',
15274 '\u2F93': '\u89D2',
15275 '\u2F94': '\u8A00',
15276 '\u2F95': '\u8C37',
15277 '\u2F96': '\u8C46',
15278 '\u2F97': '\u8C55',
15279 '\u2F98': '\u8C78',
15280 '\u2F99': '\u8C9D',
15281 '\u2F9A': '\u8D64',
15282 '\u2F9B': '\u8D70',
15283 '\u2F9C': '\u8DB3',
15284 '\u2F9D': '\u8EAB',
15285 '\u2F9E': '\u8ECA',
15286 '\u2F9F': '\u8F9B',
15287 '\u2FA0': '\u8FB0',
15288 '\u2FA1': '\u8FB5',
15289 '\u2FA2': '\u9091',
15290 '\u2FA3': '\u9149',
15291 '\u2FA4': '\u91C6',
15292 '\u2FA5': '\u91CC',
15293 '\u2FA6': '\u91D1',
15294 '\u2FA7': '\u9577',
15295 '\u2FA8': '\u9580',
15296 '\u2FA9': '\u961C',
15297 '\u2FAA': '\u96B6',
15298 '\u2FAB': '\u96B9',
15299 '\u2FAC': '\u96E8',
15300 '\u2FAD': '\u9751',
15301 '\u2FAE': '\u975E',
15302 '\u2FAF': '\u9762',
15303 '\u2FB0': '\u9769',
15304 '\u2FB1': '\u97CB',
15305 '\u2FB2': '\u97ED',
15306 '\u2FB3': '\u97F3',
15307 '\u2FB4': '\u9801',
15308 '\u2FB5': '\u98A8',
15309 '\u2FB6': '\u98DB',
15310 '\u2FB7': '\u98DF',
15311 '\u2FB8': '\u9996',
15312 '\u2FB9': '\u9999',
15313 '\u2FBA': '\u99AC',
15314 '\u2FBB': '\u9AA8',
15315 '\u2FBC': '\u9AD8',
15316 '\u2FBD': '\u9ADF',
15317 '\u2FBE': '\u9B25',
15318 '\u2FBF': '\u9B2F',
15319 '\u2FC0': '\u9B32',
15320 '\u2FC1': '\u9B3C',
15321 '\u2FC2': '\u9B5A',
15322 '\u2FC3': '\u9CE5',
15323 '\u2FC4': '\u9E75',
15324 '\u2FC5': '\u9E7F',
15325 '\u2FC6': '\u9EA5',
15326 '\u2FC7': '\u9EBB',
15327 '\u2FC8': '\u9EC3',
15328 '\u2FC9': '\u9ECD',
15329 '\u2FCA': '\u9ED1',
15330 '\u2FCB': '\u9EF9',
15331 '\u2FCC': '\u9EFD',
15332 '\u2FCD': '\u9F0E',
15333 '\u2FCE': '\u9F13',
15334 '\u2FCF': '\u9F20',
15335 '\u2FD0': '\u9F3B',
15336 '\u2FD1': '\u9F4A',
15337 '\u2FD2': '\u9F52',
15338 '\u2FD3': '\u9F8D',
15339 '\u2FD4': '\u9F9C',
15340 '\u2FD5': '\u9FA0',
15341 '\u3036': '\u3012',
15342 '\u3038': '\u5341',
15343 '\u3039': '\u5344',
15344 '\u303A': '\u5345',
15345 '\u309B': '\u0020\u3099',
15346 '\u309C': '\u0020\u309A',
15347 '\u3131': '\u1100',
15348 '\u3132': '\u1101',
15349 '\u3133': '\u11AA',
15350 '\u3134': '\u1102',
15351 '\u3135': '\u11AC',
15352 '\u3136': '\u11AD',
15353 '\u3137': '\u1103',
15354 '\u3138': '\u1104',
15355 '\u3139': '\u1105',
15356 '\u313A': '\u11B0',
15357 '\u313B': '\u11B1',
15358 '\u313C': '\u11B2',
15359 '\u313D': '\u11B3',
15360 '\u313E': '\u11B4',
15361 '\u313F': '\u11B5',
15362 '\u3140': '\u111A',
15363 '\u3141': '\u1106',
15364 '\u3142': '\u1107',
15365 '\u3143': '\u1108',
15366 '\u3144': '\u1121',
15367 '\u3145': '\u1109',
15368 '\u3146': '\u110A',
15369 '\u3147': '\u110B',
15370 '\u3148': '\u110C',
15371 '\u3149': '\u110D',
15372 '\u314A': '\u110E',
15373 '\u314B': '\u110F',
15374 '\u314C': '\u1110',
15375 '\u314D': '\u1111',
15376 '\u314E': '\u1112',
15377 '\u314F': '\u1161',
15378 '\u3150': '\u1162',
15379 '\u3151': '\u1163',
15380 '\u3152': '\u1164',
15381 '\u3153': '\u1165',
15382 '\u3154': '\u1166',
15383 '\u3155': '\u1167',
15384 '\u3156': '\u1168',
15385 '\u3157': '\u1169',
15386 '\u3158': '\u116A',
15387 '\u3159': '\u116B',
15388 '\u315A': '\u116C',
15389 '\u315B': '\u116D',
15390 '\u315C': '\u116E',
15391 '\u315D': '\u116F',
15392 '\u315E': '\u1170',
15393 '\u315F': '\u1171',
15394 '\u3160': '\u1172',
15395 '\u3161': '\u1173',
15396 '\u3162': '\u1174',
15397 '\u3163': '\u1175',
15398 '\u3164': '\u1160',
15399 '\u3165': '\u1114',
15400 '\u3166': '\u1115',
15401 '\u3167': '\u11C7',
15402 '\u3168': '\u11C8',
15403 '\u3169': '\u11CC',
15404 '\u316A': '\u11CE',
15405 '\u316B': '\u11D3',
15406 '\u316C': '\u11D7',
15407 '\u316D': '\u11D9',
15408 '\u316E': '\u111C',
15409 '\u316F': '\u11DD',
15410 '\u3170': '\u11DF',
15411 '\u3171': '\u111D',
15412 '\u3172': '\u111E',
15413 '\u3173': '\u1120',
15414 '\u3174': '\u1122',
15415 '\u3175': '\u1123',
15416 '\u3176': '\u1127',
15417 '\u3177': '\u1129',
15418 '\u3178': '\u112B',
15419 '\u3179': '\u112C',
15420 '\u317A': '\u112D',
15421 '\u317B': '\u112E',
15422 '\u317C': '\u112F',
15423 '\u317D': '\u1132',
15424 '\u317E': '\u1136',
15425 '\u317F': '\u1140',
15426 '\u3180': '\u1147',
15427 '\u3181': '\u114C',
15428 '\u3182': '\u11F1',
15429 '\u3183': '\u11F2',
15430 '\u3184': '\u1157',
15431 '\u3185': '\u1158',
15432 '\u3186': '\u1159',
15433 '\u3187': '\u1184',
15434 '\u3188': '\u1185',
15435 '\u3189': '\u1188',
15436 '\u318A': '\u1191',
15437 '\u318B': '\u1192',
15438 '\u318C': '\u1194',
15439 '\u318D': '\u119E',
15440 '\u318E': '\u11A1',
15441 '\u3200': '\u0028\u1100\u0029',
15442 '\u3201': '\u0028\u1102\u0029',
15443 '\u3202': '\u0028\u1103\u0029',
15444 '\u3203': '\u0028\u1105\u0029',
15445 '\u3204': '\u0028\u1106\u0029',
15446 '\u3205': '\u0028\u1107\u0029',
15447 '\u3206': '\u0028\u1109\u0029',
15448 '\u3207': '\u0028\u110B\u0029',
15449 '\u3208': '\u0028\u110C\u0029',
15450 '\u3209': '\u0028\u110E\u0029',
15451 '\u320A': '\u0028\u110F\u0029',
15452 '\u320B': '\u0028\u1110\u0029',
15453 '\u320C': '\u0028\u1111\u0029',
15454 '\u320D': '\u0028\u1112\u0029',
15455 '\u320E': '\u0028\u1100\u1161\u0029',
15456 '\u320F': '\u0028\u1102\u1161\u0029',
15457 '\u3210': '\u0028\u1103\u1161\u0029',
15458 '\u3211': '\u0028\u1105\u1161\u0029',
15459 '\u3212': '\u0028\u1106\u1161\u0029',
15460 '\u3213': '\u0028\u1107\u1161\u0029',
15461 '\u3214': '\u0028\u1109\u1161\u0029',
15462 '\u3215': '\u0028\u110B\u1161\u0029',
15463 '\u3216': '\u0028\u110C\u1161\u0029',
15464 '\u3217': '\u0028\u110E\u1161\u0029',
15465 '\u3218': '\u0028\u110F\u1161\u0029',
15466 '\u3219': '\u0028\u1110\u1161\u0029',
15467 '\u321A': '\u0028\u1111\u1161\u0029',
15468 '\u321B': '\u0028\u1112\u1161\u0029',
15469 '\u321C': '\u0028\u110C\u116E\u0029',
15470 '\u321D': '\u0028\u110B\u1169\u110C\u1165\u11AB\u0029',
15471 '\u321E': '\u0028\u110B\u1169\u1112\u116E\u0029',
15472 '\u3220': '\u0028\u4E00\u0029',
15473 '\u3221': '\u0028\u4E8C\u0029',
15474 '\u3222': '\u0028\u4E09\u0029',
15475 '\u3223': '\u0028\u56DB\u0029',
15476 '\u3224': '\u0028\u4E94\u0029',
15477 '\u3225': '\u0028\u516D\u0029',
15478 '\u3226': '\u0028\u4E03\u0029',
15479 '\u3227': '\u0028\u516B\u0029',
15480 '\u3228': '\u0028\u4E5D\u0029',
15481 '\u3229': '\u0028\u5341\u0029',
15482 '\u322A': '\u0028\u6708\u0029',
15483 '\u322B': '\u0028\u706B\u0029',
15484 '\u322C': '\u0028\u6C34\u0029',
15485 '\u322D': '\u0028\u6728\u0029',
15486 '\u322E': '\u0028\u91D1\u0029',
15487 '\u322F': '\u0028\u571F\u0029',
15488 '\u3230': '\u0028\u65E5\u0029',
15489 '\u3231': '\u0028\u682A\u0029',
15490 '\u3232': '\u0028\u6709\u0029',
15491 '\u3233': '\u0028\u793E\u0029',
15492 '\u3234': '\u0028\u540D\u0029',
15493 '\u3235': '\u0028\u7279\u0029',
15494 '\u3236': '\u0028\u8CA1\u0029',
15495 '\u3237': '\u0028\u795D\u0029',
15496 '\u3238': '\u0028\u52B4\u0029',
15497 '\u3239': '\u0028\u4EE3\u0029',
15498 '\u323A': '\u0028\u547C\u0029',
15499 '\u323B': '\u0028\u5B66\u0029',
15500 '\u323C': '\u0028\u76E3\u0029',
15501 '\u323D': '\u0028\u4F01\u0029',
15502 '\u323E': '\u0028\u8CC7\u0029',
15503 '\u323F': '\u0028\u5354\u0029',
15504 '\u3240': '\u0028\u796D\u0029',
15505 '\u3241': '\u0028\u4F11\u0029',
15506 '\u3242': '\u0028\u81EA\u0029',
15507 '\u3243': '\u0028\u81F3\u0029',
15508 '\u32C0': '\u0031\u6708',
15509 '\u32C1': '\u0032\u6708',
15510 '\u32C2': '\u0033\u6708',
15511 '\u32C3': '\u0034\u6708',
15512 '\u32C4': '\u0035\u6708',
15513 '\u32C5': '\u0036\u6708',
15514 '\u32C6': '\u0037\u6708',
15515 '\u32C7': '\u0038\u6708',
15516 '\u32C8': '\u0039\u6708',
15517 '\u32C9': '\u0031\u0030\u6708',
15518 '\u32CA': '\u0031\u0031\u6708',
15519 '\u32CB': '\u0031\u0032\u6708',
15520 '\u3358': '\u0030\u70B9',
15521 '\u3359': '\u0031\u70B9',
15522 '\u335A': '\u0032\u70B9',
15523 '\u335B': '\u0033\u70B9',
15524 '\u335C': '\u0034\u70B9',
15525 '\u335D': '\u0035\u70B9',
15526 '\u335E': '\u0036\u70B9',
15527 '\u335F': '\u0037\u70B9',
15528 '\u3360': '\u0038\u70B9',
15529 '\u3361': '\u0039\u70B9',
15530 '\u3362': '\u0031\u0030\u70B9',
15531 '\u3363': '\u0031\u0031\u70B9',
15532 '\u3364': '\u0031\u0032\u70B9',
15533 '\u3365': '\u0031\u0033\u70B9',
15534 '\u3366': '\u0031\u0034\u70B9',
15535 '\u3367': '\u0031\u0035\u70B9',
15536 '\u3368': '\u0031\u0036\u70B9',
15537 '\u3369': '\u0031\u0037\u70B9',
15538 '\u336A': '\u0031\u0038\u70B9',
15539 '\u336B': '\u0031\u0039\u70B9',
15540 '\u336C': '\u0032\u0030\u70B9',
15541 '\u336D': '\u0032\u0031\u70B9',
15542 '\u336E': '\u0032\u0032\u70B9',
15543 '\u336F': '\u0032\u0033\u70B9',
15544 '\u3370': '\u0032\u0034\u70B9',
15545 '\u33E0': '\u0031\u65E5',
15546 '\u33E1': '\u0032\u65E5',
15547 '\u33E2': '\u0033\u65E5',
15548 '\u33E3': '\u0034\u65E5',
15549 '\u33E4': '\u0035\u65E5',
15550 '\u33E5': '\u0036\u65E5',
15551 '\u33E6': '\u0037\u65E5',
15552 '\u33E7': '\u0038\u65E5',
15553 '\u33E8': '\u0039\u65E5',
15554 '\u33E9': '\u0031\u0030\u65E5',
15555 '\u33EA': '\u0031\u0031\u65E5',
15556 '\u33EB': '\u0031\u0032\u65E5',
15557 '\u33EC': '\u0031\u0033\u65E5',
15558 '\u33ED': '\u0031\u0034\u65E5',
15559 '\u33EE': '\u0031\u0035\u65E5',
15560 '\u33EF': '\u0031\u0036\u65E5',
15561 '\u33F0': '\u0031\u0037\u65E5',
15562 '\u33F1': '\u0031\u0038\u65E5',
15563 '\u33F2': '\u0031\u0039\u65E5',
15564 '\u33F3': '\u0032\u0030\u65E5',
15565 '\u33F4': '\u0032\u0031\u65E5',
15566 '\u33F5': '\u0032\u0032\u65E5',
15567 '\u33F6': '\u0032\u0033\u65E5',
15568 '\u33F7': '\u0032\u0034\u65E5',
15569 '\u33F8': '\u0032\u0035\u65E5',
15570 '\u33F9': '\u0032\u0036\u65E5',
15571 '\u33FA': '\u0032\u0037\u65E5',
15572 '\u33FB': '\u0032\u0038\u65E5',
15573 '\u33FC': '\u0032\u0039\u65E5',
15574 '\u33FD': '\u0033\u0030\u65E5',
15575 '\u33FE': '\u0033\u0031\u65E5',
15576 '\uFB00': '\u0066\u0066',
15577 '\uFB01': '\u0066\u0069',
15578 '\uFB02': '\u0066\u006C',
15579 '\uFB03': '\u0066\u0066\u0069',
15580 '\uFB04': '\u0066\u0066\u006C',
15581 '\uFB05': '\u017F\u0074',
15582 '\uFB06': '\u0073\u0074',
15583 '\uFB13': '\u0574\u0576',
15584 '\uFB14': '\u0574\u0565',
15585 '\uFB15': '\u0574\u056B',
15586 '\uFB16': '\u057E\u0576',
15587 '\uFB17': '\u0574\u056D',
15588 '\uFB4F': '\u05D0\u05DC',
15589 '\uFB50': '\u0671',
15590 '\uFB51': '\u0671',
15591 '\uFB52': '\u067B',
15592 '\uFB53': '\u067B',
15593 '\uFB54': '\u067B',
15594 '\uFB55': '\u067B',
15595 '\uFB56': '\u067E',
15596 '\uFB57': '\u067E',
15597 '\uFB58': '\u067E',
15598 '\uFB59': '\u067E',
15599 '\uFB5A': '\u0680',
15600 '\uFB5B': '\u0680',
15601 '\uFB5C': '\u0680',
15602 '\uFB5D': '\u0680',
15603 '\uFB5E': '\u067A',
15604 '\uFB5F': '\u067A',
15605 '\uFB60': '\u067A',
15606 '\uFB61': '\u067A',
15607 '\uFB62': '\u067F',
15608 '\uFB63': '\u067F',
15609 '\uFB64': '\u067F',
15610 '\uFB65': '\u067F',
15611 '\uFB66': '\u0679',
15612 '\uFB67': '\u0679',
15613 '\uFB68': '\u0679',
15614 '\uFB69': '\u0679',
15615 '\uFB6A': '\u06A4',
15616 '\uFB6B': '\u06A4',
15617 '\uFB6C': '\u06A4',
15618 '\uFB6D': '\u06A4',
15619 '\uFB6E': '\u06A6',
15620 '\uFB6F': '\u06A6',
15621 '\uFB70': '\u06A6',
15622 '\uFB71': '\u06A6',
15623 '\uFB72': '\u0684',
15624 '\uFB73': '\u0684',
15625 '\uFB74': '\u0684',
15626 '\uFB75': '\u0684',
15627 '\uFB76': '\u0683',
15628 '\uFB77': '\u0683',
15629 '\uFB78': '\u0683',
15630 '\uFB79': '\u0683',
15631 '\uFB7A': '\u0686',
15632 '\uFB7B': '\u0686',
15633 '\uFB7C': '\u0686',
15634 '\uFB7D': '\u0686',
15635 '\uFB7E': '\u0687',
15636 '\uFB7F': '\u0687',
15637 '\uFB80': '\u0687',
15638 '\uFB81': '\u0687',
15639 '\uFB82': '\u068D',
15640 '\uFB83': '\u068D',
15641 '\uFB84': '\u068C',
15642 '\uFB85': '\u068C',
15643 '\uFB86': '\u068E',
15644 '\uFB87': '\u068E',
15645 '\uFB88': '\u0688',
15646 '\uFB89': '\u0688',
15647 '\uFB8A': '\u0698',
15648 '\uFB8B': '\u0698',
15649 '\uFB8C': '\u0691',
15650 '\uFB8D': '\u0691',
15651 '\uFB8E': '\u06A9',
15652 '\uFB8F': '\u06A9',
15653 '\uFB90': '\u06A9',
15654 '\uFB91': '\u06A9',
15655 '\uFB92': '\u06AF',
15656 '\uFB93': '\u06AF',
15657 '\uFB94': '\u06AF',
15658 '\uFB95': '\u06AF',
15659 '\uFB96': '\u06B3',
15660 '\uFB97': '\u06B3',
15661 '\uFB98': '\u06B3',
15662 '\uFB99': '\u06B3',
15663 '\uFB9A': '\u06B1',
15664 '\uFB9B': '\u06B1',
15665 '\uFB9C': '\u06B1',
15666 '\uFB9D': '\u06B1',
15667 '\uFB9E': '\u06BA',
15668 '\uFB9F': '\u06BA',
15669 '\uFBA0': '\u06BB',
15670 '\uFBA1': '\u06BB',
15671 '\uFBA2': '\u06BB',
15672 '\uFBA3': '\u06BB',
15673 '\uFBA4': '\u06C0',
15674 '\uFBA5': '\u06C0',
15675 '\uFBA6': '\u06C1',
15676 '\uFBA7': '\u06C1',
15677 '\uFBA8': '\u06C1',
15678 '\uFBA9': '\u06C1',
15679 '\uFBAA': '\u06BE',
15680 '\uFBAB': '\u06BE',
15681 '\uFBAC': '\u06BE',
15682 '\uFBAD': '\u06BE',
15683 '\uFBAE': '\u06D2',
15684 '\uFBAF': '\u06D2',
15685 '\uFBB0': '\u06D3',
15686 '\uFBB1': '\u06D3',
15687 '\uFBD3': '\u06AD',
15688 '\uFBD4': '\u06AD',
15689 '\uFBD5': '\u06AD',
15690 '\uFBD6': '\u06AD',
15691 '\uFBD7': '\u06C7',
15692 '\uFBD8': '\u06C7',
15693 '\uFBD9': '\u06C6',
15694 '\uFBDA': '\u06C6',
15695 '\uFBDB': '\u06C8',
15696 '\uFBDC': '\u06C8',
15697 '\uFBDD': '\u0677',
15698 '\uFBDE': '\u06CB',
15699 '\uFBDF': '\u06CB',
15700 '\uFBE0': '\u06C5',
15701 '\uFBE1': '\u06C5',
15702 '\uFBE2': '\u06C9',
15703 '\uFBE3': '\u06C9',
15704 '\uFBE4': '\u06D0',
15705 '\uFBE5': '\u06D0',
15706 '\uFBE6': '\u06D0',
15707 '\uFBE7': '\u06D0',
15708 '\uFBE8': '\u0649',
15709 '\uFBE9': '\u0649',
15710 '\uFBEA': '\u0626\u0627',
15711 '\uFBEB': '\u0626\u0627',
15712 '\uFBEC': '\u0626\u06D5',
15713 '\uFBED': '\u0626\u06D5',
15714 '\uFBEE': '\u0626\u0648',
15715 '\uFBEF': '\u0626\u0648',
15716 '\uFBF0': '\u0626\u06C7',
15717 '\uFBF1': '\u0626\u06C7',
15718 '\uFBF2': '\u0626\u06C6',
15719 '\uFBF3': '\u0626\u06C6',
15720 '\uFBF4': '\u0626\u06C8',
15721 '\uFBF5': '\u0626\u06C8',
15722 '\uFBF6': '\u0626\u06D0',
15723 '\uFBF7': '\u0626\u06D0',
15724 '\uFBF8': '\u0626\u06D0',
15725 '\uFBF9': '\u0626\u0649',
15726 '\uFBFA': '\u0626\u0649',
15727 '\uFBFB': '\u0626\u0649',
15728 '\uFBFC': '\u06CC',
15729 '\uFBFD': '\u06CC',
15730 '\uFBFE': '\u06CC',
15731 '\uFBFF': '\u06CC',
15732 '\uFC00': '\u0626\u062C',
15733 '\uFC01': '\u0626\u062D',
15734 '\uFC02': '\u0626\u0645',
15735 '\uFC03': '\u0626\u0649',
15736 '\uFC04': '\u0626\u064A',
15737 '\uFC05': '\u0628\u062C',
15738 '\uFC06': '\u0628\u062D',
15739 '\uFC07': '\u0628\u062E',
15740 '\uFC08': '\u0628\u0645',
15741 '\uFC09': '\u0628\u0649',
15742 '\uFC0A': '\u0628\u064A',
15743 '\uFC0B': '\u062A\u062C',
15744 '\uFC0C': '\u062A\u062D',
15745 '\uFC0D': '\u062A\u062E',
15746 '\uFC0E': '\u062A\u0645',
15747 '\uFC0F': '\u062A\u0649',
15748 '\uFC10': '\u062A\u064A',
15749 '\uFC11': '\u062B\u062C',
15750 '\uFC12': '\u062B\u0645',
15751 '\uFC13': '\u062B\u0649',
15752 '\uFC14': '\u062B\u064A',
15753 '\uFC15': '\u062C\u062D',
15754 '\uFC16': '\u062C\u0645',
15755 '\uFC17': '\u062D\u062C',
15756 '\uFC18': '\u062D\u0645',
15757 '\uFC19': '\u062E\u062C',
15758 '\uFC1A': '\u062E\u062D',
15759 '\uFC1B': '\u062E\u0645',
15760 '\uFC1C': '\u0633\u062C',
15761 '\uFC1D': '\u0633\u062D',
15762 '\uFC1E': '\u0633\u062E',
15763 '\uFC1F': '\u0633\u0645',
15764 '\uFC20': '\u0635\u062D',
15765 '\uFC21': '\u0635\u0645',
15766 '\uFC22': '\u0636\u062C',
15767 '\uFC23': '\u0636\u062D',
15768 '\uFC24': '\u0636\u062E',
15769 '\uFC25': '\u0636\u0645',
15770 '\uFC26': '\u0637\u062D',
15771 '\uFC27': '\u0637\u0645',
15772 '\uFC28': '\u0638\u0645',
15773 '\uFC29': '\u0639\u062C',
15774 '\uFC2A': '\u0639\u0645',
15775 '\uFC2B': '\u063A\u062C',
15776 '\uFC2C': '\u063A\u0645',
15777 '\uFC2D': '\u0641\u062C',
15778 '\uFC2E': '\u0641\u062D',
15779 '\uFC2F': '\u0641\u062E',
15780 '\uFC30': '\u0641\u0645',
15781 '\uFC31': '\u0641\u0649',
15782 '\uFC32': '\u0641\u064A',
15783 '\uFC33': '\u0642\u062D',
15784 '\uFC34': '\u0642\u0645',
15785 '\uFC35': '\u0642\u0649',
15786 '\uFC36': '\u0642\u064A',
15787 '\uFC37': '\u0643\u0627',
15788 '\uFC38': '\u0643\u062C',
15789 '\uFC39': '\u0643\u062D',
15790 '\uFC3A': '\u0643\u062E',
15791 '\uFC3B': '\u0643\u0644',
15792 '\uFC3C': '\u0643\u0645',
15793 '\uFC3D': '\u0643\u0649',
15794 '\uFC3E': '\u0643\u064A',
15795 '\uFC3F': '\u0644\u062C',
15796 '\uFC40': '\u0644\u062D',
15797 '\uFC41': '\u0644\u062E',
15798 '\uFC42': '\u0644\u0645',
15799 '\uFC43': '\u0644\u0649',
15800 '\uFC44': '\u0644\u064A',
15801 '\uFC45': '\u0645\u062C',
15802 '\uFC46': '\u0645\u062D',
15803 '\uFC47': '\u0645\u062E',
15804 '\uFC48': '\u0645\u0645',
15805 '\uFC49': '\u0645\u0649',
15806 '\uFC4A': '\u0645\u064A',
15807 '\uFC4B': '\u0646\u062C',
15808 '\uFC4C': '\u0646\u062D',
15809 '\uFC4D': '\u0646\u062E',
15810 '\uFC4E': '\u0646\u0645',
15811 '\uFC4F': '\u0646\u0649',
15812 '\uFC50': '\u0646\u064A',
15813 '\uFC51': '\u0647\u062C',
15814 '\uFC52': '\u0647\u0645',
15815 '\uFC53': '\u0647\u0649',
15816 '\uFC54': '\u0647\u064A',
15817 '\uFC55': '\u064A\u062C',
15818 '\uFC56': '\u064A\u062D',
15819 '\uFC57': '\u064A\u062E',
15820 '\uFC58': '\u064A\u0645',
15821 '\uFC59': '\u064A\u0649',
15822 '\uFC5A': '\u064A\u064A',
15823 '\uFC5B': '\u0630\u0670',
15824 '\uFC5C': '\u0631\u0670',
15825 '\uFC5D': '\u0649\u0670',
15826 '\uFC5E': '\u0020\u064C\u0651',
15827 '\uFC5F': '\u0020\u064D\u0651',
15828 '\uFC60': '\u0020\u064E\u0651',
15829 '\uFC61': '\u0020\u064F\u0651',
15830 '\uFC62': '\u0020\u0650\u0651',
15831 '\uFC63': '\u0020\u0651\u0670',
15832 '\uFC64': '\u0626\u0631',
15833 '\uFC65': '\u0626\u0632',
15834 '\uFC66': '\u0626\u0645',
15835 '\uFC67': '\u0626\u0646',
15836 '\uFC68': '\u0626\u0649',
15837 '\uFC69': '\u0626\u064A',
15838 '\uFC6A': '\u0628\u0631',
15839 '\uFC6B': '\u0628\u0632',
15840 '\uFC6C': '\u0628\u0645',
15841 '\uFC6D': '\u0628\u0646',
15842 '\uFC6E': '\u0628\u0649',
15843 '\uFC6F': '\u0628\u064A',
15844 '\uFC70': '\u062A\u0631',
15845 '\uFC71': '\u062A\u0632',
15846 '\uFC72': '\u062A\u0645',
15847 '\uFC73': '\u062A\u0646',
15848 '\uFC74': '\u062A\u0649',
15849 '\uFC75': '\u062A\u064A',
15850 '\uFC76': '\u062B\u0631',
15851 '\uFC77': '\u062B\u0632',
15852 '\uFC78': '\u062B\u0645',
15853 '\uFC79': '\u062B\u0646',
15854 '\uFC7A': '\u062B\u0649',
15855 '\uFC7B': '\u062B\u064A',
15856 '\uFC7C': '\u0641\u0649',
15857 '\uFC7D': '\u0641\u064A',
15858 '\uFC7E': '\u0642\u0649',
15859 '\uFC7F': '\u0642\u064A',
15860 '\uFC80': '\u0643\u0627',
15861 '\uFC81': '\u0643\u0644',
15862 '\uFC82': '\u0643\u0645',
15863 '\uFC83': '\u0643\u0649',
15864 '\uFC84': '\u0643\u064A',
15865 '\uFC85': '\u0644\u0645',
15866 '\uFC86': '\u0644\u0649',
15867 '\uFC87': '\u0644\u064A',
15868 '\uFC88': '\u0645\u0627',
15869 '\uFC89': '\u0645\u0645',
15870 '\uFC8A': '\u0646\u0631',
15871 '\uFC8B': '\u0646\u0632',
15872 '\uFC8C': '\u0646\u0645',
15873 '\uFC8D': '\u0646\u0646',
15874 '\uFC8E': '\u0646\u0649',
15875 '\uFC8F': '\u0646\u064A',
15876 '\uFC90': '\u0649\u0670',
15877 '\uFC91': '\u064A\u0631',
15878 '\uFC92': '\u064A\u0632',
15879 '\uFC93': '\u064A\u0645',
15880 '\uFC94': '\u064A\u0646',
15881 '\uFC95': '\u064A\u0649',
15882 '\uFC96': '\u064A\u064A',
15883 '\uFC97': '\u0626\u062C',
15884 '\uFC98': '\u0626\u062D',
15885 '\uFC99': '\u0626\u062E',
15886 '\uFC9A': '\u0626\u0645',
15887 '\uFC9B': '\u0626\u0647',
15888 '\uFC9C': '\u0628\u062C',
15889 '\uFC9D': '\u0628\u062D',
15890 '\uFC9E': '\u0628\u062E',
15891 '\uFC9F': '\u0628\u0645',
15892 '\uFCA0': '\u0628\u0647',
15893 '\uFCA1': '\u062A\u062C',
15894 '\uFCA2': '\u062A\u062D',
15895 '\uFCA3': '\u062A\u062E',
15896 '\uFCA4': '\u062A\u0645',
15897 '\uFCA5': '\u062A\u0647',
15898 '\uFCA6': '\u062B\u0645',
15899 '\uFCA7': '\u062C\u062D',
15900 '\uFCA8': '\u062C\u0645',
15901 '\uFCA9': '\u062D\u062C',
15902 '\uFCAA': '\u062D\u0645',
15903 '\uFCAB': '\u062E\u062C',
15904 '\uFCAC': '\u062E\u0645',
15905 '\uFCAD': '\u0633\u062C',
15906 '\uFCAE': '\u0633\u062D',
15907 '\uFCAF': '\u0633\u062E',
15908 '\uFCB0': '\u0633\u0645',
15909 '\uFCB1': '\u0635\u062D',
15910 '\uFCB2': '\u0635\u062E',
15911 '\uFCB3': '\u0635\u0645',
15912 '\uFCB4': '\u0636\u062C',
15913 '\uFCB5': '\u0636\u062D',
15914 '\uFCB6': '\u0636\u062E',
15915 '\uFCB7': '\u0636\u0645',
15916 '\uFCB8': '\u0637\u062D',
15917 '\uFCB9': '\u0638\u0645',
15918 '\uFCBA': '\u0639\u062C',
15919 '\uFCBB': '\u0639\u0645',
15920 '\uFCBC': '\u063A\u062C',
15921 '\uFCBD': '\u063A\u0645',
15922 '\uFCBE': '\u0641\u062C',
15923 '\uFCBF': '\u0641\u062D',
15924 '\uFCC0': '\u0641\u062E',
15925 '\uFCC1': '\u0641\u0645',
15926 '\uFCC2': '\u0642\u062D',
15927 '\uFCC3': '\u0642\u0645',
15928 '\uFCC4': '\u0643\u062C',
15929 '\uFCC5': '\u0643\u062D',
15930 '\uFCC6': '\u0643\u062E',
15931 '\uFCC7': '\u0643\u0644',
15932 '\uFCC8': '\u0643\u0645',
15933 '\uFCC9': '\u0644\u062C',
15934 '\uFCCA': '\u0644\u062D',
15935 '\uFCCB': '\u0644\u062E',
15936 '\uFCCC': '\u0644\u0645',
15937 '\uFCCD': '\u0644\u0647',
15938 '\uFCCE': '\u0645\u062C',
15939 '\uFCCF': '\u0645\u062D',
15940 '\uFCD0': '\u0645\u062E',
15941 '\uFCD1': '\u0645\u0645',
15942 '\uFCD2': '\u0646\u062C',
15943 '\uFCD3': '\u0646\u062D',
15944 '\uFCD4': '\u0646\u062E',
15945 '\uFCD5': '\u0646\u0645',
15946 '\uFCD6': '\u0646\u0647',
15947 '\uFCD7': '\u0647\u062C',
15948 '\uFCD8': '\u0647\u0645',
15949 '\uFCD9': '\u0647\u0670',
15950 '\uFCDA': '\u064A\u062C',
15951 '\uFCDB': '\u064A\u062D',
15952 '\uFCDC': '\u064A\u062E',
15953 '\uFCDD': '\u064A\u0645',
15954 '\uFCDE': '\u064A\u0647',
15955 '\uFCDF': '\u0626\u0645',
15956 '\uFCE0': '\u0626\u0647',
15957 '\uFCE1': '\u0628\u0645',
15958 '\uFCE2': '\u0628\u0647',
15959 '\uFCE3': '\u062A\u0645',
15960 '\uFCE4': '\u062A\u0647',
15961 '\uFCE5': '\u062B\u0645',
15962 '\uFCE6': '\u062B\u0647',
15963 '\uFCE7': '\u0633\u0645',
15964 '\uFCE8': '\u0633\u0647',
15965 '\uFCE9': '\u0634\u0645',
15966 '\uFCEA': '\u0634\u0647',
15967 '\uFCEB': '\u0643\u0644',
15968 '\uFCEC': '\u0643\u0645',
15969 '\uFCED': '\u0644\u0645',
15970 '\uFCEE': '\u0646\u0645',
15971 '\uFCEF': '\u0646\u0647',
15972 '\uFCF0': '\u064A\u0645',
15973 '\uFCF1': '\u064A\u0647',
15974 '\uFCF2': '\u0640\u064E\u0651',
15975 '\uFCF3': '\u0640\u064F\u0651',
15976 '\uFCF4': '\u0640\u0650\u0651',
15977 '\uFCF5': '\u0637\u0649',
15978 '\uFCF6': '\u0637\u064A',
15979 '\uFCF7': '\u0639\u0649',
15980 '\uFCF8': '\u0639\u064A',
15981 '\uFCF9': '\u063A\u0649',
15982 '\uFCFA': '\u063A\u064A',
15983 '\uFCFB': '\u0633\u0649',
15984 '\uFCFC': '\u0633\u064A',
15985 '\uFCFD': '\u0634\u0649',
15986 '\uFCFE': '\u0634\u064A',
15987 '\uFCFF': '\u062D\u0649',
15988 '\uFD00': '\u062D\u064A',
15989 '\uFD01': '\u062C\u0649',
15990 '\uFD02': '\u062C\u064A',
15991 '\uFD03': '\u062E\u0649',
15992 '\uFD04': '\u062E\u064A',
15993 '\uFD05': '\u0635\u0649',
15994 '\uFD06': '\u0635\u064A',
15995 '\uFD07': '\u0636\u0649',
15996 '\uFD08': '\u0636\u064A',
15997 '\uFD09': '\u0634\u062C',
15998 '\uFD0A': '\u0634\u062D',
15999 '\uFD0B': '\u0634\u062E',
16000 '\uFD0C': '\u0634\u0645',
16001 '\uFD0D': '\u0634\u0631',
16002 '\uFD0E': '\u0633\u0631',
16003 '\uFD0F': '\u0635\u0631',
16004 '\uFD10': '\u0636\u0631',
16005 '\uFD11': '\u0637\u0649',
16006 '\uFD12': '\u0637\u064A',
16007 '\uFD13': '\u0639\u0649',
16008 '\uFD14': '\u0639\u064A',
16009 '\uFD15': '\u063A\u0649',
16010 '\uFD16': '\u063A\u064A',
16011 '\uFD17': '\u0633\u0649',
16012 '\uFD18': '\u0633\u064A',
16013 '\uFD19': '\u0634\u0649',
16014 '\uFD1A': '\u0634\u064A',
16015 '\uFD1B': '\u062D\u0649',
16016 '\uFD1C': '\u062D\u064A',
16017 '\uFD1D': '\u062C\u0649',
16018 '\uFD1E': '\u062C\u064A',
16019 '\uFD1F': '\u062E\u0649',
16020 '\uFD20': '\u062E\u064A',
16021 '\uFD21': '\u0635\u0649',
16022 '\uFD22': '\u0635\u064A',
16023 '\uFD23': '\u0636\u0649',
16024 '\uFD24': '\u0636\u064A',
16025 '\uFD25': '\u0634\u062C',
16026 '\uFD26': '\u0634\u062D',
16027 '\uFD27': '\u0634\u062E',
16028 '\uFD28': '\u0634\u0645',
16029 '\uFD29': '\u0634\u0631',
16030 '\uFD2A': '\u0633\u0631',
16031 '\uFD2B': '\u0635\u0631',
16032 '\uFD2C': '\u0636\u0631',
16033 '\uFD2D': '\u0634\u062C',
16034 '\uFD2E': '\u0634\u062D',
16035 '\uFD2F': '\u0634\u062E',
16036 '\uFD30': '\u0634\u0645',
16037 '\uFD31': '\u0633\u0647',
16038 '\uFD32': '\u0634\u0647',
16039 '\uFD33': '\u0637\u0645',
16040 '\uFD34': '\u0633\u062C',
16041 '\uFD35': '\u0633\u062D',
16042 '\uFD36': '\u0633\u062E',
16043 '\uFD37': '\u0634\u062C',
16044 '\uFD38': '\u0634\u062D',
16045 '\uFD39': '\u0634\u062E',
16046 '\uFD3A': '\u0637\u0645',
16047 '\uFD3B': '\u0638\u0645',
16048 '\uFD3C': '\u0627\u064B',
16049 '\uFD3D': '\u0627\u064B',
16050 '\uFD50': '\u062A\u062C\u0645',
16051 '\uFD51': '\u062A\u062D\u062C',
16052 '\uFD52': '\u062A\u062D\u062C',
16053 '\uFD53': '\u062A\u062D\u0645',
16054 '\uFD54': '\u062A\u062E\u0645',
16055 '\uFD55': '\u062A\u0645\u062C',
16056 '\uFD56': '\u062A\u0645\u062D',
16057 '\uFD57': '\u062A\u0645\u062E',
16058 '\uFD58': '\u062C\u0645\u062D',
16059 '\uFD59': '\u062C\u0645\u062D',
16060 '\uFD5A': '\u062D\u0645\u064A',
16061 '\uFD5B': '\u062D\u0645\u0649',
16062 '\uFD5C': '\u0633\u062D\u062C',
16063 '\uFD5D': '\u0633\u062C\u062D',
16064 '\uFD5E': '\u0633\u062C\u0649',
16065 '\uFD5F': '\u0633\u0645\u062D',
16066 '\uFD60': '\u0633\u0645\u062D',
16067 '\uFD61': '\u0633\u0645\u062C',
16068 '\uFD62': '\u0633\u0645\u0645',
16069 '\uFD63': '\u0633\u0645\u0645',
16070 '\uFD64': '\u0635\u062D\u062D',
16071 '\uFD65': '\u0635\u062D\u062D',
16072 '\uFD66': '\u0635\u0645\u0645',
16073 '\uFD67': '\u0634\u062D\u0645',
16074 '\uFD68': '\u0634\u062D\u0645',
16075 '\uFD69': '\u0634\u062C\u064A',
16076 '\uFD6A': '\u0634\u0645\u062E',
16077 '\uFD6B': '\u0634\u0645\u062E',
16078 '\uFD6C': '\u0634\u0645\u0645',
16079 '\uFD6D': '\u0634\u0645\u0645',
16080 '\uFD6E': '\u0636\u062D\u0649',
16081 '\uFD6F': '\u0636\u062E\u0645',
16082 '\uFD70': '\u0636\u062E\u0645',
16083 '\uFD71': '\u0637\u0645\u062D',
16084 '\uFD72': '\u0637\u0645\u062D',
16085 '\uFD73': '\u0637\u0645\u0645',
16086 '\uFD74': '\u0637\u0645\u064A',
16087 '\uFD75': '\u0639\u062C\u0645',
16088 '\uFD76': '\u0639\u0645\u0645',
16089 '\uFD77': '\u0639\u0645\u0645',
16090 '\uFD78': '\u0639\u0645\u0649',
16091 '\uFD79': '\u063A\u0645\u0645',
16092 '\uFD7A': '\u063A\u0645\u064A',
16093 '\uFD7B': '\u063A\u0645\u0649',
16094 '\uFD7C': '\u0641\u062E\u0645',
16095 '\uFD7D': '\u0641\u062E\u0645',
16096 '\uFD7E': '\u0642\u0645\u062D',
16097 '\uFD7F': '\u0642\u0645\u0645',
16098 '\uFD80': '\u0644\u062D\u0645',
16099 '\uFD81': '\u0644\u062D\u064A',
16100 '\uFD82': '\u0644\u062D\u0649',
16101 '\uFD83': '\u0644\u062C\u062C',
16102 '\uFD84': '\u0644\u062C\u062C',
16103 '\uFD85': '\u0644\u062E\u0645',
16104 '\uFD86': '\u0644\u062E\u0645',
16105 '\uFD87': '\u0644\u0645\u062D',
16106 '\uFD88': '\u0644\u0645\u062D',
16107 '\uFD89': '\u0645\u062D\u062C',
16108 '\uFD8A': '\u0645\u062D\u0645',
16109 '\uFD8B': '\u0645\u062D\u064A',
16110 '\uFD8C': '\u0645\u062C\u062D',
16111 '\uFD8D': '\u0645\u062C\u0645',
16112 '\uFD8E': '\u0645\u062E\u062C',
16113 '\uFD8F': '\u0645\u062E\u0645',
16114 '\uFD92': '\u0645\u062C\u062E',
16115 '\uFD93': '\u0647\u0645\u062C',
16116 '\uFD94': '\u0647\u0645\u0645',
16117 '\uFD95': '\u0646\u062D\u0645',
16118 '\uFD96': '\u0646\u062D\u0649',
16119 '\uFD97': '\u0646\u062C\u0645',
16120 '\uFD98': '\u0646\u062C\u0645',
16121 '\uFD99': '\u0646\u062C\u0649',
16122 '\uFD9A': '\u0646\u0645\u064A',
16123 '\uFD9B': '\u0646\u0645\u0649',
16124 '\uFD9C': '\u064A\u0645\u0645',
16125 '\uFD9D': '\u064A\u0645\u0645',
16126 '\uFD9E': '\u0628\u062E\u064A',
16127 '\uFD9F': '\u062A\u062C\u064A',
16128 '\uFDA0': '\u062A\u062C\u0649',
16129 '\uFDA1': '\u062A\u062E\u064A',
16130 '\uFDA2': '\u062A\u062E\u0649',
16131 '\uFDA3': '\u062A\u0645\u064A',
16132 '\uFDA4': '\u062A\u0645\u0649',
16133 '\uFDA5': '\u062C\u0645\u064A',
16134 '\uFDA6': '\u062C\u062D\u0649',
16135 '\uFDA7': '\u062C\u0645\u0649',
16136 '\uFDA8': '\u0633\u062E\u0649',
16137 '\uFDA9': '\u0635\u062D\u064A',
16138 '\uFDAA': '\u0634\u062D\u064A',
16139 '\uFDAB': '\u0636\u062D\u064A',
16140 '\uFDAC': '\u0644\u062C\u064A',
16141 '\uFDAD': '\u0644\u0645\u064A',
16142 '\uFDAE': '\u064A\u062D\u064A',
16143 '\uFDAF': '\u064A\u062C\u064A',
16144 '\uFDB0': '\u064A\u0645\u064A',
16145 '\uFDB1': '\u0645\u0645\u064A',
16146 '\uFDB2': '\u0642\u0645\u064A',
16147 '\uFDB3': '\u0646\u062D\u064A',
16148 '\uFDB4': '\u0642\u0645\u062D',
16149 '\uFDB5': '\u0644\u062D\u0645',
16150 '\uFDB6': '\u0639\u0645\u064A',
16151 '\uFDB7': '\u0643\u0645\u064A',
16152 '\uFDB8': '\u0646\u062C\u062D',
16153 '\uFDB9': '\u0645\u062E\u064A',
16154 '\uFDBA': '\u0644\u062C\u0645',
16155 '\uFDBB': '\u0643\u0645\u0645',
16156 '\uFDBC': '\u0644\u062C\u0645',
16157 '\uFDBD': '\u0646\u062C\u062D',
16158 '\uFDBE': '\u062C\u062D\u064A',
16159 '\uFDBF': '\u062D\u062C\u064A',
16160 '\uFDC0': '\u0645\u062C\u064A',
16161 '\uFDC1': '\u0641\u0645\u064A',
16162 '\uFDC2': '\u0628\u062D\u064A',
16163 '\uFDC3': '\u0643\u0645\u0645',
16164 '\uFDC4': '\u0639\u062C\u0645',
16165 '\uFDC5': '\u0635\u0645\u0645',
16166 '\uFDC6': '\u0633\u062E\u064A',
16167 '\uFDC7': '\u0646\u062C\u064A',
16168 '\uFE49': '\u203E',
16169 '\uFE4A': '\u203E',
16170 '\uFE4B': '\u203E',
16171 '\uFE4C': '\u203E',
16172 '\uFE4D': '\u005F',
16173 '\uFE4E': '\u005F',
16174 '\uFE4F': '\u005F',
16175 '\uFE80': '\u0621',
16176 '\uFE81': '\u0622',
16177 '\uFE82': '\u0622',
16178 '\uFE83': '\u0623',
16179 '\uFE84': '\u0623',
16180 '\uFE85': '\u0624',
16181 '\uFE86': '\u0624',
16182 '\uFE87': '\u0625',
16183 '\uFE88': '\u0625',
16184 '\uFE89': '\u0626',
16185 '\uFE8A': '\u0626',
16186 '\uFE8B': '\u0626',
16187 '\uFE8C': '\u0626',
16188 '\uFE8D': '\u0627',
16189 '\uFE8E': '\u0627',
16190 '\uFE8F': '\u0628',
16191 '\uFE90': '\u0628',
16192 '\uFE91': '\u0628',
16193 '\uFE92': '\u0628',
16194 '\uFE93': '\u0629',
16195 '\uFE94': '\u0629',
16196 '\uFE95': '\u062A',
16197 '\uFE96': '\u062A',
16198 '\uFE97': '\u062A',
16199 '\uFE98': '\u062A',
16200 '\uFE99': '\u062B',
16201 '\uFE9A': '\u062B',
16202 '\uFE9B': '\u062B',
16203 '\uFE9C': '\u062B',
16204 '\uFE9D': '\u062C',
16205 '\uFE9E': '\u062C',
16206 '\uFE9F': '\u062C',
16207 '\uFEA0': '\u062C',
16208 '\uFEA1': '\u062D',
16209 '\uFEA2': '\u062D',
16210 '\uFEA3': '\u062D',
16211 '\uFEA4': '\u062D',
16212 '\uFEA5': '\u062E',
16213 '\uFEA6': '\u062E',
16214 '\uFEA7': '\u062E',
16215 '\uFEA8': '\u062E',
16216 '\uFEA9': '\u062F',
16217 '\uFEAA': '\u062F',
16218 '\uFEAB': '\u0630',
16219 '\uFEAC': '\u0630',
16220 '\uFEAD': '\u0631',
16221 '\uFEAE': '\u0631',
16222 '\uFEAF': '\u0632',
16223 '\uFEB0': '\u0632',
16224 '\uFEB1': '\u0633',
16225 '\uFEB2': '\u0633',
16226 '\uFEB3': '\u0633',
16227 '\uFEB4': '\u0633',
16228 '\uFEB5': '\u0634',
16229 '\uFEB6': '\u0634',
16230 '\uFEB7': '\u0634',
16231 '\uFEB8': '\u0634',
16232 '\uFEB9': '\u0635',
16233 '\uFEBA': '\u0635',
16234 '\uFEBB': '\u0635',
16235 '\uFEBC': '\u0635',
16236 '\uFEBD': '\u0636',
16237 '\uFEBE': '\u0636',
16238 '\uFEBF': '\u0636',
16239 '\uFEC0': '\u0636',
16240 '\uFEC1': '\u0637',
16241 '\uFEC2': '\u0637',
16242 '\uFEC3': '\u0637',
16243 '\uFEC4': '\u0637',
16244 '\uFEC5': '\u0638',
16245 '\uFEC6': '\u0638',
16246 '\uFEC7': '\u0638',
16247 '\uFEC8': '\u0638',
16248 '\uFEC9': '\u0639',
16249 '\uFECA': '\u0639',
16250 '\uFECB': '\u0639',
16251 '\uFECC': '\u0639',
16252 '\uFECD': '\u063A',
16253 '\uFECE': '\u063A',
16254 '\uFECF': '\u063A',
16255 '\uFED0': '\u063A',
16256 '\uFED1': '\u0641',
16257 '\uFED2': '\u0641',
16258 '\uFED3': '\u0641',
16259 '\uFED4': '\u0641',
16260 '\uFED5': '\u0642',
16261 '\uFED6': '\u0642',
16262 '\uFED7': '\u0642',
16263 '\uFED8': '\u0642',
16264 '\uFED9': '\u0643',
16265 '\uFEDA': '\u0643',
16266 '\uFEDB': '\u0643',
16267 '\uFEDC': '\u0643',
16268 '\uFEDD': '\u0644',
16269 '\uFEDE': '\u0644',
16270 '\uFEDF': '\u0644',
16271 '\uFEE0': '\u0644',
16272 '\uFEE1': '\u0645',
16273 '\uFEE2': '\u0645',
16274 '\uFEE3': '\u0645',
16275 '\uFEE4': '\u0645',
16276 '\uFEE5': '\u0646',
16277 '\uFEE6': '\u0646',
16278 '\uFEE7': '\u0646',
16279 '\uFEE8': '\u0646',
16280 '\uFEE9': '\u0647',
16281 '\uFEEA': '\u0647',
16282 '\uFEEB': '\u0647',
16283 '\uFEEC': '\u0647',
16284 '\uFEED': '\u0648',
16285 '\uFEEE': '\u0648',
16286 '\uFEEF': '\u0649',
16287 '\uFEF0': '\u0649',
16288 '\uFEF1': '\u064A',
16289 '\uFEF2': '\u064A',
16290 '\uFEF3': '\u064A',
16291 '\uFEF4': '\u064A',
16292 '\uFEF5': '\u0644\u0622',
16293 '\uFEF6': '\u0644\u0622',
16294 '\uFEF7': '\u0644\u0623',
16295 '\uFEF8': '\u0644\u0623',
16296 '\uFEF9': '\u0644\u0625',
16297 '\uFEFA': '\u0644\u0625',
16298 '\uFEFB': '\u0644\u0627',
16299 '\uFEFC': '\u0644\u0627'
16302 function reverseIfRtl(chars) {
16303 var charsLength = chars.length;
16304 //reverse an arabic ligature
16305 if (charsLength <= 1 || !isRTLRangeFor(chars.charCodeAt(0))) {
16309 for (var ii = charsLength - 1; ii >= 0; ii--) {
16315 function adjustWidths(properties) {
16316 if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) {
16319 // adjusting width to fontMatrix scale
16320 var scale = 0.001 / properties.fontMatrix[0];
16321 var glyphsWidths = properties.widths;
16322 for (var glyph in glyphsWidths) {
16323 glyphsWidths[glyph] *= scale;
16325 properties.defaultWidth *= scale;
16328 function getFontType(type, subtype) {
16331 return subtype === 'Type1C' ? FontType.TYPE1C : FontType.TYPE1;
16332 case 'CIDFontType0':
16333 return subtype === 'CIDFontType0C' ? FontType.CIDFONTTYPE0C :
16334 FontType.CIDFONTTYPE0;
16336 return FontType.OPENTYPE;
16338 return FontType.TRUETYPE;
16339 case 'CIDFontType2':
16340 return FontType.CIDFONTTYPE2;
16342 return FontType.MMTYPE1;
16344 return FontType.TYPE0;
16346 return FontType.UNKNOWN;
16350 var Glyph = (function GlyphClosure() {
16351 function Glyph(fontChar, unicode, accent, width, vmetric, operatorListId) {
16352 this.fontChar = fontChar;
16353 this.unicode = unicode;
16354 this.accent = accent;
16355 this.width = width;
16356 this.vmetric = vmetric;
16357 this.operatorListId = operatorListId;
16360 Glyph.prototype.matchesForCache =
16361 function(fontChar, unicode, accent, width, vmetric, operatorListId) {
16362 return this.fontChar === fontChar &&
16363 this.unicode === unicode &&
16364 this.accent === accent &&
16365 this.width === width &&
16366 this.vmetric === vmetric &&
16367 this.operatorListId === operatorListId;
16373 var ToUnicodeMap = (function ToUnicodeMapClosure() {
16374 function ToUnicodeMap(cmap) {
16375 // The elements of this._map can be integers or strings, depending on how
16376 // |cmap| was created.
16380 ToUnicodeMap.prototype = {
16382 return this._map.length;
16385 forEach: function(callback) {
16386 for (var charCode in this._map) {
16387 callback(charCode, this._map[charCode].charCodeAt(0));
16392 return this._map[i] !== undefined;
16396 return this._map[i];
16399 charCodeOf: function(v) {
16400 return this._map.indexOf(v);
16404 return ToUnicodeMap;
16407 var IdentityToUnicodeMap = (function IdentityToUnicodeMapClosure() {
16408 function IdentityToUnicodeMap(firstChar, lastChar) {
16409 this.firstChar = firstChar;
16410 this.lastChar = lastChar;
16413 IdentityToUnicodeMap.prototype = {
16415 return (this.lastChar + 1) - this.firstChar;
16418 forEach: function (callback) {
16419 for (var i = this.firstChar, ii = this.lastChar; i <= ii; i++) {
16424 has: function (i) {
16425 return this.firstChar <= i && i <= this.lastChar;
16428 get: function (i) {
16429 if (this.firstChar <= i && i <= this.lastChar) {
16430 return String.fromCharCode(i);
16435 charCodeOf: function (v) {
16436 error('should not call .charCodeOf');
16440 return IdentityToUnicodeMap;
16443 var OpenTypeFileBuilder = (function OpenTypeFileBuilderClosure() {
16444 function writeInt16(dest, offset, num) {
16445 dest[offset] = (num >> 8) & 0xFF;
16446 dest[offset + 1] = num & 0xFF;
16449 function writeInt32(dest, offset, num) {
16450 dest[offset] = (num >> 24) & 0xFF;
16451 dest[offset + 1] = (num >> 16) & 0xFF;
16452 dest[offset + 2] = (num >> 8) & 0xFF;
16453 dest[offset + 3] = num & 0xFF;
16456 function writeData(dest, offset, data) {
16458 if (data instanceof Uint8Array) {
16459 dest.set(data, offset);
16460 } else if (typeof data === 'string') {
16461 for (i = 0, ii = data.length; i < ii; i++) {
16462 dest[offset++] = data.charCodeAt(i) & 0xFF;
16465 // treating everything else as array
16466 for (i = 0, ii = data.length; i < ii; i++) {
16467 dest[offset++] = data[i] & 0xFF;
16472 function OpenTypeFileBuilder(sfnt) {
16474 this.tables = Object.create(null);
16477 OpenTypeFileBuilder.getSearchParams =
16478 function OpenTypeFileBuilder_getSearchParams(entriesCount, entrySize) {
16479 var maxPower2 = 1, log2 = 0;
16480 while ((maxPower2 ^ entriesCount) > maxPower2) {
16484 var searchRange = maxPower2 * entrySize;
16486 range: searchRange,
16488 rangeShift: entrySize * entriesCount - searchRange
16492 var OTF_HEADER_SIZE = 12;
16493 var OTF_TABLE_ENTRY_SIZE = 16;
16495 OpenTypeFileBuilder.prototype = {
16496 toArray: function OpenTypeFileBuilder_toArray() {
16497 var sfnt = this.sfnt;
16499 // Tables needs to be written by ascendant alphabetic order
16500 var tables = this.tables;
16501 var tablesNames = Object.keys(tables);
16502 tablesNames.sort();
16503 var numTables = tablesNames.length;
16505 var i, j, jj, table, tableName;
16506 // layout the tables data
16507 var offset = OTF_HEADER_SIZE + numTables * OTF_TABLE_ENTRY_SIZE;
16508 var tableOffsets = [offset];
16509 for (i = 0; i < numTables; i++) {
16510 table = tables[tablesNames[i]];
16511 var paddedLength = ((table.length + 3) & ~3) >>> 0;
16512 offset += paddedLength;
16513 tableOffsets.push(offset);
16516 var file = new Uint8Array(offset);
16517 // write the table data first (mostly for checksum)
16518 for (i = 0; i < numTables; i++) {
16519 table = tables[tablesNames[i]];
16520 writeData(file, tableOffsets[i], table);
16523 // sfnt version (4 bytes)
16524 if (sfnt === 'true') {
16525 // Windows hates the Mac TrueType sfnt version number
16526 sfnt = string32(0x00010000);
16528 file[0] = sfnt.charCodeAt(0) & 0xFF;
16529 file[1] = sfnt.charCodeAt(1) & 0xFF;
16530 file[2] = sfnt.charCodeAt(2) & 0xFF;
16531 file[3] = sfnt.charCodeAt(3) & 0xFF;
16533 // numTables (2 bytes)
16534 writeInt16(file, 4, numTables);
16536 var searchParams = OpenTypeFileBuilder.getSearchParams(numTables, 16);
16538 // searchRange (2 bytes)
16539 writeInt16(file, 6, searchParams.range);
16540 // entrySelector (2 bytes)
16541 writeInt16(file, 8, searchParams.entry);
16542 // rangeShift (2 bytes)
16543 writeInt16(file, 10, searchParams.rangeShift);
16545 offset = OTF_HEADER_SIZE;
16546 // writing table entries
16547 for (i = 0; i < numTables; i++) {
16548 tableName = tablesNames[i];
16549 file[offset] = tableName.charCodeAt(0) & 0xFF;
16550 file[offset + 1] = tableName.charCodeAt(1) & 0xFF;
16551 file[offset + 2] = tableName.charCodeAt(2) & 0xFF;
16552 file[offset + 3] = tableName.charCodeAt(3) & 0xFF;
16556 for (j = tableOffsets[i], jj = tableOffsets[i + 1]; j < jj; j += 4) {
16557 var quad = (file[j] << 24) + (file[j + 1] << 16) +
16558 (file[j + 2] << 8) + file[j + 3];
16559 checksum = (checksum + quad) | 0;
16561 writeInt32(file, offset + 4, checksum);
16564 writeInt32(file, offset + 8, tableOffsets[i]);
16566 writeInt32(file, offset + 12, tables[tableName].length);
16568 offset += OTF_TABLE_ENTRY_SIZE;
16573 addTable: function OpenTypeFileBuilder_addTable(tag, data) {
16574 if (tag in this.tables) {
16575 throw new Error('Table ' + tag + ' already exists');
16577 this.tables[tag] = data;
16581 return OpenTypeFileBuilder;
16585 * 'Font' is the class the outside world should use, it encapsulate all the font
16586 * decoding logics whatever type it is (assuming the font type is supported).
16588 * For example to read a Type1 font and to attach it to the document:
16589 * var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
16590 * type1Font.bind();
16592 var Font = (function FontClosure() {
16593 function Font(name, file, properties) {
16594 var charCode, glyphName, fontChar;
16597 this.loadedName = properties.loadedName;
16598 this.isType3Font = properties.isType3Font;
16601 this.glyphCache = {};
16603 var names = name.split('+');
16604 names = names.length > 1 ? names[1] : names[0];
16605 names = names.split(/[-,_]/g)[0];
16606 this.isSerifFont = !!(properties.flags & FontFlags.Serif);
16607 this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
16608 this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
16610 var type = properties.type;
16611 var subtype = properties.subtype;
16614 this.fallbackName = (this.isMonospace ? 'monospace' :
16615 (this.isSerifFont ? 'serif' : 'sans-serif'));
16617 this.differences = properties.differences;
16618 this.widths = properties.widths;
16619 this.defaultWidth = properties.defaultWidth;
16620 this.composite = properties.composite;
16621 this.wideChars = properties.wideChars;
16622 this.cMap = properties.cMap;
16623 this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
16624 this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
16625 this.fontMatrix = properties.fontMatrix;
16626 this.bbox = properties.bbox;
16628 this.toUnicode = properties.toUnicode = this.buildToUnicode(properties);
16630 this.toFontChar = [];
16632 if (properties.type === 'Type3') {
16633 for (charCode = 0; charCode < 256; charCode++) {
16634 this.toFontChar[charCode] = (this.differences[charCode] ||
16635 properties.defaultEncoding[charCode]);
16637 this.fontType = FontType.TYPE3;
16641 this.cidEncoding = properties.cidEncoding;
16642 this.vertical = properties.vertical;
16643 if (this.vertical) {
16644 this.vmetrics = properties.vmetrics;
16645 this.defaultVMetrics = properties.defaultVMetrics;
16648 if (!file || file.isEmpty) {
16650 // Some bad PDF generators will include empty font files,
16651 // attempting to recover by assuming that no file exists.
16652 warn('Font file is empty in "' + name + '" (' + this.loadedName + ')');
16655 this.missingFile = true;
16656 // The file data is not specified. Trying to fix the font name
16657 // to be used with the canvas.font.
16658 var fontName = name.replace(/[,_]/g, '-');
16659 var isStandardFont = !!stdFontMap[fontName] ||
16660 !!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);
16661 fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
16663 this.bold = (fontName.search(/bold/gi) !== -1);
16664 this.italic = ((fontName.search(/oblique/gi) !== -1) ||
16665 (fontName.search(/italic/gi) !== -1));
16667 // Use 'name' instead of 'fontName' here because the original
16668 // name ArialBlack for example will be replaced by Helvetica.
16669 this.black = (name.search(/Black/g) !== -1);
16671 // if at least one width is present, remeasure all chars when exists
16672 this.remeasure = Object.keys(this.widths).length > 0;
16673 if (isStandardFont && type === 'CIDFontType2' &&
16674 properties.cidEncoding.indexOf('Identity-') === 0) {
16675 // Standard fonts might be embedded as CID font without glyph mapping.
16676 // Building one based on GlyphMapForStandardFonts.
16678 for (var code in GlyphMapForStandardFonts) {
16679 map[+code] = GlyphMapForStandardFonts[code];
16681 var isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
16682 if (!isIdentityUnicode) {
16683 this.toUnicode.forEach(function(charCode, unicodeCharCode) {
16684 map[+charCode] = unicodeCharCode;
16687 this.toFontChar = map;
16688 this.toUnicode = new ToUnicodeMap(map);
16689 } else if (/Symbol/i.test(fontName)) {
16690 var symbols = Encodings.SymbolSetEncoding;
16691 for (charCode in symbols) {
16692 fontChar = GlyphsUnicode[symbols[charCode]];
16696 this.toFontChar[charCode] = fontChar;
16698 for (charCode in properties.differences) {
16699 fontChar = GlyphsUnicode[properties.differences[charCode]];
16703 this.toFontChar[charCode] = fontChar;
16705 } else if (/Dingbats/i.test(fontName)) {
16706 if (/Wingdings/i.test(name)) {
16707 warn('Wingdings font without embedded font file, ' +
16708 'falling back to the ZapfDingbats encoding.');
16710 var dingbats = Encodings.ZapfDingbatsEncoding;
16711 for (charCode in dingbats) {
16712 fontChar = DingbatsGlyphsUnicode[dingbats[charCode]];
16716 this.toFontChar[charCode] = fontChar;
16718 for (charCode in properties.differences) {
16719 fontChar = DingbatsGlyphsUnicode[properties.differences[charCode]];
16723 this.toFontChar[charCode] = fontChar;
16725 } else if (isStandardFont) {
16726 this.toFontChar = [];
16727 for (charCode in properties.defaultEncoding) {
16728 glyphName = (properties.differences[charCode] ||
16729 properties.defaultEncoding[charCode]);
16730 this.toFontChar[charCode] = GlyphsUnicode[glyphName];
16733 var unicodeCharCode, notCidFont = (type.indexOf('CIDFontType') === -1);
16734 this.toUnicode.forEach(function(charCode, unicodeCharCode) {
16736 glyphName = (properties.differences[charCode] ||
16737 properties.defaultEncoding[charCode]);
16738 unicodeCharCode = (GlyphsUnicode[glyphName] || unicodeCharCode);
16740 this.toFontChar[charCode] = unicodeCharCode;
16743 this.loadedName = fontName.split('-')[0];
16744 this.loading = false;
16745 this.fontType = getFontType(type, subtype);
16749 // Some fonts might use wrong font types for Type1C or CIDFontType0C
16750 if (subtype === 'Type1C' && (type !== 'Type1' && type !== 'MMType1')) {
16751 // Some TrueType fonts by mistake claim Type1C
16752 if (isTrueTypeFile(file)) {
16753 subtype = 'TrueType';
16758 if (subtype === 'CIDFontType0C' && type !== 'CIDFontType0') {
16759 type = 'CIDFontType0';
16761 if (subtype === 'OpenType') {
16764 // Some CIDFontType0C fonts by mistake claim CIDFontType0.
16765 if (type === 'CIDFontType0') {
16766 subtype = isType1File(file) ? 'CIDFontType0' : 'CIDFontType0C';
16772 info('MMType1 font (' + name + '), falling back to Type1.');
16773 /* falls through */
16775 case 'CIDFontType0':
16776 this.mimetype = 'font/opentype';
16778 var cff = (subtype === 'Type1C' || subtype === 'CIDFontType0C') ?
16779 new CFFFont(file, properties) : new Type1Font(name, file, properties);
16781 adjustWidths(properties);
16783 // Wrap the CFF data inside an OTF font file
16784 data = this.convert(name, cff, properties);
16789 case 'CIDFontType2':
16790 this.mimetype = 'font/opentype';
16792 // Repair the TrueType file. It is can be damaged in the point of
16793 // view of the sanitizer
16794 data = this.checkAndRepair(name, file, properties);
16795 if (this.isOpenType) {
16801 error('Font ' + type + ' is not supported');
16806 this.fontType = getFontType(type, subtype);
16808 // Transfer some properties again that could change during font conversion
16809 this.fontMatrix = properties.fontMatrix;
16810 this.widths = properties.widths;
16811 this.defaultWidth = properties.defaultWidth;
16812 this.encoding = properties.baseEncoding;
16813 this.seacMap = properties.seacMap;
16815 this.loading = true;
16818 Font.getFontID = (function () {
16820 return function Font_getFontID() {
16821 return String(ID++);
16825 function int16(b0, b1) {
16826 return (b0 << 8) + b1;
16829 function int32(b0, b1, b2, b3) {
16830 return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
16833 function string16(value) {
16834 return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
16837 function safeString16(value) {
16838 // clamp value to the 16-bit int range
16839 value = (value > 0x7FFF ? 0x7FFF : (value < -0x8000 ? -0x8000 : value));
16840 return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
16843 function isTrueTypeFile(file) {
16844 var header = file.peekBytes(4);
16845 return readUint32(header, 0) === 0x00010000;
16848 function isType1File(file) {
16849 var header = file.peekBytes(2);
16850 // All Type1 font programs must begin with the comment '%!' (0x25 + 0x21).
16851 if (header[0] === 0x25 && header[1] === 0x21) {
16854 // ... obviously some fonts violate that part of the specification,
16855 // please refer to the comment in |Type1Font| below.
16856 if (header[0] === 0x80 && header[1] === 0x01) { // pfb file header.
16863 * Helper function for |adjustMapping|.
16864 * @return {boolean}
16866 function isProblematicUnicodeLocation(code) {
16867 if (code <= 0x1F) { // Control chars
16870 if (code >= 0x80 && code <= 0x9F) { // Control chars
16873 if ((code >= 0x2000 && code <= 0x200F) || // General punctuation chars
16874 (code >= 0x2028 && code <= 0x202F) ||
16875 (code >= 0x2060 && code <= 0x206F)) {
16878 if (code >= 0xFFF0 && code <= 0xFFFF) { // Specials Unicode block
16882 case 0x7F: // Control char
16883 case 0xA0: // Non breaking space
16884 case 0xAD: // Soft hyphen
16885 case 0x0E33: // Thai character SARA AM
16886 case 0x2011: // Non breaking hyphen
16887 case 0x205F: // Medium mathematical space
16888 case 0x25CC: // Dotted circle (combining mark)
16895 * Rebuilds the char code to glyph ID map by trying to replace the char codes
16896 * with their unicode value. It also moves char codes that are in known
16897 * problematic locations.
16898 * @return {Object} Two properties:
16899 * 'toFontChar' - maps original char codes(the value that will be read
16900 * from commands such as show text) to the char codes that will be used in the
16901 * font that we build
16902 * 'charCodeToGlyphId' - maps the new font char codes to glyph ids
16904 function adjustMapping(charCodeToGlyphId, properties) {
16905 var toUnicode = properties.toUnicode;
16906 var isSymbolic = !!(properties.flags & FontFlags.Symbolic);
16907 var isIdentityUnicode =
16908 properties.toUnicode instanceof IdentityToUnicodeMap;
16909 var newMap = Object.create(null);
16910 var toFontChar = [];
16911 var usedFontCharCodes = [];
16912 var nextAvailableFontCharCode = PRIVATE_USE_OFFSET_START;
16913 for (var originalCharCode in charCodeToGlyphId) {
16914 originalCharCode |= 0;
16915 var glyphId = charCodeToGlyphId[originalCharCode];
16916 var fontCharCode = originalCharCode;
16917 // First try to map the value to a unicode position if a non identity map
16919 if (!isIdentityUnicode && toUnicode.has(originalCharCode)) {
16920 var unicode = toUnicode.get(fontCharCode);
16921 // TODO: Try to map ligatures to the correct spot.
16922 if (unicode.length === 1) {
16923 fontCharCode = unicode.charCodeAt(0);
16926 // Try to move control characters, special characters and already mapped
16927 // characters to the private use area since they will not be drawn by
16928 // canvas if left in their current position. Also, move characters if the
16929 // font was symbolic and there is only an identity unicode map since the
16930 // characters probably aren't in the correct position (fixes an issue
16931 // with firefox and thuluthfont).
16932 if ((usedFontCharCodes[fontCharCode] !== undefined ||
16933 isProblematicUnicodeLocation(fontCharCode) ||
16934 (isSymbolic && isIdentityUnicode)) &&
16935 nextAvailableFontCharCode <= PRIVATE_USE_OFFSET_END) { // Room left.
16936 // Loop to try and find a free spot in the private use area.
16938 fontCharCode = nextAvailableFontCharCode++;
16940 if (SKIP_PRIVATE_USE_RANGE_F000_TO_F01F && fontCharCode === 0xF000) {
16941 fontCharCode = 0xF020;
16942 nextAvailableFontCharCode = fontCharCode + 1;
16945 } while (usedFontCharCodes[fontCharCode] !== undefined &&
16946 nextAvailableFontCharCode <= PRIVATE_USE_OFFSET_END);
16949 newMap[fontCharCode] = glyphId;
16950 toFontChar[originalCharCode] = fontCharCode;
16951 usedFontCharCodes[fontCharCode] = true;
16954 toFontChar: toFontChar,
16955 charCodeToGlyphId: newMap,
16956 nextAvailableFontCharCode: nextAvailableFontCharCode
16960 function getRanges(glyphs) {
16961 // Array.sort() sorts by characters, not numerically, so convert to an
16962 // array of characters.
16964 for (var charCode in glyphs) {
16965 codes.push({ fontCharCode: charCode | 0, glyphId: glyphs[charCode] });
16967 codes.sort(function fontGetRangesSort(a, b) {
16968 return a.fontCharCode - b.fontCharCode;
16971 // Split the sorted codes into ranges.
16973 var length = codes.length;
16974 for (var n = 0; n < length; ) {
16975 var start = codes[n].fontCharCode;
16976 var codeIndices = [codes[n].glyphId];
16979 while (n < length && end + 1 === codes[n].fontCharCode) {
16980 codeIndices.push(codes[n].glyphId);
16983 if (end === 0xFFFF) {
16987 ranges.push([start, end, codeIndices]);
16993 function createCmapTable(glyphs) {
16994 var ranges = getRanges(glyphs);
16995 var numTables = ranges[ranges.length - 1][1] > 0xFFFF ? 2 : 1;
16996 var cmap = '\x00\x00' + // version
16997 string16(numTables) + // numTables
16998 '\x00\x03' + // platformID
16999 '\x00\x01' + // encodingID
17000 string32(4 + numTables * 8); // start of the table record
17003 for (i = ranges.length - 1; i >= 0; --i) {
17004 if (ranges[i][0] <= 0xFFFF) { break; }
17006 var bmpLength = i + 1;
17008 if (ranges[i][0] < 0xFFFF && ranges[i][1] === 0xFFFF) {
17009 ranges[i][1] = 0xFFFE;
17011 var trailingRangesCount = ranges[i][1] < 0xFFFF ? 1 : 0;
17012 var segCount = bmpLength + trailingRangesCount;
17013 var searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2);
17015 // Fill up the 4 parallel arrays describing the segments.
17016 var startCount = '';
17019 var idRangeOffsets = '';
17020 var glyphsIds = '';
17023 var range, start, end, codes;
17024 for (i = 0, ii = bmpLength; i < ii; i++) {
17028 startCount += string16(start);
17029 endCount += string16(end);
17031 var contiguous = true;
17032 for (j = 1, jj = codes.length; j < jj; ++j) {
17033 if (codes[j] !== codes[j - 1] + 1) {
17034 contiguous = false;
17039 var offset = (segCount - i) * 2 + bias * 2;
17040 bias += (end - start + 1);
17042 idDeltas += string16(0);
17043 idRangeOffsets += string16(offset);
17045 for (j = 0, jj = codes.length; j < jj; ++j) {
17046 glyphsIds += string16(codes[j]);
17049 var startCode = codes[0];
17051 idDeltas += string16((startCode - start) & 0xFFFF);
17052 idRangeOffsets += string16(0);
17056 if (trailingRangesCount > 0) {
17057 endCount += '\xFF\xFF';
17058 startCount += '\xFF\xFF';
17059 idDeltas += '\x00\x01';
17060 idRangeOffsets += '\x00\x00';
17063 var format314 = '\x00\x00' + // language
17064 string16(2 * segCount) +
17065 string16(searchParams.range) +
17066 string16(searchParams.entry) +
17067 string16(searchParams.rangeShift) +
17068 endCount + '\x00\x00' + startCount +
17069 idDeltas + idRangeOffsets + glyphsIds;
17071 var format31012 = '';
17072 var header31012 = '';
17073 if (numTables > 1) {
17074 cmap += '\x00\x03' + // platformID
17075 '\x00\x0A' + // encodingID
17076 string32(4 + numTables * 8 +
17077 4 + format314.length); // start of the table record
17079 for (i = 0, ii = ranges.length; i < ii; i++) {
17083 var code = codes[0];
17084 for (j = 1, jj = codes.length; j < jj; ++j) {
17085 if (codes[j] !== codes[j - 1] + 1) {
17086 end = range[0] + j - 1;
17087 format31012 += string32(start) + // startCharCode
17088 string32(end) + // endCharCode
17089 string32(code); // startGlyphID
17094 format31012 += string32(start) + // startCharCode
17095 string32(range[1]) + // endCharCode
17096 string32(code); // startGlyphID
17098 header31012 = '\x00\x0C' + // format
17099 '\x00\x00' + // reserved
17100 string32(format31012.length + 16) + // length
17101 '\x00\x00\x00\x00' + // language
17102 string32(format31012.length / 12); // nGroups
17105 return cmap + '\x00\x04' + // format
17106 string16(format314.length + 4) + // length
17107 format314 + header31012 + format31012;
17110 function validateOS2Table(os2) {
17111 var stream = new Stream(os2.data);
17112 var version = stream.getUint16();
17113 // TODO verify all OS/2 tables fields, but currently we validate only those
17114 // that give us issues
17115 stream.getBytes(60); // skipping type, misc sizes, panose, unicode ranges
17116 var selection = stream.getUint16();
17117 if (version < 4 && (selection & 0x0300)) {
17120 var firstChar = stream.getUint16();
17121 var lastChar = stream.getUint16();
17122 if (firstChar > lastChar) {
17125 stream.getBytes(6); // skipping sTypoAscender/Descender/LineGap
17126 var usWinAscent = stream.getUint16();
17127 if (usWinAscent === 0) { // makes font unreadable by windows
17131 // OS/2 appears to be valid, resetting some fields
17132 os2.data[8] = os2.data[9] = 0; // IE rejects fonts if fsType != 0
17136 function createOS2Table(properties, charstrings, override) {
17137 override = override || {
17145 var ulUnicodeRange1 = 0;
17146 var ulUnicodeRange2 = 0;
17147 var ulUnicodeRange3 = 0;
17148 var ulUnicodeRange4 = 0;
17150 var firstCharIndex = null;
17151 var lastCharIndex = 0;
17154 for (var code in charstrings) {
17156 if (firstCharIndex > code || !firstCharIndex) {
17157 firstCharIndex = code;
17159 if (lastCharIndex < code) {
17160 lastCharIndex = code;
17163 var position = getUnicodeRangeFor(code);
17164 if (position < 32) {
17165 ulUnicodeRange1 |= 1 << position;
17166 } else if (position < 64) {
17167 ulUnicodeRange2 |= 1 << position - 32;
17168 } else if (position < 96) {
17169 ulUnicodeRange3 |= 1 << position - 64;
17170 } else if (position < 123) {
17171 ulUnicodeRange4 |= 1 << position - 96;
17173 error('Unicode ranges Bits > 123 are reserved for internal usage');
17178 firstCharIndex = 0;
17179 lastCharIndex = 255;
17182 var bbox = properties.bbox || [0, 0, 0, 0];
17183 var unitsPerEm = (override.unitsPerEm ||
17184 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0]);
17186 // if the font units differ to the PDF glyph space units
17187 // then scale up the values
17188 var scale = (properties.ascentScaled ? 1.0 :
17189 unitsPerEm / PDF_GLYPH_SPACE_UNITS);
17191 var typoAscent = (override.ascent ||
17192 Math.round(scale * (properties.ascent || bbox[3])));
17193 var typoDescent = (override.descent ||
17194 Math.round(scale * (properties.descent || bbox[1])));
17195 if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) {
17196 typoDescent = -typoDescent; // fixing incorrect descent
17198 var winAscent = override.yMax || typoAscent;
17199 var winDescent = -override.yMin || -typoDescent;
17201 return '\x00\x03' + // version
17202 '\x02\x24' + // xAvgCharWidth
17203 '\x01\xF4' + // usWeightClass
17204 '\x00\x05' + // usWidthClass
17205 '\x00\x00' + // fstype (0 to let the font loads via font-face on IE)
17206 '\x02\x8A' + // ySubscriptXSize
17207 '\x02\xBB' + // ySubscriptYSize
17208 '\x00\x00' + // ySubscriptXOffset
17209 '\x00\x8C' + // ySubscriptYOffset
17210 '\x02\x8A' + // ySuperScriptXSize
17211 '\x02\xBB' + // ySuperScriptYSize
17212 '\x00\x00' + // ySuperScriptXOffset
17213 '\x01\xDF' + // ySuperScriptYOffset
17214 '\x00\x31' + // yStrikeOutSize
17215 '\x01\x02' + // yStrikeOutPosition
17216 '\x00\x00' + // sFamilyClass
17218 String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) +
17219 '\x00\x00\x00\x00\x00\x00' + // Panose
17220 string32(ulUnicodeRange1) + // ulUnicodeRange1 (Bits 0-31)
17221 string32(ulUnicodeRange2) + // ulUnicodeRange2 (Bits 32-63)
17222 string32(ulUnicodeRange3) + // ulUnicodeRange3 (Bits 64-95)
17223 string32(ulUnicodeRange4) + // ulUnicodeRange4 (Bits 96-127)
17224 '\x2A\x32\x31\x2A' + // achVendID
17225 string16(properties.italicAngle ? 1 : 0) + // fsSelection
17226 string16(firstCharIndex ||
17227 properties.firstChar) + // usFirstCharIndex
17228 string16(lastCharIndex || properties.lastChar) + // usLastCharIndex
17229 string16(typoAscent) + // sTypoAscender
17230 string16(typoDescent) + // sTypoDescender
17231 '\x00\x64' + // sTypoLineGap (7%-10% of the unitsPerEM value)
17232 string16(winAscent) + // usWinAscent
17233 string16(winDescent) + // usWinDescent
17234 '\x00\x00\x00\x00' + // ulCodePageRange1 (Bits 0-31)
17235 '\x00\x00\x00\x00' + // ulCodePageRange2 (Bits 32-63)
17236 string16(properties.xHeight) + // sxHeight
17237 string16(properties.capHeight) + // sCapHeight
17238 string16(0) + // usDefaultChar
17239 string16(firstCharIndex || properties.firstChar) + // usBreakChar
17240 '\x00\x03'; // usMaxContext
17243 function createPostTable(properties) {
17244 var angle = Math.floor(properties.italicAngle * (Math.pow(2, 16)));
17245 return ('\x00\x03\x00\x00' + // Version number
17246 string32(angle) + // italicAngle
17247 '\x00\x00' + // underlinePosition
17248 '\x00\x00' + // underlineThickness
17249 string32(properties.fixedPitch) + // isFixedPitch
17250 '\x00\x00\x00\x00' + // minMemType42
17251 '\x00\x00\x00\x00' + // maxMemType42
17252 '\x00\x00\x00\x00' + // minMemType1
17253 '\x00\x00\x00\x00'); // maxMemType1
17256 function createNameTable(name, proto) {
17258 proto = [[], []]; // no strings and unicode strings
17262 proto[0][0] || 'Original licence', // 0.Copyright
17263 proto[0][1] || name, // 1.Font family
17264 proto[0][2] || 'Unknown', // 2.Font subfamily (font weight)
17265 proto[0][3] || 'uniqueID', // 3.Unique ID
17266 proto[0][4] || name, // 4.Full font name
17267 proto[0][5] || 'Version 0.11', // 5.Version
17268 proto[0][6] || '', // 6.Postscript name
17269 proto[0][7] || 'Unknown', // 7.Trademark
17270 proto[0][8] || 'Unknown', // 8.Manufacturer
17271 proto[0][9] || 'Unknown' // 9.Designer
17274 // Mac want 1-byte per character strings while Windows want
17275 // 2-bytes per character, so duplicate the names table
17276 var stringsUnicode = [];
17277 var i, ii, j, jj, str;
17278 for (i = 0, ii = strings.length; i < ii; i++) {
17279 str = proto[1][i] || strings[i];
17281 var strBufUnicode = [];
17282 for (j = 0, jj = str.length; j < jj; j++) {
17283 strBufUnicode.push(string16(str.charCodeAt(j)));
17285 stringsUnicode.push(strBufUnicode.join(''));
17288 var names = [strings, stringsUnicode];
17289 var platforms = ['\x00\x01', '\x00\x03'];
17290 var encodings = ['\x00\x00', '\x00\x01'];
17291 var languages = ['\x00\x00', '\x04\x09'];
17293 var namesRecordCount = strings.length * platforms.length;
17295 '\x00\x00' + // format
17296 string16(namesRecordCount) + // Number of names Record
17297 string16(namesRecordCount * 12 + 6); // Storage
17299 // Build the name records field
17301 for (i = 0, ii = platforms.length; i < ii; i++) {
17302 var strs = names[i];
17303 for (j = 0, jj = strs.length; j < jj; j++) {
17306 platforms[i] + // platform ID
17307 encodings[i] + // encoding ID
17308 languages[i] + // language ID
17309 string16(j) + // name ID
17310 string16(str.length) +
17311 string16(strOffset);
17312 nameTable += nameRecord;
17313 strOffset += str.length;
17317 nameTable += strings.join('') + stringsUnicode.join('');
17327 var renderer = FontRendererFactory.create(this);
17328 return shadow(this, 'renderer', renderer);
17331 exportData: function Font_exportData() {
17333 for (var i in this) {
17334 if (this.hasOwnProperty(i)) {
17341 checkAndRepair: function Font_checkAndRepair(name, font, properties) {
17342 function readTableEntry(file) {
17343 var tag = bytesToString(file.getBytes(4));
17345 var checksum = file.getInt32();
17346 var offset = file.getInt32() >>> 0;
17347 var length = file.getInt32() >>> 0;
17349 // Read the table associated data
17350 var previousPosition = file.pos;
17351 file.pos = file.start ? file.start : 0;
17353 var data = file.getBytes(length);
17354 file.pos = previousPosition;
17356 if (tag === 'head') {
17357 // clearing checksum adjustment
17358 data[8] = data[9] = data[10] = data[11] = 0;
17359 data[17] |= 0x20; //Set font optimized for cleartype flag
17364 checksum: checksum,
17371 function readOpenTypeHeader(ttf) {
17373 version: bytesToString(ttf.getBytes(4)),
17374 numTables: ttf.getUint16(),
17375 searchRange: ttf.getUint16(),
17376 entrySelector: ttf.getUint16(),
17377 rangeShift: ttf.getUint16()
17382 * Read the appropriate subtable from the cmap according to 9.6.6.4 from
17385 function readCmapTable(cmap, font, isSymbolicFont) {
17387 var start = (font.start ? font.start : 0) + cmap.offset;
17390 var version = font.getUint16();
17391 var numTables = font.getUint16();
17393 var potentialTable;
17394 var canBreak = false;
17395 // There's an order of preference in terms of which cmap subtable to
17397 // - non-symbolic fonts the preference is a 3,1 table then a 1,0 table
17398 // - symbolic fonts the preference is a 3,0 table then a 1,0 table
17399 // The following takes advantage of the fact that the tables are sorted
17401 for (var i = 0; i < numTables; i++) {
17402 var platformId = font.getUint16();
17403 var encodingId = font.getUint16();
17404 var offset = font.getInt32() >>> 0;
17405 var useTable = false;
17407 if (platformId === 0 && encodingId === 0) {
17409 // Continue the loop since there still may be a higher priority
17411 } else if (platformId === 1 && encodingId === 0) {
17413 // Continue the loop since there still may be a higher priority
17415 } else if (platformId === 3 && encodingId === 1 &&
17416 (!isSymbolicFont || !potentialTable)) {
17418 if (!isSymbolicFont) {
17421 } else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
17428 platformId: platformId,
17429 encodingId: encodingId,
17438 if (potentialTable) {
17439 font.pos = start + potentialTable.offset;
17441 if (!potentialTable || font.peekByte() === -1) {
17442 warn('Could not find a preferred cmap table.');
17447 hasShortCmap: false
17451 var format = font.getUint16();
17452 var length = font.getUint16();
17453 var language = font.getUint16();
17455 var hasShortCmap = false;
17459 // TODO(mack): refactor this cmap subtable reading logic out
17460 if (format === 0) {
17461 for (j = 0; j < 256; j++) {
17462 var index = font.getByte();
17471 hasShortCmap = true;
17472 } else if (format === 4) {
17473 // re-creating the table in format 4 since the encoding
17474 // might be changed
17475 var segCount = (font.getUint16() >> 1);
17476 font.getBytes(6); // skipping range fields
17477 var segIndex, segments = [];
17478 for (segIndex = 0; segIndex < segCount; segIndex++) {
17479 segments.push({ end: font.getUint16() });
17482 for (segIndex = 0; segIndex < segCount; segIndex++) {
17483 segments[segIndex].start = font.getUint16();
17486 for (segIndex = 0; segIndex < segCount; segIndex++) {
17487 segments[segIndex].delta = font.getUint16();
17490 var offsetsCount = 0;
17491 for (segIndex = 0; segIndex < segCount; segIndex++) {
17492 segment = segments[segIndex];
17493 var rangeOffset = font.getUint16();
17494 if (!rangeOffset) {
17495 segment.offsetIndex = -1;
17499 var offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);
17500 segment.offsetIndex = offsetIndex;
17501 offsetsCount = Math.max(offsetsCount, offsetIndex +
17502 segment.end - segment.start + 1);
17506 for (j = 0; j < offsetsCount; j++) {
17507 offsets.push(font.getUint16());
17510 for (segIndex = 0; segIndex < segCount; segIndex++) {
17511 segment = segments[segIndex];
17512 start = segment.start;
17513 var end = segment.end;
17514 var delta = segment.delta;
17515 offsetIndex = segment.offsetIndex;
17517 for (j = start; j <= end; j++) {
17518 if (j === 0xFFFF) {
17522 glyphId = (offsetIndex < 0 ?
17523 j : offsets[offsetIndex + j - start]);
17524 glyphId = (glyphId + delta) & 0xFFFF;
17525 if (glyphId === 0) {
17534 } else if (format === 6) {
17535 // Format 6 is a 2-bytes dense mapping, which means the font data
17536 // lives glue together even if they are pretty far in the unicode
17537 // table. (This looks weird, so I can have missed something), this
17538 // works on Linux but seems to fails on Mac so let's rewrite the
17539 // cmap table to a 3-1-4 style
17540 var firstCode = font.getUint16();
17541 var entryCount = font.getUint16();
17543 for (j = 0; j < entryCount; j++) {
17544 glyphId = font.getUint16();
17545 var charCode = firstCode + j;
17548 charCode: charCode,
17553 error('cmap table has unsupported format: ' + format);
17556 // removing duplicate entries
17557 mappings.sort(function (a, b) {
17558 return a.charCode - b.charCode;
17560 for (i = 1; i < mappings.length; i++) {
17561 if (mappings[i - 1].charCode === mappings[i].charCode) {
17562 mappings.splice(i, 1);
17568 platformId: potentialTable.platformId,
17569 encodingId: potentialTable.encodingId,
17570 mappings: mappings,
17571 hasShortCmap: hasShortCmap
17575 function sanitizeMetrics(font, header, metrics, numGlyphs) {
17578 metrics.data = null;
17583 font.pos = (font.start ? font.start : 0) + header.offset;
17584 font.pos += header.length - 2;
17585 var numOfMetrics = font.getUint16();
17587 if (numOfMetrics > numGlyphs) {
17588 info('The numOfMetrics (' + numOfMetrics + ') should not be ' +
17589 'greater than the numGlyphs (' + numGlyphs + ')');
17590 // Reduce numOfMetrics if it is greater than numGlyphs
17591 numOfMetrics = numGlyphs;
17592 header.data[34] = (numOfMetrics & 0xff00) >> 8;
17593 header.data[35] = numOfMetrics & 0x00ff;
17596 var numOfSidebearings = numGlyphs - numOfMetrics;
17597 var numMissing = numOfSidebearings -
17598 ((metrics.length - numOfMetrics * 4) >> 1);
17600 if (numMissing > 0) {
17601 // For each missing glyph, we set both the width and lsb to 0 (zero).
17602 // Since we need to add two properties for each glyph, this explains
17603 // the use of |numMissing * 2| when initializing the typed array.
17604 var entries = new Uint8Array(metrics.length + numMissing * 2);
17605 entries.set(metrics.data);
17606 metrics.data = entries;
17610 function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart,
17612 if (sourceEnd - sourceStart <= 12) {
17613 // glyph with data less than 12 is invalid one
17616 var glyf = source.subarray(sourceStart, sourceEnd);
17617 var contoursCount = (glyf[0] << 8) | glyf[1];
17618 if (contoursCount & 0x8000) {
17619 // complex glyph, writing as is
17620 dest.set(glyf, destStart);
17621 return glyf.length;
17624 var i, j = 10, flagsCount = 0;
17625 for (i = 0; i < contoursCount; i++) {
17626 var endPoint = (glyf[j] << 8) | glyf[j + 1];
17627 flagsCount = endPoint + 1;
17630 // skipping instructions
17631 var instructionsStart = j;
17632 var instructionsLength = (glyf[j] << 8) | glyf[j + 1];
17633 j += 2 + instructionsLength;
17634 var instructionsEnd = j;
17635 // validating flags
17636 var coordinatesLength = 0;
17637 for (i = 0; i < flagsCount; i++) {
17638 var flag = glyf[j++];
17640 // reserved flags must be zero, cleaning up
17641 glyf[j - 1] = flag & 0x3F;
17643 var xyLength = ((flag & 2) ? 1 : (flag & 16) ? 0 : 2) +
17644 ((flag & 4) ? 1 : (flag & 32) ? 0 : 2);
17645 coordinatesLength += xyLength;
17647 var repeat = glyf[j++];
17649 coordinatesLength += repeat * xyLength;
17652 // glyph without coordinates will be rejected
17653 if (coordinatesLength === 0) {
17656 var glyphDataLength = j + coordinatesLength;
17657 if (glyphDataLength > glyf.length) {
17658 // not enough data for coordinates
17661 if (!hintsValid && instructionsLength > 0) {
17662 dest.set(glyf.subarray(0, instructionsStart), destStart);
17663 dest.set([0, 0], destStart + instructionsStart);
17664 dest.set(glyf.subarray(instructionsEnd, glyphDataLength),
17665 destStart + instructionsStart + 2);
17666 glyphDataLength -= instructionsLength;
17667 if (glyf.length - glyphDataLength > 3) {
17668 glyphDataLength = (glyphDataLength + 3) & ~3;
17670 return glyphDataLength;
17672 if (glyf.length - glyphDataLength > 3) {
17673 // truncating and aligning to 4 bytes the long glyph data
17674 glyphDataLength = (glyphDataLength + 3) & ~3;
17675 dest.set(glyf.subarray(0, glyphDataLength), destStart);
17676 return glyphDataLength;
17678 // glyph data is fine
17679 dest.set(glyf, destStart);
17680 return glyf.length;
17683 function sanitizeHead(head, numGlyphs, locaLength) {
17684 var data = head.data;
17686 // Validate version:
17687 // Should always be 0x00010000
17688 var version = int32(data[0], data[1], data[2], data[3]);
17689 if (version >> 16 !== 1) {
17690 info('Attempting to fix invalid version in head table: ' + version);
17697 var indexToLocFormat = int16(data[50], data[51]);
17698 if (indexToLocFormat < 0 || indexToLocFormat > 1) {
17699 info('Attempting to fix invalid indexToLocFormat in head table: ' +
17702 // The value of indexToLocFormat should be 0 if the loca table
17703 // consists of short offsets, and should be 1 if the loca table
17704 // consists of long offsets.
17706 // The number of entries in the loca table should be numGlyphs + 1.
17708 // Using this information, we can work backwards to deduce if the
17709 // size of each offset in the loca table, and thus figure out the
17710 // appropriate value for indexToLocFormat.
17712 var numGlyphsPlusOne = numGlyphs + 1;
17713 if (locaLength === numGlyphsPlusOne << 1) {
17714 // 0x0000 indicates the loca table consists of short offsets
17717 } else if (locaLength === numGlyphsPlusOne << 2) {
17718 // 0x0001 indicates the loca table consists of long offsets
17722 warn('Could not fix indexToLocFormat: ' + indexToLocFormat);
17727 function sanitizeGlyphLocations(loca, glyf, numGlyphs,
17728 isGlyphLocationsLong, hintsValid,
17730 var itemSize, itemDecode, itemEncode;
17731 if (isGlyphLocationsLong) {
17733 itemDecode = function fontItemDecodeLong(data, offset) {
17734 return (data[offset] << 24) | (data[offset + 1] << 16) |
17735 (data[offset + 2] << 8) | data[offset + 3];
17737 itemEncode = function fontItemEncodeLong(data, offset, value) {
17738 data[offset] = (value >>> 24) & 0xFF;
17739 data[offset + 1] = (value >> 16) & 0xFF;
17740 data[offset + 2] = (value >> 8) & 0xFF;
17741 data[offset + 3] = value & 0xFF;
17745 itemDecode = function fontItemDecode(data, offset) {
17746 return (data[offset] << 9) | (data[offset + 1] << 1);
17748 itemEncode = function fontItemEncode(data, offset, value) {
17749 data[offset] = (value >> 9) & 0xFF;
17750 data[offset + 1] = (value >> 1) & 0xFF;
17753 var locaData = loca.data;
17754 var locaDataSize = itemSize * (1 + numGlyphs);
17755 // is loca.data too short or long?
17756 if (locaData.length !== locaDataSize) {
17757 locaData = new Uint8Array(locaDataSize);
17758 locaData.set(loca.data.subarray(0, locaDataSize));
17759 loca.data = locaData;
17761 // removing the invalid glyphs
17762 var oldGlyfData = glyf.data;
17763 var oldGlyfDataLength = oldGlyfData.length;
17764 var newGlyfData = new Uint8Array(oldGlyfDataLength);
17765 var startOffset = itemDecode(locaData, 0);
17766 var writeOffset = 0;
17767 var missingGlyphData = {};
17768 itemEncode(locaData, 0, writeOffset);
17770 for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
17771 var endOffset = itemDecode(locaData, j);
17772 if (endOffset > oldGlyfDataLength &&
17773 ((oldGlyfDataLength + 3) & ~3) === endOffset) {
17774 // Aspose breaks fonts by aligning the glyphs to the qword, but not
17775 // the glyf table size, which makes last glyph out of range.
17776 endOffset = oldGlyfDataLength;
17778 if (endOffset > oldGlyfDataLength) {
17779 // glyph end offset points outside glyf data, rejecting the glyph
17780 itemEncode(locaData, j, writeOffset);
17781 startOffset = endOffset;
17785 if (startOffset === endOffset) {
17786 missingGlyphData[i] = true;
17789 var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset,
17790 newGlyfData, writeOffset, hintsValid);
17791 writeOffset += newLength;
17792 itemEncode(locaData, j, writeOffset);
17793 startOffset = endOffset;
17796 if (writeOffset === 0) {
17797 // glyf table cannot be empty -- redoing the glyf and loca tables
17798 // to have single glyph with one point
17799 var simpleGlyph = new Uint8Array(
17800 [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]);
17801 for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
17802 itemEncode(locaData, j, simpleGlyph.length);
17804 glyf.data = simpleGlyph;
17805 return missingGlyphData;
17808 if (dupFirstEntry) {
17809 var firstEntryLength = itemDecode(locaData, itemSize);
17810 if (newGlyfData.length > firstEntryLength + writeOffset) {
17811 glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);
17813 glyf.data = new Uint8Array(firstEntryLength + writeOffset);
17814 glyf.data.set(newGlyfData.subarray(0, writeOffset));
17816 glyf.data.set(newGlyfData.subarray(0, firstEntryLength), writeOffset);
17817 itemEncode(loca.data, locaData.length - itemSize,
17818 writeOffset + firstEntryLength);
17820 glyf.data = newGlyfData.subarray(0, writeOffset);
17822 return missingGlyphData;
17825 function readPostScriptTable(post, properties, maxpNumGlyphs) {
17826 var start = (font.start ? font.start : 0) + post.offset;
17829 var length = post.length, end = start + length;
17830 var version = font.getInt32();
17831 // skip rest to the tables
17840 glyphNames = MacStandardGlyphOrdering;
17843 var numGlyphs = font.getUint16();
17844 if (numGlyphs !== maxpNumGlyphs) {
17848 var glyphNameIndexes = [];
17849 for (i = 0; i < numGlyphs; ++i) {
17850 var index = font.getUint16();
17851 if (index >= 32768) {
17855 glyphNameIndexes.push(index);
17860 var customNames = [];
17862 while (font.pos < end) {
17863 var stringLength = font.getByte();
17864 strBuf.length = stringLength;
17865 for (i = 0; i < stringLength; ++i) {
17866 strBuf[i] = String.fromCharCode(font.getByte());
17868 customNames.push(strBuf.join(''));
17871 for (i = 0; i < numGlyphs; ++i) {
17872 var j = glyphNameIndexes[i];
17874 glyphNames.push(MacStandardGlyphOrdering[j]);
17877 glyphNames.push(customNames[j - 258]);
17883 warn('Unknown/unsupported post table version ' + version);
17885 if (properties.defaultEncoding) {
17886 glyphNames = properties.defaultEncoding;
17890 properties.glyphNames = glyphNames;
17894 function readNameTable(nameTable) {
17895 var start = (font.start ? font.start : 0) + nameTable.offset;
17898 var names = [[], []];
17899 var length = nameTable.length, end = start + length;
17900 var format = font.getUint16();
17901 var FORMAT_0_HEADER_LENGTH = 6;
17902 if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {
17903 // unsupported name table format or table "too" small
17906 var numRecords = font.getUint16();
17907 var stringsStart = font.getUint16();
17909 var NAME_RECORD_LENGTH = 12;
17912 for (i = 0; i < numRecords &&
17913 font.pos + NAME_RECORD_LENGTH <= end; i++) {
17915 platform: font.getUint16(),
17916 encoding: font.getUint16(),
17917 language: font.getUint16(),
17918 name: font.getUint16(),
17919 length: font.getUint16(),
17920 offset: font.getUint16()
17922 // using only Macintosh and Windows platform/encoding names
17923 if ((r.platform === 1 && r.encoding === 0 && r.language === 0) ||
17924 (r.platform === 3 && r.encoding === 1 && r.language === 0x409)) {
17928 for (i = 0, ii = records.length; i < ii; i++) {
17929 var record = records[i];
17930 var pos = start + stringsStart + record.offset;
17931 if (pos + record.length > end) {
17932 continue; // outside of name table, ignoring
17935 var nameIndex = record.name;
17936 if (record.encoding) {
17939 for (var j = 0, jj = record.length; j < jj; j += 2) {
17940 str += String.fromCharCode(font.getUint16());
17942 names[1][nameIndex] = str;
17944 names[0][nameIndex] = bytesToString(font.getBytes(record.length));
17950 var TTOpsStackDeltas = [
17951 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5,
17952 -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1,
17953 1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1,
17954 0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2,
17955 0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1,
17956 -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1,
17957 -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
17958 -2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1,
17959 -999, -2, -2, 0, 0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2];
17960 // 0xC0-DF == -1 and 0xE0-FF == -2
17962 function sanitizeTTProgram(table, ttContext) {
17963 var data = table.data;
17964 var i = 0, j, n, b, funcId, pc, lastEndf = 0, lastDeff = 0;
17966 var callstack = [];
17967 var functionsCalled = [];
17968 var tooComplexToFollowFunctions =
17969 ttContext.tooComplexToFollowFunctions;
17970 var inFDEF = false, ifLevel = 0, inELSE = 0;
17971 for (var ii = data.length; i < ii;) {
17972 var op = data[i++];
17973 // The TrueType instruction set docs can be found at
17974 // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
17975 if (op === 0x40) { // NPUSHB - pushes n bytes
17977 if (inFDEF || inELSE) {
17980 for (j = 0; j < n; j++) {
17981 stack.push(data[i++]);
17984 } else if (op === 0x41) { // NPUSHW - pushes n words
17986 if (inFDEF || inELSE) {
17989 for (j = 0; j < n; j++) {
17991 stack.push((b << 8) | data[i++]);
17994 } else if ((op & 0xF8) === 0xB0) { // PUSHB - pushes bytes
17996 if (inFDEF || inELSE) {
17999 for (j = 0; j < n; j++) {
18000 stack.push(data[i++]);
18003 } else if ((op & 0xF8) === 0xB8) { // PUSHW - pushes words
18005 if (inFDEF || inELSE) {
18008 for (j = 0; j < n; j++) {
18010 stack.push((b << 8) | data[i++]);
18013 } else if (op === 0x2B && !tooComplexToFollowFunctions) { // CALL
18014 if (!inFDEF && !inELSE) {
18015 // collecting inforamtion about which functions are used
18016 funcId = stack[stack.length - 1];
18017 ttContext.functionsUsed[funcId] = true;
18018 if (funcId in ttContext.functionsStackDeltas) {
18019 stack.length += ttContext.functionsStackDeltas[funcId];
18020 } else if (funcId in ttContext.functionsDefined &&
18021 functionsCalled.indexOf(funcId) < 0) {
18022 callstack.push({data: data, i: i, stackTop: stack.length - 1});
18023 functionsCalled.push(funcId);
18024 pc = ttContext.functionsDefined[funcId];
18026 warn('TT: CALL non-existent function');
18027 ttContext.hintsValid = false;
18034 } else if (op === 0x2C && !tooComplexToFollowFunctions) { // FDEF
18035 if (inFDEF || inELSE) {
18036 warn('TT: nested FDEFs not allowed');
18037 tooComplexToFollowFunctions = true;
18040 // collecting inforamtion about which functions are defined
18042 funcId = stack.pop();
18043 ttContext.functionsDefined[funcId] = {data: data, i: i};
18044 } else if (op === 0x2D) { // ENDF - end of function
18049 pc = callstack.pop();
18051 warn('TT: ENDF bad stack');
18052 ttContext.hintsValid = false;
18055 funcId = functionsCalled.pop();
18058 ttContext.functionsStackDeltas[funcId] =
18059 stack.length - pc.stackTop;
18061 } else if (op === 0x89) { // IDEF - instruction definition
18062 if (inFDEF || inELSE) {
18063 warn('TT: nested IDEFs not allowed');
18064 tooComplexToFollowFunctions = true;
18067 // recording it as a function to track ENDF
18069 } else if (op === 0x58) { // IF
18071 } else if (op === 0x1B) { // ELSE
18073 } else if (op === 0x59) { // EIF
18074 if (inELSE === ifLevel) {
18078 } else if (op === 0x1C) { // JMPR
18079 if (!inFDEF && !inELSE) {
18080 var offset = stack[stack.length - 1];
18081 // only jumping forward to prevent infinite loop
18087 // Adjusting stack not extactly, but just enough to get function id
18088 if (!inFDEF && !inELSE) {
18089 var stackDelta = op <= 0x8E ? TTOpsStackDeltas[op] :
18090 op >= 0xC0 && op <= 0xDF ? -1 : op >= 0xE0 ? -2 : 0;
18091 if (op >= 0x71 && op <= 0x75) {
18094 stackDelta = -n * 2;
18097 while (stackDelta < 0 && stack.length > 0) {
18101 while (stackDelta > 0) {
18102 stack.push(NaN); // pushing any number into stack
18107 ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;
18108 var content = [data];
18109 if (i > data.length) {
18110 content.push(new Uint8Array(i - data.length));
18112 if (lastDeff > lastEndf) {
18113 warn('TT: complementing a missing function tail');
18114 // new function definition started, but not finished
18115 // complete function by [CLEAR, ENDF]
18116 content.push(new Uint8Array([0x22, 0x2D]));
18118 foldTTTable(table, content);
18121 function checkInvalidFunctions(ttContext, maxFunctionDefs) {
18122 if (ttContext.tooComplexToFollowFunctions) {
18125 if (ttContext.functionsDefined.length > maxFunctionDefs) {
18126 warn('TT: more functions defined than expected');
18127 ttContext.hintsValid = false;
18130 for (var j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
18131 if (j > maxFunctionDefs) {
18132 warn('TT: invalid function id: ' + j);
18133 ttContext.hintsValid = false;
18136 if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {
18137 warn('TT: undefined function: ' + j);
18138 ttContext.hintsValid = false;
18144 function foldTTTable(table, content) {
18145 if (content.length > 1) {
18146 // concatenating the content items
18149 for (j = 0, jj = content.length; j < jj; j++) {
18150 newLength += content[j].length;
18152 newLength = (newLength + 3) & ~3;
18153 var result = new Uint8Array(newLength);
18155 for (j = 0, jj = content.length; j < jj; j++) {
18156 result.set(content[j], pos);
18157 pos += content[j].length;
18159 table.data = result;
18160 table.length = newLength;
18164 function sanitizeTTPrograms(fpgm, prep, cvt) {
18166 functionsDefined: [],
18168 functionsStackDeltas: [],
18169 tooComplexToFollowFunctions: false,
18173 sanitizeTTProgram(fpgm, ttContext);
18176 sanitizeTTProgram(prep, ttContext);
18179 checkInvalidFunctions(ttContext, maxFunctionDefs);
18181 if (cvt && (cvt.length & 1)) {
18182 var cvtData = new Uint8Array(cvt.length + 1);
18183 cvtData.set(cvt.data);
18184 cvt.data = cvtData;
18186 return ttContext.hintsValid;
18189 // The following steps modify the original font data, making copy
18190 font = new Stream(new Uint8Array(font.getBytes()));
18192 var VALID_TABLES = ['OS/2', 'cmap', 'head', 'hhea', 'hmtx', 'maxp',
18193 'name', 'post', 'loca', 'glyf', 'fpgm', 'prep', 'cvt ', 'CFF '];
18195 var header = readOpenTypeHeader(font);
18196 var numTables = header.numTables;
18199 var tables = { 'OS/2': null, cmap: null, head: null, hhea: null,
18200 hmtx: null, maxp: null, name: null, post: null };
18202 for (var i = 0; i < numTables; i++) {
18203 table = readTableEntry(font);
18204 if (VALID_TABLES.indexOf(table.tag) < 0) {
18205 continue; // skipping table if it's not a required or optional table
18207 if (table.length === 0) {
18208 continue; // skipping empty tables
18210 tables[table.tag] = table;
18213 var isTrueType = !tables['CFF '];
18216 if (header.version === 'OTTO' ||
18217 !tables.head || !tables.hhea || !tables.maxp || !tables.post) {
18218 // no major tables: throwing everything at CFFFont
18219 cffFile = new Stream(tables['CFF '].data);
18220 cff = new CFFFont(cffFile, properties);
18222 return this.convert(name, cff, properties);
18225 delete tables.glyf;
18226 delete tables.loca;
18227 delete tables.fpgm;
18228 delete tables.prep;
18229 delete tables['cvt '];
18230 this.isOpenType = true;
18232 if (!tables.glyf || !tables.loca) {
18233 error('Required "glyf" or "loca" tables are not found');
18235 this.isOpenType = false;
18238 if (!tables.maxp) {
18239 error('Required "maxp" table is not found');
18242 font.pos = (font.start || 0) + tables.maxp.offset;
18243 var version = font.getInt32();
18244 var numGlyphs = font.getUint16();
18245 var maxFunctionDefs = 0;
18246 if (version >= 0x00010000 && tables.maxp.length >= 22) {
18247 // maxZones can be invalid
18249 var maxZones = font.getUint16();
18250 if (maxZones > 2) { // reset to 2 if font has invalid maxZones
18251 tables.maxp.data[14] = 0;
18252 tables.maxp.data[15] = 2;
18255 maxFunctionDefs = font.getUint16();
18258 var dupFirstEntry = false;
18259 if (properties.type === 'CIDFontType2' && properties.toUnicode &&
18260 properties.toUnicode.get(0) > '\u0000') {
18261 // oracle's defect (see 3427), duplicating first entry
18262 dupFirstEntry = true;
18264 tables.maxp.data[4] = numGlyphs >> 8;
18265 tables.maxp.data[5] = numGlyphs & 255;
18268 var hintsValid = sanitizeTTPrograms(tables.fpgm, tables.prep,
18269 tables['cvt '], maxFunctionDefs);
18271 delete tables.fpgm;
18272 delete tables.prep;
18273 delete tables['cvt '];
18276 // Ensure the hmtx table contains the advance width and
18277 // sidebearings information for numGlyphs in the maxp table
18278 sanitizeMetrics(font, tables.hhea, tables.hmtx, numGlyphs);
18280 if (!tables.head) {
18281 error('Required "head" table is not found');
18284 sanitizeHead(tables.head, numGlyphs, isTrueType ? tables.loca.length : 0);
18286 var missingGlyphs = {};
18288 var isGlyphLocationsLong = int16(tables.head.data[50],
18289 tables.head.data[51]);
18290 missingGlyphs = sanitizeGlyphLocations(tables.loca, tables.glyf,
18291 numGlyphs, isGlyphLocationsLong,
18292 hintsValid, dupFirstEntry);
18295 if (!tables.hhea) {
18296 error('Required "hhea" table is not found');
18299 // Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
18300 // Sometimes it's 0. That needs to be fixed
18301 if (tables.hhea.data[10] === 0 && tables.hhea.data[11] === 0) {
18302 tables.hhea.data[10] = 0xFF;
18303 tables.hhea.data[11] = 0xFF;
18306 // The 'post' table has glyphs names.
18308 var valid = readPostScriptTable(tables.post, properties, numGlyphs);
18310 tables.post = null;
18314 var charCodeToGlyphId = [], charCode, toUnicode = properties.toUnicode;
18316 function hasGlyph(glyphId, charCode) {
18317 if (!missingGlyphs[glyphId]) {
18320 if (charCode >= 0 && toUnicode.has(charCode)) {
18326 if (properties.type === 'CIDFontType2') {
18327 var cidToGidMap = properties.cidToGidMap || [];
18328 var isCidToGidMapEmpty = cidToGidMap.length === 0;
18330 properties.cMap.forEach(function(charCode, cid) {
18331 assert(cid <= 0xffff, 'Max size of CID is 65,535');
18333 if (isCidToGidMapEmpty) {
18334 glyphId = charCode;
18335 } else if (cidToGidMap[cid] !== undefined) {
18336 glyphId = cidToGidMap[cid];
18339 if (glyphId >= 0 && glyphId < numGlyphs &&
18340 hasGlyph(glyphId, charCode)) {
18341 charCodeToGlyphId[charCode] = glyphId;
18344 if (dupFirstEntry) {
18345 charCodeToGlyphId[0] = numGlyphs - 1;
18348 // Most of the following logic in this code branch is based on the
18349 // 9.6.6.4 of the PDF spec.
18350 var cmapTable = readCmapTable(tables.cmap, font, this.isSymbolicFont);
18351 var cmapPlatformId = cmapTable.platformId;
18352 var cmapEncodingId = cmapTable.encodingId;
18353 var cmapMappings = cmapTable.mappings;
18354 var cmapMappingsLength = cmapMappings.length;
18355 var hasEncoding = properties.differences.length ||
18356 !!properties.baseEncodingName;
18358 // The spec seems to imply that if the font is symbolic the encoding
18359 // should be ignored, this doesn't appear to work for 'preistabelle.pdf'
18360 // where the the font is symbolic and it has an encoding.
18362 (cmapPlatformId === 3 && cmapEncodingId === 1 ||
18363 cmapPlatformId === 1 && cmapEncodingId === 0) ||
18364 (cmapPlatformId === -1 && cmapEncodingId === -1 && // Temporary hack
18365 !!Encodings[properties.baseEncodingName])) { // Temporary hack
18366 // When no preferred cmap table was found and |baseEncodingName| is
18367 // one of the predefined encodings, we seem to obtain a better
18368 // |charCodeToGlyphId| map from the code below (fixes bug 1057544).
18369 // TODO: Note that this is a hack which should be removed as soon as
18370 // we have proper support for more exotic cmap tables.
18372 var baseEncoding = [];
18373 if (properties.baseEncodingName === 'MacRomanEncoding' ||
18374 properties.baseEncodingName === 'WinAnsiEncoding') {
18375 baseEncoding = Encodings[properties.baseEncodingName];
18377 for (charCode = 0; charCode < 256; charCode++) {
18379 if (this.differences && charCode in this.differences) {
18380 glyphName = this.differences[charCode];
18381 } else if (charCode in baseEncoding &&
18382 baseEncoding[charCode] !== '') {
18383 glyphName = baseEncoding[charCode];
18385 glyphName = Encodings.StandardEncoding[charCode];
18390 var unicodeOrCharCode;
18391 if (cmapPlatformId === 3 && cmapEncodingId === 1) {
18392 unicodeOrCharCode = GlyphsUnicode[glyphName];
18393 } else if (cmapPlatformId === 1 && cmapEncodingId === 0) {
18394 // TODO: the encoding needs to be updated with mac os table.
18395 unicodeOrCharCode = Encodings.MacRomanEncoding.indexOf(glyphName);
18399 for (i = 0; i < cmapMappingsLength; ++i) {
18400 if (cmapMappings[i].charCode === unicodeOrCharCode &&
18401 hasGlyph(cmapMappings[i].glyphId, unicodeOrCharCode)) {
18402 charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
18407 if (!found && properties.glyphNames) {
18408 // Try to map using the post table. There are currently no known
18409 // pdfs that this fixes.
18410 var glyphId = properties.glyphNames.indexOf(glyphName);
18411 if (glyphId > 0 && hasGlyph(glyphId, -1)) {
18412 charCodeToGlyphId[charCode] = glyphId;
18416 } else if (cmapPlatformId === 0 && cmapEncodingId === 0) {
18417 // Default Unicode semantics, use the charcodes as is.
18418 for (i = 0; i < cmapMappingsLength; ++i) {
18419 charCodeToGlyphId[cmapMappings[i].charCode] =
18420 cmapMappings[i].glyphId;
18423 // For (3, 0) cmap tables:
18424 // The charcode key being stored in charCodeToGlyphId is the lower
18425 // byte of the two-byte charcodes of the cmap table since according to
18426 // the spec: 'each byte from the string shall be prepended with the
18427 // high byte of the range [of charcodes in the cmap table], to form
18428 // a two-byte character, which shall be used to select the
18429 // associated glyph description from the subtable'.
18431 // For (1, 0) cmap tables:
18432 // 'single bytes from the string shall be used to look up the
18433 // associated glyph descriptions from the subtable'. This means
18434 // charcodes in the cmap will be single bytes, so no-op since
18435 // glyph.charCode & 0xFF === glyph.charCode
18436 for (i = 0; i < cmapMappingsLength; ++i) {
18437 charCode = cmapMappings[i].charCode & 0xFF;
18438 charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
18443 if (charCodeToGlyphId.length === 0) {
18444 // defines at least one glyph
18445 charCodeToGlyphId[0] = 0;
18448 // Converting glyphs and ids into font's cmap table
18449 var newMapping = adjustMapping(charCodeToGlyphId, properties);
18450 this.toFontChar = newMapping.toFontChar;
18453 data: createCmapTable(newMapping.charCodeToGlyphId)
18456 if (!tables['OS/2'] || !validateOS2Table(tables['OS/2'])) {
18457 // extract some more font properties from the OpenType head and
18458 // hhea tables; yMin and descent value are always negative
18460 unitsPerEm: int16(tables.head.data[18], tables.head.data[19]),
18461 yMax: int16(tables.head.data[42], tables.head.data[43]),
18462 yMin: int16(tables.head.data[38], tables.head.data[39]) - 0x10000,
18463 ascent: int16(tables.hhea.data[4], tables.hhea.data[5]),
18464 descent: int16(tables.hhea.data[6], tables.hhea.data[7]) - 0x10000
18469 data: createOS2Table(properties, newMapping.charCodeToGlyphId,
18474 // Rewrite the 'post' table if needed
18475 if (!tables.post) {
18478 data: createPostTable(properties)
18484 // Trying to repair CFF file
18485 cffFile = new Stream(tables['CFF '].data);
18486 var parser = new CFFParser(cffFile, properties);
18487 cff = parser.parse();
18488 var compiler = new CFFCompiler(cff);
18489 tables['CFF '].data = compiler.compile();
18491 warn('Failed to compile font ' + properties.loadedName);
18495 // Re-creating 'name' table
18496 if (!tables.name) {
18499 data: createNameTable(this.name)
18502 // ... using existing 'name' table as prototype
18503 var namePrototype = readNameTable(tables.name);
18504 tables.name.data = createNameTable(name, namePrototype);
18507 var builder = new OpenTypeFileBuilder(header.version);
18508 for (var tableTag in tables) {
18509 builder.addTable(tableTag, tables[tableTag].data);
18511 return builder.toArray();
18514 convert: function Font_convert(fontName, font, properties) {
18515 // TODO: Check the charstring widths to determine this.
18516 properties.fixedPitch = false;
18518 var mapping = font.getGlyphMapping(properties);
18519 var newMapping = adjustMapping(mapping, properties);
18520 this.toFontChar = newMapping.toFontChar;
18521 var numGlyphs = font.numGlyphs;
18523 function getCharCodes(charCodeToGlyphId, glyphId) {
18524 var charCodes = null;
18525 for (var charCode in charCodeToGlyphId) {
18526 if (glyphId === charCodeToGlyphId[charCode]) {
18530 charCodes.push(charCode | 0);
18536 function createCharCode(charCodeToGlyphId, glyphId) {
18537 for (var charCode in charCodeToGlyphId) {
18538 if (glyphId === charCodeToGlyphId[charCode]) {
18539 return charCode | 0;
18542 newMapping.charCodeToGlyphId[newMapping.nextAvailableFontCharCode] =
18544 return newMapping.nextAvailableFontCharCode++;
18547 var seacs = font.seacs;
18548 if (SEAC_ANALYSIS_ENABLED && seacs && seacs.length) {
18549 var matrix = properties.fontMatrix || FONT_IDENTITY_MATRIX;
18550 var charset = font.getCharset();
18551 var seacMap = Object.create(null);
18552 for (var glyphId in seacs) {
18554 var seac = seacs[glyphId];
18555 var baseGlyphName = Encodings.StandardEncoding[seac[2]];
18556 var accentGlyphName = Encodings.StandardEncoding[seac[3]];
18557 var baseGlyphId = charset.indexOf(baseGlyphName);
18558 var accentGlyphId = charset.indexOf(accentGlyphName);
18559 if (baseGlyphId < 0 || accentGlyphId < 0) {
18562 var accentOffset = {
18563 x: seac[0] * matrix[0] + seac[1] * matrix[2] + matrix[4],
18564 y: seac[0] * matrix[1] + seac[1] * matrix[3] + matrix[5]
18567 var charCodes = getCharCodes(mapping, glyphId);
18569 // There's no point in mapping it if the char code was never mapped
18573 for (var i = 0, ii = charCodes.length; i < ii; i++) {
18574 var charCode = charCodes[i];
18575 // Find a fontCharCode that maps to the base and accent glyphs.
18576 // If one doesn't exists, create it.
18577 var charCodeToGlyphId = newMapping.charCodeToGlyphId;
18578 var baseFontCharCode = createCharCode(charCodeToGlyphId,
18580 var accentFontCharCode = createCharCode(charCodeToGlyphId,
18582 seacMap[charCode] = {
18583 baseFontCharCode: baseFontCharCode,
18584 accentFontCharCode: accentFontCharCode,
18585 accentOffset: accentOffset
18589 properties.seacMap = seacMap;
18592 var unitsPerEm = 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];
18594 var builder = new OpenTypeFileBuilder('\x4F\x54\x54\x4F');
18595 // PostScript Font Program
18596 builder.addTable('CFF ', font.data);
18597 // OS/2 and Windows Specific metrics
18598 builder.addTable('OS/2', createOS2Table(properties,
18599 newMapping.charCodeToGlyphId));
18600 // Character to glyphs mapping
18601 builder.addTable('cmap', createCmapTable(newMapping.charCodeToGlyphId));
18603 builder.addTable('head',
18604 '\x00\x01\x00\x00' + // Version number
18605 '\x00\x00\x10\x00' + // fontRevision
18606 '\x00\x00\x00\x00' + // checksumAdjustement
18607 '\x5F\x0F\x3C\xF5' + // magicNumber
18608 '\x00\x00' + // Flags
18609 safeString16(unitsPerEm) + // unitsPerEM
18610 '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // creation date
18611 '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // modifification date
18612 '\x00\x00' + // xMin
18613 safeString16(properties.descent) + // yMin
18614 '\x0F\xFF' + // xMax
18615 safeString16(properties.ascent) + // yMax
18616 string16(properties.italicAngle ? 2 : 0) + // macStyle
18617 '\x00\x11' + // lowestRecPPEM
18618 '\x00\x00' + // fontDirectionHint
18619 '\x00\x00' + // indexToLocFormat
18620 '\x00\x00'); // glyphDataFormat
18622 // Horizontal header
18623 builder.addTable('hhea',
18624 '\x00\x01\x00\x00' + // Version number
18625 safeString16(properties.ascent) + // Typographic Ascent
18626 safeString16(properties.descent) + // Typographic Descent
18627 '\x00\x00' + // Line Gap
18628 '\xFF\xFF' + // advanceWidthMax
18629 '\x00\x00' + // minLeftSidebearing
18630 '\x00\x00' + // minRightSidebearing
18631 '\x00\x00' + // xMaxExtent
18632 safeString16(properties.capHeight) + // caretSlopeRise
18633 safeString16(Math.tan(properties.italicAngle) *
18634 properties.xHeight) + // caretSlopeRun
18635 '\x00\x00' + // caretOffset
18636 '\x00\x00' + // -reserved-
18637 '\x00\x00' + // -reserved-
18638 '\x00\x00' + // -reserved-
18639 '\x00\x00' + // -reserved-
18640 '\x00\x00' + // metricDataFormat
18641 string16(numGlyphs)); // Number of HMetrics
18643 // Horizontal metrics
18644 builder.addTable('hmtx', (function fontFieldsHmtx() {
18645 var charstrings = font.charstrings;
18646 var cffWidths = font.cff ? font.cff.widths : null;
18647 var hmtx = '\x00\x00\x00\x00'; // Fake .notdef
18648 for (var i = 1, ii = numGlyphs; i < ii; i++) {
18651 var charstring = charstrings[i - 1];
18652 width = 'width' in charstring ? charstring.width : 0;
18653 } else if (cffWidths) {
18654 width = Math.ceil(cffWidths[i] || 0);
18656 hmtx += string16(width) + string16(0);
18662 builder.addTable('maxp',
18663 '\x00\x00\x50\x00' + // Version number
18664 string16(numGlyphs)); // Num of glyphs
18667 builder.addTable('name', createNameTable(fontName));
18669 // PostScript informations
18670 builder.addTable('post', createPostTable(properties));
18672 return builder.toArray();
18676 * Builds a char code to unicode map based on section 9.10 of the spec.
18677 * @param {Object} properties Font properties object.
18678 * @return {Object} A ToUnicodeMap object.
18680 buildToUnicode: function Font_buildToUnicode(properties) {
18681 // Section 9.10.2 Mapping Character Codes to Unicode Values
18682 if (properties.toUnicode && properties.toUnicode.length !== 0) {
18683 return properties.toUnicode;
18685 // According to the spec if the font is a simple font we should only map
18686 // to unicode if the base encoding is MacRoman, MacExpert, or WinAnsi or
18687 // the differences array only contains adobe standard or symbol set names,
18688 // in pratice it seems better to always try to create a toUnicode
18689 // map based of the default encoding.
18690 var toUnicode, charcode;
18691 if (!properties.composite /* is simple font */) {
18693 var encoding = properties.defaultEncoding.slice();
18694 var baseEncodingName = properties.baseEncodingName;
18695 // Merge in the differences array.
18696 var differences = properties.differences;
18697 for (charcode in differences) {
18698 encoding[charcode] = differences[charcode];
18700 for (charcode in encoding) {
18701 // a) Map the character code to a character name.
18702 var glyphName = encoding[charcode];
18703 // b) Look up the character name in the Adobe Glyph List (see the
18704 // Bibliography) to obtain the corresponding Unicode value.
18705 if (glyphName === '') {
18707 } else if (GlyphsUnicode[glyphName] === undefined) {
18708 // (undocumented) c) Few heuristics to recognize unknown glyphs
18709 // NOTE: Adobe Reader does not do this step, but OSX Preview does
18711 switch (glyphName[0]) {
18712 case 'G': // Gxx glyph
18713 if (glyphName.length === 3) {
18714 code = parseInt(glyphName.substr(1), 16);
18717 case 'g': // g00xx glyph
18718 if (glyphName.length === 5) {
18719 code = parseInt(glyphName.substr(1), 16);
18722 case 'C': // Cddd glyph
18723 case 'c': // cddd glyph
18724 if (glyphName.length >= 3) {
18725 code = +glyphName.substr(1);
18730 // If |baseEncodingName| is one the predefined encodings,
18731 // and |code| equals |charcode|, using the glyph defined in the
18732 // baseEncoding seems to yield a better |toUnicode| mapping
18733 // (fixes issue 5070).
18734 if (baseEncodingName && code === +charcode) {
18735 var baseEncoding = Encodings[baseEncodingName];
18736 if (baseEncoding && (glyphName = baseEncoding[charcode])) {
18737 toUnicode[charcode] =
18738 String.fromCharCode(GlyphsUnicode[glyphName]);
18742 toUnicode[charcode] = String.fromCharCode(code);
18746 toUnicode[charcode] = String.fromCharCode(GlyphsUnicode[glyphName]);
18748 return new ToUnicodeMap(toUnicode);
18750 // If the font is a composite font that uses one of the predefined CMaps
18751 // listed in Table 118 (except Identity–H and Identity–V) or whose
18752 // descendant CIDFont uses the Adobe-GB1, Adobe-CNS1, Adobe-Japan1, or
18753 // Adobe-Korea1 character collection:
18754 if (properties.composite && (
18755 (properties.cMap.builtInCMap &&
18756 !(properties.cMap instanceof IdentityCMap)) ||
18757 (properties.cidSystemInfo.registry === 'Adobe' &&
18758 (properties.cidSystemInfo.ordering === 'GB1' ||
18759 properties.cidSystemInfo.ordering === 'CNS1' ||
18760 properties.cidSystemInfo.ordering === 'Japan1' ||
18761 properties.cidSystemInfo.ordering === 'Korea1')))) {
18763 // a) Map the character code to a character identifier (CID) according
18764 // to the font’s CMap.
18765 // b) Obtain the registry and ordering of the character collection used
18766 // by the font’s CMap (for example, Adobe and Japan1) from its
18767 // CIDSystemInfo dictionary.
18768 var registry = properties.cidSystemInfo.registry;
18769 var ordering = properties.cidSystemInfo.ordering;
18770 // c) Construct a second CMap name by concatenating the registry and
18771 // ordering obtained in step (b) in the format registry–ordering–UCS2
18772 // (for example, Adobe–Japan1–UCS2).
18773 var ucs2CMapName = new Name(registry + '-' + ordering + '-UCS2');
18774 // d) Obtain the CMap with the name constructed in step (c) (available
18775 // from the ASN Web site; see the Bibliography).
18776 var ucs2CMap = CMapFactory.create(ucs2CMapName,
18777 { url: PDFJS.cMapUrl, packed: PDFJS.cMapPacked }, null);
18778 var cMap = properties.cMap;
18780 cMap.forEach(function(charcode, cid) {
18781 assert(cid <= 0xffff, 'Max size of CID is 65,535');
18782 // e) Map the CID obtained in step (a) according to the CMap obtained
18783 // in step (d), producing a Unicode value.
18784 var ucs2 = ucs2CMap.lookup(cid);
18786 toUnicode[charcode] =
18787 String.fromCharCode((ucs2.charCodeAt(0) << 8) +
18788 ucs2.charCodeAt(1));
18791 return new ToUnicodeMap(toUnicode);
18794 // The viewer's choice, just use an identity map.
18795 return new IdentityToUnicodeMap(properties.firstChar,
18796 properties.lastChar);
18800 if ('_shadowWidth' in this) {
18801 return this._shadowWidth;
18804 // trying to estimate space character width
18805 var possibleSpaceReplacements = ['space', 'minus', 'one', 'i'];
18807 for (var i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) {
18808 var glyphName = possibleSpaceReplacements[i];
18809 // if possible, getting width by glyph name
18810 if (glyphName in this.widths) {
18811 width = this.widths[glyphName];
18814 var glyphUnicode = GlyphsUnicode[glyphName];
18815 // finding the charcode via unicodeToCID map
18817 if (this.composite) {
18818 if (this.cMap.contains(glyphUnicode)) {
18819 charcode = this.cMap.lookup(glyphUnicode);
18822 // ... via toUnicode map
18823 if (!charcode && 'toUnicode' in this) {
18824 charcode = this.toUnicode.charCodeOf(glyphUnicode);
18826 // setting it to unicode if negative or undefined
18827 if (charcode <= 0) {
18828 charcode = glyphUnicode;
18830 // trying to get width via charcode
18831 width = this.widths[charcode];
18833 break; // the non-zero width found
18836 width = width || this.defaultWidth;
18837 // Do not shadow the property here. See discussion:
18838 // https://github.com/mozilla/pdf.js/pull/2127#discussion_r1662280
18839 this._shadowWidth = width;
18843 charToGlyph: function Font_charToGlyph(charcode) {
18844 var fontCharCode, width, operatorListId;
18846 var widthCode = charcode;
18847 if (this.cMap && this.cMap.contains(charcode)) {
18848 widthCode = this.cMap.lookup(charcode);
18850 width = this.widths[widthCode];
18851 width = isNum(width) ? width : this.defaultWidth;
18852 var vmetric = this.vmetrics && this.vmetrics[widthCode];
18854 var unicode = this.toUnicode.get(charcode) || charcode;
18855 if (typeof unicode === 'number') {
18856 unicode = String.fromCharCode(unicode);
18859 // First try the toFontChar map, if it's not there then try falling
18860 // back to the char code.
18861 fontCharCode = this.toFontChar[charcode] || charcode;
18862 if (this.missingFile) {
18863 fontCharCode = mapSpecialUnicodeValues(fontCharCode);
18866 if (this.isType3Font) {
18867 // Font char code in this case is actually a glyph name.
18868 operatorListId = fontCharCode;
18872 if (this.seacMap && this.seacMap[charcode]) {
18873 var seac = this.seacMap[charcode];
18874 fontCharCode = seac.baseFontCharCode;
18876 fontChar: String.fromCharCode(seac.accentFontCharCode),
18877 offset: seac.accentOffset
18881 var fontChar = String.fromCharCode(fontCharCode);
18883 var glyph = this.glyphCache[charcode];
18885 !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric,
18887 glyph = new Glyph(fontChar, unicode, accent, width, vmetric,
18889 this.glyphCache[charcode] = glyph;
18894 charsToGlyphs: function Font_charsToGlyphs(chars) {
18895 var charsCache = this.charsCache;
18896 var glyphs, glyph, charcode;
18898 // if we translated this string before, just grab it from the cache
18900 glyphs = charsCache[chars];
18906 // lazily create the translation cache
18908 charsCache = this.charsCache = Object.create(null);
18912 var charsCacheKey = chars;
18916 // composite fonts have multi-byte strings convert the string from
18917 // single-byte to multi-byte
18919 while (i < chars.length) {
18920 this.cMap.readCharCode(chars, i, c);
18921 charcode = c.charcode;
18922 var length = c.length;
18924 glyph = this.charToGlyph(charcode);
18925 glyphs.push(glyph);
18926 // placing null after each word break charcode (ASCII SPACE)
18927 // Ignore occurences of 0x20 in multiple-byte codes.
18928 if (length === 1 && chars.charCodeAt(i - 1) === 0x20) {
18933 for (i = 0, ii = chars.length; i < ii; ++i) {
18934 charcode = chars.charCodeAt(i);
18935 glyph = this.charToGlyph(charcode);
18936 glyphs.push(glyph);
18937 if (charcode === 0x20) {
18943 // Enter the translated string into the cache
18944 return (charsCache[charsCacheKey] = glyphs);
18951 var ErrorFont = (function ErrorFontClosure() {
18952 function ErrorFont(error) {
18953 this.error = error;
18954 this.loadedName = 'g_font_error';
18955 this.loading = false;
18958 ErrorFont.prototype = {
18959 charsToGlyphs: function ErrorFont_charsToGlyphs() {
18962 exportData: function ErrorFont_exportData() {
18963 return {error: this.error};
18971 * Shared logic for building a char code to glyph id mapping for Type1 and
18972 * simple CFF fonts. See section 9.6.6.2 of the spec.
18973 * @param {Object} properties Font properties object.
18974 * @param {Object} builtInEncoding The encoding contained within the actual font
18976 * @param {Array} Array of glyph names where the index is the glyph ID.
18977 * @returns {Object} A char code to glyph ID map.
18979 function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) {
18980 var charCodeToGlyphId = Object.create(null);
18981 var glyphId, charCode, baseEncoding;
18983 if (properties.baseEncodingName) {
18984 // If a valid base encoding name was used, the mapping is initialized with
18986 baseEncoding = Encodings[properties.baseEncodingName];
18987 for (charCode = 0; charCode < baseEncoding.length; charCode++) {
18988 glyphId = glyphNames.indexOf(baseEncoding[charCode]);
18989 if (glyphId >= 0) {
18990 charCodeToGlyphId[charCode] = glyphId;
18992 charCodeToGlyphId[charCode] = 0; // notdef
18995 } else if (!!(properties.flags & FontFlags.Symbolic)) {
18996 // For a symbolic font the encoding should be the fonts built-in
18998 for (charCode in builtInEncoding) {
18999 charCodeToGlyphId[charCode] = builtInEncoding[charCode];
19002 // For non-symbolic fonts that don't have a base encoding the standard
19003 // encoding should be used.
19004 baseEncoding = Encodings.StandardEncoding;
19005 for (charCode = 0; charCode < baseEncoding.length; charCode++) {
19006 glyphId = glyphNames.indexOf(baseEncoding[charCode]);
19007 if (glyphId >= 0) {
19008 charCodeToGlyphId[charCode] = glyphId;
19010 charCodeToGlyphId[charCode] = 0; // notdef
19015 // Lastly, merge in the differences.
19016 var differences = properties.differences;
19018 for (charCode in differences) {
19019 var glyphName = differences[charCode];
19020 glyphId = glyphNames.indexOf(glyphName);
19021 if (glyphId >= 0) {
19022 charCodeToGlyphId[charCode] = glyphId;
19024 charCodeToGlyphId[charCode] = 0; // notdef
19028 return charCodeToGlyphId;
19032 * CharStrings are encoded following the the CharString Encoding sequence
19033 * describe in Chapter 6 of the "Adobe Type1 Font Format" specification.
19034 * The value in a byte indicates a command, a number, or subsequent bytes
19035 * that are to be interpreted in a special way.
19037 * CharString Number Encoding:
19038 * A CharString byte containing the values from 32 through 255 inclusive
19039 * indicate an integer. These values are decoded in four ranges.
19041 * 1. A CharString byte containing a value, v, between 32 and 246 inclusive,
19042 * indicate the integer v - 139. Thus, the integer values from -107 through
19043 * 107 inclusive may be encoded in single byte.
19045 * 2. A CharString byte containing a value, v, between 247 and 250 inclusive,
19046 * indicates an integer involving the next byte, w, according to the formula:
19047 * [(v - 247) x 256] + w + 108
19049 * 3. A CharString byte containing a value, v, between 251 and 254 inclusive,
19050 * indicates an integer involving the next byte, w, according to the formula:
19051 * -[(v - 251) * 256] - w - 108
19053 * 4. A CharString containing the value 255 indicates that the next 4 bytes
19054 * are a two complement signed integer. The first of these bytes contains the
19055 * highest order bits, the second byte contains the next higher order bits
19056 * and the fourth byte contain the lowest order bits.
19059 * CharString Command Encoding:
19060 * CharStrings commands are encoded in 1 or 2 bytes.
19062 * Single byte commands are encoded in 1 byte that contains a value between
19063 * 0 and 31 inclusive.
19064 * If a command byte contains the value 12, then the value in the next byte
19065 * indicates a command. This "escape" mechanism allows many extra commands
19066 * to be encoded and this encoding technique helps to minimize the length of
19069 var Type1CharString = (function Type1CharStringClosure() {
19070 var COMMAND_MAP = {
19088 function Type1CharString() {
19091 this.flexing = false;
19096 Type1CharString.prototype = {
19097 convert: function Type1CharString_convert(encoded, subrs) {
19098 var count = encoded.length;
19100 var wx, sbx, subrNumber;
19101 for (var i = 0; i < count; i++) {
19102 var value = encoded[i];
19104 if (value === 12) {
19105 value = (value << 8) + encoded[++i];
19109 if (!HINTING_ENABLED) {
19113 error = this.executeCommand(2, COMMAND_MAP.hstem);
19116 if (!HINTING_ENABLED) {
19120 error = this.executeCommand(2, COMMAND_MAP.vstem);
19123 if (this.flexing) {
19124 if (this.stack.length < 1) {
19128 // Add the dx for flex and but also swap the values so they are
19129 // the right order.
19130 var dy = this.stack.pop();
19131 this.stack.push(0, dy);
19134 error = this.executeCommand(1, COMMAND_MAP.vmoveto);
19137 error = this.executeCommand(2, COMMAND_MAP.rlineto);
19140 error = this.executeCommand(1, COMMAND_MAP.hlineto);
19143 error = this.executeCommand(1, COMMAND_MAP.vlineto);
19145 case 8: // rrcurveto
19146 error = this.executeCommand(6, COMMAND_MAP.rrcurveto);
19148 case 9: // closepath
19149 // closepath is a Type1 command that does not take argument and is
19150 // useless in Type2 and it can simply be ignored.
19153 case 10: // callsubr
19154 if (this.stack.length < 1) {
19158 subrNumber = this.stack.pop();
19159 error = this.convert(subrs[subrNumber], subrs);
19164 if (this.stack.length < 2) {
19168 // To convert to type2 we have to move the width value to the
19169 // first part of the charstring and then use hmoveto with lsb.
19170 wx = this.stack.pop();
19171 sbx = this.stack.pop();
19174 this.stack.push(wx, sbx);
19175 error = this.executeCommand(2, COMMAND_MAP.hmoveto);
19177 case 14: // endchar
19178 this.output.push(COMMAND_MAP.endchar[0]);
19180 case 21: // rmoveto
19181 if (this.flexing) {
19184 error = this.executeCommand(2, COMMAND_MAP.rmoveto);
19186 case 22: // hmoveto
19187 if (this.flexing) {
19188 // Add the dy for flex.
19189 this.stack.push(0);
19192 error = this.executeCommand(1, COMMAND_MAP.hmoveto);
19194 case 30: // vhcurveto
19195 error = this.executeCommand(4, COMMAND_MAP.vhcurveto);
19197 case 31: // hvcurveto
19198 error = this.executeCommand(4, COMMAND_MAP.hvcurveto);
19200 case (12 << 8) + 0: // dotsection
19201 // dotsection is a Type1 command to specify some hinting feature
19202 // for dots that do not take a parameter and it can safely be
19203 // ignored for Type2.
19206 case (12 << 8) + 1: // vstem3
19207 if (!HINTING_ENABLED) {
19211 // [vh]stem3 are Type1 only and Type2 supports [vh]stem with
19212 // multiple parameters, so instead of returning [vh]stem3 take a
19213 // shortcut and return [vhstem] instead.
19214 error = this.executeCommand(2, COMMAND_MAP.vstem);
19216 case (12 << 8) + 2: // hstem3
19217 if (!HINTING_ENABLED) {
19222 error = this.executeCommand(2, COMMAND_MAP.hstem);
19224 case (12 << 8) + 6: // seac
19225 // seac is like type 2's special endchar but it doesn't use the
19226 // first argument asb, so remove it.
19227 if (SEAC_ANALYSIS_ENABLED) {
19228 this.seac = this.stack.splice(-4, 4);
19229 error = this.executeCommand(0, COMMAND_MAP.endchar);
19231 error = this.executeCommand(4, COMMAND_MAP.endchar);
19234 case (12 << 8) + 7: // sbw
19235 if (this.stack.length < 4) {
19239 // To convert to type2 we have to move the width value to the
19240 // first part of the charstring and then use rmoveto with
19241 // (dx, dy). The height argument will not be used for vmtx and
19242 // vhea tables reconstruction -- ignoring it.
19243 var wy = this.stack.pop();
19244 wx = this.stack.pop();
19245 var sby = this.stack.pop();
19246 sbx = this.stack.pop();
19249 this.stack.push(wx, sbx, sby);
19250 error = this.executeCommand(3, COMMAND_MAP.rmoveto);
19252 case (12 << 8) + 12: // div
19253 if (this.stack.length < 2) {
19257 var num2 = this.stack.pop();
19258 var num1 = this.stack.pop();
19259 this.stack.push(num1 / num2);
19261 case (12 << 8) + 16: // callothersubr
19262 if (this.stack.length < 2) {
19266 subrNumber = this.stack.pop();
19267 var numArgs = this.stack.pop();
19268 if (subrNumber === 0 && numArgs === 3) {
19269 var flexArgs = this.stack.splice(this.stack.length - 17, 17);
19271 flexArgs[2] + flexArgs[0], // bcp1x + rpx
19272 flexArgs[3] + flexArgs[1], // bcp1y + rpy
19273 flexArgs[4], // bcp2x
19274 flexArgs[5], // bcp2y
19275 flexArgs[6], // p2x
19276 flexArgs[7], // p2y
19277 flexArgs[8], // bcp3x
19278 flexArgs[9], // bcp3y
19279 flexArgs[10], // bcp4x
19280 flexArgs[11], // bcp4y
19281 flexArgs[12], // p3x
19282 flexArgs[13], // p3y
19283 flexArgs[14] // flexDepth
19284 // 15 = finalx unused by flex
19285 // 16 = finaly unused by flex
19287 error = this.executeCommand(13, COMMAND_MAP.flex, true);
19288 this.flexing = false;
19289 this.stack.push(flexArgs[15], flexArgs[16]);
19290 } else if (subrNumber === 1 && numArgs === 0) {
19291 this.flexing = true;
19294 case (12 << 8) + 17: // pop
19295 // Ignore this since it is only used with othersubr.
19297 case (12 << 8) + 33: // setcurrentpoint
19302 warn('Unknown type 1 charstring command of "' + value + '"');
19309 } else if (value <= 246) {
19310 value = value - 139;
19311 } else if (value <= 250) {
19312 value = ((value - 247) * 256) + encoded[++i] + 108;
19313 } else if (value <= 254) {
19314 value = -((value - 251) * 256) - encoded[++i] - 108;
19316 value = (encoded[++i] & 0xff) << 24 | (encoded[++i] & 0xff) << 16 |
19317 (encoded[++i] & 0xff) << 8 | (encoded[++i] & 0xff) << 0;
19319 this.stack.push(value);
19324 executeCommand: function(howManyArgs, command, keepStack) {
19325 var stackLength = this.stack.length;
19326 if (howManyArgs > stackLength) {
19329 var start = stackLength - howManyArgs;
19330 for (var i = start; i < stackLength; i++) {
19331 var value = this.stack[i];
19332 if (value === (value | 0)) { // int
19333 this.output.push(28, (value >> 8) & 0xff, value & 0xff);
19334 } else { // fixed point
19335 value = (65536 * value) | 0;
19336 this.output.push(255,
19337 (value >> 24) & 0xFF,
19338 (value >> 16) & 0xFF,
19339 (value >> 8) & 0xFF,
19343 this.output.push.apply(this.output, command);
19345 this.stack.splice(start, howManyArgs);
19347 this.stack.length = 0;
19353 return Type1CharString;
19357 * Type1Parser encapsulate the needed code for parsing a Type1 font
19358 * program. Some of its logic depends on the Type2 charstrings
19360 * Note: this doesn't really parse the font since that would require evaluation
19361 * of PostScript, but it is possible in most cases to extract what we need
19362 * without a full parse.
19364 var Type1Parser = (function Type1ParserClosure() {
19366 * Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
19367 * of Plaintext Bytes. The function took a key as a parameter which can be
19368 * for decrypting the eexec block of for decoding charStrings.
19370 var EEXEC_ENCRYPT_KEY = 55665;
19371 var CHAR_STRS_ENCRYPT_KEY = 4330;
19373 function isHexDigit(code) {
19374 return code >= 48 && code <= 57 || // '0'-'9'
19375 code >= 65 && code <= 70 || // 'A'-'F'
19376 code >= 97 && code <= 102; // 'a'-'f'
19379 function decrypt(data, key, discardNumber) {
19380 var r = key | 0, c1 = 52845, c2 = 22719;
19381 var count = data.length;
19382 var decrypted = new Uint8Array(count);
19383 for (var i = 0; i < count; i++) {
19384 var value = data[i];
19385 decrypted[i] = value ^ (r >> 8);
19386 r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
19388 return Array.prototype.slice.call(decrypted, discardNumber);
19391 function decryptAscii(data, key, discardNumber) {
19392 var r = key | 0, c1 = 52845, c2 = 22719;
19393 var count = data.length, maybeLength = count >>> 1;
19394 var decrypted = new Uint8Array(maybeLength);
19396 for (i = 0, j = 0; i < count; i++) {
19397 var digit1 = data[i];
19398 if (!isHexDigit(digit1)) {
19403 while (i < count && !isHexDigit(digit2 = data[i])) {
19407 var value = parseInt(String.fromCharCode(digit1, digit2), 16);
19408 decrypted[j++] = value ^ (r >> 8);
19409 r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
19412 return Array.prototype.slice.call(decrypted, discardNumber, j);
19415 function isSpecial(c) {
19416 return c === 0x2F || // '/'
19417 c === 0x5B || c === 0x5D || // '[', ']'
19418 c === 0x7B || c === 0x7D || // '{', '}'
19419 c === 0x28 || c === 0x29; // '(', ')'
19422 function Type1Parser(stream, encrypted) {
19424 var data = stream.getBytes();
19425 var isBinary = !(isHexDigit(data[0]) && isHexDigit(data[1]) &&
19426 isHexDigit(data[2]) && isHexDigit(data[3]));
19427 stream = new Stream(isBinary ? decrypt(data, EEXEC_ENCRYPT_KEY, 4) :
19428 decryptAscii(data, EEXEC_ENCRYPT_KEY, 4));
19430 this.stream = stream;
19434 Type1Parser.prototype = {
19435 readNumberArray: function Type1Parser_readNumberArray() {
19436 this.getToken(); // read '[' or '{' (arrays can start with either)
19439 var token = this.getToken();
19440 if (token === null || token === ']' || token === '}') {
19443 array.push(parseFloat(token || 0));
19448 readNumber: function Type1Parser_readNumber() {
19449 var token = this.getToken();
19450 return parseFloat(token || 0);
19453 readInt: function Type1Parser_readInt() {
19454 // Use '| 0' to prevent setting a double into length such as the double
19455 // does not flow into the loop variable.
19456 var token = this.getToken();
19457 return parseInt(token || 0, 10) | 0;
19460 readBoolean: function Type1Parser_readBoolean() {
19461 var token = this.getToken();
19463 // Use 1 and 0 since that's what type2 charstrings use.
19464 return token === 'true' ? 1 : 0;
19467 nextChar : function Type1_nextChar() {
19468 return (this.currentChar = this.stream.getByte());
19471 getToken: function Type1Parser_getToken() {
19472 // Eat whitespace and comments.
19473 var comment = false;
19474 var ch = this.currentChar;
19481 if (ch === 0x0A || ch === 0x0D) {
19484 } else if (ch === 0x25) { // '%'
19486 } else if (!Lexer.isSpace(ch)) {
19489 ch = this.nextChar();
19491 if (isSpecial(ch)) {
19493 return String.fromCharCode(ch);
19497 token += String.fromCharCode(ch);
19498 ch = this.nextChar();
19499 } while (ch >= 0 && !Lexer.isSpace(ch) && !isSpecial(ch));
19504 * Returns an object containing a Subrs array and a CharStrings
19505 * array extracted from and eexec encrypted block of data
19507 extractFontProgram: function Type1Parser_extractFontProgram() {
19508 var stream = this.stream;
19510 var subrs = [], charstrings = [];
19520 var token, length, data, lenIV, encoded;
19521 while ((token = this.getToken()) !== null) {
19522 if (token !== '/') {
19525 token = this.getToken();
19527 case 'CharStrings':
19528 // The number immediately following CharStrings must be greater or
19529 // equal to the number of CharStrings.
19531 this.getToken(); // read in 'dict'
19532 this.getToken(); // read in 'dup'
19533 this.getToken(); // read in 'begin'
19535 token = this.getToken();
19536 if (token === null || token === 'end') {
19540 if (token !== '/') {
19543 var glyph = this.getToken();
19544 length = this.readInt();
19545 this.getToken(); // read in 'RD' or '-|'
19546 data = stream.makeSubStream(stream.pos, length);
19547 lenIV = program.properties.privateData['lenIV'];
19548 encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, lenIV);
19549 // Skip past the required space and binary data.
19550 stream.skip(length);
19552 token = this.getToken(); // read in 'ND' or '|-'
19553 if (token === 'noaccess') {
19554 this.getToken(); // read in 'def'
19563 var num = this.readInt();
19564 this.getToken(); // read in 'array'
19565 while ((token = this.getToken()) === 'dup') {
19566 var index = this.readInt();
19567 length = this.readInt();
19568 this.getToken(); // read in 'RD' or '-|'
19569 data = stream.makeSubStream(stream.pos, length);
19570 lenIV = program.properties.privateData['lenIV'];
19571 encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, lenIV);
19572 // Skip past the required space and binary data.
19573 stream.skip(length);
19575 token = this.getToken(); // read in 'NP' or '|'
19576 if (token === 'noaccess') {
19577 this.getToken(); // read in 'put'
19579 subrs[index] = encoded;
19584 case 'FamilyBlues':
19585 case 'FamilyOtherBlues':
19586 var blueArray = this.readNumberArray();
19587 // *Blue* values may contain invalid data: disables reading of
19588 // those values when hinting is disabled.
19589 if (blueArray.length > 0 && (blueArray.length % 2) === 0 &&
19591 program.properties.privateData[token] = blueArray;
19596 program.properties.privateData[token] = this.readNumberArray();
19600 program.properties.privateData[token] =
19601 this.readNumberArray()[0];
19607 case 'LanguageGroup':
19608 case 'ExpansionFactor':
19609 program.properties.privateData[token] = this.readNumber();
19612 program.properties.privateData[token] = this.readBoolean();
19617 for (var i = 0; i < charstrings.length; i++) {
19618 glyph = charstrings[i].glyph;
19619 encoded = charstrings[i].encoded;
19620 var charString = new Type1CharString();
19621 var error = charString.convert(encoded, subrs);
19622 var output = charString.output;
19624 // It seems when FreeType encounters an error while evaluating a glyph
19625 // that it completely ignores the glyph so we'll mimic that behaviour
19626 // here and put an endchar to make the validator happy.
19629 program.charstrings.push({
19631 charstring: output,
19632 width: charString.width,
19633 lsb: charString.lsb,
19634 seac: charString.seac
19641 extractFontHeader: function Type1Parser_extractFontHeader(properties) {
19643 while ((token = this.getToken()) !== null) {
19644 if (token !== '/') {
19647 token = this.getToken();
19650 var matrix = this.readNumberArray();
19651 properties.fontMatrix = matrix;
19654 var encodingArg = this.getToken();
19656 if (!/^\d+$/.test(encodingArg)) {
19657 // encoding name is specified
19658 encoding = Encodings[encodingArg];
19661 var size = parseInt(encodingArg, 10) | 0;
19662 this.getToken(); // read in 'array'
19664 for (var j = 0; j < size; j++) {
19665 token = this.getToken();
19666 // skipping till first dup or def (e.g. ignoring for statement)
19667 while (token !== 'dup' && token !== 'def') {
19668 token = this.getToken();
19669 if (token === null) {
19670 return; // invalid header
19673 if (token === 'def') {
19674 break; // read all array data
19676 var index = this.readInt();
19677 this.getToken(); // read in '/'
19678 var glyph = this.getToken();
19679 encoding[index] = glyph;
19680 this.getToken(); // read the in 'put'
19683 properties.builtInEncoding = encoding;
19686 var fontBBox = this.readNumberArray();
19687 // adjusting ascent/descent
19688 properties.ascent = fontBBox[3];
19689 properties.descent = fontBBox[1];
19690 properties.ascentScaled = true;
19697 return Type1Parser;
19701 * The CFF class takes a Type1 file and wrap it into a
19702 * 'Compact Font Format' which itself embed Type2 charstrings.
19704 var CFFStandardStrings = [
19705 '.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
19706 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus',
19707 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four',
19708 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less',
19709 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
19710 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
19711 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum',
19712 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
19713 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
19714 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent',
19715 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency',
19716 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft',
19717 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl',
19718 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase',
19719 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown',
19720 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent',
19721 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash',
19722 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
19723 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
19724 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
19725 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
19726 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
19727 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
19728 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
19729 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
19730 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
19731 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
19732 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde',
19733 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute',
19734 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex',
19735 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex',
19736 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron', 'exclamsmall',
19737 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall',
19738 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader',
19739 'onedotenleader', 'zerooldstyle', 'oneoldstyle', 'twooldstyle',
19740 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle',
19741 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'commasuperior',
19742 'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior',
19743 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior',
19744 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
19745 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior',
19746 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall',
19747 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall',
19748 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
19749 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall',
19750 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah',
19751 'Tildesmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall',
19752 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall',
19753 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior',
19754 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'questiondownsmall', 'oneeighth',
19755 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds',
19756 'zerosuperior', 'foursuperior', 'fivesuperior', 'sixsuperior',
19757 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
19758 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior',
19759 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior',
19760 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior',
19761 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall',
19762 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall',
19763 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall',
19764 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall',
19765 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall',
19766 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall',
19767 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall',
19768 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002', '001.003',
19769 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'
19772 // Type1Font is also a CIDFontType0.
19773 var Type1Font = function Type1Font(name, file, properties) {
19774 // Some bad generators embed pfb file as is, we have to strip 6-byte headers.
19775 // Also, length1 and length2 might be off by 6 bytes as well.
19776 // http://www.math.ubc.ca/~cass/piscript/type1.pdf
19777 var PFB_HEADER_SIZE = 6;
19778 var headerBlockLength = properties.length1;
19779 var eexecBlockLength = properties.length2;
19780 var pfbHeader = file.peekBytes(PFB_HEADER_SIZE);
19781 var pfbHeaderPresent = pfbHeader[0] === 0x80 && pfbHeader[1] === 0x01;
19782 if (pfbHeaderPresent) {
19783 file.skip(PFB_HEADER_SIZE);
19784 headerBlockLength = (pfbHeader[5] << 24) | (pfbHeader[4] << 16) |
19785 (pfbHeader[3] << 8) | pfbHeader[2];
19788 // Get the data block containing glyphs and subrs informations
19789 var headerBlock = new Stream(file.getBytes(headerBlockLength));
19790 var headerBlockParser = new Type1Parser(headerBlock);
19791 headerBlockParser.extractFontHeader(properties);
19793 if (pfbHeaderPresent) {
19794 pfbHeader = file.getBytes(PFB_HEADER_SIZE);
19795 eexecBlockLength = (pfbHeader[5] << 24) | (pfbHeader[4] << 16) |
19796 (pfbHeader[3] << 8) | pfbHeader[2];
19799 // Decrypt the data blocks and retrieve it's content
19800 var eexecBlock = new Stream(file.getBytes(eexecBlockLength));
19801 var eexecBlockParser = new Type1Parser(eexecBlock, true);
19802 var data = eexecBlockParser.extractFontProgram();
19803 for (var info in data.properties) {
19804 properties[info] = data.properties[info];
19807 var charstrings = data.charstrings;
19808 var type2Charstrings = this.getType2Charstrings(charstrings);
19809 var subrs = this.getType2Subrs(data.subrs);
19811 this.charstrings = charstrings;
19812 this.data = this.wrap(name, type2Charstrings, this.charstrings,
19813 subrs, properties);
19814 this.seacs = this.getSeacs(data.charstrings);
19817 Type1Font.prototype = {
19819 return this.charstrings.length + 1;
19822 getCharset: function Type1Font_getCharset() {
19823 var charset = ['.notdef'];
19824 var charstrings = this.charstrings;
19825 for (var glyphId = 0; glyphId < charstrings.length; glyphId++) {
19826 charset.push(charstrings[glyphId].glyphName);
19831 getGlyphMapping: function Type1Font_getGlyphMapping(properties) {
19832 var charstrings = this.charstrings;
19833 var glyphNames = ['.notdef'], glyphId;
19834 for (glyphId = 0; glyphId < charstrings.length; glyphId++) {
19835 glyphNames.push(charstrings[glyphId].glyphName);
19837 var encoding = properties.builtInEncoding;
19839 var builtInEncoding = {};
19840 for (var charCode in encoding) {
19841 glyphId = glyphNames.indexOf(encoding[charCode]);
19842 if (glyphId >= 0) {
19843 builtInEncoding[charCode] = glyphId;
19848 return type1FontGlyphMapping(properties, builtInEncoding, glyphNames);
19851 getSeacs: function Type1Font_getSeacs(charstrings) {
19854 for (i = 0, ii = charstrings.length; i < ii; i++) {
19855 var charstring = charstrings[i];
19856 if (charstring.seac) {
19857 // Offset by 1 for .notdef
19858 seacMap[i + 1] = charstring.seac;
19864 getType2Charstrings: function Type1Font_getType2Charstrings(
19865 type1Charstrings) {
19866 var type2Charstrings = [];
19867 for (var i = 0, ii = type1Charstrings.length; i < ii; i++) {
19868 type2Charstrings.push(type1Charstrings[i].charstring);
19870 return type2Charstrings;
19873 getType2Subrs: function Type1Font_getType2Subrs(type1Subrs) {
19875 var count = type1Subrs.length;
19876 if (count < 1133) {
19878 } else if (count < 33769) {
19884 // Add a bunch of empty subrs to deal with the Type2 bias
19885 var type2Subrs = [];
19887 for (i = 0; i < bias; i++) {
19888 type2Subrs.push([0x0B]);
19891 for (i = 0; i < count; i++) {
19892 type2Subrs.push(type1Subrs[i]);
19898 wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
19899 var cff = new CFF();
19900 cff.header = new CFFHeader(1, 0, 4, 4);
19902 cff.names = [name];
19904 var topDict = new CFFTopDict();
19905 // CFF strings IDs 0...390 are predefined names, so refering
19906 // to entries in our own String INDEX starts at SID 391.
19907 topDict.setByName('version', 391);
19908 topDict.setByName('Notice', 392);
19909 topDict.setByName('FullName', 393);
19910 topDict.setByName('FamilyName', 394);
19911 topDict.setByName('Weight', 395);
19912 topDict.setByName('Encoding', null); // placeholder
19913 topDict.setByName('FontMatrix', properties.fontMatrix);
19914 topDict.setByName('FontBBox', properties.bbox);
19915 topDict.setByName('charset', null); // placeholder
19916 topDict.setByName('CharStrings', null); // placeholder
19917 topDict.setByName('Private', null); // placeholder
19918 cff.topDict = topDict;
19920 var strings = new CFFStrings();
19921 strings.add('Version 0.11'); // Version
19922 strings.add('See original notice'); // Notice
19923 strings.add(name); // FullName
19924 strings.add(name); // FamilyName
19925 strings.add('Medium'); // Weight
19926 cff.strings = strings;
19928 cff.globalSubrIndex = new CFFIndex();
19930 var count = glyphs.length;
19931 var charsetArray = [0];
19933 for (i = 0; i < count; i++) {
19934 var index = CFFStandardStrings.indexOf(charstrings[i].glyphName);
19935 // TODO: Insert the string and correctly map it. Previously it was
19936 // thought mapping names that aren't in the standard strings to .notdef
19937 // was fine, however in issue818 when mapping them all to .notdef the
19938 // adieresis glyph no longer worked.
19939 if (index === -1) {
19942 charsetArray.push((index >> 8) & 0xff, index & 0xff);
19944 cff.charset = new CFFCharset(false, 0, [], charsetArray);
19946 var charStringsIndex = new CFFIndex();
19947 charStringsIndex.add([0x8B, 0x0E]); // .notdef
19948 for (i = 0; i < count; i++) {
19949 charStringsIndex.add(glyphs[i]);
19951 cff.charStrings = charStringsIndex;
19953 var privateDict = new CFFPrivateDict();
19954 privateDict.setByName('Subrs', null); // placeholder
19959 'FamilyOtherBlues',
19971 for (i = 0, ii = fields.length; i < ii; i++) {
19972 var field = fields[i];
19973 if (!properties.privateData.hasOwnProperty(field)) {
19976 var value = properties.privateData[field];
19977 if (isArray(value)) {
19978 // All of the private dictionary array data in CFF must be stored as
19979 // "delta-encoded" numbers.
19980 for (var j = value.length - 1; j > 0; j--) {
19981 value[j] -= value[j - 1]; // ... difference from previous value
19984 privateDict.setByName(field, value);
19986 cff.topDict.privateDict = privateDict;
19988 var subrIndex = new CFFIndex();
19989 for (i = 0, ii = subrs.length; i < ii; i++) {
19990 subrIndex.add(subrs[i]);
19992 privateDict.subrsIndex = subrIndex;
19994 var compiler = new CFFCompiler(cff);
19995 return compiler.compile();
19999 var CFFFont = (function CFFFontClosure() {
20000 function CFFFont(file, properties) {
20001 this.properties = properties;
20003 var parser = new CFFParser(file, properties);
20004 this.cff = parser.parse();
20005 var compiler = new CFFCompiler(this.cff);
20006 this.seacs = this.cff.seacs;
20008 this.data = compiler.compile();
20010 warn('Failed to compile font ' + properties.loadedName);
20011 // There may have just been an issue with the compiler, set the data
20012 // anyway and hope the font loaded.
20017 CFFFont.prototype = {
20019 return this.cff.charStrings.count;
20021 getCharset: function CFFFont_getCharset() {
20022 return this.cff.charset.charset;
20024 getGlyphMapping: function CFFFont_getGlyphMapping() {
20025 var cff = this.cff;
20026 var properties = this.properties;
20027 var charsets = cff.charset.charset;
20028 var charCodeToGlyphId;
20031 if (properties.composite) {
20032 charCodeToGlyphId = Object.create(null);
20033 if (cff.isCIDFont) {
20034 // If the font is actually a CID font then we should use the charset
20035 // to map CIDs to GIDs.
20036 for (glyphId = 0; glyphId < charsets.length; glyphId++) {
20037 var cid = charsets[glyphId];
20038 var charCode = properties.cMap.charCodeOf(cid);
20039 charCodeToGlyphId[charCode] = glyphId;
20042 // If it is NOT actually a CID font then CIDs should be mapped
20043 // directly to GIDs.
20044 for (glyphId = 0; glyphId < cff.charStrings.count; glyphId++) {
20045 charCodeToGlyphId[glyphId] = glyphId;
20048 return charCodeToGlyphId;
20051 var encoding = cff.encoding ? cff.encoding.encoding : null;
20052 charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
20053 return charCodeToGlyphId;
20060 var CFFParser = (function CFFParserClosure() {
20061 var CharstringValidationData = [
20063 { id: 'hstem', min: 2, stackClearing: true, stem: true },
20065 { id: 'vstem', min: 2, stackClearing: true, stem: true },
20066 { id: 'vmoveto', min: 1, stackClearing: true },
20067 { id: 'rlineto', min: 2, resetStack: true },
20068 { id: 'hlineto', min: 1, resetStack: true },
20069 { id: 'vlineto', min: 1, resetStack: true },
20070 { id: 'rrcurveto', min: 6, resetStack: true },
20072 { id: 'callsubr', min: 1, undefStack: true },
20073 { id: 'return', min: 0, undefStack: true },
20076 { id: 'endchar', min: 0, stackClearing: true },
20080 { id: 'hstemhm', min: 2, stackClearing: true, stem: true },
20081 { id: 'hintmask', min: 0, stackClearing: true },
20082 { id: 'cntrmask', min: 0, stackClearing: true },
20083 { id: 'rmoveto', min: 2, stackClearing: true },
20084 { id: 'hmoveto', min: 1, stackClearing: true },
20085 { id: 'vstemhm', min: 2, stackClearing: true, stem: true },
20086 { id: 'rcurveline', min: 8, resetStack: true },
20087 { id: 'rlinecurve', min: 8, resetStack: true },
20088 { id: 'vvcurveto', min: 4, resetStack: true },
20089 { id: 'hhcurveto', min: 4, resetStack: true },
20091 { id: 'callgsubr', min: 1, undefStack: true },
20092 { id: 'vhcurveto', min: 4, resetStack: true },
20093 { id: 'hvcurveto', min: 4, resetStack: true }
20095 var CharstringValidationData12 = [
20099 { id: 'and', min: 2, stackDelta: -1 },
20100 { id: 'or', min: 2, stackDelta: -1 },
20101 { id: 'not', min: 1, stackDelta: 0 },
20105 { id: 'abs', min: 1, stackDelta: 0 },
20106 { id: 'add', min: 2, stackDelta: -1,
20107 stackFn: function stack_div(stack, index) {
20108 stack[index - 2] = stack[index - 2] + stack[index - 1];
20111 { id: 'sub', min: 2, stackDelta: -1,
20112 stackFn: function stack_div(stack, index) {
20113 stack[index - 2] = stack[index - 2] - stack[index - 1];
20116 { id: 'div', min: 2, stackDelta: -1,
20117 stackFn: function stack_div(stack, index) {
20118 stack[index - 2] = stack[index - 2] / stack[index - 1];
20122 { id: 'neg', min: 1, stackDelta: 0,
20123 stackFn: function stack_div(stack, index) {
20124 stack[index - 1] = -stack[index - 1];
20127 { id: 'eq', min: 2, stackDelta: -1 },
20130 { id: 'drop', min: 1, stackDelta: -1 },
20132 { id: 'put', min: 2, stackDelta: -2 },
20133 { id: 'get', min: 1, stackDelta: 0 },
20134 { id: 'ifelse', min: 4, stackDelta: -3 },
20135 { id: 'random', min: 0, stackDelta: 1 },
20136 { id: 'mul', min: 2, stackDelta: -1,
20137 stackFn: function stack_div(stack, index) {
20138 stack[index - 2] = stack[index - 2] * stack[index - 1];
20142 { id: 'sqrt', min: 1, stackDelta: 0 },
20143 { id: 'dup', min: 1, stackDelta: 1 },
20144 { id: 'exch', min: 2, stackDelta: 0 },
20145 { id: 'index', min: 2, stackDelta: 0 },
20146 { id: 'roll', min: 3, stackDelta: -2 },
20150 { id: 'hflex', min: 7, resetStack: true },
20151 { id: 'flex', min: 13, resetStack: true },
20152 { id: 'hflex1', min: 9, resetStack: true },
20153 { id: 'flex1', min: 11, resetStack: true }
20156 function CFFParser(file, properties) {
20157 this.bytes = file.getBytes();
20158 this.properties = properties;
20160 CFFParser.prototype = {
20161 parse: function CFFParser_parse() {
20162 var properties = this.properties;
20163 var cff = new CFF();
20166 // The first five sections must be in order, all the others are reached
20167 // via offsets contained in one of the below.
20168 var header = this.parseHeader();
20169 var nameIndex = this.parseIndex(header.endPos);
20170 var topDictIndex = this.parseIndex(nameIndex.endPos);
20171 var stringIndex = this.parseIndex(topDictIndex.endPos);
20172 var globalSubrIndex = this.parseIndex(stringIndex.endPos);
20174 var topDictParsed = this.parseDict(topDictIndex.obj.get(0));
20175 var topDict = this.createDict(CFFTopDict, topDictParsed, cff.strings);
20177 cff.header = header.obj;
20178 cff.names = this.parseNameIndex(nameIndex.obj);
20179 cff.strings = this.parseStringIndex(stringIndex.obj);
20180 cff.topDict = topDict;
20181 cff.globalSubrIndex = globalSubrIndex.obj;
20183 this.parsePrivateDict(cff.topDict);
20185 cff.isCIDFont = topDict.hasName('ROS');
20187 var charStringOffset = topDict.getByName('CharStrings');
20188 var charStringsAndSeacs = this.parseCharStrings(charStringOffset);
20189 cff.charStrings = charStringsAndSeacs.charStrings;
20190 cff.seacs = charStringsAndSeacs.seacs;
20191 cff.widths = charStringsAndSeacs.widths;
20193 var fontMatrix = topDict.getByName('FontMatrix');
20195 properties.fontMatrix = fontMatrix;
20198 var fontBBox = topDict.getByName('FontBBox');
20200 // adjusting ascent/descent
20201 properties.ascent = fontBBox[3];
20202 properties.descent = fontBBox[1];
20203 properties.ascentScaled = true;
20206 var charset, encoding;
20207 if (cff.isCIDFont) {
20208 var fdArrayIndex = this.parseIndex(topDict.getByName('FDArray')).obj;
20209 for (var i = 0, ii = fdArrayIndex.count; i < ii; ++i) {
20210 var dictRaw = fdArrayIndex.get(i);
20211 var fontDict = this.createDict(CFFTopDict, this.parseDict(dictRaw),
20213 this.parsePrivateDict(fontDict);
20214 cff.fdArray.push(fontDict);
20216 // cid fonts don't have an encoding
20218 charset = this.parseCharsets(topDict.getByName('charset'),
20219 cff.charStrings.count, cff.strings, true);
20220 cff.fdSelect = this.parseFDSelect(topDict.getByName('FDSelect'),
20221 cff.charStrings.count);
20223 charset = this.parseCharsets(topDict.getByName('charset'),
20224 cff.charStrings.count, cff.strings, false);
20225 encoding = this.parseEncoding(topDict.getByName('Encoding'),
20227 cff.strings, charset.charset);
20229 cff.charset = charset;
20230 cff.encoding = encoding;
20234 parseHeader: function CFFParser_parseHeader() {
20235 var bytes = this.bytes;
20236 var bytesLength = bytes.length;
20239 // Prevent an infinite loop, by checking that the offset is within the
20240 // bounds of the bytes array. Necessary in empty, or invalid, font files.
20241 while (offset < bytesLength && bytes[offset] !== 1) {
20244 if (offset >= bytesLength) {
20245 error('Invalid CFF header');
20246 } else if (offset !== 0) {
20247 info('cff data is shifted');
20248 bytes = bytes.subarray(offset);
20249 this.bytes = bytes;
20251 var major = bytes[0];
20252 var minor = bytes[1];
20253 var hdrSize = bytes[2];
20254 var offSize = bytes[3];
20255 var header = new CFFHeader(major, minor, hdrSize, offSize);
20256 return { obj: header, endPos: hdrSize };
20258 parseDict: function CFFParser_parseDict(dict) {
20261 function parseOperand() {
20262 var value = dict[pos++];
20263 if (value === 30) {
20264 return parseFloatOperand(pos);
20265 } else if (value === 28) {
20266 value = dict[pos++];
20267 value = ((value << 24) | (dict[pos++] << 16)) >> 16;
20269 } else if (value === 29) {
20270 value = dict[pos++];
20271 value = (value << 8) | dict[pos++];
20272 value = (value << 8) | dict[pos++];
20273 value = (value << 8) | dict[pos++];
20275 } else if (value >= 32 && value <= 246) {
20276 return value - 139;
20277 } else if (value >= 247 && value <= 250) {
20278 return ((value - 247) * 256) + dict[pos++] + 108;
20279 } else if (value >= 251 && value <= 254) {
20280 return -((value - 251) * 256) - dict[pos++] - 108;
20282 error('255 is not a valid DICT command');
20287 function parseFloatOperand() {
20290 var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
20291 '9', '.', 'E', 'E-', null, '-'];
20292 var length = dict.length;
20293 while (pos < length) {
20294 var b = dict[pos++];
20308 return parseFloat(str);
20315 var end = dict.length;
20316 while (pos < end) {
20320 b = (b << 8) | dict[++pos];
20322 entries.push([b, operands]);
20326 operands.push(parseOperand());
20331 parseIndex: function CFFParser_parseIndex(pos) {
20332 var cffIndex = new CFFIndex();
20333 var bytes = this.bytes;
20334 var count = (bytes[pos++] << 8) | bytes[pos++];
20340 var offsetSize = bytes[pos++];
20341 // add 1 for offset to determine size of last object
20342 var startPos = pos + ((count + 1) * offsetSize) - 1;
20344 for (i = 0, ii = count + 1; i < ii; ++i) {
20346 for (var j = 0; j < offsetSize; ++j) {
20348 offset += bytes[pos++];
20350 offsets.push(startPos + offset);
20352 end = offsets[count];
20354 for (i = 0, ii = offsets.length - 1; i < ii; ++i) {
20355 var offsetStart = offsets[i];
20356 var offsetEnd = offsets[i + 1];
20357 cffIndex.add(bytes.subarray(offsetStart, offsetEnd));
20359 return {obj: cffIndex, endPos: end};
20361 parseNameIndex: function CFFParser_parseNameIndex(index) {
20363 for (var i = 0, ii = index.count; i < ii; ++i) {
20364 var name = index.get(i);
20365 // OTS doesn't allow names to be over 127 characters.
20366 var length = Math.min(name.length, 127);
20368 // OTS also only permits certain characters in the name.
20369 for (var j = 0; j < length; ++j) {
20371 if (j === 0 && c === 0) {
20375 if ((c < 33 || c > 126) || c === 91 /* [ */ || c === 93 /* ] */ ||
20376 c === 40 /* ( */ || c === 41 /* ) */ || c === 123 /* { */ ||
20377 c === 125 /* } */ || c === 60 /* < */ || c === 62 /* > */ ||
20378 c === 47 /* / */ || c === 37 /* % */ || c === 35 /* # */) {
20384 names.push(bytesToString(data));
20388 parseStringIndex: function CFFParser_parseStringIndex(index) {
20389 var strings = new CFFStrings();
20390 for (var i = 0, ii = index.count; i < ii; ++i) {
20391 var data = index.get(i);
20392 strings.add(bytesToString(data));
20396 createDict: function CFFParser_createDict(Type, dict, strings) {
20397 var cffDict = new Type(strings);
20398 for (var i = 0, ii = dict.length; i < ii; ++i) {
20399 var pair = dict[i];
20401 var value = pair[1];
20402 cffDict.setByKey(key, value);
20406 parseCharStrings: function CFFParser_parseCharStrings(charStringOffset) {
20407 var charStrings = this.parseIndex(charStringOffset).obj;
20410 var count = charStrings.count;
20411 for (var i = 0; i < count; i++) {
20412 var charstring = charStrings.get(i);
20416 var undefStack = true;
20419 var data = charstring;
20420 var length = data.length;
20421 var firstStackClearing = true;
20422 for (var j = 0; j < length;) {
20423 var value = data[j++];
20424 var validationCommand = null;
20425 if (value === 12) {
20428 // The CFF specification state that the 'dotsection' command
20429 // (12, 0) is deprecated and treated as a no-op, but all Type2
20430 // charstrings processors should support them. Unfortunately
20431 // the font sanitizer don't. As a workaround the sequence (12, 0)
20432 // is replaced by a useless (0, hmoveto).
20437 validationCommand = CharstringValidationData12[q];
20439 } else if (value === 28) { // number (16 bit)
20440 stack[stackSize] = ((data[j] << 24) | (data[j + 1] << 16)) >> 16;
20443 } else if (value === 14) {
20444 if (stackSize >= 4) {
20446 if (SEAC_ANALYSIS_ENABLED) {
20447 seacs[i] = stack.slice(stackSize, stackSize + 4);
20451 validationCommand = CharstringValidationData[value];
20452 } else if (value >= 32 && value <= 246) { // number
20453 stack[stackSize] = value - 139;
20455 } else if (value >= 247 && value <= 254) { // number (+1 bytes)
20456 stack[stackSize] = (value < 251 ?
20457 ((value - 247) << 8) + data[j] + 108 :
20458 -((value - 251) << 8) - data[j] - 108);
20461 } else if (value === 255) { // number (32 bit)
20462 stack[stackSize] = ((data[j] << 24) | (data[j + 1] << 16) |
20463 (data[j + 2] << 8) | data[j + 3]) / 65536;
20466 } else if (value === 19 || value === 20) {
20467 hints += stackSize >> 1;
20468 j += (hints + 7) >> 3; // skipping right amount of hints flag data
20470 validationCommand = CharstringValidationData[value];
20472 validationCommand = CharstringValidationData[value];
20474 if (validationCommand) {
20475 if (validationCommand.stem) {
20476 hints += stackSize >> 1;
20478 if ('min' in validationCommand) {
20479 if (!undefStack && stackSize < validationCommand.min) {
20480 warn('Not enough parameters for ' + validationCommand.id +
20481 '; actual: ' + stackSize +
20482 ', expected: ' + validationCommand.min);
20487 if (firstStackClearing && validationCommand.stackClearing) {
20488 firstStackClearing = false;
20489 // the optional character width can be found before the first
20490 // stack-clearing command arguments
20491 stackSize -= validationCommand.min;
20492 if (stackSize >= 2 && validationCommand.stem) {
20493 // there are even amount of arguments for stem commands
20495 } else if (stackSize > 1) {
20496 warn('Found too many parameters for stack-clearing command');
20498 if (stackSize > 0 && stack[stackSize - 1] >= 0) {
20499 widths[i] = stack[stackSize - 1];
20502 if ('stackDelta' in validationCommand) {
20503 if ('stackFn' in validationCommand) {
20504 validationCommand.stackFn(stack, stackSize);
20506 stackSize += validationCommand.stackDelta;
20507 } else if (validationCommand.stackClearing) {
20509 } else if (validationCommand.resetStack) {
20511 undefStack = false;
20512 } else if (validationCommand.undefStack) {
20515 firstStackClearing = false;
20520 // resetting invalid charstring to single 'endchar'
20521 charStrings.set(i, new Uint8Array([14]));
20524 return { charStrings: charStrings, seacs: seacs, widths: widths };
20526 emptyPrivateDictionary:
20527 function CFFParser_emptyPrivateDictionary(parentDict) {
20528 var privateDict = this.createDict(CFFPrivateDict, [],
20529 parentDict.strings);
20530 parentDict.setByKey(18, [0, 0]);
20531 parentDict.privateDict = privateDict;
20533 parsePrivateDict: function CFFParser_parsePrivateDict(parentDict) {
20534 // no private dict, do nothing
20535 if (!parentDict.hasName('Private')) {
20536 this.emptyPrivateDictionary(parentDict);
20539 var privateOffset = parentDict.getByName('Private');
20540 // make sure the params are formatted correctly
20541 if (!isArray(privateOffset) || privateOffset.length !== 2) {
20542 parentDict.removeByName('Private');
20545 var size = privateOffset[0];
20546 var offset = privateOffset[1];
20547 // remove empty dicts or ones that refer to invalid location
20548 if (size === 0 || offset >= this.bytes.length) {
20549 this.emptyPrivateDictionary(parentDict);
20553 var privateDictEnd = offset + size;
20554 var dictData = this.bytes.subarray(offset, privateDictEnd);
20555 var dict = this.parseDict(dictData);
20556 var privateDict = this.createDict(CFFPrivateDict, dict,
20557 parentDict.strings);
20558 parentDict.privateDict = privateDict;
20560 // Parse the Subrs index also since it's relative to the private dict.
20561 if (!privateDict.getByName('Subrs')) {
20564 var subrsOffset = privateDict.getByName('Subrs');
20565 var relativeOffset = offset + subrsOffset;
20566 // Validate the offset.
20567 if (subrsOffset === 0 || relativeOffset >= this.bytes.length) {
20568 this.emptyPrivateDictionary(parentDict);
20571 var subrsIndex = this.parseIndex(relativeOffset);
20572 privateDict.subrsIndex = subrsIndex.obj;
20574 parseCharsets: function CFFParser_parseCharsets(pos, length, strings, cid) {
20576 return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE,
20578 } else if (pos === 1) {
20579 return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT,
20581 } else if (pos === 2) {
20582 return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET,
20583 ExpertSubsetCharset);
20586 var bytes = this.bytes;
20588 var format = bytes[pos++];
20589 var charset = ['.notdef'];
20592 // subtract 1 for the .notdef glyph
20597 for (i = 0; i < length; i++) {
20598 id = (bytes[pos++] << 8) | bytes[pos++];
20599 charset.push(cid ? id : strings.get(id));
20603 while (charset.length <= length) {
20604 id = (bytes[pos++] << 8) | bytes[pos++];
20605 count = bytes[pos++];
20606 for (i = 0; i <= count; i++) {
20607 charset.push(cid ? id++ : strings.get(id++));
20612 while (charset.length <= length) {
20613 id = (bytes[pos++] << 8) | bytes[pos++];
20614 count = (bytes[pos++] << 8) | bytes[pos++];
20615 for (i = 0; i <= count; i++) {
20616 charset.push(cid ? id++ : strings.get(id++));
20621 error('Unknown charset format');
20623 // Raw won't be needed if we actually compile the charset.
20625 var raw = bytes.subarray(start, end);
20627 return new CFFCharset(false, format, charset, raw);
20629 parseEncoding: function CFFParser_parseEncoding(pos,
20634 var bytes = this.bytes;
20635 var predefined = false;
20636 var hasSupplement = false;
20640 function readSupplement() {
20641 var supplementsCount = bytes[pos++];
20642 for (i = 0; i < supplementsCount; i++) {
20643 var code = bytes[pos++];
20644 var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);
20645 encoding[code] = charset.indexOf(strings.get(sid));
20649 if (pos === 0 || pos === 1) {
20652 var baseEncoding = pos ? Encodings.ExpertEncoding :
20653 Encodings.StandardEncoding;
20654 for (i = 0, ii = charset.length; i < ii; i++) {
20655 var index = baseEncoding.indexOf(charset[i]);
20656 if (index !== -1) {
20657 encoding[index] = i;
20661 var dataStart = pos;
20662 format = bytes[pos++];
20663 switch (format & 0x7f) {
20665 var glyphsCount = bytes[pos++];
20666 for (i = 1; i <= glyphsCount; i++) {
20667 encoding[bytes[pos++]] = i;
20672 var rangesCount = bytes[pos++];
20674 for (i = 0; i < rangesCount; i++) {
20675 var start = bytes[pos++];
20676 var left = bytes[pos++];
20677 for (var j = start; j <= start + left; j++) {
20678 encoding[j] = gid++;
20684 error('Unknow encoding format: ' + format + ' in CFF');
20688 if (format & 0x80) {
20689 // The font sanitizer does not support CFF encoding with a
20690 // supplement, since the encoding is not really used to map
20691 // between gid to glyph, let's overwrite what is declared in
20692 // the top dictionary to let the sanitizer think the font use
20693 // StandardEncoding, that's a lie but that's ok.
20694 bytes[dataStart] &= 0x7f;
20696 hasSupplement = true;
20698 raw = bytes.subarray(dataStart, dataEnd);
20700 format = format & 0x7f;
20701 return new CFFEncoding(predefined, format, encoding, raw);
20703 parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
20705 var bytes = this.bytes;
20706 var format = bytes[pos++];
20712 for (i = 0; i < length; ++i) {
20713 var id = bytes[pos++];
20718 var rangesCount = (bytes[pos++] << 8) | bytes[pos++];
20719 for (i = 0; i < rangesCount; ++i) {
20720 var first = (bytes[pos++] << 8) | bytes[pos++];
20721 var fdIndex = bytes[pos++];
20722 var next = (bytes[pos] << 8) | bytes[pos + 1];
20723 for (var j = first; j < next; ++j) {
20724 fdSelect.push(fdIndex);
20727 // Advance past the sentinel(next).
20731 error('Unknown fdselect format ' + format);
20735 return new CFFFDSelect(fdSelect, bytes.subarray(start, end));
20741 // Compact Font Format
20742 var CFF = (function CFFClosure() {
20744 this.header = null;
20746 this.topDict = null;
20747 this.strings = new CFFStrings();
20748 this.globalSubrIndex = null;
20750 // The following could really be per font, but since we only have one font
20751 // store them here.
20752 this.encoding = null;
20753 this.charset = null;
20754 this.charStrings = null;
20756 this.fdSelect = null;
20758 this.isCIDFont = false;
20763 var CFFHeader = (function CFFHeaderClosure() {
20764 function CFFHeader(major, minor, hdrSize, offSize) {
20765 this.major = major;
20766 this.minor = minor;
20767 this.hdrSize = hdrSize;
20768 this.offSize = offSize;
20773 var CFFStrings = (function CFFStringsClosure() {
20774 function CFFStrings() {
20777 CFFStrings.prototype = {
20778 get: function CFFStrings_get(index) {
20779 if (index >= 0 && index <= 390) {
20780 return CFFStandardStrings[index];
20782 if (index - 391 <= this.strings.length) {
20783 return this.strings[index - 391];
20785 return CFFStandardStrings[0];
20787 add: function CFFStrings_add(value) {
20788 this.strings.push(value);
20791 return this.strings.length;
20797 var CFFIndex = (function CFFIndexClosure() {
20798 function CFFIndex() {
20802 CFFIndex.prototype = {
20803 add: function CFFIndex_add(data) {
20804 this.length += data.length;
20805 this.objects.push(data);
20807 set: function CFFIndex_set(index, data) {
20808 this.length += data.length - this.objects[index].length;
20809 this.objects[index] = data;
20811 get: function CFFIndex_get(index) {
20812 return this.objects[index];
20815 return this.objects.length;
20821 var CFFDict = (function CFFDictClosure() {
20822 function CFFDict(tables, strings) {
20823 this.keyToNameMap = tables.keyToNameMap;
20824 this.nameToKeyMap = tables.nameToKeyMap;
20825 this.defaults = tables.defaults;
20826 this.types = tables.types;
20827 this.opcodes = tables.opcodes;
20828 this.order = tables.order;
20829 this.strings = strings;
20832 CFFDict.prototype = {
20833 // value should always be an array
20834 setByKey: function CFFDict_setByKey(key, value) {
20835 if (!(key in this.keyToNameMap)) {
20838 // ignore empty values
20839 if (value.length === 0) {
20842 var type = this.types[key];
20843 // remove the array wrapping these types of values
20844 if (type === 'num' || type === 'sid' || type === 'offset') {
20847 this.values[key] = value;
20850 setByName: function CFFDict_setByName(name, value) {
20851 if (!(name in this.nameToKeyMap)) {
20852 error('Invalid dictionary name "' + name + '"');
20854 this.values[this.nameToKeyMap[name]] = value;
20856 hasName: function CFFDict_hasName(name) {
20857 return this.nameToKeyMap[name] in this.values;
20859 getByName: function CFFDict_getByName(name) {
20860 if (!(name in this.nameToKeyMap)) {
20861 error('Invalid dictionary name "' + name + '"');
20863 var key = this.nameToKeyMap[name];
20864 if (!(key in this.values)) {
20865 return this.defaults[key];
20867 return this.values[key];
20869 removeByName: function CFFDict_removeByName(name) {
20870 delete this.values[this.nameToKeyMap[name]];
20873 CFFDict.createTables = function CFFDict_createTables(layout) {
20882 for (var i = 0, ii = layout.length; i < ii; ++i) {
20883 var entry = layout[i];
20884 var key = isArray(entry[0]) ? (entry[0][0] << 8) + entry[0][1] : entry[0];
20885 tables.keyToNameMap[key] = entry[1];
20886 tables.nameToKeyMap[entry[1]] = key;
20887 tables.types[key] = entry[2];
20888 tables.defaults[key] = entry[3];
20889 tables.opcodes[key] = isArray(entry[0]) ? entry[0] : [entry[0]];
20890 tables.order.push(key);
20897 var CFFTopDict = (function CFFTopDictClosure() {
20899 [[12, 30], 'ROS', ['sid', 'sid', 'num'], null],
20900 [[12, 20], 'SyntheticBase', 'num', null],
20901 [0, 'version', 'sid', null],
20902 [1, 'Notice', 'sid', null],
20903 [[12, 0], 'Copyright', 'sid', null],
20904 [2, 'FullName', 'sid', null],
20905 [3, 'FamilyName', 'sid', null],
20906 [4, 'Weight', 'sid', null],
20907 [[12, 1], 'isFixedPitch', 'num', 0],
20908 [[12, 2], 'ItalicAngle', 'num', 0],
20909 [[12, 3], 'UnderlinePosition', 'num', -100],
20910 [[12, 4], 'UnderlineThickness', 'num', 50],
20911 [[12, 5], 'PaintType', 'num', 0],
20912 [[12, 6], 'CharstringType', 'num', 2],
20913 [[12, 7], 'FontMatrix', ['num', 'num', 'num', 'num', 'num', 'num'],
20914 [0.001, 0, 0, 0.001, 0, 0]],
20915 [13, 'UniqueID', 'num', null],
20916 [5, 'FontBBox', ['num', 'num', 'num', 'num'], [0, 0, 0, 0]],
20917 [[12, 8], 'StrokeWidth', 'num', 0],
20918 [14, 'XUID', 'array', null],
20919 [15, 'charset', 'offset', 0],
20920 [16, 'Encoding', 'offset', 0],
20921 [17, 'CharStrings', 'offset', 0],
20922 [18, 'Private', ['offset', 'offset'], null],
20923 [[12, 21], 'PostScript', 'sid', null],
20924 [[12, 22], 'BaseFontName', 'sid', null],
20925 [[12, 23], 'BaseFontBlend', 'delta', null],
20926 [[12, 31], 'CIDFontVersion', 'num', 0],
20927 [[12, 32], 'CIDFontRevision', 'num', 0],
20928 [[12, 33], 'CIDFontType', 'num', 0],
20929 [[12, 34], 'CIDCount', 'num', 8720],
20930 [[12, 35], 'UIDBase', 'num', null],
20931 // XXX: CID Fonts on DirectWrite 6.1 only seem to work if FDSelect comes
20933 [[12, 37], 'FDSelect', 'offset', null],
20934 [[12, 36], 'FDArray', 'offset', null],
20935 [[12, 38], 'FontName', 'sid', null]
20938 function CFFTopDict(strings) {
20939 if (tables === null) {
20940 tables = CFFDict.createTables(layout);
20942 CFFDict.call(this, tables, strings);
20943 this.privateDict = null;
20945 CFFTopDict.prototype = Object.create(CFFDict.prototype);
20949 var CFFPrivateDict = (function CFFPrivateDictClosure() {
20951 [6, 'BlueValues', 'delta', null],
20952 [7, 'OtherBlues', 'delta', null],
20953 [8, 'FamilyBlues', 'delta', null],
20954 [9, 'FamilyOtherBlues', 'delta', null],
20955 [[12, 9], 'BlueScale', 'num', 0.039625],
20956 [[12, 10], 'BlueShift', 'num', 7],
20957 [[12, 11], 'BlueFuzz', 'num', 1],
20958 [10, 'StdHW', 'num', null],
20959 [11, 'StdVW', 'num', null],
20960 [[12, 12], 'StemSnapH', 'delta', null],
20961 [[12, 13], 'StemSnapV', 'delta', null],
20962 [[12, 14], 'ForceBold', 'num', 0],
20963 [[12, 17], 'LanguageGroup', 'num', 0],
20964 [[12, 18], 'ExpansionFactor', 'num', 0.06],
20965 [[12, 19], 'initialRandomSeed', 'num', 0],
20966 [20, 'defaultWidthX', 'num', 0],
20967 [21, 'nominalWidthX', 'num', 0],
20968 [19, 'Subrs', 'offset', null]
20971 function CFFPrivateDict(strings) {
20972 if (tables === null) {
20973 tables = CFFDict.createTables(layout);
20975 CFFDict.call(this, tables, strings);
20976 this.subrsIndex = null;
20978 CFFPrivateDict.prototype = Object.create(CFFDict.prototype);
20979 return CFFPrivateDict;
20982 var CFFCharsetPredefinedTypes = {
20987 var CFFCharset = (function CFFCharsetClosure() {
20988 function CFFCharset(predefined, format, charset, raw) {
20989 this.predefined = predefined;
20990 this.format = format;
20991 this.charset = charset;
20997 var CFFEncoding = (function CFFEncodingClosure() {
20998 function CFFEncoding(predefined, format, encoding, raw) {
20999 this.predefined = predefined;
21000 this.format = format;
21001 this.encoding = encoding;
21004 return CFFEncoding;
21007 var CFFFDSelect = (function CFFFDSelectClosure() {
21008 function CFFFDSelect(fdSelect, raw) {
21009 this.fdSelect = fdSelect;
21012 return CFFFDSelect;
21015 // Helper class to keep track of where an offset is within the data and helps
21016 // filling in that offset once it's known.
21017 var CFFOffsetTracker = (function CFFOffsetTrackerClosure() {
21018 function CFFOffsetTracker() {
21021 CFFOffsetTracker.prototype = {
21022 isTracking: function CFFOffsetTracker_isTracking(key) {
21023 return key in this.offsets;
21025 track: function CFFOffsetTracker_track(key, location) {
21026 if (key in this.offsets) {
21027 error('Already tracking location of ' + key);
21029 this.offsets[key] = location;
21031 offset: function CFFOffsetTracker_offset(value) {
21032 for (var key in this.offsets) {
21033 this.offsets[key] += value;
21036 setEntryLocation: function CFFOffsetTracker_setEntryLocation(key,
21039 if (!(key in this.offsets)) {
21040 error('Not tracking location of ' + key);
21042 var data = output.data;
21043 var dataOffset = this.offsets[key];
21045 for (var i = 0, ii = values.length; i < ii; ++i) {
21046 var offset0 = i * size + dataOffset;
21047 var offset1 = offset0 + 1;
21048 var offset2 = offset0 + 2;
21049 var offset3 = offset0 + 3;
21050 var offset4 = offset0 + 4;
21051 // It's easy to screw up offsets so perform this sanity check.
21052 if (data[offset0] !== 0x1d || data[offset1] !== 0 ||
21053 data[offset2] !== 0 || data[offset3] !== 0 || data[offset4] !== 0) {
21054 error('writing to an offset that is not empty');
21056 var value = values[i];
21057 data[offset0] = 0x1d;
21058 data[offset1] = (value >> 24) & 0xFF;
21059 data[offset2] = (value >> 16) & 0xFF;
21060 data[offset3] = (value >> 8) & 0xFF;
21061 data[offset4] = value & 0xFF;
21065 return CFFOffsetTracker;
21068 // Takes a CFF and converts it to the binary representation.
21069 var CFFCompiler = (function CFFCompilerClosure() {
21070 function CFFCompiler(cff) {
21073 CFFCompiler.prototype = {
21074 compile: function CFFCompiler_compile() {
21075 var cff = this.cff;
21079 add: function CFFCompiler_add(data) {
21080 this.data = this.data.concat(data);
21081 this.length = this.data.length;
21085 // Compile the five entries that must be in order.
21086 var header = this.compileHeader(cff.header);
21087 output.add(header);
21089 var nameIndex = this.compileNameIndex(cff.names);
21090 output.add(nameIndex);
21092 if (cff.isCIDFont) {
21093 // The spec is unclear on how font matrices should relate to each other
21094 // when there is one in the main top dict and the sub top dicts.
21095 // Windows handles this differently than linux and osx so we have to
21096 // normalize to work on all.
21097 // Rules based off of some mailing list discussions:
21098 // - If main font has a matrix and subfont doesn't, use the main matrix.
21099 // - If no main font matrix and there is a subfont matrix, use the
21101 // - If both have matrices, concat together.
21102 // - If neither have matrices, use default.
21103 // To make this work on all platforms we move the top matrix into each
21104 // sub top dict and concat if necessary.
21105 if (cff.topDict.hasName('FontMatrix')) {
21106 var base = cff.topDict.getByName('FontMatrix');
21107 cff.topDict.removeByName('FontMatrix');
21108 for (var i = 0, ii = cff.fdArray.length; i < ii; i++) {
21109 var subDict = cff.fdArray[i];
21110 var matrix = base.slice(0);
21111 if (subDict.hasName('FontMatrix')) {
21112 matrix = Util.transform(matrix, subDict.getByName('FontMatrix'));
21114 subDict.setByName('FontMatrix', matrix);
21119 var compiled = this.compileTopDicts([cff.topDict],
21122 output.add(compiled.output);
21123 var topDictTracker = compiled.trackers[0];
21125 var stringIndex = this.compileStringIndex(cff.strings.strings);
21126 output.add(stringIndex);
21128 var globalSubrIndex = this.compileIndex(cff.globalSubrIndex);
21129 output.add(globalSubrIndex);
21131 // Now start on the other entries that have no specfic order.
21132 if (cff.encoding && cff.topDict.hasName('Encoding')) {
21133 if (cff.encoding.predefined) {
21134 topDictTracker.setEntryLocation('Encoding', [cff.encoding.format],
21137 var encoding = this.compileEncoding(cff.encoding);
21138 topDictTracker.setEntryLocation('Encoding', [output.length], output);
21139 output.add(encoding);
21143 if (cff.charset && cff.topDict.hasName('charset')) {
21144 if (cff.charset.predefined) {
21145 topDictTracker.setEntryLocation('charset', [cff.charset.format],
21148 var charset = this.compileCharset(cff.charset);
21149 topDictTracker.setEntryLocation('charset', [output.length], output);
21150 output.add(charset);
21154 var charStrings = this.compileCharStrings(cff.charStrings);
21155 topDictTracker.setEntryLocation('CharStrings', [output.length], output);
21156 output.add(charStrings);
21158 if (cff.isCIDFont) {
21159 // For some reason FDSelect must be in front of FDArray on windows. OSX
21160 // and linux don't seem to care.
21161 topDictTracker.setEntryLocation('FDSelect', [output.length], output);
21162 var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
21163 output.add(fdSelect);
21164 // It is unclear if the sub font dictionary can have CID related
21165 // dictionary keys, but the sanitizer doesn't like them so remove them.
21166 compiled = this.compileTopDicts(cff.fdArray, output.length, true);
21167 topDictTracker.setEntryLocation('FDArray', [output.length], output);
21168 output.add(compiled.output);
21169 var fontDictTrackers = compiled.trackers;
21171 this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output);
21174 this.compilePrivateDicts([cff.topDict], [topDictTracker], output);
21176 // If the font data ends with INDEX whose object data is zero-length,
21177 // the sanitizer will bail out. Add a dummy byte to avoid that.
21180 return output.data;
21182 encodeNumber: function CFFCompiler_encodeNumber(value) {
21183 if (parseFloat(value) === parseInt(value, 10) && !isNaN(value)) { // isInt
21184 return this.encodeInteger(value);
21186 return this.encodeFloat(value);
21189 encodeFloat: function CFFCompiler_encodeFloat(num) {
21190 var value = num.toString();
21192 // rounding inaccurate doubles
21193 var m = /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(value);
21195 var epsilon = parseFloat('1e' + ((m[2] ? +m[2] : 0) + m[1].length));
21196 value = (Math.round(num * epsilon) / epsilon).toString();
21201 for (i = 0, ii = value.length; i < ii; ++i) {
21204 nibbles += value[++i] === '-' ? 'c' : 'b';
21205 } else if (a === '.') {
21207 } else if (a === '-') {
21213 nibbles += (nibbles.length & 1) ? 'f' : 'ff';
21215 for (i = 0, ii = nibbles.length; i < ii; i += 2) {
21216 out.push(parseInt(nibbles.substr(i, 2), 16));
21220 encodeInteger: function CFFCompiler_encodeInteger(value) {
21222 if (value >= -107 && value <= 107) {
21223 code = [value + 139];
21224 } else if (value >= 108 && value <= 1131) {
21225 value = [value - 108];
21226 code = [(value >> 8) + 247, value & 0xFF];
21227 } else if (value >= -1131 && value <= -108) {
21228 value = -value - 108;
21229 code = [(value >> 8) + 251, value & 0xFF];
21230 } else if (value >= -32768 && value <= 32767) {
21231 code = [0x1c, (value >> 8) & 0xFF, value & 0xFF];
21234 (value >> 24) & 0xFF,
21235 (value >> 16) & 0xFF,
21236 (value >> 8) & 0xFF,
21241 compileHeader: function CFFCompiler_compileHeader(header) {
21249 compileNameIndex: function CFFCompiler_compileNameIndex(names) {
21250 var nameIndex = new CFFIndex();
21251 for (var i = 0, ii = names.length; i < ii; ++i) {
21252 nameIndex.add(stringToBytes(names[i]));
21254 return this.compileIndex(nameIndex);
21256 compileTopDicts: function CFFCompiler_compileTopDicts(dicts,
21259 var fontDictTrackers = [];
21260 var fdArrayIndex = new CFFIndex();
21261 for (var i = 0, ii = dicts.length; i < ii; ++i) {
21262 var fontDict = dicts[i];
21263 if (removeCidKeys) {
21264 fontDict.removeByName('CIDFontVersion');
21265 fontDict.removeByName('CIDFontRevision');
21266 fontDict.removeByName('CIDFontType');
21267 fontDict.removeByName('CIDCount');
21268 fontDict.removeByName('UIDBase');
21270 var fontDictTracker = new CFFOffsetTracker();
21271 var fontDictData = this.compileDict(fontDict, fontDictTracker);
21272 fontDictTrackers.push(fontDictTracker);
21273 fdArrayIndex.add(fontDictData);
21274 fontDictTracker.offset(length);
21276 fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers);
21278 trackers: fontDictTrackers,
21279 output: fdArrayIndex
21282 compilePrivateDicts: function CFFCompiler_compilePrivateDicts(dicts,
21285 for (var i = 0, ii = dicts.length; i < ii; ++i) {
21286 var fontDict = dicts[i];
21287 assert(fontDict.privateDict && fontDict.hasName('Private'),
21288 'There must be an private dictionary.');
21289 var privateDict = fontDict.privateDict;
21290 var privateDictTracker = new CFFOffsetTracker();
21291 var privateDictData = this.compileDict(privateDict, privateDictTracker);
21293 var outputLength = output.length;
21294 privateDictTracker.offset(outputLength);
21295 if (!privateDictData.length) {
21296 // The private dictionary was empty, set the output length to zero to
21297 // ensure the offset length isn't out of bounds in the eyes of the
21302 trackers[i].setEntryLocation('Private',
21303 [privateDictData.length, outputLength],
21305 output.add(privateDictData);
21307 if (privateDict.subrsIndex && privateDict.hasName('Subrs')) {
21308 var subrs = this.compileIndex(privateDict.subrsIndex);
21309 privateDictTracker.setEntryLocation('Subrs', [privateDictData.length],
21315 compileDict: function CFFCompiler_compileDict(dict, offsetTracker) {
21317 // The dictionary keys must be in a certain order.
21318 var order = dict.order;
21319 for (var i = 0; i < order.length; ++i) {
21320 var key = order[i];
21321 if (!(key in dict.values)) {
21324 var values = dict.values[key];
21325 var types = dict.types[key];
21326 if (!isArray(types)) {
21329 if (!isArray(values)) {
21333 // Remove any empty dict values.
21334 if (values.length === 0) {
21338 for (var j = 0, jj = types.length; j < jj; ++j) {
21339 var type = types[j];
21340 var value = values[j];
21344 out = out.concat(this.encodeNumber(value));
21347 // For offsets we just insert a 32bit integer so we don't have to
21348 // deal with figuring out the length of the offset when it gets
21349 // replaced later on by the compiler.
21350 var name = dict.keyToNameMap[key];
21351 // Some offsets have the offset and the length, so just record the
21352 // position of the first one.
21353 if (!offsetTracker.isTracking(name)) {
21354 offsetTracker.track(name, out.length);
21356 out = out.concat([0x1d, 0, 0, 0, 0]);
21360 out = out.concat(this.encodeNumber(value));
21361 for (var k = 1, kk = values.length; k < kk; ++k) {
21362 out = out.concat(this.encodeNumber(values[k]));
21366 error('Unknown data type of ' + type);
21370 out = out.concat(dict.opcodes[key]);
21374 compileStringIndex: function CFFCompiler_compileStringIndex(strings) {
21375 var stringIndex = new CFFIndex();
21376 for (var i = 0, ii = strings.length; i < ii; ++i) {
21377 stringIndex.add(stringToBytes(strings[i]));
21379 return this.compileIndex(stringIndex);
21381 compileGlobalSubrIndex: function CFFCompiler_compileGlobalSubrIndex() {
21382 var globalSubrIndex = this.cff.globalSubrIndex;
21383 this.out.writeByteArray(this.compileIndex(globalSubrIndex));
21385 compileCharStrings: function CFFCompiler_compileCharStrings(charStrings) {
21386 return this.compileIndex(charStrings);
21388 compileCharset: function CFFCompiler_compileCharset(charset) {
21389 return this.compileTypedArray(charset.raw);
21391 compileEncoding: function CFFCompiler_compileEncoding(encoding) {
21392 return this.compileTypedArray(encoding.raw);
21394 compileFDSelect: function CFFCompiler_compileFDSelect(fdSelect) {
21395 return this.compileTypedArray(fdSelect);
21397 compileTypedArray: function CFFCompiler_compileTypedArray(data) {
21399 for (var i = 0, ii = data.length; i < ii; ++i) {
21404 compileIndex: function CFFCompiler_compileIndex(index, trackers) {
21405 trackers = trackers || [];
21406 var objects = index.objects;
21407 // First 2 bytes contains the number of objects contained into this index
21408 var count = objects.length;
21410 // If there is no object, just create an index. This technically
21411 // should just be [0, 0] but OTS has an issue with that.
21416 var data = [(count >> 8) & 0xFF, count & 0xff];
21418 var lastOffset = 1, i;
21419 for (i = 0; i < count; ++i) {
21420 lastOffset += objects[i].length;
21424 if (lastOffset < 0x100) {
21426 } else if (lastOffset < 0x10000) {
21428 } else if (lastOffset < 0x1000000) {
21434 // Next byte contains the offset size use to reference object in the file
21435 data.push(offsetSize);
21437 // Add another offset after this one because we need a new offset
21438 var relativeOffset = 1;
21439 for (i = 0; i < count + 1; i++) {
21440 if (offsetSize === 1) {
21441 data.push(relativeOffset & 0xFF);
21442 } else if (offsetSize === 2) {
21443 data.push((relativeOffset >> 8) & 0xFF,
21444 relativeOffset & 0xFF);
21445 } else if (offsetSize === 3) {
21446 data.push((relativeOffset >> 16) & 0xFF,
21447 (relativeOffset >> 8) & 0xFF,
21448 relativeOffset & 0xFF);
21450 data.push((relativeOffset >>> 24) & 0xFF,
21451 (relativeOffset >> 16) & 0xFF,
21452 (relativeOffset >> 8) & 0xFF,
21453 relativeOffset & 0xFF);
21457 relativeOffset += objects[i].length;
21461 for (i = 0; i < count; i++) {
21462 // Notify the tracker where the object will be offset in the data.
21464 trackers[i].offset(data.length);
21466 for (var j = 0, jj = objects[i].length; j < jj; j++) {
21467 data.push(objects[i][j]);
21473 return CFFCompiler;
21476 // Workaround for seac on Windows.
21477 (function checkSeacSupport() {
21478 if (/Windows/.test(navigator.userAgent)) {
21479 SEAC_ANALYSIS_ENABLED = true;
21483 // Workaround for Private Use Area characters in Chrome on Windows
21484 // http://code.google.com/p/chromium/issues/detail?id=122465
21485 // https://github.com/mozilla/pdf.js/issues/1689
21486 (function checkChromeWindows() {
21487 if (/Windows.*Chrome/.test(navigator.userAgent)) {
21488 SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = true;
21493 var FontRendererFactory = (function FontRendererFactoryClosure() {
21494 function getLong(data, offset) {
21495 return (data[offset] << 24) | (data[offset + 1] << 16) |
21496 (data[offset + 2] << 8) | data[offset + 3];
21499 function getUshort(data, offset) {
21500 return (data[offset] << 8) | data[offset + 1];
21503 function parseCmap(data, start, end) {
21504 var offset = (getUshort(data, start + 2) === 1 ?
21505 getLong(data, start + 8) : getLong(data, start + 16));
21506 var format = getUshort(data, start + offset);
21507 var length, ranges, p, i;
21508 if (format === 4) {
21509 length = getUshort(data, start + offset + 2);
21510 var segCount = getUshort(data, start + offset + 6) >> 1;
21511 p = start + offset + 14;
21513 for (i = 0; i < segCount; i++, p += 2) {
21514 ranges[i] = {end: getUshort(data, p)};
21517 for (i = 0; i < segCount; i++, p += 2) {
21518 ranges[i].start = getUshort(data, p);
21520 for (i = 0; i < segCount; i++, p += 2) {
21521 ranges[i].idDelta = getUshort(data, p);
21523 for (i = 0; i < segCount; i++, p += 2) {
21524 var idOffset = getUshort(data, p);
21525 if (idOffset === 0) {
21528 ranges[i].ids = [];
21529 for (var j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) {
21530 ranges[i].ids[j] = getUshort(data, p + idOffset);
21535 } else if (format === 12) {
21536 length = getLong(data, start + offset + 4);
21537 var groups = getLong(data, start + offset + 12);
21538 p = start + offset + 16;
21540 for (i = 0; i < groups; i++) {
21542 start: getLong(data, p),
21543 end: getLong(data, p + 4),
21544 idDelta: getLong(data, p + 8) - getLong(data, p)
21550 error('not supported cmap: ' + format);
21553 function parseCff(data, start, end) {
21554 var properties = {};
21555 var parser = new CFFParser(new Stream(data, start, end - start),
21557 var cff = parser.parse();
21559 glyphs: cff.charStrings.objects,
21560 subrs: (cff.topDict.privateDict && cff.topDict.privateDict.subrsIndex &&
21561 cff.topDict.privateDict.subrsIndex.objects),
21562 gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects
21566 function parseGlyfTable(glyf, loca, isGlyphLocationsLong) {
21567 var itemSize, itemDecode;
21568 if (isGlyphLocationsLong) {
21570 itemDecode = function fontItemDecodeLong(data, offset) {
21571 return (data[offset] << 24) | (data[offset + 1] << 16) |
21572 (data[offset + 2] << 8) | data[offset + 3];
21576 itemDecode = function fontItemDecode(data, offset) {
21577 return (data[offset] << 9) | (data[offset + 1] << 1);
21581 var startOffset = itemDecode(loca, 0);
21582 for (var j = itemSize; j < loca.length; j += itemSize) {
21583 var endOffset = itemDecode(loca, j);
21584 glyphs.push(glyf.subarray(startOffset, endOffset));
21585 startOffset = endOffset;
21590 function lookupCmap(ranges, unicode) {
21591 var code = unicode.charCodeAt(0);
21592 var l = 0, r = ranges.length - 1;
21594 var c = (l + r + 1) >> 1;
21595 if (code < ranges[c].start) {
21601 if (ranges[l].start <= code && code <= ranges[l].end) {
21602 return (ranges[l].idDelta + (ranges[l].ids ?
21603 ranges[l].ids[code - ranges[l].start] : code)) & 0xFFFF;
21608 function compileGlyf(code, js, font) {
21609 function moveTo(x, y) {
21610 js.push('c.moveTo(' + x + ',' + y + ');');
21612 function lineTo(x, y) {
21613 js.push('c.lineTo(' + x + ',' + y + ');');
21615 function quadraticCurveTo(xa, ya, x, y) {
21616 js.push('c.quadraticCurveTo(' + xa + ',' + ya + ',' +
21617 x + ',' + y + ');');
21621 var numberOfContours = ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
21625 if (numberOfContours < 0) {
21628 flags = (code[i] << 8) | code[i + 1];
21629 var glyphIndex = (code[i + 2] << 8) | code[i + 3];
21632 if ((flags & 0x01)) {
21633 arg1 = ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
21634 arg2 = ((code[i + 2] << 24) | (code[i + 3] << 16)) >> 16;
21637 arg1 = code[i++]; arg2 = code[i++];
21639 if ((flags & 0x02)) {
21643 x = 0; y = 0; // TODO "they are points" ?
21645 var scaleX = 1, scaleY = 1, scale01 = 0, scale10 = 0;
21646 if ((flags & 0x08)) {
21648 scaleY = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824;
21650 } else if ((flags & 0x40)) {
21651 scaleX = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824;
21652 scaleY = ((code[i + 2] << 24) | (code[i + 3] << 16)) / 1073741824;
21654 } else if ((flags & 0x80)) {
21655 scaleX = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824;
21656 scale01 = ((code[i + 2] << 24) | (code[i + 3] << 16)) / 1073741824;
21657 scale10 = ((code[i + 4] << 24) | (code[i + 5] << 16)) / 1073741824;
21658 scaleY = ((code[i + 6] << 24) | (code[i + 7] << 16)) / 1073741824;
21661 var subglyph = font.glyphs[glyphIndex];
21663 js.push('c.save();');
21664 js.push('c.transform(' + scaleX + ',' + scale01 + ',' +
21665 scale10 + ',' + scaleY + ',' + x + ',' + y + ');');
21666 compileGlyf(subglyph, js, font);
21667 js.push('c.restore();');
21669 } while ((flags & 0x20));
21672 var endPtsOfContours = [];
21674 for (j = 0; j < numberOfContours; j++) {
21675 endPtsOfContours.push((code[i] << 8) | code[i + 1]);
21678 var instructionLength = (code[i] << 8) | code[i + 1];
21679 i += 2 + instructionLength; // skipping the instructions
21680 var numberOfPoints = endPtsOfContours[endPtsOfContours.length - 1] + 1;
21682 while (points.length < numberOfPoints) {
21685 if ((flags & 0x08)) {
21686 repeat += code[i++];
21688 while (repeat-- > 0) {
21689 points.push({flags: flags});
21692 for (j = 0; j < numberOfPoints; j++) {
21693 switch (points[j].flags & 0x12) {
21695 x += ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
21707 for (j = 0; j < numberOfPoints; j++) {
21708 switch (points[j].flags & 0x24) {
21710 y += ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
21723 var startPoint = 0;
21724 for (i = 0; i < numberOfContours; i++) {
21725 var endPoint = endPtsOfContours[i];
21726 // contours might have implicit points, which is located in the middle
21727 // between two neighboring off-curve points
21728 var contour = points.slice(startPoint, endPoint + 1);
21729 if ((contour[0].flags & 1)) {
21730 contour.push(contour[0]); // using start point at the contour end
21731 } else if ((contour[contour.length - 1].flags & 1)) {
21732 // first is off-curve point, trying to use one from the end
21733 contour.unshift(contour[contour.length - 1]);
21735 // start and end are off-curve points, creating implicit one
21738 x: (contour[0].x + contour[contour.length - 1].x) / 2,
21739 y: (contour[0].y + contour[contour.length - 1].y) / 2
21741 contour.unshift(p);
21744 moveTo(contour[0].x, contour[0].y);
21745 for (j = 1, jj = contour.length; j < jj; j++) {
21746 if ((contour[j].flags & 1)) {
21747 lineTo(contour[j].x, contour[j].y);
21748 } else if ((contour[j + 1].flags & 1)){
21749 quadraticCurveTo(contour[j].x, contour[j].y,
21750 contour[j + 1].x, contour[j + 1].y);
21753 quadraticCurveTo(contour[j].x, contour[j].y,
21754 (contour[j].x + contour[j + 1].x) / 2,
21755 (contour[j].y + contour[j + 1].y) / 2);
21758 startPoint = endPoint + 1;
21763 function compileCharString(code, js, font) {
21768 function moveTo(x, y) {
21769 js.push('c.moveTo(' + x + ',' + y + ');');
21771 function lineTo(x, y) {
21772 js.push('c.lineTo(' + x + ',' + y + ');');
21774 function bezierCurveTo(x1, y1, x2, y2, x, y) {
21775 js.push('c.bezierCurveTo(' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + ',' +
21776 x + ',' + y + ');');
21779 function parse(code) {
21781 while (i < code.length) {
21782 var stackClean = false;
21784 var xa, xb, ya, yb, y1, y2, y3, n, subrCode;
21787 stems += stack.length >> 1;
21791 stems += stack.length >> 1;
21800 while (stack.length > 0) {
21801 x += stack.shift();
21802 y += stack.shift();
21807 while (stack.length > 0) {
21808 x += stack.shift();
21810 if (stack.length === 0) {
21813 y += stack.shift();
21818 while (stack.length > 0) {
21819 y += stack.shift();
21821 if (stack.length === 0) {
21824 x += stack.shift();
21828 case 8: // rrcurveto
21829 while (stack.length > 0) {
21830 xa = x + stack.shift(); ya = y + stack.shift();
21831 xb = xa + stack.shift(); yb = ya + stack.shift();
21832 x = xb + stack.shift(); y = yb + stack.shift();
21833 bezierCurveTo(xa, ya, xb, yb, x, y);
21836 case 10: // callsubr
21837 n = stack.pop() + font.subrsBias;
21838 subrCode = font.subrs[n];
21849 xa = x + stack.shift();
21850 xb = xa + stack.shift(); y1 = y + stack.shift();
21851 x = xb + stack.shift();
21852 bezierCurveTo(xa, y, xb, y1, x, y1);
21853 xa = x + stack.shift();
21854 xb = xa + stack.shift();
21855 x = xb + stack.shift();
21856 bezierCurveTo(xa, y1, xb, y, x, y);
21859 xa = x + stack.shift(); ya = y + stack.shift();
21860 xb = xa + stack.shift(); yb = ya + stack.shift();
21861 x = xb + stack.shift(); y = yb + stack.shift();
21862 bezierCurveTo(xa, ya, xb, yb, x, y);
21863 xa = x + stack.shift(); ya = y + stack.shift();
21864 xb = xa + stack.shift(); yb = ya + stack.shift();
21865 x = xb + stack.shift(); y = yb + stack.shift();
21866 bezierCurveTo(xa, ya, xb, yb, x, y);
21870 xa = x + stack.shift(); y1 = y + stack.shift();
21871 xb = xa + stack.shift(); y2 = y1 + stack.shift();
21872 x = xb + stack.shift();
21873 bezierCurveTo(xa, y1, xb, y2, x, y2);
21874 xa = x + stack.shift();
21875 xb = xa + stack.shift(); y3 = y2 + stack.shift();
21876 x = xb + stack.shift();
21877 bezierCurveTo(xa, y2, xb, y3, x, y);
21880 var x0 = x, y0 = y;
21881 xa = x + stack.shift(); ya = y + stack.shift();
21882 xb = xa + stack.shift(); yb = ya + stack.shift();
21883 x = xb + stack.shift(); y = yb + stack.shift();
21884 bezierCurveTo(xa, ya, xb, yb, x, y);
21885 xa = x + stack.shift(); ya = y + stack.shift();
21886 xb = xa + stack.shift(); yb = ya + stack.shift();
21888 if (Math.abs(x - x0) > Math.abs(y - y0)) {
21889 x += stack.shift();
21891 y += stack.shift();
21893 bezierCurveTo(xa, ya, xb, yb, x, y);
21896 error('unknown operator: 12 ' + v);
21899 case 14: // endchar
21900 if (stack.length >= 4) {
21901 var achar = stack.pop();
21902 var bchar = stack.pop();
21905 js.push('c.save();');
21906 js.push('c.translate('+ x + ',' + y + ');');
21907 var gid = lookupCmap(font.cmap, String.fromCharCode(
21908 font.glyphNameMap[Encodings.StandardEncoding[achar]]));
21909 compileCharString(font.glyphs[gid], js, font);
21910 js.push('c.restore();');
21912 gid = lookupCmap(font.cmap, String.fromCharCode(
21913 font.glyphNameMap[Encodings.StandardEncoding[bchar]]));
21914 compileCharString(font.glyphs[gid], js, font);
21917 case 18: // hstemhm
21918 stems += stack.length >> 1;
21921 case 19: // hintmask
21922 stems += stack.length >> 1;
21923 i += (stems + 7) >> 3;
21926 case 20: // cntrmask
21927 stems += stack.length >> 1;
21928 i += (stems + 7) >> 3;
21931 case 21: // rmoveto
21937 case 22: // hmoveto
21942 case 23: // vstemhm
21943 stems += stack.length >> 1;
21946 case 24: // rcurveline
21947 while (stack.length > 2) {
21948 xa = x + stack.shift(); ya = y + stack.shift();
21949 xb = xa + stack.shift(); yb = ya + stack.shift();
21950 x = xb + stack.shift(); y = yb + stack.shift();
21951 bezierCurveTo(xa, ya, xb, yb, x, y);
21953 x += stack.shift();
21954 y += stack.shift();
21957 case 25: // rlinecurve
21958 while (stack.length > 6) {
21959 x += stack.shift();
21960 y += stack.shift();
21963 xa = x + stack.shift(); ya = y + stack.shift();
21964 xb = xa + stack.shift(); yb = ya + stack.shift();
21965 x = xb + stack.shift(); y = yb + stack.shift();
21966 bezierCurveTo(xa, ya, xb, yb, x, y);
21968 case 26: // vvcurveto
21969 if (stack.length % 2) {
21970 x += stack.shift();
21972 while (stack.length > 0) {
21973 xa = x; ya = y + stack.shift();
21974 xb = xa + stack.shift(); yb = ya + stack.shift();
21975 x = xb; y = yb + stack.shift();
21976 bezierCurveTo(xa, ya, xb, yb, x, y);
21979 case 27: // hhcurveto
21980 if (stack.length % 2) {
21981 y += stack.shift();
21983 while (stack.length > 0) {
21984 xa = x + stack.shift(); ya = y;
21985 xb = xa + stack.shift(); yb = ya + stack.shift();
21986 x = xb + stack.shift(); y = yb;
21987 bezierCurveTo(xa, ya, xb, yb, x, y);
21991 stack.push(((code[i] << 24) | (code[i + 1] << 16)) >> 16);
21994 case 29: // callgsubr
21995 n = stack.pop() + font.gsubrsBias;
21996 subrCode = font.gsubrs[n];
22001 case 30: // vhcurveto
22002 while (stack.length > 0) {
22003 xa = x; ya = y + stack.shift();
22004 xb = xa + stack.shift(); yb = ya + stack.shift();
22005 x = xb + stack.shift();
22006 y = yb + (stack.length === 1 ? stack.shift() : 0);
22007 bezierCurveTo(xa, ya, xb, yb, x, y);
22008 if (stack.length === 0) {
22012 xa = x + stack.shift(); ya = y;
22013 xb = xa + stack.shift(); yb = ya + stack.shift();
22014 y = yb + stack.shift();
22015 x = xb + (stack.length === 1 ? stack.shift() : 0);
22016 bezierCurveTo(xa, ya, xb, yb, x, y);
22019 case 31: // hvcurveto
22020 while (stack.length > 0) {
22021 xa = x + stack.shift(); ya = y;
22022 xb = xa + stack.shift(); yb = ya + stack.shift();
22023 y = yb + stack.shift();
22024 x = xb + (stack.length === 1 ? stack.shift() : 0);
22025 bezierCurveTo(xa, ya, xb, yb, x, y);
22026 if (stack.length === 0) {
22030 xa = x; ya = y + stack.shift();
22031 xb = xa + stack.shift(); yb = ya + stack.shift();
22032 x = xb + stack.shift();
22033 y = yb + (stack.length === 1 ? stack.shift() : 0);
22034 bezierCurveTo(xa, ya, xb, yb, x, y);
22039 error('unknown operator: ' + v);
22042 stack.push(v - 139);
22043 } else if (v < 251) {
22044 stack.push((v - 247) * 256 + code[i++] + 108);
22045 } else if (v < 255) {
22046 stack.push(-(v - 251) * 256 - code[i++] - 108);
22048 stack.push(((code[i] << 24) | (code[i + 1] << 16) |
22049 (code[i + 2] << 8) | code[i + 3]) / 65536);
22064 function CompiledFont(fontMatrix) {
22065 this.compiledGlyphs = {};
22066 this.fontMatrix = fontMatrix;
22068 CompiledFont.prototype = {
22069 getPathJs: function (unicode) {
22070 var gid = lookupCmap(this.cmap, unicode);
22071 var fn = this.compiledGlyphs[gid];
22073 this.compiledGlyphs[gid] = fn = this.compileGlyph(this.glyphs[gid]);
22078 compileGlyph: function (code) {
22079 if (!code || code.length === 0 || code[0] === 14) {
22084 js.push('c.save();');
22085 js.push('c.transform(' + this.fontMatrix.join(',') + ');');
22086 js.push('c.scale(size, -size);');
22088 this.compileGlyphImpl(code, js);
22090 js.push('c.restore();');
22092 return js.join('\n');
22095 compileGlyphImpl: function () {
22096 error('Children classes should implement this.');
22099 hasBuiltPath: function (unicode) {
22100 var gid = lookupCmap(this.cmap, unicode);
22101 return gid in this.compiledGlyphs;
22105 function TrueTypeCompiled(glyphs, cmap, fontMatrix) {
22106 fontMatrix = fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0];
22107 CompiledFont.call(this, fontMatrix);
22109 this.glyphs = glyphs;
22112 this.compiledGlyphs = [];
22115 Util.inherit(TrueTypeCompiled, CompiledFont, {
22116 compileGlyphImpl: function (code, js) {
22117 compileGlyf(code, js, this);
22121 function Type2Compiled(cffInfo, cmap, fontMatrix, glyphNameMap) {
22122 fontMatrix = fontMatrix || [0.001, 0, 0, 0.001, 0, 0];
22123 CompiledFont.call(this, fontMatrix);
22124 this.glyphs = cffInfo.glyphs;
22125 this.gsubrs = cffInfo.gsubrs || [];
22126 this.subrs = cffInfo.subrs || [];
22128 this.glyphNameMap = glyphNameMap || GlyphsUnicode;
22130 this.compiledGlyphs = [];
22131 this.gsubrsBias = (this.gsubrs.length < 1240 ?
22132 107 : (this.gsubrs.length < 33900 ? 1131 : 32768));
22133 this.subrsBias = (this.subrs.length < 1240 ?
22134 107 : (this.subrs.length < 33900 ? 1131 : 32768));
22137 Util.inherit(Type2Compiled, CompiledFont, {
22138 compileGlyphImpl: function (code, js) {
22139 compileCharString(code, js, this);
22145 create: function FontRendererFactory_create(font) {
22146 var data = new Uint8Array(font.data);
22147 var cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;
22148 var numTables = getUshort(data, 4);
22149 for (var i = 0, p = 12; i < numTables; i++, p += 16) {
22150 var tag = bytesToString(data.subarray(p, p + 4));
22151 var offset = getLong(data, p + 8);
22152 var length = getLong(data, p + 12);
22155 cmap = parseCmap(data, offset, offset + length);
22158 glyf = data.subarray(offset, offset + length);
22161 loca = data.subarray(offset, offset + length);
22164 unitsPerEm = getUshort(data, offset + 18);
22165 indexToLocFormat = getUshort(data, offset + 50);
22168 cff = parseCff(data, offset, offset + length);
22174 var fontMatrix = (!unitsPerEm ? font.fontMatrix :
22175 [1 / unitsPerEm, 0, 0, 1 / unitsPerEm, 0, 0]);
22176 return new TrueTypeCompiled(
22177 parseGlyfTable(glyf, loca, indexToLocFormat), cmap, fontMatrix);
22179 return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap);
22186 var GlyphsUnicode = {
22193 Aacutesmall: 0xF7E1,
22195 Abreveacute: 0x1EAE,
22196 Abrevecyrillic: 0x04D0,
22197 Abrevedotbelow: 0x1EB6,
22198 Abrevegrave: 0x1EB0,
22199 Abrevehookabove: 0x1EB2,
22200 Abrevetilde: 0x1EB4,
22203 Acircumflex: 0x00C2,
22204 Acircumflexacute: 0x1EA4,
22205 Acircumflexdotbelow: 0x1EAC,
22206 Acircumflexgrave: 0x1EA6,
22207 Acircumflexhookabove: 0x1EA8,
22208 Acircumflexsmall: 0xF7E2,
22209 Acircumflextilde: 0x1EAA,
22211 Acutesmall: 0xF7B4,
22215 Adieresiscyrillic: 0x04D2,
22216 Adieresismacron: 0x01DE,
22217 Adieresissmall: 0xF7E4,
22219 Adotmacron: 0x01E0,
22221 Agravesmall: 0xF7E0,
22222 Ahookabove: 0x1EA2,
22223 Aiecyrillic: 0x04D4,
22224 Ainvertedbreve: 0x0202,
22226 Alphatonos: 0x0386,
22228 Amonospace: 0xFF21,
22231 Aringacute: 0x01FA,
22232 Aringbelow: 0x1E00,
22233 Aringsmall: 0xF7E5,
22236 Atildesmall: 0xF7E3,
22237 Aybarmenian: 0x0531,
22240 Bdotaccent: 0x1E02,
22242 Becyrillic: 0x0411,
22243 Benarmenian: 0x0532,
22246 Blinebelow: 0x1E06,
22247 Bmonospace: 0xFF22,
22248 Brevesmall: 0xF6F4,
22252 Caarmenian: 0x053E,
22255 Caronsmall: 0xF6F5,
22258 Ccedillaacute: 0x1E08,
22259 Ccedillasmall: 0xF7E7,
22261 Ccircumflex: 0x0108,
22263 Cdotaccent: 0x010A,
22264 Cedillasmall: 0xF7B8,
22265 Chaarmenian: 0x0549,
22266 Cheabkhasiancyrillic: 0x04BC,
22267 Checyrillic: 0x0427,
22268 Chedescenderabkhasiancyrillic: 0x04BE,
22269 Chedescendercyrillic: 0x04B6,
22270 Chedieresiscyrillic: 0x04F4,
22271 Cheharmenian: 0x0543,
22272 Chekhakassiancyrillic: 0x04CB,
22273 Cheverticalstrokecyrillic: 0x04B8,
22276 Circumflexsmall: 0xF6F6,
22277 Cmonospace: 0xFF23,
22278 Coarmenian: 0x0551,
22283 Daarmenian: 0x0534,
22288 Dcircumflexbelow: 0x1E12,
22290 Ddotaccent: 0x1E0A,
22292 Decyrillic: 0x0414,
22295 Deltagreek: 0x0394,
22298 DieresisAcute: 0xF6CC,
22299 DieresisGrave: 0xF6CD,
22300 Dieresissmall: 0xF7A8,
22301 Digammagreek: 0x03DC,
22302 Djecyrillic: 0x0402,
22303 Dlinebelow: 0x1E0E,
22304 Dmonospace: 0xFF24,
22305 Dotaccentsmall: 0xF6F7,
22311 Dzeabkhasiancyrillic: 0x04E0,
22312 Dzecyrillic: 0x0405,
22313 Dzhecyrillic: 0x040F,
22316 Eacutesmall: 0xF7E9,
22319 Ecedillabreve: 0x1E1C,
22320 Echarmenian: 0x0535,
22322 Ecircumflex: 0x00CA,
22323 Ecircumflexacute: 0x1EBE,
22324 Ecircumflexbelow: 0x1E18,
22325 Ecircumflexdotbelow: 0x1EC6,
22326 Ecircumflexgrave: 0x1EC0,
22327 Ecircumflexhookabove: 0x1EC2,
22328 Ecircumflexsmall: 0xF7EA,
22329 Ecircumflextilde: 0x1EC4,
22333 Edieresissmall: 0xF7EB,
22335 Edotaccent: 0x0116,
22337 Efcyrillic: 0x0424,
22339 Egravesmall: 0xF7E8,
22340 Eharmenian: 0x0537,
22341 Ehookabove: 0x1EBA,
22342 Eightroman: 0x2167,
22343 Einvertedbreve: 0x0206,
22344 Eiotifiedcyrillic: 0x0464,
22345 Elcyrillic: 0x041B,
22346 Elevenroman: 0x216A,
22348 Emacronacute: 0x1E16,
22349 Emacrongrave: 0x1E14,
22350 Emcyrillic: 0x041C,
22351 Emonospace: 0xFF25,
22352 Encyrillic: 0x041D,
22353 Endescendercyrillic: 0x04A2,
22355 Enghecyrillic: 0x04A4,
22356 Enhookcyrillic: 0x04C7,
22360 Epsilontonos: 0x0388,
22361 Ercyrillic: 0x0420,
22363 Ereversedcyrillic: 0x042D,
22364 Escyrillic: 0x0421,
22365 Esdescendercyrillic: 0x04AA,
22369 Etarmenian: 0x0538,
22374 Etildebelow: 0x1E1A,
22378 Ezhreversed: 0x01B8,
22381 Fdotaccent: 0x1E1E,
22382 Feharmenian: 0x0556,
22385 Fitacyrillic: 0x0472,
22387 Fmonospace: 0xFF26,
22394 Gammaafrican: 0x0194,
22395 Gangiacoptic: 0x03EA,
22400 Gcircumflex: 0x011C,
22401 Gcommaaccent: 0x0122,
22403 Gdotaccent: 0x0120,
22404 Gecyrillic: 0x0413,
22405 Ghadarmenian: 0x0542,
22406 Ghemiddlehookcyrillic: 0x0494,
22407 Ghestrokecyrillic: 0x0492,
22408 Gheupturncyrillic: 0x0490,
22410 Gimarmenian: 0x0533,
22411 Gjecyrillic: 0x0403,
22413 Gmonospace: 0xFF27,
22415 Gravesmall: 0xF760,
22417 Gsmallhook: 0x029B,
22425 Haabkhasiancyrillic: 0x04A8,
22426 Hadescendercyrillic: 0x04B2,
22427 Hardsigncyrillic: 0x042A,
22429 Hbrevebelow: 0x1E2A,
22432 Hcircumflex: 0x0124,
22434 Hdotaccent: 0x1E22,
22436 Hmonospace: 0xFF28,
22437 Hoarmenian: 0x0540,
22438 Horicoptic: 0x03E8,
22440 Hungarumlaut: 0xF6CF,
22441 Hungarumlautsmall: 0xF6F8,
22444 IAcyrillic: 0x042F,
22446 IUcyrillic: 0x042E,
22448 Iacutesmall: 0xF7ED,
22452 Icircumflex: 0x00CE,
22453 Icircumflexsmall: 0xF7EE,
22457 Idieresisacute: 0x1E2E,
22458 Idieresiscyrillic: 0x04E4,
22459 Idieresissmall: 0xF7EF,
22461 Idotaccent: 0x0130,
22463 Iebrevecyrillic: 0x04D6,
22464 Iecyrillic: 0x0415,
22467 Igravesmall: 0xF7EC,
22468 Ihookabove: 0x1EC8,
22469 Iicyrillic: 0x0418,
22470 Iinvertedbreve: 0x020A,
22471 Iishortcyrillic: 0x0419,
22473 Imacroncyrillic: 0x04E2,
22474 Imonospace: 0xFF29,
22475 Iniarmenian: 0x053B,
22476 Iocyrillic: 0x0401,
22479 Iotaafrican: 0x0196,
22480 Iotadieresis: 0x03AA,
22485 Itildebelow: 0x1E2C,
22486 Izhitsacyrillic: 0x0474,
22487 Izhitsadblgravecyrillic: 0x0476,
22489 Jaarmenian: 0x0541,
22491 Jcircumflex: 0x0134,
22492 Jecyrillic: 0x0408,
22493 Jheharmenian: 0x054B,
22494 Jmonospace: 0xFF2A,
22499 Kabashkircyrillic: 0x04A0,
22501 Kacyrillic: 0x041A,
22502 Kadescendercyrillic: 0x049A,
22503 Kahookcyrillic: 0x04C3,
22505 Kastrokecyrillic: 0x049E,
22506 Kaverticalstrokecyrillic: 0x049C,
22510 Kcommaaccent: 0x0136,
22512 Keharmenian: 0x0554,
22513 Kenarmenian: 0x053F,
22514 Khacyrillic: 0x0425,
22515 Kheicoptic: 0x03E6,
22517 Kjecyrillic: 0x040C,
22518 Klinebelow: 0x1E34,
22519 Kmonospace: 0xFF2B,
22520 Koppacyrillic: 0x0480,
22521 Koppagreek: 0x03DE,
22522 Ksicyrillic: 0x046E,
22532 Lcircumflexbelow: 0x1E3C,
22533 Lcommaaccent: 0x013B,
22535 Ldotaccent: 0x013F,
22537 Ldotbelowmacron: 0x1E38,
22538 Liwnarmenian: 0x053C,
22540 Ljecyrillic: 0x0409,
22541 Llinebelow: 0x1E3A,
22542 Lmonospace: 0xFF2C,
22544 Lslashsmall: 0xF6F9,
22549 Macronsmall: 0xF7AF,
22552 Mdotaccent: 0x1E40,
22554 Menarmenian: 0x0544,
22555 Mmonospace: 0xFF2D,
22565 Ncircumflexbelow: 0x1E4A,
22566 Ncommaaccent: 0x0145,
22567 Ndotaccent: 0x1E44,
22572 Njecyrillic: 0x040A,
22573 Nlinebelow: 0x1E48,
22574 Nmonospace: 0xFF2E,
22575 Nowarmenian: 0x0546,
22578 Ntildesmall: 0xF7F1,
22584 Oacutesmall: 0xF7F3,
22585 Obarredcyrillic: 0x04E8,
22586 Obarreddieresiscyrillic: 0x04EA,
22589 Ocenteredtilde: 0x019F,
22591 Ocircumflex: 0x00D4,
22592 Ocircumflexacute: 0x1ED0,
22593 Ocircumflexdotbelow: 0x1ED8,
22594 Ocircumflexgrave: 0x1ED2,
22595 Ocircumflexhookabove: 0x1ED4,
22596 Ocircumflexsmall: 0xF7F4,
22597 Ocircumflextilde: 0x1ED6,
22602 Odieresiscyrillic: 0x04E6,
22603 Odieresissmall: 0xF7F6,
22605 Ogoneksmall: 0xF6FB,
22607 Ogravesmall: 0xF7F2,
22608 Oharmenian: 0x0555,
22610 Ohookabove: 0x1ECE,
22612 Ohornacute: 0x1EDA,
22613 Ohorndotbelow: 0x1EE2,
22614 Ohorngrave: 0x1EDC,
22615 Ohornhookabove: 0x1EDE,
22616 Ohorntilde: 0x1EE0,
22617 Ohungarumlaut: 0x0150,
22619 Oinvertedbreve: 0x020E,
22621 Omacronacute: 0x1E52,
22622 Omacrongrave: 0x1E50,
22624 Omegacyrillic: 0x0460,
22625 Omegagreek: 0x03A9,
22626 Omegaroundcyrillic: 0x047A,
22627 Omegatitlocyrillic: 0x047C,
22628 Omegatonos: 0x038F,
22630 Omicrontonos: 0x038C,
22631 Omonospace: 0xFF2F,
22634 Oogonekmacron: 0x01EC,
22637 Oslashacute: 0x01FE,
22638 Oslashsmall: 0xF7F8,
22640 Ostrokeacute: 0x01FE,
22641 Otcyrillic: 0x047E,
22643 Otildeacute: 0x1E4C,
22644 Otildedieresis: 0x1E4E,
22645 Otildesmall: 0xF7F5,
22649 Pdotaccent: 0x1E56,
22650 Pecyrillic: 0x041F,
22651 Peharmenian: 0x054A,
22652 Pemiddlehookcyrillic: 0x04A6,
22656 Piwrarmenian: 0x0553,
22657 Pmonospace: 0xFF30,
22659 Psicyrillic: 0x0470,
22663 Qmonospace: 0xFF31,
22666 Raarmenian: 0x054C,
22671 Rcommaaccent: 0x0156,
22673 Rdotaccent: 0x1E58,
22675 Rdotbelowmacron: 0x1E5C,
22676 Reharmenian: 0x0550,
22680 Rinvertedbreve: 0x0212,
22681 Rlinebelow: 0x1E5E,
22682 Rmonospace: 0xFF32,
22684 Rsmallinverted: 0x0281,
22685 Rsmallinvertedsuperior: 0x02B6,
22728 Sacutedotaccent: 0x1E64,
22729 Sampigreek: 0x03E0,
22731 Scarondotaccent: 0x1E66,
22732 Scaronsmall: 0xF6FD,
22735 Schwacyrillic: 0x04D8,
22736 Schwadieresiscyrillic: 0x04DA,
22738 Scircumflex: 0x015C,
22739 Scommaaccent: 0x0218,
22740 Sdotaccent: 0x1E60,
22742 Sdotbelowdotaccent: 0x1E68,
22743 Seharmenian: 0x054D,
22744 Sevenroman: 0x2166,
22745 Shaarmenian: 0x0547,
22746 Shacyrillic: 0x0428,
22747 Shchacyrillic: 0x0429,
22748 Sheicoptic: 0x03E2,
22749 Shhacyrillic: 0x04BA,
22750 Shimacoptic: 0x03EC,
22753 Smonospace: 0xFF33,
22754 Softsigncyrillic: 0x042C,
22756 Stigmagreek: 0x03DA,
22763 Tcircumflexbelow: 0x1E70,
22764 Tcommaaccent: 0x0162,
22765 Tdotaccent: 0x1E6A,
22767 Tecyrillic: 0x0422,
22768 Tedescendercyrillic: 0x04AC,
22770 Tetsecyrillic: 0x04B4,
22774 Thornsmall: 0xF7FE,
22775 Threeroman: 0x2162,
22776 Tildesmall: 0xF6FE,
22777 Tiwnarmenian: 0x054F,
22778 Tlinebelow: 0x1E6E,
22779 Tmonospace: 0xFF34,
22780 Toarmenian: 0x0539,
22784 Tretroflexhook: 0x01AE,
22785 Tsecyrillic: 0x0426,
22786 Tshecyrillic: 0x040B,
22788 Twelveroman: 0x216B,
22792 Uacutesmall: 0xF7FA,
22796 Ucircumflex: 0x00DB,
22797 Ucircumflexbelow: 0x1E76,
22798 Ucircumflexsmall: 0xF7FB,
22803 Udieresisacute: 0x01D7,
22804 Udieresisbelow: 0x1E72,
22805 Udieresiscaron: 0x01D9,
22806 Udieresiscyrillic: 0x04F0,
22807 Udieresisgrave: 0x01DB,
22808 Udieresismacron: 0x01D5,
22809 Udieresissmall: 0xF7FC,
22812 Ugravesmall: 0xF7F9,
22813 Uhookabove: 0x1EE6,
22815 Uhornacute: 0x1EE8,
22816 Uhorndotbelow: 0x1EF0,
22817 Uhorngrave: 0x1EEA,
22818 Uhornhookabove: 0x1EEC,
22819 Uhorntilde: 0x1EEE,
22820 Uhungarumlaut: 0x0170,
22821 Uhungarumlautcyrillic: 0x04F2,
22822 Uinvertedbreve: 0x0216,
22823 Ukcyrillic: 0x0478,
22825 Umacroncyrillic: 0x04EE,
22826 Umacrondieresis: 0x1E7A,
22827 Umonospace: 0xFF35,
22831 Upsilonacutehooksymbolgreek: 0x03D3,
22832 Upsilonafrican: 0x01B1,
22833 Upsilondieresis: 0x03AB,
22834 Upsilondieresishooksymbolgreek: 0x03D4,
22835 Upsilonhooksymbol: 0x03D2,
22836 Upsilontonos: 0x038E,
22838 Ushortcyrillic: 0x040E,
22840 Ustraightcyrillic: 0x04AE,
22841 Ustraightstrokecyrillic: 0x04B0,
22843 Utildeacute: 0x1E78,
22844 Utildebelow: 0x1E74,
22848 Vecyrillic: 0x0412,
22849 Vewarmenian: 0x054E,
22851 Vmonospace: 0xFF36,
22852 Voarmenian: 0x0548,
22858 Wcircumflex: 0x0174,
22860 Wdotaccent: 0x1E86,
22863 Wmonospace: 0xFF37,
22868 Xdotaccent: 0x1E8A,
22869 Xeharmenian: 0x053D,
22871 Xmonospace: 0xFF38,
22875 Yacutesmall: 0xF7FD,
22876 Yatcyrillic: 0x0462,
22878 Ycircumflex: 0x0176,
22880 Ydieresissmall: 0xF7FF,
22881 Ydotaccent: 0x1E8E,
22883 Yericyrillic: 0x042B,
22884 Yerudieresiscyrillic: 0x04F8,
22887 Yhookabove: 0x1EF6,
22888 Yiarmenian: 0x0545,
22889 Yicyrillic: 0x0407,
22890 Yiwnarmenian: 0x0552,
22891 Ymonospace: 0xFF39,
22894 Yusbigcyrillic: 0x046A,
22895 Yusbigiotifiedcyrillic: 0x046C,
22896 Yuslittlecyrillic: 0x0466,
22897 Yuslittleiotifiedcyrillic: 0x0468,
22899 Zaarmenian: 0x0536,
22902 Zcaronsmall: 0xF6FF,
22904 Zcircumflex: 0x1E90,
22906 Zdotaccent: 0x017B,
22908 Zecyrillic: 0x0417,
22909 Zedescendercyrillic: 0x0498,
22910 Zedieresiscyrillic: 0x04DE,
22912 Zhearmenian: 0x053A,
22913 Zhebrevecyrillic: 0x04C1,
22914 Zhecyrillic: 0x0416,
22915 Zhedescendercyrillic: 0x0496,
22916 Zhedieresiscyrillic: 0x04DC,
22917 Zlinebelow: 0x1E94,
22918 Zmonospace: 0xFF3A,
22925 aagujarati: 0x0A86,
22926 aagurmukhi: 0x0A06,
22927 aamatragurmukhi: 0x0A3E,
22928 aarusquare: 0x3303,
22929 aavowelsignbengali: 0x09BE,
22930 aavowelsigndeva: 0x093E,
22931 aavowelsigngujarati: 0x0ABE,
22932 abbreviationmarkarmenian: 0x055F,
22933 abbreviationsigndeva: 0x0970,
22937 abreveacute: 0x1EAF,
22938 abrevecyrillic: 0x04D1,
22939 abrevedotbelow: 0x1EB7,
22940 abrevegrave: 0x1EB1,
22941 abrevehookabove: 0x1EB3,
22942 abrevetilde: 0x1EB5,
22945 acircumflex: 0x00E2,
22946 acircumflexacute: 0x1EA5,
22947 acircumflexdotbelow: 0x1EAD,
22948 acircumflexgrave: 0x1EA7,
22949 acircumflexhookabove: 0x1EA9,
22950 acircumflextilde: 0x1EAB,
22952 acutebelowcmb: 0x0317,
22956 acutelowmod: 0x02CF,
22957 acutetonecmb: 0x0341,
22960 addakgurmukhi: 0x0A71,
22963 adieresiscyrillic: 0x04D3,
22964 adieresismacron: 0x01DF,
22966 adotmacron: 0x01E1,
23221 ahookabove: 0x1EA3,
23223 aibopomofo: 0x311E,
23225 aiecyrillic: 0x04D5,
23226 aigujarati: 0x0A90,
23227 aigurmukhi: 0x0A10,
23228 aimatragurmukhi: 0x0A48,
23230 ainfinalarabic: 0xFECA,
23231 aininitialarabic: 0xFECB,
23232 ainmedialarabic: 0xFECC,
23233 ainvertedbreve: 0x0203,
23234 aivowelsignbengali: 0x09C8,
23235 aivowelsigndeva: 0x0948,
23236 aivowelsigngujarati: 0x0AC8,
23238 akatakanahalfwidth: 0xFF71,
23241 alefarabic: 0x0627,
23242 alefdageshhebrew: 0xFB30,
23243 aleffinalarabic: 0xFE8E,
23244 alefhamzaabovearabic: 0x0623,
23245 alefhamzaabovefinalarabic: 0xFE84,
23246 alefhamzabelowarabic: 0x0625,
23247 alefhamzabelowfinalarabic: 0xFE88,
23248 alefhebrew: 0x05D0,
23249 aleflamedhebrew: 0xFB4F,
23250 alefmaddaabovearabic: 0x0622,
23251 alefmaddaabovefinalarabic: 0xFE82,
23252 alefmaksuraarabic: 0x0649,
23253 alefmaksurafinalarabic: 0xFEF0,
23254 alefmaksurainitialarabic: 0xFEF3,
23255 alefmaksuramedialarabic: 0xFEF4,
23256 alefpatahhebrew: 0xFB2E,
23257 alefqamatshebrew: 0xFB2F,
23261 alphatonos: 0x03AC,
23263 amonospace: 0xFF41,
23265 ampersandmonospace: 0xFF06,
23266 ampersandsmall: 0xF726,
23268 anbopomofo: 0x3122,
23269 angbopomofo: 0x3124,
23270 angbracketleft: 0x3008, // This glyph is missing from Adobe's original list.
23271 angbracketright: 0x3009, // This glyph is missing from Adobe's original list.
23272 angkhankhuthai: 0x0E5A,
23274 anglebracketleft: 0x3008,
23275 anglebracketleftvertical: 0xFE3F,
23276 anglebracketright: 0x3009,
23277 anglebracketrightvertical: 0xFE40,
23279 angleright: 0x232A,
23282 anudattadeva: 0x0952,
23283 anusvarabengali: 0x0982,
23284 anusvaradeva: 0x0902,
23285 anusvaragujarati: 0x0A82,
23287 apaatosquare: 0x3300,
23289 apostrophearmenian: 0x055A,
23290 apostrophemod: 0x02BC,
23292 approaches: 0x2250,
23293 approxequal: 0x2248,
23294 approxequalorimage: 0x2252,
23295 approximatelyequal: 0x2245,
23296 araeaekorean: 0x318E,
23297 araeakorean: 0x318D,
23299 arighthalfring: 0x1E9A,
23301 aringacute: 0x01FB,
23302 aringbelow: 0x1E01,
23304 arrowdashdown: 0x21E3,
23305 arrowdashleft: 0x21E0,
23306 arrowdashright: 0x21E2,
23307 arrowdashup: 0x21E1,
23308 arrowdblboth: 0x21D4,
23309 arrowdbldown: 0x21D3,
23310 arrowdblleft: 0x21D0,
23311 arrowdblright: 0x21D2,
23312 arrowdblup: 0x21D1,
23314 arrowdownleft: 0x2199,
23315 arrowdownright: 0x2198,
23316 arrowdownwhite: 0x21E9,
23317 arrowheaddownmod: 0x02C5,
23318 arrowheadleftmod: 0x02C2,
23319 arrowheadrightmod: 0x02C3,
23320 arrowheadupmod: 0x02C4,
23321 arrowhorizex: 0xF8E7,
23323 arrowleftdbl: 0x21D0,
23324 arrowleftdblstroke: 0x21CD,
23325 arrowleftoverright: 0x21C6,
23326 arrowleftwhite: 0x21E6,
23327 arrowright: 0x2192,
23328 arrowrightdblstroke: 0x21CF,
23329 arrowrightheavy: 0x279E,
23330 arrowrightoverleft: 0x21C4,
23331 arrowrightwhite: 0x21E8,
23332 arrowtableft: 0x21E4,
23333 arrowtabright: 0x21E5,
23336 arrowupdnbse: 0x21A8,
23337 arrowupdownbase: 0x21A8,
23338 arrowupleft: 0x2196,
23339 arrowupleftofdown: 0x21C5,
23340 arrowupright: 0x2197,
23341 arrowupwhite: 0x21E7,
23342 arrowvertex: 0xF8E6,
23343 asciicircum: 0x005E,
23344 asciicircummonospace: 0xFF3E,
23345 asciitilde: 0x007E,
23346 asciitildemonospace: 0xFF5E,
23348 ascriptturned: 0x0252,
23349 asmallhiragana: 0x3041,
23350 asmallkatakana: 0x30A1,
23351 asmallkatakanahalfwidth: 0xFF67,
23353 asteriskaltonearabic: 0x066D,
23354 asteriskarabic: 0x066D,
23355 asteriskmath: 0x2217,
23356 asteriskmonospace: 0xFF0A,
23357 asterisksmall: 0xFE61,
23360 asymptoticallyequal: 0x2243,
23363 atmonospace: 0xFF20,
23367 aubopomofo: 0x3120,
23369 augujarati: 0x0A94,
23370 augurmukhi: 0x0A14,
23371 aulengthmarkbengali: 0x09D7,
23372 aumatragurmukhi: 0x0A4C,
23373 auvowelsignbengali: 0x09CC,
23374 auvowelsigndeva: 0x094C,
23375 auvowelsigngujarati: 0x0ACC,
23376 avagrahadeva: 0x093D,
23377 aybarmenian: 0x0561,
23379 ayinaltonehebrew: 0xFB20,
23380 ayinhebrew: 0x05E2,
23384 backslashmonospace: 0xFF3C,
23386 bagujarati: 0x0AAC,
23387 bagurmukhi: 0x0A2C,
23388 bahiragana: 0x3070,
23390 bakatakana: 0x30D0,
23392 barmonospace: 0xFF5C,
23395 bdotaccent: 0x1E03,
23397 beamedsixteenthnotes: 0x266C,
23399 becyrillic: 0x0431,
23401 behfinalarabic: 0xFE90,
23402 behinitialarabic: 0xFE91,
23403 behiragana: 0x3079,
23404 behmedialarabic: 0xFE92,
23405 behmeeminitialarabic: 0xFC9F,
23406 behmeemisolatedarabic: 0xFC08,
23407 behnoonfinalarabic: 0xFC6D,
23408 bekatakana: 0x30D9,
23409 benarmenian: 0x0562,
23412 betasymbolgreek: 0x03D0,
23414 betdageshhebrew: 0xFB31,
23416 betrafehebrew: 0xFB4C,
23417 bhabengali: 0x09AD,
23419 bhagujarati: 0x0AAD,
23420 bhagurmukhi: 0x0A2D,
23422 bihiragana: 0x3073,
23423 bikatakana: 0x30D3,
23424 bilabialclick: 0x0298,
23425 bindigurmukhi: 0x0A02,
23426 birusquare: 0x3331,
23427 blackcircle: 0x25CF,
23428 blackdiamond: 0x25C6,
23429 blackdownpointingtriangle: 0x25BC,
23430 blackleftpointingpointer: 0x25C4,
23431 blackleftpointingtriangle: 0x25C0,
23432 blacklenticularbracketleft: 0x3010,
23433 blacklenticularbracketleftvertical: 0xFE3B,
23434 blacklenticularbracketright: 0x3011,
23435 blacklenticularbracketrightvertical: 0xFE3C,
23436 blacklowerlefttriangle: 0x25E3,
23437 blacklowerrighttriangle: 0x25E2,
23438 blackrectangle: 0x25AC,
23439 blackrightpointingpointer: 0x25BA,
23440 blackrightpointingtriangle: 0x25B6,
23441 blacksmallsquare: 0x25AA,
23442 blacksmilingface: 0x263B,
23443 blacksquare: 0x25A0,
23445 blackupperlefttriangle: 0x25E4,
23446 blackupperrighttriangle: 0x25E5,
23447 blackuppointingsmalltriangle: 0x25B4,
23448 blackuppointingtriangle: 0x25B2,
23450 blinebelow: 0x1E07,
23452 bmonospace: 0xFF42,
23453 bobaimaithai: 0x0E1A,
23454 bohiragana: 0x307C,
23455 bokatakana: 0x30DC,
23460 braceleftbt: 0xF8F3,
23461 braceleftmid: 0xF8F2,
23462 braceleftmonospace: 0xFF5B,
23463 braceleftsmall: 0xFE5B,
23464 bracelefttp: 0xF8F1,
23465 braceleftvertical: 0xFE37,
23466 braceright: 0x007D,
23467 bracerightbt: 0xF8FE,
23468 bracerightmid: 0xF8FD,
23469 bracerightmonospace: 0xFF5D,
23470 bracerightsmall: 0xFE5C,
23471 bracerighttp: 0xF8FC,
23472 bracerightvertical: 0xFE38,
23473 bracketleft: 0x005B,
23474 bracketleftbt: 0xF8F0,
23475 bracketleftex: 0xF8EF,
23476 bracketleftmonospace: 0xFF3B,
23477 bracketlefttp: 0xF8EE,
23478 bracketright: 0x005D,
23479 bracketrightbt: 0xF8FB,
23480 bracketrightex: 0xF8FA,
23481 bracketrightmonospace: 0xFF3D,
23482 bracketrighttp: 0xF8F9,
23484 brevebelowcmb: 0x032E,
23486 breveinvertedbelowcmb: 0x032F,
23487 breveinvertedcmb: 0x0311,
23488 breveinverteddoublecmb: 0x0361,
23489 bridgebelowcmb: 0x032A,
23490 bridgeinvertedbelowcmb: 0x033A,
23495 buhiragana: 0x3076,
23496 bukatakana: 0x30D6,
23498 bulletinverse: 0x25D8,
23499 bulletoperator: 0x2219,
23502 caarmenian: 0x056E,
23506 cagujarati: 0x0A9A,
23507 cagurmukhi: 0x0A1A,
23509 candrabindubengali: 0x0981,
23510 candrabinducmb: 0x0310,
23511 candrabindudeva: 0x0901,
23512 candrabindugujarati: 0x0A81,
23516 caronbelowcmb: 0x032C,
23518 carriagereturn: 0x21B5,
23522 ccedillaacute: 0x1E09,
23524 ccircumflex: 0x0109,
23527 cdotaccent: 0x010B,
23530 cedillacmb: 0x0327,
23532 centigrade: 0x2103,
23533 centinferior: 0xF6DF,
23534 centmonospace: 0xFFE0,
23535 centoldstyle: 0xF7A2,
23536 centsuperior: 0xF6E0,
23537 chaarmenian: 0x0579,
23538 chabengali: 0x099B,
23540 chagujarati: 0x0A9B,
23541 chagurmukhi: 0x0A1B,
23542 chbopomofo: 0x3114,
23543 cheabkhasiancyrillic: 0x04BD,
23545 checyrillic: 0x0447,
23546 chedescenderabkhasiancyrillic: 0x04BF,
23547 chedescendercyrillic: 0x04B7,
23548 chedieresiscyrillic: 0x04F5,
23549 cheharmenian: 0x0573,
23550 chekhakassiancyrillic: 0x04CC,
23551 cheverticalstrokecyrillic: 0x04B9,
23553 chieuchacirclekorean: 0x3277,
23554 chieuchaparenkorean: 0x3217,
23555 chieuchcirclekorean: 0x3269,
23556 chieuchkorean: 0x314A,
23557 chieuchparenkorean: 0x3209,
23558 chochangthai: 0x0E0A,
23559 chochanthai: 0x0E08,
23560 chochingthai: 0x0E09,
23561 chochoethai: 0x0E0C,
23563 cieucacirclekorean: 0x3276,
23564 cieucaparenkorean: 0x3216,
23565 cieuccirclekorean: 0x3268,
23566 cieuckorean: 0x3148,
23567 cieucparenkorean: 0x3208,
23568 cieucuparenkorean: 0x321C,
23570 circlecopyrt: 0x00A9, // This glyph is missing from Adobe's original list.
23571 circlemultiply: 0x2297,
23573 circleplus: 0x2295,
23574 circlepostalmark: 0x3036,
23575 circlewithlefthalfblack: 0x25D0,
23576 circlewithrighthalfblack: 0x25D1,
23577 circumflex: 0x02C6,
23578 circumflexbelowcmb: 0x032D,
23579 circumflexcmb: 0x0302,
23581 clickalveolar: 0x01C2,
23582 clickdental: 0x01C0,
23583 clicklateral: 0x01C1,
23584 clickretroflex: 0x01C3,
23586 clubsuitblack: 0x2663,
23587 clubsuitwhite: 0x2667,
23588 cmcubedsquare: 0x33A4,
23589 cmonospace: 0xFF43,
23590 cmsquaredsquare: 0x33A0,
23591 coarmenian: 0x0581,
23593 colonmonetary: 0x20A1,
23594 colonmonospace: 0xFF1A,
23596 colonsmall: 0xFE55,
23597 colontriangularhalfmod: 0x02D1,
23598 colontriangularmod: 0x02D0,
23600 commaabovecmb: 0x0313,
23601 commaaboverightcmb: 0x0315,
23602 commaaccent: 0xF6C3,
23603 commaarabic: 0x060C,
23604 commaarmenian: 0x055D,
23605 commainferior: 0xF6E1,
23606 commamonospace: 0xFF0C,
23607 commareversedabovecmb: 0x0314,
23608 commareversedmod: 0x02BD,
23609 commasmall: 0xFE50,
23610 commasuperior: 0xF6E2,
23611 commaturnedabovecmb: 0x0312,
23612 commaturnedmod: 0x02BB,
23615 contourintegral: 0x222E,
23617 controlACK: 0x0006,
23618 controlBEL: 0x0007,
23620 controlCAN: 0x0018,
23622 controlDC1: 0x0011,
23623 controlDC2: 0x0012,
23624 controlDC3: 0x0013,
23625 controlDC4: 0x0014,
23626 controlDEL: 0x007F,
23627 controlDLE: 0x0010,
23629 controlENQ: 0x0005,
23630 controlEOT: 0x0004,
23631 controlESC: 0x001B,
23632 controlETB: 0x0017,
23633 controlETX: 0x0003,
23639 controlNAK: 0x0015,
23643 controlSOT: 0x0002,
23644 controlSTX: 0x0001,
23645 controlSUB: 0x001A,
23646 controlSYN: 0x0016,
23650 copyrightsans: 0xF8E9,
23651 copyrightserif: 0xF6D9,
23652 cornerbracketleft: 0x300C,
23653 cornerbracketlefthalfwidth: 0xFF62,
23654 cornerbracketleftvertical: 0xFE41,
23655 cornerbracketright: 0x300D,
23656 cornerbracketrighthalfwidth: 0xFF63,
23657 cornerbracketrightvertical: 0xFE42,
23658 corporationsquare: 0x337F,
23660 coverkgsquare: 0x33C6,
23663 cstretched: 0x0297,
23672 daarmenian: 0x0564,
23676 dadfinalarabic: 0xFEBE,
23677 dadinitialarabic: 0xFEBF,
23678 dadmedialarabic: 0xFEC0,
23680 dageshhebrew: 0x05BC,
23683 dagujarati: 0x0AA6,
23684 dagurmukhi: 0x0A26,
23685 dahiragana: 0x3060,
23686 dakatakana: 0x30C0,
23689 daletdagesh: 0xFB33,
23690 daletdageshhebrew: 0xFB33,
23691 dalethebrew: 0x05D3,
23692 dalfinalarabic: 0xFEAA,
23693 dammaarabic: 0x064F,
23694 dammalowarabic: 0x064F,
23695 dammatanaltonearabic: 0x064C,
23696 dammatanarabic: 0x064C,
23698 dargahebrew: 0x05A7,
23699 dargalefthebrew: 0x05A7,
23700 dasiapneumatacyrilliccmb: 0x0485,
23702 dblanglebracketleft: 0x300A,
23703 dblanglebracketleftvertical: 0xFE3D,
23704 dblanglebracketright: 0x300B,
23705 dblanglebracketrightvertical: 0xFE3E,
23706 dblarchinvertedbelowcmb: 0x032B,
23707 dblarrowleft: 0x21D4,
23708 dblarrowright: 0x21D2,
23711 dblgravecmb: 0x030F,
23712 dblintegral: 0x222C,
23713 dbllowline: 0x2017,
23714 dbllowlinecmb: 0x0333,
23715 dbloverlinecmb: 0x033F,
23716 dblprimemod: 0x02BA,
23717 dblverticalbar: 0x2016,
23718 dblverticallineabovecmb: 0x030E,
23724 dcircumflexbelow: 0x1E13,
23726 ddabengali: 0x09A1,
23728 ddagujarati: 0x0AA1,
23729 ddagurmukhi: 0x0A21,
23730 ddalarabic: 0x0688,
23731 ddalfinalarabic: 0xFB89,
23733 ddhabengali: 0x09A2,
23735 ddhagujarati: 0x0AA2,
23736 ddhagurmukhi: 0x0A22,
23737 ddotaccent: 0x1E0B,
23739 decimalseparatorarabic: 0x066B,
23740 decimalseparatorpersian: 0x066B,
23741 decyrillic: 0x0434,
23743 dehihebrew: 0x05AD,
23744 dehiragana: 0x3067,
23746 dekatakana: 0x30C7,
23747 deleteleft: 0x232B,
23748 deleteright: 0x2326,
23750 deltaturned: 0x018D,
23751 denominatorminusonenumeratorbengali: 0x09F8,
23753 dhabengali: 0x09A7,
23755 dhagujarati: 0x0AA7,
23756 dhagurmukhi: 0x0A27,
23758 dialytikatonos: 0x0385,
23759 dialytikatonoscmb: 0x0344,
23761 diamondsuitwhite: 0x2662,
23763 dieresisacute: 0xF6D7,
23764 dieresisbelowcmb: 0x0324,
23765 dieresiscmb: 0x0308,
23766 dieresisgrave: 0xF6D8,
23767 dieresistonos: 0x0385,
23768 dihiragana: 0x3062,
23769 dikatakana: 0x30C2,
23773 divisionslash: 0x2215,
23774 djecyrillic: 0x0452,
23776 dlinebelow: 0x1E0F,
23779 dmonospace: 0xFF44,
23781 dochadathai: 0x0E0E,
23783 dohiragana: 0x3069,
23784 dokatakana: 0x30C9,
23786 dollarinferior: 0xF6E3,
23787 dollarmonospace: 0xFF04,
23788 dollaroldstyle: 0xF724,
23789 dollarsmall: 0xFE69,
23790 dollarsuperior: 0xF6E4,
23792 dorusquare: 0x3326,
23794 dotaccentcmb: 0x0307,
23795 dotbelowcmb: 0x0323,
23796 dotbelowcomb: 0x0323,
23797 dotkatakana: 0x30FB,
23800 dotlessjstrokehook: 0x0284,
23802 dottedcircle: 0x25CC,
23803 doubleyodpatah: 0xFB1F,
23804 doubleyodpatahhebrew: 0xFB1F,
23805 downtackbelowcmb: 0x031E,
23806 downtackmod: 0x02D5,
23811 duhiragana: 0x3065,
23812 dukatakana: 0x30C5,
23817 dzeabkhasiancyrillic: 0x04E1,
23818 dzecyrillic: 0x0455,
23819 dzhecyrillic: 0x045F,
23826 ecandradeva: 0x090D,
23827 ecandragujarati: 0x0A8D,
23828 ecandravowelsigndeva: 0x0945,
23829 ecandravowelsigngujarati: 0x0AC5,
23831 ecedillabreve: 0x1E1D,
23832 echarmenian: 0x0565,
23833 echyiwnarmenian: 0x0587,
23835 ecircumflex: 0x00EA,
23836 ecircumflexacute: 0x1EBF,
23837 ecircumflexbelow: 0x1E19,
23838 ecircumflexdotbelow: 0x1EC7,
23839 ecircumflexgrave: 0x1EC1,
23840 ecircumflexhookabove: 0x1EC3,
23841 ecircumflextilde: 0x1EC5,
23847 edotaccent: 0x0117,
23849 eegurmukhi: 0x0A0F,
23850 eematragurmukhi: 0x0A47,
23851 efcyrillic: 0x0444,
23854 eharmenian: 0x0567,
23855 ehbopomofo: 0x311D,
23857 ehookabove: 0x1EBB,
23858 eibopomofo: 0x311F,
23860 eightarabic: 0x0668,
23861 eightbengali: 0x09EE,
23862 eightcircle: 0x2467,
23863 eightcircleinversesansserif: 0x2791,
23865 eighteencircle: 0x2471,
23866 eighteenparen: 0x2485,
23867 eighteenperiod: 0x2499,
23868 eightgujarati: 0x0AEE,
23869 eightgurmukhi: 0x0A6E,
23870 eighthackarabic: 0x0668,
23871 eighthangzhou: 0x3028,
23872 eighthnotebeamed: 0x266B,
23873 eightideographicparen: 0x3227,
23874 eightinferior: 0x2088,
23875 eightmonospace: 0xFF18,
23876 eightoldstyle: 0xF738,
23877 eightparen: 0x247B,
23878 eightperiod: 0x248F,
23879 eightpersian: 0x06F8,
23880 eightroman: 0x2177,
23881 eightsuperior: 0x2078,
23883 einvertedbreve: 0x0207,
23884 eiotifiedcyrillic: 0x0465,
23886 ekatakanahalfwidth: 0xFF74,
23887 ekonkargurmukhi: 0x0A74,
23889 elcyrillic: 0x043B,
23891 elevencircle: 0x246A,
23892 elevenparen: 0x247E,
23893 elevenperiod: 0x2492,
23894 elevenroman: 0x217A,
23896 ellipsisvertical: 0x22EE,
23898 emacronacute: 0x1E17,
23899 emacrongrave: 0x1E15,
23900 emcyrillic: 0x043C,
23902 emdashvertical: 0xFE31,
23903 emonospace: 0xFF45,
23904 emphasismarkarmenian: 0x055B,
23906 enbopomofo: 0x3123,
23907 encyrillic: 0x043D,
23909 endashvertical: 0xFE32,
23910 endescendercyrillic: 0x04A3,
23912 engbopomofo: 0x3125,
23913 enghecyrillic: 0x04A5,
23914 enhookcyrillic: 0x04C8,
23919 eopenclosed: 0x029A,
23920 eopenreversed: 0x025C,
23921 eopenreversedclosed: 0x025E,
23922 eopenreversedhook: 0x025D,
23925 epsilontonos: 0x03AD,
23927 equalmonospace: 0xFF1D,
23928 equalsmall: 0xFE66,
23929 equalsuperior: 0x207C,
23930 equivalence: 0x2261,
23931 erbopomofo: 0x3126,
23932 ercyrillic: 0x0440,
23934 ereversedcyrillic: 0x044D,
23935 escyrillic: 0x0441,
23936 esdescendercyrillic: 0x04AB,
23939 eshortdeva: 0x090E,
23940 eshortvowelsigndeva: 0x0946,
23941 eshreversedloop: 0x01AA,
23942 eshsquatreversed: 0x0285,
23943 esmallhiragana: 0x3047,
23944 esmallkatakana: 0x30A7,
23945 esmallkatakanahalfwidth: 0xFF6A,
23949 etarmenian: 0x0568,
23953 etildebelow: 0x1E1B,
23954 etnahtafoukhhebrew: 0x0591,
23955 etnahtafoukhlefthebrew: 0x0591,
23956 etnahtahebrew: 0x0591,
23957 etnahtalefthebrew: 0x0591,
23961 evowelsignbengali: 0x09C7,
23962 evowelsigndeva: 0x0947,
23963 evowelsigngujarati: 0x0AC7,
23965 exclamarmenian: 0x055C,
23967 exclamdown: 0x00A1,
23968 exclamdownsmall: 0xF7A1,
23969 exclammonospace: 0xFF01,
23970 exclamsmall: 0xF721,
23971 existential: 0x2203,
23975 ezhreversed: 0x01B9,
23979 fagurmukhi: 0x0A5E,
23980 fahrenheit: 0x2109,
23981 fathaarabic: 0x064E,
23982 fathalowarabic: 0x064E,
23983 fathatanarabic: 0x064B,
23986 fdotaccent: 0x1E1F,
23988 feharmenian: 0x0586,
23989 fehfinalarabic: 0xFED2,
23990 fehinitialarabic: 0xFED3,
23991 fehmedialarabic: 0xFED4,
23998 fifteencircle: 0x246E,
23999 fifteenparen: 0x2482,
24000 fifteenperiod: 0x2496,
24001 figuredash: 0x2012,
24003 filledrect: 0x25AC,
24005 finalkafdagesh: 0xFB3A,
24006 finalkafdageshhebrew: 0xFB3A,
24007 finalkafhebrew: 0x05DA,
24009 finalmemhebrew: 0x05DD,
24011 finalnunhebrew: 0x05DF,
24013 finalpehebrew: 0x05E3,
24014 finaltsadi: 0x05E5,
24015 finaltsadihebrew: 0x05E5,
24016 firsttonechinese: 0x02C9,
24018 fitacyrillic: 0x0473,
24020 fivearabic: 0x0665,
24021 fivebengali: 0x09EB,
24022 fivecircle: 0x2464,
24023 fivecircleinversesansserif: 0x278E,
24025 fiveeighths: 0x215D,
24026 fivegujarati: 0x0AEB,
24027 fivegurmukhi: 0x0A6B,
24028 fivehackarabic: 0x0665,
24029 fivehangzhou: 0x3025,
24030 fiveideographicparen: 0x3224,
24031 fiveinferior: 0x2085,
24032 fivemonospace: 0xFF15,
24033 fiveoldstyle: 0xF735,
24035 fiveperiod: 0x248C,
24036 fivepersian: 0x06F5,
24038 fivesuperior: 0x2075,
24042 fmonospace: 0xFF46,
24046 fongmanthai: 0x0E4F,
24049 fourarabic: 0x0664,
24050 fourbengali: 0x09EA,
24051 fourcircle: 0x2463,
24052 fourcircleinversesansserif: 0x278D,
24054 fourgujarati: 0x0AEA,
24055 fourgurmukhi: 0x0A6A,
24056 fourhackarabic: 0x0664,
24057 fourhangzhou: 0x3024,
24058 fourideographicparen: 0x3223,
24059 fourinferior: 0x2084,
24060 fourmonospace: 0xFF14,
24061 fournumeratorbengali: 0x09F7,
24062 fouroldstyle: 0xF734,
24064 fourperiod: 0x248B,
24065 fourpersian: 0x06F4,
24067 foursuperior: 0x2074,
24068 fourteencircle: 0x246D,
24069 fourteenparen: 0x2481,
24070 fourteenperiod: 0x2495,
24072 fourthtonechinese: 0x02CB,
24081 gaffinalarabic: 0xFB93,
24082 gafinitialarabic: 0xFB94,
24083 gafmedialarabic: 0xFB95,
24084 gagujarati: 0x0A97,
24085 gagurmukhi: 0x0A17,
24086 gahiragana: 0x304C,
24087 gakatakana: 0x30AC,
24089 gammalatinsmall: 0x0263,
24090 gammasuperior: 0x02E0,
24091 gangiacoptic: 0x03EB,
24097 gcircumflex: 0x011D,
24098 gcommaaccent: 0x0123,
24100 gdotaccent: 0x0121,
24101 gecyrillic: 0x0433,
24102 gehiragana: 0x3052,
24103 gekatakana: 0x30B2,
24104 geometricallyequal: 0x2251,
24105 gereshaccenthebrew: 0x059C,
24106 gereshhebrew: 0x05F3,
24107 gereshmuqdamhebrew: 0x059D,
24108 germandbls: 0x00DF,
24109 gershayimaccenthebrew: 0x059E,
24110 gershayimhebrew: 0x05F4,
24112 ghabengali: 0x0998,
24113 ghadarmenian: 0x0572,
24115 ghagujarati: 0x0A98,
24116 ghagurmukhi: 0x0A18,
24117 ghainarabic: 0x063A,
24118 ghainfinalarabic: 0xFECE,
24119 ghaininitialarabic: 0xFECF,
24120 ghainmedialarabic: 0xFED0,
24121 ghemiddlehookcyrillic: 0x0495,
24122 ghestrokecyrillic: 0x0493,
24123 gheupturncyrillic: 0x0491,
24125 ghhagurmukhi: 0x0A5A,
24128 gihiragana: 0x304E,
24129 gikatakana: 0x30AE,
24130 gimarmenian: 0x0563,
24132 gimeldagesh: 0xFB32,
24133 gimeldageshhebrew: 0xFB32,
24134 gimelhebrew: 0x05D2,
24135 gjecyrillic: 0x0453,
24136 glottalinvertedstroke: 0x01BE,
24137 glottalstop: 0x0294,
24138 glottalstopinverted: 0x0296,
24139 glottalstopmod: 0x02C0,
24140 glottalstopreversed: 0x0295,
24141 glottalstopreversedmod: 0x02C1,
24142 glottalstopreversedsuperior: 0x02E4,
24143 glottalstopstroke: 0x02A1,
24144 glottalstopstrokereversed: 0x02A2,
24146 gmonospace: 0xFF47,
24147 gohiragana: 0x3054,
24148 gokatakana: 0x30B4,
24153 gravebelowcmb: 0x0316,
24157 gravelowmod: 0x02CE,
24158 gravemonospace: 0xFF40,
24159 gravetonecmb: 0x0340,
24161 greaterequal: 0x2265,
24162 greaterequalorless: 0x22DB,
24163 greatermonospace: 0xFF1E,
24164 greaterorequivalent: 0x2273,
24165 greaterorless: 0x2277,
24166 greateroverequal: 0x2267,
24167 greatersmall: 0xFE65,
24170 guhiragana: 0x3050,
24171 guillemotleft: 0x00AB,
24172 guillemotright: 0x00BB,
24173 guilsinglleft: 0x2039,
24174 guilsinglright: 0x203A,
24175 gukatakana: 0x30B0,
24176 guramusquare: 0x3318,
24179 haabkhasiancyrillic: 0x04A9,
24180 haaltonearabic: 0x06C1,
24182 hadescendercyrillic: 0x04B3,
24184 hagujarati: 0x0AB9,
24185 hagurmukhi: 0x0A39,
24187 hahfinalarabic: 0xFEA2,
24188 hahinitialarabic: 0xFEA3,
24189 hahiragana: 0x306F,
24190 hahmedialarabic: 0xFEA4,
24191 haitusquare: 0x332A,
24192 hakatakana: 0x30CF,
24193 hakatakanahalfwidth: 0xFF8A,
24194 halantgurmukhi: 0x0A4D,
24195 hamzaarabic: 0x0621,
24196 hamzalowarabic: 0x0621,
24197 hangulfiller: 0x3164,
24198 hardsigncyrillic: 0x044A,
24199 harpoonleftbarbup: 0x21BC,
24200 harpoonrightbarbup: 0x21C0,
24202 hatafpatah: 0x05B2,
24203 hatafpatah16: 0x05B2,
24204 hatafpatah23: 0x05B2,
24205 hatafpatah2f: 0x05B2,
24206 hatafpatahhebrew: 0x05B2,
24207 hatafpatahnarrowhebrew: 0x05B2,
24208 hatafpatahquarterhebrew: 0x05B2,
24209 hatafpatahwidehebrew: 0x05B2,
24210 hatafqamats: 0x05B3,
24211 hatafqamats1b: 0x05B3,
24212 hatafqamats28: 0x05B3,
24213 hatafqamats34: 0x05B3,
24214 hatafqamatshebrew: 0x05B3,
24215 hatafqamatsnarrowhebrew: 0x05B3,
24216 hatafqamatsquarterhebrew: 0x05B3,
24217 hatafqamatswidehebrew: 0x05B3,
24218 hatafsegol: 0x05B1,
24219 hatafsegol17: 0x05B1,
24220 hatafsegol24: 0x05B1,
24221 hatafsegol30: 0x05B1,
24222 hatafsegolhebrew: 0x05B1,
24223 hatafsegolnarrowhebrew: 0x05B1,
24224 hatafsegolquarterhebrew: 0x05B1,
24225 hatafsegolwidehebrew: 0x05B1,
24228 hbrevebelow: 0x1E2B,
24231 hcircumflex: 0x0125,
24233 hdotaccent: 0x1E23,
24237 heartsuitblack: 0x2665,
24238 heartsuitwhite: 0x2661,
24240 hedageshhebrew: 0xFB34,
24241 hehaltonearabic: 0x06C1,
24244 hehfinalaltonearabic: 0xFBA7,
24245 hehfinalalttwoarabic: 0xFEEA,
24246 hehfinalarabic: 0xFEEA,
24247 hehhamzaabovefinalarabic: 0xFBA5,
24248 hehhamzaaboveisolatedarabic: 0xFBA4,
24249 hehinitialaltonearabic: 0xFBA8,
24250 hehinitialarabic: 0xFEEB,
24251 hehiragana: 0x3078,
24252 hehmedialaltonearabic: 0xFBA9,
24253 hehmedialarabic: 0xFEEC,
24254 heiseierasquare: 0x337B,
24255 hekatakana: 0x30D8,
24256 hekatakanahalfwidth: 0xFF8D,
24257 hekutaarusquare: 0x3336,
24259 herutusquare: 0x3339,
24263 hhooksuperior: 0x02B1,
24264 hieuhacirclekorean: 0x327B,
24265 hieuhaparenkorean: 0x321B,
24266 hieuhcirclekorean: 0x326D,
24267 hieuhkorean: 0x314E,
24268 hieuhparenkorean: 0x320D,
24269 hihiragana: 0x3072,
24270 hikatakana: 0x30D2,
24271 hikatakanahalfwidth: 0xFF8B,
24276 hiriqhebrew: 0x05B4,
24277 hiriqnarrowhebrew: 0x05B4,
24278 hiriqquarterhebrew: 0x05B4,
24279 hiriqwidehebrew: 0x05B4,
24280 hlinebelow: 0x1E96,
24281 hmonospace: 0xFF48,
24282 hoarmenian: 0x0570,
24284 hohiragana: 0x307B,
24285 hokatakana: 0x30DB,
24286 hokatakanahalfwidth: 0xFF8E,
24291 holamhebrew: 0x05B9,
24292 holamnarrowhebrew: 0x05B9,
24293 holamquarterhebrew: 0x05B9,
24294 holamwidehebrew: 0x05B9,
24295 honokhukthai: 0x0E2E,
24296 hookabovecomb: 0x0309,
24298 hookpalatalizedbelowcmb: 0x0321,
24299 hookretroflexbelowcmb: 0x0322,
24300 hoonsquare: 0x3342,
24301 horicoptic: 0x03E9,
24302 horizontalbar: 0x2015,
24304 hotsprings: 0x2668,
24309 huhiragana: 0x3075,
24310 huiitosquare: 0x3333,
24311 hukatakana: 0x30D5,
24312 hukatakanahalfwidth: 0xFF8C,
24313 hungarumlaut: 0x02DD,
24314 hungarumlautcmb: 0x030B,
24317 hypheninferior: 0xF6E5,
24318 hyphenmonospace: 0xFF0D,
24319 hyphensmall: 0xFE63,
24320 hyphensuperior: 0xF6E6,
24324 iacyrillic: 0x044F,
24330 icircumflex: 0x00EE,
24333 ideographearthcircle: 0x328F,
24334 ideographfirecircle: 0x328B,
24335 ideographicallianceparen: 0x323F,
24336 ideographiccallparen: 0x323A,
24337 ideographiccentrecircle: 0x32A5,
24338 ideographicclose: 0x3006,
24339 ideographiccomma: 0x3001,
24340 ideographiccommaleft: 0xFF64,
24341 ideographiccongratulationparen: 0x3237,
24342 ideographiccorrectcircle: 0x32A3,
24343 ideographicearthparen: 0x322F,
24344 ideographicenterpriseparen: 0x323D,
24345 ideographicexcellentcircle: 0x329D,
24346 ideographicfestivalparen: 0x3240,
24347 ideographicfinancialcircle: 0x3296,
24348 ideographicfinancialparen: 0x3236,
24349 ideographicfireparen: 0x322B,
24350 ideographichaveparen: 0x3232,
24351 ideographichighcircle: 0x32A4,
24352 ideographiciterationmark: 0x3005,
24353 ideographiclaborcircle: 0x3298,
24354 ideographiclaborparen: 0x3238,
24355 ideographicleftcircle: 0x32A7,
24356 ideographiclowcircle: 0x32A6,
24357 ideographicmedicinecircle: 0x32A9,
24358 ideographicmetalparen: 0x322E,
24359 ideographicmoonparen: 0x322A,
24360 ideographicnameparen: 0x3234,
24361 ideographicperiod: 0x3002,
24362 ideographicprintcircle: 0x329E,
24363 ideographicreachparen: 0x3243,
24364 ideographicrepresentparen: 0x3239,
24365 ideographicresourceparen: 0x323E,
24366 ideographicrightcircle: 0x32A8,
24367 ideographicsecretcircle: 0x3299,
24368 ideographicselfparen: 0x3242,
24369 ideographicsocietyparen: 0x3233,
24370 ideographicspace: 0x3000,
24371 ideographicspecialparen: 0x3235,
24372 ideographicstockparen: 0x3231,
24373 ideographicstudyparen: 0x323B,
24374 ideographicsunparen: 0x3230,
24375 ideographicsuperviseparen: 0x323C,
24376 ideographicwaterparen: 0x322C,
24377 ideographicwoodparen: 0x322D,
24378 ideographiczero: 0x3007,
24379 ideographmetalcircle: 0x328E,
24380 ideographmooncircle: 0x328A,
24381 ideographnamecircle: 0x3294,
24382 ideographsuncircle: 0x3290,
24383 ideographwatercircle: 0x328C,
24384 ideographwoodcircle: 0x328D,
24387 idieresisacute: 0x1E2F,
24388 idieresiscyrillic: 0x04E5,
24390 iebrevecyrillic: 0x04D7,
24391 iecyrillic: 0x0435,
24392 ieungacirclekorean: 0x3275,
24393 ieungaparenkorean: 0x3215,
24394 ieungcirclekorean: 0x3267,
24395 ieungkorean: 0x3147,
24396 ieungparenkorean: 0x3207,
24401 ihookabove: 0x1EC9,
24403 iicyrillic: 0x0438,
24405 iigujarati: 0x0A88,
24406 iigurmukhi: 0x0A08,
24407 iimatragurmukhi: 0x0A40,
24408 iinvertedbreve: 0x020B,
24409 iishortcyrillic: 0x0439,
24410 iivowelsignbengali: 0x09C0,
24411 iivowelsigndeva: 0x0940,
24412 iivowelsigngujarati: 0x0AC0,
24415 ikatakanahalfwidth: 0xFF72,
24418 iluyhebrew: 0x05AC,
24420 imacroncyrillic: 0x04E3,
24421 imageorapproximatelyequal: 0x2253,
24422 imatragurmukhi: 0x0A3F,
24423 imonospace: 0xFF49,
24426 iniarmenian: 0x056B,
24428 integralbottom: 0x2321,
24429 integralbt: 0x2321,
24430 integralex: 0xF8F5,
24431 integraltop: 0x2320,
24432 integraltp: 0x2320,
24433 intersection: 0x2229,
24434 intisquare: 0x3305,
24437 invsmileface: 0x263B,
24438 iocyrillic: 0x0451,
24441 iotadieresis: 0x03CA,
24442 iotadieresistonos: 0x0390,
24446 irigurmukhi: 0x0A72,
24447 ismallhiragana: 0x3043,
24448 ismallkatakana: 0x30A3,
24449 ismallkatakanahalfwidth: 0xFF68,
24450 issharbengali: 0x09FA,
24453 iterationhiragana: 0x309D,
24454 iterationkatakana: 0x30FD,
24456 itildebelow: 0x1E2D,
24457 iubopomofo: 0x3129,
24458 iucyrillic: 0x044E,
24459 ivowelsignbengali: 0x09BF,
24460 ivowelsigndeva: 0x093F,
24461 ivowelsigngujarati: 0x0ABF,
24462 izhitsacyrillic: 0x0475,
24463 izhitsadblgravecyrillic: 0x0477,
24465 jaarmenian: 0x0571,
24468 jagujarati: 0x0A9C,
24469 jagurmukhi: 0x0A1C,
24473 jcircumflex: 0x0135,
24474 jcrossedtail: 0x029D,
24475 jdotlessstroke: 0x025F,
24476 jecyrillic: 0x0458,
24477 jeemarabic: 0x062C,
24478 jeemfinalarabic: 0xFE9E,
24479 jeeminitialarabic: 0xFE9F,
24480 jeemmedialarabic: 0xFEA0,
24482 jehfinalarabic: 0xFB8B,
24483 jhabengali: 0x099D,
24485 jhagujarati: 0x0A9D,
24486 jhagurmukhi: 0x0A1D,
24487 jheharmenian: 0x057B,
24489 jmonospace: 0xFF4A,
24493 kabashkircyrillic: 0x04A1,
24496 kacyrillic: 0x043A,
24497 kadescendercyrillic: 0x049B,
24502 kafdageshhebrew: 0xFB3B,
24503 kaffinalarabic: 0xFEDA,
24505 kafinitialarabic: 0xFEDB,
24506 kafmedialarabic: 0xFEDC,
24507 kafrafehebrew: 0xFB4D,
24508 kagujarati: 0x0A95,
24509 kagurmukhi: 0x0A15,
24510 kahiragana: 0x304B,
24511 kahookcyrillic: 0x04C4,
24512 kakatakana: 0x30AB,
24513 kakatakanahalfwidth: 0xFF76,
24515 kappasymbolgreek: 0x03F0,
24516 kapyeounmieumkorean: 0x3171,
24517 kapyeounphieuphkorean: 0x3184,
24518 kapyeounpieupkorean: 0x3178,
24519 kapyeounssangpieupkorean: 0x3179,
24520 karoriisquare: 0x330D,
24521 kashidaautoarabic: 0x0640,
24522 kashidaautonosidebearingarabic: 0x0640,
24523 kasmallkatakana: 0x30F5,
24525 kasraarabic: 0x0650,
24526 kasratanarabic: 0x064D,
24527 kastrokecyrillic: 0x049F,
24528 katahiraprolongmarkhalfwidth: 0xFF70,
24529 kaverticalstrokecyrillic: 0x049D,
24531 kcalsquare: 0x3389,
24535 kcommaaccent: 0x0137,
24537 keharmenian: 0x0584,
24538 kehiragana: 0x3051,
24539 kekatakana: 0x30B1,
24540 kekatakanahalfwidth: 0xFF79,
24541 kenarmenian: 0x056F,
24542 kesmallkatakana: 0x30F6,
24543 kgreenlandic: 0x0138,
24544 khabengali: 0x0996,
24545 khacyrillic: 0x0445,
24547 khagujarati: 0x0A96,
24548 khagurmukhi: 0x0A16,
24549 khaharabic: 0x062E,
24550 khahfinalarabic: 0xFEA6,
24551 khahinitialarabic: 0xFEA7,
24552 khahmedialarabic: 0xFEA8,
24553 kheicoptic: 0x03E7,
24555 khhagurmukhi: 0x0A59,
24556 khieukhacirclekorean: 0x3278,
24557 khieukhaparenkorean: 0x3218,
24558 khieukhcirclekorean: 0x326A,
24559 khieukhkorean: 0x314B,
24560 khieukhparenkorean: 0x320A,
24561 khokhaithai: 0x0E02,
24562 khokhonthai: 0x0E05,
24563 khokhuatthai: 0x0E03,
24564 khokhwaithai: 0x0E04,
24565 khomutthai: 0x0E5B,
24567 khorakhangthai: 0x0E06,
24569 kihiragana: 0x304D,
24570 kikatakana: 0x30AD,
24571 kikatakanahalfwidth: 0xFF77,
24572 kiroguramusquare: 0x3315,
24573 kiromeetorusquare: 0x3316,
24574 kirosquare: 0x3314,
24575 kiyeokacirclekorean: 0x326E,
24576 kiyeokaparenkorean: 0x320E,
24577 kiyeokcirclekorean: 0x3260,
24578 kiyeokkorean: 0x3131,
24579 kiyeokparenkorean: 0x3200,
24580 kiyeoksioskorean: 0x3133,
24581 kjecyrillic: 0x045C,
24582 klinebelow: 0x1E35,
24584 kmcubedsquare: 0x33A6,
24585 kmonospace: 0xFF4B,
24586 kmsquaredsquare: 0x33A2,
24587 kohiragana: 0x3053,
24588 kohmsquare: 0x33C0,
24590 kokatakana: 0x30B3,
24591 kokatakanahalfwidth: 0xFF7A,
24592 kooposquare: 0x331E,
24593 koppacyrillic: 0x0481,
24594 koreanstandardsymbol: 0x327F,
24595 koroniscmb: 0x0343,
24598 ksicyrillic: 0x046F,
24601 kuhiragana: 0x304F,
24602 kukatakana: 0x30AF,
24603 kukatakanahalfwidth: 0xFF78,
24610 lagujarati: 0x0AB2,
24611 lagurmukhi: 0x0A32,
24612 lakkhangyaothai: 0x0E45,
24613 lamaleffinalarabic: 0xFEFC,
24614 lamalefhamzaabovefinalarabic: 0xFEF8,
24615 lamalefhamzaaboveisolatedarabic: 0xFEF7,
24616 lamalefhamzabelowfinalarabic: 0xFEFA,
24617 lamalefhamzabelowisolatedarabic: 0xFEF9,
24618 lamalefisolatedarabic: 0xFEFB,
24619 lamalefmaddaabovefinalarabic: 0xFEF6,
24620 lamalefmaddaaboveisolatedarabic: 0xFEF5,
24623 lambdastroke: 0x019B,
24625 lameddagesh: 0xFB3C,
24626 lameddageshhebrew: 0xFB3C,
24627 lamedhebrew: 0x05DC,
24628 lamfinalarabic: 0xFEDE,
24629 lamhahinitialarabic: 0xFCCA,
24630 laminitialarabic: 0xFEDF,
24631 lamjeeminitialarabic: 0xFCC9,
24632 lamkhahinitialarabic: 0xFCCB,
24633 lamlamhehisolatedarabic: 0xFDF2,
24634 lammedialarabic: 0xFEE0,
24635 lammeemhahinitialarabic: 0xFD88,
24636 lammeeminitialarabic: 0xFCCC,
24637 largecircle: 0x25EF,
24644 lcircumflexbelow: 0x1E3D,
24645 lcommaaccent: 0x013C,
24647 ldotaccent: 0x0140,
24649 ldotbelowmacron: 0x1E39,
24650 leftangleabovecmb: 0x031A,
24651 lefttackbelowcmb: 0x0318,
24654 lessequalorgreater: 0x22DA,
24655 lessmonospace: 0xFF1C,
24656 lessorequivalent: 0x2272,
24657 lessorgreater: 0x2276,
24658 lessoverequal: 0x2266,
24662 lhookretroflex: 0x026D,
24664 liwnarmenian: 0x056C,
24666 ljecyrillic: 0x0459,
24669 llagujarati: 0x0AB3,
24670 llinebelow: 0x1E3B,
24672 llvocalicbengali: 0x09E1,
24673 llvocalicdeva: 0x0961,
24674 llvocalicvowelsignbengali: 0x09E3,
24675 llvocalicvowelsigndeva: 0x0963,
24676 lmiddletilde: 0x026B,
24677 lmonospace: 0xFF4C,
24679 lochulathai: 0x0E2C,
24680 logicaland: 0x2227,
24681 logicalnot: 0x00AC,
24682 logicalnotreversed: 0x2310,
24684 lolingthai: 0x0E25,
24686 lowlinecenterline: 0xFE4E,
24687 lowlinecmb: 0x0332,
24688 lowlinedashed: 0xFE4D,
24696 lvocalicbengali: 0x098C,
24697 lvocalicdeva: 0x090C,
24698 lvocalicvowelsignbengali: 0x09E2,
24699 lvocalicvowelsigndeva: 0x0962,
24704 macronbelowcmb: 0x0331,
24706 macronlowmod: 0x02CD,
24707 macronmonospace: 0xFFE3,
24710 magujarati: 0x0AAE,
24711 magurmukhi: 0x0A2E,
24712 mahapakhhebrew: 0x05A4,
24713 mahapakhlefthebrew: 0x05A4,
24714 mahiragana: 0x307E,
24715 maichattawalowleftthai: 0xF895,
24716 maichattawalowrightthai: 0xF894,
24717 maichattawathai: 0x0E4B,
24718 maichattawaupperleftthai: 0xF893,
24719 maieklowleftthai: 0xF88C,
24720 maieklowrightthai: 0xF88B,
24722 maiekupperleftthai: 0xF88A,
24723 maihanakatleftthai: 0xF884,
24724 maihanakatthai: 0x0E31,
24725 maitaikhuleftthai: 0xF889,
24726 maitaikhuthai: 0x0E47,
24727 maitholowleftthai: 0xF88F,
24728 maitholowrightthai: 0xF88E,
24729 maithothai: 0x0E49,
24730 maithoupperleftthai: 0xF88D,
24731 maitrilowleftthai: 0xF892,
24732 maitrilowrightthai: 0xF891,
24733 maitrithai: 0x0E4A,
24734 maitriupperleftthai: 0xF890,
24735 maiyamokthai: 0x0E46,
24736 makatakana: 0x30DE,
24737 makatakanahalfwidth: 0xFF8F,
24739 mansyonsquare: 0x3347,
24740 maqafhebrew: 0x05BE,
24742 masoracirclehebrew: 0x05AF,
24747 mcubedsquare: 0x33A5,
24748 mdotaccent: 0x1E41,
24750 meemarabic: 0x0645,
24751 meemfinalarabic: 0xFEE2,
24752 meeminitialarabic: 0xFEE3,
24753 meemmedialarabic: 0xFEE4,
24754 meemmeeminitialarabic: 0xFCD1,
24755 meemmeemisolatedarabic: 0xFC48,
24756 meetorusquare: 0x334D,
24757 mehiragana: 0x3081,
24758 meizierasquare: 0x337E,
24759 mekatakana: 0x30E1,
24760 mekatakanahalfwidth: 0xFF92,
24763 memdageshhebrew: 0xFB3E,
24765 menarmenian: 0x0574,
24766 merkhahebrew: 0x05A5,
24767 merkhakefulahebrew: 0x05A6,
24768 merkhakefulalefthebrew: 0x05A6,
24769 merkhalefthebrew: 0x05A5,
24772 middledotkatakanahalfwidth: 0xFF65,
24774 mieumacirclekorean: 0x3272,
24775 mieumaparenkorean: 0x3212,
24776 mieumcirclekorean: 0x3264,
24777 mieumkorean: 0x3141,
24778 mieumpansioskorean: 0x3170,
24779 mieumparenkorean: 0x3204,
24780 mieumpieupkorean: 0x316E,
24781 mieumsioskorean: 0x316F,
24782 mihiragana: 0x307F,
24783 mikatakana: 0x30DF,
24784 mikatakanahalfwidth: 0xFF90,
24786 minusbelowcmb: 0x0320,
24787 minuscircle: 0x2296,
24791 miribaarusquare: 0x334A,
24792 mirisquare: 0x3349,
24793 mlonglegturned: 0x0270,
24795 mmcubedsquare: 0x33A3,
24796 mmonospace: 0xFF4D,
24797 mmsquaredsquare: 0x339F,
24798 mohiragana: 0x3082,
24799 mohmsquare: 0x33C1,
24800 mokatakana: 0x30E2,
24801 mokatakanahalfwidth: 0xFF93,
24804 moverssquare: 0x33A7,
24805 moverssquaredsquare: 0x33A8,
24814 muchgreater: 0x226B,
24819 muhiragana: 0x3080,
24820 mukatakana: 0x30E0,
24821 mukatakanahalfwidth: 0xFF91,
24825 munahhebrew: 0x05A3,
24826 munahlefthebrew: 0x05A3,
24827 musicalnote: 0x266A,
24828 musicalnotedbl: 0x266B,
24829 musicflatsign: 0x266D,
24830 musicsharpsign: 0x266F,
24834 mvmegasquare: 0x33B9,
24836 mwmegasquare: 0x33BF,
24843 nagujarati: 0x0AA8,
24844 nagurmukhi: 0x0A28,
24845 nahiragana: 0x306A,
24846 nakatakana: 0x30CA,
24847 nakatakanahalfwidth: 0xFF85,
24848 napostrophe: 0x0149,
24855 ncircumflexbelow: 0x1E4B,
24856 ncommaaccent: 0x0146,
24857 ndotaccent: 0x1E45,
24859 nehiragana: 0x306D,
24860 nekatakana: 0x30CD,
24861 nekatakanahalfwidth: 0xFF88,
24862 newsheqelsign: 0x20AA,
24864 ngabengali: 0x0999,
24866 ngagujarati: 0x0A99,
24867 ngagurmukhi: 0x0A19,
24868 ngonguthai: 0x0E07,
24871 nhookretroflex: 0x0273,
24872 nieunacirclekorean: 0x326F,
24873 nieunaparenkorean: 0x320F,
24874 nieuncieuckorean: 0x3135,
24875 nieuncirclekorean: 0x3261,
24876 nieunhieuhkorean: 0x3136,
24877 nieunkorean: 0x3134,
24878 nieunpansioskorean: 0x3168,
24879 nieunparenkorean: 0x3201,
24880 nieunsioskorean: 0x3167,
24881 nieuntikeutkorean: 0x3166,
24882 nihiragana: 0x306B,
24883 nikatakana: 0x30CB,
24884 nikatakanahalfwidth: 0xFF86,
24885 nikhahitleftthai: 0xF899,
24886 nikhahitthai: 0x0E4D,
24888 ninearabic: 0x0669,
24889 ninebengali: 0x09EF,
24890 ninecircle: 0x2468,
24891 ninecircleinversesansserif: 0x2792,
24893 ninegujarati: 0x0AEF,
24894 ninegurmukhi: 0x0A6F,
24895 ninehackarabic: 0x0669,
24896 ninehangzhou: 0x3029,
24897 nineideographicparen: 0x3228,
24898 nineinferior: 0x2089,
24899 ninemonospace: 0xFF19,
24900 nineoldstyle: 0xF739,
24902 nineperiod: 0x2490,
24903 ninepersian: 0x06F9,
24905 ninesuperior: 0x2079,
24906 nineteencircle: 0x2472,
24907 nineteenparen: 0x2486,
24908 nineteenperiod: 0x249A,
24911 njecyrillic: 0x045A,
24913 nkatakanahalfwidth: 0xFF9D,
24914 nlegrightlong: 0x019E,
24915 nlinebelow: 0x1E49,
24916 nmonospace: 0xFF4E,
24918 nnabengali: 0x09A3,
24920 nnagujarati: 0x0AA3,
24921 nnagurmukhi: 0x0A23,
24923 nohiragana: 0x306E,
24924 nokatakana: 0x30CE,
24925 nokatakanahalfwidth: 0xFF89,
24926 nonbreakingspace: 0x00A0,
24929 noonarabic: 0x0646,
24930 noonfinalarabic: 0xFEE6,
24931 noonghunnaarabic: 0x06BA,
24932 noonghunnafinalarabic: 0xFB9F,
24933 nooninitialarabic: 0xFEE7,
24934 noonjeeminitialarabic: 0xFCD2,
24935 noonjeemisolatedarabic: 0xFC4B,
24936 noonmedialarabic: 0xFEE8,
24937 noonmeeminitialarabic: 0xFCD5,
24938 noonmeemisolatedarabic: 0xFC4E,
24939 noonnoonfinalarabic: 0xFC8D,
24940 notcontains: 0x220C,
24941 notelement: 0x2209,
24942 notelementof: 0x2209,
24944 notgreater: 0x226F,
24945 notgreaternorequal: 0x2271,
24946 notgreaternorless: 0x2279,
24947 notidentical: 0x2262,
24949 notlessnorequal: 0x2270,
24950 notparallel: 0x2226,
24951 notprecedes: 0x2280,
24953 notsucceeds: 0x2281,
24954 notsuperset: 0x2285,
24955 nowarmenian: 0x0576,
24961 nuhiragana: 0x306C,
24962 nukatakana: 0x30CC,
24963 nukatakanahalfwidth: 0xFF87,
24964 nuktabengali: 0x09BC,
24966 nuktagujarati: 0x0ABC,
24967 nuktagurmukhi: 0x0A3C,
24968 numbersign: 0x0023,
24969 numbersignmonospace: 0xFF03,
24970 numbersignsmall: 0xFE5F,
24971 numeralsigngreek: 0x0374,
24972 numeralsignlowergreek: 0x0375,
24976 nundageshhebrew: 0xFB40,
24980 nyabengali: 0x099E,
24982 nyagujarati: 0x0A9E,
24983 nyagurmukhi: 0x0A1E,
24988 obarredcyrillic: 0x04E9,
24989 obarreddieresiscyrillic: 0x04EB,
24993 ocandradeva: 0x0911,
24994 ocandragujarati: 0x0A91,
24995 ocandravowelsigndeva: 0x0949,
24996 ocandravowelsigngujarati: 0x0AC9,
24999 ocircumflex: 0x00F4,
25000 ocircumflexacute: 0x1ED1,
25001 ocircumflexdotbelow: 0x1ED9,
25002 ocircumflexgrave: 0x1ED3,
25003 ocircumflexhookabove: 0x1ED5,
25004 ocircumflextilde: 0x1ED7,
25010 odieresiscyrillic: 0x04E7,
25018 oharmenian: 0x0585,
25020 ohookabove: 0x1ECF,
25022 ohornacute: 0x1EDB,
25023 ohorndotbelow: 0x1EE3,
25024 ohorngrave: 0x1EDD,
25025 ohornhookabove: 0x1EDF,
25026 ohorntilde: 0x1EE1,
25027 ohungarumlaut: 0x0151,
25029 oinvertedbreve: 0x020F,
25031 okatakanahalfwidth: 0xFF75,
25035 omacronacute: 0x1E53,
25036 omacrongrave: 0x1E51,
25040 omegacyrillic: 0x0461,
25041 omegalatinclosed: 0x0277,
25042 omegaroundcyrillic: 0x047B,
25043 omegatitlocyrillic: 0x047D,
25044 omegatonos: 0x03CE,
25045 omgujarati: 0x0AD0,
25047 omicrontonos: 0x03CC,
25048 omonospace: 0xFF4F,
25051 onebengali: 0x09E7,
25053 onecircleinversesansserif: 0x278A,
25055 onedotenleader: 0x2024,
25058 onegujarati: 0x0AE7,
25059 onegurmukhi: 0x0A67,
25060 onehackarabic: 0x0661,
25062 onehangzhou: 0x3021,
25063 oneideographicparen: 0x3220,
25064 oneinferior: 0x2081,
25065 onemonospace: 0xFF11,
25066 onenumeratorbengali: 0x09F4,
25067 oneoldstyle: 0xF731,
25070 onepersian: 0x06F1,
25071 onequarter: 0x00BC,
25073 onesuperior: 0x00B9,
25077 oogonekmacron: 0x01ED,
25078 oogurmukhi: 0x0A13,
25079 oomatragurmukhi: 0x0A4B,
25082 openbullet: 0x25E6,
25084 ordfeminine: 0x00AA,
25085 ordmasculine: 0x00BA,
25086 orthogonal: 0x221F,
25087 oshortdeva: 0x0912,
25088 oshortvowelsigndeva: 0x094A,
25090 oslashacute: 0x01FF,
25091 osmallhiragana: 0x3049,
25092 osmallkatakana: 0x30A9,
25093 osmallkatakanahalfwidth: 0xFF6B,
25094 ostrokeacute: 0x01FF,
25096 otcyrillic: 0x047F,
25098 otildeacute: 0x1E4D,
25099 otildedieresis: 0x1E4F,
25100 oubopomofo: 0x3121,
25102 overlinecenterline: 0xFE4A,
25103 overlinecmb: 0x0305,
25104 overlinedashed: 0xFE49,
25105 overlinedblwavy: 0xFE4C,
25106 overlinewavy: 0xFE4B,
25108 ovowelsignbengali: 0x09CB,
25109 ovowelsigndeva: 0x094B,
25110 ovowelsigngujarati: 0x0ACB,
25112 paampssquare: 0x3380,
25113 paasentosquare: 0x332B,
25119 pagujarati: 0x0AAA,
25120 pagurmukhi: 0x0A2A,
25121 pahiragana: 0x3071,
25122 paiyannoithai: 0x0E2F,
25123 pakatakana: 0x30D1,
25124 palatalizationcyrilliccmb: 0x0484,
25125 palochkacyrillic: 0x04C0,
25126 pansioskorean: 0x317F,
25130 parenleftaltonearabic: 0xFD3E,
25131 parenleftbt: 0xF8ED,
25132 parenleftex: 0xF8EC,
25133 parenleftinferior: 0x208D,
25134 parenleftmonospace: 0xFF08,
25135 parenleftsmall: 0xFE59,
25136 parenleftsuperior: 0x207D,
25137 parenlefttp: 0xF8EB,
25138 parenleftvertical: 0xFE35,
25139 parenright: 0x0029,
25140 parenrightaltonearabic: 0xFD3F,
25141 parenrightbt: 0xF8F8,
25142 parenrightex: 0xF8F7,
25143 parenrightinferior: 0x208E,
25144 parenrightmonospace: 0xFF09,
25145 parenrightsmall: 0xFE5A,
25146 parenrightsuperior: 0x207E,
25147 parenrighttp: 0xF8F6,
25148 parenrightvertical: 0xFE36,
25149 partialdiff: 0x2202,
25150 paseqhebrew: 0x05C0,
25151 pashtahebrew: 0x0599,
25157 patahhebrew: 0x05B7,
25158 patahnarrowhebrew: 0x05B7,
25159 patahquarterhebrew: 0x05B7,
25160 patahwidehebrew: 0x05B7,
25161 pazerhebrew: 0x05A1,
25164 pdotaccent: 0x1E57,
25166 pecyrillic: 0x043F,
25168 pedageshhebrew: 0xFB44,
25169 peezisquare: 0x333B,
25170 pefinaldageshhebrew: 0xFB43,
25172 peharmenian: 0x057A,
25174 pehfinalarabic: 0xFB57,
25175 pehinitialarabic: 0xFB58,
25176 pehiragana: 0x307A,
25177 pehmedialarabic: 0xFB59,
25178 pekatakana: 0x30DA,
25179 pemiddlehookcyrillic: 0x04A7,
25180 perafehebrew: 0xFB4E,
25182 percentarabic: 0x066A,
25183 percentmonospace: 0xFF05,
25184 percentsmall: 0xFE6A,
25186 periodarmenian: 0x0589,
25187 periodcentered: 0x00B7,
25188 periodhalfwidth: 0xFF61,
25189 periodinferior: 0xF6E7,
25190 periodmonospace: 0xFF0E,
25191 periodsmall: 0xFE52,
25192 periodsuperior: 0xF6E8,
25193 perispomenigreekcmb: 0x0342,
25194 perpendicular: 0x22A5,
25195 perthousand: 0x2030,
25198 phabengali: 0x09AB,
25200 phagujarati: 0x0AAB,
25201 phagurmukhi: 0x0A2B,
25204 phieuphacirclekorean: 0x327A,
25205 phieuphaparenkorean: 0x321A,
25206 phieuphcirclekorean: 0x326C,
25207 phieuphkorean: 0x314D,
25208 phieuphparenkorean: 0x320C,
25210 phinthuthai: 0x0E3A,
25211 phisymbolgreek: 0x03D5,
25213 phophanthai: 0x0E1E,
25214 phophungthai: 0x0E1C,
25215 phosamphaothai: 0x0E20,
25217 pieupacirclekorean: 0x3273,
25218 pieupaparenkorean: 0x3213,
25219 pieupcieuckorean: 0x3176,
25220 pieupcirclekorean: 0x3265,
25221 pieupkiyeokkorean: 0x3172,
25222 pieupkorean: 0x3142,
25223 pieupparenkorean: 0x3205,
25224 pieupsioskiyeokkorean: 0x3174,
25225 pieupsioskorean: 0x3144,
25226 pieupsiostikeutkorean: 0x3175,
25227 pieupthieuthkorean: 0x3177,
25228 pieuptikeutkorean: 0x3173,
25229 pihiragana: 0x3074,
25230 pikatakana: 0x30D4,
25231 pisymbolgreek: 0x03D6,
25232 piwrarmenian: 0x0583,
25234 plusbelowcmb: 0x031F,
25235 pluscircle: 0x2295,
25238 plusmonospace: 0xFF0B,
25240 plussuperior: 0x207A,
25241 pmonospace: 0xFF50,
25243 pohiragana: 0x307D,
25244 pointingindexdownwhite: 0x261F,
25245 pointingindexleftwhite: 0x261C,
25246 pointingindexrightwhite: 0x261E,
25247 pointingindexupwhite: 0x261D,
25248 pokatakana: 0x30DD,
25250 postalmark: 0x3012,
25251 postalmarkface: 0x3020,
25254 prescription: 0x211E,
25256 primereversed: 0x2035,
25258 projective: 0x2305,
25259 prolongedkana: 0x30FC,
25261 propersubset: 0x2282,
25262 propersuperset: 0x2283,
25263 proportion: 0x2237,
25264 proportional: 0x221D,
25266 psicyrillic: 0x0471,
25267 psilipneumatacyrilliccmb: 0x0486,
25269 puhiragana: 0x3077,
25270 pukatakana: 0x30D7,
25275 qadmahebrew: 0x05A8,
25277 qaffinalarabic: 0xFED6,
25278 qafinitialarabic: 0xFED7,
25279 qafmedialarabic: 0xFED8,
25288 qamatshebrew: 0x05B8,
25289 qamatsnarrowhebrew: 0x05B8,
25290 qamatsqatanhebrew: 0x05B8,
25291 qamatsqatannarrowhebrew: 0x05B8,
25292 qamatsqatanquarterhebrew: 0x05B8,
25293 qamatsqatanwidehebrew: 0x05B8,
25294 qamatsquarterhebrew: 0x05B8,
25295 qamatswidehebrew: 0x05B8,
25296 qarneyparahebrew: 0x059F,
25300 qmonospace: 0xFF51,
25303 qofdageshhebrew: 0xFB47,
25306 quarternote: 0x2669,
25311 qubutshebrew: 0x05BB,
25312 qubutsnarrowhebrew: 0x05BB,
25313 qubutsquarterhebrew: 0x05BB,
25314 qubutswidehebrew: 0x05BB,
25316 questionarabic: 0x061F,
25317 questionarmenian: 0x055E,
25318 questiondown: 0x00BF,
25319 questiondownsmall: 0xF7BF,
25320 questiongreek: 0x037E,
25321 questionmonospace: 0xFF1F,
25322 questionsmall: 0xF73F,
25324 quotedblbase: 0x201E,
25325 quotedblleft: 0x201C,
25326 quotedblmonospace: 0xFF02,
25327 quotedblprime: 0x301E,
25328 quotedblprimereversed: 0x301D,
25329 quotedblright: 0x201D,
25331 quoteleftreversed: 0x201B,
25332 quotereversed: 0x201B,
25333 quoteright: 0x2019,
25334 quoterightn: 0x0149,
25335 quotesinglbase: 0x201A,
25336 quotesingle: 0x0027,
25337 quotesinglemonospace: 0xFF07,
25339 raarmenian: 0x057C,
25345 radoverssquare: 0x33AE,
25346 radoverssquaredsquare: 0x33AF,
25349 rafehebrew: 0x05BF,
25350 ragujarati: 0x0AB0,
25351 ragurmukhi: 0x0A30,
25352 rahiragana: 0x3089,
25353 rakatakana: 0x30E9,
25354 rakatakanahalfwidth: 0xFF97,
25355 ralowerdiagonalbengali: 0x09F1,
25356 ramiddlediagonalbengali: 0x09F0,
25363 rcommaaccent: 0x0157,
25365 rdotaccent: 0x1E59,
25367 rdotbelowmacron: 0x1E5D,
25368 referencemark: 0x203B,
25369 reflexsubset: 0x2286,
25370 reflexsuperset: 0x2287,
25371 registered: 0x00AE,
25372 registersans: 0xF8E8,
25373 registerserif: 0xF6DA,
25375 reharmenian: 0x0580,
25376 rehfinalarabic: 0xFEAE,
25377 rehiragana: 0x308C,
25378 rekatakana: 0x30EC,
25379 rekatakanahalfwidth: 0xFF9A,
25381 reshdageshhebrew: 0xFB48,
25382 reshhebrew: 0x05E8,
25383 reversedtilde: 0x223D,
25384 reviahebrew: 0x0597,
25385 reviamugrashhebrew: 0x0597,
25386 revlogicalnot: 0x2310,
25388 rfishhookreversed: 0x027F,
25389 rhabengali: 0x09DD,
25393 rhookturned: 0x027B,
25394 rhookturnedsuperior: 0x02B5,
25395 rhosymbolgreek: 0x03F1,
25396 rhotichookmod: 0x02DE,
25397 rieulacirclekorean: 0x3271,
25398 rieulaparenkorean: 0x3211,
25399 rieulcirclekorean: 0x3263,
25400 rieulhieuhkorean: 0x3140,
25401 rieulkiyeokkorean: 0x313A,
25402 rieulkiyeoksioskorean: 0x3169,
25403 rieulkorean: 0x3139,
25404 rieulmieumkorean: 0x313B,
25405 rieulpansioskorean: 0x316C,
25406 rieulparenkorean: 0x3203,
25407 rieulphieuphkorean: 0x313F,
25408 rieulpieupkorean: 0x313C,
25409 rieulpieupsioskorean: 0x316B,
25410 rieulsioskorean: 0x313D,
25411 rieulthieuthkorean: 0x313E,
25412 rieultikeutkorean: 0x316A,
25413 rieulyeorinhieuhkorean: 0x316D,
25414 rightangle: 0x221F,
25415 righttackbelowcmb: 0x0319,
25416 righttriangle: 0x22BF,
25417 rihiragana: 0x308A,
25418 rikatakana: 0x30EA,
25419 rikatakanahalfwidth: 0xFF98,
25421 ringbelowcmb: 0x0325,
25423 ringhalfleft: 0x02BF,
25424 ringhalfleftarmenian: 0x0559,
25425 ringhalfleftbelowcmb: 0x031C,
25426 ringhalfleftcentered: 0x02D3,
25427 ringhalfright: 0x02BE,
25428 ringhalfrightbelowcmb: 0x0339,
25429 ringhalfrightcentered: 0x02D2,
25430 rinvertedbreve: 0x0213,
25431 rittorusquare: 0x3351,
25432 rlinebelow: 0x1E5F,
25434 rlonglegturned: 0x027A,
25435 rmonospace: 0xFF52,
25436 rohiragana: 0x308D,
25437 rokatakana: 0x30ED,
25438 rokatakanahalfwidth: 0xFF9B,
25441 rrabengali: 0x09DC,
25443 rragurmukhi: 0x0A5C,
25444 rreharabic: 0x0691,
25445 rrehfinalarabic: 0xFB8D,
25446 rrvocalicbengali: 0x09E0,
25447 rrvocalicdeva: 0x0960,
25448 rrvocalicgujarati: 0x0AE0,
25449 rrvocalicvowelsignbengali: 0x09C4,
25450 rrvocalicvowelsigndeva: 0x0944,
25451 rrvocalicvowelsigngujarati: 0x0AC4,
25455 rturnedsuperior: 0x02B4,
25456 ruhiragana: 0x308B,
25457 rukatakana: 0x30EB,
25458 rukatakanahalfwidth: 0xFF99,
25459 rupeemarkbengali: 0x09F2,
25460 rupeesignbengali: 0x09F3,
25463 rvocalicbengali: 0x098B,
25464 rvocalicdeva: 0x090B,
25465 rvocalicgujarati: 0x0A8B,
25466 rvocalicvowelsignbengali: 0x09C3,
25467 rvocalicvowelsigndeva: 0x0943,
25468 rvocalicvowelsigngujarati: 0x0AC3,
25472 sacutedotaccent: 0x1E65,
25475 sadfinalarabic: 0xFEBA,
25476 sadinitialarabic: 0xFEBB,
25477 sadmedialarabic: 0xFEBC,
25478 sagujarati: 0x0AB8,
25479 sagurmukhi: 0x0A38,
25480 sahiragana: 0x3055,
25481 sakatakana: 0x30B5,
25482 sakatakanahalfwidth: 0xFF7B,
25483 sallallahoualayhewasallamarabic: 0xFDFA,
25485 samekhdagesh: 0xFB41,
25486 samekhdageshhebrew: 0xFB41,
25487 samekhhebrew: 0x05E1,
25488 saraaathai: 0x0E32,
25489 saraaethai: 0x0E41,
25490 saraaimaimalaithai: 0x0E44,
25491 saraaimaimuanthai: 0x0E43,
25492 saraamthai: 0x0E33,
25495 saraiileftthai: 0xF886,
25496 saraiithai: 0x0E35,
25497 saraileftthai: 0xF885,
25500 saraueeleftthai: 0xF888,
25501 saraueethai: 0x0E37,
25502 saraueleftthai: 0xF887,
25503 sarauethai: 0x0E36,
25505 sarauuthai: 0x0E39,
25508 scarondotaccent: 0x1E67,
25511 schwacyrillic: 0x04D9,
25512 schwadieresiscyrillic: 0x04DB,
25515 scircumflex: 0x015D,
25516 scommaaccent: 0x0219,
25517 sdotaccent: 0x1E61,
25519 sdotbelowdotaccent: 0x1E69,
25520 seagullbelowcmb: 0x033C,
25522 secondtonechinese: 0x02CA,
25524 seenarabic: 0x0633,
25525 seenfinalarabic: 0xFEB2,
25526 seeninitialarabic: 0xFEB3,
25527 seenmedialarabic: 0xFEB4,
25532 segolhebrew: 0x05B6,
25533 segolnarrowhebrew: 0x05B6,
25534 segolquarterhebrew: 0x05B6,
25535 segoltahebrew: 0x0592,
25536 segolwidehebrew: 0x05B6,
25537 seharmenian: 0x057D,
25538 sehiragana: 0x305B,
25539 sekatakana: 0x30BB,
25540 sekatakanahalfwidth: 0xFF7E,
25542 semicolonarabic: 0x061B,
25543 semicolonmonospace: 0xFF1B,
25544 semicolonsmall: 0xFE54,
25545 semivoicedmarkkana: 0x309C,
25546 semivoicedmarkkanahalfwidth: 0xFF9F,
25547 sentisquare: 0x3322,
25548 sentosquare: 0x3323,
25550 sevenarabic: 0x0667,
25551 sevenbengali: 0x09ED,
25552 sevencircle: 0x2466,
25553 sevencircleinversesansserif: 0x2790,
25555 seveneighths: 0x215E,
25556 sevengujarati: 0x0AED,
25557 sevengurmukhi: 0x0A6D,
25558 sevenhackarabic: 0x0667,
25559 sevenhangzhou: 0x3027,
25560 sevenideographicparen: 0x3226,
25561 seveninferior: 0x2087,
25562 sevenmonospace: 0xFF17,
25563 sevenoldstyle: 0xF737,
25564 sevenparen: 0x247A,
25565 sevenperiod: 0x248E,
25566 sevenpersian: 0x06F7,
25567 sevenroman: 0x2176,
25568 sevensuperior: 0x2077,
25569 seventeencircle: 0x2470,
25570 seventeenparen: 0x2484,
25571 seventeenperiod: 0x2498,
25574 shaarmenian: 0x0577,
25575 shabengali: 0x09B6,
25576 shacyrillic: 0x0448,
25577 shaddaarabic: 0x0651,
25578 shaddadammaarabic: 0xFC61,
25579 shaddadammatanarabic: 0xFC5E,
25580 shaddafathaarabic: 0xFC60,
25581 shaddakasraarabic: 0xFC62,
25582 shaddakasratanarabic: 0xFC5F,
25585 shadelight: 0x2591,
25586 shademedium: 0x2592,
25588 shagujarati: 0x0AB6,
25589 shagurmukhi: 0x0A36,
25590 shalshelethebrew: 0x0593,
25591 shbopomofo: 0x3115,
25592 shchacyrillic: 0x0449,
25593 sheenarabic: 0x0634,
25594 sheenfinalarabic: 0xFEB6,
25595 sheeninitialarabic: 0xFEB7,
25596 sheenmedialarabic: 0xFEB8,
25597 sheicoptic: 0x03E3,
25599 sheqelhebrew: 0x20AA,
25605 shevahebrew: 0x05B0,
25606 shevanarrowhebrew: 0x05B0,
25607 shevaquarterhebrew: 0x05B0,
25608 shevawidehebrew: 0x05B0,
25609 shhacyrillic: 0x04BB,
25610 shimacoptic: 0x03ED,
25612 shindagesh: 0xFB49,
25613 shindageshhebrew: 0xFB49,
25614 shindageshshindot: 0xFB2C,
25615 shindageshshindothebrew: 0xFB2C,
25616 shindageshsindot: 0xFB2D,
25617 shindageshsindothebrew: 0xFB2D,
25618 shindothebrew: 0x05C1,
25619 shinhebrew: 0x05E9,
25620 shinshindot: 0xFB2A,
25621 shinshindothebrew: 0xFB2A,
25622 shinsindot: 0xFB2B,
25623 shinsindothebrew: 0xFB2B,
25627 sigmafinal: 0x03C2,
25628 sigmalunatesymbolgreek: 0x03F2,
25629 sihiragana: 0x3057,
25630 sikatakana: 0x30B7,
25631 sikatakanahalfwidth: 0xFF7C,
25632 siluqhebrew: 0x05BD,
25633 siluqlefthebrew: 0x05BD,
25635 sindothebrew: 0x05C2,
25636 siosacirclekorean: 0x3274,
25637 siosaparenkorean: 0x3214,
25638 sioscieuckorean: 0x317E,
25639 sioscirclekorean: 0x3266,
25640 sioskiyeokkorean: 0x317A,
25641 sioskorean: 0x3145,
25642 siosnieunkorean: 0x317B,
25643 siosparenkorean: 0x3206,
25644 siospieupkorean: 0x317D,
25645 siostikeutkorean: 0x317C,
25648 sixbengali: 0x09EC,
25650 sixcircleinversesansserif: 0x278F,
25652 sixgujarati: 0x0AEC,
25653 sixgurmukhi: 0x0A6C,
25654 sixhackarabic: 0x0666,
25655 sixhangzhou: 0x3026,
25656 sixideographicparen: 0x3225,
25657 sixinferior: 0x2086,
25658 sixmonospace: 0xFF16,
25659 sixoldstyle: 0xF736,
25662 sixpersian: 0x06F6,
25664 sixsuperior: 0x2076,
25665 sixteencircle: 0x246F,
25666 sixteencurrencydenominatorbengali: 0x09F9,
25667 sixteenparen: 0x2483,
25668 sixteenperiod: 0x2497,
25671 slashmonospace: 0xFF0F,
25673 slongdotaccent: 0x1E9B,
25675 smonospace: 0xFF53,
25676 sofpasuqhebrew: 0x05C3,
25677 softhyphen: 0x00AD,
25678 softsigncyrillic: 0x044C,
25679 sohiragana: 0x305D,
25680 sokatakana: 0x30BD,
25681 sokatakanahalfwidth: 0xFF7F,
25682 soliduslongoverlaycmb: 0x0338,
25683 solidusshortoverlaycmb: 0x0337,
25684 sorusithai: 0x0E29,
25685 sosalathai: 0x0E28,
25689 spacehackarabic: 0x0020,
25691 spadesuitblack: 0x2660,
25692 spadesuitwhite: 0x2664,
25694 squarebelowcmb: 0x033B,
25697 squarediagonalcrosshatchfill: 0x25A9,
25698 squarehorizontalfill: 0x25A4,
25701 squarekmcapital: 0x33CE,
25707 squaremsquared: 0x33A1,
25708 squareorthogonalcrosshatchfill: 0x25A6,
25709 squareupperlefttolowerrightfill: 0x25A7,
25710 squareupperrighttolowerleftfill: 0x25A8,
25711 squareverticalfill: 0x25A5,
25712 squarewhitewithsmallblack: 0x25A3,
25714 ssabengali: 0x09B7,
25716 ssagujarati: 0x0AB7,
25717 ssangcieuckorean: 0x3149,
25718 ssanghieuhkorean: 0x3185,
25719 ssangieungkorean: 0x3180,
25720 ssangkiyeokkorean: 0x3132,
25721 ssangnieunkorean: 0x3165,
25722 ssangpieupkorean: 0x3143,
25723 ssangsioskorean: 0x3146,
25724 ssangtikeutkorean: 0x3138,
25727 sterlingmonospace: 0xFFE1,
25728 strokelongoverlaycmb: 0x0336,
25729 strokeshortoverlaycmb: 0x0335,
25731 subsetnotequal: 0x228A,
25732 subsetorequal: 0x2286,
25735 suhiragana: 0x3059,
25736 sukatakana: 0x30B9,
25737 sukatakanahalfwidth: 0xFF7D,
25738 sukunarabic: 0x0652,
25742 supersetnotequal: 0x228B,
25743 supersetorequal: 0x2287,
25745 syouwaerasquare: 0x337C,
25751 tagujarati: 0x0AA4,
25752 tagurmukhi: 0x0A24,
25754 tahfinalarabic: 0xFEC2,
25755 tahinitialarabic: 0xFEC3,
25756 tahiragana: 0x305F,
25757 tahmedialarabic: 0xFEC4,
25758 taisyouerasquare: 0x337D,
25759 takatakana: 0x30BF,
25760 takatakanahalfwidth: 0xFF80,
25761 tatweelarabic: 0x0640,
25766 tavdageshhebrew: 0xFB4A,
25773 tcheharabic: 0x0686,
25774 tchehfinalarabic: 0xFB7B,
25775 tchehinitialarabic: 0xFB7C,
25776 tchehmedialarabic: 0xFB7D,
25778 tcircumflexbelow: 0x1E71,
25779 tcommaaccent: 0x0163,
25781 tdotaccent: 0x1E6B,
25783 tecyrillic: 0x0442,
25784 tedescendercyrillic: 0x04AD,
25786 tehfinalarabic: 0xFE96,
25787 tehhahinitialarabic: 0xFCA2,
25788 tehhahisolatedarabic: 0xFC0C,
25789 tehinitialarabic: 0xFE97,
25790 tehiragana: 0x3066,
25791 tehjeeminitialarabic: 0xFCA1,
25792 tehjeemisolatedarabic: 0xFC0B,
25793 tehmarbutaarabic: 0x0629,
25794 tehmarbutafinalarabic: 0xFE94,
25795 tehmedialarabic: 0xFE98,
25796 tehmeeminitialarabic: 0xFCA4,
25797 tehmeemisolatedarabic: 0xFC0E,
25798 tehnoonfinalarabic: 0xFC73,
25799 tekatakana: 0x30C6,
25800 tekatakanahalfwidth: 0xFF83,
25802 telephoneblack: 0x260E,
25803 telishagedolahebrew: 0x05A0,
25804 telishaqetanahebrew: 0x05A9,
25806 tenideographicparen: 0x3229,
25813 tetdageshhebrew: 0xFB38,
25815 tetsecyrillic: 0x04B5,
25816 tevirhebrew: 0x059B,
25817 tevirlefthebrew: 0x059B,
25818 thabengali: 0x09A5,
25820 thagujarati: 0x0AA5,
25821 thagurmukhi: 0x0A25,
25822 thalarabic: 0x0630,
25823 thalfinalarabic: 0xFEAC,
25824 thanthakhatlowleftthai: 0xF898,
25825 thanthakhatlowrightthai: 0xF897,
25826 thanthakhatthai: 0x0E4C,
25827 thanthakhatupperleftthai: 0xF896,
25828 theharabic: 0x062B,
25829 thehfinalarabic: 0xFE9A,
25830 thehinitialarabic: 0xFE9B,
25831 thehmedialarabic: 0xFE9C,
25832 thereexists: 0x2203,
25836 thetasymbolgreek: 0x03D1,
25837 thieuthacirclekorean: 0x3279,
25838 thieuthaparenkorean: 0x3219,
25839 thieuthcirclekorean: 0x326B,
25840 thieuthkorean: 0x314C,
25841 thieuthparenkorean: 0x320B,
25842 thirteencircle: 0x246C,
25843 thirteenparen: 0x2480,
25844 thirteenperiod: 0x2494,
25845 thonangmonthothai: 0x0E11,
25847 thophuthaothai: 0x0E12,
25849 thothahanthai: 0x0E17,
25850 thothanthai: 0x0E10,
25851 thothongthai: 0x0E18,
25852 thothungthai: 0x0E16,
25853 thousandcyrillic: 0x0482,
25854 thousandsseparatorarabic: 0x066C,
25855 thousandsseparatorpersian: 0x066C,
25857 threearabic: 0x0663,
25858 threebengali: 0x09E9,
25859 threecircle: 0x2462,
25860 threecircleinversesansserif: 0x278C,
25862 threeeighths: 0x215C,
25863 threegujarati: 0x0AE9,
25864 threegurmukhi: 0x0A69,
25865 threehackarabic: 0x0663,
25866 threehangzhou: 0x3023,
25867 threeideographicparen: 0x3222,
25868 threeinferior: 0x2083,
25869 threemonospace: 0xFF13,
25870 threenumeratorbengali: 0x09F6,
25871 threeoldstyle: 0xF733,
25872 threeparen: 0x2476,
25873 threeperiod: 0x248A,
25874 threepersian: 0x06F3,
25875 threequarters: 0x00BE,
25876 threequartersemdash: 0xF6DE,
25877 threeroman: 0x2172,
25878 threesuperior: 0x00B3,
25881 tihiragana: 0x3061,
25882 tikatakana: 0x30C1,
25883 tikatakanahalfwidth: 0xFF81,
25884 tikeutacirclekorean: 0x3270,
25885 tikeutaparenkorean: 0x3210,
25886 tikeutcirclekorean: 0x3262,
25887 tikeutkorean: 0x3137,
25888 tikeutparenkorean: 0x3202,
25890 tildebelowcmb: 0x0330,
25893 tildedoublecmb: 0x0360,
25894 tildeoperator: 0x223C,
25895 tildeoverlaycmb: 0x0334,
25896 tildeverticalcmb: 0x033E,
25897 timescircle: 0x2297,
25898 tipehahebrew: 0x0596,
25899 tipehalefthebrew: 0x0596,
25900 tippigurmukhi: 0x0A70,
25901 titlocyrilliccmb: 0x0483,
25902 tiwnarmenian: 0x057F,
25903 tlinebelow: 0x1E6F,
25904 tmonospace: 0xFF54,
25905 toarmenian: 0x0569,
25906 tohiragana: 0x3068,
25907 tokatakana: 0x30C8,
25908 tokatakanahalfwidth: 0xFF84,
25909 tonebarextrahighmod: 0x02E5,
25910 tonebarextralowmod: 0x02E9,
25911 tonebarhighmod: 0x02E6,
25912 tonebarlowmod: 0x02E8,
25913 tonebarmidmod: 0x02E7,
25919 topatakthai: 0x0E0F,
25920 tortoiseshellbracketleft: 0x3014,
25921 tortoiseshellbracketleftsmall: 0xFE5D,
25922 tortoiseshellbracketleftvertical: 0xFE39,
25923 tortoiseshellbracketright: 0x3015,
25924 tortoiseshellbracketrightsmall: 0xFE5E,
25925 tortoiseshellbracketrightvertical: 0xFE3A,
25927 tpalatalhook: 0x01AB,
25930 trademarksans: 0xF8EA,
25931 trademarkserif: 0xF6DB,
25932 tretroflexhook: 0x0288,
25939 tsadidagesh: 0xFB46,
25940 tsadidageshhebrew: 0xFB46,
25941 tsadihebrew: 0x05E6,
25942 tsecyrillic: 0x0446,
25947 tserehebrew: 0x05B5,
25948 tserenarrowhebrew: 0x05B5,
25949 tserequarterhebrew: 0x05B5,
25950 tserewidehebrew: 0x05B5,
25951 tshecyrillic: 0x045B,
25953 ttabengali: 0x099F,
25955 ttagujarati: 0x0A9F,
25956 ttagurmukhi: 0x0A1F,
25957 tteharabic: 0x0679,
25958 ttehfinalarabic: 0xFB67,
25959 ttehinitialarabic: 0xFB68,
25960 ttehmedialarabic: 0xFB69,
25961 tthabengali: 0x09A0,
25963 tthagujarati: 0x0AA0,
25964 tthagurmukhi: 0x0A20,
25966 tuhiragana: 0x3064,
25967 tukatakana: 0x30C4,
25968 tukatakanahalfwidth: 0xFF82,
25969 tusmallhiragana: 0x3063,
25970 tusmallkatakana: 0x30C3,
25971 tusmallkatakanahalfwidth: 0xFF6F,
25972 twelvecircle: 0x246B,
25973 twelveparen: 0x247F,
25974 twelveperiod: 0x2493,
25975 twelveroman: 0x217B,
25976 twentycircle: 0x2473,
25977 twentyhangzhou: 0x5344,
25978 twentyparen: 0x2487,
25979 twentyperiod: 0x249B,
25982 twobengali: 0x09E8,
25984 twocircleinversesansserif: 0x278B,
25986 twodotenleader: 0x2025,
25987 twodotleader: 0x2025,
25988 twodotleadervertical: 0xFE30,
25989 twogujarati: 0x0AE8,
25990 twogurmukhi: 0x0A68,
25991 twohackarabic: 0x0662,
25992 twohangzhou: 0x3022,
25993 twoideographicparen: 0x3221,
25994 twoinferior: 0x2082,
25995 twomonospace: 0xFF12,
25996 twonumeratorbengali: 0x09F5,
25997 twooldstyle: 0xF732,
26000 twopersian: 0x06F2,
26003 twosuperior: 0x00B2,
26014 ucircumflex: 0x00FB,
26015 ucircumflexbelow: 0x1E77,
26017 udattadeva: 0x0951,
26022 udieresisacute: 0x01D8,
26023 udieresisbelow: 0x1E73,
26024 udieresiscaron: 0x01DA,
26025 udieresiscyrillic: 0x04F1,
26026 udieresisgrave: 0x01DC,
26027 udieresismacron: 0x01D6,
26033 uhookabove: 0x1EE7,
26035 uhornacute: 0x1EE9,
26036 uhorndotbelow: 0x1EF1,
26037 uhorngrave: 0x1EEB,
26038 uhornhookabove: 0x1EED,
26039 uhorntilde: 0x1EEF,
26040 uhungarumlaut: 0x0171,
26041 uhungarumlautcyrillic: 0x04F3,
26042 uinvertedbreve: 0x0217,
26044 ukatakanahalfwidth: 0xFF73,
26045 ukcyrillic: 0x0479,
26048 umacroncyrillic: 0x04EF,
26049 umacrondieresis: 0x1E7B,
26050 umatragurmukhi: 0x0A41,
26051 umonospace: 0xFF55,
26052 underscore: 0x005F,
26053 underscoredbl: 0x2017,
26054 underscoremonospace: 0xFF3F,
26055 underscorevertical: 0xFE33,
26056 underscorewavy: 0xFE4F,
26062 upperdothebrew: 0x05C4,
26064 upsilondieresis: 0x03CB,
26065 upsilondieresistonos: 0x03B0,
26066 upsilonlatin: 0x028A,
26067 upsilontonos: 0x03CD,
26068 uptackbelowcmb: 0x031D,
26070 uragurmukhi: 0x0A73,
26072 ushortcyrillic: 0x045E,
26073 usmallhiragana: 0x3045,
26074 usmallkatakana: 0x30A5,
26075 usmallkatakanahalfwidth: 0xFF69,
26076 ustraightcyrillic: 0x04AF,
26077 ustraightstrokecyrillic: 0x04B1,
26079 utildeacute: 0x1E79,
26080 utildebelow: 0x1E75,
26083 uugujarati: 0x0A8A,
26084 uugurmukhi: 0x0A0A,
26085 uumatragurmukhi: 0x0A42,
26086 uuvowelsignbengali: 0x09C2,
26087 uuvowelsigndeva: 0x0942,
26088 uuvowelsigngujarati: 0x0AC2,
26089 uvowelsignbengali: 0x09C1,
26090 uvowelsigndeva: 0x0941,
26091 uvowelsigngujarati: 0x0AC1,
26094 vagujarati: 0x0AB5,
26095 vagurmukhi: 0x0A35,
26096 vakatakana: 0x30F7,
26099 vavdagesh65: 0xFB35,
26100 vavdageshhebrew: 0xFB35,
26103 vavholamhebrew: 0xFB4B,
26104 vavvavhebrew: 0x05F0,
26105 vavyodhebrew: 0x05F1,
26108 vecyrillic: 0x0432,
26110 vehfinalarabic: 0xFB6B,
26111 vehinitialarabic: 0xFB6C,
26112 vehmedialarabic: 0xFB6D,
26113 vekatakana: 0x30F9,
26115 verticalbar: 0x007C,
26116 verticallineabovecmb: 0x030D,
26117 verticallinebelowcmb: 0x0329,
26118 verticallinelowmod: 0x02CC,
26119 verticallinemod: 0x02C8,
26120 vewarmenian: 0x057E,
26122 vikatakana: 0x30F8,
26123 viramabengali: 0x09CD,
26124 viramadeva: 0x094D,
26125 viramagujarati: 0x0ACD,
26126 visargabengali: 0x0983,
26127 visargadeva: 0x0903,
26128 visargagujarati: 0x0A83,
26129 vmonospace: 0xFF56,
26130 voarmenian: 0x0578,
26131 voicediterationhiragana: 0x309E,
26132 voicediterationkatakana: 0x30FE,
26133 voicedmarkkana: 0x309B,
26134 voicedmarkkanahalfwidth: 0xFF9E,
26135 vokatakana: 0x30FA,
26139 vuhiragana: 0x3094,
26140 vukatakana: 0x30F4,
26144 wahiragana: 0x308F,
26145 wakatakana: 0x30EF,
26146 wakatakanahalfwidth: 0xFF9C,
26148 wasmallhiragana: 0x308E,
26149 wasmallkatakana: 0x30EE,
26150 wattosquare: 0x3357,
26152 wavyunderscorevertical: 0xFE34,
26154 wawfinalarabic: 0xFEEE,
26155 wawhamzaabovearabic: 0x0624,
26156 wawhamzaabovefinalarabic: 0xFE86,
26159 wcircumflex: 0x0175,
26161 wdotaccent: 0x1E87,
26163 wehiragana: 0x3091,
26164 weierstrass: 0x2118,
26165 wekatakana: 0x30F1,
26169 whitebullet: 0x25E6,
26170 whitecircle: 0x25CB,
26171 whitecircleinverse: 0x25D9,
26172 whitecornerbracketleft: 0x300E,
26173 whitecornerbracketleftvertical: 0xFE43,
26174 whitecornerbracketright: 0x300F,
26175 whitecornerbracketrightvertical: 0xFE44,
26176 whitediamond: 0x25C7,
26177 whitediamondcontainingblacksmalldiamond: 0x25C8,
26178 whitedownpointingsmalltriangle: 0x25BF,
26179 whitedownpointingtriangle: 0x25BD,
26180 whiteleftpointingsmalltriangle: 0x25C3,
26181 whiteleftpointingtriangle: 0x25C1,
26182 whitelenticularbracketleft: 0x3016,
26183 whitelenticularbracketright: 0x3017,
26184 whiterightpointingsmalltriangle: 0x25B9,
26185 whiterightpointingtriangle: 0x25B7,
26186 whitesmallsquare: 0x25AB,
26187 whitesmilingface: 0x263A,
26188 whitesquare: 0x25A1,
26190 whitetelephone: 0x260F,
26191 whitetortoiseshellbracketleft: 0x3018,
26192 whitetortoiseshellbracketright: 0x3019,
26193 whiteuppointingsmalltriangle: 0x25B5,
26194 whiteuppointingtriangle: 0x25B3,
26195 wihiragana: 0x3090,
26196 wikatakana: 0x30F0,
26198 wmonospace: 0xFF57,
26199 wohiragana: 0x3092,
26200 wokatakana: 0x30F2,
26201 wokatakanahalfwidth: 0xFF66,
26203 wonmonospace: 0xFFE6,
26204 wowaenthai: 0x0E27,
26215 xdotaccent: 0x1E8B,
26216 xeharmenian: 0x056D,
26218 xmonospace: 0xFF58,
26222 yaadosquare: 0x334E,
26227 yagujarati: 0x0AAF,
26228 yagurmukhi: 0x0A2F,
26229 yahiragana: 0x3084,
26230 yakatakana: 0x30E4,
26231 yakatakanahalfwidth: 0xFF94,
26233 yamakkanthai: 0x0E4E,
26234 yasmallhiragana: 0x3083,
26235 yasmallkatakana: 0x30E3,
26236 yasmallkatakanahalfwidth: 0xFF6C,
26237 yatcyrillic: 0x0463,
26239 ycircumflex: 0x0177,
26241 ydotaccent: 0x1E8F,
26244 yehbarreearabic: 0x06D2,
26245 yehbarreefinalarabic: 0xFBAF,
26246 yehfinalarabic: 0xFEF2,
26247 yehhamzaabovearabic: 0x0626,
26248 yehhamzaabovefinalarabic: 0xFE8A,
26249 yehhamzaaboveinitialarabic: 0xFE8B,
26250 yehhamzaabovemedialarabic: 0xFE8C,
26251 yehinitialarabic: 0xFEF3,
26252 yehmedialarabic: 0xFEF4,
26253 yehmeeminitialarabic: 0xFCDD,
26254 yehmeemisolatedarabic: 0xFC58,
26255 yehnoonfinalarabic: 0xFC94,
26256 yehthreedotsbelowarabic: 0x06D1,
26259 yenmonospace: 0xFFE5,
26261 yeorinhieuhkorean: 0x3186,
26262 yerahbenyomohebrew: 0x05AA,
26263 yerahbenyomolefthebrew: 0x05AA,
26264 yericyrillic: 0x044B,
26265 yerudieresiscyrillic: 0x04F9,
26266 yesieungkorean: 0x3181,
26267 yesieungpansioskorean: 0x3183,
26268 yesieungsioskorean: 0x3182,
26269 yetivhebrew: 0x059A,
26272 yhookabove: 0x1EF7,
26273 yiarmenian: 0x0575,
26274 yicyrillic: 0x0457,
26277 yiwnarmenian: 0x0582,
26278 ymonospace: 0xFF59,
26281 yoddageshhebrew: 0xFB39,
26283 yodyodhebrew: 0x05F2,
26284 yodyodpatahhebrew: 0xFB1F,
26285 yohiragana: 0x3088,
26287 yokatakana: 0x30E8,
26288 yokatakanahalfwidth: 0xFF96,
26290 yosmallhiragana: 0x3087,
26291 yosmallkatakana: 0x30E7,
26292 yosmallkatakanahalfwidth: 0xFF6E,
26294 yoyaekorean: 0x3188,
26295 yoyakorean: 0x3187,
26297 yoyingthai: 0x0E0D,
26299 ypogegrammeni: 0x037A,
26300 ypogegrammenigreekcmb: 0x0345,
26306 yuhiragana: 0x3086,
26308 yukatakana: 0x30E6,
26309 yukatakanahalfwidth: 0xFF95,
26311 yusbigcyrillic: 0x046B,
26312 yusbigiotifiedcyrillic: 0x046D,
26313 yuslittlecyrillic: 0x0467,
26314 yuslittleiotifiedcyrillic: 0x0469,
26315 yusmallhiragana: 0x3085,
26316 yusmallkatakana: 0x30E5,
26317 yusmallkatakanahalfwidth: 0xFF6D,
26318 yuyekorean: 0x318B,
26319 yuyeokorean: 0x318A,
26320 yyabengali: 0x09DF,
26323 zaarmenian: 0x0566,
26326 zagurmukhi: 0x0A5B,
26328 zahfinalarabic: 0xFEC6,
26329 zahinitialarabic: 0xFEC7,
26330 zahiragana: 0x3056,
26331 zahmedialarabic: 0xFEC8,
26332 zainarabic: 0x0632,
26333 zainfinalarabic: 0xFEB0,
26334 zakatakana: 0x30B6,
26335 zaqefgadolhebrew: 0x0595,
26336 zaqefqatanhebrew: 0x0594,
26337 zarqahebrew: 0x0598,
26339 zayindagesh: 0xFB36,
26340 zayindageshhebrew: 0xFB36,
26341 zayinhebrew: 0x05D6,
26345 zcircumflex: 0x1E91,
26348 zdotaccent: 0x017C,
26350 zecyrillic: 0x0437,
26351 zedescendercyrillic: 0x0499,
26352 zedieresiscyrillic: 0x04DF,
26353 zehiragana: 0x305C,
26354 zekatakana: 0x30BC,
26356 zeroarabic: 0x0660,
26357 zerobengali: 0x09E6,
26359 zerogujarati: 0x0AE6,
26360 zerogurmukhi: 0x0A66,
26361 zerohackarabic: 0x0660,
26362 zeroinferior: 0x2080,
26363 zeromonospace: 0xFF10,
26364 zerooldstyle: 0xF730,
26365 zeropersian: 0x06F0,
26366 zerosuperior: 0x2070,
26368 zerowidthjoiner: 0xFEFF,
26369 zerowidthnonjoiner: 0x200C,
26370 zerowidthspace: 0x200B,
26372 zhbopomofo: 0x3113,
26373 zhearmenian: 0x056A,
26374 zhebrevecyrillic: 0x04C2,
26375 zhecyrillic: 0x0436,
26376 zhedescendercyrillic: 0x0497,
26377 zhedieresiscyrillic: 0x04DD,
26378 zihiragana: 0x3058,
26379 zikatakana: 0x30B8,
26380 zinorhebrew: 0x05AE,
26381 zlinebelow: 0x1E95,
26382 zmonospace: 0xFF5A,
26383 zohiragana: 0x305E,
26384 zokatakana: 0x30BE,
26386 zretroflexhook: 0x0290,
26388 zuhiragana: 0x305A,
26389 zukatakana: 0x30BA,
26393 var DingbatsGlyphsUnicode = {
26582 a89: 0x2768, // 0xF8D7
26583 a90: 0x2769, // 0xF8D8
26584 a93: 0x276A, // 0xF8D9
26585 a94: 0x276B, // 0xF8DA
26586 a91: 0x276C, // 0xF8DB
26587 a92: 0x276D, // 0xF8DC
26588 a205: 0x276E, // 0xF8DD
26589 a85: 0x276F, // 0xF8DE
26590 a206: 0x2770, // 0xF8DF
26591 a86: 0x2771, // 0xF8E0
26592 a87: 0x2772, // 0xF8E1
26593 a88: 0x2773, // 0xF8E2
26594 a95: 0x2774, // 0xF8E3
26595 a96: 0x2775, // 0xF8E4
26600 var PDFImage = (function PDFImageClosure() {
26602 * Decode the image in the main thread if it supported. Resovles the promise
26603 * when the image data is ready.
26605 function handleImageData(handler, xref, res, image) {
26606 if (image instanceof JpegStream && image.isNativelyDecodable(xref, res)) {
26607 // For natively supported jpegs send them to the main thread for decoding.
26608 var dict = image.dict;
26609 var colorSpace = dict.get('ColorSpace', 'CS');
26610 colorSpace = ColorSpace.parse(colorSpace, xref, res);
26611 var numComps = colorSpace.numComps;
26612 var decodePromise = handler.sendWithPromise('JpegDecode',
26613 [image.getIR(), numComps]);
26614 return decodePromise.then(function (message) {
26615 var data = message.data;
26616 return new Stream(data, 0, data.length, image.dict);
26619 return Promise.resolve(image);
26624 * Decode and clamp a value. The formula is different from the spec because we
26625 * don't decode to float range [0,1], we decode it in the [0,max] range.
26627 function decodeAndClamp(value, addend, coefficient, max) {
26628 value = addend + value * coefficient;
26629 // Clamp the value to the range
26630 return (value < 0 ? 0 : (value > max ? max : value));
26633 function PDFImage(xref, res, image, inline, smask, mask, isMask) {
26634 this.image = image;
26635 var dict = image.dict;
26636 if (dict.has('Filter')) {
26637 var filter = dict.get('Filter').name;
26638 if (filter === 'JPXDecode') {
26639 var jpxImage = new JpxImage();
26640 jpxImage.parseImageProperties(image.stream);
26641 image.stream.reset();
26642 image.bitsPerComponent = jpxImage.bitsPerComponent;
26643 image.numComps = jpxImage.componentsCount;
26644 } else if (filter === 'JBIG2Decode') {
26645 image.bitsPerComponent = 1;
26646 image.numComps = 1;
26649 // TODO cache rendered images?
26651 this.width = dict.get('Width', 'W');
26652 this.height = dict.get('Height', 'H');
26654 if (this.width < 1 || this.height < 1) {
26655 error('Invalid image width: ' + this.width + ' or height: ' +
26659 this.interpolate = dict.get('Interpolate', 'I') || false;
26660 this.imageMask = dict.get('ImageMask', 'IM') || false;
26661 this.matte = dict.get('Matte') || false;
26663 var bitsPerComponent = image.bitsPerComponent;
26664 if (!bitsPerComponent) {
26665 bitsPerComponent = dict.get('BitsPerComponent', 'BPC');
26666 if (!bitsPerComponent) {
26667 if (this.imageMask) {
26668 bitsPerComponent = 1;
26670 error('Bits per component missing in image: ' + this.imageMask);
26674 this.bpc = bitsPerComponent;
26676 if (!this.imageMask) {
26677 var colorSpace = dict.get('ColorSpace', 'CS');
26679 info('JPX images (which do not require color spaces)');
26680 switch (image.numComps) {
26682 colorSpace = Name.get('DeviceGray');
26685 colorSpace = Name.get('DeviceRGB');
26688 colorSpace = Name.get('DeviceCMYK');
26691 error('JPX images with ' + this.numComps +
26692 ' color components not supported.');
26695 this.colorSpace = ColorSpace.parse(colorSpace, xref, res);
26696 this.numComps = this.colorSpace.numComps;
26699 this.decode = dict.get('Decode', 'D');
26700 this.needsDecode = false;
26702 ((this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode)) ||
26703 (isMask && !ColorSpace.isDefaultDecode(this.decode, 1)))) {
26704 this.needsDecode = true;
26705 // Do some preprocessing to avoid more math.
26706 var max = (1 << bitsPerComponent) - 1;
26707 this.decodeCoefficients = [];
26708 this.decodeAddends = [];
26709 for (var i = 0, j = 0; i < this.decode.length; i += 2, ++j) {
26710 var dmin = this.decode[i];
26711 var dmax = this.decode[i + 1];
26712 this.decodeCoefficients[j] = dmax - dmin;
26713 this.decodeAddends[j] = max * dmin;
26718 this.smask = new PDFImage(xref, res, smask, false);
26720 if (isStream(mask)) {
26721 this.mask = new PDFImage(xref, res, mask, false, null, null, true);
26723 // Color key mask (just an array).
26729 * Handles processing of image data and returns the Promise that is resolved
26730 * with a PDFImage when the image is ready to be used.
26732 PDFImage.buildImage = function PDFImage_buildImage(handler, xref,
26733 res, image, inline) {
26734 var imagePromise = handleImageData(handler, xref, res, image);
26738 var smask = image.dict.get('SMask');
26739 var mask = image.dict.get('Mask');
26742 smaskPromise = handleImageData(handler, xref, res, smask);
26743 maskPromise = Promise.resolve(null);
26745 smaskPromise = Promise.resolve(null);
26747 if (isStream(mask)) {
26748 maskPromise = handleImageData(handler, xref, res, mask);
26749 } else if (isArray(mask)) {
26750 maskPromise = Promise.resolve(mask);
26752 warn('Unsupported mask format.');
26753 maskPromise = Promise.resolve(null);
26756 maskPromise = Promise.resolve(null);
26759 return Promise.all([imagePromise, smaskPromise, maskPromise]).then(
26760 function(results) {
26761 var imageData = results[0];
26762 var smaskData = results[1];
26763 var maskData = results[2];
26764 return new PDFImage(xref, res, imageData, inline, smaskData, maskData);
26769 * Resize an image using the nearest neighbor algorithm. Currently only
26770 * supports one and three component images.
26771 * @param {TypedArray} pixels The original image with one component.
26772 * @param {Number} bpc Number of bits per component.
26773 * @param {Number} components Number of color components, 1 or 3 is supported.
26774 * @param {Number} w1 Original width.
26775 * @param {Number} h1 Original height.
26776 * @param {Number} w2 New width.
26777 * @param {Number} h2 New height.
26778 * @param {TypedArray} dest (Optional) The destination buffer.
26779 * @param {Number} alpha01 (Optional) Size reserved for the alpha channel.
26780 * @return {TypedArray} Resized image data.
26782 PDFImage.resize = function PDFImage_resize(pixels, bpc, components,
26783 w1, h1, w2, h2, dest, alpha01) {
26785 if (components !== 1 && components !== 3) {
26786 error('Unsupported component count for resizing.');
26789 var length = w2 * h2 * components;
26790 var temp = dest ? dest : (bpc <= 8 ? new Uint8Array(length) :
26791 (bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length)));
26792 var xRatio = w1 / w2;
26793 var yRatio = h1 / h2;
26794 var i, j, py, newIndex = 0, oldIndex;
26795 var xScaled = new Uint16Array(w2);
26796 var w1Scanline = w1 * components;
26797 if (alpha01 !== 1) {
26801 for (j = 0; j < w2; j++) {
26802 xScaled[j] = Math.floor(j * xRatio) * components;
26805 if (components === 1) {
26806 for (i = 0; i < h2; i++) {
26807 py = Math.floor(i * yRatio) * w1Scanline;
26808 for (j = 0; j < w2; j++) {
26809 oldIndex = py + xScaled[j];
26810 temp[newIndex++] = pixels[oldIndex];
26813 } else if (components === 3) {
26814 for (i = 0; i < h2; i++) {
26815 py = Math.floor(i * yRatio) * w1Scanline;
26816 for (j = 0; j < w2; j++) {
26817 oldIndex = py + xScaled[j];
26818 temp[newIndex++] = pixels[oldIndex++];
26819 temp[newIndex++] = pixels[oldIndex++];
26820 temp[newIndex++] = pixels[oldIndex++];
26821 newIndex += alpha01;
26828 PDFImage.createMask =
26829 function PDFImage_createMask(imgArray, width, height,
26830 imageIsFromDecodeStream, inverseDecode) {
26832 // |imgArray| might not contain full data for every pixel of the mask, so
26833 // we need to distinguish between |computedLength| and |actualLength|.
26834 // In particular, if inverseDecode is true, then the array we return must
26835 // have a length of |computedLength|.
26837 var computedLength = ((width + 7) >> 3) * height;
26838 var actualLength = imgArray.byteLength;
26839 var haveFullData = computedLength === actualLength;
26842 if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) {
26843 // imgArray came from a DecodeStream and its data is in an appropriate
26844 // form, so we can just transfer it.
26846 } else if (!inverseDecode) {
26847 data = new Uint8Array(actualLength);
26848 data.set(imgArray);
26850 data = new Uint8Array(computedLength);
26851 data.set(imgArray);
26852 for (i = actualLength; i < computedLength; i++) {
26857 // If necessary, invert the original mask data (but not any extra we might
26858 // have added above). It's safe to modify the array -- whether it's the
26859 // original or a copy, we're about to transfer it anyway, so nothing else
26860 // in this thread can be relying on its contents.
26861 if (inverseDecode) {
26862 for (i = 0; i < actualLength; i++) {
26863 data[i] = ~data[i];
26867 return {data: data, width: width, height: height};
26870 PDFImage.prototype = {
26872 return Math.max(this.width,
26873 this.smask && this.smask.width || 0,
26874 this.mask && this.mask.width || 0);
26878 return Math.max(this.height,
26879 this.smask && this.smask.height || 0,
26880 this.mask && this.mask.height || 0);
26883 decodeBuffer: function PDFImage_decodeBuffer(buffer) {
26884 var bpc = this.bpc;
26885 var numComps = this.numComps;
26887 var decodeAddends = this.decodeAddends;
26888 var decodeCoefficients = this.decodeCoefficients;
26889 var max = (1 << bpc) - 1;
26893 // If the buffer needed decode that means it just needs to be inverted.
26894 for (i = 0, ii = buffer.length; i < ii; i++) {
26895 buffer[i] = +!(buffer[i]);
26900 for (i = 0, ii = this.width * this.height; i < ii; i++) {
26901 for (var j = 0; j < numComps; j++) {
26902 buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j],
26903 decodeCoefficients[j], max);
26909 getComponents: function PDFImage_getComponents(buffer) {
26910 var bpc = this.bpc;
26912 // This image doesn't require any extra work.
26917 var width = this.width;
26918 var height = this.height;
26919 var numComps = this.numComps;
26921 var length = width * height * numComps;
26923 var output = (bpc <= 8 ? new Uint8Array(length) :
26924 (bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length)));
26925 var rowComps = width * numComps;
26927 var max = (1 << bpc) - 1;
26928 var i = 0, ii, buf;
26931 // Optimization for reading 1 bpc images.
26932 var mask, loop1End, loop2End;
26933 for (var j = 0; j < height; j++) {
26934 loop1End = i + (rowComps & ~7);
26935 loop2End = i + rowComps;
26937 // unroll loop for all full bytes
26938 while (i < loop1End) {
26939 buf = buffer[bufferPos++];
26940 output[i] = (buf >> 7) & 1;
26941 output[i + 1] = (buf >> 6) & 1;
26942 output[i + 2] = (buf >> 5) & 1;
26943 output[i + 3] = (buf >> 4) & 1;
26944 output[i + 4] = (buf >> 3) & 1;
26945 output[i + 5] = (buf >> 2) & 1;
26946 output[i + 6] = (buf >> 1) & 1;
26947 output[i + 7] = buf & 1;
26951 // handle remaing bits
26952 if (i < loop2End) {
26953 buf = buffer[bufferPos++];
26955 while (i < loop2End) {
26956 output[i++] = +!!(buf & mask);
26962 // The general case that handles all other bpc values.
26965 for (i = 0, ii = length; i < ii; ++i) {
26966 if (i % rowComps === 0) {
26971 while (bits < bpc) {
26972 buf = (buf << 8) | buffer[bufferPos++];
26976 var remainingBits = bits - bpc;
26977 var value = buf >> remainingBits;
26978 output[i] = (value < 0 ? 0 : (value > max ? max : value));
26979 buf = buf & ((1 << remainingBits) - 1);
26980 bits = remainingBits;
26986 fillOpacity: function PDFImage_fillOpacity(rgbaBuf, width, height,
26987 actualHeight, image) {
26988 var smask = this.smask;
26989 var mask = this.mask;
26990 var alphaBuf, sw, sh, i, ii, j;
26995 alphaBuf = new Uint8Array(sw * sh);
26996 smask.fillGrayBuffer(alphaBuf);
26997 if (sw !== width || sh !== height) {
26998 alphaBuf = PDFImage.resize(alphaBuf, smask.bpc, 1, sw, sh, width,
27002 if (mask instanceof PDFImage) {
27005 alphaBuf = new Uint8Array(sw * sh);
27007 mask.fillGrayBuffer(alphaBuf);
27009 // Need to invert values in rgbaBuf
27010 for (i = 0, ii = sw * sh; i < ii; ++i) {
27011 alphaBuf[i] = 255 - alphaBuf[i];
27014 if (sw !== width || sh !== height) {
27015 alphaBuf = PDFImage.resize(alphaBuf, mask.bpc, 1, sw, sh, width,
27018 } else if (isArray(mask)) {
27019 // Color key mask: if any of the compontents are outside the range
27020 // then they should be painted.
27021 alphaBuf = new Uint8Array(width * height);
27022 var numComps = this.numComps;
27023 for (i = 0, ii = width * height; i < ii; ++i) {
27025 var imageOffset = i * numComps;
27026 for (j = 0; j < numComps; ++j) {
27027 var color = image[imageOffset + j];
27028 var maskOffset = j * 2;
27029 if (color < mask[maskOffset] || color > mask[maskOffset + 1]) {
27034 alphaBuf[i] = opacity;
27037 error('Unknown mask format.');
27042 for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
27043 rgbaBuf[j] = alphaBuf[i];
27047 for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
27053 undoPreblend: function PDFImage_undoPreblend(buffer, width, height) {
27054 var matte = this.smask && this.smask.matte;
27058 var matteRgb = this.colorSpace.getRgb(matte, 0);
27059 var matteR = matteRgb[0];
27060 var matteG = matteRgb[1];
27061 var matteB = matteRgb[2];
27062 var length = width * height * 4;
27064 for (var i = 0; i < length; i += 4) {
27065 var alpha = buffer[i + 3];
27067 // according formula we have to get Infinity in all components
27068 // making it white (typical paper color) should be okay
27070 buffer[i + 1] = 255;
27071 buffer[i + 2] = 255;
27074 var k = 255 / alpha;
27075 r = (buffer[i] - matteR) * k + matteR;
27076 g = (buffer[i + 1] - matteG) * k + matteG;
27077 b = (buffer[i + 2] - matteB) * k + matteB;
27078 buffer[i] = r <= 0 ? 0 : r >= 255 ? 255 : r | 0;
27079 buffer[i + 1] = g <= 0 ? 0 : g >= 255 ? 255 : g | 0;
27080 buffer[i + 2] = b <= 0 ? 0 : b >= 255 ? 255 : b | 0;
27084 createImageData: function PDFImage_createImageData(forceRGBA) {
27085 var drawWidth = this.drawWidth;
27086 var drawHeight = this.drawHeight;
27087 var imgData = { // other fields are filled in below
27092 var numComps = this.numComps;
27093 var originalWidth = this.width;
27094 var originalHeight = this.height;
27095 var bpc = this.bpc;
27097 // Rows start at byte boundary.
27098 var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
27102 // If it is a 1-bit-per-pixel grayscale (i.e. black-and-white) image
27103 // without any complications, we pass a same-sized copy to the main
27104 // thread rather than expanding by 32x to RGBA form. This saves *lots*
27105 // of memory for many scanned documents. It's also much faster.
27107 // Similarly, if it is a 24-bit-per pixel RGB image without any
27108 // complications, we avoid expanding by 1.333x to RGBA form.
27110 if (this.colorSpace.name === 'DeviceGray' && bpc === 1) {
27111 kind = ImageKind.GRAYSCALE_1BPP;
27112 } else if (this.colorSpace.name === 'DeviceRGB' && bpc === 8 &&
27113 !this.needsDecode) {
27114 kind = ImageKind.RGB_24BPP;
27116 if (kind && !this.smask && !this.mask &&
27117 drawWidth === originalWidth && drawHeight === originalHeight) {
27118 imgData.kind = kind;
27120 imgArray = this.getImageBytes(originalHeight * rowBytes);
27121 // If imgArray came from a DecodeStream, we're safe to transfer it
27122 // (and thus neuter it) because it will constitute the entire
27123 // DecodeStream's data. But if it came from a Stream, we need to
27124 // copy it because it'll only be a portion of the Stream's data, and
27125 // the rest will be read later on.
27126 if (this.image instanceof DecodeStream) {
27127 imgData.data = imgArray;
27129 var newArray = new Uint8Array(imgArray.length);
27130 newArray.set(imgArray);
27131 imgData.data = newArray;
27133 if (this.needsDecode) {
27134 // Invert the buffer (which must be grayscale if we reached here).
27135 assert(kind === ImageKind.GRAYSCALE_1BPP);
27136 var buffer = imgData.data;
27137 for (var i = 0, ii = buffer.length; i < ii; i++) {
27143 if (this.image instanceof JpegStream && !this.smask && !this.mask) {
27144 imgData.kind = ImageKind.RGB_24BPP;
27145 imgData.data = this.getImageBytes(originalHeight * rowBytes,
27146 drawWidth, drawHeight, true);
27151 imgArray = this.getImageBytes(originalHeight * rowBytes);
27152 // imgArray can be incomplete (e.g. after CCITT fax encoding).
27153 var actualHeight = 0 | (imgArray.length / rowBytes *
27154 drawHeight / originalHeight);
27156 var comps = this.getComponents(imgArray);
27158 // If opacity data is present, use RGBA_32BPP form. Otherwise, use the
27159 // more compact RGB_24BPP form if allowable.
27160 var alpha01, maybeUndoPreblend;
27161 if (!forceRGBA && !this.smask && !this.mask) {
27162 imgData.kind = ImageKind.RGB_24BPP;
27163 imgData.data = new Uint8Array(drawWidth * drawHeight * 3);
27165 maybeUndoPreblend = false;
27167 imgData.kind = ImageKind.RGBA_32BPP;
27168 imgData.data = new Uint8Array(drawWidth * drawHeight * 4);
27170 maybeUndoPreblend = true;
27172 // Color key masking (opacity) must be performed before decoding.
27173 this.fillOpacity(imgData.data, drawWidth, drawHeight, actualHeight,
27177 if (this.needsDecode) {
27178 this.decodeBuffer(comps);
27180 this.colorSpace.fillRgb(imgData.data, originalWidth, originalHeight,
27181 drawWidth, drawHeight, actualHeight, bpc, comps,
27183 if (maybeUndoPreblend) {
27184 this.undoPreblend(imgData.data, drawWidth, actualHeight);
27190 fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) {
27191 var numComps = this.numComps;
27192 if (numComps !== 1) {
27193 error('Reading gray scale from a color image: ' + numComps);
27196 var width = this.width;
27197 var height = this.height;
27198 var bpc = this.bpc;
27200 // rows start at byte boundary
27201 var rowBytes = (width * numComps * bpc + 7) >> 3;
27202 var imgArray = this.getImageBytes(height * rowBytes);
27204 var comps = this.getComponents(imgArray);
27208 // inline decoding (= inversion) for 1 bpc images
27209 length = width * height;
27210 if (this.needsDecode) {
27211 // invert and scale to {0, 255}
27212 for (i = 0; i < length; ++i) {
27213 buffer[i] = (comps[i] - 1) & 255;
27216 // scale to {0, 255}
27217 for (i = 0; i < length; ++i) {
27218 buffer[i] = (-comps[i]) & 255;
27224 if (this.needsDecode) {
27225 this.decodeBuffer(comps);
27227 length = width * height;
27228 // we aren't using a colorspace so we need to scale the value
27229 var scale = 255 / ((1 << bpc) - 1);
27230 for (i = 0; i < length; ++i) {
27231 buffer[i] = (scale * comps[i]) | 0;
27235 getImageBytes: function PDFImage_getImageBytes(length,
27236 drawWidth, drawHeight,
27238 this.image.reset();
27239 this.image.drawWidth = drawWidth || this.width;
27240 this.image.drawHeight = drawHeight || this.height;
27241 this.image.forceRGB = !!forceRGB;
27242 return this.image.getBytes(length);
27249 // The Metrics object contains glyph widths (in glyph space units).
27250 // As per PDF spec, for most fonts (Type 3 being an exception) a glyph
27251 // space unit corresponds to 1/1000th of text space unit.
27254 'Courier-Bold': 600,
27255 'Courier-BoldOblique': 600,
27256 'Courier-Oblique': 600,
27317 'bracketleft': 278,
27319 'bracketright': 278,
27320 'asciicircum': 469,
27361 'quotesingle': 191,
27362 'quotedblleft': 333,
27363 'guillemotleft': 556,
27364 'guilsinglleft': 333,
27365 'guilsinglright': 333,
27371 'periodcentered': 278,
27374 'quotesinglbase': 222,
27375 'quotedblbase': 333,
27376 'quotedblright': 333,
27377 'guillemotright': 556,
27379 'perthousand': 1000,
27380 'questiondown': 611,
27391 'hungarumlaut': 333,
27396 'ordfeminine': 370,
27400 'ordmasculine': 365,
27410 'uhungarumlaut': 556,
27415 'Acircumflex': 667,
27417 'Ucircumflex': 722,
27419 'scommaaccent': 500,
27420 'ecircumflex': 556,
27428 'commaaccent': 250,
27433 'Ncommaaccent': 722,
27436 'Tcommaaccent': 611,
27445 'Gcommaaccent': 778,
27446 'ucircumflex': 556,
27447 'acircumflex': 556,
27459 'threesuperior': 333,
27466 'partialdiff': 476,
27469 'icircumflex': 278,
27470 'Ecircumflex': 667,
27489 'greaterequal': 549,
27492 'lcommaaccent': 222,
27506 'Ocircumflex': 778,
27510 'twosuperior': 333,
27514 'ohungarumlaut': 556,
27517 'threequarters': 834,
27520 'Kcommaaccent': 667,
27529 'ocircumflex': 556,
27531 'Uhungarumlaut': 722,
27537 'Scommaaccent': 667,
27538 'Ohungarumlaut': 778,
27545 'rcommaaccent': 333,
27548 'Rcommaaccent': 722,
27549 'Lcommaaccent': 556,
27557 'kcommaaccent': 500,
27559 'Icircumflex': 278,
27561 'tcommaaccent': 278,
27566 'gcommaaccent': 556,
27569 'ncommaaccent': 556,
27570 'onesuperior': 333,
27574 'Helvetica-Bold': {
27634 'bracketleft': 333,
27636 'bracketright': 333,
27637 'asciicircum': 584,
27678 'quotesingle': 238,
27679 'quotedblleft': 500,
27680 'guillemotleft': 556,
27681 'guilsinglleft': 333,
27682 'guilsinglright': 333,
27688 'periodcentered': 278,
27691 'quotesinglbase': 278,
27692 'quotedblbase': 500,
27693 'quotedblright': 500,
27694 'guillemotright': 556,
27696 'perthousand': 1000,
27697 'questiondown': 611,
27708 'hungarumlaut': 333,
27713 'ordfeminine': 370,
27717 'ordmasculine': 365,
27727 'uhungarumlaut': 611,
27732 'Acircumflex': 722,
27734 'Ucircumflex': 722,
27736 'scommaaccent': 556,
27737 'ecircumflex': 556,
27745 'commaaccent': 250,
27750 'Ncommaaccent': 722,
27753 'Tcommaaccent': 611,
27762 'Gcommaaccent': 778,
27763 'ucircumflex': 611,
27764 'acircumflex': 556,
27776 'threesuperior': 333,
27783 'partialdiff': 494,
27786 'icircumflex': 278,
27787 'Ecircumflex': 667,
27806 'greaterequal': 549,
27809 'lcommaaccent': 278,
27823 'Ocircumflex': 778,
27827 'twosuperior': 333,
27831 'ohungarumlaut': 611,
27834 'threequarters': 834,
27837 'Kcommaaccent': 722,
27846 'ocircumflex': 611,
27848 'Uhungarumlaut': 722,
27854 'Scommaaccent': 667,
27855 'Ohungarumlaut': 778,
27862 'rcommaaccent': 389,
27865 'Rcommaaccent': 722,
27866 'Lcommaaccent': 611,
27874 'kcommaaccent': 556,
27876 'Icircumflex': 278,
27878 'tcommaaccent': 333,
27883 'gcommaaccent': 611,
27886 'ncommaaccent': 611,
27887 'onesuperior': 333,
27891 'Helvetica-BoldOblique': {
27951 'bracketleft': 333,
27953 'bracketright': 333,
27954 'asciicircum': 584,
27995 'quotesingle': 238,
27996 'quotedblleft': 500,
27997 'guillemotleft': 556,
27998 'guilsinglleft': 333,
27999 'guilsinglright': 333,
28005 'periodcentered': 278,
28008 'quotesinglbase': 278,
28009 'quotedblbase': 500,
28010 'quotedblright': 500,
28011 'guillemotright': 556,
28013 'perthousand': 1000,
28014 'questiondown': 611,
28025 'hungarumlaut': 333,
28030 'ordfeminine': 370,
28034 'ordmasculine': 365,
28044 'uhungarumlaut': 611,
28049 'Acircumflex': 722,
28051 'Ucircumflex': 722,
28053 'scommaaccent': 556,
28054 'ecircumflex': 556,
28062 'commaaccent': 250,
28067 'Ncommaaccent': 722,
28070 'Tcommaaccent': 611,
28079 'Gcommaaccent': 778,
28080 'ucircumflex': 611,
28081 'acircumflex': 556,
28093 'threesuperior': 333,
28100 'partialdiff': 494,
28103 'icircumflex': 278,
28104 'Ecircumflex': 667,
28123 'greaterequal': 549,
28126 'lcommaaccent': 278,
28140 'Ocircumflex': 778,
28144 'twosuperior': 333,
28148 'ohungarumlaut': 611,
28151 'threequarters': 834,
28154 'Kcommaaccent': 722,
28163 'ocircumflex': 611,
28165 'Uhungarumlaut': 722,
28171 'Scommaaccent': 667,
28172 'Ohungarumlaut': 778,
28179 'rcommaaccent': 389,
28182 'Rcommaaccent': 722,
28183 'Lcommaaccent': 611,
28191 'kcommaaccent': 556,
28193 'Icircumflex': 278,
28195 'tcommaaccent': 333,
28200 'gcommaaccent': 611,
28203 'ncommaaccent': 611,
28204 'onesuperior': 333,
28208 'Helvetica-Oblique' : {
28268 'bracketleft': 278,
28270 'bracketright': 278,
28271 'asciicircum': 469,
28312 'quotesingle': 191,
28313 'quotedblleft': 333,
28314 'guillemotleft': 556,
28315 'guilsinglleft': 333,
28316 'guilsinglright': 333,
28322 'periodcentered': 278,
28325 'quotesinglbase': 222,
28326 'quotedblbase': 333,
28327 'quotedblright': 333,
28328 'guillemotright': 556,
28330 'perthousand': 1000,
28331 'questiondown': 611,
28342 'hungarumlaut': 333,
28347 'ordfeminine': 370,
28351 'ordmasculine': 365,
28361 'uhungarumlaut': 556,
28366 'Acircumflex': 667,
28368 'Ucircumflex': 722,
28370 'scommaaccent': 500,
28371 'ecircumflex': 556,
28379 'commaaccent': 250,
28384 'Ncommaaccent': 722,
28387 'Tcommaaccent': 611,
28396 'Gcommaaccent': 778,
28397 'ucircumflex': 556,
28398 'acircumflex': 556,
28410 'threesuperior': 333,
28417 'partialdiff': 476,
28420 'icircumflex': 278,
28421 'Ecircumflex': 667,
28440 'greaterequal': 549,
28443 'lcommaaccent': 222,
28457 'Ocircumflex': 778,
28461 'twosuperior': 333,
28465 'ohungarumlaut': 556,
28468 'threequarters': 834,
28471 'Kcommaaccent': 667,
28480 'ocircumflex': 556,
28482 'Uhungarumlaut': 722,
28488 'Scommaaccent': 667,
28489 'Ohungarumlaut': 778,
28496 'rcommaaccent': 333,
28499 'Rcommaaccent': 722,
28500 'Lcommaaccent': 556,
28508 'kcommaaccent': 500,
28510 'Icircumflex': 278,
28512 'tcommaaccent': 278,
28517 'gcommaaccent': 556,
28520 'ncommaaccent': 556,
28521 'onesuperior': 333,
28530 'existential': 549,
28536 'asteriskmath': 500,
28585 'bracketleft': 333,
28587 'bracketright': 333,
28588 'perpendicular': 658,
28640 'greaterequal': 549,
28642 'proportional': 713,
28643 'partialdiff': 494,
28647 'equivalence': 549,
28648 'approxequal': 549,
28650 'arrowvertex': 603,
28651 'arrowhorizex': 1000,
28652 'carriagereturn': 658,
28656 'weierstrass': 987,
28657 'circlemultiply': 768,
28660 'intersection': 768,
28662 'propersuperset': 713,
28663 'reflexsuperset': 713,
28665 'propersubset': 713,
28666 'reflexsubset': 713,
28671 'registerserif': 790,
28672 'copyrightserif': 790,
28673 'trademarkserif': 890,
28680 'arrowdblboth': 1042,
28681 'arrowdblleft': 987,
28683 'arrowdblright': 987,
28684 'arrowdbldown': 603,
28687 'registersans': 790,
28688 'copyrightsans': 790,
28689 'trademarksans': 786,
28691 'parenlefttp': 384,
28692 'parenleftex': 384,
28693 'parenleftbt': 384,
28694 'bracketlefttp': 384,
28695 'bracketleftex': 384,
28696 'bracketleftbt': 384,
28697 'bracelefttp': 494,
28698 'braceleftmid': 494,
28699 'braceleftbt': 494,
28706 'parenrighttp': 384,
28707 'parenrightex': 384,
28708 'parenrightbt': 384,
28709 'bracketrighttp': 384,
28710 'bracketrightex': 384,
28711 'bracketrightbt': 384,
28712 'bracerighttp': 494,
28713 'bracerightmid': 494,
28714 'bracerightbt': 494,
28777 'bracketleft': 333,
28779 'bracketright': 333,
28780 'asciicircum': 469,
28821 'quotesingle': 180,
28822 'quotedblleft': 444,
28823 'guillemotleft': 500,
28824 'guilsinglleft': 333,
28825 'guilsinglright': 333,
28831 'periodcentered': 250,
28834 'quotesinglbase': 333,
28835 'quotedblbase': 444,
28836 'quotedblright': 444,
28837 'guillemotright': 500,
28839 'perthousand': 1000,
28840 'questiondown': 444,
28851 'hungarumlaut': 333,
28856 'ordfeminine': 276,
28860 'ordmasculine': 310,
28870 'uhungarumlaut': 500,
28875 'Acircumflex': 722,
28877 'Ucircumflex': 722,
28879 'scommaaccent': 389,
28880 'ecircumflex': 444,
28888 'commaaccent': 250,
28893 'Ncommaaccent': 722,
28896 'Tcommaaccent': 611,
28905 'Gcommaaccent': 722,
28906 'ucircumflex': 500,
28907 'acircumflex': 444,
28919 'threesuperior': 300,
28926 'partialdiff': 476,
28929 'icircumflex': 278,
28930 'Ecircumflex': 611,
28949 'greaterequal': 549,
28952 'lcommaaccent': 278,
28966 'Ocircumflex': 722,
28970 'twosuperior': 300,
28974 'ohungarumlaut': 500,
28977 'threequarters': 750,
28980 'Kcommaaccent': 722,
28989 'ocircumflex': 500,
28991 'Uhungarumlaut': 722,
28997 'Scommaaccent': 556,
28998 'Ohungarumlaut': 722,
29005 'rcommaaccent': 333,
29008 'Rcommaaccent': 667,
29009 'Lcommaaccent': 611,
29017 'kcommaaccent': 500,
29019 'Icircumflex': 333,
29021 'tcommaaccent': 278,
29026 'gcommaaccent': 500,
29029 'ncommaaccent': 500,
29030 'onesuperior': 300,
29094 'bracketleft': 333,
29096 'bracketright': 333,
29097 'asciicircum': 581,
29138 'quotesingle': 278,
29139 'quotedblleft': 500,
29140 'guillemotleft': 500,
29141 'guilsinglleft': 333,
29142 'guilsinglright': 333,
29148 'periodcentered': 250,
29151 'quotesinglbase': 333,
29152 'quotedblbase': 500,
29153 'quotedblright': 500,
29154 'guillemotright': 500,
29156 'perthousand': 1000,
29157 'questiondown': 500,
29168 'hungarumlaut': 333,
29173 'ordfeminine': 300,
29177 'ordmasculine': 330,
29187 'uhungarumlaut': 556,
29192 'Acircumflex': 722,
29194 'Ucircumflex': 722,
29196 'scommaaccent': 389,
29197 'ecircumflex': 444,
29205 'commaaccent': 250,
29210 'Ncommaaccent': 722,
29213 'Tcommaaccent': 667,
29222 'Gcommaaccent': 778,
29223 'ucircumflex': 556,
29224 'acircumflex': 500,
29236 'threesuperior': 300,
29243 'partialdiff': 494,
29246 'icircumflex': 278,
29247 'Ecircumflex': 667,
29266 'greaterequal': 549,
29269 'lcommaaccent': 278,
29283 'Ocircumflex': 778,
29287 'twosuperior': 300,
29291 'ohungarumlaut': 500,
29294 'threequarters': 750,
29297 'Kcommaaccent': 778,
29306 'ocircumflex': 500,
29308 'Uhungarumlaut': 722,
29314 'Scommaaccent': 556,
29315 'Ohungarumlaut': 778,
29322 'rcommaaccent': 444,
29325 'Rcommaaccent': 722,
29326 'Lcommaaccent': 667,
29334 'kcommaaccent': 556,
29336 'Icircumflex': 389,
29338 'tcommaaccent': 333,
29343 'gcommaaccent': 500,
29346 'ncommaaccent': 556,
29347 'onesuperior': 300,
29351 'Times-BoldItalic': {
29411 'bracketleft': 333,
29413 'bracketright': 333,
29414 'asciicircum': 570,
29455 'quotesingle': 278,
29456 'quotedblleft': 500,
29457 'guillemotleft': 500,
29458 'guilsinglleft': 333,
29459 'guilsinglright': 333,
29465 'periodcentered': 250,
29468 'quotesinglbase': 333,
29469 'quotedblbase': 500,
29470 'quotedblright': 500,
29471 'guillemotright': 500,
29473 'perthousand': 1000,
29474 'questiondown': 500,
29485 'hungarumlaut': 333,
29490 'ordfeminine': 266,
29494 'ordmasculine': 300,
29504 'uhungarumlaut': 556,
29509 'Acircumflex': 667,
29511 'Ucircumflex': 722,
29513 'scommaaccent': 389,
29514 'ecircumflex': 444,
29522 'commaaccent': 250,
29527 'Ncommaaccent': 722,
29530 'Tcommaaccent': 611,
29539 'Gcommaaccent': 722,
29540 'ucircumflex': 556,
29541 'acircumflex': 500,
29553 'threesuperior': 300,
29560 'partialdiff': 494,
29563 'icircumflex': 278,
29564 'Ecircumflex': 667,
29583 'greaterequal': 549,
29586 'lcommaaccent': 278,
29600 'Ocircumflex': 722,
29604 'twosuperior': 300,
29608 'ohungarumlaut': 500,
29611 'threequarters': 750,
29614 'Kcommaaccent': 667,
29623 'ocircumflex': 500,
29625 'Uhungarumlaut': 722,
29631 'Scommaaccent': 556,
29632 'Ohungarumlaut': 722,
29639 'rcommaaccent': 389,
29642 'Rcommaaccent': 667,
29643 'Lcommaaccent': 611,
29651 'kcommaaccent': 500,
29653 'Icircumflex': 389,
29655 'tcommaaccent': 278,
29660 'gcommaaccent': 500,
29663 'ncommaaccent': 556,
29664 'onesuperior': 300,
29728 'bracketleft': 389,
29730 'bracketright': 389,
29731 'asciicircum': 422,
29772 'quotesingle': 214,
29773 'quotedblleft': 556,
29774 'guillemotleft': 500,
29775 'guilsinglleft': 333,
29776 'guilsinglright': 333,
29782 'periodcentered': 250,
29785 'quotesinglbase': 333,
29786 'quotedblbase': 556,
29787 'quotedblright': 556,
29788 'guillemotright': 500,
29790 'perthousand': 1000,
29791 'questiondown': 500,
29802 'hungarumlaut': 333,
29807 'ordfeminine': 276,
29811 'ordmasculine': 310,
29821 'uhungarumlaut': 500,
29826 'Acircumflex': 611,
29828 'Ucircumflex': 722,
29830 'scommaaccent': 389,
29831 'ecircumflex': 444,
29839 'commaaccent': 250,
29844 'Ncommaaccent': 667,
29847 'Tcommaaccent': 556,
29856 'Gcommaaccent': 722,
29857 'ucircumflex': 500,
29858 'acircumflex': 500,
29870 'threesuperior': 300,
29877 'partialdiff': 476,
29880 'icircumflex': 278,
29881 'Ecircumflex': 611,
29900 'greaterequal': 549,
29903 'lcommaaccent': 278,
29917 'Ocircumflex': 722,
29921 'twosuperior': 300,
29925 'ohungarumlaut': 500,
29928 'threequarters': 750,
29931 'Kcommaaccent': 667,
29940 'ocircumflex': 500,
29942 'Uhungarumlaut': 722,
29948 'Scommaaccent': 500,
29949 'Ohungarumlaut': 722,
29956 'rcommaaccent': 389,
29959 'Rcommaaccent': 611,
29960 'Lcommaaccent': 556,
29968 'kcommaaccent': 444,
29970 'Icircumflex': 333,
29972 'tcommaaccent': 278,
29977 'gcommaaccent': 500,
29980 'ncommaaccent': 500,
29981 'onesuperior': 300,
30194 function isEOF(v) {
30195 return (v === EOF);
30198 var MAX_LENGTH_TO_CACHE = 1000;
30200 var Parser = (function ParserClosure() {
30201 function Parser(lexer, allowStreams, xref) {
30202 this.lexer = lexer;
30203 this.allowStreams = allowStreams;
30205 this.imageCache = {};
30209 Parser.prototype = {
30210 refill: function Parser_refill() {
30211 this.buf1 = this.lexer.getObj();
30212 this.buf2 = this.lexer.getObj();
30214 shift: function Parser_shift() {
30215 if (isCmd(this.buf2, 'ID')) {
30216 this.buf1 = this.buf2;
30219 this.buf1 = this.buf2;
30220 this.buf2 = this.lexer.getObj();
30223 getObj: function Parser_getObj(cipherTransform) {
30224 var buf1 = this.buf1;
30227 if (buf1 instanceof Cmd) {
30228 switch (buf1.cmd) {
30229 case 'BI': // inline image
30230 return this.makeInlineImage(cipherTransform);
30233 while (!isCmd(this.buf1, ']') && !isEOF(this.buf1)) {
30234 array.push(this.getObj(cipherTransform));
30236 if (isEOF(this.buf1)) {
30237 error('End of file inside array');
30241 case '<<': // dictionary or stream
30242 var dict = new Dict(this.xref);
30243 while (!isCmd(this.buf1, '>>') && !isEOF(this.buf1)) {
30244 if (!isName(this.buf1)) {
30245 info('Malformed dictionary: key must be a name object');
30250 var key = this.buf1.name;
30252 if (isEOF(this.buf1)) {
30255 dict.set(key, this.getObj(cipherTransform));
30257 if (isEOF(this.buf1)) {
30258 error('End of file inside dictionary');
30261 // Stream objects are not allowed inside content streams or
30263 if (isCmd(this.buf2, 'stream')) {
30264 return (this.allowStreams ?
30265 this.makeStream(dict, cipherTransform) : dict);
30269 default: // simple object
30274 if (isInt(buf1)) { // indirect reference or integer
30276 if (isInt(this.buf1) && isCmd(this.buf2, 'R')) {
30277 var ref = new Ref(num, this.buf1);
30285 if (isString(buf1)) { // string
30287 if (cipherTransform) {
30288 str = cipherTransform.decryptString(str);
30297 * Find the end of the stream by searching for the /EI\s/.
30298 * @returns {number} The inline stream length.
30300 findDefaultInlineStreamEnd:
30301 function Parser_findDefaultInlineStreamEnd(stream) {
30302 var E = 0x45, I = 0x49, SPACE = 0x20, LF = 0xA, CR = 0xD;
30303 var startPos = stream.pos, state = 0, ch, i, n, followingBytes;
30304 while ((ch = stream.getByte()) !== -1) {
30306 state = (ch === E) ? 1 : 0;
30307 } else if (state === 1) {
30308 state = (ch === I) ? 2 : 0;
30310 assert(state === 2);
30311 if (ch === SPACE || ch === LF || ch === CR) {
30312 // Let's check the next five bytes are ASCII... just be sure.
30314 followingBytes = stream.peekBytes(n);
30315 for (i = 0; i < n; i++) {
30316 ch = followingBytes[i];
30317 if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7F)) {
30318 // Not a LF, CR, SPACE or any visible ASCII character, i.e.
30319 // it's binary stuff. Resetting the state.
30325 break; // Finished!
30332 return ((stream.pos - 4) - startPos);
30335 * Find the EOI (end-of-image) marker 0xFFD9 of the stream.
30336 * @returns {number} The inline stream length.
30338 findDCTDecodeInlineStreamEnd:
30339 function Parser_findDCTDecodeInlineStreamEnd(stream) {
30340 var startPos = stream.pos, foundEOI = false, b, markerLength, length;
30341 while ((b = stream.getByte()) !== -1) {
30342 if (b !== 0xFF) { // Not a valid marker.
30345 switch (stream.getByte()) {
30346 case 0x00: // Byte stuffing.
30347 // 0xFF00 appears to be a very common byte sequence in JPEG images.
30350 case 0xFF: // Fill byte.
30351 // Avoid skipping a valid marker, resetting the stream position.
30369 case 0xCA: // SOF10
30370 case 0xCB: // SOF11
30372 case 0xCD: // SOF13
30373 case 0xCE: // SOF14
30374 case 0xCF: // SOF15
30396 case 0xEA: // APP10
30397 case 0xEB: // APP11
30398 case 0xEC: // APP12
30399 case 0xED: // APP13
30400 case 0xEE: // APP14
30401 case 0xEF: // APP15
30404 // The marker should be followed by the length of the segment.
30405 markerLength = stream.getUint16();
30406 if (markerLength > 2) {
30407 // |markerLength| contains the byte length of the marker segment,
30408 // including its own length (2 bytes) and excluding the marker.
30409 stream.skip(markerLength - 2); // Jump to the next marker.
30411 // The marker length is invalid, resetting the stream position.
30420 length = stream.pos - startPos;
30422 warn('Inline DCTDecode image stream: ' +
30423 'EOI marker not found, searching for /EI/ instead.');
30424 stream.skip(-length); // Reset the stream position.
30425 return this.findDefaultInlineStreamEnd(stream);
30427 this.inlineStreamSkipEI(stream);
30431 * Find the EOD (end-of-data) marker '~>' (i.e. TILDE + GT) of the stream.
30432 * @returns {number} The inline stream length.
30434 findASCII85DecodeInlineStreamEnd:
30435 function Parser_findASCII85DecodeInlineStreamEnd(stream) {
30436 var TILDE = 0x7E, GT = 0x3E;
30437 var startPos = stream.pos, ch, length;
30438 while ((ch = stream.getByte()) !== -1) {
30439 if (ch === TILDE && stream.peekByte() === GT) {
30444 length = stream.pos - startPos;
30446 warn('Inline ASCII85Decode image stream: ' +
30447 'EOD marker not found, searching for /EI/ instead.');
30448 stream.skip(-length); // Reset the stream position.
30449 return this.findDefaultInlineStreamEnd(stream);
30451 this.inlineStreamSkipEI(stream);
30455 * Find the EOD (end-of-data) marker '>' (i.e. GT) of the stream.
30456 * @returns {number} The inline stream length.
30458 findASCIIHexDecodeInlineStreamEnd:
30459 function Parser_findASCIIHexDecodeInlineStreamEnd(stream) {
30461 var startPos = stream.pos, ch, length;
30462 while ((ch = stream.getByte()) !== -1) {
30467 length = stream.pos - startPos;
30469 warn('Inline ASCIIHexDecode image stream: ' +
30470 'EOD marker not found, searching for /EI/ instead.');
30471 stream.skip(-length); // Reset the stream position.
30472 return this.findDefaultInlineStreamEnd(stream);
30474 this.inlineStreamSkipEI(stream);
30478 * Skip over the /EI/ for streams where we search for an EOD marker.
30480 inlineStreamSkipEI: function Parser_inlineStreamSkipEI(stream) {
30481 var E = 0x45, I = 0x49;
30483 while ((ch = stream.getByte()) !== -1) {
30485 state = (ch === E) ? 1 : 0;
30486 } else if (state === 1) {
30487 state = (ch === I) ? 2 : 0;
30488 } else if (state === 2) {
30493 makeInlineImage: function Parser_makeInlineImage(cipherTransform) {
30494 var lexer = this.lexer;
30495 var stream = lexer.stream;
30497 // Parse dictionary.
30498 var dict = new Dict(null);
30499 while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) {
30500 if (!isName(this.buf1)) {
30501 error('Dictionary key must be a name object');
30503 var key = this.buf1.name;
30505 if (isEOF(this.buf1)) {
30508 dict.set(key, this.getObj(cipherTransform));
30511 // Extract the name of the first (i.e. the current) image filter.
30512 var filter = this.fetchIfRef(dict.get('Filter', 'F')), filterName;
30513 if (isName(filter)) {
30514 filterName = filter.name;
30515 } else if (isArray(filter) && isName(filter[0])) {
30516 filterName = filter[0].name;
30519 // Parse image stream.
30520 var startPos = stream.pos, length, i, ii;
30521 if (filterName === 'DCTDecode' || filterName === 'DCT') {
30522 length = this.findDCTDecodeInlineStreamEnd(stream);
30523 } else if (filterName === 'ASCII85Decide' || filterName === 'A85') {
30524 length = this.findASCII85DecodeInlineStreamEnd(stream);
30525 } else if (filterName === 'ASCIIHexDecode' || filterName === 'AHx') {
30526 length = this.findASCIIHexDecodeInlineStreamEnd(stream);
30528 length = this.findDefaultInlineStreamEnd(stream);
30530 var imageStream = stream.makeSubStream(startPos, length, dict);
30532 // Cache all images below the MAX_LENGTH_TO_CACHE threshold by their
30533 // adler32 checksum.
30535 if (length < MAX_LENGTH_TO_CACHE) {
30536 var imageBytes = imageStream.getBytes();
30537 imageStream.reset();
30541 for (i = 0, ii = imageBytes.length; i < ii; ++i) {
30542 // No modulo required in the loop if imageBytes.length < 5552.
30543 a += imageBytes[i] & 0xff;
30546 adler32 = ((b % 65521) << 16) | (a % 65521);
30548 if (this.imageCache.adler32 === adler32) {
30549 this.buf2 = Cmd.get('EI');
30552 this.imageCache[adler32].reset();
30553 return this.imageCache[adler32];
30557 if (cipherTransform) {
30558 imageStream = cipherTransform.createStream(imageStream, length);
30561 imageStream = this.filter(imageStream, dict, length);
30562 imageStream.dict = dict;
30563 if (adler32 !== undefined) {
30564 imageStream.cacheKey = 'inline_' + length + '_' + adler32;
30565 this.imageCache[adler32] = imageStream;
30568 this.buf2 = Cmd.get('EI');
30571 return imageStream;
30573 fetchIfRef: function Parser_fetchIfRef(obj) {
30574 // not relying on the xref.fetchIfRef -- xref might not be set
30575 return (isRef(obj) ? this.xref.fetch(obj) : obj);
30577 makeStream: function Parser_makeStream(dict, cipherTransform) {
30578 var lexer = this.lexer;
30579 var stream = lexer.stream;
30581 // get stream start position
30582 lexer.skipToNextLine();
30583 var pos = stream.pos - 1;
30586 var length = this.fetchIfRef(dict.get('Length'));
30587 if (!isInt(length)) {
30588 info('Bad ' + length + ' attribute in stream');
30592 // skip over the stream data
30593 stream.pos = pos + length;
30596 this.shift(); // '>>'
30597 this.shift(); // 'stream'
30598 if (!isCmd(this.buf1, 'endstream')) {
30599 // bad stream length, scanning for endstream
30601 var SCAN_BLOCK_SIZE = 2048;
30602 var ENDSTREAM_SIGNATURE_LENGTH = 9;
30603 var ENDSTREAM_SIGNATURE = [0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65,
30605 var skipped = 0, found = false, i, j;
30606 while (stream.pos < stream.end) {
30607 var scanBytes = stream.peekBytes(SCAN_BLOCK_SIZE);
30608 var scanLength = scanBytes.length - ENDSTREAM_SIGNATURE_LENGTH;
30609 if (scanLength <= 0) {
30613 for (i = 0, j = 0; i < scanLength; i++) {
30614 var b = scanBytes[i];
30615 if (b !== ENDSTREAM_SIGNATURE[j]) {
30620 if (j >= ENDSTREAM_SIGNATURE_LENGTH) {
30628 skipped += i - ENDSTREAM_SIGNATURE_LENGTH;
30629 stream.pos += i - ENDSTREAM_SIGNATURE_LENGTH;
30632 skipped += scanLength;
30633 stream.pos += scanLength;
30636 error('Missing endstream');
30644 this.shift(); // 'endstream'
30646 stream = stream.makeSubStream(pos, length, dict);
30647 if (cipherTransform) {
30648 stream = cipherTransform.createStream(stream, length);
30650 stream = this.filter(stream, dict, length);
30651 stream.dict = dict;
30654 filter: function Parser_filter(stream, dict, length) {
30655 var filter = this.fetchIfRef(dict.get('Filter', 'F'));
30656 var params = this.fetchIfRef(dict.get('DecodeParms', 'DP'));
30657 if (isName(filter)) {
30658 return this.makeFilter(stream, filter.name, length, params);
30661 var maybeLength = length;
30662 if (isArray(filter)) {
30663 var filterArray = filter;
30664 var paramsArray = params;
30665 for (var i = 0, ii = filterArray.length; i < ii; ++i) {
30666 filter = filterArray[i];
30667 if (!isName(filter)) {
30668 error('Bad filter name: ' + filter);
30672 if (isArray(paramsArray) && (i in paramsArray)) {
30673 params = paramsArray[i];
30675 stream = this.makeFilter(stream, filter.name, maybeLength, params);
30676 // after the first stream the length variable is invalid
30677 maybeLength = null;
30682 makeFilter: function Parser_makeFilter(stream, name, maybeLength, params) {
30683 if (stream.dict.get('Length') === 0) {
30684 return new NullStream(stream);
30688 params = this.fetchIfRef(params);
30690 var xrefStreamStats = this.xref.stats.streamTypes;
30691 if (name === 'FlateDecode' || name === 'Fl') {
30692 xrefStreamStats[StreamType.FLATE] = true;
30694 return new PredictorStream(new FlateStream(stream, maybeLength),
30695 maybeLength, params);
30697 return new FlateStream(stream, maybeLength);
30699 if (name === 'LZWDecode' || name === 'LZW') {
30700 xrefStreamStats[StreamType.LZW] = true;
30701 var earlyChange = 1;
30703 if (params.has('EarlyChange')) {
30704 earlyChange = params.get('EarlyChange');
30706 return new PredictorStream(
30707 new LZWStream(stream, maybeLength, earlyChange),
30708 maybeLength, params);
30710 return new LZWStream(stream, maybeLength, earlyChange);
30712 if (name === 'DCTDecode' || name === 'DCT') {
30713 xrefStreamStats[StreamType.DCT] = true;
30714 return new JpegStream(stream, maybeLength, stream.dict, this.xref);
30716 if (name === 'JPXDecode' || name === 'JPX') {
30717 xrefStreamStats[StreamType.JPX] = true;
30718 return new JpxStream(stream, maybeLength, stream.dict);
30720 if (name === 'ASCII85Decode' || name === 'A85') {
30721 xrefStreamStats[StreamType.A85] = true;
30722 return new Ascii85Stream(stream, maybeLength);
30724 if (name === 'ASCIIHexDecode' || name === 'AHx') {
30725 xrefStreamStats[StreamType.AHX] = true;
30726 return new AsciiHexStream(stream, maybeLength);
30728 if (name === 'CCITTFaxDecode' || name === 'CCF') {
30729 xrefStreamStats[StreamType.CCF] = true;
30730 return new CCITTFaxStream(stream, maybeLength, params);
30732 if (name === 'RunLengthDecode' || name === 'RL') {
30733 xrefStreamStats[StreamType.RL] = true;
30734 return new RunLengthStream(stream, maybeLength);
30736 if (name === 'JBIG2Decode') {
30737 xrefStreamStats[StreamType.JBIG] = true;
30738 return new Jbig2Stream(stream, maybeLength, stream.dict);
30740 warn('filter "' + name + '" not supported yet');
30743 if (ex instanceof MissingDataException) {
30746 warn('Invalid stream: \"' + ex + '\"');
30747 return new NullStream(stream);
30755 var Lexer = (function LexerClosure() {
30756 function Lexer(stream, knownCommands) {
30757 this.stream = stream;
30760 // While lexing, we build up many strings one char at a time. Using += for
30761 // this can result in lots of garbage strings. It's better to build an
30762 // array of single-char strings and then join() them together at the end.
30763 // And reusing a single array (i.e. |this.strBuf|) over and over for this
30764 // purpose uses less memory than using a new array for each string.
30767 // The PDFs might have "glued" commands with other commands, operands or
30768 // literals, e.g. "q1". The knownCommands is a dictionary of the valid
30769 // commands and their prefixes. The prefixes are built the following way:
30770 // if there a command that is a prefix of the other valid command or
30771 // literal (e.g. 'f' and 'false') the following prefixes must be included,
30772 // 'fa', 'fal', 'fals'. The prefixes are not needed, if the command has no
30773 // other commands or literals as a prefix. The knowCommands is optional.
30774 this.knownCommands = knownCommands;
30777 Lexer.isSpace = function Lexer_isSpace(ch) {
30778 // Space is one of the following characters: SPACE, TAB, CR or LF.
30779 return (ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A);
30782 // A '1' in this array means the character is white space. A '1' or
30783 // '2' means the character ends a name or command.
30784 var specialChars = [
30785 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x
30786 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
30787 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x
30788 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x
30789 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x
30790 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x
30791 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x
30792 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x
30793 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
30794 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
30795 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax
30796 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx
30797 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx
30798 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx
30799 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex
30800 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx
30803 function toHexDigit(ch) {
30804 if (ch >= 0x30 && ch <= 0x39) { // '0'-'9'
30807 if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) {
30808 // 'A'-'F', 'a'-'f'
30809 return (ch & 0x0F) + 9;
30814 Lexer.prototype = {
30815 nextChar: function Lexer_nextChar() {
30816 return (this.currentChar = this.stream.getByte());
30818 peekChar: function Lexer_peekChar() {
30819 return this.stream.peekByte();
30821 getNumber: function Lexer_getNumber() {
30822 var ch = this.currentChar;
30823 var eNotation = false;
30824 var divideBy = 0; // different from 0 if it's a floating point value
30827 if (ch === 0x2D) { // '-'
30829 ch = this.nextChar();
30830 } else if (ch === 0x2B) { // '+'
30831 ch = this.nextChar();
30833 if (ch === 0x2E) { // '.'
30835 ch = this.nextChar();
30837 if (ch < 0x30 || ch > 0x39) { // '0' - '9'
30838 error('Invalid number: ' + String.fromCharCode(ch));
30842 var baseValue = ch - 0x30; // '0'
30843 var powerValue = 0;
30844 var powerValueSign = 1;
30846 while ((ch = this.nextChar()) >= 0) {
30847 if (0x30 <= ch && ch <= 0x39) { // '0' - '9'
30848 var currentDigit = ch - 0x30; // '0'
30849 if (eNotation) { // We are after an 'e' or 'E'
30850 powerValue = powerValue * 10 + currentDigit;
30852 if (divideBy !== 0) { // We are after a point
30855 baseValue = baseValue * 10 + currentDigit;
30857 } else if (ch === 0x2E) { // '.'
30858 if (divideBy === 0) {
30861 // A number can have only one '.'
30864 } else if (ch === 0x2D) { // '-'
30865 // ignore minus signs in the middle of numbers to match
30866 // Adobe's behavior
30867 warn('Badly formated number');
30868 } else if (ch === 0x45 || ch === 0x65) { // 'E', 'e'
30869 // 'E' can be either a scientific notation or the beginning of a new
30871 ch = this.peekChar();
30872 if (ch === 0x2B || ch === 0x2D) { // '+', '-'
30873 powerValueSign = (ch === 0x2D) ? -1 : 1;
30874 this.nextChar(); // Consume the sign character
30875 } else if (ch < 0x30 || ch > 0x39) { // '0' - '9'
30876 // The 'E' must be the beginning of a new operator
30881 // the last character doesn't belong to us
30886 if (divideBy !== 0) {
30887 baseValue /= divideBy;
30890 baseValue *= Math.pow(10, powerValueSign * powerValue);
30892 return sign * baseValue;
30894 getString: function Lexer_getString() {
30897 var strBuf = this.strBuf;
30900 var ch = this.nextChar();
30902 var charBuffered = false;
30905 warn('Unterminated string');
30913 if (--numParen === 0) {
30914 this.nextChar(); // consume strings ')'
30921 ch = this.nextChar();
30924 warn('Unterminated string');
30945 strBuf.push(String.fromCharCode(ch));
30947 case 0x30: case 0x31: case 0x32: case 0x33: // '0'-'3'
30948 case 0x34: case 0x35: case 0x36: case 0x37: // '4'-'7'
30950 ch = this.nextChar();
30951 charBuffered = true;
30952 if (ch >= 0x30 && ch <= 0x37) { // '0'-'7'
30953 x = (x << 3) + (ch & 0x0F);
30954 ch = this.nextChar();
30955 if (ch >= 0x30 && ch <= 0x37) { // '0'-'7'
30956 charBuffered = false;
30957 x = (x << 3) + (ch & 0x0F);
30960 strBuf.push(String.fromCharCode(x));
30963 if (this.peekChar() === 0x0A) { // LF
30970 strBuf.push(String.fromCharCode(ch));
30975 strBuf.push(String.fromCharCode(ch));
30981 if (!charBuffered) {
30982 ch = this.nextChar();
30985 return strBuf.join('');
30987 getName: function Lexer_getName() {
30989 var strBuf = this.strBuf;
30991 while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
30992 if (ch === 0x23) { // '#'
30993 ch = this.nextChar();
30994 var x = toHexDigit(ch);
30996 var x2 = toHexDigit(this.nextChar());
30998 error('Illegal digit in hex char in name: ' + x2);
31000 strBuf.push(String.fromCharCode((x << 4) | x2));
31002 strBuf.push('#', String.fromCharCode(ch));
31005 strBuf.push(String.fromCharCode(ch));
31008 if (strBuf.length > 128) {
31009 error('Warning: name token is longer than allowed by the spec: ' +
31012 return Name.get(strBuf.join(''));
31014 getHexString: function Lexer_getHexString() {
31015 var strBuf = this.strBuf;
31017 var ch = this.currentChar;
31018 var isFirstHex = true;
31023 warn('Unterminated hex string');
31025 } else if (ch === 0x3E) { // '>'
31028 } else if (specialChars[ch] === 1) {
31029 ch = this.nextChar();
31033 firstDigit = toHexDigit(ch);
31034 if (firstDigit === -1) {
31035 warn('Ignoring invalid character "' + ch + '" in hex string');
31036 ch = this.nextChar();
31040 secondDigit = toHexDigit(ch);
31041 if (secondDigit === -1) {
31042 warn('Ignoring invalid character "' + ch + '" in hex string');
31043 ch = this.nextChar();
31046 strBuf.push(String.fromCharCode((firstDigit << 4) | secondDigit));
31048 isFirstHex = !isFirstHex;
31049 ch = this.nextChar();
31052 return strBuf.join('');
31054 getObj: function Lexer_getObj() {
31055 // skip whitespace and comments
31056 var comment = false;
31057 var ch = this.currentChar;
31063 if (ch === 0x0A || ch === 0x0D) { // LF, CR
31066 } else if (ch === 0x25) { // '%'
31068 } else if (specialChars[ch] !== 1) {
31071 ch = this.nextChar();
31074 // start reading token
31076 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4'
31077 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9'
31078 case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.'
31079 return this.getNumber();
31081 return this.getString();
31083 return this.getName();
31084 // array punctuation
31087 return Cmd.get('[');
31090 return Cmd.get(']');
31091 // hex string or dict punctuation
31093 ch = this.nextChar();
31095 // dict punctuation
31097 return Cmd.get('<<');
31099 return this.getHexString();
31100 // dict punctuation
31102 ch = this.nextChar();
31105 return Cmd.get('>>');
31107 return Cmd.get('>');
31110 return Cmd.get('{');
31113 return Cmd.get('}');
31115 error('Illegal character: ' + ch);
31120 var str = String.fromCharCode(ch);
31121 var knownCommands = this.knownCommands;
31122 var knownCommandFound = knownCommands && knownCommands[str] !== undefined;
31123 while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
31124 // stop if known command is found and next character does not make
31125 // the str a command
31126 var possibleCommand = str + String.fromCharCode(ch);
31127 if (knownCommandFound && knownCommands[possibleCommand] === undefined) {
31130 if (str.length === 128) {
31131 error('Command token too long: ' + str.length);
31133 str = possibleCommand;
31134 knownCommandFound = knownCommands && knownCommands[str] !== undefined;
31136 if (str === 'true') {
31139 if (str === 'false') {
31142 if (str === 'null') {
31145 return Cmd.get(str);
31147 skipToNextLine: function Lexer_skipToNextLine() {
31148 var ch = this.currentChar;
31150 if (ch === 0x0D) { // CR
31151 ch = this.nextChar();
31152 if (ch === 0x0A) { // LF
31156 } else if (ch === 0x0A) { // LF
31160 ch = this.nextChar();
31168 var Linearization = {
31169 create: function LinearizationCreate(stream) {
31170 function getInt(name, allowZeroValue) {
31171 var obj = linDict.get(name);
31172 if (isInt(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) {
31175 throw new Error('The "' + name + '" parameter in the linearization ' +
31176 'dictionary is invalid.');
31178 function getHints() {
31179 var hints = linDict.get('H'), hintsLength, item;
31180 if (isArray(hints) &&
31181 ((hintsLength = hints.length) === 2 || hintsLength === 4)) {
31182 for (var index = 0; index < hintsLength; index++) {
31183 if (!(isInt(item = hints[index]) && item > 0)) {
31184 throw new Error('Hint (' + index +
31185 ') in the linearization dictionary is invalid.');
31190 throw new Error('Hint array in the linearization dictionary is invalid.');
31192 var parser = new Parser(new Lexer(stream), false, null);
31193 var obj1 = parser.getObj();
31194 var obj2 = parser.getObj();
31195 var obj3 = parser.getObj();
31196 var linDict = parser.getObj();
31198 if (!(isInt(obj1) && isInt(obj2) && isCmd(obj3, 'obj') && isDict(linDict) &&
31199 isNum(obj = linDict.get('Linearized')) && obj > 0)) {
31200 return null; // No valid linearization dictionary found.
31201 } else if ((length = getInt('L')) !== stream.length) {
31202 throw new Error('The "L" parameter in the linearization dictionary ' +
31203 'does not equal the stream length.');
31208 objectNumberFirst: getInt('O'),
31209 endFirst: getInt('E'),
31210 numPages: getInt('N'),
31211 mainXRefEntriesOffset: getInt('T'),
31212 pageFirst: (linDict.has('P') ? getInt('P', true) : 0)
31218 var PostScriptParser = (function PostScriptParserClosure() {
31219 function PostScriptParser(lexer) {
31220 this.lexer = lexer;
31221 this.operators = [];
31225 PostScriptParser.prototype = {
31226 nextToken: function PostScriptParser_nextToken() {
31227 this.prev = this.token;
31228 this.token = this.lexer.getToken();
31230 accept: function PostScriptParser_accept(type) {
31231 if (this.token.type === type) {
31237 expect: function PostScriptParser_expect(type) {
31238 if (this.accept(type)) {
31241 error('Unexpected symbol: found ' + this.token.type + ' expected ' +
31244 parse: function PostScriptParser_parse() {
31246 this.expect(PostScriptTokenTypes.LBRACE);
31248 this.expect(PostScriptTokenTypes.RBRACE);
31249 return this.operators;
31251 parseBlock: function PostScriptParser_parseBlock() {
31253 if (this.accept(PostScriptTokenTypes.NUMBER)) {
31254 this.operators.push(this.prev.value);
31255 } else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
31256 this.operators.push(this.prev.value);
31257 } else if (this.accept(PostScriptTokenTypes.LBRACE)) {
31258 this.parseCondition();
31264 parseCondition: function PostScriptParser_parseCondition() {
31265 // Add two place holders that will be updated later
31266 var conditionLocation = this.operators.length;
31267 this.operators.push(null, null);
31270 this.expect(PostScriptTokenTypes.RBRACE);
31271 if (this.accept(PostScriptTokenTypes.IF)) {
31272 // The true block is right after the 'if' so it just falls through on
31273 // true else it jumps and skips the true block.
31274 this.operators[conditionLocation] = this.operators.length;
31275 this.operators[conditionLocation + 1] = 'jz';
31276 } else if (this.accept(PostScriptTokenTypes.LBRACE)) {
31277 var jumpLocation = this.operators.length;
31278 this.operators.push(null, null);
31279 var endOfTrue = this.operators.length;
31281 this.expect(PostScriptTokenTypes.RBRACE);
31282 this.expect(PostScriptTokenTypes.IFELSE);
31283 // The jump is added at the end of the true block to skip the false
31285 this.operators[jumpLocation] = this.operators.length;
31286 this.operators[jumpLocation + 1] = 'j';
31288 this.operators[conditionLocation] = endOfTrue;
31289 this.operators[conditionLocation + 1] = 'jz';
31291 error('PS Function: error parsing conditional.');
31295 return PostScriptParser;
31298 var PostScriptTokenTypes = {
31307 var PostScriptToken = (function PostScriptTokenClosure() {
31308 function PostScriptToken(type, value) {
31310 this.value = value;
31315 PostScriptToken.getOperator = function PostScriptToken_getOperator(op) {
31316 var opValue = opCache[op];
31320 return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
31323 PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE,
31325 PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE,
31327 PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF');
31328 PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE,
31330 return PostScriptToken;
31333 var PostScriptLexer = (function PostScriptLexerClosure() {
31334 function PostScriptLexer(stream) {
31335 this.stream = stream;
31340 PostScriptLexer.prototype = {
31341 nextChar: function PostScriptLexer_nextChar() {
31342 return (this.currentChar = this.stream.getByte());
31344 getToken: function PostScriptLexer_getToken() {
31345 var comment = false;
31346 var ch = this.currentChar;
31355 if (ch === 0x0A || ch === 0x0D) {
31358 } else if (ch === 0x25) { // '%'
31360 } else if (!Lexer.isSpace(ch)) {
31363 ch = this.nextChar();
31366 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4'
31367 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9'
31368 case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.'
31369 return new PostScriptToken(PostScriptTokenTypes.NUMBER,
31373 return PostScriptToken.LBRACE;
31376 return PostScriptToken.RBRACE;
31379 var strBuf = this.strBuf;
31381 strBuf[0] = String.fromCharCode(ch);
31383 while ((ch = this.nextChar()) >= 0 && // and 'A'-'Z', 'a'-'z'
31384 ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) {
31385 strBuf.push(String.fromCharCode(ch));
31387 var str = strBuf.join('');
31388 switch (str.toLowerCase()) {
31390 return PostScriptToken.IF;
31392 return PostScriptToken.IFELSE;
31394 return PostScriptToken.getOperator(str);
31397 getNumber: function PostScriptLexer_getNumber() {
31398 var ch = this.currentChar;
31399 var strBuf = this.strBuf;
31401 strBuf[0] = String.fromCharCode(ch);
31403 while ((ch = this.nextChar()) >= 0) {
31404 if ((ch >= 0x30 && ch <= 0x39) || // '0'-'9'
31405 ch === 0x2D || ch === 0x2E) { // '-', '.'
31406 strBuf.push(String.fromCharCode(ch));
31411 var value = parseFloat(strBuf.join(''));
31412 if (isNaN(value)) {
31413 error('Invalid floating point number: ' + value);
31418 return PostScriptLexer;
31422 var Stream = (function StreamClosure() {
31423 function Stream(arrayBuffer, start, length, dict) {
31424 this.bytes = (arrayBuffer instanceof Uint8Array ?
31425 arrayBuffer : new Uint8Array(arrayBuffer));
31426 this.start = start || 0;
31427 this.pos = this.start;
31428 this.end = (start + length) || this.bytes.length;
31432 // required methods for a stream. if a particular stream does not
31433 // implement these, an error should be thrown
31434 Stream.prototype = {
31436 return this.end - this.start;
31439 return this.length === 0;
31441 getByte: function Stream_getByte() {
31442 if (this.pos >= this.end) {
31445 return this.bytes[this.pos++];
31447 getUint16: function Stream_getUint16() {
31448 var b0 = this.getByte();
31449 var b1 = this.getByte();
31450 if (b0 === -1 || b1 === -1) {
31453 return (b0 << 8) + b1;
31455 getInt32: function Stream_getInt32() {
31456 var b0 = this.getByte();
31457 var b1 = this.getByte();
31458 var b2 = this.getByte();
31459 var b3 = this.getByte();
31460 return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
31462 // returns subarray of original buffer
31463 // should only be read
31464 getBytes: function Stream_getBytes(length) {
31465 var bytes = this.bytes;
31466 var pos = this.pos;
31467 var strEnd = this.end;
31470 return bytes.subarray(pos, strEnd);
31472 var end = pos + length;
31473 if (end > strEnd) {
31477 return bytes.subarray(pos, end);
31479 peekByte: function Stream_peekByte() {
31480 var peekedByte = this.getByte();
31484 peekBytes: function Stream_peekBytes(length) {
31485 var bytes = this.getBytes(length);
31486 this.pos -= bytes.length;
31489 skip: function Stream_skip(n) {
31495 reset: function Stream_reset() {
31496 this.pos = this.start;
31498 moveStart: function Stream_moveStart() {
31499 this.start = this.pos;
31501 makeSubStream: function Stream_makeSubStream(start, length, dict) {
31502 return new Stream(this.bytes.buffer, start, length, dict);
31510 var StringStream = (function StringStreamClosure() {
31511 function StringStream(str) {
31512 var length = str.length;
31513 var bytes = new Uint8Array(length);
31514 for (var n = 0; n < length; ++n) {
31515 bytes[n] = str.charCodeAt(n);
31517 Stream.call(this, bytes);
31520 StringStream.prototype = Stream.prototype;
31522 return StringStream;
31525 // super class for the decoding streams
31526 var DecodeStream = (function DecodeStreamClosure() {
31527 // Lots of DecodeStreams are created whose buffers are never used. For these
31528 // we share a single empty buffer. This is (a) space-efficient and (b) avoids
31529 // having special cases that would be required if we used |null| for an empty
31531 var emptyBuffer = new Uint8Array(0);
31533 function DecodeStream(maybeMinBufferLength) {
31535 this.bufferLength = 0;
31537 this.buffer = emptyBuffer;
31538 this.minBufferLength = 512;
31539 if (maybeMinBufferLength) {
31540 // Compute the first power of two that is as big as maybeMinBufferLength.
31541 while (this.minBufferLength < maybeMinBufferLength) {
31542 this.minBufferLength *= 2;
31547 DecodeStream.prototype = {
31549 while (!this.eof && this.bufferLength === 0) {
31552 return this.bufferLength === 0;
31554 ensureBuffer: function DecodeStream_ensureBuffer(requested) {
31555 var buffer = this.buffer;
31556 if (requested <= buffer.byteLength) {
31559 var size = this.minBufferLength;
31560 while (size < requested) {
31563 var buffer2 = new Uint8Array(size);
31564 buffer2.set(buffer);
31565 return (this.buffer = buffer2);
31567 getByte: function DecodeStream_getByte() {
31568 var pos = this.pos;
31569 while (this.bufferLength <= pos) {
31575 return this.buffer[this.pos++];
31577 getUint16: function DecodeStream_getUint16() {
31578 var b0 = this.getByte();
31579 var b1 = this.getByte();
31580 if (b0 === -1 || b1 === -1) {
31583 return (b0 << 8) + b1;
31585 getInt32: function DecodeStream_getInt32() {
31586 var b0 = this.getByte();
31587 var b1 = this.getByte();
31588 var b2 = this.getByte();
31589 var b3 = this.getByte();
31590 return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
31592 getBytes: function DecodeStream_getBytes(length) {
31593 var end, pos = this.pos;
31596 this.ensureBuffer(pos + length);
31597 end = pos + length;
31599 while (!this.eof && this.bufferLength < end) {
31602 var bufEnd = this.bufferLength;
31603 if (end > bufEnd) {
31607 while (!this.eof) {
31610 end = this.bufferLength;
31614 return this.buffer.subarray(pos, end);
31616 peekByte: function DecodeStream_peekByte() {
31617 var peekedByte = this.getByte();
31621 peekBytes: function DecodeStream_peekBytes(length) {
31622 var bytes = this.getBytes(length);
31623 this.pos -= bytes.length;
31626 makeSubStream: function DecodeStream_makeSubStream(start, length, dict) {
31627 var end = start + length;
31628 while (this.bufferLength <= end && !this.eof) {
31631 return new Stream(this.buffer, start, length, dict);
31633 skip: function DecodeStream_skip(n) {
31639 reset: function DecodeStream_reset() {
31642 getBaseStreams: function DecodeStream_getBaseStreams() {
31643 if (this.str && this.str.getBaseStreams) {
31644 return this.str.getBaseStreams();
31650 return DecodeStream;
31653 var StreamsSequenceStream = (function StreamsSequenceStreamClosure() {
31654 function StreamsSequenceStream(streams) {
31655 this.streams = streams;
31656 DecodeStream.call(this, /* maybeLength = */ null);
31659 StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype);
31661 StreamsSequenceStream.prototype.readBlock =
31662 function streamSequenceStreamReadBlock() {
31664 var streams = this.streams;
31665 if (streams.length === 0) {
31669 var stream = streams.shift();
31670 var chunk = stream.getBytes();
31671 var bufferLength = this.bufferLength;
31672 var newLength = bufferLength + chunk.length;
31673 var buffer = this.ensureBuffer(newLength);
31674 buffer.set(chunk, bufferLength);
31675 this.bufferLength = newLength;
31678 StreamsSequenceStream.prototype.getBaseStreams =
31679 function StreamsSequenceStream_getBaseStreams() {
31681 var baseStreams = [];
31682 for (var i = 0, ii = this.streams.length; i < ii; i++) {
31683 var stream = this.streams[i];
31684 if (stream.getBaseStreams) {
31685 Util.appendToArray(baseStreams, stream.getBaseStreams());
31688 return baseStreams;
31691 return StreamsSequenceStream;
31694 var FlateStream = (function FlateStreamClosure() {
31695 var codeLenCodeMap = new Int32Array([
31696 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
31699 var lengthDecode = new Int32Array([
31700 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a,
31701 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f,
31702 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x40053, 0x40063, 0x40073,
31703 0x50083, 0x500a3, 0x500c3, 0x500e3, 0x00102, 0x00102, 0x00102
31706 var distDecode = new Int32Array([
31707 0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d,
31708 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1,
31709 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x90601, 0xa0801, 0xa0c01,
31710 0xb1001, 0xb1801, 0xc2001, 0xc3001, 0xd4001, 0xd6001
31713 var fixedLitCodeTab = [new Int32Array([
31714 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0,
31715 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0,
31716 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80078, 0x80038, 0x900d0,
31717 0x7010c, 0x80068, 0x80028, 0x900b0, 0x80008, 0x80088, 0x80048, 0x900f0,
31718 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c8,
31719 0x7010a, 0x80064, 0x80024, 0x900a8, 0x80004, 0x80084, 0x80044, 0x900e8,
31720 0x70106, 0x8005c, 0x8001c, 0x90098, 0x70116, 0x8007c, 0x8003c, 0x900d8,
31721 0x7010e, 0x8006c, 0x8002c, 0x900b8, 0x8000c, 0x8008c, 0x8004c, 0x900f8,
31722 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c4,
31723 0x70109, 0x80062, 0x80022, 0x900a4, 0x80002, 0x80082, 0x80042, 0x900e4,
31724 0x70105, 0x8005a, 0x8001a, 0x90094, 0x70115, 0x8007a, 0x8003a, 0x900d4,
31725 0x7010d, 0x8006a, 0x8002a, 0x900b4, 0x8000a, 0x8008a, 0x8004a, 0x900f4,
31726 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cc,
31727 0x7010b, 0x80066, 0x80026, 0x900ac, 0x80006, 0x80086, 0x80046, 0x900ec,
31728 0x70107, 0x8005e, 0x8001e, 0x9009c, 0x70117, 0x8007e, 0x8003e, 0x900dc,
31729 0x7010f, 0x8006e, 0x8002e, 0x900bc, 0x8000e, 0x8008e, 0x8004e, 0x900fc,
31730 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c2,
31731 0x70108, 0x80061, 0x80021, 0x900a2, 0x80001, 0x80081, 0x80041, 0x900e2,
31732 0x70104, 0x80059, 0x80019, 0x90092, 0x70114, 0x80079, 0x80039, 0x900d2,
31733 0x7010c, 0x80069, 0x80029, 0x900b2, 0x80009, 0x80089, 0x80049, 0x900f2,
31734 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900ca,
31735 0x7010a, 0x80065, 0x80025, 0x900aa, 0x80005, 0x80085, 0x80045, 0x900ea,
31736 0x70106, 0x8005d, 0x8001d, 0x9009a, 0x70116, 0x8007d, 0x8003d, 0x900da,
31737 0x7010e, 0x8006d, 0x8002d, 0x900ba, 0x8000d, 0x8008d, 0x8004d, 0x900fa,
31738 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c6,
31739 0x70109, 0x80063, 0x80023, 0x900a6, 0x80003, 0x80083, 0x80043, 0x900e6,
31740 0x70105, 0x8005b, 0x8001b, 0x90096, 0x70115, 0x8007b, 0x8003b, 0x900d6,
31741 0x7010d, 0x8006b, 0x8002b, 0x900b6, 0x8000b, 0x8008b, 0x8004b, 0x900f6,
31742 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900ce,
31743 0x7010b, 0x80067, 0x80027, 0x900ae, 0x80007, 0x80087, 0x80047, 0x900ee,
31744 0x70107, 0x8005f, 0x8001f, 0x9009e, 0x70117, 0x8007f, 0x8003f, 0x900de,
31745 0x7010f, 0x8006f, 0x8002f, 0x900be, 0x8000f, 0x8008f, 0x8004f, 0x900fe,
31746 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c1,
31747 0x70108, 0x80060, 0x80020, 0x900a1, 0x80000, 0x80080, 0x80040, 0x900e1,
31748 0x70104, 0x80058, 0x80018, 0x90091, 0x70114, 0x80078, 0x80038, 0x900d1,
31749 0x7010c, 0x80068, 0x80028, 0x900b1, 0x80008, 0x80088, 0x80048, 0x900f1,
31750 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c9,
31751 0x7010a, 0x80064, 0x80024, 0x900a9, 0x80004, 0x80084, 0x80044, 0x900e9,
31752 0x70106, 0x8005c, 0x8001c, 0x90099, 0x70116, 0x8007c, 0x8003c, 0x900d9,
31753 0x7010e, 0x8006c, 0x8002c, 0x900b9, 0x8000c, 0x8008c, 0x8004c, 0x900f9,
31754 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c5,
31755 0x70109, 0x80062, 0x80022, 0x900a5, 0x80002, 0x80082, 0x80042, 0x900e5,
31756 0x70105, 0x8005a, 0x8001a, 0x90095, 0x70115, 0x8007a, 0x8003a, 0x900d5,
31757 0x7010d, 0x8006a, 0x8002a, 0x900b5, 0x8000a, 0x8008a, 0x8004a, 0x900f5,
31758 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cd,
31759 0x7010b, 0x80066, 0x80026, 0x900ad, 0x80006, 0x80086, 0x80046, 0x900ed,
31760 0x70107, 0x8005e, 0x8001e, 0x9009d, 0x70117, 0x8007e, 0x8003e, 0x900dd,
31761 0x7010f, 0x8006e, 0x8002e, 0x900bd, 0x8000e, 0x8008e, 0x8004e, 0x900fd,
31762 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c3,
31763 0x70108, 0x80061, 0x80021, 0x900a3, 0x80001, 0x80081, 0x80041, 0x900e3,
31764 0x70104, 0x80059, 0x80019, 0x90093, 0x70114, 0x80079, 0x80039, 0x900d3,
31765 0x7010c, 0x80069, 0x80029, 0x900b3, 0x80009, 0x80089, 0x80049, 0x900f3,
31766 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900cb,
31767 0x7010a, 0x80065, 0x80025, 0x900ab, 0x80005, 0x80085, 0x80045, 0x900eb,
31768 0x70106, 0x8005d, 0x8001d, 0x9009b, 0x70116, 0x8007d, 0x8003d, 0x900db,
31769 0x7010e, 0x8006d, 0x8002d, 0x900bb, 0x8000d, 0x8008d, 0x8004d, 0x900fb,
31770 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c7,
31771 0x70109, 0x80063, 0x80023, 0x900a7, 0x80003, 0x80083, 0x80043, 0x900e7,
31772 0x70105, 0x8005b, 0x8001b, 0x90097, 0x70115, 0x8007b, 0x8003b, 0x900d7,
31773 0x7010d, 0x8006b, 0x8002b, 0x900b7, 0x8000b, 0x8008b, 0x8004b, 0x900f7,
31774 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900cf,
31775 0x7010b, 0x80067, 0x80027, 0x900af, 0x80007, 0x80087, 0x80047, 0x900ef,
31776 0x70107, 0x8005f, 0x8001f, 0x9009f, 0x70117, 0x8007f, 0x8003f, 0x900df,
31777 0x7010f, 0x8006f, 0x8002f, 0x900bf, 0x8000f, 0x8008f, 0x8004f, 0x900ff
31780 var fixedDistCodeTab = [new Int32Array([
31781 0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c,
31782 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000,
31783 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x50015, 0x5000d, 0x5001d,
31784 0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000
31787 function FlateStream(str, maybeLength) {
31789 this.dict = str.dict;
31791 var cmf = str.getByte();
31792 var flg = str.getByte();
31793 if (cmf === -1 || flg === -1) {
31794 error('Invalid header in flate stream: ' + cmf + ', ' + flg);
31796 if ((cmf & 0x0f) !== 0x08) {
31797 error('Unknown compression method in flate stream: ' + cmf + ', ' + flg);
31799 if ((((cmf << 8) + flg) % 31) !== 0) {
31800 error('Bad FCHECK in flate stream: ' + cmf + ', ' + flg);
31803 error('FDICT bit set in flate stream: ' + cmf + ', ' + flg);
31809 DecodeStream.call(this, maybeLength);
31812 FlateStream.prototype = Object.create(DecodeStream.prototype);
31814 FlateStream.prototype.getBits = function FlateStream_getBits(bits) {
31815 var str = this.str;
31816 var codeSize = this.codeSize;
31817 var codeBuf = this.codeBuf;
31820 while (codeSize < bits) {
31821 if ((b = str.getByte()) === -1) {
31822 error('Bad encoding in flate stream');
31824 codeBuf |= b << codeSize;
31827 b = codeBuf & ((1 << bits) - 1);
31828 this.codeBuf = codeBuf >> bits;
31829 this.codeSize = codeSize -= bits;
31834 FlateStream.prototype.getCode = function FlateStream_getCode(table) {
31835 var str = this.str;
31836 var codes = table[0];
31837 var maxLen = table[1];
31838 var codeSize = this.codeSize;
31839 var codeBuf = this.codeBuf;
31842 while (codeSize < maxLen) {
31843 if ((b = str.getByte()) === -1) {
31844 // premature end of stream. code might however still be valid.
31845 // codeSize < codeLen check below guards against incomplete codeVal.
31848 codeBuf |= (b << codeSize);
31851 var code = codes[codeBuf & ((1 << maxLen) - 1)];
31852 var codeLen = code >> 16;
31853 var codeVal = code & 0xffff;
31854 if (codeLen < 1 || codeSize < codeLen) {
31855 error('Bad encoding in flate stream');
31857 this.codeBuf = (codeBuf >> codeLen);
31858 this.codeSize = (codeSize - codeLen);
31862 FlateStream.prototype.generateHuffmanTable =
31863 function flateStreamGenerateHuffmanTable(lengths) {
31864 var n = lengths.length;
31866 // find max code length
31869 for (i = 0; i < n; ++i) {
31870 if (lengths[i] > maxLen) {
31871 maxLen = lengths[i];
31876 var size = 1 << maxLen;
31877 var codes = new Int32Array(size);
31878 for (var len = 1, code = 0, skip = 2;
31880 ++len, code <<= 1, skip <<= 1) {
31881 for (var val = 0; val < n; ++val) {
31882 if (lengths[val] === len) {
31883 // bit-reverse the code
31886 for (i = 0; i < len; ++i) {
31887 code2 = (code2 << 1) | (t & 1);
31891 // fill the table entries
31892 for (i = code2; i < size; i += skip) {
31893 codes[i] = (len << 16) | val;
31900 return [codes, maxLen];
31903 FlateStream.prototype.readBlock = function FlateStream_readBlock() {
31905 var str = this.str;
31906 // read block header
31907 var hdr = this.getBits(3);
31913 if (hdr === 0) { // uncompressed block
31916 if ((b = str.getByte()) === -1) {
31917 error('Bad block header in flate stream');
31920 if ((b = str.getByte()) === -1) {
31921 error('Bad block header in flate stream');
31923 blockLen |= (b << 8);
31924 if ((b = str.getByte()) === -1) {
31925 error('Bad block header in flate stream');
31928 if ((b = str.getByte()) === -1) {
31929 error('Bad block header in flate stream');
31932 if (check !== (~blockLen & 0xffff) &&
31933 (blockLen !== 0 || check !== 0)) {
31934 // Ignoring error for bad "empty" block (see issue 1277)
31935 error('Bad uncompressed block length in flate stream');
31941 var bufferLength = this.bufferLength;
31942 buffer = this.ensureBuffer(bufferLength + blockLen);
31943 var end = bufferLength + blockLen;
31944 this.bufferLength = end;
31945 if (blockLen === 0) {
31946 if (str.peekByte() === -1) {
31950 for (var n = bufferLength; n < end; ++n) {
31951 if ((b = str.getByte()) === -1) {
31963 if (hdr === 1) { // compressed block, fixed codes
31964 litCodeTable = fixedLitCodeTab;
31965 distCodeTable = fixedDistCodeTab;
31966 } else if (hdr === 2) { // compressed block, dynamic codes
31967 var numLitCodes = this.getBits(5) + 257;
31968 var numDistCodes = this.getBits(5) + 1;
31969 var numCodeLenCodes = this.getBits(4) + 4;
31971 // build the code lengths code table
31972 var codeLenCodeLengths = new Uint8Array(codeLenCodeMap.length);
31975 for (i = 0; i < numCodeLenCodes; ++i) {
31976 codeLenCodeLengths[codeLenCodeMap[i]] = this.getBits(3);
31978 var codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths);
31980 // build the literal and distance code tables
31983 var codes = numLitCodes + numDistCodes;
31984 var codeLengths = new Uint8Array(codes);
31985 var bitsLength, bitsOffset, what;
31986 while (i < codes) {
31987 var code = this.getCode(codeLenCodeTab);
31989 bitsLength = 2; bitsOffset = 3; what = len;
31990 } else if (code === 17) {
31991 bitsLength = 3; bitsOffset = 3; what = (len = 0);
31992 } else if (code === 18) {
31993 bitsLength = 7; bitsOffset = 11; what = (len = 0);
31995 codeLengths[i++] = len = code;
31999 var repeatLength = this.getBits(bitsLength) + bitsOffset;
32000 while (repeatLength-- > 0) {
32001 codeLengths[i++] = what;
32006 this.generateHuffmanTable(codeLengths.subarray(0, numLitCodes));
32008 this.generateHuffmanTable(codeLengths.subarray(numLitCodes, codes));
32010 error('Unknown block type in flate stream');
32013 buffer = this.buffer;
32014 var limit = buffer ? buffer.length : 0;
32015 var pos = this.bufferLength;
32017 var code1 = this.getCode(litCodeTable);
32019 if (pos + 1 >= limit) {
32020 buffer = this.ensureBuffer(pos + 1);
32021 limit = buffer.length;
32023 buffer[pos++] = code1;
32026 if (code1 === 256) {
32027 this.bufferLength = pos;
32031 code1 = lengthDecode[code1];
32032 var code2 = code1 >> 16;
32034 code2 = this.getBits(code2);
32036 len = (code1 & 0xffff) + code2;
32037 code1 = this.getCode(distCodeTable);
32038 code1 = distDecode[code1];
32039 code2 = code1 >> 16;
32041 code2 = this.getBits(code2);
32043 var dist = (code1 & 0xffff) + code2;
32044 if (pos + len >= limit) {
32045 buffer = this.ensureBuffer(pos + len);
32046 limit = buffer.length;
32048 for (var k = 0; k < len; ++k, ++pos) {
32049 buffer[pos] = buffer[pos - dist];
32054 return FlateStream;
32057 var PredictorStream = (function PredictorStreamClosure() {
32058 function PredictorStream(str, maybeLength, params) {
32059 var predictor = this.predictor = params.get('Predictor') || 1;
32061 if (predictor <= 1) {
32062 return str; // no prediction
32064 if (predictor !== 2 && (predictor < 10 || predictor > 15)) {
32065 error('Unsupported predictor: ' + predictor);
32068 if (predictor === 2) {
32069 this.readBlock = this.readBlockTiff;
32071 this.readBlock = this.readBlockPng;
32075 this.dict = str.dict;
32077 var colors = this.colors = params.get('Colors') || 1;
32078 var bits = this.bits = params.get('BitsPerComponent') || 8;
32079 var columns = this.columns = params.get('Columns') || 1;
32081 this.pixBytes = (colors * bits + 7) >> 3;
32082 this.rowBytes = (columns * colors * bits + 7) >> 3;
32084 DecodeStream.call(this, maybeLength);
32088 PredictorStream.prototype = Object.create(DecodeStream.prototype);
32090 PredictorStream.prototype.readBlockTiff =
32091 function predictorStreamReadBlockTiff() {
32092 var rowBytes = this.rowBytes;
32094 var bufferLength = this.bufferLength;
32095 var buffer = this.ensureBuffer(bufferLength + rowBytes);
32097 var bits = this.bits;
32098 var colors = this.colors;
32100 var rawBytes = this.str.getBytes(rowBytes);
32101 this.eof = !rawBytes.length;
32106 var inbuf = 0, outbuf = 0;
32107 var inbits = 0, outbits = 0;
32108 var pos = bufferLength;
32112 for (i = 0; i < rowBytes; ++i) {
32113 var c = rawBytes[i];
32114 inbuf = (inbuf << 8) | c;
32115 // bitwise addition is exclusive or
32116 // first shift inbuf and then add
32117 buffer[pos++] = (c ^ (inbuf >> colors)) & 0xFF;
32118 // truncate inbuf (assumes colors < 16)
32121 } else if (bits === 8) {
32122 for (i = 0; i < colors; ++i) {
32123 buffer[pos++] = rawBytes[i];
32125 for (; i < rowBytes; ++i) {
32126 buffer[pos] = buffer[pos - colors] + rawBytes[i];
32130 var compArray = new Uint8Array(colors + 1);
32131 var bitMask = (1 << bits) - 1;
32132 var j = 0, k = bufferLength;
32133 var columns = this.columns;
32134 for (i = 0; i < columns; ++i) {
32135 for (var kk = 0; kk < colors; ++kk) {
32136 if (inbits < bits) {
32137 inbuf = (inbuf << 8) | (rawBytes[j++] & 0xFF);
32140 compArray[kk] = (compArray[kk] +
32141 (inbuf >> (inbits - bits))) & bitMask;
32143 outbuf = (outbuf << bits) | compArray[kk];
32145 if (outbits >= 8) {
32146 buffer[k++] = (outbuf >> (outbits - 8)) & 0xFF;
32152 buffer[k++] = (outbuf << (8 - outbits)) +
32153 (inbuf & ((1 << (8 - outbits)) - 1));
32156 this.bufferLength += rowBytes;
32159 PredictorStream.prototype.readBlockPng =
32160 function predictorStreamReadBlockPng() {
32162 var rowBytes = this.rowBytes;
32163 var pixBytes = this.pixBytes;
32165 var predictor = this.str.getByte();
32166 var rawBytes = this.str.getBytes(rowBytes);
32167 this.eof = !rawBytes.length;
32172 var bufferLength = this.bufferLength;
32173 var buffer = this.ensureBuffer(bufferLength + rowBytes);
32175 var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
32176 if (prevRow.length === 0) {
32177 prevRow = new Uint8Array(rowBytes);
32180 var i, j = bufferLength, up, c;
32181 switch (predictor) {
32183 for (i = 0; i < rowBytes; ++i) {
32184 buffer[j++] = rawBytes[i];
32188 for (i = 0; i < pixBytes; ++i) {
32189 buffer[j++] = rawBytes[i];
32191 for (; i < rowBytes; ++i) {
32192 buffer[j] = (buffer[j - pixBytes] + rawBytes[i]) & 0xFF;
32197 for (i = 0; i < rowBytes; ++i) {
32198 buffer[j++] = (prevRow[i] + rawBytes[i]) & 0xFF;
32202 for (i = 0; i < pixBytes; ++i) {
32203 buffer[j++] = (prevRow[i] >> 1) + rawBytes[i];
32205 for (; i < rowBytes; ++i) {
32206 buffer[j] = (((prevRow[i] + buffer[j - pixBytes]) >> 1) +
32207 rawBytes[i]) & 0xFF;
32212 // we need to save the up left pixels values. the simplest way
32213 // is to create a new buffer
32214 for (i = 0; i < pixBytes; ++i) {
32217 buffer[j++] = up + c;
32219 for (; i < rowBytes; ++i) {
32221 var upLeft = prevRow[i - pixBytes];
32222 var left = buffer[j - pixBytes];
32223 var p = left + up - upLeft;
32233 var pc = p - upLeft;
32239 if (pa <= pb && pa <= pc) {
32240 buffer[j++] = left + c;
32241 } else if (pb <= pc) {
32242 buffer[j++] = up + c;
32244 buffer[j++] = upLeft + c;
32249 error('Unsupported predictor: ' + predictor);
32251 this.bufferLength += rowBytes;
32254 return PredictorStream;
32258 * Depending on the type of JPEG a JpegStream is handled in different ways. For
32259 * JPEG's that are supported natively such as DeviceGray and DeviceRGB the image
32260 * data is stored and then loaded by the browser. For unsupported JPEG's we use
32261 * a library to decode these images and the stream behaves like all the other
32264 var JpegStream = (function JpegStreamClosure() {
32265 function JpegStream(stream, maybeLength, dict, xref) {
32266 // Some images may contain 'junk' before the SOI (start-of-image) marker.
32267 // Note: this seems to mainly affect inline images.
32269 while ((ch = stream.getByte()) !== -1) {
32270 if (ch === 0xFF) { // Find the first byte of the SOI marker (0xFFD8).
32271 stream.skip(-1); // Reset the stream position to the SOI.
32275 this.stream = stream;
32276 this.maybeLength = maybeLength;
32279 DecodeStream.call(this, maybeLength);
32282 JpegStream.prototype = Object.create(DecodeStream.prototype);
32284 Object.defineProperty(JpegStream.prototype, 'bytes', {
32285 get: function JpegStream_bytes() {
32286 // If this.maybeLength is null, we'll get the entire stream.
32287 return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength));
32292 JpegStream.prototype.ensureBuffer = function JpegStream_ensureBuffer(req) {
32293 if (this.bufferLength) {
32297 var jpegImage = new JpegImage();
32299 // checking if values needs to be transformed before conversion
32300 if (this.forceRGB && this.dict && isArray(this.dict.get('Decode'))) {
32301 var decodeArr = this.dict.get('Decode');
32302 var bitsPerComponent = this.dict.get('BitsPerComponent') || 8;
32303 var decodeArrLength = decodeArr.length;
32304 var transform = new Int32Array(decodeArrLength);
32305 var transformNeeded = false;
32306 var maxValue = (1 << bitsPerComponent) - 1;
32307 for (var i = 0; i < decodeArrLength; i += 2) {
32308 transform[i] = ((decodeArr[i + 1] - decodeArr[i]) * 256) | 0;
32309 transform[i + 1] = (decodeArr[i] * maxValue) | 0;
32310 if (transform[i] !== 256 || transform[i + 1] !== 0) {
32311 transformNeeded = true;
32314 if (transformNeeded) {
32315 jpegImage.decodeTransform = transform;
32319 jpegImage.parse(this.bytes);
32320 var data = jpegImage.getData(this.drawWidth, this.drawHeight,
32322 this.buffer = data;
32323 this.bufferLength = data.length;
32326 error('JPEG error: ' + e);
32330 JpegStream.prototype.getBytes = function JpegStream_getBytes(length) {
32331 this.ensureBuffer();
32332 return this.buffer;
32335 JpegStream.prototype.getIR = function JpegStream_getIR() {
32336 return PDFJS.createObjectURL(this.bytes, 'image/jpeg');
32339 * Checks if the image can be decoded and displayed by the browser without any
32340 * further processing such as color space conversions.
32342 JpegStream.prototype.isNativelySupported =
32343 function JpegStream_isNativelySupported(xref, res) {
32344 var cs = ColorSpace.parse(this.dict.get('ColorSpace', 'CS'), xref, res);
32345 return cs.name === 'DeviceGray' || cs.name === 'DeviceRGB';
32348 * Checks if the image can be decoded by the browser.
32350 JpegStream.prototype.isNativelyDecodable =
32351 function JpegStream_isNativelyDecodable(xref, res) {
32352 var cs = ColorSpace.parse(this.dict.get('ColorSpace', 'CS'), xref, res);
32353 var numComps = cs.numComps;
32354 return numComps === 1 || numComps === 3;
32361 * For JPEG 2000's we use a library to decode these images and
32362 * the stream behaves like all the other DecodeStreams.
32364 var JpxStream = (function JpxStreamClosure() {
32365 function JpxStream(stream, maybeLength, dict) {
32366 this.stream = stream;
32367 this.maybeLength = maybeLength;
32370 DecodeStream.call(this, maybeLength);
32373 JpxStream.prototype = Object.create(DecodeStream.prototype);
32375 Object.defineProperty(JpxStream.prototype, 'bytes', {
32376 get: function JpxStream_bytes() {
32377 // If this.maybeLength is null, we'll get the entire stream.
32378 return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength));
32383 JpxStream.prototype.ensureBuffer = function JpxStream_ensureBuffer(req) {
32384 if (this.bufferLength) {
32388 var jpxImage = new JpxImage();
32389 jpxImage.parse(this.bytes);
32391 var width = jpxImage.width;
32392 var height = jpxImage.height;
32393 var componentsCount = jpxImage.componentsCount;
32394 var tileCount = jpxImage.tiles.length;
32395 if (tileCount === 1) {
32396 this.buffer = jpxImage.tiles[0].items;
32398 var data = new Uint8Array(width * height * componentsCount);
32400 for (var k = 0; k < tileCount; k++) {
32401 var tileComponents = jpxImage.tiles[k];
32402 var tileWidth = tileComponents.width;
32403 var tileHeight = tileComponents.height;
32404 var tileLeft = tileComponents.left;
32405 var tileTop = tileComponents.top;
32407 var src = tileComponents.items;
32408 var srcPosition = 0;
32409 var dataPosition = (width * tileTop + tileLeft) * componentsCount;
32410 var imgRowSize = width * componentsCount;
32411 var tileRowSize = tileWidth * componentsCount;
32413 for (var j = 0; j < tileHeight; j++) {
32414 var rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize);
32415 data.set(rowBytes, dataPosition);
32416 srcPosition += tileRowSize;
32417 dataPosition += imgRowSize;
32420 this.buffer = data;
32422 this.bufferLength = this.buffer.length;
32430 * For JBIG2's we use a library to decode these images and
32431 * the stream behaves like all the other DecodeStreams.
32433 var Jbig2Stream = (function Jbig2StreamClosure() {
32434 function Jbig2Stream(stream, maybeLength, dict) {
32435 this.stream = stream;
32436 this.maybeLength = maybeLength;
32439 DecodeStream.call(this, maybeLength);
32442 Jbig2Stream.prototype = Object.create(DecodeStream.prototype);
32444 Object.defineProperty(Jbig2Stream.prototype, 'bytes', {
32445 get: function Jbig2Stream_bytes() {
32446 // If this.maybeLength is null, we'll get the entire stream.
32447 return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength));
32452 Jbig2Stream.prototype.ensureBuffer = function Jbig2Stream_ensureBuffer(req) {
32453 if (this.bufferLength) {
32457 var jbig2Image = new Jbig2Image();
32459 var chunks = [], xref = this.dict.xref;
32460 var decodeParams = xref.fetchIfRef(this.dict.get('DecodeParms'));
32462 // According to the PDF specification, DecodeParms can be either
32463 // a dictionary, or an array whose elements are dictionaries.
32464 if (isArray(decodeParams)) {
32465 if (decodeParams.length > 1) {
32466 warn('JBIG2 - \'DecodeParms\' array with multiple elements ' +
32469 decodeParams = xref.fetchIfRef(decodeParams[0]);
32471 if (decodeParams && decodeParams.has('JBIG2Globals')) {
32472 var globalsStream = decodeParams.get('JBIG2Globals');
32473 var globals = globalsStream.getBytes();
32474 chunks.push({data: globals, start: 0, end: globals.length});
32476 chunks.push({data: this.bytes, start: 0, end: this.bytes.length});
32477 var data = jbig2Image.parseChunks(chunks);
32478 var dataLength = data.length;
32480 // JBIG2 had black as 1 and white as 0, inverting the colors
32481 for (var i = 0; i < dataLength; i++) {
32485 this.buffer = data;
32486 this.bufferLength = dataLength;
32490 return Jbig2Stream;
32493 var DecryptStream = (function DecryptStreamClosure() {
32494 function DecryptStream(str, maybeLength, decrypt) {
32496 this.dict = str.dict;
32497 this.decrypt = decrypt;
32498 this.nextChunk = null;
32499 this.initialized = false;
32501 DecodeStream.call(this, maybeLength);
32504 var chunkSize = 512;
32506 DecryptStream.prototype = Object.create(DecodeStream.prototype);
32508 DecryptStream.prototype.readBlock = function DecryptStream_readBlock() {
32510 if (this.initialized) {
32511 chunk = this.nextChunk;
32513 chunk = this.str.getBytes(chunkSize);
32514 this.initialized = true;
32516 if (!chunk || chunk.length === 0) {
32520 this.nextChunk = this.str.getBytes(chunkSize);
32521 var hasMoreData = this.nextChunk && this.nextChunk.length > 0;
32523 var decrypt = this.decrypt;
32524 chunk = decrypt(chunk, !hasMoreData);
32526 var bufferLength = this.bufferLength;
32527 var i, n = chunk.length;
32528 var buffer = this.ensureBuffer(bufferLength + n);
32529 for (i = 0; i < n; i++) {
32530 buffer[bufferLength++] = chunk[i];
32532 this.bufferLength = bufferLength;
32535 return DecryptStream;
32538 var Ascii85Stream = (function Ascii85StreamClosure() {
32539 function Ascii85Stream(str, maybeLength) {
32541 this.dict = str.dict;
32542 this.input = new Uint8Array(5);
32544 // Most streams increase in size when decoded, but Ascii85 streams
32545 // typically shrink by ~20%.
32547 maybeLength = 0.8 * maybeLength;
32549 DecodeStream.call(this, maybeLength);
32552 Ascii85Stream.prototype = Object.create(DecodeStream.prototype);
32554 Ascii85Stream.prototype.readBlock = function Ascii85Stream_readBlock() {
32555 var TILDA_CHAR = 0x7E; // '~'
32556 var Z_LOWER_CHAR = 0x7A; // 'z'
32559 var str = this.str;
32561 var c = str.getByte();
32562 while (Lexer.isSpace(c)) {
32566 if (c === EOF || c === TILDA_CHAR) {
32571 var bufferLength = this.bufferLength, buffer;
32574 // special code for z
32575 if (c === Z_LOWER_CHAR) {
32576 buffer = this.ensureBuffer(bufferLength + 4);
32577 for (i = 0; i < 4; ++i) {
32578 buffer[bufferLength + i] = 0;
32580 this.bufferLength += 4;
32582 var input = this.input;
32584 for (i = 1; i < 5; ++i) {
32586 while (Lexer.isSpace(c)) {
32592 if (c === EOF || c === TILDA_CHAR) {
32596 buffer = this.ensureBuffer(bufferLength + i - 1);
32597 this.bufferLength += i - 1;
32601 for (; i < 5; ++i) {
32602 input[i] = 0x21 + 84;
32607 for (i = 0; i < 5; ++i) {
32608 t = t * 85 + (input[i] - 0x21);
32611 for (i = 3; i >= 0; --i) {
32612 buffer[bufferLength + i] = t & 0xFF;
32618 return Ascii85Stream;
32621 var AsciiHexStream = (function AsciiHexStreamClosure() {
32622 function AsciiHexStream(str, maybeLength) {
32624 this.dict = str.dict;
32626 this.firstDigit = -1;
32628 // Most streams increase in size when decoded, but AsciiHex streams shrink
32631 maybeLength = 0.5 * maybeLength;
32633 DecodeStream.call(this, maybeLength);
32636 AsciiHexStream.prototype = Object.create(DecodeStream.prototype);
32638 AsciiHexStream.prototype.readBlock = function AsciiHexStream_readBlock() {
32639 var UPSTREAM_BLOCK_SIZE = 8000;
32640 var bytes = this.str.getBytes(UPSTREAM_BLOCK_SIZE);
32641 if (!bytes.length) {
32646 var maxDecodeLength = (bytes.length + 1) >> 1;
32647 var buffer = this.ensureBuffer(this.bufferLength + maxDecodeLength);
32648 var bufferLength = this.bufferLength;
32650 var firstDigit = this.firstDigit;
32651 for (var i = 0, ii = bytes.length; i < ii; i++) {
32652 var ch = bytes[i], digit;
32653 if (ch >= 0x30 && ch <= 0x39) { // '0'-'9'
32655 } else if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) {
32656 // 'A'-'Z', 'a'-'z'
32657 digit = (ch & 0x0F) + 9;
32658 } else if (ch === 0x3E) { // '>'
32661 } else { // probably whitespace
32662 continue; // ignoring
32664 if (firstDigit < 0) {
32665 firstDigit = digit;
32667 buffer[bufferLength++] = (firstDigit << 4) | digit;
32671 if (firstDigit >= 0 && this.eof) {
32673 buffer[bufferLength++] = (firstDigit << 4);
32676 this.firstDigit = firstDigit;
32677 this.bufferLength = bufferLength;
32680 return AsciiHexStream;
32683 var RunLengthStream = (function RunLengthStreamClosure() {
32684 function RunLengthStream(str, maybeLength) {
32686 this.dict = str.dict;
32688 DecodeStream.call(this, maybeLength);
32691 RunLengthStream.prototype = Object.create(DecodeStream.prototype);
32693 RunLengthStream.prototype.readBlock = function RunLengthStream_readBlock() {
32694 // The repeatHeader has following format. The first byte defines type of run
32695 // and amount of bytes to repeat/copy: n = 0 through 127 - copy next n bytes
32696 // (in addition to the second byte from the header), n = 129 through 255 -
32697 // duplicate the second byte from the header (257 - n) times, n = 128 - end.
32698 var repeatHeader = this.str.getBytes(2);
32699 if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) {
32705 var bufferLength = this.bufferLength;
32706 var n = repeatHeader[0];
32709 buffer = this.ensureBuffer(bufferLength + n + 1);
32710 buffer[bufferLength++] = repeatHeader[1];
32712 var source = this.str.getBytes(n);
32713 buffer.set(source, bufferLength);
32718 var b = repeatHeader[1];
32719 buffer = this.ensureBuffer(bufferLength + n + 1);
32720 for (var i = 0; i < n; i++) {
32721 buffer[bufferLength++] = b;
32724 this.bufferLength = bufferLength;
32727 return RunLengthStream;
32730 var CCITTFaxStream = (function CCITTFaxStreamClosure() {
32733 var twoDimPass = 0;
32734 var twoDimHoriz = 1;
32735 var twoDimVert0 = 2;
32736 var twoDimVertR1 = 3;
32737 var twoDimVertL1 = 4;
32738 var twoDimVertR2 = 5;
32739 var twoDimVertL2 = 6;
32740 var twoDimVertR3 = 7;
32741 var twoDimVertL3 = 8;
32743 var twoDimTable = [
32744 [-1, -1], [-1, -1], // 000000x
32745 [7, twoDimVertL3], // 0000010
32746 [7, twoDimVertR3], // 0000011
32747 [6, twoDimVertL2], [6, twoDimVertL2], // 000010x
32748 [6, twoDimVertR2], [6, twoDimVertR2], // 000011x
32749 [4, twoDimPass], [4, twoDimPass], // 0001xxx
32750 [4, twoDimPass], [4, twoDimPass],
32751 [4, twoDimPass], [4, twoDimPass],
32752 [4, twoDimPass], [4, twoDimPass],
32753 [3, twoDimHoriz], [3, twoDimHoriz], // 001xxxx
32754 [3, twoDimHoriz], [3, twoDimHoriz],
32755 [3, twoDimHoriz], [3, twoDimHoriz],
32756 [3, twoDimHoriz], [3, twoDimHoriz],
32757 [3, twoDimHoriz], [3, twoDimHoriz],
32758 [3, twoDimHoriz], [3, twoDimHoriz],
32759 [3, twoDimHoriz], [3, twoDimHoriz],
32760 [3, twoDimHoriz], [3, twoDimHoriz],
32761 [3, twoDimVertL1], [3, twoDimVertL1], // 010xxxx
32762 [3, twoDimVertL1], [3, twoDimVertL1],
32763 [3, twoDimVertL1], [3, twoDimVertL1],
32764 [3, twoDimVertL1], [3, twoDimVertL1],
32765 [3, twoDimVertL1], [3, twoDimVertL1],
32766 [3, twoDimVertL1], [3, twoDimVertL1],
32767 [3, twoDimVertL1], [3, twoDimVertL1],
32768 [3, twoDimVertL1], [3, twoDimVertL1],
32769 [3, twoDimVertR1], [3, twoDimVertR1], // 011xxxx
32770 [3, twoDimVertR1], [3, twoDimVertR1],
32771 [3, twoDimVertR1], [3, twoDimVertR1],
32772 [3, twoDimVertR1], [3, twoDimVertR1],
32773 [3, twoDimVertR1], [3, twoDimVertR1],
32774 [3, twoDimVertR1], [3, twoDimVertR1],
32775 [3, twoDimVertR1], [3, twoDimVertR1],
32776 [3, twoDimVertR1], [3, twoDimVertR1],
32777 [1, twoDimVert0], [1, twoDimVert0], // 1xxxxxx
32778 [1, twoDimVert0], [1, twoDimVert0],
32779 [1, twoDimVert0], [1, twoDimVert0],
32780 [1, twoDimVert0], [1, twoDimVert0],
32781 [1, twoDimVert0], [1, twoDimVert0],
32782 [1, twoDimVert0], [1, twoDimVert0],
32783 [1, twoDimVert0], [1, twoDimVert0],
32784 [1, twoDimVert0], [1, twoDimVert0],
32785 [1, twoDimVert0], [1, twoDimVert0],
32786 [1, twoDimVert0], [1, twoDimVert0],
32787 [1, twoDimVert0], [1, twoDimVert0],
32788 [1, twoDimVert0], [1, twoDimVert0],
32789 [1, twoDimVert0], [1, twoDimVert0],
32790 [1, twoDimVert0], [1, twoDimVert0],
32791 [1, twoDimVert0], [1, twoDimVert0],
32792 [1, twoDimVert0], [1, twoDimVert0],
32793 [1, twoDimVert0], [1, twoDimVert0],
32794 [1, twoDimVert0], [1, twoDimVert0],
32795 [1, twoDimVert0], [1, twoDimVert0],
32796 [1, twoDimVert0], [1, twoDimVert0],
32797 [1, twoDimVert0], [1, twoDimVert0],
32798 [1, twoDimVert0], [1, twoDimVert0],
32799 [1, twoDimVert0], [1, twoDimVert0],
32800 [1, twoDimVert0], [1, twoDimVert0],
32801 [1, twoDimVert0], [1, twoDimVert0],
32802 [1, twoDimVert0], [1, twoDimVert0],
32803 [1, twoDimVert0], [1, twoDimVert0],
32804 [1, twoDimVert0], [1, twoDimVert0],
32805 [1, twoDimVert0], [1, twoDimVert0],
32806 [1, twoDimVert0], [1, twoDimVert0],
32807 [1, twoDimVert0], [1, twoDimVert0],
32808 [1, twoDimVert0], [1, twoDimVert0]
32811 var whiteTable1 = [
32813 [12, ccittEOL], // 00001
32814 [-1, -1], [-1, -1], // 0001x
32815 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 001xx
32816 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 010xx
32817 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 011xx
32818 [11, 1792], [11, 1792], // 1000x
32819 [12, 1984], // 10010
32820 [12, 2048], // 10011
32821 [12, 2112], // 10100
32822 [12, 2176], // 10101
32823 [12, 2240], // 10110
32824 [12, 2304], // 10111
32825 [11, 1856], [11, 1856], // 1100x
32826 [11, 1920], [11, 1920], // 1101x
32827 [12, 2368], // 11100
32828 [12, 2432], // 11101
32829 [12, 2496], // 11110
32830 [12, 2560] // 11111
32833 var whiteTable2 = [
32834 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000000xx
32835 [8, 29], [8, 29], // 00000010x
32836 [8, 30], [8, 30], // 00000011x
32837 [8, 45], [8, 45], // 00000100x
32838 [8, 46], [8, 46], // 00000101x
32839 [7, 22], [7, 22], [7, 22], [7, 22], // 0000011xx
32840 [7, 23], [7, 23], [7, 23], [7, 23], // 0000100xx
32841 [8, 47], [8, 47], // 00001010x
32842 [8, 48], [8, 48], // 00001011x
32843 [6, 13], [6, 13], [6, 13], [6, 13], // 000011xxx
32844 [6, 13], [6, 13], [6, 13], [6, 13],
32845 [7, 20], [7, 20], [7, 20], [7, 20], // 0001000xx
32846 [8, 33], [8, 33], // 00010010x
32847 [8, 34], [8, 34], // 00010011x
32848 [8, 35], [8, 35], // 00010100x
32849 [8, 36], [8, 36], // 00010101x
32850 [8, 37], [8, 37], // 00010110x
32851 [8, 38], [8, 38], // 00010111x
32852 [7, 19], [7, 19], [7, 19], [7, 19], // 0001100xx
32853 [8, 31], [8, 31], // 00011010x
32854 [8, 32], [8, 32], // 00011011x
32855 [6, 1], [6, 1], [6, 1], [6, 1], // 000111xxx
32856 [6, 1], [6, 1], [6, 1], [6, 1],
32857 [6, 12], [6, 12], [6, 12], [6, 12], // 001000xxx
32858 [6, 12], [6, 12], [6, 12], [6, 12],
32859 [8, 53], [8, 53], // 00100100x
32860 [8, 54], [8, 54], // 00100101x
32861 [7, 26], [7, 26], [7, 26], [7, 26], // 0010011xx
32862 [8, 39], [8, 39], // 00101000x
32863 [8, 40], [8, 40], // 00101001x
32864 [8, 41], [8, 41], // 00101010x
32865 [8, 42], [8, 42], // 00101011x
32866 [8, 43], [8, 43], // 00101100x
32867 [8, 44], [8, 44], // 00101101x
32868 [7, 21], [7, 21], [7, 21], [7, 21], // 0010111xx
32869 [7, 28], [7, 28], [7, 28], [7, 28], // 0011000xx
32870 [8, 61], [8, 61], // 00110010x
32871 [8, 62], [8, 62], // 00110011x
32872 [8, 63], [8, 63], // 00110100x
32873 [8, 0], [8, 0], // 00110101x
32874 [8, 320], [8, 320], // 00110110x
32875 [8, 384], [8, 384], // 00110111x
32876 [5, 10], [5, 10], [5, 10], [5, 10], // 00111xxxx
32877 [5, 10], [5, 10], [5, 10], [5, 10],
32878 [5, 10], [5, 10], [5, 10], [5, 10],
32879 [5, 10], [5, 10], [5, 10], [5, 10],
32880 [5, 11], [5, 11], [5, 11], [5, 11], // 01000xxxx
32881 [5, 11], [5, 11], [5, 11], [5, 11],
32882 [5, 11], [5, 11], [5, 11], [5, 11],
32883 [5, 11], [5, 11], [5, 11], [5, 11],
32884 [7, 27], [7, 27], [7, 27], [7, 27], // 0100100xx
32885 [8, 59], [8, 59], // 01001010x
32886 [8, 60], [8, 60], // 01001011x
32887 [9, 1472], // 010011000
32888 [9, 1536], // 010011001
32889 [9, 1600], // 010011010
32890 [9, 1728], // 010011011
32891 [7, 18], [7, 18], [7, 18], [7, 18], // 0100111xx
32892 [7, 24], [7, 24], [7, 24], [7, 24], // 0101000xx
32893 [8, 49], [8, 49], // 01010010x
32894 [8, 50], [8, 50], // 01010011x
32895 [8, 51], [8, 51], // 01010100x
32896 [8, 52], [8, 52], // 01010101x
32897 [7, 25], [7, 25], [7, 25], [7, 25], // 0101011xx
32898 [8, 55], [8, 55], // 01011000x
32899 [8, 56], [8, 56], // 01011001x
32900 [8, 57], [8, 57], // 01011010x
32901 [8, 58], [8, 58], // 01011011x
32902 [6, 192], [6, 192], [6, 192], [6, 192], // 010111xxx
32903 [6, 192], [6, 192], [6, 192], [6, 192],
32904 [6, 1664], [6, 1664], [6, 1664], [6, 1664], // 011000xxx
32905 [6, 1664], [6, 1664], [6, 1664], [6, 1664],
32906 [8, 448], [8, 448], // 01100100x
32907 [8, 512], [8, 512], // 01100101x
32908 [9, 704], // 011001100
32909 [9, 768], // 011001101
32910 [8, 640], [8, 640], // 01100111x
32911 [8, 576], [8, 576], // 01101000x
32912 [9, 832], // 011010010
32913 [9, 896], // 011010011
32914 [9, 960], // 011010100
32915 [9, 1024], // 011010101
32916 [9, 1088], // 011010110
32917 [9, 1152], // 011010111
32918 [9, 1216], // 011011000
32919 [9, 1280], // 011011001
32920 [9, 1344], // 011011010
32921 [9, 1408], // 011011011
32922 [7, 256], [7, 256], [7, 256], [7, 256], // 0110111xx
32923 [4, 2], [4, 2], [4, 2], [4, 2], // 0111xxxxx
32924 [4, 2], [4, 2], [4, 2], [4, 2],
32925 [4, 2], [4, 2], [4, 2], [4, 2],
32926 [4, 2], [4, 2], [4, 2], [4, 2],
32927 [4, 2], [4, 2], [4, 2], [4, 2],
32928 [4, 2], [4, 2], [4, 2], [4, 2],
32929 [4, 2], [4, 2], [4, 2], [4, 2],
32930 [4, 2], [4, 2], [4, 2], [4, 2],
32931 [4, 3], [4, 3], [4, 3], [4, 3], // 1000xxxxx
32932 [4, 3], [4, 3], [4, 3], [4, 3],
32933 [4, 3], [4, 3], [4, 3], [4, 3],
32934 [4, 3], [4, 3], [4, 3], [4, 3],
32935 [4, 3], [4, 3], [4, 3], [4, 3],
32936 [4, 3], [4, 3], [4, 3], [4, 3],
32937 [4, 3], [4, 3], [4, 3], [4, 3],
32938 [4, 3], [4, 3], [4, 3], [4, 3],
32939 [5, 128], [5, 128], [5, 128], [5, 128], // 10010xxxx
32940 [5, 128], [5, 128], [5, 128], [5, 128],
32941 [5, 128], [5, 128], [5, 128], [5, 128],
32942 [5, 128], [5, 128], [5, 128], [5, 128],
32943 [5, 8], [5, 8], [5, 8], [5, 8], // 10011xxxx
32944 [5, 8], [5, 8], [5, 8], [5, 8],
32945 [5, 8], [5, 8], [5, 8], [5, 8],
32946 [5, 8], [5, 8], [5, 8], [5, 8],
32947 [5, 9], [5, 9], [5, 9], [5, 9], // 10100xxxx
32948 [5, 9], [5, 9], [5, 9], [5, 9],
32949 [5, 9], [5, 9], [5, 9], [5, 9],
32950 [5, 9], [5, 9], [5, 9], [5, 9],
32951 [6, 16], [6, 16], [6, 16], [6, 16], // 101010xxx
32952 [6, 16], [6, 16], [6, 16], [6, 16],
32953 [6, 17], [6, 17], [6, 17], [6, 17], // 101011xxx
32954 [6, 17], [6, 17], [6, 17], [6, 17],
32955 [4, 4], [4, 4], [4, 4], [4, 4], // 1011xxxxx
32956 [4, 4], [4, 4], [4, 4], [4, 4],
32957 [4, 4], [4, 4], [4, 4], [4, 4],
32958 [4, 4], [4, 4], [4, 4], [4, 4],
32959 [4, 4], [4, 4], [4, 4], [4, 4],
32960 [4, 4], [4, 4], [4, 4], [4, 4],
32961 [4, 4], [4, 4], [4, 4], [4, 4],
32962 [4, 4], [4, 4], [4, 4], [4, 4],
32963 [4, 5], [4, 5], [4, 5], [4, 5], // 1100xxxxx
32964 [4, 5], [4, 5], [4, 5], [4, 5],
32965 [4, 5], [4, 5], [4, 5], [4, 5],
32966 [4, 5], [4, 5], [4, 5], [4, 5],
32967 [4, 5], [4, 5], [4, 5], [4, 5],
32968 [4, 5], [4, 5], [4, 5], [4, 5],
32969 [4, 5], [4, 5], [4, 5], [4, 5],
32970 [4, 5], [4, 5], [4, 5], [4, 5],
32971 [6, 14], [6, 14], [6, 14], [6, 14], // 110100xxx
32972 [6, 14], [6, 14], [6, 14], [6, 14],
32973 [6, 15], [6, 15], [6, 15], [6, 15], // 110101xxx
32974 [6, 15], [6, 15], [6, 15], [6, 15],
32975 [5, 64], [5, 64], [5, 64], [5, 64], // 11011xxxx
32976 [5, 64], [5, 64], [5, 64], [5, 64],
32977 [5, 64], [5, 64], [5, 64], [5, 64],
32978 [5, 64], [5, 64], [5, 64], [5, 64],
32979 [4, 6], [4, 6], [4, 6], [4, 6], // 1110xxxxx
32980 [4, 6], [4, 6], [4, 6], [4, 6],
32981 [4, 6], [4, 6], [4, 6], [4, 6],
32982 [4, 6], [4, 6], [4, 6], [4, 6],
32983 [4, 6], [4, 6], [4, 6], [4, 6],
32984 [4, 6], [4, 6], [4, 6], [4, 6],
32985 [4, 6], [4, 6], [4, 6], [4, 6],
32986 [4, 6], [4, 6], [4, 6], [4, 6],
32987 [4, 7], [4, 7], [4, 7], [4, 7], // 1111xxxxx
32988 [4, 7], [4, 7], [4, 7], [4, 7],
32989 [4, 7], [4, 7], [4, 7], [4, 7],
32990 [4, 7], [4, 7], [4, 7], [4, 7],
32991 [4, 7], [4, 7], [4, 7], [4, 7],
32992 [4, 7], [4, 7], [4, 7], [4, 7],
32993 [4, 7], [4, 7], [4, 7], [4, 7],
32994 [4, 7], [4, 7], [4, 7], [4, 7]
32997 var blackTable1 = [
32998 [-1, -1], [-1, -1], // 000000000000x
32999 [12, ccittEOL], [12, ccittEOL], // 000000000001x
33000 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000001xx
33001 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000010xx
33002 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000011xx
33003 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000100xx
33004 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000101xx
33005 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000110xx
33006 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000111xx
33007 [11, 1792], [11, 1792], [11, 1792], [11, 1792], // 00000001000xx
33008 [12, 1984], [12, 1984], // 000000010010x
33009 [12, 2048], [12, 2048], // 000000010011x
33010 [12, 2112], [12, 2112], // 000000010100x
33011 [12, 2176], [12, 2176], // 000000010101x
33012 [12, 2240], [12, 2240], // 000000010110x
33013 [12, 2304], [12, 2304], // 000000010111x
33014 [11, 1856], [11, 1856], [11, 1856], [11, 1856], // 00000001100xx
33015 [11, 1920], [11, 1920], [11, 1920], [11, 1920], // 00000001101xx
33016 [12, 2368], [12, 2368], // 000000011100x
33017 [12, 2432], [12, 2432], // 000000011101x
33018 [12, 2496], [12, 2496], // 000000011110x
33019 [12, 2560], [12, 2560], // 000000011111x
33020 [10, 18], [10, 18], [10, 18], [10, 18], // 0000001000xxx
33021 [10, 18], [10, 18], [10, 18], [10, 18],
33022 [12, 52], [12, 52], // 000000100100x
33023 [13, 640], // 0000001001010
33024 [13, 704], // 0000001001011
33025 [13, 768], // 0000001001100
33026 [13, 832], // 0000001001101
33027 [12, 55], [12, 55], // 000000100111x
33028 [12, 56], [12, 56], // 000000101000x
33029 [13, 1280], // 0000001010010
33030 [13, 1344], // 0000001010011
33031 [13, 1408], // 0000001010100
33032 [13, 1472], // 0000001010101
33033 [12, 59], [12, 59], // 000000101011x
33034 [12, 60], [12, 60], // 000000101100x
33035 [13, 1536], // 0000001011010
33036 [13, 1600], // 0000001011011
33037 [11, 24], [11, 24], [11, 24], [11, 24], // 00000010111xx
33038 [11, 25], [11, 25], [11, 25], [11, 25], // 00000011000xx
33039 [13, 1664], // 0000001100100
33040 [13, 1728], // 0000001100101
33041 [12, 320], [12, 320], // 000000110011x
33042 [12, 384], [12, 384], // 000000110100x
33043 [12, 448], [12, 448], // 000000110101x
33044 [13, 512], // 0000001101100
33045 [13, 576], // 0000001101101
33046 [12, 53], [12, 53], // 000000110111x
33047 [12, 54], [12, 54], // 000000111000x
33048 [13, 896], // 0000001110010
33049 [13, 960], // 0000001110011
33050 [13, 1024], // 0000001110100
33051 [13, 1088], // 0000001110101
33052 [13, 1152], // 0000001110110
33053 [13, 1216], // 0000001110111
33054 [10, 64], [10, 64], [10, 64], [10, 64], // 0000001111xxx
33055 [10, 64], [10, 64], [10, 64], [10, 64]
33058 var blackTable2 = [
33059 [8, 13], [8, 13], [8, 13], [8, 13], // 00000100xxxx
33060 [8, 13], [8, 13], [8, 13], [8, 13],
33061 [8, 13], [8, 13], [8, 13], [8, 13],
33062 [8, 13], [8, 13], [8, 13], [8, 13],
33063 [11, 23], [11, 23], // 00000101000x
33064 [12, 50], // 000001010010
33065 [12, 51], // 000001010011
33066 [12, 44], // 000001010100
33067 [12, 45], // 000001010101
33068 [12, 46], // 000001010110
33069 [12, 47], // 000001010111
33070 [12, 57], // 000001011000
33071 [12, 58], // 000001011001
33072 [12, 61], // 000001011010
33073 [12, 256], // 000001011011
33074 [10, 16], [10, 16], [10, 16], [10, 16], // 0000010111xx
33075 [10, 17], [10, 17], [10, 17], [10, 17], // 0000011000xx
33076 [12, 48], // 000001100100
33077 [12, 49], // 000001100101
33078 [12, 62], // 000001100110
33079 [12, 63], // 000001100111
33080 [12, 30], // 000001101000
33081 [12, 31], // 000001101001
33082 [12, 32], // 000001101010
33083 [12, 33], // 000001101011
33084 [12, 40], // 000001101100
33085 [12, 41], // 000001101101
33086 [11, 22], [11, 22], // 00000110111x
33087 [8, 14], [8, 14], [8, 14], [8, 14], // 00000111xxxx
33088 [8, 14], [8, 14], [8, 14], [8, 14],
33089 [8, 14], [8, 14], [8, 14], [8, 14],
33090 [8, 14], [8, 14], [8, 14], [8, 14],
33091 [7, 10], [7, 10], [7, 10], [7, 10], // 0000100xxxxx
33092 [7, 10], [7, 10], [7, 10], [7, 10],
33093 [7, 10], [7, 10], [7, 10], [7, 10],
33094 [7, 10], [7, 10], [7, 10], [7, 10],
33095 [7, 10], [7, 10], [7, 10], [7, 10],
33096 [7, 10], [7, 10], [7, 10], [7, 10],
33097 [7, 10], [7, 10], [7, 10], [7, 10],
33098 [7, 10], [7, 10], [7, 10], [7, 10],
33099 [7, 11], [7, 11], [7, 11], [7, 11], // 0000101xxxxx
33100 [7, 11], [7, 11], [7, 11], [7, 11],
33101 [7, 11], [7, 11], [7, 11], [7, 11],
33102 [7, 11], [7, 11], [7, 11], [7, 11],
33103 [7, 11], [7, 11], [7, 11], [7, 11],
33104 [7, 11], [7, 11], [7, 11], [7, 11],
33105 [7, 11], [7, 11], [7, 11], [7, 11],
33106 [7, 11], [7, 11], [7, 11], [7, 11],
33107 [9, 15], [9, 15], [9, 15], [9, 15], // 000011000xxx
33108 [9, 15], [9, 15], [9, 15], [9, 15],
33109 [12, 128], // 000011001000
33110 [12, 192], // 000011001001
33111 [12, 26], // 000011001010
33112 [12, 27], // 000011001011
33113 [12, 28], // 000011001100
33114 [12, 29], // 000011001101
33115 [11, 19], [11, 19], // 00001100111x
33116 [11, 20], [11, 20], // 00001101000x
33117 [12, 34], // 000011010010
33118 [12, 35], // 000011010011
33119 [12, 36], // 000011010100
33120 [12, 37], // 000011010101
33121 [12, 38], // 000011010110
33122 [12, 39], // 000011010111
33123 [11, 21], [11, 21], // 00001101100x
33124 [12, 42], // 000011011010
33125 [12, 43], // 000011011011
33126 [10, 0], [10, 0], [10, 0], [10, 0], // 0000110111xx
33127 [7, 12], [7, 12], [7, 12], [7, 12], // 0000111xxxxx
33128 [7, 12], [7, 12], [7, 12], [7, 12],
33129 [7, 12], [7, 12], [7, 12], [7, 12],
33130 [7, 12], [7, 12], [7, 12], [7, 12],
33131 [7, 12], [7, 12], [7, 12], [7, 12],
33132 [7, 12], [7, 12], [7, 12], [7, 12],
33133 [7, 12], [7, 12], [7, 12], [7, 12],
33134 [7, 12], [7, 12], [7, 12], [7, 12]
33137 var blackTable3 = [
33138 [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000xx
33141 [5, 7], [5, 7], // 00011x
33142 [4, 6], [4, 6], [4, 6], [4, 6], // 0010xx
33143 [4, 5], [4, 5], [4, 5], [4, 5], // 0011xx
33144 [3, 1], [3, 1], [3, 1], [3, 1], // 010xxx
33145 [3, 1], [3, 1], [3, 1], [3, 1],
33146 [3, 4], [3, 4], [3, 4], [3, 4], // 011xxx
33147 [3, 4], [3, 4], [3, 4], [3, 4],
33148 [2, 3], [2, 3], [2, 3], [2, 3], // 10xxxx
33149 [2, 3], [2, 3], [2, 3], [2, 3],
33150 [2, 3], [2, 3], [2, 3], [2, 3],
33151 [2, 3], [2, 3], [2, 3], [2, 3],
33152 [2, 2], [2, 2], [2, 2], [2, 2], // 11xxxx
33153 [2, 2], [2, 2], [2, 2], [2, 2],
33154 [2, 2], [2, 2], [2, 2], [2, 2],
33155 [2, 2], [2, 2], [2, 2], [2, 2]
33158 function CCITTFaxStream(str, maybeLength, params) {
33160 this.dict = str.dict;
33162 params = params || Dict.empty;
33164 this.encoding = params.get('K') || 0;
33165 this.eoline = params.get('EndOfLine') || false;
33166 this.byteAlign = params.get('EncodedByteAlign') || false;
33167 this.columns = params.get('Columns') || 1728;
33168 this.rows = params.get('Rows') || 0;
33169 var eoblock = params.get('EndOfBlock');
33170 if (eoblock === null || eoblock === undefined) {
33173 this.eoblock = eoblock;
33174 this.black = params.get('BlackIs1') || false;
33176 this.codingLine = new Uint32Array(this.columns + 1);
33177 this.refLine = new Uint32Array(this.columns + 2);
33179 this.codingLine[0] = this.columns;
33180 this.codingPos = 0;
33183 this.nextLine2D = this.encoding < 0;
33184 this.inputBits = 0;
33186 this.outputBits = 0;
33189 while ((code1 = this.lookBits(12)) === 0) {
33195 if (this.encoding > 0) {
33196 this.nextLine2D = !this.lookBits(1);
33200 DecodeStream.call(this, maybeLength);
33203 CCITTFaxStream.prototype = Object.create(DecodeStream.prototype);
33205 CCITTFaxStream.prototype.readBlock = function CCITTFaxStream_readBlock() {
33206 while (!this.eof) {
33207 var c = this.lookChar();
33208 this.ensureBuffer(this.bufferLength + 1);
33209 this.buffer[this.bufferLength++] = c;
33213 CCITTFaxStream.prototype.addPixels =
33214 function ccittFaxStreamAddPixels(a1, blackPixels) {
33215 var codingLine = this.codingLine;
33216 var codingPos = this.codingPos;
33218 if (a1 > codingLine[codingPos]) {
33219 if (a1 > this.columns) {
33220 info('row is wrong length');
33224 if ((codingPos & 1) ^ blackPixels) {
33228 codingLine[codingPos] = a1;
33230 this.codingPos = codingPos;
33233 CCITTFaxStream.prototype.addPixelsNeg =
33234 function ccittFaxStreamAddPixelsNeg(a1, blackPixels) {
33235 var codingLine = this.codingLine;
33236 var codingPos = this.codingPos;
33238 if (a1 > codingLine[codingPos]) {
33239 if (a1 > this.columns) {
33240 info('row is wrong length');
33244 if ((codingPos & 1) ^ blackPixels) {
33248 codingLine[codingPos] = a1;
33249 } else if (a1 < codingLine[codingPos]) {
33251 info('invalid code');
33255 while (codingPos > 0 && a1 < codingLine[codingPos - 1]) {
33258 codingLine[codingPos] = a1;
33261 this.codingPos = codingPos;
33264 CCITTFaxStream.prototype.lookChar = function CCITTFaxStream_lookChar() {
33265 var refLine = this.refLine;
33266 var codingLine = this.codingLine;
33267 var columns = this.columns;
33269 var refPos, blackPixels, bits, i;
33271 if (this.outputBits === 0) {
33277 var code1, code2, code3;
33278 if (this.nextLine2D) {
33279 for (i = 0; codingLine[i] < columns; ++i) {
33280 refLine[i] = codingLine[i];
33282 refLine[i++] = columns;
33283 refLine[i] = columns;
33285 this.codingPos = 0;
33289 while (codingLine[this.codingPos] < columns) {
33290 code1 = this.getTwoDimCode();
33293 this.addPixels(refLine[refPos + 1], blackPixels);
33294 if (refLine[refPos + 1] < columns) {
33302 code1 += (code3 = this.getBlackCode());
33303 } while (code3 >= 64);
33305 code2 += (code3 = this.getWhiteCode());
33306 } while (code3 >= 64);
33309 code1 += (code3 = this.getWhiteCode());
33310 } while (code3 >= 64);
33312 code2 += (code3 = this.getBlackCode());
33313 } while (code3 >= 64);
33315 this.addPixels(codingLine[this.codingPos] +
33316 code1, blackPixels);
33317 if (codingLine[this.codingPos] < columns) {
33318 this.addPixels(codingLine[this.codingPos] + code2,
33321 while (refLine[refPos] <= codingLine[this.codingPos] &&
33322 refLine[refPos] < columns) {
33327 this.addPixels(refLine[refPos] + 3, blackPixels);
33329 if (codingLine[this.codingPos] < columns) {
33331 while (refLine[refPos] <= codingLine[this.codingPos] &&
33332 refLine[refPos] < columns) {
33338 this.addPixels(refLine[refPos] + 2, blackPixels);
33340 if (codingLine[this.codingPos] < columns) {
33342 while (refLine[refPos] <= codingLine[this.codingPos] &&
33343 refLine[refPos] < columns) {
33349 this.addPixels(refLine[refPos] + 1, blackPixels);
33351 if (codingLine[this.codingPos] < columns) {
33353 while (refLine[refPos] <= codingLine[this.codingPos] &&
33354 refLine[refPos] < columns) {
33360 this.addPixels(refLine[refPos], blackPixels);
33362 if (codingLine[this.codingPos] < columns) {
33364 while (refLine[refPos] <= codingLine[this.codingPos] &&
33365 refLine[refPos] < columns) {
33371 this.addPixelsNeg(refLine[refPos] - 3, blackPixels);
33373 if (codingLine[this.codingPos] < columns) {
33379 while (refLine[refPos] <= codingLine[this.codingPos] &&
33380 refLine[refPos] < columns) {
33386 this.addPixelsNeg(refLine[refPos] - 2, blackPixels);
33388 if (codingLine[this.codingPos] < columns) {
33394 while (refLine[refPos] <= codingLine[this.codingPos] &&
33395 refLine[refPos] < columns) {
33401 this.addPixelsNeg(refLine[refPos] - 1, blackPixels);
33403 if (codingLine[this.codingPos] < columns) {
33409 while (refLine[refPos] <= codingLine[this.codingPos] &&
33410 refLine[refPos] < columns) {
33416 this.addPixels(columns, 0);
33420 info('bad 2d code');
33421 this.addPixels(columns, 0);
33427 this.codingPos = 0;
33429 while (codingLine[this.codingPos] < columns) {
33433 code1 += (code3 = this.getBlackCode());
33434 } while (code3 >= 64);
33437 code1 += (code3 = this.getWhiteCode());
33438 } while (code3 >= 64);
33440 this.addPixels(codingLine[this.codingPos] + code1, blackPixels);
33445 var gotEOL = false;
33447 if (this.byteAlign) {
33448 this.inputBits &= ~7;
33451 if (!this.eoblock && this.row === this.rows - 1) {
33454 code1 = this.lookBits(12);
33456 while (code1 !== EOF && code1 !== 1) {
33458 code1 = this.lookBits(12);
33461 while (code1 === 0) {
33463 code1 = this.lookBits(12);
33469 } else if (code1 === EOF) {
33474 if (!this.eof && this.encoding > 0) {
33475 this.nextLine2D = !this.lookBits(1);
33479 if (this.eoblock && gotEOL && this.byteAlign) {
33480 code1 = this.lookBits(12);
33483 if (this.encoding > 0) {
33487 if (this.encoding >= 0) {
33488 for (i = 0; i < 4; ++i) {
33489 code1 = this.lookBits(12);
33491 info('bad rtc code: ' + code1);
33494 if (this.encoding > 0) {
33502 } else if (this.err && this.eoline) {
33504 code1 = this.lookBits(13);
33505 if (code1 === EOF) {
33509 if ((code1 >> 1) === 1) {
33515 if (this.encoding > 0) {
33517 this.nextLine2D = !(code1 & 1);
33521 if (codingLine[0] > 0) {
33522 this.outputBits = codingLine[this.codingPos = 0];
33524 this.outputBits = codingLine[this.codingPos = 1];
33530 if (this.outputBits >= 8) {
33531 c = (this.codingPos & 1) ? 0 : 0xFF;
33532 this.outputBits -= 8;
33533 if (this.outputBits === 0 && codingLine[this.codingPos] < columns) {
33535 this.outputBits = (codingLine[this.codingPos] -
33536 codingLine[this.codingPos - 1]);
33542 if (this.outputBits > bits) {
33544 if (!(this.codingPos & 1)) {
33545 c |= 0xFF >> (8 - bits);
33547 this.outputBits -= bits;
33550 c <<= this.outputBits;
33551 if (!(this.codingPos & 1)) {
33552 c |= 0xFF >> (8 - this.outputBits);
33554 bits -= this.outputBits;
33555 this.outputBits = 0;
33556 if (codingLine[this.codingPos] < columns) {
33558 this.outputBits = (codingLine[this.codingPos] -
33559 codingLine[this.codingPos - 1]);
33560 } else if (bits > 0) {
33573 // This functions returns the code found from the table.
33574 // The start and end parameters set the boundaries for searching the table.
33575 // The limit parameter is optional. Function returns an array with three
33576 // values. The first array element indicates whether a valid code is being
33577 // returned. The second array element is the actual code. The third array
33578 // element indicates whether EOF was reached.
33579 CCITTFaxStream.prototype.findTableCode =
33580 function ccittFaxStreamFindTableCode(start, end, table, limit) {
33582 var limitValue = limit || 0;
33583 for (var i = start; i <= end; ++i) {
33584 var code = this.lookBits(i);
33585 if (code === EOF) {
33586 return [true, 1, false];
33591 if (!limitValue || code >= limitValue) {
33592 var p = table[code - limitValue];
33595 return [true, p[1], true];
33599 return [false, 0, false];
33602 CCITTFaxStream.prototype.getTwoDimCode =
33603 function ccittFaxStreamGetTwoDimCode() {
33607 if (this.eoblock) {
33608 code = this.lookBits(7);
33609 p = twoDimTable[code];
33610 if (p && p[0] > 0) {
33611 this.eatBits(p[0]);
33615 var result = this.findTableCode(1, 7, twoDimTable);
33616 if (result[0] && result[2]) {
33620 info('Bad two dim code');
33624 CCITTFaxStream.prototype.getWhiteCode =
33625 function ccittFaxStreamGetWhiteCode() {
33629 if (this.eoblock) {
33630 code = this.lookBits(12);
33631 if (code === EOF) {
33635 if ((code >> 5) === 0) {
33636 p = whiteTable1[code];
33638 p = whiteTable2[code >> 3];
33642 this.eatBits(p[0]);
33646 var result = this.findTableCode(1, 9, whiteTable2);
33651 result = this.findTableCode(11, 12, whiteTable1);
33656 info('bad white code');
33661 CCITTFaxStream.prototype.getBlackCode =
33662 function ccittFaxStreamGetBlackCode() {
33665 if (this.eoblock) {
33666 code = this.lookBits(13);
33667 if (code === EOF) {
33670 if ((code >> 7) === 0) {
33671 p = blackTable1[code];
33672 } else if ((code >> 9) === 0 && (code >> 7) !== 0) {
33673 p = blackTable2[(code >> 1) - 64];
33675 p = blackTable3[code >> 7];
33679 this.eatBits(p[0]);
33683 var result = this.findTableCode(2, 6, blackTable3);
33688 result = this.findTableCode(7, 12, blackTable2, 64);
33693 result = this.findTableCode(10, 13, blackTable1);
33698 info('bad black code');
33703 CCITTFaxStream.prototype.lookBits = function CCITTFaxStream_lookBits(n) {
33705 while (this.inputBits < n) {
33706 if ((c = this.str.getByte()) === -1) {
33707 if (this.inputBits === 0) {
33710 return ((this.inputBuf << (n - this.inputBits)) &
33711 (0xFFFF >> (16 - n)));
33713 this.inputBuf = (this.inputBuf << 8) + c;
33714 this.inputBits += 8;
33716 return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n));
33719 CCITTFaxStream.prototype.eatBits = function CCITTFaxStream_eatBits(n) {
33720 if ((this.inputBits -= n) < 0) {
33721 this.inputBits = 0;
33725 return CCITTFaxStream;
33728 var LZWStream = (function LZWStreamClosure() {
33729 function LZWStream(str, maybeLength, earlyChange) {
33731 this.dict = str.dict;
33732 this.cachedData = 0;
33733 this.bitsCached = 0;
33735 var maxLzwDictionarySize = 4096;
33737 earlyChange: earlyChange,
33740 dictionaryValues: new Uint8Array(maxLzwDictionarySize),
33741 dictionaryLengths: new Uint16Array(maxLzwDictionarySize),
33742 dictionaryPrevCodes: new Uint16Array(maxLzwDictionarySize),
33743 currentSequence: new Uint8Array(maxLzwDictionarySize),
33744 currentSequenceLength: 0
33746 for (var i = 0; i < 256; ++i) {
33747 lzwState.dictionaryValues[i] = i;
33748 lzwState.dictionaryLengths[i] = 1;
33750 this.lzwState = lzwState;
33752 DecodeStream.call(this, maybeLength);
33755 LZWStream.prototype = Object.create(DecodeStream.prototype);
33757 LZWStream.prototype.readBits = function LZWStream_readBits(n) {
33758 var bitsCached = this.bitsCached;
33759 var cachedData = this.cachedData;
33760 while (bitsCached < n) {
33761 var c = this.str.getByte();
33766 cachedData = (cachedData << 8) | c;
33769 this.bitsCached = (bitsCached -= n);
33770 this.cachedData = cachedData;
33771 this.lastCode = null;
33772 return (cachedData >>> bitsCached) & ((1 << n) - 1);
33775 LZWStream.prototype.readBlock = function LZWStream_readBlock() {
33776 var blockSize = 512;
33777 var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize;
33780 var lzwState = this.lzwState;
33782 return; // eof was found
33785 var earlyChange = lzwState.earlyChange;
33786 var nextCode = lzwState.nextCode;
33787 var dictionaryValues = lzwState.dictionaryValues;
33788 var dictionaryLengths = lzwState.dictionaryLengths;
33789 var dictionaryPrevCodes = lzwState.dictionaryPrevCodes;
33790 var codeLength = lzwState.codeLength;
33791 var prevCode = lzwState.prevCode;
33792 var currentSequence = lzwState.currentSequence;
33793 var currentSequenceLength = lzwState.currentSequenceLength;
33795 var decodedLength = 0;
33796 var currentBufferLength = this.bufferLength;
33797 var buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);
33799 for (i = 0; i < blockSize; i++) {
33800 var code = this.readBits(codeLength);
33801 var hasPrev = currentSequenceLength > 0;
33803 currentSequence[0] = code;
33804 currentSequenceLength = 1;
33805 } else if (code >= 258) {
33806 if (code < nextCode) {
33807 currentSequenceLength = dictionaryLengths[code];
33808 for (j = currentSequenceLength - 1, q = code; j >= 0; j--) {
33809 currentSequence[j] = dictionaryValues[q];
33810 q = dictionaryPrevCodes[q];
33813 currentSequence[currentSequenceLength++] = currentSequence[0];
33815 } else if (code === 256) {
33818 currentSequenceLength = 0;
33822 delete this.lzwState;
33827 dictionaryPrevCodes[nextCode] = prevCode;
33828 dictionaryLengths[nextCode] = dictionaryLengths[prevCode] + 1;
33829 dictionaryValues[nextCode] = currentSequence[0];
33831 codeLength = (nextCode + earlyChange) & (nextCode + earlyChange - 1) ?
33832 codeLength : Math.min(Math.log(nextCode + earlyChange) /
33833 0.6931471805599453 + 1, 12) | 0;
33837 decodedLength += currentSequenceLength;
33838 if (estimatedDecodedSize < decodedLength) {
33840 estimatedDecodedSize += decodedSizeDelta;
33841 } while (estimatedDecodedSize < decodedLength);
33842 buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);
33844 for (j = 0; j < currentSequenceLength; j++) {
33845 buffer[currentBufferLength++] = currentSequence[j];
33848 lzwState.nextCode = nextCode;
33849 lzwState.codeLength = codeLength;
33850 lzwState.prevCode = prevCode;
33851 lzwState.currentSequenceLength = currentSequenceLength;
33853 this.bufferLength = currentBufferLength;
33859 var NullStream = (function NullStreamClosure() {
33860 function NullStream() {
33861 Stream.call(this, new Uint8Array(0));
33864 NullStream.prototype = Stream.prototype;
33870 var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
33871 setup: function wphSetup(handler) {
33874 function loadDocument(recoveryMode) {
33875 var loadDocumentCapability = createPromiseCapability();
33877 var parseSuccess = function parseSuccess() {
33878 var numPagesPromise = pdfManager.ensureDoc('numPages');
33879 var fingerprintPromise = pdfManager.ensureDoc('fingerprint');
33880 var encryptedPromise = pdfManager.ensureXRef('encrypt');
33881 Promise.all([numPagesPromise, fingerprintPromise,
33882 encryptedPromise]).then(function onDocReady(results) {
33884 numPages: results[0],
33885 fingerprint: results[1],
33886 encrypted: !!results[2],
33888 loadDocumentCapability.resolve(doc);
33893 var parseFailure = function parseFailure(e) {
33894 loadDocumentCapability.reject(e);
33897 pdfManager.ensureDoc('checkHeader', []).then(function() {
33898 pdfManager.ensureDoc('parseStartXRef', []).then(function() {
33899 pdfManager.ensureDoc('parse', [recoveryMode]).then(
33900 parseSuccess, parseFailure);
33904 return loadDocumentCapability.promise;
33907 function getPdfManager(data) {
33908 var pdfManagerCapability = createPromiseCapability();
33910 var source = data.source;
33911 var disableRange = data.disableRange;
33914 pdfManager = new LocalPdfManager(source.data, source.password);
33915 pdfManagerCapability.resolve();
33917 pdfManagerCapability.reject(ex);
33919 return pdfManagerCapability.promise;
33920 } else if (source.chunkedViewerLoading) {
33922 pdfManager = new NetworkPdfManager(source, handler);
33923 pdfManagerCapability.resolve();
33925 pdfManagerCapability.reject(ex);
33927 return pdfManagerCapability.promise;
33930 var networkManager = new NetworkManager(source.url, {
33931 httpHeaders: source.httpHeaders,
33932 withCredentials: source.withCredentials
33934 var cachedChunks = [];
33935 var fullRequestXhrId = networkManager.requestFull({
33936 onHeadersReceived: function onHeadersReceived() {
33937 if (disableRange) {
33941 var fullRequestXhr = networkManager.getRequestXhr(fullRequestXhrId);
33942 if (fullRequestXhr.getResponseHeader('Accept-Ranges') !== 'bytes') {
33946 var contentEncoding =
33947 fullRequestXhr.getResponseHeader('Content-Encoding') || 'identity';
33948 if (contentEncoding !== 'identity') {
33952 var length = fullRequestXhr.getResponseHeader('Content-Length');
33953 length = parseInt(length, 10);
33954 if (!isInt(length)) {
33957 source.length = length;
33958 if (length <= 2 * RANGE_CHUNK_SIZE) {
33959 // The file size is smaller than the size of two chunks, so it does
33960 // not make any sense to abort the request and retry with a range
33965 if (networkManager.isStreamingRequest(fullRequestXhrId)) {
33966 // We can continue fetching when progressive loading is enabled,
33967 // and we don't need the autoFetch feature.
33968 source.disableAutoFetch = true;
33970 // NOTE: by cancelling the full request, and then issuing range
33971 // requests, there will be an issue for sites where you can only
33972 // request the pdf once. However, if this is the case, then the
33973 // server should not be returning that it can support range
33975 networkManager.abortRequest(fullRequestXhrId);
33979 pdfManager = new NetworkPdfManager(source, handler);
33980 pdfManagerCapability.resolve(pdfManager);
33982 pdfManagerCapability.reject(ex);
33986 onProgressiveData: source.disableStream ? null :
33987 function onProgressiveData(chunk) {
33989 cachedChunks.push(chunk);
33992 pdfManager.sendProgressiveData(chunk);
33995 onDone: function onDone(args) {
33997 return; // already processed
34001 if (args === null) {
34002 // TODO add some streaming manager, e.g. for unknown length files.
34003 // The data was returned in the onProgressiveData, combining...
34004 var pdfFileLength = 0, pos = 0;
34005 cachedChunks.forEach(function (chunk) {
34006 pdfFileLength += chunk.byteLength;
34008 if (source.length && pdfFileLength !== source.length) {
34009 warn('reported HTTP length is different from actual');
34011 var pdfFileArray = new Uint8Array(pdfFileLength);
34012 cachedChunks.forEach(function (chunk) {
34013 pdfFileArray.set(new Uint8Array(chunk), pos);
34014 pos += chunk.byteLength;
34016 pdfFile = pdfFileArray.buffer;
34018 pdfFile = args.chunk;
34021 // the data is array, instantiating directly from it
34023 pdfManager = new LocalPdfManager(pdfFile, source.password);
34024 pdfManagerCapability.resolve();
34026 pdfManagerCapability.reject(ex);
34030 onError: function onError(status) {
34032 if (status === 404) {
34033 exception = new MissingPDFException('Missing PDF "' +
34034 source.url + '".');
34035 handler.send('MissingPDF', exception);
34037 exception = new UnexpectedResponseException(
34038 'Unexpected server response (' + status +
34039 ') while retrieving PDF "' + source.url + '".', status);
34040 handler.send('UnexpectedResponse', exception);
34044 onProgress: function onProgress(evt) {
34045 handler.send('DocProgress', {
34046 loaded: evt.loaded,
34047 total: evt.lengthComputable ? evt.total : source.length
34052 return pdfManagerCapability.promise;
34055 handler.on('test', function wphSetupTest(data) {
34056 // check if Uint8Array can be sent to worker
34057 if (!(data instanceof Uint8Array)) {
34058 handler.send('test', false);
34061 // making sure postMessage transfers are working
34062 var supportTransfers = data[0] === 255;
34063 handler.postMessageTransfers = supportTransfers;
34064 // check if the response property is supported by xhr
34065 var xhr = new XMLHttpRequest();
34066 var responseExists = 'response' in xhr;
34067 // check if the property is actually implemented
34069 var dummy = xhr.responseType;
34071 responseExists = false;
34073 if (!responseExists) {
34074 handler.send('test', false);
34077 handler.send('test', {
34078 supportTypedArray: true,
34079 supportTransfers: supportTransfers
34083 handler.on('GetDocRequest', function wphSetupDoc(data) {
34085 var onSuccess = function(doc) {
34086 handler.send('GetDoc', { pdfInfo: doc });
34089 var onFailure = function(e) {
34090 if (e instanceof PasswordException) {
34091 if (e.code === PasswordResponses.NEED_PASSWORD) {
34092 handler.send('NeedPassword', e);
34093 } else if (e.code === PasswordResponses.INCORRECT_PASSWORD) {
34094 handler.send('IncorrectPassword', e);
34096 } else if (e instanceof InvalidPDFException) {
34097 handler.send('InvalidPDF', e);
34098 } else if (e instanceof MissingPDFException) {
34099 handler.send('MissingPDF', e);
34100 } else if (e instanceof UnexpectedResponseException) {
34101 handler.send('UnexpectedResponse', e);
34103 handler.send('UnknownError',
34104 new UnknownErrorException(e.message, e.toString()));
34108 PDFJS.maxImageSize = data.maxImageSize === undefined ?
34109 -1 : data.maxImageSize;
34110 PDFJS.disableFontFace = data.disableFontFace;
34111 PDFJS.disableCreateObjectURL = data.disableCreateObjectURL;
34112 PDFJS.verbosity = data.verbosity;
34113 PDFJS.cMapUrl = data.cMapUrl === undefined ?
34114 null : data.cMapUrl;
34115 PDFJS.cMapPacked = data.cMapPacked === true;
34117 getPdfManager(data).then(function () {
34118 handler.send('PDFManagerReady', null);
34119 pdfManager.onLoadedStream().then(function(stream) {
34120 handler.send('DataLoaded', { length: stream.bytes.byteLength });
34122 }).then(function pdfManagerReady() {
34123 loadDocument(false).then(onSuccess, function loadFailure(ex) {
34124 // Try again with recoveryMode == true
34125 if (!(ex instanceof XRefParseException)) {
34126 if (ex instanceof PasswordException) {
34127 // after password exception prepare to receive a new password
34128 // to repeat loading
34129 pdfManager.passwordChanged().then(pdfManagerReady);
34136 pdfManager.requestLoadedStream();
34137 pdfManager.onLoadedStream().then(function() {
34138 loadDocument(true).then(onSuccess, onFailure);
34144 handler.on('GetPage', function wphSetupGetPage(data) {
34145 return pdfManager.getPage(data.pageIndex).then(function(page) {
34146 var rotatePromise = pdfManager.ensure(page, 'rotate');
34147 var refPromise = pdfManager.ensure(page, 'ref');
34148 var viewPromise = pdfManager.ensure(page, 'view');
34150 return Promise.all([rotatePromise, refPromise, viewPromise]).then(
34151 function(results) {
34153 rotate: results[0],
34161 handler.on('GetPageIndex', function wphSetupGetPageIndex(data) {
34162 var ref = new Ref(data.ref.num, data.ref.gen);
34163 var catalog = pdfManager.pdfDocument.catalog;
34164 return catalog.getPageIndex(ref);
34167 handler.on('GetDestinations',
34168 function wphSetupGetDestinations(data) {
34169 return pdfManager.ensureCatalog('destinations');
34173 handler.on('GetDestination',
34174 function wphSetupGetDestination(data) {
34175 return pdfManager.ensureCatalog('getDestination', [ data.id ]);
34179 handler.on('GetAttachments',
34180 function wphSetupGetAttachments(data) {
34181 return pdfManager.ensureCatalog('attachments');
34185 handler.on('GetJavaScript',
34186 function wphSetupGetJavaScript(data) {
34187 return pdfManager.ensureCatalog('javaScript');
34191 handler.on('GetOutline',
34192 function wphSetupGetOutline(data) {
34193 return pdfManager.ensureCatalog('documentOutline');
34197 handler.on('GetMetadata',
34198 function wphSetupGetMetadata(data) {
34199 return Promise.all([pdfManager.ensureDoc('documentInfo'),
34200 pdfManager.ensureCatalog('metadata')]);
34204 handler.on('GetData', function wphSetupGetData(data) {
34205 pdfManager.requestLoadedStream();
34206 return pdfManager.onLoadedStream().then(function(stream) {
34207 return stream.bytes;
34211 handler.on('GetStats',
34212 function wphSetupGetStats(data) {
34213 return pdfManager.pdfDocument.xref.stats;
34217 handler.on('UpdatePassword', function wphSetupUpdatePassword(data) {
34218 pdfManager.updatePassword(data);
34221 handler.on('GetAnnotations', function wphSetupGetAnnotations(data) {
34222 return pdfManager.getPage(data.pageIndex).then(function(page) {
34223 return pdfManager.ensure(page, 'getAnnotationsData', []);
34227 handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
34228 pdfManager.getPage(data.pageIndex).then(function(page) {
34230 var pageNum = data.pageIndex + 1;
34231 var start = Date.now();
34232 // Pre compile the pdf page and fetch the fonts/images.
34233 page.getOperatorList(handler, data.intent).then(function(operatorList) {
34235 info('page=' + pageNum + ' - getOperatorList: time=' +
34236 (Date.now() - start) + 'ms, len=' + operatorList.fnArray.length);
34240 var minimumStackMessage =
34241 'worker.js: while trying to getPage() and getOperatorList()';
34243 var wrappedException;
34245 // Turn the error into an obj that can be serialized
34246 if (typeof e === 'string') {
34247 wrappedException = {
34249 stack: minimumStackMessage
34251 } else if (typeof e === 'object') {
34252 wrappedException = {
34253 message: e.message || e.toString(),
34254 stack: e.stack || minimumStackMessage
34257 wrappedException = {
34258 message: 'Unknown exception type: ' + (typeof e),
34259 stack: minimumStackMessage
34263 handler.send('PageError', {
34265 error: wrappedException,
34266 intent: data.intent
34272 handler.on('GetTextContent', function wphExtractText(data) {
34273 return pdfManager.getPage(data.pageIndex).then(function(page) {
34274 var pageNum = data.pageIndex + 1;
34275 var start = Date.now();
34276 return page.extractTextContent().then(function(textContent) {
34277 info('text indexing: page=' + pageNum + ' - time=' +
34278 (Date.now() - start) + 'ms');
34279 return textContent;
34284 handler.on('Cleanup', function wphCleanup(data) {
34285 return pdfManager.cleanup();
34288 handler.on('Terminate', function wphTerminate(data) {
34289 pdfManager.terminate();
34294 var consoleTimer = {};
34296 var workerConsole = {
34297 log: function log() {
34298 var args = Array.prototype.slice.call(arguments);
34299 globalScope.postMessage({
34300 action: 'console_log',
34305 error: function error() {
34306 var args = Array.prototype.slice.call(arguments);
34307 globalScope.postMessage({
34308 action: 'console_error',
34311 throw 'pdf.js execution error';
34314 time: function time(name) {
34315 consoleTimer[name] = Date.now();
34318 timeEnd: function timeEnd(name) {
34319 var time = consoleTimer[name];
34321 error('Unknown timer name ' + name);
34323 this.log('Timer:', name, Date.now() - time);
34329 if (typeof window === 'undefined') {
34330 if (!('console' in globalScope)) {
34331 globalScope.console = workerConsole;
34334 // Listen for unsupported features so we can pass them on to the main thread.
34335 PDFJS.UnsupportedManager.listen(function (msg) {
34336 globalScope.postMessage({
34337 action: '_unsupported_feature',
34342 var handler = new MessageHandler('worker_processor', this);
34343 WorkerMessageHandler.setup(handler);
34347 /* This class implements the QM Coder decoding as defined in
34348 * JPEG 2000 Part I Final Committee Draft Version 1.0
34349 * Annex C.3 Arithmetic decoding procedure
34350 * available at http://www.jpeg.org/public/fcd15444-1.pdf
34352 * The arithmetic decoder is used in conjunction with context models to decode
34353 * JPEG2000 and JBIG2 streams.
34355 var ArithmeticDecoder = (function ArithmeticDecoderClosure() {
34358 {qe: 0x5601, nmps: 1, nlps: 1, switchFlag: 1},
34359 {qe: 0x3401, nmps: 2, nlps: 6, switchFlag: 0},
34360 {qe: 0x1801, nmps: 3, nlps: 9, switchFlag: 0},
34361 {qe: 0x0AC1, nmps: 4, nlps: 12, switchFlag: 0},
34362 {qe: 0x0521, nmps: 5, nlps: 29, switchFlag: 0},
34363 {qe: 0x0221, nmps: 38, nlps: 33, switchFlag: 0},
34364 {qe: 0x5601, nmps: 7, nlps: 6, switchFlag: 1},
34365 {qe: 0x5401, nmps: 8, nlps: 14, switchFlag: 0},
34366 {qe: 0x4801, nmps: 9, nlps: 14, switchFlag: 0},
34367 {qe: 0x3801, nmps: 10, nlps: 14, switchFlag: 0},
34368 {qe: 0x3001, nmps: 11, nlps: 17, switchFlag: 0},
34369 {qe: 0x2401, nmps: 12, nlps: 18, switchFlag: 0},
34370 {qe: 0x1C01, nmps: 13, nlps: 20, switchFlag: 0},
34371 {qe: 0x1601, nmps: 29, nlps: 21, switchFlag: 0},
34372 {qe: 0x5601, nmps: 15, nlps: 14, switchFlag: 1},
34373 {qe: 0x5401, nmps: 16, nlps: 14, switchFlag: 0},
34374 {qe: 0x5101, nmps: 17, nlps: 15, switchFlag: 0},
34375 {qe: 0x4801, nmps: 18, nlps: 16, switchFlag: 0},
34376 {qe: 0x3801, nmps: 19, nlps: 17, switchFlag: 0},
34377 {qe: 0x3401, nmps: 20, nlps: 18, switchFlag: 0},
34378 {qe: 0x3001, nmps: 21, nlps: 19, switchFlag: 0},
34379 {qe: 0x2801, nmps: 22, nlps: 19, switchFlag: 0},
34380 {qe: 0x2401, nmps: 23, nlps: 20, switchFlag: 0},
34381 {qe: 0x2201, nmps: 24, nlps: 21, switchFlag: 0},
34382 {qe: 0x1C01, nmps: 25, nlps: 22, switchFlag: 0},
34383 {qe: 0x1801, nmps: 26, nlps: 23, switchFlag: 0},
34384 {qe: 0x1601, nmps: 27, nlps: 24, switchFlag: 0},
34385 {qe: 0x1401, nmps: 28, nlps: 25, switchFlag: 0},
34386 {qe: 0x1201, nmps: 29, nlps: 26, switchFlag: 0},
34387 {qe: 0x1101, nmps: 30, nlps: 27, switchFlag: 0},
34388 {qe: 0x0AC1, nmps: 31, nlps: 28, switchFlag: 0},
34389 {qe: 0x09C1, nmps: 32, nlps: 29, switchFlag: 0},
34390 {qe: 0x08A1, nmps: 33, nlps: 30, switchFlag: 0},
34391 {qe: 0x0521, nmps: 34, nlps: 31, switchFlag: 0},
34392 {qe: 0x0441, nmps: 35, nlps: 32, switchFlag: 0},
34393 {qe: 0x02A1, nmps: 36, nlps: 33, switchFlag: 0},
34394 {qe: 0x0221, nmps: 37, nlps: 34, switchFlag: 0},
34395 {qe: 0x0141, nmps: 38, nlps: 35, switchFlag: 0},
34396 {qe: 0x0111, nmps: 39, nlps: 36, switchFlag: 0},
34397 {qe: 0x0085, nmps: 40, nlps: 37, switchFlag: 0},
34398 {qe: 0x0049, nmps: 41, nlps: 38, switchFlag: 0},
34399 {qe: 0x0025, nmps: 42, nlps: 39, switchFlag: 0},
34400 {qe: 0x0015, nmps: 43, nlps: 40, switchFlag: 0},
34401 {qe: 0x0009, nmps: 44, nlps: 41, switchFlag: 0},
34402 {qe: 0x0005, nmps: 45, nlps: 42, switchFlag: 0},
34403 {qe: 0x0001, nmps: 45, nlps: 43, switchFlag: 0},
34404 {qe: 0x5601, nmps: 46, nlps: 46, switchFlag: 0}
34407 // C.3.5 Initialisation of the decoder (INITDEC)
34408 function ArithmeticDecoder(data, start, end) {
34411 this.dataEnd = end;
34413 this.chigh = data[start];
34418 this.chigh = ((this.chigh << 7) & 0xFFFF) | ((this.clow >> 9) & 0x7F);
34419 this.clow = (this.clow << 7) & 0xFFFF;
34424 ArithmeticDecoder.prototype = {
34425 // C.3.4 Compressed data input (BYTEIN)
34426 byteIn: function ArithmeticDecoder_byteIn() {
34427 var data = this.data;
34429 if (data[bp] === 0xFF) {
34430 var b1 = data[bp + 1];
34432 this.clow += 0xFF00;
34436 this.clow += (data[bp] << 9);
34442 this.clow += bp < this.dataEnd ? (data[bp] << 8) : 0xFF00;
34446 if (this.clow > 0xFFFF) {
34447 this.chigh += (this.clow >> 16);
34448 this.clow &= 0xFFFF;
34451 // C.3.2 Decoding a decision (DECODE)
34452 readBit: function ArithmeticDecoder_readBit(contexts, pos) {
34453 // contexts are packed into 1 byte:
34454 // highest 7 bits carry cx.index, lowest bit carries cx.mps
34455 var cx_index = contexts[pos] >> 1, cx_mps = contexts[pos] & 1;
34456 var qeTableIcx = QeTable[cx_index];
34457 var qeIcx = qeTableIcx.qe;
34459 var a = this.a - qeIcx;
34461 if (this.chigh < qeIcx) {
34466 cx_index = qeTableIcx.nmps;
34470 if (qeTableIcx.switchFlag === 1) {
34473 cx_index = qeTableIcx.nlps;
34476 this.chigh -= qeIcx;
34477 if ((a & 0x8000) !== 0) {
34484 if (qeTableIcx.switchFlag === 1) {
34487 cx_index = qeTableIcx.nlps;
34490 cx_index = qeTableIcx.nmps;
34495 if (this.ct === 0) {
34500 this.chigh = ((this.chigh << 1) & 0xFFFF) | ((this.clow >> 15) & 1);
34501 this.clow = (this.clow << 1) & 0xFFFF;
34503 } while ((a & 0x8000) === 0);
34506 contexts[pos] = cx_index << 1 | cx_mps;
34511 return ArithmeticDecoder;
34515 var JpegImage = (function jpegImage() {
34516 var dctZigZag = new Uint8Array([
34522 5, 12, 19, 26, 33, 40,
34523 48, 41, 34, 27, 20, 13, 6,
34524 7, 14, 21, 28, 35, 42, 49, 56,
34525 57, 50, 43, 36, 29, 22, 15,
34526 23, 30, 37, 44, 51, 58,
34527 59, 52, 45, 38, 31,
34534 var dctCos1 = 4017; // cos(pi/16)
34535 var dctSin1 = 799; // sin(pi/16)
34536 var dctCos3 = 3406; // cos(3*pi/16)
34537 var dctSin3 = 2276; // sin(3*pi/16)
34538 var dctCos6 = 1567; // cos(6*pi/16)
34539 var dctSin6 = 3784; // sin(6*pi/16)
34540 var dctSqrt2 = 5793; // sqrt(2)
34541 var dctSqrt1d2 = 2896; // sqrt(2) / 2
34543 function constructor() {
34546 function buildHuffmanTable(codeLengths, values) {
34547 var k = 0, code = [], i, j, length = 16;
34548 while (length > 0 && !codeLengths[length - 1]) {
34551 code.push({children: [], index: 0});
34552 var p = code[0], q;
34553 for (i = 0; i < length; i++) {
34554 for (j = 0; j < codeLengths[i]; j++) {
34556 p.children[p.index] = values[k];
34557 while (p.index > 0) {
34562 while (code.length <= i) {
34563 code.push(q = {children: [], index: 0});
34564 p.children[p.index] = q.children;
34569 if (i + 1 < length) {
34570 // p here points to last code
34571 code.push(q = {children: [], index: 0});
34572 p.children[p.index] = q.children;
34576 return code[0].children;
34579 function getBlockBufferOffset(component, row, col) {
34580 return 64 * ((component.blocksPerLine + 1) * row + col);
34583 function decodeScan(data, offset, frame, components, resetInterval,
34584 spectralStart, spectralEnd, successivePrev, successive) {
34585 var precision = frame.precision;
34586 var samplesPerLine = frame.samplesPerLine;
34587 var scanLines = frame.scanLines;
34588 var mcusPerLine = frame.mcusPerLine;
34589 var progressive = frame.progressive;
34590 var maxH = frame.maxH, maxV = frame.maxV;
34592 var startOffset = offset, bitsData = 0, bitsCount = 0;
34594 function readBit() {
34595 if (bitsCount > 0) {
34597 return (bitsData >> bitsCount) & 1;
34599 bitsData = data[offset++];
34600 if (bitsData === 0xFF) {
34601 var nextByte = data[offset++];
34603 throw 'unexpected marker: ' +
34604 ((bitsData << 8) | nextByte).toString(16);
34609 return bitsData >>> 7;
34612 function decodeHuffman(tree) {
34615 node = node[readBit()];
34616 if (typeof node === 'number') {
34619 if (typeof node !== 'object') {
34620 throw 'invalid huffman sequence';
34625 function receive(length) {
34627 while (length > 0) {
34628 n = (n << 1) | readBit();
34634 function receiveAndExtend(length) {
34635 if (length === 1) {
34636 return readBit() === 1 ? 1 : -1;
34638 var n = receive(length);
34639 if (n >= 1 << (length - 1)) {
34642 return n + (-1 << length) + 1;
34645 function decodeBaseline(component, offset) {
34646 var t = decodeHuffman(component.huffmanTableDC);
34647 var diff = t === 0 ? 0 : receiveAndExtend(t);
34648 component.blockData[offset] = (component.pred += diff);
34651 var rs = decodeHuffman(component.huffmanTableAC);
34652 var s = rs & 15, r = rs >> 4;
34661 var z = dctZigZag[k];
34662 component.blockData[offset + z] = receiveAndExtend(s);
34667 function decodeDCFirst(component, offset) {
34668 var t = decodeHuffman(component.huffmanTableDC);
34669 var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive);
34670 component.blockData[offset] = (component.pred += diff);
34673 function decodeDCSuccessive(component, offset) {
34674 component.blockData[offset] |= readBit() << successive;
34678 function decodeACFirst(component, offset) {
34683 var k = spectralStart, e = spectralEnd;
34685 var rs = decodeHuffman(component.huffmanTableAC);
34686 var s = rs & 15, r = rs >> 4;
34689 eobrun = receive(r) + (1 << r) - 1;
34696 var z = dctZigZag[k];
34697 component.blockData[offset + z] =
34698 receiveAndExtend(s) * (1 << successive);
34703 var successiveACState = 0, successiveACNextValue;
34704 function decodeACSuccessive(component, offset) {
34705 var k = spectralStart;
34706 var e = spectralEnd;
34711 var z = dctZigZag[k];
34712 switch (successiveACState) {
34713 case 0: // initial state
34714 rs = decodeHuffman(component.huffmanTableAC);
34719 eobrun = receive(r) + (1 << r);
34720 successiveACState = 4;
34723 successiveACState = 1;
34727 throw 'invalid ACn encoding';
34729 successiveACNextValue = receiveAndExtend(s);
34730 successiveACState = r ? 2 : 3;
34733 case 1: // skipping r zero items
34735 if (component.blockData[offset + z]) {
34736 component.blockData[offset + z] += (readBit() << successive);
34740 successiveACState = successiveACState === 2 ? 3 : 0;
34744 case 3: // set value for a zero item
34745 if (component.blockData[offset + z]) {
34746 component.blockData[offset + z] += (readBit() << successive);
34748 component.blockData[offset + z] =
34749 successiveACNextValue << successive;
34750 successiveACState = 0;
34754 if (component.blockData[offset + z]) {
34755 component.blockData[offset + z] += (readBit() << successive);
34761 if (successiveACState === 4) {
34763 if (eobrun === 0) {
34764 successiveACState = 0;
34769 function decodeMcu(component, decode, mcu, row, col) {
34770 var mcuRow = (mcu / mcusPerLine) | 0;
34771 var mcuCol = mcu % mcusPerLine;
34772 var blockRow = mcuRow * component.v + row;
34773 var blockCol = mcuCol * component.h + col;
34774 var offset = getBlockBufferOffset(component, blockRow, blockCol);
34775 decode(component, offset);
34778 function decodeBlock(component, decode, mcu) {
34779 var blockRow = (mcu / component.blocksPerLine) | 0;
34780 var blockCol = mcu % component.blocksPerLine;
34781 var offset = getBlockBufferOffset(component, blockRow, blockCol);
34782 decode(component, offset);
34785 var componentsLength = components.length;
34786 var component, i, j, k, n;
34789 if (spectralStart === 0) {
34790 decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
34792 decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
34795 decodeFn = decodeBaseline;
34798 var mcu = 0, marker;
34800 if (componentsLength === 1) {
34801 mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
34803 mcuExpected = mcusPerLine * frame.mcusPerColumn;
34805 if (!resetInterval) {
34806 resetInterval = mcuExpected;
34810 while (mcu < mcuExpected) {
34811 // reset interval stuff
34812 for (i = 0; i < componentsLength; i++) {
34813 components[i].pred = 0;
34817 if (componentsLength === 1) {
34818 component = components[0];
34819 for (n = 0; n < resetInterval; n++) {
34820 decodeBlock(component, decodeFn, mcu);
34824 for (n = 0; n < resetInterval; n++) {
34825 for (i = 0; i < componentsLength; i++) {
34826 component = components[i];
34829 for (j = 0; j < v; j++) {
34830 for (k = 0; k < h; k++) {
34831 decodeMcu(component, decodeFn, mcu, j, k);
34841 marker = (data[offset] << 8) | data[offset + 1];
34842 if (marker <= 0xFF00) {
34843 throw 'marker was not found';
34846 if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
34853 return offset - startOffset;
34856 // A port of poppler's IDCT method which in turn is taken from:
34857 // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
34858 // 'Practical Fast 1-D DCT Algorithms with 11 Multiplications',
34859 // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
34861 function quantizeAndInverse(component, blockBufferOffset, p) {
34862 var qt = component.quantizationTable, blockData = component.blockData;
34863 var v0, v1, v2, v3, v4, v5, v6, v7;
34864 var p0, p1, p2, p3, p4, p5, p6, p7;
34867 // inverse DCT on rows
34868 for (var row = 0; row < 64; row += 8) {
34869 // gather block data
34870 p0 = blockData[blockBufferOffset + row];
34871 p1 = blockData[blockBufferOffset + row + 1];
34872 p2 = blockData[blockBufferOffset + row + 2];
34873 p3 = blockData[blockBufferOffset + row + 3];
34874 p4 = blockData[blockBufferOffset + row + 4];
34875 p5 = blockData[blockBufferOffset + row + 5];
34876 p6 = blockData[blockBufferOffset + row + 6];
34877 p7 = blockData[blockBufferOffset + row + 7];
34882 // check for all-zero AC coefficients
34883 if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
34884 t = (dctSqrt2 * p0 + 512) >> 10;
34895 // dequant p1 ... p7
34905 v0 = (dctSqrt2 * p0 + 128) >> 8;
34906 v1 = (dctSqrt2 * p4 + 128) >> 8;
34909 v4 = (dctSqrt1d2 * (p1 - p7) + 128) >> 8;
34910 v7 = (dctSqrt1d2 * (p1 + p7) + 128) >> 8;
34915 v0 = (v0 + v1 + 1) >> 1;
34917 t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;
34918 v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;
34920 v4 = (v4 + v6 + 1) >> 1;
34922 v7 = (v7 + v5 + 1) >> 1;
34926 v0 = (v0 + v3 + 1) >> 1;
34928 v1 = (v1 + v2 + 1) >> 1;
34930 t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
34931 v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
34933 t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
34934 v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
34939 p[row + 7] = v0 - v7;
34940 p[row + 1] = v1 + v6;
34941 p[row + 6] = v1 - v6;
34942 p[row + 2] = v2 + v5;
34943 p[row + 5] = v2 - v5;
34944 p[row + 3] = v3 + v4;
34945 p[row + 4] = v3 - v4;
34948 // inverse DCT on columns
34949 for (var col = 0; col < 8; ++col) {
34959 // check for all-zero AC coefficients
34960 if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
34961 t = (dctSqrt2 * p0 + 8192) >> 14;
34962 // convert to 8 bit
34963 t = (t < -2040) ? 0 : (t >= 2024) ? 255 : (t + 2056) >> 4;
34964 blockData[blockBufferOffset + col] = t;
34965 blockData[blockBufferOffset + col + 8] = t;
34966 blockData[blockBufferOffset + col + 16] = t;
34967 blockData[blockBufferOffset + col + 24] = t;
34968 blockData[blockBufferOffset + col + 32] = t;
34969 blockData[blockBufferOffset + col + 40] = t;
34970 blockData[blockBufferOffset + col + 48] = t;
34971 blockData[blockBufferOffset + col + 56] = t;
34976 v0 = (dctSqrt2 * p0 + 2048) >> 12;
34977 v1 = (dctSqrt2 * p4 + 2048) >> 12;
34980 v4 = (dctSqrt1d2 * (p1 - p7) + 2048) >> 12;
34981 v7 = (dctSqrt1d2 * (p1 + p7) + 2048) >> 12;
34986 // Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when
34987 // converting to UInt8 range later.
34988 v0 = ((v0 + v1 + 1) >> 1) + 4112;
34990 t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
34991 v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
34993 v4 = (v4 + v6 + 1) >> 1;
34995 v7 = (v7 + v5 + 1) >> 1;
34999 v0 = (v0 + v3 + 1) >> 1;
35001 v1 = (v1 + v2 + 1) >> 1;
35003 t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
35004 v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
35006 t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
35007 v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
35020 // convert to 8-bit integers
35021 p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? 255 : p0 >> 4;
35022 p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? 255 : p1 >> 4;
35023 p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? 255 : p2 >> 4;
35024 p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? 255 : p3 >> 4;
35025 p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? 255 : p4 >> 4;
35026 p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? 255 : p5 >> 4;
35027 p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? 255 : p6 >> 4;
35028 p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? 255 : p7 >> 4;
35030 // store block data
35031 blockData[blockBufferOffset + col] = p0;
35032 blockData[blockBufferOffset + col + 8] = p1;
35033 blockData[blockBufferOffset + col + 16] = p2;
35034 blockData[blockBufferOffset + col + 24] = p3;
35035 blockData[blockBufferOffset + col + 32] = p4;
35036 blockData[blockBufferOffset + col + 40] = p5;
35037 blockData[blockBufferOffset + col + 48] = p6;
35038 blockData[blockBufferOffset + col + 56] = p7;
35042 function buildComponentData(frame, component) {
35043 var blocksPerLine = component.blocksPerLine;
35044 var blocksPerColumn = component.blocksPerColumn;
35045 var computationBuffer = new Int16Array(64);
35047 for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
35048 for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
35049 var offset = getBlockBufferOffset(component, blockRow, blockCol);
35050 quantizeAndInverse(component, offset, computationBuffer);
35053 return component.blockData;
35056 function clamp0to255(a) {
35057 return a <= 0 ? 0 : a >= 255 ? 255 : a;
35060 constructor.prototype = {
35061 parse: function parse(data) {
35063 function readUint16() {
35064 var value = (data[offset] << 8) | data[offset + 1];
35069 function readDataBlock() {
35070 var length = readUint16();
35071 var array = data.subarray(offset, offset + length - 2);
35072 offset += array.length;
35076 function prepareComponents(frame) {
35077 var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
35078 var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
35079 for (var i = 0; i < frame.components.length; i++) {
35080 component = frame.components[i];
35081 var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) *
35082 component.h / frame.maxH);
35083 var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) *
35084 component.v / frame.maxV);
35085 var blocksPerLineForMcu = mcusPerLine * component.h;
35086 var blocksPerColumnForMcu = mcusPerColumn * component.v;
35088 var blocksBufferSize = 64 * blocksPerColumnForMcu *
35089 (blocksPerLineForMcu + 1);
35090 component.blockData = new Int16Array(blocksBufferSize);
35091 component.blocksPerLine = blocksPerLine;
35092 component.blocksPerColumn = blocksPerColumn;
35094 frame.mcusPerLine = mcusPerLine;
35095 frame.mcusPerColumn = mcusPerColumn;
35098 var offset = 0, length = data.length;
35102 var frame, resetInterval;
35103 var quantizationTables = [];
35104 var huffmanTablesAC = [], huffmanTablesDC = [];
35105 var fileMarker = readUint16();
35106 if (fileMarker !== 0xFFD8) { // SOI (Start of Image)
35107 throw 'SOI not found';
35110 fileMarker = readUint16();
35111 while (fileMarker !== 0xFFD9) { // EOI (End of image)
35113 switch(fileMarker) {
35114 case 0xFFE0: // APP0 (Application Specific)
35115 case 0xFFE1: // APP1
35116 case 0xFFE2: // APP2
35117 case 0xFFE3: // APP3
35118 case 0xFFE4: // APP4
35119 case 0xFFE5: // APP5
35120 case 0xFFE6: // APP6
35121 case 0xFFE7: // APP7
35122 case 0xFFE8: // APP8
35123 case 0xFFE9: // APP9
35124 case 0xFFEA: // APP10
35125 case 0xFFEB: // APP11
35126 case 0xFFEC: // APP12
35127 case 0xFFED: // APP13
35128 case 0xFFEE: // APP14
35129 case 0xFFEF: // APP15
35130 case 0xFFFE: // COM (Comment)
35131 var appData = readDataBlock();
35133 if (fileMarker === 0xFFE0) {
35134 if (appData[0] === 0x4A && appData[1] === 0x46 &&
35135 appData[2] === 0x49 && appData[3] === 0x46 &&
35136 appData[4] === 0) { // 'JFIF\x00'
35138 version: { major: appData[5], minor: appData[6] },
35139 densityUnits: appData[7],
35140 xDensity: (appData[8] << 8) | appData[9],
35141 yDensity: (appData[10] << 8) | appData[11],
35142 thumbWidth: appData[12],
35143 thumbHeight: appData[13],
35144 thumbData: appData.subarray(14, 14 +
35145 3 * appData[12] * appData[13])
35149 // TODO APP1 - Exif
35150 if (fileMarker === 0xFFEE) {
35151 if (appData[0] === 0x41 && appData[1] === 0x64 &&
35152 appData[2] === 0x6F && appData[3] === 0x62 &&
35153 appData[4] === 0x65) { // 'Adobe'
35155 version: (appData[5] << 8) | appData[6],
35156 flags0: (appData[7] << 8) | appData[8],
35157 flags1: (appData[9] << 8) | appData[10],
35158 transformCode: appData[11]
35164 case 0xFFDB: // DQT (Define Quantization Tables)
35165 var quantizationTablesLength = readUint16();
35166 var quantizationTablesEnd = quantizationTablesLength + offset - 2;
35168 while (offset < quantizationTablesEnd) {
35169 var quantizationTableSpec = data[offset++];
35170 var tableData = new Uint16Array(64);
35171 if ((quantizationTableSpec >> 4) === 0) { // 8 bit values
35172 for (j = 0; j < 64; j++) {
35174 tableData[z] = data[offset++];
35176 } else if ((quantizationTableSpec >> 4) === 1) { //16 bit
35177 for (j = 0; j < 64; j++) {
35179 tableData[z] = readUint16();
35182 throw 'DQT: invalid table spec';
35184 quantizationTables[quantizationTableSpec & 15] = tableData;
35188 case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT)
35189 case 0xFFC1: // SOF1 (Start of Frame, Extended DCT)
35190 case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT)
35192 throw 'Only single frame JPEGs supported';
35194 readUint16(); // skip data length
35196 frame.extended = (fileMarker === 0xFFC1);
35197 frame.progressive = (fileMarker === 0xFFC2);
35198 frame.precision = data[offset++];
35199 frame.scanLines = readUint16();
35200 frame.samplesPerLine = readUint16();
35201 frame.components = [];
35202 frame.componentIds = {};
35203 var componentsCount = data[offset++], componentId;
35204 var maxH = 0, maxV = 0;
35205 for (i = 0; i < componentsCount; i++) {
35206 componentId = data[offset];
35207 var h = data[offset + 1] >> 4;
35208 var v = data[offset + 1] & 15;
35215 var qId = data[offset + 2];
35216 l = frame.components.push({
35219 quantizationTable: quantizationTables[qId]
35221 frame.componentIds[componentId] = l - 1;
35226 prepareComponents(frame);
35229 case 0xFFC4: // DHT (Define Huffman Tables)
35230 var huffmanLength = readUint16();
35231 for (i = 2; i < huffmanLength;) {
35232 var huffmanTableSpec = data[offset++];
35233 var codeLengths = new Uint8Array(16);
35234 var codeLengthSum = 0;
35235 for (j = 0; j < 16; j++, offset++) {
35236 codeLengthSum += (codeLengths[j] = data[offset]);
35238 var huffmanValues = new Uint8Array(codeLengthSum);
35239 for (j = 0; j < codeLengthSum; j++, offset++) {
35240 huffmanValues[j] = data[offset];
35242 i += 17 + codeLengthSum;
35244 ((huffmanTableSpec >> 4) === 0 ?
35245 huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] =
35246 buildHuffmanTable(codeLengths, huffmanValues);
35250 case 0xFFDD: // DRI (Define Restart Interval)
35251 readUint16(); // skip data length
35252 resetInterval = readUint16();
35255 case 0xFFDA: // SOS (Start of Scan)
35256 var scanLength = readUint16();
35257 var selectorsCount = data[offset++];
35258 var components = [], component;
35259 for (i = 0; i < selectorsCount; i++) {
35260 var componentIndex = frame.componentIds[data[offset++]];
35261 component = frame.components[componentIndex];
35262 var tableSpec = data[offset++];
35263 component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
35264 component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
35265 components.push(component);
35267 var spectralStart = data[offset++];
35268 var spectralEnd = data[offset++];
35269 var successiveApproximation = data[offset++];
35270 var processed = decodeScan(data, offset,
35271 frame, components, resetInterval,
35272 spectralStart, spectralEnd,
35273 successiveApproximation >> 4, successiveApproximation & 15);
35274 offset += processed;
35277 case 0xFFFF: // Fill bytes
35278 if (data[offset] !== 0xFF) { // Avoid skipping a valid marker.
35284 if (data[offset - 3] === 0xFF &&
35285 data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
35286 // could be incorrect encoding -- last 0xFF byte of the previous
35287 // block was eaten by the encoder
35291 throw 'unknown JPEG marker ' + fileMarker.toString(16);
35293 fileMarker = readUint16();
35296 this.width = frame.samplesPerLine;
35297 this.height = frame.scanLines;
35299 this.adobe = adobe;
35300 this.components = [];
35301 for (i = 0; i < frame.components.length; i++) {
35302 component = frame.components[i];
35303 this.components.push({
35304 output: buildComponentData(frame, component),
35305 scaleX: component.h / frame.maxH,
35306 scaleY: component.v / frame.maxV,
35307 blocksPerLine: component.blocksPerLine,
35308 blocksPerColumn: component.blocksPerColumn
35311 this.numComponents = this.components.length;
35314 _getLinearizedBlockData: function getLinearizedBlockData(width, height) {
35315 var scaleX = this.width / width, scaleY = this.height / height;
35317 var component, componentScaleX, componentScaleY, blocksPerScanline;
35322 var numComponents = this.components.length;
35323 var dataLength = width * height * numComponents;
35324 var data = new Uint8Array(dataLength);
35325 var xScaleBlockOffset = new Uint32Array(width);
35326 var mask3LSB = 0xfffffff8; // used to clear the 3 LSBs
35328 for (i = 0; i < numComponents; i++) {
35329 component = this.components[i];
35330 componentScaleX = component.scaleX * scaleX;
35331 componentScaleY = component.scaleY * scaleY;
35333 output = component.output;
35334 blocksPerScanline = (component.blocksPerLine + 1) << 3;
35335 // precalculate the xScaleBlockOffset
35336 for (x = 0; x < width; x++) {
35337 j = 0 | (x * componentScaleX);
35338 xScaleBlockOffset[x] = ((j & mask3LSB) << 3) | (j & 7);
35340 // linearize the blocks of the component
35341 for (y = 0; y < height; y++) {
35342 j = 0 | (y * componentScaleY);
35343 index = blocksPerScanline * (j & mask3LSB) | ((j & 7) << 3);
35344 for (x = 0; x < width; x++) {
35345 data[offset] = output[index + xScaleBlockOffset[x]];
35346 offset += numComponents;
35351 // decodeTransform contains pairs of multiplier (-256..256) and additive
35352 var transform = this.decodeTransform;
35354 for (i = 0; i < dataLength;) {
35355 for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
35356 data[i] = ((data[i] * transform[k]) >> 8) + transform[k + 1];
35363 _isColorConversionNeeded: function isColorConversionNeeded() {
35364 if (this.adobe && this.adobe.transformCode) {
35365 // The adobe transform marker overrides any previous setting
35367 } else if (this.numComponents === 3) {
35374 _convertYccToRgb: function convertYccToRgb(data) {
35376 for (var i = 0, length = data.length; i < length; i += 3) {
35380 data[i ] = clamp0to255(Y - 179.456 + 1.402 * Cr);
35381 data[i + 1] = clamp0to255(Y + 135.459 - 0.344 * Cb - 0.714 * Cr);
35382 data[i + 2] = clamp0to255(Y - 226.816 + 1.772 * Cb);
35387 _convertYcckToRgb: function convertYcckToRgb(data) {
35390 for (var i = 0, length = data.length; i < length; i += 4) {
35396 var r = -122.67195406894 +
35397 Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr -
35398 5.4080610064599e-5 * Y + 0.00048449797120281 * k -
35399 0.154362151871126) +
35400 Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y -
35401 0.00477271405408747 * k + 1.53380253221734) +
35402 Y * (0.000961250184130688 * Y - 0.00266257332283933 * k +
35403 0.48357088451265) +
35404 k * (-0.000336197177618394 * k + 0.484791561490776);
35406 var g = 107.268039397724 +
35407 Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr +
35408 0.000659397001245577 * Y + 0.000426105652938837 * k -
35409 0.176491792462875) +
35410 Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y +
35411 0.000770482631801132 * k - 0.151051492775562) +
35412 Y * (0.00126935368114843 * Y - 0.00265090189010898 * k +
35413 0.25802910206845) +
35414 k * (-0.000318913117588328 * k - 0.213742400323665);
35416 var b = -20.810012546947 +
35417 Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr +
35418 0.0020741088115012 * Y - 0.00288260236853442 * k +
35419 0.814272968359295) +
35420 Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y +
35421 0.000560833691242812 * k - 0.195152027534049) +
35422 Y * (0.00174418132927582 * Y - 0.00255243321439347 * k +
35423 0.116935020465145) +
35424 k * (-0.000343531996510555 * k + 0.24165260232407);
35426 data[offset++] = clamp0to255(r);
35427 data[offset++] = clamp0to255(g);
35428 data[offset++] = clamp0to255(b);
35433 _convertYcckToCmyk: function convertYcckToCmyk(data) {
35435 for (var i = 0, length = data.length; i < length; i += 4) {
35439 data[i ] = clamp0to255(434.456 - Y - 1.402 * Cr);
35440 data[i + 1] = clamp0to255(119.541 - Y + 0.344 * Cb + 0.714 * Cr);
35441 data[i + 2] = clamp0to255(481.816 - Y - 1.772 * Cb);
35442 // K in data[i + 3] is unchanged
35447 _convertCmykToRgb: function convertCmykToRgb(data) {
35450 var min = -255 * 255 * 255;
35451 var scale = 1 / 255 / 255;
35452 for (var i = 0, length = data.length; i < length; i += 4) {
35459 c * (-4.387332384609988 * c + 54.48615194189176 * m +
35460 18.82290502165302 * y + 212.25662451639585 * k -
35461 72734.4411664936) +
35462 m * (1.7149763477362134 * m - 5.6096736904047315 * y -
35463 17.873870861415444 * k - 1401.7366389350734) +
35464 y * (-2.5217340131683033 * y - 21.248923337353073 * k +
35465 4465.541406466231) -
35466 k * (21.86122147463605 * k + 48317.86113160301);
35468 c * (8.841041422036149 * c + 60.118027045597366 * m +
35469 6.871425592049007 * y + 31.159100130055922 * k -
35470 20220.756542821975) +
35471 m * (-15.310361306967817 * m + 17.575251261109482 * y +
35472 131.35250912493976 * k - 48691.05921601825) +
35473 y * (4.444339102852739 * y + 9.8632861493405 * k -
35474 6341.191035517494) -
35475 k * (20.737325471181034 * k + 47890.15695978492);
35477 c * (0.8842522430003296 * c + 8.078677503112928 * m +
35478 30.89978309703729 * y - 0.23883238689178934 * k -
35479 3616.812083916688) +
35480 m * (10.49593273432072 * m + 63.02378494754052 * y +
35481 50.606957656360734 * k - 28620.90484698408) +
35482 y * (0.03296041114873217 * y + 115.60384449646641 * k -
35483 49363.43385999684) -
35484 k * (22.33816807309886 * k + 45932.16563550634);
35486 data[offset++] = r >= 0 ? 255 : r <= min ? 0 : 255 + r * scale | 0;
35487 data[offset++] = g >= 0 ? 255 : g <= min ? 0 : 255 + g * scale | 0;
35488 data[offset++] = b >= 0 ? 255 : b <= min ? 0 : 255 + b * scale | 0;
35493 getData: function getData(width, height, forceRGBoutput) {
35494 if (this.numComponents > 4) {
35495 throw 'Unsupported color mode';
35497 // type of data: Uint8Array(width * height * numComponents)
35498 var data = this._getLinearizedBlockData(width, height);
35500 if (this.numComponents === 3) {
35501 return this._convertYccToRgb(data);
35502 } else if (this.numComponents === 4) {
35503 if (this._isColorConversionNeeded()) {
35504 if (forceRGBoutput) {
35505 return this._convertYcckToRgb(data);
35507 return this._convertYcckToCmyk(data);
35509 } else if (forceRGBoutput) {
35510 return this._convertCmykToRgb(data);
35517 return constructor;
35521 var JpxImage = (function JpxImageClosure() {
35523 var SubbandsGainLog2 = {
35529 function JpxImage() {
35530 this.failOnCorruptedImage = false;
35532 JpxImage.prototype = {
35533 parse: function JpxImage_parse(data) {
35535 var head = readUint16(data, 0);
35536 // No box header, immediate start of codestream (SOC)
35537 if (head === 0xFF4F) {
35538 this.parseCodestream(data, 0, data.length);
35542 var position = 0, length = data.length;
35543 while (position < length) {
35544 var headerSize = 8;
35545 var lbox = readUint32(data, position);
35546 var tbox = readUint32(data, position + 4);
35547 position += headerSize;
35549 // XLBox: read UInt64 according to spec.
35550 // JavaScript's int precision of 53 bit should be sufficient here.
35551 lbox = readUint32(data, position) * 4294967296 +
35552 readUint32(data, position + 4);
35557 lbox = length - position + headerSize;
35559 if (lbox < headerSize) {
35560 throw new Error('JPX Error: Invalid box field size');
35562 var dataLength = lbox - headerSize;
35563 var jumpDataLength = true;
35565 case 0x6A703268: // 'jp2h'
35566 jumpDataLength = false; // parsing child boxes
35568 case 0x636F6C72: // 'colr'
35569 // Colorspaces are not used, the CS from the PDF is used.
35570 var method = data[position];
35571 var precedence = data[position + 1];
35572 var approximation = data[position + 2];
35573 if (method === 1) {
35574 // enumerated colorspace
35575 var colorspace = readUint32(data, position + 3);
35576 switch (colorspace) {
35577 case 16: // this indicates a sRGB colorspace
35578 case 17: // this indicates a grayscale colorspace
35579 case 18: // this indicates a YUV colorspace
35582 warn('Unknown colorspace ' + colorspace);
35585 } else if (method === 2) {
35586 info('ICC profile not supported');
35589 case 0x6A703263: // 'jp2c'
35590 this.parseCodestream(data, position, position + dataLength);
35592 case 0x6A502020: // 'jP\024\024'
35593 if (0x0d0a870a !== readUint32(data, position)) {
35594 warn('Invalid JP2 signature');
35597 // The following header types are valid but currently not used:
35598 case 0x6A501A1A: // 'jP\032\032'
35599 case 0x66747970: // 'ftyp'
35600 case 0x72726571: // 'rreq'
35601 case 0x72657320: // 'res '
35602 case 0x69686472: // 'ihdr'
35605 var headerType = String.fromCharCode((tbox >> 24) & 0xFF,
35606 (tbox >> 16) & 0xFF,
35607 (tbox >> 8) & 0xFF,
35609 warn('Unsupported header type ' + tbox + ' (' + headerType + ')');
35612 if (jumpDataLength) {
35613 position += dataLength;
35617 parseImageProperties: function JpxImage_parseImageProperties(stream) {
35618 var newByte = stream.getByte();
35619 while (newByte >= 0) {
35620 var oldByte = newByte;
35621 newByte = stream.getByte();
35622 var code = (oldByte << 8) | newByte;
35623 // Image and tile size (SIZ)
35624 if (code === 0xFF51) {
35626 var Xsiz = stream.getInt32() >>> 0; // Byte 4
35627 var Ysiz = stream.getInt32() >>> 0; // Byte 8
35628 var XOsiz = stream.getInt32() >>> 0; // Byte 12
35629 var YOsiz = stream.getInt32() >>> 0; // Byte 16
35631 var Csiz = stream.getUint16(); // Byte 36
35632 this.width = Xsiz - XOsiz;
35633 this.height = Ysiz - YOsiz;
35634 this.componentsCount = Csiz;
35635 // Results are always returned as Uint8Arrays
35636 this.bitsPerComponent = 8;
35640 throw new Error('JPX Error: No size marker found in JPX stream');
35642 parseCodestream: function JpxImage_parseCodestream(data, start, end) {
35645 var doNotRecover = false;
35646 var position = start;
35647 while (position + 1 < end) {
35648 var code = readUint16(data, position);
35651 var length = 0, j, sqcd, spqcds, spqcdSize, scalarExpounded, tile;
35653 case 0xFF4F: // Start of codestream (SOC)
35654 context.mainHeader = true;
35656 case 0xFFD9: // End of codestream (EOC)
35658 case 0xFF51: // Image and tile size (SIZ)
35659 length = readUint16(data, position);
35661 siz.Xsiz = readUint32(data, position + 4);
35662 siz.Ysiz = readUint32(data, position + 8);
35663 siz.XOsiz = readUint32(data, position + 12);
35664 siz.YOsiz = readUint32(data, position + 16);
35665 siz.XTsiz = readUint32(data, position + 20);
35666 siz.YTsiz = readUint32(data, position + 24);
35667 siz.XTOsiz = readUint32(data, position + 28);
35668 siz.YTOsiz = readUint32(data, position + 32);
35669 var componentsCount = readUint16(data, position + 36);
35670 siz.Csiz = componentsCount;
35671 var components = [];
35673 for (var i = 0; i < componentsCount; i++) {
35675 precision: (data[j] & 0x7F) + 1,
35676 isSigned: !!(data[j] & 0x80),
35677 XRsiz: data[j + 1],
35680 calculateComponentDimensions(component, siz);
35681 components.push(component);
35684 context.components = components;
35685 calculateTileGrids(context, components);
35689 case 0xFF5C: // Quantization default (QCD)
35690 length = readUint16(data, position);
35694 switch (sqcd & 0x1F) {
35697 scalarExpounded = true;
35701 scalarExpounded = false;
35705 scalarExpounded = true;
35708 throw new Error('JPX Error: Invalid SQcd value ' + sqcd);
35710 qcd.noQuantization = (spqcdSize === 8);
35711 qcd.scalarExpounded = scalarExpounded;
35712 qcd.guardBits = sqcd >> 5;
35714 while (j < length + position) {
35716 if (spqcdSize === 8) {
35717 spqcd.epsilon = data[j++] >> 3;
35720 spqcd.epsilon = data[j] >> 3;
35721 spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1];
35724 spqcds.push(spqcd);
35726 qcd.SPqcds = spqcds;
35727 if (context.mainHeader) {
35730 context.currentTile.QCD = qcd;
35731 context.currentTile.QCC = [];
35734 case 0xFF5D: // Quantization component (QCC)
35735 length = readUint16(data, position);
35739 if (context.SIZ.Csiz < 257) {
35742 cqcc = readUint16(data, j);
35746 switch (sqcd & 0x1F) {
35749 scalarExpounded = true;
35753 scalarExpounded = false;
35757 scalarExpounded = true;
35760 throw new Error('JPX Error: Invalid SQcd value ' + sqcd);
35762 qcc.noQuantization = (spqcdSize === 8);
35763 qcc.scalarExpounded = scalarExpounded;
35764 qcc.guardBits = sqcd >> 5;
35766 while (j < (length + position)) {
35768 if (spqcdSize === 8) {
35769 spqcd.epsilon = data[j++] >> 3;
35772 spqcd.epsilon = data[j] >> 3;
35773 spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1];
35776 spqcds.push(spqcd);
35778 qcc.SPqcds = spqcds;
35779 if (context.mainHeader) {
35780 context.QCC[cqcc] = qcc;
35782 context.currentTile.QCC[cqcc] = qcc;
35785 case 0xFF52: // Coding style default (COD)
35786 length = readUint16(data, position);
35789 var scod = data[j++];
35790 cod.entropyCoderWithCustomPrecincts = !!(scod & 1);
35791 cod.sopMarkerUsed = !!(scod & 2);
35792 cod.ephMarkerUsed = !!(scod & 4);
35793 cod.progressionOrder = data[j++];
35794 cod.layersCount = readUint16(data, j);
35796 cod.multipleComponentTransform = data[j++];
35798 cod.decompositionLevelsCount = data[j++];
35799 cod.xcb = (data[j++] & 0xF) + 2;
35800 cod.ycb = (data[j++] & 0xF) + 2;
35801 var blockStyle = data[j++];
35802 cod.selectiveArithmeticCodingBypass = !!(blockStyle & 1);
35803 cod.resetContextProbabilities = !!(blockStyle & 2);
35804 cod.terminationOnEachCodingPass = !!(blockStyle & 4);
35805 cod.verticalyStripe = !!(blockStyle & 8);
35806 cod.predictableTermination = !!(blockStyle & 16);
35807 cod.segmentationSymbolUsed = !!(blockStyle & 32);
35808 cod.reversibleTransformation = data[j++];
35809 if (cod.entropyCoderWithCustomPrecincts) {
35810 var precinctsSizes = [];
35811 while (j < length + position) {
35812 var precinctsSize = data[j++];
35813 precinctsSizes.push({
35814 PPx: precinctsSize & 0xF,
35815 PPy: precinctsSize >> 4
35818 cod.precinctsSizes = precinctsSizes;
35820 var unsupported = [];
35821 if (cod.selectiveArithmeticCodingBypass) {
35822 unsupported.push('selectiveArithmeticCodingBypass');
35824 if (cod.resetContextProbabilities) {
35825 unsupported.push('resetContextProbabilities');
35827 if (cod.terminationOnEachCodingPass) {
35828 unsupported.push('terminationOnEachCodingPass');
35830 if (cod.verticalyStripe) {
35831 unsupported.push('verticalyStripe');
35833 if (cod.predictableTermination) {
35834 unsupported.push('predictableTermination');
35836 if (unsupported.length > 0) {
35837 doNotRecover = true;
35838 throw new Error('JPX Error: Unsupported COD options (' +
35839 unsupported.join(', ') + ')');
35841 if (context.mainHeader) {
35844 context.currentTile.COD = cod;
35845 context.currentTile.COC = [];
35848 case 0xFF90: // Start of tile-part (SOT)
35849 length = readUint16(data, position);
35851 tile.index = readUint16(data, position + 2);
35852 tile.length = readUint32(data, position + 4);
35853 tile.dataEnd = tile.length + position - 2;
35854 tile.partIndex = data[position + 8];
35855 tile.partsCount = data[position + 9];
35857 context.mainHeader = false;
35858 if (tile.partIndex === 0) {
35859 // reset component specific settings
35860 tile.COD = context.COD;
35861 tile.COC = context.COC.slice(0); // clone of the global COC
35862 tile.QCD = context.QCD;
35863 tile.QCC = context.QCC.slice(0); // clone of the global COC
35865 context.currentTile = tile;
35867 case 0xFF93: // Start of data (SOD)
35868 tile = context.currentTile;
35869 if (tile.partIndex === 0) {
35870 initializeTile(context, tile.index);
35871 buildPackets(context);
35874 // moving to the end of the data
35875 length = tile.dataEnd - position;
35876 parseTilePackets(context, data, position, length);
35878 case 0xFF55: // Tile-part lengths, main header (TLM)
35879 case 0xFF57: // Packet length, main header (PLM)
35880 case 0xFF58: // Packet length, tile-part header (PLT)
35881 case 0xFF64: // Comment (COM)
35882 length = readUint16(data, position);
35883 // skipping content
35885 case 0xFF53: // Coding style component (COC)
35886 throw new Error('JPX Error: Codestream code 0xFF53 (COC) is ' +
35887 'not implemented');
35889 throw new Error('JPX Error: Unknown codestream code: ' +
35890 code.toString(16));
35892 position += length;
35895 if (doNotRecover || this.failOnCorruptedImage) {
35898 warn('Trying to recover from ' + e.message);
35901 this.tiles = transformComponents(context);
35902 this.width = context.SIZ.Xsiz - context.SIZ.XOsiz;
35903 this.height = context.SIZ.Ysiz - context.SIZ.YOsiz;
35904 this.componentsCount = context.SIZ.Csiz;
35907 function calculateComponentDimensions(component, siz) {
35908 // Section B.2 Component mapping
35909 component.x0 = Math.ceil(siz.XOsiz / component.XRsiz);
35910 component.x1 = Math.ceil(siz.Xsiz / component.XRsiz);
35911 component.y0 = Math.ceil(siz.YOsiz / component.YRsiz);
35912 component.y1 = Math.ceil(siz.Ysiz / component.YRsiz);
35913 component.width = component.x1 - component.x0;
35914 component.height = component.y1 - component.y0;
35916 function calculateTileGrids(context, components) {
35917 var siz = context.SIZ;
35918 // Section B.3 Division into tile and tile-components
35919 var tile, tiles = [];
35920 var numXtiles = Math.ceil((siz.Xsiz - siz.XTOsiz) / siz.XTsiz);
35921 var numYtiles = Math.ceil((siz.Ysiz - siz.YTOsiz) / siz.YTsiz);
35922 for (var q = 0; q < numYtiles; q++) {
35923 for (var p = 0; p < numXtiles; p++) {
35925 tile.tx0 = Math.max(siz.XTOsiz + p * siz.XTsiz, siz.XOsiz);
35926 tile.ty0 = Math.max(siz.YTOsiz + q * siz.YTsiz, siz.YOsiz);
35927 tile.tx1 = Math.min(siz.XTOsiz + (p + 1) * siz.XTsiz, siz.Xsiz);
35928 tile.ty1 = Math.min(siz.YTOsiz + (q + 1) * siz.YTsiz, siz.Ysiz);
35929 tile.width = tile.tx1 - tile.tx0;
35930 tile.height = tile.ty1 - tile.ty0;
35931 tile.components = [];
35935 context.tiles = tiles;
35937 var componentsCount = siz.Csiz;
35938 for (var i = 0, ii = componentsCount; i < ii; i++) {
35939 var component = components[i];
35940 for (var j = 0, jj = tiles.length; j < jj; j++) {
35941 var tileComponent = {};
35943 tileComponent.tcx0 = Math.ceil(tile.tx0 / component.XRsiz);
35944 tileComponent.tcy0 = Math.ceil(tile.ty0 / component.YRsiz);
35945 tileComponent.tcx1 = Math.ceil(tile.tx1 / component.XRsiz);
35946 tileComponent.tcy1 = Math.ceil(tile.ty1 / component.YRsiz);
35947 tileComponent.width = tileComponent.tcx1 - tileComponent.tcx0;
35948 tileComponent.height = tileComponent.tcy1 - tileComponent.tcy0;
35949 tile.components[i] = tileComponent;
35953 function getBlocksDimensions(context, component, r) {
35954 var codOrCoc = component.codingStyleParameters;
35956 if (!codOrCoc.entropyCoderWithCustomPrecincts) {
35960 result.PPx = codOrCoc.precinctsSizes[r].PPx;
35961 result.PPy = codOrCoc.precinctsSizes[r].PPy;
35963 // calculate codeblock size as described in section B.7
35964 result.xcb_ = (r > 0 ? Math.min(codOrCoc.xcb, result.PPx - 1) :
35965 Math.min(codOrCoc.xcb, result.PPx));
35966 result.ycb_ = (r > 0 ? Math.min(codOrCoc.ycb, result.PPy - 1) :
35967 Math.min(codOrCoc.ycb, result.PPy));
35970 function buildPrecincts(context, resolution, dimensions) {
35971 // Section B.6 Division resolution to precincts
35972 var precinctWidth = 1 << dimensions.PPx;
35973 var precinctHeight = 1 << dimensions.PPy;
35974 // Jasper introduces codeblock groups for mapping each subband codeblocks
35975 // to precincts. Precinct partition divides a resolution according to width
35976 // and height parameters. The subband that belongs to the resolution level
35977 // has a different size than the level, unless it is the zero resolution.
35979 // From Jasper documentation: jpeg2000.pdf, section K: Tier-2 coding:
35980 // The precinct partitioning for a particular subband is derived from a
35981 // partitioning of its parent LL band (i.e., the LL band at the next higher
35982 // resolution level)... The LL band associated with each resolution level is
35983 // divided into precincts... Each of the resulting precinct regions is then
35984 // mapped into its child subbands (if any) at the next lower resolution
35985 // level. This is accomplished by using the coordinate transformation
35986 // (u, v) = (ceil(x/2), ceil(y/2)) where (x, y) and (u, v) are the
35987 // coordinates of a point in the LL band and child subband, respectively.
35988 var isZeroRes = resolution.resLevel === 0;
35989 var precinctWidthInSubband = 1 << (dimensions.PPx + (isZeroRes ? 0 : -1));
35990 var precinctHeightInSubband = 1 << (dimensions.PPy + (isZeroRes ? 0 : -1));
35991 var numprecinctswide = (resolution.trx1 > resolution.trx0 ?
35992 Math.ceil(resolution.trx1 / precinctWidth) -
35993 Math.floor(resolution.trx0 / precinctWidth) : 0);
35994 var numprecinctshigh = (resolution.try1 > resolution.try0 ?
35995 Math.ceil(resolution.try1 / precinctHeight) -
35996 Math.floor(resolution.try0 / precinctHeight) : 0);
35997 var numprecincts = numprecinctswide * numprecinctshigh;
35999 resolution.precinctParameters = {
36000 precinctWidth: precinctWidth,
36001 precinctHeight: precinctHeight,
36002 numprecinctswide: numprecinctswide,
36003 numprecinctshigh: numprecinctshigh,
36004 numprecincts: numprecincts,
36005 precinctWidthInSubband: precinctWidthInSubband,
36006 precinctHeightInSubband: precinctHeightInSubband
36009 function buildCodeblocks(context, subband, dimensions) {
36010 // Section B.7 Division sub-band into code-blocks
36011 var xcb_ = dimensions.xcb_;
36012 var ycb_ = dimensions.ycb_;
36013 var codeblockWidth = 1 << xcb_;
36014 var codeblockHeight = 1 << ycb_;
36015 var cbx0 = subband.tbx0 >> xcb_;
36016 var cby0 = subband.tby0 >> ycb_;
36017 var cbx1 = (subband.tbx1 + codeblockWidth - 1) >> xcb_;
36018 var cby1 = (subband.tby1 + codeblockHeight - 1) >> ycb_;
36019 var precinctParameters = subband.resolution.precinctParameters;
36020 var codeblocks = [];
36021 var precincts = [];
36022 var i, j, codeblock, precinctNumber;
36023 for (j = cby0; j < cby1; j++) {
36024 for (i = cbx0; i < cbx1; i++) {
36028 tbx0: codeblockWidth * i,
36029 tby0: codeblockHeight * j,
36030 tbx1: codeblockWidth * (i + 1),
36031 tby1: codeblockHeight * (j + 1)
36034 codeblock.tbx0_ = Math.max(subband.tbx0, codeblock.tbx0);
36035 codeblock.tby0_ = Math.max(subband.tby0, codeblock.tby0);
36036 codeblock.tbx1_ = Math.min(subband.tbx1, codeblock.tbx1);
36037 codeblock.tby1_ = Math.min(subband.tby1, codeblock.tby1);
36039 // Calculate precinct number for this codeblock, codeblock position
36040 // should be relative to its subband, use actual dimension and position
36041 // See comment about codeblock group width and height
36042 var pi = Math.floor((codeblock.tbx0_ - subband.tbx0) /
36043 precinctParameters.precinctWidthInSubband);
36044 var pj = Math.floor((codeblock.tby0_ - subband.tby0) /
36045 precinctParameters.precinctHeightInSubband);
36046 precinctNumber = pi + (pj * precinctParameters.numprecinctswide);
36048 codeblock.precinctNumber = precinctNumber;
36049 codeblock.subbandType = subband.type;
36050 codeblock.Lblock = 3;
36052 if (codeblock.tbx1_ <= codeblock.tbx0_ ||
36053 codeblock.tby1_ <= codeblock.tby0_) {
36056 codeblocks.push(codeblock);
36057 // building precinct for the sub-band
36058 var precinct = precincts[precinctNumber];
36059 if (precinct !== undefined) {
36060 if (i < precinct.cbxMin) {
36061 precinct.cbxMin = i;
36062 } else if (i > precinct.cbxMax) {
36063 precinct.cbxMax = i;
36065 if (j < precinct.cbyMin) {
36066 precinct.cbxMin = j;
36067 } else if (j > precinct.cbyMax) {
36068 precinct.cbyMax = j;
36071 precincts[precinctNumber] = precinct = {
36078 codeblock.precinct = precinct;
36081 subband.codeblockParameters = {
36082 codeblockWidth: xcb_,
36083 codeblockHeight: ycb_,
36084 numcodeblockwide: cbx1 - cbx0 + 1,
36085 numcodeblockhigh: cby1 - cby0 + 1
36087 subband.codeblocks = codeblocks;
36088 subband.precincts = precincts;
36090 function createPacket(resolution, precinctNumber, layerNumber) {
36091 var precinctCodeblocks = [];
36092 // Section B.10.8 Order of info in packet
36093 var subbands = resolution.subbands;
36094 // sub-bands already ordered in 'LL', 'HL', 'LH', and 'HH' sequence
36095 for (var i = 0, ii = subbands.length; i < ii; i++) {
36096 var subband = subbands[i];
36097 var codeblocks = subband.codeblocks;
36098 for (var j = 0, jj = codeblocks.length; j < jj; j++) {
36099 var codeblock = codeblocks[j];
36100 if (codeblock.precinctNumber !== precinctNumber) {
36103 precinctCodeblocks.push(codeblock);
36107 layerNumber: layerNumber,
36108 codeblocks: precinctCodeblocks
36111 function LayerResolutionComponentPositionIterator(context) {
36112 var siz = context.SIZ;
36113 var tileIndex = context.currentTile.index;
36114 var tile = context.tiles[tileIndex];
36115 var layersCount = tile.codingStyleDefaultParameters.layersCount;
36116 var componentsCount = siz.Csiz;
36117 var maxDecompositionLevelsCount = 0;
36118 for (var q = 0; q < componentsCount; q++) {
36119 maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount,
36120 tile.components[q].codingStyleParameters.decompositionLevelsCount);
36123 var l = 0, r = 0, i = 0, k = 0;
36125 this.nextPacket = function JpxImage_nextPacket() {
36126 // Section B.12.1.1 Layer-resolution-component-position
36127 for (; l < layersCount; l++) {
36128 for (; r <= maxDecompositionLevelsCount; r++) {
36129 for (; i < componentsCount; i++) {
36130 var component = tile.components[i];
36131 if (r > component.codingStyleParameters.decompositionLevelsCount) {
36135 var resolution = component.resolutions[r];
36136 var numprecincts = resolution.precinctParameters.numprecincts;
36137 for (; k < numprecincts;) {
36138 var packet = createPacket(resolution, k, l);
36148 throw new Error('JPX Error: Out of packets');
36151 function ResolutionLayerComponentPositionIterator(context) {
36152 var siz = context.SIZ;
36153 var tileIndex = context.currentTile.index;
36154 var tile = context.tiles[tileIndex];
36155 var layersCount = tile.codingStyleDefaultParameters.layersCount;
36156 var componentsCount = siz.Csiz;
36157 var maxDecompositionLevelsCount = 0;
36158 for (var q = 0; q < componentsCount; q++) {
36159 maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount,
36160 tile.components[q].codingStyleParameters.decompositionLevelsCount);
36163 var r = 0, l = 0, i = 0, k = 0;
36165 this.nextPacket = function JpxImage_nextPacket() {
36166 // Section B.12.1.2 Resolution-layer-component-position
36167 for (; r <= maxDecompositionLevelsCount; r++) {
36168 for (; l < layersCount; l++) {
36169 for (; i < componentsCount; i++) {
36170 var component = tile.components[i];
36171 if (r > component.codingStyleParameters.decompositionLevelsCount) {
36175 var resolution = component.resolutions[r];
36176 var numprecincts = resolution.precinctParameters.numprecincts;
36177 for (; k < numprecincts;) {
36178 var packet = createPacket(resolution, k, l);
36188 throw new Error('JPX Error: Out of packets');
36191 function ResolutionPositionComponentLayerIterator(context) {
36192 var siz = context.SIZ;
36193 var tileIndex = context.currentTile.index;
36194 var tile = context.tiles[tileIndex];
36195 var layersCount = tile.codingStyleDefaultParameters.layersCount;
36196 var componentsCount = siz.Csiz;
36198 var maxDecompositionLevelsCount = 0;
36199 for (c = 0; c < componentsCount; c++) {
36200 var component = tile.components[c];
36201 maxDecompositionLevelsCount = Math.max(maxDecompositionLevelsCount,
36202 component.codingStyleParameters.decompositionLevelsCount);
36204 var maxNumPrecinctsInLevel = new Int32Array(
36205 maxDecompositionLevelsCount + 1);
36206 for (r = 0; r <= maxDecompositionLevelsCount; ++r) {
36207 var maxNumPrecincts = 0;
36208 for (c = 0; c < componentsCount; ++c) {
36209 var resolutions = tile.components[c].resolutions;
36210 if (r < resolutions.length) {
36211 maxNumPrecincts = Math.max(maxNumPrecincts,
36212 resolutions[r].precinctParameters.numprecincts);
36215 maxNumPrecinctsInLevel[r] = maxNumPrecincts;
36222 this.nextPacket = function JpxImage_nextPacket() {
36223 // Section B.12.1.3 Resolution-position-component-layer
36224 for (; r <= maxDecompositionLevelsCount; r++) {
36225 for (; p < maxNumPrecinctsInLevel[r]; p++) {
36226 for (; c < componentsCount; c++) {
36227 var component = tile.components[c];
36228 if (r > component.codingStyleParameters.decompositionLevelsCount) {
36231 var resolution = component.resolutions[r];
36232 var numprecincts = resolution.precinctParameters.numprecincts;
36233 if (p >= numprecincts) {
36236 for (; l < layersCount;) {
36237 var packet = createPacket(resolution, p, l);
36247 throw new Error('JPX Error: Out of packets');
36250 function PositionComponentResolutionLayerIterator(context) {
36251 var siz = context.SIZ;
36252 var tileIndex = context.currentTile.index;
36253 var tile = context.tiles[tileIndex];
36254 var layersCount = tile.codingStyleDefaultParameters.layersCount;
36255 var componentsCount = siz.Csiz;
36256 var precinctsSizes = getPrecinctSizesInImageScale(tile);
36257 var precinctsIterationSizes = precinctsSizes;
36258 var l = 0, r = 0, c = 0, px = 0, py = 0;
36260 this.nextPacket = function JpxImage_nextPacket() {
36261 // Section B.12.1.4 Position-component-resolution-layer
36262 for (; py < precinctsIterationSizes.maxNumHigh; py++) {
36263 for (; px < precinctsIterationSizes.maxNumWide; px++) {
36264 for (; c < componentsCount; c++) {
36265 var component = tile.components[c];
36266 var decompositionLevelsCount =
36267 component.codingStyleParameters.decompositionLevelsCount;
36268 for (; r <= decompositionLevelsCount; r++) {
36269 var resolution = component.resolutions[r];
36270 var sizeInImageScale =
36271 precinctsSizes.components[c].resolutions[r];
36272 var k = getPrecinctIndexIfExist(
36276 precinctsIterationSizes,
36281 for (; l < layersCount;) {
36282 var packet = createPacket(resolution, k, l);
36294 throw new Error('JPX Error: Out of packets');
36297 function ComponentPositionResolutionLayerIterator(context) {
36298 var siz = context.SIZ;
36299 var tileIndex = context.currentTile.index;
36300 var tile = context.tiles[tileIndex];
36301 var layersCount = tile.codingStyleDefaultParameters.layersCount;
36302 var componentsCount = siz.Csiz;
36303 var precinctsSizes = getPrecinctSizesInImageScale(tile);
36304 var l = 0, r = 0, c = 0, px = 0, py = 0;
36306 this.nextPacket = function JpxImage_nextPacket() {
36307 // Section B.12.1.5 Component-position-resolution-layer
36308 for (; c < componentsCount; ++c) {
36309 var component = tile.components[c];
36310 var precinctsIterationSizes = precinctsSizes.components[c];
36311 var decompositionLevelsCount =
36312 component.codingStyleParameters.decompositionLevelsCount;
36313 for (; py < precinctsIterationSizes.maxNumHigh; py++) {
36314 for (; px < precinctsIterationSizes.maxNumWide; px++) {
36315 for (; r <= decompositionLevelsCount; r++) {
36316 var resolution = component.resolutions[r];
36317 var sizeInImageScale = precinctsIterationSizes.resolutions[r];
36318 var k = getPrecinctIndexIfExist(
36322 precinctsIterationSizes,
36327 for (; l < layersCount;) {
36328 var packet = createPacket(resolution, k, l);
36340 throw new Error('JPX Error: Out of packets');
36343 function getPrecinctIndexIfExist(
36344 pxIndex, pyIndex, sizeInImageScale, precinctIterationSizes, resolution) {
36345 var posX = pxIndex * precinctIterationSizes.minWidth;
36346 var posY = pyIndex * precinctIterationSizes.minHeight;
36347 if (posX % sizeInImageScale.width !== 0 ||
36348 posY % sizeInImageScale.height !== 0) {
36351 var startPrecinctRowIndex =
36352 (posY / sizeInImageScale.width) *
36353 resolution.precinctParameters.numprecinctswide;
36354 return (posX / sizeInImageScale.height) + startPrecinctRowIndex;
36356 function getPrecinctSizesInImageScale(tile) {
36357 var componentsCount = tile.components.length;
36358 var minWidth = Number.MAX_VALUE;
36359 var minHeight = Number.MAX_VALUE;
36360 var maxNumWide = 0;
36361 var maxNumHigh = 0;
36362 var sizePerComponent = new Array(componentsCount);
36363 for (var c = 0; c < componentsCount; c++) {
36364 var component = tile.components[c];
36365 var decompositionLevelsCount =
36366 component.codingStyleParameters.decompositionLevelsCount;
36367 var sizePerResolution = new Array(decompositionLevelsCount + 1);
36368 var minWidthCurrentComponent = Number.MAX_VALUE;
36369 var minHeightCurrentComponent = Number.MAX_VALUE;
36370 var maxNumWideCurrentComponent = 0;
36371 var maxNumHighCurrentComponent = 0;
36373 for (var r = decompositionLevelsCount; r >= 0; --r) {
36374 var resolution = component.resolutions[r];
36375 var widthCurrentResolution =
36376 scale * resolution.precinctParameters.precinctWidth;
36377 var heightCurrentResolution =
36378 scale * resolution.precinctParameters.precinctHeight;
36379 minWidthCurrentComponent = Math.min(
36380 minWidthCurrentComponent,
36381 widthCurrentResolution);
36382 minHeightCurrentComponent = Math.min(
36383 minHeightCurrentComponent,
36384 heightCurrentResolution);
36385 maxNumWideCurrentComponent = Math.max(maxNumWideCurrentComponent,
36386 resolution.precinctParameters.numprecinctswide);
36387 maxNumHighCurrentComponent = Math.max(maxNumHighCurrentComponent,
36388 resolution.precinctParameters.numprecinctshigh);
36389 sizePerResolution[r] = {
36390 width: widthCurrentResolution,
36391 height: heightCurrentResolution
36395 minWidth = Math.min(minWidth, minWidthCurrentComponent);
36396 minHeight = Math.min(minHeight, minHeightCurrentComponent);
36397 maxNumWide = Math.max(maxNumWide, maxNumWideCurrentComponent);
36398 maxNumHigh = Math.max(maxNumHigh, maxNumHighCurrentComponent);
36399 sizePerComponent[c] = {
36400 resolutions: sizePerResolution,
36401 minWidth: minWidthCurrentComponent,
36402 minHeight: minHeightCurrentComponent,
36403 maxNumWide: maxNumWideCurrentComponent,
36404 maxNumHigh: maxNumHighCurrentComponent
36408 components: sizePerComponent,
36409 minWidth: minWidth,
36410 minHeight: minHeight,
36411 maxNumWide: maxNumWide,
36412 maxNumHigh: maxNumHigh
36415 function buildPackets(context) {
36416 var siz = context.SIZ;
36417 var tileIndex = context.currentTile.index;
36418 var tile = context.tiles[tileIndex];
36419 var componentsCount = siz.Csiz;
36420 // Creating resolutions and sub-bands for each component
36421 for (var c = 0; c < componentsCount; c++) {
36422 var component = tile.components[c];
36423 var decompositionLevelsCount =
36424 component.codingStyleParameters.decompositionLevelsCount;
36425 // Section B.5 Resolution levels and sub-bands
36426 var resolutions = [];
36428 for (var r = 0; r <= decompositionLevelsCount; r++) {
36429 var blocksDimensions = getBlocksDimensions(context, component, r);
36430 var resolution = {};
36431 var scale = 1 << (decompositionLevelsCount - r);
36432 resolution.trx0 = Math.ceil(component.tcx0 / scale);
36433 resolution.try0 = Math.ceil(component.tcy0 / scale);
36434 resolution.trx1 = Math.ceil(component.tcx1 / scale);
36435 resolution.try1 = Math.ceil(component.tcy1 / scale);
36436 resolution.resLevel = r;
36437 buildPrecincts(context, resolution, blocksDimensions);
36438 resolutions.push(resolution);
36442 // one sub-band (LL) with last decomposition
36444 subband.type = 'LL';
36445 subband.tbx0 = Math.ceil(component.tcx0 / scale);
36446 subband.tby0 = Math.ceil(component.tcy0 / scale);
36447 subband.tbx1 = Math.ceil(component.tcx1 / scale);
36448 subband.tby1 = Math.ceil(component.tcy1 / scale);
36449 subband.resolution = resolution;
36450 buildCodeblocks(context, subband, blocksDimensions);
36451 subbands.push(subband);
36452 resolution.subbands = [subband];
36454 var bscale = 1 << (decompositionLevelsCount - r + 1);
36455 var resolutionSubbands = [];
36456 // three sub-bands (HL, LH and HH) with rest of decompositions
36458 subband.type = 'HL';
36459 subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);
36460 subband.tby0 = Math.ceil(component.tcy0 / bscale);
36461 subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);
36462 subband.tby1 = Math.ceil(component.tcy1 / bscale);
36463 subband.resolution = resolution;
36464 buildCodeblocks(context, subband, blocksDimensions);
36465 subbands.push(subband);
36466 resolutionSubbands.push(subband);
36469 subband.type = 'LH';
36470 subband.tbx0 = Math.ceil(component.tcx0 / bscale);
36471 subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);
36472 subband.tbx1 = Math.ceil(component.tcx1 / bscale);
36473 subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);
36474 subband.resolution = resolution;
36475 buildCodeblocks(context, subband, blocksDimensions);
36476 subbands.push(subband);
36477 resolutionSubbands.push(subband);
36480 subband.type = 'HH';
36481 subband.tbx0 = Math.ceil(component.tcx0 / bscale - 0.5);
36482 subband.tby0 = Math.ceil(component.tcy0 / bscale - 0.5);
36483 subband.tbx1 = Math.ceil(component.tcx1 / bscale - 0.5);
36484 subband.tby1 = Math.ceil(component.tcy1 / bscale - 0.5);
36485 subband.resolution = resolution;
36486 buildCodeblocks(context, subband, blocksDimensions);
36487 subbands.push(subband);
36488 resolutionSubbands.push(subband);
36490 resolution.subbands = resolutionSubbands;
36493 component.resolutions = resolutions;
36494 component.subbands = subbands;
36496 // Generate the packets sequence
36497 var progressionOrder = tile.codingStyleDefaultParameters.progressionOrder;
36498 switch (progressionOrder) {
36500 tile.packetsIterator =
36501 new LayerResolutionComponentPositionIterator(context);
36504 tile.packetsIterator =
36505 new ResolutionLayerComponentPositionIterator(context);
36508 tile.packetsIterator =
36509 new ResolutionPositionComponentLayerIterator(context);
36512 tile.packetsIterator =
36513 new PositionComponentResolutionLayerIterator(context);
36516 tile.packetsIterator =
36517 new ComponentPositionResolutionLayerIterator(context);
36520 throw new Error('JPX Error: Unsupported progression order ' +
36524 function parseTilePackets(context, data, offset, dataLength) {
36526 var buffer, bufferSize = 0, skipNextBit = false;
36527 function readBits(count) {
36528 while (bufferSize < count) {
36529 var b = data[offset + position];
36532 buffer = (buffer << 7) | b;
36534 skipNextBit = false;
36536 buffer = (buffer << 8) | b;
36540 skipNextBit = true;
36543 bufferSize -= count;
36544 return (buffer >>> bufferSize) & ((1 << count) - 1);
36546 function skipMarkerIfEqual(value) {
36547 if (data[offset + position - 1] === 0xFF &&
36548 data[offset + position] === value) {
36551 } else if (data[offset + position] === 0xFF &&
36552 data[offset + position + 1] === value) {
36558 function skipBytes(count) {
36561 function alignToByte() {
36565 skipNextBit = false;
36568 function readCodingpasses() {
36569 if (readBits(1) === 0) {
36572 if (readBits(1) === 0) {
36575 var value = readBits(2);
36579 value = readBits(5);
36583 value = readBits(7);
36586 var tileIndex = context.currentTile.index;
36587 var tile = context.tiles[tileIndex];
36588 var sopMarkerUsed = context.COD.sopMarkerUsed;
36589 var ephMarkerUsed = context.COD.ephMarkerUsed;
36590 var packetsIterator = tile.packetsIterator;
36591 while (position < dataLength) {
36593 if (sopMarkerUsed && skipMarkerIfEqual(0x91)) {
36594 // Skip also marker segment length and packet sequence ID
36597 var packet = packetsIterator.nextPacket();
36598 if (!readBits(1)) {
36601 var layerNumber = packet.layerNumber;
36602 var queue = [], codeblock;
36603 for (var i = 0, ii = packet.codeblocks.length; i < ii; i++) {
36604 codeblock = packet.codeblocks[i];
36605 var precinct = codeblock.precinct;
36606 var codeblockColumn = codeblock.cbx - precinct.cbxMin;
36607 var codeblockRow = codeblock.cby - precinct.cbyMin;
36608 var codeblockIncluded = false;
36609 var firstTimeInclusion = false;
36611 if (codeblock['included'] !== undefined) {
36612 codeblockIncluded = !!readBits(1);
36614 // reading inclusion tree
36615 precinct = codeblock.precinct;
36616 var inclusionTree, zeroBitPlanesTree;
36617 if (precinct['inclusionTree'] !== undefined) {
36618 inclusionTree = precinct.inclusionTree;
36620 // building inclusion and zero bit-planes trees
36621 var width = precinct.cbxMax - precinct.cbxMin + 1;
36622 var height = precinct.cbyMax - precinct.cbyMin + 1;
36623 inclusionTree = new InclusionTree(width, height, layerNumber);
36624 zeroBitPlanesTree = new TagTree(width, height);
36625 precinct.inclusionTree = inclusionTree;
36626 precinct.zeroBitPlanesTree = zeroBitPlanesTree;
36629 if (inclusionTree.reset(codeblockColumn, codeblockRow, layerNumber)) {
36632 valueReady = !inclusionTree.nextLevel();
36634 codeblock.included = true;
36635 codeblockIncluded = firstTimeInclusion = true;
36639 inclusionTree.incrementValue(layerNumber);
36645 if (!codeblockIncluded) {
36648 if (firstTimeInclusion) {
36649 zeroBitPlanesTree = precinct.zeroBitPlanesTree;
36650 zeroBitPlanesTree.reset(codeblockColumn, codeblockRow);
36653 valueReady = !zeroBitPlanesTree.nextLevel();
36658 zeroBitPlanesTree.incrementValue();
36661 codeblock.zeroBitPlanes = zeroBitPlanesTree.value;
36663 var codingpasses = readCodingpasses();
36664 while (readBits(1)) {
36665 codeblock.Lblock++;
36667 var codingpassesLog2 = log2(codingpasses);
36668 // rounding down log2
36669 var bits = ((codingpasses < (1 << codingpassesLog2)) ?
36670 codingpassesLog2 - 1 : codingpassesLog2) + codeblock.Lblock;
36671 var codedDataLength = readBits(bits);
36673 codeblock: codeblock,
36674 codingpasses: codingpasses,
36675 dataLength: codedDataLength
36679 if (ephMarkerUsed) {
36680 skipMarkerIfEqual(0x92);
36682 while (queue.length > 0) {
36683 var packetItem = queue.shift();
36684 codeblock = packetItem.codeblock;
36685 if (codeblock['data'] === undefined) {
36686 codeblock.data = [];
36688 codeblock.data.push({
36690 start: offset + position,
36691 end: offset + position + packetItem.dataLength,
36692 codingpasses: packetItem.codingpasses
36694 position += packetItem.dataLength;
36699 function copyCoefficients(coefficients, levelWidth, levelHeight, subband,
36700 delta, mb, reversible, segmentationSymbolUsed) {
36701 var x0 = subband.tbx0;
36702 var y0 = subband.tby0;
36703 var width = subband.tbx1 - subband.tbx0;
36704 var codeblocks = subband.codeblocks;
36705 var right = subband.type.charAt(0) === 'H' ? 1 : 0;
36706 var bottom = subband.type.charAt(1) === 'H' ? levelWidth : 0;
36708 for (var i = 0, ii = codeblocks.length; i < ii; ++i) {
36709 var codeblock = codeblocks[i];
36710 var blockWidth = codeblock.tbx1_ - codeblock.tbx0_;
36711 var blockHeight = codeblock.tby1_ - codeblock.tby0_;
36712 if (blockWidth === 0 || blockHeight === 0) {
36715 if (codeblock['data'] === undefined) {
36719 var bitModel, currentCodingpassType;
36720 bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType,
36721 codeblock.zeroBitPlanes, mb);
36722 currentCodingpassType = 2; // first bit plane starts from cleanup
36725 var data = codeblock.data, totalLength = 0, codingpasses = 0;
36726 var j, jj, dataItem;
36727 for (j = 0, jj = data.length; j < jj; j++) {
36728 dataItem = data[j];
36729 totalLength += dataItem.end - dataItem.start;
36730 codingpasses += dataItem.codingpasses;
36732 var encodedData = new Uint8Array(totalLength);
36734 for (j = 0, jj = data.length; j < jj; j++) {
36735 dataItem = data[j];
36736 var chunk = dataItem.data.subarray(dataItem.start, dataItem.end);
36737 encodedData.set(chunk, position);
36738 position += chunk.length;
36740 // decoding the item
36741 var decoder = new ArithmeticDecoder(encodedData, 0, totalLength);
36742 bitModel.setDecoder(decoder);
36744 for (j = 0; j < codingpasses; j++) {
36745 switch (currentCodingpassType) {
36747 bitModel.runSignificancePropogationPass();
36750 bitModel.runMagnitudeRefinementPass();
36753 bitModel.runCleanupPass();
36754 if (segmentationSymbolUsed) {
36755 bitModel.checkSegmentationSymbol();
36759 currentCodingpassType = (currentCodingpassType + 1) % 3;
36762 var offset = (codeblock.tbx0_ - x0) + (codeblock.tby0_ - y0) * width;
36763 var sign = bitModel.coefficentsSign;
36764 var magnitude = bitModel.coefficentsMagnitude;
36765 var bitsDecoded = bitModel.bitsDecoded;
36766 var magnitudeCorrection = reversible ? 0 : 0.5;
36769 // Do the interleaving of Section F.3.3 here, so we do not need
36770 // to copy later. LL level is not interleaved, just copied.
36771 var interleave = (subband.type !== 'LL');
36772 for (j = 0; j < blockHeight; j++) {
36773 var row = (offset / width) | 0; // row in the non-interleaved subband
36774 var levelOffset = 2 * row * (levelWidth - width) + right + bottom;
36775 for (k = 0; k < blockWidth; k++) {
36776 n = magnitude[position];
36778 n = (n + magnitudeCorrection) * delta;
36779 if (sign[position] !== 0) {
36782 nb = bitsDecoded[position];
36783 var pos = interleave ? (levelOffset + (offset << 1)) : offset;
36784 if (reversible && (nb >= mb)) {
36785 coefficients[pos] = n;
36787 coefficients[pos] = n * (1 << (mb - nb));
36793 offset += width - blockWidth;
36797 function transformTile(context, tile, c) {
36798 var component = tile.components[c];
36799 var codingStyleParameters = component.codingStyleParameters;
36800 var quantizationParameters = component.quantizationParameters;
36801 var decompositionLevelsCount =
36802 codingStyleParameters.decompositionLevelsCount;
36803 var spqcds = quantizationParameters.SPqcds;
36804 var scalarExpounded = quantizationParameters.scalarExpounded;
36805 var guardBits = quantizationParameters.guardBits;
36806 var segmentationSymbolUsed = codingStyleParameters.segmentationSymbolUsed;
36807 var precision = context.components[c].precision;
36809 var reversible = codingStyleParameters.reversibleTransformation;
36810 var transform = (reversible ? new ReversibleTransform() :
36811 new IrreversibleTransform());
36813 var subbandCoefficients = [];
36815 for (var i = 0; i <= decompositionLevelsCount; i++) {
36816 var resolution = component.resolutions[i];
36818 var width = resolution.trx1 - resolution.trx0;
36819 var height = resolution.try1 - resolution.try0;
36820 // Allocate space for the whole sublevel.
36821 var coefficients = new Float32Array(width * height);
36823 for (var j = 0, jj = resolution.subbands.length; j < jj; j++) {
36825 if (!scalarExpounded) {
36828 epsilon = spqcds[0].epsilon + (i > 0 ? 1 - i : 0);
36831 epsilon = spqcds[b].epsilon;
36835 var subband = resolution.subbands[j];
36836 var gainLog2 = SubbandsGainLog2[subband.type];
36838 // calulate quantization coefficient (Section E.1.1.1)
36839 var delta = (reversible ? 1 :
36840 Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048));
36841 var mb = (guardBits + epsilon - 1);
36843 // In the first resolution level, copyCoefficients will fill the
36844 // whole array with coefficients. In the succeding passes,
36845 // copyCoefficients will consecutively fill in the values that belong
36846 // to the interleaved positions of the HL, LH, and HH coefficients.
36847 // The LL coefficients will then be interleaved in Transform.iterate().
36848 copyCoefficients(coefficients, width, height, subband, delta, mb,
36849 reversible, segmentationSymbolUsed);
36851 subbandCoefficients.push({
36854 items: coefficients
36858 var result = transform.calculate(subbandCoefficients,
36859 component.tcx0, component.tcy0);
36861 left: component.tcx0,
36862 top: component.tcy0,
36863 width: result.width,
36864 height: result.height,
36865 items: result.items
36868 function transformComponents(context) {
36869 var siz = context.SIZ;
36870 var components = context.components;
36871 var componentsCount = siz.Csiz;
36872 var resultImages = [];
36873 for (var i = 0, ii = context.tiles.length; i < ii; i++) {
36874 var tile = context.tiles[i];
36875 var transformedTiles = [];
36877 for (c = 0; c < componentsCount; c++) {
36878 transformedTiles[c] = transformTile(context, tile, c);
36880 var tile0 = transformedTiles[0];
36881 var out = new Uint8Array(tile0.items.length * componentsCount);
36885 width: tile0.width,
36886 height: tile0.height,
36890 // Section G.2.2 Inverse multi component transform
36891 var shift, offset, max, min, maxK;
36892 var pos = 0, j, jj, y0, y1, y2, r, g, b, k, val;
36893 if (tile.codingStyleDefaultParameters.multipleComponentTransform) {
36894 var fourComponents = componentsCount === 4;
36895 var y0items = transformedTiles[0].items;
36896 var y1items = transformedTiles[1].items;
36897 var y2items = transformedTiles[2].items;
36898 var y3items = fourComponents ? transformedTiles[3].items : null;
36900 // HACK: The multiple component transform formulas below assume that
36901 // all components have the same precision. With this in mind, we
36902 // compute shift and offset only once.
36903 shift = components[0].precision - 8;
36904 offset = (128 << shift) + 0.5;
36905 max = 255 * (1 << shift);
36909 var component0 = tile.components[0];
36910 var alpha01 = componentsCount - 3;
36911 jj = y0items.length;
36912 if (!component0.codingStyleParameters.reversibleTransformation) {
36913 // inverse irreversible multiple component transform
36914 for (j = 0; j < jj; j++, pos += alpha01) {
36915 y0 = y0items[j] + offset;
36918 r = y0 + 1.402 * y2;
36919 g = y0 - 0.34413 * y1 - 0.71414 * y2;
36920 b = y0 + 1.772 * y1;
36921 out[pos++] = r <= 0 ? 0 : r >= max ? 255 : r >> shift;
36922 out[pos++] = g <= 0 ? 0 : g >= max ? 255 : g >> shift;
36923 out[pos++] = b <= 0 ? 0 : b >= max ? 255 : b >> shift;
36926 // inverse reversible multiple component transform
36927 for (j = 0; j < jj; j++, pos += alpha01) {
36928 y0 = y0items[j] + offset;
36931 g = y0 - ((y2 + y1) >> 2);
36934 out[pos++] = r <= 0 ? 0 : r >= max ? 255 : r >> shift;
36935 out[pos++] = g <= 0 ? 0 : g >= max ? 255 : g >> shift;
36936 out[pos++] = b <= 0 ? 0 : b >= max ? 255 : b >> shift;
36939 if (fourComponents) {
36940 for (j = 0, pos = 3; j < jj; j++, pos += 4) {
36942 out[pos] = k <= min ? 0 : k >= maxK ? 255 : (k + offset) >> shift;
36945 } else { // no multi-component transform
36946 for (c = 0; c < componentsCount; c++) {
36947 var items = transformedTiles[c].items;
36948 shift = components[c].precision - 8;
36949 offset = (128 << shift) + 0.5;
36950 max = (127.5 * (1 << shift));
36952 for (pos = c, j = 0, jj = items.length; j < jj; j++) {
36954 out[pos] = val <= min ? 0 :
36955 val >= max ? 255 : (val + offset) >> shift;
36956 pos += componentsCount;
36960 resultImages.push(result);
36962 return resultImages;
36964 function initializeTile(context, tileIndex) {
36965 var siz = context.SIZ;
36966 var componentsCount = siz.Csiz;
36967 var tile = context.tiles[tileIndex];
36968 for (var c = 0; c < componentsCount; c++) {
36969 var component = tile.components[c];
36970 var qcdOrQcc = (context.currentTile.QCC[c] !== undefined ?
36971 context.currentTile.QCC[c] : context.currentTile.QCD);
36972 component.quantizationParameters = qcdOrQcc;
36973 var codOrCoc = (context.currentTile.COC[c] !== undefined ?
36974 context.currentTile.COC[c] : context.currentTile.COD);
36975 component.codingStyleParameters = codOrCoc;
36977 tile.codingStyleDefaultParameters = context.currentTile.COD;
36980 // Section B.10.2 Tag trees
36981 var TagTree = (function TagTreeClosure() {
36982 function TagTree(width, height) {
36983 var levelsLength = log2(Math.max(width, height)) + 1;
36985 for (var i = 0; i < levelsLength; i++) {
36991 this.levels.push(level);
36992 width = Math.ceil(width / 2);
36993 height = Math.ceil(height / 2);
36996 TagTree.prototype = {
36997 reset: function TagTree_reset(i, j) {
36998 var currentLevel = 0, value = 0, level;
36999 while (currentLevel < this.levels.length) {
37000 level = this.levels[currentLevel];
37001 var index = i + j * level.width;
37002 if (level.items[index] !== undefined) {
37003 value = level.items[index];
37006 level.index = index;
37012 level = this.levels[currentLevel];
37013 level.items[level.index] = value;
37014 this.currentLevel = currentLevel;
37017 incrementValue: function TagTree_incrementValue() {
37018 var level = this.levels[this.currentLevel];
37019 level.items[level.index]++;
37021 nextLevel: function TagTree_nextLevel() {
37022 var currentLevel = this.currentLevel;
37023 var level = this.levels[currentLevel];
37024 var value = level.items[level.index];
37026 if (currentLevel < 0) {
37027 this.value = value;
37031 this.currentLevel = currentLevel;
37032 level = this.levels[currentLevel];
37033 level.items[level.index] = value;
37040 var InclusionTree = (function InclusionTreeClosure() {
37041 function InclusionTree(width, height, defaultValue) {
37042 var levelsLength = log2(Math.max(width, height)) + 1;
37044 for (var i = 0; i < levelsLength; i++) {
37045 var items = new Uint8Array(width * height);
37046 for (var j = 0, jj = items.length; j < jj; j++) {
37047 items[j] = defaultValue;
37055 this.levels.push(level);
37057 width = Math.ceil(width / 2);
37058 height = Math.ceil(height / 2);
37061 InclusionTree.prototype = {
37062 reset: function InclusionTree_reset(i, j, stopValue) {
37063 var currentLevel = 0;
37064 while (currentLevel < this.levels.length) {
37065 var level = this.levels[currentLevel];
37066 var index = i + j * level.width;
37067 level.index = index;
37068 var value = level.items[index];
37070 if (value === 0xFF) {
37074 if (value > stopValue) {
37075 this.currentLevel = currentLevel;
37076 // already know about this one, propagating the value to top levels
37077 this.propagateValues();
37085 this.currentLevel = currentLevel - 1;
37088 incrementValue: function InclusionTree_incrementValue(stopValue) {
37089 var level = this.levels[this.currentLevel];
37090 level.items[level.index] = stopValue + 1;
37091 this.propagateValues();
37093 propagateValues: function InclusionTree_propagateValues() {
37094 var levelIndex = this.currentLevel;
37095 var level = this.levels[levelIndex];
37096 var currentValue = level.items[level.index];
37097 while (--levelIndex >= 0) {
37098 level = this.levels[levelIndex];
37099 level.items[level.index] = currentValue;
37102 nextLevel: function InclusionTree_nextLevel() {
37103 var currentLevel = this.currentLevel;
37104 var level = this.levels[currentLevel];
37105 var value = level.items[level.index];
37106 level.items[level.index] = 0xFF;
37108 if (currentLevel < 0) {
37112 this.currentLevel = currentLevel;
37113 level = this.levels[currentLevel];
37114 level.items[level.index] = value;
37118 return InclusionTree;
37121 // Section D. Coefficient bit modeling
37122 var BitModel = (function BitModelClosure() {
37123 var UNIFORM_CONTEXT = 17;
37124 var RUNLENGTH_CONTEXT = 18;
37126 // The index is binary presentation: 0dddvvhh, ddd - sum of Di (0..4),
37127 // vv - sum of Vi (0..2), and hh - sum of Hi (0..2)
37128 var LLAndLHContextsLabel = new Uint8Array([
37129 0, 5, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 1, 6, 8, 0, 3, 7, 8, 0, 4,
37130 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6,
37131 8, 0, 3, 7, 8, 0, 4, 7, 8, 0, 0, 0, 0, 0, 2, 6, 8, 0, 3, 7, 8, 0, 4, 7, 8
37133 var HLContextLabel = new Uint8Array([
37134 0, 3, 4, 0, 5, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 1, 3, 4, 0, 6, 7, 7, 0, 8,
37135 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3,
37136 4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8
37138 var HHContextLabel = new Uint8Array([
37139 0, 1, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 3, 4, 5, 0, 4, 5, 5, 0, 5,
37140 5, 5, 0, 0, 0, 0, 0, 6, 7, 7, 0, 7, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 8, 8,
37141 8, 0, 8, 8, 8, 0, 8, 8, 8, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8
37144 function BitModel(width, height, subband, zeroBitPlanes, mb) {
37145 this.width = width;
37146 this.height = height;
37148 this.contextLabelTable = (subband === 'HH' ? HHContextLabel :
37149 (subband === 'HL' ? HLContextLabel : LLAndLHContextsLabel));
37151 var coefficientCount = width * height;
37153 // coefficients outside the encoding region treated as insignificant
37154 // add border state cells for significanceState
37155 this.neighborsSignificance = new Uint8Array(coefficientCount);
37156 this.coefficentsSign = new Uint8Array(coefficientCount);
37157 this.coefficentsMagnitude = mb > 14 ? new Uint32Array(coefficientCount) :
37158 mb > 6 ? new Uint16Array(coefficientCount) :
37159 new Uint8Array(coefficientCount);
37160 this.processingFlags = new Uint8Array(coefficientCount);
37162 var bitsDecoded = new Uint8Array(coefficientCount);
37163 if (zeroBitPlanes !== 0) {
37164 for (var i = 0; i < coefficientCount; i++) {
37165 bitsDecoded[i] = zeroBitPlanes;
37168 this.bitsDecoded = bitsDecoded;
37173 BitModel.prototype = {
37174 setDecoder: function BitModel_setDecoder(decoder) {
37175 this.decoder = decoder;
37177 reset: function BitModel_reset() {
37178 // We have 17 contexts that are accessed via context labels,
37179 // plus the uniform and runlength context.
37180 this.contexts = new Int8Array(19);
37182 // Contexts are packed into 1 byte:
37183 // highest 7 bits carry the index, lowest bit carries mps
37184 this.contexts[0] = (4 << 1) | 0;
37185 this.contexts[UNIFORM_CONTEXT] = (46 << 1) | 0;
37186 this.contexts[RUNLENGTH_CONTEXT] = (3 << 1) | 0;
37188 setNeighborsSignificance:
37189 function BitModel_setNeighborsSignificance(row, column, index) {
37190 var neighborsSignificance = this.neighborsSignificance;
37191 var width = this.width, height = this.height;
37192 var left = (column > 0);
37193 var right = (column + 1 < width);
37199 neighborsSignificance[i - 1] += 0x10;
37202 neighborsSignificance[i + 1] += 0x10;
37204 neighborsSignificance[i] += 0x04;
37207 if (row + 1 < height) {
37210 neighborsSignificance[i - 1] += 0x10;
37213 neighborsSignificance[i + 1] += 0x10;
37215 neighborsSignificance[i] += 0x04;
37219 neighborsSignificance[index - 1] += 0x01;
37222 neighborsSignificance[index + 1] += 0x01;
37224 neighborsSignificance[index] |= 0x80;
37226 runSignificancePropogationPass:
37227 function BitModel_runSignificancePropogationPass() {
37228 var decoder = this.decoder;
37229 var width = this.width, height = this.height;
37230 var coefficentsMagnitude = this.coefficentsMagnitude;
37231 var coefficentsSign = this.coefficentsSign;
37232 var neighborsSignificance = this.neighborsSignificance;
37233 var processingFlags = this.processingFlags;
37234 var contexts = this.contexts;
37235 var labels = this.contextLabelTable;
37236 var bitsDecoded = this.bitsDecoded;
37237 var processedInverseMask = ~1;
37238 var processedMask = 1;
37239 var firstMagnitudeBitMask = 2;
37241 for (var i0 = 0; i0 < height; i0 += 4) {
37242 for (var j = 0; j < width; j++) {
37243 var index = i0 * width + j;
37244 for (var i1 = 0; i1 < 4; i1++, index += width) {
37249 // clear processed flag first
37250 processingFlags[index] &= processedInverseMask;
37252 if (coefficentsMagnitude[index] ||
37253 !neighborsSignificance[index]) {
37257 var contextLabel = labels[neighborsSignificance[index]];
37258 var decision = decoder.readBit(contexts, contextLabel);
37260 var sign = this.decodeSignBit(i, j, index);
37261 coefficentsSign[index] = sign;
37262 coefficentsMagnitude[index] = 1;
37263 this.setNeighborsSignificance(i, j, index);
37264 processingFlags[index] |= firstMagnitudeBitMask;
37266 bitsDecoded[index]++;
37267 processingFlags[index] |= processedMask;
37272 decodeSignBit: function BitModel_decodeSignBit(row, column, index) {
37273 var width = this.width, height = this.height;
37274 var coefficentsMagnitude = this.coefficentsMagnitude;
37275 var coefficentsSign = this.coefficentsSign;
37276 var contribution, sign0, sign1, significance1;
37277 var contextLabel, decoded;
37279 // calculate horizontal contribution
37280 significance1 = (column > 0 && coefficentsMagnitude[index - 1] !== 0);
37281 if (column + 1 < width && coefficentsMagnitude[index + 1] !== 0) {
37282 sign1 = coefficentsSign[index + 1];
37283 if (significance1) {
37284 sign0 = coefficentsSign[index - 1];
37285 contribution = 1 - sign1 - sign0;
37287 contribution = 1 - sign1 - sign1;
37289 } else if (significance1) {
37290 sign0 = coefficentsSign[index - 1];
37291 contribution = 1 - sign0 - sign0;
37295 var horizontalContribution = 3 * contribution;
37297 // calculate vertical contribution and combine with the horizontal
37298 significance1 = (row > 0 && coefficentsMagnitude[index - width] !== 0);
37299 if (row + 1 < height && coefficentsMagnitude[index + width] !== 0) {
37300 sign1 = coefficentsSign[index + width];
37301 if (significance1) {
37302 sign0 = coefficentsSign[index - width];
37303 contribution = 1 - sign1 - sign0 + horizontalContribution;
37305 contribution = 1 - sign1 - sign1 + horizontalContribution;
37307 } else if (significance1) {
37308 sign0 = coefficentsSign[index - width];
37309 contribution = 1 - sign0 - sign0 + horizontalContribution;
37311 contribution = horizontalContribution;
37314 if (contribution >= 0) {
37315 contextLabel = 9 + contribution;
37316 decoded = this.decoder.readBit(this.contexts, contextLabel);
37318 contextLabel = 9 - contribution;
37319 decoded = this.decoder.readBit(this.contexts, contextLabel) ^ 1;
37323 runMagnitudeRefinementPass:
37324 function BitModel_runMagnitudeRefinementPass() {
37325 var decoder = this.decoder;
37326 var width = this.width, height = this.height;
37327 var coefficentsMagnitude = this.coefficentsMagnitude;
37328 var neighborsSignificance = this.neighborsSignificance;
37329 var contexts = this.contexts;
37330 var bitsDecoded = this.bitsDecoded;
37331 var processingFlags = this.processingFlags;
37332 var processedMask = 1;
37333 var firstMagnitudeBitMask = 2;
37334 var length = width * height;
37335 var width4 = width * 4;
37337 for (var index0 = 0, indexNext; index0 < length; index0 = indexNext) {
37338 indexNext = Math.min(length, index0 + width4);
37339 for (var j = 0; j < width; j++) {
37340 for (var index = index0 + j; index < indexNext; index += width) {
37342 // significant but not those that have just become
37343 if (!coefficentsMagnitude[index] ||
37344 (processingFlags[index] & processedMask) !== 0) {
37348 var contextLabel = 16;
37349 if ((processingFlags[index] & firstMagnitudeBitMask) !== 0) {
37350 processingFlags[index] ^= firstMagnitudeBitMask;
37351 // first refinement
37352 var significance = neighborsSignificance[index] & 127;
37353 contextLabel = significance === 0 ? 15 : 14;
37356 var bit = decoder.readBit(contexts, contextLabel);
37357 coefficentsMagnitude[index] =
37358 (coefficentsMagnitude[index] << 1) | bit;
37359 bitsDecoded[index]++;
37360 processingFlags[index] |= processedMask;
37365 runCleanupPass: function BitModel_runCleanupPass() {
37366 var decoder = this.decoder;
37367 var width = this.width, height = this.height;
37368 var neighborsSignificance = this.neighborsSignificance;
37369 var coefficentsMagnitude = this.coefficentsMagnitude;
37370 var coefficentsSign = this.coefficentsSign;
37371 var contexts = this.contexts;
37372 var labels = this.contextLabelTable;
37373 var bitsDecoded = this.bitsDecoded;
37374 var processingFlags = this.processingFlags;
37375 var processedMask = 1;
37376 var firstMagnitudeBitMask = 2;
37377 var oneRowDown = width;
37378 var twoRowsDown = width * 2;
37379 var threeRowsDown = width * 3;
37381 for (var i0 = 0; i0 < height; i0 = iNext) {
37382 iNext = Math.min(i0 + 4, height);
37383 var indexBase = i0 * width;
37384 var checkAllEmpty = i0 + 3 < height;
37385 for (var j = 0; j < width; j++) {
37386 var index0 = indexBase + j;
37387 // using the property: labels[neighborsSignificance[index]] === 0
37388 // when neighborsSignificance[index] === 0
37389 var allEmpty = (checkAllEmpty &&
37390 processingFlags[index0] === 0 &&
37391 processingFlags[index0 + oneRowDown] === 0 &&
37392 processingFlags[index0 + twoRowsDown] === 0 &&
37393 processingFlags[index0 + threeRowsDown] === 0 &&
37394 neighborsSignificance[index0] === 0 &&
37395 neighborsSignificance[index0 + oneRowDown] === 0 &&
37396 neighborsSignificance[index0 + twoRowsDown] === 0 &&
37397 neighborsSignificance[index0 + threeRowsDown] === 0);
37398 var i1 = 0, index = index0;
37401 var hasSignificantCoefficent =
37402 decoder.readBit(contexts, RUNLENGTH_CONTEXT);
37403 if (!hasSignificantCoefficent) {
37404 bitsDecoded[index0]++;
37405 bitsDecoded[index0 + oneRowDown]++;
37406 bitsDecoded[index0 + twoRowsDown]++;
37407 bitsDecoded[index0 + threeRowsDown]++;
37408 continue; // next column
37410 i1 = (decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
37411 decoder.readBit(contexts, UNIFORM_CONTEXT);
37414 index += i1 * width;
37417 sign = this.decodeSignBit(i, j, index);
37418 coefficentsSign[index] = sign;
37419 coefficentsMagnitude[index] = 1;
37420 this.setNeighborsSignificance(i, j, index);
37421 processingFlags[index] |= firstMagnitudeBitMask;
37424 for (var i2 = i0; i2 <= i; i2++, index += width) {
37425 bitsDecoded[index]++;
37430 for (i = i0 + i1; i < iNext; i++, index += width) {
37431 if (coefficentsMagnitude[index] ||
37432 (processingFlags[index] & processedMask) !== 0) {
37436 var contextLabel = labels[neighborsSignificance[index]];
37437 var decision = decoder.readBit(contexts, contextLabel);
37438 if (decision === 1) {
37439 sign = this.decodeSignBit(i, j, index);
37440 coefficentsSign[index] = sign;
37441 coefficentsMagnitude[index] = 1;
37442 this.setNeighborsSignificance(i, j, index);
37443 processingFlags[index] |= firstMagnitudeBitMask;
37445 bitsDecoded[index]++;
37450 checkSegmentationSymbol: function BitModel_checkSegmentationSymbol() {
37451 var decoder = this.decoder;
37452 var contexts = this.contexts;
37453 var symbol = (decoder.readBit(contexts, UNIFORM_CONTEXT) << 3) |
37454 (decoder.readBit(contexts, UNIFORM_CONTEXT) << 2) |
37455 (decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
37456 decoder.readBit(contexts, UNIFORM_CONTEXT);
37457 if (symbol !== 0xA) {
37458 throw new Error('JPX Error: Invalid segmentation symbol');
37466 // Section F, Discrete wavelet transformation
37467 var Transform = (function TransformClosure() {
37468 function Transform() {}
37470 Transform.prototype.calculate =
37471 function transformCalculate(subbands, u0, v0) {
37472 var ll = subbands[0];
37473 for (var i = 1, ii = subbands.length; i < ii; i++) {
37474 ll = this.iterate(ll, subbands[i], u0, v0);
37478 Transform.prototype.extend = function extend(buffer, offset, size) {
37479 // Section F.3.7 extending... using max extension of 4
37480 var i1 = offset - 1, j1 = offset + 1;
37481 var i2 = offset + size - 2, j2 = offset + size;
37482 buffer[i1--] = buffer[j1++];
37483 buffer[j2++] = buffer[i2--];
37484 buffer[i1--] = buffer[j1++];
37485 buffer[j2++] = buffer[i2--];
37486 buffer[i1--] = buffer[j1++];
37487 buffer[j2++] = buffer[i2--];
37488 buffer[i1] = buffer[j1];
37489 buffer[j2] = buffer[i2];
37491 Transform.prototype.iterate = function Transform_iterate(ll, hl_lh_hh,
37493 var llWidth = ll.width, llHeight = ll.height, llItems = ll.items;
37494 var width = hl_lh_hh.width;
37495 var height = hl_lh_hh.height;
37496 var items = hl_lh_hh.items;
37497 var i, j, k, l, u, v;
37499 // Interleave LL according to Section F.3.3
37500 for (k = 0, i = 0; i < llHeight; i++) {
37502 for (j = 0; j < llWidth; j++, k++, l += 2) {
37503 items[l] = llItems[k];
37506 // The LL band is not needed anymore.
37507 llItems = ll.items = null;
37509 var bufferPadding = 4;
37510 var rowBuffer = new Float32Array(width + 2 * bufferPadding);
37512 // Section F.3.4 HOR_SR
37514 // if width = 1, when u0 even keep items as is, when odd divide by 2
37515 if ((u0 & 1) !== 0) {
37516 for (v = 0, k = 0; v < height; v++, k += width) {
37521 for (v = 0, k = 0; v < height; v++, k += width) {
37522 rowBuffer.set(items.subarray(k, k + width), bufferPadding);
37524 this.extend(rowBuffer, bufferPadding, width);
37525 this.filter(rowBuffer, bufferPadding, width);
37528 rowBuffer.subarray(bufferPadding, bufferPadding + width),
37533 // Accesses to the items array can take long, because it may not fit into
37534 // CPU cache and has to be fetched from main memory. Since subsequent
37535 // accesses to the items array are not local when reading columns, we
37536 // have a cache miss every time. To reduce cache misses, get up to
37537 // 'numBuffers' items at a time and store them into the individual
37538 // buffers. The colBuffers should be small enough to fit into CPU cache.
37539 var numBuffers = 16;
37540 var colBuffers = [];
37541 for (i = 0; i < numBuffers; i++) {
37542 colBuffers.push(new Float32Array(height + 2 * bufferPadding));
37544 var b, currentBuffer = 0;
37545 ll = bufferPadding + height;
37547 // Section F.3.5 VER_SR
37548 if (height === 1) {
37549 // if height = 1, when v0 even keep items as is, when odd divide by 2
37550 if ((v0 & 1) !== 0) {
37551 for (u = 0; u < width; u++) {
37556 for (u = 0; u < width; u++) {
37557 // if we ran out of buffers, copy several image columns at once
37558 if (currentBuffer === 0) {
37559 numBuffers = Math.min(width - u, numBuffers);
37560 for (k = u, l = bufferPadding; l < ll; k += width, l++) {
37561 for (b = 0; b < numBuffers; b++) {
37562 colBuffers[b][l] = items[k + b];
37565 currentBuffer = numBuffers;
37569 var buffer = colBuffers[currentBuffer];
37570 this.extend(buffer, bufferPadding, height);
37571 this.filter(buffer, bufferPadding, height);
37573 // If this is last buffer in this group of buffers, flush all buffers.
37574 if (currentBuffer === 0) {
37575 k = u - numBuffers + 1;
37576 for (l = bufferPadding; l < ll; k += width, l++) {
37577 for (b = 0; b < numBuffers; b++) {
37578 items[k + b] = colBuffers[b][l];
37594 // Section 3.8.2 Irreversible 9-7 filter
37595 var IrreversibleTransform = (function IrreversibleTransformClosure() {
37596 function IrreversibleTransform() {
37597 Transform.call(this);
37600 IrreversibleTransform.prototype = Object.create(Transform.prototype);
37601 IrreversibleTransform.prototype.filter =
37602 function irreversibleTransformFilter(x, offset, length) {
37603 var len = length >> 1;
37604 offset = offset | 0;
37605 var j, n, current, next;
37607 var alpha = -1.586134342059924;
37608 var beta = -0.052980118572961;
37609 var gamma = 0.882911075530934;
37610 var delta = 0.443506852043971;
37611 var K = 1.230174104914001;
37614 // step 1 is combined with step 3
37618 for (n = len + 4; n--; j += 2) {
37624 current = delta * x[j -1];
37625 for (n = len + 3; n--; j += 2) {
37626 next = delta * x[j + 1];
37627 x[j] = K * x[j] - current - next;
37630 current = delta * x[j + 1];
37631 x[j] = K * x[j] - current - next;
37639 current = gamma * x[j - 1];
37640 for (n = len + 2; n--; j += 2) {
37641 next = gamma * x[j + 1];
37642 x[j] -= current + next;
37645 current = gamma * x[j + 1];
37646 x[j] -= current + next;
37654 current = beta * x[j - 1];
37655 for (n = len + 1; n--; j += 2) {
37656 next = beta * x[j + 1];
37657 x[j] -= current + next;
37660 current = beta * x[j + 1];
37661 x[j] -= current + next;
37670 current = alpha * x[j - 1];
37671 for (n = len; n--; j += 2) {
37672 next = alpha * x[j + 1];
37673 x[j] -= current + next;
37676 current = alpha * x[j + 1];
37677 x[j] -= current + next;
37685 return IrreversibleTransform;
37688 // Section 3.8.1 Reversible 5-3 filter
37689 var ReversibleTransform = (function ReversibleTransformClosure() {
37690 function ReversibleTransform() {
37691 Transform.call(this);
37694 ReversibleTransform.prototype = Object.create(Transform.prototype);
37695 ReversibleTransform.prototype.filter =
37696 function reversibleTransformFilter(x, offset, length) {
37697 var len = length >> 1;
37698 offset = offset | 0;
37701 for (j = offset, n = len + 1; n--; j += 2) {
37702 x[j] -= (x[j - 1] + x[j + 1] + 2) >> 2;
37705 for (j = offset + 1, n = len; n--; j += 2) {
37706 x[j] += (x[j - 1] + x[j + 1]) >> 1;
37710 return ReversibleTransform;
37717 var Jbig2Image = (function Jbig2ImageClosure() {
37718 // Utility data structures
37719 function ContextCache() {}
37721 ContextCache.prototype = {
37722 getContexts: function(id) {
37726 return (this[id] = new Int8Array(1 << 16));
37730 function DecodingContext(data, start, end) {
37732 this.start = start;
37736 DecodingContext.prototype = {
37738 var decoder = new ArithmeticDecoder(this.data, this.start, this.end);
37739 return shadow(this, 'decoder', decoder);
37741 get contextCache() {
37742 var cache = new ContextCache();
37743 return shadow(this, 'contextCache', cache);
37747 // Annex A. Arithmetic Integer Decoding Procedure
37748 // A.2 Procedure for decoding values
37749 function decodeInteger(contextCache, procedure, decoder) {
37750 var contexts = contextCache.getContexts(procedure);
37753 function readBits(length) {
37755 for (var i = 0; i < length; i++) {
37756 var bit = decoder.readBit(contexts, prev);
37757 prev = (prev < 256 ? (prev << 1) | bit :
37758 (((prev << 1) | bit) & 511) | 256);
37759 v = (v << 1) | bit;
37764 var sign = readBits(1);
37765 var value = readBits(1) ?
37770 (readBits(32) + 4436) :
37771 readBits(12) + 340) :
37772 readBits(8) + 84) :
37773 readBits(6) + 20) :
37776 return (sign === 0 ? value : (value > 0 ? -value : null));
37779 // A.3 The IAID decoding procedure
37780 function decodeIAID(contextCache, decoder, codeLength) {
37781 var contexts = contextCache.getContexts('IAID');
37784 for (var i = 0; i < codeLength; i++) {
37785 var bit = decoder.readBit(contexts, prev);
37786 prev = (prev << 1) | bit;
37788 if (codeLength < 31) {
37789 return prev & ((1 << codeLength) - 1);
37791 return prev & 0x7FFFFFFF;
37794 // 7.3 Segment types
37795 var SegmentTypes = [
37796 'SymbolDictionary', null, null, null, 'IntermediateTextRegion', null,
37797 'ImmediateTextRegion', 'ImmediateLosslessTextRegion', null, null, null,
37798 null, null, null, null, null, 'patternDictionary', null, null, null,
37799 'IntermediateHalftoneRegion', null, 'ImmediateHalftoneRegion',
37800 'ImmediateLosslessHalftoneRegion', null, null, null, null, null, null, null,
37801 null, null, null, null, null, 'IntermediateGenericRegion', null,
37802 'ImmediateGenericRegion', 'ImmediateLosslessGenericRegion',
37803 'IntermediateGenericRefinementRegion', null,
37804 'ImmediateGenericRefinementRegion',
37805 'ImmediateLosslessGenericRefinementRegion', null, null, null, null,
37806 'PageInformation', 'EndOfPage', 'EndOfStripe', 'EndOfFile', 'Profiles',
37807 'Tables', null, null, null, null, null, null, null, null,
37811 var CodingTemplates = [
37812 [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: -2, y: -1},
37813 {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: 2, y: -1},
37814 {x: -4, y: 0}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}],
37815 [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: 2, y: -2},
37816 {x: -2, y: -1}, {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1},
37817 {x: 2, y: -1}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}],
37818 [{x: -1, y: -2}, {x: 0, y: -2}, {x: 1, y: -2}, {x: -2, y: -1},
37819 {x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: -2, y: 0},
37821 [{x: -3, y: -1}, {x: -2, y: -1}, {x: -1, y: -1}, {x: 0, y: -1},
37822 {x: 1, y: -1}, {x: -4, y: 0}, {x: -3, y: 0}, {x: -2, y: 0}, {x: -1, y: 0}]
37825 var RefinementTemplates = [
37827 coding: [{x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}],
37828 reference: [{x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}, {x: 0, y: 0},
37829 {x: 1, y: 0}, {x: -1, y: 1}, {x: 0, y: 1}, {x: 1, y: 1}]
37832 coding: [{x: -1, y: -1}, {x: 0, y: -1}, {x: 1, y: -1}, {x: -1, y: 0}],
37833 reference: [{x: 0, y: -1}, {x: -1, y: 0}, {x: 0, y: 0}, {x: 1, y: 0},
37834 {x: 0, y: 1}, {x: 1, y: 1}]
37838 // See 6.2.5.7 Decoding the bitmap.
37839 var ReusedContexts = [
37840 0x9B25, // 10011 0110010 0101
37841 0x0795, // 0011 110010 101
37842 0x00E5, // 001 11001 01
37843 0x0195 // 011001 0101
37846 var RefinementReusedContexts = [
37847 0x0020, // '000' + '0' (coding) + '00010000' + '0' (reference)
37848 0x0008 // '0000' + '001000'
37851 function decodeBitmapTemplate0(width, height, decodingContext) {
37852 var decoder = decodingContext.decoder;
37853 var contexts = decodingContext.contextCache.getContexts('GB');
37854 var contextLabel, i, j, pixel, row, row1, row2, bitmap = [];
37857 // ..ooooooo... Context template for current pixel (X)
37858 // .ooooX...... (concatenate values of 'o'-pixels to get contextLabel)
37859 var OLD_PIXEL_MASK = 0x7BF7; // 01111 0111111 0111
37861 for (i = 0; i < height; i++) {
37862 row = bitmap[i] = new Uint8Array(width);
37863 row1 = (i < 1) ? row : bitmap[i - 1];
37864 row2 = (i < 2) ? row : bitmap[i - 2];
37866 // At the beginning of each row:
37867 // Fill contextLabel with pixels that are above/right of (X)
37868 contextLabel = (row2[0] << 13) | (row2[1] << 12) | (row2[2] << 11) |
37869 (row1[0] << 7) | (row1[1] << 6) | (row1[2] << 5) |
37872 for (j = 0; j < width; j++) {
37873 row[j] = pixel = decoder.readBit(contexts, contextLabel);
37875 // At each pixel: Clear contextLabel pixels that are shifted
37876 // out of the context, then add new ones.
37877 contextLabel = ((contextLabel & OLD_PIXEL_MASK) << 1) |
37878 (j + 3 < width ? row2[j + 3] << 11 : 0) |
37879 (j + 4 < width ? row1[j + 4] << 4 : 0) | pixel;
37886 // 6.2 Generic Region Decoding Procedure
37887 function decodeBitmap(mmr, width, height, templateIndex, prediction, skip, at,
37890 error('JBIG2 error: MMR encoding is not supported');
37893 // Use optimized version for the most common case
37894 if (templateIndex === 0 && !skip && !prediction && at.length === 4 &&
37895 at[0].x === 3 && at[0].y === -1 && at[1].x === -3 && at[1].y === -1 &&
37896 at[2].x === 2 && at[2].y === -2 && at[3].x === -2 && at[3].y === -2) {
37897 return decodeBitmapTemplate0(width, height, decodingContext);
37900 var useskip = !!skip;
37901 var template = CodingTemplates[templateIndex].concat(at);
37903 // Sorting is non-standard, and it is not required. But sorting increases
37904 // the number of template bits that can be reused from the previous
37905 // contextLabel in the main loop.
37906 template.sort(function (a, b) {
37907 return (a.y - b.y) || (a.x - b.x);
37910 var templateLength = template.length;
37911 var templateX = new Int8Array(templateLength);
37912 var templateY = new Int8Array(templateLength);
37913 var changingTemplateEntries = [];
37914 var reuseMask = 0, minX = 0, maxX = 0, minY = 0;
37917 for (k = 0; k < templateLength; k++) {
37918 templateX[k] = template[k].x;
37919 templateY[k] = template[k].y;
37920 minX = Math.min(minX, template[k].x);
37921 maxX = Math.max(maxX, template[k].x);
37922 minY = Math.min(minY, template[k].y);
37923 // Check if the template pixel appears in two consecutive context labels,
37924 // so it can be reused. Otherwise, we add it to the list of changing
37925 // template entries.
37926 if (k < templateLength - 1 &&
37927 template[k].y === template[k + 1].y &&
37928 template[k].x === template[k + 1].x - 1) {
37929 reuseMask |= 1 << (templateLength - 1 - k);
37931 changingTemplateEntries.push(k);
37934 var changingEntriesLength = changingTemplateEntries.length;
37936 var changingTemplateX = new Int8Array(changingEntriesLength);
37937 var changingTemplateY = new Int8Array(changingEntriesLength);
37938 var changingTemplateBit = new Uint16Array(changingEntriesLength);
37939 for (c = 0; c < changingEntriesLength; c++) {
37940 k = changingTemplateEntries[c];
37941 changingTemplateX[c] = template[k].x;
37942 changingTemplateY[c] = template[k].y;
37943 changingTemplateBit[c] = 1 << (templateLength - 1 - k);
37946 // Get the safe bounding box edges from the width, height, minX, maxX, minY
37947 var sbb_left = -minX;
37948 var sbb_top = -minY;
37949 var sbb_right = width - maxX;
37951 var pseudoPixelContext = ReusedContexts[templateIndex];
37952 var row = new Uint8Array(width);
37955 var decoder = decodingContext.decoder;
37956 var contexts = decodingContext.contextCache.getContexts('GB');
37958 var ltp = 0, j, i0, j0, contextLabel = 0, bit, shift;
37959 for (var i = 0; i < height; i++) {
37961 var sltp = decoder.readBit(contexts, pseudoPixelContext);
37964 bitmap.push(row); // duplicate previous row
37968 row = new Uint8Array(row);
37970 for (j = 0; j < width; j++) {
37971 if (useskip && skip[i][j]) {
37975 // Are we in the middle of a scanline, so we can reuse contextLabel
37977 if (j >= sbb_left && j < sbb_right && i >= sbb_top) {
37978 // If yes, we can just shift the bits that are reusable and only
37979 // fetch the remaining ones.
37980 contextLabel = (contextLabel << 1) & reuseMask;
37981 for (k = 0; k < changingEntriesLength; k++) {
37982 i0 = i + changingTemplateY[k];
37983 j0 = j + changingTemplateX[k];
37984 bit = bitmap[i0][j0];
37986 bit = changingTemplateBit[k];
37987 contextLabel |= bit;
37991 // compute the contextLabel from scratch
37993 shift = templateLength - 1;
37994 for (k = 0; k < templateLength; k++, shift--) {
37995 j0 = j + templateX[k];
37996 if (j0 >= 0 && j0 < width) {
37997 i0 = i + templateY[k];
37999 bit = bitmap[i0][j0];
38001 contextLabel |= bit << shift;
38007 var pixel = decoder.readBit(contexts, contextLabel);
38014 // 6.3.2 Generic Refinement Region Decoding Procedure
38015 function decodeRefinement(width, height, templateIndex, referenceBitmap,
38016 offsetX, offsetY, prediction, at,
38018 var codingTemplate = RefinementTemplates[templateIndex].coding;
38019 if (templateIndex === 0) {
38020 codingTemplate = codingTemplate.concat([at[0]]);
38022 var codingTemplateLength = codingTemplate.length;
38023 var codingTemplateX = new Int32Array(codingTemplateLength);
38024 var codingTemplateY = new Int32Array(codingTemplateLength);
38026 for (k = 0; k < codingTemplateLength; k++) {
38027 codingTemplateX[k] = codingTemplate[k].x;
38028 codingTemplateY[k] = codingTemplate[k].y;
38031 var referenceTemplate = RefinementTemplates[templateIndex].reference;
38032 if (templateIndex === 0) {
38033 referenceTemplate = referenceTemplate.concat([at[1]]);
38035 var referenceTemplateLength = referenceTemplate.length;
38036 var referenceTemplateX = new Int32Array(referenceTemplateLength);
38037 var referenceTemplateY = new Int32Array(referenceTemplateLength);
38038 for (k = 0; k < referenceTemplateLength; k++) {
38039 referenceTemplateX[k] = referenceTemplate[k].x;
38040 referenceTemplateY[k] = referenceTemplate[k].y;
38042 var referenceWidth = referenceBitmap[0].length;
38043 var referenceHeight = referenceBitmap.length;
38045 var pseudoPixelContext = RefinementReusedContexts[templateIndex];
38048 var decoder = decodingContext.decoder;
38049 var contexts = decodingContext.contextCache.getContexts('GR');
38052 for (var i = 0; i < height; i++) {
38054 var sltp = decoder.readBit(contexts, pseudoPixelContext);
38057 error('JBIG2 error: prediction is not supported');
38060 var row = new Uint8Array(width);
38062 for (var j = 0; j < width; j++) {
38064 var contextLabel = 0;
38065 for (k = 0; k < codingTemplateLength; k++) {
38066 i0 = i + codingTemplateY[k];
38067 j0 = j + codingTemplateX[k];
38068 if (i0 < 0 || j0 < 0 || j0 >= width) {
38069 contextLabel <<= 1; // out of bound pixel
38071 contextLabel = (contextLabel << 1) | bitmap[i0][j0];
38074 for (k = 0; k < referenceTemplateLength; k++) {
38075 i0 = i + referenceTemplateY[k] + offsetY;
38076 j0 = j + referenceTemplateX[k] + offsetX;
38077 if (i0 < 0 || i0 >= referenceHeight || j0 < 0 ||
38078 j0 >= referenceWidth) {
38079 contextLabel <<= 1; // out of bound pixel
38081 contextLabel = (contextLabel << 1) | referenceBitmap[i0][j0];
38084 var pixel = decoder.readBit(contexts, contextLabel);
38092 // 6.5.5 Decoding the symbol dictionary
38093 function decodeSymbolDictionary(huffman, refinement, symbols,
38094 numberOfNewSymbols, numberOfExportedSymbols,
38095 huffmanTables, templateIndex, at,
38096 refinementTemplateIndex, refinementAt,
38099 error('JBIG2 error: huffman is not supported');
38102 var newSymbols = [];
38103 var currentHeight = 0;
38104 var symbolCodeLength = log2(symbols.length + numberOfNewSymbols);
38106 var decoder = decodingContext.decoder;
38107 var contextCache = decodingContext.contextCache;
38109 while (newSymbols.length < numberOfNewSymbols) {
38110 var deltaHeight = decodeInteger(contextCache, 'IADH', decoder); // 6.5.6
38111 currentHeight += deltaHeight;
38112 var currentWidth = 0;
38113 var totalWidth = 0;
38115 var deltaWidth = decodeInteger(contextCache, 'IADW', decoder); // 6.5.7
38116 if (deltaWidth === null) {
38119 currentWidth += deltaWidth;
38120 totalWidth += currentWidth;
38123 // 6.5.8.2 Refinement/aggregate-coded symbol bitmap
38124 var numberOfInstances = decodeInteger(contextCache, 'IAAI', decoder);
38125 if (numberOfInstances > 1) {
38126 bitmap = decodeTextRegion(huffman, refinement,
38127 currentWidth, currentHeight, 0,
38128 numberOfInstances, 1, //strip size
38129 symbols.concat(newSymbols),
38133 1, //top left 7.4.3.1.1
38136 refinementTemplateIndex, refinementAt,
38139 var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
38140 var rdx = decodeInteger(contextCache, 'IARDX', decoder); // 6.4.11.3
38141 var rdy = decodeInteger(contextCache, 'IARDY', decoder); // 6.4.11.4
38142 var symbol = (symbolId < symbols.length ? symbols[symbolId] :
38143 newSymbols[symbolId - symbols.length]);
38144 bitmap = decodeRefinement(currentWidth, currentHeight,
38145 refinementTemplateIndex, symbol, rdx, rdy, false, refinementAt,
38149 // 6.5.8.1 Direct-coded symbol bitmap
38150 bitmap = decodeBitmap(false, currentWidth, currentHeight,
38151 templateIndex, false, null, at, decodingContext);
38153 newSymbols.push(bitmap);
38156 // 6.5.10 Exported symbols
38157 var exportedSymbols = [];
38158 var flags = [], currentFlag = false;
38159 var totalSymbolsLength = symbols.length + numberOfNewSymbols;
38160 while (flags.length < totalSymbolsLength) {
38161 var runLength = decodeInteger(contextCache, 'IAEX', decoder);
38162 while (runLength--) {
38163 flags.push(currentFlag);
38165 currentFlag = !currentFlag;
38167 for (var i = 0, ii = symbols.length; i < ii; i++) {
38169 exportedSymbols.push(symbols[i]);
38172 for (var j = 0; j < numberOfNewSymbols; i++, j++) {
38174 exportedSymbols.push(newSymbols[j]);
38177 return exportedSymbols;
38180 function decodeTextRegion(huffman, refinement, width, height,
38181 defaultPixelValue, numberOfSymbolInstances,
38182 stripSize, inputSymbols, symbolCodeLength,
38183 transposed, dsOffset, referenceCorner,
38184 combinationOperator, huffmanTables,
38185 refinementTemplateIndex, refinementAt,
38188 error('JBIG2 error: huffman is not supported');
38194 for (i = 0; i < height; i++) {
38195 row = new Uint8Array(width);
38196 if (defaultPixelValue) {
38197 for (var j = 0; j < width; j++) {
38198 row[j] = defaultPixelValue;
38204 var decoder = decodingContext.decoder;
38205 var contextCache = decodingContext.contextCache;
38206 var stripT = -decodeInteger(contextCache, 'IADT', decoder); // 6.4.6
38209 while (i < numberOfSymbolInstances) {
38210 var deltaT = decodeInteger(contextCache, 'IADT', decoder); // 6.4.6
38213 var deltaFirstS = decodeInteger(contextCache, 'IAFS', decoder); // 6.4.7
38214 firstS += deltaFirstS;
38215 var currentS = firstS;
38217 var currentT = (stripSize === 1 ? 0 :
38218 decodeInteger(contextCache, 'IAIT', decoder)); // 6.4.9
38219 var t = stripSize * stripT + currentT;
38220 var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
38221 var applyRefinement = (refinement &&
38222 decodeInteger(contextCache, 'IARI', decoder));
38223 var symbolBitmap = inputSymbols[symbolId];
38224 var symbolWidth = symbolBitmap[0].length;
38225 var symbolHeight = symbolBitmap.length;
38226 if (applyRefinement) {
38227 var rdw = decodeInteger(contextCache, 'IARDW', decoder); // 6.4.11.1
38228 var rdh = decodeInteger(contextCache, 'IARDH', decoder); // 6.4.11.2
38229 var rdx = decodeInteger(contextCache, 'IARDX', decoder); // 6.4.11.3
38230 var rdy = decodeInteger(contextCache, 'IARDY', decoder); // 6.4.11.4
38231 symbolWidth += rdw;
38232 symbolHeight += rdh;
38233 symbolBitmap = decodeRefinement(symbolWidth, symbolHeight,
38234 refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx,
38235 (rdh >> 1) + rdy, false, refinementAt,
38238 var offsetT = t - ((referenceCorner & 1) ? 0 : symbolHeight);
38239 var offsetS = currentS - ((referenceCorner & 2) ? symbolWidth : 0);
38240 var s2, t2, symbolRow;
38242 // Place Symbol Bitmap from T1,S1
38243 for (s2 = 0; s2 < symbolHeight; s2++) {
38244 row = bitmap[offsetS + s2];
38248 symbolRow = symbolBitmap[s2];
38249 // To ignore Parts of Symbol bitmap which goes
38250 // outside bitmap region
38251 var maxWidth = Math.min(width - offsetT, symbolWidth);
38252 switch (combinationOperator) {
38254 for (t2 = 0; t2 < maxWidth; t2++) {
38255 row[offsetT + t2] |= symbolRow[t2];
38259 for (t2 = 0; t2 < maxWidth; t2++) {
38260 row[offsetT + t2] ^= symbolRow[t2];
38264 error('JBIG2 error: operator ' + combinationOperator +
38265 ' is not supported');
38268 currentS += symbolHeight - 1;
38270 for (t2 = 0; t2 < symbolHeight; t2++) {
38271 row = bitmap[offsetT + t2];
38275 symbolRow = symbolBitmap[t2];
38276 switch (combinationOperator) {
38278 for (s2 = 0; s2 < symbolWidth; s2++) {
38279 row[offsetS + s2] |= symbolRow[s2];
38283 for (s2 = 0; s2 < symbolWidth; s2++) {
38284 row[offsetS + s2] ^= symbolRow[s2];
38288 error('JBIG2 error: operator ' + combinationOperator +
38289 ' is not supported');
38292 currentS += symbolWidth - 1;
38295 var deltaS = decodeInteger(contextCache, 'IADS', decoder); // 6.4.8
38296 if (deltaS === null) {
38299 currentS += deltaS + dsOffset;
38305 function readSegmentHeader(data, start) {
38306 var segmentHeader = {};
38307 segmentHeader.number = readUint32(data, start);
38308 var flags = data[start + 4];
38309 var segmentType = flags & 0x3F;
38310 if (!SegmentTypes[segmentType]) {
38311 error('JBIG2 error: invalid segment type: ' + segmentType);
38313 segmentHeader.type = segmentType;
38314 segmentHeader.typeName = SegmentTypes[segmentType];
38315 segmentHeader.deferredNonRetain = !!(flags & 0x80);
38317 var pageAssociationFieldSize = !!(flags & 0x40);
38318 var referredFlags = data[start + 5];
38319 var referredToCount = (referredFlags >> 5) & 7;
38320 var retainBits = [referredFlags & 31];
38321 var position = start + 6;
38322 if (referredFlags === 7) {
38323 referredToCount = readUint32(data, position - 1) & 0x1FFFFFFF;
38325 var bytes = (referredToCount + 7) >> 3;
38326 retainBits[0] = data[position++];
38327 while (--bytes > 0) {
38328 retainBits.push(data[position++]);
38330 } else if (referredFlags === 5 || referredFlags === 6) {
38331 error('JBIG2 error: invalid referred-to flags');
38334 segmentHeader.retainBits = retainBits;
38335 var referredToSegmentNumberSize = (segmentHeader.number <= 256 ? 1 :
38336 (segmentHeader.number <= 65536 ? 2 : 4));
38337 var referredTo = [];
38339 for (i = 0; i < referredToCount; i++) {
38340 var number = (referredToSegmentNumberSize === 1 ? data[position] :
38341 (referredToSegmentNumberSize === 2 ? readUint16(data, position) :
38342 readUint32(data, position)));
38343 referredTo.push(number);
38344 position += referredToSegmentNumberSize;
38346 segmentHeader.referredTo = referredTo;
38347 if (!pageAssociationFieldSize) {
38348 segmentHeader.pageAssociation = data[position++];
38350 segmentHeader.pageAssociation = readUint32(data, position);
38353 segmentHeader.length = readUint32(data, position);
38356 if (segmentHeader.length === 0xFFFFFFFF) {
38357 // 7.2.7 Segment data length, unknown segment length
38358 if (segmentType === 38) { // ImmediateGenericRegion
38359 var genericRegionInfo = readRegionSegmentInformation(data, position);
38360 var genericRegionSegmentFlags = data[position +
38361 RegionSegmentInformationFieldLength];
38362 var genericRegionMmr = !!(genericRegionSegmentFlags & 1);
38363 // searching for the segment end
38364 var searchPatternLength = 6;
38365 var searchPattern = new Uint8Array(searchPatternLength);
38366 if (!genericRegionMmr) {
38367 searchPattern[0] = 0xFF;
38368 searchPattern[1] = 0xAC;
38370 searchPattern[2] = (genericRegionInfo.height >>> 24) & 0xFF;
38371 searchPattern[3] = (genericRegionInfo.height >> 16) & 0xFF;
38372 searchPattern[4] = (genericRegionInfo.height >> 8) & 0xFF;
38373 searchPattern[5] = genericRegionInfo.height & 0xFF;
38374 for (i = position, ii = data.length; i < ii; i++) {
38376 while (j < searchPatternLength && searchPattern[j] === data[i + j]) {
38379 if (j === searchPatternLength) {
38380 segmentHeader.length = i + searchPatternLength;
38384 if (segmentHeader.length === 0xFFFFFFFF) {
38385 error('JBIG2 error: segment end was not found');
38388 error('JBIG2 error: invalid unknown segment length');
38391 segmentHeader.headerEnd = position;
38392 return segmentHeader;
38395 function readSegments(header, data, start, end) {
38397 var position = start;
38398 while (position < end) {
38399 var segmentHeader = readSegmentHeader(data, position);
38400 position = segmentHeader.headerEnd;
38402 header: segmentHeader,
38405 if (!header.randomAccess) {
38406 segment.start = position;
38407 position += segmentHeader.length;
38408 segment.end = position;
38410 segments.push(segment);
38411 if (segmentHeader.type === 51) {
38412 break; // end of file is found
38415 if (header.randomAccess) {
38416 for (var i = 0, ii = segments.length; i < ii; i++) {
38417 segments[i].start = position;
38418 position += segments[i].header.length;
38419 segments[i].end = position;
38425 // 7.4.1 Region segment information field
38426 function readRegionSegmentInformation(data, start) {
38428 width: readUint32(data, start),
38429 height: readUint32(data, start + 4),
38430 x: readUint32(data, start + 8),
38431 y: readUint32(data, start + 12),
38432 combinationOperator: data[start + 16] & 7
38435 var RegionSegmentInformationFieldLength = 17;
38437 function processSegment(segment, visitor) {
38438 var header = segment.header;
38440 var data = segment.data, position = segment.start, end = segment.end;
38441 var args, at, i, atLength;
38442 switch (header.type) {
38443 case 0: // SymbolDictionary
38444 // 7.4.2 Symbol dictionary segment syntax
38445 var dictionary = {};
38446 var dictionaryFlags = readUint16(data, position); // 7.4.2.1.1
38447 dictionary.huffman = !!(dictionaryFlags & 1);
38448 dictionary.refinement = !!(dictionaryFlags & 2);
38449 dictionary.huffmanDHSelector = (dictionaryFlags >> 2) & 3;
38450 dictionary.huffmanDWSelector = (dictionaryFlags >> 4) & 3;
38451 dictionary.bitmapSizeSelector = (dictionaryFlags >> 6) & 1;
38452 dictionary.aggregationInstancesSelector = (dictionaryFlags >> 7) & 1;
38453 dictionary.bitmapCodingContextUsed = !!(dictionaryFlags & 256);
38454 dictionary.bitmapCodingContextRetained = !!(dictionaryFlags & 512);
38455 dictionary.template = (dictionaryFlags >> 10) & 3;
38456 dictionary.refinementTemplate = (dictionaryFlags >> 12) & 1;
38458 if (!dictionary.huffman) {
38459 atLength = dictionary.template === 0 ? 4 : 1;
38461 for (i = 0; i < atLength; i++) {
38463 x: readInt8(data, position),
38464 y: readInt8(data, position + 1)
38468 dictionary.at = at;
38470 if (dictionary.refinement && !dictionary.refinementTemplate) {
38472 for (i = 0; i < 2; i++) {
38474 x: readInt8(data, position),
38475 y: readInt8(data, position + 1)
38479 dictionary.refinementAt = at;
38481 dictionary.numberOfExportedSymbols = readUint32(data, position);
38483 dictionary.numberOfNewSymbols = readUint32(data, position);
38485 args = [dictionary, header.number, header.referredTo,
38486 data, position, end];
38488 case 6: // ImmediateTextRegion
38489 case 7: // ImmediateLosslessTextRegion
38490 var textRegion = {};
38491 textRegion.info = readRegionSegmentInformation(data, position);
38492 position += RegionSegmentInformationFieldLength;
38493 var textRegionSegmentFlags = readUint16(data, position);
38495 textRegion.huffman = !!(textRegionSegmentFlags & 1);
38496 textRegion.refinement = !!(textRegionSegmentFlags & 2);
38497 textRegion.stripSize = 1 << ((textRegionSegmentFlags >> 2) & 3);
38498 textRegion.referenceCorner = (textRegionSegmentFlags >> 4) & 3;
38499 textRegion.transposed = !!(textRegionSegmentFlags & 64);
38500 textRegion.combinationOperator = (textRegionSegmentFlags >> 7) & 3;
38501 textRegion.defaultPixelValue = (textRegionSegmentFlags >> 9) & 1;
38502 textRegion.dsOffset = (textRegionSegmentFlags << 17) >> 27;
38503 textRegion.refinementTemplate = (textRegionSegmentFlags >> 15) & 1;
38504 if (textRegion.huffman) {
38505 var textRegionHuffmanFlags = readUint16(data, position);
38507 textRegion.huffmanFS = (textRegionHuffmanFlags) & 3;
38508 textRegion.huffmanDS = (textRegionHuffmanFlags >> 2) & 3;
38509 textRegion.huffmanDT = (textRegionHuffmanFlags >> 4) & 3;
38510 textRegion.huffmanRefinementDW = (textRegionHuffmanFlags >> 6) & 3;
38511 textRegion.huffmanRefinementDH = (textRegionHuffmanFlags >> 8) & 3;
38512 textRegion.huffmanRefinementDX = (textRegionHuffmanFlags >> 10) & 3;
38513 textRegion.huffmanRefinementDY = (textRegionHuffmanFlags >> 12) & 3;
38514 textRegion.huffmanRefinementSizeSelector =
38515 !!(textRegionHuffmanFlags & 14);
38517 if (textRegion.refinement && !textRegion.refinementTemplate) {
38519 for (i = 0; i < 2; i++) {
38521 x: readInt8(data, position),
38522 y: readInt8(data, position + 1)
38526 textRegion.refinementAt = at;
38528 textRegion.numberOfSymbolInstances = readUint32(data, position);
38530 // TODO 7.4.3.1.7 Symbol ID Huffman table decoding
38531 if (textRegion.huffman) {
38532 error('JBIG2 error: huffman is not supported');
38534 args = [textRegion, header.referredTo, data, position, end];
38536 case 38: // ImmediateGenericRegion
38537 case 39: // ImmediateLosslessGenericRegion
38538 var genericRegion = {};
38539 genericRegion.info = readRegionSegmentInformation(data, position);
38540 position += RegionSegmentInformationFieldLength;
38541 var genericRegionSegmentFlags = data[position++];
38542 genericRegion.mmr = !!(genericRegionSegmentFlags & 1);
38543 genericRegion.template = (genericRegionSegmentFlags >> 1) & 3;
38544 genericRegion.prediction = !!(genericRegionSegmentFlags & 8);
38545 if (!genericRegion.mmr) {
38546 atLength = genericRegion.template === 0 ? 4 : 1;
38548 for (i = 0; i < atLength; i++) {
38550 x: readInt8(data, position),
38551 y: readInt8(data, position + 1)
38555 genericRegion.at = at;
38557 args = [genericRegion, data, position, end];
38559 case 48: // PageInformation
38561 width: readUint32(data, position),
38562 height: readUint32(data, position + 4),
38563 resolutionX: readUint32(data, position + 8),
38564 resolutionY: readUint32(data, position + 12)
38566 if (pageInfo.height === 0xFFFFFFFF) {
38567 delete pageInfo.height;
38569 var pageSegmentFlags = data[position + 16];
38570 var pageStripingInformatiom = readUint16(data, position + 17);
38571 pageInfo.lossless = !!(pageSegmentFlags & 1);
38572 pageInfo.refinement = !!(pageSegmentFlags & 2);
38573 pageInfo.defaultPixelValue = (pageSegmentFlags >> 2) & 1;
38574 pageInfo.combinationOperator = (pageSegmentFlags >> 3) & 3;
38575 pageInfo.requiresBuffer = !!(pageSegmentFlags & 32);
38576 pageInfo.combinationOperatorOverride = !!(pageSegmentFlags & 64);
38579 case 49: // EndOfPage
38581 case 50: // EndOfStripe
38583 case 51: // EndOfFile
38585 case 62: // 7.4.15 defines 2 extension types which
38586 // are comments and can be ignored.
38589 error('JBIG2 error: segment type ' + header.typeName + '(' +
38590 header.type + ') is not implemented');
38592 var callbackName = 'on' + header.typeName;
38593 if (callbackName in visitor) {
38594 visitor[callbackName].apply(visitor, args);
38598 function processSegments(segments, visitor) {
38599 for (var i = 0, ii = segments.length; i < ii; i++) {
38600 processSegment(segments[i], visitor);
38604 function parseJbig2(data, start, end) {
38605 var position = start;
38606 if (data[position] !== 0x97 || data[position + 1] !== 0x4A ||
38607 data[position + 2] !== 0x42 || data[position + 3] !== 0x32 ||
38608 data[position + 4] !== 0x0D || data[position + 5] !== 0x0A ||
38609 data[position + 6] !== 0x1A || data[position + 7] !== 0x0A) {
38610 error('JBIG2 error: invalid header');
38614 var flags = data[position++];
38615 header.randomAccess = !(flags & 1);
38616 if (!(flags & 2)) {
38617 header.numberOfPages = readUint32(data, position);
38620 var segments = readSegments(header, data, position, end);
38621 error('Not implemented');
38622 // processSegments(segments, new SimpleSegmentVisitor());
38625 function parseJbig2Chunks(chunks) {
38626 var visitor = new SimpleSegmentVisitor();
38627 for (var i = 0, ii = chunks.length; i < ii; i++) {
38628 var chunk = chunks[i];
38629 var segments = readSegments({}, chunk.data, chunk.start, chunk.end);
38630 processSegments(segments, visitor);
38632 return visitor.buffer;
38635 function SimpleSegmentVisitor() {}
38637 SimpleSegmentVisitor.prototype = {
38638 onPageInformation: function SimpleSegmentVisitor_onPageInformation(info) {
38639 this.currentPageInfo = info;
38640 var rowSize = (info.width + 7) >> 3;
38641 var buffer = new Uint8Array(rowSize * info.height);
38642 // The contents of ArrayBuffers are initialized to 0.
38643 // Fill the buffer with 0xFF only if info.defaultPixelValue is set
38644 if (info.defaultPixelValue) {
38645 for (var i = 0, ii = buffer.length; i < ii; i++) {
38649 this.buffer = buffer;
38651 drawBitmap: function SimpleSegmentVisitor_drawBitmap(regionInfo, bitmap) {
38652 var pageInfo = this.currentPageInfo;
38653 var width = regionInfo.width, height = regionInfo.height;
38654 var rowSize = (pageInfo.width + 7) >> 3;
38655 var combinationOperator = pageInfo.combinationOperatorOverride ?
38656 regionInfo.combinationOperator : pageInfo.combinationOperator;
38657 var buffer = this.buffer;
38658 var mask0 = 128 >> (regionInfo.x & 7);
38659 var offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3);
38660 var i, j, mask, offset;
38661 switch (combinationOperator) {
38663 for (i = 0; i < height; i++) {
38666 for (j = 0; j < width; j++) {
38667 if (bitmap[i][j]) {
38668 buffer[offset] |= mask;
38676 offset0 += rowSize;
38680 for (i = 0; i < height; i++) {
38683 for (j = 0; j < width; j++) {
38684 if (bitmap[i][j]) {
38685 buffer[offset] ^= mask;
38693 offset0 += rowSize;
38697 error('JBIG2 error: operator ' + combinationOperator +
38698 ' is not supported');
38701 onImmediateGenericRegion:
38702 function SimpleSegmentVisitor_onImmediateGenericRegion(region, data,
38704 var regionInfo = region.info;
38705 var decodingContext = new DecodingContext(data, start, end);
38706 var bitmap = decodeBitmap(region.mmr, regionInfo.width, regionInfo.height,
38707 region.template, region.prediction, null,
38708 region.at, decodingContext);
38709 this.drawBitmap(regionInfo, bitmap);
38711 onImmediateLosslessGenericRegion:
38712 function SimpleSegmentVisitor_onImmediateLosslessGenericRegion() {
38713 this.onImmediateGenericRegion.apply(this, arguments);
38715 onSymbolDictionary:
38716 function SimpleSegmentVisitor_onSymbolDictionary(dictionary,
38719 data, start, end) {
38721 if (dictionary.huffman) {
38722 error('JBIG2 error: huffman is not supported');
38725 // Combines exported symbols from all referred segments
38726 var symbols = this.symbols;
38728 this.symbols = symbols = {};
38731 var inputSymbols = [];
38732 for (var i = 0, ii = referredSegments.length; i < ii; i++) {
38733 inputSymbols = inputSymbols.concat(symbols[referredSegments[i]]);
38736 var decodingContext = new DecodingContext(data, start, end);
38737 symbols[currentSegment] = decodeSymbolDictionary(dictionary.huffman,
38738 dictionary.refinement, inputSymbols, dictionary.numberOfNewSymbols,
38739 dictionary.numberOfExportedSymbols, huffmanTables,
38740 dictionary.template, dictionary.at,
38741 dictionary.refinementTemplate, dictionary.refinementAt,
38744 onImmediateTextRegion:
38745 function SimpleSegmentVisitor_onImmediateTextRegion(region,
38747 data, start, end) {
38748 var regionInfo = region.info;
38751 // Combines exported symbols from all referred segments
38752 var symbols = this.symbols;
38753 var inputSymbols = [];
38754 for (var i = 0, ii = referredSegments.length; i < ii; i++) {
38755 inputSymbols = inputSymbols.concat(symbols[referredSegments[i]]);
38757 var symbolCodeLength = log2(inputSymbols.length);
38759 var decodingContext = new DecodingContext(data, start, end);
38760 var bitmap = decodeTextRegion(region.huffman, region.refinement,
38761 regionInfo.width, regionInfo.height, region.defaultPixelValue,
38762 region.numberOfSymbolInstances, region.stripSize, inputSymbols,
38763 symbolCodeLength, region.transposed, region.dsOffset,
38764 region.referenceCorner, region.combinationOperator, huffmanTables,
38765 region.refinementTemplate, region.refinementAt, decodingContext);
38766 this.drawBitmap(regionInfo, bitmap);
38768 onImmediateLosslessTextRegion:
38769 function SimpleSegmentVisitor_onImmediateLosslessTextRegion() {
38770 this.onImmediateTextRegion.apply(this, arguments);
38774 function Jbig2Image() {}
38776 Jbig2Image.prototype = {
38777 parseChunks: function Jbig2Image_parseChunks(chunks) {
38778 return parseJbig2Chunks(chunks);
38786 var bidi = PDFJS.bidi = (function bidiClosure() {
38787 // Character types for symbols from 0000 to 00FF.
38789 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS',
38790 'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
38791 'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON',
38792 'ON', 'ON', 'ON', 'ON', 'ON', 'CS', 'ON', 'CS', 'ON', 'EN', 'EN', 'EN',
38793 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'ON', 'ON', 'ON', 'ON', 'ON',
38794 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
38795 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON',
38796 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
38797 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
38798 'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN',
38799 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
38800 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
38801 'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON',
38802 'ON', 'ON', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON',
38803 'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
38804 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
38805 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
38806 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
38807 'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'
38810 // Character types for symbols from 0600 to 06FF
38811 var arabicTypes = [
38812 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38813 'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL',
38814 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38815 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38816 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38817 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38818 'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
38819 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL',
38820 'AL', 'AL', 'AL', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN',
38821 'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL',
38822 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38823 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38824 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38825 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38826 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38827 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38828 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38829 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38830 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
38831 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
38832 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
38833 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
38836 function isOdd(i) {
38837 return (i & 1) !== 0;
38840 function isEven(i) {
38841 return (i & 1) === 0;
38844 function findUnequal(arr, start, value) {
38845 for (var j = start, jj = arr.length; j < jj; ++j) {
38846 if (arr[j] !== value) {
38853 function setValues(arr, start, end, value) {
38854 for (var j = start; j < end; ++j) {
38859 function reverseValues(arr, start, end) {
38860 for (var i = start, j = end - 1; i < j; ++i, --j) {
38867 function createBidiText(str, isLTR, vertical) {
38870 dir: (vertical ? 'ttb' : (isLTR ? 'ltr' : 'rtl'))
38874 // These are used in bidi(), which is called frequently. We re-use them on
38875 // each call to avoid unnecessary allocations.
38879 function bidi(str, startLevel, vertical) {
38881 var strLength = str.length;
38882 if (strLength === 0 || vertical) {
38883 return createBidiText(str, isLTR, vertical);
38886 // Get types and fill arrays
38887 chars.length = strLength;
38888 types.length = strLength;
38892 for (i = 0; i < strLength; ++i) {
38893 chars[i] = str.charAt(i);
38895 var charCode = str.charCodeAt(i);
38896 var charType = 'L';
38897 if (charCode <= 0x00ff) {
38898 charType = baseTypes[charCode];
38899 } else if (0x0590 <= charCode && charCode <= 0x05f4) {
38901 } else if (0x0600 <= charCode && charCode <= 0x06ff) {
38902 charType = arabicTypes[charCode & 0xff];
38903 } else if (0x0700 <= charCode && charCode <= 0x08AC) {
38906 if (charType === 'R' || charType === 'AL' || charType === 'AN') {
38909 types[i] = charType;
38912 // Detect the bidi method
38913 // - If there are no rtl characters then no bidi needed
38914 // - If less than 30% chars are rtl then string is primarily ltr
38915 // - If more than 30% chars are rtl then string is primarily rtl
38916 if (numBidi === 0) {
38918 return createBidiText(str, isLTR);
38921 if (startLevel === -1) {
38922 if ((strLength / numBidi) < 0.3) {
38932 for (i = 0; i < strLength; ++i) {
38933 levels[i] = startLevel;
38937 X1-X10: skip most of this, since we are NOT doing the embeddings.
38939 var e = (isOdd(startLevel) ? 'R' : 'L');
38944 W1. Examine each non-spacing mark (NSM) in the level run, and change the
38945 type of the NSM to the type of the previous character. If the NSM is at the
38946 start of the level run, it will get the type of sor.
38948 var lastType = sor;
38949 for (i = 0; i < strLength; ++i) {
38950 if (types[i] === 'NSM') {
38951 types[i] = lastType;
38953 lastType = types[i];
38958 W2. Search backwards from each instance of a European number until the
38959 first strong type (R, L, AL, or sor) is found. If an AL is found, change
38960 the type of the European number to Arabic number.
38964 for (i = 0; i < strLength; ++i) {
38967 types[i] = (lastType === 'AL') ? 'AN' : 'EN';
38968 } else if (t === 'R' || t === 'L' || t === 'AL') {
38974 W3. Change all ALs to R.
38976 for (i = 0; i < strLength; ++i) {
38984 W4. A single European separator between two European numbers changes to a
38985 European number. A single common separator between two numbers of the same
38986 type changes to that type:
38988 for (i = 1; i < strLength - 1; ++i) {
38989 if (types[i] === 'ES' && types[i - 1] === 'EN' && types[i + 1] === 'EN') {
38992 if (types[i] === 'CS' &&
38993 (types[i - 1] === 'EN' || types[i - 1] === 'AN') &&
38994 types[i + 1] === types[i - 1]) {
38995 types[i] = types[i - 1];
39000 W5. A sequence of European terminators adjacent to European numbers changes
39001 to all European numbers:
39003 for (i = 0; i < strLength; ++i) {
39004 if (types[i] === 'EN') {
39007 for (j = i - 1; j >= 0; --j) {
39008 if (types[j] !== 'ET') {
39014 for (j = i + 1; j < strLength; --j) {
39015 if (types[j] !== 'ET') {
39024 W6. Otherwise, separators and terminators change to Other Neutral:
39026 for (i = 0; i < strLength; ++i) {
39028 if (t === 'WS' || t === 'ES' || t === 'ET' || t === 'CS') {
39034 W7. Search backwards from each instance of a European number until the
39035 first strong type (R, L, or sor) is found. If an L is found, then change
39036 the type of the European number to L.
39039 for (i = 0; i < strLength; ++i) {
39042 types[i] = ((lastType === 'L') ? 'L' : 'EN');
39043 } else if (t === 'R' || t === 'L') {
39049 N1. A sequence of neutrals takes the direction of the surrounding strong
39050 text if the text on both sides has the same direction. European and Arabic
39051 numbers are treated as though they were R. Start-of-level-run (sor) and
39052 end-of-level-run (eor) are used at level run boundaries.
39054 for (i = 0; i < strLength; ++i) {
39055 if (types[i] === 'ON') {
39056 var end = findUnequal(types, i + 1, 'ON');
39059 before = types[i - 1];
39063 if (end + 1 < strLength) {
39064 after = types[end + 1];
39066 if (before !== 'L') {
39069 if (after !== 'L') {
39072 if (before === after) {
39073 setValues(types, i, end, before);
39075 i = end - 1; // reset to end (-1 so next iteration is ok)
39080 N2. Any remaining neutrals take the embedding direction.
39082 for (i = 0; i < strLength; ++i) {
39083 if (types[i] === 'ON') {
39089 I1. For all characters with an even (left-to-right) embedding direction,
39090 those of type R go up one level and those of type AN or EN go up two
39092 I2. For all characters with an odd (right-to-left) embedding direction,
39093 those of type L, EN or AN go up one level.
39095 for (i = 0; i < strLength; ++i) {
39097 if (isEven(levels[i])) {
39100 } else if (t === 'AN' || t === 'EN') {
39104 if (t === 'L' || t === 'AN' || t === 'EN') {
39111 L1. On each line, reset the embedding level of the following characters to
39112 the paragraph embedding level:
39114 segment separators,
39115 paragraph separators,
39116 any sequence of whitespace characters preceding a segment separator or
39117 paragraph separator, and any sequence of white space characters at the end
39121 // don't bother as text is only single line
39124 L2. From the highest level found in the text to the lowest odd level on
39125 each line, reverse any contiguous sequence of characters that are at that
39129 // find highest level & lowest odd level
39130 var highestLevel = -1;
39131 var lowestOddLevel = 99;
39133 for (i = 0, ii = levels.length; i < ii; ++i) {
39135 if (highestLevel < level) {
39136 highestLevel = level;
39138 if (lowestOddLevel > level && isOdd(level)) {
39139 lowestOddLevel = level;
39143 // now reverse between those limits
39144 for (level = highestLevel; level >= lowestOddLevel; --level) {
39145 // find segments to reverse
39147 for (i = 0, ii = levels.length; i < ii; ++i) {
39148 if (levels[i] < level) {
39150 reverseValues(chars, start, i);
39153 } else if (start < 0) {
39158 reverseValues(chars, start, levels.length);
39163 L3. Combining marks applied to a right-to-left base character will at this
39164 point precede their base character. If the rendering engine expects them to
39165 follow the base characters in the final display process, then the ordering
39166 of the marks and the base character must be reversed.
39169 // don't bother for now
39172 L4. A character that possesses the mirrored property as specified by
39173 Section 4.7, Mirrored, must be depicted by a mirrored glyph if the resolved
39174 directionality of that character is R.
39177 // don't mirror as characters are already mirrored in the pdf
39179 // Finally, return string
39181 for (i = 0, ii = chars.length; i < ii; ++i) {
39183 if (ch !== '<' && ch !== '>') {
39187 return createBidiText(result, isLTR);
39193 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
39194 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
39196 /* Copyright 2014 Opera Software ASA
39198 * Licensed under the Apache License, Version 2.0 (the "License");
39199 * you may not use this file except in compliance with the License.
39200 * You may obtain a copy of the License at
39202 * http://www.apache.org/licenses/LICENSE-2.0
39204 * Unless required by applicable law or agreed to in writing, software
39205 * distributed under the License is distributed on an "AS IS" BASIS,
39206 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39207 * See the License for the specific language governing permissions and
39208 * limitations under the License.
39211 * Based on https://code.google.com/p/smhasher/wiki/MurmurHash3.
39212 * Hashes roughly 100 KB per millisecond on i7 3.4 GHz.
39214 /* globals Uint32ArrayView */
39218 var MurmurHash3_64 = (function MurmurHash3_64Closure (seed) {
39219 // Workaround for missing math precison in JS.
39220 var MASK_HIGH = 0xffff0000;
39221 var MASK_LOW = 0xffff;
39223 function MurmurHash3_64 (seed) {
39224 var SEED = 0xc3d2e1f0;
39225 this.h1 = seed ? seed & 0xffffffff : SEED;
39226 this.h2 = seed ? seed & 0xffffffff : SEED;
39229 var alwaysUseUint32ArrayView = false;
39230 // old webkits have issues with non-aligned arrays
39232 new Uint32Array(new Uint8Array(5).buffer, 0, 1);
39234 alwaysUseUint32ArrayView = true;
39237 MurmurHash3_64.prototype = {
39238 update: function MurmurHash3_64_update(input) {
39239 var useUint32ArrayView = alwaysUseUint32ArrayView;
39241 if (typeof input === 'string') {
39242 var data = new Uint8Array(input.length * 2);
39244 for (i = 0; i < input.length; i++) {
39245 var code = input.charCodeAt(i);
39246 if (code <= 0xff) {
39247 data[length++] = code;
39250 data[length++] = code >>> 8;
39251 data[length++] = code & 0xff;
39254 } else if (input instanceof Uint8Array) {
39256 length = data.length;
39257 } else if (typeof input === 'object' && ('length' in input)) {
39258 // processing regular arrays as well, e.g. for IE9
39260 length = data.length;
39261 useUint32ArrayView = true;
39263 throw new Error('Wrong data format in MurmurHash3_64_update. ' +
39264 'Input must be a string or array.');
39267 var blockCounts = length >> 2;
39268 var tailLength = length - blockCounts * 4;
39269 // we don't care about endianness here
39270 var dataUint32 = useUint32ArrayView ?
39271 new Uint32ArrayView(data, blockCounts) :
39272 new Uint32Array(data.buffer, 0, blockCounts);
39277 var C1 = 0xcc9e2d51;
39278 var C2 = 0x1b873593;
39279 var C1_LOW = C1 & MASK_LOW;
39280 var C2_LOW = C2 & MASK_LOW;
39282 for (i = 0; i < blockCounts; i++) {
39284 k1 = dataUint32[i];
39285 k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW);
39286 k1 = k1 << 15 | k1 >>> 17;
39287 k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW);
39289 h1 = h1 << 13 | h1 >>> 19;
39290 h1 = h1 * 5 + 0xe6546b64;
39292 k2 = dataUint32[i];
39293 k2 = (k2 * C1 & MASK_HIGH) | (k2 * C1_LOW & MASK_LOW);
39294 k2 = k2 << 15 | k2 >>> 17;
39295 k2 = (k2 * C2 & MASK_HIGH) | (k2 * C2_LOW & MASK_LOW);
39297 h2 = h2 << 13 | h2 >>> 19;
39298 h2 = h2 * 5 + 0xe6546b64;
39304 switch (tailLength) {
39306 k1 ^= data[blockCounts * 4 + 2] << 16;
39307 /* falls through */
39309 k1 ^= data[blockCounts * 4 + 1] << 8;
39310 /* falls through */
39312 k1 ^= data[blockCounts * 4];
39313 /* falls through */
39314 k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW);
39315 k1 = k1 << 15 | k1 >>> 17;
39316 k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW);
39317 if (blockCounts & 1) {
39329 hexdigest: function MurmurHash3_64_hexdigest () {
39334 h1 = (h1 * 0xed558ccd & MASK_HIGH) | (h1 * 0x8ccd & MASK_LOW);
39335 h2 = (h2 * 0xff51afd7 & MASK_HIGH) |
39336 (((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16);
39338 h1 = (h1 * 0x1a85ec53 & MASK_HIGH) | (h1 * 0xec53 & MASK_LOW);
39339 h2 = (h2 * 0xc4ceb9fe & MASK_HIGH) |
39340 (((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16);
39343 for (var i = 0, arr = [h1, h2], str = ''; i < arr.length; i++) {
39344 var hex = (arr[i] >>> 0).toString(16);
39345 while (hex.length < 8) {
39355 return MurmurHash3_64;
39359 }).call((typeof window === 'undefined') ? this : window);
39361 if (!PDFJS.workerSrc && typeof document !== 'undefined') {
39362 // workerSrc is not set -- using last script url to define default location
39363 PDFJS.workerSrc = (function () {
39365 var scriptTagContainer = document.body ||
39366 document.getElementsByTagName('head')[0];
39367 var pdfjsSrc = scriptTagContainer.lastChild.src;
39368 return pdfjsSrc && pdfjsSrc.replace(/\.js$/i, '.worker.js');