M PLUS 1 Code Term (font resource for extensions)Extension ID: A general-purpose font resource extension that supplies the M PLUS 1 Code Term
font to other extensions. It has no UI and no commands of its own
( M PLUS 1 Code Term is a modified version of M PLUS 1 Code, refitted for
terminal use: the symbols a terminal counts as one column wide are brought under
the width at which a renderer squashes them sideways. Ink wider than 1.15
cells is scaled down to exactly that width, and
any ink that reaches past its cell is centred on the cell, so the overhang is the
same on both sides and never exceeds 38 units of a 500-unit cell (measured; the
ratio puts it at 37.5 and integer rounding accounts for the rest — about 7.6%).
A symbol may therefore touch its neighbour slightly; what it never does is
reach the squashing threshold. Advance widths are not part of that: the cell grid
does not move, and this file's The two axes are scaled by different factors (since #293): the refit takes
the height to Bundled font
Two static faces, where this package used to ship one variable file. That is
not a packaging preference: editing a variable font's outlines corrupts its
See Scope limitation: it does not work in
|
| conversion input | sha256 |
|---|---|
MPLUS1Code.ttf (the pre-#275 bundled file) |
ff68678c5bd7e9d9d6ab6d57e4355aabe30f6b8f8bff9bd59baf6a7807dcfd36 |
Upstream project: M PLUS FONTS https://github.com/coz-m/MPLUS_FONTS. The
license text bundled at fonts/OFL.txt names the copyright holder as
Copyright 2021 The M+ FONTS Project Authors (line 1), and LICENSE.txt at the
package root is byte-identical to it.
2. Conversion (what turned input into output)
Pipeline:
scripts/font-fit/run.shin this repository, driven byscripts/font-fit/manifest.json(this package is one entry in it; membership of that file is what makes a package a converted one).instance.pypins the variable axis,fit.pydoes the refit,verify.pygates the result, anddump-widths.mjsproduces the cell-width table from@xterm/addon-unicode11's own provider — a hand-written width table is forbidden. The order is fixed: instance first, refit second. Refitting a variable font's outlines would corrupt itsgvardeltas.Which converter, identified by content rather than by commit — introduced by issue #275, and pinned by the sha256 of each file that determines the output:
pipeline file sha256 scripts/font-fit/fit.pyc634881d418a59d68f1892db4d35f4fc66780a71cff277448cbef91479470bf7scripts/font-fit/instance.py86240ac88d1dfa4527ebaa1e7e97caf6c656e97cd04833d12b21149d2fefe2afscripts/font-fit/verify.py5c7d58d6a8ff4e4d248990c4bc39bcf379faee2fec9303ea3966c6ce1c5d9cf5scripts/font-fit/dump-widths.mjs4813198fba79edb3d3086aa07b8808183a362406b1a1da9c7d614c5b90633a03scripts/font-fit/run.sh4966ccca260ded4b1d7aa94fa71bc9802956363e9a646c788166ee530ab8c55ascripts/font-fit/manifest.json0fed27ae2614fd0cbe570cde156bb4ed5ff32fe7a0a7c814b97d71acaf49629bWhy not a commit SHA. The first draft of this section recorded one, and it was false within the same PR — and this package is where it broke visibly:
fit.pygained the style-bit fix below, this package's Bold output changed hash, and the recorded SHA still pointed at the converter from before the fix, so checking it out and re-running produced a different hash than the one recorded three lines below. That is not a slip that better care avoids: a git SHA is content-addressed, so a file cannot name the commit that contains it. Recording one means either splitting the TTFs, thechecksums.jsonrecord and this table across two commits — the one coupling R13 exists to prevent — or leaving a placeholder for someone to substitute, which ships silently when they forget. Hashing the scripts has neither problem: the values are knowable while writing, they survive rebases and rewrites, and they identify the converter directly instead of via a commit that also carries unrelated changes.run.shadditionally appends-dirtyto the commit it prints when the working tree differs fromHEAD, so a provenance line generated from uncommitted edits can no longer be mistaken for one generated from a clean checkout.Measured tool versions (printed by
run.shat the start of the run, not copied from documentation):tool version fontTools 4.63.0Python 3.14.4@xterm/addon-unicode110.10.0-beta.288Fixed environment:
PYTHONHASHSEED=0,LC_ALL=C.UTF-8, and fontTools saved withrecalcTimestamp=False(a recalculatedhead.modifiedwould make every run produce different bytes and the output hashes below meaningless).What the run changed, per face — measured, and reported by
run.sh. The source axis iswght: (100.0, 400.0, 700.0), pinned per output face:output face instanced at glyphs refitted of which moved only / scaled already fitting Regular wght=40046 46 / 0 295 Bold wght=70067 61 / 6 274 Two orders of magnitude fewer than the Mgen+ families, and that is the point of the design brief: this face's ambiguous-width symbols were mostly correct already, and only the overflowing ones were touched. At
fitRatio1.15 the Regular face is scaled not at all — measured: its widest in-scope ink is 555 units against a room of 575, so every refitted glyph there is a re-centring and nothing more.The
fitRatiothe run was driven by: 1.15 (manifest.json's top-level default; this package declares no override). Ink wider thanA × 1.15— 575 units of the 500-unit cell — is scaled down to it. The value's meaning and its 1.5 ceiling are defined inscripts/font-fit/manifest.json'snoteand not restated here.The
vstretchthe run was driven by: 1.3 (manifest.json's top-level default; this package declares no override), with the vertical limit measured from each face rather than declared — the top and bottom edges of 漢 (U+6F22),y = −77…824in the Regular face and−93…827in the Bold, asrun.shprinted them. The clamp bound the stretch on 46 of the 46 refitted glyphs in the Regular face and 67 of 67 in the Bold; of those, 27 and 42 respectively had no room left at equal scaling and were left completely unmoved (the clamp's floor is 1.0). So the glyphs that actually grew taller are 19 of 46 and 25 of 67 — the vertical change is not universal, and the counts are the honest way to say so. Of the refitted glyphs, 27 (Regular) / 42 (Bold) already reached outside the 漢 box at equal scaling, and none of either face reaches outside it that did not already (measured on the shipped bytes against avstretch = 1.0bake of the same instanced input). The horizontal side is untouched by this:hmtxis byte-identical to the pre-#293 release across all 6,747 glyphs (measured), and the widest in-scope ink is unchanged at 555 / 575.All three gates passed on both faces (
verify.py, run against the instanced file the converter actually read, at the declared ratio and stretch): contract ① — in-scope code points outside the declared band (ink ≤A × 1.15, centred when it overhangs, advance== A): 0, with the widest in-scope ink at Regular 555 → 555 (nothing to scale: already inside the room) and Bold 650 → 575 (the declared room exactly); contract ② — body glyphs whose outlines changed: 0 of 5,567; contract ③ — refitted glyphs whose top and bottom edges differ from what the declaredvstretchand the measured wall (this face's own 漢 y reach) imply: 0 of 46 (Regular) and 0 of 67 (Bold), checked as an equality in both directions, so bytes baked at a different stretch would fail whichever way they differed — and the half of that gate which recomputes no formula (③c) reports 0 glyphs newly outside the wall on both faces. In-scope is load-bearing rather than a hedge: the pipeline deliberately excludes ASCII / Latin-1, the BMP private use area, box drawing and block elements, and everything not width 1 (font-packages/README.md, "Terminal width conventions"). This package ships the measured instance of that: infonts/MPLUS1CodeTerm-Bold.ttf(A= 500)î(U+00EE) advances 500 with ink74 … 524andï(U+00EF)74 … 509— width 1, outside the cell, and correctly untouched.The family and style names in the
nametable are rewritten (M PLUS 1 Code→M PLUS 1 Code Term); nameID 0, the copyright field, is carried through byte-identically (re-measured on the output):Copyright 2021 The M+ FONTS Project Authors (https://github.com/coz-m/MPLUS_FONTS). The style bits move with the names:OS/2.fsSelectionbit 5 (BOLD) and bit 6 (REGULAR), andhead.macStylebit 0, are derived from the style rather than inherited. That matters only on this package, and only because it is the variable one:instantiateVariableFontupdatesusWeightClassbut not the bits, so the default (Regular) instance's bits would otherwise ride into the Bold face and the file would name itselfBoldwhile flagging itself Regular. Measured on the output: RegularfsSelection=0x00C0/macStyle=0x0000, BoldfsSelection=0x00A0/macStyle=0x0001— bit 7 (USE_TYPO_METRICS) is preserved in both, because the fix sets and clears individual bits rather than assigning the field.Reproducibility, as measured, not as promised: re-running the pipeline on the same machine, from the same input, with the tool versions above, produced byte-identical output — the sha256s below. Nothing is claimed about a different fontTools or Python: that was not tested, and neither was the effect of environment variables beyond the three fixed above.
3. Output (what is shipped)
Measured on the placed files, and recorded in scripts/font-fit/checksums.json
as the same values — from #276 npm test compares the two, so a TTF and its
record can only move together. Since #291 the record carries the fitRatio
beside each sha256 and since #293 the vstretch as well, and npm test compares
both against the manifest too.
| placed at | sha256 (placed) | fitRatio | vstretch |
|---|---|---|---|
fonts/MPLUS1CodeTerm-Regular.ttf |
63c80ed65c2a9eff8a80d54cf918aa2cbf68a06f6f23dc04d0a04ed401521ebf |
1.15 |
1.3 |
fonts/MPLUS1CodeTerm-Bold.ttf |
06f4fd340d71a39f44dc516f03968fd40608f4d300d9594269cc7ec7b4474f0f |
1.15 |
1.3 |
Reserved Font Name check (measured)
OFL 1.1 §3 forbids a Modified Version from carrying a Reserved Font Name, so a converted package must establish which names are reserved before choosing its own. Measured over every license text this package ships:
Superseded (#288). This
grepis no longer the procedure. Runscripts/font-fit/check-license.pyinstead — the steps are infont-packages/README.md("Reserved Font Name check"). The command below cannot reach thenametable, where a package can declare a reserved name that appears in no file at all (measured on Source Han Code JP, #285); it is also blind to license documents that are not UTF-8. The table below is kept as the record this grep produced, not as instructions: it predates thename-table stage, so anyfonts/*.ttf#0:nameID Nrow the tool reports is missing from it. Adding those rows is #289's work.
grep -rniI 'reserved font name' \
font-packages/font-mplus1code/LICENSE.txt \
font-packages/font-mplus1code/fonts/
The flags and the path list are load-bearing, and this package is the one
where it matters most, because "no hits" is the answer it relies on — a
search must not be able to produce that answer by accident. -i keeps a line
that shouts the phrase from being skipped; naming fonts/ rather than filtering
--include='*.txt' keeps an extensionless license document from being read past
(this package has none, but its sibling packages do, and the command should not
differ between them for no reason); -I keeps the TTFs from being reported as
matching binaries. The table lists every line the search returns:
| file:line | declares |
|---|---|
LICENSE.txt:33 |
no name — the OFL definition clause itself |
fonts/OFL.txt:33 |
no name — the same clause (the two files are byte-identical) |
The name-table stage adds no row here (#289, measured with
scripts/font-fit/check-license.py). The superseded note above says any
fonts/*.ttf#0:nameID N row the tool reports is missing from this table; the
tool reports none — the two shipped faces' name table declares no reserved
name either, so the answer this package relies on is now backed by the stage a
grep cannot reach as well as by the two lines above. Because the total is zero,
the tool's OK is vacuously true and it says so, exiting 4 rather than 0
and printing the verdict as
'M PLUS 1 Code Term': どの予約名も含まない → OK **(照合した予約名 0 件——この OK は空虚に真)**.
That distinction is the point of the exit code: a pass measured against nothing
must not look like a pass measured against four names. It is legitimate here, and
--allow-zero-reserved is how one says so explicitly.
No Reserved Font Name is declared anywhere in this package. The copyright
line (LICENSE.txt:1) carries no with Reserved Font Name … clause, which is
the position §3 attaches one to. M PLUS 1 Code Term is therefore unconstrained
by §3 — the check found nothing to avoid, which is a result, not a reason to
skip the check next time. (For contrast, font-plemoljp-console declares
"PlemolJP" as reserved — which is one reason that package is not converted at
all.)
family name (measured, not assumed)
Measured on the converted files — the ones that ship. Read from the name table
directly:
| field | MPLUS1CodeTerm-Regular.ttf |
MPLUS1CodeTerm-Bold.ttf |
|---|---|---|
| nameID 1 (family) | M PLUS 1 Code Term |
M PLUS 1 Code Term |
| nameID 2 (subfamily) | Regular |
Bold |
| nameID 4 (full name) | M PLUS 1 Code Term Regular |
M PLUS 1 Code Term Bold |
| nameID 6 (PostScript) | MPLUS1CodeTerm-Regular |
MPLUS1CodeTerm-Bold |
OS/2.usWeightClass |
400 |
700 |
fvar |
absent (static) | absent (static) |
Neither face carries a nameID 16 / 17 pair; nameID 1 is the family, and it is
identical across the two, which is what makes pairing them under a single CSS
font-family legitimate. The injection does not depend on it — @font-face
declares a family name rather than reading one, and CSS font matching never
consults a file's internal names; font-packages/README.md states that
mechanism in full. This table is evidence about the fonts, not about the
mechanism.
M PLUS 1 Code Term is the value this extension's providedFont.family carries,
and it must stay identical to the consumer-side registry entry (tmux-opener's
FONT_REGISTRY).
Note for consumers: the pre-conversion name M PLUS 1 Code is not what these
files declare any more. tmux-opener keeps it working by registering it as an
alias and injecting an alias-named @font-face over the same files, so an
existing settings.json renders with the converted bytes; that is a
consumer-side compatibility layer, not a property of this package.
Monospace metrics (measured)
npm test in the repository root (test/font-metrics.test.js) covers each TTF
in this package — it globs them, so the bold face is asserted on every run too:
- unitsPerEm 1000; every ASCII printable codepoint 0x21–0x7E is present in cmap and shares one advance A = 500.
- The fullwidth samples 一 / 漢 / あ / ん / ア each advance 1000 = 2A.
- The two faces agree with each other: same unitsPerEm, same A. That agreement became checkable only with #275 — while this package shipped a single file there was nothing for it to disagree with, and the test armed itself the moment a second TTF appeared, with no edit.
These values are hmtx advances, and they are unchanged by the conversion. So,
as it happens, is every advance in this package — but that is a fact about
this typeface, not a property of the tool, and the difference matters:
- The refit sets a selected glyph's advance to the cell width. Where upstream drew a width-1 code point at the full-width advance, the conversion changes it. In the Mgen+ families that applies to ~1000 glyphs per face (1024 → 512).
- Here it selected 46 and 67 glyphs and changed no advance at all, measured
by diffing the shipped files against the instanced inputs the converter read:
0 advance changes in either face. M PLUS 1 Code already gave those code
points the correct advance; what overflowed was the ink (stacked accents like
ẤỐreaching past the box), so moving and scaling the outline was the whole of the work. - One advance does differ from the original variable font, and it is not the
refit's doing:
uni20A9.BRACKET.varAlt02goes 1015 → 1044 when the font is instanced atwght=700(measured at the instancing step, before any refit; atwght=400nothing changes). That glyph is not reachable fromcmap. - A body glyph is untouched in both outline and advance — contract ② in "Provenance", verified glyph by glyph — and the cell grid does not move: A stays 500 and full-width glyphs stay 1000.
The earlier version of this paragraph said the conversion "never touches an advance width". For this package the shipped result is the same either way, which is exactly why the wrong reason survived review: it was stated as a property of the tool, and as such it is false — the Mgen+ packages prove it. Contract ② is about the glyphs the refit did not select; a selected glyph may change in both outline and advance, by design.
About this convention
To repeat: VS Code has no official mechanism for sharing fonts between
extensions. The borrowing steps are an unofficial, homegrown convention agreed
between the supplier (this extension) and the consumer (tmux-opener and the
like); the providedFont contract field is the source of truth for that
agreement, and font-packages/README.md is the source of truth for the
convention's description. If you change the shape of the contract field or the
location of the font file, update both sides together, keeping in mind that
consumers will break silently.