Skip to main content

Document

Struct Document 

Source
pub struct Document {
Show 14 fields pub document: Document, pub pending: Vec<Node>, pub node: Node, pub node_boxes: FxHashMap<usize, Digested>, pub node_fonts: FxHashMap<u64, Font>, pub idstore: FxHashMap<String, Node>, pub spilled_ids: FxHashSet<String>, pub rewrite_labels: FxHashMap<String, String>, pub rewrite_labels_shared: Option<Rc<FxHashMap<String, String>>>, pub scoped_rules_strict: bool, pub literal_placeholders: bool, pub spill_flat: bool, pub fragment_ancestor_ids: FxHashSet<String>, pub fragment_parent_qname: Option<SymStr>, /* private fields */
}

Fields§

§document: Document§pending: Vec<Node>§node: Node§node_boxes: FxHashMap<usize, Digested>§node_fonts: FxHashMap<u64, Font>§idstore: FxHashMap<String, Node>§spilled_ids: FxHashSet<String>

Streaming pass 1: every xml:id moved out of idstore by a spill. The GLOBAL half of id-collision dedup — a later build-time minting of the same id must rename exactly as eager would have (witness: the 131 MB book restarts chapter numbering per part, and the second Ch1 collided only with a SPILLED chapter).

§rewrite_labels: FxHashMap<String, String>§rewrite_labels_shared: Option<Rc<FxHashMap<String, String>>>

Document-wide labels SHARED by every streaming pass-2 fragment, consulted only when rewrite_labels misses. Pass 2 used to copy the whole spilled index into each fragment’s own map, which is quadratic in document size: 28,068 labels × 459,579 segments on the 131 MB witness = 12.9 billion String allocations. Frag-local labels still win, preserving exactly the entry().or_insert_with() precedence that copy had.

§scoped_rules_strict: bool

Streaming only: an UNRESOLVED label:/id: rewrite scope makes the rule INERT instead of continuing unscoped. Perl (and the eager path) continue with the remaining clauses on the same tree when a scope fails to resolve — harmless there, because in a whole document a scope=section id always resolves. In a fragment (or on the spine, where sections are spilled placeholders) “not here” usually means “in another fragment”, and continuing unscoped applies the rule to EVERYTHING — sweep witness tests/math/declare.tex, where section-7 declarations stamped section-1 math.

§literal_placeholders: bool

Streaming: serialize spill placeholders LITERALLY (<_spilled_ ref=…/>) instead of splicing the segment text. True during pass 1 (a spilling ancestor must keep its children’s placeholders — inlining them rebuilt multi-GB segments: an 841 MB and a 1.85 GB one on the 131 MB witness, and pass 2 died re-parsing them) and on pass-2 fragment docs (which re-emit the placeholders they contain). False only at final assembly, where the splice resolves placeholders RECURSIVELY.

§spill_flat: bool

Serialize spill segments FLAT — no indentation, no decorative newlines.

A spilled segment’s text is an intermediate: pass 2 re-parses it, processes it, and re-serializes with serialize_aux at the recorded depth, and only THAT text reaches the output. The indentation pass 1 used to emit was therefore generated, written to disk, read back, materialized as libxml2 text nodes, deleted again by strip_indentation_whitespace, and finally regenerated — pure round-trip tax.

Measured on the 131 MB witness: 51.2% of serialized bytes are leading whitespace + newlines (189.4 MB of 381.5 MB sampled, 6,092,937 lines). Over ~2.45 GB of segment text that is ~1.2 GB generated and written, read back, and ~40M text nodes allocated in pass 2’s parse — which strip_indentation_whitespace then unlink_nodes, and unlink does not free, so they are orphaned for the fragment’s lifetime.

Set for pass 1 only. Fragment documents in pass 2 are separate Documents and default to false, so the OUTPUT keeps its formatting exactly.

§fragment_ancestor_ids: FxHashSet<String>

Streaming pass 2 only: the fragment’s ancestor xml:ids at spill time (SegmentMeta::ancestors). A label:/id:-scoped rewrite whose scope resolves to one of these covers the WHOLE fragment.

§fragment_parent_qname: Option<SymStr>

Streaming pass 2 only: the recorded qname of the fragment’s REAL parent (SegmentMeta::parent). finalize_rec substitutes it for the ltx:_lxfragment parse wrapper in schema decisions, so top-level fragment content is judged against the element it will splice back under (e.g. the empty-ltx:text collapse needs can_contain(parent, grandchild)).

Implementations§

Source§

impl Document

Source

pub fn new() -> Self

Source

