From a2d7c18aee4d66cd37085bf2597808acfeaec8e9 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Fri, 17 Jun 2011 07:37:14 -0500 Subject: [PATCH 1/7] readXRefStream and PNG predictor 12 --- pdf.js | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 160 insertions(+), 16 deletions(-) diff --git a/pdf.js b/pdf.js index 17537d233..dc2302d2d 100644 --- a/pdf.js +++ b/pdf.js @@ -506,6 +506,94 @@ var FlateStream = (function() { return constructor; })(); +var PredictorStream = (function() { + function constructor(stream, params) { + this.stream = stream; + this.predictor = params.get("Predictor") || 1; + if (this.predictor <= 1) { + return stream; // no prediction + } + if (params.has("EarlyChange")) { + error("EarlyChange predictor parameter is not supported"); + } + this.colors = params.get("Colors") || 1; + this.bitsPerComponent = params.get("BitsPerComponent") || 8; + this.columns = params.get("Columns") || 1; + if (this.colors !== 1 || this.bitsPerComponent !== 8) { + error("Multi-color and multi-byte predictors are not supported"); + } + if (this.predictor < 10 || this.predictor > 15) { + error("Unsupported predictor"); + } + this.currentRow = new Uint8Array(this.columns); + this.pos = 0; + this.bufferLength = 0; + } + + constructor.prototype = { + readRow : function() { + var lastRow = this.currentRow; + var predictor = this.stream.getByte(); + var currentRow = this.stream.getBytes(this.columns), i; + switch (predictor) { + default: + error("Unsupported predictor"); + break; + case 0: + break; + case 2: + for (i = 0; i < currentRow.length; ++i) { + currentRow[i] = (lastRow[i] + currentRow[i]) & 0xFF; + } + break; + } + this.pos = 0; + this.bufferLength = currentRow.length; + this.currentRow = currentRow; + }, + getByte : function() { + if (this.pos >= this.bufferLength) { + this.readRow(); + } + return this.currentRow[this.pos++]; + }, + getBytes : function(n) { + var i, bytes; + bytes = new Uint8Array(n); + for (i = 0; i < n; ++i) { + if (this.pos >= this.bufferLength) { + this.readRow(); + } + bytes[i] = this.currentRow[this.pos++]; + } + return bytes; + }, + getChar : function() { + return String.formCharCode(this.getByte()); + }, + lookChar : function() { + if (this.pos >= this.bufferLength) { + this.readRow(); + } + return String.formCharCode(this.currentRow[this.pos]); + }, + skip : function(n) { + var i; + if (!n) { + n = 1; + } + while (n > this.bufferLength - this.pos) { + n -= this.bufferLength - this.pos; + this.readRow(); + if (this.bufferLength === 0) break; + } + this.pos += n; + } + }; + + return constructor; +})(); + var DecryptStream = (function() { function constructor(str, fileKey, encAlgorithm, keyLength) { // TODO @@ -1079,7 +1167,9 @@ var Parser = (function() { this.encAlgorithm, this.keyLength); } - return this.filter(stream, dict); + stream = this.filter(stream, dict); + stream.parameters = dict; + return stream; }, filter: function(stream, dict) { var filter = dict.get2("Filter", "F"); @@ -1104,8 +1194,9 @@ var Parser = (function() { }, makeFilter: function(stream, name, params) { if (name == "FlateDecode" || name == "Fl") { - if (params) - error("params not supported yet for FlateDecode"); + if (params) { + return new PredictorStream(new FlateStream(stream), params); + } return new FlateStream(stream); } else { error("filter '" + name + "' not supported yet"); @@ -1198,10 +1289,10 @@ var XRef = (function() { this.stream = stream; this.entries = []; this.xrefstms = {}; - this.readXRef(startXRef); + var trailerDict = this.readXRef(startXRef); // get the root dictionary (catalog) object - if (!IsRef(this.root = this.trailerDict.get("Root"))) + if (!IsRef(this.root = trailerDict.get("Root"))) error("Invalid root reference"); // prepare the XRef cache @@ -1256,18 +1347,18 @@ var XRef = (function() { error("Invalid XRef table"); // get the 'Prev' pointer - var more = false; + var prev; obj = dict.get("Prev"); if (IsInt(obj)) { - this.prev = obj; - more = true; + prev = obj; } else if (IsRef(obj)) { // certain buggy PDF generators generate "/Prev NNN 0 R" instead // of "/Prev NNN" - this.prev = obj.num; - more = true; + prev = obj.num; + } + if (prev) { + this.readXRef(prev); } - this.trailerDict = dict; // check for 'XRefStm' key if (IsInt(obj = dict.get("XRefStm"))) { @@ -1277,11 +1368,64 @@ var XRef = (function() { this.xrefstms[pos] = 1; // avoid infinite recursion this.readXRef(pos); } - - return more; + return dict; }, - readXRefStream: function(parser) { - error("Invalid XRef stream"); + readXRefStream: function(stream) { + var streamParameters = stream.parameters; + var length = streamParameters.get("Length"); + var byteWidths = streamParameters.get("W"); + var range = streamParameters.get("Index"); + 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)) { + error("Invalid XRef range fields"); + } + var typeFieldWidth = byteWidths[0], offsetFieldWidth = byteWidths[1], generationFieldWidth = byteWidths[2]; + 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) { + type = (type << 8) | stream.getByte(); + } + for (j = 0; j < offsetFieldWidth; ++j) { + offset = (offset << 8) | stream.getByte(); + } + for (j = 0; j < generationFieldWidth; ++j) { + generation = (generation << 8) | stream.getByte(); + } + var entry = { offset: offset, gen: generation }; + if (typeFieldWidth > 0) { + switch (type) { + case 0: + entry.free = true; + break; + case 1: + entry.uncompressed = true; + break; + case 2: + break; + default: + error("Invalid XRef entry type"); + break; + } + } + if (!this.entries[first + i]) { + this.entries[first + i] = entry; + } + } + range.splice(0, 2); + } + var prev = streamParameters.get("Prev"); + if (IsInt(prev)) { + this.readXRef(prev); + } + return streamParameters; }, readXRef: function(startXRef) { var stream = this.stream; @@ -1565,7 +1709,7 @@ var PDFDoc = (function() { }, getPage: function(n) { var linearization = this.linearization; - assert(!linearization, "linearized page access not implemented"); + // assert(!linearization, "linearized page access not implemented"); return this.catalog.getPage(n); } }; From 26fc2f17b101c513e605bf1ae55ee0259bbaac2e Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 18 Jun 2011 23:49:31 -0500 Subject: [PATCH 2/7] Add Yury to the contributors list --- LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE b/LICENSE index 3d42ba007..81658476c 100644 --- a/LICENSE +++ b/LICENSE @@ -6,6 +6,7 @@ Shaon Barman Vivien Nicolas <21@vingtetun.org> Justin D'Arcangelo + Yury Delendik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), From d4a42a4e79dc920efea07057b0c21281a5ccb162 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 18 Jun 2011 22:04:36 -0700 Subject: [PATCH 3/7] mozMeasureText() is non-standard and deprecated --- fonts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fonts.js b/fonts.js index 7978cd60d..6c643ae40 100644 --- a/fonts.js +++ b/fonts.js @@ -203,7 +203,7 @@ Font.prototype = { } } ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; - var textWidth = ctx.mozMeasureText(testString); + var textWidth = ctx.measureText(testString).width; if (debug) ctx.fillText(testString, 20, 20); @@ -218,7 +218,7 @@ Font.prototype = { window.clearInterval(interval); Fonts[fontName].loading = false; warn("Is " + fontName + " for charset: " + charset + " loaded?"); - } else if (textWidth != ctx.mozMeasureText(testString)) { + } else if (textWidth != ctx.measureText(testString).width) { window.clearInterval(interval); Fonts[fontName].loading = false; } From 92fa629d107cf4de1cb486366d783e890e153306 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 18 Jun 2011 23:18:46 -0700 Subject: [PATCH 4/7] use strict mode and fix violators --- fonts.js | 5 ++++- glyphlist.js | 5 +++++ multi-page-viewer.js | 2 ++ pdf.js | 18 +++++++++++------- viewer.js | 8 +++++--- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/fonts.js b/fonts.js index 6c643ae40..0c8725fb4 100644 --- a/fonts.js +++ b/fonts.js @@ -1,6 +1,8 @@ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +"use strict"; + /** * Maximum file size of the font. */ @@ -1042,7 +1044,8 @@ var Type1Parser = function() { this.extractFontProgram = function t1_extractFontProgram(aStream) { var eexecString = decrypt(aStream, kEexecEncryptionKey, 4); var subrs = [], glyphs = []; - var inSubrs = inGlyphs = false; + var inGlyphs = false; + var inSubrs = false; var glyph = ""; var token = ""; diff --git a/glyphlist.js b/glyphlist.js index 1a0190133..72a90431f 100644 --- a/glyphlist.js +++ b/glyphlist.js @@ -1,3 +1,8 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +"use strict"; + var GlyphsUnicode = { A: 0x0041, AE: 0x00C6, diff --git a/multi-page-viewer.js b/multi-page-viewer.js index cc4a286ff..2410eb7bf 100644 --- a/multi-page-viewer.js +++ b/multi-page-viewer.js @@ -1,6 +1,8 @@ /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ +"use strict"; + var PDFViewer = { queryParams: {}, diff --git a/pdf.js b/pdf.js index 993b5063d..a051e57f3 100644 --- a/pdf.js +++ b/pdf.js @@ -1,6 +1,8 @@ /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ +"use strict"; + var ERRORS = 0, WARNINGS = 1, TODOS = 5; var verbosity = WARNINGS; @@ -389,6 +391,12 @@ var FlateStream = (function() { return [codes, maxLen]; }, readBlock: function() { + function repeat(stream, array, len, offset, what) { + var repeat = stream.getBits(len) + offset; + while (repeat-- > 0) + array[i++] = what; + } + var stream = this.stream; // read block header @@ -449,11 +457,6 @@ var FlateStream = (function() { var codes = numLitCodes + numDistCodes; var codeLengths = new Array(codes); while (i < codes) { - function repeat(stream, array, len, offset, what) { - var repeat = stream.getBits(len) + offset; - while (repeat-- > 0) - array[i++] = what; - } var code = this.getCode(codeLenCodeTab); if (code == 16) { repeat(this, codeLengths, 2, 3, len); @@ -813,6 +816,7 @@ var Lexer = (function() { var done = false; var str = ""; var stream = this.stream; + var ch; do { switch (ch = stream.getChar()) { case undefined: @@ -1573,7 +1577,7 @@ var Catalog = (function() { return shadow(this, "toplevelPagesDict", obj); }, get numPages() { - obj = this.toplevelPagesDict.get("Count"); + var obj = this.toplevelPagesDict.get("Count"); assertWellFormed(IsInt(obj), "page count in top level pages object is not an integer"); // shadow the prototype getter @@ -2527,7 +2531,7 @@ var CanvasGraphics = (function() { error("No support for array of functions"); else if (!IsPDFFunction(fnObj)) error("Invalid function"); - fn = new PDFFunction(this.xref, fnObj); + var fn = new PDFFunction(this.xref, fnObj); var gradient = this.ctx.createLinearGradient(x0, y0, x1, y1); var step = (t1 - t0) / 10; diff --git a/viewer.js b/viewer.js index 59d8167a2..675f2fb87 100644 --- a/viewer.js +++ b/viewer.js @@ -1,12 +1,14 @@ /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ -var pdfDocument, canvas, pageDisplay, pageNum, pageInterval; +"use strict"; + +var pdfDocument, canvas, numPages, pageDisplay, pageNum, pageInterval; function load(userInput) { canvas = document.getElementById("canvas"); canvas.mozOpaque = true; pageNum = parseInt(queryParams().page) || 1; - fileName = userInput; + var fileName = userInput; if (!userInput) { fileName = queryParams().file || "compressed.tracemonkey-pldi-09.pdf"; } @@ -26,7 +28,7 @@ function queryParams() { function open(url) { document.title = url; - req = new XMLHttpRequest(); + var req = new XMLHttpRequest(); req.open("GET", url); req.mozResponseType = req.responseType = "arraybuffer"; req.expected = (document.URL.indexOf("file:") == 0) ? 0 : 200; From 8c802e5025bb8b67c574540f98fb4850b01c0714 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 18 Jun 2011 23:21:30 -0700 Subject: [PATCH 5/7] use strict in the utils/ too --- utils/cffStandardStrings.js | 11 ++++++++--- utils/fonts_utils.js | 5 +++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/utils/cffStandardStrings.js b/utils/cffStandardStrings.js index 8977cd8f2..1b328a2da 100644 --- a/utils/cffStandardStrings.js +++ b/utils/cffStandardStrings.js @@ -1,5 +1,10 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +"use strict"; + var CFFStrings = [ - ".notdef", + ".notdef", "space", "exclam", "quotedbl", @@ -490,7 +495,7 @@ var CFFDictDataMap = { }, "10": { name: "StdHW" - }, + }, "11": { name: "StdVW" }, @@ -597,7 +602,7 @@ var CFFDictDataMap = { }, "18": { name: "Private", - operand: "number number" + operand: "number number" }, "19": { name: "Subrs" diff --git a/utils/fonts_utils.js b/utils/fonts_utils.js index 086648fe2..79ecf257f 100644 --- a/utils/fonts_utils.js +++ b/utils/fonts_utils.js @@ -1,3 +1,8 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +"use strict"; + /** * The Type2 reader code below is only used for debugging purpose since Type2 * is only a CharString format and is never used directly as a Font file. From 8935715d968bc0377c1b4509757713dc16a20d6a Mon Sep 17 00:00:00 2001 From: Justin D'Arcangelo Date: Sun, 19 Jun 2011 14:54:13 -0400 Subject: [PATCH 6/7] 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 50f3dd72a117cfe8ba825b14710bfcb892c7b227 Mon Sep 17 00:00:00 2001 From: Justin D'Arcangelo Date: Sun, 19 Jun 2011 14:57:45 -0400 Subject: [PATCH 7/7] 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 {