(MPLIST_VAL_MANAGED_P): Delete this macro.
[m17n/m17n-lib.git] / src / mtext-lbrk.c
1 /* mtext-lbrk.c -- line break
2    Copyright (C) 2005
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
24 /*** @addtogroup m17nInternal
25      @{ */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "config.h"
32 #include "m17n.h"
33 #include "m17n-misc.h"
34 #include "internal.h"
35 #include "mtext.h"
36
37 enum LineBreakClass
38   {
39     LBC_OP, /* open */
40     LBC_CL, /* close */
41     LBC_QU, /* quotation */
42     LBC_GL, /* glue */
43     LBC_NS, /* no-start */
44     LBC_EX, /* exclamation/interrogation */
45     LBC_SY, /* Syntax (slash) */
46     LBC_IS, /* infix (numeric) separator */
47     LBC_PR, /* prefix */
48     LBC_PO, /* postfix */
49     LBC_NU, /* numeric */
50     LBC_AL, /* alphabetic */
51     LBC_ID, /* ideograph (atomic) */
52     LBC_IN, /* inseparable */
53     LBC_HY, /* hyphen */
54     LBC_BA, /* break after */
55     LBC_BB, /* break before */
56     LBC_B2, /* break both */
57     LBC_ZW, /* ZW space */
58     LBC_CM, /* combining mark */
59     LBC_WJ, /* word joiner */
60
61     /* used for 4.1 pair table */
62     LBC_H2, /* Hamgul 2 Jamo Syllable */
63     LBC_H3, /* Hangul 3 Jamo Syllable */
64     LBC_JL, /* Jamo leading consonant */
65     LBC_JV, /* Jamo vowel */
66     LBC_JT, /* Jamo trailing consonant */
67
68     /* These are not handled in the pair tables. */
69     LBC_SA, /* south (east) asian */
70     LBC_SP, /* space */
71     LBC_PS, /* paragraph and line separators */
72     LBC_BK, /* hard break (newline) */
73     LBC_CR, /* carriage return */
74     LBC_LF, /* line feed */
75     LBC_NL, /* next line */
76     LBC_CB, /* contingent break opportunity */
77     LBC_SG, /* surrogate */
78     LBC_AI, /* ambiguous */
79     LBC_XX, /* unknown */
80     LBC_MAX
81   };
82
83 enum LineBreakAction
84   {
85     LBA_DIRECT =                '_',
86     LBA_INDIRECT =              '%',
87     LBA_COMBINING_INDIRECT =    '#',
88     LBA_COMBINING_PROHIBITED =  '@',
89     LBA_PROHIBITED =            '^',
90     LBA_MAX
91   };
92
93 /* The pair table of line break actions.  */
94 static char *lba_pair_table[] =
95   /* OP GL SY PO ID BA ZW H2 JV
96       CL NS IS NU IN BB CM H3 JT
97        QU EX PR AL HY B2 WJ JL  */
98   { "^^^^^^^^^^^^^^^^^^^@^^^^^^", /* OP */
99     "_^%%^^^^_%____%%__^#^_____", /* CL */
100     "^^%%%^^^%%%%%%%%%%^#^%%%%%", /* QU */
101     "%^%%%^^^%%%%%%%%%%^#^%%%%%", /* GL */
102     "_^%%%^^^______%%__^#^_____", /* NS */
103     "_^%%%^^^______%%__^#^_____", /* EX */
104     "_^%%%^^^__%___%%__^#^_____", /* SY */
105     "_^%%%^^^__%%__%%__^#^_____", /* IS */
106     "%^%%%^^^__%%%_%%__^#^%%%%%", /* PR */
107     "_^%%%^^^______%%__^#^_____", /* PO */
108     "_^%%%^^^_%%%_%%%__^#^_____", /* NU */
109     "_^%%%^^^__%%_%%%__^#^_____", /* AL */
110     "_^%%%^^^_%___%%%__^#^_____", /* ID */
111     "_^%%%^^^_____%%%__^#^_____", /* IN */
112     "_^%%%^^^__%___%%__^#^_____", /* HY */
113     "_^%%%^^^______%%__^#^_____", /* BA */
114     "%^%%%^^^%%%%%%%%%%^#^%%%%%", /* BB */
115     "_^%%%^^^______%%_^^#^_____", /* B2 */
116     "__________________^_______", /* ZW */
117     "_^%%%^^^__%%_%%%__^#^_____", /* CM */
118     "%^%%%^^^%%%%%%%%%%^#^%%%%%", /* WJ */
119     "_^%%%^^^_%___%%%__^#^___%%", /* H2 */
120     "_^%%%^^^_%___%%%__^#^____%", /* H3 */
121     "_^%%%^^^_%___%%%__^#^%%%%_", /* JL */
122     "_^%%%^^^_%___%%%__^#^___%%", /* JV */
123     "_^%%%^^^_%___%%%__^#^____%"  /* JT */
124   };
125
126 static MCharTable *lbc_table;
127
128 /* Set LBC to enum LineBreakClass of the character at POS of MT
129    (length is LEN) while converting LBC_AI and LBC_XX to LBC_AL,
130    LBC_CB to LBC_B2, LBC_CR, LBC_LF, and LBC_NL to LBC_BK.  If POS is
131    out of range, set LBC to LBC_BK.  */
132
133 #define GET_LBC(LBC, MT, LEN, POS, OPTION)                              \
134   do {                                                                  \
135     if ((POS) < 0 || (POS) >= (LEN))                                    \
136       (LBC) = LBC_BK;                                                   \
137     else                                                                \
138       {                                                                 \
139         int c = mtext_ref_char ((MT), (POS));                           \
140         (LBC) = (enum LineBreakClass) mchartable_lookup (lbc_table, c); \
141         if ((LBC) == LBC_NL)                                            \
142           (LBC) = LBC_BK;                                               \
143         else if ((LBC) == LBC_AI)                                       \
144           (LBC) = ((OPTION) & MTEXT_LBO_AI_AS_ID) ? LBC_ID : LBC_AL;    \
145         else if (! ((OPTION) & MTEXT_LBO_KOREAN_SP)                     \
146                  && (LBC) >= LBC_H2 && (LBC) <= LBC_JT)                 \
147           (LBC) = LBC_AL;                                               \
148         else if ((LBC) == LBC_CB)                                       \
149           (LBC) = LBC_B2;                                               \
150         else if ((LBC) == LBC_XX)                                       \
151           (LBC) = LBC_AL;                                               \
152       }                                                                 \
153   } while (0)
154
155
156 /*** @} */
157 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
158
159 \f
160 /* External API */
161
162 /*** @addtogroup m17nMtext */
163 /*** @{ */
164 /*=*/
165
166 int
167 mtext_line_break (MText *mt, int pos, int option, int *after)
168 {
169   int break_before, break_after;
170   int len = mtext_len (mt);
171   enum LineBreakClass lbc;
172   enum LineBreakClass Blbc, Albc; /* B(efore) and A(fter) lbcs.  */
173   int Bpos, Apos;                 /* B(efore) and A(fter) positions.  */
174   enum LineBreakAction action;
175   
176   if (pos >= len)
177     {
178       /* The end of text is an explicit break position.  */
179       if (after)
180         *after = pos;
181       return pos;
182     }
183
184   if (! lbc_table)
185     {
186       MSymbol key = mchar_define_property ("linebreak", Minteger);
187
188       lbc_table = mchar_get_prop_table (key, NULL);
189     }
190
191   GET_LBC (lbc, mt, len, pos, option);
192   Apos = pos;
193   Albc = lbc;
194   if (Albc == LBC_SP)
195     {
196       if (option & MTEXT_LBO_SP_CM)
197         {
198           GET_LBC (Albc, mt, len, Apos + 1, option);
199           Albc = (Albc == LBC_CM) ? LBC_ID : LBC_SP;
200         }
201       while (Albc == LBC_SP)
202         {
203           Apos--;
204           GET_LBC (Albc, mt, len, Apos, option);
205         }
206     }
207   if ((option & MTEXT_LBO_SP_CM) && (Albc == LBC_CM))
208     {
209       Apos--;
210       GET_LBC (Albc, mt, len, Apos, option);
211       if (Albc == LBC_SP)
212         Albc = LBC_ID;
213       else
214         Apos++, Albc = LBC_CM;
215     }
216
217   if (Albc == LBC_CR)
218     Albc = LBC_BK;
219   else if (Albc == LBC_LF)
220     {
221       GET_LBC (Albc, mt, len, Apos - 1, option);
222       if (Albc == LBC_CR)
223         Apos--;
224       Albc = LBC_BK;
225     }
226   else if (Albc == LBC_SA)
227     Albc = mtext__word_segment (mt, Apos, &Apos, NULL) > 0 ? LBC_BB : LBC_AL;
228   Bpos = Apos;
229   /* After exiting from the following loop, if Apos is positive, it is
230      the previous (including POS) break position.  */
231   while (Apos > 0)
232     {
233       int indirect;
234       int next = -1;
235
236       /* Now Bpos == Apos.  */
237       do {
238         Bpos--;
239         GET_LBC (Blbc, mt, len, Bpos, option);
240       } while (Blbc == LBC_SP);
241
242       if (Blbc == LBC_BK || Blbc == LBC_LF || Blbc == LBC_CR)
243         {
244           /* Explicit break.  */
245           break;
246         }
247
248       indirect = Bpos + 1 < Apos;
249
250       if (Blbc == LBC_CM)
251         {
252           do {
253               Bpos--;
254               GET_LBC (Blbc, mt, len, Bpos, option);
255           } while (Blbc == LBC_CM);
256           if ((option & MTEXT_LBO_SP_CM) && (Blbc == LBC_SP))
257             Blbc = LBC_ID;
258           else if (Blbc == LBC_SP || Blbc == LBC_ZW
259                    || Blbc == LBC_BK || Blbc == LBC_LF || Blbc == LBC_CR)
260             {
261               Blbc = LBC_AL;
262               Bpos++;
263             }
264         }                  
265       if (Blbc == LBC_SA)
266         {
267           mtext__word_segment (mt, Bpos, &next, NULL);
268           Blbc = LBC_AL;
269         }
270
271       if (Albc != LBC_BK)
272         {
273           action = lba_pair_table[Blbc][Albc];
274           if (action == LBA_DIRECT)
275             break;
276           else if (action == LBA_INDIRECT)
277             {
278               if (indirect)
279                 break;
280             }
281           else if (action == LBA_COMBINING_INDIRECT)
282             {
283               if (indirect)
284                 break;
285             }
286         }
287       if (next >= 0)
288         Apos = next, Albc = LBC_BB;
289       else
290         Apos = Bpos, Albc = Blbc;
291     }
292   break_before = Apos;
293   if (break_before > 0)
294     {
295       if (! after)
296         return break_before;
297       if (break_before == pos)
298         {
299           if (after)
300             *after = break_before;
301           return break_before;
302         }
303     }
304
305   /* Now find a break position after POS.  */
306   break_after = 0;
307   Bpos = pos;
308   Blbc = lbc;
309   if (Blbc == LBC_CM)
310     {
311       do {
312         Bpos--;
313         GET_LBC (Blbc, mt, len, Bpos, option);
314       } while (Blbc == LBC_CM);
315       if (Blbc == LBC_SP || Blbc == LBC_ZW
316           || Blbc == LBC_BK || Blbc == LBC_LF || Blbc == LBC_CR)
317         {
318           if ((Blbc == LBC_SP) && (option & MTEXT_LBO_SP_CM))
319             Blbc = LBC_ID;
320           else
321             Blbc = LBC_AL;
322         }
323       Bpos = pos;
324     }
325   if (Blbc == LBC_SA)
326     {
327       mtext__word_segment (mt, Bpos, NULL, &Bpos);
328       Blbc = LBC_AL;
329     }
330   else if (Blbc == LBC_SP)
331     {
332       if (option & MTEXT_LBO_SP_CM)
333         {
334           GET_LBC (Blbc, mt, len, Bpos + 1, option);
335           if (Blbc == LBC_CM)
336             Blbc = LBC_ID, Bpos++;
337           else
338             Blbc = LBC_SP;
339         }
340       while (Blbc == LBC_SP)
341         {
342           Bpos--;
343           GET_LBC (Blbc, mt, len, Bpos, option);
344         }
345       if (Bpos < 0)
346         Bpos = pos;
347     }
348   Apos = Bpos;
349   /* After exiting from the following loop, if Apos is positive, it is
350      the next break position.  */
351   while (1)
352     {
353       int indirect;
354       int next = -1;
355
356       /* Now Bpos == Apos.  */
357       if (Blbc == LBC_LF || Blbc == LBC_BK || Blbc == LBC_CR)
358         {
359           Apos++;
360           if (Blbc == LBC_CR)
361             {
362               GET_LBC (Blbc, mt, len, Bpos + 1, option);
363               if (Blbc == LBC_LF)
364                 Apos++;
365             }
366           break;
367         }
368
369       do {
370         Apos++;
371         GET_LBC (Albc, mt, len, Apos, option);
372       } while (Albc == LBC_SP);
373       
374       if (Blbc == LBC_SP)
375         break;
376
377       if (Apos == len)
378         /* Explicit break at the end of text.  */
379         break;
380
381       indirect = Bpos + 1 < Apos;
382
383       if (Albc == LBC_SA)
384         Albc = mtext__word_segment (mt, Apos, NULL, &next) ? LBC_BB : LBC_AL;
385
386       action = lba_pair_table[Blbc][Albc];
387       if (action == LBA_DIRECT)
388         /* Direct break at Apos.  */
389         break;
390       else if (action == LBA_INDIRECT)
391         {
392           if (indirect)
393             break;
394         }
395       else if (action == LBA_COMBINING_INDIRECT)
396         {
397           if (indirect)
398             {
399               if (option & MTEXT_LBO_SP_CM)
400                 Apos--;
401               break;
402             }
403         }
404       if (next >= 0)
405         Bpos = next, Blbc = LBC_AL;
406       else
407         {
408           Bpos = Apos;
409           if (Albc != LBC_CM)
410             Blbc = Albc;
411         }
412     }
413   break_after = Apos;
414   if (after)
415     *after = break_after;
416
417   return (break_before > 0 ? break_before : break_after);
418 }
419
420 /*** @} */ 
421
422 /*
423   Local Variables:
424   coding: euc-japan
425   End:
426 */