modules/utf8.js

/* global jsPDF */
/**
* @name utf8
* @module
*/
(function (jsPDF) {
    'use strict';
        var jsPDFAPI = jsPDF.API;

      /***************************************************************************************************/
      /* function : pdfEscape16                                                                          */
      /* comment : The character id of a 2-byte string is converted to a hexadecimal number by obtaining */
      /*   the corresponding glyph id and width, and then adding padding to the string.                  */
      /***************************************************************************************************/
          var pdfEscape16 = jsPDFAPI.pdfEscape16 = function (text, font) {
            var widths = font.metadata.Unicode.widths;
            var padz = ["", "0", "00", "000", "0000"];
            var ar = [""];
            for (var i = 0, l = text.length, t; i < l; ++i) {
              t = font.metadata.characterToGlyph(text.charCodeAt(i));
              font.metadata.glyIdsUsed.push(t);
              font.metadata.toUnicode[t] = text.charCodeAt(i);
              if (widths.indexOf(t) == -1) {
                widths.push(t);
                widths.push([parseInt(font.metadata.widthOfGlyph(t), 10)]);
              }
              if (t == '0') { //Spaces are not allowed in cmap.
                return ar.join("");
              } else {
                t = t.toString(16);
                ar.push(padz[4 - t.length], t);
              }
            }
            return ar.join("");
          };

          var toUnicodeCmap = function (map) {
              var code, codes, range, unicode, unicodeMap, _i, _len;
              unicodeMap = '/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo <<\n  /Registry (Adobe)\n  /Ordering (UCS)\n  /Supplement 0\n>> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000><ffff>\nendcodespacerange';
              codes = Object.keys(map).sort(function (a, b) {
                return a - b;
              });
              
              range = [];
              for (_i = 0, _len = codes.length; _i < _len; _i++) {
                code = codes[_i];
                if (range.length >= 100) {
                  unicodeMap += "\n" + range.length + " beginbfchar\n" + range.join('\n') + "\nendbfchar";
                  range = [];
                }

                if (map[code] !== undefined && map[code] !== null && typeof map[code].toString === "function") {
                  unicode = ('0000' + map[code].toString(16)).slice(-4);
                  code = ('0000' + (+code).toString(16)).slice(-4);
                  range.push("<" + code + "><" + unicode + ">");
                }
              }

              if (range.length) {
                unicodeMap += "\n" + range.length + " beginbfchar\n" + range.join('\n') + "\nendbfchar\n";
              }
              unicodeMap += 'endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend';
              return unicodeMap;
          };

          var identityHFunction = function (options) {
            var font = options.font;
            var out = options.out;
            var newObject = options.newObject;
            var putStream = options.putStream;
            var pdfEscapeWithNeededParanthesis = options.pdfEscapeWithNeededParanthesis;

              if ((font.metadata instanceof jsPDF.API.TTFFont) && (font.encoding === 'Identity-H')) { //Tag with Identity-H
                var widths = font.metadata.Unicode.widths;
                var data = font.metadata.subset.encode(font.metadata.glyIdsUsed, 1);
                var pdfOutput = data;
                var pdfOutput2 = "";
                for (var i = 0; i < pdfOutput.length; i++) {
                  pdfOutput2 += String.fromCharCode(pdfOutput[i]);
                }
                var fontTable = newObject();
                putStream({data: pdfOutput2, addLength1: true});
                out('endobj');

                var cmap = newObject();
                var cmapData = toUnicodeCmap(font.metadata.toUnicode);
                putStream({data: cmapData, addLength1: true});
                out('endobj');
                
                var fontDescriptor = newObject();
                out('<<');
                out('/Type /FontDescriptor');
                out('/FontName /' + pdfEscapeWithNeededParanthesis(font.fontName));
                out('/FontFile2 ' + fontTable + ' 0 R');
                out('/FontBBox ' + jsPDF.API.PDFObject.convert(font.metadata.bbox));
                out('/Flags ' + font.metadata.flags);
                out('/StemV ' + font.metadata.stemV);
                out('/ItalicAngle ' + font.metadata.italicAngle);
                out('/Ascent ' + font.metadata.ascender);
                out('/Descent ' + font.metadata.decender);
                out('/CapHeight ' + font.metadata.capHeight);
                out('>>');
                out('endobj');

                var DescendantFont = newObject();
                out('<<');
                out('/Type /Font');
                out('/BaseFont /' + pdfEscapeWithNeededParanthesis(font.fontName));
                out('/FontDescriptor ' + fontDescriptor + ' 0 R');
                out('/W ' + jsPDF.API.PDFObject.convert(widths));
                out('/CIDToGIDMap /Identity');
                out('/DW 1000');
                out('/Subtype /CIDFontType2');
                out('/CIDSystemInfo');
                out('<<');
                out('/Supplement 0');
                out('/Registry (Adobe)');
                out('/Ordering (' + font.encoding + ')');
                out('>>');
                out('>>');
                out('endobj');

                font.objectNumber = newObject();
                out('<<');
                out('/Type /Font');
                out('/Subtype /Type0');
                out('/ToUnicode ' + cmap + ' 0 R');
                out('/BaseFont /' + font.fontName);
                out('/Encoding /' + font.encoding);
                out('/DescendantFonts [' + DescendantFont + ' 0 R]');
                out('>>');
                out('endobj');

                font.isAlreadyPutted = true;
              }
          }
          

          jsPDFAPI.events.push([ 
              'putFont'
              ,function(args) {
                  identityHFunction(args);
          }]);

        
        var winAnsiEncodingFunction = function (options) {
            var font = options.font;
            var out = options.out;
            var newObject = options.newObject;
            var putStream = options.putStream;
            var pdfEscapeWithNeededParanthesis = options.pdfEscapeWithNeededParanthesis;
            
            if ((font.metadata instanceof jsPDF.API.TTFFont) && font.encoding === 'WinAnsiEncoding') { //Tag with WinAnsi encoding
              var data = font.metadata.rawData;
              var pdfOutput = data;
              var pdfOutput2 = "";
              for (var i = 0; i < pdfOutput.length; i++) {
                pdfOutput2 += String.fromCharCode(pdfOutput[i]);
              }
              var fontTable = newObject();
              putStream({data: pdfOutput2,addLength1: true});
              out('endobj');

              var cmap = newObject();
              var cmapData = toUnicodeCmap(font.metadata.toUnicode);
              putStream({data: cmapData, addLength1: true});
              out('endobj');
              
              var fontDescriptor = newObject();
              out('<<');
              out('/Descent ' + font.metadata.decender);
              out('/CapHeight ' + font.metadata.capHeight);
              out('/StemV ' + font.metadata.stemV);
              out('/Type /FontDescriptor');
              out('/FontFile2 ' + fontTable + ' 0 R');
              out('/Flags 96');
              out('/FontBBox ' + jsPDF.API.PDFObject.convert(font.metadata.bbox));
              out('/FontName /' + pdfEscapeWithNeededParanthesis(font.fontName));
              out('/ItalicAngle ' + font.metadata.italicAngle);
              out('/Ascent ' + font.metadata.ascender);
              out('>>');
              out('endobj');
              font.objectNumber = newObject();
              for (var j = 0; j < font.metadata.hmtx.widths.length; j++) {
                font.metadata.hmtx.widths[j] = parseInt(font.metadata.hmtx.widths[j] * (1000 / font.metadata.head.unitsPerEm)); //Change the width of Em units to Point units.
              }
              out('<</Subtype/TrueType/Type/Font/ToUnicode ' + cmap + ' 0 R/BaseFont/' + font.fontName + '/FontDescriptor ' + fontDescriptor + ' 0 R' + '/Encoding/' + font.encoding + ' /FirstChar 29 /LastChar 255 /Widths ' + jsPDF.API.PDFObject.convert(font.metadata.hmtx.widths) + '>>');
              out('endobj');
              font.isAlreadyPutted = true;
            }
        }
        
        jsPDFAPI.events.push([ 
            'putFont'
            ,function(args) {
                winAnsiEncodingFunction(args);
            }
        ]);
        
        var utf8TextFunction = function (args) {
            var text = args.text || '';
            var x = args.x;
            var y = args.y;
            var options = args.options || {};
            var mutex = args.mutex || {};
            
            var pdfEscape = mutex.pdfEscape;
            var activeFontKey = mutex.activeFontKey;
            var fonts = mutex.fonts;
            var key = activeFontKey;
    
            var str = '',
            s = 0,
            cmapConfirm;
            var strText = '';
            var encoding = fonts[key].encoding;
            
            if (fonts[key].encoding !== 'Identity-H') {
                return {
                    text : text,
                    x : x,
                    y : y,
                    options: options,
                    mutex: mutex
                };
            }
            strText = text;
            
            key = activeFontKey;
            if (Array.isArray(text)) {
                strText = text[0];
            }
          for (s = 0; s < strText.length; s += 1) {
          if (fonts[key].metadata.hasOwnProperty('cmap')) {
              cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)];
              /*
             if (Object.prototype.toString.call(text) === '[object Array]') {
                var i = 0;
               // for (i = 0; i < text.length; i += 1) {
                    if (Object.prototype.toString.call(text[s]) === '[object Array]') {
                        cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s][0].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id
                    } else {
                        
                    }
                //}
                
            } else {
                cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id
            }*/
          }
            if (!cmapConfirm) {
                if (strText[s].charCodeAt(0) < 256 && fonts[key].metadata.hasOwnProperty('Unicode')) {
                  str += strText[s];
                } else {
                  str += '';
                }
            } else {
            str += strText[s];
            }
          }
          var result = '';
          if ((parseInt(key.slice(1)) < 14) || encoding === 'WinAnsiEncoding') { //For the default 13 font
                result = pdfEscape(str, key).split('').map(function (cv) {return cv.charCodeAt(0).toString(16)}).join('');
              } else if (encoding === 'Identity-H') {
                  result = pdfEscape16(str, fonts[key]);
              }
              mutex.isHex = true;
            
            return {
                text : result,
                x : x,
                y : y,
                options: options,
                mutex: mutex
            };
        }
        
        var utf8EscapeFunction = function(parms) {
            var text = parms.text || '',
            x = parms.x,
            y = parms.y,
            options = parms.options,
            mutex = parms.mutex;
            var tmpText = [];
            var args = {
                    text : text,
                    x : x,
                    y : y,
                    options: options,
                    mutex: mutex
                };

            if (Array.isArray(text)) {
                var i = 0;
                for (i = 0; i < text.length; i += 1) {
                    if (Array.isArray(text[i])) {
                        if (text[i].length === 3) {
                            tmpText.push([utf8TextFunction(Object.assign({}, args, {text: text[i][0]})).text, text[i][1], text[i][2]]);
                        } else {
                            tmpText.push(utf8TextFunction(Object.assign({}, args, {text: text[i]})).text);
                        }
                    } else {
                        tmpText.push(utf8TextFunction(Object.assign({}, args, {text: text[i]})).text);
                    }
                }
                parms.text = tmpText;
                
            } else {
                parms.text = utf8TextFunction(Object.assign({}, args, {text: text})).text;
            }
        }

        jsPDFAPI.events.push([ 
            'postProcessText'
            ,utf8EscapeFunction
        ]);
        
})(jsPDF);