GNU Unifont  15.1.01
Pan-Unicode font with complete Unicode Plane 0 coverage and partial coverage of higher planes
unihangul-support.c
Go to the documentation of this file.
1 /**
2  @file unihangul-support.c
3 
4  @brief Functions for converting Hangul letters into syllables
5 
6  This file contains functions for reading in Hangul letters
7  arranged in a Johab 6/3/1 pattern and composing syllables
8  with them. One function maps an iniital letter (choseong),
9  medial letter (jungseong), and final letter (jongseong)
10  into the Hangul Syllables Unicode block, U+AC00..U+D7A3.
11  Other functions allow formation of glyphs that include
12  the ancient Hangul letters that Hanterm supported. More
13  can be added if desired, with appropriate changes to
14  start positions and lengths defined in "hangul.h".
15 
16  @author Paul Hardy
17 
18  @copyright Copyright © 2023 Paul Hardy
19 */
20 /*
21  LICENSE:
22 
23  This program is free software: you can redistribute it and/or modify
24  it under the terms of the GNU General Public License as published by
25  the Free Software Foundation, either version 2 of the License, or
26  (at your option) any later version.
27 
28  This program is distributed in the hope that it will be useful,
29  but WITHOUT ANY WARRANTY; without even the implied warranty of
30  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31  GNU General Public License for more details.
32 
33  You should have received a copy of the GNU General Public License
34  along with this program. If not, see <http://www.gnu.org/licenses/>.
35 */
36 
37 #include <stdio.h>
38 #include "hangul.h"
39 
40 
41 /**
42  @brief Read hangul-base.hex file into a unsigned char array.
43 
44  Read a Hangul base .hex file with separate choseong, jungseong,
45  and jongseong glyphs for syllable formation. The order is:
46 
47  - Empty glyph in 0x0000 position.
48  - Initial consonants (choseong).
49  - Medial vowels and dipthongs (jungseong).
50  - Final consonants (jongseong).
51  - Individual letter forms in isolation, not for syllable formation.
52 
53  The letters are arranged with all variations for one letter
54  before continuing to the next letter. In the current
55  encoding, there are 6 variations of choseong, 3 of jungseong,
56  and 1 of jongseong per letter.
57 
58  @param[in] Input file pointer; can be stdin.
59  @param[out] Array of bit patterns, with 32 8-bit values per letter.
60  @return The maximum code point value read in the file.
61 */
62 unsigned
63 hangul_read_base8 (FILE *infp, unsigned char base[][32]) {
64  unsigned codept;
65  unsigned max_codept;
66  int i, j;
67  char instring[MAXLINE];
68 
69 
70  max_codept = 0;
71 
72  while (fgets (instring, MAXLINE, infp) != NULL) {
73  sscanf (instring, "%X", &codept);
74  codept -= PUA_START;
75  /* If code point is within range, add it */
76  if (codept < MAX_GLYPHS) {
77  /* Find the start of the glyph bitmap. */
78  for (i = 1; instring[i] != '\0' && instring[i] != ':'; i++);
79  if (instring[i] == ':') {
80  i++; /* Skip over ':' to get to start of bitmap. */
81  for (j = 0; j < 32; j++) {
82  sscanf (&instring[i], "%2hhX", &base[codept][j]);
83  i += 2;
84  }
85  if (codept > max_codept) max_codept = codept;
86  }
87  }
88  }
89 
90  return max_codept;
91 }
92 
93 
94 /**
95  @brief Read hangul-base.hex file into a unsigned array.
96 
97  Read a Hangul base .hex file with separate choseong, jungseong,
98  and jongseong glyphs for syllable formation. The order is:
99 
100  - Empty glyph in 0x0000 position.
101  - Initial consonants (choseong).
102  - Medial vowels and dipthongs (jungseong).
103  - Final consonants (jongseong).
104  - Individual letter forms in isolation, not for syllable formation.
105 
106  The letters are arranged with all variations for one letter
107  before continuing to the next letter. In the current
108  encoding, there are 6 variations of choseong, 3 of jungseong,
109  and 1 of jongseong per letter.
110 
111  @param[in] Input file pointer; can be stdin.
112  @param[out] Array of bit patterns, with 16 16-bit values per letter.
113  @return The maximum code point value read in the file.
114 */
115 unsigned
116 hangul_read_base16 (FILE *infp, unsigned base[][16]) {
117  unsigned codept;
118  unsigned max_codept;
119  int i, j;
120  char instring[MAXLINE];
121 
122 
123  max_codept = 0;
124 
125  while (fgets (instring, MAXLINE, infp) != NULL) {
126  sscanf (instring, "%X", &codept);
127  codept -= PUA_START;
128  /* If code point is within range, add it */
129  if (codept < MAX_GLYPHS) {
130  /* Find the start of the glyph bitmap. */
131  for (i = 1; instring[i] != '\0' && instring[i] != ':'; i++);
132  if (instring[i] == ':') {
133  i++; /* Skip over ':' to get to start of bitmap. */
134  for (j = 0; j < 16; j++) {
135  sscanf (&instring[i], "%4X", &base[codept][j]);
136  i += 4;
137  }
138  if (codept > max_codept) max_codept = codept;
139  }
140  }
141  }
142 
143  return max_codept;
144 }
145 
146 
147 /**
148  @brief Decompose a Hangul Syllables code point into three letters.
149 
150  Decompose a Hangul Syllables code point (U+AC00..U+D7A3) into:
151 
152  - Choseong 0-19
153  - Jungseong 0-20
154  - Jongseong 0-27 or -1 if no jongseong
155 
156  All letter values are set to -1 if the letters do not
157  form a syllable in the Hangul Syllables range. This function
158  only handles modern Hangul, because that is all that is in
159  the Hangul Syllables range.
160 
161  @param[in] codept The Unicode code point to decode, from 0xAC00 to 0xD7A3.
162  @param[out] initial The 1st letter (choseong) in the syllable.
163  @param[out] initial The 2nd letter (jungseong) in the syllable.
164  @param[out] initial The 3rd letter (jongseong) in the syllable.
165 */
166 void
167 hangul_decompose (unsigned codept, int *initial, int *medial, int *final) {
168 
169  if (codept < 0xAC00 || codept > 0xD7A3) {
170  *initial = *medial = *final = -1;
171  }
172  else {
173  codept -= 0xAC00;
174  *initial = codept / (28 * 21);
175  *medial = (codept / 28) % 21;
176  *final = codept % 28 - 1;
177  }
178 
179  return;
180 }
181 
182 
183 /**
184  @brief Compose a Hangul syllable into a code point, or 0 if none exists.
185 
186  This function takes three letters that can form a modern Hangul
187  syllable and returns the corresponding Unicode Hangul Syllables
188  code point in the range 0xAC00 to 0xD7A3.
189 
190  If a three-letter combination includes one or more archaic letters,
191  it will not map into the Hangul Syllables range. In that case,
192  the returned code point will be 0 to indicate that no valid
193  Hangul Syllables code point exists.
194 
195  @param[in] initial The first letter (choseong), 0 to 18.
196  @param[in] medial The second letter (jungseong), 0 to 20.
197  @param[in] final The third letter (jongseong), 0 to 26 or -1 if none.
198  @return The Unicode Hangul Syllables code point, 0xAC00 to 0xD7A3.
199 */
200 unsigned
201 hangul_compose (int initial, int medial, int final) {
202  unsigned codept;
203 
204 
205  if (initial >= 0 && initial <= 18 &&
206  medial >= 0 && medial <= 20 &&
207  final >= 0 && final <= 26) {
208 
209  codept = 0xAC00;
210  codept += initial * 21 * 28;
211  codept += medial * 28;
212  codept += final + 1;
213  }
214  else {
215  codept = 0;
216  }
217 
218  return codept;
219 }
220 
221 
222 /**
223  @brief Determine index values to the bitmaps for a syllable's components.
224 
225  This function reads these input values for modern and ancient Hangul letters:
226 
227  - Choseong number (0 to the number of modern and archaic choseong - 1.
228  - Jungseong number (0 to the number of modern and archaic jungseong - 1.
229  - Jongseong number (0 to the number of modern and archaic jongseong - 1, or -1 if none.
230 
231  It then determines the variation of each letter given the combination with
232  the other two letters (or just choseong and jungseong if the jongseong value
233  is -1).
234 
235  These variations are then converted into index locations within the
236  glyph array that was read in from the hangul-base.hex file. Those
237  index locations can then be used to form a composite syllable.
238 
239  There is no restriction to only use the modern Hangul letters.
240 
241  @param[in] choseong The 1st letter in the syllable.
242  @param[in] jungseong The 2nd letter in the syllable.
243  @param[in] jongseong The 3rd letter in the syllable, or -1 if none.
244  @param[out] cho_index Index location to the 1st letter variation from the hangul-base.hex file.
245  @param[out] jung_index Index location to the 2nd letter variation from the hangul-base.hex file.
246  @param[out] jong_index Index location to the 3rd letter variation from the hangul-base.hex file.
247 */
248 void
249 hangul_hex_indices (int choseong, int jungseong, int jongseong,
250  int *cho_index, int *jung_index, int *jong_index) {
251 
252  int cho_variation, jung_variation, jong_variation; /* Letter variations */
253 
254  void hangul_variations (int choseong, int jungseong, int jongseong,
255  int *cho_variation, int *jung_variation, int *jong_variation);
256 
257 
258  hangul_variations (choseong, jungseong, jongseong,
260 
261  *cho_index = CHO_HEX + choseong * CHO_VARIATIONS + cho_variation;
262  *jung_index = JUNG_HEX + jungseong * JUNG_VARIATIONS + jung_variation;;
263  *jong_index = jongseong < 0 ? 0x0000 :
264  JONG_HEX + jongseong * JONG_VARIATIONS + jong_variation;
265 
266  return;
267 }
268 
269 
270 /**
271  @brief Determine the variations of each letter in a Hangul syllable.
272 
273  Given the three letters that will form a syllable, return the variation
274  of each letter used to form the composite glyph.
275 
276  This function can determine variations for both modern and archaic
277  Hangul letters; it is not limited to only the letters combinations
278  that comprise the Unicode Hangul Syllables range.
279 
280  This function reads these input values for modern and ancient Hangul letters:
281 
282  - Choseong number (0 to the number of modern and archaic choseong - 1.
283  - Jungseong number (0 to the number of modern and archaic jungseong - 1.
284  - Jongseong number (0 to the number of modern and archaic jongseong - 1, or -1 if none.
285 
286  It then determines the variation of each letter given the combination with
287  the other two letters (or just choseong and jungseong if the jongseong value
288  is -1).
289 
290  @param[in] choseong The 1st letter in the syllable.
291  @param[in] jungseong The 2nd letter in the syllable.
292  @param[in] jongseong The 3rd letter in the syllable, or -1 if none.
293  @param[out] cho_var Variation of the 1st letter from the hangul-base.hex file.
294  @param[out] jung_var Variation of the 2nd letter from the hangul-base.hex file.
295  @param[out] jong_var Variation of the 3rd letter from the hangul-base.hex file.
296 */
297 void
298 hangul_variations (int choseong, int jungseong, int jongseong,
299  int *cho_var, int *jung_var, int *jong_var) {
300 
301  int cho_variation (int choseong, int jungseong, int jongseong);
302  int jung_variation (int choseong, int jungseong, int jongseong);
303  int jong_variation (int choseong, int jungseong, int jongseong);
304 
305  /*
306  Find the variation for each letter component.
307  */
308  *cho_var = cho_variation (choseong, jungseong, jongseong);
309  *jung_var = jung_variation (choseong, jungseong, jongseong);
310  *jong_var = jong_variation (choseong, jungseong, jongseong);
311 
312 
313  return;
314 }
315 
316 
317 /**
318  @brief Return the Johab 6/3/1 choseong variation for a syllable.
319 
320  This function takes the two or three (if jongseong is included)
321  letters that comprise a syllable and determine the variation
322  of the initial consonant (choseong).
323 
324  Each choseong has 6 variations:
325 
326  Variation Occurrence
327  --------- ----------
328  0 Choseong with a vertical vowel such as "A".
329  1 Choseong with a horizontal vowel such as "O".
330  2 Choseong with a vertical and horizontal vowel such as "WA".
331  3 Same as variation 0, but with jongseong (final consonant).
332  4 Same as variation 1, but with jongseong (final consonant).
333  Also a horizontal vowel pointing down, such as U and YU.
334  5 Same as variation 2, but with jongseong (final consonant).
335  Also a horizontal vowel pointing down with vertical element,
336  such as WEO, WE, and WI.
337 
338  In addition, if the vowel is horizontal and a downward-pointing stroke
339  as in the modern letters U, WEO, WE, WI, and YU, and in archaic
340  letters YU-YEO, YU-YE, YU-I, araea, and araea-i, then 3 is added
341  to the initial variation of 0 to 2, resulting in a choseong variation
342  of 3 to 5, respectively.
343 
344  @param[in] choseong The 1st letter in the syllable.
345  @param[in] jungseong The 2nd letter in the syllable.
346  @param[in] jongseong The 3rd letter in the syllable.
347  @return The choseong variation, 0 to 5.
348 */
349 int
350 cho_variation (int choseong, int jungseong, int jongseong) {
351  int cho_variation; /* Return value */
352 
353  /*
354  The Choseong cho_var is determined by the
355  21 modern + 50 ancient Jungseong, and whether
356  or not the syllable contains a final consonant
357  (Jongseong).
358  */
359  static int choseong_var [TOTAL_JUNG + 1] = {
360  /*
361  Modern Jungseong in positions 0..20.
362  */
363 /* Location Variations Unicode Range Vowel # Vowel Names */
364 /* -------- ---------- -------------- ------- ----------- */
365 /* 0x2FB */ 0, 0, 0, // U+1161..U+1163-->[ 0.. 2] A, AE, YA
366 /* 0x304 */ 0, 0, 0, // U+1164..U+1166-->[ 3.. 5] YAE, EO, E
367 /* 0x30D */ 0, 0, // U+1167..U+1168-->[ 6.. 7] YEO, YE
368 /* 0x313 */ 1, // U+1169 -->[ 8] O
369 /* 0x316 */ 2, 2, 2, // U+116A..U+116C-->[ 9..11] WA, WAE, WE
370 /* 0x31F */ 1, 4, // U+116D..U+116E-->[12..13] YO, U
371 /* 0x325 */ 5, 5, 5, // U+116F..U+1171-->[14..16] WEO, WE, WI
372 /* 0x32E */ 4, 1, // U+1172..U+1173-->[17..18] YU, EU
373 /* 0x334 */ 2, // U+1174 -->[19] YI
374 /* 0x337 */ 0, // U+1175 -->[20] I
375  /*
376  Ancient Jungseong in positions 21..70.
377  */
378 /* Location Variations Unicode Range Vowel # Vowel Names */
379 /* -------- ---------- -------------- ------- ----------- */
380 /* 0x33A: */ 2, 5, 2, // U+1176..U+1178-->[21..23] A-O, A-U, YA-O
381 /* 0x343: */ 2, 2, 5, // U+1179..U+117B-->[24..26] YA-YO, EO-O, EU-U
382 /* 0x34C: */ 2, 2, 5, // U+117C..U+117E-->[27..29] EO-EU, YEO-O, YEO-U
383 /* 0x355: */ 2, 5, 5, // U+117F..U+1181-->[30..32] O-EO, O-E, O-YE,
384 /* 0x35E: */ 4, 4, 2, // U+1182..U+1184-->[33..35] O-O, O-U, YO-YA,
385 /* 0x367: */ 2, 2, 5, // U+1185..U+1187-->[36..38] YO-YAE, YO-YEO, YO-O,
386 /* 0x370: */ 2, 5, 5, // U+1188..U+118A-->[39..41] YO-I, U-A, U-AE,
387 /* 0x379: */ 5, 5, 5, // U+118B..U+118D-->[42..44] U-EO-EU, U-YE, U-U,
388 /* 0x382: */ 5, 5, 5, // U+118E..U+1190-->[45..47] YU-A, YU-EO, YU-E,
389 /* 0x38B: */ 5, 5, 2, // U+1191..U+1193-->[48..50] YU-YEO, YU-YE, YU-U,
390 /* 0x394: */ 5, 2, 2, // U+1194..U+1196-->[51..53] YU-I, EU-U, EU-EU,
391 /* 0x39D: */ 2, 0, 0, // U+1197..U+1199-->[54..56] YI-U, I-A, I-YA,
392 /* 0x3A6: */ 2, 5, 2, // U+119A..U+119C-->[57..59] I-O, I-U, I-EU,
393 /* 0x3AF: */ 0, 1, 2, // U+119D..U+119F-->[60..62] I-ARAEA, ARAEA, ARAEA-EO,
394 /* 0x3B8: */ 1, 2, 1, // U+11A0..U+11A2-->[63..65] ARAEA-U, ARAEA-I,SSANGARAEA,
395 /* 0x3C1: */ 2, 5, 0, // U+11A3..U+11A5-->[66..68] A-EU, YA-U, YEO-YA,
396 /* 0x3CA: */ 2, 2, // U+11A6..U+11A7-->[69..70] O-YA, O-YAE,
397 #ifdef EXTENDED_HANGUL
398 /* 0x3D0: */ 2, 4, 5, // U+D7B0..U+D7B2-->[71..73] O-YEO, O-O-I, YO-A,
399 /* 0x3D9: */ 5, 2, 5, // U+D7B3..U+D7B5-->[74..76] YO-AE, YO-EO, U-YEO,
400 /* 0x3E2: */ 5, 5, 4, // U+D7B6..U+D7B8-->[77..79] U-I-I, YU-AE, YU-O,
401 /* 0x3EB: */ 5, 2, 5, // U+D7B9..U+D7BB-->[80..82] EU-A, EU-EO, EU-E,
402 /* 0x3F4: */ 4, 2, 3, // U+D7BC..U+D7BE-->[83..85] EU-O, I-YA-O, I-YAE,
403 /* 0x3FD: */ 3, 3, 2, // U+D7BF..U+D7C1-->[86..88] I-YEO, I-YE, I-O-I,
404 /* 0x406: */ 2, 2, 0, // U+D7C2..U+D7C4-->[89..91] I-YO, I-YU, I-I,
405 /* 0x40F: */ 2, 2, // U+D7C5..U+D7C6-->[92..93] ARAEA-A, ARAEA-E,
406 /* 0x415: */ -1 // Mark end of list of vowels.
407 #else
408 /* 0x310: */ -1 // Mark end of list of vowels.
409 #endif
410  };
411 
412 
413  if (jungseong < 0 || jungseong >= TOTAL_JUNG) {
414  cho_variation = -1;
415  }
416  else {
417  cho_variation = choseong_var [jungseong];
418  if (choseong >= 0 && jongseong >= 0 && cho_variation < 3)
419  cho_variation += 3;
420  }
421 
422 
423  return cho_variation;
424 }
425 
426 
427 /**
428  @brief Whether vowel has rightmost vertical stroke to the right.
429 
430  @param[in] vowel Vowel number, from 0 to TOTAL_JUNG - 1.
431  @return 1 if this vowel's vertical stroke is wide on the right side; else 0.
432 */
433 int
434 is_wide_vowel (int vowel) {
435  int retval; /* Return value. */
436 
437  static int wide_vowel [TOTAL_JUNG + 1] = {
438  /*
439  Modern Jungseong in positions 0..20.
440  */
441 /* Location Variations Unicode Range Vowel # Vowel Names */
442 /* -------- ---------- -------------- ------- ----------- */
443 /* 0x2FB */ 0, 1, 0, // U+1161..U+1163-->[ 0.. 2] A, AE, YA
444 /* 0x304 */ 1, 0, 1, // U+1164..U+1166-->[ 3.. 5] YAE, EO, E
445 /* 0x30D */ 0, 1, // U+1167..U+1168-->[ 6.. 7] YEO, YE
446 /* 0x313 */ 0, // U+1169 -->[ 8] O
447 /* 0x316 */ 0, 1, 0, // U+116A..U+116C-->[ 9..11] WA, WAE, WE
448 /* 0x31F */ 0, 0, // U+116D..U+116E-->[12..13] YO, U
449 /* 0x325 */ 0, 1, 0, // U+116F..U+1171-->[14..16] WEO, WE, WI
450 /* 0x32E */ 0, 0, // U+1172..U+1173-->[17..18] YU, EU
451 /* 0x334 */ 0, // U+1174 -->[19] YI
452 /* 0x337 */ 0, // U+1175 -->[20] I
453  /*
454  Ancient Jungseong in positions 21..70.
455  */
456 /* Location Variations Unicode Range Vowel # Vowel Names */
457 /* -------- ---------- -------------- ------- ----------- */
458 /* 0x33A: */ 0, 0, 0, // U+1176..U+1178-->[21..23] A-O, A-U, YA-O
459 /* 0x343: */ 0, 0, 0, // U+1179..U+117B-->[24..26] YA-YO, EO-O, EU-U
460 /* 0x34C: */ 0, 0, 0, // U+117C..U+117E-->[27..29] EO-EU, YEO-O, YEO-U
461 /* 0x355: */ 0, 1, 1, // U+117F..U+1181-->[30..32] O-EO, O-E, O-YE,
462 /* 0x35E: */ 0, 0, 0, // U+1182..U+1184-->[33..35] O-O, O-U, YO-YA,
463 /* 0x367: */ 1, 0, 0, // U+1185..U+1187-->[36..38] YO-YAE, YO-YEO, YO-O,
464 /* 0x370: */ 0, 0, 1, // U+1188..U+118A-->[39..41] YO-I, U-A, U-AE,
465 /* 0x379: */ 0, 1, 0, // U+118B..U+118D-->[42..44] U-EO-EU, U-YE, U-U,
466 /* 0x382: */ 0, 0, 1, // U+118E..U+1190-->[45..47] YU-A, YU-EO, YU-E,
467 /* 0x38B: */ 0, 1, 0, // U+1191..U+1193-->[48..50] YU-YEO, YU-YE, YU-U,
468 /* 0x394: */ 0, 0, 0, // U+1194..U+1196-->[51..53] YU-I, EU-U, EU-EU,
469 /* 0x39D: */ 0, 0, 0, // U+1197..U+1199-->[54..56] YI-U, I-A, I-YA,
470 /* 0x3A6: */ 0, 0, 0, // U+119A..U+119C-->[57..59] I-O, I-U, I-EU,
471 /* 0x3AF: */ 0, 0, 0, // U+119D..U+119F-->[60..62] I-ARAEA, ARAEA, ARAEA-EO,
472 /* 0x3B8: */ 0, 0, 0, // U+11A0..U+11A2-->[63..65] ARAEA-U, ARAEA-I,SSANGARAEA,
473 /* 0x3C1: */ 0, 0, 0, // U+11A3..U+11A5-->[66..68] A-EU, YA-U, YEO-YA,
474 /* 0x3CA: */ 0, 1, // U+11A6..U+11A7-->[69..70] O-YA, O-YAE
475 #ifdef EXTENDED_HANGUL
476 /* 0x3D0: */ 0, 0, 0, // U+D7B0..U+D7B2-->[71..73] O-YEO, O-O-I, YO-A,
477 /* 0x3D9: */ 1, 0, 0, // U+D7B3..U+D7B5-->[74..76] YO-AE, YO-EO, U-YEO,
478 /* 0x3E2: */ 1, 1, 0, // U+D7B6..U+D7B8-->[77..79] U-I-I, YU-AE, YU-O,
479 /* 0x3EB: */ 0, 0, 1, // U+D7B9..U+D7BB-->[80..82] EU-A, EU-EO, EU-E,
480 /* 0x3F4: */ 0, 0, 1, // U+D7BC..U+D7BE-->[83..85] EU-O, I-YA-O, I-YAE,
481 /* 0x3FD: */ 0, 1, 0, // U+D7BF..U+D7C1-->[86..88] I-YEO, I-YE, I-O-I,
482 /* 0x406: */ 0, 0, 1, // U+D7C2..U+D7C4-->[89..91] I-YO, I-YU, I-I,
483 /* 0x40F: */ 0, 1, // U+D7C5..U+D7C6-->[92..93] ARAEA-A, ARAEA-E,
484 /* 0x415: */ -1 // Mark end of list of vowels.
485 #else
486 /* 0x310: */ -1 // Mark end of list of vowels.
487 #endif
488  };
489 
490 
491  if (vowel >= 0 && vowel < TOTAL_JUNG) {
492  retval = wide_vowel [vowel];
493  }
494  else {
495  retval = 0;
496  }
497 
498 
499  return retval;
500 }
501 
502 
503 /**
504  @brief Return the Johab 6/3/1 jungseong variation.
505 
506  This function takes the two or three (if jongseong is included)
507  letters that comprise a syllable and determine the variation
508  of the vowel (jungseong).
509 
510  Each jungseong has 3 variations:
511 
512  Variation Occurrence
513  --------- ----------
514  0 Jungseong with only chungseong (no jungseong).
515  1 Jungseong with chungseong and jungseong (except nieun).
516  2 Jungseong with chungseong and jungseong nieun.
517 
518  @param[in] choseong The 1st letter in the syllable.
519  @param[in] jungseong The 2nd letter in the syllable.
520  @param[in] jongseong The 3rd letter in the syllable.
521  @return The jungseong variation, 0 to 2.
522 */
523 inline int
524 jung_variation (int choseong, int jungseong, int jongseong) {
525  int jung_variation; /* Return value */
526 
527  if (jungseong < 0) {
528  jung_variation = -1;
529  }
530  else {
531  jung_variation = 0;
532  if (jongseong >= 0) {
533  if (jongseong == 3)
534  jung_variation = 2; /* Vowel for final Nieun. */
535  else
536  jung_variation = 1;
537  }
538  }
539 
540 
541  return jung_variation;
542 }
543 
544 
545 /**
546  @brief Return the Johab 6/3/1 jongseong variation.
547 
548  There is only one jongseong variation, so this function
549  always returns 0. It is a placeholder function for
550  possible future adaptation to other johab encodings.
551 
552  @param[in] choseong The 1st letter in the syllable.
553  @param[in] jungseong The 2nd letter in the syllable.
554  @param[in] jongseong The 3rd letter in the syllable.
555  @return The jongseong variation, always 0.
556 */
557 inline int
558 jong_variation (int choseong, int jungseong, int jongseong) {
559 
560  return 0; /* There is only one Jongseong variation. */
561 }
562 
563 
564 /**
565  @brief Given letters in a Hangul syllable, return a glyph.
566 
567  This function returns a glyph bitmap comprising up to three
568  Hangul letters that form a syllable. It reads the three
569  component letters (choseong, jungseong, and jungseong),
570  then calls a function that determines the appropriate
571  variation of each letter, returning the letter bitmap locations
572  in the glyph array. Then these letter bitmaps are combined
573  with a logical OR operation to produce a final bitmap,
574  which forms a 16 row by 16 column bitmap glyph.
575 
576  @param[in] choseong The 1st letter in the composite glyph.
577  @param[in] jungseong The 2nd letter in the composite glyph.
578  @param[in] jongseong The 3rd letter in the composite glyph.
579  @param[in] hangul_base The glyphs read from the "hangul_base.hex" file.
580  @return syllable The composite syllable, as a 16 by 16 pixel bitmap.
581 */
582 void
583 hangul_syllable (int choseong, int jungseong, int jongseong,
584  unsigned char hangul_base[][32], unsigned char *syllable) {
585 
586  int i; /* loop variable */
587  int cho_hex, jung_hex, jong_hex;
588  unsigned char glyph_byte;
589 
590 
591  hangul_hex_indices (choseong, jungseong, jongseong,
592  &cho_hex, &jung_hex, &jong_hex);
593 
594  for (i = 0; i < 32; i++) {
595  glyph_byte = hangul_base [cho_hex][i];
596  glyph_byte |= hangul_base [jung_hex][i];
597  if (jong_hex >= 0) glyph_byte |= hangul_base [jong_hex][i];
598  syllable[i] = glyph_byte;
599  }
600 
601  return;
602 }
603 
604 
605 /**
606  @brief See if two glyphs overlap.
607 
608  @param[in] glyph1 The first glyph, as a 16-row bitmap.
609  @param[in] glyph2 The second glyph, as a 16-row bitmap.
610  @return 0 if no overlaps between glyphs, 1 otherwise.
611 */
612 int
613 glyph_overlap (unsigned *glyph1, unsigned *glyph2) {
614  int overlaps; /* Return value; 0 if no overlaps, -1 if overlaps. */
615  int i;
616 
617  /* Check for overlaps between the two glyphs. */
618 
619  i = 0;
620  do {
621  overlaps = (glyph1[i] & glyph2[i]) != 0;
622  i++;
623  } while (i < 16 && overlaps == 0);
624 
625  return overlaps;
626 }
627 
628 
629 /**
630  @brief Combine two glyphs into one glyph.
631 
632  @param[in] glyph1 The first glyph to overlap.
633  @param[in] glyph2 The second glyph to overlap.
634  @param[out] combined_glyph The returned combination glyph.
635 */
636 void
637 combine_glyphs (unsigned *glyph1, unsigned *glyph2,
638  unsigned *combined_glyph) {
639  int i;
640 
641  for (i = 0; i < 16; i++)
642  combined_glyph [i] = glyph1 [i] | glyph2 [i];
643 
644  return;
645 }
646 
647 
648 /**
649  @brief Print one glyph in Unifont hexdraw plain text style.
650 
651  @param[in] fp The file pointer for output.
652  @param[in] codept The Unicode code point to print with the glyph.
653  @param[in] this_glyph The 16-row by 16-column glyph to print.
654 */
655 void
656 print_glyph_txt (FILE *fp, unsigned codept, unsigned *this_glyph) {
657  int i;
658  unsigned mask;
659 
660 
661  fprintf (fp, "%04X:", codept);
662 
663  /* for each this_glyph row */
664  for (i = 0; i < 16; i++) {
665  mask = 0x8000;
666  fputc ('\t', fp);
667  while (mask != 0x0000) {
668  if (mask & this_glyph [i]) {
669  fputc ('#', fp);
670  }
671  else {
672  fputc ('-', fp);
673  }
674  mask >>= 1; /* shift to next bit in this_glyph row */
675  }
676  fputc ('\n', fp);
677  }
678  fputc ('\n', fp);
679 
680  return;
681 }
682 
683 
684 /**
685  @brief Print one glyph in Unifont hexdraw hexadecimal string style.
686 
687  @param[in] fp The file pointer for output.
688  @param[in] codept The Unicode code point to print with the glyph.
689  @param[in] this_glyph The 16-row by 16-column glyph to print.
690 */
691 void
692 print_glyph_hex (FILE *fp, unsigned codept, unsigned *this_glyph) {
693 
694  int i;
695 
696 
697  fprintf (fp, "%04X:", codept);
698 
699  /* for each this_glyph row */
700  for (i = 0; i < 16; i++) {
701  fprintf (fp, "%04X", this_glyph[i]);
702  }
703  fputc ('\n', fp);
704 
705  return;
706 }
707 
708 
709 /**
710  @brief Convert Hangul Jamo choseong, jungseong, and jongseong into a glyph.
711 
712  @param[in] glyph_table The collection of all jamo glyphs.
713  @param[in] jamo The Unicode code point, 0 or 0x1100..0x115F.
714  @param[out] jamo_glyph The output glyph, 16 columns in each of 16 rows.
715 */
716 void
717 one_jamo (unsigned glyph_table [MAX_GLYPHS][16],
718  unsigned jamo, unsigned *jamo_glyph) {
719 
720  int i; /* Loop variable */
721  int glyph_index; /* Location of glyph in "hangul-base.hex" array */
722 
723 
724  /* If jamo is invalid range, use blank glyph, */
725  if (jamo >= 0x1100 && jamo <= 0x11FF) {
726  glyph_index = jamo - 0x1100 + JAMO_HEX;
727  }
728  else if (jamo >= 0xA960 && jamo <= 0xA97F) {
729  glyph_index = jamo - 0xA960 + JAMO_EXTA_HEX;
730  }
731  else if (jamo >= 0xD7B0 && jamo <= 0xD7FF) {
732  glyph_index = jamo - 0x1100 + JAMO_EXTB_HEX;
733  }
734  else {
735  glyph_index = 0;
736  }
737 
738  for (i = 0; i < 16; i++) {
739  jamo_glyph [i] = glyph_table [glyph_index] [i];
740  }
741 
742  return;
743 }
744 
745 
746 /**
747  @brief Convert Hangul Jamo choseong, jungseong, and jongseong into a glyph.
748 
749  This function converts input Hangul choseong, jungseong, and jongseong
750  Unicode code triplets into a Hangul syllable. Any of those with an
751  out of range code point are assigned a blank glyph for combining.
752 
753  This function performs the following steps:
754 
755  1) Determine the sequence number of choseong, jungseong,
756  and jongseong, from 0 to the total number of choseong,
757  jungseong, or jongseong, respectively, minus one. The
758  sequence for each is as follows:
759 
760  a) Choseong: Unicode code points of U+1100..U+115E
761  and then U+A960..U+A97C.
762 
763  b) Jungseong: Unicode code points of U+1161..U+11A7
764  and then U+D7B0..U+D7C6.
765 
766  c) Jongseong: Unicode code points of U+11A8..U+11FF
767  and then U+D7CB..U+D7FB.
768 
769  2) From the choseong, jungseong, and jongseong sequence number,
770  determine the variation of choseong and jungseong (there is
771  only one jongseong variation, although it is shifted right
772  by one column for some vowels with a pair of long vertical
773  strokes on the right side).
774 
775  3) Convert the variation numbers for the three syllable
776  components to index locations in the glyph array.
777 
778  4) Combine the glyph array glyphs into a syllable.
779 
780  @param[in] glyph_table The collection of all jamo glyphs.
781  @param[in] cho The choseong Unicode code point, 0 or 0x1100..0x115F.
782  @param[in] jung The jungseong Unicode code point, 0 or 0x1160..0x11A7.
783  @param[in] jong The jongseong Unicode code point, 0 or 0x11A8..0x11FF.
784  @param[out] combined_glyph The output glyph, 16 columns in each of 16 rows.
785 */
786 void
787 combined_jamo (unsigned glyph_table [MAX_GLYPHS][16],
788  unsigned cho, unsigned jung, unsigned jong,
789  unsigned *combined_glyph) {
790 
791  int i; /* Loop variable. */
792  int cho_num, jung_num, jong_num;
793  int cho_group, jung_group, jong_group;
794  int cho_index, jung_index, jong_index;
795 
796  unsigned tmp_glyph[16]; /* Hold shifted jongsung for wide vertical vowel. */
797 
798  int cho_variation (int choseong, int jungseong, int jongseong);
799 
800  void combine_glyphs (unsigned *glyph1, unsigned *glyph2,
801  unsigned *combined_glyph);
802 
803 
804  /* Choose a blank glyph for each syllalbe by default. */
805  cho_index = jung_index = jong_index = 0x000;
806 
807  /*
808  Convert Unicode code points to jamo sequence number
809  of each letter, or -1 if letter is not in valid range.
810  */
811  if (cho >= 0x1100 && cho <= 0x115E)
812  cho_num = cho - CHO_UNICODE_START;
813  else if (cho >= CHO_EXTA_UNICODE_START &&
816  else
817  cho_num = -1;
818 
819  if (jung >= 0x1161 && jung <= 0x11A7)
820  jung_num = jung - JUNG_UNICODE_START;
821  else if (jung >= JUNG_EXTB_UNICODE_START &&
823  jung_num = jung - JUNG_EXTB_UNICODE_START + NJUNG_MODERN + NJUNG_ANCIENT;
824  else
825  jung_num = -1;
826 
827  if (jong >= 0x11A8 && jong <= 0x11FF)
828  jong_num = jong - JONG_UNICODE_START;
829  else if (jong >= JONG_EXTB_UNICODE_START &&
831  jong_num = jong - JONG_EXTB_UNICODE_START + NJONG_MODERN + NJONG_ANCIENT;
832  else
833  jong_num = -1;
834 
835  /*
836  Choose initial consonant (choseong) variation based upon
837  the vowel (jungseong) if both are specified.
838  */
839  if (cho_num < 0) {
840  cho_index = cho_group = 0; /* Use blank glyph for choseong. */
841  }
842  else {
843  if (jung_num < 0 && jong_num < 0) { /* Choseong is by itself. */
844  cho_group = 0;
845  if (cho_index < (NCHO_MODERN + NCHO_ANCIENT))
846  cho_index = cho_num + JAMO_HEX;
847  else /* Choseong is in Hangul Jamo Extended-A range. */
848  cho_index = cho_num - (NCHO_MODERN + NCHO_ANCIENT)
849  + JAMO_EXTA_HEX;
850  }
851  else {
852  if (jung_num >= 0) { /* Valid jungseong with choseong. */
853  cho_group = cho_variation (cho_num, jung_num, jong_num);
854  }
855  else { /* Invalid vowel; see if final consonant is valid. */
856  /*
857  If initial consonant and final consonant are specified,
858  set cho_group to 4, which is the group tha would apply
859  to a horizontal-only vowel such as Hangul "O", so the
860  consonant appears full-width.
861  */
862  cho_group = 0;
863  if (jong_num >= 0) {
864  cho_group = 4;
865  }
866  }
867  cho_index = CHO_HEX + CHO_VARIATIONS * cho_num +
868  cho_group;
869  } /* Choseong combined with jungseong and/or jongseong. */
870  } /* Valid choseong. */
871 
872  /*
873  Choose vowel (jungseong) variation based upon the choseong
874  and jungseong.
875  */
876  jung_index = jung_group = 0; /* Use blank glyph for jungseong. */
877 
878  if (jung_num >= 0) {
879  if (cho_num < 0 && jong_num < 0) { /* Jungseong is by itself. */
880  jung_group = 0;
881  jung_index = jung_num + JUNG_UNICODE_START;
882  }
883  else {
884  if (jong_num >= 0) { /* If there is a final consonant. */
885  if (jong_num == 3) /* Nieun; choose variation 3. */
886  jung_group = 2;
887  else
888  jung_group = 1;
889  } /* Valid jongseong. */
890  /* If valid choseong but no jongseong, choose jungseong variation 0. */
891  else if (cho_num >= 0)
892  jung_group = 0;
893  }
894  jung_index = JUNG_HEX + JUNG_VARIATIONS * jung_num + jung_group;
895  }
896 
897  /*
898  Choose final consonant (jongseong) based upon whether choseong
899  and/or jungseong are present.
900  */
901  if (jong_num < 0) {
902  jong_index = jong_group = 0; /* Use blank glyph for jongseong. */
903  }
904  else { /* Valid jongseong. */
905  if (cho_num < 0 && jung_num < 0) { /* Jongseong is by itself. */
906  jong_group = 0;
907  jong_index = jung_num + 0x4A8;
908  }
909  else { /* There is only one jongseong variation if combined. */
910  jong_group = 0;
911  jong_index = JONG_HEX + JONG_VARIATIONS * jong_num +
912  jong_group;
913  }
914  }
915 
916  /*
917  Now that we know the index locations for choseong, jungseong, and
918  jongseong glyphs, combine them into one glyph.
919  */
920  combine_glyphs (glyph_table [cho_index], glyph_table [jung_index],
921  combined_glyph);
922 
923  if (jong_index > 0) {
924  /*
925  If the vowel has a vertical stroke that is one column
926  away from the right border, shift this jongseung right
927  by one column to line up with the rightmost vertical
928  stroke in the vowel.
929  */
930  if (is_wide_vowel (jung_num)) {
931  for (i = 0; i < 16; i++) {
932  tmp_glyph [i] = glyph_table [jong_index] [i] >> 1;
933  }
934  combine_glyphs (combined_glyph, tmp_glyph,
935  combined_glyph);
936  }
937  else {
938  combine_glyphs (combined_glyph, glyph_table [jong_index],
939  combined_glyph);
940  }
941  }
942 
943  return;
944 }
945 
Define constants and function prototypes for using Hangul glyphs.
#define NJONG_EXTB
Hangul Extended-B jongseong.
Definition: hangul.h:81
#define NCHO_EXTA
Hangul Extended-A choseong.
Definition: hangul.h:71
#define CHO_VARIATIONS
6 choseong variations
Definition: hangul.h:88
#define JONG_VARIATIONS
1 jongseong variation
Definition: hangul.h:90
#define NJONG_MODERN
28 modern Hangul Jamo jongseong
Definition: hangul.h:79
#define JAMO_EXTB_HEX
Start of U+D7B0..U+D7FF glyphs.
Definition: hangul.h:140
#define CHO_UNICODE_START
Modern Hangul choseong start.
Definition: hangul.h:50
#define JAMO_EXTA_HEX
Start of U+A960..U+A97F glyphs.
Definition: hangul.h:136
#define JUNG_EXTB_UNICODE_START
Hangul Extended-B jungseong start.
Definition: hangul.h:57
#define JUNG_HEX
Location of first jungseong (will be 0x2FB)
Definition: hangul.h:108
#define NCHO_ANCIENT
ancient Hangul Jamo choseong
Definition: hangul.h:70
#define MAXLINE
Length of maximum file input line.
Definition: hangul.h:33
#define CHO_EXTA_UNICODE_START
Hangul Extended-A choseong start.
Definition: hangul.h:52
#define NCHO_MODERN
19 modern Hangul Jamo choseong
Definition: hangul.h:69
#define JAMO_HEX
Start of U+1100..U+11FF glyphs.
Definition: hangul.h:132
#define JONG_UNICODE_START
Modern Hangul jongseong start.
Definition: hangul.h:60
#define NJUNG_MODERN
21 modern Hangul Jamo jungseong
Definition: hangul.h:74
#define JUNG_UNICODE_START
Modern Hangul jungseong start.
Definition: hangul.h:55
#define JUNG_VARIATIONS
3 jungseong variations
Definition: hangul.h:89
#define NJONG_ANCIENT
ancient Hangul Jamo jongseong
Definition: hangul.h:80
#define NJUNG_ANCIENT
ancient Hangul Jamo jungseong
Definition: hangul.h:75
#define CHO_HEX
Location of first choseong (location 0x0000 is a blank glyph)
Definition: hangul.h:96
#define JONG_EXTB_UNICODE_START
Hangul Extended-B jongseong start.
Definition: hangul.h:62
#define NJUNG_EXTB
Hangul Extended-B jungseong.
Definition: hangul.h:76
#define JONG_HEX
Location of first jongseong (will be 0x421)
Definition: hangul.h:120
#define MAX_GLYPHS
An OpenType font has at most 65536 glyphs.
Definition: hex2otf.c:85
void print_glyph_hex(FILE *fp, unsigned codept, unsigned *this_glyph)
Print one glyph in Unifont hexdraw hexadecimal string style.
void one_jamo(unsigned glyph_table[MAX_GLYPHS][16], unsigned jamo, unsigned *jamo_glyph)
Convert Hangul Jamo choseong, jungseong, and jongseong into a glyph.
void combined_jamo(unsigned glyph_table[MAX_GLYPHS][16], unsigned cho, unsigned jung, unsigned jong, unsigned *combined_glyph)
Convert Hangul Jamo choseong, jungseong, and jongseong into a glyph.
void print_glyph_txt(FILE *fp, unsigned codept, unsigned *this_glyph)
Print one glyph in Unifont hexdraw plain text style.
unsigned hangul_read_base8(FILE *infp, unsigned char base[][32])
Read hangul-base.hex file into a unsigned char array.
int is_wide_vowel(int vowel)
Whether vowel has rightmost vertical stroke to the right.
void hangul_hex_indices(int choseong, int jungseong, int jongseong, int *cho_index, int *jung_index, int *jong_index)
Determine index values to the bitmaps for a syllable's components.
void hangul_decompose(unsigned codept, int *initial, int *medial, int *final)
Decompose a Hangul Syllables code point into three letters.
int glyph_overlap(unsigned *glyph1, unsigned *glyph2)
See if two glyphs overlap.
void hangul_variations(int choseong, int jungseong, int jongseong, int *cho_var, int *jung_var, int *jong_var)
Determine the variations of each letter in a Hangul syllable.
void combine_glyphs(unsigned *glyph1, unsigned *glyph2, unsigned *combined_glyph)
Combine two glyphs into one glyph.
int jong_variation(int choseong, int jungseong, int jongseong)
Return the Johab 6/3/1 jongseong variation.
int cho_variation(int choseong, int jungseong, int jongseong)
Return the Johab 6/3/1 choseong variation for a syllable.
int jung_variation(int choseong, int jungseong, int jongseong)
Return the Johab 6/3/1 jungseong variation.
void hangul_syllable(int choseong, int jungseong, int jongseong, unsigned char hangul_base[][32], unsigned char *syllable)
Given letters in a Hangul syllable, return a glyph.
unsigned hangul_compose(int initial, int medial, int final)
Compose a Hangul syllable into a code point, or 0 if none exists.
unsigned hangul_read_base16(FILE *infp, unsigned base[][16])
Read hangul-base.hex file into a unsigned array.