Current section
Files
Jump to
Current section
Files
iconvex_bibliographic
README.md
README.md
# Iconvex Bibliographic
`iconvex_bibliographic` is the source-qualified library, cataloguing, and TeX
extension for [Iconvex](https://hex.pm/packages/iconvex). It is pure Elixir:
there is no NIF, port process, platform `iconv`, or runtime dependency on
`iconvex_specs`.
Add the package and its Core dependency:
```elixir
def deps do
[
{:iconvex, "~> 0.1.1"},
{:iconvex_bibliographic, "~> 0.1"}
]
end
```
Starting the OTP application atomically registers every byte codec. Stopping
it atomically removes only the routes owned by this package.
## The collection
| Family | Canonical encoding | What makes it interesting |
| --- | --- | --- |
| MARC | `MARC-8` | Stateful ISO 2022 designations, EACC 24-bit characters, ANSEL combining marks, and spanning half marks |
| MARC | `ANSEL` | Strict ANSI/NISO Z39.47 ASCII + Extended Latin environment |
| Cork | `TEX-T1-EC-GLYPH` | The EC glyph interpretation, including ligature scalars and exact `SS` sequence semantics |
| Cork | `TEX-T1-CMAP-1.0J` | CTAN CMap extraction with deterministic longest-match `ffi`/`ffl`/`ff`/`fi`/`fl` encoding |
| OT1 | `TEX-OT1-CMAP-1.0J` | Normal CTAN OT1 extraction; byte `20` is deliberately undefined |
| OT1 | `TEX-OT1TT-CMAP-1.0J` | Typewriter OT1, where byte `20` is U+2423 OPEN BOX |
| TeX math | `TEX-LIVE-OML-CMMI10-TOUNICODE-2026` | Exact 7-bit `cmmi10` ToUnicode semantics, with packed MSB/LSB transports |
| TeX math | `TEX-LIVE-OMS-CMSY10-TOUNICODE-2026` | Exact 7-bit `cmsy10` ToUnicode semantics, with packed MSB/LSB transports |
| Glyph vectors | `CTAN-LY1-TEXNANSI-1.1-AGL-4036A9CA` | A pinned TeX'n'ANSI 1.1 vector resolved through a pinned Adobe Glyph List revision |
| Glyph vectors | `ADOBE-POSTSCRIPT-3-ISOLATIN1-AGL-4036A9CA` | PostScript LanguageLevel 3 ISOLatin1Encoding—specifically not ISO-8859-1 |
`Iconvex.Bibliographic.registrations/0`, `codecs/0`, `encodings/0`, and
`packed_profiles/0` expose the complete manifest-ordered surface.
## MARC-8: combining marks and EACC
MARC-8 stores an ANSEL combining mark before its base character. Iconvex gives
you normal Unicode order:
```elixir
iex> Iconvex.convert(<<0xE2, ?e>>, "MARC-8", "UTF-8")
{:ok, "e\u0301"}
iex> Iconvex.convert("e\u0301", "UTF-8", "MARC8")
{:ok, <<0xE2, ?e>>}
```
The same codec handles custom and ISO 2022 designations plus three-byte EACC:
```elixir
iex> Iconvex.Bibliographic.MARC8.decode(<<0x1B, ?$, ?1, 0x21, 0x30, 0x21>>)
{:ok, [0x4E00]}
iex> Iconvex.Bibliographic.MARC8.decode(<<0xEB, ?a, 0xEC, ?b>>)
{:ok, [?a, 0x0361, ?b]}
```
## TeX ligatures with exact inverse semantics
The CMap profiles encode the longest source-defined sequence first:
```elixir
iex> Iconvex.convert("ffifflfffifl", "UTF-8", "TEX-OT1-CMAP-1.0J")
{:ok, <<0x0E, 0x0F, 0x0B, 0x0C, 0x0D>>}
iex> Iconvex.convert("ffiAfflASS", "UTF-8", "TEX-T1-CMAP-1.0J")
{:ok, <<0x1E, ?A, 0x1F, ?A, 0xDF>>}
```
These are Unicode-extraction profiles, not claims that every historical font
with the same broad label has identical semantics.
## Seven-bit math, actually packed
The ordinary OML and OMS codecs use one logical 7-bit unit per byte, which is
convenient for `Iconvex.convert/4`. The packed facade also emits true
contiguous units:
```elixir
alias Iconvex.Bibliographic.Packed
iex> Packed.encode_from_utf8("Γ∆", "OML")
{:ok, <<0::7, 1::7>>}
iex> {:ok, lsb} = Packed.encode_from_utf8("Γ∆", "OML-PACKED-LSB")
iex> lsb
%Iconvex.Packed.LSB{data: <<0x80, 0x00>>, bit_size: 14, unit_bits: 7, bit_order: :lsb}
iex> Packed.decode_to_utf8(lsb, "OML-PACKED-LSB")
{:ok, "Γ∆"}
```
The fully qualified named forms are
`TEX-LIVE-OML-CMMI10-TOUNICODE-2026-PACKED-MSB`/`-PACKED-LSB` and the
corresponding OMS names. Explicit and name-implied bit orders must agree.
## Streams and recovery policies
Every codec implements the Iconvex strict, discard, substitution, direct UTF-8,
and chunk callbacks. Stateful MARC-8 and longest-match TeX sequences remain
correct across arbitrary stream boundaries:
```elixir
chunks = [<<0x1B, ?$, ?1, 0x21>>, <<0x30, 0x21>>]
{:ok, stream} = Iconvex.stream(chunks, "MARC-8", "UTF-8")
IO.iodata_to_binary(Enum.to_list(stream))
# => "一"
```
## Why the long names?
Many typographic “encodings” are really a vector + glyph list + extraction
policy + source revision. Short aliases would silently merge distinct things.
The canonical names retain the source boundary; only reviewed, unambiguous
aliases are registered.
The package ships the exact runtime/source assets needed by those profiles,
their SHA-256-bearing metadata, retained AGL/LPPL terms, and the 12-row
`SURFACE_MANIFEST.tsv`. Tests exhaust all 256 bytes where applicable, all 128
OML/OMS units, inverse mappings, malformed input, every stream split, all
1,112,064 Unicode scalar values for both glyph-vector encoders, application
restart ownership, and both packed orders.
See [BENCHMARKS.md](BENCHMARKS.md) for reproducible native-path gates and
[NOTICE](NOTICE) for upstream provenance and license boundaries.