Browse Source

merge upstream

Artur Adib 13 years ago
parent
commit
863dd0d214
  1. 4
      extensions/firefox/components/PdfStreamConverter.js
  2. 4
      l10n/en-US/viewer.properties
  3. 4
      l10n/ja/viewer.properties
  4. 4
      l10n/ru/viewer.properties
  5. 1
      l10n/zh-TW/chrome.properties
  6. 16
      l10n/zh-TW/viewer.properties
  7. 19
      src/api.js
  8. 21
      src/canvas.js
  9. 157
      src/evaluator.js
  10. 147
      src/fonts.js
  11. 65
      test/driver.js
  12. 7
      test/test.py
  13. 25
      test/test_manifest.json
  14. 35
      web/index.html.template
  15. 14
      web/viewer.js

4
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 MAX_DATABASE_LENGTH = 4096;
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}'; const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}'; 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/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://gre/modules/Services.jsm');
@ -50,7 +51,8 @@ if (appInfo.ID === FIREFOX_ID) {
privateBrowsing = Cc['@mozilla.org/privatebrowsing;1'] privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
.getService(Ci.nsIPrivateBrowsingService); .getService(Ci.nsIPrivateBrowsingService);
inPrivateBrowsing = privateBrowsing.privateBrowsingEnabled; inPrivateBrowsing = privateBrowsing.privateBrowsingEnabled;
} else if (appInfo.ID === SEAMONKEY_ID) { } else if (appInfo.ID === SEAMONKEY_ID ||
appInfo.ID === METRO_ID) {
privateBrowsing = null; privateBrowsing = null;
inPrivateBrowsing = false; inPrivateBrowsing = false;
} }

4
l10n/en-US/viewer.properties

@ -51,8 +51,8 @@ thumb_page_title=Page {{page}}
thumb_page_canvas=Thumbnail of Page {{page}} thumb_page_canvas=Thumbnail of Page {{page}}
# Context menu # Context menu
page_rotate_cw=Rotate Clockwise page_rotate_cw.label=Rotate Clockwise
page_rotate_ccw=Rotate Counter-Clockwise page_rotate_ccw.label=Rotate Counter-Clockwise
# Search panel button title and messages # Search panel button title and messages
search=Find search=Find

4
l10n/ja/viewer.properties

@ -50,6 +50,10 @@ thumb_page_title={{page}} ページ
# number. # number.
thumb_page_canvas=ページの縮小版 {{page}} thumb_page_canvas=ページの縮小版 {{page}}
# Context menu
page_rotate_cw.label=右回転
page_rotate_ccw.label=左回転
# Search panel button title and messages # Search panel button title and messages
search=検索 search=検索
search_terms_not_found=(見つかりませんでした) search_terms_not_found=(見つかりませんでした)

4
l10n/ru/viewer.properties

@ -43,3 +43,7 @@ zoom.title=Масштаб
thumb_page_title=Страница {{page}} thumb_page_title=Страница {{page}}
thumb_page_canvas=Уменьшенное изображение страницы {{page}} thumb_page_canvas=Уменьшенное изображение страницы {{page}}
request_password=PDF защищён паролем: request_password=PDF защищён паролем:
fullscreen.title=Полный экран
fullscreen_label=Полный экран
page_rotate_cw.label=Повернуть по часовой стрелке
page_rotate_ccw.label=Повернуть против часовой стрелки

1
l10n/zh-TW/chrome.properties

@ -1,3 +1,4 @@
# Chrome 通知欄的訊息及按鍵 # Chrome 通知欄的訊息及按鍵
unsupported_feature=本 PDF 文件可能無法正常顯示。 unsupported_feature=本 PDF 文件可能無法正常顯示。
open_with_different_viewer=使用其他檢視器打開文件 open_with_different_viewer=使用其他檢視器打開文件
open_with_different_viewer.accessKey=o

16
l10n/zh-TW/viewer.properties

