From 89a1f3ea179a85684daa2d7f2618817d2e6d4f7f Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 11:29:18 -0700 Subject: [PATCH 01/20] no need for new for typed array constructors --- pdf.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pdf.js b/pdf.js index c0e4791d4..8f61646d1 100644 --- a/pdf.js +++ b/pdf.js @@ -48,7 +48,7 @@ function shadow(obj, prop, value) { var Stream = (function() { function constructor(arrayBuffer, start, length, dict) { - this.bytes = new Uint8Array(arrayBuffer); + this.bytes = Uint8Array(arrayBuffer); this.start = start || 0; this.pos = this.start; this.end = (start + length) || this.bytes.byteLength; @@ -113,7 +113,7 @@ var Stream = (function() { var StringStream = (function() { function constructor(str) { var length = str.length; - var bytes = new Uint8Array(length); + var bytes = Uint8Array(length); for (var n = 0; n < length; ++n) bytes[n] = str.charCodeAt(n); Stream.call(this, bytes); @@ -125,11 +125,11 @@ var StringStream = (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 ]); - const lengthDecode = new Uint32Array([ + const lengthDecode = Uint32Array([ 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f, 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, @@ -137,7 +137,7 @@ var FlateStream = (function() { 0x00102, 0x00102, 0x00102 ]); - const distDecode = new Uint32Array([ + const distDecode = Uint32Array([ 0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d, 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1, 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, @@ -145,7 +145,7 @@ var FlateStream = (function() { 0xd4001, 0xd6001 ]); - const fixedLitCodeTab = [new Uint32Array([ + const fixedLitCodeTab = [Uint32Array([ 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0, 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0, 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, @@ -222,7 +222,7 @@ var FlateStream = (function() { 0x900ff ]), 9]; - const fixedDistCodeTab = [new Uint32Array([ + const fixedDistCodeTab = [Uint32Array([ 0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c, 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000, 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, @@ -298,7 +298,7 @@ var FlateStream = (function() { var size = 512; while (size < requested) size <<= 1; - var buffer2 = new Uint8Array(size); + var buffer2 = Uint8Array(size); for (var i = 0; i < current; ++i) buffer2[i] = buffer[i]; return this.buffer = buffer2; @@ -363,7 +363,7 @@ var FlateStream = (function() { // build the table var size = 1 << maxLen; - var codes = new Uint32Array(size); + var codes = Uint32Array(size); for (var len = 1, code = 0, skip = 2; len <= maxLen; ++len, code <<= 1, skip <<= 1) { From cf0e22ffafe30c21fd2df04c76eb2f38cc061fbb Mon Sep 17 00:00:00 2001 From: Justin D'Arcangelo Date: Sun, 19 Jun 2011 14:54:13 -0400 Subject: [PATCH 02/20] Changed the zoom input to utilize a pre-populated combo box. --- images/combobox.png | Bin 0 -> 1964 bytes images/source/ComboBox.psd.zip | Bin 0 -> 17724 bytes multi-page-viewer.css | 60 +++++++++++++++++++++++++++++++++ multi-page-viewer.html | 13 +++++-- multi-page-viewer.js | 40 +++++++++++++++++++--- 5 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 images/combobox.png create mode 100644 images/source/ComboBox.psd.zip diff --git a/images/combobox.png b/images/combobox.png new file mode 100644 index 0000000000000000000000000000000000000000..f1527d6a2e4cdcf3f72df511bdbde4e9f843a0f9 GIT binary patch literal 1964 zcmaJ?eNYp38r}fLFKoE>;0P*ggJ^6?vI$8@);sq@tFtSFs37Ov}b`3}&&cRx8sQ#Y9azSZtwC=+)qG7zBc0-ebfyHipq0 zKC2)_%vzHk!}X{U^eSq$qZV9DBQpJ^1OxV3)@Xj!CZb_1n+9XCnUJ@nS)fAk|DguM zYqS|pMSjiqe+rvbdoYBRikMN0NlOeaE8H6jgC!s{oJ`k&8hc_tD8F) z?wni}kqpZ_*#8>ztc%zl@AdWC62|MrM~uX_n~1f(R$!?F06$PJjaAwDM~|&v|6c5G zZ)_OiwS_HAIus{~!G5zVe9=(c`ar*w^oL1>LG3qg6+Wv>YfrMqrB5x~lHRb`y4mk{ zO()|vof=xx2G6utWYm7~@Koyqj&ISWDC*r8BC}`s$)k~xaNgr1iq+FoC^SO zL7vcgQ(ex#RZAq2=9U&2f*^n23moIMzT)ym#;3VMpF#kg`50TSe*sO33*0 zz}B|57Zsh>-Sp7%iW|e7qyHQnRB}V=E?>T3DN)}HL>3puZFtJNb>~9$TZ@k+pFVr` ztURx*wTL&lm9*fqD_8zHIl23};qNUik1Uf^wKPQ)saC70=w+JwUrzMq0r|82z>k~! z8m@2c@_4M@1h!thc(u8l`+eZ@@v*V7(y-AP^4_wVn;+mLO4lcijem^qixLPD_qhgB zGFPoy_32KV$K%nP%@u!8tDC9{R~7`#JimVZ`jMwY^DVc1{}j0DD`#p;OUqzoWu+^U z#hMuxtw>DoJ?K2i3F{2iBd#M6cWvifuCU6tk!rWwF|#kY56TJd>t+-WxE=L(`1$us z+Ah^@SXsekvuj!(|6sQq*mSAt{{8#(H;Y9-uleYdQn0N4_;Knxbh<7S3yIn%cW|`J?Z}r`(Ra-^!E732UAy28!W{M(=6d@#Pe|NXamnVR#FO&2aCOiWB1 z-+PcM#B@4oo@cG}%uXP)gXd2!;KK0FiTq`up|_^1_*2z)j}8eP!wdSlyJH#~8~Y0w z3{QJ21d^*1W6h b81(T623#fQfN0SX@BdRSOO}2Tm%0Bx3gr>A literal 0 HcmV?d00001 diff --git a/images/source/ComboBox.psd.zip b/images/source/ComboBox.psd.zip new file mode 100644 index 0000000000000000000000000000000000000000..232604c7514e825ae93b017a0fe186447399f32f GIT binary patch literal 17724 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 F@c^Z#N{Ijf literal 0 HcmV?d00001 diff --git a/multi-page-viewer.css b/multi-page-viewer.css index 53a28f129..293b32241 100644 --- a/multi-page-viewer.css +++ b/multi-page-viewer.css @@ -113,6 +113,66 @@ 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; +} + #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 +
+
    +
  • 50%
  • +
  • 75%
  • +
  • 100%
  • +
  • 125%
  • +
  • 150%
  • +
  • 200%
  • +
+
diff --git a/multi-page-viewer.js b/multi-page-viewer.js index 2410eb7bf..6cb46a08a 100644 --- a/multi-page-viewer.js +++ b/multi-page-viewer.js @@ -8,9 +8,10 @@ var PDFViewer = { element: null, - pageNumberInput: null, previousPageButton: null, nextPageButton: null, + pageNumberInput: null, + scaleInput: null, willJumpToPage: false, @@ -158,6 +159,8 @@ var PDFViewer = { PDFViewer.drawPage(1); } } + + PDFViewer.scaleInput.value = Math.floor(PDFViewer.scale * 100) + '%'; }, goToPage: function(num) { @@ -317,13 +320,40 @@ window.onload = function() { this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : ''; }; - var scaleInput = document.getElementById('scale'); - scaleInput.onchange = function(evt) { - PDFViewer.changeScale(this.value); + PDFViewer.scaleInput = document.getElementById('scale'); + PDFViewer.scaleInput.buttonElement = document.getElementById('scaleComboBoxButton'); + 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.scale = parseInt(scaleInput.value) / 100 || 1.0; + PDFViewer.scale = parseInt(PDFViewer.scaleInput.value) / 100 || 1.0; PDFViewer.open(PDFViewer.queryParams.file || PDFViewer.url); window.onscroll = function(evt) { From a3781157cf7f2f70b06742c02d0e534d3039365c Mon Sep 17 00:00:00 2001 From: Justin D'Arcangelo Date: Sun, 19 Jun 2011 14:57:45 -0400 Subject: [PATCH 03/20] Minor CSS fix for zoom drop down. --- multi-page-viewer.css | 1 + 1 file changed, 1 insertion(+) diff --git a/multi-page-viewer.css b/multi-page-viewer.css index 293b32241..c96567465 100644 --- a/multi-page-viewer.css +++ b/multi-page-viewer.css @@ -171,6 +171,7 @@ span { #scaleComboBoxList > ul > li:hover { background-color: #09f; + color: #fff; } #pageNumber, #scale { From 5afd963c8a62102591d6b60f31e354f51670a3af Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 15:14:07 -0700 Subject: [PATCH 04/20] handle surrogate pairs in glyph to unicode translation --- fonts.js | 6 +- glyphlist.js | 152 +++++++++++++++++++++++++-------------------------- 2 files changed, 81 insertions(+), 77 deletions(-) diff --git a/fonts.js b/fonts.js index 0c8725fb4..6cf9d6c1d 100644 --- a/fonts.js +++ b/fonts.js @@ -62,6 +62,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); } @@ -83,7 +87,7 @@ var Fonts = { var Font = 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]) { this.font = Fonts[aName].data; return; 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, From 7bb098dfc7b91151cb20d786c7a4bc190b0c85bc Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 15:30:55 -0700 Subject: [PATCH 05/20] don't use an array to translate from a typed array to a string, and always store font data as typed array, never as a stream --- fonts.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fonts.js b/fonts.js index 6cf9d6c1d..e018061e9 100644 --- a/fonts.js +++ b/fonts.js @@ -154,14 +154,13 @@ Font.prototype = { 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])); + // 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.join("")); + var dataBase64 = window.btoa(str); var fontName = this.name; /** Hack begin */ @@ -752,7 +751,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(); } }; From f6e85f00cbb155f0c4bba2555d6df8468ea32e41 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 15:46:58 -0700 Subject: [PATCH 06/20] add a closure around Font so we can hide helper functions in it --- fonts.js | 829 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 416 insertions(+), 413 deletions(-) diff --git a/fonts.js b/fonts.js index e018061e9..85a5c199d 100644 --- a/fonts.js +++ b/fonts.js @@ -84,17 +84,18 @@ 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 it - 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"; @@ -124,448 +125,450 @@ 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; - - // 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); } - 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 = " "; - // 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); + /** + * 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, + + 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); + _createOpenTypeHeader: function font_createOpenTypeHeader(aFile, aOffsets, aNumTables) { + // sfnt version (4 bytes) + var version = [0x4F, 0x54, 0x54, 0X4F]; - // rangeShift (2 bytes) - var rangeShift = numTables * 16 - searchRange; + // numTables (2 bytes) + var numTables = aNumTables; - 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; - }, + // searchRange (2 bytes) + var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); + var searchRange = tablesMaxPower2 * 16; - _createTableEntry: function font_createTableEntry(aFile, aOffsets, aTag, aData) { - // tag - var tag = [ - aTag.charCodeAt(0), - aTag.charCodeAt(1), - aTag.charCodeAt(2), - aTag.charCodeAt(3) - ]; + // entrySelector (2 bytes) + var entrySelector = Math.log(tablesMaxPower2) / Math.log(2); - // offset - var offset = aOffsets.virtualOffset; + // rangeShift (2 bytes) + var rangeShift = numTables * 16 - searchRange; - // Per spec tables must be 4-bytes align so add some 0x00 if needed - while (aData.length & 3) - aData.push(0x00); + 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; + }, - // length - var length = aData.length; + _createTableEntry: function font_createTableEntry(aFile, aOffsets, aTag, aData) { + // tag + var tag = [ + aTag.charCodeAt(0), + aTag.charCodeAt(1), + aTag.charCodeAt(2), + aTag.charCodeAt(3) + ]; - // checksum - var checksum = FontsUtils.bytesToInteger(tag) + offset + length; + // offset + var offset = aOffsets.virtualOffset; - 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; - }, + // Per spec tables must be 4-bytes align so add some 0x00 if needed + while (aData.length & 3) + aData.push(0x00); - _createCMAPTable: function font_createCMAPTable(aGlyphs) { - var characters = new Uint16Array(kMaxGlyphsCount); - for (var i = 0; i < aGlyphs.length; i++) - characters[aGlyphs[i].unicode] = i + 1; + // length + var length = aData.length; - // 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 = []; + // 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 = []; + } } - } - // 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)); + // 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) - ); + 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); + // 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; + 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; + // 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]); + 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); - for (var j = 0; j < range.length; j++) - glyphsIdsArray.push(range[j]); + 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; } - 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 From 886054080e38ad325e4fe3400bce82c738790295 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 15:54:27 -0700 Subject: [PATCH 07/20] make createOpenTypeHeader and createTableEntry inner functions --- fonts.js | 135 +++++++++++++++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 68 deletions(-) diff --git a/fonts.js b/fonts.js index 85a5c199d..fcd55d20e 100644 --- a/fonts.js +++ b/fonts.js @@ -138,6 +138,63 @@ var Font = (function () { this.bind(); }; + function createOpenTypeHeader(aFile, aOffsets, aNumTables) { + // sfnt version (4 bytes) + var version = [0x4F, 0x54, 0x54, 0X4F]; + + // numTables (2 bytes) + var numTables = aNumTables; + + // searchRange (2 bytes) + var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); + var searchRange = tablesMaxPower2 * 16; + + // entrySelector (2 bytes) + var entrySelector = Math.log(tablesMaxPower2) / Math.log(2); + + // 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; + } + + function 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; + } /** * A bunch of the OpenType code is duplicate between this class and the @@ -242,64 +299,6 @@ var Font = (function () { styleSheet.insertRule(rule, styleSheet.length); }, - _createOpenTypeHeader: function font_createOpenTypeHeader(aFile, aOffsets, aNumTables) { - // sfnt version (4 bytes) - var version = [0x4F, 0x54, 0x54, 0X4F]; - - // numTables (2 bytes) - var numTables = aNumTables; - - // searchRange (2 bytes) - var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); - var searchRange = tablesMaxPower2 * 16; - - // entrySelector (2 bytes) - var entrySelector = Math.log(tablesMaxPower2) / Math.log(2); - - // 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++) @@ -408,12 +407,12 @@ var Font = (function () { // For files with only one font the offset table is the first thing of the // file - this._createOpenTypeHeader(otf, offsets, tables.length); + 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); + createTableEntry(otf, offsets, "CFF ", CFF); /** OS/2 */ OS2 = [ @@ -455,14 +454,14 @@ var Font = (function () { 0x00, 0xCD, // usBreakChar 0x00, 0x02 // usMaxContext ]; - this._createTableEntry(otf, offsets, "OS/2", OS2); + 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); + createTableEntry(otf, offsets, "cmap", cmap); /** HEAD */ head = [ @@ -484,7 +483,7 @@ var Font = (function () { 0x00, 0x00, // indexToLocFormat 0x00, 0x00 // glyphDataFormat ]; - this._createTableEntry(otf, offsets, "head", head); + createTableEntry(otf, offsets, "head", head); /** HHEA */ hhea = [].concat( @@ -508,7 +507,7 @@ var Font = (function () { ], FontsUtils.integerToBytes(charstrings.length, 2) // numberOfHMetrics ); - this._createTableEntry(otf, offsets, "hhea", hhea); + createTableEntry(otf, offsets, "hhea", hhea); /** HMTX */ hmtx = [0x01, 0xF4, 0x00, 0x00]; @@ -518,7 +517,7 @@ var Font = (function () { var lsb = FontsUtils.integerToBytes(charstring[0], 2); hmtx = hmtx.concat(width, lsb); } - this._createTableEntry(otf, offsets, "hmtx", hmtx); + createTableEntry(otf, offsets, "hmtx", hmtx); /** MAXP */ maxp = [].concat( @@ -527,7 +526,7 @@ var Font = (function () { ], FontsUtils.integerToBytes(charstrings.length + 1, 2) // Num of glyphs (+1 to pass the sanitizer...) ); - this._createTableEntry(otf, offsets, "maxp", maxp); + createTableEntry(otf, offsets, "maxp", maxp); /** NAME */ name = [ @@ -535,7 +534,7 @@ var Font = (function () { 0x00, 0x00, // Number of names Record 0x00, 0x00 // Storage ]; - this._createTableEntry(otf, offsets, "name", name); + createTableEntry(otf, offsets, "name", name); /** POST */ // FIXME Get those informations from the FontInfo structure @@ -550,7 +549,7 @@ var Font = (function () { 0x00, 0x00, 0x00, 0x00, // minMemType1 0x00, 0x00, 0x00, 0x00 // maxMemType1 ]; - this._createTableEntry(otf, offsets, "post", 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]; From 1a5105741734578c3215af114b8c885a16b83498 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 15:58:02 -0700 Subject: [PATCH 08/20] make createOpenTypeHeader and createTableEntry inner functions of co(n)ver(t) so we can use an upvar to collect the output --- fonts.js | 116 +++++++++++++++++++++++++++---------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/fonts.js b/fonts.js index fcd55d20e..1250bf558 100644 --- a/fonts.js +++ b/fonts.js @@ -138,64 +138,6 @@ var Font = (function () { this.bind(); }; - function createOpenTypeHeader(aFile, aOffsets, aNumTables) { - // sfnt version (4 bytes) - var version = [0x4F, 0x54, 0x54, 0X4F]; - - // numTables (2 bytes) - var numTables = aNumTables; - - // searchRange (2 bytes) - var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); - var searchRange = tablesMaxPower2 * 16; - - // entrySelector (2 bytes) - var entrySelector = Math.log(tablesMaxPower2) / Math.log(2); - - // 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; - } - - function 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; - } - /** * 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 @@ -384,6 +326,64 @@ var Font = (function () { cover: function font_cover(aFont, aProperties) { var otf = new Uint8Array(kMaxFontFileSize); + function createOpenTypeHeader(aFile, aOffsets, aNumTables) { + // sfnt version (4 bytes) + var version = [0x4F, 0x54, 0x54, 0X4F]; + + // numTables (2 bytes) + var numTables = aNumTables; + + // searchRange (2 bytes) + var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); + var searchRange = tablesMaxPower2 * 16; + + // entrySelector (2 bytes) + var entrySelector = Math.log(tablesMaxPower2) / Math.log(2); + + // 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; + } + + function 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; + } + // Required Tables var CFF = aFont.data, // PostScript Font Program OS2 = [], // OS/2 and Windows Specific metrics From bb40d20eae6ddf0e92dfe5b074545630547ecb61 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 15:58:30 -0700 Subject: [PATCH 09/20] rename conver to convert --- fonts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fonts.js b/fonts.js index 1250bf558..5ee3cc8f7 100644 --- a/fonts.js +++ b/fonts.js @@ -101,7 +101,7 @@ var Font = (function () { 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": @@ -323,7 +323,7 @@ var Font = (function () { idDeltas, idRangeOffsets, glyphsIdsArray); }, - cover: function font_cover(aFont, aProperties) { + convert: function font_convert(aFont, aProperties) { var otf = new Uint8Array(kMaxFontFileSize); function createOpenTypeHeader(aFile, aOffsets, aNumTables) { From c09ee48094b504028cf2f55976842bb3149bad1e Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 16:13:59 -0700 Subject: [PATCH 10/20] write OTF header using a string, not an array --- fonts.js | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/fonts.js b/fonts.js index 5ee3cc8f7..c599af2a4 100644 --- a/fonts.js +++ b/fonts.js @@ -326,29 +326,39 @@ var Font = (function () { convert: function font_convert(aFont, aProperties) { var otf = new Uint8Array(kMaxFontFileSize); - function createOpenTypeHeader(aFile, aOffsets, aNumTables) { + function s2a(s) { + var a = []; + for (var i = 0; i < s.length; ++i) + a[i] = s.charCodeAt(i); + return a; + } + + function createOpenTypeHeader(aFile, aOffsets, numTables) { + var header = ""; + + function WriteHeader16(value) { + header += String.fromCharCode(value >> 8); + header += String.fromCharCode(value & 0xff); + } + // sfnt version (4 bytes) - var version = [0x4F, 0x54, 0x54, 0X4F]; + header += "\x4F\x54\x54\x4F"; // numTables (2 bytes) - var numTables = aNumTables; + WriteHeader16(numTables); // searchRange (2 bytes) var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); var searchRange = tablesMaxPower2 * 16; + WriteHeader16(searchRange); // entrySelector (2 bytes) - var entrySelector = Math.log(tablesMaxPower2) / Math.log(2); + WriteHeader16(Math.log(tablesMaxPower2) / Math.log(2)); // rangeShift (2 bytes) - var rangeShift = numTables * 16 - searchRange; + WriteHeader16(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); + aFile.set(s2a(header), aOffsets.currentOffset); aOffsets.currentOffset += header.length; aOffsets.virtualOffset += header.length; } From a118f9035a82b2a576d849533a1c6495c5319f9d Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 16:32:26 -0700 Subject: [PATCH 11/20] in preparation for using strings to write out font data, store constant blobs as strings --- fonts.js | 195 +++++++++++++++++++++++++++---------------------------- 1 file changed, 96 insertions(+), 99 deletions(-) diff --git a/fonts.js b/fonts.js index c599af2a4..3bc71e683 100644 --- a/fonts.js +++ b/fonts.js @@ -333,30 +333,29 @@ var Font = (function () { return a; } + function s16(value) { + return String.fromCharCode(value >> 8) + String.fromCharCode(value & 0xff); + } + function createOpenTypeHeader(aFile, aOffsets, numTables) { var header = ""; - function WriteHeader16(value) { - header += String.fromCharCode(value >> 8); - header += String.fromCharCode(value & 0xff); - } - // sfnt version (4 bytes) header += "\x4F\x54\x54\x4F"; // numTables (2 bytes) - WriteHeader16(numTables); + header += s16(numTables); // searchRange (2 bytes) var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); var searchRange = tablesMaxPower2 * 16; - WriteHeader16(searchRange); + header += s16(searchRange); // entrySelector (2 bytes) - WriteHeader16(Math.log(tablesMaxPower2) / Math.log(2)); + header += s16(Math.log(tablesMaxPower2) / Math.log(2)); // rangeShift (2 bytes) - WriteHeader16(numTables * 16 - searchRange); + header += s16(numTables * 16 - searchRange); aFile.set(s2a(header), aOffsets.currentOffset); aOffsets.currentOffset += header.length; @@ -425,45 +424,45 @@ var Font = (function () { 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 - ]; + 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 @@ -474,49 +473,47 @@ var Font = (function () { 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 - ]; + 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 = [].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 - ); + 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 */ @@ -548,17 +545,17 @@ var Font = (function () { /** 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 - ]; + post = s2a( + "\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 + ); createTableEntry(otf, offsets, "post", post); // Once all the table entries header are written, dump the data! From 4fc8a1fa755480a26eb0f427260549259dcfd3e2 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 19 Jun 2011 16:35:32 -0700 Subject: [PATCH 12/20] make createCMAPTable a nested function function --- fonts.js | 166 +++++++++++++++++++++++++++---------------------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/fonts.js b/fonts.js index 3bc71e683..2ee8a0d3c 100644 --- a/fonts.js +++ b/fonts.js @@ -241,88 +241,6 @@ var Font = (function () { styleSheet.insertRule(rule, styleSheet.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 = []; - } - } - - // 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); - }, - convert: function font_convert(aFont, aProperties) { var otf = new Uint8Array(kMaxFontFileSize); @@ -392,6 +310,88 @@ var Font = (function () { aOffsets.currentOffset += tableEntry.length; aOffsets.virtualOffset += aData.length; } + + function 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 = []; + } + } + + // 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); + } // Required Tables var CFF = aFont.data, // PostScript Font Program @@ -469,7 +469,7 @@ var Font = (function () { var charstrings = aFont.getOrderedCharStrings(aProperties.glyphs); /** CMAP */ - cmap = this._createCMAPTable(charstrings); + cmap = createCMAPTable(charstrings); createTableEntry(otf, offsets, "cmap", cmap); /** HEAD */ From c2ce7fa7b2015f5dadaeec7eee63e08f532d22ea Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 19 Jun 2011 18:55:02 -0500 Subject: [PATCH 13/20] Ability to fetch ObjStm objects; fix DEFLATE stream double-eof-call issue; multi-item page content --- pdf.js | 80 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/pdf.js b/pdf.js index a051e57f3..f03615a64 100644 --- a/pdf.js +++ b/pdf.js @@ -308,7 +308,7 @@ var FlateStream = (function() { getByte: function() { var bufferLength = this.bufferLength; var pos = this.pos; - if (bufferLength == pos) { + if (bufferLength <= pos) { if (this.eof) return; this.readBlock(); @@ -333,7 +333,7 @@ var FlateStream = (function() { lookChar: function() { var bufferLength = this.bufferLength; var pos = this.pos; - if (bufferLength == pos) { + if (bufferLength <= pos) { if (this.eof) return; this.readBlock(); @@ -599,7 +599,7 @@ var PredictorStream = (function() { var DecryptStream = (function() { function constructor(str, fileKey, encAlgorithm, keyLength) { - // TODO + TODO("decrypt stream is not implemented"); } constructor.prototype = Stream.prototype; @@ -1475,11 +1475,12 @@ var XRef = (function() { e = this.getEntry(num); var gen = ref.gen; + var stream, parser; if (e.uncompressed) { if (e.gen != gen) throw("inconsistent generation in XRef"); - var stream = this.stream.makeSubStream(e.offset); - var parser = new Parser(new Lexer(stream), true, this); + stream = this.stream.makeSubStream(e.offset); + parser = new Parser(new Lexer(stream), true, this); var obj1 = parser.getObj(); var obj2 = parser.getObj(); var obj3 = parser.getObj(); @@ -1503,7 +1504,39 @@ var XRef = (function() { this.cache[num] = e; return e; } - error("compressed entry"); + // compressed entry + stream = this.fetch({num:e.offset, gen: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() { return this.fetch(this.root); @@ -1534,20 +1567,39 @@ var Page = (function() { : null)); }, compile: function(gfx, fonts) { - if (!this.code) { - var xref = this.xref; - var content = xref.fetchIfRef(this.content); - var resources = xref.fetchIfRef(this.resources); - this.code = gfx.compile(content, xref, resources, fonts); + if (this.code) { + // content was compiled + return; } + 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) { + assert(this.code instanceof Function, "page content must be compiled first"); var xref = this.xref; - var content = xref.fetchIfRef(this.content); var resources = xref.fetchIfRef(this.resources); var mediaBox = xref.fetchIfRef(this.mediaBox); - assertWellFormed(IsStream(content) && IsDict(resources), - "invalid page content or resources"); + assertWellFormed(IsDict(resources), "invalid page resources"); gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1], width: mediaBox[2] - mediaBox[0], height: mediaBox[3] - mediaBox[1] }); From fc4ca2a9dde75d7a536cf81debae674e106a4aaf Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 01:20:05 -0400 Subject: [PATCH 14/20] style fixes, we should post some sensible style guide --- pdf.js | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/pdf.js b/pdf.js index e507d406f..eb40df573 100644 --- a/pdf.js +++ b/pdf.js @@ -1385,31 +1385,25 @@ var XRef = (function() { var length = streamParameters.get("Length"); var byteWidths = streamParameters.get("W"); var range = streamParameters.get("Index"); - if (!range) { + if (!range) range = [0, streamParameters.get("Size")]; - } var i, j; while (range.length > 0) { var first = range[0], n = range[1]; - if (!IsInt(first) || !IsInt(n)) { + if (!IsInt(first) || !IsInt(n)) error("Invalid XRef range fields"); - } 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"); - } for (i = 0; i < n; ++i) { var type = 0, offset = 0, generation = 0; - for (j = 0; j < typeFieldWidth; ++j) { + for (j = 0; j < typeFieldWidth; ++j) type = (type << 8) | stream.getByte(); - } - for (j = 0; j < offsetFieldWidth; ++j) { + for (j = 0; j < offsetFieldWidth; ++j) offset = (offset << 8) | stream.getByte(); - } - for (j = 0; j < generationFieldWidth; ++j) { + for (j = 0; j < generationFieldWidth; ++j) generation = (generation << 8) | stream.getByte(); - } - var entry = { offset: offset, gen: generation }; + var entry = new Ref(offset, generation); if (typeFieldWidth > 0) { switch (type) { case 0: @@ -1425,16 +1419,14 @@ var XRef = (function() { break; } } - if (!this.entries[first + i]) { + if (!this.entries[first + i]) this.entries[first + i] = entry; - } } range.splice(0, 2); } var prev = streamParameters.get("Prev"); - if (IsInt(prev)) { + if (IsInt(prev)) this.readXRef(prev); - } return streamParameters; }, readXRef: function(startXRef) { @@ -1505,7 +1497,7 @@ var XRef = (function() { return e; } // compressed entry - stream = this.fetch({num:e.offset, gen:0}); + stream = this.fetch(new Ref(e.offset, 0)); if (!IsStream(stream)) error("bad ObjStm stream"); var first = stream.parameters.get("First"); From 52e1631030ce6494fca5f3818bc4030172e4acdf Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 03:33:52 -0400 Subject: [PATCH 15/20] use Array.sort to calculate glyph ranges instead of large typed arrays --- fonts.js | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/fonts.js b/fonts.js index 2ee8a0d3c..49a78cb53 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 */ @@ -310,24 +305,30 @@ var Font = (function () { aOffsets.currentOffset += tableEntry.length; aOffsets.virtualOffset += aData.length; } - - function 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. + 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(); + 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 = []; - } + for (var n = 0; n < length; ) { + var range = []; + do { + var current = codes[n++].charCodeAt(0); + range.push(current); + } while (n < length && current + 1 == codes[n].charCodeAt(0)); + ranges.push(range); } + return ranges; + } + + function createCMAPTable(aGlyphs) { + var ranges = getRanges(aGlyphs); // 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 From 5f75130fbff18bc422112466cd5e9f7820ac94a1 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 03:49:40 -0400 Subject: [PATCH 16/20] store ranges as [start, end], instead of storing all codes --- fonts.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/fonts.js b/fonts.js index 49a78cb53..73d42d8be 100644 --- a/fonts.js +++ b/fonts.js @@ -315,14 +315,16 @@ var Font = (function () { codes.push(String.fromCharCode(glyphs[n].unicode)) codes.sort(); + // Split the sorted codes into ranges. var ranges = []; for (var n = 0; n < length; ) { - var range = []; - do { - var current = codes[n++].charCodeAt(0); - range.push(current); - } while (n < length && current + 1 == codes[n].charCodeAt(0)); - ranges.push(range); + 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; } @@ -367,10 +369,10 @@ var Font = (function () { 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 end = FontsUtils.integerToBytes(range[1], 2); var delta = FontsUtils.integerToBytes(((range[0] - 1) - bias) % 65536, 2); - bias += range.length; + bias += (range[1] - range[0] + 1); // deltas are signed shorts delta[0] ^= 0xFF; @@ -382,8 +384,8 @@ var Font = (function () { idDeltas.push(delta[0], delta[1]); idRangeOffsets.push(0x00, 0x00); - for (var j = 0; j < range.length; j++) - glyphsIdsArray.push(range[j]); + for (var j = range[0]; j <= range[1]; j++) + glyphsIdsArray.push(j); } startCount.push(0xFF, 0xFF); endCount.push(0xFF, 0xFF); From e97f74f6e3a1462c2314b4442189d7feddbce66b Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 04:16:41 -0400 Subject: [PATCH 17/20] simplify glyph segment writing code --- fonts.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fonts.js b/fonts.js index 73d42d8be..abdd71db9 100644 --- a/fonts.js +++ b/fonts.js @@ -368,23 +368,21 @@ var Font = (function () { 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[1], 2); + var start = range[0]; + var end = range[1]; + var delta = (((start - 1) - bias) ^ 0xffff) + 1; + bias += (end - start + 1); - var delta = FontsUtils.integerToBytes(((range[0] - 1) - bias) % 65536, 2); - bias += (range[1] - range[0] + 1); - - // deltas are signed shorts - delta[0] ^= 0xFF; - delta[1] ^= 0xFF; - delta[1] += 1; + var start = FontsUtils.integerToBytes(start, 2); + var end = FontsUtils.integerToBytes(end, 2); + var delta = FontsUtils.integerToBytes(delta, 2); 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 = range[0]; j <= range[1]; j++) + for (var j = start; j <= end; j++) glyphsIdsArray.push(j); } startCount.push(0xFF, 0xFF); From 32880025fc1ccdcc6e47a77fa4c2be4ceed29012 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 04:20:51 -0400 Subject: [PATCH 18/20] write font cmap using a string --- fonts.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/fonts.js b/fonts.js index abdd71db9..54aa46efb 100644 --- a/fonts.js +++ b/fonts.js @@ -342,22 +342,20 @@ var Font = (function () { 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) - ); + + 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); + cmap = s2a(cmap); // Fill up the 4 parallel arrays describing the segments. var startCount = []; From 0c948d7b0631215385cd4ffaae1dea01c3650978 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 04:27:21 -0400 Subject: [PATCH 19/20] assemble cmap table from strings instead of arrays --- fonts.js | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/fonts.js b/fonts.js index 54aa46efb..6a8614022 100644 --- a/fonts.js +++ b/fonts.js @@ -332,11 +332,7 @@ var Font = (function () { function createCMAPTable(aGlyphs) { var ranges = getRanges(aGlyphs); - // 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; @@ -355,14 +351,13 @@ var Font = (function () { s16(searchRange) + s16(searchEntry) + s16(rangeShift); - cmap = s2a(cmap); // Fill up the 4 parallel arrays describing the segments. - var startCount = []; - var endCount = []; - var idDeltas = []; - var idRangeOffsets = []; - var glyphsIdsArray = []; + 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]; @@ -371,25 +366,22 @@ var Font = (function () { var delta = (((start - 1) - bias) ^ 0xffff) + 1; bias += (end - start + 1); - var start = FontsUtils.integerToBytes(start, 2); - var end = FontsUtils.integerToBytes(end, 2); - var delta = FontsUtils.integerToBytes(delta, 2); - - startCount.push(start[0], start[1]); - endCount.push(end[0], end[1]); - idDeltas.push(delta[0], delta[1]); - idRangeOffsets.push(0x00, 0x00); + startCount += s16(start); + endCount += s16(end); + idDeltas += s16(delta); + idRangeOffsets += s16(0); for (var j = start; j <= end; j++) - glyphsIdsArray.push(j); + glyphsIds += String.fromCharCode(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); + startCount += "\xFF\xFF"; + endCount += "\xFF\xFF"; + idDeltas += "\x00\x01"; + idRangeOffsets += "\x00\x00"; + + return s2a(cmap + endCount + "\x00\x00" + startCount + + idDeltas + idRangeOffsets + glyphsIds); } // Required Tables From f3fcbd0fbc233abf238e39a53bc2f13a905e91b3 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 14:42:39 -0400 Subject: [PATCH 20/20] remove remaining uses of integerToBytes in type1 font code --- fonts.js | 82 +++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/fonts.js b/fonts.js index 6a8614022..b8a490369 100644 --- a/fonts.js +++ b/fonts.js @@ -247,7 +247,12 @@ var Font = (function () { } function s16(value) { - return String.fromCharCode(value >> 8) + String.fromCharCode(value & 0xff); + 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) { @@ -276,18 +281,10 @@ var Font = (function () { } function 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 + // Per spec tables must be 4-bytes align so add padding as needed while (aData.length & 3) aData.push(0x00); @@ -295,12 +292,15 @@ var Font = (function () { var length = aData.length; // checksum - var checksum = FontsUtils.bytesToInteger(tag) + offset + length; + var checksum = aTag.charCodeAt(0) + + aTag.charCodeAt(1) + + aTag.charCodeAt(2) + + aTag.charCodeAt(3) + + offset + + length; - var tableEntry = [].concat(tag, - FontsUtils.integerToBytes(checksum, 4), - FontsUtils.integerToBytes(offset, 4), - FontsUtils.integerToBytes(length, 4)); + 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; @@ -409,7 +409,7 @@ var Font = (function () { // file createOpenTypeHeader(otf, offsets, tables.length); - // XXX It is probable that in a future we want to get rid of this glue + // 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); @@ -508,45 +508,41 @@ var Font = (function () { createTableEntry(otf, offsets, "hhea", hhea); /** HMTX */ - hmtx = [0x01, 0xF4, 0x00, 0x00]; + hmtx = "\x01\xF4\x00\x00"; 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); + var width = charstring[1]; + var lsb = charstring[0]; + hmtx += s16(width) + s16(lsb); } + hmtx = s2a(hmtx); 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...) - ); + 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 = [ - 0x00, 0x00, // format - 0x00, 0x00, // Number of names Record - 0x00, 0x00 // Storage - ]; + name = "\x00\x00" + // Format + "\x00\x00" + // Number of name records + "\x00\x00"; // Storage + name = s2a(name); createTableEntry(otf, offsets, "name", name); /** POST */ - // FIXME Get those informations from the FontInfo structure - post = s2a( - "\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 - ); + // 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!