Импорт HTML5 paged media

THPDFHTMLImporter.RenderHTML5 превращает терпимый к ошибкам HTML5-маркап и print CSS в разбитый на страницы PDF-контент, сохраняя компактный прежний путь RenderHTML

CSS-длины и Margin, BaseFontSize, ListIndent и BlockSpacing импортера заданы в пунктах, поэтому оба рендерера дают одинаковую страницу при любом THotPDF.Resolution; ваша собственная отрисовка до и после импорта по-прежнему использует 1/Resolution дюйма

Объявление

function RenderHTML5(
  const HTML: WideString;
  const AuthorStyleSheet: WideString = ''
): Boolean;

function HPDFRenderHTML5Document(
  Doc: THotPDF;
  const HTML: WideString;
  const AuthorStyleSheet: WideString = '';
  MarginPt: Single = 72;
  const FontName: AnsiString = 'Arial';
  FontSize: Single = 12
): Boolean;

Контракт профиля

HTML5ProfileVersion возвращает 1, а HTML5ProfileMilestones перечисляет этапы: парсер и cascade, постраничная вёрстка, таблицы и формы, ограниченные ресурсы

Измеряемые абзацы и направление

Абзацы переносятся по измеренным ширинам текущего шрифта, включая смены шрифта, прогоны bold и italic, letter spacing, word spacing, явные разрывы строк и границы East Asian символов; выравнивание и декор применяются отдельно к каждой получившейся строке

direction: ltr, direction: rtl и HTML-атрибут dir выбирают направление абзаца; dir="auto" использует направление Unicode первого сильного символа

В Windows двунаправленные прогоны скриптов разрешаются через Uniscribe и переупорядочиваются через границы стилей, а существующий текстовый движок PDF обрабатывает шейпинг глифов внутри каждого прогона; направление документа, viewer preferences и сохранённое текстовое состояние страницы восстанавливаются после строки

Inline-переопределения направления, bdi и bdo пока используют порядок абзаца и дают диагностику hdkLayoutFallback; unicode-bidi вне этого профиля, а шейпинг скриптов по-прежнему следует конфигурации шейпинга документа

Фрагментация абзацев

widows и orphans — наследуемые положительные целые, обе по умолчанию 2; пагинация использует измеренные строчные боксы, а не оценочное число символов

Импортер переносит абзац или подстраивает его разрыв так, чтобы до и после разрыва страницы осталось запрошенное минимальное число строк; break-inside: avoid держит абзац целиком, когда он помещается на свежей странице

Когда страница не может вместить запрошенное ограничение, рендеринг продолжает движение, ослабляя ограничение и записывая hdkPaginationConstraint; каждый фрагмент страницы получает собственные фон и рамку

Опциональные переносы

hyphens: none подавляет discretionary-разрывы, а hyphens: manual по умолчанию использует U+00AD или ­; видимый дефис ставится, только когда выбранный разрыв использован

hyphens: auto дополнительно вызывает OnHyphenate со словом и унаследованным значением HTML lang; без callback сохраняются ручные разрывы и сообщается layout fallback, потому что словарь языка в комплект не входит

Позиции разрывов считаются в единицах кода UTF-16 от начала слова; невалидные, вне диапазона, режущие surrogate и режущие базовые combining mark позиции игнорируются

THPDFHTMLHyphenationEvent = procedure(
  Sender: TObject;
  const WordText, Language: WideString;
  var BreakPositions: TArray<Integer>
) of object;

Ограниченные flex- и grid-боксы

display: flex поддерживает row, row-reverse, column и column-reverse; дети строки используют явные ширины или делят остаток ширины, а положительные flex-grow взвешивают дополнительное пространство

Строки поддерживают justify-content start, end, center, space-between, space-around и space-evenly плюс выравнивание поперечной оси start, end, center и stretch; поток колонок поддерживает естественную вертикальную вёрстку и даёт диагностику на неподдерживаемое распределение фиксированной высоты или выравнивание поперечной оси

display: grid поддерживает положительные треки колонок фиксированной длины и fr, включая ограниченный repeat(n, tracks) до 64 колонок; ячейки размещаются в порядке исходника, заполняя по одной строке за раз

grid-column: span N и grid-column-end: span N позволяют ребёнку занять последовательные треки и промежуточные gap; элемент, не влезающий в текущую строку, переходит на следующую, а span больше числа треков даёт layout fallback

flex-wrap: wrap начинает новую строку, когда явная ширина следующего элемента и gap не влезают; каждая строка независимо применяет flex-grow и justify-content, при этом row-reverse и RTL обращают позиции внутри каждой строки, не перенося элементы между строками

gap принимает одну или две длины, с независимыми переопределениями row-gap и column-gap; параллельные строки остаются на одной странице и проверяются до отрисовки