@ -8,7 +8,7 @@ next_label=下一頁
# 這些字符串會連接成 "Page: X of Y" 的表示方式。 # 這些字符串會連接成 "Page: X of Y" 的表示方式。
# 不要翻譯 "{{pageCount}}" , 因為它用來表示總頁數。 # 不要翻譯 "{{pageCount}}" , 因為它用來表示總頁數。
page_label=第 page_label=第
page_of=頁, 共 {{pageCount}} 頁 page_of=頁共 {{pageCount}} 頁
zoom_out.title=縮小 zoom_out.title=縮小
zoom_out_label=縮小 zoom_out_label=縮小
@ -17,6 +17,8 @@ zoom_in_label=放大
zoom.title=縮放 zoom.title=縮放
print.title=列印 print.title=列印
print_label=列印 print_label=列印
fullscreen.title=全螢幕
fullscreen_label=全螢幕
open_file.title=開啟檔案 open_file.title=開啟檔案
open_file_label=開啟 open_file_label=開啟
download.title=下載 download.title=下載
@ -44,9 +46,13 @@ thumb_page_title=第 {{page}} 頁
# 本地化提示 (thumb_page_canvas): "{{page}}" 會被頁數取代。 # 本地化提示 (thumb_page_canvas): "{{page}}" 會被頁數取代。
thumb_page_canvas=第 {{page}} 頁的縮圖 thumb_page_canvas=第 {{page}} 頁的縮圖
# 右鍵菜單
page_rotate_cw.label=順時針旋轉
page_rotate_ccw.label=逆時針旋轉
# 搜尋面板按鍵文字及訊息 # 搜尋面板按鍵文字及訊息
search=搜索 search=搜索
search_terms_not_found=(沒有找到) search_terms_not_found=(沒有找到)
# 錯誤面板標籤 # 錯誤面板標籤
error_more_info=更多資訊 error_more_info=更多資訊
@ -71,13 +77,13 @@ page_scale_auto=自動縮放
page_scale_actual=實際大小 page_scale_actual=實際大小
# 載入指示訊息 # 載入指示訊息
# 本地化提示 (error_line): "{{percent}}" 會被百分比取代。
loading=正在載入... {{percent}}%
loading_error_indicator=錯誤 loading_error_indicator=錯誤
loading_error=載入PDF檔案時發生錯誤。 loading_error=載入PDF檔案時發生錯誤。
# 其他標籤和訊息 # 其他標籤和訊息
# "{{type}}" 用來表示PDF格式規範 (32000-1:2008 Table 169 – Annotation types) 入面所定義的註解種類。 # "{{type}}" 用來表示PDF格式規範 (32000-1:2008 Table 169 – Annotation types) 入面所定義的註解種類。
# 一些常見的類型有: "Check"、 "Text"、 "Comment"、 "Note" # 一些常見的類型有: "Check"、 "Text"、 "Comment"、 "Note"
text_annotation_type=[{{type}} 註解] text_annotation_type=[{{type}} 註解]
request_password=PDF檔案受密碼保護: request_password=PDF檔案受密碼保護:
printing_not_supported=警告:這個瀏覽器不完全支援列印。

19
src/api.js

@ -575,24 +575,15 @@ var WorkerTransport = (function WorkerTransportClosure() {
this.objs.resolve(id, imageData); this.objs.resolve(id, imageData);
break; break;
case 'Font': case 'Font':
var name = data[2]; var exportedData = 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);
}
// At this point, only the font object is created but the font is // 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`. // not yet attached to the DOM. This is done in `FontLoader.bind`.
var font; var font;
try { if ('error' in exportedData)
font = new Font(name, file, properties); font = new ErrorFont(exportedData.error);
} catch (e) { else
font = new ErrorFont(e); font = new Font(exportedData);
}
this.objs.resolve(id, font); this.objs.resolve(id, font);
break; break;
default: default:

21
src/canvas.js

