Javascript в PDF
Недалек тот час, когда PDF документы можно будет полноценно отображать средствами Javascript. При этом обратная возможность, а именно использование Javascript в PDF документах, существует уже очень давно. Об этом и пойдет речь в данной статье.
Любое ПО содержит некоторый активно используемый функционал и значительную долю редко используемого функционала. Можно прикинуть, все ли возможности операционной системы (или Microsoft Word, или своей IDE) мы используем в повседневной работе или вообще использовали хоть раз в жизни. Как правило, далеко не все.
Формат PDF не является исключением. Все мы привыкли к тексту и изображениям в PDF документах, однако это лишь малая часть того, что можно использовать. В частности, формат PDF включает в себя разнообразные возможности для создания документов с динамическим контентом, зависящим от читателя и его действий. Одной из таких возможностей является использование Javascript.
- Для изменения содержимого документа в зависимости от некоторых событий. Например, скрыть часть документа при отправке на печать. Или при открытии документа автоматически заполнить часть полей формы.
- Для ограничения действий читателя. Например, для валидации вводимых значений при заполнении форм.
Рассмотрим ряд практических примеров использования Javascript в PDF документах.
Hello World
Начнем рассмотрение темы с традиционного Hello World примера. В данном и последующих примерах используется язык C# и библиотека Docotic.Pdf для работы с PDF документами. Ссылка на исходники с кодом всех примеров приводится в конце статьи.
using BitMiracle.Docotic.Pdf; namespace JavascriptInPdf < public static class Demo < public static void Main(string[] args) < PdfDocument pdf = new PdfDocument(); pdf.OnOpenDocument = pdf.CreateJavaScriptAction("app.alert(\"Привет, Хабр!\", 3);"); pdf.Save("Hello world.pdf"); >> >
Если открыть созданный этим кодом документ в Adobe Reader, то увидим примерно следующее:
Что же происходит в примере? Суть заключена в строке
pdf.OnOpenDocument = pdf.CreateJavaScriptAction("app.alert(\"Привет, Хабр!\", 3);");
Формат PDF включает поддержку actions – это действия, происходящие по тому или иному событию. Например, когда в оглавлении в некотором PDF документе кликаем на ссылку с номером страницы – срабатывает определенный action для перехода на соответствующую страницу:
Для Javascript также есть соответствующий action. Мы создаем его с помощью метода PdfDocument.CreateJavaScriptAction, которому передаем в качестве параметра JS код. Созданный action мы привязываем к событию OnOpenDocument, происходящему при открытии документа просмотрщиком.
Непосредственно Javascript код выглядит так:
Статический класс app является частью Javascript API и предоставляет набор методов для взаимодействия с приложением-просмотрщиком. В частности, он содержит несколько перегрузок метода alert для показа модального диалога с сообщением. В данном примере используется перегрузка со вторым необязательным параметром – индексом иконки диалога, значение 3 соответствует Status Icon.
От простого – к сложному
Рассмотрим более реалистичный пример.
Многие PDF документы описывают некоторые формы для заполнения – это может быть договор открытия банковского вклада, анкета на получение визы или загран. паспорта, заявление на отпуск и т.п. Довольно удобно, поскольку такую форму можно заполнить прямо в просмотрщике и сохранить или распечатать. Авторы PDF документов, содержащих формы, могут облегчить пользователю их заполнение с помощью Javascript.
Реальные документы часто содержат поля для ввода даты заполнения. Например, это может выглядеть так:
В принципе, создавая документ, можно на этом и остановиться. Однако, можно пойти чуть дальше и немного упростить задачу заполняющему – например, устанавливать дату по умолчанию в текущую – в 99% случаев именно это и необходимо.
Устанавливаем дату по умолчанию
C# код по созданию полей для даты, как на скриншоте, в данном случае не так интересен. Рассмотрим лишь часть, касающуюся Javascript. Необходимо при открытии документа устанавливать в полях текущую дату, делается это так:
function setDay(date) < var dayField = this.getField("day"); if (dayField.value.length == 0) < dayField.value = util.printd("dd", date); >> function setMonth(date) < var monthField = this.getField("month"); if (monthField.value.length == 0) < monthField.value = util.printd("date(ru)", date, true); > > function setYear(date) < var yearField = this.getField("year"); if (yearField.value.length == 0) < yearField.value = util.printd("yyyy", date); >> function setCurrentDate() < var now = new Date(); setDay(now); setMonth(now); setYear(now); >setCurrentDate();
По сравнению с предыдущим примером JS код увеличился в объемах, поэтому использовать его напрямую в C# строке стало неудобно из-за необходимости экранировать кавычки и вставлять переносы строк. Поэтому поместим данный код в ресурсы, тогда использоваться скрипт будет так:
pdf.OnOpenDocument = pdf.CreateJavaScriptAction(Resources.SetCurrentDate);
В результате при открытии документа увидим примерно следующую картину:
Обратите внимание на код для установки месяца – мы используем специфическую перегрузку метода util.printd для вывода локализованного месяца.
monthField.value = util.printd("date(ru)", date, true);
Это дает отличные результаты в Adobe Reader, но, к сожалению, не гарантируется, что другие просмотрщики будут корректно поддерживать столь специфические конструкции. При проектировании документа это нужно учитывать. Возможно, стоит заменить этот код на более многословный (самостоятельное получение названия месяца в нужном падеже), но зато поддерживаемый большим количеством просмотрщиков.
В данном примере также важно то, что значения устанавливаются только в пустые поля. Без этих проверок может возникнуть ситуация, когда пользователь сохранит заполненную форму, а при открытии такой сохраненной формы дата будет изменена на текущую.
Валидация вводимых значений
Если при заполнении формы все-таки необходимо поменять дату, то имеет смысл разрешать ввод только цифр в поля для дня и года. Используем для этого следующий Javascript код:
function validateNumeric(event) < var validCharacters = "0123456789"; for (var i = 0; i < event.change.length; i++) < if (validCharacters.indexOf(event.change.charAt(i)) == -1) < app.beep(0); event.rc = false; break; >> > validateNumeric(event);
В C# коде используем событие OnKeyPress у контролов для проверки вводимого символа:
PdfJavaScriptAction validateNumericAction = m_document.CreateJavaScriptAction(Resources.ValidateNumeric); dayTextBox.OnKeyPress = validateNumericAction; yearTextBox.OnKeyPress = validateNumericAction;
После этого в поля для дня и года будет невозможно ввести любой символ, отличный от цифры. Вставить строку из буфера обмена, содержащую некорректный символ, также не удастся.
Синхронизация значений полей
Часто бывает, что одну и ту же информацию в документе нужно указывать несколько раз. И в случае PDF документов с помощью Javascript можно избавить пользователя от повторения одних и тех же действий.
Предположим, имеется документ следующего вида:
Модифицируем его так, чтобы при изменении одного из полей с ФИО обновлялось и другое.
Используем простую Javascript функцию:
function synchronizeFields(sourceFieldName, destinationFieldName) < var source = this.getField(sourceFieldName); var destination = this.getField(destinationFieldName); if (source != null && destination != null) < destination.value = source.value; >>
PdfDocument pdf = new PdfDocument(“Names.pdf”); pdf.SharedScripts.Add( pdf.CreateJavaScriptAction(Resources.SynchronizeFields) ); pdf.GetControl("name0").OnLostFocus = pdf.CreateJavaScriptAction("synchronizeFields(\"name0\", \"name1\");"); pdf.GetControl("name1").OnLostFocus = pdf.CreateJavaScriptAction("synchronizeFields(\"name1\", \"name0\");"); pdf.Save("NamesModified.pdf");
Теперь при потере фокуса любым из текстбоксов будет обновлено значение другого. Обратите внимание на прием, не встречавшийся ранее, — общий Javascript код помещается в коллекцию PdfDocument.SharedScripts, и далее мы получаем возможность из конкретных action’ов вызывать функцию, определенную в общем коде.
Подытожим
Использовать Javascript можно не только в web-разработке, но и в такой области, как оформление PDF документов. Немного дополнительных усилий, и создаваемые PDF документы порадуют читателя не меньше, чем программа с удобным и продуманным интерфейсом – искушенного пользователя.
- Сложнее писать Javascript код, чем в случае обычной web-разработки. Нужно создать и открыть документ, чтобы проверить корректность написанного кода.
- Javascript полноценно поддерживается лишь просмотрщиками от Adobe. В альтернативных просмотрщиках поддержка существенно ограничена либо отсутствует вообще.
- Javascript может быть отключен в просмотрщике PDF.
- Теоретически исполнение Javascript скриптов в документе небезопасно, и периодически обнаруживаются различные уязвимости.
Скачать код примеров из статьи можно здесь.