diff --git a/fonts.js b/fonts.js
index 0c8725fb4..b8a490369 100644
--- a/fonts.js
+++ b/fonts.js
@@ -8,11 +8,6 @@
*/
var kMaxFontFileSize = 40000;
-/**
- * Maximum number of glyphs per font.
-*/
-var kMaxGlyphsCount = 65526;
-
/**
* Maximum time to wait for a font to be loaded by @font-face
*/
@@ -62,6 +57,10 @@ var Fonts = {
var uc = encoding[ch];
if (uc instanceof Name) // we didn't convert the glyph yet
uc = encoding[ch] = GlyphsUnicode[uc.name];
+ if (uc > 0xffff) { // handle surrogate pairs
+ ret += String.fromCharCode(uc & 0xffff);
+ uc >>= 16;
+ }
ret += String.fromCharCode(uc);
}
@@ -80,23 +79,24 @@ var Fonts = {
* var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
* type1Font.bind();
*/
-var Font = function(aName, aFile, aProperties) {
- this.name = aName;
+var Font = (function () {
+ var constructor = function(aName, aFile, aProperties) {
+ this.name = aName;
- // If the font has already been decoded simply return
- if (Fonts[aName]) {
- this.font = Fonts[aName].data;
- return;
- }
- fontCount++;
+ // If the font has already been decoded simply return it
+ if (Fonts[aName]) {
+ this.font = Fonts[aName].data;
+ return;
+ }
+ fontCount++;
- switch (aProperties.type) {
+ switch (aProperties.type) {
case "Type1":
var cff = new CFF(aName, aFile, aProperties);
this.mimetype = "font/otf";
// Wrap the CFF data inside an OTF font file
- this.font = this.cover(cff, aProperties);
+ this.font = this.convert(cff, aProperties);
break;
case "TrueType":
@@ -120,449 +120,448 @@ var Font = function(aName, aFile, aProperties) {
default:
warn("Font " + aProperties.type + " is not supported");
break;
- }
-
- Fonts[aName] = {
- data: this.font,
- properties: aProperties,
- loading: true,
- cache: Object.create(null)
- }
-
- // Attach the font to the document
- this.bind();
-};
-
-
-/**
- * A bunch of the OpenType code is duplicate between this class and the
- * TrueType code, this is intentional and will merge in a future version
- * where all the code relative to OpenType will probably have its own
- * class and will take decision without the Fonts consent.
- * But at the moment it allows to develop around the TrueType rewriting
- * on the fly without messing up with the 'regular' Type1 to OTF conversion.
- */
-Font.prototype = {
- name: null,
- font: null,
- mimetype: null,
-
- bind: function font_bind() {
- var data = this.font;
-
- // Compute the binary data to base 64
- var str = [];
- var count = data.length;
- for (var i = 0; i < count; i++)
- str.push(data.getChar ? data.getChar()
- : String.fromCharCode(data[i]));
-
- var dataBase64 = window.btoa(str.join(""));
- var fontName = this.name;
-
- /** Hack begin */
-
- // Actually there is not event when a font has finished downloading so
- // the following tons of code are a dirty hack to 'guess' when a font is
- // ready
- var debug = false;
-
- if (debug) {
- var name = document.createElement("font");
- name.setAttribute("style", "position: absolute; left: 20px; top: " +
- (100 * fontCount + 60) + "px");
- name.innerHTML = fontName;
- document.body.appendChild(name);
}
- var canvas = document.createElement("canvas");
- var style = "border: 1px solid black; position:absolute; top: " +
- (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px";
- canvas.setAttribute("style", style);
- canvas.setAttribute("width", 340);
- canvas.setAttribute("heigth", 100);
- document.body.appendChild(canvas);
+ Fonts[aName] = {
+ data: this.font,
+ properties: aProperties,
+ loading: true,
+ cache: Object.create(null)
+ }
- // Retrieve font charset
- var charset = Fonts[fontName].charset || [];
- // if the charset is too small make it repeat a few times
- var count = 30;
- while (count-- && charset.length <= 30)
- charset = charset.concat(charset.slice());
+ // Attach the font to the document
+ this.bind();
+ };
- // Get the font size canvas think it will be for 'spaces'
- var ctx = canvas.getContext("2d");
- var testString = " ";
+ /**
+ * A bunch of the OpenType code is duplicate between this class and the
+ * TrueType code, this is intentional and will merge in a future version
+ * where all the code relative to OpenType will probably have its own
+ * class and will take decision without the Fonts consent.
+ * But at the moment it allows to develop around the TrueType rewriting
+ * on the fly without messing up with the 'regular' Type1 to OTF conversion.
+ */
+ constructor.prototype = {
+ name: null,
+ font: null,
+ mimetype: null,
- // When debugging use the characters provided by the charsets to visually
- // see what's happening
- if (debug) {
- for (var i = 0; i < charset.length; i++) {
- var unicode = GlyphsUnicode[charset[i]];
- if (!unicode)
- error("Unicode for " + charset[i] + " is has not been found in the glyphs list");
- testString += String.fromCharCode(unicode);
+ bind: function font_bind() {
+ var data = this.font;
+
+ // Get the base64 encoding of the binary font data
+ var str = "";
+ var length = data.length;
+ for (var i = 0; i < length; ++i)
+ str += String.fromCharCode(data[i]);
+
+ var dataBase64 = window.btoa(str);
+ var fontName = this.name;
+
+ /** Hack begin */
+
+ // Actually there is not event when a font has finished downloading so
+ // the following tons of code are a dirty hack to 'guess' when a font is
+ // ready
+ var debug = false;
+
+ if (debug) {
+ var name = document.createElement("font");
+ name.setAttribute("style", "position: absolute; left: 20px; top: " +
+ (100 * fontCount + 60) + "px");
+ name.innerHTML = fontName;
+ document.body.appendChild(name);
}
- }
- ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
- var textWidth = ctx.measureText(testString).width;
- if (debug)
- ctx.fillText(testString, 20, 20);
+ var canvas = document.createElement("canvas");
+ var style = "border: 1px solid black; position:absolute; top: " +
+ (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px";
+ canvas.setAttribute("style", style);
+ canvas.setAttribute("width", 340);
+ canvas.setAttribute("heigth", 100);
+ document.body.appendChild(canvas);
- var start = Date.now();
- var interval = window.setInterval(function canvasInterval(self) {
+ // Retrieve font charset
+ var charset = Fonts[fontName].charset || [];
+ // if the charset is too small make it repeat a few times
+ var count = 30;
+ while (count-- && charset.length <= 30)
+ charset = charset.concat(charset.slice());
+
+ // Get the font size canvas think it will be for 'spaces'
+ var ctx = canvas.getContext("2d");
+ var testString = " ";
+
+ // When debugging use the characters provided by the charsets to visually
+ // see what's happening
+ if (debug) {
+ for (var i = 0; i < charset.length; i++) {
+ var unicode = GlyphsUnicode[charset[i]];
+ if (!unicode)
+ error("Unicode for " + charset[i] + " is has not been found in the glyphs list");
+ testString += String.fromCharCode(unicode);
+ }
+ }
ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
-
- // For some reasons the font has not loaded, so mark it loaded for the
- // page to proceed but cry
- if ((Date.now() - start) >= kMaxWaitForFontFace) {
- window.clearInterval(interval);
- Fonts[fontName].loading = false;
- warn("Is " + fontName + " for charset: " + charset + " loaded?");
- } else if (textWidth != ctx.measureText(testString).width) {
- window.clearInterval(interval);
- Fonts[fontName].loading = false;
- }
+ var textWidth = ctx.measureText(testString).width;
if (debug)
- ctx.fillText(testString, 20, 50);
- }, 50, this);
+ ctx.fillText(testString, 20, 20);
- /** Hack end */
+ var start = Date.now();
+ var interval = window.setInterval(function canvasInterval(self) {
+ ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
- // Add the @font-face rule to the document
- var url = "url(data:" + this.mimetype + ";base64," + dataBase64 + ");";
- var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
- var styleSheet = document.styleSheets[0];
- styleSheet.insertRule(rule, styleSheet.length);
- },
+ // For some reasons the font has not loaded, so mark it loaded for the
+ // page to proceed but cry
+ if ((Date.now() - start) >= kMaxWaitForFontFace) {
+ window.clearInterval(interval);
+ Fonts[fontName].loading = false;
+ warn("Is " + fontName + " for charset: " + charset + " loaded?");
+ } else if (textWidth != ctx.measureText(testString).width) {
+ window.clearInterval(interval);
+ Fonts[fontName].loading = false;
+ }
- _createOpenTypeHeader: function font_createOpenTypeHeader(aFile, aOffsets, aNumTables) {
- // sfnt version (4 bytes)
- var version = [0x4F, 0x54, 0x54, 0X4F];
+ if (debug)
+ ctx.fillText(testString, 20, 50);
+ }, 50, this);
- // numTables (2 bytes)
- var numTables = aNumTables;
+ /** Hack end */
- // searchRange (2 bytes)
- var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables);
- var searchRange = tablesMaxPower2 * 16;
+ // Add the @font-face rule to the document
+ var url = "url(data:" + this.mimetype + ";base64," + dataBase64 + ");";
+ var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
+ var styleSheet = document.styleSheets[0];
+ styleSheet.insertRule(rule, styleSheet.length);
+ },
- // entrySelector (2 bytes)
- var entrySelector = Math.log(tablesMaxPower2) / Math.log(2);
+ convert: function font_convert(aFont, aProperties) {
+ var otf = new Uint8Array(kMaxFontFileSize);
- // rangeShift (2 bytes)
- var rangeShift = numTables * 16 - searchRange;
-
- var header = [].concat(version,
- FontsUtils.integerToBytes(numTables, 2),
- FontsUtils.integerToBytes(searchRange, 2),
- FontsUtils.integerToBytes(entrySelector, 2),
- FontsUtils.integerToBytes(rangeShift, 2));
- aFile.set(header, aOffsets.currentOffset);
- aOffsets.currentOffset += header.length;
- aOffsets.virtualOffset += header.length;
- },
-
- _createTableEntry: function font_createTableEntry(aFile, aOffsets, aTag, aData) {
- // tag
- var tag = [
- aTag.charCodeAt(0),
- aTag.charCodeAt(1),
- aTag.charCodeAt(2),
- aTag.charCodeAt(3)
- ];
-
- // offset
- var offset = aOffsets.virtualOffset;
-
- // Per spec tables must be 4-bytes align so add some 0x00 if needed
- while (aData.length & 3)
- aData.push(0x00);
-
- // length
- var length = aData.length;
-
- // checksum
- var checksum = FontsUtils.bytesToInteger(tag) + offset + length;
-
- var tableEntry = [].concat(tag,
- FontsUtils.integerToBytes(checksum, 4),
- FontsUtils.integerToBytes(offset, 4),
- FontsUtils.integerToBytes(length, 4));
- aFile.set(tableEntry, aOffsets.currentOffset);
- aOffsets.currentOffset += tableEntry.length;
- aOffsets.virtualOffset += aData.length;
- },
-
- _createCMAPTable: function font_createCMAPTable(aGlyphs) {
- var characters = new Uint16Array(kMaxGlyphsCount);
- for (var i = 0; i < aGlyphs.length; i++)
- characters[aGlyphs[i].unicode] = i + 1;
-
- // Separate the glyphs into continuous range of codes, aka segment.
- var ranges = [];
- var range = [];
- var count = characters.length;
- for (var i = 0; i < count; i++) {
- if (characters[i]) {
- range.push(i);
- } else if (range.length) {
- ranges.push(range.slice());
- range = [];
+ function s2a(s) {
+ var a = [];
+ for (var i = 0; i < s.length; ++i)
+ a[i] = s.charCodeAt(i);
+ return a;
}
+
+ function s16(value) {
+ return String.fromCharCode((value >> 8) & 0xff) + String.fromCharCode(value & 0xff);
+ }
+
+ function s32(value) {
+ return String.fromCharCode((value >> 24) & 0xff) + String.fromCharCode((value >> 16) & 0xff) +
+ String.fromCharCode((value >> 8) & 0xff) + String.fromCharCode(value & 0xff);
+ }
+
+ function createOpenTypeHeader(aFile, aOffsets, numTables) {
+ var header = "";
+
+ // sfnt version (4 bytes)
+ header += "\x4F\x54\x54\x4F";
+
+ // numTables (2 bytes)
+ header += s16(numTables);
+
+ // searchRange (2 bytes)
+ var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables);
+ var searchRange = tablesMaxPower2 * 16;
+ header += s16(searchRange);
+
+ // entrySelector (2 bytes)
+ header += s16(Math.log(tablesMaxPower2) / Math.log(2));
+
+ // rangeShift (2 bytes)
+ header += s16(numTables * 16 - searchRange);
+
+ aFile.set(s2a(header), aOffsets.currentOffset);
+ aOffsets.currentOffset += header.length;
+ aOffsets.virtualOffset += header.length;
+ }
+
+ function createTableEntry(aFile, aOffsets, aTag, aData) {
+ // offset
+ var offset = aOffsets.virtualOffset;
+
+ // Per spec tables must be 4-bytes align so add padding as needed
+ while (aData.length & 3)
+ aData.push(0x00);
+
+ // length
+ var length = aData.length;
+
+ // checksum
+ var checksum = aTag.charCodeAt(0) +
+ aTag.charCodeAt(1) +
+ aTag.charCodeAt(2) +
+ aTag.charCodeAt(3) +
+ offset +
+ length;
+
+ var tableEntry = aTag + s32(checksum) + s32(offset) + s32(length);
+ tableEntry = s2a(tableEntry);
+ aFile.set(tableEntry, aOffsets.currentOffset);
+ aOffsets.currentOffset += tableEntry.length;
+ aOffsets.virtualOffset += aData.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(aGlyphs) {
+ var ranges = getRanges(aGlyphs);
+
+ 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
+ s16(headerSize) + // length
+ "\x00\x00" + // languages
+ s16(segCount2) +
+ s16(searchRange) +
+ s16(searchEntry) +
+ s16(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 += s16(start);
+ endCount += s16(end);
+ idDeltas += s16(delta);
+ idRangeOffsets += s16(0);
+
+ for (var j = start; j <= end; j++)
+ glyphsIds += String.fromCharCode(j);
+ }
+
+ startCount += "\xFF\xFF";
+ endCount += "\xFF\xFF";
+ idDeltas += "\x00\x01";
+ idRangeOffsets += "\x00\x00";
+
+ return s2a(cmap + endCount + "\x00\x00" + startCount +
+ idDeltas + idRangeOffsets + glyphsIds);
+ }
+
+ // Required Tables
+ var CFF = aFont.data, // PostScript Font Program
+ OS2 = [], // OS/2 and Windows Specific metrics
+ cmap = [], // Character to glyphs mapping
+ head = [], // Font eader
+ hhea = [], // Horizontal header
+ hmtx = [], // Horizontal metrics
+ maxp = [], // Maximum profile
+ name = [], // Naming tables
+ post = []; // PostScript informations
+ var tables = [CFF, OS2, cmap, head, hhea, hmtx, maxp, name, post];
+
+ // 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 draw the actual data of a particular
+ // table
+ var offsets = {
+ currentOffset: 0,
+ virtualOffset: tables.length * (4 * 4)
+ };
+
+ // For files with only one font the offset table is the first thing of the
+ // file
+ createOpenTypeHeader(otf, offsets, tables.length);
+
+ // TODO: It is probable that in a future we want to get rid of this glue
+ // between the CFF and the OTF format in order to be able to embed TrueType
+ // data.
+ createTableEntry(otf, offsets, "CFF ", CFF);
+
+ /** OS/2 */
+ OS2 = s2a(
+ "\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\x20" + // fsSelection
+ "\x00\x2D" + // usFirstCharIndex
+ "\x00\x7A" + // usLastCharIndex
+ "\x00\x03" + // sTypoAscender
+ "\x00\x20" + // sTypeDescender
+ "\x00\x38" + // sTypoLineGap
+ "\x00\x5A" + // usWinAscent
+ "\x02\xB4" + // usWinDescent
+ "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
+ "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
+ "\x00\x00" + // sxHeight
+ "\x00\x00" + // sCapHeight
+ "\x00\x01" + // usDefaultChar
+ "\x00\xCD" + // usBreakChar
+ "\x00\x02" // usMaxContext
+ );
+ createTableEntry(otf, offsets, "OS/2", OS2);
+
+ //XXX Getting charstrings here seems wrong since this is another CFF glue
+ var charstrings = aFont.getOrderedCharStrings(aProperties.glyphs);
+
+ /** CMAP */
+ cmap = createCMAPTable(charstrings);
+ createTableEntry(otf, offsets, "cmap", cmap);
+
+ /** HEAD */
+ head = s2a(
+ "\x00\x01\x00\x00" + // Version number
+ "\x00\x00\x50\x00" + // fontRevision
+ "\x00\x00\x00\x00" + // checksumAdjustement
+ "\x5F\x0F\x3C\xF5" + // magicNumber
+ "\x00\x00" + // Flags
+ "\x03\xE8" + // unitsPerEM (defaulting to 1000)
+ "\x00\x00\x00\x00\x00\x00\x00\x00" + // creation date
+ "\x00\x00\x00\x00\x00\x00\x00\x00" + // modifification date
+ "\x00\x00" + // xMin
+ "\x00\x00" + // yMin
+ "\x00\x00" + // xMax
+ "\x00\x00" + // yMax
+ "\x00\x00" + // macStyle
+ "\x00\x00" + // lowestRecPPEM
+ "\x00\x00" + // fontDirectionHint
+ "\x00\x00" + // indexToLocFormat
+ "\x00\x00" // glyphDataFormat
+ );
+ createTableEntry(otf, offsets, "head", head);
+
+ /** HHEA */
+ hhea = s2a(
+ "\x00\x01\x00\x00" + // Version number
+ "\x00\x00" + // Typographic Ascent
+ "\x00\x00" + // Typographic Descent
+ "\x00\x00" + // Line Gap
+ "\xFF\xFF" + // advanceWidthMax
+ "\x00\x00" + // minLeftSidebearing
+ "\x00\x00" + // minRightSidebearing
+ "\x00\x00" + // xMaxExtent
+ "\x00\x00" + // caretSlopeRise
+ "\x00\x00" + // caretSlopeRun
+ "\x00\x00" + // caretOffset
+ "\x00\x00" + // -reserved-
+ "\x00\x00" + // -reserved-
+ "\x00\x00" + // -reserved-
+ "\x00\x00" + // -reserved-
+ "\x00\x00" + // metricDataFormat
+ s16(charstrings.length)
+ );
+ createTableEntry(otf, offsets, "hhea", hhea);
+
+ /** HMTX */
+ hmtx = "\x01\xF4\x00\x00";
+ for (var i = 0; i < charstrings.length; i++) {
+ var charstring = charstrings[i].charstring;
+ var width = charstring[1];
+ var lsb = charstring[0];
+ hmtx += s16(width) + s16(lsb);
+ }
+ hmtx = s2a(hmtx);
+ createTableEntry(otf, offsets, "hmtx", hmtx);
+
+ /** MAXP */
+ maxp = "\x00\x00\x50\x00" + // Version number
+ s16(charstrings.length + 1); // Num of glyphs (+1 to pass the sanitizer...)
+ maxp = s2a(maxp);
+ createTableEntry(otf, offsets, "maxp", maxp);
+
+ /** NAME */
+ name = "\x00\x00" + // Format
+ "\x00\x00" + // Number of name records
+ "\x00\x00"; // Storage
+ name = s2a(name);
+ createTableEntry(otf, offsets, "name", name);
+
+ /** POST */
+ // TODO: get those informations from the FontInfo structure
+ post = "\x00\x03\x00\x00" + // Version number
+ "\x00\x00\x01\x00" + // 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
+ post = s2a(post);
+ createTableEntry(otf, offsets, "post", post);
+
+ // Once all the table entries header are written, dump the data!
+ var tables = [CFF, OS2, cmap, head, hhea, hmtx, maxp, name, post];
+ for (var i = 0; i < tables.length; i++) {
+ var table = tables[i];
+ otf.set(table, offsets.currentOffset);
+ offsets.currentOffset += table.length;
+ }
+
+ var fontData = [];
+ for (var i = 0; i < offsets.currentOffset; i++)
+ fontData.push(otf[i]);
+ return fontData;
}
+ };
- // The size in bytes of the header is equal to the size of the
- // different fields * length of a short + (size of the 4 parallels arrays
- // describing segments * length of a short).
- 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 = [].concat(
- [
- 0x00, 0x00, // version
- 0x00, 0x01, // numTables
- 0x00, 0x03, // platformID
- 0x00, 0x01, // encodingID
- 0x00, 0x00, 0x00, 0x0C, // start of the table record
- 0x00, 0x04 // format
- ],
- FontsUtils.integerToBytes(headerSize, 2), // length
- [0x00, 0x00], // language
- FontsUtils.integerToBytes(segCount2, 2),
- FontsUtils.integerToBytes(searchRange, 2),
- FontsUtils.integerToBytes(searchEntry, 2),
- FontsUtils.integerToBytes(rangeShift, 2)
- );
-
- // Fill up the 4 parallel arrays describing the segments.
- var startCount = [];
- var endCount = [];
- var idDeltas = [];
- var idRangeOffsets = [];
- var glyphsIdsArray = [];
- var bias = 0;
- for (var i = 0; i < segCount - 1; i++) {
- var range = ranges[i];
- var start = FontsUtils.integerToBytes(range[0], 2);
- var end = FontsUtils.integerToBytes(range[range.length - 1], 2);
-
- var delta = FontsUtils.integerToBytes(((range[0] - 1) - bias) % 65536, 2);
- bias += range.length;
-
- // deltas are signed shorts
- delta[0] ^= 0xFF;
- delta[1] ^= 0xFF;
- delta[1] += 1;
-
- startCount.push(start[0], start[1]);
- endCount.push(end[0], end[1]);
- idDeltas.push(delta[0], delta[1]);
- idRangeOffsets.push(0x00, 0x00);
-
- for (var j = 0; j < range.length; j++)
- glyphsIdsArray.push(range[j]);
- }
- startCount.push(0xFF, 0xFF);
- endCount.push(0xFF, 0xFF);
- idDeltas.push(0x00, 0x01);
- idRangeOffsets.push(0x00, 0x00);
-
- return cmap.concat(endCount, [0x00, 0x00], startCount,
- idDeltas, idRangeOffsets, glyphsIdsArray);
- },
-
- cover: function font_cover(aFont, aProperties) {
- var otf = new Uint8Array(kMaxFontFileSize);
-
- // Required Tables
- var CFF = aFont.data, // PostScript Font Program
- OS2 = [], // OS/2 and Windows Specific metrics
- cmap = [], // Character to glyphs mapping
- head = [], // Font eader
- hhea = [], // Horizontal header
- hmtx = [], // Horizontal metrics
- maxp = [], // Maximum profile
- name = [], // Naming tables
- post = []; // PostScript informations
- var tables = [CFF, OS2, cmap, head, hhea, hmtx, maxp, name, post];
-
- // 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 draw the actual data of a particular
- // table
- var offsets = {
- currentOffset: 0,
- virtualOffset: tables.length * (4 * 4)
- };
-
- // For files with only one font the offset table is the first thing of the
- // file
- this._createOpenTypeHeader(otf, offsets, tables.length);
-
- // XXX It is probable that in a future we want to get rid of this glue
- // between the CFF and the OTF format in order to be able to embed TrueType
- // data.
- this._createTableEntry(otf, offsets, "CFF ", CFF);
-
- /** OS/2 */
- OS2 = [
- 0x00, 0x03, // version
- 0x02, 0x24, // xAvgCharWidth
- 0x01, 0xF4, // usWeightClass
- 0x00, 0x05, // usWidthClass
- 0x00, 0x00, // fstype
- 0x02, 0x8A, // ySubscriptXSize
- 0x02, 0xBB, // ySubscriptYSize
- 0x00, 0x00, // ySubscriptXOffset
- 0x00, 0x8C, // ySubscriptYOffset
- 0x02, 0x8A, // ySuperScriptXSize
- 0x02, 0xBB, // ySuperScriptYSize
- 0x00, 0x00, // ySuperScriptXOffset
- 0x01, 0xDF, // ySuperScriptYOffset
- 0x00, 0x31, // yStrikeOutSize
- 0x01, 0x02, // yStrikeOutPosition
- 0x00, 0x00, // sFamilyClass
- 0x02, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Panose
- 0xFF, 0xFF, 0xFF, 0xFF, // ulUnicodeRange1 (Bits 0-31)
- 0xFF, 0xFF, 0xFF, 0xFF, // ulUnicodeRange1 (Bits 32-63)
- 0xFF, 0xFF, 0xFF, 0xFF, // ulUnicodeRange1 (Bits 64-95)
- 0xFF, 0xFF, 0xFF, 0xFF, // ulUnicodeRange1 (Bits 96-127)
- 0x2A, 0x32, 0x31, 0x2A, // achVendID
- 0x00, 0x20, // fsSelection
- 0x00, 0x2D, // usFirstCharIndex
- 0x00, 0x7A, // usLastCharIndex
- 0x00, 0x03, // sTypoAscender
- 0x00, 0x20, // sTypeDescender
- 0x00, 0x38, // sTypoLineGap
- 0x00, 0x5A, // usWinAscent
- 0x02, 0xB4, // usWinDescent
- 0x00, 0xCE, 0x00, 0x00, // ulCodePageRange1 (Bits 0-31)
- 0x00, 0x01, 0x00, 0x00, // ulCodePageRange2 (Bits 32-63)
- 0x00, 0x00, // sxHeight
- 0x00, 0x00, // sCapHeight
- 0x00, 0x01, // usDefaultChar
- 0x00, 0xCD, // usBreakChar
- 0x00, 0x02 // usMaxContext
- ];
- this._createTableEntry(otf, offsets, "OS/2", OS2);
-
- //XXX Getting charstrings here seems wrong since this is another CFF glue
- var charstrings = aFont.getOrderedCharStrings(aProperties.glyphs);
-
- /** CMAP */
- cmap = this._createCMAPTable(charstrings);
- this._createTableEntry(otf, offsets, "cmap", cmap);
-
- /** HEAD */
- head = [
- 0x00, 0x01, 0x00, 0x00, // Version number
- 0x00, 0x00, 0x50, 0x00, // fontRevision
- 0x00, 0x00, 0x00, 0x00, // checksumAdjustement
- 0x5F, 0x0F, 0x3C, 0xF5, // magicNumber
- 0x00, 0x00, // Flags
- 0x03, 0xE8, // unitsPerEM (defaulting to 1000)
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // creation date
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // modifification date
- 0x00, 0x00, // xMin
- 0x00, 0x00, // yMin
- 0x00, 0x00, // xMax
- 0x00, 0x00, // yMax
- 0x00, 0x00, // macStyle
- 0x00, 0x00, // lowestRecPPEM
- 0x00, 0x00, // fontDirectionHint
- 0x00, 0x00, // indexToLocFormat
- 0x00, 0x00 // glyphDataFormat
- ];
- this._createTableEntry(otf, offsets, "head", head);
-
- /** HHEA */
- hhea = [].concat(
- [
- 0x00, 0x01, 0x00, 0x00, // Version number
- 0x00, 0x00, // Typographic Ascent
- 0x00, 0x00, // Typographic Descent
- 0x00, 0x00, // Line Gap
- 0xFF, 0xFF, // advanceWidthMax
- 0x00, 0x00, // minLeftSidebearing
- 0x00, 0x00, // minRightSidebearing
- 0x00, 0x00, // xMaxExtent
- 0x00, 0x00, // caretSlopeRise
- 0x00, 0x00, // caretSlopeRun
- 0x00, 0x00, // caretOffset
- 0x00, 0x00, // -reserved-
- 0x00, 0x00, // -reserved-
- 0x00, 0x00, // -reserved-
- 0x00, 0x00, // -reserved-
- 0x00, 0x00 // metricDataFormat
- ],
- FontsUtils.integerToBytes(charstrings.length, 2) // numberOfHMetrics
- );
- this._createTableEntry(otf, offsets, "hhea", hhea);
-
- /** HMTX */
- hmtx = [0x01, 0xF4, 0x00, 0x00];
- for (var i = 0; i < charstrings.length; i++) {
- var charstring = charstrings[i].charstring;
- var width = FontsUtils.integerToBytes(charstring[1], 2);
- var lsb = FontsUtils.integerToBytes(charstring[0], 2);
- hmtx = hmtx.concat(width, lsb);
- }
- this._createTableEntry(otf, offsets, "hmtx", hmtx);
-
- /** MAXP */
- maxp = [].concat(
- [
- 0x00, 0x00, 0x50, 0x00, // Version number
- ],
- FontsUtils.integerToBytes(charstrings.length + 1, 2) // Num of glyphs (+1 to pass the sanitizer...)
- );
- this._createTableEntry(otf, offsets, "maxp", maxp);
-
- /** NAME */
- name = [
- 0x00, 0x00, // format
- 0x00, 0x00, // Number of names Record
- 0x00, 0x00 // Storage
- ];
- this._createTableEntry(otf, offsets, "name", name);
-
- /** POST */
- // FIXME Get those informations from the FontInfo structure
- post = [
- 0x00, 0x03, 0x00, 0x00, // Version number
- 0x00, 0x00, 0x01, 0x00, // italicAngle
- 0x00, 0x00, // underlinePosition
- 0x00, 0x00, // underlineThickness
- 0x00, 0x00, 0x00, 0x00, // isFixedPitch
- 0x00, 0x00, 0x00, 0x00, // minMemType42
- 0x00, 0x00, 0x00, 0x00, // maxMemType42
- 0x00, 0x00, 0x00, 0x00, // minMemType1
- 0x00, 0x00, 0x00, 0x00 // maxMemType1
- ];
- this._createTableEntry(otf, offsets, "post", post);
-
- // Once all the table entries header are written, dump the data!
- var tables = [CFF, OS2, cmap, head, hhea, hmtx, maxp, name, post];
- for (var i = 0; i < tables.length; i++) {
- var table = tables[i];
- otf.set(table, offsets.currentOffset);
- offsets.currentOffset += table.length;
- }
-
- var fontData = [];
- for (var i = 0; i < offsets.currentOffset; i++)
- fontData.push(otf[i]);
- return fontData;
- }
-};
-
+ return constructor;
+})();
/**
* FontsUtils is a static class dedicated to hold codes that are not related
@@ -748,7 +747,7 @@ var TrueType = function(aFile) {
} else if (requiredTables.lenght) {
error("Table " + requiredTables[0] + " is missing from the TruType font");
} else {
- this.data = aFile;
+ this.data = aFile.getBytes();
}
};
diff --git a/glyphlist.js b/glyphlist.js
index 72a90431f..85ab876f9 100644
--- a/glyphlist.js
+++ b/glyphlist.js
@@ -1505,27 +1505,27 @@ var GlyphsUnicode = {
dalet: 0x05D3,
daletdagesh: 0xFB33,
daletdageshhebrew: 0xFB33,
- dalethatafpatah: "05D3 05B2",
- dalethatafpatahhebrew: "05D3 05B2",
- dalethatafsegol: "05D3 05B1",
- dalethatafsegolhebrew: "05D3 05B1",
+ dalethatafpatah: 0x05D305B2,
+ dalethatafpatahhebrew: 0x05D305B2,
+ dalethatafsegol: 0x05D305B1,
+ dalethatafsegolhebrew: 0x05D305B1,
dalethebrew: 0x05D3,
- dalethiriq: "05D3 05B4",
- dalethiriqhebrew: "05D3 05B4",
- daletholam: "05D3 05B9",
- daletholamhebrew: "05D3 05B9",
- daletpatah: "05D3 05B7",
- daletpatahhebrew: "05D3 05B7",
- daletqamats: "05D3 05B8",
- daletqamatshebrew: "05D3 05B8",
- daletqubuts: "05D3 05BB",
- daletqubutshebrew: "05D3 05BB",
- daletsegol: "05D3 05B6",
- daletsegolhebrew: "05D3 05B6",
- daletsheva: "05D3 05B0",
- daletshevahebrew: "05D3 05B0",
- dalettsere: "05D3 05B5",
- dalettserehebrew: "05D3 05B5",
+ dalethiriq: 0x05D305B4,
+ dalethiriqhebrew: 0x05D305B4,
+ daletholam: 0x05D305B9,
+ daletholamhebrew: 0x05D305B9,
+ daletpatah: 0x05D305B7,
+ daletpatahhebrew: 0x05D305B7,
+ daletqamats: 0x05D305B8,
+ daletqamatshebrew: 0x05D305B8,
+ daletqubuts: 0x05D305BB,
+ daletqubutshebrew: 0x05D305BB,
+ daletsegol: 0x05D305B6,
+ daletsegolhebrew: 0x05D305B6,
+ daletsheva: 0x05D305B0,
+ daletshevahebrew: 0x05D305B0,
+ dalettsere: 0x05D305B5,
+ dalettserehebrew: 0x05D305B5,
dalfinalarabic: 0xFEAA,
dammaarabic: 0x064F,
dammalowarabic: 0x064F,
@@ -1842,10 +1842,10 @@ var GlyphsUnicode = {
finalkafdagesh: 0xFB3A,
finalkafdageshhebrew: 0xFB3A,
finalkafhebrew: 0x05DA,
- finalkafqamats: "05DA 05B8",
- finalkafqamatshebrew: "05DA 05B8",
- finalkafsheva: "05DA 05B0",
- finalkafshevahebrew: "05DA 05B0",
+ finalkafqamats: 0x05DA05B8,
+ finalkafqamatshebrew: 0x05DA05B8,
+ finalkafsheva: 0x05DA05B0,
+ finalkafshevahebrew: 0x05DA05B0,
finalmem: 0x05DD,
finalmemhebrew: 0x05DD,
finalnun: 0x05DF,
@@ -2034,14 +2034,14 @@ var GlyphsUnicode = {
hakatakanahalfwidth: 0xFF8A,
halantgurmukhi: 0x0A4D,
hamzaarabic: 0x0621,
- hamzadammaarabic: "0621 064F",
- hamzadammatanarabic: "0621 064C",
- hamzafathaarabic: "0621 064E",
- hamzafathatanarabic: "0621 064B",
+ hamzadammaarabic: 0x0621064F,
+ hamzadammatanarabic: 0x0621064C,
+ hamzafathaarabic: 0x0621064E,
+ hamzafathatanarabic: 0x0621064B,
hamzalowarabic: 0x0621,
- hamzalowkasraarabic: "0621 0650",
- hamzalowkasratanarabic: "0621 064D",
- hamzasukunarabic: "0621 0652",
+ hamzalowkasraarabic: 0x06210650,
+ hamzalowkasratanarabic: 0x0621064D,
+ hamzasukunarabic: 0x06210652,
hangulfiller: 0x3164,
hardsigncyrillic: 0x044A,
harpoonleftbarbup: 0x21BC,
@@ -2473,10 +2473,10 @@ var GlyphsUnicode = {
lameddagesh: 0xFB3C,
lameddageshhebrew: 0xFB3C,
lamedhebrew: 0x05DC,
- lamedholam: "05DC 05B9",
+ lamedholam: 0x05DC05B9,
lamedholamdagesh: "05DC 05B9 05BC",
lamedholamdageshhebrew: "05DC 05B9 05BC",
- lamedholamhebrew: "05DC 05B9",
+ lamedholamhebrew: 0x05DC05B9,
lamfinalarabic: 0xFEDE,
lamhahinitialarabic: 0xFCCA,
laminitialarabic: 0xFEDF,
@@ -2784,7 +2784,7 @@ var GlyphsUnicode = {
noonfinalarabic: 0xFEE6,
noonghunnaarabic: 0x06BA,
noonghunnafinalarabic: 0xFB9F,
- noonhehinitialarabic: "FEE7 FEEC",
+ noonhehinitialarabic: 0xFEE7FEEC,
nooninitialarabic: 0xFEE7,
noonjeeminitialarabic: 0xFCD2,
noonjeemisolatedarabic: 0xFC4B,
@@ -3156,27 +3156,27 @@ var GlyphsUnicode = {
qof: 0x05E7,
qofdagesh: 0xFB47,
qofdageshhebrew: 0xFB47,
- qofhatafpatah: "05E7 05B2",
- qofhatafpatahhebrew: "05E7 05B2",
- qofhatafsegol: "05E7 05B1",
- qofhatafsegolhebrew: "05E7 05B1",
+ qofhatafpatah: 0x05E705B2,
+ qofhatafpatahhebrew: 0x05E705B2,
+ qofhatafsegol: 0x05E705B1,
+ qofhatafsegolhebrew: 0x05E705B1,
qofhebrew: 0x05E7,
- qofhiriq: "05E7 05B4",
- qofhiriqhebrew: "05E7 05B4",
- qofholam: "05E7 05B9",
- qofholamhebrew: "05E7 05B9",
- qofpatah: "05E7 05B7",
- qofpatahhebrew: "05E7 05B7",
- qofqamats: "05E7 05B8",
- qofqamatshebrew: "05E7 05B8",
- qofqubuts: "05E7 05BB",
- qofqubutshebrew: "05E7 05BB",
- qofsegol: "05E7 05B6",
- qofsegolhebrew: "05E7 05B6",
- qofsheva: "05E7 05B0",
- qofshevahebrew: "05E7 05B0",
- qoftsere: "05E7 05B5",
- qoftserehebrew: "05E7 05B5",
+ qofhiriq: 0x05E705B4,
+ qofhiriqhebrew: 0x05E705B4,
+ qofholam: 0x05E705B9,
+ qofholamhebrew: 0x05E705B9,
+ qofpatah: 0x05E705B7,
+ qofpatahhebrew: 0x05E705B7,
+ qofqamats: 0x05E705B8,
+ qofqamatshebrew: 0x05E705B8,
+ qofqubuts: 0x05E705BB,
+ qofqubutshebrew: 0x05E705BB,
+ qofsegol: 0x05E705B6,
+ qofsegolhebrew: 0x05E705B6,
+ qofsheva: 0x05E705B0,
+ qofshevahebrew: 0x05E705B0,
+ qoftsere: 0x05E705B5,
+ qoftserehebrew: 0x05E705B5,
qparen: 0x24AC,
quarternote: 0x2669,
qubuts: 0x05BB,
@@ -3255,27 +3255,27 @@ var GlyphsUnicode = {
rekatakanahalfwidth: 0xFF9A,
resh: 0x05E8,
reshdageshhebrew: 0xFB48,
- reshhatafpatah: "05E8 05B2",
- reshhatafpatahhebrew: "05E8 05B2",
- reshhatafsegol: "05E8 05B1",
- reshhatafsegolhebrew: "05E8 05B1",
+ reshhatafpatah: 0x05E805B2,
+ reshhatafpatahhebrew: 0x05E805B2,
+ reshhatafsegol: 0x05E805B1,
+ reshhatafsegolhebrew: 0x05E805B1,
reshhebrew: 0x05E8,
- reshhiriq: "05E8 05B4",
- reshhiriqhebrew: "05E8 05B4",
- reshholam: "05E8 05B9",
- reshholamhebrew: "05E8 05B9",
- reshpatah: "05E8 05B7",
- reshpatahhebrew: "05E8 05B7",
- reshqamats: "05E8 05B8",
- reshqamatshebrew: "05E8 05B8",
- reshqubuts: "05E8 05BB",
- reshqubutshebrew: "05E8 05BB",
- reshsegol: "05E8 05B6",
- reshsegolhebrew: "05E8 05B6",
- reshsheva: "05E8 05B0",
- reshshevahebrew: "05E8 05B0",
- reshtsere: "05E8 05B5",
- reshtserehebrew: "05E8 05B5",
+ reshhiriq: 0x05E805B4,
+ reshhiriqhebrew: 0x05E805B4,
+ reshholam: 0x05E805B9,
+ reshholamhebrew: 0x05E805B9,
+ reshpatah: 0x05E805B7,
+ reshpatahhebrew: 0x05E805B7,
+ reshqamats: 0x05E805B8,
+ reshqamatshebrew: 0x05E805B8,
+ reshqubuts: 0x05E805BB,
+ reshqubutshebrew: 0x05E805BB,
+ reshsegol: 0x05E805B6,
+ reshsegolhebrew: 0x05E805B6,
+ reshsheva: 0x05E805B0,
+ reshshevahebrew: 0x05E805B0,
+ reshtsere: 0x05E805B5,
+ reshtserehebrew: 0x05E805B5,
reversedtilde: 0x223D,
reviahebrew: 0x0597,
reviamugrashhebrew: 0x0597,
@@ -3474,7 +3474,7 @@ var GlyphsUnicode = {
shaddadammaarabic: 0xFC61,
shaddadammatanarabic: 0xFC5E,
shaddafathaarabic: 0xFC60,
- shaddafathatanarabic: "0651 064B",
+ shaddafathatanarabic: 0x0651064B,
shaddakasraarabic: 0xFC62,
shaddakasratanarabic: 0xFC5F,
shade: 0x2592,
@@ -3671,7 +3671,7 @@ var GlyphsUnicode = {
tchehfinalarabic: 0xFB7B,
tchehinitialarabic: 0xFB7C,
tchehmedialarabic: 0xFB7D,
- tchehmeeminitialarabic: "FB7C FEE4",
+ tchehmeeminitialarabic: 0xFB7CFEE4,
tcircle: 0x24E3,
tcircumflexbelow: 0x1E71,
tcommaaccent: 0x0163,
diff --git a/images/combobox.png b/images/combobox.png
new file mode 100644
index 000000000..f1527d6a2
Binary files /dev/null and b/images/combobox.png differ
diff --git a/images/source/ComboBox.psd.zip b/images/source/ComboBox.psd.zip
new file mode 100644
index 000000000..232604c75
Binary files /dev/null and b/images/source/ComboBox.psd.zip differ
diff --git a/multi-page-viewer.css b/multi-page-viewer.css
index 53a28f129..c96567465 100644
--- a/multi-page-viewer.css
+++ b/multi-page-viewer.css
@@ -113,6 +113,67 @@ span {
background: url('images/buttons.png') no-repeat -28px 0px;
}
+#scaleComboBoxInput {
+ background: url('images/combobox.png') no-repeat 0px -23px;
+ display: inline-block;
+ float: left;
+ margin: 0px;
+ width: 35px;
+ height: 23px;
+}
+
+#scaleComboBoxInput input {
+ background: none;
+ border: 0px;
+ margin: 3px 2px 0px;
+ width: 31px;
+}
+
+#scaleComboBoxButton {
+ background: url('images/combobox.png') no-repeat -41px -23px;
+ cursor: pointer;
+ display: inline-block;
+ float: left;
+ margin: 0px;
+ width: 21px;
+ height: 23px;
+}
+
+#scaleComboBoxButton.down {
+ background: url('images/combobox.png') no-repeat -41px -46px;
+}
+
+#scaleComboBoxButton.disabled {
+ background: url('images/combobox.png') no-repeat -41px 0px;
+}
+
+#scaleComboBoxList {
+ background-color: #fff;
+ border: 1px solid #666;
+ clear: both;
+ position: relative;
+ display: none;
+ top: -20px;
+ width: 48px;
+}
+
+#scaleComboBoxList > ul {
+ list-style: none;
+ padding: 0px;
+ margin: 0px;
+}
+
+#scaleComboBoxList > ul > li {
+ display: inline-block;
+ cursor: pointer;
+ width: 100%;
+}
+
+#scaleComboBoxList > ul > li:hover {
+ background-color: #09f;
+ color: #fff;
+}
+
#pageNumber, #scale {
text-align: right;
}
diff --git a/multi-page-viewer.html b/multi-page-viewer.html
index aec84a42f..692cfb1c4 100644
--- a/multi-page-viewer.html
+++ b/multi-page-viewer.html
@@ -21,9 +21,18 @@
Page Number
-
- %
+
Zoom
+
+
+