@ -596,8 +596,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
(fontObj.bold ? 'bold' : 'normal'); (fontObj.bold ? 'bold' : 'normal');
var italic = fontObj.italic ? 'italic' : 'normal'; var italic = fontObj.italic ? 'italic' : 'normal';
var serif = fontObj.isSerifFont ? 'serif' : 'sans-serif'; var typeface = '"' + name + '", ' + fontObj.fallbackName;
var typeface = '"' + name + '", ' + serif;
// Some font backends cannot handle fonts below certain size. // Some font backends cannot handle fonts below certain size.
// Keeping the font at minimal size and using the fontSizeScale to change // Keeping the font at minimal size and using the fontSizeScale to change
@ -782,15 +781,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
x += charWidth; x += charWidth;
var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode; var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
var glyphUnicodeLength = glyphUnicode.length; if (glyphUnicode in NormalizedUnicodes)
//reverse an arabic ligature glyphUnicode = NormalizedUnicodes[glyphUnicode];
if (glyphUnicodeLength > 1 && text.str += reverseIfRtl(glyphUnicode);
isRTLRangeFor(glyphUnicode.charCodeAt(0))) {
for (var ii = glyphUnicodeLength - 1; ii >= 0; ii--)
text.str += glyphUnicode[ii];
} else
text.str += glyphUnicode;
text.length += glyphUnicodeLength;
text.canvasWidth += charWidth; text.canvasWidth += charWidth;
} }
current.x += x * textHScale2; current.x += x * textHScale2;
@ -798,7 +791,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
} }
if (textSelection) if (textSelection)
this.textLayer.appendText(text, font.loadedName, fontSize); this.textLayer.appendText(text, font.fallbackName, fontSize);
return text; return text;
}, },
@ -842,7 +835,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
var numFakeSpaces = Math.round(-e / text.geom.spaceWidth); var numFakeSpaces = Math.round(-e / text.geom.spaceWidth);
if (numFakeSpaces > 0) { if (numFakeSpaces > 0) {
text.str += '\u00A0'; text.str += '\u00A0';
text.length++;
} }
} }
} }
@ -856,7 +848,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
text.str += shownText.str; text.str += shownText.str;
} }
text.canvasWidth += shownText.canvasWidth; text.canvasWidth += shownText.canvasWidth;
text.length += shownText.length;
} }
} else { } else {
error('TJ array element ' + e + ' is not string or num'); error('TJ array element ' + e + ' is not string or num');
@ -864,7 +855,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
} }
if (textSelection) if (textSelection)
this.textLayer.appendText(text, font.loadedName, fontSize); this.textLayer.appendText(text, font.fallbackName, fontSize);
}, },
nextLineShowText: function CanvasGraphics_nextLineShowText(text) { nextLineShowText: function CanvasGraphics_nextLineShowText(text) {
this.nextLine(); this.nextLine();

157
src/evaluator.js

@ -26,6 +26,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
this.handler = handler; this.handler = handler;
this.uniquePrefix = uniquePrefix; this.uniquePrefix = uniquePrefix;
this.objIdCounter = 0; this.objIdCounter = 0;
this.fontIdCounter = 0;
} }
var OP_MAP = { var OP_MAP = {
@ -138,6 +139,35 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}; };
PartialEvaluator.prototype = { 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, getOperatorList: function PartialEvaluator_getOperatorList(stream,
resources, resources,
dependency, dependency,
@ -160,46 +190,35 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
} }
function handleSetFont(fontName, font) { function handleSetFont(fontName, font) {
var loadedName = null; font = self.loadFont(fontName, font, xref, resources, dependency);
var fontRes = resources.get('Font');
assert(fontRes, 'fontRes not available');
font = xref.fetchIfRef(font) || fontRes.get(fontName); var loadedName = font.loadedName;
assertWellFormed(isDict(font)); if (!font.sent) {
var data = font.translated;
++self.objIdCounter; if (data.loadCharProcs) {
if (!font.loadedName) { delete data.loadCharProcs;
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; var charProcs = font.get('CharProcs').getAll();
// Convert the file to an ArrayBuffer which will be turned back into var fontResources = font.get('Resources') || resources;
// a Stream in the main thread. var charProcOperatorList = {};
if (translated.file) for (var key in charProcs) {
translated.file = translated.file.getBytes(); var glyphStream = charProcs[key];
if (translated.properties.file) { charProcOperatorList[key] =
translated.properties.file = self.getOperatorList(glyphStream, fontResources, dependency);
translated.properties.file.getBytes();
} }
data.charProcOperatorList = charProcOperatorList;
}
if (data instanceof Font)
data = data.export();
handler.send('obj', [ handler.send('obj', [
loadedName, loadedName,
'Font', 'Font',
translated.name, data
translated.file,
translated.properties
]); ]);
font.sent = true;
} }
}
loadedName = loadedName || font.loadedName;
// Ensure the font is ready before the font is set // Ensure the font is ready before the font is set
// and later on used for drawing. // and later on used for drawing.
@ -492,20 +511,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var xref = this.xref; var xref = this.xref;
function handleSetFont(fontName, fontRef) { function handleSetFont(fontName, fontRef) {
var fontRes = resources.get('Font'); return self.loadFont(fontName, fontRef, xref, resources, null);
// 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;
} }
resources = xref.fetchIfRef(resources) || new Dict(); resources = xref.fetchIfRef(resources) || new Dict();
@ -522,13 +528,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var cmd = obj.cmd; var cmd = obj.cmd;
switch (cmd) { switch (cmd) {
case 'Tf': case 'Tf':
font = handleSetFont(args[0].name); font = handleSetFont(args[0].name).translated;
break; break;
case 'TJ': case 'TJ':
var items = args[0]; var items = args[0];
for (var j = 0, jj = items.length; j < jj; j++) { for (var j = 0, jj = items.length; j < jj; j++) {
if (typeof items[j] === 'string') { if (typeof items[j] === 'string') {
chunk += items[j]; chunk += fontCharsToUnicode(items[j], font);
} else if (items[j] < 0) { } else if (items[j] < 0) {
// making all negative offsets a space - better to have // making all negative offsets a space - better to have
// a space in incorrect place than not have them at all // a space in incorrect place than not have them at all
@ -537,17 +543,17 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
} }
break; break;
case 'Tj': case 'Tj':
chunk += args[0]; chunk += fontCharsToUnicode(args[0], font);
break; break;
case "'": case "'":
chunk += args[0] + ' '; chunk += fontCharsToUnicode(args[0], font) + ' ';
break; break;
case '"': case '"':
chunk += args[2] + ' '; chunk += fontCharsToUnicode(args[2], font) + ' ';
break; break;
} // switch } // switch
if (chunk !== '') { if (chunk !== '') {
text += fontCharsToUnicode(chunk, font.translated.properties); text += chunk;
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.defaultWidth = defaultWidth;
properties.widths = glyphsWidths; properties.widths = glyphsWidths;
}, },
getBaseFontMetrics: function PartialEvaluator_getBaseFontMetrics(name) { getBaseFontMetrics: function PartialEvaluator_getBaseFontMetrics(name) {
var defaultWidth = 0, widths = []; var defaultWidth = 0, widths = [], monospace = false;
var glyphWidths = Metrics[stdFontMap[name] || name]; var glyphWidths = Metrics[stdFontMap[name] || name];
if (isNum(glyphWidths)) { if (isNum(glyphWidths)) {
defaultWidth = glyphWidths; defaultWidth = glyphWidths;
monospace = true;
} else { } else {
widths = glyphWidths; widths = glyphWidths;
} }
return { return {
defaultWidth: defaultWidth, defaultWidth: defaultWidth,
monospace: monospace,
widths: widths widths: widths
}; };
}, },
@ -854,7 +880,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// - get the FontDescriptor from the descendant font // - get the FontDescriptor from the descendant font
var df = dict.get('DescendantFonts'); var df = dict.get('DescendantFonts');
if (!df) if (!df)
return null; error('Descendant fonts are not specified');
dict = isArray(df) ? xref.fetchIfRef(df[0]) : df; dict = isArray(df) ? xref.fetchIfRef(df[0]) : df;
@ -877,7 +903,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// This case is here for compatibility. // This case is here for compatibility.
var baseFontName = dict.get('BaseFont'); var baseFontName = dict.get('BaseFont');
if (!isName(baseFontName)) if (!isName(baseFontName))
return null; error('Base font is not specified');
// Using base font name as a font name. // Using base font name as a font name.
baseFontName = baseFontName.name.replace(/[,_]/g, '-'); baseFontName = baseFontName.name.replace(/[,_]/g, '-');
@ -887,6 +913,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var fontNameWoStyle = baseFontName.split('-')[0]; var fontNameWoStyle = baseFontName.split('-')[0];
var flags = (serifFonts[fontNameWoStyle] || var flags = (serifFonts[fontNameWoStyle] ||
(fontNameWoStyle.search(/serif/gi) != -1) ? FontFlags.Serif : 0) | (fontNameWoStyle.search(/serif/gi) != -1) ? FontFlags.Serif : 0) |
(metrics.monospace ? FontFlags.FixedPitch : 0) |
(symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic : (symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic :
FontFlags.Nonsymbolic); FontFlags.Nonsymbolic);
@ -900,11 +927,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}; };
this.extractDataStructures(dict, dict, xref, properties); this.extractDataStructures(dict, dict, xref, properties);
return { return new Font(baseFontName, null, properties);
name: baseFontName,
dict: baseDict,
properties: properties
};
} }
} }
@ -940,6 +963,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
file: fontFile, file: fontFile,
length1: length1, length1: length1,
length2: length2, length2: length2,
loadedName: baseDict.loadedName,
composite: composite, composite: composite,
wideChars: composite, wideChars: composite,
fixedPitch: false, fixedPitch: false,
@ -960,22 +984,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (type.name === 'Type3') { if (type.name === 'Type3') {
properties.coded = true; 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 { return new Font(fontName.name, fontFile, properties);
name: fontName.name,
dict: baseDict,
file: fontFile,
properties: properties
};
} }
}; };

