Repository navigation
feat(html,core,cli): HTML reader — M5 5.1 - #36
Merged
Merged
Conversation
The web is the third format, and the first that is not an OOXML package.
It is also the first where the direction is usually not in the document.
`dir="rtl"` is the version an attribute linter sees. `body { direction:
rtl }` in a stylesheet is where a great many Arabic sites actually state
it, and an adapter reading attributes alone would report
`direction-unset` on every one of them — a finding on formatting the
author chose, which is invariant 2 reached through the adapter rather
than through a rule. So `css.rs` runs a real, if small, cascade:
selectors, specificity, `!important`, the inherited properties, over
`<style>`, the `style` attribute and stylesheets linked by a relative
path on this machine. At-rules are skipped whole, because a declaration
inside `@media` applies under a viewport this tool does not have.
Every omission can only cost a finding; none can manufacture one. That
is the test each shortcut was held to.
`dir` enters the cascade at the origin a browser gives it — the user
agent's — so a page whose CSS says `direction: ltr` under a `dir="rtl"`
is reported as left to right. What the reader sees is what the tool
reports.
`dir="auto"` is `Unset`: it asks the browser to guess from the first
strong character, which is what that state already says. It gets
`مرحبا 2026` right and `2026 مرحبا` wrong, so a paragraph correct under
it is correct by luck of today's text. A warning, the fragile tier. It
also stops inheriting, because a browser stops consulting the ancestors
too.
CSS gives an element one `font-family` for every script, not OOXML's
Latin and complex-script pair, so the stack lowers into both slots and
`complex-font-missing` is structurally silent on HTML — a defect a web
author cannot write, as a hard left edge is one a Word author cannot
write. The mirror holds: CSS's `left` is a physical edge, so
`alignment-incoherent` is live here in a way it is not in Word.
The tree is html5ever's, through a sink this crate owns.
`markup5ever_rcdom` describes itself as unsupported and not for
production. Tree construction matters because `dir` is inherited along
ancestors and HTML moves them: `<p>a<p>b` is two siblings, and text
between `<table>` and its first `<tr>` is foster-parented out of the
table.
One core change, and ADR 0009 records why the port is where it belongs.
A package holds everything that decides its own text; a page can link a
stylesheet on a server this tool will never call, because mirsam
performs no network I/O. The rules in it are rules nobody applied, so a
`direction-unset` finding on such a page may be answered by CSS the tool
never saw. `DocumentReader::unread_sources` defaults to nothing, and
every report now carries `sources: {unread: [...]}` beside `fonts:
{checked: ...}`. It returns opaque strings the engine never parses.
The conformance suite moved to `mirsam-conformance`, a crate with no
library code: it asks whether the adapters agree, so it has to see all
of them, and inside `mirsam-ooxml` it would have made that crate depend
on its peers. Its vocabulary trait now writes a document rather than a
package, so a single-file format needs no exception.
Twenty-four of twenty-nine cases pass with HTML among the formats
without a line changing. The five that moved record the two refusals CSS
adds and make the two assertions those refusals touch sharper.
`quarterly-page.html` carries one instance of each defect the adapter
can see, `quarterly-page-correct.html` is the page the tool must leave
completely alone. Unlike the decks beside them they are not generated: a
page is its own source.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The first item of M5, and the third format. PLAN §5.1.
mirsam audit page.htmlnow works. HTML is the first adapter that is not an OOXML package — which is the point of doing it before XLSX, whose adapter would have reused everythingmirsam-ooxmlalready has and proved nothing about the abstraction.The direction is usually not in the document
dir="rtl"is the version an attribute linter sees.body { direction: rtl }and.rtl { direction: rtl; text-align: right }are where a great many Arabic sites actually state it, and an adapter that read attributes alone would reportdirection-unseton every one of those pages. That is a finding on formatting the author chose — invariant 2, reached through the adapter rather than through a rule, and the failure mode this project treats as worse than a miss.So
css.rsruns a real, if small, cascade: type /*/#id/.class/[attr]/[attr=value]/:root, descendant and child combinators, specificity,!important, and the properties CSS says are inherited. It reads<style>elements, thestyleattribute, and stylesheets linked by a relative path on this machine.At-rules are skipped whole. A declaration inside
@mediaapplies under a viewport this tool does not have, and applying one would be claiming to know the reader's screen.Every omission can only cost a finding; none of them can manufacture one. That is the test each shortcut was held to. A selector this crate cannot match is dropped — that selector, never the whole rule — and a declaration nobody applied leaves the property where it already was.
diris a hint the stylesheet outranksIn a browser
dir="rtl"is a rule in the user agent's stylesheet, so an author'sdirection: ltrbeats it. Readingdiras the last word would report the wrong direction for a page whose CSS disagrees with its markup, and then propose a repair that changes nothing a reader sees.CascadeOriginmodels the three origins and the adapter reports what the reader gets.dir="auto"isUnset, and it stops inheritingautodoes not state a direction. It asks the browser to take the first strong character, which is precisely the sentenceResolved::Unsetalready carries — nothing anywhere; the renderer picks. So Arabic under it isdirection-unset: a warning, the fragile tier.That is the honest reading rather than a harsh one. First-strong gets
مرحبا 2026right and2026 مرحباwrong — the second begins with a number, resolves left to right, and puts the year on the wrong side. A paragraph is correct underautowhen today's text happens to agree, which is not a decision about the element.It also replaces the inherited direction rather than deferring to it. Arabic marked
autounder anltrbody renders right to left, and reporting it as contradicted-by-the-chain would be a finding on text the browser already gets right.One font stack, so
complex-font-missingcannot fire hereOOXML gives a run a Latin slot and a complex-script slot, and that rule reports the document that filled the first and left the second empty. CSS's
font-familyanswers for every script on the element, so the stack lowers into both slots and the rule's precondition is unreachable. A defect a web author cannot write, exactly as a hard left edge is one a Word author cannot write — recorded as a refusal in the conformance suite, never a skip.The mirror of it: CSS's
leftandrightare physical, soalignment-incoherentis live on HTML in a way it is not on Word.The tree is html5ever's, the DOM is ours
<p>a<p>bis two siblings; a<div>inside a<p>closes it; text between<table>and its first<tr>is foster-parented out of the table. Each of those moves a node's ancestors, which is exactly whatdiris inherited along — a hand-rolled nesting stack would resolve direction from a chain no browser has.markup5ever_rcdomis not taken: it describes itself as unsupported, unfuzzed and not for production.dom.rsis the ~200 lines that avoid depending on that claim.The one core change — ADR 0009
A package holds everything that decides its own text. A page can link a stylesheet on a server this tool will never call, because mirsam performs no network I/O — and the rules in it are rules nobody applied, so a
direction-unsetfinding on such a page may be answered by CSS the tool never saw.Standing rule 4 says that has to be sayable.
DocumentReadergainsunread_sources(), defaulting to nothing, and every report carries:beside
fonts: {checked: ...}. It reads{"unread": []}on every.pptxand.docx, which for a package is the whole truth. It returns opaque strings the engine never parses — the core did not learn what a stylesheet is.The alternative was for the CLI to downcast to
HtmlDocument, which would put a format's name in the driving adapter and leave a library caller unable to ask.The conformance suite moved
It asks whether the adapters agree, so it has to see all of them. Inside
mirsam-ooxmlit would have made that crate depend on its peers — the hexagon leaking through the test tree. It now lives inmirsam-conformance, a crate with no library code in it, and its vocabulary trait writes a document rather than a package, so a single-file format needs no exception to join.Acceptance
All 29 conformance cases pass with HTML among the formats, 24 of them without a line changing — the same situation comes back as the same finding whichever of the three applications wrote the file.
The five that moved:
text-alignhas no distributed value, and onefont-familycannot fill a Latin slot and leave the complex-script one empty;a_latin_font_with_an_empty_arabic_slot_…now asserts the two formats with two slots report it and the one with a single stack cannot state it;a_hard_left_edge_…now asserts both formats that can state it report it identically, where it used to assert only one format could;Corpus:
quarterly-page.htmlcarries one instance of each defect the adapter can see — including the unread stylesheet and the exit coderepairgives a readable format with no writer — andquarterly-page-correct.htmlis the page the tool must leave completely alone. Unlike the decks beside them they are not generated: a page is its own source.make verifyis green.