Надішліть арабське речення يوضح ملف PDF هذا у звичайний TextOut - і сторінка, яка повернеться, буде неправильною одразу у двох сенсах. Слова йдуть зліва направо замість справа наліво, а літери стоять окремо у своїх ізольованих формах замість того, щоб зчіплюватися у зв'язані слова. Жодної помилки не виникає. Delphi компілює, файл відкривається, а рецензент, який читає арабською, каже вам, що вивід непридатний. Виправлення полягає в одному виклику, а не в заміні бібліотеки: HotPDF пропускає текст справа наліво через окремий метод RtLTextOut, який виконує перевпорядкування, чого звичайний TextOut не робить. Ця сторінка є робочим довідником по цьому методу: підпис і його параметри, аргумент charset, що обирає писемність, побічний ефект на рівні документа, налаштування шрифту, яке має йти першим, і ті збої, які справді доходять до підтримки, кожен зі своїм виправленням
Підпис і параметри
procedure RtLTextOut(X, Y: Single; angle: Extended;
Text: WideString); overload;
procedure RtLTextOut(X, Y: Single; angle: Extended;
Text: PWORD; TextLength: Integer); overload;
X та Y прив'язують серію у власній системі координат сторінки, відміряній від лівого нижнього кута, де Y зростає вгору, тобто в тому самому початку координат, яким користується кожен виклик TextOut; RtLTextOut змінює порядок гліфів, а не те, звідки сторінка відмірює. angle повертає базову лінію рівно так само, як у TextOut, тож 0 малює горизонтальний рядок. Text є рядком у логічному порядку, тобто в тому порядку, у якому ви його набрали б, а друге перевантаження приймає ті самі дані UTF-16 як сирий буфер PWORD з явним лічильником кодових одиниць, і це та форма, яку варто вживати, коли текст надходить з API, а не з рядка Delphi. У старіших версіях Delphi, що передують розв'язанню перевантажень для цих типів, рядкова форма відкрита під іменем RtLTextOutStr з ідентичним переліком параметрів
Розподіл праці між двома викликами виводу є суворим. TextOut малює кодові позиції в тому порядку, у якому ви їх передали, що правильно для латиниці, кирилиці та CJK і неправильно для арабської та івриту. RtLTextOut спершу перевпорядковує кожен рядок у візуальний порядок справа наліво, а потім малює, лишаючи вкраплені латинські слова та цифри читабельними зліва направо всередині рядка. HotPDF навмисно тримає ці два методи окремо, замість того щоб вгадувати напрямок із символів, тож вибір того, який із них викликати, є вибором тієї поведінки писемності, яку ви отримаєте; беріть RtLTextOut для серій справа наліво, TextOut для всього іншого й ніколи не пропускайте один через інший. Чому перевпорядкування взагалі існує, що насправді роблять двонаправлений алгоритм Unicode і контекстне зчеплення арабських літер і де зупиняється шейпінг у HotPDF - це теми супутнього матеріалу про шейпінг арабського та RTL-тексту в HotPDF; усе, що нижче, є практичним налаштуванням