147
src/fonts.js

@ -418,11 +418,26 @@ function mapPrivateUseChars(code) {
} }
var FontLoader = { var FontLoader = {
//#if !(MOZCENTRAL)
loadingContext: { loadingContext: {
requests: [], requests: [],
nextRequestId: 0 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) { bind: function fontLoaderBind(fonts, callback) {
assert(!isWorker, 'bind() shall be called from main thread'); assert(!isWorker, 'bind() shall be called from main thread');
@ -437,23 +452,15 @@ var FontLoader = {
} }
font.attached = true; font.attached = true;
var str = ''; var rule = font.bindDOM();
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) { if (rule) {
rules.push(rule); rules.push(rule);
fontsToLoad.push(font); fontsToLoad.push(font);
} }
} }
}
var request = FontLoader.queueLoadingCallback(callback); var request = FontLoader.queueLoadingCallback(callback);
if (rules.length > 0) { if (rules.length > 0 && !this.isSyncFontLoadingSupported) {
FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request); FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request);
} else { } else {
request.complete(); request.complete();
@ -595,6 +602,22 @@ var FontLoader = {
document.body.appendChild(frame); document.body.appendChild(frame);
/** Hack end */ /** 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 = [ var UnicodeRanges = [
@ -1464,54 +1487,30 @@ var NormalizedUnicodes = {
'\uFE4F': '\u005F' '\uFE4F': '\u005F'
}; };
function fontCharsToUnicode(charCodes, fontProperties) { function reverseIfRtl(chars) {
var toUnicode = fontProperties.toUnicode; var charsLength = chars.length;
var composite = fontProperties.composite; //reverse an arabic ligature
var encoding, differences, cidToUnicode; if (charsLength <= 1 || !isRTLRangeFor(chars.charCodeAt(0)))
return chars;
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 = ''; var result = '';
if (composite) { for (var i = 0, ii = glyphs.length; i < ii; i++) {
cidToUnicode = fontProperties.cidToUnicode; var glyph = glyphs[i];
for (var i = 0, ii = charCodes.length; i < ii; i += 2) { if (!glyph)
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; continue;
}
var glyphName = charCode in differences ? differences[charCode] : var glyphUnicode = glyph.unicode;
encoding[charCode]; if (glyphUnicode in NormalizedUnicodes)
if (glyphName in GlyphsUnicode) { glyphUnicode = NormalizedUnicodes[glyphUnicode];
result += String.fromCharCode(GlyphsUnicode[glyphName]); result += reverseIfRtl(glyphUnicode);
continue;
}
result += String.fromCharCode(charCode);
}
}
// normalizing the unicode characters
for (var i = 0, ii = result.length; i < ii; i++) {
if (!(result[i] in NormalizedUnicodes))
continue;
result = result.substring(0, i) + NormalizedUnicodes[result[i]] +
result.substring(i + 1);
ii = result.length;
} }
return result; return result;
} }
@ -1526,9 +1525,19 @@ function fontCharsToUnicode(charCodes, fontProperties) {
*/ */
var Font = (function FontClosure() { var Font = (function FontClosure() {
function Font(name, file, properties) { 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.name = name;
this.loadedName = properties.loadedName;
this.coded = properties.coded; this.coded = properties.coded;
this.charProcOperatorList = properties.charProcOperatorList; this.loadCharProcs = properties.coded;
this.sizes = []; this.sizes = [];
var names = name.split('+'); var names = name.split('+');
@ -1536,17 +1545,13 @@ var Font = (function FontClosure() {
names = names.split(/[-,_]/g)[0]; names = names.split(/[-,_]/g)[0];
this.isSerifFont = !!(properties.flags & FontFlags.Serif); this.isSerifFont = !!(properties.flags & FontFlags.Serif);
this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic); this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
var type = properties.type; var type = properties.type;
this.type = type; this.type = type;
// If the font is to be ignored, register it like an already loaded font this.fallbackName = this.isMonospace ? 'monospace' :
// to avoid the cost of waiting for it be be loaded by the platform. this.isSerifFont ? 'serif' : 'sans-serif';
if (properties.ignore) {
this.loadedName = this.isSerifFont ? 'serif' : 'sans-serif';
this.loading = false;
return;
}
this.differences = properties.differences; this.differences = properties.differences;
this.widths = properties.widths; this.widths = properties.widths;
@ -1632,7 +1637,6 @@ var Font = (function FontClosure() {
this.widthMultiplier = !properties.fontMatrix ? 1.0 : this.widthMultiplier = !properties.fontMatrix ? 1.0 :
1.0 / properties.fontMatrix[0]; 1.0 / properties.fontMatrix[0];
this.encoding = properties.baseEncoding; this.encoding = properties.baseEncoding;
this.loadedName = properties.loadedName;
this.loading = true; this.loading = true;
}; };
@ -2036,6 +2040,15 @@ var Font = (function FontClosure() {
mimetype: null, mimetype: null,
encoding: 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) { checkAndRepair: function Font_checkAndRepair(name, font, properties) {
function readTableEntry(file) { function readTableEntry(file) {
var tag = file.getBytes(4); 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; var fontName = this.loadedName;
// Add the font-face rule to the document // Add the font-face rule to the document

65
test/driver.js

@ -26,7 +26,7 @@
// "firefox-bin: Fatal IO error 12 (Cannot allocate memory) on X server :1." // "firefox-bin: Fatal IO error 12 (Cannot allocate memory) on X server :1."
// PDFJS.disableWorker = true; // PDFJS.disableWorker = true;
var appPath, browser, canvas, currentTaskIdx, manifest, stdout; var appPath, browser, canvas, dummyCanvas, currentTaskIdx, manifest, stdout;
var inFlightRequests = 0; var inFlightRequests = 0;
function queryParams() { function queryParams() {
@ -148,6 +148,46 @@ function canvasToDataURL() {
return canvas.toDataURL('image/png'); 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) { function nextPage(task, loadError) {
var failure = loadError || ''; var failure = loadError || '';
@ -196,16 +236,21 @@ function nextPage(task, loadError) {
canvas.height = viewport.height; canvas.height = viewport.height;
clear(ctx); clear(ctx);
// using the text layer builder that does nothing to test var drawContext, textLayerBuilder;
// text layer creation operations if (task.type == 'text') {
var textLayerBuilder = { // using dummy canvas for pdf context drawing operations
beginLayout: function nullTextLayerBuilderBeginLayout() {}, if (!dummyCanvas) {
endLayout: function nullTextLayerBuilderEndLayout() {}, dummyCanvas = document.createElement('canvas');
appendText: function nullTextLayerBuilderAppendText(text, fontName, }
fontSize) {} 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 = { var renderContext = {
canvasContext: ctx, canvasContext: drawContext,
textLayer: textLayerBuilder, textLayer: textLayerBuilder,
viewport: viewport viewport: viewport
}; };

7
test/test.py

@ -514,7 +514,7 @@ def check(task, results, browser, masterMode):
return return
kind = task['type'] kind = task['type']
if 'eq' == kind: if 'eq' == kind or 'text' == kind:
checkEq(task, results, browser, masterMode) checkEq(task, results, browser, masterMode)
elif 'fbf' == kind: elif 'fbf' == kind:
checkFBF(task, results, browser) checkFBF(task, results, browser)
@ -528,6 +528,7 @@ def checkEq(task, results, browser, masterMode):
pfx = os.path.join(REFDIR, sys.platform, browser, task['id']) pfx = os.path.join(REFDIR, sys.platform, browser, task['id'])
results = results[0] results = results[0]
taskId = task['id'] taskId = task['id']
taskType = task['type']
passed = True passed = True
for page in xrange(len(results)): for page in xrange(len(results)):
@ -547,7 +548,7 @@ def checkEq(task, results, browser, masterMode):
eq = (ref == snapshot) eq = (ref == snapshot)
if not eq: 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: if not State.eqLog:
State.eqLog = open(EQLOG_FILE, 'w') State.eqLog = open(EQLOG_FILE, 'w')
@ -576,7 +577,7 @@ def checkEq(task, results, browser, masterMode):
of.close() of.close()
if passed: 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): def checkFBF(task, results, browser):
round0, round1 = results[0], results[1] round0, round1 = results[0], results[1]

25
test/test_manifest.json

@ -11,6 +11,12 @@
"rounds": 2, "rounds": 2,
"type": "fbf" "type": "fbf"
}, },
{ "id": "tracemonkey-text",
"file": "pdfs/tracemonkey.pdf",
"md5": "9a192d8b1a7dc652a19835f6f08098bd",
"rounds": 1,
"type": "text"
},
{ "id": "html5-canvas-cheat-sheet-load", { "id": "html5-canvas-cheat-sheet-load",
"file": "pdfs/canvas.pdf", "file": "pdfs/canvas.pdf",
"md5": "59510028561daf62e00bf9f6f066b033", "md5": "59510028561daf62e00bf9f6f066b033",
@ -77,6 +83,12 @@
"rounds": 1, "rounds": 1,
"type": "load" "type": "load"
}, },
{ "id": "arabiccidtruetype-text",
"file": "pdfs/ArabicCIDTrueType.pdf",
"md5": "d66dbd18bdb572d3ac8b88b32de2ece6",
"rounds": 1,
"type": "text"
},
{ "id": "complexttffont-pdf", { "id": "complexttffont-pdf",
"file": "pdfs/complex_ttf_font.pdf", "file": "pdfs/complex_ttf_font.pdf",
"md5": "76de93f9116b01b693bf8583b3e76d91", "md5": "76de93f9116b01b693bf8583b3e76d91",
@ -89,6 +101,12 @@
"rounds": 1, "rounds": 1,
"type": "eq" "type": "eq"
}, },
{ "id": "thuluthfont-text",
"file": "pdfs/ThuluthFeatures.pdf",
"md5": "b7e18bf7a3d6a9c82aefa12d721072fc",
"rounds": 1,
"type": "text"
},
{ "id": "freeculture", { "id": "freeculture",
"file": "pdfs/freeculture.pdf", "file": "pdfs/freeculture.pdf",
"md5": "dcdf3a8268e6a18938a42d5149efcfca", "md5": "dcdf3a8268e6a18938a42d5149efcfca",
@ -263,6 +281,13 @@
"rounds": 1, "rounds": 1,
"type": "eq" "type": "eq"
}, },
{ "id": "simpletype3font-text",
"file": "pdfs/simpletype3font.pdf",
"md5": "b374c7543920840c61999e9e86939f99",
"link": false,
"rounds": 1,
"type": "text"
},
{ "id": "close-path-bug", { "id": "close-path-bug",
"file": "pdfs/close-path-bug.pdf", "file": "pdfs/close-path-bug.pdf",
"md5": "48dd17ef58393857d2d038d33699cac5", "md5": "48dd17ef58393857d2d038d33699cac5",

35
web/index.html.template

@ -8,7 +8,8 @@
<style type="text/css"> <style type="text/css">
body { body {
margin-top: 1.0em; margin-top: 1.0em;
background-color: #482a30; background-color: rgb(64, 64, 64);
background-image: url(web/images/texture.png);
font-family: Helvetica, Arial, FreeSans, san-serif; font-family: Helvetica, Arial, FreeSans, san-serif;
color: #ffffff; color: #ffffff;
} }
@ -50,20 +51,28 @@
</div> </div>
<h2>Try it out!</h2> <h2>Try it out!</h2>
<p>Live <a href="web/viewer.html">demo</a> lives here.</p> <p>Live <a href="web/viewer.html">demo</a> or firefox <a href="https://addons.mozilla.org/en-US/firefox/addon/pdfjs/">extension</a>.</p>
<h2>Authors</h2> <h2>Authors</h2>
<p>Vivien Nicolas (21@vingtetun.org) Andreas Gal (gal@mozilla.com)<br/>
<br/>Andreas Gal (andreas.gal@gmail.com) Chris G Jones (cjones@mozilla.com)<br/>
<br/>Soumya Deb (debloper@gmail.com) Shaon Barman (shaon.barman@gmail.com)<br/>
<br/>Chris Jones (jones.chris.g@gmail.com) Vivien Nicolas (21@vingtetun.org)<br/>
<br/>Justin D'Arcangelo (justindarc@gmail.com) Justin D'Arcangelo (justindarc@gmail.com)<br/>
<br/>sbarman (sbarman@eecs.berkeley.edu) Yury Delendik (ydelendik@mozilla.com)<br/>
<br/> Kalervo Kujala<br/>
<br/> </p> Adil Allawi (@ironymark)<br/>
<h2>Contact</h2> Jakob Miland (saebekassebil@gmail.com)<br/>
<p> (andreas.gal@gmail.com) Artur Adib (aadib@mozilla.com)<br/>
<br/> </p> Brendan Dahl (bdahl@mozilla.com)<br/>
David Quintana (gigaherz@gmail.com)<br/>
Julian Viereck<br/>
<a href="https://github.com/mozilla/pdf.js/graphs/contributors">...</a>
</p>
<h2>Contact</h2>
<p> (dev-pdf-js@lists.mozilla.org)
<br/> </p>
<h2>Download</h2> <h2>Download</h2>

14
web/viewer.js

@ -1843,22 +1843,18 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
while (textDivs.length > 0) { while (textDivs.length > 0) {
var textDiv = textDivs.shift(); var textDiv = textDivs.shift();
if (textDiv.dataset.textLength > 0) {
textLayerFrag.appendChild(textDiv); textLayerFrag.appendChild(textDiv);
if (textDiv.dataset.textLength > 1) { // avoid div by zero ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
// Adjust div width to match canvas text
ctx.font = textDiv.style.fontSize + ' sans-serif';
var width = ctx.measureText(textDiv.textContent).width; var width = ctx.measureText(textDiv.textContent).width;
if (width > 0) {
var textScale = textDiv.dataset.canvasWidth / width; var textScale = textDiv.dataset.canvasWidth / width;
CustomStyle.setProp('transform' , textDiv, CustomStyle.setProp('transform' , textDiv,
'scale(' + textScale + ', 1)'); 'scale(' + textScale + ', 1)');
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
} }
} // textLength > 0
} }
textLayerDiv.appendChild(textLayerFrag); textLayerDiv.appendChild(textLayerFrag);
@ -1889,14 +1885,13 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
// vScale and hScale already contain the scaling to pixel units // vScale and hScale already contain the scaling to pixel units
var fontHeight = fontSize * text.geom.vScale; var fontHeight = fontSize * text.geom.vScale;
textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale; textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
textDiv.dataset.fontName = fontName;
textDiv.style.fontSize = fontHeight + 'px'; textDiv.style.fontSize = fontHeight + 'px';
textDiv.style.fontFamily = fontName;
textDiv.style.left = text.geom.x + 'px'; textDiv.style.left = text.geom.x + 'px';
textDiv.style.top = (text.geom.y - fontHeight) + 'px'; textDiv.style.top = (text.geom.y - fontHeight) + 'px';
textDiv.textContent = PDFJS.bidi(text, -1); textDiv.textContent = PDFJS.bidi(text, -1);
textDiv.dir = text.direction; textDiv.dir = text.direction;
textDiv.dataset.textLength = text.length;
this.textDivs.push(textDiv); this.textDivs.push(textDiv);
}; };
}; };
@ -2065,7 +2060,7 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
}); });
document.getElementById('searchTermsInput').addEventListener('keydown', document.getElementById('searchTermsInput').addEventListener('keydown',
function() { function(event) {
if (event.keyCode == 13) { if (event.keyCode == 13) {
PDFView.search(); PDFView.search();
} }
@ -2292,6 +2287,7 @@ window.addEventListener('keydown', function keydown(evt) {
PDFView.zoomIn(); PDFView.zoomIn();
handled = true; handled = true;
break; break;
case 173: // FF/Mac '-'
case 109: // FF '-' case 109: // FF '-'
case 189: // Chrome '-' case 189: // Chrome '-'
PDFView.zoomOut(); PDFView.zoomOut();

Loading…
Cancel
Save