npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hb61/thakar-gujarati-font-converter

v2.2.1

Published

Convert Gujarati text between Unicode and legacy typing-font encodings (KAP113 and more), by font name. Works in Node.js and in browser/React bundlers.

Readme

@hb61/thakar-gujarati-font-converter

Convert Gujarati text between Unicode and legacy Gujarati typing-font encodings (KAP113 today, more fonts to follow). Pick the font by name and pass your string through — the package handles the rest. Works in Node.js and in browser bundlers (React, Vite, Next.js, CRA, webpack, ...) with no extra config.

Install

npm install @hb61/thakar-gujarati-font-converter

Usage

toFont and toUnicode always return a plain JavaScript string — never a Promise, a Buffer, or a mutated argument — so you can assign it straight to a variable, render it, or store it.

Node.js (CommonJS)

const { toFont, toUnicode, listFonts } = require('@hb61/thakar-gujarati-font-converter');

listFonts();
// -> [{ name: 'kap113', label: 'KAP113', mapped: true, fontFile: 'kap113_0.ttf' }, ...]

const kapText = toFont('kap113', 'હાર્દિક');   // Unicode -> font key sequence (string)
const unicodeText = toUnicode('kap113', 'CFlN"S'); // font key sequence -> Unicode (string)

Node.js (ESM) / React / Vite / Next.js / webpack

import { toFont, toUnicode, listFonts } from '@hb61/thakar-gujarati-font-converter';

function LabelInKap113({ text }) {
  const kapText = toFont('kap113', text); // plain string, safe to render directly
  return <span style={{ fontFamily: 'KAP113' }}>{kapText}</span>;
}

Both entry points (require and import) resolve to the same conversion logic — pick whichever matches your project's module system. The core library has no Node built-in dependencies (no fs, no path), so it bundles cleanly for the browser.

Font names are case-insensitive ('kap113', 'KAP113' resolve the same). Passing an unregistered font name throws immediately with the list of available fonts, rather than silently returning garbage.

Using a KAP font as a CSS font-family in the browser

Every font's .ttf is published as part of the package and can be imported directly through your bundler via a subpath import, then wired up with a normal @font-face rule. Use the font's label (from listFonts()) as the font-family name so it stays consistent with the package's own naming.

// React / Vite / webpack / CRA — bundler resolves the .ttf to a URL
import kap113Ttf from '@hb61/thakar-gujarati-font-converter/fonts/kap113_0.ttf';
/* app.css */
@font-face {
  font-family: 'KAP113';
  src: url('@hb61/thakar-gujarati-font-converter/fonts/kap113_0.ttf') format('truetype');
}

.kap113-text {
  font-family: 'KAP113', sans-serif;
}
import { toFont } from '@hb61/thakar-gujarati-font-converter';
import './app.css';

function Card() {
  const kapText = toFont('kap113', 'હાર્દિક'); // string, ready to render
  return <p className="kap113-text">{kapText}</p>;
}

Any registered font's file (mapped or not) is reachable the same way, e.g. @hb61/thakar-gujarati-font-converter/fonts/kap050_0.ttf — check listFonts() for each font's exact fontFile name.

Node/server-side (no bundler)

For server-rendered @font-faces or copying the file at build time, use the Node-only helper (not exported from the main entry, since it uses node:path):

const { getFontFilePath } = require('@hb61/thakar-gujarati-font-converter/src/node-fonts');

getFontFilePath('kap113'); // -> absolute path to kap113_0.ttf on disk

About mapped: false fonts

src/fonts/ currently holds ~156 .ttf files (kap001_0.ttf through kap160_0.ttf, plus KAP113). Only KAP113 has a real, working Unicode↔font mapping right now (src/mappings/kap113.js) — building that table required real bilingual examples and a lot of verification (see "Open items" below), and it still has gaps.

Every other font is registered so listFonts() reflects what's physically on disk, but calling toFont/toUnicode with one of them throws a clear error (... is registered but has no mapping data yet) instead of returning garbage. See "Adding a new font" below for the process to promote one from placeholder to working.