Аргумент charset вирішує, яка це писемність
Те, що каже RtLTextOut, чи компонує він арабську, чи іврит, є не методом, а шрифтом. SetFont приймає набір символів Windows як свій четвертий аргумент, і це значення заносить правила писемності у виклик справа наліво: 178 обирає арабську, 177 обирає іврит. Задайте набір символів, потім малюйте - і два рядки нижче вийдуть у правильному порядку читання без жодного додаткового налаштування
// Арабська: charset 178 каже RtLTextOut застосувати арабські правила
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
// Іврит: charset 177 перемикає правила на іврит
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');
Одну деталь послідовності легко пропустити: SetFont має йти першим і має повторюватися після кожного AddPage, бо поточний шрифт разом із набором символів не переживає розриву сторінки. Забудете повторити - і друга сторінка відкотиться до того шрифту, який був активним, а для арабської це зазвичай означає порожні прямокутники
Він не перевертає текст, який ви вже перевернули
Єдина помилка, яка з'їдає тут найбільше часу на налагодження, - це подавання в RtLTextOut рядка, який ви вже перевернули вручну. Люди доходять до цього методу після того, як перша спроба зі звичайним TextOut вийшла задом наперед, і поширеним тимчасовим ходом є перевернути символи в коді перед малюванням. RtLTextOut перевертає всередині сам, тож заздалегідь перевернутий рядок перевертається вдруге й повертається туди, звідки почав. Передавайте текст у логічному порядку, тобто в тому, у якому ви набрали б його й прочитали вголос, і дайте виклику зробити перевпорядкування
Ця пастка гидкіша за простий переворот, бо двічі перевернутий рядок може виглядати правильним на одній суто арабській тестовій фразі, а потім зламатися тієї ж миті, коли рядок нестиме латинське слово чи число. Усередині рядка справа наліво ці вкраплені серії мають читатися зліва направо, а ручний переворот руйнує це вкладення, тоді як суто арабський випадок його випадково переживає. Тож вада прослизає крізь ваш перший димовий тест і випливає пізніше на справжньому рахунку з номером рахунку в ньому. Виріжте кожен ручний переворот тієї миті, коли переходите на RtLTextOut
Побічний ефект на Direction, про який варто знати
Виклик RtLTextOut змінює більше, ніж рядок, який ви малюєте. Він також перемикає документову перевагу напрямку читання на справа наліво, тобто те саме, що ви інакше задали б самі через властивість Direction. Цей сетер додає vpDirection до ViewerPreferences документа, а це каже переглядачеві, як укладати розвороти по дві сторінки й з якого боку починається компонування розворотів. Коли весь документ арабський чи на івриті, це саме те, чого ви хочете, і ви отримуєте це безкоштовно
Про це варто знати саме тому, що на одній сторінці воно невидиме. Якщо документ переважно зліва направо з одним блоком справа наліво, перший виклик RtLTextOut усе одно перехилить перевагу всього файлу, і ніщо у вашій одностроінковій пробі цього не покаже. Симптом з'явиться через тижні, коли хтось надрукує двосторонню брошуру й розвороти вийдуть дзеркальними. Якщо це не те, чого ви хочете, поверніть Direction явно після серії справа наліво:
// RtLTextOut уже встановив напрямок документа на RightToLeft;
// відновіть зліва направо, якщо документ переважно LTR
Pdf.Direction := LeftToRight;
Для документа, який справді читається справа наліво, лишіть усе як є. Суть у тому, щоб знати про загальнодокументовий ефект виклику, і тоді сюрприз із брошурою ніколи не станеться
Реєструйте той шрифт, який відвантажуєте, а не той, на встановлення якого сподіваєтеся
Жодне перевпорядкування не має значення, якщо у шрифта немає гліфів для малювання. Класичний збій - це звіт, який бездоганно рендериться на машині розробника, де випадково є Arial Unicode MS, і виходить рядами порожніх прямокутників на сервері клієнта, де Windows тихцем підставила шрифт узагалі без арабського покриття. Ліками є перестати довіряти встановленим системним шрифтам і зареєструвати той, який ви постачаєте разом із застосунком
// Постачайте відомий арабський шрифт і реєструйте його перед малюванням
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
Разом із реєстрацією їдуть дві межі. Шрифт, внесений через RegisterUnicodeTTF, вбудовується, а обробка вбудованого Unicode в HotPDF потребує документа версії PDF 1.5 або пізнішої; це кусає лише тоді, коли щось нижче за течією наполягає на PDF 1.4, але коли кусає, збій є мовчазним. Друга межа радше юридична, ніж технічна: файли TrueType несуть біти дозволів на вбудовування, і накреслення, яке чудово виглядає на екрані, може бути ліцензоване так, що забороняє постачати його всередині документів клієнтів. Підтвердьте ліцензію до вбудовування, а не після скарги
Повний консольний приклад
Складаючи частини разом, ось самодостатня програма, яка пише одну сторінку з арабським рядком, рядком на івриті та змішаним рядком, що несе латинську назву продукту. Кожен блок задає свій набір символів, а потім малює в логічному порядку
program RtLTextOutDemo;
{$APPTYPE CONSOLE}
uses
HPDFDoc; // головний модуль HotPDF
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'RtLTextOut.pdf';
Pdf.BeginDoc;
// Латинський заголовок іде звичайним шляхом через TextOut
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(40, 780, 0, 'Right-to-left text with HotPDF');
// Арабська: charset 178, логічний порядок, перевпорядкування робить RtLTextOut
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 720, 0,
'يوضح ملف PDF هذا كيفية التعامل مع النص العربي.');
// Іврит: charset 177
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 680, 0,
'קובץ PDF זה מדגים טקסט עברי הזורם מימין לשמאל.');
// Змішаний рядок: вкраплене латинське слово досі читається зліва направо
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 640, 0,
'مرحبا بالعالم! تم إنشاؤه بواسطة HotPDF');
Pdf.EndDoc;
Writeln('Wrote RtLTextOut.pdf');
finally
Pdf.Free;
end;
end.
Запустіть її й відкрийте результат. Рядки арабською та івритом читаються справа наліво, літери зчіплюються там, де писемність їх зчіплює, а в останньому рядку токен HotPDF сидить зліва направо всередині арабської серії. Це вкладення є правильним двонаправленим результатом, а не вадою, хоча рецензенти вперше регулярно заводять на нього тікет; стаття про шейпінг, посилання на яку є вище, пояснює, чому правила Unicode цього вимагають і як сформулювати ваші критерії приймання, щоб той звіт ніколи не з'явився
Типові помилки та їх виправлення
Кожен збій нижче з'являвся у справжній гілці підтримки, і кожен веде назад до одного з розділів вище
- Вивід читається задом наперед або плутається на змішаних рядках — рядок перевернули вручну перед викликом, зазвичай це залишок обхідного маневру зі спроби через
TextOut. Видаліть кожен ручний переворот і передавайте логічний порядок;RtLTextOutперевертає всередині - Літери друкуються роз'єднано в ізольованих формах — текст пішов через звичайний
TextOut, абоSetFontвикликали без набору символів для письма справа наліво. Малюйте черезRtLTextOutі передавайте 178 для арабської чи 177 для івриту четвертим аргументомSetFont - Порожні прямокутники на машині клієнта — Windows підставила шрифт без покриття арабської чи івриту. Перестаньте називати встановлені шрифти; зареєструйте накреслення, яке постачаєте, через
RegisterUnicodeTTFі задайте його вSetFontза цим іменем - Друга сторінка рендериться не тим шрифтом — поточний шрифт не переживає
AddPage. Повторюйте викликSetFontразом із набором символів після кожного розриву сторінки - Двосторонні розвороти друкуються дзеркально в переважно LTR-документі — перший виклик
RtLTextOutперекинувDirectionдокумента як побічний ефект. ЗадайтеPdf.Direction := LeftToRightпісля серії справа наліво - Вбудований текст Unicode мовчки деградує нижче за течією — щось у конвеєрі змушує PDF 1.4, а обробка вбудованого Unicode в HotPDF потребує 1.5 або пізнішої. Підніміть версію документа або приберіть обмеження нижче за течією
Перш ніж формат піде в реліз, перевіряйте не лише на око: скопіюйте текст назад із переглядача, проженіть пошук усередині документа, відкрийте файл на машині без ваших шрифтів розробки й покладіть один справжній документ перед носієм мови. Повний чекліст перевірки, карта покриття по писемностях і корпус тестових рядків, який варто побудувати, живуть у супутній статті про шейпінг арабського та RTL-тексту в HotPDF
Показані тут виклики RtLTextOut, SetFont та RegisterUnicodeTTF є частиною HotPDF Delphi Component для Delphi та C++Builder