diff --git a/extensions/firefox/chrome-mozcentral.manifest b/extensions/firefox/chrome-mozcentral.manifest
new file mode 100644
index 000000000..a2a6757c3
--- /dev/null
+++ b/extensions/firefox/chrome-mozcentral.manifest
@@ -0,0 +1,3 @@
+resource pdf.js content/
+component {d0c5195d-e798-49d4-b1d3-9324328b2291} components/PdfStreamConverter.js
+contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {d0c5195d-e798-49d4-b1d3-9324328b2291}
diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
index f866a6b2f..532075bed 100644
--- a/extensions/firefox/components/PdfStreamConverter.js
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -11,7 +11,7 @@ const Cr = Components.results;
const Cu = Components.utils;
const PDFJS_EVENT_ID = 'pdf.js.message';
const PDF_CONTENT_TYPE = 'application/pdf';
-const EXT_PREFIX = 'extensions.uriloader@pdf.js';
+const PREF_PREFIX = 'PDFJSSCRIPT_PREF_PREFIX';
const MAX_DATABASE_LENGTH = 4096;
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
@@ -19,6 +19,7 @@ const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm');
+Cu.import('resource://gre/modules/AddonManager.jsm');
let appInfo = Cc['@mozilla.org/xre/app-info;1']
.getService(Ci.nsIXULAppInfo);
@@ -57,7 +58,7 @@ function getStringPref(pref, def) {
}
function log(aMsg) {
- if (!getBoolPref(EXT_PREFIX + '.pdfBugEnabled', false))
+ if (!getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false))
return;
let msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
Services.console.logStringMessage(msg);
@@ -91,9 +92,15 @@ function getLocalizedStrings(path) {
}
return map;
}
+function getLocalizedString(strings, id) {
+ if (id in strings)
+ return strings[id]['textContent'];
+ return id;
+}
// All the priviledged actions.
-function ChromeActions() {
+function ChromeActions(domWindow) {
+ this.domWindow = domWindow;
}
ChromeActions.prototype = {
@@ -136,12 +143,12 @@ ChromeActions.prototype = {
// Protect against something sending tons of data to setDatabase.
if (data.length > MAX_DATABASE_LENGTH)
return;
- setStringPref(EXT_PREFIX + '.database', data);
+ setStringPref(PREF_PREFIX + '.database', data);
},
getDatabase: function() {
if (inPrivateBrowsing)
return '{}';
- return getStringPref(EXT_PREFIX + '.database', '{}');
+ return getStringPref(PREF_PREFIX + '.database', '{}');
},
getLocale: function() {
return getStringPref('general.useragent.locale', 'en-US');
@@ -164,10 +171,30 @@ ChromeActions.prototype = {
},
searchEnabled: function() {
return getBoolPref(EXT_PREFIX + '.searchEnabled', false);
+ },
+ fallback: function(url) {
+ var self = this;
+ var domWindow = this.domWindow;
+ var strings = getLocalizedStrings('chrome.properties');
+ var message = getLocalizedString(strings, 'unsupported_feature');
+
+ var win = Services.wm.getMostRecentWindow('navigator:browser');
+ var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document);
+ var notificationBox = win.gBrowser.getNotificationBox(browser);
+
+ var buttons = [{
+ label: getLocalizedString(strings, 'open_with_different_viewer'),
+ accessKey: null,
+ callback: function() {
+ self.download(url);
+ }
+ }];
+ notificationBox.appendNotification(message, 'pdfjs-fallback', null,
+ notificationBox.PRIORITY_WARNING_LOW,
+ buttons);
}
};
-
// Event listener to trigger chrome privedged code.
function RequestListener(actions) {
this.actions = actions;
@@ -193,7 +220,7 @@ function PdfStreamConverter() {
PdfStreamConverter.prototype = {
// properties required for XPCOM registration:
- classID: Components.ID('{6457a96b-2d68-439a-bcfa-44465fbcdbb1}'),
+ classID: Components.ID('{PDFJSSCRIPT_STREAM_CONVERTER_ID}'),
classDescription: 'pdf.js Component',
contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*',
@@ -270,7 +297,8 @@ PdfStreamConverter.prototype = {
var domWindow = getDOMWindow(channel);
// Double check the url is still the correct one.
if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
- let requestListener = new RequestListener(new ChromeActions);
+ let requestListener = new RequestListener(
+ new ChromeActions(domWindow));
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
requestListener.receive(event);
}, false, true);
diff --git a/extensions/firefox/install.rdf.in b/extensions/firefox/install.rdf.in
deleted file mode 100644
index 084d6dc2d..000000000
--- a/extensions/firefox/install.rdf.in
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-#filter substitution
-
-
-
-
- uriloader@pdf.js
-
- PDF Viewer
- PDFJSSCRIPT_VERSION
-
-
- {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
- @FIREFOX_VERSION@
- @FIREFOX_VERSION@
-
-
- true
- true
- Mozilla
- Uses HTML5 to display PDF files directly in Firefox.
- https://support.mozilla.org/kb/Opening%20PDF%20files%20within%20Firefox
- 2
-
-
diff --git a/l10n/cs/metadata.inc b/l10n/cs/metadata.inc
new file mode 100644
index 000000000..ed5c2a1c7
--- /dev/null
+++ b/l10n/cs/metadata.inc
@@ -0,0 +1,8 @@
+
+
+ cs
+ PDF Viewer
+ Používá HTML5 pro zobrazení PDF souborů přímo ve Firefoxu.
+
+
+
diff --git a/l10n/cs/viewer.properties b/l10n/cs/viewer.properties
new file mode 100644
index 000000000..96b596b16
--- /dev/null
+++ b/l10n/cs/viewer.properties
@@ -0,0 +1,45 @@
+bookmark.title=Aktuální zobrazení(zkopírovat nebo otevřít v novém okně)
+previous.title=Předchozí stránka
+next.title=Další stránka
+print.title=Tisk
+download.title=Stáhnout
+zoom_out.title=Zmenšit
+zoom_in.title=Zvětšit
+error_more_info=Více informací
+error_less_info=Méně informací
+error_close=Zavřít
+error_build=PDF.JS Build: {{build}}
+error_message=Zpráva:{{message}}
+error_stack=Stack:{{stack}}
+error_file=Soubor:{{file}}
+error_line=Řádek:{{line}}
+page_scale_width=Šířka stránky
+page_scale_fit=Stránka
+page_scale_auto=Automatické přibližení
+page_scale_actual=Skutečná velikost
+toggle_slider.title=Přepnout posuvník
+thumbs.title=Zobrazit náhledy
+outline.title=Zobrazit osnovu dokumentu
+loading=Načítám... {{percent}}%
+loading_error_indicator=Chyba
+loading_error=Došlo k chybě při načítání PDF.
+rendering_error=Došlo k chybě při vykreslování stránky.
+page_label=Stránka:
+page_of=z{{pageCount}}
+no_outline=Žádné osnovy k dispozici
+open_file.title=Otevřít soubor
+text_annotation_type=[{{type}}Anotace]
+toggle_slider_label=Přepnout posuvník
+thumbs_label=Náhledy
+outline_label=Přehled dokumentu
+bookmark_label=Aktuální zobrazení
+previous_label=Předchozí
+next_label=Další
+print_label=Tisk
+download_label=Stáhnout
+zoom_out_label=Zmenšit
+zoom_in_label=Přiblížit
+zoom.title=Zvětšit
+thumb_page_title=Stránka{{page}}
+thumb_page_canvas=Náhled stránky {{page}}
+request_password=PDF je chráněn heslem:
diff --git a/l10n/da/viewer.properties b/l10n/da/viewer.properties
index 6d208db70..f9d77da1b 100644
--- a/l10n/da/viewer.properties
+++ b/l10n/da/viewer.properties
@@ -17,7 +17,7 @@ page_scale_width=Sidebredde
page_scale_fit=Helside
page_scale_auto=Automatisk zoom
page_scale_actual=Faktisk størrelse
-toggle_slider.title=Skift Slider
+toggle_slider.title=Skift slider
thumbs.title=Vis thumbnails
outline.title=Vis dokumentoversigt
loading=Indlæser... {{percent}}%
@@ -29,3 +29,17 @@ page_of=af {{pageCount}}
no_outline=Ingen dokumentoversigt tilgængelig
open_file.title=Åbn fil
text_annotation_type=[{{type}} Kommentar]
+toggle_slider_label=Skift slider
+thumbs_label=Thumbnails
+outline_label=Dokumentoversigt
+bookmark_label=Aktuel visning
+previous_label=Forrige
+next_label=Næste
+print_label=Udskriv
+download_label=Hent
+zoom_out_label=Zoom ud
+zoom_in_label=Zoom ind
+zoom.title=Zoom
+thumb_page_title=Side {{page}}
+thumb_page_canvas=Thumbnail af side {{page}}
+request_password=PDF filen er beskyttet med et kodeord:
diff --git a/l10n/en-US/chrome.properties b/l10n/en-US/chrome.properties
new file mode 100644
index 000000000..467d5920b
--- /dev/null
+++ b/l10n/en-US/chrome.properties
@@ -0,0 +1,2 @@
+unsupported_feature=This PDF document might not be displayed correctly.
+open_with_different_viewer=Open With Different Viewer
diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties
index c8dbe4aba..0922062a6 100644
--- a/l10n/en-US/viewer.properties
+++ b/l10n/en-US/viewer.properties
@@ -42,3 +42,5 @@ zoom_in_label=Zoom In
zoom.title=Zoom
thumb_page_title=Page {{page}}
thumb_page_canvas=Thumbnail of Page {{page}}
+request_password=PDF is protected by a password:
+open_file_label=Open
diff --git a/l10n/fr/metadata.inc b/l10n/fr/metadata.inc
new file mode 100644
index 000000000..874e93a18
--- /dev/null
+++ b/l10n/fr/metadata.inc
@@ -0,0 +1,7 @@
+
+
+ fr
+ PDF Viewer
+ Utilise HTML5 pour afficher les documents PDF directement dans Firefox.
+
+
diff --git a/l10n/fr/viewer.properties b/l10n/fr/viewer.properties
new file mode 100644
index 000000000..c93df3681
--- /dev/null
+++ b/l10n/fr/viewer.properties
@@ -0,0 +1,45 @@
+bookmark.title=Vue courante (copier ou ouvrir dans une nouvelle fenêtre)
+previous.title=Précédente
+next.title=Suivante
+print.title=Imprimer
+download.title=Télécharger
+zoom_out.title=Zoom arrière
+zoom_in.title=Zoom avant
+error_more_info=Plus d'informations
+error_less_info=Moins d'informations
+error_close=Fermer
+error_build=Version de PDF.JS : {{build}}
+error_message=Message : {{message}}
+error_stack=Pile : {{stack}}
+error_file=Fichier : {{file}}
+error_line=Ligne : {{line}}
+page_scale_width=Largeur de la page
+page_scale_fit=Ajuster à la page
+page_scale_auto=Zoom automatique
+page_scale_actual=Taille réelle
+toggle_slider.title=Afficher/Masquer la barre latérale
+thumbs.title=Afficher/masquer les vignettes
+outline.title=Afficher/masquer la structure
+loading=Chargement… {{percent}}%
+loading_error_indicator=Erreur
+loading_error=Une erreur est survenue lors du chargement du PDF.
+rendering_error=Une erreur est survenue lors de l'affichage de la page.
+page_label=Page :
+page_of=sur {{pageCount}}
+no_outline=Pas de structure disponible
+open_file.title=Ouvrir un fichier
+text_annotation_type=[Annotation {{type}}]
+toggle_slider_label=Afficher/Masquer la barre latérale
+thumbs_label=Vignettes
+outline_label=Structure du document
+bookmark_label=Vue courante
+previous_label=Précédente
+next_label=Suivante
+print_label=Imprimer
+download_label=Télécharger
+zoom_out_label=Zoom arrière
+zoom_in_label=Zoom avant
+zoom.title=Zoom
+thumb_page_title=Page {{page}}
+thumb_page_canvas=Aperçu de la page {{page}}
+request_password=Ce PDF est protégé par un mot de passe :
diff --git a/l10n/he/metadata.inc b/l10n/he/metadata.inc
new file mode 100644
index 000000000..3493c0b7c
--- /dev/null
+++ b/l10n/he/metadata.inc
@@ -0,0 +1,8 @@
+
+
+ he
+ קורא PDF
+ הצגת קבצי PDF ישירות ב־Firefox באמצעות HTML5.
+
+
+
diff --git a/l10n/he/viewer.properties b/l10n/he/viewer.properties
new file mode 100644
index 000000000..974343e04
--- /dev/null
+++ b/l10n/he/viewer.properties
@@ -0,0 +1,46 @@
+bookmark.title=דף נוכחי (העתקה או פתיחה בחלון חדש)
+previous.title=דף קודם
+next.title=דף הבא
+print.title=הדפסה
+download.title=הורדה
+zoom_out.title=התרחקות
+zoom_in.title=התקרבות
+error_more_info=יותר מידע
+error_less_info=פחות מידע
+error_close=סגירה
+error_build=בניית PDF.JS: {{build}}
+error_message=הודעה: {{message}}
+error_stack=מחסנית: {{stack}}
+error_file=קובץ: {{file}}
+error_line=שורה: {{line}}
+page_scale_width=רוחב דף
+page_scale_fit=גודל דף
+page_scale_auto=התקרבות אוטומטית
+page_scale_actual=גודל אמיתי
+toggle_slider.title=מתג החלקה
+thumbs.title=הצגת תמונות ממוזערות
+outline.title=הצגת מתאר מסמך
+loading=בטעינה... {{percent}}%
+loading_error_indicator=שגיאה
+loading_error=אירעה שגיאה בעת טעינת קובץ PDF.
+rendering_error=אירעה שגיאה בעת עיבוד הדף.
+page_label=דף:
+page_of=מתוך {{pageCount}}
+no_outline=אין מתאר זמין
+open_file.title=פתיחת קובץ
+text_annotation_type=[{{type}} Annotation]
+toggle_slider_label=מתג החלקה
+thumbs_label=תמונות ממוזערות
+outline_label=מתאר מסמך
+bookmark_label=תצוגה נוכחית
+previous_label=קודם
+next_label=הבא
+print_label=הדפסה
+download_label=הורדה
+zoom_out_label=התרחקות
+zoom_in_label=התקרבות
+zoom.title=מרחק מתצוגה
+thumb_page_title=דף {{page}}
+thumb_page_canvas=תמונה ממוזערת של דף {{page}}
+request_password=קובץ PDF מוגן בססמה:
+open_file_label=פתיחה
diff --git a/l10n/ja/metadata.inc b/l10n/ja/metadata.inc
new file mode 100644
index 000000000..7ca474685
--- /dev/null
+++ b/l10n/ja/metadata.inc
@@ -0,0 +1,8 @@
+
+
+ ja
+ PDF Viewer
+ HTML5を使用して、Firefoxで直接PDFファイルを表示します。
+
+
+
diff --git a/l10n/ja/viewer.properties b/l10n/ja/viewer.properties
new file mode 100644
index 000000000..4bbe8c48b
--- /dev/null
+++ b/l10n/ja/viewer.properties
@@ -0,0 +1,45 @@
+bookmark.title=現在のビュー (コピーまたは新しいウインドウで開く)
+previous.title=前のページ
+next.title=次のページ
+print.title=印刷
+download.title=ダウンロード
+zoom_out.title=縮小
+zoom_in.title=拡大
+error_more_info=詳細情報
+error_less_info=詳細情報の非表示
+error_close=閉じる
+error_build=PDF.JS Build: {{build}}
+error_message=メッセージ: {{message}}
+error_stack=スタック: {{stack}}
+error_file=ファイル: {{file}}
+error_line=ライン: {{line}}
+page_scale_width=幅に合わせる
+page_scale_fit=ページのサイズに合わせる
+page_scale_auto=自動ズーム
+page_scale_actual=実際のサイズ
+toggle_slider.title=サイドバーの切り替え
+thumbs.title=縮小版を表示
+outline.title=文書の目次を表示
+loading=読み込み中... {{percent}}%
+loading_error_indicator=エラー
+loading_error=PDFの読み込み中にエラーが発生しました
+rendering_error=ページのレンダリング中にエラーが発生しました
+page_label=ページ:
+page_of=of {{pageCount}}
+no_outline=利用可能な目次はありません
+open_file.title=ファイルを開く
+text_annotation_type=[{{type}} 注釈]
+toggle_slider_label=サイドバーの切り替え
+thumbs_label=縮小版
+outline_label=文書の目次
+bookmark_label=現在のビュー
+previous_label=前へ
+next_label=次へ
+print_label=印刷
+download_label=ダウンロード
+zoom_out_label=縮小
+zoom_in_label=拡大
+zoom.title=ズーム
+thumb_page_title=ページ {{page}}
+thumb_page_canvas=ページの縮小版 {{page}}
+request_password=PDFはパスワードによって保護されています
diff --git a/l10n/ru/viewer.properties b/l10n/ru/viewer.properties
index a946cde98..796add2af 100644
--- a/l10n/ru/viewer.properties
+++ b/l10n/ru/viewer.properties
@@ -40,3 +40,6 @@ download_label=Загрузить
zoom_out_label=Уменьшить
zoom_in_label=Увеличить
zoom.title=Масштаб
+thumb_page_title=Страница {{page}}
+thumb_page_canvas=Уменьшенное изображение страницы {{page}}
+request_password=PDF защищён паролем:
diff --git a/l10n/sv/metadata.inc b/l10n/sv/metadata.inc
new file mode 100644
index 000000000..6753d83e9
--- /dev/null
+++ b/l10n/sv/metadata.inc
@@ -0,0 +1,8 @@
+
+
+ sv
+ PDF Läsare
+ Använder HTML5 för att visa PDF filer direkt i Firefox.
+
+
+
diff --git a/l10n/sv/viewer.properties b/l10n/sv/viewer.properties
new file mode 100644
index 000000000..3aadfd44f
--- /dev/null
+++ b/l10n/sv/viewer.properties
@@ -0,0 +1,45 @@
+bookmark.title=Aktuell vy (visa eller öppna i nytt fönster)
+previous.title=Föregående sida
+next.title=Nästa sida
+print.title=Skriv ut
+download.title=Ladda ner
+zoom_out.title=Zooma ut
+zoom_in.title=Zooma in
+error_more_info=Mer information
+error_less_info=Mindre information
+error_close=Stäng
+error_build=PDF.JS bygge: {{build}}
+error_message=Meddelande: {{message}}
+error_stack=Stack: {{stack}}
+error_file=Fil: {{file}}
+error_line=Rad: {{line}}
+page_scale_width=Sidbredd
+page_scale_fit=Passa sida
+page_scale_auto=Automatisk zoom
+page_scale_actual=Faktisk storlek
+toggle_slider.title=Visa/Dölj panel
+thumbs.title=Visa miniatyrer
+outline.title=Visa dokumentdisposition
+loading=Laddar... {{percent}}%
+loading_error_indicator=Fel
+loading_error=Ett fel inträffade när PDF dokumentet laddades.
+rendering_error=Ett fel inträffade när PDF dokumentet renderades.
+page_label=Sida:
+page_of=av {{pageCount}}
+no_outline=Ingen dokumentdisposition tillgänglig
+open_file.title=Öppna fil
+text_annotation_type=[{{type}} Annotering]
+toggle_slider_label=Visa/Dölj panel
+thumbs_label=Miniatyrer
+outline_label=Disposition
+bookmark_label=Aktuell vy
+previous_label=Föregående
+next_label=Nästa
+print_label=Skriv ut
+download_label=Ladda ner
+zoom_out_label=Zooma ut
+zoom_in_label=Zooma in
+zoom.title=Zooma
+thumb_page_title=Sida {{page}}
+thumb_page_canvas=Miniatyr av sida {{page}}
+request_password=PDF dokumentet är skyddat av ett lösenord:
\ No newline at end of file
diff --git a/l10n/zh-CN/metadata.inc b/l10n/zh-CN/metadata.inc
new file mode 100644
index 000000000..04be27c78
--- /dev/null
+++ b/l10n/zh-CN/metadata.inc
@@ -0,0 +1,8 @@
+
+
+ zh-CN
+ PDF 查看器
+ 使用 HTML5 来支持在 Firefox 中直接显示 PDF 文件。
+
+
+
diff --git a/l10n/zh-CN/viewer.properties b/l10n/zh-CN/viewer.properties
new file mode 100644
index 000000000..8b670fe12
--- /dev/null
+++ b/l10n/zh-CN/viewer.properties
@@ -0,0 +1,45 @@
+bookmark.title=当前视图(复制或在新窗口中打开)
+previous.title=上一页
+next.title=下一页
+print.title=打印
+download.title=下载
+zoom_out.title=缩小
+zoom_in.title=放大
+error_more_info=更多信息
+error_less_info=更少信息
+error_close=关闭
+error_build=PDF.JS Build: {{build}}
+error_message=Message: {{message}}
+error_stack=Stack: {{stack}}
+error_file=File: {{file}}
+error_line=Line: {{line}}
+page_scale_width=适合页宽
+page_scale_fit=适合页面
+page_scale_auto=自动缩放
+page_scale_actual=实际大小
+toggle_slider.title=切换侧栏
+thumbs.title=显示缩略图
+outline.title=显示文档大纲
+loading=正在载入... {{percent}}%
+loading_error_indicator=错误
+loading_error=载入PDF时发生错误。
+rendering_error=呈现页面时发生错误。
+page_label=页:
+page_of=/ {{pageCount}}
+no_outline=无可用大纲
+open_file.title=打开文件
+text_annotation_type=[{{type}} 注解]
+toggle_slider_label=切换侧栏
+thumbs_label=缩略图
+outline_label=文档大纲
+bookmark_label=当前视图
+previous_label=向上
+next_label=向下
+print_label=打印
+download_label=下载
+zoom_out_label=缩小
+zoom_in_label=放大
+zoom.title=缩放
+thumb_page_title=页码 {{page}}
+thumb_page_canvas=页面 {{page}} 的缩略图
+request_password=PDF 被密码保护:
diff --git a/l10n/zh-TW/metadata.inc b/l10n/zh-TW/metadata.inc
new file mode 100644
index 000000000..6ef722be8
--- /dev/null
+++ b/l10n/zh-TW/metadata.inc
@@ -0,0 +1,8 @@
+
+
+ zh-TW
+ PDF 瀏覽器
+ 利用 HTML5 技術在 Firefox 中直接顯示 PDF 格式檔案。
+
+
+
diff --git a/l10n/zh-TW/viewer.properties b/l10n/zh-TW/viewer.properties
new file mode 100644
index 000000000..0f2182594
--- /dev/null
+++ b/l10n/zh-TW/viewer.properties
@@ -0,0 +1,45 @@
+bookmark.title=目前檢視(複製或在新視窗中開啟)
+previous.title=上一頁
+next.title=下一頁
+print.title=列印
+download.title=下載
+zoom_out.title=縮小
+zoom_in.title=放大
+error_more_info=更多資訊
+error_less_info=更少資訊
+error_close=關閉
+error_build=PDF.JS 版本號: {{build}}
+error_message=錯誤信息: {{message}}
+error_stack=堆疊: {{stack}}
+error_file=檔案: {{file}}
+error_line=行數: {{line}}
+page_scale_width=符合頁寬
+page_scale_fit=符合頁面
+page_scale_auto=自動縮放
+page_scale_actual=實際大小
+toggle_slider.title=切換側邊欄
+thumbs.title=顯示縮圖
+outline.title=顯示文件綱要
+loading=正在載入... {{percent}}%
+loading_error_indicator=錯誤
+loading_error=載入PDF檔案時發生錯誤。
+rendering_error=渲染頁面時發生錯誤。
+page_label=頁次:
+page_of=, 共 {{pageCount}} 頁
+no_outline=無可用的綱要
+open_file.title=開啟檔案
+text_annotation_type=[{{type}} 註解]
+toggle_slider_label=切換側邊欄
+thumbs_label=縮圖
+outline_label=文件綱要
+bookmark_label=目前檢視
+previous_label=上一頁
+next_label=下一頁
+print_label=列印
+download_label=下載
+zoom_out_label=縮小
+zoom_in_label=放大
+zoom.title=縮放
+thumb_page_title=第 {{page}} 頁
+thumb_page_canvas=第 {{page}} 頁的縮圖
+request_password=PDF 檔案受密碼保護:
diff --git a/make.js b/make.js
index 88f7c69a0..227abed4b 100755
--- a/make.js
+++ b/make.js
@@ -9,7 +9,11 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root
LOCALE_SRC_DIR = 'l10n/',
GH_PAGES_DIR = BUILD_DIR + 'gh-pages/',
REPO = 'git@github.com:mozilla/pdf.js.git',
- PYTHON_BIN = 'python2.7';
+ PYTHON_BIN = 'python2.7',
+ MOZCENTRAL_PREF_PREFIX = 'pdfjs',
+ FIREFOX_PREF_PREFIX = 'extensions.uriloader@pdf.js',
+ MOZCENTRAL_STREAM_CONVERTER_ID = 'd0c5195d-e798-49d4-b1d3-9324328b2291',
+ FIREFOX_STREAM_CONVERTER_ID = '6457a96b-2d68-439a-bcfa-44465fbcdbb1';
//
// make all
@@ -106,6 +110,10 @@ target.locale = function() {
cp(path + '/viewer.properties', EXTENSION_LOCALE_OUTPUT + '/' + locale);
}
+ if (test('-f', path + '/chrome.properties')) {
+ cp(path + '/chrome.properties', EXTENSION_LOCALE_OUTPUT + '/' + locale);
+ }
+
if (test('-f', path + '/metadata.inc')) {
var metadata = cat(path + '/metadata.inc');
metadataContent += metadata;
@@ -348,6 +356,9 @@ target.firefox = function() {
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/install.rdf');
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/update.rdf');
+ sed('-i', /PDFJSSCRIPT_STREAM_CONVERTER_ID/, FIREFOX_STREAM_CONVERTER_ID, FIREFOX_BUILD_DIR + 'components/PdfStreamConverter.js');
+ sed('-i', /PDFJSSCRIPT_PREF_PREFIX/, FIREFOX_PREF_PREFIX, FIREFOX_BUILD_DIR + 'components/PdfStreamConverter.js');
+
// Update localized metadata
var localizedMetadata = cat(EXTENSION_SRC_DIR + '/firefox/metadata.inc');
sed('-i', /.*PDFJS_LOCALIZED_METADATA.*\n/, localizedMetadata, FIREFOX_BUILD_DIR + '/install.rdf');
@@ -377,26 +388,24 @@ target.mozcentral = function() {
echo('### Building mozilla-central extension');
var MOZCENTRAL_DIR = BUILD_DIR + 'mozcentral/',
- MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + 'browser/app/profile/extensions/uriloader@pdf.js/',
+ MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + 'browser/extensions/pdfjs/',
MOZCENTRAL_CONTENT_DIR = MOZCENTRAL_EXTENSION_DIR + 'content/',
MOZCENTRAL_L10N_DIR = MOZCENTRAL_DIR + 'browser/locales/en-US/pdfviewer/',
+ MOZCENTRAL_TEST_DIR = MOZCENTRAL_EXTENSION_DIR + 'test/',
FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/',
FIREFOX_EXTENSION_FILES_TO_COPY =
- ['*.js',
+ ['components/*.js',
'*.svg',
'*.png',
'*.manifest',
- 'install.rdf.in',
'README.mozilla',
'components',
'../../LICENSE'],
DEFAULT_LOCALE_FILES =
- [LOCALE_SRC_DIR + 'en-US/viewer.properties'],
+ [LOCALE_SRC_DIR + 'en-US/viewer.properties',
+ LOCALE_SRC_DIR + 'en-US/chrome.properties'],
FIREFOX_MC_EXTENSION_FILES =
- ['bootstrap.js',
- 'icon.png',
- 'icon64.png',
- 'chrome.manifest',
+ ['chrome.manifest',
'components',
'content',
'LICENSE'];
@@ -415,6 +424,8 @@ target.mozcentral = function() {
// Copy extension files
cd('extensions/firefox');
cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + MOZCENTRAL_EXTENSION_DIR);
+ mv('-f', ROOT_DIR + MOZCENTRAL_EXTENSION_DIR + '/chrome-mozcentral.manifest',
+ ROOT_DIR + MOZCENTRAL_EXTENSION_DIR + '/chrome.manifest')
cd(ROOT_DIR);
// Copy a standalone version of pdf.js inside the content directory
@@ -446,9 +457,11 @@ target.mozcentral = function() {
cp(DEFAULT_LOCALE_FILES, MOZCENTRAL_L10N_DIR);
// Update the build version number
- sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, MOZCENTRAL_EXTENSION_DIR + 'install.rdf.in');
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, MOZCENTRAL_EXTENSION_DIR + 'README.mozilla');
+ sed('-i', /PDFJSSCRIPT_STREAM_CONVERTER_ID/, MOZCENTRAL_STREAM_CONVERTER_ID, MOZCENTRAL_EXTENSION_DIR + 'components/PdfStreamConverter.js');
+ sed('-i', /PDFJSSCRIPT_PREF_PREFIX/, MOZCENTRAL_PREF_PREFIX, MOZCENTRAL_EXTENSION_DIR + 'components/PdfStreamConverter.js');
+
// List all files for mozilla-central
cd(MOZCENTRAL_EXTENSION_DIR);
var extensionFiles = '';
@@ -457,6 +470,11 @@ target.mozcentral = function() {
extensionFiles += file+'\n';
});
extensionFiles.to('extension-files');
+ cd(ROOT_DIR);
+
+ // Copy test files
+ mkdir('-p', MOZCENTRAL_TEST_DIR);
+ cp('-Rf', 'test/mozcentral/*', MOZCENTRAL_TEST_DIR);
};
//
@@ -490,6 +508,9 @@ target.chrome = function() {
// Copy a standalone version of pdf.js inside the content directory
cp(BUILD_TARGET, CHROME_BUILD_CONTENT_DIR + BUILD_DIR);
cp('-R', EXTENSION_WEB_FILES, CHROME_BUILD_CONTENT_DIR + '/web');
+ // Replacing the l10n.js file with regular gh-pages one
+ rm(CHROME_BUILD_CONTENT_DIR + '/web/l10n.js');
+ cp('external/webL10n/l10n.js', CHROME_BUILD_CONTENT_DIR + '/web');
cp('web/locale.properties', CHROME_BUILD_CONTENT_DIR + '/web');
mv('-f', CHROME_BUILD_CONTENT_DIR + '/web/viewer-production.html',
CHROME_BUILD_CONTENT_DIR + '/web/viewer.html');
diff --git a/src/api.js b/src/api.js
index 976faf898..59f9661bf 100644
--- a/src/api.js
+++ b/src/api.js
@@ -7,20 +7,46 @@
* is used, which means it must follow the same origin rules that any XHR does
* e.g. No cross domain requests without CORS.
*
- * @param {string|TypedAray} source Either a url to a PDF is located or a
- * typed array (Uint8Array) already populated with data.
- * @param {Object} headers An object containing the http headers like this:
- * { Authorization: "BASIC XXX" }.
+ * @param {string|TypedAray|object} source Can be an url to where a PDF is
+ * located, a typed array (Uint8Array) already populated with data or
+ * and parameter object with the following possible fields:
+ * - url - The URL of the PDF.
+ * - data - A typed array with PDF data.
+ * - httpHeaders - Basic authentication headers.
+ * - password - For decrypting password-protected PDFs.
+ *
* @return {Promise} A promise that is resolved with {PDFDocumentProxy} object.
*/
-PDFJS.getDocument = function getDocument(source, headers) {
+PDFJS.getDocument = function getDocument(source) {
+ var url, data, headers, password, parameters = {};
+ if (typeof source === 'string') {
+ url = source;
+ } else if (isArrayBuffer(source)) {
+ data = source;
+ } else if (typeof source === 'object') {
+ url = source.url;
+ data = source.data;
+ headers = source.httpHeaders;
+ password = source.password;
+ parameters.password = password || null;
+
+ if (!url && !data)
+ error('Invalid parameter array, need either .data or .url');
+ } else {
+ error('Invalid parameter in getDocument, need either Uint8Array, ' +
+ 'string or a parameter object');
+ }
+
var promise = new PDFJS.Promise();
var transport = new WorkerTransport(promise);
- if (typeof source === 'string') {
+ if (data) {
+ // assuming the data is array, instantiating directly from it
+ transport.sendData(data, parameters);
+ } else if (url) {
// fetch url
PDFJS.getPdf(
{
- url: source,
+ url: url,
progress: function getPDFProgress(evt) {
if (evt.lengthComputable)
promise.progress({
@@ -35,12 +61,10 @@ PDFJS.getDocument = function getDocument(source, headers) {
headers: headers
},
function getPDFLoad(data) {
- transport.sendData(data);
+ transport.sendData(data, parameters);
});
- } else {
- // assuming the source is array, instantiating directly from it
- transport.sendData(source);
}
+
return promise;
};
@@ -122,6 +146,11 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
});
return promise;
},
+ isEncrypted: function PDFDocumentProxy_isEncrypted() {
+ var promise = new PDFJS.Promise();
+ promise.resolve(this.pdfInfo.encrypted);
+ return promise;
+ },
destroy: function PDFDocumentProxy_destroy() {
this.transport.destroy();
}
@@ -429,7 +458,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
messageHandler.send('test', testObj);
return;
} catch (e) {
- warn('The worker has been disabled.');
+ info('The worker has been disabled.');
}
}
// Either workers are disabled, not supported or have thrown an exception.
@@ -473,6 +502,14 @@ var WorkerTransport = (function WorkerTransportClosure() {
this.workerReadyPromise.resolve(pdfDocument);
}, this);
+ messageHandler.on('NeedPassword', function transportPassword(data) {
+ this.workerReadyPromise.reject(data.exception.message, data.exception);
+ }, this);
+
+ messageHandler.on('IncorrectPassword', function transportBadPass(data) {
+ this.workerReadyPromise.reject(data.exception.message, data.exception);
+ }, this);
+
messageHandler.on('GetPage', function transportPage(data) {
var pageInfo = data.pageInfo;
var page = new PDFPageProxy(pageInfo, this);
@@ -575,8 +612,8 @@ var WorkerTransport = (function WorkerTransportClosure() {
});
},
- sendData: function WorkerTransport_sendData(data) {
- this.messageHandler.send('GetDocRequest', data);
+ sendData: function WorkerTransport_sendData(data, params) {
+ this.messageHandler.send('GetDocRequest', {data: data, params: params});
},
getPage: function WorkerTransport_getPage(pageNumber, promise) {
diff --git a/src/canvas.js b/src/canvas.js
index 9d470fbec..85e82aaa5 100644
--- a/src/canvas.js
+++ b/src/canvas.js
@@ -343,10 +343,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.ctx.webkitLineDashOffset = dashPhase;
},
setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
- TODO('set rendering intent: ' + intent);
+ // Maybe if we one day fully support color spaces this will be important
+ // for now we can ignore.
+ // TODO set rendering intent?
},
setFlatness: function CanvasGraphics_setFlatness(flatness) {
- TODO('set flatness: ' + flatness);
+ // There's no way to control this with canvas, but we can safely ignore.
+ // TODO set flatness?
},
setGState: function CanvasGraphics_setGState(states) {
for (var i = 0, ii = states.length; i < ii; i++) {
@@ -841,7 +844,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
text.length += shownText.length;
}
} else {
- malformed('TJ array element ' + e + ' is not string or num');
+ error('TJ array element ' + e + ' is not string or num');
}
}
diff --git a/src/colorspace.js b/src/colorspace.js
index 8d8290109..26e7c1f0f 100644
--- a/src/colorspace.js
+++ b/src/colorspace.js
@@ -451,12 +451,12 @@ var LabCS = (function LabCSClosure() {
error('Invalid WhitePoint components, no fallback available');
if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
- warn('Invalid BlackPoint, falling back to default');
+ info('Invalid BlackPoint, falling back to default');
this.XB = this.YB = this.ZB = 0;
}
if (this.amin > this.amax || this.bmin > this.bmax) {
- warn('Invalid Range, falling back to defaults');
+ info('Invalid Range, falling back to defaults');
this.amin = -100;
this.amax = 100;
this.bmin = -100;
diff --git a/src/core.js b/src/core.js
index e1a9476a3..8e07078ef 100644
--- a/src/core.js
+++ b/src/core.js
@@ -7,7 +7,7 @@ var globalScope = (typeof window === 'undefined') ? this : window;
var isWorker = (typeof window == 'undefined');
-var ERRORS = 0, WARNINGS = 1, TODOS = 5;
+var ERRORS = 0, WARNINGS = 1, INFOS = 5;
var verbosity = WARNINGS;
// The global PDFJS object exposes the API
@@ -362,19 +362,19 @@ var Page = (function PageClosure() {
* `PDFDocument` objects on the main thread created.
*/
var PDFDocument = (function PDFDocumentClosure() {
- function PDFDocument(arg, callback) {
+ function PDFDocument(arg, password) {
if (isStream(arg))
- init.call(this, arg);
+ init.call(this, arg, password);
else if (isArrayBuffer(arg))
- init.call(this, new Stream(arg));
+ init.call(this, new Stream(arg), password);
else
error('PDFDocument: Unknown argument type');
}
- function init(stream) {
+ function init(stream, password) {
assertWellFormed(stream.length > 0, 'stream must have data');
this.stream = stream;
- this.setup();
+ this.setup(password);
this.acroForm = this.catalog.catDict.get('AcroForm');
}
@@ -465,11 +465,12 @@ var PDFDocument = (function PDFDocumentClosure() {
}
// May not be a PDF file, continue anyway.
},
- setup: function PDFDocument_setup(ownerPassword, userPassword) {
+ setup: function PDFDocument_setup(password) {
this.checkHeader();
var xref = new XRef(this.stream,
this.startXRef,
- this.mainXRefEntriesOffset);
+ this.mainXRefEntriesOffset,
+ password);
this.xref = xref;
this.catalog = new Catalog(xref);
},
diff --git a/src/crypto.js b/src/crypto.js
index dcd820554..c86551f36 100644
--- a/src/crypto.js
+++ b/src/crypto.js
@@ -556,7 +556,9 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
var encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
ownerPassword, userPassword, flags,
revision, keyLength, encryptMetadata);
- if (!encryptionKey && password) {
+ if (!encryptionKey && !password) {
+ throw new PasswordException('No password given', 'needpassword');
+ } else if (!encryptionKey && password) {
// Attempting use the password as an owner password
var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword,
revision, keyLength);
@@ -566,7 +568,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
}
if (!encryptionKey)
- error('incorrect password or encryption data');
+ throw new PasswordException('Incorrect Password', 'incorrectpassword');
this.encryptionKey = encryptionKey;
diff --git a/src/evaluator.js b/src/evaluator.js
index f99e79a37..8849a994a 100644
--- a/src/evaluator.js
+++ b/src/evaluator.js
@@ -108,39 +108,21 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// Compatibility
BX: 'beginCompat',
- EX: 'endCompat'
+ EX: 'endCompat',
+
+ // (reserved partial commands for the lexer)
+ BM: null,
+ BD: null,
+ 'true': null,
+ fa: null,
+ fal: null,
+ fals: null,
+ 'false': null,
+ nu: null,
+ nul: null,
+ 'null': null
};
- function splitCombinedOperations(operations) {
- // Two or more operations can be combined together, trying to find which
- // operations were concatenated.
- var result = [];
- var opIndex = 0;
-
- if (!operations) {
- return null;
- }
-
- while (opIndex < operations.length) {
- var currentOp = '';
- for (var op in OP_MAP) {
- if (op == operations.substr(opIndex, op.length) &&
- op.length > currentOp.length) {
- currentOp = op;
- }
- }
-
- if (currentOp.length > 0) {
- result.push(operations.substr(opIndex, currentOp.length));
- opIndex += currentOp.length;
- } else {
- return null;
- }
- }
-
- return result;
- }
-
PartialEvaluator.prototype = {
getOperatorList: function PartialEvaluator_getOperatorList(stream,
resources,
@@ -285,39 +267,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
resources = resources || new Dict();
var xobjs = resources.get('XObject') || new Dict();
var patterns = resources.get('Pattern') || new Dict();
- var parser = new Parser(new Lexer(stream), false, xref);
+ var parser = new Parser(new Lexer(stream, OP_MAP), false, xref);
var res = resources;
- var hasNextObj = false, nextObjs;
var args = [], obj;
var TILING_PATTERN = 1, SHADING_PATTERN = 2;
while (true) {
- if (hasNextObj) {
- obj = nextObjs.pop();
- hasNextObj = (nextObjs.length > 0);
- } else {
- obj = parser.getObj();
- if (isEOF(obj))
- break;
- }
+ obj = parser.getObj();
+ if (isEOF(obj))
+ break;
if (isCmd(obj)) {
var cmd = obj.cmd;
var fn = OP_MAP[cmd];
- if (!fn) {
- // invalid content command, trying to recover
- var cmds = splitCombinedOperations(cmd);
- if (cmds) {
- cmd = cmds[0];
- fn = OP_MAP[cmd];
- // feeding other command on the next iteration
- hasNextObj = true;
- nextObjs = [];
- for (var idx = 1; idx < cmds.length; idx++) {
- nextObjs.push(Cmd.get(cmds[idx]));
- }
- }
- }
assertWellFormed(fn, 'Unknown command "' + cmd + '"');
// TODO figure out how to type-check vararg functions
@@ -457,6 +419,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
value[1]
]);
break;
+ case 'BM':
+ // We support the default so don't trigger the TODO.
+ if (!isName(value) || value.name != 'Normal')
+ TODO('graphic state operator ' + key);
+ break;
+ case 'SMask':
+ // We support the default so don't trigger the TODO.
+ if (!isName(value) || value.name != 'None')
+ TODO('graphic state operator ' + key);
+ break;
+ // Only generate info log messages for the following since
+ // they are unlikey to have a big impact on the rendering.
case 'OP':
case 'op':
case 'OPM':
@@ -469,14 +443,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
case 'HT':
case 'SM':
case 'SA':
- case 'BM':
- case 'SMask':
case 'AIS':
case 'TK':
- TODO('graphic state operator ' + key);
+ // TODO implement these operators.
+ info('graphic state operator ' + key);
break;
default:
- warn('Unknown graphic state operator ' + key);
+ info('Unknown graphic state operator ' + key);
break;
}
}
@@ -725,8 +698,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}
} else if (octet == 0x3E) {
if (token.length) {
- // XXX guessing chars size by checking number size in the CMap
- if (token.length <= 2 && properties.composite)
+ // Heuristic: guessing chars size by checking numbers sizes
+ // in the CMap entries.
+ if (token.length == 2 && properties.composite)
properties.wideChars = false;
if (token.length <= 4) {
diff --git a/src/fonts.js b/src/fonts.js
index f7c6e6f4e..72bcecbce 100644
--- a/src/fonts.js
+++ b/src/fonts.js
@@ -2706,9 +2706,9 @@ var Font = (function FontClosure() {
this.isSymbolicFont = false;
}
- // heuristics: if removed more than 2 glyphs encoding WinAnsiEncoding
- // does not set properly
- if (glyphsRemoved > 2) {
+ // heuristics: if removed more than 10 glyphs encoding WinAnsiEncoding
+ // does not set properly (broken PDFs have about 100 removed glyphs)
+ if (glyphsRemoved > 10) {
warn('Switching TrueType encoding to MacRomanEncoding for ' +
this.name + ' font');
encoding = Encodings.MacRomanEncoding;
@@ -4208,7 +4208,7 @@ var CFFFont = (function CFFFontClosure() {
this.properties = properties;
var parser = new CFFParser(file, properties);
- var cff = parser.parse();
+ var cff = parser.parse(true);
var compiler = new CFFCompiler(cff);
this.readExtra(cff);
try {
@@ -4299,7 +4299,7 @@ var CFFParser = (function CFFParserClosure() {
this.properties = properties;
}
CFFParser.prototype = {
- parse: function CFFParser_parse() {
+ parse: function CFFParser_parse(normalizeCIDData) {
var properties = this.properties;
var cff = new CFF();
this.cff = cff;
@@ -4354,6 +4354,21 @@ var CFFParser = (function CFFParserClosure() {
cff.charset = charset;
cff.encoding = encoding;
+ if (!cff.isCIDFont || !normalizeCIDData)
+ return cff;
+
+ // DirectWrite does not like CID fonts data. Trying to convert/flatten
+ // the font data and remove CID properties.
+ if (cff.fdArray.length !== 1)
+ error('Unable to normalize CID font in CFF data');
+
+ var fontDict = cff.fdArray[0];
+ fontDict.setByKey(17, topDict.getByName('CharStrings'));
+ cff.topDict = fontDict;
+ cff.isCIDFont = false;
+ delete cff.fdArray;
+ delete cff.fdSelect;
+
return cff;
},
parseHeader: function CFFParser_parseHeader() {
@@ -4364,7 +4379,7 @@ var CFFParser = (function CFFParserClosure() {
++offset;
if (offset != 0) {
- warn('cff data is shifted');
+ info('cff data is shifted');
bytes = bytes.subarray(offset);
this.bytes = bytes;
}
@@ -4952,9 +4967,9 @@ var CFFPrivateDict = (function CFFPrivateDictClosure() {
[[12, 17], 'LanguageGroup', 'num', 0],
[[12, 18], 'ExpansionFactor', 'num', 0.06],
[[12, 19], 'initialRandomSeed', 'num', 0],
- [19, 'Subrs', 'offset', null],
[20, 'defaultWidthX', 'num', 0],
- [21, 'nominalWidthX', 'num', 0]
+ [21, 'nominalWidthX', 'num', 0],
+ [19, 'Subrs', 'offset', null]
];
var tables = null;
function CFFPrivateDict(strings) {
diff --git a/src/obj.js b/src/obj.js
index 9b99eb8f7..3432ac68d 100644
--- a/src/obj.js
+++ b/src/obj.js
@@ -298,7 +298,7 @@ var Catalog = (function CatalogClosure() {
})();
var XRef = (function XRefClosure() {
- function XRef(stream, startXRef, mainXRefEntriesOffset) {
+ function XRef(stream, startXRef, mainXRefEntriesOffset, password) {
this.stream = stream;
this.entries = [];
this.xrefstms = {};
@@ -311,8 +311,7 @@ var XRef = (function XRefClosure() {
var encrypt = trailerDict.get('Encrypt');
if (encrypt) {
var fileId = trailerDict.get('ID');
- this.encrypt = new CipherTransformFactory(encrypt,
- fileId[0] /*, password */);
+ this.encrypt = new CipherTransformFactory(encrypt, fileId[0], password);
}
// get the root dictionary (catalog) object
diff --git a/src/parser.js b/src/parser.js
index 2855018a6..6c9b4e67f 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -264,8 +264,16 @@ var Parser = (function ParserClosure() {
})();
var Lexer = (function LexerClosure() {
- function Lexer(stream) {
+ function Lexer(stream, knownCommands) {
this.stream = stream;
+ // The PDFs might have "glued" commands with other commands, operands or
+ // literals, e.g. "q1". The knownCommands is a dictionary of the valid
+ // commands and their prefixes. The prefixes are built the following way:
+ // if there a command that is a prefix of the other valid command or
+ // literal (e.g. 'f' and 'false') the following prefixes must be included,
+ // 'fa', 'fal', 'fals'. The prefixes are not needed, if the command has no
+ // other commands or literals as a prefix. The knowCommands is optional.
+ this.knownCommands = knownCommands;
}
Lexer.isSpace = function Lexer_isSpace(ch) {
@@ -529,12 +537,18 @@ var Lexer = (function LexerClosure() {
// command
var str = ch;
+ var knownCommands = this.knownCommands;
+ var knownCommandFound = knownCommands && (str in knownCommands);
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) {
+ // stop if known command is found and next character does not make
+ // the str a command
+ if (knownCommandFound && !((str + ch) in knownCommands))
+ break;
stream.skip();
if (str.length == 128)
error('Command token too long: ' + str.length);
-
str += ch;
+ knownCommandFound = knownCommands && (str in knownCommands);
}
if (str == 'true')
return true;
diff --git a/src/stream.js b/src/stream.js
index 48c462fb2..b0e1beb34 100644
--- a/src/stream.js
+++ b/src/stream.js
@@ -1687,7 +1687,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (a1 > codingLine[codingPos]) {
if (a1 > this.columns) {
- warn('row is wrong length');
+ info('row is wrong length');
this.err = true;
a1 = this.columns;
}
@@ -1707,7 +1707,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (a1 > codingLine[codingPos]) {
if (a1 > this.columns) {
- warn('row is wrong length');
+ info('row is wrong length');
this.err = true;
a1 = this.columns;
}
@@ -1717,7 +1717,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
codingLine[codingPos] = a1;
} else if (a1 < codingLine[codingPos]) {
if (a1 < 0) {
- warn('invalid code');
+ info('invalid code');
this.err = true;
a1 = 0;
}
@@ -1879,7 +1879,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
this.eof = true;
break;
default:
- warn('bad 2d code');
+ info('bad 2d code');
this.addPixels(columns, 0);
this.err = true;
}
@@ -1942,7 +1942,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
for (var i = 0; i < 4; ++i) {
code1 = this.lookBits(12);
if (code1 != 1)
- warn('bad rtc code: ' + code1);
+ info('bad rtc code: ' + code1);
this.eatBits(12);
if (this.encoding > 0) {
this.lookBits(1);
@@ -2065,7 +2065,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (result[0] && result[2])
return result[1];
}
- warn('Bad two dim code');
+ info('Bad two dim code');
return EOF;
};
@@ -2098,7 +2098,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (result[0])
return result[1];
}
- warn('bad white code');
+ info('bad white code');
this.eatBits(1);
return 1;
};
@@ -2135,7 +2135,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (result[0])
return result[1];
}
- warn('bad black code');
+ info('bad black code');
this.eatBits(1);
return 1;
};
diff --git a/src/util.js b/src/util.js
index 140b18cf1..b8359b5d1 100644
--- a/src/util.js
+++ b/src/util.js
@@ -3,6 +3,8 @@
'use strict';
+// Use only for debugging purposes. This should not be used in any code that is
+// in mozilla master.
function log(msg) {
if (console && console.log)
console.log(msg);
@@ -10,9 +12,36 @@ function log(msg) {
print(msg);
}
+// A notice for devs that will not trigger the fallback UI. These are good
+// for things that are helpful to devs, such as warning that Workers were
+// disabled, which is important to devs but not end users.
+function info(msg) {
+ if (verbosity >= INFOS) {
+ log('Info: ' + msg);
+ PDFJS.LogManager.notify('info', msg);
+ }
+}
+
+// Non-fatal warnings that should trigger the fallback UI.
function warn(msg) {
- if (verbosity >= WARNINGS)
+ if (verbosity >= WARNINGS) {
log('Warning: ' + msg);
+ PDFJS.LogManager.notify('warn', msg);
+ }
+}
+
+// Fatal errors that should trigger the fallback UI and halt execution by
+// throwing an exception.
+function error(msg) {
+ log('Error: ' + msg);
+ log(backtrace());
+ PDFJS.LogManager.notify('error', msg);
+ throw new Error(msg);
+}
+
+// Missing features that should trigger the fallback UI.
+function TODO(what) {
+ warn('TODO: ' + what);
}
function backtrace() {
@@ -23,21 +52,6 @@ function backtrace() {
}
}
-function error(msg) {
- log('Error: ' + msg);
- log(backtrace());
- throw new Error(msg);
-}
-
-function TODO(what) {
- if (verbosity >= TODOS)
- log('TODO: ' + what);
-}
-
-function malformed(msg) {
- error('Malformed PDF: ' + msg);
-}
-
function assert(cond, msg) {
if (!cond)
error(msg);
@@ -47,9 +61,25 @@ function assert(cond, msg) {
// behavior is undefined.
function assertWellFormed(cond, msg) {
if (!cond)
- malformed(msg);
+ error(msg);
}
+var LogManager = PDFJS.LogManager = (function LogManagerClosure() {
+ var loggers = [];
+ return {
+ addLogger: function logManager_addLogger(logger) {
+ loggers.push(logger);
+ },
+ notify: function(type, message) {
+ for (var i = 0, ii = loggers.length; i < ii; i++) {
+ var logger = loggers[i];
+ if (logger[type])
+ logger[type](message);
+ }
+ }
+ };
+})();
+
function shadow(obj, prop, value) {
Object.defineProperty(obj, prop, { value: value,
enumerable: true,
@@ -58,6 +88,19 @@ function shadow(obj, prop, value) {
return value;
}
+var PasswordException = (function PasswordExceptionClosure() {
+ function PasswordException(msg, code) {
+ this.name = 'PasswordException';
+ this.message = msg;
+ this.code = code;
+ }
+
+ PasswordException.prototype = new Error();
+ PasswordException.constructor = PasswordException;
+
+ return PasswordException;
+})();
+
function bytesToString(bytes) {
var str = '';
var length = bytes.length;
@@ -456,7 +499,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
}
this.isResolved = true;
- this.data = data || null;
+ this.data = (typeof data !== 'undefined') ? data : null;
var callbacks = this.callbacks;
for (var i = 0, ii = callbacks.length; i < ii; i++) {
@@ -471,7 +514,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
}
},
- reject: function Promise_reject(reason) {
+ reject: function Promise_reject(reason, exception) {
if (this.isRejected) {
error('A Promise can be rejected only once ' + this.name);
}
@@ -484,7 +527,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
var errbacks = this.errbacks;
for (var i = 0, ii = errbacks.length; i < ii; i++) {
- errbacks[i].call(null, reason);
+ errbacks[i].call(null, reason, exception);
}
},
diff --git a/src/worker.js b/src/worker.js
index b3ba7671e..a93c36e95 100644
--- a/src/worker.js
+++ b/src/worker.js
@@ -11,10 +11,13 @@ function MessageHandler(name, comObj) {
var ah = this.actionHandler = {};
ah['console_log'] = [function ahConsoleLog(data) {
- console.log.apply(console, data);
+ console.log.apply(console, data);
}];
ah['console_error'] = [function ahConsoleError(data) {
- console.error.apply(console, data);
+ console.error.apply(console, data);
+ }];
+ ah['_warn'] = [function ah_Warn(data) {
+ warn(data);
}];
comObj.onmessage = function messageHandlerComObjOnMessage(event) {
@@ -88,14 +91,35 @@ var WorkerMessageHandler = {
handler.on('GetDocRequest', function wphSetupDoc(data) {
// Create only the model of the PDFDoc, which is enough for
// processing the content of the pdf.
- pdfModel = new PDFDocument(new Stream(data));
+ var pdfData = data.data;
+ var pdfPassword = data.params.password;
+ try {
+ pdfModel = new PDFDocument(new Stream(pdfData), pdfPassword);
+ } catch (e) {
+ if (e instanceof PasswordException) {
+ if (e.code === 'needpassword') {
+ handler.send('NeedPassword', {
+ exception: e
+ });
+ } else if (e.code === 'incorrectpassword') {
+ handler.send('IncorrectPassword', {
+ exception: e
+ });
+ }
+
+ return;
+ } else {
+ throw e;
+ }
+ }
var doc = {
numPages: pdfModel.numPages,
fingerprint: pdfModel.getFingerprint(),
destinations: pdfModel.catalog.destinations,
outline: pdfModel.catalog.documentOutline,
info: pdfModel.getDocumentInfo(),
- metadata: pdfModel.catalog.metadata
+ metadata: pdfModel.catalog.metadata,
+ encrypted: !!pdfModel.xref.encrypt
};
handler.send('GetDoc', {pdfInfo: doc});
});
@@ -240,6 +264,17 @@ var workerConsole = {
if (typeof window === 'undefined') {
globalScope.console = workerConsole;
+ // Add a logger so we can pass warnings on to the main thread, errors will
+ // throw an exception which will be forwarded on automatically.
+ PDFJS.LogManager.addLogger({
+ warn: function(msg) {
+ postMessage({
+ action: '_warn',
+ data: msg
+ });
+ }
+ });
+
var handler = new MessageHandler('worker_processor', this);
WorkerMessageHandler.setup(handler);
}
diff --git a/test/mozcentral/Makefile.in b/test/mozcentral/Makefile.in
new file mode 100644
index 000000000..8c9face17
--- /dev/null
+++ b/test/mozcentral/Makefile.in
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+relativesrcdir = browser/extensions/pdfjs/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_TEST_FILES = \
+ head.js \
+ browser_pdfjs_main.js \
+ file_pdfjs_test.pdf \
+ $(NULL)
+
+libs:: $(_BROWSER_TEST_FILES)
+ $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
diff --git a/test/mozcentral/browser_pdfjs_main.js b/test/mozcentral/browser_pdfjs_main.js
new file mode 100644
index 000000000..d3f5dd646
--- /dev/null
+++ b/test/mozcentral/browser_pdfjs_main.js
@@ -0,0 +1,102 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const RELATIVE_DIR = "browser/extensions/pdfjs/test/";
+const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
+
+function test() {
+ waitForExplicitFinish();
+
+ var tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf");
+ var newTabBrowser = gBrowser.getBrowserForTab(tab);
+ newTabBrowser.addEventListener("pagechange", function onPageChange() {
+ newTabBrowser.removeEventListener("pagechange", onPageChange, true);
+
+ var document = newTabBrowser.contentDocument,
+ window = newTabBrowser.contentWindow;
+
+ //
+ // Overall sanity tests
+ //
+ ok(document.querySelector('div#viewer'), "document content has viewer UI");
+ ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object");
+
+ //
+ // Sidebar: open
+ //
+ var sidebar = document.querySelector('button#sidebarToggle'),
+ outerContainer = document.querySelector('div#outerContainer');
+
+ sidebar.click();
+ ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click');
+
+ // Thumbnails are created asynchronously - wait for them
+ waitForElement(document, 'canvas#thumbnail2', function(error) {
+ if (error)
+ finish();
+
+ //
+ // Page change from thumbnail click
+ //
+ var pageNumber = document.querySelector('input#pageNumber');
+ is(parseInt(pageNumber.value), 1, 'initial page is 1');
+
+ var thumbnail = document.querySelector('canvas#thumbnail2');
+ ok(thumbnail, 'thumbnail2 is available');
+ if (thumbnail) {
+ thumbnail.click();
+ is(parseInt(pageNumber.value), 2, 'clicking on thumbnail changes page');
+ }
+
+ //
+ // Sidebar: close
+ //
+ sidebar.click();
+ ok(!outerContainer.classList.contains('sidebarOpen'), 'sidebar closes on click');
+
+ //
+ // Page change from prev/next buttons
+ //
+ var prevPage = document.querySelector('button#previous'),
+ nextPage = document.querySelector('button#next');
+
+ nextPage.click();
+ is(parseInt(pageNumber.value), 2, 'page increases after clicking on next');
+
+ prevPage.click();
+ is(parseInt(pageNumber.value), 1, 'page decreases after clicking on previous');
+
+ //
+ // Bookmark button
+ //
+ var viewBookmark = document.querySelector('a#viewBookmark');
+ viewBookmark.click();
+ ok(viewBookmark.href.length > 0, 'viewBookmark button has href');
+
+ //
+ // Zoom in/out
+ //
+ var zoomOut = document.querySelector('button.zoomOut'),
+ zoomIn = document.querySelector('button.zoomIn');
+
+ // Zoom in
+ var oldWidth = document.querySelector('canvas#page1').width;
+ zoomIn.click();
+ var newWidth = document.querySelector('canvas#page1').width;
+ ok(oldWidth < newWidth, 'zooming in increases page width (old: '+oldWidth+', new: '+newWidth+')');
+
+ // Zoom out
+ var oldWidth = document.querySelector('canvas#page1').width;
+ zoomOut.click();
+ var newWidth = document.querySelector('canvas#page1').width;
+ ok(oldWidth > newWidth, 'zooming out decreases page width (old: '+oldWidth+', new: '+newWidth+')');
+
+ finish();
+ });
+ }, true, true);
+
+ registerCleanupFunction(function() {
+ gBrowser.removeTab(tab);
+ });
+}
diff --git a/test/mozcentral/file_pdfjs_test.pdf b/test/mozcentral/file_pdfjs_test.pdf
new file mode 100644
index 000000000..ea5ebe395
Binary files /dev/null and b/test/mozcentral/file_pdfjs_test.pdf differ
diff --git a/test/mozcentral/head.js b/test/mozcentral/head.js
new file mode 100644
index 000000000..6ee421b2f
--- /dev/null
+++ b/test/mozcentral/head.js
@@ -0,0 +1,21 @@
+// Waits for element 'el' to exist in the DOM of 'doc' before executing 'callback'
+// Useful when elements are created asynchronously, e.g. after a Web Worker task
+function waitForElement(doc, el, callback) {
+ var time = 0,
+ interval = 10,
+ timeout = 5000;
+
+ var checkEl = setInterval(function() {
+ if (doc.querySelector(el)) {
+ clearInterval(checkEl);
+ if (callback) callback();
+ }
+
+ time += interval;
+ if (time > timeout) {
+ ok(false, 'waitForElement timed out on element: '+el);
+ clearInterval(checkEl);
+ if (callback) callback(true);
+ }
+ }, interval);
+}
diff --git a/test/pdfs/issue1629.pdf.link b/test/pdfs/issue1629.pdf.link
new file mode 100644
index 000000000..1d18104b5
--- /dev/null
+++ b/test/pdfs/issue1629.pdf.link
@@ -0,0 +1 @@
+http://is.muni.cz/www/20544/noty/sbm/Taize/Adoramus_te_Christe.pdf
diff --git a/test/pdfs/issue1709.pdf.link b/test/pdfs/issue1709.pdf.link
new file mode 100644
index 000000000..ca5121b2d
--- /dev/null
+++ b/test/pdfs/issue1709.pdf.link
@@ -0,0 +1 @@
+http://www.mft-online.de/files/medizinerreport_2012.pdf
diff --git a/test/pdfs/issue1721.pdf.link b/test/pdfs/issue1721.pdf.link
new file mode 100644
index 000000000..5a36166d8
--- /dev/null
+++ b/test/pdfs/issue1721.pdf.link
@@ -0,0 +1 @@
+http://www.lezarts.org/07oforhom/Faitsdivers/Hadopi/Le%20Rapport%20Hadopi,%20Intox.pdf
diff --git a/test/test_manifest.json b/test/test_manifest.json
index 5ec9e850f..25d2a2080 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -402,6 +402,14 @@
"link": true,
"type": "eq"
},
+ { "id": "issue1709",
+ "file": "pdfs/issue1709.pdf",
+ "md5": "84497bd23b7c82d03d2681a1cb1d9ed0",
+ "rounds": 1,
+ "pageLimit": 10,
+ "link": true,
+ "type": "eq"
+ },
{ "id": "issue1015",
"file": "pdfs/issue1015.pdf",
"md5": "b61503d1b445742b665212866afb60e2",
@@ -451,6 +459,13 @@
"link": true,
"type": "eq"
},
+ { "id": "issue1629",
+ "file": "pdfs/issue1629.pdf",
+ "md5": "0f2cbbf268383a377e95e6bbe36c6a9a",
+ "rounds": 1,
+ "link": true,
+ "type": "eq"
+ },
{ "id": "issue1169",
"file": "pdfs/issue1169.pdf",
"md5": "3df3ed21fd43ac7fdb21e2015c8a7809",
@@ -543,6 +558,14 @@
"link": false,
"type": "eq"
},
+ { "id": "issue1721",
+ "file": "pdfs/issue1721.pdf",
+ "md5": "b47177f9e5197a76ec498733ecab60e6",
+ "rounds": 1,
+ "pageLimit": 2,
+ "link": true,
+ "type": "eq"
+ },
{ "id": "issue1243",
"file": "pdfs/issue1243.pdf",
"md5": "130c849b83513d5ac5e03c6421fc7489",
diff --git a/test/unit/evaluator_spec.js b/test/unit/evaluator_spec.js
index 4ee0768a7..e31a525ac 100644
--- a/test/unit/evaluator_spec.js
+++ b/test/unit/evaluator_spec.js
@@ -78,6 +78,54 @@ describe('evaluator', function() {
expect(result.fnArray[1]).toEqual('save');
expect(result.fnArray[2]).toEqual('save');
});
+
+ it('should handle three glued operations #2', function() {
+ var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+ 'prefix');
+ var resources = new ResourcesMock();
+ resources.Res1 = {};
+ var stream = new StringStream('B*BBMC');
+ var result = evaluator.getOperatorList(stream, resources, []);
+
+ expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+ expect(result.fnArray.length).toEqual(3);
+ expect(result.fnArray[0]).toEqual('eoFillStroke');
+ expect(result.fnArray[1]).toEqual('fillStroke');
+ expect(result.fnArray[2]).toEqual('beginMarkedContent');
+ });
+
+ it('should handle glued operations and operands', function() {
+ var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+ 'prefix');
+ var stream = new StringStream('q5 Ts');
+ var result = evaluator.getOperatorList(stream, new ResourcesMock(), []);
+
+ expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+ expect(result.fnArray.length).toEqual(2);
+ expect(result.fnArray[0]).toEqual('save');
+ expect(result.fnArray[1]).toEqual('setTextRise');
+ expect(result.argsArray.length).toEqual(2);
+ expect(result.argsArray[1].length).toEqual(1);
+ expect(result.argsArray[1][0]).toEqual(5);
+ });
+
+ it('should handle glued operations and literals', function() {
+ var evaluator = new PartialEvaluator(new XrefMock(), new HandlerMock(),
+ 'prefix');
+ var stream = new StringStream('trueifalserinulli');
+ var result = evaluator.getOperatorList(stream, new ResourcesMock(), []);
+
+ expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+ expect(result.fnArray.length).toEqual(3);
+ expect(result.fnArray[0]).toEqual('setFlatness');
+ expect(result.fnArray[1]).toEqual('setRenderingIntent');
+ expect(result.fnArray[2]).toEqual('setFlatness');
+ expect(result.argsArray.length).toEqual(3);
+ expect(result.argsArray[0].length).toEqual(1);
+ expect(result.argsArray[0][0]).toEqual(true);
+ expect(result.argsArray[1].length).toEqual(1);
+ expect(result.argsArray[1][0]).toEqual(false);
+ });
});
});
diff --git a/web/compatibility.js b/web/compatibility.js
index 5c192c9a9..528841bb6 100644
--- a/web/compatibility.js
+++ b/web/compatibility.js
@@ -120,6 +120,9 @@
return new Uint8Array(new VBArray(this.responseBody).toArray());
}
});
+ Object.defineProperty(xhrPrototype, 'overrideMimeType', {
+ value: function xmlHttpRequestOverrideMimeType(mimeType) {}
+ });
return;
}
@@ -217,15 +220,84 @@
var div = document.createElement('div');
if ('dataset' in div)
return; // dataset property exists
- var oldCreateElement = document.createElement;
- document.createElement = function newCreateElement() {
- var result = oldCreateElement.apply(document, arguments);
- if (arguments[0] === 'div') {
- // creating dataset property for the div elements
- result.dataset = {};
+
+ Object.defineProperty(HTMLElement.prototype, 'dataset', {
+ get: function() {
+ if (this._dataset)
+ return this._dataset;
+
+ var dataset = {};
+ for (var j = 0, jj = this.attributes.length; j < jj; j++) {
+ var attribute = this.attributes[j];
+ if (attribute.name.substring(0, 5) != 'data-')
+ continue;
+ var key = attribute.name.substring(5).replace(/\-([a-z])/g,
+ function(all, ch) { return ch.toUpperCase(); });
+ dataset[key] = attribute.value;
+ }
+
+ Object.defineProperty(this, '_dataset', {
+ value: dataset,
+ writable: false,
+ enumerable: false
+ });
+ return dataset;
+ },
+ enumerable: true
+ });
+})();
+
+// HTMLElement classList property
+(function checkClassListProperty() {
+ var div = document.createElement('div');
+ if ('classList' in div)
+ return; // classList property exists
+
+ function changeList(element, itemName, add, remove) {
+ var s = element.className || '';
+ var list = s.split(/\s+/g);
+ if (list[0] == '') list.shift();
+ var index = list.indexOf(itemName);
+ if (index < 0 && add)
+ list.push(itemName);
+ if (index >= 0 && remove)
+ list.splice(index, 1);
+ element.className = list.join(' ');
+ }
+
+ var classListPrototype = {
+ add: function(name) {
+ changeList(this.element, name, true, false);
+ },
+ remove: function(name) {
+ changeList(this.element, name, false, true);
+ },
+ toggle: function(name) {
+ changeList(this.element, name, true, true);
}
- return result;
};
+
+ Object.defineProperty(HTMLElement.prototype, 'classList', {
+ get: function() {
+ if (this._classList)
+ return this._classList;
+
+ var classList = Object.create(classListPrototype, {
+ element: {
+ value: this,
+ writable: false,
+ enumerable: true
+ }
+ });
+ Object.defineProperty(this, '_classList', {
+ value: classList,
+ writable: false,
+ enumerable: false
+ });
+ return classList;
+ },
+ enumerable: true
+ });
})();
// Check console compatability
@@ -252,3 +324,17 @@
document.addEventListener('click', ignoreIfTargetDisabled, true);
}
})();
+
+// Checks if navigator.language is supported
+(function checkNavigatorLanguage() {
+ if ('language' in navigator)
+ return;
+ Object.defineProperty(navigator, 'language', {
+ get: function navigatorLanguage() {
+ var language = navigator.userLanguage || 'en-US';
+ return language.substring(0, 2).toLowerCase() +
+ language.substring(2).toUpperCase();
+ },
+ enumerable: true
+ });
+})();
diff --git a/web/images/toolbarButton-openFile.png b/web/images/toolbarButton-openFile.png
new file mode 100644
index 000000000..12ce45f87
Binary files /dev/null and b/web/images/toolbarButton-openFile.png differ
diff --git a/web/viewer.css b/web/viewer.css
index 4fc426c64..0853986c4 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -428,6 +428,10 @@ html[dir='rtl'] .dropdownToolbarButton {
background: transparent;
}
+.dropdownToolbarButton > select > option {
+ background: hsl(0,0%,24%);
+}
+
#customScaleOption {
display: none;
}
@@ -501,6 +505,11 @@ html[dir='rtl'] .toolbarButton.pageDown::before {
content: url(images/toolbarButton-print.png);
}
+.toolbarButton.openFile::before {
+ display: inline-block;
+ content: url(images/toolbarButton-openFile.png);
+}
+
.toolbarButton.download::before {
display: inline-block;
content: url(images/toolbarButton-download.png);
@@ -985,3 +994,76 @@ canvas {
#PDFBug table {
font-size: 10px;
}
+
+@media print {
+ #sidebarContainer, .toolbar, #loadingBox, #errorWrapper, .textLayer {
+ display: none;
+ }
+
+ #mainContainer, #viewerContainer, .page, .page canvas {
+ position: static;
+ padding: 0;
+ margin: 0;
+ }
+
+ .page {
+ float: left;
+ display: none;
+ box-shadow: none;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ }
+
+ .page[data-loaded] {
+ display: block;
+ }
+}
+
+@media all and (max-width: 950px) {
+ html[dir='ltr'] #outerContainer.sidebarMoving .outerCenter,
+ html[dir='ltr'] #outerContainer.sidebarOpen .outerCenter {
+ float: left;
+ left: 180px;
+ }
+ html[dir='rtl'] #outerContainer.sidebarMoving .outerCenter,
+ html[dir='rtl'] #outerContainer.sidebarOpen .outerCenter {
+ float: right;
+ right: 180px;
+ }
+}
+
+@media all and (max-width: 770px) {
+ #sidebarContainer {
+ top: 33px;
+ z-index: 100;
+ }
+ #sidebarContent {
+ top: 32px;
+ background-color: hsla(0,0%,0%,.7);
+ }
+ #thumbnailView, #outlineView {
+ top: 66px;
+ }
+
+ html[dir='ltr'] #outerContainer.sidebarOpen > #mainContainer {
+ left: 0px;
+ }
+ html[dir='rtl'] #outerContainer.sidebarOpen > #mainContainer {
+ right: 0px;
+ }
+
+ html[dir='ltr'] .outerCenter {
+ float: left;
+ left: 180px;
+ }
+ html[dir='rtl'] .outerCenter {
+ float: right;
+ right: 180px;
+ }
+}
+
+@media all and (max-width: 600px) {
+ #toolbarViewerRight {
+ display: none;
+ }
+}
diff --git a/web/viewer.html b/web/viewer.html
index 11c6ad0b7..237c94d10 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -89,20 +89,16 @@
Next
-
+
-
-
-