Aller au contenu

HTML

phil/markup

Reader\Html\HtmlReader lit du HTML et en fait un document UJO : le HTML que produit un éditeur par blocs (celui de phil/ujo, dont les classes ujo-* sont relues en les blocs d'où elles viennent), le HTML qu'apporte un collage depuis un traitement de texte ou un autre site, et le HTML de textes plus anciens. DefaultCompiler compile le HTML tel qu'il est écrit, pour l'assainisseur ; c'est le lecteur qui le convertit en blocs, quand un texte est publié en UJO ou ouvert dans un éditeur par blocs. Formats place chaque élément à côté de la syntaxe des autres formats.

$reading = new HtmlReader()->read('<h2>Rules</h2><p>Roll <strong>two</strong> dice.</p>');

$reading->document->blocks[0];   // a `header` block, level 2, its anchor `rules`
$reading->document->blocks[1];   // a `paragraph` block, `Roll <b>two</b> dice.`

La source est analysée par l'analyseur HTML5 de PHP, Dom\HTMLDocument, comme un navigateur l'analyse : les balises laissées ouvertes sont fermées, celles qui sont mal placées sont déplacées, les entités sont lues. Tout texte est un document ; le lecteur ne lance jamais d'exception. Le constructeur prend, dans l'ordre, le normaliseur de phil/ujo qui vérifie le document, les EmbedServices auxquels un cadre est comparé, l'ExtractedElementStore qui stocke les dessins en ligne, et le SvgSanitizer qui les assainit.

Blocs

HTMLUJO
<p>, <address>, le texte entre les blocsparagraph
<h1>…<h6>header, son ancre étant l'id, ou générée
<ul>, <ol>, <menu>, <li>list ; start et type (a, A, i, I) gardés ; un autre élément que contient la liste est aussi un élément ; une liste dans un élément donne ses éléments imbriqués, les autres blocs sont repliés dans son contenu
une case à cocher, ou data-checked, dans chaque élémentlist, style checklist
<blockquote>quote
<figure> qui contient <img> et <figcaption>image, l'alt de l'image comme texte de remplacement, le <figcaption> comme légende, la mise en page de la figure comme la sienne
<figure> qui contient <blockquote> et <figcaption>quote, la légende comme attribution
<pre>, son <code class="language-php">code, son langage
<hr>delimiter
<table>, ses <thead>, <tbody>, <tfoot>, <tr>, <th>, <td>table, ses lignes allongées jusqu'à la plus longue, fusions comprises ; une première ligne faite seulement de <th> donne ses en-têtes ; <caption>, colspan, rowspan, un <th> ailleurs et un alignement (align, text-align, ujo-align-…) en font la forme riche ; une liste dans une cellule devient des lignes, chaque élément après sa puce ou son numéro
<iframe> sur un hôte d'un service de EmbedServicesembed, ses title, width et height gardés ; tout autre cadre est un lien vers sa source
<video>, <audio>video, audio : leur src et leurs <source> dans l'ordre, <track> avec kind, srclang, label et default, poster, controls, loop, muted, la taille et title ; rien sans source
<math display="block">math, son MathML, et le LaTeX de son annotation application/x-tex
<p> qui ne contient que <a download>, ou un lien vers un fichier (.pdf, .epub, .zip, .odt, .docx, .csv…)attaches : son adresse, le nom et l'extension du fichier, le texte du lien comme titre
lang, dir sur un blocl'ajustement language, quand la langue diffère de celle qui l'entoure
data-* sur un blocl'ajustement data, sauf pour ceux que le lecteur lit lui-même ou qu'écrit phil/ujo
un commentairel'ajustement note du bloc suivant, ou du dernier de son conteneur
<dl>, <dt>, <dd>definitionList
<details>, <summary>toggle, ouvert quand les détails le sont
<center>, align="…", style="text-align:…"l'ajustement alignment
<div>, <section>, <article>, <main>, <header>, <footer>, <nav>, <aside>, et un élément en ligne qui contient des blocsleurs blocs
<img> dans un paragrapheimage, le paragraphe coupé autour ; son alt comme texte de remplacement, son title comme légende, width, height et align comme mise en page ; dans un lien, son texte de remplacement, ou sa légende à défaut

Le HTML de phil/ujo

Les blocs que produit phil/ujo portent la classe de leur type, que le lecteur relit :

ClasseUJO
ujo-note, ujo-descriptive, leur ujo-box__titlenote, descriptive
ujo-warning, ses ujo-warning__title et ujo-warning__messagewarning
ujo-alert, ujo-alert--dangeralert, son type
ujo-wide, ujo-container et ses autres classeswide, container
ujo-columns et ses ujo-column, ujo-column-flow--3columns, columnFlow
ujo-page-break, ujo-column-break, ujo-page-style et son data-variantpageBreak, columnBreak, pageStyle
ujo-spacer et son data-lines, ujo-page-number et son data-modespacer, pageNumber
ujo-decoration--ornament-12 et sa couleurdecoration
ujo-running-footer, ujo-watermark, ujo-bannerrunningFooter, watermark, banner
ujo-toc et son ujo-toc__titletoc, générée de nouveau à partir des titres
ujo-credit et son ujo-credit__titlecredit
<sup data-footnote="fn-1"> et l'<aside id="footnote-fn-1"> qu'il nommeune note de bas de page, numérotée de nouveau dans l'ordre des références
ujo-align-…, ujo-wrap-left, ujo-wrap-rightl'ajustement alignment, l'habillage d'une image
ujo-embed, ujo-video, ujo-audio et leur ujo-print-fallbackembed, video, audio, la page, l'affiche, le titre et le code QR de leur solution de repli pour le papier relus

Un bloc de statistiques se lit comme ses blocs, une table de classe comme son tableau.

En ligne

HTMLUJO
<b>, <strong> ; <i>, <em>, <cite>, <dfn>, <var><b> ; <i>
<u>, <ins> ; <s>, <del>, <strike> ; <mark> ; <sup>, <sub><u> ; <s> ; <mark> ; <sup>, <sub>
<code>, <kbd>, <samp>, <tt><code>
<a href> vers une adresse d'un schéma que permet phil/ujo (le Web, mailto, tel, sms, geo, les messageries) ou un chemin du site<a href> ; toute autre adresse garde le texte seul
<a href="#…"><a href> quand un élément de la page a cet id ; son texte seul sinon
<abbr title>, <acronym title><abbr title>
lang, dir ; <bdi>, <bdo dir><span lang dir>, un <bdi> sans dir donnant dir="auto"
<ruby>, <rt>, <rp>, <rb><ruby>, chaque lecture entre les parenthèses <rp> écrite à nouveau
<math>la formule, gardée comme le permet le profil rich de phil/ujo
<svg>assaini par phil/svg-sanitizer, stocké par l'ExtractedElementStore, et une image de son adresse ; son <title> ou son aria-label comme texte de remplacement
<br><br>
<q>son texte entre guillemets
<span class="spoiler">, <span data-color>, <i data-icon>, <span data-hspace>le divulgâcheur, la couleur, l'icône et l'espace d'UJO
un style font-weight: bold ou 700, font-style: italic, text-decoration: underline ou line-through<b>, <i>, <u>, <s> : ce qu'écrit un collage depuis un traitement de texte
<b style="font-weight:normal">son contenu seul : l'enveloppe qu'un traitement de texte met autour d'un collage
tout autre élémentson contenu

Les espaces du texte sont réduites comme un navigateur les réduit, hors de <pre>.

Ce qui est laissé de côté, et les pertes

Les scripts, les styles, les objets, les formulaires et leurs champs et l'en-tête d'une page sont laissés de côté avec leur contenu. Le titre d'une page, sa langue, <html lang>, et ses <meta name="…" content="…"> sont ses métadonnées.

Ce que le lecteur ne garde pas est listé dans Reading::$losses, dans l'ordre de la source, jusqu'à 1 000 : chaque Loss nomme l'élément, son chemin (body > div[2] > p[1], chaque élément compté parmi ses frères du même nom) et sa LossReason :

RaisonQuand
UnknownElementun élément laissé de côté avec son contenu
UnknownEmbedun cadre d'un service que EmbedServices ne connaît pas, gardé comme lien
RefusedSchemeun lien ou une source d'un schéma que phil/ujo refuse, son texte gardé
UnknownAnchorun lien vers #… qu'aucun élément de la page ne nomme, son texte gardé
Depthun élément imbriqué plus profondément que ce qui est lu, son texte gardé
SvgWithoutStoreun dessin en ligne lu sans ExtractedElementStore
RefusedSvgun dessin que l'assainisseur a refusé en entier, ou que le magasin n'a pas pris
MissingSourceune vidéo ou un son sans aucune source gardée
DataAttributedata-* sur un élément en ligne : UJO ne les garde que sur les blocs
Commentun commentaire qu'aucun bloc n'a pris comme note

Un site les montre à l'auteur avant la publication ; phil/content-bundle le fait dans phil:content:publish.

Dessins en ligne

Un <svg> en ligne est extrait plutôt qu'écrit dans le texte : le lecteur l'écrit en SVG, l'assainit avec phil/svg-sanitizer (aucun script, aucun gestionnaire d'événements, aucune référence hors de l'image, aucun foreignObject) et le donne à un ExtractedElementStore, qui stocke le fichier (une médiathèque, un stockage du site) et retourne son adresse. Le dessin devient une image de cette adresse. Sans magasin, ou quand le magasin retourne null ou une adresse qui n'est ni http, ni https, ni relative, le dessin est laissé de côté et la perte est enregistrée.

final class MediaStore implements ExtractedElementStore
{
    public function store(string $content, string $mediaType, string $name): ?string
    {
        return $this->library->upload($content, $mediaType, $name)?->url();
    }
}

$reading = new HtmlReader(store: new MediaStore($library))->read($html);

Limites

Les conteneurs s'imbriquent aussi profondément que le permet UJO, et ceux qui sont plus profonds sont laissés de côté, leurs blocs gardés. Les éléments imbriqués sur plus de 128 niveaux sont lus comme leur texte, pour qu'un document imbriqué sur des milliers de niveaux se lise en temps linéaire sans épuiser la pile.