]> git.parisson.com Git - pdf.js.git/commitdiff
Hack something up that renders font right again
authorJulian Viereck <julian.viereck@gmail.com>
Sat, 8 Oct 2011 16:15:18 +0000 (18:15 +0200)
committerJulian Viereck <julian.viereck@gmail.com>
Sat, 8 Oct 2011 16:15:18 +0000 (18:15 +0200)
fonts.js
pdf.js
worker/processor_handler.js

index 371a47239081cb0f0b5b31f84e642ab4b1778afb..2ab3a90b76eee461e938d5b55ffb41994d6fa752 100644 (file)
--- a/fonts.js
+++ b/fonts.js
@@ -122,118 +122,16 @@ var serifFonts = {
   'Wide Latin': true, 'Windsor': true, 'XITS': true
 };
 
-// Create the FontMeasure object only on the main thread.
-if (!isWorker) {
-  var FontMeasure = (function FontMeasure() {
-    var kScalePrecision = 30;
-    var ctx = document.createElement('canvas').getContext('2d');
-    ctx.scale(1 / kScalePrecision, 1);
-
-    var current;
-    var measureCache;
-
-    return {
-      setActive: function fonts_setActive(font, size) {
-        if (current == font) {
-          var sizes = current.sizes;
-          if (!(measureCache = sizes[size]))
-            measureCache = sizes[size] = Object.create(null);
-        } else {
-          measureCache = null;
-        }
-
-        var name = font.loadedName;
-        var bold = font.bold ? 'bold' : 'normal';
-        var italic = font.italic ? 'italic' : 'normal';
-        size *= kScalePrecision;
-        var rule = italic + ' ' + bold + ' ' + size + 'px "' + name + '"';
-        ctx.font = rule;
-        current = font;
-      },
-      measureText: function fonts_measureText(text) {
-        var width;
-        if (measureCache && (width = measureCache[text]))
-          return width;
-        width = ctx.measureText(text).width / kScalePrecision;
-        if (measureCache)
-          measureCache[text] = width;
-        return width;
-      }
-    };
-  })();  
-}
-
-/**
- * The FontLoader binds a fontObj to the DOM and checks if it is loaded.
- * At the point of writing (11/9/14) there is no DOM event to detect loading
- * of fonts. Therefore, we measure the font using a canvas before the
- * font is attached to the DOM and later on test if the width changed.
- * To ensure there is a change in the font size, two fallback fonts are used.
- * The font used might look like this:
- *    ctx.font = "normal normmal 'p0_font_0', 'Arial'
- *
- * As long as the font 'p0_font_0' isn't loaded, the font 'Arial' is used. A
- * second measurement is done against the font 'Courier':
- *    ctx.font = "normal normmal 'p0_font_0', 'Courier'
- *
- * The font sizes of Arial and Courier are quite different which ensures there
- * gone be a change nomatter what the font looks like (e.g. you could end up
- * with a font that looks like Arial which means you don't see any change in
- * size, but as Courier is checked as well, there will be difference in this
- * font).
- *
- * !!! The test string used for measurements is really important. Some fonts
- * don't have definitions for characters like "a" or "b", but only for some
- * unicode characters. Therefore, the test string have to be build using the
- * encoding of the fontObj.
- */
 var FontLoader = {
   listeningForFontLoad: false,
 
-  /**
-   * Attach the fontObj to the DOM.
-   */
-  bindDOM: function font_bindDom(fontObj) {
-    // The browser isn't loading a font until it's used on the page. Attaching
-    // a hidden div that uses the font 'tells' the browser to load the font.
-    var div = document.createElement('div');
-    div.setAttribute('style',
-                     'visibility: hidden;' +
-                     'width: 10px; height: 10px;' +
-                     'position: absolute; top: 0px; left: 0px;' +
-                     'font-family: ' + fontObj.loadedName);
-    div.innerHTML = "Hi";
-    document.body.appendChild(div);
-    
-    // Add the font-face rule to the document
-    var fontName = fontObj.loadedName;
-    var url = ('url(data:' + fontObj.mimetype + ';base64,' +
-                 window.btoa(fontObj.str) + ');');
-    var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
-    var styleSheet = document.styleSheets[0];
-    styleSheet.insertRule(rule, styleSheet.cssRules.length);
-    return rule;
-  },
-
-  bind: function fontLoaderBind(fonts, callback, objects) {
-    var fontsToLoad = {};
-    // check if there are twice the same font.
-    for (var i = 0; i < fonts.length; i++) {
-      var fontName = fonts[i].loadedName;
-      if (fontsToLoad[fontName]) {
-        throw "Got twice the same font!";
-      } else {
-        fontsToLoad[fontName] = true;
-      }
-    }
-
+  bind: function fontLoaderBind(fonts, callback) {
     function checkFontsLoaded() {
       for (var i = 0; i < objs.length; i++) {
         var fontObj = objs[i];
         if (fontObj.loading) {
           return false;
         }
-        objects.resolve(fontObj.loadedName);
       }
 
       document.documentElement.removeEventListener(
@@ -248,16 +146,22 @@ var FontLoader = {
     for (var i = 0; i < fonts.length; i++) {
       var font = fonts[i];
 
-      // If there is no string on the font, then the font can't get loaded /
-      // get attached to the DOM. Skip this font.
-      if (!font.str) {
-        continue;
-      }
+      var obj = new Font(font.name, font.file, font.properties);
+      objs.push(obj);
 
-      objs.push(font);
+      var str = '';
+      var data = obj.data;
+      if (data) {
+        var length = data.length;
+        for (var j = 0; j < length; j++)
+          str += String.fromCharCode(data[j]);
 
-      rules.push(this.bindDOM(font));
-      names.push(font.loadedName);
+        var rule = isWorker ? obj.bindWorker(str) : obj.bindDOM(str);
+        if (rule) {
+          rules.push(rule);
+          names.push(obj.loadedName);
+        }
+      }
     }
 
     this.listeningForFontLoad = false;
@@ -272,7 +176,6 @@ var FontLoader = {
 
     return objs;
   },
-
   // Set things up so that at least one pdfjsFontLoad event is
   // dispatched when all the @font-face |rules| for |names| have been
   // loaded in a subdocument.  It's expected that the load of |rules|
@@ -500,110 +403,6 @@ function getUnicodeRangeFor(value) {
   return -1;
 }
 
-/**
- * FontShape is the minimal shape a FontObject can have to be useful during
- * executing the IRQueue.
- */
-var FontShape = (function FontShape() {
-  var constructor = function FontShape_constructor(obj) {
-    for (var name in obj) {
-      this[name] = obj[name];
-    }
-    
-    var name = this.loadedName;
-    var bold = this.black ? (this.bold ? 'bolder' : 'bold') :
-                            (this.bold ? 'bold' : 'normal');
-
-    var italic = this.italic ? 'italic' : 'normal';
-    this.fontFallback = this.serif ? 'serif' : 'sans-serif';
-
-    this.namePart1 = italic + ' ' + bold + ' ';
-    this.namePart2 = 'px "' + name + '", "';
-    
-    this.supported = Object.keys(this.encoding).length != 0;
-
-    // Set the loading flag. Gets set to false in FontLoader.bind().
-    this.loading = true;
-  };
-
-  function int16(bytes) {
-    return (bytes[0] << 8) + (bytes[1] & 0xff);
-  };
-  
-  constructor.prototype = {
-    getRule: function fonts_getRule(size, fallback) {
-      fallback = fallback || this.fontFallback;
-      return this.namePart1 + size + this.namePart2 + fallback + '"';
-    },
-    
-    charsToUnicode: function fonts_chars2Unicode(chars) {
-      var charsCache = this.charsCache;
-      var str;
-
-      // if we translated this string before, just grab it from the cache
-      if (charsCache) {
-        str = charsCache[chars];
-        if (str)
-          return str;
-      }
-
-      // lazily create the translation cache
-      if (!charsCache)
-        charsCache = this.charsCache = Object.create(null);
-
-      // translate the string using the font's encoding
-      var encoding = this.encoding;
-      if (!encoding)
-        return chars;
-      str = '';
-
-      if (this.composite) {
-        // composite fonts have multi-byte strings convert the string from
-        // single-byte to multi-byte
-        // XXX assuming CIDFonts are two-byte - later need to extract the
-        // correct byte encoding according to the PDF spec
-        var length = chars.length - 1; // looping over two bytes at a time so
-                                       // loop should never end on the last byte
-        for (var i = 0; i < length; i++) {
-          var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
-          var unicode = encoding[charcode];
-          if ('undefined' == typeof(unicode)) {
-            warn('Unencoded charcode ' + charcode);
-            unicode = charcode;
-          } else {
-            unicode = unicode.unicode;
-          }
-          str += String.fromCharCode(unicode);
-        }
-      }
-      else {
-        for (var i = 0; i < chars.length; ++i) {
-          var charcode = chars.charCodeAt(i);
-          var unicode = encoding[charcode];
-          if ('undefined' == typeof(unicode)) {
-            warn('Unencoded charcode ' + charcode);
-            unicode = charcode;
-          } else {
-            unicode = unicode.unicode;
-          }
-
-          // Handle surrogate pairs
-          if (unicode > 0xFFFF) {
-            str += String.fromCharCode(unicode & 0xFFFF);
-            unicode >>= 16;
-          }
-          str += String.fromCharCode(unicode);
-        }
-      }
-
-      // Enter the translated string into the cache
-      return (charsCache[chars] = str);
-    }
-  }
-  
-  return constructor;
-})();
-
 /**
  * 'Font' is the class the outside world should use, it encapsulate all the font
  * decoding logics whatever type it is (assuming the font type is supported).
@@ -616,10 +415,6 @@ var Font = (function Font() {
   var constructor = function font_constructor(name, file, properties) {
     this.name = name;
     this.encoding = properties.encoding;
-    
-    // Glyhps are no needed anymore? MERGE
-    // this.glyphs = properties.glyphs;
-    this.loadedName = properties.loadedName;
     this.coded = properties.coded;
     this.resources = properties.resources;
     this.sizes = [];
@@ -656,8 +451,7 @@ var Font = (function Font() {
       this.black = (name.search(/Black/g) != -1);
 
       this.defaultWidth = properties.defaultWidth;
-      // MERGE
-      // this.loadedName = fontName.split('-')[0];
+      this.loadedName = fontName.split('-')[0];
       this.composite = properties.composite;
       this.loading = false;
       return;
@@ -693,23 +487,19 @@ var Font = (function Font() {
     }
 
     this.data = data;
-    // MERGE
-    //this.textMatrix = properties.textMatrix;
     this.type = type;
     this.fontMatrix = properties.fontMatrix;
     this.defaultWidth = properties.defaultWidth;
-    this.loadedName = properties.loadedName;
+    this.loadedName = getUniqueName();
     this.composite = properties.composite;
-    
-    // TODO: Remove this once we can be sure nothing got broken to du changes
-    // in this commit.
-    if (!this.loadedName) {
-      throw "There has to be a `loadedName`";
-    }
-
     this.loading = true;
   };
 
+  var numFonts = 0;
+  function getUniqueName() {
+    return 'pdfFont' + numFonts++;
+  }
+
   function stringToArray(str) {
     var array = [];
     for (var i = 0; i < str.length; ++i)
@@ -1692,6 +1482,35 @@ var Font = (function Font() {
       }
     },
 
+    bindWorker: function font_bindWorker(data) {
+      postMessage({
+        action: 'font',
+        data: {
+          raw: data,
+          fontName: this.loadedName,
+          mimetype: this.mimetype
+        }
+      });
+    },
+
+    bindDOM: function font_bindDom(data) {
+      var fontName = this.loadedName;
+
+      // Add the font-face rule to the document
+      var url = ('url(data:' + this.mimetype + ';base64,' +
+                 window.btoa(data) + ');');
+      var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
+      var styleSheet = document.styleSheets[0];
+      if (!styleSheet) {
+        document.documentElement.firstChild.appendChild(
+          document.createElement('style'));
+        styleSheet = document.styleSheets[0];
+      }
+      styleSheet.insertRule(rule, styleSheet.cssRules.length);
+
+      return rule;
+    },
+
     charsToGlyphs: function fonts_chars2Glyphs(chars) {
       var charsCache = this.charsCache;
       var glyphs;
diff --git a/pdf.js b/pdf.js
index a9b264affe550d1ad93c5e818669917c7a529e19..21bc8811960028336cd213a6747c345bb188c85c 100644 (file)
--- a/pdf.js
+++ b/pdf.js
@@ -3554,13 +3554,13 @@ var Page = (function pagePage() {
         // Firefox error reporting from XHR callbacks.
         setTimeout(function pageSetTimeout() {
           var exc = null;
-          try {
+          // try {
             self.display(gfx, continuation);
-          } catch (e) {
-            exc = e.toString();
-            continuation(exc);
-            throw e;
-          }
+          // } catch (e) {
+          //   exc = e.toString();
+          //   continuation(exc);
+          //   throw e;
+          // }
         });
       };
       
@@ -3594,14 +3594,14 @@ var Page = (function pagePage() {
     ensureFonts: function(fonts, callback) {
       console.log('--ensureFonts--', '' + fonts);
       // Convert the font names to the corresponding font obj.
-      for (var i = 0; i < fonts.length; i++) {
-        // HACK FOR NOW. Access the data directly. This isn't allowed as the
-        // font object isn't resolved yet.
-        fonts[i] = this.objs.objs[fonts[i]].data;
-      }
+      // for (var i = 0; i < fonts.length; i++) {
+      //   // HACK FOR NOW. Access the data directly. This isn't allowed as the
+      //   // font object isn't resolved yet.
+      //   fonts[i] = this.objs.objs[fonts[i]].data;
+      // }
 
       // Load all the fonts
-      FontLoader.bind(
+      var fontObjs = FontLoader.bind(
         fonts,
         function(fontObjs) {
           this.stats.fonts = Date.now();
@@ -3610,6 +3610,9 @@ var Page = (function pagePage() {
         }.bind(this),
         this.objs
       );
+      
+      for (var i = 0, ii = fonts.length; i < ii; ++i)
+        fonts[i].dict.fontObj = fontObjs[i];
     },
     
     display: function(gfx, callback) {
@@ -3617,6 +3620,9 @@ var Page = (function pagePage() {
       var resources = xref.fetchIfRef(this.resources);
       var mediaBox = xref.fetchIfRef(this.mediaBox);
       assertWellFormed(isDict(resources), 'invalid page resources');
+      
+      gfx.xref = xref;
+      gfx.res = resources;
       gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1],
             width: this.width,
             height: this.height,
@@ -4087,7 +4093,8 @@ var PDFDoc = (function() {
         page.startRenderingFromIRQueue(data.IRQueue, fontsToLoad);
       }
 
-      checkFontData();
+//      checkFontData();
+        page.startRenderingFromIRQueue(data.IRQueue, data.depFonts);
     }, this);
 
     processorHandler.on("obj", function(data) {
@@ -4187,7 +4194,7 @@ var PDFDoc = (function() {
       WorkerProcessorHandler.setup(processorHandler);
     }
     
-    processorHandler.send("doc", data);
+    processorHandler.send("doc", this.pdf);
   }
 
   constructor.prototype = {
@@ -4865,7 +4872,22 @@ var PartialEvaluator = (function partialEvaluator() {
               }
             }
           } else if (cmd == 'Tf') { // eagerly collect all fonts
-            args[0].name = handleSetFont(args[0].name);
+            var fontRes = resources.get('Font');
+            if (fontRes) {
+              fontRes = xref.fetchIfRef(fontRes);
+              var font = xref.fetchIfRef(fontRes.get(args[0].name));
+              assertWellFormed(isDict(font));
+              if (!font.translated) {
+                font.translated = this.translateFont(font, xref, resources);
+                if (font.translated) {
+                  // keep track of each font we translated so the caller can
+                  // load them asynchronously before calling display on a page
+                  // fonts.push(font.translated);
+                  dependency.push(font.translated);
+                }
+              }
+            }
+
           } else if (cmd == 'EI') {
             buildPaintImageXObject(args[0], true);
           }
@@ -5767,24 +5789,37 @@ var CanvasGraphics = (function canvasGraphics() {
       this.current.leading = -leading;
     },
     setFont: function canvasGraphicsSetFont(fontRef, size) {
-      // Lookup the fontObj using fontRef only.
-      var fontRefName = fontRef.name;
-      var fontObj = this.objs.get(fontRefName);
-      
-      if (!fontObj) {
-        throw "Can't find font for " + fontRefName;
-      }
-      
-      var name = fontObj.loadedName || 'sans-serif';
-      console.log('setFont', name);
-
+      var font;
+      // the tf command uses a name, but graphics state uses a reference
+      if (isName(fontRef)) {
+        font = this.xref.fetchIfRef(this.res.get('Font'));
+        if (!isDict(font))
+         return;
+
+        font = font.get(fontRef.name);
+      } else if (isRef(fontRef)) {
+        font = fontRef;
+      }
+      font = this.xref.fetchIfRef(font);
+      if (!font)
+        error('Referenced font is not found');
+
+      var fontObj = font.fontObj;
       this.current.font = fontObj;
       this.current.fontSize = size;
 
+      var name = fontObj.loadedName || 'sans-serif';
       if (this.ctx.$setFont) {
         this.ctx.$setFont(name, size);
       } else {
-        this.ctx.font = fontObj.getRule(size);
+        var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
+                                   (fontObj.bold ? 'bold' : 'normal');
+
+        var italic = fontObj.italic ? 'italic' : 'normal';
+        var serif = fontObj.serif ? 'serif' : 'sans-serif';
+        var typeface = '"' + name + '", ' + serif;
+        var rule = italic + ' ' + bold + ' ' + size + 'px ' + typeface;
+        this.ctx.font = rule;
       }
     },
     setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) {
@@ -5820,10 +5855,10 @@ var CanvasGraphics = (function canvasGraphics() {
     showText: function canvasGraphicsShowText(text) {
       // If the current font isn't supported, we can't display the text and
       // bail out.
-      if (!this.current.font.supported) {
-        console.log("showText BAIL OUT");
-        return;
-      }
+      // if (!this.current.font.supported) {
+      //   console.log("showText BAIL OUT");
+      //   return;
+      // }
 
       console.log("showText", text);
 
@@ -5902,10 +5937,10 @@ var CanvasGraphics = (function canvasGraphics() {
 
       // If the current font isn't supported, we can't display the text and
       // bail out.
-      if (!this.current.font.supported) {
-        console.log("showSpacedText BAIL OUT");
-        return;
-      }
+      // if (!this.current.font.supported) {
+      //   console.log("showSpacedText BAIL OUT");
+      //   return;
+      // }
 
       console.log("showSpacedText", arr);
       
index 20f8c8b54c4e7deb5bf2f02a4045e7be166d2372..09810aae44c3bf72be67b80005e2920185aaf9ec 100644 (file)
@@ -10,7 +10,7 @@ var WorkerProcessorHandler = {
     handler.on("doc", function(data) {
       // Create only the model of the PDFDoc, which is enough for
       // processing the content of the pdf.
-      pdfDoc = new PDFDocModel(new Stream(data));
+      pdfDoc = data;//new PDFDocModel(new Stream(data));
     });
   
     handler.on("page_request", function(pageNum) {
@@ -50,19 +50,26 @@ var WorkerProcessorHandler = {
       }
 
       // Filter the dependecies for fonts.
-      var fonts = {};
+      // var fonts = {};
+      // for (var i = 0; i < dependency.length; i++) {
+      //   var dep = dependency[i];
+      //   if (dep.indexOf('font_') == 0) {
+      //     fonts[dep] = true;
+      //   }
+      // }
+
+      var fonts = [];
       for (var i = 0; i < dependency.length; i++) {
         var dep = dependency[i];
-        if (dep.indexOf('font_') == 0) {
-          fonts[dep] = true;
+        if (typeof dep === "object") {
+          fonts.push(dep);
         }
       }
 
-
       handler.send("page", {
         pageNum:  pageNum,
         IRQueue:  IRQueue,
-        depFonts: Object.keys(fonts)
+        depFonts: fonts
       });
     }, this);