pub fn from_xml_document( doc: XmlDoc, node_fonts: HashMap<u64, Font>, ) -> Result<Self>

Wrap an existing XML document (a streamed fragment from crate::sxml::FragmentReader) as a Document, so the pass-2 phases — rewrites, math parsing, per-fragment finalize — run on it with the same machinery the eager path uses. The insertion point starts at the root; the idstore is rebuilt from the fragment’s own xml:ids; node_fonts is seeded by the caller (the hash→Font table is conversion-global and pointer-free, so sharing its contents is safe).

Source

pub fn get_element(&self) -> Option<Node>

Get the element at (or containing) the current insertion point.

Source

pub fn findnodes(&mut self, xpath: &str, node_opt: Option<&Node>) -> Vec<Node>

Find the nodes according to the given xpath expression, the xpath is relative to $node (if given), otherwise to the document node.

Source

pub fn get_xpath(&mut self) -> &mut XPath

Get an XPath context that knows about our namespace mappings.

Source

pub fn findnode(&mut self, xpath: &str, node: Option<&Node>) -> Option<Node>

Like findnodes, but only returns the first matched node

Source

pub fn findvalues( &mut self, xpath: &str, node_opt: Option<&Node>, ) -> Vec<String>

Like findnodes, but expects an xpath that evaluates to a literal value (e.g. for attributes)

Source

pub fn get_node(&self) -> &Node

The current insertion point: the node that absorbed content is appended to, and the one open_element opens beneath. Perl’s $document->getNode.

Source

pub fn get_node_mut(&mut self) -> &mut Node

Source

pub fn get_document(&self) -> &XmlDoc

Source

pub fn get_document_mut(&mut self) -> &mut XmlDoc

Source

pub fn finalize(&mut self) -> Result<()>

Source

pub fn finalize_subtree(&mut self, node: &mut Node) -> Result<()>

finalize restricted to one subtree: resolve fonts against ancestors and strip the _font/_autoopened/… bookkeeping attributes, without the document-level passes (idstore rebuild, XMDual pruning, RDFa/namespace declarations) that only make sense for a complete document.

Used by latexml_post::make_bibliography to render a .bib field value into an XML fragment through the real engine: it absorbs into a scratch ltx:text wrapper and serializes that wrapper’s children.

Whole-document finalize cannot serve that caller — not because it errors (it returns Ok), but because running finalize_rec from the ROOT legitimately UNWRAPS a redundant font-only ltx:text: measured on such a scratch document, the content survives at the root while the caller’s wrapper handle is left detached and childless (wrapper_children 1 -> 0, parent = None), so it serializes to nothing. Starting the recursion AT the wrapper keeps it addressable.

Source

pub fn cleanup_unreferenced_xmtok_ids(&mut self)

Remove xml:ids from XMTok elements that aren’t referenced by any idref. The Rust math parser generates xml:ids on XMTok nodes for internal XMRef linkage during parsing. After finalization (which includes prune_xmduals), some ids are no longer referenced. Perl’s parser doesn’t generate these ids.

Source

pub fn absorb( &mut self, object: &Digested, props_opt: Option<SymHashMap<Stored>>, ) -> Result<()>

Document construction at the Current Insertion Point.

absorb the given $box into the DOM (called from constructors). This will return a list of whatever nodes were created. Note that this may include nodes that are children of other nodes in the list or nodes that are no longer in the document. Also, note that when a text nodes is appended to, the complete text node is in the list, not just the portion that was added. [Note that recording the nodes being constructed isn’t all that costly, but filtering them for parent/child relations IS, particularly since it usually isn’t needed]

A box that is a TBox, or List, or Whatsit, is responsible for carrying out its own insertion, but it should ultimately call methods of Document that will record the nodes that were created. $box can also be a plain string (Digested::Postponed) which will be inserted according to whatever font, mode, etc, are in %props.

Source

pub fn get_constructed_nodes(&self) -> &[Node]

Source

pub fn absorb_string( &mut self, object: &str, props: &SymHashMap<Stored>, ) -> Result<Option<Node>>

This is a refactored else cases from the main absorb routine, to allow for better type hygiene

Source

pub fn insert_element_before( &self, point: &Node, qname: &str, attrib: Option<HashMap<String, String>>, ) -> Result<Node>

Perl: insertElementBefore — insert a new element before a given point node. Creates a new element with the given qname and attributes, inserts it before point in the DOM tree, and returns the new node.

Source

pub fn insert_element( &mut self, qname: &str, content: Vec<&Digested>, attrib: Option<HashMap<String, String>>, ) -> Result<Node>

