iris: drop bundled Noto Sans, match Compose's platform-font fonts
Iris's call: "remove the font for now; just match what compose does." Removes the six embedded Noto Sans/Noto Sans Mono TTFs (3.6 MB) that TextData::default used to register ahead of the platform's own fonts; fontique's system font discovery was already on by default and now runs unshadowed (Roboto/Roboto Flex on Android, fontconfig on the desktop). .so -3,748,136 bytes (11,193,608 -> 7,445,472), matching the estimate. Verified fallback still lands on visible tofu for CJK/emoji rather than blank, and flagged (not fixed) a fontique Android backend gap that leaves Monospace unresolved -- see RUST.md's "Platform fonts (2026-09-07)" and DECISIONS.md/IRIS.md's dated entries.
This commit is contained in:
1 parent
690161e5e9
commit
896c93a59a
12 files changed
+193
-312
No files matched your search
@@ -5,6 +5,40 @@ they can be judged and reversed later. Detail lives in RUST.md (and IRIS.md
|
|||||||
for iris API changes); this file is only the summary. Newest first. Items
|
for iris API changes); this file is only the summary. Newest first. Items
|
||||||
marked **DEFERRED** are ones the agent chose not to decide alone.
|
marked **DEFERRED** are ones the agent chose not to decide alone.
|
||||||
|
|
||||||
|
## 2026-09-07 (platform fonts, not bundled ones)
|
||||||
|
|
||||||
|
- **Iris's own decision, carried out as directed**: removed the 3.6 MB of
|
||||||
|
bundled Noto Sans/Noto Sans Mono TTFs from `iris-core` and load text
|
||||||
|
from the platform's own font collection instead (`fontique`'s system
|
||||||
|
discovery, already on by default). Matches what the Compose app does --
|
||||||
|
it takes body text from `FontFamily.Default` and code text from
|
||||||
|
`FontFamily.Monospace`, both platform-resolved, and ships no text font
|
||||||
|
of its own. Rejected alternative (the one this pass had left open
|
||||||
|
2026-09-06): subsetting the bundled Noto Sans to Latin/common
|
||||||
|
punctuation instead of removing it outright, which would have kept
|
||||||
|
identical rendering across devices for a smaller (not zero) size cost;
|
||||||
|
Iris chose to match Compose instead.
|
||||||
|
- `.so` **-3,748,136 bytes** (11,193,608 -> 7,445,472), matching the
|
||||||
|
original 3.6 MB estimate. Fallback still lands on the platform's own
|
||||||
|
tofu for a codepoint no resolved face has (checked with CJK + emoji on
|
||||||
|
desktop) rather than blank space, so the UI_RULES unknown-glyph rule
|
||||||
|
still holds.
|
||||||
|
- **Flagging for Iris, not blocking on it**: this fontique version's
|
||||||
|
Android backend never resolves the `Monospace` generic family at all
|
||||||
|
(confirmed on this checkout's emulator, `mono=None` in the startup
|
||||||
|
diagnostic) -- a pre-existing ordering bug in fontique's own
|
||||||
|
`fonts.xml` parsing, not something this change introduced, but this
|
||||||
|
change is what stopped masking it (the bundled mono font used to be
|
||||||
|
registered ahead of the broken platform lookup, so it always won).
|
||||||
|
Effect: code blocks and the tool-card chevron marks fall through to
|
||||||
|
the same face as body text on Android instead of a genuinely
|
||||||
|
monospaced one -- still visible, not blank, just not monospaced.
|
||||||
|
Compose does not share this gap; it resolves `FontFamily.Monospace`
|
||||||
|
through Android's own `Typeface.MONOSPACE`, not through fontique.
|
||||||
|
docs/RUST.md's "Platform fonts (2026-09-07)" has the full account and
|
||||||
|
why a fix (an OEM-specific named-family pin, or patching around
|
||||||
|
fontique) is left as a follow-up rather than done in this pass.
|
||||||
|
|
||||||
## 2026-09-06 (how a tool call looks, P1b)
|
## 2026-09-06 (how a tool call looks, P1b)
|
||||||
|
|
||||||
- **A card that never got a result says "no result", in yellow, and it is
|
- **A card that never got a result says "no result", in yellow, and it is
|
||||||
|
|||||||
@@ -8,6 +8,27 @@ capability that moved. Small and trivial changes do not go here.
|
|||||||
An entry gives the date, what changed, why, and a short before/after where
|
An entry gives the date, what changed, why, and a short before/after where
|
||||||
it helps judge the change without the session that made it. Newest first.
|
it helps judge the change without the session that made it. Newest first.
|
||||||
|
|
||||||
|
## 2026-09-07: `TextData` no longer bundles a font
|
||||||
|
|
||||||
|
Iris's call: "remove the font for now; just match what compose does."
|
||||||
|
`TextData::default()` used to embed six Noto Sans/Noto Sans Mono `.ttf`s
|
||||||
|
(3.6 MB, `include_bytes!`) and register them ahead of the platform's own
|
||||||
|
fonts in the `SansSerif`/`Monospace` fallback lists. That registration is
|
||||||
|
gone; `TextData::default()`'s signature is unchanged, but what it produces
|
||||||
|
now depends entirely on `fontique`'s platform discovery (already on by
|
||||||
|
default, previously shadowed) -- Roboto/Roboto Flex on Android, whatever
|
||||||
|
the desktop's fontconfig resolves on Linux. No caller-visible type or
|
||||||
|
method changed, but every consumer of `iris-core` text now renders with
|
||||||
|
whatever the host platform's fonts are, not a fixed bundled face -- worth
|
||||||
|
knowing if you were relying on pixel-identical text across devices.
|
||||||
|
`.so` shrank by 3.75 MB. One real gap surfaced by the switch: this
|
||||||
|
fontique version's Android backend never resolves the `Monospace`
|
||||||
|
generic family (a fontique ordering bug, not new in this change), so
|
||||||
|
`Family::Monospace` text falls through to the same face as
|
||||||
|
`SansSerif` on Android rather than a true monospaced one -- still
|
||||||
|
visible, not blank, just not monospaced. docs/RUST.md's "Platform fonts
|
||||||
|
(2026-09-07)" has the full account.
|
||||||
|
|
||||||
## 2026-09-07: a headless harness, replayed touch, and physical-pixel desktop layout
|
## 2026-09-07: a headless harness, replayed touch, and physical-pixel desktop layout
|
||||||
|
|
||||||
Layer 1 and 2 of docs/RUST.md's "Three test layers".
|
Layer 1 and 2 of docs/RUST.md's "Three test layers".
|
||||||
|
|||||||
+110
-9
@@ -110,6 +110,7 @@ override, reusing the incremental `target/`), and are the raw file sizes
|
|||||||
| + `codegen-units = 1` | 15,185,204 | 13,052,736 | -566,008 |
|
| + `codegen-units = 1` | 15,185,204 | 13,052,736 | -566,008 |
|
||||||
| + `opt-level = "s"` | 13,326,076 | 11,193,608 | -1,859,128 |
|
| + `opt-level = "s"` | 13,326,076 | 11,193,608 | -1,859,128 |
|
||||||
| + `opt-level = "z"` (not adopted, see below) | 12,507,276 | 10,374,808 | -818,800 |
|
| + `opt-level = "z"` (not adopted, see below) | 12,507,276 | 10,374,808 | -818,800 |
|
||||||
|
| + platform fonts, no bundled Noto (2026-09-07) | 9,577,940 | 7,445,472 | -3,748,136 |
|
||||||
|
|
||||||
Adopted: `strip = true`, `lto = "fat"`, `codegen-units = 1`, `opt-level = "s"`.
|
Adopted: `strip = true`, `lto = "fat"`, `codegen-units = 1`, `opt-level = "s"`.
|
||||||
Baseline to final: `libmain.so` **18,546,488 -> 11,193,608 bytes (-39.7%)**,
|
Baseline to final: `libmain.so` **18,546,488 -> 11,193,608 bytes (-39.7%)**,
|
||||||
@@ -181,7 +182,99 @@ rather than a fixed-codepoint one -- a real behaviour change (text in a
|
|||||||
language outside the subset would fall back to tofu or a missing glyph) and
|
language outside the subset would fall back to tofu or a missing glyph) and
|
||||||
out of scope for a size-only pass. Left as a follow-up, flagged for Iris:
|
out of scope for a size-only pass. Left as a follow-up, flagged for Iris:
|
||||||
subsetting would plausibly save 1-2 MB but changes what scripts render
|
subsetting would plausibly save 1-2 MB but changes what scripts render
|
||||||
correctly, which is a product decision.
|
correctly, which is a product decision. **Superseded 2026-09-07**: Iris
|
||||||
|
decided to remove the embedding outright rather than subset it -- see
|
||||||
|
"Platform fonts (2026-09-07)" below.
|
||||||
|
|
||||||
|
### Platform fonts (2026-09-07)
|
||||||
|
|
||||||
|
Iris's verdict on the open question above: **"remove the font for now;
|
||||||
|
just match what compose does."** The Compose app takes its body text from
|
||||||
|
`FontFamily.Default` (platform Roboto on Android) and its code/tool-output
|
||||||
|
text from `FontFamily.Monospace`, and ships no text font of its own --
|
||||||
|
only its committed Nerd Fonts icon subset (`app/build-icon-font.sh`) for
|
||||||
|
~100 fixed glyphs, a different case (a small, known, closed set of
|
||||||
|
codepoints, unlike arbitrary transcript text). iris had no equivalent icon
|
||||||
|
font to keep; it draws no icons through a font today, so there was nothing
|
||||||
|
parallel to preserve.
|
||||||
|
|
||||||
|
**What changed**: `TextData::register_bundled_fonts` and the six
|
||||||
|
`include_bytes!` Noto Sans/Noto Sans Mono constants are gone from
|
||||||
|
`iris/core/src/primitive/text.rs`, along with the `.ttf`s themselves and
|
||||||
|
their `OFL.txt` (`iris/core/assets/fonts/`, now removed -- nothing else in
|
||||||
|
the tree referenced the licence file). `TextData::default` now does
|
||||||
|
nothing but `FontContext::new()`, which was already discovering the
|
||||||
|
platform's fonts underneath the bundled ones -- `fontique` 0.11.1's
|
||||||
|
`CollectionOptions::system_fonts` defaults to `true`, and both platforms
|
||||||
|
this crate ships on have a real backend behind it: `backend/fontconfig.rs`
|
||||||
|
on Linux (this VM's desktop has a full Noto install, confirmed with
|
||||||
|
`fc-match sans-serif`/`fc-match monospace`), `backend/android.rs` on
|
||||||
|
Android (parses `/system/fonts` and `/system/etc/fonts.xml`, mapping
|
||||||
|
`SansSerif`/`SystemUi` to `["Roboto Flex", "Roboto", "Noto Sans"]` and
|
||||||
|
`Monospace` to `["monospace"]` -- see the fallback finding below for why
|
||||||
|
that last one does not actually resolve on this fontique version). So
|
||||||
|
removing the bundled registration did not need a replacement call; the
|
||||||
|
platform path was already live, just shadowed.
|
||||||
|
|
||||||
|
**Fallback, and the unknown-glyph state (UI_RULES: design it, don't let
|
||||||
|
it default to blank)**. `parley`'s shaper sets both an explicit family
|
||||||
|
list *and* a script/locale-keyed fallback chain per run
|
||||||
|
(`parley-0.11.1/src/shape/mod.rs`'s `query.set_families`/
|
||||||
|
`query.set_fallbacks`), so a codepoint the resolved family lacks still
|
||||||
|
gets a query against fontique's fallback map before giving up. Checked
|
||||||
|
with a throwaway example (`iris/examples/font_check.rs`, deleted after
|
||||||
|
use -- not part of the crate) shaping `"🎉🔥▸▾▲你好"` headless on the
|
||||||
|
desktop at `run-headless.sh`: the emoji and CJK characters drew as
|
||||||
|
visible **tofu boxes** (the platform's own missing-glyph box, not blank
|
||||||
|
space), and the chevron marks (U+25B8/25BE/25B4, the ones
|
||||||
|
`transcript-ui/src/tool.rs`'s `CLOSED_MARK`/`OPEN_MARK`/`UP_MARK` draw)
|
||||||
|
shaped as real triangles. So the failure mode this crate now depends on
|
||||||
|
is "the platform's own tofu," which is the correct unknown-glyph state
|
||||||
|
per UI_RULES, not "nothing drawn." `tool.rs`'s doc comment on those marks
|
||||||
|
is updated to say this is now a bet on the platform's coverage rather
|
||||||
|
than a checked fact about a bundled `cmap`.
|
||||||
|
|
||||||
|
**One real gap, found on this checkout's emulator, not the desktop**:
|
||||||
|
`fonts: 208 families found, default=Some("Roboto Flex") mono=None` in the
|
||||||
|
startup log (`FontDiagnostics`, read via `adb logcat` after installing the
|
||||||
|
`force-gles` debug build -- the emulator's default Vulkan backend has no
|
||||||
|
adapter here, a pre-existing, documented condition unrelated to this
|
||||||
|
change, and aborts with `Could not get adapter!` without that feature).
|
||||||
|
`mono=None` means fontique's Android backend never resolves the
|
||||||
|
`Monospace` generic family at all on this system image: reading
|
||||||
|
`fontique-0.11.1/src/backend/android.rs`, `DEFAULT_GENERIC_FAMILIES`'s
|
||||||
|
`["monospace"]` is looked up against `name_map` *before* the `fonts.xml`
|
||||||
|
parse that would register a family literally named `"monospace"` runs --
|
||||||
|
so even though this AVD's `/system/etc/fonts.xml` does declare
|
||||||
|
`<family name="monospace"><font ...>DroidSansMono.ttf</font></family>`,
|
||||||
|
fontique's own ordering means that declaration is registered too late to
|
||||||
|
be found by the generic-family lookup, on every Android device this
|
||||||
|
fontique version runs on, not just this AVD. The visible effect is not
|
||||||
|
blank text -- `Family::Monospace`'s explicit-family list comes up empty,
|
||||||
|
but the script-based fallback chain (independent of the generic-family
|
||||||
|
list) still resolves a real font, the same one `SansSerif` gets -- so
|
||||||
|
code blocks and the tool-card chevrons render, just without a genuinely
|
||||||
|
monospaced face. Compose does not have this gap: `FontFamily.Monospace`
|
||||||
|
resolves through Android's own `Typeface.MONOSPACE` constant, a different
|
||||||
|
and unconditionally-populated path that fontique does not use. Left as a
|
||||||
|
follow-up rather than fixed here, since a fix means either patching
|
||||||
|
around fontique's Android backend or pinning `Family::Named("Droid Sans
|
||||||
|
Mono")` (fragile: an OEM-specific font name, not guaranteed across real
|
||||||
|
devices) -- out of the scope Iris gave this pass ("remove the font,
|
||||||
|
match Compose"), and a real product-visible difference worth her knowing
|
||||||
|
about rather than silently living with.
|
||||||
|
|
||||||
|
**Verified**: `cargo test -p transcript-fixture` (6 tests, all headless
|
||||||
|
layers) and `cargo clippy -p iris-core --all-targets` both clean;
|
||||||
|
`./run-headless.sh phone --phone --shot` (the bench-fixture transcript,
|
||||||
|
bold/italic/monospace code fences all shaping correctly) and
|
||||||
|
`./run-headless.sh tabs` (the desktop example with several distinct text
|
||||||
|
styles, per this task's "negative case" check) both look right by eye;
|
||||||
|
the emulator's own bench run (`run-bench.sh`, `force-gles` debug build)
|
||||||
|
completed a full fling/stream/type/keyboard cycle with text visibly
|
||||||
|
drawn throughout and no crash. `.so` size: see the APK size table's final
|
||||||
|
row, **11,193,608 -> 7,445,472 bytes**, a 3,748,136-byte drop matching
|
||||||
|
the 3.6 MB estimate almost exactly.
|
||||||
|
|
||||||
**naga/wgpu backend features: investigated, not trimmed, because the
|
**naga/wgpu backend features: investigated, not trimmed, because the
|
||||||
trim would not change the binary.** `iris/core/Cargo.toml` and
|
trim would not change the binary.** `iris/core/Cargo.toml` and
|
||||||
@@ -279,14 +372,22 @@ closes it.
|
|||||||
recent log; a debug button copies it; Dev Updater reads it), write
|
recent log; a debug button copies it; Dev Updater reads it), write
|
||||||
the decision in docs/DECISIONS.md, build it.
|
the decision in docs/DECISIONS.md, build it.
|
||||||
- [x] APK size: release profile tuned (`42af780`), -35% APK, -40% .so;
|
- [x] APK size: release profile tuned (`42af780`), -35% APK, -40% .so;
|
||||||
see "APK size (2026-09-07)". **Open question for Iris**: 3.6 MB of the
|
see "APK size (2026-09-07)". **Iris's verdict, 2026-09-07: "remove the
|
||||||
remaining 11.2 MB .so is six embedded Noto Sans TTFs
|
font for now; just match what compose does."** Done same day -- the six
|
||||||
(`iris/core/src/primitive/text.rs`). Loading the platform's own fonts
|
bundled Noto Sans TTFs are gone, text now loads from
|
||||||
instead (fontique's system collection: Roboto/Noto on Android, the
|
`fontique`'s platform collection (`FontContext::new()`'s default
|
||||||
desktop's own on Linux) removes them and makes text match what the
|
`CollectionOptions::system_fonts`), and `.so` dropped by 3,748,136 bytes
|
||||||
rest of her phone shows (UI_RULES "show what the reader already sees
|
(11,193,608 -> 7,445,472), matching the 3.6 MB estimate almost exactly.
|
||||||
elsewhere"), at the cost of the app no longer looking identical on
|
See "APK size" table's final row and "Platform fonts (2026-09-07)"
|
||||||
every device. Not done without her verdict.
|
below for the fallback behaviour and one real gap it surfaced: this
|
||||||
|
fontique version's Android backend never resolves the `Monospace`
|
||||||
|
generic family at all (`mono=None` in the startup diagnostic, measured
|
||||||
|
on this checkout's emulator) -- code/tool-card text still renders (the
|
||||||
|
script fallback chain still lands on a real face, never blank), just
|
||||||
|
not in a genuinely monospaced one. Compose does not have this gap; it
|
||||||
|
resolves `FontFamily.Monospace` through Android's own Typeface
|
||||||
|
constant rather than through fontique. Flagged as a follow-up, not
|
||||||
|
fixed here -- out of the scope Iris gave.
|
||||||
- [ ] Scroll clamped at both ends, and Compose's impulse velocity
|
- [ ] Scroll clamped at both ends, and Compose's impulse velocity
|
||||||
estimator with min/max fling velocity (docs/IRIS_TODO.md, 2026-09-07
|
estimator with min/max fling velocity (docs/IRIS_TODO.md, 2026-09-07
|
||||||
later). After the culling fix lands (same file).
|
later). After the culling fix lands (same file).
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
@@ -2,34 +2,14 @@ use crate::{Align, GlyphAtlas, GlyphKey, PlacedGlyph, RegionAlign, Textures, UiC
|
|||||||
use parley::{
|
use parley::{
|
||||||
Alignment, AlignmentOptions, FontContext, FontFamily, FontFamilyName, FontStyle, FontWeight,
|
Alignment, AlignmentOptions, FontContext, FontFamily, FontFamilyName, FontStyle, FontWeight,
|
||||||
GenericFamily, Layout, LayoutContext, LineHeight, PositionedLayoutItem, StyleProperty,
|
GenericFamily, Layout, LayoutContext, LineHeight, PositionedLayoutItem, StyleProperty,
|
||||||
fontique::{Blob, FamilyId},
|
|
||||||
};
|
};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
|
||||||
use swash::{
|
use swash::{
|
||||||
FontRef,
|
FontRef,
|
||||||
scale::{Render, ScaleContext, Source, StrikeWith},
|
scale::{Render, ScaleContext, Source, StrikeWith},
|
||||||
zeno::{Format, Vector},
|
zeno::{Format, Vector},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Bundled fonts, registered over the system collection rather than relied
|
|
||||||
/// on alone -- see `TextData::register_bundled_fonts`'s doc comment for
|
|
||||||
/// why. Static weight/style cuts, not a variable font: parley/fontique
|
|
||||||
/// resolve a variable font's weight axis by picking normalized coordinates
|
|
||||||
/// on whatever single face registers for the family, and a phone whose
|
|
||||||
/// system "Roboto" is actually the variable "Roboto Flex" is exactly the
|
|
||||||
/// device class this sidesteps, rather than depends on working correctly.
|
|
||||||
/// Noto Sans, OFL-licensed (`assets/fonts/OFL.txt`), chosen for coverage
|
|
||||||
/// breadth (a transcript's content is not known in advance) over a
|
|
||||||
/// smaller-footprint alternative -- see the doc comment for the size this
|
|
||||||
/// added.
|
|
||||||
const NOTO_SANS_REGULAR: &[u8] = include_bytes!("../../assets/fonts/NotoSans-Regular.ttf");
|
|
||||||
const NOTO_SANS_BOLD: &[u8] = include_bytes!("../../assets/fonts/NotoSans-Bold.ttf");
|
|
||||||
const NOTO_SANS_ITALIC: &[u8] = include_bytes!("../../assets/fonts/NotoSans-Italic.ttf");
|
|
||||||
const NOTO_SANS_BOLD_ITALIC: &[u8] = include_bytes!("../../assets/fonts/NotoSans-BoldItalic.ttf");
|
|
||||||
const NOTO_SANS_MONO_REGULAR: &[u8] = include_bytes!("../../assets/fonts/NotoSansMono-Regular.ttf");
|
|
||||||
const NOTO_SANS_MONO_BOLD: &[u8] = include_bytes!("../../assets/fonts/NotoSansMono-Bold.ttf");
|
|
||||||
|
|
||||||
/// What starting up found about text rendering, for the on-screen
|
/// What starting up found about text rendering, for the on-screen
|
||||||
/// Diagnostics page and the one startup log line (RUST.md's P0 box, "log
|
/// Diagnostics page and the one startup log line (RUST.md's P0 box, "log
|
||||||
/// once at startup ... the number of font families found, the default
|
/// once at startup ... the number of font families found, the default
|
||||||
@@ -80,91 +60,31 @@ pub struct TextData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextData {
|
impl Default for TextData {
|
||||||
|
/// Text comes entirely from the platform's own font collection --
|
||||||
|
/// `FontContext::new()` builds a `fontique::Collection` with
|
||||||
|
/// `CollectionOptions::system_fonts` on by default, which is real
|
||||||
|
/// discovery on both targets this crate ships on: Android's backend
|
||||||
|
/// parses `/system/fonts` and `/system/etc/fonts.xml` and maps
|
||||||
|
/// `SansSerif`/`SystemUi` to `["Roboto Flex", "Roboto", "Noto Sans"]`
|
||||||
|
/// and `Monospace` to the platform's `"monospace"` alias; the desktop
|
||||||
|
/// build's backend is fontconfig. No font is bundled or registered
|
||||||
|
/// here -- see DECISIONS.md's 2026-09-07 entry for why (matching what
|
||||||
|
/// the Compose app does: it takes body/monospace text from
|
||||||
|
/// `FontFamily.Default`/`FontFamily.Monospace`, i.e. Android's Roboto
|
||||||
|
/// and its platform monospace face, and ships no text font of its own,
|
||||||
|
/// only its committed Nerd Fonts icon subset for fixed glyphs).
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut data = Self {
|
Self {
|
||||||
font_cx: FontContext::new(),
|
font_cx: FontContext::new(),
|
||||||
layout_cx: LayoutContext::new(),
|
layout_cx: LayoutContext::new(),
|
||||||
scale_cx: ScaleContext::new(),
|
scale_cx: ScaleContext::new(),
|
||||||
atlas: GlyphAtlas::default(),
|
atlas: GlyphAtlas::default(),
|
||||||
density: 1.0,
|
density: 1.0,
|
||||||
};
|
}
|
||||||
data.register_bundled_fonts();
|
|
||||||
data
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextData {
|
impl TextData {
|
||||||
/// Registers Noto Sans (regular/bold/italic/bold-italic) and Noto Sans
|
|
||||||
/// Mono (regular/bold) as static faces, and puts them **first** in the
|
|
||||||
/// `SansSerif`/`Monospace` generic-family fallback lists -- ahead of,
|
|
||||||
/// not instead of, whatever the platform already found, so a script
|
|
||||||
/// Noto Sans lacks (CJK, emoji, ...) still falls through to the system
|
|
||||||
/// font the same as before this existed.
|
|
||||||
///
|
|
||||||
/// Exists because text rendering must not depend on the platform's own
|
|
||||||
/// font enumeration succeeding or resolving weight/style the way this
|
|
||||||
/// crate assumes: RUST.md's P0 box found bold spans on a real phone
|
|
||||||
/// rendering as blank gaps of the correct advance width (the glyph
|
|
||||||
/// simply wasn't rasterised -- `TextData::place`'s `None` arm), while
|
|
||||||
/// the emulator's system fonts happened to resolve every style. A
|
|
||||||
/// bundled, static-per-style family removes fontique's Android font
|
|
||||||
/// scan (`fontique::backend::android::SystemFonts::new`, which parses
|
|
||||||
/// `/system/fonts` and `/system/etc/fonts.xml`) from the path a glyph
|
|
||||||
/// has to survive to reach the screen at all.
|
|
||||||
///
|
|
||||||
/// Cost: six static `.ttf`s, ~3.6 MB uncompressed
|
|
||||||
/// (`iris/core/assets/fonts/`), landing in the APK compressed --
|
|
||||||
/// `build-apk.sh`'s own output is what says the delivered number, not
|
|
||||||
/// this comment.
|
|
||||||
fn register_bundled_fonts(&mut self) {
|
|
||||||
fn register(cx: &mut FontContext, bytes: &'static [u8]) -> Option<FamilyId> {
|
|
||||||
let blob = Blob::new(Arc::new(bytes));
|
|
||||||
cx.collection
|
|
||||||
.register_fonts(blob, None)
|
|
||||||
.into_iter()
|
|
||||||
.map(|(id, _)| id)
|
|
||||||
.next()
|
|
||||||
}
|
|
||||||
let sans_id = register(&mut self.font_cx, NOTO_SANS_REGULAR);
|
|
||||||
register(&mut self.font_cx, NOTO_SANS_BOLD);
|
|
||||||
register(&mut self.font_cx, NOTO_SANS_ITALIC);
|
|
||||||
register(&mut self.font_cx, NOTO_SANS_BOLD_ITALIC);
|
|
||||||
let mono_id = register(&mut self.font_cx, NOTO_SANS_MONO_REGULAR);
|
|
||||||
register(&mut self.font_cx, NOTO_SANS_MONO_BOLD);
|
|
||||||
|
|
||||||
if let Some(sans_id) = sans_id {
|
|
||||||
let existing: Vec<_> = self
|
|
||||||
.font_cx
|
|
||||||
.collection
|
|
||||||
.generic_families(GenericFamily::SansSerif)
|
|
||||||
.collect();
|
|
||||||
self.font_cx.collection.set_generic_families(
|
|
||||||
GenericFamily::SansSerif,
|
|
||||||
std::iter::once(sans_id).chain(existing),
|
|
||||||
);
|
|
||||||
let existing: Vec<_> = self
|
|
||||||
.font_cx
|
|
||||||
.collection
|
|
||||||
.generic_families(GenericFamily::SystemUi)
|
|
||||||
.collect();
|
|
||||||
self.font_cx.collection.set_generic_families(
|
|
||||||
GenericFamily::SystemUi,
|
|
||||||
std::iter::once(sans_id).chain(existing),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(mono_id) = mono_id {
|
|
||||||
let existing: Vec<_> = self
|
|
||||||
.font_cx
|
|
||||||
.collection
|
|
||||||
.generic_families(GenericFamily::Monospace)
|
|
||||||
.collect();
|
|
||||||
self.font_cx.collection.set_generic_families(
|
|
||||||
GenericFamily::Monospace,
|
|
||||||
std::iter::once(mono_id).chain(existing),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds the startup report -- see `FontDiagnostics`. Queries the
|
/// Builds the startup report -- see `FontDiagnostics`. Queries the
|
||||||
/// collection directly (`fontique::Query`) rather than shaping a real
|
/// collection directly (`fontique::Query`) rather than shaping a real
|
||||||
/// string, since all that's needed is which family each axis lands on.
|
/// string, since all that's needed is which family each axis lands on.
|
||||||
|
|||||||
@@ -110,13 +110,19 @@ const _: () = assert!(OUTPUT_LINES > 0 && OUTPUT_BYTES > 0);
|
|||||||
/// The mark that says a card opens, always drawn from the **monospace**
|
/// The mark that says a card opens, always drawn from the **monospace**
|
||||||
/// face.
|
/// face.
|
||||||
///
|
///
|
||||||
/// Not a style choice: `NotoSans-Regular.ttf`, which every other string
|
/// Not a style choice, historically: with the bundled Noto Sans this crate
|
||||||
/// here is set in, has no glyph at U+25B8/U+25BE/U+25B4 at all, while
|
/// used to embed, the sans face every other string here is set in had no
|
||||||
/// `NotoSansMono-Regular.ttf` does -- read out of both bundled `cmap`s on
|
/// glyph at U+25B8/U+25BE/U+25B4 at all, while the bundled monospace face
|
||||||
/// 2026-09-06. A missing glyph is the failure nobody who wrote the code
|
/// did. As of 2026-09-07 iris takes both faces from the platform's own
|
||||||
/// ever sees, so the face that has the glyph is named at the one place the
|
/// collection instead (DECISIONS.md's 2026-09-07 entry, matching what
|
||||||
/// character is written. IRIS_TODO's "a drawn chevron" has the real fix,
|
/// Compose ships), so this is no longer a checked fact about a specific
|
||||||
/// which needs a line primitive iris does not have.
|
/// font's `cmap` -- it is a bet that whatever the platform resolves for
|
||||||
|
/// `Family::Monospace` covers these three codepoints, same as it was
|
||||||
|
/// before. If a platform's monospace face turns out not to, the fallback
|
||||||
|
/// chain still applies (see `TextData::default`'s doc comment) and the
|
||||||
|
/// glyph should still land, just not necessarily monospaced. IRIS_TODO's
|
||||||
|
/// "a drawn chevron" has the real fix, which needs a line primitive iris
|
||||||
|
/// does not have.
|
||||||
const CLOSED_MARK: &str = "\u{25b8}";
|
const CLOSED_MARK: &str = "\u{25b8}";
|
||||||
const OPEN_MARK: &str = "\u{25be}";
|
const OPEN_MARK: &str = "\u{25be}";
|
||||||
const UP_MARK: &str = "\u{25b4}";
|
const UP_MARK: &str = "\u{25b4}";
|
||||||
|
|||||||
Reference in new issue
Block a user