JavaScript у PDF

Ця стаття описує, як за допомогою JavaScript створювати інтерактивні PDF документи.

pic

Вступ

JavaScript може відображати PDF документи. Можливість виконувати JavaScript в PDF документах існує вже досить довго. Ця стаття присвячена саме можливості виконання коду.

Будь-яке програмне забезпечення містить не лише активно використовуваний набір функцій, а й частку рідко використовуваних функцій. Іноді ця друга частина може бути значною. Можна згадати функції Microsoft Word або вашої улюбленої IDE, наприклад. Ймовірно, вони містять достатньо функцій, якими ви ніколи не користувалися.

Portable Document Format також має багато рідко використовуваних функцій. Ми всі звикли до тексту та зображень у PDF документах, але це лише підмножина того, що цей формат може запропонувати. PDF має набір функцій для створення документів, які можуть змінювати свій вміст у відповідь на дії читача. Одна з таких функцій — це можливість використовувати JavaScript у PDF документах.

JavaScript у PDF найчастіше використовується для наступних завдань:

  • Зміна вмісту документа у відповідь на певні події. Наприклад, приховати частину документа перед друком або попередньо заповнити деякі поля форми, коли користувач відкриває документ.
  • Обмеження дій читача. Наприклад, перевірка введених значень у полях форми та відхилення некоректних даних.
  • Введення шкідливого коду в документи.

Adobe розробила JavaScript API, щоб переглядачі PDF могли інтерпретувати JavaScript код. Продукти сімейства Adobe Acrobat підтримують повний API, але альтернативні переглядачі зазвичай підтримують лише його підмножину.

Давайте подивимося на кілька прикладів.

Hello World

Ось традиційний приклад "Hello World" для початку. Зверніть увагу, що для прикладів я буду використовувати C# та бібліотеку Docotic.Pdf.

Ви можете завантажити повний вихідний код в репозиторії прикладів.

using var pdf = new PdfDocument();  
pdf.OnOpenDocument = pdf.CreateJavaScriptAction(“app.alert(\”Hello, Medium!\”, 3);”);  
pdf.Save(“HelloWorld.pdf”);

Якщо ви відкриєте PDF, створений за допомогою цього прикладу в Adobe Reader, ви побачите щось схоже на наступний скріншот:

pic

Отже, що робить цей приклад? Ось основний рядок:

pdf.OnOpenDocument = pdf.CreateJavaScriptAction(“app.alert(\”Hello, Medium!\”, 3);”);

PDF підтримує дії. Це те, що виконується, коли відбувається подія. Наприклад, натискання на посилання в структурі PDF документа може спричинити дію, і програма переглядача відкриє певну сторінку документа.

Є різні типи дій. Один з них — це JavaScript дії. Ви можете створити дію цього типу за допомогою методу PdfDocument.CreateJavaScriptAction. Цей метод приймає JavaScript код. Потім ви можете прикріпити дію до події OnOpenDocument. Очевидно, що ця подія відбувається, коли програма переглядач відкриває документ.

Ось сам JavaScript код:

app.alert(“Hello, Medium!”, 3);

Статичний клас app є частиною JavaScript API. Цей клас містить методи для взаємодії з переглядачем PDF. Зокрема, цей клас має методи alert. Ви можете використовувати ці методи для відображення модального вікна з повідомленням. Код вище використовує перевантаження з необов'язковим другим параметром. Цей параметр вказує значок, який має бути відображений (3 — це код для значка стану).

Більш складні сценарії

Давайте спробуємо щось більш прагматичне.

Існує багато PDF документів з заповнюваними формами. Документ з формою може бути угодою про депозитний рахунок, анкетою на візу, опитувальником тощо.
Користувач може зручно заповнювати такі документи безпосередньо у переглядачі та друкувати або зберігати їх для подальшого використання. Створювачі таких документів можуть використовувати JavaScript для покращення взаємодії з формою.

Заповнювані форми часто містять поля для дати. Такі поля можуть виглядати ось так:

pic

Звісно, створювач документа може просто додати поле для дати і на цьому зупинитись. Однак було б корисно допомогти тому, хто буде заповнювати документ пізніше, вставивши поточну дату у поле для дати. Зазвичай це очікувана поведінка.

Значення за замовчуванням для поля дати

Я не буду заглиблюватися у деталі створення поля для дати. Зосередимося лише на частині з 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(en){MMMM}", 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();

Як ви можете побачити, тут коду набагато більше, ніж у прикладі "Hello World". Використовувати рядок для всього цього коду, ймовірно, не дуже зручно, тому що вам потрібно буде екранувати лапки. А редагувати такий код пізніше може бути болючим через відсутність форматування. Я рекомендую помістити JavaScript код у текстові файли або ресурси програми.

