How Archify's Renderer Handles CJK-Aware Text Measurement
Archify's diagram renderers detect full-width CJK characters and emoji using a Unicode-aware regular expression in archify/renderers/shared/utils.mjs, counting each full-width glyph as two units and ASCII characters as one unit to calculate accurate layout dimensions.
Archify is an open-source diagramming tool that renders architectural diagrams from code definitions. When generating visualizations containing East Asian scripts, the renderer must accurately estimate text width to prevent label overflow and node overlap. The project implements CJK-aware text measurement through a specialized utility module that distinguishes between half-width ASCII and full-width Unicode blocks.
The Core Measurement Algorithm
The text measurement logic resides in archify/renderers/shared/utils.mjs, where a single regular expression and a counting function handle all width calculations across the diagram renderers.
Detecting Full-Width Unicode Ranges
The module defines FULLWIDTH_RE, a regular expression that matches characters across multiple Unicode planes that render as full-width in monospace fonts:
- Hangul syllables and jamo
- CJK Unified Ideographs and Compatibility Ideographs
- Full-width punctuation and symbols
- Supplementary-plane CJK extensions (including Extension B)
- Emoji ranges
// archify/renderers/shared/utils.mjs
const FULLWIDTH_RE = /[ᄀ-ᅟ⺀-가-힣豈-︰-﹏-⦆¢-₩ -〿\u{1F000}-\u{1FAFF}\u{20000}-\u{3FFFD}]/u;
The textUnits Function
The textUnits function iterates over each character in a string, testing against FULLWIDTH_RE and accumulating width units:
export function textUnits(text) {
let units = 0;
for (const ch of String(text ?? '')) units += FULLWIDTH_RE.test(ch) ? 2 : 1;
return units;
}
This approach guarantees that a single CJK character occupies roughly the same horizontal space as two ASCII characters, preventing layout distortion in diagrams mixing Latin and East Asian scripts.
Integration in Diagram Renderers
The textUnits utility propagates through all Archify renderers. According to the documentation in archify/renderers/workflow/README.md, archify/renderers/sequence/README.md, archify/renderers/lifecycle/README.md, and archify/renderers/dataflow/README.md, each renderer notes that "Text width is estimated CJK-aware: fullwidth glyphs count as two units."
Layout calculations multiply these units by a constant character width (typically around 8 pixels) to determine SVG element dimensions:
import { textUnits } from './archify/renderers/shared/utils.mjs';
function layoutNode(node) {
const labelWidth = textUnits(node.label) * CHAR_UNIT_WIDTH; // CHAR_UNIT_WIDTH ≈ 8 px
// Use `labelWidth` to set the SVG <rect> width, enforce minimum width, etc.
}
Code Examples and Usage
Basic Text Measurement
Import textUnits to calculate visual width for labels containing CJK characters:
import { textUnits } from './archify/renderers/shared/utils.mjs';
const label = '用户登录'; // 4 CJK characters → 8 units
console.log(textUnits(label)); // 8
const mixed = 'Login 登录'; // 5 ASCII + 2 CJK → 5 + 4 = 9 units
console.log(textUnits(mixed)); // 9
Layout Calculations in Renderers
Renderers combine textUnits with dimensional constants to size nodes:
import { textUnits } from './archify/renderers/shared/utils.mjs';
import { renderNode } from './archify/renderers/workflow/render-node.mjs';
function layoutNode(node) {
const labelWidth = textUnits(node.label) * CHAR_UNIT_WIDTH; // CHAR_UNIT_WIDTH ≈ 8 px
// …use `labelWidth` to set the SVG <rect> width, enforce minimum width, etc.
}
Unit Test Verification
The test suite in archify/test/geometry.test.mjs validates the double-width logic across character types:
// archify/test/geometry.test.mjs
test('textUnits: ASCII=1, CJK=2, mixed sums, fullwidth supplementary=2', () => {
assert.equal(textUnits('ABC'), 3); // ASCII
assert.equal(textUnits('汉字'), 4); // CJK (2 × 2)
assert.equal(textUnits('A汉'), 3); // Mixed
assert.equal(textUnits('𠀀'), 2); // CJK Extension B (supplementary plane)
});
Summary
- Archify's renderer uses a Unicode-aware regex (
FULLWIDTH_RE) inarchify/renderers/shared/utils.mjsto identify full-width characters across Hangul, CJK, and supplementary planes. - The
textUnitsfunction assigns 2 units to full-width characters and 1 unit to ASCII, providing a consistent measurement system for mixed-script diagrams. - All diagram renderers (workflow, sequence, lifecycle, and data-flow) rely on this utility to calculate label widths and prevent text overflow.
- The approach ensures that CJK characters occupy appropriate horizontal space relative to ASCII text in SVG output.
Frequently Asked Questions
What Unicode ranges does Archify consider full-width?
Archify's FULLWIDTH_RE regex covers Hangul (Jamo and syllables), CJK Unified Ideographs, CJK Compatibility Ideographs, full-width punctuation and symbols, supplementary-plane CJK extensions (including Extension B), and emoji ranges from \u{1F000} to \u{1FAFF}.
How does the textUnits function handle null or undefined input?
The textUnits function coalesces null or undefined values to an empty string using String(text ?? ''), ensuring it always returns 0 for nullish inputs rather than throwing an error.
Why does Archify use a unit-based system instead of pixel measurements?
Archify uses abstract units (1 for ASCII, 2 for CJK) rather than direct pixel measurements because the actual rendered width depends on the specific monospace font stack and rendering context. The unit system provides a font-agnostic approximation that scales consistently across different environments when multiplied by a character width constant.
Which renderers benefit from CJK-aware text measurement?
All Archify diagram renderers benefit from this logic, including the workflow, sequence, lifecycle, and data-flow renderers. Each renderer's documentation explicitly references CJK-aware width estimation to ensure proper node sizing and label placement in diagrams containing East Asian text.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →