Encoding Hangeul, Koreas writing system

Hangeul (한글) is the modern writing system for the Korean language, created in 1443 by King Sejong the great, the fourth king of the Joseon dynasty0. Before its invention, Korean was written using Hanja (한자) — Chinese characters adapted for Korean. Even after Hangeul's creation, Hanja remained in regular use for centuries, particularly in formal and scholarly contexts0.

Hangeul is a syllabic writing system where each syllable is composed of letters called Jamo (자모). These Jamo function like Latin letters, each representing an individual sound. There are a total of 11,172 syllabic combinations of Jamo which can be used to form Hangeul0.

However, learning Hangeul doesn't require memorizing thousands of combinations. Instead, one only needs to learn the 68 different Jamo shapes and rules governing the construction of Hangeul. This is much easier than it sounds with the basics of the Korean alphabet being able to be learned within 60-90 minutes.

A Korean syllable consists of a lead consonant, a medial vowel, and a tail consonant. For example,

한 (han) can be deconstructed like so:

ㅎ (h) is the lead consonant.

ㅏ (a) is the medial vowel.

ㄴ (n) is the tail consonant.

자 (ja) can be deconstructed like so:

ㅈ (j) is the lead consonant.

ㅏ (a) is the medial vowel.

There is no tail consonant.

Syllables starting with an initial vowel, for example 안 (an), must be prefixed with ㅇ (eung), a special lead consonant that produces no sound.

There are 19 different lead consonants ranging between Unicode values U+1100 and U+1112. There are 21 different vowels ranging between Unicode values U+1161 and U+1175. Finally, there are 27 tail consonants ranging between Unicode values U+11A8 and U+11C2 + a null consonant used when there is no tail. This range of Unicode is called the Hangeul Jamo block.

Below is a set of tables providing each Jamo, its canonical order, as well as its Unicode value. When Jamo are used in combination with each other, they change their shape, size, and positions slightly to fit into a single Hangeul. Unicode has a second range of Jamo characters called "Hangeul Compatibility Jamo" starting at U+3130 which represent the initial and final consonants with the same Unicode codepoint as opposed to the Hangeul Jamo block which assigns them separate codepoints0. These compatibility Jamo typically render much more clearer.

The table below shows both, the conjoining Jamo as well as the [compatibility Jamo] with the character reference hex value referring to the former0. Depending on your browser, both Jamo may render the same.

Although each Jamo has its own individual Unicode value, Unicode values U+AC00 to U+D7A3 define every combination of Jamo, or every Hangeul character. This range of Unicode is called the Hangeul Syllables block0.

These syllables can be directly mapped by algorithm back to sequences of two or three Jamo in the Hangeul Jamo block mentioned earlier0. It's due to this that it's theoretically possible to encode Korean texts with Jamo only and let the font rendered handle Hangeul construction. This is not recommended in practice though due to limitations and lack of support in current renderers.

Isolated Jamo are rarely found in Korean texts instead opting for use of the precomposed Hangeul syllables. The codepoint of a Hangeul can be calculated from its Jamo components using the following formula0.

Hangeul Unicode = (((lead − 1) * 588) + ((vowel − 1) * 28) + tail) + 44032

In the formula, lead, vowel, and tail refer to the small integers given in the above tables. If there is no tail, the value 0 is used. The value 28 is the sum of the tail count. The value 588 is the sum of vowel and tail count. 44032 (0xAC00) is the first character of the Hangeul Syllables Unicode block.

As an example, let's take the Hangeul 한 (han), which in Unicode is U+D55C. Using the Jamo that construct it, let's try reach the final value.

ㅎ = 19 // Lead consonant
ㅏ = 1 // Median vowel
ㄴ = 4 // Tail consonant

한 = U+D55C = 54620 = (((19 − 1) * 588) + ((1 − 1) * 28) + 4) + 44032
= (10584 + 0 + 4) + 44032
= 10588 + 44032
= 54620

Given the above formula, it can be rearranged to extract the Jamo of a Hangeul.

tail = mod(Hangeul Unicode - 44032, 28)
vowel = 1 + mod(Hangeul Unicode - 44032 - tail, 588) / 28
lead = 1 + floor[(Hangeul Unicode - 44032) / 588]

As another example, let's try work backwards. Given the Hangeul 한 (han), let's try deconstruct it.

한 = U+D55C = 54620

// tail
ㄴ = 4 = mod(54620 - 44032, 28)
= mod(10588, 28)
= 4

// vowel
ㅏ = 1 = 1 + mod(54620 - 44032 - 4, 588) / 28
= 1 + mod(10584, 588) / 28
= 1 + 0 / 28
= 1

// lead
ㅎ = 19 = 1 + floor[(54620 - 44032) / 588]
= 1 + floor[10592 / 588]
= 1 + 18
= 19

Overtime, Hangeul has undergone many changes with several Jamo no longer being used in modern Korean0.

ㆍ (아래아, arae-a): A short "a" sound.

ㆆ (여린히읗, yeorin-hieut): A "light h", akin to a softer version of the English "h".

ㅿ (반시옷, bansiot): A "z" sound.

ㆁ (옛이응, yet-ieung): A velar nasal sound comparable to "ng" in the word "sing".

Since these obsolete characters are still used in older literature and historical texts, there's still a need to have some way of using them. However, because they're obsolete, Unicode does not offer precomposed Hangeul. Instead, syllables using them must be coded by Jamo.