При відкритті документа зі скриптом він повинен виглядати ось так:

pic

Зверніть увагу на цю частину коду:

monthField.value = util.printd(“date(en){MMMM}”, date, true);

Будь ласка, зверніть увагу, що я використовую перевантаження методу util.printd, щоб отримати локалізовану назву місяця. Це працює як очікується в Adobe Reader, але, на жаль, інші переглядачі PDF можуть не підтримувати всі вбудовані методи JavaScript API. Враховуйте це, якщо ви плануєте підтримувати щось, окрім Adobe Reader. Інший підхід — реалізувати власний код, який робить те ж саме, що й util.printd.

Також зверніть увагу, що код заповнює лише порожні поля. Без цієї додаткової перевірки може статися наступний сценарій: людина заповнює документ, зберігає його, але при наступному відкритті документа всі поля дати скидаються на поточну дату.

Перевірка даних

Припустимо, ви хочете переконатися, що день та рік є числами. Це легко зробити. Давайте використаємо такий код:

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);

Поля PDF викликають подію OnKeyPress (подія натискання клавіші) щоразу, коли хтось вводить текст у поле. Створіть дію з кодом перевірки та прикріпіть її до події OnKeyPress (подія натискання клавіші) полів.

var validateNumeric = File.ReadAllText("../Sample Data/ValidateNumeric.js");  
var action = pdf.CreateJavaScriptAction(validateNumeric);  
dayTextBox.OnKeyPress = action;  
yearTextBox.OnKeyPress = action;

Після цього особа, яка заповнюватиме форму, не зможе ввести нічого, окрім цифр, у полях для дня та року. Код також перевірятиме все, що вставляється з буфера обміну.

Синхронізація даних

Вам може знадобитися вставити однакові дані в різні частини форми.
У таких випадках деякий JavaScript код може допомогти уникнути повторення зусиль.

Припустимо, є документ ось такого вигляду:

pic

Було б чудово, якби ці два поля були синхронізовані. Це можна зробити за допомогою наступного методу JavaScript:

function synchronizeFields(sourceFieldName, destinationFieldName) {  
 var source = this.getField(sourceFieldName);  
 var destination = this.getField(destinationFieldName);  
 if (source != null && destination != null) {  
 destination.value = source.value;  
 }  
}

Використовувати метод JavaScript можна так:

static PdfControl GetControl(PdfDocument pdf, string name)  
{  
 return pdf.GetControl(name) ?? throw new Exception($"Не вдалося отримати поле {name}");  
}  

using (var pdf = new PdfDocument(@"..\Sample Data\Names.pdf"))  
{  
 var synchronizeFields = File.ReadAllText("../Sample Data/SynchronizeFields.js");  
 pdf.SharedScripts.Add(  
 pdf.CreateJavaScriptAction(synchronizeFields)  
 );  

 var control = GetControl(pdf, "name0");  
 control.OnLostFocus = pdf.CreateJavaScriptAction("synchronizeFields(\"name0\", \"name1\");");  

 control = GetControl(pdf, "name1");  
 control.OnLostFocus = pdf.CreateJavaScriptAction("synchronizeFields(\"name1\", \"name0\");");  

 pdf.Save("SynchronizeData.pdf");  
}

Це забезпечить синхронізацію даних в обох полях. Коли одне з полів втратить фокус, дані будуть синхронізовані. Зверніть увагу, що я додаю код для методу synchronizeFields до колекції спільних скриптів (PdfDocument.SharedScripts). Це дозволяє використовувати той самий код в кількох діях.

Підсумок

Як ви бачите, JavaScript це не тільки для веб-розробки. Ви також можете створити форму PDF з кодом JavaScript. І така форма може задовольнити потреби тих, хто буде її заповнювати, майже так само, як і програма з продуманим інтерфейсом.

Не зрозумійте мене неправильно, найважливіша частина PDF файлу — це його вміст. JavaScript — це лише приємне доповнення. І є кілька обмежень:

  • Написання коду Javascript для PDF файлів не так зручно, як для веб-сторінок
  • Існує неповна підтримка JavaScript API в переглядачах PDF, окрім Adobe Reader
  • Налаштування переглядача можуть вимкнути виконання скриптів

Також JavaScript у PDF файлах є загрозою для безпеки. Час від часу в переглядачах знаходять уразливості, які потім виправляються. Не здивуйтеся, якщо відкритий PDF файл запропонує вам гру в шахи, щоб відволікти вас від виконання шкідливих дій у фоновому режимі. 🙂

Перекладено з: JavaScript in PDF

Leave a Reply

Your email address will not be published. Required fields are marked *