Merge remote-tracking branch 'upstream/master' into dev
Conflicts: Makefile test/unit/unit_test.html
This commit is contained in:
		
						commit
						09eed8d971
					
				
							
								
								
									
										1
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								LICENSE
									
									
									
									
									
								
							| @ -9,6 +9,7 @@ | |||||||
|                   Yury Delendik |                   Yury Delendik | ||||||
|                   Kalervo Kujala |                   Kalervo Kujala | ||||||
|                   Adil Allawi <@ironymark> |                   Adil Allawi <@ironymark> | ||||||
|  |                   Jakob Miland <saebekassebil@gmail.com> | ||||||
| 
 | 
 | ||||||
|     Permission is hereby granted, free of charge, to any person obtaining a |     Permission is hereby granted, free of charge, to any person obtaining a | ||||||
|     copy of this software and associated documentation files (the "Software"), |     copy of this software and associated documentation files (the "Software"), | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Makefile
									
									
									
									
									
								
							| @ -68,7 +68,8 @@ bundle: | $(BUILD_DIR) | |||||||
| 	@cd src; \
 | 	@cd src; \
 | ||||||
| 	cat $(PDF_JS_FILES) > all_files.tmp; \
 | 	cat $(PDF_JS_FILES) > all_files.tmp; \
 | ||||||
| 	sed '/PDFJSSCRIPT_INCLUDE_ALL/ r all_files.tmp' pdf.js > ../$(BUILD_TARGET); \
 | 	sed '/PDFJSSCRIPT_INCLUDE_ALL/ r all_files.tmp' pdf.js > ../$(BUILD_TARGET); \
 | ||||||
| 	sed -i '' "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%H" -n 1`/" ../$(BUILD_TARGET); \
 | 	sed -i.bak "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%h" -n 1`/" ../$(BUILD_TARGET); \
 | ||||||
|  | 	rm -f ../$(BUILD_TARGET).bak | ||||||
| 	rm -f *.tmp; \
 | 	rm -f *.tmp; \
 | ||||||
| 	cd .. | 	cd .. | ||||||
| 
 | 
 | ||||||
| @ -138,8 +139,8 @@ browser-test: | |||||||
| # To install gjslint, see:
 | # To install gjslint, see:
 | ||||||
| #
 | #
 | ||||||
| # <http://code.google.com/closure/utilities/docs/linter_howto.html>
 | # <http://code.google.com/closure/utilities/docs/linter_howto.html>
 | ||||||
| SRC_DIRS := . src utils web test test/unit examples/helloworld \
 | SRC_DIRS := . src utils web test examples/helloworld extensions/firefox \
 | ||||||
|             extensions/firefox extensions/firefox/components extensions/chrome |             extensions/firefox/components extensions/chrome test/unit | ||||||
| GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js)) | GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js)) | ||||||
| lint: | lint: | ||||||
| 	gjslint --nojsdoc $(GJSLINT_FILES) | 	gjslint --nojsdoc $(GJSLINT_FILES) | ||||||
|  | |||||||
| @ -205,3 +205,4 @@ a "PDF Reference" from Adobe: | |||||||
| 
 | 
 | ||||||
| Recommended chapters to read: "2. Overview", "3.4 File Structure",  | Recommended chapters to read: "2. Overview", "3.4 File Structure",  | ||||||
| "4.1 Graphics Objects" that lists the PDF commands. | "4.1 Graphics Objects" that lists the PDF commands. | ||||||
|  | 
 | ||||||
|  | |||||||
							
								
								
									
										141
									
								
								examples/acroforms/forms.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								examples/acroforms/forms.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,141 @@ | |||||||
|  | /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||||||
|  | /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // Basic AcroForms input controls rendering
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | var formFields = {}; | ||||||
|  | 
 | ||||||
|  | function setupForm(div, content, scale) { | ||||||
|  |   function bindInputItem(input, item) { | ||||||
|  |     if (input.name in formFields) { | ||||||
|  |       var value = formFields[input.name]; | ||||||
|  |       if (input.type == 'checkbox') | ||||||
|  |         input.checked = value; | ||||||
|  |       else if (!input.type || input.type == 'text') | ||||||
|  |         input.value = value; | ||||||
|  |     } | ||||||
|  |     input.onchange = function pageViewSetupInputOnBlur() { | ||||||
|  |       if (input.type == 'checkbox') | ||||||
|  |         formFields[input.name] = input.checked; | ||||||
|  |       else if (!input.type || input.type == 'text') | ||||||
|  |         formFields[input.name] = input.value; | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |   function createElementWithStyle(tagName, item) { | ||||||
|  |     var element = document.createElement(tagName); | ||||||
|  |     element.style.left = (item.x * scale) + 'px'; | ||||||
|  |     element.style.top = (item.y * scale) + 'px'; | ||||||
|  |     element.style.width = Math.ceil(item.width * scale) + 'px'; | ||||||
|  |     element.style.height = Math.ceil(item.height * scale) + 'px'; | ||||||
|  |     return element; | ||||||
|  |   } | ||||||
|  |   function assignFontStyle(element, item) { | ||||||
|  |     var fontStyles = ''; | ||||||
|  |     if ('fontSize' in item) | ||||||
|  |       fontStyles += 'font-size: ' + Math.round(item.fontSize * scale) + 'px;'; | ||||||
|  |     switch (item.textAlignment) { | ||||||
|  |       case 0: | ||||||
|  |         fontStyles += 'text-align: left;'; | ||||||
|  |         break; | ||||||
|  |       case 1: | ||||||
|  |         fontStyles += 'text-align: center;'; | ||||||
|  |         break; | ||||||
|  |       case 2: | ||||||
|  |         fontStyles += 'text-align: right;'; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     element.setAttribute('style', element.getAttribute('style') + fontStyles); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var items = content.getAnnotations(); | ||||||
|  |   for (var i = 0; i < items.length; i++) { | ||||||
|  |     var item = items[i]; | ||||||
|  |     switch (item.type) { | ||||||
|  |       case 'Widget': | ||||||
|  |         if (item.fieldType != 'Tx' && item.fieldType != 'Btn' && | ||||||
|  |             item.fieldType != 'Ch') | ||||||
|  |           break; | ||||||
|  |         var inputDiv = createElementWithStyle('div', item); | ||||||
|  |         inputDiv.className = 'inputHint'; | ||||||
|  |         div.appendChild(inputDiv); | ||||||
|  |         var input; | ||||||
|  |         if (item.fieldType == 'Tx') { | ||||||
|  |           input = createElementWithStyle('input', item); | ||||||
|  |         } | ||||||
|  |         if (item.fieldType == 'Btn') { | ||||||
|  |           input = createElementWithStyle('input', item); | ||||||
|  |           if (item.flags & 32768) { | ||||||
|  |             input.type = 'radio'; | ||||||
|  |              // radio button is not supported
 | ||||||
|  |           } else if (item.flags & 65536) { | ||||||
|  |             input.type = 'button'; | ||||||
|  |             // pushbutton is not supported
 | ||||||
|  |           } else { | ||||||
|  |             input.type = 'checkbox'; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         if (item.fieldType == 'Ch') { | ||||||
|  |           input = createElementWithStyle('select', item); | ||||||
|  |           // select box is not supported
 | ||||||
|  |         } | ||||||
|  |         input.className = 'inputControl'; | ||||||
|  |         input.name = item.fullName; | ||||||
|  |         input.title = item.alternativeText; | ||||||
|  |         assignFontStyle(input, item); | ||||||
|  |         bindInputItem(input, item); | ||||||
|  |         div.appendChild(input); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function renderPage(div, pdf, pageNumber, callback) { | ||||||
|  |   var page = pdf.getPage(pageNumber); | ||||||
|  |   var scale = 1.5; | ||||||
|  | 
 | ||||||
|  |   var pageDisplayWidth = page.width * scale; | ||||||
|  |   var pageDisplayHeight = page.height * scale; | ||||||
|  | 
 | ||||||
|  |   var pageDivHolder = document.createElement('div'); | ||||||
|  |   pageDivHolder.className = 'pdfpage'; | ||||||
|  |   pageDivHolder.style.width = pageDisplayWidth + 'px'; | ||||||
|  |   pageDivHolder.style.height = pageDisplayHeight + 'px'; | ||||||
|  |   div.appendChild(pageDivHolder); | ||||||
|  | 
 | ||||||
|  |   // Prepare canvas using PDF page dimensions
 | ||||||
|  |   var canvas = document.createElement('canvas'); | ||||||
|  |   var context = canvas.getContext('2d'); | ||||||
|  |   canvas.width = pageDisplayWidth; | ||||||
|  |   canvas.height = pageDisplayHeight; | ||||||
|  |   pageDivHolder.appendChild(canvas); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   // Render PDF page into canvas context
 | ||||||
|  |   page.startRendering(context, callback); | ||||||
|  | 
 | ||||||
|  |   // Prepare and populate form elements layer
 | ||||||
|  |   var formDiv = document.createElement('div'); | ||||||
|  |   pageDivHolder.appendChild(formDiv); | ||||||
|  | 
 | ||||||
|  |   setupForm(formDiv, page, scale); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PDFJS.getPdf(pdfWithFormsPath, function getPdfForm(data) { | ||||||
|  |   // Instantiate PDFDoc with PDF data
 | ||||||
|  |   var pdf = new PDFJS.PDFDoc(data); | ||||||
|  |    | ||||||
|  |   // Rendering all pages starting from first
 | ||||||
|  |   var viewer = document.getElementById('viewer'); | ||||||
|  |   var pageNumber = 1; | ||||||
|  |   renderPage(viewer, pdf, pageNumber++, function pageRenderingComplete() { | ||||||
|  |     if (pageNumber > pdf.numPages) | ||||||
|  |       return; // All pages rendered
 | ||||||
|  |     // Continue rendering of the next page
 | ||||||
|  |     renderPage(viewer, pdf, pageNumber++, pageRenderingComplete); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
							
								
								
									
										52
									
								
								examples/acroforms/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								examples/acroforms/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | <!doctype html> | ||||||
|  | <html> | ||||||
|  | 
 | ||||||
|  | <head> | ||||||
|  |   <!-- In production, only one script (pdf.js) is necessary --> | ||||||
|  |   <!-- In production, change the content of PDFJS.workerSrc below --> | ||||||
|  |   <script type="text/javascript" src="../../src/core.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/util.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/canvas.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/obj.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/function.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/charsets.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/cidmaps.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/colorspace.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/crypto.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/evaluator.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/fonts.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/glyphlist.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/image.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/metrics.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/parser.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/pattern.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/stream.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../src/worker.js"></script> | ||||||
|  |   <script type="text/javascript" src="../../external/jpgjs/jpg.js"></script> | ||||||
|  | 
 | ||||||
|  |   <script type="text/javascript"> | ||||||
|  |     // Specify the main script used to create a new PDF.JS web worker. | ||||||
|  |     // In production, change this to point to the combined `pdf.js` file. | ||||||
|  |     PDFJS.workerSrc = '../../src/worker_loader.js'; | ||||||
|  | 
 | ||||||
|  |     // Specify the PDF with AcroForm here | ||||||
|  |     var pdfWithFormsPath = '../../test/pdfs/f1040.pdf'; | ||||||
|  |   </script> | ||||||
|  |    | ||||||
|  |   <style> | ||||||
|  |   .pdfpage { position:relative; top: 0; left: 0; border: solid 1px black; margin: 10px; } | ||||||
|  |   .pdfpage > canvas { position: absolute; top: 0; left: 0; } | ||||||
|  |   .pdfpage > div { position: absolute; top: 0; left: 0; } | ||||||
|  |   .inputControl { background: transparent; border: 0px none; position: absolute; margin: auto; } | ||||||
|  |   .inputControl[type='checkbox'] { margin: 0px; } | ||||||
|  |   .inputHint { opacity: 0.2; background: #ccc; position: absolute; } | ||||||
|  |   </style> | ||||||
|  | 
 | ||||||
|  |   <script type="text/javascript" src="forms.js"></script> | ||||||
|  | </head> | ||||||
|  | 
 | ||||||
|  | <body> | ||||||
|  |   <div id="viewer"></div> | ||||||
|  | </body> | ||||||
|  | 
 | ||||||
|  | </html> | ||||||
| @ -6,13 +6,13 @@ | |||||||
|   <Description about="urn:mozilla:install-manifest"> |   <Description about="urn:mozilla:install-manifest"> | ||||||
|     <em:id>uriloader@pdf.js</em:id> |     <em:id>uriloader@pdf.js</em:id> | ||||||
|     <em:name>pdf.js</em:name> |     <em:name>pdf.js</em:name> | ||||||
|     <em:version>0.1</em:version> |     <em:version>0.1.0</em:version> | ||||||
| 	  <em:iconURL>chrome://pdf.js/skin/logo.png</em:iconURL> | 	  <em:iconURL>chrome://pdf.js/skin/logo.png</em:iconURL> | ||||||
|     <em:targetApplication> |     <em:targetApplication> | ||||||
|       <Description> |       <Description> | ||||||
|        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> |        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> | ||||||
|        <em:minVersion>6.0</em:minVersion> |        <em:minVersion>6.0</em:minVersion> | ||||||
|        <em:maxVersion>11.0.*</em:maxVersion> |        <em:maxVersion>11.0a1</em:maxVersion> | ||||||
|      </Description> |      </Description> | ||||||
|     </em:targetApplication> |     </em:targetApplication> | ||||||
|     <em:bootstrap>true</em:bootstrap> |     <em:bootstrap>true</em:bootstrap> | ||||||
| @ -20,5 +20,6 @@ | |||||||
|     <em:creator>Vivien Nicolas</em:creator> |     <em:creator>Vivien Nicolas</em:creator> | ||||||
|     <em:description>pdf.js uri loader</em:description> |     <em:description>pdf.js uri loader</em:description> | ||||||
|     <em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL> |     <em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL> | ||||||
|  |     <em:type>2</em:type> | ||||||
|   </Description> |   </Description> | ||||||
| </RDF> | </RDF> | ||||||
|  | |||||||
							
								
								
									
										463
									
								
								src/canvas.js
									
									
									
									
									
								
							
							
						
						
									
										463
									
								
								src/canvas.js
									
									
									
									
									
								
							| @ -6,8 +6,19 @@ | |||||||
| // <canvas> contexts store most of the state we need natively.
 | // <canvas> contexts store most of the state we need natively.
 | ||||||
| // However, PDF needs a bit more state, which we store here.
 | // However, PDF needs a bit more state, which we store here.
 | ||||||
| 
 | 
 | ||||||
| var CanvasExtraState = (function canvasExtraState() { | var TextRenderingMode = { | ||||||
|   function constructor(old) { |   FILL: 0, | ||||||
|  |   STROKE: 1, | ||||||
|  |   FILL_STROKE: 2, | ||||||
|  |   INVISIBLE: 3, | ||||||
|  |   FILL_ADD_TO_PATH: 4, | ||||||
|  |   STROKE_ADD_TO_PATH: 5, | ||||||
|  |   FILL_STROKE_ADD_TO_PATH: 6, | ||||||
|  |   ADD_TO_PATH: 7 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | var CanvasExtraState = (function CanvasExtraStateClosure() { | ||||||
|  |   function CanvasExtraState(old) { | ||||||
|     // Are soft masks and alpha values shapes or opacities?
 |     // Are soft masks and alpha values shapes or opacities?
 | ||||||
|     this.alphaIsShape = false; |     this.alphaIsShape = false; | ||||||
|     this.fontSize = 0; |     this.fontSize = 0; | ||||||
| @ -23,6 +34,7 @@ var CanvasExtraState = (function canvasExtraState() { | |||||||
|     this.charSpacing = 0; |     this.charSpacing = 0; | ||||||
|     this.wordSpacing = 0; |     this.wordSpacing = 0; | ||||||
|     this.textHScale = 1; |     this.textHScale = 1; | ||||||
|  |     this.textRenderingMode = TextRenderingMode.FILL; | ||||||
|     // Color spaces
 |     // Color spaces
 | ||||||
|     this.fillColorSpace = new DeviceGrayCS(); |     this.fillColorSpace = new DeviceGrayCS(); | ||||||
|     this.fillColorSpaceObj = null; |     this.fillColorSpaceObj = null; | ||||||
| @ -40,7 +52,7 @@ var CanvasExtraState = (function canvasExtraState() { | |||||||
|     this.old = old; |     this.old = old; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   CanvasExtraState.prototype = { | ||||||
|     clone: function canvasextra_clone() { |     clone: function canvasextra_clone() { | ||||||
|       return Object.create(this); |       return Object.create(this); | ||||||
|     }, |     }, | ||||||
| @ -49,7 +61,7 @@ var CanvasExtraState = (function canvasExtraState() { | |||||||
|       this.y = y; |       this.y = y; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return CanvasExtraState; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| function ScratchCanvas(width, height) { | function ScratchCanvas(width, height) { | ||||||
| @ -59,16 +71,122 @@ function ScratchCanvas(width, height) { | |||||||
|   return canvas; |   return canvas; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var CanvasGraphics = (function canvasGraphics() { | function addContextCurrentTransform(ctx) { | ||||||
|  |   // If the context doesn't expose a `mozCurrentTransform`, add a JS based on.
 | ||||||
|  |   if (!ctx.mozCurrentTransform) { | ||||||
|  |     // Store the original context
 | ||||||
|  |     ctx._originalSave = ctx.save; | ||||||
|  |     ctx._originalRestore = ctx.restore; | ||||||
|  |     ctx._originalRotate = ctx.rotate; | ||||||
|  |     ctx._originalScale = ctx.scale; | ||||||
|  |     ctx._originalTranslate = ctx.translate; | ||||||
|  |     ctx._originalTransform = ctx.transform; | ||||||
|  | 
 | ||||||
|  |     ctx._transformMatrix = [1, 0, 0, 1, 0, 0]; | ||||||
|  |     ctx._transformStack = []; | ||||||
|  | 
 | ||||||
|  |     Object.defineProperty(ctx, 'mozCurrentTransform', { | ||||||
|  |       get: function getCurrentTransform() { | ||||||
|  |         return this._transformMatrix; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     Object.defineProperty(ctx, 'mozCurrentTransformInverse', { | ||||||
|  |       get: function getCurrentTransformInverse() { | ||||||
|  |         // Calculation done using WolframAlpha:
 | ||||||
|  |         // http://www.wolframalpha.com/input/?
 | ||||||
|  |         //   i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}}
 | ||||||
|  | 
 | ||||||
|  |         var m = this._transformMatrix; | ||||||
|  |         var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5]; | ||||||
|  | 
 | ||||||
|  |         var ad_bc = a * d - b * c; | ||||||
|  |         var bc_ad = b * c - a * d; | ||||||
|  | 
 | ||||||
|  |         return [ | ||||||
|  |           d / ad_bc, | ||||||
|  |           b / bc_ad, | ||||||
|  |           c / bc_ad, | ||||||
|  |           a / ad_bc, | ||||||
|  |           (d * e - c * f) / bc_ad, | ||||||
|  |           (b * e - a * f) / ad_bc | ||||||
|  |         ]; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     ctx.save = function ctxSave() { | ||||||
|  |       var old = this._transformMatrix; | ||||||
|  |       this._transformStack.push(old); | ||||||
|  |       this._transformMatrix = old.slice(0, 6); | ||||||
|  | 
 | ||||||
|  |       this._originalSave(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     ctx.restore = function ctxRestore() { | ||||||
|  |       var prev = this._transformStack.pop(); | ||||||
|  |       if (prev) { | ||||||
|  |         this._transformMatrix = prev; | ||||||
|  |         this._originalRestore(); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     ctx.translate = function ctxTranslate(x, y) { | ||||||
|  |       var m = this._transformMatrix; | ||||||
|  |       m[4] = m[0] * x + m[2] * y + m[4]; | ||||||
|  |       m[5] = m[1] * x + m[3] * y + m[5]; | ||||||
|  | 
 | ||||||
|  |       this._originalTranslate(x, y); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     ctx.scale = function ctxScale(x, y) { | ||||||
|  |       var m = this._transformMatrix; | ||||||
|  |       m[0] = m[0] * x; | ||||||
|  |       m[1] = m[1] * x; | ||||||
|  |       m[2] = m[2] * y; | ||||||
|  |       m[3] = m[3] * y; | ||||||
|  | 
 | ||||||
|  |       this._originalScale(x, y); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     ctx.transform = function ctxTransform(a, b, c, d, e, f) { | ||||||
|  |       var m = this._transformMatrix; | ||||||
|  |       this._transformMatrix = [ | ||||||
|  |         m[0] * a + m[2] * b, | ||||||
|  |         m[1] * a + m[3] * b, | ||||||
|  |         m[0] * c + m[2] * d, | ||||||
|  |         m[1] * c + m[3] * d, | ||||||
|  |         m[0] * e + m[2] * f + m[4], | ||||||
|  |         m[1] * e + m[3] * f + m[5] | ||||||
|  |       ]; | ||||||
|  | 
 | ||||||
|  |       ctx._originalTransform(a, b, c, d, e, f); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     ctx.rotate = function ctxRotate(angle) { | ||||||
|  |       var cosValue = Math.cos(angle); | ||||||
|  |       var sinValue = Math.sin(angle); | ||||||
|  | 
 | ||||||
|  |       var m = this._transformMatrix; | ||||||
|  |       this._transformMatrix = [ | ||||||
|  |         m[0] * cosValue + m[2] * sinValue, | ||||||
|  |         m[1] * cosValue + m[3] * sinValue, | ||||||
|  |         m[0] * (-sinValue) + m[2] * cosValue, | ||||||
|  |         m[1] * (-sinValue) + m[3] * cosValue, | ||||||
|  |         m[4], | ||||||
|  |         m[5] | ||||||
|  |       ]; | ||||||
|  | 
 | ||||||
|  |       this._originalRotate(angle); | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var CanvasGraphics = (function CanvasGraphicsClosure() { | ||||||
|   // Defines the time the executeIRQueue is going to be executing
 |   // Defines the time the executeIRQueue is going to be executing
 | ||||||
|   // before it stops and shedules a continue of execution.
 |   // before it stops and shedules a continue of execution.
 | ||||||
|   var kExecutionTime = 50; |   var kExecutionTime = 50; | ||||||
| 
 | 
 | ||||||
|   // Number of IR commands to execute before checking
 |   function CanvasGraphics(canvasCtx, objs, textLayer) { | ||||||
|   // if we execute longer then `kExecutionTime`.
 |  | ||||||
|   var kExecutionTimeCheck = 500; |  | ||||||
| 
 |  | ||||||
|   function constructor(canvasCtx, objs) { |  | ||||||
|     this.ctx = canvasCtx; |     this.ctx = canvasCtx; | ||||||
|     this.current = new CanvasExtraState(); |     this.current = new CanvasExtraState(); | ||||||
|     this.stateStack = []; |     this.stateStack = []; | ||||||
| @ -77,6 +195,10 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|     this.xobjs = null; |     this.xobjs = null; | ||||||
|     this.ScratchCanvas = ScratchCanvas; |     this.ScratchCanvas = ScratchCanvas; | ||||||
|     this.objs = objs; |     this.objs = objs; | ||||||
|  |     this.textLayer = textLayer; | ||||||
|  |     if (canvasCtx) { | ||||||
|  |       addContextCurrentTransform(canvasCtx); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   var LINE_CAP_STYLES = ['butt', 'round', 'square']; |   var LINE_CAP_STYLES = ['butt', 'round', 'square']; | ||||||
| @ -84,7 +206,36 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|   var NORMAL_CLIP = {}; |   var NORMAL_CLIP = {}; | ||||||
|   var EO_CLIP = {}; |   var EO_CLIP = {}; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   CanvasGraphics.prototype = { | ||||||
|  |     slowCommands: { | ||||||
|  |       'stroke': true, | ||||||
|  |       'closeStroke': true, | ||||||
|  |       'fill': true, | ||||||
|  |       'eoFill': true, | ||||||
|  |       'fillStroke': true, | ||||||
|  |       'eoFillStroke': true, | ||||||
|  |       'closeFillStroke': true, | ||||||
|  |       'closeEOFillStroke': true, | ||||||
|  |       'showText': true, | ||||||
|  |       'showSpacedText': true, | ||||||
|  |       'setStrokeColorSpace': true, | ||||||
|  |       'setFillColorSpace': true, | ||||||
|  |       'setStrokeColor': true, | ||||||
|  |       'setStrokeColorN': true, | ||||||
|  |       'setFillColor': true, | ||||||
|  |       'setFillColorN_IR': true, | ||||||
|  |       'setStrokeGray': true, | ||||||
|  |       'setFillGray': true, | ||||||
|  |       'setStrokeRGBColor': true, | ||||||
|  |       'setFillRGBColor': true, | ||||||
|  |       'setStrokeCMYKColor': true, | ||||||
|  |       'setFillCMYKColor': true, | ||||||
|  |       'paintJpegXObject': true, | ||||||
|  |       'paintImageXObject': true, | ||||||
|  |       'paintImageMaskXObject': true, | ||||||
|  |       'shadingFill': true | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|     beginDrawing: function canvasGraphicsBeginDrawing(mediaBox) { |     beginDrawing: function canvasGraphicsBeginDrawing(mediaBox) { | ||||||
|       var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height; |       var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height; | ||||||
|       this.ctx.save(); |       this.ctx.save(); | ||||||
| @ -102,7 +253,13 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|           this.ctx.transform(0, -1, -1, 0, cw, ch); |           this.ctx.transform(0, -1, -1, 0, cw, ch); | ||||||
|           break; |           break; | ||||||
|       } |       } | ||||||
|  |       // Scale so that canvas units are the same as PDF user space units
 | ||||||
|       this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height); |       this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height); | ||||||
|  |       // Move the media left-top corner to the (0,0) canvas position
 | ||||||
|  |       this.ctx.translate(-mediaBox.x, -mediaBox.y); | ||||||
|  | 
 | ||||||
|  |       if (this.textLayer) | ||||||
|  |         this.textLayer.beginLayout(); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR, |     executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR, | ||||||
| @ -112,32 +269,39 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|       var i = executionStartIdx || 0; |       var i = executionStartIdx || 0; | ||||||
|       var argsArrayLen = argsArray.length; |       var argsArrayLen = argsArray.length; | ||||||
| 
 | 
 | ||||||
|  |       // Sometimes the IRQueue to execute is empty.
 | ||||||
|  |       if (argsArrayLen == i) { | ||||||
|  |         return i; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       var executionEndIdx; |       var executionEndIdx; | ||||||
|       var startTime = Date.now(); |       var endTime = Date.now() + kExecutionTime; | ||||||
| 
 | 
 | ||||||
|       var objs = this.objs; |       var objs = this.objs; | ||||||
|  |       var fnName; | ||||||
|  |       var slowCommands = this.slowCommands; | ||||||
| 
 | 
 | ||||||
|       do { |       while (true) { | ||||||
|         executionEndIdx = Math.min(argsArrayLen, i + kExecutionTimeCheck); |         fnName = fnArray[i]; | ||||||
| 
 | 
 | ||||||
|         for (i; i < executionEndIdx; i++) { |         if (fnName !== 'dependency') { | ||||||
|           if (fnArray[i] !== 'dependency') { |           this[fnName].apply(this, argsArray[i]); | ||||||
|             this[fnArray[i]].apply(this, argsArray[i]); |         } else { | ||||||
|           } else { |           var deps = argsArray[i]; | ||||||
|             var deps = argsArray[i]; |           for (var n = 0, nn = deps.length; n < nn; n++) { | ||||||
|             for (var n = 0, nn = deps.length; n < nn; n++) { |             var depObjId = deps[n]; | ||||||
|               var depObjId = deps[n]; |  | ||||||
| 
 | 
 | ||||||
|               // If the promise isn't resolved yet, add the continueCallback
 |             // If the promise isn't resolved yet, add the continueCallback
 | ||||||
|               // to the promise and bail out.
 |             // to the promise and bail out.
 | ||||||
|               if (!objs.isResolved(depObjId)) { |             if (!objs.isResolved(depObjId)) { | ||||||
|                 objs.get(depObjId, continueCallback); |               objs.get(depObjId, continueCallback); | ||||||
|                 return i; |               return i; | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         i++; | ||||||
|  | 
 | ||||||
|         // If the entire IRQueue was executed, stop as were done.
 |         // If the entire IRQueue was executed, stop as were done.
 | ||||||
|         if (i == argsArrayLen) { |         if (i == argsArrayLen) { | ||||||
|           return i; |           return i; | ||||||
| @ -146,18 +310,21 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|         // If the execution took longer then a certain amount of time, shedule
 |         // If the execution took longer then a certain amount of time, shedule
 | ||||||
|         // to continue exeution after a short delay.
 |         // to continue exeution after a short delay.
 | ||||||
|         // However, this is only possible if a 'continueCallback' is passed in.
 |         // However, this is only possible if a 'continueCallback' is passed in.
 | ||||||
|         if (continueCallback && (Date.now() - startTime) > kExecutionTime) { |         if (continueCallback && slowCommands[fnName] && Date.now() > endTime) { | ||||||
|           setTimeout(continueCallback, 0); |           setTimeout(continueCallback, 0); | ||||||
|           return i; |           return i; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // If the IRQueue isn't executed completly yet OR the execution time
 |         // If the IRQueue isn't executed completly yet OR the execution time
 | ||||||
|         // was short enough, do another execution round.
 |         // was short enough, do another execution round.
 | ||||||
|       } while (true); |       } | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     endDrawing: function canvasGraphicsEndDrawing() { |     endDrawing: function canvasGraphicsEndDrawing() { | ||||||
|       this.ctx.restore(); |       this.ctx.restore(); | ||||||
|  | 
 | ||||||
|  |       if (this.textLayer) | ||||||
|  |         this.textLayer.endLayout(); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     // Graphics state
 |     // Graphics state
 | ||||||
| @ -176,6 +343,8 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|     setDash: function canvasGraphicsSetDash(dashArray, dashPhase) { |     setDash: function canvasGraphicsSetDash(dashArray, dashPhase) { | ||||||
|       this.ctx.mozDash = dashArray; |       this.ctx.mozDash = dashArray; | ||||||
|       this.ctx.mozDashOffset = dashPhase; |       this.ctx.mozDashOffset = dashPhase; | ||||||
|  |       this.ctx.webkitLineDash = dashArray; | ||||||
|  |       this.ctx.webkitLineDashOffset = dashPhase; | ||||||
|     }, |     }, | ||||||
|     setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) { |     setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) { | ||||||
|       TODO('set rendering intent: ' + intent); |       TODO('set rendering intent: ' + intent); | ||||||
| @ -394,7 +563,9 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|       this.ctx.font = rule; |       this.ctx.font = rule; | ||||||
|     }, |     }, | ||||||
|     setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) { |     setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) { | ||||||
|       TODO('text rendering mode: ' + mode); |       if (mode >= TextRenderingMode.FILL_ADD_TO_PATH) | ||||||
|  |         TODO('unsupported text rendering mode: ' + mode); | ||||||
|  |       this.current.textRenderingMode = mode; | ||||||
|     }, |     }, | ||||||
|     setTextRise: function canvasGraphicsSetTextRise(rise) { |     setTextRise: function canvasGraphicsSetTextRise(rise) { | ||||||
|       TODO('text rise: ' + rise); |       TODO('text rise: ' + rise); | ||||||
| @ -416,23 +587,67 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|     nextLine: function canvasGraphicsNextLine() { |     nextLine: function canvasGraphicsNextLine() { | ||||||
|       this.moveText(0, this.current.leading); |       this.moveText(0, this.current.leading); | ||||||
|     }, |     }, | ||||||
|     showText: function canvasGraphicsShowText(text) { |     applyTextTransforms: function canvasApplyTransforms() { | ||||||
|  |       var ctx = this.ctx; | ||||||
|  |       var current = this.current; | ||||||
|  |       var textHScale = current.textHScale; | ||||||
|  |       var fontMatrix = current.font.fontMatrix || IDENTITY_MATRIX; | ||||||
|  | 
 | ||||||
|  |       ctx.transform.apply(ctx, current.textMatrix); | ||||||
|  |       ctx.scale(1, -1); | ||||||
|  |       ctx.translate(current.x, -1 * current.y); | ||||||
|  |       ctx.transform.apply(ctx, fontMatrix); | ||||||
|  |       ctx.scale(textHScale, 1); | ||||||
|  |     }, | ||||||
|  |     getTextGeometry: function canvasGetTextGeometry() { | ||||||
|  |       var geometry = {}; | ||||||
|  |       var ctx = this.ctx; | ||||||
|  |       var font = this.current.font; | ||||||
|  |       var ctxMatrix = ctx.mozCurrentTransform; | ||||||
|  |       if (ctxMatrix) { | ||||||
|  |         var bl = Util.applyTransform([0, 0], ctxMatrix); | ||||||
|  |         var tr = Util.applyTransform([1, 1], ctxMatrix); | ||||||
|  |         geometry.x = bl[0]; | ||||||
|  |         geometry.y = bl[1]; | ||||||
|  |         geometry.hScale = tr[0] - bl[0]; | ||||||
|  |         geometry.vScale = tr[1] - bl[1]; | ||||||
|  |       } | ||||||
|  |       geometry.spaceWidth = font.spaceWidth; | ||||||
|  |       return geometry; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     showText: function canvasGraphicsShowText(str, skipTextSelection) { | ||||||
|       var ctx = this.ctx; |       var ctx = this.ctx; | ||||||
|       var current = this.current; |       var current = this.current; | ||||||
|       var font = current.font; |       var font = current.font; | ||||||
|       var glyphs = font.charsToGlyphs(text); |       var glyphs = font.charsToGlyphs(str); | ||||||
|       var fontSize = current.fontSize; |       var fontSize = current.fontSize; | ||||||
|       var charSpacing = current.charSpacing; |       var charSpacing = current.charSpacing; | ||||||
|       var wordSpacing = current.wordSpacing; |       var wordSpacing = current.wordSpacing; | ||||||
|       var textHScale = current.textHScale; |       var textHScale = current.textHScale; | ||||||
|  |       var fontMatrix = font.fontMatrix || IDENTITY_MATRIX; | ||||||
|  |       var textHScale2 = textHScale * fontMatrix[0]; | ||||||
|       var glyphsLength = glyphs.length; |       var glyphsLength = glyphs.length; | ||||||
|  |       var textLayer = this.textLayer; | ||||||
|  |       var text = {str: '', length: 0, canvasWidth: 0, geom: {}}; | ||||||
|  |       var textSelection = textLayer && !skipTextSelection ? true : false; | ||||||
|  |       var textRenderingMode = current.textRenderingMode; | ||||||
|  | 
 | ||||||
|  |       // Type3 fonts - each glyph is a "mini-PDF"
 | ||||||
|       if (font.coded) { |       if (font.coded) { | ||||||
|         ctx.save(); |         ctx.save(); | ||||||
|         ctx.transform.apply(ctx, current.textMatrix); |         ctx.transform.apply(ctx, current.textMatrix); | ||||||
|         ctx.translate(current.x, current.y); |         ctx.translate(current.x, current.y); | ||||||
| 
 | 
 | ||||||
|         var fontMatrix = font.fontMatrix || IDENTITY_MATRIX; |         ctx.scale(textHScale, 1); | ||||||
|         ctx.scale(1 / textHScale, 1); |         ctx.lineWidth /= current.textMatrix[0]; | ||||||
|  | 
 | ||||||
|  |         if (textSelection) { | ||||||
|  |           this.save(); | ||||||
|  |           ctx.scale(1, -1); | ||||||
|  |           text.geom = this.getTextGeometry(); | ||||||
|  |           this.restore(); | ||||||
|  |         } | ||||||
|         for (var i = 0; i < glyphsLength; ++i) { |         for (var i = 0; i < glyphsLength; ++i) { | ||||||
| 
 | 
 | ||||||
|           var glyph = glyphs[i]; |           var glyph = glyphs[i]; | ||||||
| @ -452,18 +667,20 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|           var width = transformed[0] * fontSize + charSpacing; |           var width = transformed[0] * fontSize + charSpacing; | ||||||
| 
 | 
 | ||||||
|           ctx.translate(width, 0); |           ctx.translate(width, 0); | ||||||
|           current.x += width; |           current.x += width * textHScale; | ||||||
| 
 | 
 | ||||||
|  |           text.str += glyph.unicode; | ||||||
|  |           text.length++; | ||||||
|  |           text.canvasWidth += width; | ||||||
|         } |         } | ||||||
|         ctx.restore(); |         ctx.restore(); | ||||||
|       } else { |       } else { | ||||||
|         ctx.save(); |         ctx.save(); | ||||||
|         ctx.transform.apply(ctx, current.textMatrix); |         this.applyTextTransforms(); | ||||||
|         ctx.scale(1, -1); |         ctx.lineWidth /= current.textMatrix[0] * fontMatrix[0]; | ||||||
|         ctx.translate(current.x, -1 * current.y); |  | ||||||
|         ctx.transform.apply(ctx, font.fontMatrix || IDENTITY_MATRIX); |  | ||||||
| 
 | 
 | ||||||
|         ctx.scale(1 / textHScale, 1); |         if (textSelection) | ||||||
|  |           text.geom = this.getTextGeometry(); | ||||||
| 
 | 
 | ||||||
|         var width = 0; |         var width = 0; | ||||||
|         for (var i = 0; i < glyphsLength; ++i) { |         for (var i = 0; i < glyphsLength; ++i) { | ||||||
| @ -474,36 +691,106 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|             continue; |             continue; | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           var unicode = glyph.unicode; |           var char = glyph.fontChar; | ||||||
|           var char = (unicode >= 0x10000) ? |           var charWidth = glyph.width * fontSize * 0.001 + charSpacing; | ||||||
|             String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10), |  | ||||||
|             0xDC00 | (unicode & 0x3FF)) : String.fromCharCode(unicode); |  | ||||||
| 
 | 
 | ||||||
|           ctx.fillText(char, width, 0); |           switch (textRenderingMode) { | ||||||
|           width += glyph.width * fontSize * 0.001 + charSpacing; |             default: // other unsupported rendering modes
 | ||||||
|  |             case TextRenderingMode.FILL: | ||||||
|  |             case TextRenderingMode.FILL_ADD_TO_PATH: | ||||||
|  |               ctx.fillText(char, width, 0); | ||||||
|  |               break; | ||||||
|  |             case TextRenderingMode.STROKE: | ||||||
|  |             case TextRenderingMode.STROKE_ADD_TO_PATH: | ||||||
|  |               ctx.strokeText(char, width, 0); | ||||||
|  |               break; | ||||||
|  |             case TextRenderingMode.FILL_STROKE: | ||||||
|  |             case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: | ||||||
|  |               ctx.fillText(char, width, 0); | ||||||
|  |               ctx.strokeText(char, width, 0); | ||||||
|  |               break; | ||||||
|  |             case TextRenderingMode.INVISIBLE: | ||||||
|  |               break; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           width += charWidth; | ||||||
|  | 
 | ||||||
|  |           text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode; | ||||||
|  |           text.length++; | ||||||
|  |           text.canvasWidth += charWidth; | ||||||
|         } |         } | ||||||
|         current.x += width; |         current.x += width * textHScale2; | ||||||
| 
 |  | ||||||
|         ctx.restore(); |         ctx.restore(); | ||||||
|       } |       } | ||||||
|     }, |  | ||||||
| 
 | 
 | ||||||
|  |       if (textSelection) | ||||||
|  |         this.textLayer.appendText(text, font.loadedName, fontSize); | ||||||
|  | 
 | ||||||
|  |       return text; | ||||||
|  |     }, | ||||||
|     showSpacedText: function canvasGraphicsShowSpacedText(arr) { |     showSpacedText: function canvasGraphicsShowSpacedText(arr) { | ||||||
|       var ctx = this.ctx; |       var ctx = this.ctx; | ||||||
|       var current = this.current; |       var current = this.current; | ||||||
|  |       var font = current.font; | ||||||
|       var fontSize = current.fontSize; |       var fontSize = current.fontSize; | ||||||
|       var textHScale = current.textHScale; |       var textHScale = current.textHScale; | ||||||
|  |       if (!font.coded) | ||||||
|  |         textHScale *= (font.fontMatrix || IDENTITY_MATRIX)[0]; | ||||||
|       var arrLength = arr.length; |       var arrLength = arr.length; | ||||||
|  |       var textLayer = this.textLayer; | ||||||
|  |       var text = {str: '', length: 0, canvasWidth: 0, geom: {}}; | ||||||
|  |       var textSelection = textLayer ? true : false; | ||||||
|  | 
 | ||||||
|  |       if (textSelection) { | ||||||
|  |         ctx.save(); | ||||||
|  |         // Type3 fonts - each glyph is a "mini-PDF" (see also showText)
 | ||||||
|  |         if (font.coded) { | ||||||
|  |           ctx.transform.apply(ctx, current.textMatrix); | ||||||
|  |           ctx.scale(1, -1); | ||||||
|  |           ctx.translate(current.x, -1 * current.y); | ||||||
|  |           ctx.scale(textHScale, 1); | ||||||
|  |         } else | ||||||
|  |           this.applyTextTransforms(); | ||||||
|  |         text.geom = this.getTextGeometry(); | ||||||
|  |         ctx.restore(); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       for (var i = 0; i < arrLength; ++i) { |       for (var i = 0; i < arrLength; ++i) { | ||||||
|         var e = arr[i]; |         var e = arr[i]; | ||||||
|         if (isNum(e)) { |         if (isNum(e)) { | ||||||
|           current.x -= e * 0.001 * fontSize * textHScale; |           var spacingLength = -e * 0.001 * fontSize * textHScale; | ||||||
|  |           current.x += spacingLength; | ||||||
|  | 
 | ||||||
|  |           if (textSelection) { | ||||||
|  |             // Emulate precise spacing via HTML spaces
 | ||||||
|  |             text.canvasWidth += spacingLength; | ||||||
|  |             if (e < 0 && text.geom.spaceWidth > 0) { // avoid div by zero
 | ||||||
|  |               var numFakeSpaces = Math.round(-e / text.geom.spaceWidth); | ||||||
|  |               if (numFakeSpaces > 0) { | ||||||
|  |                 text.str += '\u00A0'; | ||||||
|  |                 text.length++; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|         } else if (isString(e)) { |         } else if (isString(e)) { | ||||||
|           this.showText(e); |           var shownText = this.showText(e, true); | ||||||
|  | 
 | ||||||
|  |           if (textSelection) { | ||||||
|  |             if (shownText.str === ' ') { | ||||||
|  |               text.str += '\u00A0'; | ||||||
|  |             } else { | ||||||
|  |               text.str += shownText.str; | ||||||
|  |             } | ||||||
|  |             text.canvasWidth += shownText.canvasWidth; | ||||||
|  |             text.length += e.length; | ||||||
|  |           } | ||||||
|         } else { |         } else { | ||||||
|           malformed('TJ array element ' + e + ' is not string or num'); |           malformed('TJ array element ' + e + ' is not string or num'); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       if (textSelection) | ||||||
|  |         this.textLayer.appendText(text, font.loadedName, fontSize); | ||||||
|     }, |     }, | ||||||
|     nextLineShowText: function canvasGraphicsNextLineShowText(text) { |     nextLineShowText: function canvasGraphicsNextLineShowText(text) { | ||||||
|       this.nextLine(); |       this.nextLine(); | ||||||
| @ -658,9 +945,9 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|         var height = canvas.height; |         var height = canvas.height; | ||||||
| 
 | 
 | ||||||
|         var bl = Util.applyTransform([0, 0], inv); |         var bl = Util.applyTransform([0, 0], inv); | ||||||
|         var br = Util.applyTransform([0, width], inv); |         var br = Util.applyTransform([0, height], inv); | ||||||
|         var ul = Util.applyTransform([height, 0], inv); |         var ul = Util.applyTransform([width, 0], inv); | ||||||
|         var ur = Util.applyTransform([height, width], inv); |         var ur = Util.applyTransform([width, height], inv); | ||||||
| 
 | 
 | ||||||
|         var x0 = Math.min(bl[0], br[0], ul[0], ur[0]); |         var x0 = Math.min(bl[0], br[0], ul[0], ur[0]); | ||||||
|         var y0 = Math.min(bl[1], br[1], ul[1], ur[1]); |         var y0 = Math.min(bl[1], br[1], ul[1], ur[1]); | ||||||
| @ -710,8 +997,8 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) { |     paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) { | ||||||
|       var image = this.objs.get(objId); |       var domImage = this.objs.get(objId); | ||||||
|       if (!image) { |       if (!domImage) { | ||||||
|         error('Dependent image isn\'t ready yet'); |         error('Dependent image isn\'t ready yet'); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -721,7 +1008,6 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|       // scale the image to the unit square
 |       // scale the image to the unit square
 | ||||||
|       ctx.scale(1 / w, -1 / h); |       ctx.scale(1 / w, -1 / h); | ||||||
| 
 | 
 | ||||||
|       var domImage = image.getImage(); |  | ||||||
|       ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, |       ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, | ||||||
|                     0, -h, w, h); |                     0, -h, w, h); | ||||||
| 
 | 
 | ||||||
| @ -777,7 +1063,11 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|       this.restore(); |       this.restore(); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     paintImageXObject: function canvasGraphicsPaintImageXObject(imgData) { |     paintImageXObject: function canvasGraphicsPaintImageXObject(objId) { | ||||||
|  |       var imgData = this.objs.get(objId); | ||||||
|  |       if (!imgData) | ||||||
|  |         error('Dependent image isn\'t ready yet'); | ||||||
|  | 
 | ||||||
|       this.save(); |       this.save(); | ||||||
|       var ctx = this.ctx; |       var ctx = this.ctx; | ||||||
|       var w = imgData.width; |       var w = imgData.width; | ||||||
| @ -787,26 +1077,16 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
| 
 | 
 | ||||||
|       var tmpCanvas = new this.ScratchCanvas(w, h); |       var tmpCanvas = new this.ScratchCanvas(w, h); | ||||||
|       var tmpCtx = tmpCanvas.getContext('2d'); |       var tmpCtx = tmpCanvas.getContext('2d'); | ||||||
|       var tmpImgData; |       this.putBinaryImageData(tmpCtx, imgData, w, h); | ||||||
| 
 | 
 | ||||||
|       // Some browsers can set an UInt8Array directly as imageData, some
 |  | ||||||
|       // can't. As long as we don't have proper feature detection, just
 |  | ||||||
|       // copy over each pixel and set the imageData that way.
 |  | ||||||
|       tmpImgData = tmpCtx.getImageData(0, 0, w, h); |  | ||||||
| 
 |  | ||||||
|       // Copy over the imageData.
 |  | ||||||
|       var tmpImgDataPixels = tmpImgData.data; |  | ||||||
|       var len = tmpImgDataPixels.length; |  | ||||||
| 
 |  | ||||||
|       while (len--) { |  | ||||||
|         tmpImgDataPixels[len] = imgData.data[len]; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       tmpCtx.putImageData(tmpImgData, 0, 0); |  | ||||||
|       ctx.drawImage(tmpCanvas, 0, -h); |       ctx.drawImage(tmpCanvas, 0, -h); | ||||||
|       this.restore(); |       this.restore(); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|  |     putBinaryImageData: function canvasPutBinaryImageData() { | ||||||
|  |       //
 | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|     // Marked content
 |     // Marked content
 | ||||||
| 
 | 
 | ||||||
|     markPoint: function canvasGraphicsMarkPoint(tag) { |     markPoint: function canvasGraphicsMarkPoint(tag) { | ||||||
| @ -864,6 +1144,41 @@ var CanvasGraphics = (function canvasGraphics() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return CanvasGraphics; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | if (!isWorker) { | ||||||
|  |   // Feature detection if the browser can use an Uint8Array directly as imgData.
 | ||||||
|  |   var canvas = document.createElement('canvas'); | ||||||
|  |   canvas.width = 1; | ||||||
|  |   canvas.height = 1; | ||||||
|  |   var ctx = canvas.getContext('2d'); | ||||||
|  | 
 | ||||||
|  |   try { | ||||||
|  |     ctx.putImageData({ | ||||||
|  |       width: 1, | ||||||
|  |       height: 1, | ||||||
|  |       data: new Uint8Array(4) | ||||||
|  |     }, 0, 0); | ||||||
|  | 
 | ||||||
|  |     CanvasGraphics.prototype.putBinaryImageData = | ||||||
|  |       function CanvasGraphicsPutBinaryImageDataNative(ctx, imgData) { | ||||||
|  |         ctx.putImageData(imgData, 0, 0); | ||||||
|  |       }; | ||||||
|  |   } catch (e) { | ||||||
|  |     CanvasGraphics.prototype.putBinaryImageData = | ||||||
|  |       function CanvasGraphicsPutBinaryImageDataShim(ctx, imgData, w, h) { | ||||||
|  |         var tmpImgData = ctx.getImageData(0, 0, w, h); | ||||||
|  | 
 | ||||||
|  |         // Copy over the imageData pixel by pixel.
 | ||||||
|  |         var tmpImgDataPixels = tmpImgData.data; | ||||||
|  |         var len = tmpImgDataPixels.length; | ||||||
|  | 
 | ||||||
|  |         while (len--) { | ||||||
|  |           tmpImgDataPixels[len] = imgData.data[len]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ctx.putImageData(tmpImgData, 0, 0); | ||||||
|  |       }; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | |||||||
| @ -3,13 +3,13 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var ColorSpace = (function colorSpaceColorSpace() { | var ColorSpace = (function ColorSpaceClosure() { | ||||||
|   // Constructor should define this.numComps, this.defaultColor, this.name
 |   // Constructor should define this.numComps, this.defaultColor, this.name
 | ||||||
|   function constructor() { |   function ColorSpace() { | ||||||
|     error('should not call ColorSpace constructor'); |     error('should not call ColorSpace constructor'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   ColorSpace.prototype = { | ||||||
|     // Input: array of size numComps representing color component values
 |     // Input: array of size numComps representing color component values
 | ||||||
|     // Output: array of rgb values, each value ranging from [0.1]
 |     // Output: array of rgb values, each value ranging from [0.1]
 | ||||||
|     getRgb: function colorSpaceGetRgb(color) { |     getRgb: function colorSpaceGetRgb(color) { | ||||||
| @ -22,15 +22,15 @@ var ColorSpace = (function colorSpaceColorSpace() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.parse = function colorSpaceParse(cs, xref, res) { |   ColorSpace.parse = function colorSpaceParse(cs, xref, res) { | ||||||
|     var IR = constructor.parseToIR(cs, xref, res); |     var IR = ColorSpace.parseToIR(cs, xref, res); | ||||||
|     if (IR instanceof AlternateCS) |     if (IR instanceof AlternateCS) | ||||||
|       return IR; |       return IR; | ||||||
| 
 | 
 | ||||||
|     return constructor.fromIR(IR); |     return ColorSpace.fromIR(IR); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.fromIR = function colorSpaceFromIR(IR) { |   ColorSpace.fromIR = function colorSpaceFromIR(IR) { | ||||||
|     var name = isArray(IR) ? IR[0] : IR; |     var name = isArray(IR) ? IR[0] : IR; | ||||||
| 
 | 
 | ||||||
|     switch (name) { |     switch (name) { | ||||||
| @ -63,7 +63,7 @@ var ColorSpace = (function colorSpaceColorSpace() { | |||||||
|     return null; |     return null; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.parseToIR = function colorSpaceParseToIR(cs, xref, res) { |   ColorSpace.parseToIR = function colorSpaceParseToIR(cs, xref, res) { | ||||||
|     if (isName(cs)) { |     if (isName(cs)) { | ||||||
|       var colorSpaces = xref.fetchIfRef(res.get('ColorSpace')); |       var colorSpaces = xref.fetchIfRef(res.get('ColorSpace')); | ||||||
|       if (isDict(colorSpaces)) { |       if (isDict(colorSpaces)) { | ||||||
| @ -154,8 +154,31 @@ var ColorSpace = (function colorSpaceColorSpace() { | |||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   }; |   }; | ||||||
|  |   /** | ||||||
|  |    * Checks if a decode map matches the default decode map for a color space. | ||||||
|  |    * This handles the general decode maps where there are two values per | ||||||
|  |    * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color. | ||||||
|  |    * This does not handle Lab, Indexed, or Pattern decode maps since they are | ||||||
|  |    * slightly different. | ||||||
|  |    * @param {Array} decode Decode map (usually from an image). | ||||||
|  |    * @param {Number} n Number of components the color space has. | ||||||
|  |    */ | ||||||
|  |   ColorSpace.isDefaultDecode = function colorSpaceIsDefaultDecode(decode, n) { | ||||||
|  |     if (!decode) | ||||||
|  |       return true; | ||||||
| 
 | 
 | ||||||
|   return constructor; |     if (n * 2 !== decode.length) { | ||||||
|  |       warning('The decode map is not the correct length'); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     for (var i = 0, ii = decode.length; i < ii; i += 2) { | ||||||
|  |       if (decode[i] != 0 || decode[i + 1] != 1) | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return ColorSpace; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -164,8 +187,8 @@ var ColorSpace = (function colorSpaceColorSpace() { | |||||||
|  * Both color spaces use a tinting function to convert colors to a base color |  * Both color spaces use a tinting function to convert colors to a base color | ||||||
|  * space. |  * space. | ||||||
|  */ |  */ | ||||||
| var AlternateCS = (function alternateCS() { | var AlternateCS = (function AlternateCSClosure() { | ||||||
|   function constructor(numComps, base, tintFn) { |   function AlternateCS(numComps, base, tintFn) { | ||||||
|     this.name = 'Alternate'; |     this.name = 'Alternate'; | ||||||
|     this.numComps = numComps; |     this.numComps = numComps; | ||||||
|     this.defaultColor = []; |     this.defaultColor = []; | ||||||
| @ -175,7 +198,7 @@ var AlternateCS = (function alternateCS() { | |||||||
|     this.tintFn = tintFn; |     this.tintFn = tintFn; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   AlternateCS.prototype = { | ||||||
|     getRgb: function altcs_getRgb(color) { |     getRgb: function altcs_getRgb(color) { | ||||||
|       var tinted = this.tintFn(color); |       var tinted = this.tintFn(color); | ||||||
|       return this.base.getRgb(tinted); |       return this.base.getRgb(tinted); | ||||||
| @ -200,24 +223,27 @@ var AlternateCS = (function alternateCS() { | |||||||
|           baseBuf[pos++] = 255 * tinted[j]; |           baseBuf[pos++] = 255 * tinted[j]; | ||||||
|       } |       } | ||||||
|       return base.getRgbBuffer(baseBuf, 8); |       return base.getRgbBuffer(baseBuf, 8); | ||||||
|  |     }, | ||||||
|  |     isDefaultDecode: function altcs_isDefaultDecode(decodeMap) { | ||||||
|  |       return ColorSpace.isDefaultDecode(decodeMap, this.numComps); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return AlternateCS; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var PatternCS = (function patternCS() { | var PatternCS = (function PatternCSClosure() { | ||||||
|   function constructor(baseCS) { |   function PatternCS(baseCS) { | ||||||
|     this.name = 'Pattern'; |     this.name = 'Pattern'; | ||||||
|     this.base = baseCS; |     this.base = baseCS; | ||||||
|   } |   } | ||||||
|   constructor.prototype = {}; |   PatternCS.prototype = {}; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return PatternCS; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var IndexedCS = (function indexedCS() { | var IndexedCS = (function IndexedCSClosure() { | ||||||
|   function constructor(base, highVal, lookup) { |   function IndexedCS(base, highVal, lookup) { | ||||||
|     this.name = 'Indexed'; |     this.name = 'Indexed'; | ||||||
|     this.numComps = 1; |     this.numComps = 1; | ||||||
|     this.defaultColor = [0]; |     this.defaultColor = [0]; | ||||||
| @ -240,7 +266,7 @@ var IndexedCS = (function indexedCS() { | |||||||
|     this.lookup = lookupArray; |     this.lookup = lookupArray; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   IndexedCS.prototype = { | ||||||
|     getRgb: function indexcs_getRgb(color) { |     getRgb: function indexcs_getRgb(color) { | ||||||
|       var numComps = this.base.numComps; |       var numComps = this.base.numComps; | ||||||
|       var start = color[0] * numComps; |       var start = color[0] * numComps; | ||||||
| @ -267,19 +293,23 @@ var IndexedCS = (function indexedCS() { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return base.getRgbBuffer(baseBuf, 8); |       return base.getRgbBuffer(baseBuf, 8); | ||||||
|  |     }, | ||||||
|  |     isDefaultDecode: function indexcs_isDefaultDecode(decodeMap) { | ||||||
|  |       // indexed color maps shouldn't be changed
 | ||||||
|  |       return true; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return IndexedCS; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var DeviceGrayCS = (function deviceGrayCS() { | var DeviceGrayCS = (function DeviceGrayCSClosure() { | ||||||
|   function constructor() { |   function DeviceGrayCS() { | ||||||
|     this.name = 'DeviceGray'; |     this.name = 'DeviceGray'; | ||||||
|     this.numComps = 1; |     this.numComps = 1; | ||||||
|     this.defaultColor = [0]; |     this.defaultColor = [0]; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   DeviceGrayCS.prototype = { | ||||||
|     getRgb: function graycs_getRgb(color) { |     getRgb: function graycs_getRgb(color) { | ||||||
|       var c = color[0]; |       var c = color[0]; | ||||||
|       return [c, c, c]; |       return [c, c, c]; | ||||||
| @ -295,18 +325,21 @@ var DeviceGrayCS = (function deviceGrayCS() { | |||||||
|         rgbBuf[j++] = c; |         rgbBuf[j++] = c; | ||||||
|       } |       } | ||||||
|       return rgbBuf; |       return rgbBuf; | ||||||
|  |     }, | ||||||
|  |     isDefaultDecode: function graycs_isDefaultDecode(decodeMap) { | ||||||
|  |       return ColorSpace.isDefaultDecode(decodeMap, this.numComps); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return DeviceGrayCS; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var DeviceRgbCS = (function deviceRgbCS() { | var DeviceRgbCS = (function DeviceRgbCSClosure() { | ||||||
|   function constructor() { |   function DeviceRgbCS() { | ||||||
|     this.name = 'DeviceRGB'; |     this.name = 'DeviceRGB'; | ||||||
|     this.numComps = 3; |     this.numComps = 3; | ||||||
|     this.defaultColor = [0, 0, 0]; |     this.defaultColor = [0, 0, 0]; | ||||||
|   } |   } | ||||||
|   constructor.prototype = { |   DeviceRgbCS.prototype = { | ||||||
|     getRgb: function rgbcs_getRgb(color) { |     getRgb: function rgbcs_getRgb(color) { | ||||||
|       return color; |       return color; | ||||||
|     }, |     }, | ||||||
| @ -319,18 +352,21 @@ var DeviceRgbCS = (function deviceRgbCS() { | |||||||
|       for (i = 0; i < length; ++i) |       for (i = 0; i < length; ++i) | ||||||
|         rgbBuf[i] = (scale * input[i]) | 0; |         rgbBuf[i] = (scale * input[i]) | 0; | ||||||
|       return rgbBuf; |       return rgbBuf; | ||||||
|  |     }, | ||||||
|  |     isDefaultDecode: function rgbcs_isDefaultDecode(decodeMap) { | ||||||
|  |       return ColorSpace.isDefaultDecode(decodeMap, this.numComps); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return DeviceRgbCS; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var DeviceCmykCS = (function deviceCmykCS() { | var DeviceCmykCS = (function DeviceCmykCSClosure() { | ||||||
|   function constructor() { |   function DeviceCmykCS() { | ||||||
|     this.name = 'DeviceCMYK'; |     this.name = 'DeviceCMYK'; | ||||||
|     this.numComps = 4; |     this.numComps = 4; | ||||||
|     this.defaultColor = [0, 0, 0, 1]; |     this.defaultColor = [0, 0, 0, 1]; | ||||||
|   } |   } | ||||||
|   constructor.prototype = { |   DeviceCmykCS.prototype = { | ||||||
|     getRgb: function cmykcs_getRgb(color) { |     getRgb: function cmykcs_getRgb(color) { | ||||||
|       var c = color[0], m = color[1], y = color[2], k = color[3]; |       var c = color[0], m = color[1], y = color[2], k = color[3]; | ||||||
|       var c1 = 1 - c, m1 = 1 - m, y1 = 1 - y, k1 = 1 - k; |       var c1 = 1 - c, m1 = 1 - m, y1 = 1 - y, k1 = 1 - k; | ||||||
| @ -403,9 +439,12 @@ var DeviceCmykCS = (function deviceCmykCS() { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return rgbBuf; |       return rgbBuf; | ||||||
|  |     }, | ||||||
|  |     isDefaultDecode: function cmykcs_isDefaultDecode(decodeMap) { | ||||||
|  |       return ColorSpace.isDefaultDecode(decodeMap, this.numComps); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return DeviceCmykCS; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										390
									
								
								src/core.js
									
									
									
									
									
								
							
							
						
						
									
										390
									
								
								src/core.js
									
									
									
									
									
								
							| @ -5,6 +5,8 @@ | |||||||
| 
 | 
 | ||||||
| var globalScope = (typeof window === 'undefined') ? this : window; | var globalScope = (typeof window === 'undefined') ? this : window; | ||||||
| 
 | 
 | ||||||
|  | var isWorker = (typeof window == 'undefined'); | ||||||
|  | 
 | ||||||
| var ERRORS = 0, WARNINGS = 1, TODOS = 5; | var ERRORS = 0, WARNINGS = 1, TODOS = 5; | ||||||
| var verbosity = WARNINGS; | var verbosity = WARNINGS; | ||||||
| 
 | 
 | ||||||
| @ -31,7 +33,7 @@ function getPdf(arg, callback) { | |||||||
|   var xhr = new XMLHttpRequest(); |   var xhr = new XMLHttpRequest(); | ||||||
|   xhr.open('GET', params.url); |   xhr.open('GET', params.url); | ||||||
|   xhr.mozResponseType = xhr.responseType = 'arraybuffer'; |   xhr.mozResponseType = xhr.responseType = 'arraybuffer'; | ||||||
|   xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200; |   xhr.expected = (params.url.indexOf('file:') === 0) ? 0 : 200; | ||||||
| 
 | 
 | ||||||
|   if ('progress' in params) |   if ('progress' in params) | ||||||
|     xhr.onprogress = params.progress || undefined; |     xhr.onprogress = params.progress || undefined; | ||||||
| @ -39,19 +41,23 @@ function getPdf(arg, callback) { | |||||||
|   if ('error' in params) |   if ('error' in params) | ||||||
|     xhr.onerror = params.error || undefined; |     xhr.onerror = params.error || undefined; | ||||||
| 
 | 
 | ||||||
|   xhr.onreadystatechange = function getPdfOnreadystatechange() { |   xhr.onreadystatechange = function getPdfOnreadystatechange(e) { | ||||||
|     if (xhr.readyState === 4 && xhr.status === xhr.expected) { |     if (xhr.readyState === 4) { | ||||||
|       var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || |       if (xhr.status === xhr.expected) { | ||||||
|                   xhr.responseArrayBuffer || xhr.response); |         var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || | ||||||
|       callback(data); |                     xhr.responseArrayBuffer || xhr.response); | ||||||
|  |         callback(data); | ||||||
|  |       } else if (params.error) { | ||||||
|  |         params.error(e); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   xhr.send(null); |   xhr.send(null); | ||||||
| } | } | ||||||
| globalScope.PDFJS.getPdf = getPdf; | globalScope.PDFJS.getPdf = getPdf; | ||||||
| 
 | 
 | ||||||
| var Page = (function pagePage() { | var Page = (function PageClosure() { | ||||||
|   function constructor(xref, pageNumber, pageDict, ref) { |   function Page(xref, pageNumber, pageDict, ref) { | ||||||
|     this.pageNumber = pageNumber; |     this.pageNumber = pageNumber; | ||||||
|     this.pageDict = pageDict; |     this.pageDict = pageDict; | ||||||
|     this.stats = { |     this.stats = { | ||||||
| @ -63,9 +69,11 @@ var Page = (function pagePage() { | |||||||
|     }; |     }; | ||||||
|     this.xref = xref; |     this.xref = xref; | ||||||
|     this.ref = ref; |     this.ref = ref; | ||||||
|  | 
 | ||||||
|  |     this.displayReadyPromise = null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Page.prototype = { | ||||||
|     getPageProp: function pageGetPageProp(key) { |     getPageProp: function pageGetPageProp(key) { | ||||||
|       return this.xref.fetchIfRef(this.pageDict.get(key)); |       return this.xref.fetchIfRef(this.pageDict.get(key)); | ||||||
|     }, |     }, | ||||||
| @ -101,9 +109,11 @@ var Page = (function pagePage() { | |||||||
|         width: this.width, |         width: this.width, | ||||||
|         height: this.height |         height: this.height | ||||||
|       }; |       }; | ||||||
|  |       var mediaBox = this.mediaBox; | ||||||
|  |       var offsetX = mediaBox[0], offsetY = mediaBox[1]; | ||||||
|       if (isArray(obj) && obj.length == 4) { |       if (isArray(obj) && obj.length == 4) { | ||||||
|         var tl = this.rotatePoint(obj[0], obj[1]); |         var tl = this.rotatePoint(obj[0] - offsetX, obj[1] - offsetY); | ||||||
|         var br = this.rotatePoint(obj[2], obj[3]); |         var br = this.rotatePoint(obj[2] - offsetX, obj[3] - offsetY); | ||||||
|         view.x = Math.min(tl.x, br.x); |         view.x = Math.min(tl.x, br.x); | ||||||
|         view.y = Math.min(tl.y, br.y); |         view.y = Math.min(tl.y, br.y); | ||||||
|         view.width = Math.abs(tl.x - br.x); |         view.width = Math.abs(tl.x - br.x); | ||||||
| @ -156,18 +166,12 @@ var Page = (function pagePage() { | |||||||
|                                                 IRQueue, fonts) { |                                                 IRQueue, fonts) { | ||||||
|       var self = this; |       var self = this; | ||||||
|       this.IRQueue = IRQueue; |       this.IRQueue = IRQueue; | ||||||
|       var gfx = new CanvasGraphics(this.ctx, this.objs); |  | ||||||
| 
 | 
 | ||||||
|       var displayContinuation = function pageDisplayContinuation() { |       var displayContinuation = function pageDisplayContinuation() { | ||||||
|         // Always defer call to display() to work around bug in
 |         // Always defer call to display() to work around bug in
 | ||||||
|         // Firefox error reporting from XHR callbacks.
 |         // Firefox error reporting from XHR callbacks.
 | ||||||
|         setTimeout(function pageSetTimeout() { |         setTimeout(function pageSetTimeout() { | ||||||
|           try { |           self.displayReadyPromise.resolve(); | ||||||
|             self.display(gfx, self.callback); |  | ||||||
|           } catch (e) { |  | ||||||
|             if (self.callback) self.callback(e.toString()); |  | ||||||
|             throw e; |  | ||||||
|           } |  | ||||||
|         }); |         }); | ||||||
|       }; |       }; | ||||||
| 
 | 
 | ||||||
| @ -241,6 +245,7 @@ var Page = (function pagePage() { | |||||||
|         startIdx = gfx.executeIRQueue(IRQueue, startIdx, next); |         startIdx = gfx.executeIRQueue(IRQueue, startIdx, next); | ||||||
|         if (startIdx == length) { |         if (startIdx == length) { | ||||||
|           self.stats.render = Date.now(); |           self.stats.render = Date.now(); | ||||||
|  |           gfx.endDrawing(); | ||||||
|           if (callback) callback(); |           if (callback) callback(); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @ -262,57 +267,160 @@ var Page = (function pagePage() { | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     getLinks: function pageGetLinks() { |     getLinks: function pageGetLinks() { | ||||||
|  |       var links = []; | ||||||
|  |       var annotations = pageGetAnnotations(); | ||||||
|  |       var i, n = annotations.length; | ||||||
|  |       for (i = 0; i < n; ++i) { | ||||||
|  |         if (annotations[i].type != 'Link') | ||||||
|  |           continue; | ||||||
|  |         links.push(annotations[i]); | ||||||
|  |       } | ||||||
|  |       return links; | ||||||
|  |     }, | ||||||
|  |     getAnnotations: function pageGetAnnotations() { | ||||||
|       var xref = this.xref; |       var xref = this.xref; | ||||||
|  |       function getInheritableProperty(annotation, name) { | ||||||
|  |         var item = annotation; | ||||||
|  |         while (item && !item.has(name)) { | ||||||
|  |           item = xref.fetchIfRef(item.get('Parent')); | ||||||
|  |         } | ||||||
|  |         if (!item) | ||||||
|  |           return null; | ||||||
|  |         return item.get(name); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       var annotations = xref.fetchIfRef(this.annotations) || []; |       var annotations = xref.fetchIfRef(this.annotations) || []; | ||||||
|       var i, n = annotations.length; |       var i, n = annotations.length; | ||||||
|       var links = []; |       var items = []; | ||||||
|       for (i = 0; i < n; ++i) { |       for (i = 0; i < n; ++i) { | ||||||
|         var annotation = xref.fetch(annotations[i]); |         var annotationRef = annotations[i]; | ||||||
|  |         var annotation = xref.fetch(annotationRef); | ||||||
|         if (!isDict(annotation)) |         if (!isDict(annotation)) | ||||||
|           continue; |           continue; | ||||||
|         var subtype = annotation.get('Subtype'); |         var subtype = annotation.get('Subtype'); | ||||||
|         if (!isName(subtype) || subtype.name != 'Link') |         if (!isName(subtype)) | ||||||
|           continue; |           continue; | ||||||
|         var rect = annotation.get('Rect'); |         var rect = annotation.get('Rect'); | ||||||
|         var topLeftCorner = this.rotatePoint(rect[0], rect[1]); |         var topLeftCorner = this.rotatePoint(rect[0], rect[1]); | ||||||
|         var bottomRightCorner = this.rotatePoint(rect[2], rect[3]); |         var bottomRightCorner = this.rotatePoint(rect[2], rect[3]); | ||||||
| 
 | 
 | ||||||
|         var link = {}; |         var item = {}; | ||||||
|         link.x = Math.min(topLeftCorner.x, bottomRightCorner.x); |         item.type = subtype.name; | ||||||
|         link.y = Math.min(topLeftCorner.y, bottomRightCorner.y); |         item.x = Math.min(topLeftCorner.x, bottomRightCorner.x); | ||||||
|         link.width = Math.abs(topLeftCorner.x - bottomRightCorner.x); |         item.y = Math.min(topLeftCorner.y, bottomRightCorner.y); | ||||||
|         link.height = Math.abs(topLeftCorner.y - bottomRightCorner.y); |         item.width = Math.abs(topLeftCorner.x - bottomRightCorner.x); | ||||||
|         var a = this.xref.fetchIfRef(annotation.get('A')); |         item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y); | ||||||
|         if (a) { |         switch (subtype.name) { | ||||||
|           switch (a.get('S').name) { |           case 'Link': | ||||||
|             case 'URI': |             var a = this.xref.fetchIfRef(annotation.get('A')); | ||||||
|               link.url = a.get('URI'); |             if (a) { | ||||||
|  |               switch (a.get('S').name) { | ||||||
|  |                 case 'URI': | ||||||
|  |                   item.url = a.get('URI'); | ||||||
|  |                   break; | ||||||
|  |                 case 'GoTo': | ||||||
|  |                   item.dest = a.get('D'); | ||||||
|  |                   break; | ||||||
|  |                 default: | ||||||
|  |                   TODO('other link types'); | ||||||
|  |               } | ||||||
|  |             } else if (annotation.has('Dest')) { | ||||||
|  |               // simple destination link
 | ||||||
|  |               var dest = annotation.get('Dest'); | ||||||
|  |               item.dest = isName(dest) ? dest.name : dest; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |           case 'Widget': | ||||||
|  |             var fieldType = getInheritableProperty(annotation, 'FT'); | ||||||
|  |             if (!isName(fieldType)) | ||||||
|               break; |               break; | ||||||
|             case 'GoTo': |             item.fieldType = fieldType.name; | ||||||
|               link.dest = a.get('D'); |             // Building the full field name by collecting the field and
 | ||||||
|               break; |             // its ancestors 'T' properties and joining them using '.'.
 | ||||||
|             default: |             var fieldName = []; | ||||||
|               TODO('other link types'); |             var namedItem = annotation, ref = annotationRef; | ||||||
|           } |             while (namedItem) { | ||||||
|         } else if (annotation.has('Dest')) { |               var parentRef = namedItem.get('Parent'); | ||||||
|           // simple destination link
 |               var parent = xref.fetchIfRef(parentRef); | ||||||
|           var dest = annotation.get('Dest'); |               var name = namedItem.get('T'); | ||||||
|           link.dest = isName(dest) ? dest.name : dest; |               if (name) | ||||||
|  |                 fieldName.unshift(stringToPDFString(name)); | ||||||
|  |               else { | ||||||
|  |                 // The field name is absent, that means more than one field
 | ||||||
|  |                 // with the same name may exist. Replacing the empty name
 | ||||||
|  |                 // with the '`' plus index in the parent's 'Kids' array.
 | ||||||
|  |                 // This is not in the PDF spec but necessary to id the
 | ||||||
|  |                 // the input controls.
 | ||||||
|  |                 var kids = xref.fetchIfRef(parent.get('Kids')); | ||||||
|  |                 var j, jj; | ||||||
|  |                 for (j = 0, jj = kids.length; j < jj; j++) { | ||||||
|  |                   if (kids[j].num == ref.num && kids[j].gen == ref.gen) | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 fieldName.unshift('`' + j); | ||||||
|  |               } | ||||||
|  |               namedItem = parent; | ||||||
|  |               ref = parentRef; | ||||||
|  |             } | ||||||
|  |             item.fullName = fieldName.join('.'); | ||||||
|  |             var alternativeText = stringToPDFString(annotation.get('TU') || ''); | ||||||
|  |             item.alternativeText = alternativeText; | ||||||
|  |             var da = getInheritableProperty(annotation, 'DA') || ''; | ||||||
|  |             var m = /([\d\.]+)\sTf/.exec(da); | ||||||
|  |             if (m) | ||||||
|  |               item.fontSize = parseFloat(m[1]); | ||||||
|  |             item.textAlignment = getInheritableProperty(annotation, 'Q'); | ||||||
|  |             item.flags = getInheritableProperty(annotation, 'Ff') || 0; | ||||||
|  |             break; | ||||||
|  |           case 'Text': | ||||||
|  |             var content = annotation.get('Contents'); | ||||||
|  |             var title = annotation.get('T'); | ||||||
|  |             item.content = stringToPDFString(content || ''); | ||||||
|  |             item.title = stringToPDFString(title || ''); | ||||||
|  |             item.name = annotation.get('Name').name; | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             TODO('unimplemented annotation type: ' + subtype.name); | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
|         links.push(link); |         items.push(item); | ||||||
|       } |       } | ||||||
|       return links; |       return items; | ||||||
|     }, |     }, | ||||||
|     startRendering: function pageStartRendering(ctx, callback)  { |     startRendering: function pageStartRendering(ctx, callback, textLayer)  { | ||||||
|       this.ctx = ctx; |  | ||||||
|       this.callback = callback; |  | ||||||
| 
 |  | ||||||
|       this.startRenderingTime = Date.now(); |       this.startRenderingTime = Date.now(); | ||||||
|       this.pdf.startRendering(this); | 
 | ||||||
|  |       // If there is no displayReadyPromise yet, then the IRQueue was never
 | ||||||
|  |       // requested before. Make the request and create the promise.
 | ||||||
|  |       if (!this.displayReadyPromise) { | ||||||
|  |         this.pdf.startRendering(this); | ||||||
|  |         this.displayReadyPromise = new Promise(); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Once the IRQueue and fonts are loaded, perform the actual rendering.
 | ||||||
|  |       this.displayReadyPromise.then( | ||||||
|  |         function pageDisplayReadyPromise() { | ||||||
|  |           var gfx = new CanvasGraphics(ctx, this.objs, textLayer); | ||||||
|  |           try { | ||||||
|  |             this.display(gfx, callback); | ||||||
|  |           } catch (e) { | ||||||
|  |             if (callback) | ||||||
|  |               callback(e); | ||||||
|  |             else | ||||||
|  |               throw e; | ||||||
|  |           } | ||||||
|  |         }.bind(this), | ||||||
|  |         function pageDisplayReadPromiseError(reason) { | ||||||
|  |           if (callback) | ||||||
|  |             callback(reason); | ||||||
|  |           else | ||||||
|  |             throw reason; | ||||||
|  |         } | ||||||
|  |       ); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Page; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -325,8 +433,8 @@ var Page = (function pagePage() { | |||||||
|  * need for the `PDFDocModel` anymore and there is only one object on the |  * need for the `PDFDocModel` anymore and there is only one object on the | ||||||
|  * main thread and not one entire copy on each worker instance. |  * main thread and not one entire copy on each worker instance. | ||||||
|  */ |  */ | ||||||
| var PDFDocModel = (function pdfDoc() { | var PDFDocModel = (function PDFDocModelClosure() { | ||||||
|   function constructor(arg, callback) { |   function PDFDocModel(arg, callback) { | ||||||
|     if (isStream(arg)) |     if (isStream(arg)) | ||||||
|       init.call(this, arg); |       init.call(this, arg); | ||||||
|     else if (isArrayBuffer(arg)) |     else if (isArrayBuffer(arg)) | ||||||
| @ -339,6 +447,7 @@ var PDFDocModel = (function pdfDoc() { | |||||||
|     assertWellFormed(stream.length > 0, 'stream must have data'); |     assertWellFormed(stream.length > 0, 'stream must have data'); | ||||||
|     this.stream = stream; |     this.stream = stream; | ||||||
|     this.setup(); |     this.setup(); | ||||||
|  |     this.acroForm = this.xref.fetchIfRef(this.catalog.catDict.get('AcroForm')); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function find(stream, needle, limit, backwards) { |   function find(stream, needle, limit, backwards) { | ||||||
| @ -357,7 +466,7 @@ var PDFDocModel = (function pdfDoc() { | |||||||
|     return true; /* found */ |     return true; /* found */ | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   PDFDocModel.prototype = { | ||||||
|     get linearization() { |     get linearization() { | ||||||
|       var length = this.stream.length; |       var length = this.stream.length; | ||||||
|       var linearization = false; |       var linearization = false; | ||||||
| @ -379,12 +488,17 @@ var PDFDocModel = (function pdfDoc() { | |||||||
|         if (find(stream, 'endobj', 1024)) |         if (find(stream, 'endobj', 1024)) | ||||||
|           startXRef = stream.pos + 6; |           startXRef = stream.pos + 6; | ||||||
|       } else { |       } else { | ||||||
|         // Find startxref at the end of the file.
 |         // Find startxref by jumping backward from the end of the file.
 | ||||||
|         var start = stream.end - 1024; |         var step = 1024; | ||||||
|         if (start < 0) |         var found = false, pos = stream.end; | ||||||
|           start = 0; |         while (!found && pos > 0) { | ||||||
|         stream.pos = start; |           pos -= step - 'startxref'.length; | ||||||
|         if (find(stream, 'startxref', 1024, true)) { |           if (pos < 0) | ||||||
|  |             pos = 0; | ||||||
|  |           stream.pos = pos; | ||||||
|  |           found = find(stream, 'startxref', step, true); | ||||||
|  |         } | ||||||
|  |         if (found) { | ||||||
|           stream.skip(9); |           stream.skip(9); | ||||||
|           var ch; |           var ch; | ||||||
|           do { |           do { | ||||||
| @ -425,10 +539,19 @@ var PDFDocModel = (function pdfDoc() { | |||||||
|     }, |     }, | ||||||
|     setup: function pdfDocSetup(ownerPassword, userPassword) { |     setup: function pdfDocSetup(ownerPassword, userPassword) { | ||||||
|       this.checkHeader(); |       this.checkHeader(); | ||||||
|       this.xref = new XRef(this.stream, |       var xref = new XRef(this.stream, | ||||||
|                            this.startXRef, |                           this.startXRef, | ||||||
|                            this.mainXRefEntriesOffset); |                           this.mainXRefEntriesOffset); | ||||||
|       this.catalog = new Catalog(this.xref); |       this.xref = xref; | ||||||
|  |       this.catalog = new Catalog(xref); | ||||||
|  |       if (xref.trailer && xref.trailer.has('ID')) { | ||||||
|  |         var fileID = ''; | ||||||
|  |         var id = xref.fetchIfRef(xref.trailer.get('ID'))[0]; | ||||||
|  |         id.split('').forEach(function(el) { | ||||||
|  |           fileID += Number(el.charCodeAt(0)).toString(16); | ||||||
|  |         }); | ||||||
|  |         this.fileID = fileID; | ||||||
|  |       } | ||||||
|     }, |     }, | ||||||
|     get numPages() { |     get numPages() { | ||||||
|       var linearization = this.linearization; |       var linearization = this.linearization; | ||||||
| @ -436,16 +559,32 @@ var PDFDocModel = (function pdfDoc() { | |||||||
|       // shadow the prototype getter
 |       // shadow the prototype getter
 | ||||||
|       return shadow(this, 'numPages', num); |       return shadow(this, 'numPages', num); | ||||||
|     }, |     }, | ||||||
|  |     getFingerprint: function pdfDocGetFingerprint() { | ||||||
|  |       if (this.fileID) { | ||||||
|  |         return this.fileID; | ||||||
|  |       } else { | ||||||
|  |         // If we got no fileID, then we generate one,
 | ||||||
|  |         // from the first 100 bytes of PDF
 | ||||||
|  |         var data = this.stream.bytes.subarray(0, 100); | ||||||
|  |         var hash = calculateMD5(data, 0, data.length); | ||||||
|  |         var strHash = ''; | ||||||
|  |         for (var i = 0, length = hash.length; i < length; i++) { | ||||||
|  |           strHash += Number(hash[i]).toString(16); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return strHash; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     getPage: function pdfDocGetPage(n) { |     getPage: function pdfDocGetPage(n) { | ||||||
|       return this.catalog.getPage(n); |       return this.catalog.getPage(n); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return PDFDocModel; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var PDFDoc = (function pdfDoc() { | var PDFDoc = (function PDFDocClosure() { | ||||||
|   function constructor(arg, callback) { |   function PDFDoc(arg, callback) { | ||||||
|     var stream = null; |     var stream = null; | ||||||
|     var data = null; |     var data = null; | ||||||
| 
 | 
 | ||||||
| @ -462,7 +601,7 @@ var PDFDoc = (function pdfDoc() { | |||||||
|     this.data = data; |     this.data = data; | ||||||
|     this.stream = stream; |     this.stream = stream; | ||||||
|     this.pdf = new PDFDocModel(stream); |     this.pdf = new PDFDocModel(stream); | ||||||
| 
 |     this.fingerprint = this.pdf.getFingerprint(); | ||||||
|     this.catalog = this.pdf.catalog; |     this.catalog = this.pdf.catalog; | ||||||
|     this.objs = new PDFObjects(); |     this.objs = new PDFObjects(); | ||||||
| 
 | 
 | ||||||
| @ -481,39 +620,38 @@ var PDFDoc = (function pdfDoc() { | |||||||
|         throw 'No PDFJS.workerSrc specified'; |         throw 'No PDFJS.workerSrc specified'; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var worker; |  | ||||||
|       try { |       try { | ||||||
|         worker = new Worker(workerSrc); |  | ||||||
|       } catch (e) { |  | ||||||
|         // Some versions of FF can't create a worker on localhost, see:
 |         // Some versions of FF can't create a worker on localhost, see:
 | ||||||
|         // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
 |         // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
 | ||||||
|         globalScope.PDFJS.disableWorker = true; |         var worker = new Worker(workerSrc); | ||||||
|         this.setupFakeWorker(); | 
 | ||||||
|  |         var messageHandler = new MessageHandler('main', worker); | ||||||
|  |         // Tell the worker the file it was created from.
 | ||||||
|  |         messageHandler.send('workerSrc', workerSrc); | ||||||
|  |         messageHandler.on('test', function pdfDocTest(supportTypedArray) { | ||||||
|  |           if (supportTypedArray) { | ||||||
|  |             this.worker = worker; | ||||||
|  |             this.setupMessageHandler(messageHandler); | ||||||
|  |           } else { | ||||||
|  |             globalScope.PDFJS.disableWorker = true; | ||||||
|  |             this.setupFakeWorker(); | ||||||
|  |           } | ||||||
|  |         }.bind(this)); | ||||||
|  | 
 | ||||||
|  |         var testObj = new Uint8Array(1); | ||||||
|  |         // Some versions of Opera throw a DATA_CLONE_ERR on
 | ||||||
|  |         // serializing the typed array.
 | ||||||
|  |         messageHandler.send('test', testObj); | ||||||
|         return; |         return; | ||||||
|       } |       } catch (e) {} | ||||||
| 
 |  | ||||||
|       var messageHandler = new MessageHandler('main', worker); |  | ||||||
| 
 |  | ||||||
|       // Tell the worker the file it was created from.
 |  | ||||||
|       messageHandler.send('workerSrc', workerSrc); |  | ||||||
| 
 |  | ||||||
|       messageHandler.on('test', function pdfDocTest(supportTypedArray) { |  | ||||||
|         if (supportTypedArray) { |  | ||||||
|           this.worker = worker; |  | ||||||
|           this.setupMessageHandler(messageHandler); |  | ||||||
|         } else { |  | ||||||
|           this.setupFakeWorker(); |  | ||||||
|         } |  | ||||||
|       }.bind(this)); |  | ||||||
| 
 |  | ||||||
|       var testObj = new Uint8Array(1); |  | ||||||
|       messageHandler.send('test', testObj); |  | ||||||
|     } else { |  | ||||||
|       this.setupFakeWorker(); |  | ||||||
|     } |     } | ||||||
|  |     // Either workers are disabled, not supported or have thrown an exception.
 | ||||||
|  |     // Thus, we fallback to a faked worker.
 | ||||||
|  |     globalScope.PDFJS.disableWorker = true; | ||||||
|  |     this.setupFakeWorker(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   PDFDoc.prototype = { | ||||||
|     setupFakeWorker: function() { |     setupFakeWorker: function() { | ||||||
|       // If we don't use a worker, just post/sendMessage to the main thread.
 |       // If we don't use a worker, just post/sendMessage to the main thread.
 | ||||||
|       var fakeWorker = { |       var fakeWorker = { | ||||||
| @ -549,8 +687,12 @@ var PDFDoc = (function pdfDoc() { | |||||||
| 
 | 
 | ||||||
|         switch (type) { |         switch (type) { | ||||||
|           case 'JpegStream': |           case 'JpegStream': | ||||||
|             var IR = data[2]; |             var imageData = data[2]; | ||||||
|             new JpegImageLoader(id, IR, this.objs); |             loadJpegStream(id, imageData, this.objs); | ||||||
|  |             break; | ||||||
|  |           case 'Image': | ||||||
|  |             var imageData = data[2]; | ||||||
|  |             this.objs.resolve(id, imageData); | ||||||
|             break; |             break; | ||||||
|           case 'Font': |           case 'Font': | ||||||
|             var name = data[2]; |             var name = data[2]; | ||||||
| @ -558,20 +700,9 @@ var PDFDoc = (function pdfDoc() { | |||||||
|             var properties = data[4]; |             var properties = data[4]; | ||||||
| 
 | 
 | ||||||
|             if (file) { |             if (file) { | ||||||
|  |               // Rewrap the ArrayBuffer in a stream.
 | ||||||
|               var fontFileDict = new Dict(); |               var fontFileDict = new Dict(); | ||||||
|               fontFileDict.map = file.dict.map; |               file = new Stream(file, 0, file.length, fontFileDict); | ||||||
| 
 |  | ||||||
|               var fontFile = new Stream(file.bytes, file.start, |  | ||||||
|                                         file.end - file.start, fontFileDict); |  | ||||||
| 
 |  | ||||||
|               // Check if this is a FlateStream. Otherwise just use the created
 |  | ||||||
|               // Stream one. This makes complex_ttf_font.pdf work.
 |  | ||||||
|               var cmf = file.bytes[0]; |  | ||||||
|               if ((cmf & 0x0f) == 0x08) { |  | ||||||
|                 file = new FlateStream(fontFile); |  | ||||||
|               } else { |  | ||||||
|                 file = fontFile; |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // For now, resolve the font object here direclty. The real font
 |             // For now, resolve the font object here direclty. The real font
 | ||||||
| @ -599,6 +730,49 @@ var PDFDoc = (function pdfDoc() { | |||||||
|         } |         } | ||||||
|       }.bind(this)); |       }.bind(this)); | ||||||
| 
 | 
 | ||||||
|  |       messageHandler.on('page_error', function pdfDocError(data) { | ||||||
|  |         var page = this.pageCache[data.pageNum]; | ||||||
|  |         if (page.displayReadyPromise) | ||||||
|  |           page.displayReadyPromise.reject(data.error); | ||||||
|  |         else | ||||||
|  |           throw data.error; | ||||||
|  |       }, this); | ||||||
|  | 
 | ||||||
|  |       messageHandler.on('jpeg_decode', function(data, promise) { | ||||||
|  |         var imageData = data[0]; | ||||||
|  |         var components = data[1]; | ||||||
|  |         if (components != 3 && components != 1) | ||||||
|  |           error('Only 3 component or 1 component can be returned'); | ||||||
|  | 
 | ||||||
|  |         var img = new Image(); | ||||||
|  |         img.onload = (function jpegImageLoaderOnload() { | ||||||
|  |           var width = img.width; | ||||||
|  |           var height = img.height; | ||||||
|  |           var size = width * height; | ||||||
|  |           var rgbaLength = size * 4; | ||||||
|  |           var buf = new Uint8Array(size * components); | ||||||
|  |           var tmpCanvas = new ScratchCanvas(width, height); | ||||||
|  |           var tmpCtx = tmpCanvas.getContext('2d'); | ||||||
|  |           tmpCtx.drawImage(img, 0, 0); | ||||||
|  |           var data = tmpCtx.getImageData(0, 0, width, height).data; | ||||||
|  | 
 | ||||||
|  |           if (components == 3) { | ||||||
|  |             for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { | ||||||
|  |               buf[j] = data[i]; | ||||||
|  |               buf[j + 1] = data[i + 1]; | ||||||
|  |               buf[j + 2] = data[i + 2]; | ||||||
|  |             } | ||||||
|  |           } else if (components == 1) { | ||||||
|  |             for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) { | ||||||
|  |               buf[j] = data[i]; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           promise.resolve({ data: buf, width: width, height: height}); | ||||||
|  |         }).bind(this); | ||||||
|  |         var src = 'data:image/jpeg;base64,' + window.btoa(imageData); | ||||||
|  |         img.src = src; | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|       setTimeout(function pdfDocFontReadySetTimeout() { |       setTimeout(function pdfDocFontReadySetTimeout() { | ||||||
|         messageHandler.send('doc', this.data); |         messageHandler.send('doc', this.data); | ||||||
|         this.workerReadyPromise.resolve(true); |         this.workerReadyPromise.resolve(true); | ||||||
| @ -645,7 +819,7 @@ var PDFDoc = (function pdfDoc() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return PDFDoc; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| globalScope.PDFJS.PDFDoc = PDFDoc; | globalScope.PDFJS.PDFDoc = PDFDoc; | ||||||
|  | |||||||
| @ -3,8 +3,8 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var ARCFourCipher = (function arcFourCipher() { | var ARCFourCipher = (function ARCFourCipherClosure() { | ||||||
|   function constructor(key) { |   function ARCFourCipher(key) { | ||||||
|     this.a = 0; |     this.a = 0; | ||||||
|     this.b = 0; |     this.b = 0; | ||||||
|     var s = new Uint8Array(256); |     var s = new Uint8Array(256); | ||||||
| @ -20,7 +20,7 @@ var ARCFourCipher = (function arcFourCipher() { | |||||||
|     this.s = s; |     this.s = s; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   ARCFourCipher.prototype = { | ||||||
|     encryptBlock: function arcFourCipherEncryptBlock(data) { |     encryptBlock: function arcFourCipherEncryptBlock(data) { | ||||||
|       var i, n = data.length, tmp, tmp2; |       var i, n = data.length, tmp, tmp2; | ||||||
|       var a = this.a, b = this.b, s = this.s; |       var a = this.a, b = this.b, s = this.s; | ||||||
| @ -39,12 +39,12 @@ var ARCFourCipher = (function arcFourCipher() { | |||||||
|       return output; |       return output; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   constructor.prototype.decryptBlock = constructor.prototype.encryptBlock; |   ARCFourCipher.prototype.decryptBlock = ARCFourCipher.prototype.encryptBlock; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return ARCFourCipher; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var calculateMD5 = (function calculateMD5() { | var calculateMD5 = (function calculateMD5Closure() { | ||||||
|   var r = new Uint8Array([ |   var r = new Uint8Array([ | ||||||
|     7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, |     7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, | ||||||
|     5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, |     5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, | ||||||
| @ -128,20 +128,20 @@ var calculateMD5 = (function calculateMD5() { | |||||||
|   return hash; |   return hash; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var NullCipher = (function nullCipher() { | var NullCipher = (function NullCipherClosure() { | ||||||
|   function constructor() { |   function NullCipher() { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   NullCipher.prototype = { | ||||||
|     decryptBlock: function nullCipherDecryptBlock(data) { |     decryptBlock: function nullCipherDecryptBlock(data) { | ||||||
|       return data; |       return data; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return NullCipher; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var AES128Cipher = (function aes128Cipher() { | var AES128Cipher = (function AES128CipherClosure() { | ||||||
|   var rcon = new Uint8Array([ |   var rcon = new Uint8Array([ | ||||||
|     0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, |     0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, | ||||||
|     0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, |     0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, | ||||||
| @ -330,7 +330,7 @@ var AES128Cipher = (function aes128Cipher() { | |||||||
|     return state; |     return state; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function constructor(key) { |   function AES128Cipher(key) { | ||||||
|     this.key = expandKey128(key); |     this.key = expandKey128(key); | ||||||
|     this.buffer = new Uint8Array(16); |     this.buffer = new Uint8Array(16); | ||||||
|     this.bufferPosition = 0; |     this.bufferPosition = 0; | ||||||
| @ -370,7 +370,7 @@ var AES128Cipher = (function aes128Cipher() { | |||||||
|     return output; |     return output; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   AES128Cipher.prototype = { | ||||||
|     decryptBlock: function aes128CipherDecryptBlock(data) { |     decryptBlock: function aes128CipherDecryptBlock(data) { | ||||||
|       var i, sourceLength = data.length; |       var i, sourceLength = data.length; | ||||||
|       var buffer = this.buffer, bufferLength = this.bufferPosition; |       var buffer = this.buffer, bufferLength = this.bufferPosition; | ||||||
| @ -391,15 +391,15 @@ var AES128Cipher = (function aes128Cipher() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return AES128Cipher; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var CipherTransform = (function cipherTransform() { | var CipherTransform = (function CipherTransformClosure() { | ||||||
|   function constructor(stringCipherConstructor, streamCipherConstructor) { |   function CipherTransform(stringCipherConstructor, streamCipherConstructor) { | ||||||
|     this.stringCipherConstructor = stringCipherConstructor; |     this.stringCipherConstructor = stringCipherConstructor; | ||||||
|     this.streamCipherConstructor = streamCipherConstructor; |     this.streamCipherConstructor = streamCipherConstructor; | ||||||
|   } |   } | ||||||
|   constructor.prototype = { |   CipherTransform.prototype = { | ||||||
|     createStream: function cipherTransformCreateStream(stream) { |     createStream: function cipherTransformCreateStream(stream) { | ||||||
|       var cipher = new this.streamCipherConstructor(); |       var cipher = new this.streamCipherConstructor(); | ||||||
|       return new DecryptStream(stream, |       return new DecryptStream(stream, | ||||||
| @ -415,10 +415,10 @@ var CipherTransform = (function cipherTransform() { | |||||||
|       return bytesToString(data); |       return bytesToString(data); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return CipherTransform; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var CipherTransformFactory = (function cipherTransformFactory() { | var CipherTransformFactory = (function CipherTransformFactoryClosure() { | ||||||
|   function prepareKeyData(fileId, password, ownerPassword, userPassword, |   function prepareKeyData(fileId, password, ownerPassword, userPassword, | ||||||
|                           flags, revision, keyLength, encryptMetadata) { |                           flags, revision, keyLength, encryptMetadata) { | ||||||
|     var defaultPasswordBytes = new Uint8Array([ |     var defaultPasswordBytes = new Uint8Array([ | ||||||
| @ -490,7 +490,7 @@ var CipherTransformFactory = (function cipherTransformFactory() { | |||||||
| 
 | 
 | ||||||
|   var identityName = new Name('Identity'); |   var identityName = new Name('Identity'); | ||||||
| 
 | 
 | ||||||
|   function constructor(dict, fileId, password) { |   function CipherTransformFactory(dict, fileId, password) { | ||||||
|     var filter = dict.get('Filter'); |     var filter = dict.get('Filter'); | ||||||
|     if (!isName(filter) || filter.name != 'Standard') |     if (!isName(filter) || filter.name != 'Standard') | ||||||
|       error('unknown encryption method'); |       error('unknown encryption method'); | ||||||
| @ -573,7 +573,7 @@ var CipherTransformFactory = (function cipherTransformFactory() { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   CipherTransformFactory.prototype = { | ||||||
|     createCipherTransform: function buildCipherCreateCipherTransform(num, |     createCipherTransform: function buildCipherCreateCipherTransform(num, | ||||||
|                                                                      gen) { |                                                                      gen) { | ||||||
|       if (this.algorithm == 4) { |       if (this.algorithm == 4) { | ||||||
| @ -592,6 +592,6 @@ var CipherTransformFactory = (function cipherTransformFactory() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return CipherTransformFactory; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										137
									
								
								src/evaluator.js
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								src/evaluator.js
									
									
									
									
									
								
							| @ -3,8 +3,8 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var PartialEvaluator = (function partialEvaluator() { | var PartialEvaluator = (function PartialEvaluatorClosure() { | ||||||
|   function constructor(xref, handler, uniquePrefix) { |   function PartialEvaluator(xref, handler, uniquePrefix) { | ||||||
|     this.state = new EvalState(); |     this.state = new EvalState(); | ||||||
|     this.stateStack = []; |     this.stateStack = []; | ||||||
| 
 | 
 | ||||||
| @ -111,7 +111,7 @@ var PartialEvaluator = (function partialEvaluator() { | |||||||
|     EX: 'endCompat' |     EX: 'endCompat' | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   PartialEvaluator.prototype = { | ||||||
|     getIRQueue: function partialEvaluatorGetIRQueue(stream, resources, |     getIRQueue: function partialEvaluatorGetIRQueue(stream, resources, | ||||||
|                                     queue, dependency) { |                                     queue, dependency) { | ||||||
| 
 | 
 | ||||||
| @ -155,6 +155,11 @@ var PartialEvaluator = (function partialEvaluator() { | |||||||
|             font.loadedName = loadedName; |             font.loadedName = loadedName; | ||||||
| 
 | 
 | ||||||
|             var translated = font.translated; |             var translated = font.translated; | ||||||
|  |             // Convert the file to an ArrayBuffer which will be turned back into
 | ||||||
|  |             // a Stream in the main thread.
 | ||||||
|  |             if (translated.file) | ||||||
|  |               translated.file = translated.file.getBytes(); | ||||||
|  | 
 | ||||||
|             handler.send('obj', [ |             handler.send('obj', [ | ||||||
|                 loadedName, |                 loadedName, | ||||||
|                 'Font', |                 'Font', | ||||||
| @ -179,62 +184,54 @@ var PartialEvaluator = (function partialEvaluator() { | |||||||
|         var w = dict.get('Width', 'W'); |         var w = dict.get('Width', 'W'); | ||||||
|         var h = dict.get('Height', 'H'); |         var h = dict.get('Height', 'H'); | ||||||
| 
 | 
 | ||||||
|         if (image instanceof JpegStream && image.isNative) { |         var imageMask = dict.get('ImageMask', 'IM') || false; | ||||||
|           var objId = 'img_' + uniquePrefix + (++self.objIdCounter); |         if (imageMask) { | ||||||
|           handler.send('obj', [objId, 'JpegStream', image.getIR()]); |           // This depends on a tmpCanvas beeing filled with the
 | ||||||
|  |           // current fillStyle, such that processing the pixel
 | ||||||
|  |           // data can't be done here. Instead of creating a
 | ||||||
|  |           // complete PDFImage, only read the information needed
 | ||||||
|  |           // for later.
 | ||||||
| 
 | 
 | ||||||
|           // Add the dependency on the image object.
 |           var width = dict.get('Width', 'W'); | ||||||
|           insertDependency([objId]); |           var height = dict.get('Height', 'H'); | ||||||
| 
 |           var bitStrideLength = (width + 7) >> 3; | ||||||
|           // The normal fn.
 |           var imgArray = image.getBytes(bitStrideLength * height); | ||||||
|           fn = 'paintJpegXObject'; |           var decode = dict.get('Decode', 'D'); | ||||||
|           args = [objId, w, h]; |           var inverseDecode = !!decode && decode[0] > 0; | ||||||
| 
 | 
 | ||||||
|  |           fn = 'paintImageMaskXObject'; | ||||||
|  |           args = [imgArray, inverseDecode, width, height]; | ||||||
|           return; |           return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Needs to be rendered ourself.
 |  | ||||||
| 
 |  | ||||||
|         // Figure out if the image has an imageMask.
 |  | ||||||
|         var imageMask = dict.get('ImageMask', 'IM') || false; |  | ||||||
| 
 |  | ||||||
|         // If there is no imageMask, create the PDFImage and a lot
 |         // If there is no imageMask, create the PDFImage and a lot
 | ||||||
|         // of image processing can be done here.
 |         // of image processing can be done here.
 | ||||||
|         if (!imageMask) { |         var objId = 'img_' + uniquePrefix + (++self.objIdCounter); | ||||||
|           var imageObj = new PDFImage(xref, resources, image, inline); |         insertDependency([objId]); | ||||||
|  |         args = [objId, w, h]; | ||||||
| 
 | 
 | ||||||
|           if (imageObj.imageMask) { |         var softMask = dict.get('SMask', 'IM') || false; | ||||||
|             throw 'Can\'t handle this in the web worker :/'; |         if (!softMask && image instanceof JpegStream && image.isNative) { | ||||||
|           } |           // These JPEGs don't need any more processing so we can just send it.
 | ||||||
| 
 |           fn = 'paintJpegXObject'; | ||||||
|           var imgData = { |           handler.send('obj', [objId, 'JpegStream', image.getIR()]); | ||||||
|             width: w, |  | ||||||
|             height: h, |  | ||||||
|             data: new Uint8Array(w * h * 4) |  | ||||||
|           }; |  | ||||||
|           var pixels = imgData.data; |  | ||||||
|           imageObj.fillRgbaBuffer(pixels, imageObj.decode); |  | ||||||
| 
 |  | ||||||
|           fn = 'paintImageXObject'; |  | ||||||
|           args = [imgData]; |  | ||||||
|           return; |           return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // This depends on a tmpCanvas beeing filled with the
 |         fn = 'paintImageXObject'; | ||||||
|         // current fillStyle, such that processing the pixel
 |  | ||||||
|         // data can't be done here. Instead of creating a
 |  | ||||||
|         // complete PDFImage, only read the information needed
 |  | ||||||
|         // for later.
 |  | ||||||
|         fn = 'paintImageMaskXObject'; |  | ||||||
| 
 | 
 | ||||||
|         var width = dict.get('Width', 'W'); |         PDFImage.buildImage(function(imageObj) { | ||||||
|         var height = dict.get('Height', 'H'); |             var drawWidth = imageObj.drawWidth; | ||||||
|         var bitStrideLength = (width + 7) >> 3; |             var drawHeight = imageObj.drawHeight; | ||||||
|         var imgArray = image.getBytes(bitStrideLength * height); |             var imgData = { | ||||||
|         var decode = dict.get('Decode', 'D'); |               width: drawWidth, | ||||||
|         var inverseDecode = !!decode && decode[0] > 0; |               height: drawHeight, | ||||||
| 
 |               data: new Uint8Array(drawWidth * drawHeight * 4) | ||||||
|         args = [imgArray, inverseDecode, width, height]; |             }; | ||||||
|  |             var pixels = imgData.data; | ||||||
|  |             imageObj.fillRgbaBuffer(pixels, drawWidth, drawHeight); | ||||||
|  |             handler.send('obj', [objId, 'Image', imgData]); | ||||||
|  |           }, handler, xref, resources, image, inline); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       uniquePrefix = uniquePrefix || ''; |       uniquePrefix = uniquePrefix || ''; | ||||||
| @ -493,6 +490,8 @@ var PartialEvaluator = (function partialEvaluator() { | |||||||
|           var baseName = encoding.get('BaseEncoding'); |           var baseName = encoding.get('BaseEncoding'); | ||||||
|           if (baseName) |           if (baseName) | ||||||
|             baseEncoding = Encodings[baseName.name]; |             baseEncoding = Encodings[baseName.name]; | ||||||
|  |           else | ||||||
|  |             hasEncoding = false; // base encoding was not provided
 | ||||||
| 
 | 
 | ||||||
|           // Load the differences between the base and original
 |           // Load the differences between the base and original
 | ||||||
|           if (encoding.has('Differences')) { |           if (encoding.has('Differences')) { | ||||||
| @ -512,6 +511,7 @@ var PartialEvaluator = (function partialEvaluator() { | |||||||
|           error('Encoding is not a Name nor a Dict'); |           error('Encoding is not a Name nor a Dict'); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|       properties.differences = differences; |       properties.differences = differences; | ||||||
|       properties.baseEncoding = baseEncoding; |       properties.baseEncoding = baseEncoding; | ||||||
|       properties.hasEncoding = hasEncoding; |       properties.hasEncoding = hasEncoding; | ||||||
| @ -554,9 +554,21 @@ var PartialEvaluator = (function partialEvaluator() { | |||||||
|                   var startRange = tokens[j]; |                   var startRange = tokens[j]; | ||||||
|                   var endRange = tokens[j + 1]; |                   var endRange = tokens[j + 1]; | ||||||
|                   var code = tokens[j + 2]; |                   var code = tokens[j + 2]; | ||||||
|                   while (startRange <= endRange) { |                   if (code == 0xFFFF) { | ||||||
|                     charToUnicode[startRange] = code++; |                     // CMap is broken, assuming code == startRange
 | ||||||
|                     ++startRange; |                     code = startRange; | ||||||
|  |                   } | ||||||
|  |                   if (isArray(code)) { | ||||||
|  |                     var codeindex = 0; | ||||||
|  |                     while (startRange <= endRange) { | ||||||
|  |                       charToUnicode[startRange] = code[codeindex++]; | ||||||
|  |                       ++startRange; | ||||||
|  |                     } | ||||||
|  |                   } else { | ||||||
|  |                     while (startRange <= endRange) { | ||||||
|  |                       charToUnicode[startRange] = code++; | ||||||
|  |                       ++startRange; | ||||||
|  |                     } | ||||||
|                   } |                   } | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
| @ -595,9 +607,18 @@ var PartialEvaluator = (function partialEvaluator() { | |||||||
|             } |             } | ||||||
|           } else if (byte == 0x3E) { |           } else if (byte == 0x3E) { | ||||||
|             if (token.length) { |             if (token.length) { | ||||||
|               // parsing hex number
 |               if (token.length <= 4) { | ||||||
|               tokens.push(parseInt(token, 16)); |                 // parsing hex number
 | ||||||
|               token = ''; |                 tokens.push(parseInt(token, 16)); | ||||||
|  |                 token = ''; | ||||||
|  |               } else { | ||||||
|  |                 // parsing hex UTF-16BE numbers
 | ||||||
|  |                 var str = []; | ||||||
|  |                 for (var i = 0, ii = token.length; i < ii; i += 4) | ||||||
|  |                   str.push(parseInt(token.substr(i, 4), 16)); | ||||||
|  |                 tokens.push(String.fromCharCode.apply(String, str)); | ||||||
|  |                 token = ''; | ||||||
|  |               } | ||||||
|             } |             } | ||||||
|           } else { |           } else { | ||||||
|             token += String.fromCharCode(byte); |             token += String.fromCharCode(byte); | ||||||
| @ -829,11 +850,11 @@ var PartialEvaluator = (function partialEvaluator() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return PartialEvaluator; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var EvalState = (function evalState() { | var EvalState = (function EvalStateClosure() { | ||||||
|   function constructor() { |   function EvalState() { | ||||||
|     // Are soft masks and alpha values shapes or opacities?
 |     // Are soft masks and alpha values shapes or opacities?
 | ||||||
|     this.alphaIsShape = false; |     this.alphaIsShape = false; | ||||||
|     this.fontSize = 0; |     this.fontSize = 0; | ||||||
| @ -850,8 +871,8 @@ var EvalState = (function evalState() { | |||||||
|     this.fillColorSpace = null; |     this.fillColorSpace = null; | ||||||
|     this.strokeColorSpace = null; |     this.strokeColorSpace = null; | ||||||
|   } |   } | ||||||
|   constructor.prototype = { |   EvalState.prototype = { | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return EvalState; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										435
									
								
								src/fonts.js
									
									
									
									
									
								
							
							
						
						
									
										435
									
								
								src/fonts.js
									
									
									
									
									
								
							| @ -3,8 +3,6 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var isWorker = (typeof window == 'undefined'); |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * Maximum time to wait for a font to be loaded by font-face rules. |  * Maximum time to wait for a font to be loaded by font-face rules. | ||||||
|  */ |  */ | ||||||
| @ -719,20 +717,10 @@ function getUnicodeRangeFor(value) { | |||||||
|   return -1; |   return -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function adaptUnicode(unicode) { |  | ||||||
|   return (unicode <= 0x1F || (unicode >= 127 && unicode < kSizeOfGlyphArea)) ? |  | ||||||
|     unicode + kCmapGlyphOffset : unicode; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function isAdaptedUnicode(unicode) { |  | ||||||
|   return unicode >= kCmapGlyphOffset && |  | ||||||
|     unicode < kCmapGlyphOffset + kSizeOfGlyphArea; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function isSpecialUnicode(unicode) { | function isSpecialUnicode(unicode) { | ||||||
|   return (unicode <= 0x1F || (unicode >= 127 && unicode < kSizeOfGlyphArea)) || |   return (unicode <= 0x1F || (unicode >= 127 && unicode < kSizeOfGlyphArea)) || | ||||||
|     unicode >= kCmapGlyphOffset && |     (unicode >= kCmapGlyphOffset && | ||||||
|     unicode < kCmapGlyphOffset + kSizeOfGlyphArea; |     unicode < kCmapGlyphOffset + kSizeOfGlyphArea); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -743,8 +731,8 @@ function isSpecialUnicode(unicode) { | |||||||
|  *   var type1Font = new Font("MyFontName", binaryFile, propertiesObject); |  *   var type1Font = new Font("MyFontName", binaryFile, propertiesObject); | ||||||
|  *   type1Font.bind(); |  *   type1Font.bind(); | ||||||
|  */ |  */ | ||||||
| var Font = (function Font() { | var Font = (function FontClosure() { | ||||||
|   var constructor = function font_constructor(name, file, properties) { |   function Font(name, file, properties) { | ||||||
|     this.name = name; |     this.name = name; | ||||||
|     this.coded = properties.coded; |     this.coded = properties.coded; | ||||||
|     this.charProcIRQueues = properties.charProcIRQueues; |     this.charProcIRQueues = properties.charProcIRQueues; | ||||||
| @ -771,16 +759,23 @@ var Font = (function Font() { | |||||||
|     this.widths = properties.widths; |     this.widths = properties.widths; | ||||||
|     this.defaultWidth = properties.defaultWidth; |     this.defaultWidth = properties.defaultWidth; | ||||||
|     this.composite = properties.composite; |     this.composite = properties.composite; | ||||||
|     this.toUnicode = properties.toUnicode; |  | ||||||
|     this.hasEncoding = properties.hasEncoding; |     this.hasEncoding = properties.hasEncoding; | ||||||
| 
 | 
 | ||||||
|     this.fontMatrix = properties.fontMatrix; |     this.fontMatrix = properties.fontMatrix; | ||||||
|     if (properties.type == 'Type3') |     this.widthMultiplier = 1.0; | ||||||
|  |     if (properties.type == 'Type3') { | ||||||
|  |       this.encoding = properties.baseEncoding; | ||||||
|       return; |       return; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Trying to fix encoding using glyph CIDSystemInfo.
 |     // Trying to fix encoding using glyph CIDSystemInfo.
 | ||||||
|     this.loadCidToUnicode(properties); |     this.loadCidToUnicode(properties); | ||||||
| 
 | 
 | ||||||
|  |     if (properties.toUnicode) | ||||||
|  |       this.toUnicode = properties.toUnicode; | ||||||
|  |     else | ||||||
|  |       this.rebuildToUnicode(properties); | ||||||
|  | 
 | ||||||
|     if (!file) { |     if (!file) { | ||||||
|       // The file data is not specified. Trying to fix the font name
 |       // The file data is not specified. Trying to fix the font name
 | ||||||
|       // to be used with the canvas.font.
 |       // to be used with the canvas.font.
 | ||||||
| @ -832,6 +827,8 @@ var Font = (function Font() { | |||||||
| 
 | 
 | ||||||
|     this.data = data; |     this.data = data; | ||||||
|     this.fontMatrix = properties.fontMatrix; |     this.fontMatrix = properties.fontMatrix; | ||||||
|  |     this.widthMultiplier = !properties.fontMatrix ? 1.0 : | ||||||
|  |       1.0 / properties.fontMatrix[0]; | ||||||
|     this.encoding = properties.baseEncoding; |     this.encoding = properties.baseEncoding; | ||||||
|     this.hasShortCmap = properties.hasShortCmap; |     this.hasShortCmap = properties.hasShortCmap; | ||||||
|     this.loadedName = getUniqueName(); |     this.loadedName = getUniqueName(); | ||||||
| @ -887,6 +884,13 @@ var Font = (function Font() { | |||||||
|            String.fromCharCode(value & 0xff); |            String.fromCharCode(value & 0xff); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |   function safeString16(value) { | ||||||
|  |     // clamp value to the 16-bit int range
 | ||||||
|  |     value = value > 0x7FFF ? 0x7FFF : value < -0x8000 ? -0x8000 : value; | ||||||
|  |     return String.fromCharCode((value >> 8) & 0xff) + | ||||||
|  |            String.fromCharCode(value & 0xff); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|   function string32(value) { |   function string32(value) { | ||||||
|     return String.fromCharCode((value >> 24) & 0xff) + |     return String.fromCharCode((value >> 24) & 0xff) + | ||||||
|            String.fromCharCode((value >> 16) & 0xff) + |            String.fromCharCode((value >> 16) & 0xff) + | ||||||
| @ -961,15 +965,15 @@ var Font = (function Font() { | |||||||
|     var ranges = []; |     var ranges = []; | ||||||
|     for (var n = 0; n < length; ) { |     for (var n = 0; n < length; ) { | ||||||
|       var start = codes[n].unicode; |       var start = codes[n].unicode; | ||||||
|       var startCode = codes[n].code; |       var codeIndices = [codes[n].code]; | ||||||
|       ++n; |       ++n; | ||||||
|       var end = start; |       var end = start; | ||||||
|       while (n < length && end + 1 == codes[n].unicode) { |       while (n < length && end + 1 == codes[n].unicode) { | ||||||
|  |         codeIndices.push(codes[n].code); | ||||||
|         ++end; |         ++end; | ||||||
|         ++n; |         ++n; | ||||||
|       } |       } | ||||||
|       var endCode = codes[n - 1].code; |       ranges.push([start, end, codeIndices]); | ||||||
|       ranges.push([start, end, startCode, endCode]); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ranges; |     return ranges; | ||||||
| @ -1012,17 +1016,16 @@ var Font = (function Font() { | |||||||
|         idDeltas += string16(0); |         idDeltas += string16(0); | ||||||
|         idRangeOffsets += string16(offset); |         idRangeOffsets += string16(offset); | ||||||
| 
 | 
 | ||||||
|         var startCode = range[2]; |         var codes = range[2]; | ||||||
|         var endCode = range[3]; |         for (var j = 0, jj = codes.length; j < jj; ++j) | ||||||
|         for (var j = startCode; j <= endCode; ++j) |           glyphsIds += string16(deltas[codes[j]]); | ||||||
|           glyphsIds += string16(deltas[j]); |  | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       for (var i = 0; i < segCount - 1; i++) { |       for (var i = 0; i < segCount - 1; i++) { | ||||||
|         var range = ranges[i]; |         var range = ranges[i]; | ||||||
|         var start = range[0]; |         var start = range[0]; | ||||||
|         var end = range[1]; |         var end = range[1]; | ||||||
|         var startCode = range[2]; |         var startCode = range[2][0]; | ||||||
| 
 | 
 | ||||||
|         startCount += string16(start); |         startCount += string16(start); | ||||||
|         endCount += string16(end); |         endCount += string16(end); | ||||||
| @ -1226,7 +1229,7 @@ var Font = (function Font() { | |||||||
|     return nameTable; |     return nameTable; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Font.prototype = { | ||||||
|     name: null, |     name: null, | ||||||
|     font: null, |     font: null, | ||||||
|     mimetype: null, |     mimetype: null, | ||||||
| @ -1299,7 +1302,7 @@ var Font = (function Font() { | |||||||
|           properties.baseEncoding = encoding; |           properties.baseEncoding = encoding; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function replaceCMapTable(cmap, font, properties) { |       function readCMapTable(cmap, font) { | ||||||
|         var start = (font.start ? font.start : 0) + cmap.offset; |         var start = (font.start ? font.start : 0) + cmap.offset; | ||||||
|         font.pos = start; |         font.pos = start; | ||||||
| 
 | 
 | ||||||
| @ -1316,7 +1319,7 @@ var Font = (function Font() { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Check that table are sorted by platformID then encodingID,
 |         // Check that table are sorted by platformID then encodingID,
 | ||||||
|         records.sort(function fontReplaceCMapTableSort(a, b) { |         records.sort(function fontReadCMapTableSort(a, b) { | ||||||
|           return ((a.platformID << 16) + a.encodingID) - |           return ((a.platformID << 16) + a.encodingID) - | ||||||
|                  ((b.platformID << 16) + b.encodingID); |                  ((b.platformID << 16) + b.encodingID); | ||||||
|         }); |         }); | ||||||
| @ -1371,16 +1374,15 @@ var Font = (function Font() { | |||||||
|             for (var j = 0; j < 256; j++) { |             for (var j = 0; j < 256; j++) { | ||||||
|               var index = font.getByte(); |               var index = font.getByte(); | ||||||
|               if (index) { |               if (index) { | ||||||
|                 var unicode = adaptUnicode(j); |                 glyphs.push({ unicode: j, code: j }); | ||||||
|                 glyphs.push({ unicode: unicode, code: j }); |  | ||||||
|                 ids.push(index); |                 ids.push(index); | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
| 
 |             return { | ||||||
|             properties.hasShortCmap = true; |               glyphs: glyphs, | ||||||
| 
 |               ids: ids, | ||||||
|             createGlyphNameMap(glyphs, ids, properties); |               hasShortCmap: true | ||||||
|             return cmap.data = createCMapTable(glyphs, ids); |             }; | ||||||
|           } else if (format == 4) { |           } else if (format == 4) { | ||||||
|             // re-creating the table in format 4 since the encoding
 |             // re-creating the table in format 4 since the encoding
 | ||||||
|             // might be changed
 |             // might be changed
 | ||||||
| @ -1432,17 +1434,18 @@ var Font = (function Font() { | |||||||
|                 var glyphCode = offsetIndex < 0 ? j : |                 var glyphCode = offsetIndex < 0 ? j : | ||||||
|                   offsets[offsetIndex + j - start]; |                   offsets[offsetIndex + j - start]; | ||||||
|                 glyphCode = (glyphCode + delta) & 0xFFFF; |                 glyphCode = (glyphCode + delta) & 0xFFFF; | ||||||
|                 if (glyphCode == 0 || isAdaptedUnicode(j)) |                 if (glyphCode == 0) | ||||||
|                   continue; |                   continue; | ||||||
| 
 | 
 | ||||||
|                 var unicode = adaptUnicode(j); |                 glyphs.push({ unicode: j, code: j }); | ||||||
|                 glyphs.push({ unicode: unicode, code: j }); |  | ||||||
|                 ids.push(glyphCode); |                 ids.push(glyphCode); | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             createGlyphNameMap(glyphs, ids, properties); |             return { | ||||||
|             return cmap.data = createCMapTable(glyphs, ids); |               glyphs: glyphs, | ||||||
|  |               ids: ids | ||||||
|  |             }; | ||||||
|           } else if (format == 6) { |           } else if (format == 6) { | ||||||
|             // Format 6 is a 2-bytes dense mapping, which means the font data
 |             // Format 6 is a 2-bytes dense mapping, which means the font data
 | ||||||
|             // lives glue together even if they are pretty far in the unicode
 |             // lives glue together even if they are pretty far in the unicode
 | ||||||
| @ -1457,19 +1460,18 @@ var Font = (function Font() { | |||||||
|             for (var j = 0; j < entryCount; j++) { |             for (var j = 0; j < entryCount; j++) { | ||||||
|               var glyphCode = int16(font.getBytes(2)); |               var glyphCode = int16(font.getBytes(2)); | ||||||
|               var code = firstCode + j; |               var code = firstCode + j; | ||||||
|               if (isAdaptedUnicode(glyphCode)) |  | ||||||
|                 continue; |  | ||||||
| 
 | 
 | ||||||
|               var unicode = adaptUnicode(code); |               glyphs.push({ unicode: code, code: code }); | ||||||
|               glyphs.push({ unicode: unicode, code: code }); |  | ||||||
|               ids.push(glyphCode); |               ids.push(glyphCode); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             createGlyphNameMap(glyphs, ids, properties); |             return { | ||||||
|             return cmap.data = createCMapTable(glyphs, ids); |               glyphs: glyphs, | ||||||
|  |               ids: ids | ||||||
|  |             }; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         return cmap.data; |         error('Unsupported cmap table format'); | ||||||
|       }; |       }; | ||||||
| 
 | 
 | ||||||
|       function sanitizeMetrics(font, header, metrics, numGlyphs) { |       function sanitizeMetrics(font, header, metrics, numGlyphs) { | ||||||
| @ -1708,17 +1710,108 @@ var Font = (function Font() { | |||||||
|           tables.push(cmap); |           tables.push(cmap); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var glyphs = []; |         var cidToGidMap = properties.cidToGidMap || []; | ||||||
|         for (i = 1; i < numGlyphs; i++) { |         var gidToCidMap = [0]; | ||||||
|           if (isAdaptedUnicode(i)) |         if (cidToGidMap.length > 0) { | ||||||
|             continue; |           for (var j = cidToGidMap.length - 1; j >= 0; j--) { | ||||||
| 
 |             var gid = cidToGidMap[j]; | ||||||
|           glyphs.push({ unicode: adaptUnicode(i) }); |             if (gid) | ||||||
|  |               gidToCidMap[gid] = j; | ||||||
|  |           } | ||||||
|  |           // filling the gaps using CID above the CIDs currently used in font
 | ||||||
|  |           var nextCid = cidToGidMap.length; | ||||||
|  |           for (var i = 1; i < numGlyphs; i++) { | ||||||
|  |             if (!gidToCidMap[i]) | ||||||
|  |               gidToCidMap[i] = nextCid++; | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|         cmap.data = createCMapTable(glyphs); | 
 | ||||||
|  |         var glyphs = [], ids = []; | ||||||
|  |         var usedUnicodes = []; | ||||||
|  |         var unassignedUnicodeItems = []; | ||||||
|  |         for (var i = 1; i < numGlyphs; i++) { | ||||||
|  |           var cid = gidToCidMap[i] || i; | ||||||
|  |           var unicode = this.toUnicode[cid]; | ||||||
|  |           if (!unicode || isSpecialUnicode(unicode) || | ||||||
|  |               unicode in usedUnicodes) { | ||||||
|  |             unassignedUnicodeItems.push(i); | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           usedUnicodes[unicode] = true; | ||||||
|  |           glyphs.push({ unicode: unicode, code: cid }); | ||||||
|  |           ids.push(i); | ||||||
|  |         } | ||||||
|  |         // trying to fit as many unassigned symbols as we can
 | ||||||
|  |         // in the range allocated for the user defined symbols
 | ||||||
|  |         var unusedUnicode = kCmapGlyphOffset; | ||||||
|  |         for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; j++) { | ||||||
|  |           var i = unassignedUnicodeItems[j]; | ||||||
|  |           var cid = gidToCidMap[i] || i; | ||||||
|  |           while (unusedUnicode in usedUnicodes) | ||||||
|  |             unusedUnicode++; | ||||||
|  |           if (unusedUnicode >= kCmapGlyphOffset + kSizeOfGlyphArea) | ||||||
|  |             break; | ||||||
|  |           var unicode = unusedUnicode++; | ||||||
|  |           this.toUnicode[cid] = unicode; | ||||||
|  |           usedUnicodes[unicode] = true; | ||||||
|  |           glyphs.push({ unicode: unicode, code: cid }); | ||||||
|  |           ids.push(i); | ||||||
|  |         } | ||||||
|  |         cmap.data = createCMapTable(glyphs, ids); | ||||||
|       } else { |       } else { | ||||||
|         replaceCMapTable(cmap, font, properties); |         var cmapTable = readCMapTable(cmap, font); | ||||||
|  |         var glyphs = cmapTable.glyphs; | ||||||
|  |         var ids = cmapTable.ids; | ||||||
|  |         var hasShortCmap = !!cmapTable.hasShortCmap; | ||||||
|  |         var toUnicode = this.toUnicode; | ||||||
|  | 
 | ||||||
|  |         if (toUnicode && toUnicode.length > 0) { | ||||||
|  |           // checking if cmap is just identity map
 | ||||||
|  |           var isIdentity = true; | ||||||
|  |           for (var i = 0, ii = glyphs.length; i < ii; i++) { | ||||||
|  |             if (glyphs[i].unicode != i + 1) { | ||||||
|  |               isIdentity = false; | ||||||
|  |               break; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           // if it is, replacing with meaningful toUnicode values
 | ||||||
|  |           if (isIdentity) { | ||||||
|  |             var usedUnicodes = [], unassignedUnicodeItems = []; | ||||||
|  |             for (var i = 0, ii = glyphs.length; i < ii; i++) { | ||||||
|  |               var unicode = toUnicode[i + 1]; | ||||||
|  |               if (!unicode || unicode in usedUnicodes) { | ||||||
|  |                 unassignedUnicodeItems.push(i); | ||||||
|  |                 continue; | ||||||
|  |               } | ||||||
|  |               glyphs[i].unicode = unicode; | ||||||
|  |               usedUnicodes[unicode] = true; | ||||||
|  |             } | ||||||
|  |             var unusedUnicode = kCmapGlyphOffset; | ||||||
|  |             for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; j++) { | ||||||
|  |               var i = unassignedUnicodeItems[j]; | ||||||
|  |               while (unusedUnicode in usedUnicodes) | ||||||
|  |                 unusedUnicode++; | ||||||
|  |               var cid = i + 1; | ||||||
|  |               // override only if unicode mapping is not specified
 | ||||||
|  |               if (!(cid in toUnicode)) | ||||||
|  |                 toUnicode[cid] = unusedUnicode; | ||||||
|  |               glyphs[i].unicode = unusedUnicode++; | ||||||
|  |             } | ||||||
|  |             this.useToUnicode = true; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         properties.hasShortCmap = hasShortCmap; | ||||||
|  | 
 | ||||||
|  |         // remove glyph references outside range of avaialable glyphs
 | ||||||
|  |         for (var i = 0, ii = ids.length; i < ii; i++) { | ||||||
|  |           if (ids[i] >= numGlyphs) | ||||||
|  |             ids[i] = 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         createGlyphNameMap(glyphs, ids, properties); | ||||||
|         this.glyphNameMap = properties.glyphNameMap; |         this.glyphNameMap = properties.glyphNameMap; | ||||||
|  | 
 | ||||||
|  |         cmap.data = createCMapTable(glyphs, ids); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       // Rewrite the 'post' table if needed
 |       // Rewrite the 'post' table if needed
 | ||||||
| @ -1808,6 +1901,14 @@ var Font = (function Font() { | |||||||
|         } |         } | ||||||
|         properties.baseEncoding = encoding; |         properties.baseEncoding = encoding; | ||||||
|       } |       } | ||||||
|  |       if (properties.subtype == 'CIDFontType0C') { | ||||||
|  |         var toUnicode = []; | ||||||
|  |         for (var i = 0; i < charstrings.length; ++i) { | ||||||
|  |           var charstring = charstrings[i]; | ||||||
|  |           toUnicode[charstring.code] = charstring.unicode; | ||||||
|  |         } | ||||||
|  |         this.toUnicode = toUnicode; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       var fields = { |       var fields = { | ||||||
|         // PostScript Font Program
 |         // PostScript Font Program
 | ||||||
| @ -1832,9 +1933,9 @@ var Font = (function Font() { | |||||||
|               '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // creation date
 |               '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // creation date
 | ||||||
|               '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // modifification date
 |               '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // modifification date
 | ||||||
|               '\x00\x00' + // xMin
 |               '\x00\x00' + // xMin
 | ||||||
|               string16(properties.descent) + // yMin
 |               safeString16(properties.descent) + // yMin
 | ||||||
|               '\x0F\xFF' + // xMax
 |               '\x0F\xFF' + // xMax
 | ||||||
|               string16(properties.ascent) + // yMax
 |               safeString16(properties.ascent) + // yMax
 | ||||||
|               string16(properties.italicAngle ? 2 : 0) + // macStyle
 |               string16(properties.italicAngle ? 2 : 0) + // macStyle
 | ||||||
|               '\x00\x11' + // lowestRecPPEM
 |               '\x00\x11' + // lowestRecPPEM
 | ||||||
|               '\x00\x00' + // fontDirectionHint
 |               '\x00\x00' + // fontDirectionHint
 | ||||||
| @ -1846,15 +1947,15 @@ var Font = (function Font() { | |||||||
|         'hhea': (function fontFieldsHhea() { |         'hhea': (function fontFieldsHhea() { | ||||||
|           return stringToArray( |           return stringToArray( | ||||||
|               '\x00\x01\x00\x00' + // Version number
 |               '\x00\x01\x00\x00' + // Version number
 | ||||||
|               string16(properties.ascent) + // Typographic Ascent
 |               safeString16(properties.ascent) + // Typographic Ascent
 | ||||||
|               string16(properties.descent) + // Typographic Descent
 |               safeString16(properties.descent) + // Typographic Descent
 | ||||||
|               '\x00\x00' + // Line Gap
 |               '\x00\x00' + // Line Gap
 | ||||||
|               '\xFF\xFF' + // advanceWidthMax
 |               '\xFF\xFF' + // advanceWidthMax
 | ||||||
|               '\x00\x00' + // minLeftSidebearing
 |               '\x00\x00' + // minLeftSidebearing
 | ||||||
|               '\x00\x00' + // minRightSidebearing
 |               '\x00\x00' + // minRightSidebearing
 | ||||||
|               '\x00\x00' + // xMaxExtent
 |               '\x00\x00' + // xMaxExtent
 | ||||||
|               string16(properties.capHeight) + // caretSlopeRise
 |               safeString16(properties.capHeight) + // caretSlopeRise
 | ||||||
|               string16(Math.tan(properties.italicAngle) * |               safeString16(Math.tan(properties.italicAngle) * | ||||||
|                        properties.xHeight) + // caretSlopeRun
 |                        properties.xHeight) + // caretSlopeRun
 | ||||||
|               '\x00\x00' + // caretOffset
 |               '\x00\x00' + // caretOffset
 | ||||||
|               '\x00\x00' + // -reserved-
 |               '\x00\x00' + // -reserved-
 | ||||||
| @ -1868,8 +1969,11 @@ var Font = (function Font() { | |||||||
|         // Horizontal metrics
 |         // Horizontal metrics
 | ||||||
|         'hmtx': (function fontFieldsHmtx() { |         'hmtx': (function fontFieldsHmtx() { | ||||||
|           var hmtx = '\x00\x00\x00\x00'; // Fake .notdef
 |           var hmtx = '\x00\x00\x00\x00'; // Fake .notdef
 | ||||||
|           for (var i = 0, ii = charstrings.length; i < ii; i++) |           for (var i = 0, ii = charstrings.length; i < ii; i++) { | ||||||
|             hmtx += string16(charstrings[i].width) + string16(0); |             var charstring = charstrings[i]; | ||||||
|  |             var width = 'width' in charstring ? charstring.width : 0; | ||||||
|  |             hmtx += string16(width) + string16(0); | ||||||
|  |           } | ||||||
|           return stringToArray(hmtx); |           return stringToArray(hmtx); | ||||||
|         })(), |         })(), | ||||||
| 
 | 
 | ||||||
| @ -1898,17 +2002,35 @@ var Font = (function Font() { | |||||||
|       return stringToArray(otf.file); |       return stringToArray(otf.file); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     loadCidToUnicode: function font_loadCidToUnicode(properties) { |     rebuildToUnicode: function font_rebuildToUnicode(properties) { | ||||||
|       if (properties.cidToGidMap) { |       var firstChar = properties.firstChar, lastChar = properties.lastChar; | ||||||
|         this.cidToUnicode = properties.cidToGidMap; |       var map = []; | ||||||
|         return; |       if (properties.composite) { | ||||||
|  |         var isIdentityMap = this.cidToUnicode.length == 0; | ||||||
|  |         for (var i = firstChar, ii = lastChar; i <= ii; i++) { | ||||||
|  |           // TODO missing map the character according font's CMap
 | ||||||
|  |           var cid = i; | ||||||
|  |           map[i] = isIdentityMap ? cid : this.cidToUnicode[cid]; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         for (var i = firstChar, ii = lastChar; i <= ii; i++) { | ||||||
|  |           var glyph = properties.differences[i]; | ||||||
|  |           if (!glyph) | ||||||
|  |             glyph = properties.baseEncoding[i]; | ||||||
|  |           if (!!glyph && (glyph in GlyphsUnicode)) | ||||||
|  |             map[i] = GlyphsUnicode[glyph]; | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|  |       this.toUnicode = map; | ||||||
|  |     }, | ||||||
| 
 | 
 | ||||||
|  |     loadCidToUnicode: function font_loadCidToUnicode(properties) { | ||||||
|       if (!properties.cidSystemInfo) |       if (!properties.cidSystemInfo) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|       var cidToUnicodeMap = []; |       var cidToUnicodeMap = [], unicodeToCIDMap = []; | ||||||
|       this.cidToUnicode = cidToUnicodeMap; |       this.cidToUnicode = cidToUnicodeMap; | ||||||
|  |       this.unicodeToCID = unicodeToCIDMap; | ||||||
| 
 | 
 | ||||||
|       var cidSystemInfo = properties.cidSystemInfo; |       var cidSystemInfo = properties.cidSystemInfo; | ||||||
|       var cidToUnicode; |       var cidToUnicode; | ||||||
| @ -1920,28 +2042,34 @@ var Font = (function Font() { | |||||||
|       if (!cidToUnicode) |       if (!cidToUnicode) | ||||||
|         return; // identity encoding
 |         return; // identity encoding
 | ||||||
| 
 | 
 | ||||||
|       var glyph = 1, i, j, k, ii; |       var cid = 1, i, j, k, ii; | ||||||
|       for (i = 0, ii = cidToUnicode.length; i < ii; ++i) { |       for (i = 0, ii = cidToUnicode.length; i < ii; ++i) { | ||||||
|         var unicode = cidToUnicode[i]; |         var unicode = cidToUnicode[i]; | ||||||
|         if (isArray(unicode)) { |         if (isArray(unicode)) { | ||||||
|           var length = unicode.length; |           var length = unicode.length; | ||||||
|           for (j = 0; j < length; j++) |           for (j = 0; j < length; j++) { | ||||||
|             cidToUnicodeMap[unicode[j]] = glyph; |             cidToUnicodeMap[cid] = unicode[j]; | ||||||
|           glyph++; |             unicodeToCIDMap[unicode[j]] = cid; | ||||||
|  |           } | ||||||
|  |           cid++; | ||||||
|         } else if (typeof unicode === 'object') { |         } else if (typeof unicode === 'object') { | ||||||
|           var fillLength = unicode.f; |           var fillLength = unicode.f; | ||||||
|           if (fillLength) { |           if (fillLength) { | ||||||
|             k = unicode.c; |             k = unicode.c; | ||||||
|             for (j = 0; j < fillLength; ++j) { |             for (j = 0; j < fillLength; ++j) { | ||||||
|               cidToUnicodeMap[k] = glyph++; |               cidToUnicodeMap[cid] = k; | ||||||
|  |               unicodeToCIDMap[k] = cid; | ||||||
|  |               cid++; | ||||||
|               k++; |               k++; | ||||||
|             } |             } | ||||||
|           } else |           } else | ||||||
|             glyph += unicode.s; |             cid += unicode.s; | ||||||
|         } else if (unicode) { |         } else if (unicode) { | ||||||
|           cidToUnicodeMap[unicode] = glyph++; |           cidToUnicodeMap[cid] = unicode; | ||||||
|  |           unicodeToCIDMap[unicode] = cid; | ||||||
|  |           cid++; | ||||||
|         } else |         } else | ||||||
|           glyph++; |           cid++; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
| @ -1964,7 +2092,7 @@ var Font = (function Font() { | |||||||
|                  window.btoa(data) + ');'); |                  window.btoa(data) + ');'); | ||||||
|       var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}'; |       var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}'; | ||||||
| 
 | 
 | ||||||
|       document.documentElement.firstChild.appendChild( |       document.documentElement.getElementsByTagName('head')[0].appendChild( | ||||||
|         document.createElement('style')); |         document.createElement('style')); | ||||||
| 
 | 
 | ||||||
|       var styleSheet = document.styleSheets[document.styleSheets.length - 1]; |       var styleSheet = document.styleSheets[document.styleSheets.length - 1]; | ||||||
| @ -1973,6 +2101,37 @@ var Font = (function Font() { | |||||||
|       return rule; |       return rule; | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|  |     get spaceWidth() { | ||||||
|  |       // trying to estimate space character width
 | ||||||
|  |       var possibleSpaceReplacements = ['space', 'minus', 'one', 'i']; | ||||||
|  |       var width; | ||||||
|  |       for (var i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) { | ||||||
|  |         var glyphName = possibleSpaceReplacements[i]; | ||||||
|  |         // if possible, getting width by glyph name
 | ||||||
|  |         if (glyphName in this.widths) { | ||||||
|  |           width = this.widths[glyphName]; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         var glyphUnicode = GlyphsUnicode[glyphName]; | ||||||
|  |         // finding the charcode via unicodeToCID map
 | ||||||
|  |         var charcode = 0; | ||||||
|  |         if (this.composite) | ||||||
|  |           charcode = this.unicodeToCID[glyphUnicode]; | ||||||
|  |         // ... via toUnicode map
 | ||||||
|  |         if (!charcode && 'toUnicode' in this) | ||||||
|  |           charcode = this.toUnicode.indexOf(glyphUnicode); | ||||||
|  |         // setting it to unicode if negative or undefined
 | ||||||
|  |         if (!(charcode > 0)) | ||||||
|  |           charcode = glyphUnicode; | ||||||
|  |         // trying to get width via charcode
 | ||||||
|  |         width = this.widths[charcode]; | ||||||
|  |         if (width) | ||||||
|  |           break; // the non-zero width found
 | ||||||
|  |       } | ||||||
|  |       width = (width || this.defaultWidth) * this.widthMultiplier; | ||||||
|  |       return shadow(this, 'spaceWidth', width); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|     charToGlyph: function fonts_charToGlyph(charcode) { |     charToGlyph: function fonts_charToGlyph(charcode) { | ||||||
|       var unicode, width, codeIRQueue; |       var unicode, width, codeIRQueue; | ||||||
| 
 | 
 | ||||||
| @ -1981,30 +2140,30 @@ var Font = (function Font() { | |||||||
|       switch (this.type) { |       switch (this.type) { | ||||||
|         case 'CIDFontType0': |         case 'CIDFontType0': | ||||||
|           if (this.noUnicodeAdaptation) { |           if (this.noUnicodeAdaptation) { | ||||||
|             width = this.widths[this.cidToUnicode[charcode]]; |             width = this.widths[this.unicodeToCID[charcode] || charcode]; | ||||||
|             unicode = charcode; |             unicode = charcode; | ||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|           unicode = adaptUnicode(this.cidToUnicode[charcode] || charcode); |           unicode = this.toUnicode[charcode] || charcode; | ||||||
|           break; |           break; | ||||||
|         case 'CIDFontType2': |         case 'CIDFontType2': | ||||||
|           if (this.noUnicodeAdaptation) { |           if (this.noUnicodeAdaptation) { | ||||||
|             width = this.widths[this.cidToUnicode[charcode]]; |             width = this.widths[this.unicodeToCID[charcode] || charcode]; | ||||||
|             unicode = charcode; |             unicode = charcode; | ||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|           unicode = adaptUnicode(this.cidToUnicode[charcode] || charcode); |           unicode = this.toUnicode[charcode] || charcode; | ||||||
|           break; |           break; | ||||||
|         case 'Type1': |         case 'Type1': | ||||||
|           var glyphName = this.differences[charcode] || this.encoding[charcode]; |           var glyphName = this.differences[charcode] || this.encoding[charcode]; | ||||||
|  |           if (!isNum(width)) | ||||||
|  |             width = this.widths[glyphName]; | ||||||
|           if (this.noUnicodeAdaptation) { |           if (this.noUnicodeAdaptation) { | ||||||
|             if (!isNum(width)) |  | ||||||
|               width = this.widths[glyphName]; |  | ||||||
|             unicode = GlyphsUnicode[glyphName] || charcode; |             unicode = GlyphsUnicode[glyphName] || charcode; | ||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|           unicode = this.glyphNameMap[glyphName] || |           unicode = this.glyphNameMap[glyphName] || | ||||||
|             adaptUnicode(GlyphsUnicode[glyphName] || charcode); |             GlyphsUnicode[glyphName] || charcode; | ||||||
|           break; |           break; | ||||||
|         case 'Type3': |         case 'Type3': | ||||||
|           var glyphName = this.differences[charcode] || this.encoding[charcode]; |           var glyphName = this.differences[charcode] || this.encoding[charcode]; | ||||||
| @ -2012,6 +2171,10 @@ var Font = (function Font() { | |||||||
|           unicode = charcode; |           unicode = charcode; | ||||||
|           break; |           break; | ||||||
|         case 'TrueType': |         case 'TrueType': | ||||||
|  |           if (this.useToUnicode) { | ||||||
|  |             unicode = this.toUnicode[charcode] || charcode; | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|           var glyphName = this.differences[charcode] || this.encoding[charcode]; |           var glyphName = this.differences[charcode] || this.encoding[charcode]; | ||||||
|           if (!glyphName) |           if (!glyphName) | ||||||
|             glyphName = Encodings.StandardEncoding[charcode]; |             glyphName = Encodings.StandardEncoding[charcode]; | ||||||
| @ -2022,16 +2185,16 @@ var Font = (function Font() { | |||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|           if (!this.hasEncoding) { |           if (!this.hasEncoding) { | ||||||
|             unicode = adaptUnicode(charcode); |             unicode = this.useToUnicode ? this.toUnicode[charcode] : charcode; | ||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|           if (this.hasShortCmap) { |           if (this.hasShortCmap && false) { | ||||||
|             var j = Encodings.MacRomanEncoding.indexOf(glyphName); |             var j = Encodings.MacRomanEncoding.indexOf(glyphName); | ||||||
|             unicode = j >= 0 && !isSpecialUnicode(j) ? j : |             unicode = j >= 0 ? j : | ||||||
|               this.glyphNameMap[glyphName]; |               this.glyphNameMap[glyphName]; | ||||||
|           } else { |           } else { | ||||||
|             unicode = glyphName in GlyphsUnicode ? |             unicode = glyphName in GlyphsUnicode ? | ||||||
|               adaptUnicode(GlyphsUnicode[glyphName]) : |               GlyphsUnicode[glyphName] : | ||||||
|               this.glyphNameMap[glyphName]; |               this.glyphNameMap[glyphName]; | ||||||
|           } |           } | ||||||
|           break; |           break; | ||||||
| @ -2039,14 +2202,23 @@ var Font = (function Font() { | |||||||
|           warn('Unsupported font type: ' + this.type); |           warn('Unsupported font type: ' + this.type); | ||||||
|           break; |           break; | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       var unicodeChars = !('toUnicode' in this) ? charcode : | ||||||
|  |         this.toUnicode[charcode] || charcode; | ||||||
|  |       if (typeof unicodeChars === 'number') | ||||||
|  |         unicodeChars = String.fromCharCode(unicodeChars); | ||||||
|  | 
 | ||||||
|  |       width = (isNum(width) ? width : this.defaultWidth) * this.widthMultiplier; | ||||||
|  | 
 | ||||||
|       return { |       return { | ||||||
|         unicode: unicode, |         fontChar: String.fromCharCode(unicode), | ||||||
|         width: isNum(width) ? width : this.defaultWidth, |         unicode: unicodeChars, | ||||||
|  |         width: width, | ||||||
|         codeIRQueue: codeIRQueue |         codeIRQueue: codeIRQueue | ||||||
|       }; |       }; | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     charsToGlyphs: function fonts_chars2Glyphs(chars) { |     charsToGlyphs: function fonts_charsToGlyphs(chars) { | ||||||
|       var charsCache = this.charsCache; |       var charsCache = this.charsCache; | ||||||
|       var glyphs; |       var glyphs; | ||||||
| 
 | 
 | ||||||
| @ -2094,7 +2266,7 @@ var Font = (function Font() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Font; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
| @ -2753,22 +2925,13 @@ CFF.prototype = { | |||||||
|   getOrderedCharStrings: function cff_getOrderedCharStrings(glyphs, |   getOrderedCharStrings: function cff_getOrderedCharStrings(glyphs, | ||||||
|                                                             properties) { |                                                             properties) { | ||||||
|     var charstrings = []; |     var charstrings = []; | ||||||
|     var reverseMapping = {}; |  | ||||||
|     var encoding = properties.baseEncoding; |  | ||||||
|     var i, length, glyphName; |     var i, length, glyphName; | ||||||
|     for (i = 0, length = encoding.length; i < length; ++i) { |  | ||||||
|       glyphName = encoding[i]; |  | ||||||
|       if (!glyphName || isSpecialUnicode(i)) |  | ||||||
|         continue; |  | ||||||
|       reverseMapping[glyphName] = i; |  | ||||||
|     } |  | ||||||
|     reverseMapping['.notdef'] = 0; |  | ||||||
|     var unusedUnicode = kCmapGlyphOffset; |     var unusedUnicode = kCmapGlyphOffset; | ||||||
|     for (i = 0, length = glyphs.length; i < length; i++) { |     for (i = 0, length = glyphs.length; i < length; i++) { | ||||||
|       var item = glyphs[i]; |       var item = glyphs[i]; | ||||||
|       var glyphName = item.glyph; |       var glyphName = item.glyph; | ||||||
|       var unicode = glyphName in reverseMapping ? |       var unicode = glyphName in GlyphsUnicode ? | ||||||
|         reverseMapping[glyphName] : unusedUnicode++; |         GlyphsUnicode[glyphName] : unusedUnicode++; | ||||||
|       charstrings.push({ |       charstrings.push({ | ||||||
|         glyph: glyphName, |         glyph: glyphName, | ||||||
|         unicode: unicode, |         unicode: unicode, | ||||||
| @ -3013,9 +3176,9 @@ CFF.prototype = { | |||||||
|   } |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| var Type2CFF = (function type2CFF() { | var Type2CFF = (function Type2CFFClosure() { | ||||||
|   // TODO: replace parsing code with the Type2Parser in font_utils.js
 |   // TODO: replace parsing code with the Type2Parser in font_utils.js
 | ||||||
|   function constructor(file, properties) { |   function Type2CFF(file, properties) { | ||||||
|     var bytes = file.getBytes(); |     var bytes = file.getBytes(); | ||||||
|     this.bytes = bytes; |     this.bytes = bytes; | ||||||
|     this.properties = properties; |     this.properties = properties; | ||||||
| @ -3023,7 +3186,7 @@ var Type2CFF = (function type2CFF() { | |||||||
|     this.data = this.parse(); |     this.data = this.parse(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Type2CFF.prototype = { | ||||||
|     parse: function cff_parse() { |     parse: function cff_parse() { | ||||||
|       var header = this.parseHeader(); |       var header = this.parseHeader(); | ||||||
|       var properties = this.properties; |       var properties = this.properties; | ||||||
| @ -3055,16 +3218,14 @@ var Type2CFF = (function type2CFF() { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var charStrings = this.parseIndex(topDict.CharStrings); |       var charStrings = this.parseIndex(topDict.CharStrings); | ||||||
|       var charset = this.parseCharsets(topDict.charset, |  | ||||||
|                                        charStrings.length, strings); |  | ||||||
|       var encoding = this.parseEncoding(topDict.Encoding, properties, |  | ||||||
|                                              strings, charset); |  | ||||||
| 
 | 
 | ||||||
|       var charset, encoding; |       var charset, encoding; | ||||||
|       var isCIDFont = properties.subtype == 'CIDFontType0C'; |       var isCIDFont = properties.subtype == 'CIDFontType0C'; | ||||||
|       if (isCIDFont) { |       if (isCIDFont) { | ||||||
|         charset = []; |         charset = ['.notdef']; | ||||||
|         charset.length = charStrings.length; |         for (var i = 1, ii = charStrings.length; i < ii; ++i) | ||||||
|  |           charset.push('glyph' + i); | ||||||
|  | 
 | ||||||
|         encoding = this.parseCidMap(topDict.charset, |         encoding = this.parseCidMap(topDict.charset, | ||||||
|                                     charStrings.length); |                                     charStrings.length); | ||||||
|       } else { |       } else { | ||||||
| @ -3133,38 +3294,44 @@ var Type2CFF = (function type2CFF() { | |||||||
|       var charstrings = []; |       var charstrings = []; | ||||||
|       var unicodeUsed = []; |       var unicodeUsed = []; | ||||||
|       var unassignedUnicodeItems = []; |       var unassignedUnicodeItems = []; | ||||||
|  |       var inverseEncoding = []; | ||||||
|  |       for (var charcode in encoding) | ||||||
|  |         inverseEncoding[encoding[charcode]] = charcode | 0; | ||||||
|       for (var i = 0, ii = charsets.length; i < ii; i++) { |       for (var i = 0, ii = charsets.length; i < ii; i++) { | ||||||
|         var glyph = charsets[i]; |         var glyph = charsets[i]; | ||||||
|         var encodingFound = false; |         if (glyph == '.notdef') { | ||||||
|         for (var charcode in encoding) { |           charstrings.push({ | ||||||
|           if (encoding[charcode] == i) { |             unicode: 0, | ||||||
|             var code = charcode | 0; |             code: 0, | ||||||
|             charstrings.push({ |             gid: i, | ||||||
|               unicode: adaptUnicode(code), |             glyph: glyph | ||||||
|               code: code, |           }); | ||||||
|               gid: i, |           continue; | ||||||
|               glyph: glyph |  | ||||||
|             }); |  | ||||||
|             unicodeUsed[code] = true; |  | ||||||
|             encodingFound = true; |  | ||||||
|             break; |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|         if (!encodingFound) { |         var code = inverseEncoding[i]; | ||||||
|  |         if (!code || isSpecialUnicode(code)) { | ||||||
|           unassignedUnicodeItems.push(i); |           unassignedUnicodeItems.push(i); | ||||||
|  |           continue; | ||||||
|         } |         } | ||||||
|  |         charstrings.push({ | ||||||
|  |           unicode: code, | ||||||
|  |           code: code, | ||||||
|  |           gid: i, | ||||||
|  |           glyph: glyph | ||||||
|  |         }); | ||||||
|  |         unicodeUsed[code] = true; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var nextUnusedUnicode = 0x21; |       var nextUnusedUnicode = kCmapGlyphOffset; | ||||||
|       for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; ++j) { |       for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; ++j) { | ||||||
|         var i = unassignedUnicodeItems[j]; |         var i = unassignedUnicodeItems[j]; | ||||||
|         // giving unicode value anyway
 |         // giving unicode value anyway
 | ||||||
|         while (unicodeUsed[nextUnusedUnicode]) |         while (nextUnusedUnicode in unicodeUsed) | ||||||
|           nextUnusedUnicode++; |           nextUnusedUnicode++; | ||||||
|         var code = nextUnusedUnicode++; |         var unicode = nextUnusedUnicode++; | ||||||
|         charstrings.push({ |         charstrings.push({ | ||||||
|           unicode: adaptUnicode(code), |           unicode: unicode, | ||||||
|           code: code, |           code: inverseEncoding[i] || 0, | ||||||
|           gid: i, |           gid: i, | ||||||
|           glyph: charsets[i] |           glyph: charsets[i] | ||||||
|         }); |         }); | ||||||
| @ -3563,6 +3730,6 @@ var Type2CFF = (function type2CFF() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Type2CFF; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										555
									
								
								src/function.js
									
									
									
									
									
								
							
							
						
						
									
										555
									
								
								src/function.js
									
									
									
									
									
								
							| @ -3,7 +3,7 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var PDFFunction = (function pdfFunction() { | var PDFFunction = (function PDFFunctionClosure() { | ||||||
|   var CONSTRUCT_SAMPLED = 0; |   var CONSTRUCT_SAMPLED = 0; | ||||||
|   var CONSTRUCT_INTERPOLATED = 2; |   var CONSTRUCT_INTERPOLATED = 2; | ||||||
|   var CONSTRUCT_STICHED = 3; |   var CONSTRUCT_STICHED = 3; | ||||||
| @ -270,7 +270,6 @@ var PDFFunction = (function pdfFunction() { | |||||||
| 
 | 
 | ||||||
|     constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) { |     constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) { | ||||||
|       var domain = dict.get('Domain'); |       var domain = dict.get('Domain'); | ||||||
|       var range = dict.get('Range'); |  | ||||||
| 
 | 
 | ||||||
|       if (!domain) |       if (!domain) | ||||||
|         error('No domain'); |         error('No domain'); | ||||||
| @ -279,13 +278,13 @@ var PDFFunction = (function pdfFunction() { | |||||||
|       if (inputSize != 1) |       if (inputSize != 1) | ||||||
|         error('Bad domain for stiched function'); |         error('Bad domain for stiched function'); | ||||||
| 
 | 
 | ||||||
|       var fnRefs = dict.get('Functions'); |       var fnRefs = xref.fetchIfRef(dict.get('Functions')); | ||||||
|       var fns = []; |       var fns = []; | ||||||
|       for (var i = 0, ii = fnRefs.length; i < ii; ++i) |       for (var i = 0, ii = fnRefs.length; i < ii; ++i) | ||||||
|         fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i]))); |         fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i]))); | ||||||
| 
 | 
 | ||||||
|       var bounds = dict.get('Bounds'); |       var bounds = xref.fetchIfRef(dict.get('Bounds')); | ||||||
|       var encode = dict.get('Encode'); |       var encode = xref.fetchIfRef(dict.get('Encode')); | ||||||
| 
 | 
 | ||||||
|       return [CONSTRUCT_STICHED, domain, bounds, encode, fns]; |       return [CONSTRUCT_STICHED, domain, bounds, encode, fns]; | ||||||
|     }, |     }, | ||||||
| @ -336,16 +335,550 @@ var PDFFunction = (function pdfFunction() { | |||||||
|       }; |       }; | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     constructPostScript: function pdfFunctionConstructPostScript() { |     constructPostScript: function pdfFunctionConstructPostScript(fn, dict, | ||||||
|       return [CONSTRUCT_POSTSCRIPT]; |                                                                   xref) { | ||||||
|  |       var domain = dict.get('Domain'); | ||||||
|  |       var range = dict.get('Range'); | ||||||
|  | 
 | ||||||
|  |       if (!domain) | ||||||
|  |         error('No domain.'); | ||||||
|  | 
 | ||||||
|  |       if (!range) | ||||||
|  |         error('No range.'); | ||||||
|  | 
 | ||||||
|  |       var lexer = new PostScriptLexer(fn); | ||||||
|  |       var parser = new PostScriptParser(lexer); | ||||||
|  |       var code = parser.parse(); | ||||||
|  | 
 | ||||||
|  |       return [CONSTRUCT_POSTSCRIPT, domain, range, code]; | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     constructPostScriptFromIR: function pdfFunctionConstructPostScriptFromIR() { |     constructPostScriptFromIR: | ||||||
|       TODO('unhandled type of function'); |                           function pdfFunctionConstructPostScriptFromIR(IR) { | ||||||
|       return function constructPostScriptFromIRResult() { |       var domain = IR[1]; | ||||||
|         return [255, 105, 180]; |       var range = IR[2]; | ||||||
|  |       var code = IR[3]; | ||||||
|  |       var numOutputs = range.length / 2; | ||||||
|  |       var evaluator = new PostScriptEvaluator(code); | ||||||
|  |       // Cache the values for a big speed up, the cache size is limited though
 | ||||||
|  |       // since the number of possible values can be huge from a PS function.
 | ||||||
|  |       var cache = new FunctionCache(); | ||||||
|  |       return function constructPostScriptFromIRResult(args) { | ||||||
|  |         var initialStack = []; | ||||||
|  |         for (var i = 0, ii = (domain.length / 2); i < ii; ++i) { | ||||||
|  |           initialStack.push(args[i]); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var key = initialStack.join('_'); | ||||||
|  |         if (cache.has(key)) | ||||||
|  |           return cache.get(key); | ||||||
|  | 
 | ||||||
|  |         var stack = evaluator.execute(initialStack); | ||||||
|  |         var transformed = new Array(numOutputs); | ||||||
|  |         for (i = numOutputs - 1; i >= 0; --i) { | ||||||
|  |           var out = stack.pop(); | ||||||
|  |           var rangeIndex = 2 * i; | ||||||
|  |           if (out < range[rangeIndex]) | ||||||
|  |             out = range[rangeIndex]; | ||||||
|  |           else if (out > range[rangeIndex + 1]) | ||||||
|  |             out = range[rangeIndex + 1]; | ||||||
|  |           transformed[i] = out; | ||||||
|  |         } | ||||||
|  |         cache.set(key, transformed); | ||||||
|  |         return transformed; | ||||||
|       }; |       }; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | var FunctionCache = (function FunctionCacheClosure() { | ||||||
|  |   // Of 10 PDF's with type4 functions the maxium number of distinct values seen
 | ||||||
|  |   // was 256. This still may need some tweaking in the future though.
 | ||||||
|  |   var MAX_CACHE_SIZE = 1024; | ||||||
|  |   function FunctionCache() { | ||||||
|  |     this.cache = {}; | ||||||
|  |     this.total = 0; | ||||||
|  |   } | ||||||
|  |   FunctionCache.prototype = { | ||||||
|  |     has: function has(key) { | ||||||
|  |       return key in this.cache; | ||||||
|  |     }, | ||||||
|  |     get: function get(key) { | ||||||
|  |       return this.cache[key]; | ||||||
|  |     }, | ||||||
|  |     set: function set(key, value) { | ||||||
|  |       if (this.total < MAX_CACHE_SIZE) { | ||||||
|  |         this.cache[key] = value; | ||||||
|  |         this.total++; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   return FunctionCache; | ||||||
|  | })(); | ||||||
|  | 
 | ||||||
|  | var PostScriptStack = (function PostScriptStackClosure() { | ||||||
|  |   var MAX_STACK_SIZE = 100; | ||||||
|  |   function PostScriptStack(initialStack) { | ||||||
|  |     this.stack = initialStack || []; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   PostScriptStack.prototype = { | ||||||
|  |     push: function push(value) { | ||||||
|  |       if (this.stack.length >= MAX_STACK_SIZE) | ||||||
|  |         error('PostScript function stack overflow.'); | ||||||
|  |       this.stack.push(value); | ||||||
|  |     }, | ||||||
|  |     pop: function pop() { | ||||||
|  |       if (this.stack.length <= 0) | ||||||
|  |         error('PostScript function stack underflow.'); | ||||||
|  |       return this.stack.pop(); | ||||||
|  |     }, | ||||||
|  |     copy: function copy(n) { | ||||||
|  |       if (this.stack.length + n >= MAX_STACK_SIZE) | ||||||
|  |         error('PostScript function stack overflow.'); | ||||||
|  |       var stack = this.stack; | ||||||
|  |       for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++) | ||||||
|  |         stack.push(stack[i]); | ||||||
|  |     }, | ||||||
|  |     index: function index(n) { | ||||||
|  |       this.push(this.stack[this.stack.length - n - 1]); | ||||||
|  |     }, | ||||||
|  |     // rotate the last n stack elements p times
 | ||||||
|  |     roll: function roll(n, p) { | ||||||
|  |       var stack = this.stack; | ||||||
|  |       var l = stack.length - n; | ||||||
|  |       var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t; | ||||||
|  |       for (i = l, j = r; i < j; i++, j--) { | ||||||
|  |         t = stack[i]; stack[i] = stack[j]; stack[j] = t; | ||||||
|  |       } | ||||||
|  |       for (i = l, j = c - 1; i < j; i++, j--) { | ||||||
|  |         t = stack[i]; stack[i] = stack[j]; stack[j] = t; | ||||||
|  |       } | ||||||
|  |       for (i = c, j = r; i < j; i++, j--) { | ||||||
|  |         t = stack[i]; stack[i] = stack[j]; stack[j] = t; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   return PostScriptStack; | ||||||
|  | })(); | ||||||
|  | var PostScriptEvaluator = (function PostScriptEvaluatorClosure() { | ||||||
|  |   function PostScriptEvaluator(operators, operands) { | ||||||
|  |     this.operators = operators; | ||||||
|  |     this.operands = operands; | ||||||
|  |   } | ||||||
|  |   PostScriptEvaluator.prototype = { | ||||||
|  |     execute: function execute(initialStack) { | ||||||
|  |       var stack = new PostScriptStack(initialStack); | ||||||
|  |       var counter = 0; | ||||||
|  |       var operators = this.operators; | ||||||
|  |       var length = operators.length; | ||||||
|  |       var operator, a, b; | ||||||
|  |       while (counter < length) { | ||||||
|  |         operator = operators[counter++]; | ||||||
|  |         if (typeof operator == 'number') { | ||||||
|  |           // Operator is really an operand and should be pushed to the stack.
 | ||||||
|  |           stack.push(operator); | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  |         switch (operator) { | ||||||
|  |           // non standard ps operators
 | ||||||
|  |           case 'jz': // jump if false
 | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             if (!a) | ||||||
|  |               counter = b; | ||||||
|  |             break; | ||||||
|  |           case 'j': // jump
 | ||||||
|  |             a = stack.pop(); | ||||||
|  |             counter = a; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |           // all ps operators in alphabetical order (excluding if/ifelse)
 | ||||||
|  |           case 'abs': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.abs(a)); | ||||||
|  |             break; | ||||||
|  |           case 'add': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a + b); | ||||||
|  |             break; | ||||||
|  |           case 'and': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             if (isBool(a) && isBool(b)) | ||||||
|  |               stack.push(a && b); | ||||||
|  |             else | ||||||
|  |               stack.push(a & b); | ||||||
|  |             break; | ||||||
|  |           case 'atan': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.atan(a)); | ||||||
|  |             break; | ||||||
|  |           case 'bitshift': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             if (a > 0) | ||||||
|  |               stack.push(a << b); | ||||||
|  |             else | ||||||
|  |               stack.push(a >> b); | ||||||
|  |             break; | ||||||
|  |           case 'ceiling': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.ceil(a)); | ||||||
|  |             break; | ||||||
|  |           case 'copy': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.copy(a); | ||||||
|  |             break; | ||||||
|  |           case 'cos': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.cos(a)); | ||||||
|  |             break; | ||||||
|  |           case 'cvi': | ||||||
|  |             a = stack.pop() | 0; | ||||||
|  |             stack.push(a); | ||||||
|  |             break; | ||||||
|  |           case 'cvr': | ||||||
|  |             // noop
 | ||||||
|  |             break; | ||||||
|  |           case 'div': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a / b); | ||||||
|  |             break; | ||||||
|  |           case 'dup': | ||||||
|  |             stack.copy(1); | ||||||
|  |             break; | ||||||
|  |           case 'eq': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a == b); | ||||||
|  |             break; | ||||||
|  |           case 'exch': | ||||||
|  |             stack.roll(2, 1); | ||||||
|  |             break; | ||||||
|  |           case 'exp': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.pow(a, b)); | ||||||
|  |             break; | ||||||
|  |           case 'false': | ||||||
|  |             stack.push(false); | ||||||
|  |             break; | ||||||
|  |           case 'floor': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.floor(a)); | ||||||
|  |             break; | ||||||
|  |           case 'ge': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a >= b); | ||||||
|  |             break; | ||||||
|  |           case 'gt': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a > b); | ||||||
|  |             break; | ||||||
|  |           case 'idiv': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push((a / b) | 0); | ||||||
|  |             break; | ||||||
|  |           case 'index': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.index(a); | ||||||
|  |             break; | ||||||
|  |           case 'le': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a <= b); | ||||||
|  |             break; | ||||||
|  |           case 'ln': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.log(a)); | ||||||
|  |             break; | ||||||
|  |           case 'log': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.log(a) / Math.LN10); | ||||||
|  |             break; | ||||||
|  |           case 'lt': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a < b); | ||||||
|  |             break; | ||||||
|  |           case 'mod': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a % b); | ||||||
|  |             break; | ||||||
|  |           case 'mul': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a * b); | ||||||
|  |             break; | ||||||
|  |           case 'ne': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a != b); | ||||||
|  |             break; | ||||||
|  |           case 'neg': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(-b); | ||||||
|  |             break; | ||||||
|  |           case 'not': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             if (isBool(a) && isBool(b)) | ||||||
|  |               stack.push(a && b); | ||||||
|  |             else | ||||||
|  |               stack.push(a & b); | ||||||
|  |             break; | ||||||
|  |           case 'or': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             if (isBool(a) && isBool(b)) | ||||||
|  |               stack.push(a || b); | ||||||
|  |             else | ||||||
|  |               stack.push(a | b); | ||||||
|  |             break; | ||||||
|  |           case 'pop': | ||||||
|  |             stack.pop(); | ||||||
|  |             break; | ||||||
|  |           case 'roll': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.roll(a, b); | ||||||
|  |             break; | ||||||
|  |           case 'round': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.round(a)); | ||||||
|  |             break; | ||||||
|  |           case 'sin': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.sin(a)); | ||||||
|  |             break; | ||||||
|  |           case 'sqrt': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(Math.sqrt(a)); | ||||||
|  |             break; | ||||||
|  |           case 'sub': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             stack.push(a - b); | ||||||
|  |             break; | ||||||
|  |           case 'true': | ||||||
|  |             stack.push(true); | ||||||
|  |             break; | ||||||
|  |           case 'truncate': | ||||||
|  |             a = stack.pop(); | ||||||
|  |             a = a < 0 ? Math.ceil(a) : Math.floor(a); | ||||||
|  |             stack.push(a); | ||||||
|  |             break; | ||||||
|  |           case 'xor': | ||||||
|  |             b = stack.pop(); | ||||||
|  |             a = stack.pop(); | ||||||
|  |             if (isBool(a) && isBool(b)) | ||||||
|  |               stack.push(a != b); | ||||||
|  |             else | ||||||
|  |               stack.push(a ^ b); | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             error('Unknown operator ' + operator); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return stack.stack; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   return PostScriptEvaluator; | ||||||
|  | })(); | ||||||
|  | 
 | ||||||
|  | var PostScriptParser = (function PostScriptParserClosure() { | ||||||
|  |   function PostScriptParser(lexer) { | ||||||
|  |     this.lexer = lexer; | ||||||
|  |     this.operators = []; | ||||||
|  |     this.token; | ||||||
|  |     this.prev; | ||||||
|  |   } | ||||||
|  |   PostScriptParser.prototype = { | ||||||
|  |     nextToken: function nextToken() { | ||||||
|  |       this.prev = this.token; | ||||||
|  |       this.token = this.lexer.getToken(); | ||||||
|  |     }, | ||||||
|  |     accept: function accept(type) { | ||||||
|  |       if (this.token.type == type) { | ||||||
|  |         this.nextToken(); | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |       return false; | ||||||
|  |     }, | ||||||
|  |     expect: function expect(type) { | ||||||
|  |       if (this.accept(type)) | ||||||
|  |         return true; | ||||||
|  |       error('Unexpected symbol: found ' + this.token.type + ' expected ' + | ||||||
|  |             type + '.'); | ||||||
|  |     }, | ||||||
|  |     parse: function parse() { | ||||||
|  |       this.nextToken(); | ||||||
|  |       this.expect(PostScriptTokenTypes.LBRACE); | ||||||
|  |       this.parseBlock(); | ||||||
|  |       this.expect(PostScriptTokenTypes.RBRACE); | ||||||
|  |       return this.operators; | ||||||
|  |     }, | ||||||
|  |     parseBlock: function parseBlock() { | ||||||
|  |       while (true) { | ||||||
|  |         if (this.accept(PostScriptTokenTypes.NUMBER)) { | ||||||
|  |           this.operators.push(this.prev.value); | ||||||
|  |         } else if (this.accept(PostScriptTokenTypes.OPERATOR)) { | ||||||
|  |           this.operators.push(this.prev.value); | ||||||
|  |         } else if (this.accept(PostScriptTokenTypes.LBRACE)) { | ||||||
|  |           this.parseCondition(); | ||||||
|  |         } else { | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     parseCondition: function parseCondition() { | ||||||
|  |       // Add two place holders that will be updated later
 | ||||||
|  |       var conditionLocation = this.operators.length; | ||||||
|  |       this.operators.push(null, null); | ||||||
|  | 
 | ||||||
|  |       this.parseBlock(); | ||||||
|  |       this.expect(PostScriptTokenTypes.RBRACE); | ||||||
|  |       if (this.accept(PostScriptTokenTypes.IF)) { | ||||||
|  |         // The true block is right after the 'if' so it just falls through on
 | ||||||
|  |         // true else it jumps and skips the true block.
 | ||||||
|  |         this.operators[conditionLocation] = this.operators.length; | ||||||
|  |         this.operators[conditionLocation + 1] = 'jz'; | ||||||
|  |       } else if (this.accept(PostScriptTokenTypes.LBRACE)) { | ||||||
|  |         var jumpLocation = this.operators.length; | ||||||
|  |         this.operators.push(null, null); | ||||||
|  |         var endOfTrue = this.operators.length; | ||||||
|  |         this.parseBlock(); | ||||||
|  |         this.expect(PostScriptTokenTypes.RBRACE); | ||||||
|  |         this.expect(PostScriptTokenTypes.IFELSE); | ||||||
|  |         // The jump is added at the end of the true block to skip the false
 | ||||||
|  |         // block.
 | ||||||
|  |         this.operators[jumpLocation] = this.operators.length; | ||||||
|  |         this.operators[jumpLocation + 1] = 'j'; | ||||||
|  | 
 | ||||||
|  |         this.operators[conditionLocation] = endOfTrue; | ||||||
|  |         this.operators[conditionLocation + 1] = 'jz'; | ||||||
|  |       } else { | ||||||
|  |         error('PS Function: error parsing conditional.'); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   return PostScriptParser; | ||||||
|  | })(); | ||||||
|  | 
 | ||||||
|  | var PostScriptTokenTypes = { | ||||||
|  |   LBRACE: 0, | ||||||
|  |   RBRACE: 1, | ||||||
|  |   NUMBER: 2, | ||||||
|  |   OPERATOR: 3, | ||||||
|  |   IF: 4, | ||||||
|  |   IFELSE: 5 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | var PostScriptToken = (function PostScriptTokenClosure() { | ||||||
|  |   function PostScriptToken(type, value) { | ||||||
|  |     this.type = type; | ||||||
|  |     this.value = value; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var opCache = {}; | ||||||
|  | 
 | ||||||
|  |   PostScriptToken.getOperator = function getOperator(op) { | ||||||
|  |     var opValue = opCache[op]; | ||||||
|  |     if (opValue) | ||||||
|  |       return opValue; | ||||||
|  | 
 | ||||||
|  |     return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE, | ||||||
|  |                                                 '{'); | ||||||
|  |   PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE, | ||||||
|  |                                                 '}'); | ||||||
|  |   PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF'); | ||||||
|  |   PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE, | ||||||
|  |                                                 'IFELSE'); | ||||||
|  |   return PostScriptToken; | ||||||
|  | })(); | ||||||
|  | 
 | ||||||
|  | var PostScriptLexer = (function PostScriptLexerClosure() { | ||||||
|  |   function PostScriptLexer(stream) { | ||||||
|  |     this.stream = stream; | ||||||
|  |   } | ||||||
|  |   PostScriptLexer.prototype = { | ||||||
|  |     getToken: function getToken() { | ||||||
|  |       var s = ''; | ||||||
|  |       var ch; | ||||||
|  |       var comment = false; | ||||||
|  |       var stream = this.stream; | ||||||
|  | 
 | ||||||
|  |       // skip comments
 | ||||||
|  |       while (true) { | ||||||
|  |         if (!(ch = stream.getChar())) | ||||||
|  |           return EOF; | ||||||
|  | 
 | ||||||
|  |         if (comment) { | ||||||
|  |           if (ch == '\x0a' || ch == '\x0d') | ||||||
|  |             comment = false; | ||||||
|  |         } else if (ch == '%') { | ||||||
|  |           comment = true; | ||||||
|  |         } else if (!Lexer.isSpace(ch)) { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       switch (ch) { | ||||||
|  |         case '0': case '1': case '2': case '3': case '4': | ||||||
|  |         case '5': case '6': case '7': case '8': case '9': | ||||||
|  |         case '+': case '-': case '.': | ||||||
|  |           return new PostScriptToken(PostScriptTokenTypes.NUMBER, | ||||||
|  |                                       this.getNumber(ch)); | ||||||
|  |         case '{': | ||||||
|  |           return PostScriptToken.LBRACE; | ||||||
|  |         case '}': | ||||||
|  |           return PostScriptToken.RBRACE; | ||||||
|  |       } | ||||||
|  |       // operator
 | ||||||
|  |       var str = ch.toLowerCase(); | ||||||
|  |       while (true) { | ||||||
|  |         ch = stream.lookChar().toLowerCase(); | ||||||
|  |         if (ch >= 'a' && ch <= 'z') | ||||||
|  |           str += ch; | ||||||
|  |         else | ||||||
|  |           break; | ||||||
|  |         stream.skip(); | ||||||
|  |       } | ||||||
|  |       switch (str) { | ||||||
|  |         case 'if': | ||||||
|  |           return PostScriptToken.IF; | ||||||
|  |         case 'ifelse': | ||||||
|  |           return PostScriptToken.IFELSE; | ||||||
|  |         default: | ||||||
|  |           return PostScriptToken.getOperator(str); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     getNumber: function getNumber(ch) { | ||||||
|  |       var str = ch; | ||||||
|  |       var stream = this.stream; | ||||||
|  |       while (true) { | ||||||
|  |         ch = stream.lookChar(); | ||||||
|  |         if ((ch >= '0' && ch <= '9') || ch == '-' || ch == '.') | ||||||
|  |           str += ch; | ||||||
|  |         else | ||||||
|  |           break; | ||||||
|  |         stream.skip(); | ||||||
|  |       } | ||||||
|  |       var value = parseFloat(str); | ||||||
|  |       if (isNaN(value)) | ||||||
|  |         error('Invalid floating point number: ' + value); | ||||||
|  |       return value; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   return PostScriptLexer; | ||||||
|  | })(); | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -4287,6 +4287,7 @@ var GlyphsUnicode = { | |||||||
|   zretroflexhook: 0x0290, |   zretroflexhook: 0x0290, | ||||||
|   zstroke: 0x01B6, |   zstroke: 0x01B6, | ||||||
|   zuhiragana: 0x305A, |   zuhiragana: 0x305A, | ||||||
|   zukatakana: 0x30BA |   zukatakana: 0x30BA, | ||||||
|  |   '.notdef': 0x0000 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										262
									
								
								src/image.js
									
									
									
									
									
								
							
							
						
						
									
										262
									
								
								src/image.js
									
									
									
									
									
								
							| @ -3,8 +3,37 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var PDFImage = (function pdfImage() { | var PDFImage = (function PDFImageClosure() { | ||||||
|   function constructor(xref, res, image, inline) { |   /** | ||||||
|  |    * Decode the image in the main thread if it supported. Resovles the promise | ||||||
|  |    * when the image data is ready. | ||||||
|  |    */ | ||||||
|  |   function handleImageData(handler, xref, res, image, promise) { | ||||||
|  |     if (image instanceof JpegStream && image.isNative) { | ||||||
|  |       // For natively supported jpegs send them to the main thread for decoding.
 | ||||||
|  |       var dict = image.dict; | ||||||
|  |       var colorSpace = dict.get('ColorSpace', 'CS'); | ||||||
|  |       colorSpace = ColorSpace.parse(colorSpace, xref, res); | ||||||
|  |       var numComps = colorSpace.numComps; | ||||||
|  |       handler.send('jpeg_decode', [image.getIR(), numComps], function(message) { | ||||||
|  |         var data = message.data; | ||||||
|  |         var stream = new Stream(data, 0, data.length, image.dict); | ||||||
|  |         promise.resolve(stream); | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       promise.resolve(image); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   /** | ||||||
|  |    * Decode and clamp a value. The formula is different from the spec because we | ||||||
|  |    * don't decode to float range [0,1], we decode it in the [0,max] range. | ||||||
|  |    */ | ||||||
|  |   function decodeAndClamp(value, addend, coefficient, max) { | ||||||
|  |     value = addend + value * coefficient; | ||||||
|  |     // Clamp the value to the range
 | ||||||
|  |     return value < 0 ? 0 : value > max ? max : value; | ||||||
|  |   } | ||||||
|  |   function PDFImage(xref, res, image, inline, smask) { | ||||||
|     this.image = image; |     this.image = image; | ||||||
|     if (image.getParams) { |     if (image.getParams) { | ||||||
|       // JPX/JPEG2000 streams directly contain bits per component
 |       // JPX/JPEG2000 streams directly contain bits per component
 | ||||||
| @ -49,34 +78,142 @@ var PDFImage = (function pdfImage() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.decode = dict.get('Decode', 'D'); |     this.decode = dict.get('Decode', 'D'); | ||||||
|  |     this.needsDecode = false; | ||||||
|  |     if (this.decode && this.colorSpace && | ||||||
|  |         !this.colorSpace.isDefaultDecode(this.decode)) { | ||||||
|  |       this.needsDecode = true; | ||||||
|  |       // Do some preprocessing to avoid more math.
 | ||||||
|  |       var max = (1 << bitsPerComponent) - 1; | ||||||
|  |       this.decodeCoefficients = []; | ||||||
|  |       this.decodeAddends = []; | ||||||
|  |       for (var i = 0, j = 0; i < this.decode.length; i += 2, ++j) { | ||||||
|  |         var dmin = this.decode[i]; | ||||||
|  |         var dmax = this.decode[i + 1]; | ||||||
|  |         this.decodeCoefficients[j] = dmax - dmin; | ||||||
|  |         this.decodeAddends[j] = max * dmin; | ||||||
|  |       } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     var mask = xref.fetchIfRef(dict.get('Mask')); |     var mask = xref.fetchIfRef(dict.get('Mask')); | ||||||
|     var smask = xref.fetchIfRef(dict.get('SMask')); |  | ||||||
| 
 | 
 | ||||||
|     if (mask) { |     if (mask) { | ||||||
|       TODO('masked images'); |       TODO('masked images'); | ||||||
|     } else if (smask) { |     } else if (smask) { | ||||||
|       this.smask = new PDFImage(xref, res, smask); |       this.smask = new PDFImage(xref, res, smask, false); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |   /** | ||||||
|  |    * Handles processing of image data and calls the callback with an argument | ||||||
|  |    * of a PDFImage when the image is ready to be used. | ||||||
|  |    */ | ||||||
|  |   PDFImage.buildImage = function buildImage(callback, handler, xref, res, | ||||||
|  |                                                image, inline) { | ||||||
|  |     var imageDataPromise = new Promise(); | ||||||
|  |     var smaskPromise = new Promise(); | ||||||
|  |     // The image data and smask data may not be ready yet, wait till both are
 | ||||||
|  |     // resolved.
 | ||||||
|  |     Promise.all([imageDataPromise, smaskPromise]).then(function(results) { | ||||||
|  |       var imageData = results[0], smaskData = results[1]; | ||||||
|  |       var image = new PDFImage(xref, res, imageData, inline, smaskData); | ||||||
|  |       callback(image); | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |     handleImageData(handler, xref, res, image, imageDataPromise); | ||||||
|     getComponents: function getComponents(buffer, decodeMap) { | 
 | ||||||
|  |     var smask = xref.fetchIfRef(image.dict.get('SMask')); | ||||||
|  |     if (smask) | ||||||
|  |       handleImageData(handler, xref, res, smask, smaskPromise); | ||||||
|  |     else | ||||||
|  |       smaskPromise.resolve(null); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Resize an image using the nearest neighbor algorithm.  Currently only | ||||||
|  |    * supports one and three component images. | ||||||
|  |    * @param {TypedArray} pixels The original image with one component. | ||||||
|  |    * @param {Number} bpc Number of bits per component. | ||||||
|  |    * @param {Number} components Number of color components, 1 or 3 is supported. | ||||||
|  |    * @param {Number} w1 Original width. | ||||||
|  |    * @param {Number} h1 Original height. | ||||||
|  |    * @param {Number} w2 New width. | ||||||
|  |    * @param {Number} h2 New height. | ||||||
|  |    * @return {TypedArray} Resized image data. | ||||||
|  |    */ | ||||||
|  |   PDFImage.resize = function resize(pixels, bpc, components, w1, h1, w2, h2) { | ||||||
|  |     var length = w2 * h2 * components; | ||||||
|  |     var temp = bpc <= 8 ? new Uint8Array(length) : | ||||||
|  |         bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length); | ||||||
|  |     var xRatio = w1 / w2; | ||||||
|  |     var yRatio = h1 / h2; | ||||||
|  |     var px, py, newIndex, oldIndex; | ||||||
|  |     for (var i = 0; i < h2; i++) { | ||||||
|  |       for (var j = 0; j < w2; j++) { | ||||||
|  |         px = Math.floor(j * xRatio); | ||||||
|  |         py = Math.floor(i * yRatio); | ||||||
|  |         newIndex = (i * w2) + j; | ||||||
|  |         oldIndex = ((py * w1) + px); | ||||||
|  |         if (components === 1) { | ||||||
|  |           temp[newIndex] = pixels[oldIndex]; | ||||||
|  |         } else if (components === 3) { | ||||||
|  |           newIndex *= 3; | ||||||
|  |           oldIndex *= 3; | ||||||
|  |           temp[newIndex] = pixels[oldIndex]; | ||||||
|  |           temp[newIndex + 1] = pixels[oldIndex + 1]; | ||||||
|  |           temp[newIndex + 2] = pixels[oldIndex + 2]; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return temp; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   PDFImage.prototype = { | ||||||
|  |     get drawWidth() { | ||||||
|  |       if (!this.smask) | ||||||
|  |         return this.width; | ||||||
|  |       return Math.max(this.width, this.smask.width); | ||||||
|  |     }, | ||||||
|  |     get drawHeight() { | ||||||
|  |       if (!this.smask) | ||||||
|  |         return this.height; | ||||||
|  |       return Math.max(this.height, this.smask.height); | ||||||
|  |     }, | ||||||
|  |     getComponents: function getComponents(buffer) { | ||||||
|       var bpc = this.bpc; |       var bpc = this.bpc; | ||||||
|       if (bpc == 8) |       var needsDecode = this.needsDecode; | ||||||
|  |       var decodeMap = this.decode; | ||||||
|  | 
 | ||||||
|  |       // This image doesn't require any extra work.
 | ||||||
|  |       if (bpc == 8 && !needsDecode) | ||||||
|         return buffer; |         return buffer; | ||||||
| 
 | 
 | ||||||
|  |       var bufferLength = buffer.length; | ||||||
|       var width = this.width; |       var width = this.width; | ||||||
|       var height = this.height; |       var height = this.height; | ||||||
|       var numComps = this.numComps; |       var numComps = this.numComps; | ||||||
| 
 | 
 | ||||||
|       var length = width * height; |       var length = width * height * numComps; | ||||||
|       var bufferPos = 0; |       var bufferPos = 0; | ||||||
|       var output = bpc <= 8 ? new Uint8Array(length) : |       var output = bpc <= 8 ? new Uint8Array(length) : | ||||||
|         bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length); |         bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length); | ||||||
|       var rowComps = width * numComps; |       var rowComps = width * numComps; | ||||||
|  |       var decodeAddends, decodeCoefficients; | ||||||
|  |       if (needsDecode) { | ||||||
|  |         decodeAddends = this.decodeAddends; | ||||||
|  |         decodeCoefficients = this.decodeCoefficients; | ||||||
|  |       } | ||||||
|  |       var max = (1 << bpc) - 1; | ||||||
| 
 | 
 | ||||||
|       if (bpc == 1) { |       if (bpc == 8) { | ||||||
|  |         // Optimization for reading 8 bpc images that have a decode.
 | ||||||
|  |         for (var i = 0, ii = length; i < ii; ++i) { | ||||||
|  |           var compIndex = i % numComps; | ||||||
|  |           var value = buffer[i]; | ||||||
|  |           value = decodeAndClamp(value, decodeAddends[compIndex], | ||||||
|  |                           decodeCoefficients[compIndex], max); | ||||||
|  |           output[i] = value; | ||||||
|  |         } | ||||||
|  |       } else if (bpc == 1) { | ||||||
|  |         // Optimization for reading 1 bpc images.
 | ||||||
|         var valueZero = 0, valueOne = 1; |         var valueZero = 0, valueOne = 1; | ||||||
|         if (decodeMap) { |         if (decodeMap) { | ||||||
|           valueZero = decodeMap[0] ? 1 : 0; |           valueZero = decodeMap[0] ? 1 : 0; | ||||||
| @ -101,8 +238,7 @@ var PDFImage = (function pdfImage() { | |||||||
|           output[i] = !(buf & mask) ? valueZero : valueOne; |           output[i] = !(buf & mask) ? valueZero : valueOne; | ||||||
|         } |         } | ||||||
|       } else { |       } else { | ||||||
|         if (decodeMap != null) |         // The general case that handles all other bpc values.
 | ||||||
|           TODO('interpolate component values'); |  | ||||||
|         var bits = 0, buf = 0; |         var bits = 0, buf = 0; | ||||||
|         for (var i = 0, ii = length; i < ii; ++i) { |         for (var i = 0, ii = length; i < ii; ++i) { | ||||||
|           if (i % rowComps == 0) { |           if (i % rowComps == 0) { | ||||||
| @ -116,41 +252,34 @@ var PDFImage = (function pdfImage() { | |||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           var remainingBits = bits - bpc; |           var remainingBits = bits - bpc; | ||||||
|           output[i] = buf >> remainingBits; |           var value = buf >> remainingBits; | ||||||
|  |           if (needsDecode) { | ||||||
|  |             var compIndex = i % numComps; | ||||||
|  |             value = decodeAndClamp(value, decodeAddends[compIndex], | ||||||
|  |                             decodeCoefficients[compIndex], max); | ||||||
|  |           } | ||||||
|  |           output[i] = value; | ||||||
|           buf = buf & ((1 << remainingBits) - 1); |           buf = buf & ((1 << remainingBits) - 1); | ||||||
|           bits = remainingBits; |           bits = remainingBits; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       return output; |       return output; | ||||||
|     }, |     }, | ||||||
|     getOpacity: function getOpacity() { |     getOpacity: function getOpacity(width, height) { | ||||||
|       var smask = this.smask; |       var smask = this.smask; | ||||||
|       var width = this.width; |       var originalWidth = this.width; | ||||||
|       var height = this.height; |       var originalHeight = this.height; | ||||||
|       var buf = new Uint8Array(width * height); |       var buf; | ||||||
| 
 | 
 | ||||||
|       if (smask) { |       if (smask) { | ||||||
|         if (smask.image.getImage) { |  | ||||||
|           // smask is a DOM image
 |  | ||||||
|           var tempCanvas = new ScratchCanvas(width, height); |  | ||||||
|           var tempCtx = tempCanvas.getContext('2d'); |  | ||||||
|           var domImage = smask.image.getImage(); |  | ||||||
|           tempCtx.drawImage(domImage, 0, 0, domImage.width, domImage.height, |  | ||||||
|             0, 0, width, height); |  | ||||||
|           var data = tempCtx.getImageData(0, 0, width, height).data; |  | ||||||
|           for (var i = 0, j = 0, ii = width * height; i < ii; ++i, j += 4) |  | ||||||
|             buf[i] = data[j]; // getting first component value
 |  | ||||||
|           return buf; |  | ||||||
|         } |  | ||||||
|         var sw = smask.width; |         var sw = smask.width; | ||||||
|         var sh = smask.height; |         var sh = smask.height; | ||||||
|         if (sw != this.width || sh != this.height) |         buf = new Uint8Array(sw * sh); | ||||||
|           error('smask dimensions do not match image dimensions: ' + sw + |  | ||||||
|                 ' != ' + this.width + ', ' + sh + ' != ' + this.height); |  | ||||||
| 
 |  | ||||||
|         smask.fillGrayBuffer(buf); |         smask.fillGrayBuffer(buf); | ||||||
|         return buf; |         if (sw != width || sh != height) | ||||||
|  |           buf = PDFImage.resize(buf, smask.bps, 1, sw, sh, width, height); | ||||||
|       } else { |       } else { | ||||||
|  |         buf = new Uint8Array(width * height); | ||||||
|         for (var i = 0, ii = width * height; i < ii; ++i) |         for (var i = 0, ii = width * height; i < ii; ++i) | ||||||
|           buf[i] = 255; |           buf[i] = 255; | ||||||
|       } |       } | ||||||
| @ -159,8 +288,7 @@ var PDFImage = (function pdfImage() { | |||||||
|     applyStencilMask: function applyStencilMask(buffer, inverseDecode) { |     applyStencilMask: function applyStencilMask(buffer, inverseDecode) { | ||||||
|       var width = this.width, height = this.height; |       var width = this.width, height = this.height; | ||||||
|       var bitStrideLength = (width + 7) >> 3; |       var bitStrideLength = (width + 7) >> 3; | ||||||
|       this.image.reset(); |       var imgArray = this.getImageBytes(bitStrideLength * height); | ||||||
|       var imgArray = this.image.getBytes(bitStrideLength * height); |  | ||||||
|       var imgArrayPos = 0; |       var imgArrayPos = 0; | ||||||
|       var i, j, mask, buf; |       var i, j, mask, buf; | ||||||
|       // removing making non-masked pixels transparent
 |       // removing making non-masked pixels transparent
 | ||||||
| @ -180,21 +308,23 @@ var PDFImage = (function pdfImage() { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     fillRgbaBuffer: function fillRgbaBuffer(buffer, decodeMap) { |     fillRgbaBuffer: function fillRgbaBuffer(buffer, width, height) { | ||||||
|       var numComps = this.numComps; |       var numComps = this.numComps; | ||||||
|       var width = this.width; |       var originalWidth = this.width; | ||||||
|       var height = this.height; |       var originalHeight = this.height; | ||||||
|       var bpc = this.bpc; |       var bpc = this.bpc; | ||||||
| 
 | 
 | ||||||
|       // rows start at byte boundary;
 |       // rows start at byte boundary;
 | ||||||
|       var rowBytes = (width * numComps * bpc + 7) >> 3; |       var rowBytes = (originalWidth * numComps * bpc + 7) >> 3; | ||||||
|       this.image.reset(); |       var imgArray = this.getImageBytes(originalHeight * rowBytes); | ||||||
|       var imgArray = this.image.getBytes(height * rowBytes); |  | ||||||
| 
 | 
 | ||||||
|       var comps = this.colorSpace.getRgbBuffer( |       var comps = this.colorSpace.getRgbBuffer( | ||||||
|         this.getComponents(imgArray, decodeMap), bpc); |         this.getComponents(imgArray), bpc); | ||||||
|  |       if (originalWidth != width || originalHeight != height) | ||||||
|  |         comps = PDFImage.resize(comps, this.bpc, 3, originalWidth, | ||||||
|  |                                 originalHeight, width, height); | ||||||
|       var compsPos = 0; |       var compsPos = 0; | ||||||
|       var opacity = this.getOpacity(); |       var opacity = this.getOpacity(width, height); | ||||||
|       var opacityPos = 0; |       var opacityPos = 0; | ||||||
|       var length = width * height * 4; |       var length = width * height * 4; | ||||||
| 
 | 
 | ||||||
| @ -216,42 +346,28 @@ var PDFImage = (function pdfImage() { | |||||||
| 
 | 
 | ||||||
|       // rows start at byte boundary;
 |       // rows start at byte boundary;
 | ||||||
|       var rowBytes = (width * numComps * bpc + 7) >> 3; |       var rowBytes = (width * numComps * bpc + 7) >> 3; | ||||||
|       this.image.reset(); |       var imgArray = this.getImageBytes(height * rowBytes); | ||||||
|       var imgArray = this.image.getBytes(height * rowBytes); |  | ||||||
| 
 | 
 | ||||||
|       var comps = this.getComponents(imgArray); |       var comps = this.getComponents(imgArray); | ||||||
|       var length = width * height; |       var length = width * height; | ||||||
| 
 |       // we aren't using a colorspace so we need to scale the value
 | ||||||
|  |       var scale = 255 / ((1 << bpc) - 1); | ||||||
|       for (var i = 0; i < length; ++i) |       for (var i = 0; i < length; ++i) | ||||||
|         buffer[i] = comps[i]; |         buffer[i] = (scale * comps[i]) | 0; | ||||||
|  |     }, | ||||||
|  |     getImageBytes: function getImageBytes(length) { | ||||||
|  |       this.image.reset(); | ||||||
|  |       return this.image.getBytes(length); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return PDFImage; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var JpegImageLoader = (function jpegImage() { | function loadJpegStream(id, imageData, objs) { | ||||||
|   function JpegImageLoader(objId, imageData, objs) { |   var img = new Image(); | ||||||
|     var src = 'data:image/jpeg;base64,' + window.btoa(imageData); |   img.onload = (function jpegImageLoaderOnload() { | ||||||
| 
 |     objs.resolve(id, img); | ||||||
|     var img = new Image(); |   }); | ||||||
|     img.onload = (function jpegImageLoaderOnload() { |   img.src = 'data:image/jpeg;base64,' + window.btoa(imageData); | ||||||
|       this.loaded = true; | } | ||||||
| 
 |  | ||||||
|       objs.resolve(objId, this); |  | ||||||
| 
 |  | ||||||
|       if (this.onLoad) |  | ||||||
|         this.onLoad(); |  | ||||||
|     }).bind(this); |  | ||||||
|     img.src = src; |  | ||||||
|     this.domImage = img; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   JpegImageLoader.prototype = { |  | ||||||
|     getImage: function jpegImageLoaderGetImage() { |  | ||||||
|       return this.domImage; |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   return JpegImageLoader; |  | ||||||
| })(); |  | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,9 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
|  | // The Metrics object contains glyph widths (in glyph space units).
 | ||||||
|  | // As per PDF spec, for most fonts (Type 3 being an exception) a glyph
 | ||||||
|  | // space unit corresponds to 1/1000th of text space unit.
 | ||||||
| var Metrics = { | var Metrics = { | ||||||
|   'Courier': 600, |   'Courier': 600, | ||||||
|   'Courier-Bold': 600, |   'Courier-Bold': 600, | ||||||
|  | |||||||
							
								
								
									
										106
									
								
								src/obj.js
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/obj.js
									
									
									
									
									
								
							| @ -3,34 +3,42 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var Name = (function nameName() { | var Name = (function NameClosure() { | ||||||
|   function constructor(name) { |   function Name(name) { | ||||||
|     this.name = name; |     this.name = name; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Name.prototype = {}; | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Name; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var Cmd = (function cmdCmd() { | var Cmd = (function CmdClosure() { | ||||||
|   function constructor(cmd) { |   function Cmd(cmd) { | ||||||
|     this.cmd = cmd; |     this.cmd = cmd; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Cmd.prototype = {}; | ||||||
|  | 
 | ||||||
|  |   var cmdCache = {}; | ||||||
|  | 
 | ||||||
|  |   Cmd.get = function cmdGet(cmd) { | ||||||
|  |     var cmdValue = cmdCache[cmd]; | ||||||
|  |     if (cmdValue) | ||||||
|  |       return cmdValue; | ||||||
|  | 
 | ||||||
|  |     return cmdCache[cmd] = new Cmd(cmd); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Cmd; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var Dict = (function dictDict() { | var Dict = (function DictClosure() { | ||||||
|   function constructor() { |   function Dict() { | ||||||
|     this.map = Object.create(null); |     this.map = Object.create(null); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Dict.prototype = { | ||||||
|     get: function dictGet(key1, key2, key3) { |     get: function dictGet(key1, key2, key3) { | ||||||
|       var value; |       var value; | ||||||
|       if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map || |       if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map || | ||||||
| @ -60,29 +68,28 @@ var Dict = (function dictDict() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Dict; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var Ref = (function refRef() { | var Ref = (function RefClosure() { | ||||||
|   function constructor(num, gen) { |   function Ref(num, gen) { | ||||||
|     this.num = num; |     this.num = num; | ||||||
|     this.gen = gen; |     this.gen = gen; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Ref.prototype = {}; | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Ref; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| // The reference is identified by number and generation,
 | // The reference is identified by number and generation,
 | ||||||
| // this structure stores only one instance of the reference.
 | // this structure stores only one instance of the reference.
 | ||||||
| var RefSet = (function refSet() { | var RefSet = (function RefSetClosure() { | ||||||
|   function constructor() { |   function RefSet() { | ||||||
|     this.dict = {}; |     this.dict = {}; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   RefSet.prototype = { | ||||||
|     has: function refSetHas(ref) { |     has: function refSetHas(ref) { | ||||||
|       return !!this.dict['R' + ref.num + '.' + ref.gen]; |       return !!this.dict['R' + ref.num + '.' + ref.gen]; | ||||||
|     }, |     }, | ||||||
| @ -92,18 +99,18 @@ var RefSet = (function refSet() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return RefSet; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var Catalog = (function catalogCatalog() { | var Catalog = (function CatalogClosure() { | ||||||
|   function constructor(xref) { |   function Catalog(xref) { | ||||||
|     this.xref = xref; |     this.xref = xref; | ||||||
|     var obj = xref.getCatalogObj(); |     var obj = xref.getCatalogObj(); | ||||||
|     assertWellFormed(isDict(obj), 'catalog object is not a dictionary'); |     assertWellFormed(isDict(obj), 'catalog object is not a dictionary'); | ||||||
|     this.catDict = obj; |     this.catDict = obj; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Catalog.prototype = { | ||||||
|     get toplevelPagesDict() { |     get toplevelPagesDict() { | ||||||
|       var pagesObj = this.catDict.get('Pages'); |       var pagesObj = this.catDict.get('Pages'); | ||||||
|       assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference'); |       assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference'); | ||||||
| @ -253,16 +260,16 @@ var Catalog = (function catalogCatalog() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Catalog; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var XRef = (function xRefXRef() { | var XRef = (function XRefClosure() { | ||||||
|   function constructor(stream, startXRef, mainXRefEntriesOffset) { |   function XRef(stream, startXRef, mainXRefEntriesOffset) { | ||||||
|     this.stream = stream; |     this.stream = stream; | ||||||
|     this.entries = []; |     this.entries = []; | ||||||
|     this.xrefstms = {}; |     this.xrefstms = {}; | ||||||
|     var trailerDict = this.readXRef(startXRef); |     var trailerDict = this.readXRef(startXRef); | ||||||
| 
 |     this.trailer = trailerDict; | ||||||
|     // prepare the XRef cache
 |     // prepare the XRef cache
 | ||||||
|     this.cache = []; |     this.cache = []; | ||||||
| 
 | 
 | ||||||
| @ -278,7 +285,7 @@ var XRef = (function xRefXRef() { | |||||||
|       error('Invalid root reference'); |       error('Invalid root reference'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   XRef.prototype = { | ||||||
|     readXRefTable: function readXRefTable(parser) { |     readXRefTable: function readXRefTable(parser) { | ||||||
|       var obj; |       var obj; | ||||||
|       while (true) { |       while (true) { | ||||||
| @ -518,20 +525,29 @@ var XRef = (function xRefXRef() { | |||||||
|     readXRef: function readXref(startXRef) { |     readXRef: function readXref(startXRef) { | ||||||
|       var stream = this.stream; |       var stream = this.stream; | ||||||
|       stream.pos = startXRef; |       stream.pos = startXRef; | ||||||
|       var parser = new Parser(new Lexer(stream), true); | 
 | ||||||
|       var obj = parser.getObj(); |       try { | ||||||
|       // parse an old-style xref table
 |         var parser = new Parser(new Lexer(stream), true); | ||||||
|       if (isCmd(obj, 'xref')) |         var obj = parser.getObj(); | ||||||
|         return this.readXRefTable(parser); | 
 | ||||||
|       // parse an xref stream
 |         // parse an old-style xref table
 | ||||||
|       if (isInt(obj)) { |         if (isCmd(obj, 'xref')) | ||||||
|         if (!isInt(parser.getObj()) || |           return this.readXRefTable(parser); | ||||||
|             !isCmd(parser.getObj(), 'obj') || | 
 | ||||||
|             !isStream(obj = parser.getObj())) { |         // parse an xref stream
 | ||||||
|           error('Invalid XRef stream'); |         if (isInt(obj)) { | ||||||
|  |           if (!isInt(parser.getObj()) || | ||||||
|  |               !isCmd(parser.getObj(), 'obj') || | ||||||
|  |               !isStream(obj = parser.getObj())) { | ||||||
|  |             error('Invalid XRef stream'); | ||||||
|  |           } | ||||||
|  |           return this.readXRefStream(obj); | ||||||
|         } |         } | ||||||
|         return this.readXRefStream(obj); |       } catch (e) { | ||||||
|  |         log('Reading of the xref table/stream failed: ' + e); | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       warn('Indexing all PDF objects'); | ||||||
|       return this.indexObjects(); |       return this.indexObjects(); | ||||||
|     }, |     }, | ||||||
|     getEntry: function xRefGetEntry(i) { |     getEntry: function xRefGetEntry(i) { | ||||||
| @ -589,7 +605,7 @@ var XRef = (function xRefXRef() { | |||||||
|           e = parser.getObj(); |           e = parser.getObj(); | ||||||
|         } |         } | ||||||
|         // Don't cache streams since they are mutable (except images).
 |         // Don't cache streams since they are mutable (except images).
 | ||||||
|         if (!isStream(e) || e.getImage) |         if (!isStream(e) || e instanceof JpegStream) | ||||||
|           this.cache[num] = e; |           this.cache[num] = e; | ||||||
|         return e; |         return e; | ||||||
|       } |       } | ||||||
| @ -633,7 +649,7 @@ var XRef = (function xRefXRef() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return XRef; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -642,7 +658,7 @@ var XRef = (function xRefXRef() { | |||||||
|  * inside of a worker. The `PDFObjects` implements some basic functions to |  * inside of a worker. The `PDFObjects` implements some basic functions to | ||||||
|  * manage these objects. |  * manage these objects. | ||||||
|  */ |  */ | ||||||
| var PDFObjects = (function pdfObjects() { | var PDFObjects = (function PDFObjectsClosure() { | ||||||
|   function PDFObjects() { |   function PDFObjects() { | ||||||
|     this.objs = {}; |     this.objs = {}; | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -9,8 +9,8 @@ function isEOF(v) { | |||||||
|   return v == EOF; |   return v == EOF; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var Parser = (function parserParser() { | var Parser = (function ParserClosure() { | ||||||
|   function constructor(lexer, allowStreams, xref) { |   function Parser(lexer, allowStreams, xref) { | ||||||
|     this.lexer = lexer; |     this.lexer = lexer; | ||||||
|     this.allowStreams = allowStreams; |     this.allowStreams = allowStreams; | ||||||
|     this.xref = xref; |     this.xref = xref; | ||||||
| @ -18,7 +18,7 @@ var Parser = (function parserParser() { | |||||||
|     this.refill(); |     this.refill(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Parser.prototype = { | ||||||
|     refill: function parserRefill() { |     refill: function parserRefill() { | ||||||
|       this.buf1 = this.lexer.getObj(); |       this.buf1 = this.lexer.getObj(); | ||||||
|       this.buf2 = this.lexer.getObj(); |       this.buf2 = this.lexer.getObj(); | ||||||
| @ -157,7 +157,7 @@ var Parser = (function parserParser() { | |||||||
|       imageStream = this.filter(imageStream, dict, length); |       imageStream = this.filter(imageStream, dict, length); | ||||||
|       imageStream.parameters = dict; |       imageStream.parameters = dict; | ||||||
| 
 | 
 | ||||||
|       this.buf2 = new Cmd('EI'); |       this.buf2 = Cmd.get('EI'); | ||||||
|       this.shift(); |       this.shift(); | ||||||
| 
 | 
 | ||||||
|       return imageStream; |       return imageStream; | ||||||
| @ -225,7 +225,8 @@ var Parser = (function parserParser() { | |||||||
|           return new PredictorStream(new FlateStream(stream), params); |           return new PredictorStream(new FlateStream(stream), params); | ||||||
|         } |         } | ||||||
|         return new FlateStream(stream); |         return new FlateStream(stream); | ||||||
|       } else if (name == 'LZWDecode' || name == 'LZW') { |       } | ||||||
|  |       if (name == 'LZWDecode' || name == 'LZW') { | ||||||
|         var earlyChange = 1; |         var earlyChange = 1; | ||||||
|         if (params) { |         if (params) { | ||||||
|           if (params.has('EarlyChange')) |           if (params.has('EarlyChange')) | ||||||
| @ -234,31 +235,34 @@ var Parser = (function parserParser() { | |||||||
|             new LZWStream(stream, earlyChange), params); |             new LZWStream(stream, earlyChange), params); | ||||||
|         } |         } | ||||||
|         return new LZWStream(stream, earlyChange); |         return new LZWStream(stream, earlyChange); | ||||||
|       } else if (name == 'DCTDecode' || name == 'DCT') { |       } | ||||||
|  |       if (name == 'DCTDecode' || name == 'DCT') { | ||||||
|         var bytes = stream.getBytes(length); |         var bytes = stream.getBytes(length); | ||||||
|         return new JpegStream(bytes, stream.dict, this.xref); |         return new JpegStream(bytes, stream.dict, this.xref); | ||||||
|       } else if (name == 'ASCII85Decode' || name == 'A85') { |  | ||||||
|         return new Ascii85Stream(stream); |  | ||||||
|       } else if (name == 'ASCIIHexDecode' || name == 'AHx') { |  | ||||||
|         return new AsciiHexStream(stream); |  | ||||||
|       } else if (name == 'CCITTFaxDecode' || name == 'CCF') { |  | ||||||
|         return new CCITTFaxStream(stream, params); |  | ||||||
|       } else { |  | ||||||
|         TODO('filter "' + name + '" not supported yet'); |  | ||||||
|       } |       } | ||||||
|  |       if (name == 'ASCII85Decode' || name == 'A85') { | ||||||
|  |         return new Ascii85Stream(stream); | ||||||
|  |       } | ||||||
|  |       if (name == 'ASCIIHexDecode' || name == 'AHx') { | ||||||
|  |         return new AsciiHexStream(stream); | ||||||
|  |       } | ||||||
|  |       if (name == 'CCITTFaxDecode' || name == 'CCF') { | ||||||
|  |         return new CCITTFaxStream(stream, params); | ||||||
|  |       } | ||||||
|  |       warn('filter "' + name + '" not supported yet'); | ||||||
|       return stream; |       return stream; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Parser; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var Lexer = (function lexer() { | var Lexer = (function LexerClosure() { | ||||||
|   function constructor(stream) { |   function Lexer(stream) { | ||||||
|     this.stream = stream; |     this.stream = stream; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.isSpace = function lexerIsSpace(ch) { |   Lexer.isSpace = function lexerIsSpace(ch) { | ||||||
|     return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a'; |     return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a'; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -292,7 +296,7 @@ var Lexer = (function lexer() { | |||||||
|     return -1; |     return -1; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Lexer.prototype = { | ||||||
|     getNumber: function lexerGetNumber(ch) { |     getNumber: function lexerGetNumber(ch) { | ||||||
|       var floating = false; |       var floating = false; | ||||||
|       var str = ch; |       var str = ch; | ||||||
| @ -492,14 +496,14 @@ var Lexer = (function lexer() { | |||||||
|         // array punctuation
 |         // array punctuation
 | ||||||
|         case '[': |         case '[': | ||||||
|         case ']': |         case ']': | ||||||
|           return new Cmd(ch); |           return Cmd.get(ch); | ||||||
|         // hex string or dict punctuation
 |         // hex string or dict punctuation
 | ||||||
|         case '<': |         case '<': | ||||||
|           ch = stream.lookChar(); |           ch = stream.lookChar(); | ||||||
|           if (ch == '<') { |           if (ch == '<') { | ||||||
|             // dict punctuation
 |             // dict punctuation
 | ||||||
|             stream.skip(); |             stream.skip(); | ||||||
|             return new Cmd('<<'); |             return Cmd.get('<<'); | ||||||
|           } |           } | ||||||
|           return this.getHexString(ch); |           return this.getHexString(ch); | ||||||
|         // dict punctuation
 |         // dict punctuation
 | ||||||
| @ -507,11 +511,11 @@ var Lexer = (function lexer() { | |||||||
|           ch = stream.lookChar(); |           ch = stream.lookChar(); | ||||||
|           if (ch == '>') { |           if (ch == '>') { | ||||||
|             stream.skip(); |             stream.skip(); | ||||||
|             return new Cmd('>>'); |             return Cmd.get('>>'); | ||||||
|           } |           } | ||||||
|         case '{': |         case '{': | ||||||
|         case '}': |         case '}': | ||||||
|           return new Cmd(ch); |           return Cmd.get(ch); | ||||||
|         // fall through
 |         // fall through
 | ||||||
|         case ')': |         case ')': | ||||||
|           error('Illegal character: ' + ch); |           error('Illegal character: ' + ch); | ||||||
| @ -534,7 +538,7 @@ var Lexer = (function lexer() { | |||||||
|         return false; |         return false; | ||||||
|       if (str == 'null') |       if (str == 'null') | ||||||
|         return null; |         return null; | ||||||
|       return new Cmd(str); |       return Cmd.get(str); | ||||||
|     }, |     }, | ||||||
|     skipToNextLine: function lexerSkipToNextLine() { |     skipToNextLine: function lexerSkipToNextLine() { | ||||||
|       var stream = this.stream; |       var stream = this.stream; | ||||||
| @ -554,11 +558,11 @@ var Lexer = (function lexer() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Lexer; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var Linearization = (function linearizationLinearization() { | var Linearization = (function LinearizationClosure() { | ||||||
|   function constructor(stream) { |   function Linearization(stream) { | ||||||
|     this.parser = new Parser(new Lexer(stream), false); |     this.parser = new Parser(new Lexer(stream), false); | ||||||
|     var obj1 = this.parser.getObj(); |     var obj1 = this.parser.getObj(); | ||||||
|     var obj2 = this.parser.getObj(); |     var obj2 = this.parser.getObj(); | ||||||
| @ -572,7 +576,7 @@ var Linearization = (function linearizationLinearization() { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Linearization.prototype = { | ||||||
|     getInt: function linearizationGetInt(name) { |     getInt: function linearizationGetInt(name) { | ||||||
|       var linDict = this.linDict; |       var linDict = this.linDict; | ||||||
|       var obj; |       var obj; | ||||||
| @ -631,6 +635,6 @@ var Linearization = (function linearizationLinearization() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Linearization; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,13 +3,18 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var Pattern = (function patternPattern() { | var PatternType = { | ||||||
|  |   AXIAL: 2, | ||||||
|  |   RADIAL: 3 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | var Pattern = (function PatternClosure() { | ||||||
|   // Constructor should define this.getPattern
 |   // Constructor should define this.getPattern
 | ||||||
|   function constructor() { |   function Pattern() { | ||||||
|     error('should not call Pattern constructor'); |     error('should not call Pattern constructor'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Pattern.prototype = { | ||||||
|     // Input: current Canvas context
 |     // Input: current Canvas context
 | ||||||
|     // Output: the appropriate fillStyle or strokeStyle
 |     // Output: the appropriate fillStyle or strokeStyle
 | ||||||
|     getPattern: function pattern_getStyle(ctx) { |     getPattern: function pattern_getStyle(ctx) { | ||||||
| @ -17,34 +22,34 @@ var Pattern = (function patternPattern() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.shadingFromIR = function pattern_shadingFromIR(ctx, raw) { |   Pattern.shadingFromIR = function pattern_shadingFromIR(ctx, raw) { | ||||||
|     return Shadings[raw[0]].fromIR(ctx, raw); |     return Shadings[raw[0]].fromIR(ctx, raw); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.parseShading = function pattern_shading(shading, matrix, xref, |   Pattern.parseShading = function pattern_shading(shading, matrix, xref, | ||||||
|                                                       res, ctx) { |                                                       res, ctx) { | ||||||
| 
 | 
 | ||||||
|     var dict = isStream(shading) ? shading.dict : shading; |     var dict = isStream(shading) ? shading.dict : shading; | ||||||
|     var type = dict.get('ShadingType'); |     var type = dict.get('ShadingType'); | ||||||
| 
 | 
 | ||||||
|     switch (type) { |     switch (type) { | ||||||
|       case 2: |       case PatternType.AXIAL: | ||||||
|       case 3: |       case PatternType.RADIAL: | ||||||
|         // both radial and axial shadings are handled by RadialAxial shading
 |         // Both radial and axial shadings are handled by RadialAxial shading.
 | ||||||
|         return new Shadings.RadialAxial(dict, matrix, xref, res, ctx); |         return new Shadings.RadialAxial(dict, matrix, xref, res, ctx); | ||||||
|       default: |       default: | ||||||
|         return new Shadings.Dummy(); |         return new Shadings.Dummy(); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return Pattern; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var Shadings = {}; | var Shadings = {}; | ||||||
| 
 | 
 | ||||||
| // Radial and axial shading have very similar implementations
 | // Radial and axial shading have very similar implementations
 | ||||||
| // If needed, the implementations can be broken into two classes
 | // If needed, the implementations can be broken into two classes
 | ||||||
| Shadings.RadialAxial = (function radialAxialShading() { | Shadings.RadialAxial = (function RadialAxialClosure() { | ||||||
|   function constructor(dict, matrix, xref, res, ctx) { |   function RadialAxial(dict, matrix, xref, res, ctx) { | ||||||
|     this.matrix = matrix; |     this.matrix = matrix; | ||||||
|     this.coordsArr = dict.get('Coords'); |     this.coordsArr = dict.get('Coords'); | ||||||
|     this.shadingType = dict.get('ShadingType'); |     this.shadingType = dict.get('ShadingType'); | ||||||
| @ -97,7 +102,7 @@ Shadings.RadialAxial = (function radialAxialShading() { | |||||||
|     this.colorStops = colorStops; |     this.colorStops = colorStops; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.fromIR = function radialAxialShadingGetIR(ctx, raw) { |   RadialAxial.fromIR = function radialAxialShadingGetIR(ctx, raw) { | ||||||
|     var type = raw[1]; |     var type = raw[1]; | ||||||
|     var colorStops = raw[2]; |     var colorStops = raw[2]; | ||||||
|     var p0 = raw[3]; |     var p0 = raw[3]; | ||||||
| @ -117,9 +122,9 @@ Shadings.RadialAxial = (function radialAxialShading() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var grad; |     var grad; | ||||||
|     if (type == 2) |     if (type == PatternType.AXIAL) | ||||||
|       grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]); |       grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]); | ||||||
|     else if (type == 3) |     else if (type == PatternType.RADIAL) | ||||||
|       grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1); |       grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1); | ||||||
| 
 | 
 | ||||||
|     for (var i = 0, ii = colorStops.length; i < ii; ++i) { |     for (var i = 0, ii = colorStops.length; i < ii; ++i) { | ||||||
| @ -129,16 +134,16 @@ Shadings.RadialAxial = (function radialAxialShading() { | |||||||
|     return grad; |     return grad; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   RadialAxial.prototype = { | ||||||
|     getIR: function radialAxialShadingGetIR() { |     getIR: function radialAxialShadingGetIR() { | ||||||
|       var coordsArr = this.coordsArr; |       var coordsArr = this.coordsArr; | ||||||
|       var type = this.shadingType; |       var type = this.shadingType; | ||||||
|       if (type == 2) { |       if (type == PatternType.AXIAL) { | ||||||
|         var p0 = [coordsArr[0], coordsArr[1]]; |         var p0 = [coordsArr[0], coordsArr[1]]; | ||||||
|         var p1 = [coordsArr[2], coordsArr[3]]; |         var p1 = [coordsArr[2], coordsArr[3]]; | ||||||
|         var r0 = null; |         var r0 = null; | ||||||
|         var r1 = null; |         var r1 = null; | ||||||
|       } else if (type == 3) { |       } else if (type == PatternType.RADIAL) { | ||||||
|         var p0 = [coordsArr[0], coordsArr[1]]; |         var p0 = [coordsArr[0], coordsArr[1]]; | ||||||
|         var p1 = [coordsArr[3], coordsArr[4]]; |         var p1 = [coordsArr[3], coordsArr[4]]; | ||||||
|         var r0 = coordsArr[2]; |         var r0 = coordsArr[2]; | ||||||
| @ -157,28 +162,32 @@ Shadings.RadialAxial = (function radialAxialShading() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return RadialAxial; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| Shadings.Dummy = (function dummyShading() { | Shadings.Dummy = (function DummyClosure() { | ||||||
|   function constructor() { |   function Dummy() { | ||||||
|     this.type = 'Pattern'; |     this.type = 'Pattern'; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.fromIR = function dummyShadingFromIR() { |   Dummy.fromIR = function dummyShadingFromIR() { | ||||||
|     return 'hotpink'; |     return 'hotpink'; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   Dummy.prototype = { | ||||||
|     getIR: function dummyShadingGetIR() { |     getIR: function dummyShadingGetIR() { | ||||||
|       return ['Dummy']; |       return ['Dummy']; | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   return constructor; |   return Dummy; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var TilingPattern = (function tilingPattern() { | var TilingPattern = (function TilingPatternClosure() { | ||||||
|   var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2; |   var PaintType = { | ||||||
|  |     COLORED: 1, | ||||||
|  |     UNCOLORED: 2 | ||||||
|  |   }; | ||||||
|  |   var MAX_PATTERN_SIZE = 512; | ||||||
| 
 | 
 | ||||||
|   function TilingPattern(IR, color, ctx, objs) { |   function TilingPattern(IR, color, ctx, objs) { | ||||||
|     var IRQueue = IR[2]; |     var IRQueue = IR[2]; | ||||||
| @ -204,13 +213,13 @@ var TilingPattern = (function tilingPattern() { | |||||||
|     var width = botRight[0] - topLeft[0]; |     var width = botRight[0] - topLeft[0]; | ||||||
|     var height = botRight[1] - topLeft[1]; |     var height = botRight[1] - topLeft[1]; | ||||||
| 
 | 
 | ||||||
|     // TODO: hack to avoid OOM, we would idealy compute the tiling
 |     // TODO: hack to avoid OOM, we would ideally compute the tiling
 | ||||||
|     // pattern to be only as large as the acual size in device space
 |     // pattern to be only as large as the acual size in device space
 | ||||||
|     // This could be computed with .mozCurrentTransform, but still
 |     // This could be computed with .mozCurrentTransform, but still
 | ||||||
|     // needs to be implemented
 |     // needs to be implemented
 | ||||||
|     while (Math.abs(width) > 512 || Math.abs(height) > 512) { |     while (Math.abs(width) > MAX_PATTERN_SIZE || | ||||||
|       width = 512; |            Math.abs(height) > MAX_PATTERN_SIZE) { | ||||||
|       height = 512; |       width = height = MAX_PATTERN_SIZE; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var tmpCanvas = new ScratchCanvas(width, height); |     var tmpCanvas = new ScratchCanvas(width, height); | ||||||
| @ -220,11 +229,11 @@ var TilingPattern = (function tilingPattern() { | |||||||
|     var graphics = new CanvasGraphics(tmpCtx, objs); |     var graphics = new CanvasGraphics(tmpCtx, objs); | ||||||
| 
 | 
 | ||||||
|     switch (paintType) { |     switch (paintType) { | ||||||
|       case PAINT_TYPE_COLORED: |       case PaintType.COLORED: | ||||||
|         tmpCtx.fillStyle = ctx.fillStyle; |         tmpCtx.fillStyle = ctx.fillStyle; | ||||||
|         tmpCtx.strokeStyle = ctx.strokeStyle; |         tmpCtx.strokeStyle = ctx.strokeStyle; | ||||||
|         break; |         break; | ||||||
|       case PAINT_TYPE_UNCOLORED: |       case PaintType.UNCOLORED: | ||||||
|         color = Util.makeCssRgb.apply(this, color); |         color = Util.makeCssRgb.apply(this, color); | ||||||
|         tmpCtx.fillStyle = color; |         tmpCtx.fillStyle = color; | ||||||
|         tmpCtx.strokeStyle = color; |         tmpCtx.strokeStyle = color; | ||||||
|  | |||||||
							
								
								
									
										184
									
								
								src/stream.js
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								src/stream.js
									
									
									
									
									
								
							| @ -3,8 +3,8 @@ | |||||||
| 
 | 
 | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var Stream = (function streamStream() { | var Stream = (function StreamClosure() { | ||||||
|   function constructor(arrayBuffer, start, length, dict) { |   function Stream(arrayBuffer, start, length, dict) { | ||||||
|     this.bytes = new Uint8Array(arrayBuffer); |     this.bytes = new Uint8Array(arrayBuffer); | ||||||
|     this.start = start || 0; |     this.start = start || 0; | ||||||
|     this.pos = this.start; |     this.pos = this.start; | ||||||
| @ -14,7 +14,7 @@ var Stream = (function streamStream() { | |||||||
| 
 | 
 | ||||||
|   // required methods for a stream. if a particular stream does not
 |   // required methods for a stream. if a particular stream does not
 | ||||||
|   // implement these, an error should be thrown
 |   // implement these, an error should be thrown
 | ||||||
|   constructor.prototype = { |   Stream.prototype = { | ||||||
|     get length() { |     get length() { | ||||||
|       return this.end - this.start; |       return this.end - this.start; | ||||||
|     }, |     }, | ||||||
| @ -67,11 +67,11 @@ var Stream = (function streamStream() { | |||||||
|     isStream: true |     isStream: true | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Stream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var StringStream = (function stringStream() { | var StringStream = (function StringStreamClosure() { | ||||||
|   function constructor(str) { |   function StringStream(str) { | ||||||
|     var length = str.length; |     var length = str.length; | ||||||
|     var bytes = new Uint8Array(length); |     var bytes = new Uint8Array(length); | ||||||
|     for (var n = 0; n < length; ++n) |     for (var n = 0; n < length; ++n) | ||||||
| @ -79,21 +79,21 @@ var StringStream = (function stringStream() { | |||||||
|     Stream.call(this, bytes); |     Stream.call(this, bytes); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Stream.prototype; |   StringStream.prototype = Stream.prototype; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return StringStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| // super class for the decoding streams
 | // super class for the decoding streams
 | ||||||
| var DecodeStream = (function decodeStream() { | var DecodeStream = (function DecodeStreamClosure() { | ||||||
|   function constructor() { |   function DecodeStream() { | ||||||
|     this.pos = 0; |     this.pos = 0; | ||||||
|     this.bufferLength = 0; |     this.bufferLength = 0; | ||||||
|     this.eof = false; |     this.eof = false; | ||||||
|     this.buffer = null; |     this.buffer = null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = { |   DecodeStream.prototype = { | ||||||
|     ensureBuffer: function decodestream_ensureBuffer(requested) { |     ensureBuffer: function decodestream_ensureBuffer(requested) { | ||||||
|       var buffer = this.buffer; |       var buffer = this.buffer; | ||||||
|       var current = buffer ? buffer.byteLength : 0; |       var current = buffer ? buffer.byteLength : 0; | ||||||
| @ -178,24 +178,24 @@ var DecodeStream = (function decodeStream() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return DecodeStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var FakeStream = (function fakeStream() { | var FakeStream = (function FakeStreamClosure() { | ||||||
|   function constructor(stream) { |   function FakeStream(stream) { | ||||||
|     this.dict = stream.dict; |     this.dict = stream.dict; | ||||||
|     DecodeStream.call(this); |     DecodeStream.call(this); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   FakeStream.prototype = Object.create(DecodeStream.prototype); | ||||||
|   constructor.prototype.readBlock = function fakeStreamReadBlock() { |   FakeStream.prototype.readBlock = function fakeStreamReadBlock() { | ||||||
|     var bufferLength = this.bufferLength; |     var bufferLength = this.bufferLength; | ||||||
|     bufferLength += 1024; |     bufferLength += 1024; | ||||||
|     var buffer = this.ensureBuffer(bufferLength); |     var buffer = this.ensureBuffer(bufferLength); | ||||||
|     this.bufferLength = bufferLength; |     this.bufferLength = bufferLength; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.getBytes = function fakeStreamGetBytes(length) { |   FakeStream.prototype.getBytes = function fakeStreamGetBytes(length) { | ||||||
|     var end, pos = this.pos; |     var end, pos = this.pos; | ||||||
| 
 | 
 | ||||||
|     if (length) { |     if (length) { | ||||||
| @ -217,18 +217,20 @@ var FakeStream = (function fakeStream() { | |||||||
|     return this.buffer.subarray(pos, end); |     return this.buffer.subarray(pos, end); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return FakeStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var StreamsSequenceStream = (function streamSequenceStream() { | var StreamsSequenceStream = (function StreamsSequenceStreamClosure() { | ||||||
|   function constructor(streams) { |   function StreamsSequenceStream(streams) { | ||||||
|     this.streams = streams; |     this.streams = streams; | ||||||
|     DecodeStream.call(this); |     DecodeStream.call(this); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype); | ||||||
|  | 
 | ||||||
|  |   StreamsSequenceStream.prototype.readBlock = | ||||||
|  |     function streamSequenceStreamReadBlock() { | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlock = function streamSequenceStreamReadBlock() { |  | ||||||
|     var streams = this.streams; |     var streams = this.streams; | ||||||
|     if (streams.length == 0) { |     if (streams.length == 0) { | ||||||
|       this.eof = true; |       this.eof = true; | ||||||
| @ -243,10 +245,10 @@ var StreamsSequenceStream = (function streamSequenceStream() { | |||||||
|     this.bufferLength = newLength; |     this.bufferLength = newLength; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return StreamsSequenceStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var FlateStream = (function flateStream() { | var FlateStream = (function FlateStreamClosure() { | ||||||
|   var codeLenCodeMap = new Uint32Array([ |   var codeLenCodeMap = new Uint32Array([ | ||||||
|     16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 |     16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 | ||||||
|   ]); |   ]); | ||||||
| @ -339,7 +341,7 @@ var FlateStream = (function flateStream() { | |||||||
|     0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000 |     0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000 | ||||||
|   ]), 5]; |   ]), 5]; | ||||||
| 
 | 
 | ||||||
|   function constructor(stream) { |   function FlateStream(stream) { | ||||||
|     var bytes = stream.getBytes(); |     var bytes = stream.getBytes(); | ||||||
|     var bytesPos = 0; |     var bytesPos = 0; | ||||||
| 
 | 
 | ||||||
| @ -364,9 +366,9 @@ var FlateStream = (function flateStream() { | |||||||
|     DecodeStream.call(this); |     DecodeStream.call(this); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   FlateStream.prototype = Object.create(DecodeStream.prototype); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.getBits = function flateStreamGetBits(bits) { |   FlateStream.prototype.getBits = function flateStreamGetBits(bits) { | ||||||
|     var codeSize = this.codeSize; |     var codeSize = this.codeSize; | ||||||
|     var codeBuf = this.codeBuf; |     var codeBuf = this.codeBuf; | ||||||
|     var bytes = this.bytes; |     var bytes = this.bytes; | ||||||
| @ -386,7 +388,7 @@ var FlateStream = (function flateStream() { | |||||||
|     return b; |     return b; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.getCode = function flateStreamGetCode(table) { |   FlateStream.prototype.getCode = function flateStreamGetCode(table) { | ||||||
|     var codes = table[0]; |     var codes = table[0]; | ||||||
|     var maxLen = table[1]; |     var maxLen = table[1]; | ||||||
|     var codeSize = this.codeSize; |     var codeSize = this.codeSize; | ||||||
| @ -412,7 +414,7 @@ var FlateStream = (function flateStream() { | |||||||
|     return codeVal; |     return codeVal; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.generateHuffmanTable = |   FlateStream.prototype.generateHuffmanTable = | ||||||
|     function flateStreamGenerateHuffmanTable(lengths) { |     function flateStreamGenerateHuffmanTable(lengths) { | ||||||
|     var n = lengths.length; |     var n = lengths.length; | ||||||
| 
 | 
 | ||||||
| @ -451,7 +453,7 @@ var FlateStream = (function flateStream() { | |||||||
|     return [codes, maxLen]; |     return [codes, maxLen]; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlock = function flateStreamReadBlock() { |   FlateStream.prototype.readBlock = function flateStreamReadBlock() { | ||||||
|     // read block header
 |     // read block header
 | ||||||
|     var hdr = this.getBits(3); |     var hdr = this.getBits(3); | ||||||
|     if (hdr & 1) |     if (hdr & 1) | ||||||
| @ -582,11 +584,11 @@ var FlateStream = (function flateStream() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return FlateStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var PredictorStream = (function predictorStream() { | var PredictorStream = (function PredictorStreamClosure() { | ||||||
|   function constructor(stream, params) { |   function PredictorStream(stream, params) { | ||||||
|     var predictor = this.predictor = params.get('Predictor') || 1; |     var predictor = this.predictor = params.get('Predictor') || 1; | ||||||
| 
 | 
 | ||||||
|     if (predictor <= 1) |     if (predictor <= 1) | ||||||
| @ -613,9 +615,9 @@ var PredictorStream = (function predictorStream() { | |||||||
|     return this; |     return this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   PredictorStream.prototype = Object.create(DecodeStream.prototype); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlockTiff = |   PredictorStream.prototype.readBlockTiff = | ||||||
|     function predictorStreamReadBlockTiff() { |     function predictorStreamReadBlockTiff() { | ||||||
|     var rowBytes = this.rowBytes; |     var rowBytes = this.rowBytes; | ||||||
| 
 | 
 | ||||||
| @ -676,7 +678,9 @@ var PredictorStream = (function predictorStream() { | |||||||
|     this.bufferLength += rowBytes; |     this.bufferLength += rowBytes; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlockPng = function predictorStreamReadBlockPng() { |   PredictorStream.prototype.readBlockPng = | ||||||
|  |     function predictorStreamReadBlockPng() { | ||||||
|  | 
 | ||||||
|     var rowBytes = this.rowBytes; |     var rowBytes = this.rowBytes; | ||||||
|     var pixBytes = this.pixBytes; |     var pixBytes = this.pixBytes; | ||||||
| 
 | 
 | ||||||
| @ -753,7 +757,7 @@ var PredictorStream = (function predictorStream() { | |||||||
|     this.bufferLength += rowBytes; |     this.bufferLength += rowBytes; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return PredictorStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -763,7 +767,7 @@ var PredictorStream = (function predictorStream() { | |||||||
|  * a library to decode these images and the stream behaves like all the other |  * a library to decode these images and the stream behaves like all the other | ||||||
|  * DecodeStreams. |  * DecodeStreams. | ||||||
|  */ |  */ | ||||||
| var JpegStream = (function jpegStream() { | var JpegStream = (function JpegStreamClosure() { | ||||||
|   function isAdobeImage(bytes) { |   function isAdobeImage(bytes) { | ||||||
|     var maxBytesScanned = Math.max(bytes.length - 16, 1024); |     var maxBytesScanned = Math.max(bytes.length - 16, 1024); | ||||||
|     // Looking for APP14, 'Adobe'
 |     // Looking for APP14, 'Adobe'
 | ||||||
| @ -794,7 +798,7 @@ var JpegStream = (function jpegStream() { | |||||||
|     return newBytes; |     return newBytes; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function constructor(bytes, dict, xref) { |   function JpegStream(bytes, dict, xref) { | ||||||
|     // TODO: per poppler, some images may have 'junk' before that
 |     // TODO: per poppler, some images may have 'junk' before that
 | ||||||
|     // need to be removed
 |     // need to be removed
 | ||||||
|     this.dict = dict; |     this.dict = dict; | ||||||
| @ -825,9 +829,9 @@ var JpegStream = (function jpegStream() { | |||||||
|     DecodeStream.call(this); |     DecodeStream.call(this); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   JpegStream.prototype = Object.create(DecodeStream.prototype); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) { |   JpegStream.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) { | ||||||
|     if (this.bufferLength) |     if (this.bufferLength) | ||||||
|       return; |       return; | ||||||
|     var jpegImage = new JpegImage(); |     var jpegImage = new JpegImage(); | ||||||
| @ -839,18 +843,18 @@ var JpegStream = (function jpegStream() { | |||||||
|     this.buffer = data; |     this.buffer = data; | ||||||
|     this.bufferLength = data.length; |     this.bufferLength = data.length; | ||||||
|   }; |   }; | ||||||
|   constructor.prototype.getIR = function jpegStreamGetIR() { |   JpegStream.prototype.getIR = function jpegStreamGetIR() { | ||||||
|     return this.src; |     return this.src; | ||||||
|   }; |   }; | ||||||
|   constructor.prototype.getChar = function jpegStreamGetChar() { |   JpegStream.prototype.getChar = function jpegStreamGetChar() { | ||||||
|       error('internal error: getChar is not valid on JpegStream'); |       error('internal error: getChar is not valid on JpegStream'); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return JpegStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var DecryptStream = (function decryptStream() { | var DecryptStream = (function DecryptStreamClosure() { | ||||||
|   function constructor(str, decrypt) { |   function DecryptStream(str, decrypt) { | ||||||
|     this.str = str; |     this.str = str; | ||||||
|     this.dict = str.dict; |     this.dict = str.dict; | ||||||
|     this.decrypt = decrypt; |     this.decrypt = decrypt; | ||||||
| @ -860,9 +864,9 @@ var DecryptStream = (function decryptStream() { | |||||||
| 
 | 
 | ||||||
|   var chunkSize = 512; |   var chunkSize = 512; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   DecryptStream.prototype = Object.create(DecodeStream.prototype); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlock = function decryptStreamReadBlock() { |   DecryptStream.prototype.readBlock = function decryptStreamReadBlock() { | ||||||
|     var chunk = this.str.getBytes(chunkSize); |     var chunk = this.str.getBytes(chunkSize); | ||||||
|     if (!chunk || chunk.length == 0) { |     if (!chunk || chunk.length == 0) { | ||||||
|       this.eof = true; |       this.eof = true; | ||||||
| @ -879,11 +883,11 @@ var DecryptStream = (function decryptStream() { | |||||||
|     this.bufferLength = bufferLength; |     this.bufferLength = bufferLength; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return DecryptStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var Ascii85Stream = (function ascii85Stream() { | var Ascii85Stream = (function Ascii85StreamClosure() { | ||||||
|   function constructor(str) { |   function Ascii85Stream(str) { | ||||||
|     this.str = str; |     this.str = str; | ||||||
|     this.dict = str.dict; |     this.dict = str.dict; | ||||||
|     this.input = new Uint8Array(5); |     this.input = new Uint8Array(5); | ||||||
| @ -891,9 +895,9 @@ var Ascii85Stream = (function ascii85Stream() { | |||||||
|     DecodeStream.call(this); |     DecodeStream.call(this); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   Ascii85Stream.prototype = Object.create(DecodeStream.prototype); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlock = function ascii85StreamReadBlock() { |   Ascii85Stream.prototype.readBlock = function ascii85StreamReadBlock() { | ||||||
|     var tildaCode = '~'.charCodeAt(0); |     var tildaCode = '~'.charCodeAt(0); | ||||||
|     var zCode = 'z'.charCodeAt(0); |     var zCode = 'z'.charCodeAt(0); | ||||||
|     var str = this.str; |     var str = this.str; | ||||||
| @ -948,11 +952,11 @@ var Ascii85Stream = (function ascii85Stream() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Ascii85Stream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var AsciiHexStream = (function asciiHexStream() { | var AsciiHexStream = (function AsciiHexStreamClosure() { | ||||||
|   function constructor(str) { |   function AsciiHexStream(str) { | ||||||
|     this.str = str; |     this.str = str; | ||||||
|     this.dict = str.dict; |     this.dict = str.dict; | ||||||
| 
 | 
 | ||||||
| @ -986,9 +990,9 @@ var AsciiHexStream = (function asciiHexStream() { | |||||||
|       102: 15 |       102: 15 | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   AsciiHexStream.prototype = Object.create(DecodeStream.prototype); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlock = function asciiHexStreamReadBlock() { |   AsciiHexStream.prototype.readBlock = function asciiHexStreamReadBlock() { | ||||||
|     var gtCode = '>'.charCodeAt(0), bytes = this.str.getBytes(), c, n, |     var gtCode = '>'.charCodeAt(0), bytes = this.str.getBytes(), c, n, | ||||||
|         decodeLength, buffer, bufferLength, i, length; |         decodeLength, buffer, bufferLength, i, length; | ||||||
| 
 | 
 | ||||||
| @ -1018,10 +1022,10 @@ var AsciiHexStream = (function asciiHexStream() { | |||||||
|     this.eof = true; |     this.eof = true; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return AsciiHexStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var CCITTFaxStream = (function ccittFaxStream() { | var CCITTFaxStream = (function CCITTFaxStreamClosure() { | ||||||
| 
 | 
 | ||||||
|   var ccittEOL = -2; |   var ccittEOL = -2; | ||||||
|   var twoDimPass = 0; |   var twoDimPass = 0; | ||||||
| @ -1449,7 +1453,7 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     [2, 2], [2, 2], [2, 2], [2, 2] |     [2, 2], [2, 2], [2, 2], [2, 2] | ||||||
|   ]; |   ]; | ||||||
| 
 | 
 | ||||||
|   function constructor(str, params) { |   function CCITTFaxStream(str, params) { | ||||||
|     this.str = str; |     this.str = str; | ||||||
|     this.dict = str.dict; |     this.dict = str.dict; | ||||||
| 
 | 
 | ||||||
| @ -1494,9 +1498,9 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     DecodeStream.call(this); |     DecodeStream.call(this); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   CCITTFaxStream.prototype = Object.create(DecodeStream.prototype); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlock = function ccittFaxStreamReadBlock() { |   CCITTFaxStream.prototype.readBlock = function ccittFaxStreamReadBlock() { | ||||||
|     while (!this.eof) { |     while (!this.eof) { | ||||||
|       var c = this.lookChar(); |       var c = this.lookChar(); | ||||||
|       this.buf = EOF; |       this.buf = EOF; | ||||||
| @ -1505,7 +1509,7 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.addPixels = |   CCITTFaxStream.prototype.addPixels = | ||||||
|     function ccittFaxStreamAddPixels(a1, blackPixels) { |     function ccittFaxStreamAddPixels(a1, blackPixels) { | ||||||
|     var codingLine = this.codingLine; |     var codingLine = this.codingLine; | ||||||
|     var codingPos = this.codingPos; |     var codingPos = this.codingPos; | ||||||
| @ -1525,7 +1529,7 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     this.codingPos = codingPos; |     this.codingPos = codingPos; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.addPixelsNeg = |   CCITTFaxStream.prototype.addPixelsNeg = | ||||||
|     function ccittFaxStreamAddPixelsNeg(a1, blackPixels) { |     function ccittFaxStreamAddPixelsNeg(a1, blackPixels) { | ||||||
|     var codingLine = this.codingLine; |     var codingLine = this.codingLine; | ||||||
|     var codingPos = this.codingPos; |     var codingPos = this.codingPos; | ||||||
| @ -1554,7 +1558,7 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     this.codingPos = codingPos; |     this.codingPos = codingPos; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.lookChar = function ccittFaxStreamLookChar() { |   CCITTFaxStream.prototype.lookChar = function ccittFaxStreamLookChar() { | ||||||
|     if (this.buf != EOF) |     if (this.buf != EOF) | ||||||
|       return this.buf; |       return this.buf; | ||||||
| 
 | 
 | ||||||
| @ -1852,10 +1856,10 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|   // values. The first array element indicates whether a valid code is being
 |   // values. The first array element indicates whether a valid code is being
 | ||||||
|   // returned. The second array element is the actual code. The third array
 |   // returned. The second array element is the actual code. The third array
 | ||||||
|   // element indicates whether EOF was reached.
 |   // element indicates whether EOF was reached.
 | ||||||
|   var findTableCode = function ccittFaxStreamFindTableCode(start, end, table, |   CCITTFaxStream.prototype.findTableCode = | ||||||
|                                                            limit) { |     function ccittFaxStreamFindTableCode(start, end, table, limit) { | ||||||
|     var limitValue = limit || 0; |  | ||||||
| 
 | 
 | ||||||
|  |     var limitValue = limit || 0; | ||||||
|     for (var i = start; i <= end; ++i) { |     for (var i = start; i <= end; ++i) { | ||||||
|       var code = this.lookBits(i); |       var code = this.lookBits(i); | ||||||
|       if (code == EOF) |       if (code == EOF) | ||||||
| @ -1873,7 +1877,9 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     return [false, 0, false]; |     return [false, 0, false]; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.getTwoDimCode = function ccittFaxStreamGetTwoDimCode() { |   CCITTFaxStream.prototype.getTwoDimCode = | ||||||
|  |     function ccittFaxStreamGetTwoDimCode() { | ||||||
|  | 
 | ||||||
|     var code = 0; |     var code = 0; | ||||||
|     var p; |     var p; | ||||||
|     if (this.eoblock) { |     if (this.eoblock) { | ||||||
| @ -1884,7 +1890,7 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|         return p[1]; |         return p[1]; | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       var result = findTableCode(1, 7, twoDimTable); |       var result = this.findTableCode(1, 7, twoDimTable); | ||||||
|       if (result[0] && result[2]) |       if (result[0] && result[2]) | ||||||
|         return result[1]; |         return result[1]; | ||||||
|     } |     } | ||||||
| @ -1892,7 +1898,9 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     return EOF; |     return EOF; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.getWhiteCode = function ccittFaxStreamGetWhiteCode() { |   CCITTFaxStream.prototype.getWhiteCode = | ||||||
|  |     function ccittFaxStreamGetWhiteCode() { | ||||||
|  | 
 | ||||||
|     var code = 0; |     var code = 0; | ||||||
|     var p; |     var p; | ||||||
|     var n; |     var n; | ||||||
| @ -1911,11 +1919,11 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|         return p[1]; |         return p[1]; | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       var result = findTableCode(1, 9, whiteTable2); |       var result = this.findTableCode(1, 9, whiteTable2); | ||||||
|       if (result[0]) |       if (result[0]) | ||||||
|         return result[1]; |         return result[1]; | ||||||
| 
 | 
 | ||||||
|       result = findTableCode(11, 12, whiteTable1); |       result = this.findTableCode(11, 12, whiteTable1); | ||||||
|       if (result[0]) |       if (result[0]) | ||||||
|         return result[1]; |         return result[1]; | ||||||
|     } |     } | ||||||
| @ -1924,7 +1932,9 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     return 1; |     return 1; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.getBlackCode = function ccittFaxStreamGetBlackCode() { |   CCITTFaxStream.prototype.getBlackCode = | ||||||
|  |     function ccittFaxStreamGetBlackCode() { | ||||||
|  | 
 | ||||||
|     var code, p; |     var code, p; | ||||||
|     if (this.eoblock) { |     if (this.eoblock) { | ||||||
|       code = this.lookBits(13); |       code = this.lookBits(13); | ||||||
| @ -1942,15 +1952,15 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|         return p[1]; |         return p[1]; | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       var result = findTableCode(2, 6, blackTable3); |       var result = this.findTableCode(2, 6, blackTable3); | ||||||
|       if (result[0]) |       if (result[0]) | ||||||
|         return result[1]; |         return result[1]; | ||||||
| 
 | 
 | ||||||
|       result = findTableCode(7, 12, blackTable2, 64); |       result = this.findTableCode(7, 12, blackTable2, 64); | ||||||
|       if (result[0]) |       if (result[0]) | ||||||
|         return result[1]; |         return result[1]; | ||||||
| 
 | 
 | ||||||
|       result = findTableCode(10, 13, blackTable1); |       result = this.findTableCode(10, 13, blackTable1); | ||||||
|       if (result[0]) |       if (result[0]) | ||||||
|         return result[1]; |         return result[1]; | ||||||
|     } |     } | ||||||
| @ -1959,7 +1969,7 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     return 1; |     return 1; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.lookBits = function ccittFaxStreamLookBits(n) { |   CCITTFaxStream.prototype.lookBits = function ccittFaxStreamLookBits(n) { | ||||||
|     var c; |     var c; | ||||||
|     while (this.inputBits < n) { |     while (this.inputBits < n) { | ||||||
|       if ((c = this.str.getByte()) == null) { |       if ((c = this.str.getByte()) == null) { | ||||||
| @ -1974,16 +1984,16 @@ var CCITTFaxStream = (function ccittFaxStream() { | |||||||
|     return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n)); |     return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n)); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.eatBits = function ccittFaxStreamEatBits(n) { |   CCITTFaxStream.prototype.eatBits = function ccittFaxStreamEatBits(n) { | ||||||
|     if ((this.inputBits -= n) < 0) |     if ((this.inputBits -= n) < 0) | ||||||
|       this.inputBits = 0; |       this.inputBits = 0; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return CCITTFaxStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var LZWStream = (function lzwStream() { | var LZWStream = (function LZWStreamClosure() { | ||||||
|   function constructor(str, earlyChange) { |   function LZWStream(str, earlyChange) { | ||||||
|     this.str = str; |     this.str = str; | ||||||
|     this.dict = str.dict; |     this.dict = str.dict; | ||||||
|     this.cachedData = 0; |     this.cachedData = 0; | ||||||
| @ -2009,9 +2019,9 @@ var LZWStream = (function lzwStream() { | |||||||
|     DecodeStream.call(this); |     DecodeStream.call(this); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor.prototype = Object.create(DecodeStream.prototype); |   LZWStream.prototype = Object.create(DecodeStream.prototype); | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBits = function lzwStreamReadBits(n) { |   LZWStream.prototype.readBits = function lzwStreamReadBits(n) { | ||||||
|     var bitsCached = this.bitsCached; |     var bitsCached = this.bitsCached; | ||||||
|     var cachedData = this.cachedData; |     var cachedData = this.cachedData; | ||||||
|     while (bitsCached < n) { |     while (bitsCached < n) { | ||||||
| @ -2029,7 +2039,7 @@ var LZWStream = (function lzwStream() { | |||||||
|     return (cachedData >>> bitsCached) & ((1 << n) - 1); |     return (cachedData >>> bitsCached) & ((1 << n) - 1); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   constructor.prototype.readBlock = function lzwStreamReadBlock() { |   LZWStream.prototype.readBlock = function lzwStreamReadBlock() { | ||||||
|     var blockSize = 512; |     var blockSize = 512; | ||||||
|     var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize; |     var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize; | ||||||
|     var i, j, q; |     var i, j, q; | ||||||
| @ -2108,6 +2118,6 @@ var LZWStream = (function lzwStream() { | |||||||
|     this.bufferLength = currentBufferLength; |     this.bufferLength = currentBufferLength; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return LZWStream; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										74
									
								
								src/util.js
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								src/util.js
									
									
									
									
									
								
							| @ -76,24 +76,24 @@ function stringToBytes(str) { | |||||||
| 
 | 
 | ||||||
| var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; | var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; | ||||||
| 
 | 
 | ||||||
| var Util = (function utilUtil() { | var Util = (function UtilClosure() { | ||||||
|   function constructor() {} |   function Util() {} | ||||||
|   constructor.makeCssRgb = function makergb(r, g, b) { |   Util.makeCssRgb = function makergb(r, g, b) { | ||||||
|     var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0; |     var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0; | ||||||
|     return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; |     return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; | ||||||
|   }; |   }; | ||||||
|   constructor.makeCssCmyk = function makecmyk(c, m, y, k) { |   Util.makeCssCmyk = function makecmyk(c, m, y, k) { | ||||||
|     c = (new DeviceCmykCS()).getRgb([c, m, y, k]); |     c = (new DeviceCmykCS()).getRgb([c, m, y, k]); | ||||||
|     var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0; |     var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0; | ||||||
|     return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; |     return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; | ||||||
|   }; |   }; | ||||||
|   constructor.applyTransform = function apply(p, m) { |   Util.applyTransform = function apply(p, m) { | ||||||
|     var xt = p[0] * m[0] + p[1] * m[2] + m[4]; |     var xt = p[0] * m[0] + p[1] * m[2] + m[4]; | ||||||
|     var yt = p[0] * m[1] + p[1] * m[3] + m[5]; |     var yt = p[0] * m[1] + p[1] * m[3] + m[5]; | ||||||
|     return [xt, yt]; |     return [xt, yt]; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return constructor; |   return Util; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var PDFStringTranslateTable = [ | var PDFStringTranslateTable = [ | ||||||
| @ -197,7 +197,7 @@ function isPDFFunction(v) { | |||||||
|  * can be set. If any of these happens twice or the data is required before |  * can be set. If any of these happens twice or the data is required before | ||||||
|  * it was set, an exception is throw. |  * it was set, an exception is throw. | ||||||
|  */ |  */ | ||||||
| var Promise = (function promise() { | var Promise = (function PromiseClosure() { | ||||||
|   var EMPTY_PROMISE = {}; |   var EMPTY_PROMISE = {}; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
| @ -206,6 +206,8 @@ var Promise = (function promise() { | |||||||
|    */ |    */ | ||||||
|   function Promise(name, data) { |   function Promise(name, data) { | ||||||
|     this.name = name; |     this.name = name; | ||||||
|  |     this.isRejected = false; | ||||||
|  |     this.error = null; | ||||||
|     // If you build a promise and pass in some data it's already resolved.
 |     // If you build a promise and pass in some data it's already resolved.
 | ||||||
|     if (data != null) { |     if (data != null) { | ||||||
|       this.isResolved = true; |       this.isResolved = true; | ||||||
| @ -216,8 +218,35 @@ var Promise = (function promise() { | |||||||
|       this._data = EMPTY_PROMISE; |       this._data = EMPTY_PROMISE; | ||||||
|     } |     } | ||||||
|     this.callbacks = []; |     this.callbacks = []; | ||||||
|  |     this.errbacks = []; | ||||||
|  |   }; | ||||||
|  |   /** | ||||||
|  |    * Builds a promise that is resolved when all the passed in promises are | ||||||
|  |    * resolved. | ||||||
|  |    * @param {Promise[]} promises Array of promises to wait for. | ||||||
|  |    * @return {Promise} New dependant promise. | ||||||
|  |    */ | ||||||
|  |   Promise.all = function(promises) { | ||||||
|  |     var deferred = new Promise(); | ||||||
|  |     var unresolved = promises.length; | ||||||
|  |     var results = []; | ||||||
|  |     if (unresolved === 0) { | ||||||
|  |       deferred.resolve(results); | ||||||
|  |       return deferred; | ||||||
|  |     } | ||||||
|  |     for (var i = 0; i < unresolved; ++i) { | ||||||
|  |       var promise = promises[i]; | ||||||
|  |       promise.then((function(i) { | ||||||
|  |         return function(value) { | ||||||
|  |           results[i] = value; | ||||||
|  |           unresolved--; | ||||||
|  |           if (unresolved === 0) | ||||||
|  |             deferred.resolve(results); | ||||||
|  |         }; | ||||||
|  |       })(i)); | ||||||
|  |     } | ||||||
|  |     return deferred; | ||||||
|   }; |   }; | ||||||
| 
 |  | ||||||
|   Promise.prototype = { |   Promise.prototype = { | ||||||
|     hasData: false, |     hasData: false, | ||||||
| 
 | 
 | ||||||
| @ -256,9 +285,12 @@ var Promise = (function promise() { | |||||||
|       if (this.isResolved) { |       if (this.isResolved) { | ||||||
|         throw 'A Promise can be resolved only once ' + this.name; |         throw 'A Promise can be resolved only once ' + this.name; | ||||||
|       } |       } | ||||||
|  |       if (this.isRejected) { | ||||||
|  |         throw 'The Promise was already rejected ' + this.name; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       this.isResolved = true; |       this.isResolved = true; | ||||||
|       this.data = data; |       this.data = data || null; | ||||||
|       var callbacks = this.callbacks; |       var callbacks = this.callbacks; | ||||||
| 
 | 
 | ||||||
|       for (var i = 0, ii = callbacks.length; i < ii; i++) { |       for (var i = 0, ii = callbacks.length; i < ii; i++) { | ||||||
| @ -266,7 +298,24 @@ var Promise = (function promise() { | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     then: function promiseThen(callback) { |     reject: function proimseReject(reason) { | ||||||
|  |       if (this.isRejected) { | ||||||
|  |         throw 'A Promise can be rejected only once ' + this.name; | ||||||
|  |       } | ||||||
|  |       if (this.isResolved) { | ||||||
|  |         throw 'The Promise was already resolved ' + this.name; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.isRejected = true; | ||||||
|  |       this.error = reason || null; | ||||||
|  |       var errbacks = this.errbacks; | ||||||
|  | 
 | ||||||
|  |       for (var i = 0, ii = errbacks.length; i < ii; i++) { | ||||||
|  |         errbacks[i].call(null, reason); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     then: function promiseThen(callback, errback) { | ||||||
|       if (!callback) { |       if (!callback) { | ||||||
|         throw 'Requiring callback' + this.name; |         throw 'Requiring callback' + this.name; | ||||||
|       } |       } | ||||||
| @ -275,8 +324,13 @@ var Promise = (function promise() { | |||||||
|       if (this.isResolved) { |       if (this.isResolved) { | ||||||
|         var data = this.data; |         var data = this.data; | ||||||
|         callback.call(null, data); |         callback.call(null, data); | ||||||
|  |       } else if (this.isRejected && errorback) { | ||||||
|  |         var error = this.error; | ||||||
|  |         errback.call(null, error); | ||||||
|       } else { |       } else { | ||||||
|         this.callbacks.push(callback); |         this.callbacks.push(callback); | ||||||
|  |         if (errback) | ||||||
|  |           this.errbacks.push(errback); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ | |||||||
| function MessageHandler(name, comObj) { | function MessageHandler(name, comObj) { | ||||||
|   this.name = name; |   this.name = name; | ||||||
|   this.comObj = comObj; |   this.comObj = comObj; | ||||||
|  |   this.callbackIndex = 1; | ||||||
|  |   var callbacks = this.callbacks = {}; | ||||||
|   var ah = this.actionHandler = {}; |   var ah = this.actionHandler = {}; | ||||||
| 
 | 
 | ||||||
|   ah['console_log'] = [function ahConsoleLog(data) { |   ah['console_log'] = [function ahConsoleLog(data) { | ||||||
| @ -17,9 +19,30 @@ function MessageHandler(name, comObj) { | |||||||
| 
 | 
 | ||||||
|   comObj.onmessage = function messageHandlerComObjOnMessage(event) { |   comObj.onmessage = function messageHandlerComObjOnMessage(event) { | ||||||
|     var data = event.data; |     var data = event.data; | ||||||
|     if (data.action in ah) { |     if (data.isReply) { | ||||||
|  |       var callbackId = data.callbackId; | ||||||
|  |       if (data.callbackId in callbacks) { | ||||||
|  |         var callback = callbacks[callbackId]; | ||||||
|  |         delete callbacks[callbackId]; | ||||||
|  |         callback(data.data); | ||||||
|  |       } else { | ||||||
|  |         throw 'Cannot resolve callback ' + callbackId; | ||||||
|  |       } | ||||||
|  |     } else if (data.action in ah) { | ||||||
|       var action = ah[data.action]; |       var action = ah[data.action]; | ||||||
|       action[0].call(action[1], data.data); |       if (data.callbackId) { | ||||||
|  |         var promise = new Promise(); | ||||||
|  |         promise.then(function(resolvedData) { | ||||||
|  |           comObj.postMessage({ | ||||||
|  |             isReply: true, | ||||||
|  |             callbackId: data.callbackId, | ||||||
|  |             data: resolvedData | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
|  |         action[0].call(action[1], data.data, promise); | ||||||
|  |       } else { | ||||||
|  |         action[0].call(action[1], data.data); | ||||||
|  |       } | ||||||
|     } else { |     } else { | ||||||
|       throw 'Unkown action from worker: ' + data.action; |       throw 'Unkown action from worker: ' + data.action; | ||||||
|     } |     } | ||||||
| @ -34,12 +57,23 @@ MessageHandler.prototype = { | |||||||
|     } |     } | ||||||
|     ah[actionName] = [handler, scope]; |     ah[actionName] = [handler, scope]; | ||||||
|   }, |   }, | ||||||
| 
 |   /** | ||||||
|   send: function messageHandlerSend(actionName, data) { |    * Sends a message to the comObj to invoke the action with the supplied data. | ||||||
|     this.comObj.postMessage({ |    * @param {String} actionName Action to call. | ||||||
|  |    * @param {JSON} data JSON data to send. | ||||||
|  |    * @param {function} [callback] Optional callback that will handle a reply. | ||||||
|  |    */ | ||||||
|  |   send: function messageHandlerSend(actionName, data, callback) { | ||||||
|  |     var message = { | ||||||
|       action: actionName, |       action: actionName, | ||||||
|       data: data |       data: data | ||||||
|     }); |     }; | ||||||
|  |     if (callback) { | ||||||
|  |       var callbackId = this.callbackIndex++; | ||||||
|  |       this.callbacks[callbackId] = callback; | ||||||
|  |       message.callbackId = callbackId; | ||||||
|  |     } | ||||||
|  |     this.comObj.postMessage(message); | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -67,7 +101,6 @@ var WorkerMessageHandler = { | |||||||
|     handler.on('page_request', function wphSetupPageRequest(pageNum) { |     handler.on('page_request', function wphSetupPageRequest(pageNum) { | ||||||
|       pageNum = parseInt(pageNum); |       pageNum = parseInt(pageNum); | ||||||
| 
 | 
 | ||||||
|       var page = pdfDoc.getPage(pageNum); |  | ||||||
| 
 | 
 | ||||||
|       // The following code does quite the same as
 |       // The following code does quite the same as
 | ||||||
|       // Page.prototype.startRendering, but stops at one point and sends the
 |       // Page.prototype.startRendering, but stops at one point and sends the
 | ||||||
| @ -77,9 +110,23 @@ var WorkerMessageHandler = { | |||||||
|       var start = Date.now(); |       var start = Date.now(); | ||||||
| 
 | 
 | ||||||
|       var dependency = []; |       var dependency = []; | ||||||
| 
 |       var IRQueue = null; | ||||||
|       // Pre compile the pdf page and fetch the fonts/images.
 |       try { | ||||||
|       var IRQueue = page.getIRQueue(handler, dependency); |         var page = pdfDoc.getPage(pageNum); | ||||||
|  |         // Pre compile the pdf page and fetch the fonts/images.
 | ||||||
|  |         IRQueue = page.getIRQueue(handler, dependency); | ||||||
|  |       } catch (e) { | ||||||
|  |         // Turn the error into an obj that can be serialized
 | ||||||
|  |         e = { | ||||||
|  |           message: typeof e === 'object' ? e.message : e, | ||||||
|  |           stack: typeof e === 'object' ? e.stack : null | ||||||
|  |         }; | ||||||
|  |         handler.send('page_error', { | ||||||
|  |           pageNum: pageNum, | ||||||
|  |           error: e | ||||||
|  |         }); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       console.log('page=%d - getIRQueue: time=%dms, len=%d', pageNum, |       console.log('page=%d - getIRQueue: time=%dms, len=%d', pageNum, | ||||||
|                                   Date.now() - start, IRQueue.fnArray.length); |                                   Date.now() - start, IRQueue.fnArray.length); | ||||||
|  | |||||||
| @ -139,6 +139,11 @@ function nextPage(task, loadError) { | |||||||
|   if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) { |   if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) { | ||||||
|     log(' skipping page ' + task.pageNum + '/' + task.pdfDoc.numPages + |     log(' skipping page ' + task.pageNum + '/' + task.pdfDoc.numPages + | ||||||
|         '... '); |         '... '); | ||||||
|  |     // empty the canvas
 | ||||||
|  |     canvas.width = 1; | ||||||
|  |     canvas.height = 1; | ||||||
|  |     clear(canvas.getContext('2d')); | ||||||
|  | 
 | ||||||
|     snapshotCurrentPage(task, ''); |     snapshotCurrentPage(task, ''); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @ -160,12 +165,24 @@ function nextPage(task, loadError) { | |||||||
|       canvas.height = pageHeight * pdfToCssUnitsCoef; |       canvas.height = pageHeight * pdfToCssUnitsCoef; | ||||||
|       clear(ctx); |       clear(ctx); | ||||||
| 
 | 
 | ||||||
|  |       // using the text layer builder that does nothing to test
 | ||||||
|  |       // text layer creation operations
 | ||||||
|  |       var textLayerBuilder = { | ||||||
|  |         beginLayout: function nullTextLayerBuilderBeginLayout() {}, | ||||||
|  |         endLayout: function nullTextLayerBuilderEndLayout() {}, | ||||||
|  |         appendText: function nullTextLayerBuilderAppendText(text, fontName, | ||||||
|  |                                                             fontSize) {} | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|       page.startRendering( |       page.startRendering( | ||||||
|         ctx, |         ctx, | ||||||
|         function nextPageStartRendering(e) { |         function nextPageStartRendering(error) { | ||||||
|           snapshotCurrentPage(task, (!failure && e) ? |           var failureMessage = false; | ||||||
|             ('render : ' + e) : failure); |           if (error) | ||||||
|         } |             failureMessage = 'render : ' + error.message; | ||||||
|  |           snapshotCurrentPage(task, failureMessage); | ||||||
|  |         }, | ||||||
|  |         textLayerBuilder | ||||||
|       ); |       ); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       failure = 'page setup : ' + e.toString(); |       failure = 'page setup : ' + e.toString(); | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -16,3 +16,9 @@ | |||||||
| !alphatrans.pdf | !alphatrans.pdf | ||||||
| !devicen.pdf | !devicen.pdf | ||||||
| !cmykjpeg.pdf | !cmykjpeg.pdf | ||||||
|  | !issue840.pdf | ||||||
|  | !scan-bad.pdf | ||||||
|  | !freeculture.pdf | ||||||
|  | !issue918.pdf | ||||||
|  | !smaskdim.pdf | ||||||
|  | !type4psfunc.pdf | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								test/pdfs/aboutstacks.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/aboutstacks.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://greenhousechallenge.org/media/item/313/38/About-Stacks.pdf | ||||||
							
								
								
									
										1
									
								
								test/pdfs/bpl13210.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/bpl13210.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://h20000.www2.hp.com/bc/docs/support/SupportManual/bpl13210/bpl13210.pdf | ||||||
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/freeculture.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/freeculture.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								test/pdfs/geothermal.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/geothermal.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://geothermal.inel.gov/publications/future_of_geothermal_energy.pdf | ||||||
							
								
								
									
										1
									
								
								test/pdfs/issue1001.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/issue1001.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://www.myhillsapartment.com/island_club/floorplans/images/links/Island_IC_brochure.pdf | ||||||
							
								
								
									
										1
									
								
								test/pdfs/issue1015.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/issue1015.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://faculty.washington.edu/fidelr/RayaPubs/TheCaseStudyMethod.pdf | ||||||
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/issue840.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/issue840.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/issue918.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/issue918.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								test/pdfs/issue919.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/issue919.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://agb.traviangames.com/Travian_AR_Terms.pdf | ||||||
							
								
								
									
										1
									
								
								test/pdfs/lista_preliminar.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/lista_preliminar.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://www.lfg.com.br/concursodebolsas/lista_preliminar_classificao.pdf | ||||||
							
								
								
									
										1
									
								
								test/pdfs/ocs.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/ocs.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://www.unibuc.ro/uploads_en/29535/10/Cyrillic_Alphabets-Chars.pdf | ||||||
							
								
								
									
										1
									
								
								test/pdfs/piperine.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/piperine.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://www.erowid.org/archive/rhodium/chemistry/3base/piperonal.pepper/piperine.pepper/465e03piperine.pdf | ||||||
							
								
								
									
										1
									
								
								test/pdfs/protectip.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/protectip.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://leahy.senate.gov/imo/media/doc/BillText-PROTECTIPAct.pdf | ||||||
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/scan-bad.pdf
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/scan-bad.pdf
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/smaskdim.pdf
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/smaskdim.pdf
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								test/pdfs/tutorial.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/tutorial.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | http://cplusplus.com/files/tutorial.pdf | ||||||
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/type4psfunc.pdf
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/type4psfunc.pdf
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -12,6 +12,7 @@ DOC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),"..")) | |||||||
| ANAL = True | ANAL = True | ||||||
| DEFAULT_MANIFEST_FILE = 'test_manifest.json' | DEFAULT_MANIFEST_FILE = 'test_manifest.json' | ||||||
| EQLOG_FILE = 'eq.log' | EQLOG_FILE = 'eq.log' | ||||||
|  | BROWSERLOG_FILE = 'browser.log' | ||||||
| REFDIR = 'ref' | REFDIR = 'ref' | ||||||
| TMPDIR = 'tmp' | TMPDIR = 'tmp' | ||||||
| VERBOSE = False | VERBOSE = False | ||||||
| @ -229,6 +230,7 @@ class BaseBrowserCommand(object): | |||||||
|     def setup(self): |     def setup(self): | ||||||
|         self.tempDir = tempfile.mkdtemp() |         self.tempDir = tempfile.mkdtemp() | ||||||
|         self.profileDir = os.path.join(self.tempDir, "profile") |         self.profileDir = os.path.join(self.tempDir, "profile") | ||||||
|  |         self.browserLog = open(BROWSERLOG_FILE, "w") | ||||||
| 
 | 
 | ||||||
|     def teardown(self): |     def teardown(self): | ||||||
|         # If the browser is still running, wait up to ten seconds for it to quit |         # If the browser is still running, wait up to ten seconds for it to quit | ||||||
| @ -245,6 +247,8 @@ class BaseBrowserCommand(object): | |||||||
|         if self.tempDir is not None and os.path.exists(self.tempDir): |         if self.tempDir is not None and os.path.exists(self.tempDir): | ||||||
|             shutil.rmtree(self.tempDir) |             shutil.rmtree(self.tempDir) | ||||||
| 
 | 
 | ||||||
|  |         self.browserLog.close() | ||||||
|  | 
 | ||||||
|     def start(self, url): |     def start(self, url): | ||||||
|         raise Exception("Can't start BaseBrowserCommand") |         raise Exception("Can't start BaseBrowserCommand") | ||||||
| 
 | 
 | ||||||
| @ -262,7 +266,7 @@ class FirefoxBrowserCommand(BaseBrowserCommand): | |||||||
|         if platform.system() == "Darwin": |         if platform.system() == "Darwin": | ||||||
|             cmds.append("-foreground") |             cmds.append("-foreground") | ||||||
|         cmds.extend(["-no-remote", "-profile", self.profileDir, url]) |         cmds.extend(["-no-remote", "-profile", self.profileDir, url]) | ||||||
|         self.process = subprocess.Popen(cmds) |         self.process = subprocess.Popen(cmds, stdout = self.browserLog, stderr = self.browserLog) | ||||||
| 
 | 
 | ||||||
| class ChromeBrowserCommand(BaseBrowserCommand): | class ChromeBrowserCommand(BaseBrowserCommand): | ||||||
|     def _fixupMacPath(self): |     def _fixupMacPath(self): | ||||||
| @ -272,7 +276,7 @@ class ChromeBrowserCommand(BaseBrowserCommand): | |||||||
|         cmds = [self.path] |         cmds = [self.path] | ||||||
|         cmds.extend(["--user-data-dir=%s" % self.profileDir, |         cmds.extend(["--user-data-dir=%s" % self.profileDir, | ||||||
|                      "--no-first-run", "--disable-sync", url]) |                      "--no-first-run", "--disable-sync", url]) | ||||||
|         self.process = subprocess.Popen(cmds) |         self.process = subprocess.Popen(cmds, stdout = self.browserLog, stderr = self.browserLog) | ||||||
| 
 | 
 | ||||||
| def makeBrowserCommand(browser): | def makeBrowserCommand(browser): | ||||||
|     path = browser["path"].lower() |     path = browser["path"].lower() | ||||||
|  | |||||||
| @ -17,13 +17,13 @@ | |||||||
|        "rounds": 1, |        "rounds": 1, | ||||||
|        "type": "load" |        "type": "load" | ||||||
|     }, |     }, | ||||||
|     {  "id": "intelisa-load", |     {  "id": "intelisa-eq", | ||||||
|        "file": "pdfs/intelisa.pdf", |        "file": "pdfs/intelisa.pdf", | ||||||
|        "md5": "f5712097d29287a97f1278839814f682", |        "md5": "f5712097d29287a97f1278839814f682", | ||||||
|        "md5": "f3ed5487d1afa34d8b77c0c734a95c79", |  | ||||||
|        "link": true, |        "link": true, | ||||||
|  |        "pageLimit": 100, | ||||||
|        "rounds": 1, |        "rounds": 1, | ||||||
|        "type": "load" |        "type": "eq" | ||||||
|     }, |     }, | ||||||
|     {  "id": "pdfspec-load", |     {  "id": "pdfspec-load", | ||||||
|        "file": "pdfs/pdf.pdf", |        "file": "pdfs/pdf.pdf", | ||||||
| @ -88,6 +88,13 @@ | |||||||
|        "rounds": 1, |        "rounds": 1, | ||||||
|        "type": "eq" |        "type": "eq" | ||||||
|     }, |     }, | ||||||
|  |     {  "id": "freeculture", | ||||||
|  |        "file": "pdfs/freeculture.pdf", | ||||||
|  |        "md5": "dcdf3a8268e6a18938a42d5149efcfca", | ||||||
|  |        "rounds": 1, | ||||||
|  |        "pageLimit": 5, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|     {  "id": "wnv_chinese-pdf", |     {  "id": "wnv_chinese-pdf", | ||||||
|        "file": "pdfs/wnv_chinese.pdf", |        "file": "pdfs/wnv_chinese.pdf", | ||||||
|        "md5": "db682638e68391125e8982d3c984841e", |        "md5": "db682638e68391125e8982d3c984841e", | ||||||
| @ -221,6 +228,12 @@ | |||||||
|        "rounds": 1, |        "rounds": 1, | ||||||
|        "type": "load" |        "type": "load" | ||||||
|     }, |     }, | ||||||
|  |     {  "id": "scan-bad", | ||||||
|  |        "file": "pdfs/scan-bad.pdf", | ||||||
|  |        "md5": "4cf988f01ab83f61aca57f406dfd6584", | ||||||
|  |        "rounds": 1, | ||||||
|  |        "type": "load" | ||||||
|  |     }, | ||||||
|     {  "id": "ibwa-bad", |     {  "id": "ibwa-bad", | ||||||
|        "file": "pdfs/ibwa-bad.pdf", |        "file": "pdfs/ibwa-bad.pdf", | ||||||
|        "md5": "6ca059d32b74ac2688ae06f727fee755", |        "md5": "6ca059d32b74ac2688ae06f727fee755", | ||||||
| @ -276,5 +289,111 @@ | |||||||
|        "link": false, |        "link": false, | ||||||
|        "rounds": 1, |        "rounds": 1, | ||||||
|        "type": "eq" |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "protectip", | ||||||
|  |        "file": "pdfs/protectip.pdf", | ||||||
|  |        "md5": "676e7a7b8f96d04825361832b1838a93", | ||||||
|  |        "link": true, | ||||||
|  |        "rounds": 1, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "piperine", | ||||||
|  |        "file": "pdfs/piperine.pdf", | ||||||
|  |        "md5": "603ca43dc5732dbba1579f122958c0c2", | ||||||
|  |        "link": true, | ||||||
|  |        "rounds": 1, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "issue840", | ||||||
|  |        "file": "pdfs/issue840.pdf", | ||||||
|  |        "md5": "20d88011dd7e3c4fb5274979094dab93", | ||||||
|  |        "rounds": 1, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "bpl13210", | ||||||
|  |        "file": "pdfs/bpl13210.pdf", | ||||||
|  |        "md5": "8a08512baa9fa95378d9ad4b995947c7", | ||||||
|  |        "link": true, | ||||||
|  |        "pageLimit": 5, | ||||||
|  |        "rounds": 1, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "tutorial", | ||||||
|  |        "file": "pdfs/tutorial.pdf", | ||||||
|  |        "md5": "6e122f618c27f3aa9a689423e3be6b8d", | ||||||
|  |        "link": true, | ||||||
|  |        "rounds": 1, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "geothermal.pdf", | ||||||
|  |        "file": "pdfs/geothermal.pdf", | ||||||
|  |        "md5": "ecffc0ce38ffdf1e90dc952f186e9a91", | ||||||
|  |        "rounds": 1, | ||||||
|  |        "link": true, | ||||||
|  |        "pageLimit": 5, | ||||||
|  |        "skipPages": [1], | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "lista_preliminar", | ||||||
|  |        "file": "pdfs/lista_preliminar.pdf", | ||||||
|  |        "md5": "4eff251319eeb660ba8a7a5cfac7787d", | ||||||
|  |        "rounds": 1, | ||||||
|  |        "link": true, | ||||||
|  |        "pageLimit": 3, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "issue919", | ||||||
|  |        "file": "pdfs/issue919.pdf", | ||||||
|  |        "md5": "3a1716a512aca4d7a8d6106bd4885d14", | ||||||
|  |        "rounds": 1, | ||||||
|  |        "link": true, | ||||||
|  |        "pageLimit": 3, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "issue918", | ||||||
|  |        "file": "pdfs/issue918.pdf", | ||||||
|  |        "md5": "d582cc0f2592ae82936589ced2a47e55", | ||||||
|  |        "rounds": 1, | ||||||
|  |        "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "issue1001", | ||||||
|  |       "file": "pdfs/issue1001.pdf", | ||||||
|  |       "md5": "0f1496e80a82a923e91d9e74c55ad94e", | ||||||
|  |       "rounds": 1, | ||||||
|  |       "link": true, | ||||||
|  |       "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "aboutstacks", | ||||||
|  |       "file": "pdfs/aboutstacks.pdf", | ||||||
|  |       "md5": "6e7c8416a293ba2d83bc8dd20c6ccf51", | ||||||
|  |       "rounds": 1, | ||||||
|  |       "link": true, | ||||||
|  |       "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "smaskdim", | ||||||
|  |       "file": "pdfs/smaskdim.pdf", | ||||||
|  |       "md5": "de80aeca7cbf79940189fd34d59671ee", | ||||||
|  |       "rounds": 1, | ||||||
|  |       "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "type4psfunc", | ||||||
|  |       "file": "pdfs/type4psfunc.pdf", | ||||||
|  |       "md5": "7e6027a02ff78577f74dccdf84e37189", | ||||||
|  |       "rounds": 1, | ||||||
|  |       "type": "eq" | ||||||
|  |     }, | ||||||
|  |     {  "id": "ocs", | ||||||
|  |       "file": "pdfs/ocs.pdf", | ||||||
|  |       "md5": "2ade57e954ae7632749cf328daeaa7a8", | ||||||
|  |       "rounds": 1, | ||||||
|  |       "link": true, | ||||||
|  |       "type": "load" | ||||||
|  |     }, | ||||||
|  |     {  "id": "issue1015", | ||||||
|  |       "file": "pdfs/issue1015.pdf", | ||||||
|  |       "md5": "b61503d1b445742b665212866afb60e2", | ||||||
|  |       "rounds": 1, | ||||||
|  |       "link": true, | ||||||
|  |       "type": "eq" | ||||||
|     } |     } | ||||||
| ] | ] | ||||||
|  | |||||||
							
								
								
									
										225
									
								
								test/unit/function_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								test/unit/function_spec.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,225 @@ | |||||||
|  | /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||||||
|  | /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | ||||||
|  | 
 | ||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | describe('function', function() { | ||||||
|  |   beforeEach(function() { | ||||||
|  |     this.addMatchers({ | ||||||
|  |       toMatchArray: function(expected) { | ||||||
|  |         var actual = this.actual; | ||||||
|  |         if (actual.length != expected.length) | ||||||
|  |           return false; | ||||||
|  |         for (var i = 0; i < expected.length; i++) { | ||||||
|  |           var a = actual[i], b = expected[i]; | ||||||
|  |           if (isArray(b)) { | ||||||
|  |             if (a.length != b.length) | ||||||
|  |               return false; | ||||||
|  |             for (var j = 0; j < a.length; j++) { | ||||||
|  |               var suba = a[j], subb = b[j]; | ||||||
|  |               if (suba !== subb) | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             if (a !== b) | ||||||
|  |               return false; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('PostScriptParser', function() { | ||||||
|  |     function parse(program) { | ||||||
|  |       var stream = new StringStream(program); | ||||||
|  |       var parser = new PostScriptParser(new PostScriptLexer(stream)); | ||||||
|  |       return parser.parse(); | ||||||
|  |     } | ||||||
|  |     it('parses empty programs', function() { | ||||||
|  |       var output = parse('{}'); | ||||||
|  |       expect(output.length).toEqual(0); | ||||||
|  |     }); | ||||||
|  |     it('parses positive numbers', function() { | ||||||
|  |       var number = 999; | ||||||
|  |       var program = parse('{ ' + number + ' }'); | ||||||
|  |       var expectedProgram = [number]; | ||||||
|  |       expect(program).toMatchArray(expectedProgram); | ||||||
|  |     }); | ||||||
|  |     it('parses negative numbers', function() { | ||||||
|  |       var number = -999; | ||||||
|  |       var program = parse('{ ' + number + ' }'); | ||||||
|  |       var expectedProgram = [number]; | ||||||
|  |       expect(program).toMatchArray(expectedProgram); | ||||||
|  |     }); | ||||||
|  |     it('parses negative floats', function() { | ||||||
|  |       var number = 3.3; | ||||||
|  |       var program = parse('{ ' + number + ' }'); | ||||||
|  |       var expectedProgram = [number]; | ||||||
|  |       expect(program).toMatchArray(expectedProgram); | ||||||
|  |     }); | ||||||
|  |     it('parses operators', function() { | ||||||
|  |       var program = parse('{ sub }'); | ||||||
|  |       var expectedProgram = ['sub']; | ||||||
|  |       expect(program).toMatchArray(expectedProgram); | ||||||
|  |     }); | ||||||
|  |     it('parses if statements', function() { | ||||||
|  |       var program = parse('{ { 99 } if }'); | ||||||
|  |       var expectedProgram = [3, 'jz', 99]; | ||||||
|  |       expect(program).toMatchArray(expectedProgram); | ||||||
|  |     }); | ||||||
|  |     it('parses ifelse statements', function() { | ||||||
|  |       var program = parse('{ { 99 } { 44 } ifelse }'); | ||||||
|  |       var expectedProgram = [5, 'jz', 99, 6, 'j', 44]; | ||||||
|  |       expect(program).toMatchArray(expectedProgram); | ||||||
|  |     }); | ||||||
|  |     it('handles missing brackets', function() { | ||||||
|  |       expect(function() { parse('{'); }).toThrow( | ||||||
|  |                   new Error('Unexpected symbol: found undefined expected 1.')); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('PostScriptEvaluator', function() { | ||||||
|  |     function evaluate(program) { | ||||||
|  |       var stream = new StringStream(program); | ||||||
|  |       var parser = new PostScriptParser(new PostScriptLexer(stream)); | ||||||
|  |       var code = parser.parse(); | ||||||
|  |       var evaluator = new PostScriptEvaluator(code); | ||||||
|  |       var output = evaluator.execute(); | ||||||
|  |       return output; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     it('pushes stack', function() { | ||||||
|  |       var stack = evaluate('{ 99 }'); | ||||||
|  |       var expectedStack = [99]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('handles if with true', function() { | ||||||
|  |       var stack = evaluate('{ 1 {99} if }'); | ||||||
|  |       var expectedStack = [99]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('handles if with false', function() { | ||||||
|  |       var stack = evaluate('{ 0 {99} if }'); | ||||||
|  |       var expectedStack = []; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('handles ifelse with true', function() { | ||||||
|  |       var stack = evaluate('{ 1 {99} {77} ifelse }'); | ||||||
|  |       var expectedStack = [99]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('handles ifelse with false', function() { | ||||||
|  |       var stack = evaluate('{ 0 {99} {77} ifelse }'); | ||||||
|  |       var expectedStack = [77]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('handles nested if', function() { | ||||||
|  |       var stack = evaluate('{ 1 {1 {77} if} if }'); | ||||||
|  |       var expectedStack = [77]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('abs', function() { | ||||||
|  |       var stack = evaluate('{ -2 abs }'); | ||||||
|  |       var expectedStack = [2]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('adds', function() { | ||||||
|  |       var stack = evaluate('{ 1 2 add }'); | ||||||
|  |       var expectedStack = [3]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('boolean ands', function() { | ||||||
|  |       var stack = evaluate('{ true false and }'); | ||||||
|  |       var expectedStack = [false]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('bitwise ands', function() { | ||||||
|  |       var stack = evaluate('{ 254 1 and }'); | ||||||
|  |       var expectedStack = [254 & 1]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     // TODO atan
 | ||||||
|  |     // TODO bitshift
 | ||||||
|  |     // TODO ceiling
 | ||||||
|  |     // TODO copy
 | ||||||
|  |     // TODO cos
 | ||||||
|  |     it('converts to int', function() { | ||||||
|  |       var stack = evaluate('{ 9.9 cvi }'); | ||||||
|  |       var expectedStack = [9]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('converts negatives to int', function() { | ||||||
|  |       var stack = evaluate('{ -9.9 cvi }'); | ||||||
|  |       var expectedStack = [-9]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     // TODO cvr
 | ||||||
|  |     // TODO div
 | ||||||
|  |     it('duplicates', function() { | ||||||
|  |       var stack = evaluate('{ 99 dup }'); | ||||||
|  |       var expectedStack = [99, 99]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     // TODO eq
 | ||||||
|  |     it('exchanges', function() { | ||||||
|  |       var stack = evaluate('{ 44 99 exch }'); | ||||||
|  |       var expectedStack = [99, 44]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     // TODO exp
 | ||||||
|  |     // TODO false
 | ||||||
|  |     // TODO floor
 | ||||||
|  |     // TODO ge
 | ||||||
|  |     // TODO gt
 | ||||||
|  |     it('divides to integer', function() { | ||||||
|  |       var stack = evaluate('{ 2 3 idiv }'); | ||||||
|  |       var expectedStack = [0]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('divides to negative integer', function() { | ||||||
|  |       var stack = evaluate('{ -2 3 idiv }'); | ||||||
|  |       var expectedStack = [0]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('duplicates index', function() { | ||||||
|  |       var stack = evaluate('{ 4 3 2 1 2 index }'); | ||||||
|  |       var expectedStack = [4, 3, 2, 1, 3]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     // TODO le
 | ||||||
|  |     // TODO ln
 | ||||||
|  |     // TODO log
 | ||||||
|  |     // TODO lt
 | ||||||
|  |     // TODO mod
 | ||||||
|  |     // TODO mul
 | ||||||
|  |     // TODO ne
 | ||||||
|  |     // TODO neg
 | ||||||
|  |     // TODO not
 | ||||||
|  |     // TODO or
 | ||||||
|  |     it('pops stack', function() { | ||||||
|  |       var stack = evaluate('{ 1 2 pop }'); | ||||||
|  |       var expectedStack = [1]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('rolls stack right', function() { | ||||||
|  |       var stack = evaluate('{ 1 3 2 2 4 1 roll }'); | ||||||
|  |       var expectedStack = [2, 1, 3, 2]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     it('rolls stack left', function() { | ||||||
|  |       var stack = evaluate('{ 1 3 2 2 4 -1 roll }'); | ||||||
|  |       var expectedStack = [3, 2, 2, 1]; | ||||||
|  |       expect(stack).toMatchArray(expectedStack); | ||||||
|  |     }); | ||||||
|  |     // TODO round
 | ||||||
|  |     // TODO sin
 | ||||||
|  |     // TODO sqrt
 | ||||||
|  |     // TODO sub
 | ||||||
|  |     // TODO true
 | ||||||
|  |     // TODO truncate
 | ||||||
|  |     // TODO xor
 | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| @ -12,5 +12,120 @@ describe('obj', function() { | |||||||
|       expect(name.name).toEqual(givenName); |       expect(name.name).toEqual(givenName); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   describe('Cmd', function() { | ||||||
|  |     it('should retain the given cmd name', function() { | ||||||
|  |       var givenCmd = 'BT'; | ||||||
|  |       var cmd = new Cmd(givenCmd); | ||||||
|  |       expect(cmd.cmd).toEqual(givenCmd); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should create only one object for a command and cache it', function() { | ||||||
|  |       var firstBT = Cmd.get('BT'); | ||||||
|  |       var secondBT = Cmd.get('BT'); | ||||||
|  |       var firstET = Cmd.get('ET'); | ||||||
|  |       var secondET = Cmd.get('ET'); | ||||||
|  |       expect(firstBT).toBe(secondBT); | ||||||
|  |       expect(firstET).toBe(secondET); | ||||||
|  |       expect(firstBT).not.toBe(firstET); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('Dict', function() { | ||||||
|  |     var checkInvalidHasValues = function(dict) { | ||||||
|  |       expect(dict.has()).toBeFalsy(); | ||||||
|  |       expect(dict.has('Prev')).toBeFalsy(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     var checkInvalidKeyValues = function(dict) { | ||||||
|  |       expect(dict.get()).toBeUndefined(); | ||||||
|  |       expect(dict.get('Prev')).toBeUndefined(); | ||||||
|  |       expect(dict.get('Decode', 'D')).toBeUndefined(); | ||||||
|  | 
 | ||||||
|  |       // Note that the getter with three arguments breaks the pattern here.
 | ||||||
|  |       expect(dict.get('FontFile', 'FontFile2', 'FontFile3')).toBeNull(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     var emptyDict, dictWithSizeKey, dictWithManyKeys; | ||||||
|  |     var storedSize = 42; | ||||||
|  |     var testFontFile = 'file1'; | ||||||
|  |     var testFontFile2 = 'file2'; | ||||||
|  |     var testFontFile3 = 'file3'; | ||||||
|  | 
 | ||||||
|  |     beforeEach(function() { | ||||||
|  |       emptyDict = new Dict(); | ||||||
|  | 
 | ||||||
|  |       dictWithSizeKey = new Dict(); | ||||||
|  |       dictWithSizeKey.set('Size', storedSize); | ||||||
|  | 
 | ||||||
|  |       dictWithManyKeys = new Dict(); | ||||||
|  |       dictWithManyKeys.set('FontFile', testFontFile); | ||||||
|  |       dictWithManyKeys.set('FontFile2', testFontFile2); | ||||||
|  |       dictWithManyKeys.set('FontFile3', testFontFile3); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return invalid values for unknown keys', function() { | ||||||
|  |       checkInvalidHasValues(emptyDict); | ||||||
|  |       checkInvalidKeyValues(emptyDict); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return correct value for stored Size key', function() { | ||||||
|  |       expect(dictWithSizeKey.has('Size')).toBeTruthy(); | ||||||
|  | 
 | ||||||
|  |       expect(dictWithSizeKey.get('Size')).toEqual(storedSize); | ||||||
|  |       expect(dictWithSizeKey.get('Prev', 'Size')).toEqual(storedSize); | ||||||
|  |       expect(dictWithSizeKey.get('Prev', 'Root', 'Size')).toEqual(storedSize); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return invalid values for unknown keys when Size key is stored', | ||||||
|  |        function() { | ||||||
|  |       checkInvalidHasValues(dictWithSizeKey); | ||||||
|  |       checkInvalidKeyValues(dictWithSizeKey); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return correct value for stored Size key with undefined value', | ||||||
|  |        function() { | ||||||
|  |       var dict = new Dict(); | ||||||
|  |       dict.set('Size'); | ||||||
|  | 
 | ||||||
|  |       expect(dict.has('Size')).toBeTruthy(); | ||||||
|  | 
 | ||||||
|  |       checkInvalidKeyValues(dict); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return correct values for multiple stored keys', function() { | ||||||
|  |       expect(dictWithManyKeys.has('FontFile')).toBeTruthy(); | ||||||
|  |       expect(dictWithManyKeys.has('FontFile2')).toBeTruthy(); | ||||||
|  |       expect(dictWithManyKeys.has('FontFile3')).toBeTruthy(); | ||||||
|  | 
 | ||||||
|  |       expect(dictWithManyKeys.get('FontFile3')).toEqual(testFontFile3); | ||||||
|  |       expect(dictWithManyKeys.get('FontFile2', 'FontFile3')) | ||||||
|  |                              .toEqual(testFontFile2); | ||||||
|  |       expect(dictWithManyKeys.get('FontFile', 'FontFile2', 'FontFile3')) | ||||||
|  |                              .toEqual(testFontFile); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should callback for each stored key', function() { | ||||||
|  |       var callbackSpy = jasmine.createSpy('spy on callback in dictionary'); | ||||||
|  | 
 | ||||||
|  |       dictWithManyKeys.forEach(callbackSpy); | ||||||
|  | 
 | ||||||
|  |       expect(callbackSpy).wasCalled(); | ||||||
|  |       expect(callbackSpy.argsForCall[0]).toEqual(['FontFile', testFontFile]); | ||||||
|  |       expect(callbackSpy.argsForCall[1]).toEqual(['FontFile2', testFontFile2]); | ||||||
|  |       expect(callbackSpy.argsForCall[2]).toEqual(['FontFile3', testFontFile3]); | ||||||
|  |       expect(callbackSpy.callCount).toEqual(3); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('Ref', function() { | ||||||
|  |     it('should retain the stored values', function() { | ||||||
|  |       var storedNum = 4; | ||||||
|  |       var storedGen = 2; | ||||||
|  |       var ref = new Ref(storedNum, storedGen); | ||||||
|  |       expect(ref.num).toEqual(storedNum); | ||||||
|  |       expect(ref.gen).toEqual(storedGen); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,11 +5,16 @@ | |||||||
| 
 | 
 | ||||||
| // Checking if the typed arrays are supported
 | // Checking if the typed arrays are supported
 | ||||||
| (function checkTypedArrayCompatibility() { | (function checkTypedArrayCompatibility() { | ||||||
|   if (typeof Uint8Array !== 'undefined') |   if (typeof Uint8Array !== 'undefined') { | ||||||
|  |     // some mobile version might not support Float64Array
 | ||||||
|  |     if (typeof Float64Array === 'undefined') | ||||||
|  |       window.Float64Array = Float32Array; | ||||||
|  | 
 | ||||||
|     return; |     return; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   function subarray(start, end) { |   function subarray(start, end) { | ||||||
|     return this.slice(start, end); |     return new TypedArray(this.slice(start, end)); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function setArrayOffset(array, offset) { |   function setArrayOffset(array, offset) { | ||||||
| @ -46,6 +51,8 @@ | |||||||
|   window.Uint32Array = TypedArray; |   window.Uint32Array = TypedArray; | ||||||
|   window.Int32Array = TypedArray; |   window.Int32Array = TypedArray; | ||||||
|   window.Uint16Array = TypedArray; |   window.Uint16Array = TypedArray; | ||||||
|  |   window.Float32Array = TypedArray; | ||||||
|  |   window.Float64Array = TypedArray; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| // Object.create() ?
 | // Object.create() ?
 | ||||||
| @ -205,3 +212,15 @@ | |||||||
|   }); |   }); | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | // HTMLElement dataset property
 | ||||||
|  | (function checkDatasetProperty() { | ||||||
|  |   var div = document.createElement('div'); | ||||||
|  |   if ('dataset' in div) | ||||||
|  |     return; // dataset property exists
 | ||||||
|  |   Object.defineProperty(HTMLElement.prototype, 'dataset', { | ||||||
|  |     get: function htmlElementDatasetGetter() { | ||||||
|  |       // adding dataset field to the actual object
 | ||||||
|  |       return (this.dataset = {}); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | })(); | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								web/images/check.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								web/images/check.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | <svg height="40" width="40" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <path d="M2.379,14.729 5.208,11.899 12.958,19.648 25.877,6.733 28.707,9.561 12.958,25.308z" fill="#333333"></path> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 188 B | 
							
								
								
									
										3
									
								
								web/images/comment.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								web/images/comment.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | <svg height="40" width="40" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |   <path d="M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z" fill="#333333"></path> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 289 B | 
| @ -15,6 +15,7 @@ body { | |||||||
| /* === Toolbar === */ | /* === Toolbar === */ | ||||||
| #controls { | #controls { | ||||||
|   background-color: #eee; |   background-color: #eee; | ||||||
|  |   background: -o-linear-gradient(bottom,#eee 0%,#fff 100%); | ||||||
|   background: -moz-linear-gradient(center bottom, #eee 0%, #fff 100%); |   background: -moz-linear-gradient(center bottom, #eee 0%, #fff 100%); | ||||||
|   background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff)); |   background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff)); | ||||||
|   border-bottom: 1px solid #666; |   border-bottom: 1px solid #666; | ||||||
| @ -82,6 +83,7 @@ span#info { | |||||||
|   bottom: 18px; |   bottom: 18px; | ||||||
|   left: -290px; |   left: -290px; | ||||||
|   transition: left 0.25s ease-in-out 1s; |   transition: left 0.25s ease-in-out 1s; | ||||||
|  |   -o-transition: left 0.25s ease-in-out 1s; | ||||||
|   -moz-transition: left 0.25s ease-in-out 1s; |   -moz-transition: left 0.25s ease-in-out 1s; | ||||||
|   -webkit-transition: left 0.25s ease-in-out 1s; |   -webkit-transition: left 0.25s ease-in-out 1s; | ||||||
|   z-index: 1; |   z-index: 1; | ||||||
| @ -90,6 +92,7 @@ span#info { | |||||||
| #sidebar:hover { | #sidebar:hover { | ||||||
|   left: 0px; |   left: 0px; | ||||||
|   transition: left 0.25s ease-in-out 0s; |   transition: left 0.25s ease-in-out 0s; | ||||||
|  |   -o-transition: left 0.25s ease-in-out 0s; | ||||||
|   -moz-transition: left 0.25s ease-in-out 0s; |   -moz-transition: left 0.25s ease-in-out 0s; | ||||||
|   -webkit-transition: left 0.25s ease-in-out 0s; |   -webkit-transition: left 0.25s ease-in-out 0s; | ||||||
| } | } | ||||||
| @ -232,6 +235,56 @@ canvas { | |||||||
|   -webkit-box-shadow: 0px 2px 10px #ff0; |   -webkit-box-shadow: 0px 2px 10px #ff0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .textLayer { | ||||||
|  |   position: absolute; | ||||||
|  |   left: 0; | ||||||
|  |   top: 0; | ||||||
|  |   right: 0; | ||||||
|  |   bottom: 0; | ||||||
|  |   color: #000; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .textLayer > div { | ||||||
|  |   color: transparent; | ||||||
|  |   position: absolute; | ||||||
|  |   line-height:1.3; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .annotComment > div { | ||||||
|  |   position: absolute; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .annotComment > img { | ||||||
|  |   position: absolute; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .annotComment > img:hover { | ||||||
|  |   cursor: pointer; | ||||||
|  |   opacity: 0.7; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .annotComment > div { | ||||||
|  |   padding: 0.2em; | ||||||
|  |   max-width: 20em; | ||||||
|  |   background-color: #F1E47B; | ||||||
|  |   box-shadow: 0px 2px 10px #333; | ||||||
|  |   -moz-box-shadow: 0px 2px 10px #333; | ||||||
|  |   -webkit-box-shadow: 0px 2px 10px #333; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .annotComment > div > h1 { | ||||||
|  |   font-weight: normal; | ||||||
|  |   font-size: 1.2em; | ||||||
|  |   border-bottom: 1px solid #000000; | ||||||
|  |   margin: 0px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* TODO: file FF bug to support ::-moz-selection:window-inactive | ||||||
|  |    so we can override the opaque grey background when the window is inactive; | ||||||
|  |    see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */ | ||||||
|  | ::selection { background:rgba(0,0,255,0.3); } | ||||||
|  | ::-moz-selection { background:rgba(0,0,255,0.3); } | ||||||
|  | 
 | ||||||
| #viewer { | #viewer { | ||||||
|   margin: 44px 0px 0px; |   margin: 44px 0px 0px; | ||||||
|   padding: 8px 0px; |   padding: 8px 0px; | ||||||
| @ -252,6 +305,38 @@ canvas { | |||||||
|   display: none; |   display: none; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #errorWrapper { | ||||||
|  |   background: none repeat scroll 0 0 #FF5555; | ||||||
|  |   color: white; | ||||||
|  |   left: 0; | ||||||
|  |   position: fixed; | ||||||
|  |   right: 0; | ||||||
|  |   top: 30px; | ||||||
|  |   z-index: 1000; | ||||||
|  |   padding: 3px; | ||||||
|  |   font-size: 0.8em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #errorMessageLeft { | ||||||
|  |   float: left; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #errorMessageRight { | ||||||
|  |   float: right; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #errorMoreInfo { | ||||||
|  |   background-color: #FFFFFF; | ||||||
|  |   color: black; | ||||||
|  |   padding: 3px; | ||||||
|  |   margin: 3px; | ||||||
|  |   width: 98%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .clearBoth { | ||||||
|  |   clear: both; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* === Printed media overrides === */ | /* === Printed media overrides === */ | ||||||
| @media print { | @media print { | ||||||
|   #sidebar { |   #sidebar { | ||||||
|  | |||||||
| @ -27,9 +27,9 @@ | |||||||
|         <script type="text/javascript" src="../src/worker.js"></script>  <!-- PDFJSSCRIPT_REMOVE --> |         <script type="text/javascript" src="../src/worker.js"></script>  <!-- PDFJSSCRIPT_REMOVE --> | ||||||
|         <script type="text/javascript" src="../external/jpgjs/jpg.js"></script>  <!-- PDFJSSCRIPT_REMOVE --> |         <script type="text/javascript" src="../external/jpgjs/jpg.js"></script>  <!-- PDFJSSCRIPT_REMOVE --> | ||||||
|         <script type="text/javascript">PDFJS.workerSrc = '../src/worker_loader.js';</script> <!-- PDFJSSCRIPT_REMOVE --> |         <script type="text/javascript">PDFJS.workerSrc = '../src/worker_loader.js';</script> <!-- PDFJSSCRIPT_REMOVE --> | ||||||
| 
 |  | ||||||
|         <script type="text/javascript" src="viewer.js"></script> |         <script type="text/javascript" src="viewer.js"></script> | ||||||
|   </head> | 
 | ||||||
|  |     </head> | ||||||
| 
 | 
 | ||||||
|   <body> |   <body> | ||||||
|     <div id="controls"> |     <div id="controls"> | ||||||
| @ -67,10 +67,11 @@ | |||||||
|         <option value="0.75">75%</option> |         <option value="0.75">75%</option> | ||||||
|         <option value="1">100%</option> |         <option value="1">100%</option> | ||||||
|         <option value="1.25">125%</option> |         <option value="1.25">125%</option> | ||||||
|         <option value="1.5" selected="selected">150%</option> |         <option value="1.5">150%</option> | ||||||
|         <option value="2">200%</option> |         <option value="2">200%</option> | ||||||
|         <option id="pageWidthOption" value="page-width">Page Width</option> |         <option id="pageWidthOption" value="page-width">Page Width</option> | ||||||
|         <option id="pageFitOption" value="page-fit">Page Fit</option> |         <option id="pageFitOption" value="page-fit">Page Fit</option> | ||||||
|  |         <option id="pageAutoOption" value="auto" selected="selected">Auto</option> | ||||||
|       </select> |       </select> | ||||||
| 
 | 
 | ||||||
|       <div class="separator"></div> |       <div class="separator"></div> | ||||||
| @ -97,6 +98,24 @@ | |||||||
| 
 | 
 | ||||||
|       <span id="info">--</span> |       <span id="info">--</span> | ||||||
|     </div> |     </div> | ||||||
|  |     <div id="errorWrapper" hidden='true'> | ||||||
|  |       <div id="errorMessageLeft"> | ||||||
|  |         <span id="errorMessage"></span> | ||||||
|  |         <button id="errorShowMore" onclick="" oncontextmenu="return false;"> | ||||||
|  |           More Information | ||||||
|  |         </button> | ||||||
|  |         <button id="errorShowLess" onclick="" oncontextmenu="return false;" hidden='true'> | ||||||
|  |           Less Information | ||||||
|  |         </button> | ||||||
|  |       </div> | ||||||
|  |       <div id="errorMessageRight"> | ||||||
|  |         <button id="errorClose" oncontextmenu="return false;"> | ||||||
|  |           Close | ||||||
|  |         </button> | ||||||
|  |       </div> | ||||||
|  |       <div class="clearBoth"></div> | ||||||
|  |       <textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea> | ||||||
|  |     </div> | ||||||
| 
 | 
 | ||||||
|     <div id="sidebar"> |     <div id="sidebar"> | ||||||
|       <div id="sidebarBox"> |       <div id="sidebarBox"> | ||||||
| @ -121,4 +140,3 @@ | |||||||
|     <div id="viewer"></div> |     <div id="viewer"></div> | ||||||
|   </body> |   </body> | ||||||
| </html> | </html> | ||||||
| 
 |  | ||||||
|  | |||||||
							
								
								
									
										649
									
								
								web/viewer.js
									
									
									
									
									
								
							
							
						
						
									
										649
									
								
								web/viewer.js
									
									
									
									
									
								
							| @ -4,14 +4,15 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf'; | var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf'; | ||||||
| var kDefaultScale = 1.5; | var kDefaultScale = 'auto'; | ||||||
| var kDefaultScaleDelta = 1.1; | var kDefaultScaleDelta = 1.1; | ||||||
| var kCacheSize = 20; | var kCacheSize = 20; | ||||||
| var kCssUnits = 96.0 / 72.0; | var kCssUnits = 96.0 / 72.0; | ||||||
| var kScrollbarPadding = 40; | var kScrollbarPadding = 40; | ||||||
| var kMinScale = 0.25; | var kMinScale = 0.25; | ||||||
| var kMaxScale = 4.0; | var kMaxScale = 4.0; | ||||||
| 
 | var kImageDirectory = './images/'; | ||||||
|  | var kSettingsMemory = 20; | ||||||
| 
 | 
 | ||||||
| var Cache = function cacheCache(size) { | var Cache = function cacheCache(size) { | ||||||
|   var data = []; |   var data = []; | ||||||
| @ -25,16 +26,136 @@ var Cache = function cacheCache(size) { | |||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | var RenderingQueue = (function RenderingQueueClosure() { | ||||||
|  |   function RenderingQueue() { | ||||||
|  |     this.items = []; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   RenderingQueue.prototype = { | ||||||
|  |     enqueueDraw: function RenderingQueueEnqueueDraw(item) { | ||||||
|  |       if (!item.drawingRequired()) | ||||||
|  |         return; // as no redraw required, no need for queueing.
 | ||||||
|  | 
 | ||||||
|  |       if ('rendering' in item) | ||||||
|  |         return; // is already in the queue
 | ||||||
|  | 
 | ||||||
|  |       item.rendering = true; | ||||||
|  |       this.items.push(item); | ||||||
|  |       if (this.items.length > 1) | ||||||
|  |         return; // not first item
 | ||||||
|  | 
 | ||||||
|  |       item.draw(this.continueExecution.bind(this)); | ||||||
|  |     }, | ||||||
|  |     continueExecution: function RenderingQueueContinueExecution() { | ||||||
|  |       var item = this.items.shift(); | ||||||
|  |       delete item.rendering; | ||||||
|  | 
 | ||||||
|  |       if (this.items.length == 0) | ||||||
|  |         return; // queue is empty
 | ||||||
|  | 
 | ||||||
|  |       item = this.items[0]; | ||||||
|  |       item.draw(this.continueExecution.bind(this)); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return RenderingQueue; | ||||||
|  | })(); | ||||||
|  | 
 | ||||||
|  | // Settings Manager - This is a utility for saving settings
 | ||||||
|  | // First we see if localStorage is available, FF bug #495747
 | ||||||
|  | // If not, we use FUEL in FF
 | ||||||
|  | var Settings = (function SettingsClosure() { | ||||||
|  |   var isLocalStorageEnabled = (function localStorageEnabledTest() { | ||||||
|  |     try { | ||||||
|  |       localStorage; | ||||||
|  |     } catch (e) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   })(); | ||||||
|  |   var extPrefix = 'extensions.uriloader@pdf.js'; | ||||||
|  |   var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled; | ||||||
|  |   var inPrivateBrowsing = false; | ||||||
|  |   if (isExtension) { | ||||||
|  |     var pbs = Components.classes['@mozilla.org/privatebrowsing;1'] | ||||||
|  |               .getService(Components.interfaces.nsIPrivateBrowsingService); | ||||||
|  |     inPrivateBrowsing = pbs.privateBrowsingEnabled; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function Settings(fingerprint) { | ||||||
|  |     var database = null; | ||||||
|  |     var index; | ||||||
|  |     if (inPrivateBrowsing) | ||||||
|  |       return false; | ||||||
|  |     else if (isExtension) | ||||||
|  |       database = Application.prefs.getValue(extPrefix + '.database', '{}'); | ||||||
|  |     else if (isLocalStorageEnabled) | ||||||
|  |       database = localStorage.getItem('database') || '{}'; | ||||||
|  |     else | ||||||
|  |       return false; | ||||||
|  | 
 | ||||||
|  |     database = JSON.parse(database); | ||||||
|  |     if (!('files' in database)) | ||||||
|  |       database.files = []; | ||||||
|  |     if (database.files.length >= kSettingsMemory) | ||||||
|  |       database.files.shift(); | ||||||
|  |     for (var i = 0, length = database.files.length; i < length; i++) { | ||||||
|  |       var branch = database.files[i]; | ||||||
|  |       if (branch.fingerprint == fingerprint) { | ||||||
|  |         index = i; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (typeof index != 'number') | ||||||
|  |       index = database.files.push({fingerprint: fingerprint}) - 1; | ||||||
|  |     this.file = database.files[index]; | ||||||
|  |     this.database = database; | ||||||
|  |     if (isExtension) | ||||||
|  |       Application.prefs.setValue(extPrefix + '.database', | ||||||
|  |           JSON.stringify(database)); | ||||||
|  |     else if (isLocalStorageEnabled) | ||||||
|  |       localStorage.setItem('database', JSON.stringify(database)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Settings.prototype = { | ||||||
|  |     set: function settingsSet(name, val) { | ||||||
|  |       if (inPrivateBrowsing) | ||||||
|  |         return false; | ||||||
|  |       var file = this.file; | ||||||
|  |       file[name] = val; | ||||||
|  |       if (isExtension) | ||||||
|  |         Application.prefs.setValue(extPrefix + '.database', | ||||||
|  |             JSON.stringify(this.database)); | ||||||
|  |       else if (isLocalStorageEnabled) | ||||||
|  |         localStorage.setItem('database', JSON.stringify(this.database)); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     get: function settingsGet(name, defaultValue) { | ||||||
|  |       if (inPrivateBrowsing) | ||||||
|  |         return defaultValue; | ||||||
|  |       else | ||||||
|  |         return this.file[name] || defaultValue; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return Settings; | ||||||
|  | })(); | ||||||
|  | 
 | ||||||
| var cache = new Cache(kCacheSize); | var cache = new Cache(kCacheSize); | ||||||
|  | var renderingQueue = new RenderingQueue(); | ||||||
| var currentPageNumber = 1; | var currentPageNumber = 1; | ||||||
| 
 | 
 | ||||||
| var PDFView = { | var PDFView = { | ||||||
|   pages: [], |   pages: [], | ||||||
|   thumbnails: [], |   thumbnails: [], | ||||||
|   currentScale: kDefaultScale, |   currentScale: 0, | ||||||
|  |   currentScaleValue: null, | ||||||
|   initialBookmark: document.location.hash.substring(1), |   initialBookmark: document.location.hash.substring(1), | ||||||
| 
 | 
 | ||||||
|   setScale: function pdfViewSetScale(val, resetAutoSettings) { |   setScale: function pdfViewSetScale(val, resetAutoSettings) { | ||||||
|  |     if (val == this.currentScale) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|     var pages = this.pages; |     var pages = this.pages; | ||||||
|     for (var i = 0; i < pages.length; i++) |     for (var i = 0; i < pages.length; i++) | ||||||
|       pages[i].update(val * kCssUnits); |       pages[i].update(val * kCssUnits); | ||||||
| @ -55,6 +176,7 @@ var PDFView = { | |||||||
|       return; |       return; | ||||||
| 
 | 
 | ||||||
|     var scale = parseFloat(value); |     var scale = parseFloat(value); | ||||||
|  |     this.currentScaleValue = value; | ||||||
|     if (scale) { |     if (scale) { | ||||||
|       this.setScale(scale, true); |       this.setScale(scale, true); | ||||||
|       return; |       return; | ||||||
| @ -73,6 +195,10 @@ var PDFView = { | |||||||
|       this.setScale( |       this.setScale( | ||||||
|         Math.min(pageWidthScale, pageHeightScale), resetAutoSettings); |         Math.min(pageWidthScale, pageHeightScale), resetAutoSettings); | ||||||
|     } |     } | ||||||
|  |     if ('auto' == value) | ||||||
|  |       this.setScale(Math.min(1.0, pageWidthScale), resetAutoSettings); | ||||||
|  | 
 | ||||||
|  |     selectScaleOption(value); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   zoomIn: function pdfViewZoomIn() { |   zoomIn: function pdfViewZoomIn() { | ||||||
| @ -129,7 +255,14 @@ var PDFView = { | |||||||
|           if (evt.lengthComputable) |           if (evt.lengthComputable) | ||||||
|             self.progress(evt.loaded / evt.total); |             self.progress(evt.loaded / evt.total); | ||||||
|         }, |         }, | ||||||
|         error: self.error |         error: function getPdfError(e) { | ||||||
|  |           var loadingIndicator = document.getElementById('loading'); | ||||||
|  |           loadingIndicator.innerHTML = 'Error'; | ||||||
|  |           var moreInfo = { | ||||||
|  |             message: 'Unexpected server response of ' + e.target.status + '.' | ||||||
|  |           }; | ||||||
|  |           self.error('An error occurred while loading the PDF.', moreInfo); | ||||||
|  |         } | ||||||
|       }, |       }, | ||||||
|       function getPdfLoad(data) { |       function getPdfLoad(data) { | ||||||
|         self.loading = true; |         self.loading = true; | ||||||
| @ -168,7 +301,8 @@ var PDFView = { | |||||||
|         (destRef + 1); |         (destRef + 1); | ||||||
|       if (pageNumber) { |       if (pageNumber) { | ||||||
|         var pdfOpenParams = '#page=' + pageNumber; |         var pdfOpenParams = '#page=' + pageNumber; | ||||||
|         if (isName(dest[1], 'XYZ')) { |         var destKind = dest[1]; | ||||||
|  |         if ('name' in destKind && destKind.name == 'XYZ') { | ||||||
|           var scale = (dest[4] || this.currentScale); |           var scale = (dest[4] || this.currentScale); | ||||||
|           pdfOpenParams += '&zoom=' + (scale * 100); |           pdfOpenParams += '&zoom=' + (scale * 100); | ||||||
|           if (dest[2] || dest[3]) { |           if (dest[2] || dest[3]) { | ||||||
| @ -181,9 +315,48 @@ var PDFView = { | |||||||
|     return ''; |     return ''; | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   error: function pdfViewError() { |   /** | ||||||
|     var loadingIndicator = document.getElementById('loading'); |    * Show the error box. | ||||||
|     loadingIndicator.innerHTML = 'Error'; |    * @param {String} message A message that is human readable. | ||||||
|  |    * @param {Object} moreInfo (optional) Further information about the error | ||||||
|  |    *                            that is more technical.  Should have a 'message' | ||||||
|  |    *                            and optionally a 'stack' property. | ||||||
|  |    */ | ||||||
|  |   error: function pdfViewError(message, moreInfo) { | ||||||
|  |     var errorWrapper = document.getElementById('errorWrapper'); | ||||||
|  |     errorWrapper.removeAttribute('hidden'); | ||||||
|  | 
 | ||||||
|  |     var errorMessage = document.getElementById('errorMessage'); | ||||||
|  |     errorMessage.innerHTML = message; | ||||||
|  | 
 | ||||||
|  |     var closeButton = document.getElementById('errorClose'); | ||||||
|  |     closeButton.onclick = function() { | ||||||
|  |       errorWrapper.setAttribute('hidden', 'true'); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     var errorMoreInfo = document.getElementById('errorMoreInfo'); | ||||||
|  |     var moreInfoButton = document.getElementById('errorShowMore'); | ||||||
|  |     var lessInfoButton = document.getElementById('errorShowLess'); | ||||||
|  |     moreInfoButton.onclick = function() { | ||||||
|  |       errorMoreInfo.removeAttribute('hidden'); | ||||||
|  |       moreInfoButton.setAttribute('hidden', 'true'); | ||||||
|  |       lessInfoButton.removeAttribute('hidden'); | ||||||
|  |     }; | ||||||
|  |     lessInfoButton.onclick = function() { | ||||||
|  |       errorMoreInfo.setAttribute('hidden', 'true'); | ||||||
|  |       moreInfoButton.removeAttribute('hidden'); | ||||||
|  |       lessInfoButton.setAttribute('hidden', 'true'); | ||||||
|  |     }; | ||||||
|  |     moreInfoButton.removeAttribute('hidden'); | ||||||
|  |     lessInfoButton.setAttribute('hidden', 'true'); | ||||||
|  |     errorMoreInfo.value = 'PDF.JS Build: ' + PDFJS.build + '\n'; | ||||||
|  | 
 | ||||||
|  |     if (moreInfo) { | ||||||
|  |       errorMoreInfo.value += 'Message: ' + moreInfo.message; | ||||||
|  |       if (moreInfo.stack) | ||||||
|  |         errorMoreInfo.value += '\n' + 'Stack: ' + moreInfo.stack; | ||||||
|  |     } | ||||||
|  |     errorMoreInfo.rows = errorMoreInfo.value.split('\n').length - 1; | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   progress: function pdfViewProgress(level) { |   progress: function pdfViewProgress(level) { | ||||||
| @ -193,6 +366,17 @@ var PDFView = { | |||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   load: function pdfViewLoad(data, scale) { |   load: function pdfViewLoad(data, scale) { | ||||||
|  |     function bindOnAfterDraw(pageView, thumbnailView) { | ||||||
|  |       // when page is painted, using the image as thumbnail base
 | ||||||
|  |       pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() { | ||||||
|  |         thumbnailView.setImage(pageView.canvas); | ||||||
|  |         preDraw(); | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var errorWrapper = document.getElementById('errorWrapper'); | ||||||
|  |     errorWrapper.setAttribute('hidden', 'true'); | ||||||
|  | 
 | ||||||
|     var loadingIndicator = document.getElementById('loading'); |     var loadingIndicator = document.getElementById('loading'); | ||||||
|     loadingIndicator.setAttribute('hidden', 'true'); |     loadingIndicator.setAttribute('hidden', 'true'); | ||||||
| 
 | 
 | ||||||
| @ -209,28 +393,48 @@ var PDFView = { | |||||||
|     while (container.hasChildNodes()) |     while (container.hasChildNodes()) | ||||||
|       container.removeChild(container.lastChild); |       container.removeChild(container.lastChild); | ||||||
| 
 | 
 | ||||||
|     var pdf = new PDFJS.PDFDoc(data); |     var pdf; | ||||||
|  |     try { | ||||||
|  |       pdf = new PDFJS.PDFDoc(data); | ||||||
|  |     } catch (e) { | ||||||
|  |       this.error('An error occurred while reading the PDF.', e); | ||||||
|  |     } | ||||||
|     var pagesCount = pdf.numPages; |     var pagesCount = pdf.numPages; | ||||||
|  |     var id = pdf.fingerprint; | ||||||
|  |     var storedHash = null; | ||||||
|     document.getElementById('numPages').innerHTML = pagesCount; |     document.getElementById('numPages').innerHTML = pagesCount; | ||||||
|     document.getElementById('pageNumber').max = pagesCount; |     document.getElementById('pageNumber').max = pagesCount; | ||||||
|  |     PDFView.documentFingerprint = id; | ||||||
|  |     var store = PDFView.store = new Settings(id); | ||||||
|  |     if (store.get('exists', false)) { | ||||||
|  |       var page = store.get('page', '1'); | ||||||
|  |       var zoom = store.get('zoom', PDFView.currentScale); | ||||||
|  |       var left = store.get('scrollLeft', '0'); | ||||||
|  |       var top = store.get('scrollTop', '0'); | ||||||
|  | 
 | ||||||
|  |       storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     var pages = this.pages = []; |     var pages = this.pages = []; | ||||||
|     var pagesRefMap = {}; |     var pagesRefMap = {}; | ||||||
|     var thumbnails = this.thumbnails = []; |     var thumbnails = this.thumbnails = []; | ||||||
|     for (var i = 1; i <= pagesCount; i++) { |     for (var i = 1; i <= pagesCount; i++) { | ||||||
|       var page = pdf.getPage(i); |       var page = pdf.getPage(i); | ||||||
|       pages.push(new PageView(container, page, i, page.width, page.height, |       var pageView = new PageView(container, page, i, page.width, page.height, | ||||||
|                               page.stats, this.navigateTo.bind(this))); |                                   page.stats, this.navigateTo.bind(this)); | ||||||
|       thumbnails.push(new ThumbnailView(sidebar, page, i, |       var thumbnailView = new ThumbnailView(sidebar, page, i, | ||||||
|                                         page.width / page.height)); |                                             page.width / page.height); | ||||||
|  |       bindOnAfterDraw(pageView, thumbnailView); | ||||||
|  | 
 | ||||||
|  |       pages.push(pageView); | ||||||
|  |       thumbnails.push(thumbnailView); | ||||||
|       var pageRef = page.ref; |       var pageRef = page.ref; | ||||||
|       pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; |       pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.setScale(scale || kDefaultScale, true); |  | ||||||
| 
 |  | ||||||
|     this.pagesRefMap = pagesRefMap; |     this.pagesRefMap = pagesRefMap; | ||||||
|     this.destinations = pdf.catalog.destinations; |     this.destinations = pdf.catalog.destinations; | ||||||
|  | 
 | ||||||
|     if (pdf.catalog.documentOutline) { |     if (pdf.catalog.documentOutline) { | ||||||
|       this.outline = new DocumentOutlineView(pdf.catalog.documentOutline); |       this.outline = new DocumentOutlineView(pdf.catalog.documentOutline); | ||||||
|       var outlineSwitchButton = document.getElementById('outlineSwitch'); |       var outlineSwitchButton = document.getElementById('outlineSwitch'); | ||||||
| @ -238,12 +442,20 @@ var PDFView = { | |||||||
|       this.switchSidebarView('outline'); |       this.switchSidebarView('outline'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Reset the current scale, as otherwise the page's scale might not get
 | ||||||
|  |     // updated if the zoom level stayed the same.
 | ||||||
|  |     this.currentScale = 0; | ||||||
|  |     this.currentScaleValue = null; | ||||||
|     if (this.initialBookmark) { |     if (this.initialBookmark) { | ||||||
|       this.setHash(this.initialBookmark); |       this.setHash(this.initialBookmark); | ||||||
|       this.initialBookmark = null; |       this.initialBookmark = null; | ||||||
|     } |     } | ||||||
|     else |     else if (storedHash) | ||||||
|  |       this.setHash(storedHash); | ||||||
|  |     else { | ||||||
|  |       this.parseScale(scale || kDefaultScale, true); | ||||||
|       this.page = 1; |       this.page = 1; | ||||||
|  |     } | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   setHash: function pdfViewSetHash(hash) { |   setHash: function pdfViewSetHash(hash) { | ||||||
| @ -269,8 +481,16 @@ var PDFView = { | |||||||
|         if ('zoom' in params) { |         if ('zoom' in params) { | ||||||
|           var zoomArgs = params.zoom.split(','); // scale,left,top
 |           var zoomArgs = params.zoom.split(','); // scale,left,top
 | ||||||
|           // building destination array
 |           // building destination array
 | ||||||
|           var dest = [null, new Name('XYZ'), (zoomArgs[1] | 0), | 
 | ||||||
|             (zoomArgs[2] | 0), (zoomArgs[0] | 0) / 100]; |           // If the zoom value, it has to get divided by 100. If it is a string,
 | ||||||
|  |           // it should stay as it is.
 | ||||||
|  |           var zoomArg = zoomArgs[0]; | ||||||
|  |           var zoomArgNumber = parseFloat(zoomArg); | ||||||
|  |           if (zoomArgNumber) | ||||||
|  |             zoomArg = zoomArgNumber / 100; | ||||||
|  | 
 | ||||||
|  |           var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0), | ||||||
|  |             (zoomArgs[2] | 0), zoomArg]; | ||||||
|           var currentPage = this.pages[pageNumber - 1]; |           var currentPage = this.pages[pageNumber - 1]; | ||||||
|           currentPage.scrollIntoView(dest); |           currentPage.scrollIntoView(dest); | ||||||
|         } else |         } else | ||||||
| @ -294,6 +514,7 @@ var PDFView = { | |||||||
|         outlineScrollView.setAttribute('hidden', 'true'); |         outlineScrollView.setAttribute('hidden', 'true'); | ||||||
|         thumbsSwitchButton.setAttribute('data-selected', true); |         thumbsSwitchButton.setAttribute('data-selected', true); | ||||||
|         outlineSwitchButton.removeAttribute('data-selected'); |         outlineSwitchButton.removeAttribute('data-selected'); | ||||||
|  |         updateThumbViewArea(); | ||||||
|         break; |         break; | ||||||
|       case 'outline': |       case 'outline': | ||||||
|         thumbsScrollView.setAttribute('hidden', 'true'); |         thumbsScrollView.setAttribute('hidden', 'true'); | ||||||
| @ -328,6 +549,34 @@ var PDFView = { | |||||||
|       currentHeight += singlePage.height * singlePage.scale + kBottomMargin; |       currentHeight += singlePage.height * singlePage.scale + kBottomMargin; | ||||||
|     } |     } | ||||||
|     return visiblePages; |     return visiblePages; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   getVisibleThumbs: function pdfViewGetVisibleThumbs() { | ||||||
|  |     var thumbs = this.thumbnails; | ||||||
|  |     var kBottomMargin = 5; | ||||||
|  |     var visibleThumbs = []; | ||||||
|  | 
 | ||||||
|  |     var view = document.getElementById('sidebarScrollView'); | ||||||
|  |     var currentHeight = kBottomMargin; | ||||||
|  |     var top = view.scrollTop; | ||||||
|  |     for (var i = 1; i <= thumbs.length; ++i) { | ||||||
|  |       var thumb = thumbs[i - 1]; | ||||||
|  |       var thumbHeight = thumb.height * thumb.scaleY + kBottomMargin; | ||||||
|  |       if (currentHeight + thumbHeight > top) | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |       currentHeight += thumbHeight; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var bottom = top + view.clientHeight; | ||||||
|  |     for (; i <= thumbs.length && currentHeight < bottom; ++i) { | ||||||
|  |       var singleThumb = thumbs[i - 1]; | ||||||
|  |       visibleThumbs.push({ id: singleThumb.id, y: currentHeight, | ||||||
|  |                           view: singleThumb }); | ||||||
|  |       currentHeight += singleThumb.height * singleThumb.scaleY + kBottomMargin; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return visibleThumbs; | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -360,9 +609,11 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, | |||||||
|     while (div.hasChildNodes()) |     while (div.hasChildNodes()) | ||||||
|       div.removeChild(div.lastChild); |       div.removeChild(div.lastChild); | ||||||
|     div.removeAttribute('data-loaded'); |     div.removeAttribute('data-loaded'); | ||||||
|  | 
 | ||||||
|  |     delete this.canvas; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   function setupLinks(content, scale) { |   function setupAnnotations(content, scale) { | ||||||
|     function bindLink(link, dest) { |     function bindLink(link, dest) { | ||||||
|       link.href = PDFView.getDestinationHash(dest); |       link.href = PDFView.getDestinationHash(dest); | ||||||
|       link.onclick = function pageViewSetupLinksOnclick() { |       link.onclick = function pageViewSetupLinksOnclick() { | ||||||
| @ -371,18 +622,67 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, | |||||||
|         return false; |         return false; | ||||||
|       }; |       }; | ||||||
|     } |     } | ||||||
|  |     function createElementWithStyle(tagName, item) { | ||||||
|  |       var element = document.createElement(tagName); | ||||||
|  |       element.style.left = (Math.floor(item.x - view.x) * scale) + 'px'; | ||||||
|  |       element.style.top = (Math.floor(item.y - view.y) * scale) + 'px'; | ||||||
|  |       element.style.width = Math.ceil(item.width * scale) + 'px'; | ||||||
|  |       element.style.height = Math.ceil(item.height * scale) + 'px'; | ||||||
|  |       return element; | ||||||
|  |     } | ||||||
|  |     function createCommentAnnotation(type, item) { | ||||||
|  |       var container = document.createElement('section'); | ||||||
|  |       container.className = 'annotComment'; | ||||||
| 
 | 
 | ||||||
|     var links = content.getLinks(); |       var image = createElementWithStyle('img', item); | ||||||
|     for (var i = 0; i < links.length; i++) { |       image.src = kImageDirectory + type.toLowerCase() + '.svg'; | ||||||
|       var link = document.createElement('a'); |       var content = document.createElement('div'); | ||||||
|       link.style.left = (Math.floor(links[i].x - view.x) * scale) + 'px'; |       content.setAttribute('hidden', true); | ||||||
|       link.style.top = (Math.floor(links[i].y - view.y) * scale) + 'px'; |       var title = document.createElement('h1'); | ||||||
|       link.style.width = Math.ceil(links[i].width * scale) + 'px'; |       var text = document.createElement('p'); | ||||||
|       link.style.height = Math.ceil(links[i].height * scale) + 'px'; |       var offsetPos = Math.floor(item.x - view.x + item.width); | ||||||
|       link.href = links[i].url || ''; |       content.style.left = (offsetPos * scale) + 'px'; | ||||||
|       if (!links[i].url) |       content.style.top = (Math.floor(item.y - view.y) * scale) + 'px'; | ||||||
|         bindLink(link, ('dest' in links[i]) ? links[i].dest : null); |       title.textContent = item.title; | ||||||
|       div.appendChild(link); | 
 | ||||||
|  |       if (!item.content) { | ||||||
|  |         content.setAttribute('hidden', true); | ||||||
|  |       } else { | ||||||
|  |         text.innerHTML = item.content.replace('\n', '<br />'); | ||||||
|  |         image.addEventListener('mouseover', function annotationImageOver() { | ||||||
|  |            this.nextSibling.removeAttribute('hidden'); | ||||||
|  |         }, false); | ||||||
|  | 
 | ||||||
|  |         image.addEventListener('mouseout', function annotationImageOut() { | ||||||
|  |            this.nextSibling.setAttribute('hidden', true); | ||||||
|  |         }, false); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       content.appendChild(title); | ||||||
|  |       content.appendChild(text); | ||||||
|  |       container.appendChild(image); | ||||||
|  |       container.appendChild(content); | ||||||
|  | 
 | ||||||
|  |       return container; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var items = content.getAnnotations(); | ||||||
|  |     for (var i = 0; i < items.length; i++) { | ||||||
|  |       var item = items[i]; | ||||||
|  |       switch (item.type) { | ||||||
|  |         case 'Link': | ||||||
|  |           var link = createElementWithStyle('a', item); | ||||||
|  |           link.href = item.url || ''; | ||||||
|  |           if (!item.url) | ||||||
|  |             bindLink(link, ('dest' in item) ? item.dest : null); | ||||||
|  |           div.appendChild(link); | ||||||
|  |           break; | ||||||
|  |         case 'Text': | ||||||
|  |           var comment = createCommentAnnotation(item.name, item); | ||||||
|  |           if (comment) | ||||||
|  |             div.appendChild(comment); | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -441,7 +741,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, | |||||||
|       ]; |       ]; | ||||||
| 
 | 
 | ||||||
|       if (scale && scale !== PDFView.currentScale) |       if (scale && scale !== PDFView.currentScale) | ||||||
|         PDFView.setScale(scale, true); |         PDFView.parseScale(scale, true); | ||||||
| 
 | 
 | ||||||
|       setTimeout(function pageViewScrollIntoViewRelayout() { |       setTimeout(function pageViewScrollIntoViewRelayout() { | ||||||
|         // letting page to re-layout before scrolling
 |         // letting page to re-layout before scrolling
 | ||||||
| @ -464,16 +764,26 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, | |||||||
|       }, 0); |       }, 0); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   this.draw = function pageviewDraw() { |   this.drawingRequired = function() { | ||||||
|     if (div.hasChildNodes()) { |     return !div.hasChildNodes(); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   this.draw = function pageviewDraw(callback) { | ||||||
|  |     if (!this.drawingRequired()) { | ||||||
|       this.updateStats(); |       this.updateStats(); | ||||||
|       return false; |       callback(); | ||||||
|  |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var canvas = document.createElement('canvas'); |     var canvas = document.createElement('canvas'); | ||||||
|     canvas.id = 'page' + this.id; |     canvas.id = 'page' + this.id; | ||||||
|     canvas.mozOpaque = true; |     canvas.mozOpaque = true; | ||||||
|     div.appendChild(canvas); |     div.appendChild(canvas); | ||||||
|  |     this.canvas = canvas; | ||||||
|  | 
 | ||||||
|  |     var textLayer = document.createElement('div'); | ||||||
|  |     textLayer.className = 'textLayer'; | ||||||
|  |     div.appendChild(textLayer); | ||||||
| 
 | 
 | ||||||
|     var scale = this.scale; |     var scale = this.scale; | ||||||
|     canvas.width = pageWidth * scale; |     canvas.width = pageWidth * scale; | ||||||
| @ -487,12 +797,21 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, | |||||||
|     ctx.translate(-this.x * scale, -this.y * scale); |     ctx.translate(-this.x * scale, -this.y * scale); | ||||||
| 
 | 
 | ||||||
|     stats.begin = Date.now(); |     stats.begin = Date.now(); | ||||||
|     this.content.startRendering(ctx, this.updateStats); |     this.content.startRendering(ctx, | ||||||
|  |       (function pageViewDrawCallback(error) { | ||||||
|  |         if (error) | ||||||
|  |           PDFView.error('An error occurred while rendering the page.', error); | ||||||
|  |         this.updateStats(); | ||||||
|  |         if (this.onAfterDraw) | ||||||
|  |           this.onAfterDraw(); | ||||||
| 
 | 
 | ||||||
|     setupLinks(this.content, this.scale); |         cache.push(this); | ||||||
|  |         callback(); | ||||||
|  |       }).bind(this), new TextLayerBuilder(textLayer) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     setupAnnotations(this.content, this.scale); | ||||||
|     div.setAttribute('data-loaded', true); |     div.setAttribute('data-loaded', true); | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   this.updateStats = function pageViewUpdateStats() { |   this.updateStats = function pageViewUpdateStats() { | ||||||
| @ -511,6 +830,19 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) { | |||||||
|     return false; |     return false; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |   var view = page.view; | ||||||
|  |   this.width = view.width; | ||||||
|  |   this.height = view.height; | ||||||
|  |   this.id = id; | ||||||
|  | 
 | ||||||
|  |   var maxThumbSize = 134; | ||||||
|  |   var canvasWidth = pageRatio >= 1 ? maxThumbSize : | ||||||
|  |     maxThumbSize * pageRatio; | ||||||
|  |   var canvasHeight = pageRatio <= 1 ? maxThumbSize : | ||||||
|  |     maxThumbSize / pageRatio; | ||||||
|  |   var scaleX = this.scaleX = (canvasWidth / this.width); | ||||||
|  |   var scaleY = this.scaleY = (canvasHeight / this.height); | ||||||
|  | 
 | ||||||
|   var div = document.createElement('div'); |   var div = document.createElement('div'); | ||||||
|   div.id = 'thumbnailContainer' + id; |   div.id = 'thumbnailContainer' + id; | ||||||
|   div.className = 'thumbnail'; |   div.className = 'thumbnail'; | ||||||
| @ -518,19 +850,15 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) { | |||||||
|   anchor.appendChild(div); |   anchor.appendChild(div); | ||||||
|   container.appendChild(anchor); |   container.appendChild(anchor); | ||||||
| 
 | 
 | ||||||
|   this.draw = function thumbnailViewDraw() { |   this.hasImage = false; | ||||||
|     if (div.hasChildNodes()) |  | ||||||
|       return; |  | ||||||
| 
 | 
 | ||||||
|  |   function getPageDrawContext() { | ||||||
|     var canvas = document.createElement('canvas'); |     var canvas = document.createElement('canvas'); | ||||||
|     canvas.id = 'thumbnail' + id; |     canvas.id = 'thumbnail' + id; | ||||||
|     canvas.mozOpaque = true; |     canvas.mozOpaque = true; | ||||||
| 
 | 
 | ||||||
|     var maxThumbSize = 134; |     canvas.width = canvasWidth; | ||||||
|     canvas.width = pageRatio >= 1 ? maxThumbSize : |     canvas.height = canvasHeight; | ||||||
|       maxThumbSize * pageRatio; |  | ||||||
|     canvas.height = pageRatio <= 1 ? maxThumbSize : |  | ||||||
|       maxThumbSize / pageRatio; |  | ||||||
| 
 | 
 | ||||||
|     div.setAttribute('data-loaded', true); |     div.setAttribute('data-loaded', true); | ||||||
|     div.appendChild(canvas); |     div.appendChild(canvas); | ||||||
| @ -542,14 +870,37 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) { | |||||||
|     ctx.restore(); |     ctx.restore(); | ||||||
| 
 | 
 | ||||||
|     var view = page.view; |     var view = page.view; | ||||||
|     var scaleX = (canvas.width / page.width); |  | ||||||
|     var scaleY = (canvas.height / page.height); |  | ||||||
|     ctx.translate(-view.x * scaleX, -view.y * scaleY); |     ctx.translate(-view.x * scaleX, -view.y * scaleY); | ||||||
|     div.style.width = (view.width * scaleX) + 'px'; |     div.style.width = (view.width * scaleX) + 'px'; | ||||||
|     div.style.height = (view.height * scaleY) + 'px'; |     div.style.height = (view.height * scaleY) + 'px'; | ||||||
|     div.style.lineHeight = (view.height * scaleY) + 'px'; |     div.style.lineHeight = (view.height * scaleY) + 'px'; | ||||||
| 
 | 
 | ||||||
|     page.startRendering(ctx, function thumbnailViewDrawStartRendering() {}); |     return ctx; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   this.draw = function thumbnailViewDraw(callback) { | ||||||
|  |     if (this.hasImage) { | ||||||
|  |       callback(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var ctx = getPageDrawContext(); | ||||||
|  |     page.startRendering(ctx, function thumbnailViewDrawStartRendering() { | ||||||
|  |       callback(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     this.hasImage = true; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   this.setImage = function thumbnailViewSetImage(img) { | ||||||
|  |     if (this.hasImage) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |     var ctx = getPageDrawContext(); | ||||||
|  |     ctx.drawImage(img, 0, 0, img.width, img.height, | ||||||
|  |                   0, 0, ctx.canvas.width, ctx.canvas.height); | ||||||
|  | 
 | ||||||
|  |     this.hasImage = true; | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -589,6 +940,53 @@ var DocumentOutlineView = function documentOutlineView(outline) { | |||||||
|   } |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { | ||||||
|  |   this.textLayerDiv = textLayerDiv; | ||||||
|  | 
 | ||||||
|  |   this.beginLayout = function textLayerBuilderBeginLayout() { | ||||||
|  |     this.textDivs = []; | ||||||
|  |     this.textLayerQueue = []; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   this.endLayout = function textLayerBuilderEndLayout() { | ||||||
|  |     var self = this; | ||||||
|  |     var textDivs = this.textDivs; | ||||||
|  |     var textLayerDiv = this.textLayerDiv; | ||||||
|  |     this.textLayerTimer = setInterval(function renderTextLayer() { | ||||||
|  |       if (textDivs.length === 0) { | ||||||
|  |         clearInterval(self.textLayerTimer); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       var textDiv = textDivs.shift(); | ||||||
|  |       if (textDiv.dataset.textLength >= 1) { // avoid div by zero
 | ||||||
|  |         textLayerDiv.appendChild(textDiv); | ||||||
|  |         // Adjust div width (via letterSpacing) to match canvas text
 | ||||||
|  |         // Due to the .offsetWidth calls, this is slow
 | ||||||
|  |         textDiv.style.letterSpacing = | ||||||
|  |           ((textDiv.dataset.canvasWidth - textDiv.offsetWidth) / | ||||||
|  |             (textDiv.dataset.textLength - 1)) + 'px'; | ||||||
|  |       } | ||||||
|  |     }, 0); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   this.appendText = function textLayerBuilderAppendText(text, | ||||||
|  |                                                         fontName, fontSize) { | ||||||
|  |     var textDiv = document.createElement('div'); | ||||||
|  | 
 | ||||||
|  |     // vScale and hScale already contain the scaling to pixel units
 | ||||||
|  |     var fontHeight = fontSize * text.geom.vScale; | ||||||
|  |     textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale; | ||||||
|  | 
 | ||||||
|  |     textDiv.style.fontSize = fontHeight + 'px'; | ||||||
|  |     textDiv.style.fontFamily = fontName || 'sans-serif'; | ||||||
|  |     textDiv.style.left = text.geom.x + 'px'; | ||||||
|  |     textDiv.style.top = (text.geom.y - fontHeight) + 'px'; | ||||||
|  |     textDiv.textContent = text.str; | ||||||
|  |     textDiv.dataset.textLength = text.length; | ||||||
|  |     this.textDivs.push(textDiv); | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| window.addEventListener('load', function webViewerLoad(evt) { | window.addEventListener('load', function webViewerLoad(evt) { | ||||||
|   var params = document.location.search.substring(1).split('&'); |   var params = document.location.search.substring(1).split('&'); | ||||||
|   for (var i = 0; i < params.length; i++) { |   for (var i = 0; i < params.length; i++) { | ||||||
| @ -596,44 +994,95 @@ window.addEventListener('load', function webViewerLoad(evt) { | |||||||
|     params[unescape(param[0])] = unescape(param[1]); |     params[unescape(param[0])] = unescape(param[1]); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   var scale = ('scale' in params) ? params.scale : kDefaultScale; |   var scale = ('scale' in params) ? params.scale : 0; | ||||||
|   PDFView.open(params.file || kDefaultURL, parseFloat(scale)); |   PDFView.open(params.file || kDefaultURL, parseFloat(scale)); | ||||||
| 
 | 
 | ||||||
|   if (!window.File || !window.FileReader || !window.FileList || !window.Blob) |   if (!window.File || !window.FileReader || !window.FileList || !window.Blob) | ||||||
|     document.getElementById('fileInput').setAttribute('hidden', 'true'); |     document.getElementById('fileInput').setAttribute('hidden', 'true'); | ||||||
|   else |   else | ||||||
|     document.getElementById('fileInput').value = null; |     document.getElementById('fileInput').value = null; | ||||||
|  | 
 | ||||||
|  |   if ('disableWorker' in params) | ||||||
|  |     PDFJS.disableWorker = params['disableWorker'] === 'true' ? true : false; | ||||||
|  | 
 | ||||||
|  |   var sidebarScrollView = document.getElementById('sidebarScrollView'); | ||||||
|  |   sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true); | ||||||
| }, true); | }, true); | ||||||
| 
 | 
 | ||||||
| window.addEventListener('unload', function webViewerUnload(evt) { | window.addEventListener('unload', function webViewerUnload(evt) { | ||||||
|   window.scrollTo(0, 0); |   window.scrollTo(0, 0); | ||||||
| }, true); | }, true); | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Render the next not yet visible page already such that it is | ||||||
|  |  * hopefully ready once the user scrolls to it. | ||||||
|  |  */ | ||||||
|  | function preDraw() { | ||||||
|  |   var pages = PDFView.pages; | ||||||
|  |   var visible = PDFView.getVisiblePages(); | ||||||
|  |   var last = visible[visible.length - 1]; | ||||||
|  |   // PageView.id is the actual page number, which is + 1 compared
 | ||||||
|  |   // to the index in `pages`. That means, pages[last.id] is the next
 | ||||||
|  |   // PageView instance.
 | ||||||
|  |   if (pages[last.id] && pages[last.id].drawingRequired()) { | ||||||
|  |     renderingQueue.enqueueDraw(pages[last.id]); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   // If there is nothing to draw on the next page, maybe the user
 | ||||||
|  |   // is scrolling up, so, let's try to render the next page *before*
 | ||||||
|  |   // the first visible page
 | ||||||
|  |   if (pages[visible[0].id - 2]) { | ||||||
|  |     renderingQueue.enqueueDraw(pages[visible[0].id - 2]); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function updateViewarea() { | function updateViewarea() { | ||||||
|   var visiblePages = PDFView.getVisiblePages(); |   var visiblePages = PDFView.getVisiblePages(); | ||||||
|  |   var pageToDraw; | ||||||
|   for (var i = 0; i < visiblePages.length; i++) { |   for (var i = 0; i < visiblePages.length; i++) { | ||||||
|     var page = visiblePages[i]; |     var page = visiblePages[i]; | ||||||
|     if (PDFView.pages[page.id - 1].draw()) |     var pageObj = PDFView.pages[page.id - 1]; | ||||||
|       cache.push(page.view); | 
 | ||||||
|  |     pageToDraw |= pageObj.drawingRequired(); | ||||||
|  |     renderingQueue.enqueueDraw(pageObj); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (!visiblePages.length) |   if (!visiblePages.length) | ||||||
|     return; |     return; | ||||||
| 
 | 
 | ||||||
|  |   // If there is no need to draw a page that is currenlty visible, preDraw the
 | ||||||
|  |   // next page the user might scroll to.
 | ||||||
|  |   if (!pageToDraw) { | ||||||
|  |     preDraw(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   updateViewarea.inProgress = true; // used in "set page"
 |   updateViewarea.inProgress = true; // used in "set page"
 | ||||||
|   var currentId = PDFView.page; |   var currentId = PDFView.page; | ||||||
|   var firstPage = visiblePages[0]; |   var firstPage = visiblePages[0]; | ||||||
|   PDFView.page = firstPage.id; |   PDFView.page = firstPage.id; | ||||||
|   updateViewarea.inProgress = false; |   updateViewarea.inProgress = false; | ||||||
| 
 | 
 | ||||||
|  |   var currentScale = PDFView.currentScale; | ||||||
|  |   var currentScaleValue = PDFView.currentScaleValue; | ||||||
|  |   var normalizedScaleValue = currentScaleValue == currentScale ? | ||||||
|  |     currentScale * 100 : currentScaleValue; | ||||||
|  | 
 | ||||||
|   var kViewerTopMargin = 52; |   var kViewerTopMargin = 52; | ||||||
|   var pageNumber = firstPage.id; |   var pageNumber = firstPage.id; | ||||||
|   var pdfOpenParams = '#page=' + pageNumber; |   var pdfOpenParams = '#page=' + pageNumber; | ||||||
|   pdfOpenParams += '&zoom=' + Math.round(PDFView.currentScale * 100); |   pdfOpenParams += '&zoom=' + normalizedScaleValue; | ||||||
|   var currentPage = PDFView.pages[pageNumber - 1]; |   var currentPage = PDFView.pages[pageNumber - 1]; | ||||||
|   var topLeft = currentPage.getPagePoint(window.pageXOffset, |   var topLeft = currentPage.getPagePoint(window.pageXOffset, | ||||||
|     window.pageYOffset - firstPage.y - kViewerTopMargin); |     window.pageYOffset - firstPage.y - kViewerTopMargin); | ||||||
|   pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y); |   pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y); | ||||||
|  | 
 | ||||||
|  |   var store = PDFView.store; | ||||||
|  |   store.set('exists', true); | ||||||
|  |   store.set('page', pageNumber); | ||||||
|  |   store.set('zoom', normalizedScaleValue); | ||||||
|  |   store.set('scrollLeft', Math.round(topLeft.x)); | ||||||
|  |   store.set('scrollTop', Math.round(topLeft.y)); | ||||||
|  | 
 | ||||||
|   document.getElementById('viewBookmark').href = pdfOpenParams; |   document.getElementById('viewBookmark').href = pdfOpenParams; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -641,9 +1090,33 @@ window.addEventListener('scroll', function webViewerScroll(evt) { | |||||||
|   updateViewarea(); |   updateViewarea(); | ||||||
| }, true); | }, true); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | var thumbnailTimer; | ||||||
|  | 
 | ||||||
|  | function updateThumbViewArea() { | ||||||
|  |   // Only render thumbs after pausing scrolling for this amount of time
 | ||||||
|  |   // (makes UI more responsive)
 | ||||||
|  |   var delay = 50; // in ms
 | ||||||
|  | 
 | ||||||
|  |   if (thumbnailTimer) | ||||||
|  |     clearTimeout(thumbnailTimer); | ||||||
|  | 
 | ||||||
|  |   thumbnailTimer = setTimeout(function() { | ||||||
|  |     var visibleThumbs = PDFView.getVisibleThumbs(); | ||||||
|  |     for (var i = 0; i < visibleThumbs.length; i++) { | ||||||
|  |       var thumb = visibleThumbs[i]; | ||||||
|  |       renderingQueue.enqueueDraw(PDFView.thumbnails[thumb.id - 1]); | ||||||
|  |     } | ||||||
|  |   }, delay); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | window.addEventListener('transitionend', updateThumbViewArea, true); | ||||||
|  | window.addEventListener('webkitTransitionEnd', updateThumbViewArea, true); | ||||||
|  | 
 | ||||||
| window.addEventListener('resize', function webViewerResize(evt) { | window.addEventListener('resize', function webViewerResize(evt) { | ||||||
|   if (document.getElementById('pageWidthOption').selected || |   if (document.getElementById('pageWidthOption').selected || | ||||||
|       document.getElementById('pageFitOption').selected) |       document.getElementById('pageFitOption').selected || | ||||||
|  |       document.getElementById('pageAutoOption').selected) | ||||||
|       PDFView.parseScale(document.getElementById('scaleSelect').value); |       PDFView.parseScale(document.getElementById('scaleSelect').value); | ||||||
|   updateViewarea(); |   updateViewarea(); | ||||||
| }); | }); | ||||||
| @ -673,7 +1146,6 @@ window.addEventListener('change', function webViewerChange(evt) { | |||||||
|   // implemented in Firefox.
 |   // implemented in Firefox.
 | ||||||
|   var file = files[0]; |   var file = files[0]; | ||||||
|   fileReader.readAsBinaryString(file); |   fileReader.readAsBinaryString(file); | ||||||
| 
 |  | ||||||
|   document.title = file.name; |   document.title = file.name; | ||||||
| 
 | 
 | ||||||
|   // URL does not reflect proper document location - hiding some icons.
 |   // URL does not reflect proper document location - hiding some icons.
 | ||||||
| @ -681,35 +1153,9 @@ window.addEventListener('change', function webViewerChange(evt) { | |||||||
|   document.getElementById('download').setAttribute('hidden', 'true'); |   document.getElementById('download').setAttribute('hidden', 'true'); | ||||||
| }, true); | }, true); | ||||||
| 
 | 
 | ||||||
| window.addEventListener('transitionend', function webViewerTransitionend(evt) { | function selectScaleOption(value) { | ||||||
|   var pageIndex = 0; |  | ||||||
|   var pagesCount = PDFView.pages.length; |  | ||||||
| 
 |  | ||||||
|   var container = document.getElementById('sidebarView'); |  | ||||||
|   container._interval = window.setInterval(function interval() { |  | ||||||
|     if (pageIndex >= pagesCount) { |  | ||||||
|       window.clearInterval(container._interval); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     PDFView.thumbnails[pageIndex++].draw(); |  | ||||||
|   }, 500); |  | ||||||
| }, true); |  | ||||||
| 
 |  | ||||||
| window.addEventListener('scalechange', function scalechange(evt) { |  | ||||||
|   var customScaleOption = document.getElementById('customScaleOption'); |  | ||||||
|   customScaleOption.selected = false; |  | ||||||
| 
 |  | ||||||
|   if (!evt.resetAutoSettings && |  | ||||||
|        (document.getElementById('pageWidthOption').selected || |  | ||||||
|         document.getElementById('pageFitOption').selected)) { |  | ||||||
|       updateViewarea(); |  | ||||||
|       return; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   var options = document.getElementById('scaleSelect').options; |   var options = document.getElementById('scaleSelect').options; | ||||||
|   var predefinedValueFound = false; |   var predefinedValueFound = false; | ||||||
|   var value = '' + evt.scale; |  | ||||||
|   for (var i = 0; i < options.length; i++) { |   for (var i = 0; i < options.length; i++) { | ||||||
|     var option = options[i]; |     var option = options[i]; | ||||||
|     if (option.value != value) { |     if (option.value != value) { | ||||||
| @ -719,7 +1165,22 @@ window.addEventListener('scalechange', function scalechange(evt) { | |||||||
|     option.selected = true; |     option.selected = true; | ||||||
|     predefinedValueFound = true; |     predefinedValueFound = true; | ||||||
|   } |   } | ||||||
|  |   return predefinedValueFound; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | window.addEventListener('scalechange', function scalechange(evt) { | ||||||
|  |   var customScaleOption = document.getElementById('customScaleOption'); | ||||||
|  |   customScaleOption.selected = false; | ||||||
|  | 
 | ||||||
|  |   if (!evt.resetAutoSettings && | ||||||
|  |        (document.getElementById('pageWidthOption').selected || | ||||||
|  |         document.getElementById('pageFitOption').selected || | ||||||
|  |         document.getElementById('pageAutoOption').selected)) { | ||||||
|  |       updateViewarea(); | ||||||
|  |       return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var predefinedValueFound = selectScaleOption('' + evt.scale); | ||||||
|   if (!predefinedValueFound) { |   if (!predefinedValueFound) { | ||||||
|     customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%'; |     customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%'; | ||||||
|     customScaleOption.selected = true; |     customScaleOption.selected = true; | ||||||
| @ -737,25 +1198,49 @@ window.addEventListener('pagechange', function pagechange(evt) { | |||||||
| }, true); | }, true); | ||||||
| 
 | 
 | ||||||
| window.addEventListener('keydown', function keydown(evt) { | window.addEventListener('keydown', function keydown(evt) { | ||||||
|  |   if (evt.ctrlKey || evt.altKey || evt.shiftKey || evt.metaKey) | ||||||
|  |     return; | ||||||
|   var curElement = document.activeElement; |   var curElement = document.activeElement; | ||||||
|  |   if (curElement && curElement.tagName == 'INPUT') | ||||||
|  |     return; | ||||||
|   var controlsElement = document.getElementById('controls'); |   var controlsElement = document.getElementById('controls'); | ||||||
|   while (curElement) { |   while (curElement) { | ||||||
|     if (curElement === controlsElement) |     if (curElement === controlsElement) | ||||||
|       return; // ignoring if the 'controls' element is focused
 |       return; // ignoring if the 'controls' element is focused
 | ||||||
|     curElement = curElement.parentNode; |     curElement = curElement.parentNode; | ||||||
|   } |   } | ||||||
|  |   var handled = false; | ||||||
|   switch (evt.keyCode) { |   switch (evt.keyCode) { | ||||||
|     case 61: // FF/Mac '='
 |     case 61: // FF/Mac '='
 | ||||||
|     case 107: // FF '+' and '='
 |     case 107: // FF '+' and '='
 | ||||||
|     case 187: // Chrome '+'
 |     case 187: // Chrome '+'
 | ||||||
|       PDFView.zoomIn(); |       PDFView.zoomIn(); | ||||||
|  |       handled = true; | ||||||
|       break; |       break; | ||||||
|     case 109: // FF '-'
 |     case 109: // FF '-'
 | ||||||
|     case 189: // Chrome '-'
 |     case 189: // Chrome '-'
 | ||||||
|       PDFView.zoomOut(); |       PDFView.zoomOut(); | ||||||
|  |       handled = true; | ||||||
|       break; |       break; | ||||||
|     case 48: // '0'
 |     case 48: // '0'
 | ||||||
|       PDFView.setScale(kDefaultScale, true); |       PDFView.setScale(kDefaultScale, true); | ||||||
|  |       handled = true; | ||||||
|  |       break; | ||||||
|  |     case 37: // left arrow
 | ||||||
|  |     case 75: // 'k'
 | ||||||
|  |     case 80: // 'p'
 | ||||||
|  |       PDFView.page--; | ||||||
|  |       handled = true; | ||||||
|  |       break; | ||||||
|  |     case 39: // right arrow
 | ||||||
|  |     case 74: // 'j'
 | ||||||
|  |     case 78: // 'n'
 | ||||||
|  |       PDFView.page++; | ||||||
|  |       handled = true; | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   if (handled) { | ||||||
|  |     evt.preventDefault(); | ||||||
|  |   } | ||||||
| }); | }); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user