Shorthand for open,absorb,close, but returns the new node.

Source

pub fn insert_pi( &mut self, op: &str, attributes_opt: Option<HashMap<String, String>>, ) -> Result<()>

Insert a ProcessingInstruction of the form Does NOT move the current insertion point to the PI, but may move up past a text node.

Source

pub fn open_element( &mut self, qname: &str, attributes: Option<HashMap<String, String>>, font_opt: Option<&Font>, ) -> Result<Node>

Open a new qname element and make it the current insertion point.

Where it opens is the model’s decision, not the caller’s: the insertion point is first moved by find_insertion_point, which auto-opens any element the schema requires in between and auto-closes any that cannot contain qname. So a binding may open a ltx:para without first checking what is currently open.

See open_element_at to open at an explicit node instead, and close_element for the counterpart.

Source

pub fn close_element(&mut self, qname: &str) -> Result<Option<Node>>

Note: This closes the deepest open node of a given type. This can cause problems with auto-opened nodes, esp. ones for fontswitches! Since this is an “explicit request”, we’re currently skipping over those nodes, ie. we’re automatically closing them, even if they’re the same type as we’re asking to close!!! This is kinda risky! Maybe we should try to request closing of specific nodes.

Source

pub fn is_openable(&self, test_qname: &str) -> bool

Source

pub fn is_closeable<T: IntoVDQS>(&self, tags: T) -> Option<Node>

Check whether it is possible to close each element in @tags, any intervening nodes must be autocloseable. returning the last Some(node) that would be closed if it is possible, otherwise None

Source

pub fn maybe_close_element(&mut self, qname: &str) -> Result<Option<Node>>

Close qname, if it is closeable — the forgiving counterpart to close_element.

Returns the closed node, or None when no such element is open (or it cannot be auto-closed from here). That “or nothing happens” is the point: it lets a binding close an element it may have opened without having to track whether it did, where close_element would report an error.

Source

pub fn close_to_node(&mut self, node: &Node, ifopen: bool) -> Result<()>

Closes all nodes until $node becomes the current point.

Source

pub fn close_node(&mut self, node: &Node) -> Result<()>

Closes all nodes until $node is closed.

Source

pub fn maybe_close_node(&mut self, node: &Node) -> Result<()>

Only if needed/possible: closes all nodes until $node is closed

Source

pub fn close_node_with_strictness( &mut self, strict: bool, node: &Node, ) -> Result<()>

Source

pub fn get_tag_action_list_parts( &self, tag: SymStr, when: TagOptionName, ) -> (Vec<TagConstructionClosure>, Vec<TagConstructionClosure>)

Like Self::get_tag_action_list, but keeps the _Late bucket separate. Streaming needs the split: a root’s early/normal hooks (structural — the pending-resource drain) run at open as usual, while its _late hooks (semantically “with digestion complete” — frontmatter placement, root classes) are deferred to end-of-digestion.

Source

pub fn get_tag_action_list( &self, tag: SymStr, when: TagOptionName, ) -> Vec<TagConstructionClosure>

get the actions that should be performed on afterOpen or afterClose

Source

pub fn serialize_to_string(&self) -> String

Source

pub fn serialize_aux( &self, node: &Node, depth: usize, noindent: bool, heuristic: bool, ) -> String

