]> git.parisson.com Git - pdf.js.git/commitdiff
Support font with characters below 0x20 declared in format 1 cmap and be more general...
authorVivien Nicolas <21@vingtetun.org>
Fri, 15 Jul 2011 12:58:14 +0000 (14:58 +0200)
committerVivien Nicolas <21@vingtetun.org>
Fri, 15 Jul 2011 12:59:34 +0000 (14:59 +0200)
fonts.js
web/viewer.html

index 607e2aab93241a8187fa6420152f30cf98dd25d4..b3961c8fc9f4af292b311127890b3f2ae20cc79e 100755 (executable)
--- a/fonts.js
+++ b/fonts.js
@@ -372,7 +372,7 @@ function getUnicodeRangeFor(value) {
  *   var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
  *   type1Font.bind();
  */
-var Font = (function() {
+var Font = (function Font() {
   var constructor = function font_constructor(name, file, properties) {
     this.name = name;
     this.textMatrix = properties.textMatrix || IDENTITY_MATRIX;
@@ -531,6 +531,19 @@ var Font = (function() {
       }
       ranges.push([start, end]);
     }
+    
+    // Removes duplicate ranges
+    for (var i = ranges.length - 1; i > 0; i--) {
+      var range = [];
+      var range1 = ranges[i];
+      var range2 = ranges[i - 1];
+      if (range1[0] == range2[1]) {
+        range2[0] = range2[0] - 1;
+        range2[1] = range1[1];
+        ranges.splice(i, 1);
+      }
+    }
+    
     return ranges;
   };
 
@@ -790,21 +803,57 @@ var Font = (function() {
       };
 
       function replaceCMapTable(cmap, font, properties) {
-        font.pos = (font.start ? font.start : 0) + cmap.length;
+        var start = (font.start ? font.start : 0) + cmap.length;
+        font.pos = start;
 
         var version = int16(font.getBytes(2));
-        var numTables = int16(font.getBytes(2));
+        var numRecords = int16(font.getBytes(2));
+
+        var records = [];
+        for (var i = 0; i < numRecords; i++) {
+          records.push({
+            platformID: int16(font.getBytes(2)),
+            encodingID: int16(font.getBytes(2)),
+            offset: int32(font.getBytes(4))
+          });
+        };
 
-        for (var i = 0; i < numTables; i++) {
-          var platformID = int16(font.getBytes(2));
-          var encodingID = int16(font.getBytes(2));
-          var offset = int32(font.getBytes(4));
+        
+        for (var i = 0; i < numRecords; i++) {
+          var table = records[i];
+          font.pos = start + table.offset;
+          
           var format = int16(font.getBytes(2));
           var length = int16(font.getBytes(2));
           var language = int16(font.getBytes(2));
-
-          if ((format == 0 && numTables == 1) ||
-              (format == 6 && numTables == 1 && !properties.encoding.empty)) {
+          
+          if (format == 0 && numRecords > 1) {
+            // Characters below 0x20 are controls characters that are hardcoded
+            // into the platform so if some characters in the font are assigned
+            // under this limit they will not be displayed so let's rewrite the
+            // CMap.
+            var map = [];
+            var rewrite = false;
+            for (var j = 0; j < 256; j++) {
+              var index = font.getByte();
+              if (index != 0) {
+                map.push(index);
+                if (j < 0x20)
+                  rewrite = true;
+              }
+            }
+            
+            if (rewrite) {
+              var glyphs = [];
+              for (var j = 0x20; j < 256; j++) {
+                // TODO do not hardcode WinAnsiEncoding
+                var unicode = GlyphsUnicode[Encodings["WinAnsiEncoding"][j]];
+                glyphs.push({ unicode: unicode });
+              }
+              cmap.data = createCMapTable(glyphs, true);
+            }
+          } else if ((format == 0 && numRecords == 1) ||
+                     (format == 6 && numRecords == 1 && !properties.encoding.empty)) {
             // Format 0 alone is not allowed by the sanitizer so let's rewrite
             // that to a 3-1-4 Unicode BMP table
             TODO('Use an other source of informations than ' +
@@ -818,7 +867,7 @@ var Font = (function() {
             }
 
             cmap.data = createCMapTable(glyphs);
-          } else if (format == 6 && numTables == 1) {
+          } else if (format == 6 && numRecords == 1) {
             // 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
             // table. (This looks weird, so I can have missed something), this
@@ -871,10 +920,7 @@ var Font = (function() {
       var header = readOpenTypeHeader(font);
       var numTables = header.numTables;
 
-      // This keep a reference to the CMap and the post tables since they can
-      // be rewritted
-      var cmap, post, nameTable, maxp;
-
+      var cmap, maxp;
       var tables = [];
       for (var i = 0; i < numTables; i++) {
         var table = readTableEntry(font);
@@ -882,10 +928,6 @@ var Font = (function() {
         if (index != -1) {
           if (table.tag == 'cmap')
             cmap = table;
-          else if (table.tag == 'post')
-            post = table;
-          else if (table.tag == 'name')
-            nameTable = table;
           else if (table.tag == 'maxp')
             maxp = table;
 
@@ -894,136 +936,127 @@ var Font = (function() {
         tables.push(table);
       }
 
-      // If any tables are still in the array this means some required
-      // tables are missing, which means that we need to rebuild the
-      // font in order to pass the sanitizer.
-      if (requiredTables.length && requiredTables[0] == 'OS/2') {
-        // Create a new file to hold the new version of our truetype with a new
-        // header and new offsets
-        var ttf = new Uint8Array(kMaxFontFileSize);
-
-        // The offsets object holds at the same time a representation of where
-        // to write the table entry information about a table and another offset
-        // representing the offset where to put the actual data of a particular
-        // table
-        var numTables = header.numTables + requiredTables.length;
-        var offsets = {
-          currentOffset: 0,
-          virtualOffset: numTables * (4 * 4)
-        };
+      // Create a new file to hold the new version of our truetype with a new
+      // header and new offsets
+      var ttf = new Uint8Array(kMaxFontFileSize);
+
+      // The offsets object holds at the same time a representation of where
+      // to write the table entry information about a table and another offset
+      // representing the offset where to put the actual data of a particular
+      // table
+      var numTables = header.numTables + requiredTables.length;
+      var offsets = {
+       currentOffset: 0,
+        virtualOffset: numTables * (4 * 4)
+      };
 
-        // The new numbers of tables will be the last one plus the num
-        // of missing tables
-        createOpenTypeHeader('\x00\x01\x00\x00', ttf, offsets, numTables);
+      // The new numbers of tables will be the last one plus the num
+      // of missing tables
+      createOpenTypeHeader('\x00\x01\x00\x00', ttf, offsets, numTables);
 
-        // Insert the missing table
+                       if (requiredTables.indexOf('OS/2') != -1) {
         tables.push({
           tag: 'OS/2',
           data: stringToArray(createOS2Table(properties))
         });
+                       }
 
-        // Replace the old CMAP table with a shiny new one
-        if (properties.type == 'CIDFontType2') {
-          // Type2 composite fonts map characters directly to glyphs so the cmap
-          // table must be replaced.
+      // Replace the old CMAP table with a shiny new one
+      if (properties.type == 'CIDFontType2') {
+       // Type2 composite fonts map characters directly to glyphs so the cmap
+        // table must be replaced.
           
-          var glyphs = [];
-          var charset = properties.charset;
-          if (!charset.length) {
-            // PDF did not contain a GIDMap for the font so create an identity cmap
+        var glyphs = [];
+        var charset = properties.charset;
+        if (!charset.length) {
+          // PDF did not contain a GIDMap for the font so create an identity cmap
             
-            // First get the number of glyphs from the maxp table
-            font.pos = (font.start ? font.start : 0) + maxp.length;
-            var version = int16(font.getBytes(4));
-            var numGlyphs = int16(font.getBytes(2));
+          // First get the number of glyphs from the maxp table
+          font.pos = (font.start ? font.start : 0) + maxp.length;
+          var version = int16(font.getBytes(4));
+          var numGlyphs = int16(font.getBytes(2));
             
-            // Now create an identity mapping
-            for (var i = 1; i < numGlyphs; i++) {
+          // Now create an identity mapping
+          for (var i = 1; i < numGlyphs; i++) {
+            glyphs.push({
+              unicode: i
+            });
+          }
+        } else {
+          for (var i = 1; i < charset.length; i++) {
+            var index = charset.indexOf(i);
+            if (index != -1) {
               glyphs.push({
-                unicode: i
+                unicode: index
               });
-            }
-          } else {
-            for (var i = 1; i < charset.length; i++) {
-              var index = charset.indexOf(i);
-              if (index != -1) {
-                glyphs.push({
-                  unicode: index
-                });
-              } else {
-                break;
-              }
+            } else {
+              break;
             }
           }
-          
-          if (!cmap) {
-            // Font did not contain a cmap
-            tables.push({
-              tag: 'cmap',
-              data: createCMapTable(glyphs)
-            })
-          } else {
-            cmap.data = createCMapTable(glyphs);
-          }
-        } else {
-          replaceCMapTable(cmap, font, properties);          
-        }
-
-        // Rewrite the 'post' table if needed
-        if (!post) {
-          tables.push({
-            tag: 'post',
-            data: stringToArray(createPostTable(properties))
-          });
         }
-
-        // Rewrite the 'name' table if needed
-        if (!nameTable) {
+          
+        if (!cmap) {
+          // Font did not contain a cmap
           tables.push({
-            tag: 'name',
-            data: stringToArray(createNameTable(this.name))
-          });
+            tag: 'cmap',
+            data: createCMapTable(glyphs)
+          })
+        } else {
+          cmap.data = createCMapTable(glyphs);
         }
+      } else {
+        replaceCMapTable(cmap, font, properties);          
+      }
 
-        // Tables needs to be written by ascendant alphabetic order
-        tables.sort(function tables_sort(a, b) {
-          return (a.tag > b.tag) - (a.tag < b.tag);
+      // Rewrite the 'post' table if needed
+      if (requiredTables.indexOf('post') != -1) {
+        tables.push({
+          tag: 'post',
+          data: stringToArray(createPostTable(properties))
         });
+      }
 
-        // rewrite the tables but tweak offsets
-        for (var i = 0; i < tables.length; i++) {
-          var table = tables[i];
-          var data = [];
+      // Rewrite the 'name' table if needed
+      if (requiredTables.indexOf('name') != -1) {
+        tables.push({
+          tag: 'name',
+          data: stringToArray(createNameTable(this.name))
+        });
+      }
 
-          var tableData = table.data;
-          for (var j = 0; j < tableData.length; j++)
-            data.push(tableData[j]);
-          createTableEntry(ttf, offsets, table.tag, data);
-        }
+      // Tables needs to be written by ascendant alphabetic order
+      tables.sort(function tables_sort(a, b) {
+        return (a.tag > b.tag) - (a.tag < b.tag);
+      });
 
-        // Add the table datas
-        for (var i = 0; i < tables.length; i++) {
-          var table = tables[i];
-          var tableData = table.data;
-          ttf.set(tableData, offsets.currentOffset);
-          offsets.currentOffset += tableData.length;
+      // rewrite the tables but tweak offsets
+      for (var i = 0; i < tables.length; i++) {
+        var table = tables[i];
+        var data = [];
 
-          // 4-byte aligned data
-          while (offsets.currentOffset & 3)
-            offsets.currentOffset++;
-        }
+        var tableData = table.data;
+        for (var j = 0; j < tableData.length; j++)
+          data.push(tableData[j]);
+        createTableEntry(ttf, offsets, table.tag, data);
+      }
 
-        var fontData = [];
-        for (var i = 0; i < offsets.currentOffset; i++)
-          fontData.push(ttf[i]);
+      // Add the table datas
+      for (var i = 0; i < tables.length; i++) {
+        var table = tables[i];
+        var tableData = table.data;
+        ttf.set(tableData, offsets.currentOffset);
+        offsets.currentOffset += tableData.length;
 
-        return fontData;
-      } else if (requiredTables.length) {
-        error('Table ' + requiredTables[0] +
-              ' is missing from the TrueType font');
+        // 4-byte aligned data
+        while (offsets.currentOffset & 3)
+          offsets.currentOffset++;
       }
 
-      return font.getBytes();
+      var fontData = [];
+      for (var i = 0; i < offsets.currentOffset; i++)
+        fontData.push(ttf[i]);
+
+      return fontData;
     },
 
     convert: function font_convert(fontName, font, properties) {
index df9604db4a082038010cee4850128a26621d3ee5..8caa40f3e13be9263cf4c8ca0f55ad49db42bbb4 100644 (file)
@@ -8,6 +8,7 @@
         <script type="text/javascript" src="viewer.js"></script>
         <script type="text/javascript" src="../pdf.js"></script>
         <script type="text/javascript" src="../fonts.js"></script>
+        <script type="text/javascript" src="../utils/fonts_utils.js"></script>
         <script type="text/javascript" src="../crypto.js"></script>
         <script type="text/javascript" src="../glyphlist.js"></script>
   </head>