⚠️ Open items

  1. l and " were changed without verification (2026-07-16): on direct user instruction (l: ર્ reph → િ; ": િ → ૅ candra-E) without a fresh bilingual example to confirm them — repeated requests for one went unanswered. If output involving િ, ૅ, or ર્ looks wrong on real data, this is the first place to check.

  2. Two unresolved conflicts from a vowel-list test: that same test implied Z might be ૨ (digit 2) instead of its current DERIVED value ર (RA), and o might be ઃ (visarga) instead of its current CONFIRMED value સ્ (SA+virama). Both were left as-is (Z=ર, o=સ્) rather than guessed at, since the clarifying answers given were too ambiguous to act on safely - "Z used for both" isn't possible for a single static byte in this font, and doesn't resolve which is right.

  3. Resolved, noted for history: the vowel-list test's implication that unshifted 5 → ૫ (Gujarati digit five) turned out to be wrong - a follow-up test of the full unshifted digit row (1234567890 → ક્ષદ્રઘલપણજ્ઞટઠડ) explicitly overrode it. The unshifted digit row is consonants/conjuncts, not digits - Gujarati digits live on Shift+0-9 (DERIVED_ENTRIES) as originally expected.

  4. New territory discovered: a conjunct-list test surfaced 5 keys in the 128-159 byte range (Alt+0130-0139-ish), which had never been tested at all before - all prior extended-range work only covered 160-254. The rest of 128-159 is still completely unexplored and may contain more glyphs worth a dedicated pass.

  5. Q corrected: was ટ (TTA, READ tier), now ષ (SSA) per direct confirmation. ટ itself stays fully covered via digit key 8 (from the digit-row batch), so no coverage was lost.

  6. Resolved: ૌ (dependent AU matra) was failing - visible directly in a rendered screenshot. Fixed by adding it as a compose entry (F{, the same "au" suffix confirmed via ઔ=V+F+{), which works after any consonant since compose-matching is position-independent.

  7. Still unresolved: a conjunct example (ક્સિ → ˜S;) hasn't been cleanly parsed - S and ; already account for ક and સ, leaving ˜ (byte 0x98) needing to cover both the connecting virama and the િ matra, and it's unclear whether it's one or the other or both. Still fails visibly (confirmed via screenshot) since ˜ remains entirely unmapped. Needs a cleaner example to resolve rather than a guess - e.g. testing ક્સ alone (without the િ) would isolate what ˜ does.

  8. Pre-existing data conflict, found during npm packaging (2026-07-16): = (0x3D) has exactly one entry in CONFIRMED_ENTRIES, mapping it to ^ (added as a later "correction" batch), but an earlier documented assumption said = was a true no-op. test/convert.test.js still asserts the no-op behavior, so npm test currently fails on this one line. This is a data disagreement, not a bug in the conversion engine — left unresolved rather than guessed, same policy as the other open items here. Needs a real bilingual example containing = to settle it.

See the "OPEN ITEM"/"KNOWN GAP" comments in src/mappings/kap113.js for the full detail on both. Everything else in this table is verified the way it says below.

Status: two confidence tiers active, ~190-glyph table still incomplete

KAP113 is a "typing font" — every Gujarati glyph sits on a plain ASCII key code (33–126, plus a conjunct/matra-combo range at 160–255). There's no public specification for this mapping, so it's built from two sources:

  1. CONFIRMED — verified against real bilingual examples (the same text typed in both Unicode and KAP113, from actual ERP records). Ground truth, no ambiguity — except the open items above.
  2. DERIVED — read from a Terafont-Varun keyboard layout chart (a different font product), on the theory that legacy Gujarati "typing fonts" share one standardized keyboard convention. This isn't a blind guess: 5 of the 6 original CONFIRMED entries independently matched this chart exactly, a 5/6 hit rate that's real evidence. DERIVED entries are scoped strictly to patterns with a validated anchor — see src/mappings/kap113.js for exactly what's included and why.
  3. COMPOSED — some independent vowels (આ, એ, ઐ, ઓ, ઔ, ઑ, અં) aren't on their own KAP113 key at all; they're visually built by typing multiple existing keys together (e.g. આ = V + F). Confirmed via a real Unicode→KAP113 conversion. See applyPositionalRuleToTokens and the compose-lookup logic in src/engine.js for how this is handled architecturally differently from the single-byte entries.

Currently active: 144 confirmed + 26 derived + 9 read + 8 composed = 187 total.

| KAP key | Unicode | Character | Tier | |---|---|---|---| | C | U+0AB9 | હ (HA) | confirmed | | F | U+0ABE | ા (AA matra) | confirmed | | l | U+0ABF | િ (I matra) | unverified, see warning above | | N | U+0AA6 | દ (DA) | confirmed | | " | U+0AC5 | ૅ (candra E matra) | unverified, see warning above | | S | U+0A95 | ક (KA) | confirmed | | ¨ | U+0AB0 U+0ACD | ર્ (RA+virama, reph) | confirmed (extended range) | | A D G H J K L | — | બ મ ન જ વ છ ી | derived | | Z X V B M | — | ર શ અ ખ ો | derived | | !@#$%^&*() | — | ૧–૦ (digits) | derived |

Still unmapped (needs real confirmation, not a chart guess): the Tab row (Q–P), unshifted/lowercase key states, and the Alt+0161–254 extended conjunct range. Don't point this at production ERP data without spot-checking the DERIVED tier against at least one more real example — see src/mappings/kap113.js for the full rationale and exact scope.

Adding a new font

The package is built as a registry of fonts, not a single hardcoded mapping, so adding a font doesn't touch the conversion engine at all:

  1. Drop the font file at src/fonts/<name>.ttf (e.g. src/fonts/kap1.ttf).
  2. Build its mapping module at src/mappings/<name>.js, same shape as src/mappings/kap113.js (KAP_TO_UNICODE, UNICODE_TO_KAP, COMPOSED_* tables, name/label). See "How to expand the mapping table" below for the process of actually deriving those entries.
  3. Register it in src/registry.js:
    const kap1 = require('./mappings/kap1');
    const FONTS = { kap113, kap1 };
  4. toFont('kap1', text) / toUnicode('kap1', text) work immediately — listFonts() picks it up automatically too.

How to expand a font's mapping table

  1. Look at assets/glyph_sheet_0.png, glyph_sheet_1.png, glyph_sheet_2.png — every glyph in the font rendered next to its ASCII key code (regenerate per-font with npm run render-glyphs, see below).
  2. Fastest: if you have any more real bilingual examples (a name, a sentence — same text in Unicode and typed in the font), add them to scripts/examples.json and run:
    npm run learn            # defaults to kap113
    npm run learn -- kap1    # or check a different registered font
    It'll auto-verify against what's already confirmed and print exactly what's left to look up.
  3. Add confirmed entries to CONFIRMED_ENTRIES in the font's module under src/mappings/ (format: [asciiCode, unicodeSequence]).
  4. Re-run npm test.

If you ever need to regenerate the glyph sheets from a font file, run npm run render-glyphs (needs Python 3 + Pillow on that machine). The render scripts currently point at src/fonts/kap113.ttf; update the FONT_PATH constant in each scripts/render-*.js to target a different font's file when working on one.

Open investigation: does KAP113 share a layout with other Gujarati fonts?

Many legacy Gujarati "typing fonts" (KAP, Terafont, Krishna, LMG, ...) were built around the same shared "Gujarati typewriter" key convention on the main keyboard layer, so typists could switch fonts without retraining. A Terafont-Varun keyboard chart was checked against KAP113 and got one independent confirmation: KAP113's ! key (Shift+1) shape-matched to ૧ via automated comparison against Noto Sans Gujarati — the same value the Terafont-Varun chart shows for Shift+1. That's one data point, not a verified table.

Two comparison tools exist to push this further:

npm run render-keyboard-grid   # -> assets/kap113_keyboard_grid.png
npm run render-altcode-grid    # -> assets/kap113_altcode_grid.png

The first renders every KAP113 glyph in physical-key shape (base + shift) so it can be laid next to any Gujarati font's keyboard chart. The second does the same for the Alt+0161–0254 extended byte range — but that range is normally vendor-specific even when the main layer is shared, so don't expect it to match by default. A reference chart (Terafont-Varun) is saved in assets/reference-charts/ for this comparison.

See the "OPEN INVESTIGATION" block at the bottom of src/mappings/kap113.js for the promotion path once specific keys are confirmed.

Unmapped characters pass through unchanged rather than being dropped, so gaps in the table are visually obvious (garbled text) instead of silently losing data.

Design notes

  • Reph / conjuncts: some KAP113 keys render a ligature that corresponds to more than one Unicode codepoint (e.g. RA+virama → single reph glyph, key l - also reachable via ¨ in the extended range, l wins ties since it's the original confirmed key). The engine handles this generically via UNICODE_SEQUENCES_LONGEST_FIRST — a greedy longest-match tokenizer, so multi-codepoint entries are matched before falling back to single characters.
  • Matra order: Unicode always stores a dependent vowel sign after its consonant, even when it displays before/around it (e.g. િ visually sits to the left of its consonant but is stored after it). This font keeps that same order when typing - confirmed directly against the rendered font and the original હાર્દિક example (l=reph before N=દ, "=િ naturally after N, giving CFlN"S with no reordering). A detour briefly added explicit reordering logic based on a misread correction; that was reverted once the original example turned out to already be correct. No manual reordering step is needed - direct sequence mapping is correct for everything confirmed so far.

Files

src/
  fonts/
    kap113_0.ttf            — font file, importable directly by bundlers for @font-face (one per registered font)
  mappings/
    kap113.js              — KAP113's ASCII <-> Unicode table (edit this to add entries)
  registry.js               — font-name -> mapping module lookup (register new fonts here); no Node built-ins, browser-safe
  engine.js                 — generic conversion engine, shared by every font
  index.js                  — public exports (CommonJS): toFont, toUnicode, listFonts
  index.mjs                 — public exports (ESM), for `import` / bundlers
  node-fonts.js              — Node-only helper: getFontFilePath(name) -> absolute .ttf path
scripts/
  learn-mapping.js         — cross-check new examples against confirmed entries (per font)
  render-glyph-sheet.js    — regenerate assets/glyph_sheet_*.png from a font file
  render-keyboard-grid.js  — regenerate assets/kap113_keyboard_grid.png (main layer, chart comparison)
  render-altcode-grid.js   — regenerate assets/kap113_altcode_grid.png (Alt+0161-0254 range)
demo/
  kap113-converter.html    — self-contained interactive demo (embeds the font + mapping)
assets/
  glyph_sheet_*.png          — every glyph rendered with its ASCII code
  kap113_keyboard_grid.png   — main layer rendered in keyboard shape, for chart comparison
  kap113_altcode_grid.png    — extended byte range rendered for chart comparison
  reference-charts/          — third-party layout charts used for cross-checking (not confirmed identical)
test/
  convert.test.js        — round-trip test using the confirmed example