We ought to try for something close to C14N (http://www.w3.org/TR/xml-c14n), but keep XML declaration, comments and don’t convert empty elements.

Source

pub fn set_node(&mut self, node: &Node)

Move the current insertion point to node — Perl Document.pm:setNode L74-87.

A document fragment is not itself an insertion point: as in Perl, a single-child fragment is descended into, and a fragment with several children is an error (reported, not fatal — see the body comment on why this returns ()).

Source

pub fn insert_math_token( &mut self, text: &str, attributes: HashMap<String, String>, font_opt: Option<&Font>, ) -> Result<Node>

Source

pub fn insert_comment(&mut self, text: &str) -> Result<Node>

Insert a new comment, or append to previous comment. Does NOT move the current insertion point to the Comment, but may move up past a text node. Perl: Document.pm lines 678-698

Source

pub fn open_text(&mut self, text: &str, font: &Font) -> Result<Option<Node>>

Source

pub fn close_text_internal(&mut self) -> Result<Node>

Source

pub fn close_node_internal(&mut self, node: &Node) -> Result<()>

Close node, and any current nodes below it. No checking! Use this when you’ve already verified that node can be closed. and, of course, node must be current or some ancestor of it!!!

Source

pub fn merge_attributes( &mut self, from: &Node, to: &mut Node, force: Option<&HashSet<&'static str>>, ) -> Result<()>

Source

pub fn record_constructed_node(&mut self, node: &Node)

Note that a box has been absorbed creating node; This does book keeping so that we can return the sequence of nodes that were added by absorbing material.

Source

pub fn filter_deletions(&self, nodes: Vec<Node>) -> Vec<Node>

Source

pub fn filter_children(&self, nodes: Vec<Node>) -> Vec<Node>

Given a list of nodes such as from ->absorb, filter out all the nodes that are children of other nodes in the list.

Source

pub fn get_insertion_context(&self, levels_opt: Option<usize>) -> Result<String>

Return a string indicating the path to the current insertion point in the document. if $levels is defined, show only that many levels

Source

pub fn find_insertion_point( &mut self, qname: &str, has_opened_opt: Option<SymStr>, ) -> Result<Node>

Find the node where an element with qualified name qname can be inserted. This will move up the tree (closing auto-closable elements), or down (inserting auto-openable elements), as needed.

Source

pub fn find_insertion_point_qsym( &mut self, qsym: SymStr, has_opened_opt: Option<SymStr>, ) -> Result<Node>

Source

pub fn node_set_attribute(&mut self, key: &str, value: &str) -> Result<()>

Source

pub fn node_get_attribute(&mut self, name: &str) -> Option<String>

Source

pub fn set_attribute( &mut self, node: &mut Node, key: &str, value: &str, ) -> Result<()>

Set any allowed attribute on a node, decoding the prefix, if any. Also records, and checks, any id attributes. [xml:id and namespaced attributes are always allowed]

“Allowed” is the document model’s call, so this is not a plain libxml write: an attribute the schema does not permit on node is dropped rather than emitted, which is what keeps a binding from producing a document that fails validation.

Source

pub fn add_ss_values( &mut self, node: &mut Node, key: &str, values_str: &str, ) -> Result<()>

Source

pub fn add_class(&mut self, node: &mut Node, class: &str) -> Result<()>

Add one or more CSS classes to a node, without disturbing the ones it already carries.

class is treated as the space-separated set it is (add_ss_values), so several bindings can each contribute a class to the same element — which is why this exists rather than a plain setAttribute("class", …), and what remove_ss_values undoes.

Source

pub fn remove_ss_values(&mut self, node: &mut Node, key: &str, values: &str)

Remove space-separated values from an attribute. Perl: sub removeSSValues (Document.pm lines 1423-1437)

Source

pub fn remove_class(&mut self, node: &mut Node, class: &str)

Remove CSS class from element. Perl: sub removeClass (Document.pm lines 1439-1442)

Source

pub fn float_to_attribute(&mut self, key: &str) -> Option<Node>

Float to a node that can accept the given attribute. Returns the previous node so it can be restored after setting the attribute. Perl: sub floatToAttribute (Document.pm lines 1080-1092)

Source

pub fn is_open(&self, node: &Node) -> bool

Check if a node is currently open (i.e., is or contains the current node). Perl: sub isOpen (Document.pm lines 1998-2006)

Source

pub fn unrecord_id(&mut self, id: &str)

Guardian-safe unlink: walk node’s subtree invalidating every xml:id idstore entry, then detach it from its parent. Use this in preference to a raw node.unlink() anywhere the node might carry an xml:id (subtree reshuffles in math-parser, post-processing cleanup, etc.), to prevent the dangling-Node class of bug that produced the 1605.08055 Finalizing-phase SIGSEGV (see SYNC_STATUS.md D3b).

This is the unlink-only half of remove_node — it does not adjust self.node / the insertion point, which is correct for callers that intend to re-parent the unlinked subtree elsewhere (the common case). Callers that want the insertion-point bookkeeping should use remove_node instead.

Source

pub fn record_node_ids(&mut self, node: &Node) -> Result<()>

These are used to record or unrecord, in bulk, all the ids within a node (tree).

When record_id_with_node detects a duplicate it renames the id (e.g. X.1.mfX.1.mfa). Any sibling <ltx:XMRef idref="X.1.mf"/> in the same subtree would otherwise become a dangling reference for the post- processor. After re-recording IDs, sweep the subtree once and update any XMRef whose idref matches an entry in the rename map.

Perl Core::Document::recordNodeIDs (Document.pm L1466-1472) has the same latent bug — recording renames but XMRefs aren’t touched. The bug surfaces in our port because the math-parser path (parser.rs::install_replacements-style unrecord+record round-trips) is denser than Perl’s; both end up needing this remap to keep XMRef chains intact. Intentional surpass-Perl divergence; tracked in SYNC_STATUS Task #10.

Source

pub fn unrecord_node_ids(&mut self, node: &Node)

Source

pub fn spill_closed_subtrees( &mut self, index: &mut FragmentIndex, ) -> Result<usize>

Spill closed subtrees to the segment store, freeing their DOM memory (streaming pass 1).

Walks the SPINE — root element down to the current insertion element — and, at every level, spills the runs of closed element children that precede the open (spine) child: serialize in the pre-finalize form with the exact (depth, noindent) the eager serializer would use, write to store, record every spilled xml:id/label in index, purge the pointer-keyed registries (idstore via unrecord_id, node_boxes by key — freed nodes’ pointers can be REUSED, so a stale entry is not just dangling but can mis-associate a box with a future node), replace the run with one <_spilled_ ref="N"/> placeholder, and unlink+free the nodes. Serialization at assembly time splices the processed segment where the placeholder sits, so document order, nesting and spine-attribute finality are preserved by construction.

Two deliberate exclusions:

  • ROOT-level children spill only when sectional (ROOT_SPILLABLE): the frontmatter fallback and maybe_promote_leading_title operate on the leading non-sectional root children at end-of-build.
  • Children of the insertion element itself never spill: in-flight construction state may still reference recently built nodes.

Returns the number of runs spilled.

Source

pub fn purge_node_boxes_rec(&mut self, node: &Node)

Remove the pointer-keyed node_boxes entries for node and its whole subtree. MANDATORY before freeing nodes mid-conversion: a freed node’s address is REUSED by later allocations, and a stale entry then mis-associates the old box (and its font) with an unrelated new node — observed as spurious <text font="italic"> wrappers the moment the math parser started freeing replaced trees promptly.

Source

pub fn discard_subtree(&mut self, node: Node)

Discard a garbage subtree for good: purge the pointer-keyed node_boxes entries, then free the C memory NOW via the fork’s Node::free_subtree — the missing half of every unlink-to-discard site (rust-libxml Linkage: a doc-created node that is unlinked but never re-attached is freed by NOBODY; math parsing discards thousands of replaced subtrees per document, measured ~1.4 MB/formula, ~1.8 GB retained per 32 MB streaming segment). free_subtree also neutralizes every live wrapper into the subtree, so stray clones in long-lived collections (constructed_nodes, an idstore epoch) go inert instead of firing a deferred xmlFreeNode after the document itself is gone.

PRECONDITIONS (the caller’s contract, satisfied at current sites):

  • every xml:id in the subtree is already unrecorded or transferred — this deliberately does NOT unrecord_node_ids, because discard always follows a copy (append_tree) that re-recorded the SAME id strings for the copies, and unrecording here would kill those fresh entries;
  • no live handle into the subtree is used afterwards.
Source

pub fn sweep_stale_node_boxes(&mut self)

Drop every node_boxes entry whose node is no longer IN this document tree (streaming pass 1’s self-healing sweep). Entries are written for every constructed node and purged at the spill/discard chokepoints — but dozens of build-time discard paths (alignment rearrangement above all: 105k align environments on the 131 MB witness) detach nodes without purging, and each stale entry pins a whole Digested box tree. Measured: ~518k stale entries before the FIRST spill, growing past 1.75M — the dominant residual pass-1 creep after the C-side frees. A mark-and-retain against the live tree is immune to every such path, including future ones. Runs when the map is large; the post-spill spine is small, so the mark phase is cheap.

Source

pub fn set_spill_store(&mut self, store: SegmentStore)

Attach the processed-segment store for serialization: from here on, serialize_into splices each segment’s output text where its placeholder sits (streaming assembly).

Source

pub fn set_defer_root_after_open(&mut self, defer: bool)

Streaming pass 1 switch: defer (true) or restore (false) the ROOT element’s after_open hook dispatch. See the field docs.

Source

pub fn root_after_open_deferred(&self) -> bool

Is the root’s late-hook dispatch currently deferred (streaming pass 1)? Engine bindings consult this to defer digestion-state-dependent insertions (frontmatter) to end-of-digestion.

Source

pub fn process_pending_resources_at_top(&mut self) -> Result<()>

Streaming pass 1: fold freshly queued resources into the live document as they arrive, at the top of the root. Perl’s own contract (Package.pm:RequireResource): with a live document, addResource inserts DIRECTLY; only a document-less preamble queues. The eager path keeps the queue until the root’s after_open because its build starts post-digestion; under streaming that drain is deferred to end-of-digestion, and mid-digestion consumers — the frontmatter fallback’s /ltx:document/ltx:resource[last()] anchor — need the resources already placed.

Source

pub fn dispatch_deferred_root_hooks(&mut self) -> Result<()>

Streaming: dispatch the ROOT’s deferred after-open hooks at end-of-digestion — the eager timing (build starts only after digestion ends) — with the insertion point at the TOP of the root, as if it had just opened empty. after_open itself cannot be used here: it pins the current node to the dispatched element, so everything the hooks insert (resources, frontmatter) would APPEND after the built content instead of leading it. A ltx:_Capture_ wrapper as first child recreates the empty-root insertion context (the frontmatter fallback’s own technique), and is unwrapped afterwards.

Source

pub fn add_extra_rdfa_prefixes<'a>( &mut self, prefixes: impl Iterator<Item = &'a str>, )

Merge RDFa prefixes recorded from spilled content, so the root’s prefix= attribute covers usages the live-DOM scan can no longer see.

Source

pub fn take_spill_store(&mut self) -> Option<SegmentStore>

Detach the spill store (the streaming driver takes it out for pass 2 — which mutates segments while fragment documents exist independently — and re-attaches it for assembly).

Source

pub fn rebuild_idstore_from_dom(&mut self) -> Result<()>

Discard the in-memory idstore cache and rebuild it from the current DOM state. Historically guarded the 1605.08055 SIGSEGV where mark_xmnode_visibility dereferenced dangling lookup_id entries while recursing through XMRef targets.

As of cycle 72, the 5 call sites that previously dropped nodes without unrecord_id — math-parser replace_tree at parser.rs:456/690 (cascades via remove_node) and unbind_node loops at parser.rs:639/856 + rewrite.rs:522 (all have preceding unrecord_node_ids guards) — are ID-safe. This rebuild is retained as a belt-and-suspenders probe until the 1605.08055 verification per SYNC_STATUS.md D3b lands.

The rebuild is a DOM walk, so live id uniqueness is restored alongside — duplicates already in DOM are resolved with modify_id, matching record_node_ids semantics.

Source

pub fn modify_id(&mut self, id: String) -> String

Get a new, related, but unique id. Sneaky option: try “ID_SUFFIX” as a suffix for id, first. Perl: sub modifyID (Document.pm lines 1483-1494)

Source

pub fn lookup_id(&self, id: &str) -> Option<&Node>

Source

pub fn get_idstore_clone(&self) -> HashMap<String, Node>

Clone the idstore for use in thread-local contexts (math parsing).

Source

pub fn mark_xmnode_visibility(&mut self) -> Result<()>

Source

pub fn prune_dangling_split_xmrefs(&mut self) -> Result<()>

Remove ltx:XMRef[@_split_ref="1"] whose idref no longer resolves. These are the XMRefs minted by amsmath::rearrange_ams_split to mirror the flattened cell sequence inside an XMDual(XMWrap(refs), XMArray(cells)). The math parser can later absorb some cells (typically inserted MULOP times-ops on \mathcal{L}\rho chains) into wrapping XMApps, dropping their xml:id from the live DOM and leaving the sibling XMRefs dangling. Left in place, each dangling XMRef trips three separate diagnostics later — math parser’s read_xmref Warn, finalize’s mark_xmnode_visibility Warn, and post-process’s mark_xm_node_visibility Error.

We restrict the sweep to the _split_ref marker so refs from other provenance (base_xmath \lx@dual, renamed-id cases like declare_test’s S1.Ex1.m1.1.1a rename) stay untouched.

Content-preserving: XMRefs are structural cross-references, not author body, and the math parser has already absorbed the referenced cell into the visible XMArray branch — no glyph or formula material is lost.

Source

pub fn prune_xmduals(&mut self) -> Result<()>

Reduce any ltx:XMDual’s to just the visible branch, if the other is not visible (according to markXMNodeVisibility) If we could be 100% sure that the marking had stayed consistent (after various doc surgery) we could avoid re-marking, but we’d better be sure before removing nodes!

Source

pub fn set_node_box(&mut self, node: &Node, digested: Digested)

Record the Box that created this node.

Source

pub fn get_node_box(&self, node: &Node) -> Option<Digested>

Source

pub fn set_node_font(&mut self, node: &mut Node, font: &Font) -> Result<()>

Record the Font of a node

Source

pub fn copy_node_font(&mut self, from: &Node, to: &mut Node) -> Result<()>

Source

pub fn merge_node_font_rec(&mut self, node: &Node, font: &Font) -> Result<()>

Possibly a sign of a design flaw; Set the node’s font & all children that HAD the same font.

Source

pub fn set_box_font(&mut self, node: &mut Node) -> Result<()>

Source

pub fn get_node_font(&self, node: &Node) -> &Font

Source

pub fn decode_font(&self, font_hash: &str) -> Option<&Font>

Decode a _font hash string to a Font object

Source

pub fn has_node_font(&self, node: &Node) -> bool

Source

pub fn get_node_language(&self, node: &Node) -> String

Source

pub fn remove_node(&mut self, node: Node)

Remove a node from the document (from it’s parent).

Two pieces of bookkeeping come with it: the xml:id of the node and of every descendant is un-recorded, so the ids are free for reuse and no dangling reference is left behind; and if the insertion point was inside what is being removed, it is rescued up to the parent — otherwise the document would go on building into a detached subtree.

Source

pub fn open_element_at( &mut self, point: &mut Node, qname: &str, attributes: Option<HashMap<String, String>>, font_opt: Option<Font>, ) -> Result<Node>

This opens a new element at the specified point, rather than the current insertion point. This is useful during document rearrangement or augmentation that may be needed later in the process.

Source

pub fn close_element_at(&mut self, node: &mut Node) -> Result<()>

Whenever a node has been created using openElementAt, closeElementAt ought to be used to close it, when you’re finished inserting into $node. Basically, this just runs any afterClose operations.

Source

pub fn after_open(&mut self, node: &mut Node) -> Result<()>

Source

pub fn after_close(&mut self, node: &mut Node) -> Result<()>

Source

pub fn append_clone( &mut self, node: &mut Node, new_children: Vec<Node>, ) -> Result<()>

Append COPIES of new_children under node.

Cloning rather than moving is what makes this usable on nodes that belong to another document — moving them would remove them from it. Three things are repaired on the way in: document fragments are expanded to their children, the namespace structure is rebuilt clean (libxml2 otherwise has a tendency to introduce annoying “default” namespace prefix declarations), and every xml:id in the copy is rewritten to a fresh id, with internal references remapped to match — otherwise the copy would duplicate the original’s ids.

Source

pub fn wrap_nodes( &mut self, qname: &str, nodes: Vec<Node>, ) -> Result<Option<Node>>

Wrap nodes with an element named qname, making the new element replace the first node, and all nodes becomes the child of the new node. [this makes most sense if nodes are a sequence of siblings]

Returns None if qname isn’t allowed in the parent, or if nodes aren’t allowed in qname, otherwise the newly created qname — so a caller must treat “wrapped” as a request the model may decline, not as a guarantee. None also covers a first node with no parent (already detached, or the root): there is nothing to wrap it in place of. Witness 1804.09736.

The wrapper inherits the parent’s font and box, so wrapping does not change how the enclosed material renders. unwrap_nodes is the inverse.

Source

pub fn unwrap_nodes(&mut self, node: Node) -> Result<()>

Unwrap the children of $node, by replacing $node by its children.

Source

pub fn replace_node(&mut self, node: Node, with: Vec<Node>) -> Result<()>

Replace node by nodes (presumably descendants of some kind?)

Source

pub fn rename_node( &mut self, node: Node, newname: &str, reinsert: bool, ) -> Result<Node>

Rename an element to newname, returning the new node.

Not an in-place rename: a fresh element is opened next to the original, the attributes and (when reinsert) the children are carried over, and the original is removed. Perl went this way “initially since $node->setNodeName was broken in XML::LibXML 1.58”, and kept it because building the replacement through the normal open path is what runs the model’s checks and afterOpen hooks for the new tag — a raw rename would leave an element the schema never vetted.

rename_node_qsym is the same for an already interned name.

Source

pub fn rename_node_qsym( &mut self, node: Node, newsym: SymStr, reinsert: bool, ) -> Result<Node>

Source

pub fn trim_node_whitespace(&mut self, node: &Node) -> Result<()>

Source

pub fn add_resource(&mut self, resource: Resource) -> Result<()>

Source

pub fn process_pending_resources(&mut self) -> Result<()>

Source

pub fn make_error(&mut self, error_class: &str, content: &str) -> Result<()>

Source

pub fn float_to_element( &mut self, qname: &str, closeifpossible: bool, ) -> Result<Option<Node>>

Find a node in the document that can contain an element qname

Source

pub fn float_to_label(&mut self) -> Option<Node>

Source

pub fn set_box_to_absorb(&mut self, arg: Option<Digested>)

Source

pub fn expire_box_to_absorb(&mut self)

Source

pub fn lookup_rewrite_label(&self, key: &str) -> Option<String>

Resolve a rewrite label: this document’s own labels first, then the shared document-wide map a streaming fragment carries (rewrite_labels_shared). The two-level lookup replaces copying the whole label index into every fragment — see the field docs.

Source

pub fn load_labels_for_rewrite(&mut self) -> Result<()>

Source

pub fn generate_id(&mut self, node: &mut Node, prefix: &str) -> Result<()>

This function computes an xml:id for a node, if it hasn’t already got one. It is suitable for use in Tag afterOpen as Tag('ltx:para',afterOpen=>sub { GenerateID(@_,'p'); }); It generates an id of the form <parentid>.<prefix><number> The parent node (the one with ID=<parentid>) also maintains a counter stored in an attribute _ID_counter_<prefix> recording the last used number for prefix amongst its descendents.

Source

pub fn replace_tree(&mut self, new: Node, old: Node) -> Result<Option<Node>>

Source

pub fn replace_tree_free( &mut self, new: Node, old: Node, ) -> Result<Option<Node>>

replace_tree for a caller that owns BOTH trees as garbage-after-copy (the math parser’s rebuild sites): the replacement is copied into place exactly as replace_tree does (Perl appendTree parity — elements are re-created, sources abandoned), and then the sources are FREED: old’s subtree, plus new’s detached root when new is a standalone built tree rather than a node inside old. Without the frees every replaced formula leaks its pre-parse tree AND the built parse tree (see discard_subtree).

On the None return (old had no parent) nothing was copied and NOTHING is freed — the caller keeps using new as-is.

Source

pub fn append_tree(&mut self, node: &mut Node, data: Vec<Node>) -> Result<()>

Source

pub fn insert_xml(&mut self, xml: &str) -> Result<()>

Parse an XML / (X)HTML markup string and splice the resulting subtree into the document at the current insertion point.

Named as the markup counterpart to Document::insert_element (Perl insertElement): both insert an already-FINISHED thing at the current point. Deliberately NOT an absorb* name — in Perl absorb consumes a digested Box and has no XML::LibXML branch at all (it would die on a node), so borrowing that verb here would imply a kinship that does not exist.

This is the Rust analog of Perl BookML’s \bmlRawHTML idiom, which composes two mechanisms core never chains itself: XML::LibXML->parse_string (LaTeXML::Common::XML::Parser parseChunk, Common/XML/Parser.pm:36-39) and $document->appendTree (Document.pm:2093, foreign-node branch :2105-2124). Both halves already exist here — libxml’s parser (a direct latexml_core dep, used in common/relaxng/scan.rs) and Document::append_tree — so this is pure glue.

The markup must be WELL-FORMED, but need not be a single root: several sibling nodes, or bare text, are accepted as a document fragment (OXIDIZED_DESIGN #66 — Perl’s parseChunk is single-node only). The parsed nodes are re-created one by one through append_tree’s model-aware path, so namespaces declared in the snippet (e.g. xhtml) are preserved and xml:ids re-registered. Malformed markup is REJECTED, never salvaged — see crate::common::xml::parse_chunk for what libxml’s recovery mode destroys — and surfaces as a clean Error: that inserts nothing, degrading the offending binding rather than aborting the conversion (the runtime-bindings failure-isolation contract).

Source

pub fn insert_nodes(&mut self, nodes: Vec<Node>) -> Result<()>

Splice ALREADY-PARSED nodes into the document at the current insertion point. The shared tail of Document::insert_xml and of any caller that obtained its nodes some other way (a script that parsed once and inserts repeatedly, or that edited the parsed tree before inserting).

The caller must keep whatever owns nodes alive across this call: libxml nodes are pointers into their document. crate::common::xml::ParsedFragment exists to make that ownership explicit rather than a comment.

Trait Implementations§

Source§

impl Default for Document

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Object for Document

Source§

fn get_locator(&self) -> Option<Locator>

The object’s stored source locator, if it has one. None is the honest “no recorded source position” (replacing the old Locator::default() file!()/line!() sentinel). For “where the parser is now” (error reporting, box creation), use the free fn gullet::get_locator().
Source§

fn stringify(&self) -> String

Source§

fn isa_box(&self) -> bool

Source§

fn is_expandable(&self) -> bool

Source§

fn is_definition(&self) -> bool

Source§

fn is_comment(&self) -> bool

Source§

fn revert(&self) -> Result<Tokens>

each concrete object needs to provide its own path back to tokens

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.