diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index a313766f4..76b7788a8 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -32,6 +32,7 @@ const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html'; const MAX_DATABASE_LENGTH = 4096; const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}'; const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}'; +const METRO_ID = '{99bceaaa-e3c6-48c1-b981-ef9b46b67d60}'; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); @@ -50,7 +51,8 @@ if (appInfo.ID === FIREFOX_ID) { privateBrowsing = Cc['@mozilla.org/privatebrowsing;1'] .getService(Ci.nsIPrivateBrowsingService); inPrivateBrowsing = privateBrowsing.privateBrowsingEnabled; -} else if (appInfo.ID === SEAMONKEY_ID) { +} else if (appInfo.ID === SEAMONKEY_ID || + appInfo.ID === METRO_ID) { privateBrowsing = null; inPrivateBrowsing = false; } diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties index 2e16898c0..80f9cd925 100644 --- a/l10n/en-US/viewer.properties +++ b/l10n/en-US/viewer.properties @@ -51,8 +51,8 @@ thumb_page_title=Page {{page}} thumb_page_canvas=Thumbnail of Page {{page}} # Context menu -page_rotate_cw=Rotate Clockwise -page_rotate_ccw=Rotate Counter-Clockwise +page_rotate_cw.label=Rotate Clockwise +page_rotate_ccw.label=Rotate Counter-Clockwise # Search panel button title and messages search=Find diff --git a/l10n/ja/viewer.properties b/l10n/ja/viewer.properties index c8bfde461..33f00ac4c 100644 --- a/l10n/ja/viewer.properties +++ b/l10n/ja/viewer.properties @@ -50,6 +50,10 @@ thumb_page_title={{page}} ページ # number. thumb_page_canvas=ページの縮小版 {{page}} +# Context menu +page_rotate_cw.label=右回転 +page_rotate_ccw.label=左回転 + # Search panel button title and messages search=検索 search_terms_not_found=(見つかりませんでした) diff --git a/l10n/ru/viewer.properties b/l10n/ru/viewer.properties index 796add2af..f30c670af 100644 --- a/l10n/ru/viewer.properties +++ b/l10n/ru/viewer.properties @@ -43,3 +43,7 @@ zoom.title=Масштаб thumb_page_title=Страница {{page}} thumb_page_canvas=Уменьшенное изображение страницы {{page}} request_password=PDF защищён паролем: +fullscreen.title=Полный экран +fullscreen_label=Полный экран +page_rotate_cw.label=Повернуть по часовой стрелке +page_rotate_ccw.label=Повернуть против часовой стрелки diff --git a/l10n/zh-TW/chrome.properties b/l10n/zh-TW/chrome.properties index 6a668cf55..6fc1ede45 100644 --- a/l10n/zh-TW/chrome.properties +++ b/l10n/zh-TW/chrome.properties @@ -1,3 +1,4 @@ -# Chrome 通知欄的訊息及按鍵 -unsupported_feature=本 PDF 文件可能無法正常顯示。 -open_with_different_viewer=使用其他檢視器打開文件 +# Chrome 通知欄的訊息及按鍵 +unsupported_feature=本 PDF 文件可能無法正常顯示。 +open_with_different_viewer=使用其他檢視器打開文件 +open_with_different_viewer.accessKey=o \ No newline at end of file diff --git a/l10n/zh-TW/viewer.properties b/l10n/zh-TW/viewer.properties index 84fb5ec9e..b7ff63235 100644 --- a/l10n/zh-TW/viewer.properties +++ b/l10n/zh-TW/viewer.properties @@ -1,83 +1,89 @@ -# 主工具列按鍵 (工具提示和圖像的替代文字) -previous.title=上一頁 -previous_label=上一頁 -next.title=下一頁 -next_label=下一頁 - -# 本地化提示 (page_label, page_of): -# 這些字符串會連接成 "Page: X of Y" 的表示方式。 -# 不要翻譯 "{{pageCount}}" , 因為它用來表示總頁數。 -page_label=第 -page_of=頁, 共 {{pageCount}} 頁 - -zoom_out.title=縮小 -zoom_out_label=縮小 -zoom_in.title=放大 -zoom_in_label=放大 -zoom.title=縮放 -print.title=列印 -print_label=列印 -open_file.title=開啟檔案 -open_file_label=開啟 -download.title=下載 -download_label=下載 -bookmark.title=目前檢視(複製或在新視窗中開啟) -bookmark_label=目前檢視 - -# 側邊欄工具列按鍵 (工具提示和圖像的替代文字) -# (_label 字符串是按鍵的替代文字, .title 字符串是工具提示) -toggle_slider.title=切換側邊欄 -toggle_slider_label=切換側邊欄 -outline.title=顯示文件綱要 -outline_label=文件綱要 -thumbs.title=顯示縮圖 -thumbs_label=縮圖 -search_panel.title=搜索文件 -search_panel_label=搜索 - -# 文件綱要相關訊息 -no_outline=無可用的綱要 - -# 縮圖面板項目 (工具提示和圖像的替代文字) -# 本地化提示 (thumb_page_title): "{{page}}" 會被頁數取代。 -thumb_page_title=第 {{page}} 頁 -# 本地化提示 (thumb_page_canvas): "{{page}}" 會被頁數取代。 -thumb_page_canvas=第 {{page}} 頁的縮圖 - -# 搜尋面板按鍵文字及訊息 -search=搜索 -search_terms_not_found=(沒有找到) - -# 錯誤面板標籤 -error_more_info=更多資訊 -error_less_info=更少資訊 -error_close=關閉 -# 本地化提示 (error_build): "{{build}}" 會被PDF.JS版本號取代。 -error_build=PDF.JS 版本號: {{build}} -# 本地化提示 (error_message): "{{message}}" 會被英文的錯誤描述取代。 -error_message=錯誤信息: {{message}} -# 本地化提示 (error_stack): "{{stack}}" 會被錯誤堆疊取代。 -error_stack=堆疊: {{stack}} -# 本地化提示 (error_file): "{{file}}" 會被檔案名稱取代。 -error_file=檔案: {{file}} -# 本地化提示 (error_line): "{{line}}" 會被行數取代。 -error_line=行數: {{line}} -rendering_error=渲染頁面時發生錯誤。 - -# 預設的縮放值 -page_scale_width=符合頁寬 -page_scale_fit=符合頁面 -page_scale_auto=自動縮放 -page_scale_actual=實際大小 - -# 載入指示訊息 -# 本地化提示 (error_line): "{{percent}}" 會被百分比取代。 -loading=正在載入... {{percent}}% -loading_error_indicator=錯誤 -loading_error=載入PDF檔案時發生錯誤。 - -# 其他標籤和訊息 -# "{{type}}" 用來表示PDF格式規範 (32000-1:2008 Table 169 – Annotation types) 入面所定義的註解種類。 -# 一些常見的類型有: "Check"、 "Text"、 "Comment"、 "Note" -text_annotation_type=[{{type}} 註解] -request_password=PDF檔案受密碼保護: +# 主工具列按鍵 (工具提示和圖像的替代文字) +previous.title=上一頁 +previous_label=上一頁 +next.title=下一頁 +next_label=下一頁 + +# 本地化提示 (page_label, page_of): +# 這些字符串會連接成 "Page: X of Y" 的表示方式。 +# 不要翻譯 "{{pageCount}}" , 因為它用來表示總頁數。 +page_label=第 +page_of=頁,共 {{pageCount}} 頁 + +zoom_out.title=縮小 +zoom_out_label=縮小 +zoom_in.title=放大 +zoom_in_label=放大 +zoom.title=縮放 +print.title=列印 +print_label=列印 +fullscreen.title=全螢幕 +fullscreen_label=全螢幕 +open_file.title=開啟檔案 +open_file_label=開啟 +download.title=下載 +download_label=下載 +bookmark.title=目前檢視(複製或在新視窗中開啟) +bookmark_label=目前檢視 + +# 側邊欄工具列按鍵 (工具提示和圖像的替代文字) +# (_label 字符串是按鍵的替代文字, .title 字符串是工具提示) +toggle_slider.title=切換側邊欄 +toggle_slider_label=切換側邊欄 +outline.title=顯示文件綱要 +outline_label=文件綱要 +thumbs.title=顯示縮圖 +thumbs_label=縮圖 +search_panel.title=搜索文件 +search_panel_label=搜索 + +# 文件綱要相關訊息 +no_outline=無可用的綱要 + +# 縮圖面板項目 (工具提示和圖像的替代文字) +# 本地化提示 (thumb_page_title): "{{page}}" 會被頁數取代。 +thumb_page_title=第 {{page}} 頁 +# 本地化提示 (thumb_page_canvas): "{{page}}" 會被頁數取代。 +thumb_page_canvas=第 {{page}} 頁的縮圖 + +# 右鍵菜單 +page_rotate_cw.label=順時針旋轉 +page_rotate_ccw.label=逆時針旋轉 + +# 搜尋面板按鍵文字及訊息 +search=搜索 +search_terms_not_found=(沒有找到) + +# 錯誤面板標籤 +error_more_info=更多資訊 +error_less_info=更少資訊 +error_close=關閉 +# 本地化提示 (error_build): "{{build}}" 會被PDF.JS版本號取代。 +error_build=PDF.JS 版本號: {{build}} +# 本地化提示 (error_message): "{{message}}" 會被英文的錯誤描述取代。 +error_message=錯誤信息: {{message}} +# 本地化提示 (error_stack): "{{stack}}" 會被錯誤堆疊取代。 +error_stack=堆疊: {{stack}} +# 本地化提示 (error_file): "{{file}}" 會被檔案名稱取代。 +error_file=檔案: {{file}} +# 本地化提示 (error_line): "{{line}}" 會被行數取代。 +error_line=行數: {{line}} +rendering_error=渲染頁面時發生錯誤。 + +# 預設的縮放值 +page_scale_width=符合頁寬 +page_scale_fit=符合頁面 +page_scale_auto=自動縮放 +page_scale_actual=實際大小 + +# 載入指示訊息 +loading_error_indicator=錯誤 +loading_error=載入PDF檔案時發生錯誤。 + +# 其他標籤和訊息 +# "{{type}}" 用來表示PDF格式規範 (32000-1:2008 Table 169 – Annotation types) 入面所定義的註解種類。 +# 一些常見的類型有: "Check"、 "Text"、 "Comment"、 "Note" +text_annotation_type=[{{type}} 註解] +request_password=PDF檔案受密碼保護: + +printing_not_supported=警告:這個瀏覽器不完全支援列印。 diff --git a/src/api.js b/src/api.js index d6d13a095..169faea0d 100644 --- a/src/api.js +++ b/src/api.js @@ -575,24 +575,15 @@ var WorkerTransport = (function WorkerTransportClosure() { this.objs.resolve(id, imageData); break; case 'Font': - var name = data[2]; - var file = data[3]; - var properties = data[4]; - - if (file) { - // Rewrap the ArrayBuffer in a stream. - var fontFileDict = new Dict(); - file = new Stream(file, 0, file.length, fontFileDict); - } + var exportedData = data[2]; // At this point, only the font object is created but the font is // not yet attached to the DOM. This is done in `FontLoader.bind`. var font; - try { - font = new Font(name, file, properties); - } catch (e) { - font = new ErrorFont(e); - } + if ('error' in exportedData) + font = new ErrorFont(exportedData.error); + else + font = new Font(exportedData); this.objs.resolve(id, font); break; default: diff --git a/src/canvas.js b/src/canvas.js index e5a204cb7..27f4e4858 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -596,8 +596,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { (fontObj.bold ? 'bold' : 'normal'); var italic = fontObj.italic ? 'italic' : 'normal'; - var serif = fontObj.isSerifFont ? 'serif' : 'sans-serif'; - var typeface = '"' + name + '", ' + serif; + var typeface = '"' + name + '", ' + fontObj.fallbackName; // Some font backends cannot handle fonts below certain size. // Keeping the font at minimal size and using the fontSizeScale to change @@ -782,15 +781,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { x += charWidth; var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode; - var glyphUnicodeLength = glyphUnicode.length; - //reverse an arabic ligature - if (glyphUnicodeLength > 1 && - isRTLRangeFor(glyphUnicode.charCodeAt(0))) { - for (var ii = glyphUnicodeLength - 1; ii >= 0; ii--) - text.str += glyphUnicode[ii]; - } else - text.str += glyphUnicode; - text.length += glyphUnicodeLength; + if (glyphUnicode in NormalizedUnicodes) + glyphUnicode = NormalizedUnicodes[glyphUnicode]; + text.str += reverseIfRtl(glyphUnicode); text.canvasWidth += charWidth; } current.x += x * textHScale2; @@ -798,7 +791,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } if (textSelection) - this.textLayer.appendText(text, font.loadedName, fontSize); + this.textLayer.appendText(text, font.fallbackName, fontSize); return text; }, @@ -842,7 +835,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var numFakeSpaces = Math.round(-e / text.geom.spaceWidth); if (numFakeSpaces > 0) { text.str += '\u00A0'; - text.length++; } } } @@ -856,7 +848,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { text.str += shownText.str; } text.canvasWidth += shownText.canvasWidth; - text.length += shownText.length; } } else { error('TJ array element ' + e + ' is not string or num'); @@ -864,7 +855,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } if (textSelection) - this.textLayer.appendText(text, font.loadedName, fontSize); + this.textLayer.appendText(text, font.fallbackName, fontSize); }, nextLineShowText: function CanvasGraphics_nextLineShowText(text) { this.nextLine(); diff --git a/src/evaluator.js b/src/evaluator.js index 621dd613f..f0e775cdb 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -26,6 +26,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { this.handler = handler; this.uniquePrefix = uniquePrefix; this.objIdCounter = 0; + this.fontIdCounter = 0; } var OP_MAP = { @@ -138,6 +139,35 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }; PartialEvaluator.prototype = { + loadFont: function PartialEvaluator_loadFont(fontName, font, xref, + resources, dependency) { + var fontRes = resources.get('Font'); + + assert(fontRes, 'fontRes not available'); + + font = xref.fetchIfRef(font) || fontRes.get(fontName); + assertWellFormed(isDict(font)); + + ++this.fontIdCounter; + var loadedName = font.loadedName; + if (!loadedName) { + // keep track of each font we translated so the caller can + // load them asynchronously before calling display on a page + loadedName = 'font_' + this.uniquePrefix + this.fontIdCounter; + font.loadedName = loadedName; + + var translated; + try { + translated = this.translateFont(font, xref, resources, + dependency); + } catch (e) { + translated = { error: e }; + } + font.translated = translated; + } + return font; + }, + getOperatorList: function PartialEvaluator_getOperatorList(stream, resources, dependency, @@ -160,46 +190,35 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } function handleSetFont(fontName, font) { - var loadedName = null; - - var fontRes = resources.get('Font'); - - assert(fontRes, 'fontRes not available'); - - font = xref.fetchIfRef(font) || fontRes.get(fontName); - assertWellFormed(isDict(font)); - - ++self.objIdCounter; - if (!font.loadedName) { - font.translated = self.translateFont(font, xref, resources, - dependency); - if (font.translated) { - // keep track of each font we translated so the caller can - // load them asynchronously before calling display on a page - loadedName = 'font_' + uniquePrefix + self.objIdCounter; - font.translated.properties.loadedName = loadedName; - font.loadedName = loadedName; - - var translated = font.translated; - // Convert the file to an ArrayBuffer which will be turned back into - // a Stream in the main thread. - if (translated.file) - translated.file = translated.file.getBytes(); - if (translated.properties.file) { - translated.properties.file = - translated.properties.file.getBytes(); + font = self.loadFont(fontName, font, xref, resources, dependency); + + var loadedName = font.loadedName; + if (!font.sent) { + var data = font.translated; + if (data.loadCharProcs) { + delete data.loadCharProcs; + + var charProcs = font.get('CharProcs').getAll(); + var fontResources = font.get('Resources') || resources; + var charProcOperatorList = {}; + for (var key in charProcs) { + var glyphStream = charProcs[key]; + charProcOperatorList[key] = + self.getOperatorList(glyphStream, fontResources, dependency); } - - handler.send('obj', [ - loadedName, - 'Font', - translated.name, - translated.file, - translated.properties - ]); + data.charProcOperatorList = charProcOperatorList; } + + if (data instanceof Font) + data = data.export(); + + handler.send('obj', [ + loadedName, + 'Font', + data + ]); + font.sent = true; } - loadedName = loadedName || font.loadedName; // Ensure the font is ready before the font is set // and later on used for drawing. @@ -492,20 +511,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var xref = this.xref; function handleSetFont(fontName, fontRef) { - var fontRes = resources.get('Font'); - - // TODO: TOASK: Is it possible to get here? If so, what does - // args[0].name should be like??? - assert(fontRes, 'fontRes not available'); - - fontRes = xref.fetchIfRef(fontRes); - fontRef = fontRef || fontRes.get(fontName); - var font = xref.fetchIfRef(fontRef), tra; - assertWellFormed(isDict(font)); - if (!font.translated) { - font.translated = self.translateFont(font, xref, resources); - } - return font; + return self.loadFont(fontName, fontRef, xref, resources, null); } resources = xref.fetchIfRef(resources) || new Dict(); @@ -522,13 +528,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var cmd = obj.cmd; switch (cmd) { case 'Tf': - font = handleSetFont(args[0].name); + font = handleSetFont(args[0].name).translated; break; case 'TJ': var items = args[0]; for (var j = 0, jj = items.length; j < jj; j++) { if (typeof items[j] === 'string') { - chunk += items[j]; + chunk += fontCharsToUnicode(items[j], font); } else if (items[j] < 0) { // making all negative offsets a space - better to have // a space in incorrect place than not have them at all @@ -537,17 +543,17 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } break; case 'Tj': - chunk += args[0]; + chunk += fontCharsToUnicode(args[0], font); break; case "'": - chunk += args[0] + ' '; + chunk += fontCharsToUnicode(args[0], font) + ' '; break; case '"': - chunk += args[2] + ' '; + chunk += fontCharsToUnicode(args[2], font) + ' '; break; } // switch if (chunk !== '') { - text += fontCharsToUnicode(chunk, font.translated.properties); + text += chunk; chunk = ''; } @@ -819,21 +825,41 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } } + // Heuristic: detection of monospace font by checking all non-zero widths + var isMonospace = true, firstWidth = defaultWidth; + for (var glyph in glyphsWidths) { + var glyphWidth = glyphsWidths[glyph]; + if (!glyphWidth) + continue; + if (!firstWidth) { + firstWidth = glyphWidth; + continue; + } + if (firstWidth != glyphWidth) { + isMonospace = false; + break; + } + } + if (isMonospace) + properties.flags |= FontFlags.FixedPitch; + properties.defaultWidth = defaultWidth; properties.widths = glyphsWidths; }, getBaseFontMetrics: function PartialEvaluator_getBaseFontMetrics(name) { - var defaultWidth = 0, widths = []; + var defaultWidth = 0, widths = [], monospace = false; var glyphWidths = Metrics[stdFontMap[name] || name]; if (isNum(glyphWidths)) { defaultWidth = glyphWidths; + monospace = true; } else { widths = glyphWidths; } return { defaultWidth: defaultWidth, + monospace: monospace, widths: widths }; }, @@ -854,7 +880,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // - get the FontDescriptor from the descendant font var df = dict.get('DescendantFonts'); if (!df) - return null; + error('Descendant fonts are not specified'); dict = isArray(df) ? xref.fetchIfRef(df[0]) : df; @@ -877,7 +903,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // This case is here for compatibility. var baseFontName = dict.get('BaseFont'); if (!isName(baseFontName)) - return null; + error('Base font is not specified'); // Using base font name as a font name. baseFontName = baseFontName.name.replace(/[,_]/g, '-'); @@ -887,6 +913,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var fontNameWoStyle = baseFontName.split('-')[0]; var flags = (serifFonts[fontNameWoStyle] || (fontNameWoStyle.search(/serif/gi) != -1) ? FontFlags.Serif : 0) | + (metrics.monospace ? FontFlags.FixedPitch : 0) | (symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic : FontFlags.Nonsymbolic); @@ -900,11 +927,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }; this.extractDataStructures(dict, dict, xref, properties); - return { - name: baseFontName, - dict: baseDict, - properties: properties - }; + return new Font(baseFontName, null, properties); } } @@ -940,6 +963,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { file: fontFile, length1: length1, length2: length2, + loadedName: baseDict.loadedName, composite: composite, wideChars: composite, fixedPitch: false, @@ -960,22 +984,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (type.name === 'Type3') { properties.coded = true; - var charProcs = dict.get('CharProcs').getAll(); - var fontResources = dict.get('Resources') || resources; - properties.charProcOperatorList = {}; - for (var key in charProcs) { - var glyphStream = charProcs[key]; - properties.charProcOperatorList[key] = - this.getOperatorList(glyphStream, fontResources, dependency); - } } - return { - name: fontName.name, - dict: baseDict, - file: fontFile, - properties: properties - }; + return new Font(fontName.name, fontFile, properties); } }; diff --git a/src/fonts.js b/src/fonts.js index 15af2fa39..e344bbf4b 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -418,11 +418,26 @@ function mapPrivateUseChars(code) { } var FontLoader = { +//#if !(MOZCENTRAL) loadingContext: { requests: [], nextRequestId: 0 }, + isSyncFontLoadingSupported: (function detectSyncFontLoadingSupport() { + if (isWorker) + return false; + + // User agent string sniffing is bad, but there is no reliable way to tell + // if font is fully loaded and ready to be used with canvas. + var userAgent = window.navigator.userAgent; + var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(userAgent); + if (m && m[1] >= 14) + return true; + // TODO other browsers + return false; + })(), + bind: function fontLoaderBind(fonts, callback) { assert(!isWorker, 'bind() shall be called from main thread'); @@ -437,23 +452,15 @@ var FontLoader = { } font.attached = true; - var str = ''; - var data = font.data; - if (data) { - var length = data.length; - for (var j = 0; j < length; j++) - str += String.fromCharCode(data[j]); - - var rule = font.bindDOM(str); - if (rule) { - rules.push(rule); - fontsToLoad.push(font); - } + var rule = font.bindDOM(); + if (rule) { + rules.push(rule); + fontsToLoad.push(font); } } var request = FontLoader.queueLoadingCallback(callback); - if (rules.length > 0) { + if (rules.length > 0 && !this.isSyncFontLoadingSupported) { FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request); } else { request.complete(); @@ -595,6 +602,22 @@ var FontLoader = { document.body.appendChild(frame); /** Hack end */ } +//#else +//bind: function fontLoaderBind(fonts, callback) { +// assert(!isWorker, 'bind() shall be called from main thread'); +// +// for (var i = 0, ii = fonts.length; i < ii; i++) { +// var font = fonts[i]; +// if (font.attached) +// continue; +// +// font.attached = true; +// font.bindDOM() +// } +// +// setTimeout(callback); +//} +//#endif }; var UnicodeRanges = [ @@ -1464,54 +1487,30 @@ var NormalizedUnicodes = { '\uFE4F': '\u005F' }; -function fontCharsToUnicode(charCodes, fontProperties) { - var toUnicode = fontProperties.toUnicode; - var composite = fontProperties.composite; - var encoding, differences, cidToUnicode; - var result = ''; - if (composite) { - cidToUnicode = fontProperties.cidToUnicode; - for (var i = 0, ii = charCodes.length; i < ii; i += 2) { - var charCode = (charCodes.charCodeAt(i) << 8) | - charCodes.charCodeAt(i + 1); - if (toUnicode && charCode in toUnicode) { - var unicode = toUnicode[charCode]; - result += typeof unicode !== 'number' ? unicode : - String.fromCharCode(unicode); - continue; - } - result += String.fromCharCode(!cidToUnicode ? charCode : - cidToUnicode[charCode] || charCode); - } - } else { - differences = fontProperties.differences; - encoding = fontProperties.baseEncoding; - for (var i = 0, ii = charCodes.length; i < ii; i++) { - var charCode = charCodes.charCodeAt(i); - var unicode; - if (toUnicode && charCode in toUnicode) { - var unicode = toUnicode[charCode]; - result += typeof unicode !== 'number' ? unicode : - String.fromCharCode(unicode); - continue; - } +function reverseIfRtl(chars) { + var charsLength = chars.length; + //reverse an arabic ligature + if (charsLength <= 1 || !isRTLRangeFor(chars.charCodeAt(0))) + return chars; - var glyphName = charCode in differences ? differences[charCode] : - encoding[charCode]; - if (glyphName in GlyphsUnicode) { - result += String.fromCharCode(GlyphsUnicode[glyphName]); - continue; - } - result += String.fromCharCode(charCode); - } - } - // normalizing the unicode characters - for (var i = 0, ii = result.length; i < ii; i++) { - if (!(result[i] in NormalizedUnicodes)) + var s = ''; + for (var ii = charsLength - 1; ii >= 0; ii--) + s += chars[ii]; + return s; +} + +function fontCharsToUnicode(charCodes, font) { + var glyphs = font.charsToGlyphs(charCodes); + var result = ''; + for (var i = 0, ii = glyphs.length; i < ii; i++) { + var glyph = glyphs[i]; + if (!glyph) continue; - result = result.substring(0, i) + NormalizedUnicodes[result[i]] + - result.substring(i + 1); - ii = result.length; + + var glyphUnicode = glyph.unicode; + if (glyphUnicode in NormalizedUnicodes) + glyphUnicode = NormalizedUnicodes[glyphUnicode]; + result += reverseIfRtl(glyphUnicode); } return result; } @@ -1526,9 +1525,19 @@ function fontCharsToUnicode(charCodes, fontProperties) { */ var Font = (function FontClosure() { function Font(name, file, properties) { + if (arguments.length === 1) { + // importing translated data + var data = arguments[0]; + for (var i in data) { + this[i] = data[i]; + } + return; + } + this.name = name; + this.loadedName = properties.loadedName; this.coded = properties.coded; - this.charProcOperatorList = properties.charProcOperatorList; + this.loadCharProcs = properties.coded; this.sizes = []; var names = name.split('+'); @@ -1536,17 +1545,13 @@ var Font = (function FontClosure() { names = names.split(/[-,_]/g)[0]; this.isSerifFont = !!(properties.flags & FontFlags.Serif); this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic); + this.isMonospace = !!(properties.flags & FontFlags.FixedPitch); var type = properties.type; this.type = type; - // If the font is to be ignored, register it like an already loaded font - // to avoid the cost of waiting for it be be loaded by the platform. - if (properties.ignore) { - this.loadedName = this.isSerifFont ? 'serif' : 'sans-serif'; - this.loading = false; - return; - } + this.fallbackName = this.isMonospace ? 'monospace' : + this.isSerifFont ? 'serif' : 'sans-serif'; this.differences = properties.differences; this.widths = properties.widths; @@ -1632,7 +1637,6 @@ var Font = (function FontClosure() { this.widthMultiplier = !properties.fontMatrix ? 1.0 : 1.0 / properties.fontMatrix[0]; this.encoding = properties.baseEncoding; - this.loadedName = properties.loadedName; this.loading = true; }; @@ -2036,6 +2040,15 @@ var Font = (function FontClosure() { mimetype: null, encoding: null, + export: function Font_export() { + var data = {}; + for (var i in this) { + if (this.hasOwnProperty(i)) + data[i] = this[i]; + } + return data; + }, + checkAndRepair: function Font_checkAndRepair(name, font, properties) { function readTableEntry(file) { var tag = file.getBytes(4); @@ -3138,7 +3151,11 @@ var Font = (function FontClosure() { } }, - bindDOM: function Font_bindDOM(data) { + bindDOM: function Font_bindDOM() { + if (!this.data) + return null; + + var data = bytesToString(this.data); var fontName = this.loadedName; // Add the font-face rule to the document diff --git a/test/driver.js b/test/driver.js index 54179f6cb..998527807 100644 --- a/test/driver.js +++ b/test/driver.js @@ -26,7 +26,7 @@ // "firefox-bin: Fatal IO error 12 (Cannot allocate memory) on X server :1." // PDFJS.disableWorker = true; -var appPath, browser, canvas, currentTaskIdx, manifest, stdout; +var appPath, browser, canvas, dummyCanvas, currentTaskIdx, manifest, stdout; var inFlightRequests = 0; function queryParams() { @@ -148,6 +148,46 @@ function canvasToDataURL() { return canvas.toDataURL('image/png'); } +function NullTextLayerBuilder() { +} +NullTextLayerBuilder.prototype = { + beginLayout: function NullTextLayerBuilder_BeginLayout() {}, + endLayout: function NullTextLayerBuilder_EndLayout() {}, + appendText: function NullTextLayerBuilder_AppendText() {} +}; + +function SimpleTextLayerBuilder(ctx, viewport) { + this.ctx = ctx; + this.viewport = viewport; +} +SimpleTextLayerBuilder.prototype = { + beginLayout: function SimpleTextLayerBuilder_BeginLayout() { + this.ctx.save(); + }, + endLayout: function SimpleTextLayerBuilder_EndLayout() { + this.ctx.restore(); + }, + appendText: function SimpleTextLayerBuilder_AppendText(text, fontName, + fontSize) { + var ctx = this.ctx, viewport = this.viewport; + // vScale and hScale already contain the scaling to pixel units + var fontHeight = fontSize * text.geom.vScale; + ctx.beginPath(); + ctx.strokeStyle = 'red'; + ctx.fillStyle = 'yellow'; + ctx.rect(text.geom.x, text.geom.y - fontHeight, + text.canvasWidth * text.geom.hScale, fontHeight); + ctx.stroke(); + ctx.fill(); + + var textContent = bidi(text, -1); + ctx.font = fontHeight + 'px ' + fontName; + ctx.fillStyle = 'black'; + ctx.fillText(textContent, text.geom.x, text.geom.y); + } +}; + + function nextPage(task, loadError) { var failure = loadError || ''; @@ -196,16 +236,21 @@ function nextPage(task, loadError) { canvas.height = viewport.height; clear(ctx); - // using the text layer builder that does nothing to test - // text layer creation operations - var textLayerBuilder = { - beginLayout: function nullTextLayerBuilderBeginLayout() {}, - endLayout: function nullTextLayerBuilderEndLayout() {}, - appendText: function nullTextLayerBuilderAppendText(text, fontName, - fontSize) {} - }; + var drawContext, textLayerBuilder; + if (task.type == 'text') { + // using dummy canvas for pdf context drawing operations + if (!dummyCanvas) { + dummyCanvas = document.createElement('canvas'); + } + drawContext = dummyCanvas.getContext('2d'); + // ... text builder will draw its content on the test canvas + textLayerBuilder = new SimpleTextLayerBuilder(ctx, viewport); + } else { + drawContext = ctx; + textLayerBuilder = new NullTextLayerBuilder(); + } var renderContext = { - canvasContext: ctx, + canvasContext: drawContext, textLayer: textLayerBuilder, viewport: viewport }; diff --git a/test/test.py b/test/test.py index b5e3241b9..0e62aa9fe 100644 --- a/test/test.py +++ b/test/test.py @@ -514,7 +514,7 @@ def check(task, results, browser, masterMode): return kind = task['type'] - if 'eq' == kind: + if 'eq' == kind or 'text' == kind: checkEq(task, results, browser, masterMode) elif 'fbf' == kind: checkFBF(task, results, browser) @@ -528,6 +528,7 @@ def checkEq(task, results, browser, masterMode): pfx = os.path.join(REFDIR, sys.platform, browser, task['id']) results = results[0] taskId = task['id'] + taskType = task['type'] passed = True for page in xrange(len(results)): @@ -547,7 +548,7 @@ def checkEq(task, results, browser, masterMode): eq = (ref == snapshot) if not eq: - print 'TEST-UNEXPECTED-FAIL | eq', taskId, '| in', browser, '| rendering of page', page + 1, '!= reference rendering' + print 'TEST-UNEXPECTED-FAIL | ', taskType, taskId, '| in', browser, '| rendering of page', page + 1, '!= reference rendering' if not State.eqLog: State.eqLog = open(EQLOG_FILE, 'w') @@ -576,7 +577,7 @@ def checkEq(task, results, browser, masterMode): of.close() if passed: - print 'TEST-PASS | eq test', task['id'], '| in', browser + print 'TEST-PASS | ', taskType, ' test', task['id'], '| in', browser def checkFBF(task, results, browser): round0, round1 = results[0], results[1] diff --git a/test/test_manifest.json b/test/test_manifest.json index d19633947..e8cac9a20 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -11,6 +11,12 @@ "rounds": 2, "type": "fbf" }, + { "id": "tracemonkey-text", + "file": "pdfs/tracemonkey.pdf", + "md5": "9a192d8b1a7dc652a19835f6f08098bd", + "rounds": 1, + "type": "text" + }, { "id": "html5-canvas-cheat-sheet-load", "file": "pdfs/canvas.pdf", "md5": "59510028561daf62e00bf9f6f066b033", @@ -77,6 +83,12 @@ "rounds": 1, "type": "load" }, + { "id": "arabiccidtruetype-text", + "file": "pdfs/ArabicCIDTrueType.pdf", + "md5": "d66dbd18bdb572d3ac8b88b32de2ece6", + "rounds": 1, + "type": "text" + }, { "id": "complexttffont-pdf", "file": "pdfs/complex_ttf_font.pdf", "md5": "76de93f9116b01b693bf8583b3e76d91", @@ -89,6 +101,12 @@ "rounds": 1, "type": "eq" }, + { "id": "thuluthfont-text", + "file": "pdfs/ThuluthFeatures.pdf", + "md5": "b7e18bf7a3d6a9c82aefa12d721072fc", + "rounds": 1, + "type": "text" + }, { "id": "freeculture", "file": "pdfs/freeculture.pdf", "md5": "dcdf3a8268e6a18938a42d5149efcfca", @@ -263,6 +281,13 @@ "rounds": 1, "type": "eq" }, + { "id": "simpletype3font-text", + "file": "pdfs/simpletype3font.pdf", + "md5": "b374c7543920840c61999e9e86939f99", + "link": false, + "rounds": 1, + "type": "text" + }, { "id": "close-path-bug", "file": "pdfs/close-path-bug.pdf", "md5": "48dd17ef58393857d2d038d33699cac5", diff --git a/web/index.html.template b/web/index.html.template index 44e9a0cbe..a99cf8d14 100644 --- a/web/index.html.template +++ b/web/index.html.template @@ -8,7 +8,8 @@