← text2d · version 0.4.6
28 public items, in source order. Generated from the registry index, which re-derives them from the package source at publish time — so a signature here is the one that version actually ships.
pub fn blit_glyph(cv: graphics::Canvas, code: integer, x: integer, y: integer, colour: integer, scale: integer)
Blit one codepoint's glyph with its top-left at (x, y). Shared by the direct blitter and by the atlas, so the bit-walk has ONE home and the two can never disagree about what a glyph looks like.
pub fn text_width(s: text, scale: integer) -> integer
Width in pixels of s at scale, INCLUDING the gap after every glyph but the last — the ink extent. Exact: it is what write_text answers, so the caller can centre with it and be right.
pub fn write_text(cv: graphics::Canvas, s: text, x: integer, y: integer, colour: integer, scale: integer) -> integer
⚠ NAMED write_text, NOT draw_text. graphics already has draw_text as a METHOD on Canvas, and a name has one body per receiver type: a free draw_text(cv: Canvas, …) here would be refused as a redefinition of it.
Draw s with its top-left at (x, y). Answers the width drawn — text_width(s, scale), 0 for an empty string — so a caller can lay out the next run without measuring twice.
⚠ Iterates CHARACTERS, not bytes. len(text) counts characters and an indexed read is bytes, so a byte loop truncates multi-byte text silently — the trap the strict-index lint exists for. A character with no glyph draws a blank of the same width rather than being skipped, so a string never changes length because one character was missing.
pub fn write_centred(cv: graphics::Canvas, s: text, bx: integer, by: integer, bw: integer, bh: integer, colour: integer, scale: integer) -> integer
Draw s centred in the box (bx, by, bw, bh). The commonest thing a HUD wants, and the commonest place an off-by-one lands.
pub struct Atlas { a_cv: graphics::Canvas, a_codes: vector<integer>, a_scale: integer, a_cols: integer, a_next: integer, a_writes: integer }
A glyph atlas: a canvas of glyph cells, the codepoint each cell holds, the scale they were drawn at, and how many times the sheet itself changed (a_writes).
pub struct Quad { q_x: integer, q_y: integer, q_cell: integer }
One glyph placed on a line: where it goes, and which cell it comes from.
pub fn atlas_new(scale: integer, capacity: integer) -> Atlas
A sheet with room for capacity glyphs at scale. Shelf-packed in a grid, which is enough for a fixed-width face — a proportional one would want a real packer, and would still not change what the caller sees.
pub fn atlas_glyph_count(self: const Atlas) -> integer
How many distinct glyphs the sheet holds. It rises only when a code is met for the FIRST time, so it measures what the atlas actually cost rather than how much text has been drawn through it.
pub fn atlas_writes(self: const Atlas) -> integer
How many times the sheet has been WRITTEN. A GL consumer uploads on exactly these, so a caller can assert that a changing label costs no upload — which is the property the whole arc exists for.
pub fn atlas_cell(self: Atlas, code: integer) -> integer
The cell holding code, rasterising it on first use. -1 when the sheet is full.
pub fn layout(self: Atlas, s: text, x: integer, y: integer) -> vector<Quad>
Lay a string out over the sheet. Answers one quad per character; NOTHING is written to the sheet unless a character is new to it.
pub fn draw_quads(cv: graphics::Canvas, atlas: const Atlas, quads: const vector<Quad>, colour: integer)
Blit laid-out quads from the sheet onto a canvas.
pub struct Metrics { m_adv64: integer, m_line_h: integer, m_mono: boolean }
What the consumer measured.
⚠ m_mono IS A FACT ABOUT THE FACE THAT RESOLVED, not the one that was asked for. Requesting DejaVuSansMono.ttf gives a fixed-pitch face on the desktop and a PROPORTIONAL fallback in the browser, because the browser resolves the base name to a CSS family it does not know. Only a measurement can tell.
pub fn metrics_measured(wide_run: float, narrow_run: float, n: integer, line_h: integer) -> Metrics
Build metrics from two runs the consumer measured: n copies of a WIDE character and n copies of a NARROW one.
⚠ THE TWO RUNS ARE THE WHOLE INSTRUMENT, and one width cannot replace them. Ten M's are ten M's in any font, so a single measurement gives the advance of an M and nothing about whether the face is fixed-pitch. Two same-length, opposite-width strings answer it in one comparison.
The advance is taken from the WIDE run: under fixed pitch the two agree, and when they do not, the wider one is the safe basis for a fits-in-the-box question — it over-estimates rather than overflowing.
pub fn metrics_builtin(scale: integer) -> Metrics
The built-in face's own metrics, so a layout written against this seam works with no font file at all — the property B0 exists for, carried forward.
pub fn advance64(self: const Metrics) -> integer
The advance in 1/64 px, as measured.
pub fn advance(self: const Metrics) -> integer
The advance in whole pixels, for a caller that wants to show it.
⚠ DERIVED, NOT A SECOND FIELD. One quantity stored twice is how two numbers describing one distance drift apart the moment either is edited.
pub fn height(self: const Metrics) -> integer
The line height in pixels — what to advance y by between two lines.
pub fn is_mono(self: const Metrics) -> boolean
Is the pitch fixed? True is what makes width exact instead of an over-estimate, so a caller laying out columns can trust the number.
pub fn width(self: const Metrics, s: text) -> integer
The ADVANCE width of s — what to reserve a box by.
⚠ ROUNDED UP. The residual is a fraction of a pixel per character, and the safe direction to spend it is outwards: an over-estimate reserves a pixel too many and the text still fits, an under-estimate spills out of a box that was just measured as fitting. Exact under fixed pitch, an over-estimate otherwise — the same reason the advance comes from the wide run.
Distinct from text_width, which is the built-in face's INK extent: the two differ by the one trailing gap the ink extent leaves off, and they answer different questions — centre by the ink, reserve by the advance.
pub fn fits(self: const Metrics, s: text, px: integer) -> boolean
Does s fit in px? Asks the ADVANCE width, which rounds outwards, so a true here means drawing it will not spill — the direction a caller needs. fit_text is what to reach for when the answer is no.
pub fn fit_text(self: const Metrics, s: text, px: integer) -> text
The longest prefix of s that fits in px, with ".." marking the cut.
⚠ AN UNTOUCHED STRING COMES BACK UNTOUCHED, so a caller cannot tell "fitted" from "never measured" by looking for the marker — it appears only when something was actually removed.
".." rather than a single "…": the ellipsis is one code point but several bytes, and a marker whose own width is ambiguous is the wrong thing to measure a truncation with.
⚠ CUTS BY CHARACTERS. This shipped in B1m as s[0..keep], a BYTE range given a character count — safe, because loft snaps a byte cut outward to a character boundary, but wrong: a non-ASCII label came back shorter than it needed to be. B2's take_chars is the cure and the trap is the same one wrapping has.
pub fn take_chars(s: text, n: integer) -> text
The first n CHARACTERS of s. The cure for the trap above, and public because every caller that slices text by a measured count needs it.
pub fn wrap(self: const Metrics, s: text, px: integer) -> vector<text>
Break s into lines that each fit px, greedily, on measured advances.
⚠ A LINE ALWAYS TAKES AT LEAST ONE CHARACTER. In a box narrower than the narrowest glyph the alternatives are an infinite loop or dropping the text, and a line that overflows and says so is better than either.
⚠ AND THE RESULTING TABLE IS PER TARGET. Native measures a real TTF, the browser measures whatever family resolved, so the same string breaks in different places. The invariant that travels is self-consistency: every line here fits the box THESE metrics measured.
pub fn align_x(self: const Metrics, line: text, bx: integer, bw: integer, align: integer) -> integer
Where to start drawing line inside a box at bx of width bw.
⚠ A LINE WIDER THAN ITS BOX STARTS AT THE BOX. Centring or right-aligning it arithmetically would push its beginning off the left edge, where it cannot be clipped back into view — overflow to the right is recoverable, overflow to the left loses the start of the text.