<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

"use strict";

/**
 * Maximum file size of the font.
 */
var kMaxFontFileSize = 40000;

/**
 * Maximum time to wait for a font to be loaded by @font-face
 */
var kMaxWaitForFontFace = 1000;

/**
 * Useful for debugging when you want to certains operations depending on how
 * many fonts are loaded.
 */
var fontCount = 0;
var fontName  = "";

/**
 * If for some reason one want to debug without fonts activated, it just need
 * to turn this pref to true/false.
 */
var kDisableFonts = false;


/**
 * Hold a map of decoded fonts and of the standard fourteen Type1 fonts and
 * their acronyms.
 * TODO Add the standard fourteen Type1 fonts list by default
 *      http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
 */

var kScalePrecision = 40;
var Fonts = {
  _active: null,

  get active() {
    return this._active;
  },

  setActive: function fonts_setActive(name, size) {
    this._active = this[name];
    this.ctx.font = (size * kScalePrecision) + 'px "' + name + '"';
  },

  charsToUnicode: function fonts_chars2Unicode(chars) {
    var active = this._active;
    if (!active)
      return chars;

    // if we translated this string before, just grab it from the cache
    var str = active.cache[chars];
    if (str)
      return str;

    // translate the string using the font's encoding
    var encoding = active.properties.encoding;
    if (!encoding)
      return chars;

    str = "";
    for (var i = 0; i < chars.length; ++i) {
      var charcode = chars.charCodeAt(i);
      var unicode = encoding[charcode];

      // Check if the glyph has already been converted
      if (!IsNum(unicode))
          unicode = encoding[unicode] = GlyphsUnicode[unicode.name];

      // 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 active.cache[chars] = str;
  },

  get ctx() {
    var ctx = document.createElement("canvas").getContext("2d");
    ctx.scale(1 / kScalePrecision, 1);
    return shadow(this, "ctx", ctx);
  },

  measureText: function fonts_measureText(text) {
    return this.ctx.measureText(text).width / kScalePrecision;
  }
};

var FontLoader = {
  bind: function(fonts) {
    var worker = (typeof window == "undefined");
    var ready = true;

    for (var i = 0; i < fonts.length; i++) {
      var font = fonts[i];
      if (Fonts[font.name]) {
        ready = ready && !Fonts[font.name].loading;
        continue;
      }

      ready = false;

      var obj = new Font(font.name, font.file, font.properties);

      var str = "";
      var data = Fonts[font.name].data;
      var length = data.length;
      for (var j = 0; j < length; j++)
        str += String.fromCharCode(data[j]);

      worker ? obj.bindWorker(str) : obj.bindDOM(str);
    }

    return ready;
  }
};


/**
 * '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).
 *
 * For example to read a Type1 font and to attach it to the document:
 *   var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
 *   type1Font.bind();
 */
var Font = (function () {
  var constructor = function font_constructor(name, file, properties) {
    this.name = name;
    this.encoding = properties.encoding;

    // If the font has already been decoded simply return it
    if (Fonts[name]) {
      this.font = Fonts[name].data;
      return;
    }
    fontCount++;
    fontName = name;

    // If the font is to be ignored, register it like an already loaded font
    // to avoid the cost of waiting for it be be loaded by the platform.
    if (properties.ignore || kDisableFonts) {
      Fonts[name] = {
        data: file,
        loading: false,
        properties: {},
        cache: Object.create(null)
      }
      return;
    }

    var data;
    switch (properties.type) {
      case "Type1":
        var cff = new CFF(name, file, properties);
        this.mimetype = "font/opentype";

        // Wrap the CFF data inside an OTF font file
        data = this.convert(name, cff, properties);
        break;

      case "TrueType":
        this.mimetype = "font/opentype";

        // Repair the TrueType file if it is can be damaged in the point of
        // view of the sanitizer
        data = this.checkAndRepair(name, file, properties);
        break;

      default:
        warn("Font " + properties.type + " is not supported");
        break;
    }
    this.data = data;

    Fonts[name] = {
      data: data,
      properties: properties,
      loading: true,
      cache: Object.create(null)
    };
  };

  function stringToArray(str) {
    var array = [];
    for (var i = 0; i < str.length; ++i)
      array[i] = str.charCodeAt(i);

    return array;
  };

  function string16(value) {
    return String.fromCharCode((value >> 8) & 0xff) +
           String.fromCharCode(value & 0xff);
  };

  function string32(value) {
    return String.fromCharCode((value >> 24) & 0xff) +
           String.fromCharCode((value >> 16) & 0xff) +
           String.fromCharCode((value >> 8) & 0xff) +
           String.fromCharCode(value & 0xff);
  };

  function createOpenTypeHeader(sfnt, file, offsets, numTables) {
    // sfnt version (4 bytes)
    var header = sfnt;

    // numTables (2 bytes)
    header += string16(numTables);

    // searchRange (2 bytes)
    var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables);
    var searchRange = tablesMaxPower2 * 16;
    header += string16(searchRange);

    // entrySelector (2 bytes)
    header += string16(Math.log(tablesMaxPower2) / Math.log(2));

    // rangeShift (2 bytes)
    header += string16(numTables * 16 - searchRange);

    file.set(stringToArray(header), offsets.currentOffset);
    offsets.currentOffset += header.length;
    offsets.virtualOffset += header.length;
  };

  function createTableEntry(file, offsets, tag, data) {
    // offset
    var offset = offsets.virtualOffset;

    // length
    var length = data.length;

    // Per spec tables must be 4-bytes align so add padding as needed
    while (data.length & 3)
      data.push(0x00);

    while (offsets.virtualOffset & 3)
      offsets.virtualOffset++;

    // checksum
    var checksum = 0;
    for (var i = 0; i < length; i+=4)
      checksum += FontsUtils.bytesToInteger([data[i], data[i+1], data[i+2], data[i+3]]);

    var tableEntry = tag + string32(checksum) + string32(offset) + string32(length);
    tableEntry = stringToArray(tableEntry);
    file.set(tableEntry, offsets.currentOffset);

    offsets.currentOffset += tableEntry.length;
    offsets.virtualOffset += data.length;
  };

  function getRanges(glyphs) {
    // Array.sort() sorts by characters, not numerically, so convert to an
    // array of characters.
    var codes = [];
    var length = glyphs.length;
    for (var n = 0; n < length; ++n)
      codes.push(String.fromCharCode(glyphs[n].unicode))
    codes.sort();

    // Split the sorted codes into ranges.
    var ranges = [];
    for (var n = 0; n < length; ) {
      var start = codes[n++].charCodeAt(0);
      var end = start;
      while (n < length && end + 1 == codes[n].charCodeAt(0)) {
        ++end;
        ++n;
      }
      ranges.push([start, end]);
    }
    return ranges;
  };

  function createCMapTable(glyphs) {
    var ranges = getRanges(glyphs);

    var headerSize = (12 * 2 + (ranges.length * 4 * 2));
    var segCount = ranges.length + 1;
    var segCount2 = segCount * 2;
    var searchRange = FontsUtils.getMaxPower2(segCount) * 2;
    var searchEntry = Math.log(segCount) / Math.log(2);
    var rangeShift = 2 * segCount - searchRange;

    var cmap = "\x00\x00" + // version
               "\x00\x01" + // numTables
               "\x00\x03" + // platformID
               "\x00\x01" + // encodingID
               "\x00\x00\x00\x0C" + // start of the table record
               "\x00\x04" + // format
               string16(headerSize) + // length
               "\x00\x00" + // languages
               string16(segCount2) +
               string16(searchRange) +
               string16(searchEntry) +
               string16(rangeShift);

    // Fill up the 4 parallel arrays describing the segments.
    var startCount = "";
    var endCount = "";
    var idDeltas = "";
    var idRangeOffsets = "";
    var glyphsIds = "";
    var bias = 0;
    for (var i = 0; i < segCount - 1; i++) {
      var range = ranges[i];
      var start = range[0];
      var end = range[1];
      var delta = (((start - 1) - bias) ^ 0xffff) + 1;
      bias += (end - start + 1);

      startCount += string16(start);
      endCount += string16(end);
      idDeltas += string16(delta);
      idRangeOffsets += string16(0);

      for (var j = 0; j < range.length; j++)
        glyphsIds += String.fromCharCode(range[j]);
    }

    startCount += "\xFF\xFF";
    endCount += "\xFF\xFF";
    idDeltas += "\x00\x01";
    idRangeOffsets += "\x00\x00";

    return stringToArray(cmap + endCount + "\x00\x00" + startCount +
                         idDeltas + idRangeOffsets + glyphsIds);
  };

  function createOS2Table(properties) {
    return "\x00\x03" + // version
           "\x02\x24" + // xAvgCharWidth
           "\x01\xF4" + // usWeightClass
           "\x00\x05" + // usWidthClass
           "\x00\x00" + // fstype
           "\x02\x8A" + // ySubscriptXSize
           "\x02\xBB" + // ySubscriptYSize
           "\x00\x00" + // ySubscriptXOffset
           "\x00\x8C" + // ySubscriptYOffset
           "\x02\x8A" + // ySuperScriptXSize
           "\x02\xBB" + // ySuperScriptYSize
           "\x00\x00" + // ySuperScriptXOffset
           "\x01\xDF" + // ySuperScriptYOffset
           "\x00\x31" + // yStrikeOutSize
           "\x01\x02" + // yStrikeOutPosition
           "\x00\x00" + // sFamilyClass
           "\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose
           "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31)
           "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63)
           "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95)
           "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127)
           "\x2A\x32\x31\x2A" + // achVendID
           "\x00\x00" + // fsSelection
           "\x00\x2D" + // usFirstCharIndex
           "\x00\x7A" + // usLastCharIndex
           "\x00\x20" + // sTypoAscender
           "\x00\x00" + // sTypeDescender
           "\x00\x00" + // sTypoLineGap
           string16(properties.ascent)  + // usWinAscent
           string16(properties.descent) + // usWinDescent
           "\x00\x00\x00\x00" + // ulCodePageRange1 (Bits 0-31)
           "\x00\x00\x00\x00" + // ulCodePageRange2 (Bits 32-63)
           string16(properties.xHeight)   + // sxHeight
           string16(properties.capHeight) + // sCapHeight
           "\x00\x01" + // usDefaultChar
           "\x00\xCD" + // usBreakChar
           "\x00\x00";  // usMaxContext
  };

  function createPostTable(properties) {
    TODO("Fill with real values from the font dict");

    return "\x00\x03\x00\x00"               + // Version number
           string32(properties.italicAngle) + // italicAngle
           "\x00\x00"                       + // underlinePosition
           "\x00\x00"                       + // underlineThickness
           "\x00\x00\x00\x00"               + // isFixedPitch
           "\x00\x00\x00\x00"               + // minMemType42
           "\x00\x00\x00\x00"               + // maxMemType42
           "\x00\x00\x00\x00"               + // minMemType1
           "\x00\x00\x00\x00";                // maxMemType1
  };

  constructor.prototype = {
    name: null,
    font: null,
    mimetype: null,<commit_msg>Add better informations for the OS/2 table<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

"use strict";

/**
 * Maximum file size of the font.
 */
var kMaxFontFileSize = 40000;

/**
 * Maximum time to wait for a font to be loaded by @font-face
 */
var kMaxWaitForFontFace = 1000;

/**
 * Useful for debugging when you want to certains operations depending on how
 * many fonts are loaded.
 */
var fontCount = 0;
var fontName  = "";

/**
 * If for some reason one want to debug without fonts activated, it just need
 * to turn this pref to true/false.
 */
var kDisableFonts = false;


/**
 * Hold a map of decoded fonts and of the standard fourteen Type1 fonts and
 * their acronyms.
 * TODO Add the standard fourteen Type1 fonts list by default
 *      http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
 */

var kScalePrecision = 40;
var Fonts = {
  _active: null,

  get active() {
    return this._active;
  },

  setActive: function fonts_setActive(name, size) {
    this._active = this[name];
    this.ctx.font = (size * kScalePrecision) + 'px "' + name + '"';
  },

  charsToUnicode: function fonts_chars2Unicode(chars) {
    var active = this._active;
    if (!active)
      return chars;

    // if we translated this string before, just grab it from the cache
    var str = active.cache[chars];
    if (str)
      return str;

    // translate the string using the font's encoding
    var encoding = active.properties.encoding;
    if (!encoding)
      return chars;

    str = "";
    for (var i = 0; i < chars.length; ++i) {
      var charcode = chars.charCodeAt(i);
      var unicode = encoding[charcode];

      // Check if the glyph has already been converted
      if (!IsNum(unicode))
          unicode = encoding[unicode] = GlyphsUnicode[unicode.name];

      // 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 active.cache[chars] = str;
  },

  get ctx() {
    var ctx = document.createElement("canvas").getContext("2d");
    ctx.scale(1 / kScalePrecision, 1);
    return shadow(this, "ctx", ctx);
  },

  measureText: function fonts_measureText(text) {
    return this.ctx.measureText(text).width / kScalePrecision;
  }
};

var FontLoader = {
  bind: function(fonts) {
    var worker = (typeof window == "undefined");
    var ready = true;

    for (var i = 0; i < fonts.length; i++) {
      var font = fonts[i];
      if (Fonts[font.name]) {
        ready = ready && !Fonts[font.name].loading;
        continue;
      }

      ready = false;

      var obj = new Font(font.name, font.file, font.properties);

      var str = "";
      var data = Fonts[font.name].data;
      var length = data.length;
      for (var j = 0; j < length; j++)
        str += String.fromCharCode(data[j]);

      worker ? obj.bindWorker(str) : obj.bindDOM(str);
    }

    return ready;
  }
};

var UnicodeRanges = [
  { "begin": 0x0000, "end": 0x007F }, // Basic Latin
  { "begin": 0x0080, "end": 0x00FF }, // Latin-1 Supplement
  { "begin": 0x0100, "end": 0x017F }, // Latin Extended-A
  { "begin": 0x0180, "end": 0x024F }, // Latin Extended-B
  { "begin": 0x0250, "end": 0x02AF }, // IPA Extensions
  { "begin": 0x02B0, "end": 0x02FF }, // Spacing Modifier Letters
  { "begin": 0x0300, "end": 0x036F }, // Combining Diacritical Marks
  { "begin": 0x0370, "end": 0x03FF }, // Greek and Coptic
  { "begin": 0x2C80, "end": 0x2CFF }, // Coptic
  { "begin": 0x0400, "end": 0x04FF }, // Cyrillic
  { "begin": 0x0530, "end": 0x058F }, // Armenian
  { "begin": 0x0590, "end": 0x05FF }, // Hebrew
  { "begin": 0xA500, "end": 0xA63F }, // Vai
  { "begin": 0x0600, "end": 0x06FF }, // Arabic
  { "begin": 0x07C0, "end": 0x07FF }, // NKo
  { "begin": 0x0900, "end": 0x097F }, // Devanagari
  { "begin": 0x0980, "end": 0x09FF }, // Bengali
  { "begin": 0x0A00, "end": 0x0A7F }, // Gurmukhi
  { "begin": 0x0A80, "end": 0x0AFF }, // Gujarati
  { "begin": 0x0B00, "end": 0x0B7F }, // Oriya
  { "begin": 0x0B80, "end": 0x0BFF }, // Tamil
  { "begin": 0x0C00, "end": 0x0C7F }, // Telugu
  { "begin": 0x0C80, "end": 0x0CFF }, // Kannada
  { "begin": 0x0D00, "end": 0x0D7F }, // Malayalam
  { "begin": 0x0E00, "end": 0x0E7F }, // Thai
  { "begin": 0x0E80, "end": 0x0EFF }, // Lao
  { "begin": 0x10A0, "end": 0x10FF }, // Georgian
  { "begin": 0x1B00, "end": 0x1B7F }, // Balinese
  { "begin": 0x1100, "end": 0x11FF }, // Hangul Jamo
  { "begin": 0x1E00, "end": 0x1EFF }, // Latin Extended Additional
  { "begin": 0x1F00, "end": 0x1FFF }, // Greek Extended
  { "begin": 0x2000, "end": 0x206F }, // General Punctuation
  { "begin": 0x2070, "end": 0x209F }, // Superscripts And Subscripts
  { "begin": 0x20A0, "end": 0x20CF }, // Currency Symbol
  { "begin": 0x20D0, "end": 0x20FF }, // Combining Diacritical Marks For Symbols
  { "begin": 0x2100, "end": 0x214F }, // Letterlike Symbols
  { "begin": 0x2150, "end": 0x218F }, // Number Forms
  { "begin": 0x2190, "end": 0x21FF }, // Arrows
  { "begin": 0x2200, "end": 0x22FF }, // Mathematical Operators
  { "begin": 0x2300, "end": 0x23FF }, // Miscellaneous Technical
  { "begin": 0x2400, "end": 0x243F }, // Control Pictures
  { "begin": 0x2440, "end": 0x245F }, // Optical Character Recognition
  { "begin": 0x2460, "end": 0x24FF }, // Enclosed Alphanumerics
  { "begin": 0x2500, "end": 0x257F }, // Box Drawing
  { "begin": 0x2580, "end": 0x259F }, // Block Elements
  { "begin": 0x25A0, "end": 0x25FF }, // Geometric Shapes
  { "begin": 0x2600, "end": 0x26FF }, // Miscellaneous Symbols
  { "begin": 0x2700, "end": 0x27BF }, // Dingbats
  { "begin": 0x3000, "end": 0x303F }, // CJK Symbols And Punctuation
  { "begin": 0x3040, "end": 0x309F }, // Hiragana
  { "begin": 0x30A0, "end": 0x30FF }, // Katakana
  { "begin": 0x3100, "end": 0x312F }, // Bopomofo
  { "begin": 0x3130, "end": 0x318F }, // Hangul Compatibility Jamo
  { "begin": 0xA840, "end": 0xA87F }, // Phags-pa
  { "begin": 0x3200, "end": 0x32FF }, // Enclosed CJK Letters And Months
  { "begin": 0x3300, "end": 0x33FF }, // CJK Compatibility
  { "begin": 0xAC00, "end": 0xD7AF }, // Hangul Syllables
  { "begin": 0xD800, "end": 0xDFFF }, // Non-Plane 0 *
  { "begin": 0x10900, "end": 0x1091F }, // Phoenicia
  { "begin": 0x4E00, "end": 0x9FFF }, // CJK Unified Ideographs
  { "begin": 0xE000, "end": 0xF8FF }, // Private Use Area (plane 0)
  { "begin": 0x31C0, "end": 0x31EF }, // CJK Strokes
  { "begin": 0xFB00, "end": 0xFB4F }, // Alphabetic Presentation Forms
  { "begin": 0xFB50, "end": 0xFDFF }, // Arabic Presentation Forms-A
  { "begin": 0xFE20, "end": 0xFE2F }, // Combining Half Marks
  { "begin": 0xFE10, "end": 0xFE1F }, // Vertical Forms
  { "begin": 0xFE50, "end": 0xFE6F }, // Small Form Variants
  { "begin": 0xFE70, "end": 0xFEFF }, // Arabic Presentation Forms-B
  { "begin": 0xFF00, "end": 0xFFEF }, // Halfwidth And Fullwidth Forms
  { "begin": 0xFFF0, "end": 0xFFFF }, // Specials
  { "begin": 0x0F00, "end": 0x0FFF }, // Tibetan
  { "begin": 0x0700, "end": 0x074F }, // Syriac
  { "begin": 0x0780, "end": 0x07BF }, // Thaana
  { "begin": 0x0D80, "end": 0x0DFF }, // Sinhala
  { "begin": 0x1000, "end": 0x109F }, // Myanmar
  { "begin": 0x1200, "end": 0x137F }, // Ethiopic
  { "begin": 0x13A0, "end": 0x13FF }, // Cherokee
  { "begin": 0x1400, "end": 0x167F }, // Unified Canadian Aboriginal Syllabics
  { "begin": 0x1680, "end": 0x169F }, // Ogham
  { "begin": 0x16A0, "end": 0x16FF }, // Runic
  { "begin": 0x1780, "end": 0x17FF }, // Khmer
  { "begin": 0x1800, "end": 0x18AF }, // Mongolian
  { "begin": 0x2800, "end": 0x28FF }, // Braille Patterns
  { "begin": 0xA000, "end": 0xA48F }, // Yi Syllables
  { "begin": 0x1700, "end": 0x171F }, // Tagalog
  { "begin": 0x10300, "end": 0x1032F }, // Old Italic
  { "begin": 0x10330, "end": 0x1034F }, // Gothic
  { "begin": 0x10400, "end": 0x1044F }, // Deseret
  { "begin": 0x1D000, "end": 0x1D0FF }, // Byzantine Musical Symbols
  { "begin": 0x1D400, "end": 0x1D7FF }, // Mathematical Alphanumeric Symbols
  { "begin": 0xFF000, "end": 0xFFFFD }, // Private Use (plane 15)
  { "begin": 0xFE00, "end": 0xFE0F }, // Variation Selectors
  { "begin": 0xE0000, "end": 0xE007F }, // Tags
  { "begin": 0x1900, "end": 0x194F }, // Limbu
  { "begin": 0x1950, "end": 0x197F }, // Tai Le
  { "begin": 0x1980, "end": 0x19DF }, // New Tai Lue
  { "begin": 0x1A00, "end": 0x1A1F }, // Buginese
  { "begin": 0x2C00, "end": 0x2C5F }, // Glagolitic
  { "begin": 0x2D30, "end": 0x2D7F }, // Tifinagh
  { "begin": 0x4DC0, "end": 0x4DFF }, // Yijing Hexagram Symbols
  { "begin": 0xA800, "end": 0xA82F }, // Syloti Nagri
  { "begin": 0x10000, "end": 0x1007F }, // Linear B Syllabary
  { "begin": 0x10140, "end": 0x1018F }, // Ancient Greek Numbers
  { "begin": 0x10380, "end": 0x1039F }, // Ugaritic
  { "begin": 0x103A0, "end": 0x103DF }, // Old Persian
  { "begin": 0x10450, "end": 0x1047F }, // Shavian
  { "begin": 0x10480, "end": 0x104AF }, // Osmanya
  { "begin": 0x10800, "end": 0x1083F }, // Cypriot Syllabary
  { "begin": 0x10A00, "end": 0x10A5F }, // Kharoshthi
  { "begin": 0x1D300, "end": 0x1D35F }, // Tai Xuan Jing Symbols
  { "begin": 0x12000, "end": 0x123FF }, // Cuneiform
  { "begin": 0x1D360, "end": 0x1D37F }, // Counting Rod Numerals
  { "begin": 0x1B80, "end": 0x1BBF }, // Sundanese
  { "begin": 0x1C00, "end": 0x1C4F }, // Lepcha
  { "begin": 0x1C50, "end": 0x1C7F }, // <NAME>
  { "begin": 0xA880, "end": 0xA8DF }, // Saurashtra
  { "begin": 0xA900, "end": 0xA92F }, // Kayah Li
  { "begin": 0xA930, "end": 0xA95F }, // Rejang
  { "begin": 0xAA00, "end": 0xAA5F }, // Cham
  { "begin": 0x10190, "end": 0x101CF }, // Ancient Symbols
  { "begin": 0x101D0, "end": 0x101FF }, // Phaistos Disc
  { "begin": 0x102A0, "end": 0x102DF }, // Carian
  { "begin": 0x1F030, "end": 0x1F09F }  // Domino Tiles
];

function getUnicodeRangeFor(value) {
  for (var i = 0; i < UnicodeRanges.length; i++) {
    var range = UnicodeRanges[i];
    if (value >= range.begin && value < range.end)
      return i;
  }
  return -1;
};

/**
 * '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).
 *
 * For example to read a Type1 font and to attach it to the document:
 *   var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
 *   type1Font.bind();
 */
var Font = (function () {
  var constructor = function font_constructor(name, file, properties) {
    this.name = name;
    this.encoding = properties.encoding;

    // If the font has already been decoded simply return it
    if (Fonts[name]) {
      this.font = Fonts[name].data;
      return;
    }
    fontCount++;
    fontName = name;

    // If the font is to be ignored, register it like an already loaded font
    // to avoid the cost of waiting for it be be loaded by the platform.
    if (properties.ignore || kDisableFonts) {
      Fonts[name] = {
        data: file,
        loading: false,
        properties: {},
        cache: Object.create(null)
      }
      return;
    }

    var data;
    switch (properties.type) {
      case "Type1":
        var cff = new CFF(name, file, properties);
        this.mimetype = "font/opentype";

        // Wrap the CFF data inside an OTF font file
        data = this.convert(name, cff, properties);
        break;

      case "TrueType":
        this.mimetype = "font/opentype";

        // Repair the TrueType file if it is can be damaged in the point of
        // view of the sanitizer
        data = this.checkAndRepair(name, file, properties);
        break;

      default:
        warn("Font " + properties.type + " is not supported");
        break;
    }
    this.data = data;

    Fonts[name] = {
      data: data,
      properties: properties,
      loading: true,
      cache: Object.create(null)
    };
  };

  function stringToArray(str) {
    var array = [];
    for (var i = 0; i < str.length; ++i)
      array[i] = str.charCodeAt(i);

    return array;
  };

  function string16(value) {
    return String.fromCharCode((value >> 8) & 0xff) +
           String.fromCharCode(value & 0xff);
  };

  function string32(value) {
    return String.fromCharCode((value >> 24) & 0xff) +
           String.fromCharCode((value >> 16) & 0xff) +
           String.fromCharCode((value >> 8) & 0xff) +
           String.fromCharCode(value & 0xff);
  };

  function createOpenTypeHeader(sfnt, file, offsets, numTables) {
    // sfnt version (4 bytes)
    var header = sfnt;

    // numTables (2 bytes)
    header += string16(numTables);

    // searchRange (2 bytes)
    var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables);
    var searchRange = tablesMaxPower2 * 16;
    header += string16(searchRange);

    // entrySelector (2 bytes)
    header += string16(Math.log(tablesMaxPower2) / Math.log(2));

    // rangeShift (2 bytes)
    header += string16(numTables * 16 - searchRange);

    file.set(stringToArray(header), offsets.currentOffset);
    offsets.currentOffset += header.length;
    offsets.virtualOffset += header.length;
  };

  function createTableEntry(file, offsets, tag, data) {
    // offset
    var offset = offsets.virtualOffset;

    // length
    var length = data.length;

    // Per spec tables must be 4-bytes align so add padding as needed
    while (data.length & 3)
      data.push(0x00);

    while (offsets.virtualOffset & 3)
      offsets.virtualOffset++;

    // checksum
    var checksum = 0;
    for (var i = 0; i < length; i+=4)
      checksum += FontsUtils.bytesToInteger([data[i], data[i+1], data[i+2], data[i+3]]);

    var tableEntry = tag + string32(checksum) + string32(offset) + string32(length);
    tableEntry = stringToArray(tableEntry);
    file.set(tableEntry, offsets.currentOffset);

    offsets.currentOffset += tableEntry.length;
    offsets.virtualOffset += data.length;
  };

  function getRanges(glyphs) {
    // Array.sort() sorts by characters, not numerically, so convert to an
    // array of characters.
    var codes = [];
    var length = glyphs.length;
    for (var n = 0; n < length; ++n)
      codes.push(String.fromCharCode(glyphs[n].unicode))
    codes.sort();

    // Split the sorted codes into ranges.
    var ranges = [];
    for (var n = 0; n < length; ) {
      var start = codes[n++].charCodeAt(0);
      var end = start;
      while (n < length && end + 1 == codes[n].charCodeAt(0)) {
        ++end;
        ++n;
      }
      ranges.push([start, end]);
    }
    return ranges;
  };

  function createCMapTable(glyphs) {
    var ranges = getRanges(glyphs);

    var headerSize = (12 * 2 + (ranges.length * 4 * 2));
    var segCount = ranges.length + 1;
    var segCount2 = segCount * 2;
    var searchRange = FontsUtils.getMaxPower2(segCount) * 2;
    var searchEntry = Math.log(segCount) / Math.log(2);
    var rangeShift = 2 * segCount - searchRange;

    var cmap = "\x00\x00" + // version
               "\x00\x01" + // numTables
               "\x00\x03" + // platformID
               "\x00\x01" + // encodingID
               "\x00\x00\x00\x0C" + // start of the table record
               "\x00\x04" + // format
               string16(headerSize) + // length
               "\x00\x00" + // languages
               string16(segCount2) +
               string16(searchRange) +
               string16(searchEntry) +
               string16(rangeShift);

    // Fill up the 4 parallel arrays describing the segments.
    var startCount = "";
    var endCount = "";
    var idDeltas = "";
    var idRangeOffsets = "";
    var glyphsIds = "";
    var bias = 0;
    for (var i = 0; i < segCount - 1; i++) {
      var range = ranges[i];
      var start = range[0];
      var end = range[1];
      var delta = (((start - 1) - bias) ^ 0xffff) + 1;
      bias += (end - start + 1);

      startCount += string16(start);
      endCount += string16(end);
      idDeltas += string16(delta);
      idRangeOffsets += string16(0);

      for (var j = 0; j < range.length; j++)
        glyphsIds += String.fromCharCode(range[j]);
    }

    startCount += "\xFF\xFF";
    endCount += "\xFF\xFF";
    idDeltas += "\x00\x01";
    idRangeOffsets += "\x00\x00";

    return stringToArray(cmap + endCount + "\x00\x00" + startCount +
                         idDeltas + idRangeOffsets + glyphsIds);
  };

  function createOS2Table(properties) {
    var ulUnicodeRange1 = 0;
    var ulUnicodeRange2 = 0;
    var ulUnicodeRange3 = 0;
    var ulUnicodeRange4 = 0;

    var charset = properties.charset;
    if (charset && charset.length) {
      // XXX why is the first character equal to ''?
      for (var i = 1; i < charset.length; i++) {
        var position = getUnicodeRangeFor(GlyphsUnicode[charset[i]]);
        if (position < 32) {
          ulUnicodeRange1 |= 1 << position;
        } else if (position < 64) {
          ulUnicodeRange2 |= 1 << position - 32;
        } else if (position < 96) {
          ulUnicodeRange3 |= 1 << position - 64;
        } else if (position < 123) {
          ulUnicodeRange4 |= 1 << position - 96;
        } else {
          error("Unicode ranges Bits > 123 are reserved for internal usage");
        }
      }
    }

    return "\x00\x03" + // version
           "\x02\x24" + // xAvgCharWidth
           "\x01\xF4" + // usWeightClass
           "\x00\x05" + // usWidthClass
           "\x00\x00" + // fstype
           "\x02\x8A" + // ySubscriptXSize
           "\x02\xBB" + // ySubscriptYSize
           "\x00\x00" + // ySubscriptXOffset
           "\x00\x8C" + // ySubscriptYOffset
           "\x02\x8A" + // ySuperScriptXSize
           "\x02\xBB" + // ySuperScriptYSize
           "\x00\x00" + // ySuperScriptXOffset
           "\x01\xDF" + // ySuperScriptYOffset
           "\x00\x31" + // yStrikeOutSize
           "\x01\x02" + // yStrikeOutPosition
           "\x00\x00" + // sFamilyClass
           "\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose
           string32(ulUnicodeRange1) + // ulUnicodeRange1 (Bits 0-31)
           string32(ulUnicodeRange2) + // ulUnicodeRange2 (Bits 32-63)
           string32(ulUnicodeRange3) + // ulUnicodeRange3 (Bits 64-95)
           string32(ulUnicodeRange4) + // ulUnicodeRange4 (Bits 96-127)
           "\x2A\x32\x31\x2A" + // achVendID
           "\x00\x00" + // fsSelection
           string16(properties.firstChar) + // usFirstCharIndex
           string16(properties.lastChar) +  // usLastCharIndex
           "\x00\x20" + // sTypoAscender
           "\x00\x00" + // sTypeDescender
           "\x00\x00" + // sTypoLineGap
           string16(properties.ascent)  + // usWinAscent
           string16(properties.descent) + // usWinDescent
           "\x00\x00\x00\x00" + // ulCodePageRange1 (Bits 0-31)
           "\x00\x00\x00\x00" + // ulCodePageRange2 (Bits 32-63)
           string16(properties.xHeight)   + // sxHeight
           string16(properties.capHeight) + // sCapHeight
           // XXX mapping .notdef in the cmap table will allow us to use it
           // here instead, especially because these field are limit to 0xFFFF
           string16(properties.firstChar) + // usDefaultChar
           string16(properties.firstChar) + // usBreakChar
           "\x00\x00";  // usMaxContext
  };

  function createPostTable(properties) {
    TODO("Fill with real values from the font dict");

    return "\x00\x03\x00\x00"               + // Version number
           string32(properties.italicAngle) + // italicAngle
           "\x00\x00"                       + // underlinePosition
           "\x00\x00"                       + // underlineThickness
           "\x00\x00\x00\x00"               + // isFixedPitch
           "\x00\x00\x00\x00"               + // minMemType42
           "\x00\x00\x00\x00"               + // maxMemType42
           "\x00\x00\x00\x00"               + // minMemType1
           "\x00\x00\x00\x00";                // maxMemType1
  };

  constructor.prototype = {
    name: null,
    font: null,
    mimetype: null,
{'fraction_non_alphanumeric': 0.14965540898743257, 'fraction_numerical': 0.05703682913899024, 'mean_word_length': 2.304957229722766, 'pattern_counts': {'":': 250, '<': 39, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4677588', 'n_tokens_mistral': 12305, 'n_tokens_neox': 10954, 'n_words': 2789}