Merge branch 'master' of github.com:andreasgal/pdf.js into streamrewrite2

Conflicts:
	pdf.js
This commit is contained in:
sbarman 2011-06-20 14:16:09 -07:00
commit 91601bf9be
8 changed files with 702 additions and 559 deletions

875
fonts.js
View File

@ -8,11 +8,6 @@
*/ */
var kMaxFontFileSize = 40000; 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 * Maximum time to wait for a font to be loaded by @font-face
*/ */
@ -62,6 +57,10 @@ var Fonts = {
var uc = encoding[ch]; var uc = encoding[ch];
if (uc instanceof Name) // we didn't convert the glyph yet if (uc instanceof Name) // we didn't convert the glyph yet
uc = encoding[ch] = GlyphsUnicode[uc.name]; uc = encoding[ch] = GlyphsUnicode[uc.name];
if (uc > 0xffff) { // handle surrogate pairs
ret += String.fromCharCode(uc & 0xffff);
uc >>= 16;
}
ret += String.fromCharCode(uc); ret += String.fromCharCode(uc);
} }
@ -80,23 +79,24 @@ var Fonts = {
* var type1Font = new Font("MyFontName", binaryFile, propertiesObject); * var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
* type1Font.bind(); * type1Font.bind();
*/ */
var Font = function(aName, aFile, aProperties) { var Font = (function () {
this.name = aName; var constructor = function(aName, aFile, aProperties) {
this.name = aName;
// If the font has already been decoded simply return // If the font has already been decoded simply return it
if (Fonts[aName]) { if (Fonts[aName]) {
this.font = Fonts[aName].data; this.font = Fonts[aName].data;
return; return;
} }
fontCount++; fontCount++;
switch (aProperties.type) { switch (aProperties.type) {
case "Type1": case "Type1":
var cff = new CFF(aName, aFile, aProperties); var cff = new CFF(aName, aFile, aProperties);
this.mimetype = "font/otf"; this.mimetype = "font/otf";
// Wrap the CFF data inside an OTF font file // Wrap the CFF data inside an OTF font file
this.font = this.cover(cff, aProperties); this.font = this.convert(cff, aProperties);
break; break;
case "TrueType": case "TrueType":
@ -120,449 +120,448 @@ var Font = function(aName, aFile, aProperties) {
default: default:
warn("Font " + aProperties.type + " is not supported"); warn("Font " + aProperties.type + " is not supported");
break; 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"); Fonts[aName] = {
var style = "border: 1px solid black; position:absolute; top: " + data: this.font,
(debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px"; properties: aProperties,
canvas.setAttribute("style", style); loading: true,
canvas.setAttribute("width", 340); cache: Object.create(null)
canvas.setAttribute("heigth", 100); }
document.body.appendChild(canvas);
// Retrieve font charset // Attach the font to the document
var charset = Fonts[fontName].charset || []; this.bind();
// 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"); * A bunch of the OpenType code is duplicate between this class and the
var testString = " "; * 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 bind: function font_bind() {
// see what's happening var data = this.font;
if (debug) {
for (var i = 0; i < charset.length; i++) { // Get the base64 encoding of the binary font data
var unicode = GlyphsUnicode[charset[i]]; var str = "";
if (!unicode) var length = data.length;
error("Unicode for " + charset[i] + " is has not been found in the glyphs list"); for (var i = 0; i < length; ++i)
testString += String.fromCharCode(unicode); 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) var canvas = document.createElement("canvas");
ctx.fillText(testString, 20, 20); 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(); // Retrieve font charset
var interval = window.setInterval(function canvasInterval(self) { 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"; ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
var textWidth = ctx.measureText(testString).width;
// 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;
}
if (debug) if (debug)
ctx.fillText(testString, 20, 50); ctx.fillText(testString, 20, 20);
}, 50, this);
/** 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 // For some reasons the font has not loaded, so mark it loaded for the
var url = "url(data:" + this.mimetype + ";base64," + dataBase64 + ");"; // page to proceed but cry
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}"; if ((Date.now() - start) >= kMaxWaitForFontFace) {
var styleSheet = document.styleSheets[0]; window.clearInterval(interval);
styleSheet.insertRule(rule, styleSheet.length); 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) { if (debug)
// sfnt version (4 bytes) ctx.fillText(testString, 20, 50);
var version = [0x4F, 0x54, 0x54, 0X4F]; }, 50, this);
// numTables (2 bytes) /** Hack end */
var numTables = aNumTables;
// searchRange (2 bytes) // Add the @font-face rule to the document
var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); var url = "url(data:" + this.mimetype + ";base64," + dataBase64 + ");";
var searchRange = tablesMaxPower2 * 16; var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
var styleSheet = document.styleSheets[0];
styleSheet.insertRule(rule, styleSheet.length);
},
// entrySelector (2 bytes) convert: function font_convert(aFont, aProperties) {
var entrySelector = Math.log(tablesMaxPower2) / Math.log(2); var otf = new Uint8Array(kMaxFontFileSize);
// rangeShift (2 bytes) function s2a(s) {
var rangeShift = numTables * 16 - searchRange; var a = [];
for (var i = 0; i < s.length; ++i)
var header = [].concat(version, a[i] = s.charCodeAt(i);
FontsUtils.integerToBytes(numTables, 2), return a;
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 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 return constructor;
// 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;
}
};
/** /**
* FontsUtils is a static class dedicated to hold codes that are not related * 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) { } else if (requiredTables.lenght) {
error("Table " + requiredTables[0] + " is missing from the TruType font"); error("Table " + requiredTables[0] + " is missing from the TruType font");
} else { } else {
this.data = aFile; this.data = aFile.getBytes();
} }
}; };

View File

@ -1505,27 +1505,27 @@ var GlyphsUnicode = {
dalet: 0x05D3, dalet: 0x05D3,
daletdagesh: 0xFB33, daletdagesh: 0xFB33,
daletdageshhebrew: 0xFB33, daletdageshhebrew: 0xFB33,
dalethatafpatah: "05D3 05B2", dalethatafpatah: 0x05D305B2,
dalethatafpatahhebrew: "05D3 05B2", dalethatafpatahhebrew: 0x05D305B2,
dalethatafsegol: "05D3 05B1", dalethatafsegol: 0x05D305B1,
dalethatafsegolhebrew: "05D3 05B1", dalethatafsegolhebrew: 0x05D305B1,
dalethebrew: 0x05D3, dalethebrew: 0x05D3,
dalethiriq: "05D3 05B4", dalethiriq: 0x05D305B4,
dalethiriqhebrew: "05D3 05B4", dalethiriqhebrew: 0x05D305B4,
daletholam: "05D3 05B9", daletholam: 0x05D305B9,
daletholamhebrew: "05D3 05B9", daletholamhebrew: 0x05D305B9,
daletpatah: "05D3 05B7", daletpatah: 0x05D305B7,
daletpatahhebrew: "05D3 05B7", daletpatahhebrew: 0x05D305B7,
daletqamats: "05D3 05B8", daletqamats: 0x05D305B8,
daletqamatshebrew: "05D3 05B8", daletqamatshebrew: 0x05D305B8,
daletqubuts: "05D3 05BB", daletqubuts: 0x05D305BB,
daletqubutshebrew: "05D3 05BB", daletqubutshebrew: 0x05D305BB,
daletsegol: "05D3 05B6", daletsegol: 0x05D305B6,
daletsegolhebrew: "05D3 05B6", daletsegolhebrew: 0x05D305B6,
daletsheva: "05D3 05B0", daletsheva: 0x05D305B0,
daletshevahebrew: "05D3 05B0", daletshevahebrew: 0x05D305B0,
dalettsere: "05D3 05B5", dalettsere: 0x05D305B5,
dalettserehebrew: "05D3 05B5", dalettserehebrew: 0x05D305B5,
dalfinalarabic: 0xFEAA, dalfinalarabic: 0xFEAA,
dammaarabic: 0x064F, dammaarabic: 0x064F,
dammalowarabic: 0x064F, dammalowarabic: 0x064F,
@ -1842,10 +1842,10 @@ var GlyphsUnicode = {
finalkafdagesh: 0xFB3A, finalkafdagesh: 0xFB3A,
finalkafdageshhebrew: 0xFB3A, finalkafdageshhebrew: 0xFB3A,
finalkafhebrew: 0x05DA, finalkafhebrew: 0x05DA,
finalkafqamats: "05DA 05B8", finalkafqamats: 0x05DA05B8,
finalkafqamatshebrew: "05DA 05B8", finalkafqamatshebrew: 0x05DA05B8,
finalkafsheva: "05DA 05B0", finalkafsheva: 0x05DA05B0,
finalkafshevahebrew: "05DA 05B0", finalkafshevahebrew: 0x05DA05B0,
finalmem: 0x05DD, finalmem: 0x05DD,
finalmemhebrew: 0x05DD, finalmemhebrew: 0x05DD,
finalnun: 0x05DF, finalnun: 0x05DF,
@ -2034,14 +2034,14 @@ var GlyphsUnicode = {
hakatakanahalfwidth: 0xFF8A, hakatakanahalfwidth: 0xFF8A,
halantgurmukhi: 0x0A4D, halantgurmukhi: 0x0A4D,
hamzaarabic: 0x0621, hamzaarabic: 0x0621,
hamzadammaarabic: "0621 064F", hamzadammaarabic: 0x0621064F,
hamzadammatanarabic: "0621 064C", hamzadammatanarabic: 0x0621064C,
hamzafathaarabic: "0621 064E", hamzafathaarabic: 0x0621064E,
hamzafathatanarabic: "0621 064B", hamzafathatanarabic: 0x0621064B,
hamzalowarabic: 0x0621, hamzalowarabic: 0x0621,
hamzalowkasraarabic: "0621 0650", hamzalowkasraarabic: 0x06210650,
hamzalowkasratanarabic: "0621 064D", hamzalowkasratanarabic: 0x0621064D,
hamzasukunarabic: "0621 0652", hamzasukunarabic: 0x06210652,
hangulfiller: 0x3164, hangulfiller: 0x3164,
hardsigncyrillic: 0x044A, hardsigncyrillic: 0x044A,
harpoonleftbarbup: 0x21BC, harpoonleftbarbup: 0x21BC,
@ -2473,10 +2473,10 @@ var GlyphsUnicode = {
lameddagesh: 0xFB3C, lameddagesh: 0xFB3C,
lameddageshhebrew: 0xFB3C, lameddageshhebrew: 0xFB3C,
lamedhebrew: 0x05DC, lamedhebrew: 0x05DC,
lamedholam: "05DC 05B9", lamedholam: 0x05DC05B9,
lamedholamdagesh: "05DC 05B9 05BC", lamedholamdagesh: "05DC 05B9 05BC",
lamedholamdageshhebrew: "05DC 05B9 05BC", lamedholamdageshhebrew: "05DC 05B9 05BC",
lamedholamhebrew: "05DC 05B9", lamedholamhebrew: 0x05DC05B9,
lamfinalarabic: 0xFEDE, lamfinalarabic: 0xFEDE,
lamhahinitialarabic: 0xFCCA, lamhahinitialarabic: 0xFCCA,
laminitialarabic: 0xFEDF, laminitialarabic: 0xFEDF,
@ -2784,7 +2784,7 @@ var GlyphsUnicode = {
noonfinalarabic: 0xFEE6, noonfinalarabic: 0xFEE6,
noonghunnaarabic: 0x06BA, noonghunnaarabic: 0x06BA,
noonghunnafinalarabic: 0xFB9F, noonghunnafinalarabic: 0xFB9F,
noonhehinitialarabic: "FEE7 FEEC", noonhehinitialarabic: 0xFEE7FEEC,
nooninitialarabic: 0xFEE7, nooninitialarabic: 0xFEE7,
noonjeeminitialarabic: 0xFCD2, noonjeeminitialarabic: 0xFCD2,
noonjeemisolatedarabic: 0xFC4B, noonjeemisolatedarabic: 0xFC4B,
@ -3156,27 +3156,27 @@ var GlyphsUnicode = {
qof: 0x05E7, qof: 0x05E7,
qofdagesh: 0xFB47, qofdagesh: 0xFB47,
qofdageshhebrew: 0xFB47, qofdageshhebrew: 0xFB47,
qofhatafpatah: "05E7 05B2", qofhatafpatah: 0x05E705B2,
qofhatafpatahhebrew: "05E7 05B2", qofhatafpatahhebrew: 0x05E705B2,
qofhatafsegol: "05E7 05B1", qofhatafsegol: 0x05E705B1,
qofhatafsegolhebrew: "05E7 05B1", qofhatafsegolhebrew: 0x05E705B1,
qofhebrew: 0x05E7, qofhebrew: 0x05E7,
qofhiriq: "05E7 05B4", qofhiriq: 0x05E705B4,
qofhiriqhebrew: "05E7 05B4", qofhiriqhebrew: 0x05E705B4,
qofholam: "05E7 05B9", qofholam: 0x05E705B9,
qofholamhebrew: "05E7 05B9", qofholamhebrew: 0x05E705B9,
qofpatah: "05E7 05B7", qofpatah: 0x05E705B7,
qofpatahhebrew: "05E7 05B7", qofpatahhebrew: 0x05E705B7,
qofqamats: "05E7 05B8", qofqamats: 0x05E705B8,
qofqamatshebrew: "05E7 05B8", qofqamatshebrew: 0x05E705B8,
qofqubuts: "05E7 05BB", qofqubuts: 0x05E705BB,
qofqubutshebrew: "05E7 05BB", qofqubutshebrew: 0x05E705BB,
qofsegol: "05E7 05B6", qofsegol: 0x05E705B6,
qofsegolhebrew: "05E7 05B6", qofsegolhebrew: 0x05E705B6,
qofsheva: "05E7 05B0", qofsheva: 0x05E705B0,
qofshevahebrew: "05E7 05B0", qofshevahebrew: 0x05E705B0,
qoftsere: "05E7 05B5", qoftsere: 0x05E705B5,
qoftserehebrew: "05E7 05B5", qoftserehebrew: 0x05E705B5,
qparen: 0x24AC, qparen: 0x24AC,
quarternote: 0x2669, quarternote: 0x2669,
qubuts: 0x05BB, qubuts: 0x05BB,
@ -3255,27 +3255,27 @@ var GlyphsUnicode = {
rekatakanahalfwidth: 0xFF9A, rekatakanahalfwidth: 0xFF9A,
resh: 0x05E8, resh: 0x05E8,
reshdageshhebrew: 0xFB48, reshdageshhebrew: 0xFB48,
reshhatafpatah: "05E8 05B2", reshhatafpatah: 0x05E805B2,
reshhatafpatahhebrew: "05E8 05B2", reshhatafpatahhebrew: 0x05E805B2,
reshhatafsegol: "05E8 05B1", reshhatafsegol: 0x05E805B1,
reshhatafsegolhebrew: "05E8 05B1", reshhatafsegolhebrew: 0x05E805B1,
reshhebrew: 0x05E8, reshhebrew: 0x05E8,
reshhiriq: "05E8 05B4", reshhiriq: 0x05E805B4,
reshhiriqhebrew: "05E8 05B4", reshhiriqhebrew: 0x05E805B4,
reshholam: "05E8 05B9", reshholam: 0x05E805B9,
reshholamhebrew: "05E8 05B9", reshholamhebrew: 0x05E805B9,
reshpatah: "05E8 05B7", reshpatah: 0x05E805B7,
reshpatahhebrew: "05E8 05B7", reshpatahhebrew: 0x05E805B7,
reshqamats: "05E8 05B8", reshqamats: 0x05E805B8,
reshqamatshebrew: "05E8 05B8", reshqamatshebrew: 0x05E805B8,
reshqubuts: "05E8 05BB", reshqubuts: 0x05E805BB,
reshqubutshebrew: "05E8 05BB", reshqubutshebrew: 0x05E805BB,
reshsegol: "05E8 05B6", reshsegol: 0x05E805B6,
reshsegolhebrew: "05E8 05B6", reshsegolhebrew: 0x05E805B6,
reshsheva: "05E8 05B0", reshsheva: 0x05E805B0,
reshshevahebrew: "05E8 05B0", reshshevahebrew: 0x05E805B0,
reshtsere: "05E8 05B5", reshtsere: 0x05E805B5,
reshtserehebrew: "05E8 05B5", reshtserehebrew: 0x05E805B5,
reversedtilde: 0x223D, reversedtilde: 0x223D,
reviahebrew: 0x0597, reviahebrew: 0x0597,
reviamugrashhebrew: 0x0597, reviamugrashhebrew: 0x0597,
@ -3474,7 +3474,7 @@ var GlyphsUnicode = {
shaddadammaarabic: 0xFC61, shaddadammaarabic: 0xFC61,
shaddadammatanarabic: 0xFC5E, shaddadammatanarabic: 0xFC5E,
shaddafathaarabic: 0xFC60, shaddafathaarabic: 0xFC60,
shaddafathatanarabic: "0651 064B", shaddafathatanarabic: 0x0651064B,
shaddakasraarabic: 0xFC62, shaddakasraarabic: 0xFC62,
shaddakasratanarabic: 0xFC5F, shaddakasratanarabic: 0xFC5F,
shade: 0x2592, shade: 0x2592,
@ -3671,7 +3671,7 @@ var GlyphsUnicode = {
tchehfinalarabic: 0xFB7B, tchehfinalarabic: 0xFB7B,
tchehinitialarabic: 0xFB7C, tchehinitialarabic: 0xFB7C,
tchehmedialarabic: 0xFB7D, tchehmedialarabic: 0xFB7D,
tchehmeeminitialarabic: "FB7C FEE4", tchehmeeminitialarabic: 0xFB7CFEE4,
tcircle: 0x24E3, tcircle: 0x24E3,
tcircumflexbelow: 0x1E71, tcircumflexbelow: 0x1E71,
tcommaaccent: 0x0163, tcommaaccent: 0x0163,

BIN
images/combobox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

View File

@ -113,6 +113,67 @@ span {
background: url('images/buttons.png') no-repeat -28px 0px; 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 { #pageNumber, #scale {
text-align: right; text-align: right;
} }

View File

@ -21,9 +21,18 @@
<span class="label">Page Number</span> <span class="label">Page Number</span>
</span> </span>
<span class="control"> <span class="control">
<input type="text" id="scale" value="100" size="2"/> <span id="scaleComboBoxInput"><input type="text" id="scale" value="100%" size="2"/></span><span id="scaleComboBoxButton"></span>
<span>%</span>
<span class="label">Zoom</span> <span class="label">Zoom</span>
<div id="scaleComboBoxList">
<ul>
<li>50%</li>
<li>75%</li>
<li>100%</li>
<li>125%</li>
<li>150%</li>
<li>200%</li>
</ul>
</div>
</span> </span>
</div> </div>
<div id="viewer"></div> <div id="viewer"></div>

View File

@ -8,9 +8,10 @@ var PDFViewer = {
element: null, element: null,
pageNumberInput: null,
previousPageButton: null, previousPageButton: null,
nextPageButton: null, nextPageButton: null,
pageNumberInput: null,
scaleInput: null,
willJumpToPage: false, willJumpToPage: false,
@ -158,6 +159,8 @@ var PDFViewer = {
PDFViewer.drawPage(1); PDFViewer.drawPage(1);
} }
} }
PDFViewer.scaleInput.value = Math.floor(PDFViewer.scale * 100) + '%';
}, },
goToPage: function(num) { goToPage: function(num) {
@ -317,13 +320,40 @@ window.onload = function() {
this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : ''; this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
}; };
var scaleInput = document.getElementById('scale'); PDFViewer.scaleInput = document.getElementById('scale');
scaleInput.onchange = function(evt) { PDFViewer.scaleInput.buttonElement = document.getElementById('scaleComboBoxButton');
PDFViewer.changeScale(this.value); PDFViewer.scaleInput.buttonElement.listElement = document.getElementById('scaleComboBoxList');
PDFViewer.scaleInput.onchange = function(evt) {
PDFViewer.changeScale(parseInt(this.value));
}; };
PDFViewer.scaleInput.buttonElement.onclick = function(evt) {
this.listElement.style.display = (this.listElement.style.display === 'block') ? 'none' : 'block';
};
PDFViewer.scaleInput.buttonElement.onmousedown = function(evt) {
if (this.className.indexOf('disabled') === -1) {
this.className = 'down';
}
};
PDFViewer.scaleInput.buttonElement.onmouseup = function(evt) {
this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
};
PDFViewer.scaleInput.buttonElement.onmouseout = function(evt) {
this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
};
var listItems = PDFViewer.scaleInput.buttonElement.listElement.getElementsByTagName('LI');
for (var i = 0; i < listItems.length; i++) {
var listItem = listItems[i];
listItem.onclick = function(evt) {
PDFViewer.changeScale(parseInt(this.innerHTML));
PDFViewer.scaleInput.buttonElement.listElement.style.display = 'none';
};
}
PDFViewer.pageNumber = parseInt(PDFViewer.queryParams.page) || PDFViewer.pageNumber; PDFViewer.pageNumber = parseInt(PDFViewer.queryParams.page) || PDFViewer.pageNumber;
PDFViewer.scale = parseInt(scaleInput.value) / 100 || 1.0; PDFViewer.scale = parseInt(PDFViewer.scaleInput.value) / 100 || 1.0;
PDFViewer.open(PDFViewer.queryParams.file || PDFViewer.url); PDFViewer.open(PDFViewer.queryParams.file || PDFViewer.url);
window.onscroll = function(evt) { window.onscroll = function(evt) {

120
pdf.js
View File

@ -50,7 +50,7 @@ function shadow(obj, prop, value) {
var Stream = (function() { var Stream = (function() {
function constructor(arrayBuffer, start, length, dict) { function constructor(arrayBuffer, start, length, dict) {
this.bytes = new Uint8Array(arrayBuffer); this.bytes = Uint8Array(arrayBuffer);
this.start = start || 0; this.start = start || 0;
this.pos = this.start; this.pos = this.start;
this.end = (start + length) || this.bytes.byteLength; this.end = (start + length) || this.bytes.byteLength;
@ -115,7 +115,7 @@ var Stream = (function() {
var StringStream = (function() { var StringStream = (function() {
function constructor(str) { function constructor(str) {
var length = str.length; var length = str.length;
var bytes = new Uint8Array(length); var bytes = Uint8Array(length);
for (var n = 0; n < length; ++n) for (var n = 0; n < length; ++n)
bytes[n] = str.charCodeAt(n); bytes[n] = str.charCodeAt(n);
Stream.call(this, bytes); Stream.call(this, bytes);
@ -127,11 +127,11 @@ var StringStream = (function() {
})(); })();
var FlateStream = (function() { var FlateStream = (function() {
const codeLenCodeMap = new Uint32Array([ const codeLenCodeMap = Uint32Array([
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
]); ]);
const lengthDecode = new Uint32Array([ const lengthDecode = Uint32Array([
0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009,
0x0000a, 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x0000a, 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017,
0x2001b, 0x2001f, 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x2001b, 0x2001f, 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043,
@ -139,7 +139,7 @@ var FlateStream = (function() {
0x00102, 0x00102, 0x00102 0x00102, 0x00102, 0x00102
]); ]);
const distDecode = new Uint32Array([ const distDecode = Uint32Array([
0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009,
0x2000d, 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x2000d, 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061,
0x60081, 0x600c1, 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x60081, 0x600c1, 0x70101, 0x70181, 0x80201, 0x80301, 0x90401,
@ -147,7 +147,7 @@ var FlateStream = (function() {
0xd4001, 0xd6001 0xd4001, 0xd6001
]); ]);
const fixedLitCodeTab = [new Uint32Array([ const fixedLitCodeTab = [Uint32Array([
0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030,
0x900c0, 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x900c0, 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080,
0x80040, 0x900e0, 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80040, 0x900e0, 0x70104, 0x80058, 0x80018, 0x90090, 0x70114,
@ -224,7 +224,7 @@ var FlateStream = (function() {
0x900ff 0x900ff
]), 9]; ]), 9];
const fixedDistCodeTab = [new Uint32Array([ const fixedDistCodeTab = [Uint32Array([
0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c,
0x5001c, 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5001c, 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016,
0x5000e, 0x00000, 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x5000e, 0x00000, 0x50001, 0x50011, 0x50009, 0x50019, 0x50005,
@ -311,7 +311,7 @@ var FlateStream = (function() {
var size = 512; var size = 512;
while (size < requested) while (size < requested)
size <<= 1; size <<= 1;
var buffer2 = new Uint8Array(size); var buffer2 = Uint8Array(size);
for (var i = 0; i < current; ++i) for (var i = 0; i < current; ++i)
buffer2[i] = buffer[i]; buffer2[i] = buffer[i];
return this.buffer = buffer2; return this.buffer = buffer2;
@ -373,7 +373,7 @@ var FlateStream = (function() {
// build the table // build the table
var size = 1 << maxLen; var size = 1 << maxLen;
var codes = new Uint32Array(size); var codes = Uint32Array(size);
for (var len = 1, code = 0, skip = 2; for (var len = 1, code = 0, skip = 2;
len <= maxLen; len <= maxLen;
++len, code <<= 1, skip <<= 1) { ++len, code <<= 1, skip <<= 1) {
@ -608,7 +608,7 @@ var PredictorStream = (function() {
var DecryptStream = (function() { var DecryptStream = (function() {
function constructor(str, fileKey, encAlgorithm, keyLength) { function constructor(str, fileKey, encAlgorithm, keyLength) {
// TODO TODO("decrypt stream is not implemented");
} }
constructor.prototype = Stream.prototype; constructor.prototype = Stream.prototype;
@ -1394,31 +1394,25 @@ var XRef = (function() {
var length = streamParameters.get("Length"); var length = streamParameters.get("Length");
var byteWidths = streamParameters.get("W"); var byteWidths = streamParameters.get("W");
var range = streamParameters.get("Index"); var range = streamParameters.get("Index");
if (!range) { if (!range)
range = [0, streamParameters.get("Size")]; range = [0, streamParameters.get("Size")];
}
var i, j; var i, j;
while (range.length > 0) { while (range.length > 0) {
var first = range[0], n = range[1]; var first = range[0], n = range[1];
if (!IsInt(first) || !IsInt(n)) { if (!IsInt(first) || !IsInt(n))
error("Invalid XRef range fields"); error("Invalid XRef range fields");
}
var typeFieldWidth = byteWidths[0], offsetFieldWidth = byteWidths[1], generationFieldWidth = byteWidths[2]; var typeFieldWidth = byteWidths[0], offsetFieldWidth = byteWidths[1], generationFieldWidth = byteWidths[2];
if (!IsInt(typeFieldWidth) || !IsInt(offsetFieldWidth) || !IsInt(generationFieldWidth)) { if (!IsInt(typeFieldWidth) || !IsInt(offsetFieldWidth) || !IsInt(generationFieldWidth))
error("Invalid XRef entry fields length"); error("Invalid XRef entry fields length");
}
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
var type = 0, offset = 0, generation = 0; var type = 0, offset = 0, generation = 0;
for (j = 0; j < typeFieldWidth; ++j) { for (j = 0; j < typeFieldWidth; ++j)
type = (type << 8) | stream.getByte(); type = (type << 8) | stream.getByte();
} for (j = 0; j < offsetFieldWidth; ++j)
for (j = 0; j < offsetFieldWidth; ++j) {
offset = (offset << 8) | stream.getByte(); offset = (offset << 8) | stream.getByte();
} for (j = 0; j < generationFieldWidth; ++j)
for (j = 0; j < generationFieldWidth; ++j) {
generation = (generation << 8) | stream.getByte(); generation = (generation << 8) | stream.getByte();
} var entry = new Ref(offset, generation);
var entry = { offset: offset, gen: generation };
if (typeFieldWidth > 0) { if (typeFieldWidth > 0) {
switch (type) { switch (type) {
case 0: case 0:
@ -1434,16 +1428,14 @@ var XRef = (function() {
break; break;
} }
} }
if (!this.entries[first + i]) { if (!this.entries[first + i])
this.entries[first + i] = entry; this.entries[first + i] = entry;
}
} }
range.splice(0, 2); range.splice(0, 2);
} }
var prev = streamParameters.get("Prev"); var prev = streamParameters.get("Prev");
if (IsInt(prev)) { if (IsInt(prev))
this.readXRef(prev); this.readXRef(prev);
}
return streamParameters; return streamParameters;
}, },
readXRef: function(startXRef) { readXRef: function(startXRef) {
@ -1484,11 +1476,12 @@ var XRef = (function() {
e = this.getEntry(num); e = this.getEntry(num);
var gen = ref.gen; var gen = ref.gen;
var stream, parser;
if (e.uncompressed) { if (e.uncompressed) {
if (e.gen != gen) if (e.gen != gen)
throw("inconsistent generation in XRef"); throw("inconsistent generation in XRef");
var stream = this.stream.makeSubStream(e.offset); stream = this.stream.makeSubStream(e.offset);
var parser = new Parser(new Lexer(stream), true, this); parser = new Parser(new Lexer(stream), true, this);
var obj1 = parser.getObj(); var obj1 = parser.getObj();
var obj2 = parser.getObj(); var obj2 = parser.getObj();
var obj3 = parser.getObj(); var obj3 = parser.getObj();
@ -1512,7 +1505,39 @@ var XRef = (function() {
this.cache[num] = e; this.cache[num] = e;
return e; return e;
} }
error("compressed entry"); // compressed entry
stream = this.fetch(new Ref(e.offset, 0));
if (!IsStream(stream))
error("bad ObjStm stream");
var first = stream.parameters.get("First");
var n = stream.parameters.get("N");
if (!IsInt(first) || !IsInt(n)) {
error("invalid first and n parameters for ObjStm stream");
}
parser = new Parser(new Lexer(stream), false);
var i, entries = [], nums = [];
// read the object numbers to populate cache
for (i = 0; i < n; ++i) {
var num = parser.getObj();
if (!IsInt(num)) {
error("invalid object number in the ObjStm stream");
}
nums.push(num);
var offset = parser.getObj();
if (!IsInt(offset)) {
error("invalid object offset in the ObjStm stream");
}
}
// read stream objects for cache
for (i = 0; i < n; ++i) {
entries.push(parser.getObj());
this.cache[nums[i]] = entries[i];
}
e = entries[e.gen];
if (!e) {
error("bad XRef entry for compressed object");
}
return e;
}, },
getCatalogObj: function() { getCatalogObj: function() {
return this.fetch(this.root); return this.fetch(this.root);
@ -1543,20 +1568,39 @@ var Page = (function() {
: null)); : null));
}, },
compile: function(gfx, fonts) { compile: function(gfx, fonts) {
if (!this.code) { if (this.code) {
var xref = this.xref; // content was compiled
var content = xref.fetchIfRef(this.content); return;
var resources = xref.fetchIfRef(this.resources);
this.code = gfx.compile(content, xref, resources, fonts);
} }
var xref = this.xref;
var content;
var resources = xref.fetchIfRef(this.resources);
if (!IsArray(this.content)) {
// content is not an array, shortcut
content = xref.fetchIfRef(this.content);
this.code = gfx.compile(content, xref, resources, fonts);
return;
}
// the content is an array, compiling all items
var i, n = this.content.length, compiledItems = [];
for (i = 0; i < n; ++i) {
content = xref.fetchIfRef(this.content[i]);
compiledItems.push(gfx.compile(content, xref, resources, fonts));
}
// creating the function that executes all compiled items
this.code = function(gfx) {
var i, n = compiledItems.length;
for (i = 0; i < n; ++i) {
compiledItems[i](gfx);
}
};
}, },
display: function(gfx) { display: function(gfx) {
assert(this.code instanceof Function, "page content must be compiled first");
var xref = this.xref; var xref = this.xref;
var content = xref.fetchIfRef(this.content);
var resources = xref.fetchIfRef(this.resources); var resources = xref.fetchIfRef(this.resources);
var mediaBox = xref.fetchIfRef(this.mediaBox); var mediaBox = xref.fetchIfRef(this.mediaBox);
assertWellFormed(IsStream(content) && IsDict(resources), assertWellFormed(IsDict(resources), "invalid page resources");
"invalid page content or resources");
gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1], gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1],
width: mediaBox[2] - mediaBox[0], width: mediaBox[2] - mediaBox[0],
height: mediaBox[3] - mediaBox[1] }); height: mediaBox[3] - mediaBox[1] });