diff --git a/examples/acroforms/index.html b/examples/acroforms/index.html index 0b12a53d859cd..4d61bad4116f1 100644 --- a/examples/acroforms/index.html +++ b/examples/acroforms/index.html @@ -12,6 +12,7 @@ + diff --git a/examples/helloworld/index.html b/examples/helloworld/index.html index 778f40682afdb..6cf34aaf13530 100644 --- a/examples/helloworld/index.html +++ b/examples/helloworld/index.html @@ -12,6 +12,7 @@ + diff --git a/extensions/firefox/tools/l10n.js b/extensions/firefox/tools/l10n.js index 9cb97428f9005..ff9192ba0f468 100644 --- a/extensions/firefox/tools/l10n.js +++ b/extensions/firefox/tools/l10n.js @@ -105,7 +105,10 @@ // Arabic, Hebrew, Farsi, Pashto, Urdu var rtlList = ['ar', 'he', 'fa', 'ps', 'ur']; return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr'; - } + }, + + // translate an element or document fragment + translate: translateFragment }; })(this); diff --git a/l10n/ar/viewer.properties b/l10n/ar/viewer.properties index 988abecec3625..6d22d27cbbe7d 100644 --- a/l10n/ar/viewer.properties +++ b/l10n/ar/viewer.properties @@ -98,11 +98,11 @@ page_scale_actual=الحجم الحقيقي loading_error_indicator=خطأ loading_error=حدث خطأ أثناء تحميل وثيقه الـPDF -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[ملاحظة {{type}}] +text_annotation_type.alt=[ملاحظة {{type}}] request_password=الـPDF محمي بكلمة مرور: printing_not_supported=تحذير: الطباعة ليست مدعومة كليًا في هذا المتصفح. diff --git a/l10n/ca/viewer.properties b/l10n/ca/viewer.properties index 84b515d6996d5..1bbddebcd9abf 100644 --- a/l10n/ca/viewer.properties +++ b/l10n/ca/viewer.properties @@ -114,11 +114,11 @@ loading_error_indicator=Error loading_error=Ha ocorregut un error mentres es carregava el PDF. invalid_file_error=Invàlid o fitxer PDF corrupte. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[Anotació {{type}}] +text_annotation_type.alt=[Anotació {{type}}] request_password=El PDF està protegit amb una contrasenya: printing_not_supported=Avís: La impressió no és compatible totalment en aquest navegador. diff --git a/l10n/cs/viewer.properties b/l10n/cs/viewer.properties index 83f12b2665f7b..beac468acc9c8 100644 --- a/l10n/cs/viewer.properties +++ b/l10n/cs/viewer.properties @@ -41,7 +41,7 @@ rendering_error=Došlo k chybě při vykreslování stránky. page_label=Stránka: page_of=z{{pageCount}} open_file.title=Otevřít soubor -text_annotation_type=[{{type}}Anotace] +text_annotation_type.alt=[{{type}}Anotace] toggle_slider_label=Přepnout posuvník thumbs_label=Náhledy outline_label=Přehled dokumentu diff --git a/l10n/da/viewer.properties b/l10n/da/viewer.properties index 0c566b96eba7a..e1d43ee436c2a 100644 --- a/l10n/da/viewer.properties +++ b/l10n/da/viewer.properties @@ -114,7 +114,7 @@ missing_file_error=Manglende PDF-fil # "{{type}}" vil blive ersattet af en kommentar type fra en liste # defineret i PDF specifikationen (32000-1:2008 Table 169 – Annotation types). # Nogle almindelige typer er f.eks.: "Check", "Text", "Comment" og "Note" -text_annotation_type=[{{type}} Kommentar] +text_annotation_type.alt=[{{type}} Kommentar] request_password=PDF filen er beskyttet med et kodeord: invalid_password=Ugyldigt kodeord. diff --git a/l10n/de/viewer.properties b/l10n/de/viewer.properties index d1be81f6cc53d..c38fb6a673fcd 100644 --- a/l10n/de/viewer.properties +++ b/l10n/de/viewer.properties @@ -109,11 +109,11 @@ loading_error_indicator=Fehler loading_error=Das PDF konnte nicht geladen werden. invalid_file_error=Ungültige oder beschädigte PDF-Datei. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}} Annotation] +text_annotation_type.alt=[{{type}} Annotation] request_password=Das PDF ist passwortgeschützt: printing_not_supported=Warnung: Drucken wird durch diesen Browser nicht vollständig unterstützt. diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties index 987c90431625b..ffc025362b6fd 100644 --- a/l10n/en-US/viewer.properties +++ b/l10n/en-US/viewer.properties @@ -110,11 +110,11 @@ loading_error=An error occurred while loading the PDF. invalid_file_error=Invalid or corrupted PDF file. missing_file_error=Missing PDF file. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}} Annotation] +text_annotation_type.alt=[{{type}} Annotation] request_password=PDF is protected by a password: invalid_password=Invalid Password. diff --git a/l10n/es/viewer.properties b/l10n/es/viewer.properties index 059eed69a150f..eda8354fb90d4 100644 --- a/l10n/es/viewer.properties +++ b/l10n/es/viewer.properties @@ -114,7 +114,7 @@ missing_file_error=Falta el archivo PDF. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[Anotación {{type}}] +text_annotation_type.alt=[Anotación {{type}}] request_password=El archivo PDF está protegido por una contraseña: printing_not_supported=Aviso: Este navegador no es compatible completamente con la impresión. diff --git a/l10n/fi/viewer.properties b/l10n/fi/viewer.properties index f8cb93f0b7760..d032bcc10dcc2 100644 --- a/l10n/fi/viewer.properties +++ b/l10n/fi/viewer.properties @@ -110,11 +110,11 @@ loading_error=Virhe on tapahtunut PDF:ää ladattaessa. invalid_file_error=Virheellinen tai vioittunut PDF tiedosto. missing_file_error=PDF tiedostoa ei löytynyt. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}} Selite] +text_annotation_type.alt=[{{type}} Selite] request_password=PDF on salasanasuojattu: printing_not_supported=Varoitus: Tämä selain ei täysin tue tulostusta. diff --git a/l10n/fr/viewer.properties b/l10n/fr/viewer.properties index cdd1989aabfb9..2a0094b2b1802 100644 --- a/l10n/fr/viewer.properties +++ b/l10n/fr/viewer.properties @@ -114,7 +114,7 @@ missing_file_error=Fichier PDF manquant. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[Annotation {{type}}] +text_annotation_type.alt=[Annotation {{type}}] request_password=Le PDF est protégé par un mot de passe : printing_not_supported=Attention : l'impression n'est pas totalement prise en charge par ce navigateur. diff --git a/l10n/he/viewer.properties b/l10n/he/viewer.properties index ca3741cfff49d..38f280c0dea30 100644 --- a/l10n/he/viewer.properties +++ b/l10n/he/viewer.properties @@ -41,7 +41,7 @@ rendering_error=אירעה שגיאה בעת עיבוד הדף. page_label=דף: page_of=מתוך {{pageCount}} open_file.title=פתיחת קובץ -text_annotation_type=[{{type}} Annotation] +text_annotation_type.alt=[{{type}} Annotation] toggle_slider_label=מתג החלקה thumbs_label=תמונות ממוזערות outline_label=מתאר מסמך diff --git a/l10n/it/viewer.properties b/l10n/it/viewer.properties index 962226446a4f2..79331b452900a 100644 --- a/l10n/it/viewer.properties +++ b/l10n/it/viewer.properties @@ -41,4 +41,4 @@ rendering_error= page_label=Pagina: page_of=di {{pageCount}} open_file.title=Apri File -text_annotation_type=[{{type}} Annotazione] +text_annotation_type.alt=[{{type}} Annotazione] diff --git a/l10n/ja/viewer.properties b/l10n/ja/viewer.properties index 3a43dee55fc42..542ee397dd60b 100644 --- a/l10n/ja/viewer.properties +++ b/l10n/ja/viewer.properties @@ -110,11 +110,11 @@ loading_error=PDF の読み込み中にエラーが発生しました invalid_file_error=無効または破損した PDF ファイル missing_file_error=PDF ファイルが見つかりません。 -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}} 注釈] +text_annotation_type.alt=[{{type}} 注釈] request_password=PDF はパスワードによって保護されています printing_not_supported=警告:このブラウザでは印刷が完全にサポートされていません diff --git a/l10n/lt/viewer.properties b/l10n/lt/viewer.properties index 2ad6406e4950a..c2e457712a42a 100644 --- a/l10n/lt/viewer.properties +++ b/l10n/lt/viewer.properties @@ -110,11 +110,11 @@ loading_error=PDF bylos įkelimo metu įvyko klaida. invalid_file_error=Neteisinga arba pažeista PDF byla. missing_file_error=Trūksta PDF bylos. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}} Pastaba] +text_annotation_type.alt=[{{type}} Pastaba] request_password=PDF byla yra apsaugota slaptažodžiu: printing_not_supported=Dėmesio: Naršyklė pilnai nepalaiko spausdinimo. diff --git a/l10n/nl/viewer.properties b/l10n/nl/viewer.properties index 73aafa8af130b..a731a76f1e726 100644 --- a/l10n/nl/viewer.properties +++ b/l10n/nl/viewer.properties @@ -110,11 +110,11 @@ loading_error=Er is een fout opgetreden bij het laden van het PDF-bestand. invalid_file_error=Ongeldig of corrupt PDF-bestand. missing_file_error=Ontbrekend PDF-bestand. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}}-aantekening] +text_annotation_type.alt=[{{type}}-aantekening] request_password=Dit PDF-bestand is beveiligd met een wachtwoord: printing_not_supported=Waarschuwing: afdrukken wordt niet volledig ondersteund door deze browser. diff --git a/l10n/pl/viewer.properties b/l10n/pl/viewer.properties index 9aa576f3acf59..dd9b3162b355b 100644 --- a/l10n/pl/viewer.properties +++ b/l10n/pl/viewer.properties @@ -110,11 +110,11 @@ loading_error=Wystąpił błąd podczas wczytywania pliku PDF. invalid_file_error=Błędny lub uszkodzony plik PDF. missing_file_error=Nie znaleziono pliku PDF. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 - Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[Komentarz {{type}}] +text_annotation_type.alt=[Komentarz {{type}}] request_password=Plik PDF jest chroniony przez hasło: printing_not_supported=Ostrzeżenie: Drukowanie nie jest w pełni obsługiwane przez tę przeglądarkę. diff --git a/l10n/pt-BR/viewer.properties b/l10n/pt-BR/viewer.properties index 9f47b9cee735c..edea5f11cf346 100644 --- a/l10n/pt-BR/viewer.properties +++ b/l10n/pt-BR/viewer.properties @@ -41,4 +41,4 @@ rendering_error=Um erro ocorreu ao apresentar a página. page_label=Página: page_of=de {{pageCount}} open_file.title=Abrir arquivo -text_annotation_type=[{{type}} Anotações] +text_annotation_type.alt=[{{type}} Anotações] diff --git a/l10n/ro/viewer.properties b/l10n/ro/viewer.properties index 985be8d694280..ae37ec72d4a4e 100644 --- a/l10n/ro/viewer.properties +++ b/l10n/ro/viewer.properties @@ -41,7 +41,7 @@ rendering_error=S-a produs o eroare în timpul procesării paginii. page_label=Pagina: page_of=din {{pageCount}} open_file.title=Deschide fișier -text_annotation_type=[Adnotare {{type}}] +text_annotation_type.alt=[Adnotare {{type}}] toggle_slider_label=Vedere de ansamblu thumbs_label=Miniaturi outline_label=Cuprins diff --git a/l10n/ru/viewer.properties b/l10n/ru/viewer.properties index 0465fd8a8b74f..f86b601a2f1aa 100644 --- a/l10n/ru/viewer.properties +++ b/l10n/ru/viewer.properties @@ -41,7 +41,7 @@ rendering_error=Произошла ошибка во время создания page_label=Страница: page_of=из {{pageCount}} open_file.title=Открыть файл -text_annotation_type=[Аннотация {{type}}] +text_annotation_type.alt=[Аннотация {{type}}] toggle_slider_label=Вспомогательная панель thumbs_label=Уменьшенные изображения outline_label=Содержание документа diff --git a/l10n/sr/viewer.properties b/l10n/sr/viewer.properties index 7ec9bfd7cc78f..9fe3666e266e7 100644 --- a/l10n/sr/viewer.properties +++ b/l10n/sr/viewer.properties @@ -41,7 +41,7 @@ rendering_error=Дошло је до грешке приликом приказ page_label=Страна: page_of=од {{pageCount}} open_file.title=Отвори датотеку -text_annotation_type=[{{type}} Annotation] +text_annotation_type.alt=[{{type}} Annotation] toggle_slider_label=Клизач thumbs_label=Сличице outline_label=Документи у линијама diff --git a/l10n/sv/viewer.properties b/l10n/sv/viewer.properties index c0191110a98ba..f4bcbe3f94d35 100644 --- a/l10n/sv/viewer.properties +++ b/l10n/sv/viewer.properties @@ -110,11 +110,11 @@ loading_error=Ett fel inträffade när PDF-filen laddades. invalid_file_error=Ogiltig eller korrupt PDF-fil. missing_file_error=PDF-filen saknas. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}}-anteckning] +text_annotation_type.alt=[{{type}}-anteckning] request_password=PDF-filen är lösenordsskyddad: printing_not_supported=Varning: Utskrifter stöds inte fullt ut av denna webbläsare. diff --git a/l10n/tr/viewer.properties b/l10n/tr/viewer.properties index a9d69dd941d79..ecd51c6cc9ff3 100644 --- a/l10n/tr/viewer.properties +++ b/l10n/tr/viewer.properties @@ -110,11 +110,11 @@ loading_error=PDF yüklenirken hata. invalid_file_error=Geçersiz yada bozuk dosya. missing_file_error=PDF dosyası bulunamadı. -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}} Not] +text_annotation_type.alt=[{{type}} Not] request_password=PDF Şifre ile korunmakta: printing_not_supported=Uyarı: Yazdırma işlemi bu tarayıcı ile tam desteklenmiyor. diff --git a/l10n/zh-CN/viewer.properties b/l10n/zh-CN/viewer.properties index 5b045b25068a7..c27e60973d4f8 100644 --- a/l10n/zh-CN/viewer.properties +++ b/l10n/zh-CN/viewer.properties @@ -110,11 +110,11 @@ loading_error=加载 PDF 文件时出错。 invalid_file_error=PDF 文件无效或已损坏。 missing_file_error=缺失 PDF 文件。 -# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip. # "{{type}}" will be replaced with an annotation type from a list defined in # the PDF spec (32000-1:2008 Table 169 – Annotation types). # Some common types are e.g.: "Check", "Text", "Comment", "Note" -text_annotation_type=[{{type}} 注解] +text_annotation_type.alt=[{{type}} 注解] request_password=该 PDF 文档受密码保护: printing_not_supported=警告:该浏览器不能完全支持打印。 diff --git a/l10n/zh-TW/viewer.properties b/l10n/zh-TW/viewer.properties index 1ff8ad603a187..7e68d5b025b62 100644 --- a/l10n/zh-TW/viewer.properties +++ b/l10n/zh-TW/viewer.properties @@ -104,7 +104,7 @@ missing_file_error=遺失PDF檔案。 # 其他標籤和訊息 # "{{type}}" 用來表示PDF格式規範 (32000-1:2008 Table 169 – Annotation types) 入面所定義的註解種類。 # 一些常見的類型有: "Check"、 "Text"、 "Comment"、 "Note" -text_annotation_type=[{{type}} 註解] +text_annotation_type.alt=[{{type}} 註解] request_password=PDF檔案受密碼保護: printing_not_supported=警告:這個瀏覽器不完全支援列印。 diff --git a/make.js b/make.js index 7e1d565ee5f57..fbdd137ccdeb5 100644 --- a/make.js +++ b/make.js @@ -239,6 +239,7 @@ target.bundle = function(args) { 'api.js', 'canvas.js', 'obj.js', + 'annotation.js', 'function.js', 'charsets.js', 'cidmaps.js', diff --git a/src/annotation.js b/src/annotation.js new file mode 100644 index 0000000000000..288d4d5e91821 --- /dev/null +++ b/src/annotation.js @@ -0,0 +1,501 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals Util, isDict, isName, stringToPDFString, TODO, Dict, Stream, + stringToBytes, PDFJS, isWorker, assert, NotImplementedException, + Promise */ + +'use strict'; + +var Annotation = (function AnnotationClosure() { + // 12.5.5: Algorithm: Appearance streams + function getTransformMatrix(rect, bbox, matrix) { + var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); + var minX = bounds[0]; + var minY = bounds[1]; + var maxX = bounds[2]; + var maxY = bounds[3]; + + if (minX === maxX || minY === maxY) { + // From real-life file, bbox was [0, 0, 0, 0]. In this case, + // just apply the transform for rect + return [1, 0, 0, 1, rect[0], rect[1]]; + } + + var xRatio = (rect[2] - rect[0]) / (maxX - minX); + var yRatio = (rect[3] - rect[1]) / (maxY - minY); + return [ + xRatio, + 0, + 0, + yRatio, + rect[0] - minX * xRatio, + rect[1] - minY * yRatio + ]; + } + + function getDefaultAppearance(dict) { + var appearanceState = dict.get('AP'); + if (!isDict(appearanceState)) { + return; + } + + var appearance; + var appearances = appearanceState.get('N'); + if (isDict(appearances)) { + var as = dict.get('AS'); + if (as && appearances.has(as.name)) { + appearance = appearances.get(as.name); + } + } else { + appearance = appearances; + } + return appearance; + } + + function Annotation(params) { + if (params.data) { + this.data = params.data; + return; + } + + var dict = params.dict; + var data = this.data = {}; + + data.subtype = dict.get('Subtype').name; + var rect = dict.get('Rect'); + data.rect = Util.normalizeRect(rect); + data.annotationFlags = dict.get('F'); + + var border = dict.get('BS'); + if (isDict(border)) { + var borderWidth = border.has('W') ? border.get('W') : 1; + data.border = { + width: borderWidth, + type: border.get('S') || 'S', + rgb: dict.get('C') || [0, 0, 1] + }; + } + + this.appearance = getDefaultAppearance(dict); + } + + Annotation.prototype = { + + getData: function Annotation_getData() { + return this.data; + }, + + hasHtml: function Annotation_hasHtml() { + return false; + }, + + getHtmlElement: function Annotation_getHtmlElement(commonObjs) { + throw new NotImplementedException( + 'getHtmlElement() should be implemented in subclass'); + }, + + getEmptyContainer: function Annotaiton_getEmptyContainer(tagName, rect) { + assert(!isWorker, + 'getEmptyContainer() should be called from main thread'); + + rect = rect || this.data.rect; + var element = document.createElement(tagName); + element.style.width = Math.ceil(rect[2] - rect[0]) + 'px'; + element.style.height = Math.ceil(rect[3] - rect[1]) + 'px'; + return element; + }, + + isViewable: function Annotation_isViewable() { + var data = this.data; + return !!( + data && + (!data.annotationFlags || + !(data.annotationFlags & 0x22)) && // Hidden or NoView + data.rect // rectangle is nessessary + ); + }, + + getOperatorList: function Annotation_appendToOperatorList(evaluator) { + + var promise = new Promise(); + + if (!this.appearance) { + promise.resolve({ + queue: { + fnArray: [], + argsArray: [] + }, + dependency: {} + }); + return promise; + } + + var data = this.data; + + var appearanceDict = this.appearance.dict; + var resources = appearanceDict.get('Resources'); + var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1]; + var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0]; + var transform = getTransformMatrix(data.rect, bbox, matrix); + + var border = data.border; + + var listPromise = evaluator.getOperatorList(this.appearance, resources); + listPromise.then(function(appearanceStreamData) { + var fnArray = appearanceStreamData.queue.fnArray; + var argsArray = appearanceStreamData.queue.argsArray; + + fnArray.unshift('beginAnnotation'); + argsArray.unshift([data.rect, transform, matrix]); + + fnArray.push('endAnnotation'); + argsArray.push([]); + + promise.resolve(appearanceStreamData); + }); + + return promise; + } + }; + + Annotation.getConstructor = + function Annotation_getConstructor(subtype, fieldType) { + + if (!subtype) { + return; + } + + // TODO(mack): Implement FreeText annotations + if (subtype === 'Link') { + return LinkAnnotation; + } else if (subtype === 'Text') { + return TextAnnotation; + } else if (subtype === 'Widget') { + if (!fieldType) { + return; + } + + return WidgetAnnotation; + } else { + return Annotation; + } + }; + + // TODO(mack): Support loading annotation from data + Annotation.fromData = function Annotation_fromData(data) { + var subtype = data.subtype; + var fieldType = data.fieldType; + var Constructor = Annotation.getConstructor(subtype, fieldType); + if (Constructor) { + return new Constructor({ data: data }); + } + }; + + Annotation.fromRef = function Annotation_fromRef(xref, ref) { + + var dict = xref.fetchIfRef(ref); + if (!isDict(dict)) { + return; + } + + var subtype = dict.get('Subtype'); + subtype = isName(subtype) ? subtype.name : ''; + if (!subtype) { + return; + } + + var fieldType = Util.getInheritableProperty(dict, 'FT'); + fieldType = isName(fieldType) ? fieldType.name : ''; + + var Constructor = Annotation.getConstructor(subtype, fieldType); + if (!Constructor) { + return; + } + + var params = { + dict: dict, + ref: ref, + }; + + var annotation = new Constructor(params); + + if (annotation.isViewable()) { + return annotation; + } else { + TODO('unimplemented annotation type: ' + subtype); + } + }; + + return Annotation; +})(); +PDFJS.Annotation = Annotation; + + +var WidgetAnnotation = (function WidgetAnnotationClosure() { + + function WidgetAnnotation(params) { + Annotation.call(this, params); + + if (params.data) { + return; + } + + var dict = params.dict; + var data = this.data; + + data.fieldValue = stringToPDFString( + Util.getInheritableProperty(dict, 'V') || ''); + data.alternativeText = stringToPDFString(dict.get('TU') || ''); + data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || ''; + var fieldType = Util.getInheritableProperty(dict, 'FT'); + data.fieldType = isName(fieldType) ? fieldType.name : ''; + data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0; + this.fieldResources = Util.getInheritableProperty(dict, 'DR') || new Dict(); + + // Building the full field name by collecting the field and + // its ancestors 'T' data and joining them using '.'. + var fieldName = []; + var namedItem = dict; + var ref = params.ref; + while (namedItem) { + var parent = namedItem.get('Parent'); + var parentRef = namedItem.getRaw('Parent'); + var name = namedItem.get('T'); + if (name) { + fieldName.unshift(stringToPDFString(name)); + } else { + // The field name is absent, that means more than one field + // with the same name may exist. Replacing the empty name + // with the '`' plus index in the parent's 'Kids' array. + // This is not in the PDF spec but necessary to id the + // the input controls. + var kids = parent.get('Kids'); + var j, jj; + for (j = 0, jj = kids.length; j < jj; j++) { + var kidRef = kids[j]; + if (kidRef.num == ref.num && kidRef.gen == ref.gen) + break; + } + fieldName.unshift('`' + j); + } + namedItem = parent; + ref = parentRef; + } + data.fullName = fieldName.join('.'); + } + + var parent = Annotation.prototype; + Util.inherit(WidgetAnnotation, Annotation, { + isViewable: function WidgetAnnotation_isViewable() { + if (this.data.fieldType === 'Sig') { + TODO('unimplemented annotation type: Widget signature'); + return false; + } + + return parent.isViewable.call(this); + } + }); + + return WidgetAnnotation; +})(); + +var TextAnnotation = (function TextAnnotationClosure() { + function TextAnnotation(params) { + Annotation.call(this, params); + + if (params.data) { + return; + } + + var dict = params.dict; + var data = this.data; + + var content = dict.get('Contents'); + var title = dict.get('T'); + data.content = stringToPDFString(content || ''); + data.title = stringToPDFString(title || ''); + data.name = !dict.has('Name') ? 'Note' : dict.get('Name').name; + } + + var ANNOT_MIN_SIZE = 10; + var IMAGE_DIR = './images/'; + + Util.inherit(TextAnnotation, Annotation, { + + appendToOperatorList: function TextAnnotation_appendToOperatorList( + operatorList, dependencies, evaluator) { + return; + }, + + hasHtml: function TextAnnotation_hasHtml() { + return true; + }, + + getHtmlElement: function TextAnnotation_getHtmlElement(commonObjs) { + assert(!isWorker, 'getHtmlElement() shall be called from main thread'); + + var item = this.data; + var rect = item.rect; + + // sanity check because of OOo-generated PDFs + if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) { + rect[3] = rect[1] + ANNOT_MIN_SIZE; + } + if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) { + rect[2] = rect[0] + (rect[3] - rect[1]); // make it square + } + + var container = this.getEmptyContainer('section', rect); + container.className = 'annotText'; + + var image = document.createElement('img'); + image.style.width = container.style.width; + image.style.height = container.style.height; + var iconName = item.name; + image.src = IMAGE_DIR + 'annotation-' + + iconName.toLowerCase() + '.svg'; + image.alt = '[{{type}} Annotation]'; + image.dataset.l10nId = 'text_annotation_type'; + image.dataset.l10nArgs = JSON.stringify({type: iconName}); + var content = document.createElement('div'); + content.setAttribute('hidden', true); + var title = document.createElement('h1'); + var text = document.createElement('p'); + content.style.left = Math.floor(rect[2] - rect[0]) + 'px'; + content.style.top = '0px'; + title.textContent = item.title; + + if (!item.content && !item.title) { + content.setAttribute('hidden', true); + } else { + var e = document.createElement('span'); + var lines = item.content.split(/(?:\r\n?|\n)/); + for (var i = 0, ii = lines.length; i < ii; ++i) { + var line = lines[i]; + e.appendChild(document.createTextNode(line)); + if (i < (ii - 1)) + e.appendChild(document.createElement('br')); + } + text.appendChild(e); + image.addEventListener('mouseover', function annotationImageOver() { + container.style.zIndex += 1; + content.removeAttribute('hidden'); + }, false); + + image.addEventListener('mouseout', function annotationImageOut() { + container.style.zIndex -= 1; + content.setAttribute('hidden', true); + }, false); + } + + content.appendChild(title); + content.appendChild(text); + container.appendChild(image); + container.appendChild(content); + + return container; + } + }); + + return TextAnnotation; +})(); + +var LinkAnnotation = (function LinkAnnotationClosure() { + function isValidUrl(url) { + if (!url) + return false; + var colon = url.indexOf(':'); + if (colon < 0) + return false; + var protocol = url.substr(0, colon); + switch (protocol) { + case 'http': + case 'https': + case 'ftp': + case 'mailto': + return true; + default: + return false; + } + } + + function LinkAnnotation(params) { + Annotation.call(this, params); + + if (params.data) { + return; + } + + var dict = params.dict; + var data = this.data; + + var action = dict.get('A'); + if (action) { + var linkType = action.get('S').name; + if (linkType === 'URI') { + var url = action.get('URI'); + // TODO: pdf spec mentions urls can be relative to a Base + // entry in the dictionary. + if (!isValidUrl(url)) { + url = ''; + } + data.url = url; + } else if (linkType === 'GoTo') { + data.dest = action.get('D'); + } else if (linkType === 'GoToR') { + var urlDict = action.get('F'); + if (isDict(urlDict)) { + // We assume that the 'url' is a Filspec dictionary + // and fetch the url without checking any further + url = urlDict.get('F') || ''; + } + + // TODO: pdf reference says that GoToR + // can also have 'NewWindow' attribute + if (!isValidUrl(url)) { + url = ''; + } + data.url = url; + data.dest = action.get('D'); + } else { + TODO('unrecognized link type: ' + linkType); + } + } else if (dict.has('Dest')) { + // simple destination link + var dest = dict.get('Dest'); + data.dest = isName(dest) ? dest.name : dest; + } + } + + Util.inherit(LinkAnnotation, Annotation, { + hasOperatorList: function LinkAnnotation_hasOperatorList() { + return false; + }, + + hasHtml: function LinkAnnotation_hasHtml() { + return true; + }, + + getHtmlElement: function LinkAnnotation_getHtmlElement(commonObjs) { + var element = this.getEmptyContainer('a'); + element.href = this.data.url || ''; + return element; + } + }); + + return LinkAnnotation; +})(); diff --git a/src/canvas.js b/src/canvas.js index 1b62bce4e4291..08bb7c8630852 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -1476,24 +1476,12 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { }, beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform, - matrix, border) { + matrix) { this.save(); if (rect && isArray(rect) && 4 == rect.length) { var width = rect[2] - rect[0]; var height = rect[3] - rect[1]; - - if (border) { - // TODO(mack): Support different border styles - this.save(); - var rgb = border.rgb; - this.setStrokeRGBColor(rgb[0], rgb[1], rgb[2]); - this.setLineWidth(border.width); - this.rectangle(rect[0], rect[1], width, height); - this.stroke(); - this.restore(); - } - this.rectangle(rect[0], rect[1], width, height); this.clip(); this.endPath(); diff --git a/src/core.js b/src/core.js index e6391610ca26d..4c262b2d75e2e 100644 --- a/src/core.js +++ b/src/core.js @@ -18,7 +18,7 @@ isArrayBuffer, isDict, isName, isStream, isString, Lexer, Linearization, NullStream, PartialEvaluator, shadow, Stream, StreamsSequenceStream, stringToPDFString, TODO, Util, warn, XRef, - MissingDataException, Promise */ + MissingDataException, Promise, Annotation */ 'use strict'; @@ -41,25 +41,6 @@ globalScope.PDFJS.pdfBug = false; var Page = (function PageClosure() { - function getDefaultAnnotationAppearance(annotationDict) { - var appearanceState = annotationDict.get('AP'); - if (!isDict(appearanceState)) { - return; - } - - var appearance; - var appearances = appearanceState.get('N'); - if (isDict(appearances)) { - var as = annotationDict.get('AS'); - if (as && appearances.has(as.name)) { - appearance = appearances.get(as.name); - } - } else { - appearance = appearances; - } - return appearance; - } - function Page(pdfManager, xref, pageIndex, pageDict, ref) { this.pdfManager = pdfManager; this.pageIndex = pageIndex; @@ -116,8 +97,8 @@ var Page = (function PageClosure() { return shadow(this, 'view', cropBox); }, - get annotations() { - return shadow(this, 'annotations', this.inheritPageProp('Annots')); + get annotationRefs() { + return shadow(this, 'annotationRefs', this.inheritPageProp('Annots')); }, get rotate() { var rotate = this.inheritPageProp('Rotate') || 0; @@ -157,7 +138,6 @@ var Page = (function PageClosure() { var promise = new Promise(); var pageListPromise = new Promise(); - var annotationListPromise = new Promise(); var pdfManager = this.pdfManager; var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', @@ -185,37 +165,41 @@ var Page = (function PageClosure() { ); }); - pdfManager.ensure(this, 'getAnnotationsForDraw', []).then( - function(annotations) { - pdfManager.ensure(partialEvaluator, 'getAnnotationsOperatorList', - [annotations]).then( - function(opListPromise) { - opListPromise.then(function(data) { - annotationListPromise.resolve(data); - }); - } - ); - } - ); - - Promise.all([pageListPromise, annotationListPromise]).then( - function(datas) { - var pageData = datas[0]; - var pageQueue = pageData.queue; - var annotationData = datas[1]; - var annotationQueue = annotationData.queue; - Util.concatenateToArray(pageQueue.fnArray, annotationQueue.fnArray); - Util.concatenateToArray(pageQueue.argsArray, - annotationQueue.argsArray); - PartialEvaluator.optimizeQueue(pageQueue); - Util.extendObj(pageData.dependencies, annotationData.dependencies); - - promise.resolve(pageData); + var annotationsPromise = pdfManager.ensure(this, 'annotations'); + Promise.all([pageListPromise, annotationsPromise]).then(function(datas) { + var pageData = datas[0]; + var pageQueue = pageData.queue; + var annotations = datas[1]; + + var ensurePromises = []; + for (var i = 0, n = annotations.length; i < n; ++i) { + var ensurePromise = pdfManager.ensure(annotations[i], + 'getOperatorList', + [partialEvaluator]); + ensurePromises.push(ensurePromise); } - ); - return promise; + Promise.all(ensurePromises).then(function(listPromises) { + Promise.all(listPromises).then(function(datas) { + for (var i = 0, n = datas.length; i < n; ++i) { + var annotationData = datas[i]; + var annotationQueue = annotationData.queue; + Util.concatenateToArray(pageQueue.fnArray, + annotationQueue.fnArray); + Util.concatenateToArray(pageQueue.argsArray, + annotationQueue.argsArray); + Util.extendObj(pageData.dependencies, + annotationData.dependencies); + } + + PartialEvaluator.optimizeQueue(pageQueue); + promise.resolve(pageData); + }); + }); + }); + + return promise; }, extractTextContent: function Page_extractTextContent() { var handler = { @@ -259,230 +243,27 @@ var Page = (function PageClosure() { return textContentPromise; }, - getLinks: function Page_getLinks() { - var links = []; - var annotations = this.getAnnotations(); - var i, n = annotations.length; - for (i = 0; i < n; ++i) { - if (annotations[i].type != 'Link') - continue; - links.push(annotations[i]); - } - return links; - }, - getAnnotations: function Page_getAnnotations() { - var annotations = this.getAnnotationsBase(); - var items = []; - for (var i = 0, length = annotations.length; i < length; ++i) { - items.push(annotations[i].item); + getAnnotationsData: function Page_getAnnotationsData() { + var annotations = this.annotations; + var annotationsData = []; + for (var i = 0, n = annotations.length; i < n; ++i) { + annotationsData.push(annotations[i].getData()); } - return items; + return annotationsData; }, - getAnnotationsForDraw: function Page_getAnnotationsForDraw() { - var annotations = this.getAnnotationsBase(); - var items = []; - for (var i = 0, length = annotations.length; i < length; ++i) { - var item = annotations[i].item; - var annotationDict = annotations[i].dict; - - item.annotationFlags = annotationDict.get('F'); - - var appearance = getDefaultAnnotationAppearance(annotationDict); - if (appearance && - // TODO(mack): The proper implementation requires that the - // appearance stream overrides Name, but we're currently - // doing it the other way around for 'Text' annotations since we - // have special rendering for it - item.type !== 'Text') { - - item.appearance = appearance; - var appearanceDict = appearance.dict; - item.resources = appearanceDict.get('Resources'); - item.bbox = appearanceDict.get('BBox') || [0, 0, 1, 1]; - item.matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0]; - } - - var border = annotationDict.get('BS'); - if (isDict(border) && !item.appearance) { - var borderWidth = border.has('W') ? border.get('W') : 1; - if (borderWidth !== 0) { - item.border = { - width: borderWidth, - type: border.get('S') || 'S', - rgb: annotationDict.get('C') || [0, 0, 1] - }; - } - } - - items.push(item); - } - - return items; - }, - - getAnnotationsBase: function Page_getAnnotationsBase() { - var xref = this.xref; - function getInheritableProperty(annotation, name) { - var item = annotation; - while (item && !item.has(name)) { - item = item.get('Parent'); - } - if (!item) - return null; - return item.get(name); - } - function isValidUrl(url) { - if (!url) - return false; - var colon = url.indexOf(':'); - if (colon < 0) - return false; - var protocol = url.substr(0, colon); - switch (protocol) { - case 'http': - case 'https': - case 'ftp': - case 'mailto': - return true; - default: - return false; - } - } - - var annotations = this.annotations || []; - var i, n = annotations.length; - var items = []; - for (i = 0; i < n; ++i) { - var annotationRef = annotations[i]; - var annotation = xref.fetchIfRef(annotationRef); - if (!isDict(annotation)) - continue; - var subtype = annotation.get('Subtype'); - if (!isName(subtype)) - continue; - - var item = {}; - item.type = subtype.name; - var rect = annotation.get('Rect'); - item.rect = Util.normalizeRect(rect); - - var includeAnnotation = true; - switch (subtype.name) { - case 'Link': - var a = annotation.get('A'); - if (a) { - switch (a.get('S').name) { - case 'URI': - var url = a.get('URI'); - // TODO: pdf spec mentions urls can be relative to a Base - // entry in the dictionary. - if (!isValidUrl(url)) - url = ''; - item.url = url; - break; - case 'GoTo': - item.dest = a.get('D'); - break; - case 'GoToR': - var url = a.get('F'); - if (isDict(url)) { - // We assume that the 'url' is a Filspec dictionary - // and fetch the url without checking any further - url = url.get('F') || ''; - } - - // TODO: pdf reference says that GoToR - // can also have 'NewWindow' attribute - if (!isValidUrl(url)) - url = ''; - item.url = url; - item.dest = a.get('D'); - break; - default: - TODO('unrecognized link type: ' + a.get('S').name); - } - } else if (annotation.has('Dest')) { - // simple destination link - var dest = annotation.get('Dest'); - item.dest = isName(dest) ? dest.name : dest; - } - break; - case 'Widget': - var fieldType = getInheritableProperty(annotation, 'FT'); - if (!isName(fieldType)) - break; - - // Do not display digital signatures since we do not currently - // validate them. - if (fieldType.name === 'Sig') { - includeAnnotation = false; - break; - } - - item.fieldType = fieldType.name; - // Building the full field name by collecting the field and - // its ancestors 'T' properties and joining them using '.'. - var fieldName = []; - var namedItem = annotation, ref = annotationRef; - while (namedItem) { - var parent = namedItem.get('Parent'); - var parentRef = namedItem.getRaw('Parent'); - var name = namedItem.get('T'); - if (name) { - fieldName.unshift(stringToPDFString(name)); - } else { - // The field name is absent, that means more than one field - // with the same name may exist. Replacing the empty name - // with the '`' plus index in the parent's 'Kids' array. - // This is not in the PDF spec but necessary to id the - // the input controls. - var kids = parent.get('Kids'); - var j, jj; - for (j = 0, jj = kids.length; j < jj; j++) { - var kidRef = kids[j]; - if (kidRef.num == ref.num && kidRef.gen == ref.gen) - break; - } - fieldName.unshift('`' + j); - } - namedItem = parent; - ref = parentRef; - } - item.fullName = fieldName.join('.'); - var alternativeText = stringToPDFString(annotation.get('TU') || ''); - item.alternativeText = alternativeText; - var da = getInheritableProperty(annotation, 'DA') || ''; - var m = /([\d\.]+)\sTf/.exec(da); - if (m) - item.fontSize = parseFloat(m[1]); - item.textAlignment = getInheritableProperty(annotation, 'Q'); - item.flags = getInheritableProperty(annotation, 'Ff') || 0; - break; - case 'Text': - var content = annotation.get('Contents'); - var title = annotation.get('T'); - item.content = stringToPDFString(content || ''); - item.title = stringToPDFString(title || ''); - item.name = !annotation.has('Name') ? 'Note' : - annotation.get('Name').name; - break; - default: - var appearance = getDefaultAnnotationAppearance(annotation); - if (!appearance) { - TODO('unimplemented annotation type: ' + subtype.name); - } - break; - } - if (includeAnnotation) { - items.push({ - item: item, - dict: annotation - }); + get annotations() { + var annotations = []; + var annotationRefs = this.annotationRefs || []; + for (var i = 0, n = annotationRefs.length; i < n; ++i) { + var annotationRef = annotationRefs[i]; + var annotation = Annotation.fromRef(this.xref, annotationRef); + if (annotation) { + annotations.push(annotation); } } - return items; + return shadow(this, 'annotations', annotations); } }; diff --git a/src/evaluator.js b/src/evaluator.js index 830d38c053787..d082feee3ee8e 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -504,10 +504,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var promise = new Promise(); var fontRes = resources.get('Font'); + if (!fontRes) { + warn('fontRes not available'); + } - assert(fontRes, 'fontRes not available'); - - font = xref.fetchIfRef(font) || fontRes.get(fontName); + font = xref.fetchIfRef(font) || (fontRes && fontRes.get(fontName)); if (!isDict(font)) { ++this.idCounters.font; promise.resolve({ @@ -828,93 +829,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return promise; }, - getAnnotationsOperatorList: - function PartialEvaluator_getAnnotationsOperatorList(annotations, - dependency) { - var promise = new Promise(); - - // 12.5.5: Algorithm: Appearance streams - function getTransformMatrix(rect, bbox, matrix) { - var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); - var minX = bounds[0]; - var minY = bounds[1]; - var maxX = bounds[2]; - var maxY = bounds[3]; - var width = rect[2] - rect[0]; - var height = rect[3] - rect[1]; - var xRatio = width / (maxX - minX); - var yRatio = height / (maxY - minY); - return [ - xRatio, - 0, - 0, - yRatio, - rect[0] - minX * xRatio, - rect[1] - minY * yRatio - ]; - } - - var opListPromises = []; - var includedAnnotations = []; - - // deal with annotations - for (var i = 0, length = annotations.length; i < length; ++i) { - var annotation = annotations[i]; - - // check whether we can visualize annotation - if (!annotation || - !annotation.annotationFlags || - (annotation.annotationFlags & 0x0022) || // Hidden or NoView - !annotation.rect || // rectangle is nessessary - !annotation.appearance) { // appearance is nessessary - continue; - } - - includedAnnotations.push(annotation); - - if (annotation.appearance) { - var opListPromise = this.getOperatorList(annotation.appearance, - annotation.resources); - opListPromises.push(opListPromise); - } else { - var opListPromise = new Promise(); - opListPromise.resolve(createOperatorList()); - opListPromises.push(opListPromise); - } - } - - Promise.all(opListPromises).then(function(datas) { - var fnArray = []; - var argsArray = []; - var dependencies = {}; - for (var i = 0, n = datas.length; i < n; ++i) { - var annotation = includedAnnotations[i]; - var data = datas[i]; - - // apply rectangle - var rect = annotation.rect; - var bbox = annotation.bbox; - var matrix = annotation.matrix; - var transform = getTransformMatrix(rect, bbox, matrix); - var border = annotation.border; - - fnArray.push('beginAnnotation'); - argsArray.push([rect, transform, matrix, border]); - - Util.concatenateToArray(fnArray, data.queue.fnArray); - Util.concatenateToArray(argsArray, data.queue.argsArray); - Util.extendObj(dependencies, data.dependencies); - - fnArray.push('endAnnotation'); - argsArray.push([]); - } - - promise.resolve(createOperatorList(fnArray, argsArray, dependencies)); - }); - - return promise; - }, - getTextContent: function PartialEvaluator_getTextContent( stream, resources) { diff --git a/src/util.js b/src/util.js index 4094c33505ca6..0ee9ef6ed873d 100644 --- a/src/util.js +++ b/src/util.js @@ -424,11 +424,30 @@ var Util = PDFJS.Util = (function UtilClosure() { } }; + Util.getInheritableProperty = function Util_getInheritableProperty(dict, + name) { + while (dict && !dict.has(name)) { + dict = dict.get('Parent'); + } + if (!dict) { + return null; + } + return dict.get(name); + }; + + Util.inherit = function Util_inherit(sub, base, prototype) { + sub.prototype = Object.create(base.prototype); + sub.prototype.constructor = sub; + for (var prop in prototype) { + sub.prototype[prop] = prototype[prop]; + } + }; + return Util; })(); var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { - function PageViewport(viewBox, scale, rotation, offsetX, offsetY) { + function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) { this.viewBox = viewBox; this.scale = scale; this.rotation = rotation; @@ -440,25 +459,28 @@ var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { var centerX = (viewBox[2] + viewBox[0]) / 2; var centerY = (viewBox[3] + viewBox[1]) / 2; var rotateA, rotateB, rotateC, rotateD; - switch (rotation % 360) { - case -180: + rotation = rotation % 360; + rotation = rotation < 0 ? rotation + 360 : rotation; + switch (rotation) { case 180: rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1; break; - case -270: case 90: rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0; break; - case -90: case 270: rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0; break; - //case 360: //case 0: default: rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1; break; } + + if (dontFlip) { + rotateC = -rotateC; rotateD = -rotateD; + } + var offsetCanvasX, offsetCanvasY; var width, height; if (rotateA === 0) { @@ -494,7 +516,7 @@ var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { var scale = 'scale' in args ? args.scale : this.scale; var rotation = 'rotation' in args ? args.rotation : this.rotation; return new PageViewport(this.viewBox.slice(), scale, rotation, - this.offsetX, this.offsetY); + this.offsetX, this.offsetY, args.dontFlip); }, convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) { return Util.applyTransform([x, y], this.transform); diff --git a/src/worker.js b/src/worker.js index a77387385ed7f..5209c6015ebd1 100644 --- a/src/worker.js +++ b/src/worker.js @@ -350,11 +350,11 @@ var WorkerMessageHandler = { handler.on('GetAnnotationsRequest', function wphSetupGetAnnotations(data) { pdfManager.getPage(data.pageIndex).then(function(page) { - pdfManager.ensure(page, 'getAnnotations',[]).then( - function(annotations) { + pdfManager.ensure(page, 'getAnnotationsData', []).then( + function(annotationsData) { handler.send('GetAnnotations', { pageIndex: data.pageIndex, - annotations: annotations + annotations: annotationsData }); } ); diff --git a/src/worker_loader.js b/src/worker_loader.js index ca464b03b851f..9f28b62cc176a 100644 --- a/src/worker_loader.js +++ b/src/worker_loader.js @@ -26,6 +26,7 @@ var files = [ 'util.js', 'canvas.js', 'obj.js', + 'annotation.js', 'function.js', 'charsets.js', 'cidmaps.js', diff --git a/test/font/font_test.html b/test/font/font_test.html index 9321ce5afcb41..976216102746e 100644 --- a/test/font/font_test.html +++ b/test/font/font_test.html @@ -20,6 +20,7 @@ + diff --git a/test/test_slave.html b/test/test_slave.html index 3268111b748d7..0424f78179ee6 100644 --- a/test/test_slave.html +++ b/test/test_slave.html @@ -27,6 +27,7 @@ + diff --git a/test/unit/unit_test.html b/test/unit/unit_test.html index 1c5ac6712f3f0..26ffbeba5654b 100644 --- a/test/unit/unit_test.html +++ b/test/unit/unit_test.html @@ -19,6 +19,7 @@ + diff --git a/web/viewer.html b/web/viewer.html index e7ac263f2021a..6f515729e4ed0 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -47,6 +47,7 @@ + diff --git a/web/viewer.js b/web/viewer.js index 56023cf3214d7..81f19e42a9fd2 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -28,9 +28,7 @@ var SCROLLBAR_PADDING = 40; var VERTICAL_PADDING = 5; var MIN_SCALE = 0.25; var MAX_SCALE = 4.0; -var IMAGE_DIR = './images/'; var SETTINGS_MEMORY = 20; -var ANNOT_MIN_SIZE = 10; var RenderingStates = { INITIAL: 0, RUNNING: 1, @@ -2115,7 +2113,8 @@ var PageView = function pageView(container, id, scale, enumerable: true }); - function setupAnnotations(pdfPage, viewport) { + function setupAnnotations(annotationsDiv, pdfPage, viewport) { + function bindLink(link, dest) { link.href = PDFView.getDestinationHash(dest); link.onclick = function pageViewSetupLinksOnclick() { @@ -2125,91 +2124,43 @@ var PageView = function pageView(container, id, scale, }; link.className = 'internalLink'; } - function createElementWithStyle(tagName, item, rect) { - if (!rect) { - rect = viewport.convertToViewportRectangle(item.rect); - rect = PDFJS.Util.normalizeRect(rect); - } - var element = document.createElement(tagName); - element.style.left = Math.floor(rect[0]) + 'px'; - element.style.top = Math.floor(rect[1]) + 'px'; - element.style.width = Math.ceil(rect[2] - rect[0]) + 'px'; - element.style.height = Math.ceil(rect[3] - rect[1]) + 'px'; - return element; - } - function createTextAnnotation(item) { - var container = document.createElement('section'); - container.className = 'annotText'; - - var rect = viewport.convertToViewportRectangle(item.rect); - rect = PDFJS.Util.normalizeRect(rect); - // sanity check because of OOo-generated PDFs - if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) { - rect[3] = rect[1] + ANNOT_MIN_SIZE; - } - if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) { - rect[2] = rect[0] + (rect[3] - rect[1]); // make it square - } - var image = createElementWithStyle('img', item, rect); - var iconName = item.name; - image.src = IMAGE_DIR + 'annotation-' + - iconName.toLowerCase() + '.svg'; - image.alt = mozL10n.get('text_annotation_type', {type: iconName}, - '[{{type}} Annotation]'); - var content = document.createElement('div'); - content.setAttribute('hidden', true); - var title = document.createElement('h1'); - var text = document.createElement('p'); - content.style.left = Math.floor(rect[2]) + 'px'; - content.style.top = Math.floor(rect[1]) + 'px'; - title.textContent = item.title; - - if (!item.content && !item.title) { - content.setAttribute('hidden', true); - } else { - var e = document.createElement('span'); - var lines = item.content.split(/(?:\r\n?|\n)/); - for (var i = 0, ii = lines.length; i < ii; ++i) { - var line = lines[i]; - e.appendChild(document.createTextNode(line)); - if (i < (ii - 1)) - e.appendChild(document.createElement('br')); - } - text.appendChild(e); - image.addEventListener('mouseover', function annotationImageOver() { - content.removeAttribute('hidden'); - }, false); - - image.addEventListener('mouseout', function annotationImageOut() { - content.setAttribute('hidden', true); - }, false); - } - content.appendChild(title); - content.appendChild(text); - container.appendChild(image); - container.appendChild(content); - - return container; - } + pdfPage.getAnnotations().then(function(annotationsData) { + viewport = viewport.clone({ dontFlip: true }); + for (var i = 0; i < annotationsData.length; i++) { + var data = annotationsData[i]; + var annotation = PDFJS.Annotation.fromData(data); + if (!annotation || !annotation.hasHtml()) { + continue; + } - pdfPage.getAnnotations().then(function(items) { - for (var i = 0; i < items.length; i++) { - var item = items[i]; - switch (item.type) { - case 'Link': - var link = createElementWithStyle('a', item); - link.href = item.url || ''; - if (!item.url) - bindLink(link, ('dest' in item) ? item.dest : null); - div.appendChild(link); - break; - case 'Text': - var textAnnotation = createTextAnnotation(item); - if (textAnnotation) - div.appendChild(textAnnotation); - break; + var element = annotation.getHtmlElement(pdfPage.commonObjs); + mozL10n.translate(element); + + data = annotation.getData(); + var rect = data.rect; + var view = pdfPage.view; + rect = PDFJS.Util.normalizeRect([ + rect[0], + view[3] - rect[1] + view[1], + rect[2], + view[3] - rect[3] + view[1] + ]); + element.style.left = rect[0] + 'px'; + element.style.top = rect[1] + 'px'; + element.style.position = 'absolute'; + + var transform = viewport.transform; + var transformStr = 'matrix(' + transform.join(',') + ')'; + CustomStyle.setProp('transform', element, transformStr); + var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px'; + CustomStyle.setProp('transformOrigin', element, transformOriginStr); + + if (data.subtype === 'Link' && !data.url) { + bindLink(element, ('dest' in data) ? data.dest : null); } + + annotationsDiv.appendChild(element); } }); } @@ -2457,7 +2408,7 @@ var PageView = function pageView(container, id, scale, ); } - setupAnnotations(this.pdfPage, this.viewport); + setupAnnotations(div, pdfPage, this.viewport); div.setAttribute('data-loaded', true); };