# How Archify's Renderer Handles CJK-Aware Text Measurement

> Learn how Archify's renderer efficiently handles CJK-aware text measurement by detecting full-width characters and emoji with Unicode-aware regex for accurate layout dimensions.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: internals
- Published: 2026-07-14

---

**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

```js
// 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:

```js
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`](https://github.com/tt-a1i/archify/blob/main/archify/renderers/workflow/README.md), [`archify/renderers/sequence/README.md`](https://github.com/tt-a1i/archify/blob/main/archify/renderers/sequence/README.md), [`archify/renderers/lifecycle/README.md`](https://github.com/tt-a1i/archify/blob/main/archify/renderers/lifecycle/README.md), and [`archify/renderers/dataflow/README.md`](https://github.com/tt-a1i/archify/blob/main/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:

```js
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:

```js
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:

```js
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:

```js
// 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`) in `archify/renderers/shared/utils.mjs` to identify full-width characters across Hangul, CJK, and supplementary planes.
- The **`textUnits`** function 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.