Параллельные дети поддерживают текстовые блоки с inline-стилями, рекурсивно вложенные обычные блочные контейнеры с padding, рамками и фонами, вложенные flex- и grid-контейнеры, изображения и статические или нативные контролы форм

Изображения используют байты из callback или data URI, сохраняют внутренние пропорции, когда высота не задана, и декодируются один раз на узел DOM; preflight и fallback переиспользуют то же кэшированное изображение, не расходуя новый запрос ресурса

Все дочерние боксы измеряются до отрисовки любой параллельной строки, поэтому неподдерживаемое или слишком крупное содержимое откатывается к вертикальному потоку страницы без повторения уже нарисованных строк; вложенное планирование ограничено 64 уровнями и расходует существующий бюджет операций вёрстки

Оборачивающиеся дети без явной ширины занимают целую строку; таблицы и списки внутри параллельных детей, flex basis и shrink, wrap-reverse, intrinsic sizing треков, треки строк и row span grid, а также явное grid-размещение остаются вне профиля и дают наблюдаемую диагностику

Диагностика неподдерживаемого CSS

Diagnostics раскрывает записи с Kind, Subject, Value и MessageText; OnDiagnostic получает ту же информацию во время рендеринга

Неизвестные свойства, неподдерживаемые значения, селекторы и at-rules игнорируются с диагностикой, включая объявления в неиспользуемых правилах стилей; pseudo-селекторы пропускаются, а не трактуются как обычные селекторы типа

StrictCSS, по умолчанию выключенный, поднимает EHPDFHTMLUnsupportedCSS на диагностике; предыдущее содержимое уже могло быть выведено, поэтому для импорта «всё или ничего» используйте отдельный документ

MaxDiagnostics по умолчанию 256 и ограничивает хранимые записи; DiagnosticCount и доставка в callback остаются наблюдаемыми после достижения лимита хранения, а неположительный лимит отключает хранение записей

Ограниченные ресурсы

Внешние стили, изображения и шрифты @font-face отключены, пока OnResource не передаст их байты, поэтому HTML-импорт никогда не открывает файлы и сетевые соединения неявно

@font-face поддерживает один источник url(...), опциональные format('truetype') или format('opentype'), псевдоним семейства, стиль normal или italic и вес normal, bold, 400 или 700; font-display принимает стандартные политики, потому что офлайн-импорт загружает ресурсы синхронно

Шрифты должны быть встраиваемыми ресурсами TrueType или OpenType sfnt с ASCII-именем семейства Windows длиной до 31 символа; WOFF, коллекции шрифтов, local(...), variable-дескрипторы и списки источников дают диагностику

Принятые шрифты регистрируются приватно в памяти процесса на Windows и остаются собственностью документа до его уничтожения, поэтому освобождение импортера до EndDoc не инвалидирует отложенное встраивание шрифтов

Ресурсы data: и результаты callback делят лимиты MaxResources, MaxResourceBytes и MaxTotalResourceBytes

MaxDecodedImageBytes по умолчанию 128 MiB и независимо ограничивает кэш декодированных изображений четырьмя байтами на пиксель; размеры BMP и PNG проверяются до декодирования, и каждая принятая картинка проверяется перед сохранением

MaxDOMNodes, MaxCSSRules и MaxLayoutOperations независимо ограничивают парсинг, построение cascade и работу вёрстки

THPDFHTMLResourceEvent = procedure(
  Sender: TObject;
  const URI: WideString;
  ResourceKind: THPDFHTMLResourceKind;
  var Data: TBytes;
  var ContentType: string;
  var Handled: Boolean
) of object;

Статистика

THPDFHTMLImportStatistics сообщает версию профиля, распарсенные узлы, CSS-правила, операции вёрстки, разрывы страниц, таблицы, контролы форм, запросы ресурсов, принятые и отклонённые загрузки, принятые байты ресурсов, измеренные текстовые строки, строки с переносами, диагностику, flex-контейнеры и grid-контейнеры

OnLineLayout получает индексы страницы и строки абзаца (с нуля), начало, измеренные ширину и высоту, логический текст, направление и флаг переноса после фактического вывода PDF-текста

Каждый вызов RenderHTML5 сбрасывает запись статистики, а нарушение бюджетов поднимает EHPDFHTMLImportBudget

Пример

Importer := THPDFHTMLImporter.Create(Doc);
try
  Importer.CreateAcroForms := True;
  Importer.MaxResources := 16;
  Importer.RenderHTML5(
    '<style>@page { margin: 36pt } ' +
    'table { width: 100% }</style>' +
    '<h1>Invoice</h1>' +
    '<table><tr><td>Total</td><td>42</td></tr></table>' +
    '<input name="approved" type="checkbox">'
  );
finally
  Importer.Free;
end;

См. также: THPDFPage.TextOutHTML, Декларативный layout DOM, Поддержка AcroForm