PageRenderTime 714ms CodeModel.GetById 201ms app.highlight 297ms RepoModel.GetById 206ms app.codeStats 0ms

/src/ftk_text_layout_bidi.c

http://ftk.googlecode.com/
C | 992 lines | 778 code | 110 blank | 104 comment | 156 complexity | f7ce9dd4369cf4159ea55b6ef4f8f06c MD5 | raw file
  1/*
  2 * File: ftk_text_layout_bidi.c
  3 *
  4 * Author:  Li XianJing <xianjimli@hotmail.com>
  5 * Brief: interface for text layout(bidi,line break,shape join).
  6 *
  7 * Copyright (c) 2009 - 2010  Li XianJing <xianjimli@hotmail.com>
  8 *
  9 * Licensed under the Academic Free License version 2.1
 10 *
 11 * This program is free software; you can redistribute it and/or modify
 12 * it under the terms of the GNU General Public License as published by
 13 * the Free Software Foundation; either version 2 of the License, or
 14 * (at your option) any later version.
 15 *
 16 * This program is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 19 * GNU General Public License for more details.
 20 *
 21 * You should have received a copy of the GNU General Public License
 22 * along with this program; if not, write to the Free Software
 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 24 */
 25
 26/*
 27 * History:
 28 * ================================================================
 29 * 2010-07-18 Li XianJing <xianjimli@hotmail.com> created
 30 *
 31 */
 32
 33/*
 34 * XXX: Part of the code in this file come from microwindow, the author has its copyright.
 35 */
 36
 37/*
 38 * Copyright (c) 2000, 2002, 2003, 2005 Greg Haerr <greg@censoft.com>
 39 * Portions Copyright (c) 2002 by Koninklijke Philips Electronics N.V.
 40 */
 41
 42#include <stdio.h>
 43#include <stdlib.h>
 44#include <string.h>
 45#include "ftk_text_layout.h"
 46
 47/**
 48 * UTF-8 to UTF-16 conversion.  Surrogates are handeled properly, e.g.
 49 * a single 4-byte UTF-8 character is encoded into a surrogate pair.
 50 * On the other hand, if the UTF-8 string contains surrogate values, this
 51 * is considered an error and returned as such.
 52 *
 53 * The destination array must be able to hold as many Unicode-16 characters
 54 * as there are ASCII characters in the UTF-8 string.  This in case all UTF-8
 55 * characters are ASCII characters.  No more will be needed.
 56 *
 57 * This function will also accept Java's variant of UTF-8.  This encodes
 58 * U+0000 as two characters rather than one, so the UTF-8 does not contain
 59 * any zeroes.
 60 *
 61 * @author Copyright (c) 2000 Morten Rolland, Screen Media
 62 *
 63 * @param utf8      Input string in UTF8 format.
 64 * @param cc        Number of bytes to convert.
 65 * @param unicode16 Destination buffer.
 66 * @return          Number of characters converted, or -1 if input is not
 67 *                  valid UTF8.
 68 */
 69static int
 70utf8_to_utf16(const unsigned char *utf8, int cc, unsigned short *unicode16)
 71{
 72	int count = 0;
 73	unsigned char c0, c1;
 74	unsigned long scalar;
 75
 76	while(--cc >= 0) {
 77		c0 = *utf8++;
 78		/*DPRINTF("Trying: %02x\n",c0);*/
 79
 80		if ( c0 < 0x80 ) {
 81			/* Plain ASCII character, simple translation :-) */
 82			*unicode16++ = c0;
 83			count++;
 84			continue;
 85		}
 86
 87		if ( (c0 & 0xc0) == 0x80 )
 88			/* Illegal; starts with 10xxxxxx */
 89			return -1;
 90
 91		/* c0 must be 11xxxxxx if we get here => at least 2 bytes */
 92		scalar = c0;
 93		if(--cc < 0)
 94			return -1;
 95		c1 = *utf8++;
 96		/*DPRINTF("c1=%02x\n",c1);*/
 97		if ( (c1 & 0xc0) != 0x80 )
 98			/* Bad byte */
 99			return -1;
100		scalar <<= 6;
101		scalar |= (c1 & 0x3f);
102
103		if ( !(c0 & 0x20) ) {
104			/* Two bytes UTF-8 */
105			if ( (scalar != 0) && (scalar < 0x80) )
106				return -1;	/* Overlong encoding */
107			*unicode16++ = scalar & 0x7ff;
108			count++;
109			continue;
110		}
111
112		/* c0 must be 111xxxxx if we get here => at least 3 bytes */
113		if(--cc < 0)
114			return -1;
115		c1 = *utf8++;
116		/*DPRINTF("c1=%02x\n",c1);*/
117		if ( (c1 & 0xc0) != 0x80 )
118			/* Bad byte */
119			return -1;
120		scalar <<= 6;
121		scalar |= (c1 & 0x3f);
122
123		if ( !(c0 & 0x10) ) {
124			/*DPRINTF("####\n");*/
125			/* Three bytes UTF-8 */
126			if ( scalar < 0x800 )
127				return -1;	/* Overlong encoding */
128			if ( scalar >= 0xd800 && scalar < 0xe000 )
129				return -1;	/* UTF-16 high/low halfs */
130			*unicode16++ = scalar & 0xffff;
131			count++;
132			continue;
133		}
134
135		/* c0 must be 1111xxxx if we get here => at least 4 bytes */
136		c1 = *utf8++;
137		if(--cc < 0)
138			return -1;
139		/*DPRINTF("c1=%02x\n",c1);*/
140		if ( (c1 & 0xc0) != 0x80 )
141			/* Bad byte */
142			return -1;
143		scalar <<= 6;
144		scalar |= (c1 & 0x3f);
145
146		if ( !(c0 & 0x08) ) {
147			/* Four bytes UTF-8, needs encoding as surrogates */
148			if ( scalar < 0x10000 )
149				return -1;	/* Overlong encoding */
150			scalar -= 0x10000;
151			*unicode16++ = ((scalar >> 10) & 0x3ff) + 0xd800;
152			*unicode16++ = (scalar & 0x3ff) + 0xdc00;
153			count += 2;
154			continue;
155		}
156
157		return -1;	/* No support for more than four byte UTF-8 */
158	}
159	return count;
160}
161
162/* 
163 * warning: the length of output string may exceed six x the length of the input 
164 */ 
165static int
166uc16_to_utf8(const unsigned short *us, int cc, unsigned char *s)
167{
168	int i;
169	unsigned char *t = s;
170	unsigned short uc16;
171	
172	for (i = 0; i < cc; i++) {
173		uc16 = us[i];
174		if (uc16 <= 0x7F) { 
175			*t++ = (char) uc16;
176		} else if (uc16 <= 0x7FF) {
177			*t++ = 0xC0 | (unsigned char) ((uc16 >> 6) & 0x1F); /* upper 5 bits */
178			*t++ = 0x80 | (unsigned char) (uc16 & 0x3F);        /* lower 6 bits */
179		} else {
180			*t++ = 0xE0 | (unsigned char) ((uc16 >> 12) & 0x0F);/* upper 4 bits */
181			*t++ = 0x80 | (unsigned char) ((uc16 >> 6) & 0x3F); /* next 6 bits */
182			*t++ = 0x80 | (unsigned char) (uc16 & 0x3F);        /* lowest 6 bits */
183		}
184	}
185	*t = 0;
186	return (t - s);
187}
188
189/* 
190   UTF8 utility: 
191   This map return the expected count of bytes based on the first char 
192 */
193const char utf8_len_map[256] = {
194  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
195  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
196  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
197  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
198  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
199  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
200  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
201  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
202};
203
204#ifdef DEBUG_TEXT_SHAPING
205/*
206 *  Return the number of character (not byte) of UTF-8 string
207 */
208int utf8_nchar ( const char *str )
209{
210	int n = 0;
211	int al = strlen ( str );
212
213	while ( n < al )
214		n += utf8_len_map[(unsigned char)str[n]];
215	return (n < al) ? n : al;
216}
217
218#endif	
219
220#define HAVE_SHAPEJOINING_SUPPORT 1
221
222#if HAVE_SHAPEJOINING_SUPPORT
223typedef struct char_shaped {
224	unsigned short isolated;
225	unsigned short initial;
226	unsigned short medial;
227	unsigned short final;
228} chr_shpjoin_t;
229
230/* This table start from a base of 0x0621, up to 0x06D3 */
231
232#define SHAPED_TABLE_START	0x0621
233#define SHAPED_TABLE_TOP	0x06D3
234
235static const chr_shpjoin_t shaped_table[] =
236{
237	/*  base       s       i       m       f */
238	{ /*0x0621*/ 0xFE80, 0x0000, 0x0000, 0x0000, },  /* HAMZA */
239	{ /*0x0622*/ 0xFE81, 0x0000, 0x0000, 0xFE82, },  /* ALEF_MADDA */
240	{ /*0x0623*/ 0xFE83, 0x0000, 0x0000, 0xFE84, },  /* ALEF_HAMZA_ABOVE */
241	{ /*0x0624*/ 0xFE85, 0x0000, 0x0000, 0xFE86, },  /* WAW_HAMZA */
242	{ /*0x0625*/ 0xFE87, 0x0000, 0x0000, 0xFE88, },  /* ALEF_HAMZA_BELOW */
243	{ /*0x0626*/ 0xFE89, 0xFE8B, 0xFE8C, 0xFE8A, },  /* YEH_HAMZA */
244	{ /*0x0627*/ 0xFE8D, 0x0000, 0x0000, 0xFE8E, },  /* ALEF */
245	{ /*0x0628*/ 0xFE8F, 0xFE91, 0xFE92, 0xFE90, },  /* BEH */
246	{ /*0x0629*/ 0xFE93, 0x0000, 0x0000, 0xFE94, },  /* TEH_MARBUTA */
247	{ /*0x062A*/ 0xFE95, 0xFE97, 0xFE98, 0xFE96, },  /* TEH */
248	{ /*0x062B*/ 0xFE99, 0xFE9B, 0xFE9C, 0xFE9A, },  /* THEH */
249	{ /*0x062C*/ 0xFE9D, 0xFE9F, 0xFEA0, 0xFE9E, },  /* JEEM */
250	{ /*0x062D*/ 0xFEA1, 0xFEA3, 0xFEA4, 0xFEA2, },  /* HAH */
251	{ /*0x062E*/ 0xFEA5, 0xFEA7, 0xFEA8, 0xFEA6, },  /* KHAH */
252	{ /*0x062F*/ 0xFEA9, 0x0000, 0x0000, 0xFEAA, },  /* DAL */
253	{ /*0x0630*/ 0xFEAB, 0x0000, 0x0000, 0xFEAC, },  /* THAL */
254	{ /*0x0631*/ 0xFEAD, 0x0000, 0x0000, 0xFEAE, },  /* REH */
255	{ /*0x0632*/ 0xFEAF, 0x0000, 0x0000, 0xFEB0, },  /* ZAIN */
256	{ /*0x0633*/ 0xFEB1, 0xFEB3, 0xFEB4, 0xFEB2, },  /* SEEN */
257	{ /*0x0634*/ 0xFEB5, 0xFEB7, 0xFEB8, 0xFEB6, },  /* SHEEN */
258	{ /*0x0635*/ 0xFEB9, 0xFEBB, 0xFEBC, 0xFEBA, },  /* SAD */
259	{ /*0x0636*/ 0xFEBD, 0xFEBF, 0xFEC0, 0xFEBE, },  /* DAD */
260	{ /*0x0637*/ 0xFEC1, 0xFEC3, 0xFEC4, 0xFEC2, },  /* TAH */
261	{ /*0x0638*/ 0xFEC5, 0xFEC7, 0xFEC8, 0xFEC6, },  /* ZAH */
262	{ /*0x0639*/ 0xFEC9, 0xFECB, 0xFECC, 0xFECA, },  /* AIN */
263	{ /*0x063A*/ 0xFECD, 0xFECF, 0xFED0, 0xFECE, },  /* GHAIN */
264	{ /*0x063B*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
265	{ /*0x063C*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
266	{ /*0x063D*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
267	{ /*0x063E*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
268	{ /*0x063F*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
269	{ /*0x0640*/ 0x0640, 0x0640, 0x0640, 0x0640, },  /* TATWEEL */
270	{ /*0x0641*/ 0xFED1, 0xFED3, 0xFED4, 0xFED2, },  /* FEH */
271	{ /*0x0642*/ 0xFED5, 0xFED7, 0xFED8, 0xFED6, },  /* QAF */
272	{ /*0x0643*/ 0xFED9, 0xFEDB, 0xFEDC, 0xFEDA, },  /* KAF */
273	{ /*0x0644*/ 0xFEDD, 0xFEDF, 0xFEE0, 0xFEDE, },  /* LAM */
274	{ /*0x0645*/ 0xFEE1, 0xFEE3, 0xFEE4, 0xFEE2, },  /* MEEM */
275	{ /*0x0646*/ 0xFEE5, 0xFEE7, 0xFEE8, 0xFEE6, },  /* NOON */
276	{ /*0x0647*/ 0xFEE9, 0xFEEB, 0xFEEC, 0xFEEA, },  /* HEH */
277	{ /*0x0648*/ 0xFEED, 0x0000, 0x0000, 0xFEEE, },  /* WAW */
278	{ /*0x0649*/ 0xFEEF, 0xFBE8, 0xFBE9, 0xFEF0, },  /* ALEF_MAKSURA */
279	{ /*0x064A*/ 0xFEF1, 0xFEF3, 0xFEF4, 0xFEF2, },  /* YEH */
280	{ /*0x064B*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
281	{ /*0x064C*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
282	{ /*0x064D*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
283	{ /*0x064E*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
284	{ /*0x064F*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
285	{ /*0x0650*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
286	{ /*0x0651*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
287	{ /*0x0652*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
288	{ /*0x0653*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
289	{ /*0x0654*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
290	{ /*0x0655*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
291	{ /*0x0656*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
292	{ /*0x0657*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
293	{ /*0x0658*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
294	{ /*0x0659*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
295	{ /*0x065A*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
296	{ /*0x065B*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
297	{ /*0x065C*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
298	{ /*0x065D*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
299	{ /*0x065E*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
300	{ /*0x065F*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
301	{ /*0x0660*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
302	{ /*0x0661*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
303	{ /*0x0662*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
304	{ /*0x0663*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
305	{ /*0x0664*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
306	{ /*0x0665*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
307	{ /*0x0666*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
308	{ /*0x0667*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
309	{ /*0x0668*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
310	{ /*0x0669*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
311	{ /*0x066A*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
312	{ /*0x066B*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
313	{ /*0x066C*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
314	{ /*0x066D*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
315	{ /*0x066E*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
316	{ /*0x066F*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
317	{ /*0x0670*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
318	{ /*0x0671*/ 0xFB50, 0x0000, 0x0000, 0xFB51, },
319	{ /*0x0672*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
320	{ /*0x0673*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
321	{ /*0x0674*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
322	{ /*0x0675*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
323	{ /*0x0676*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
324	{ /*0x0677*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
325	{ /*0x0678*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
326	{ /*0x0679*/ 0xFB66, 0xFB68, 0xFB69, 0xFB67, },
327	{ /*0x067A*/ 0xFB5E, 0xFB60, 0xFB61, 0xFB5F, },
328	{ /*0x067B*/ 0xFB52, 0xFB54, 0xFB55, 0xFB53, },
329	{ /*0x067C*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
330	{ /*0x067D*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
331	{ /*0x067E*/ 0xFB56, 0xFB58, 0xFB59, 0xFB57, },
332	{ /*0x067F*/ 0xFB62, 0xFB64, 0xFB65, 0xFB63, },
333	{ /*0x0680*/ 0xFB5A, 0xFB5C, 0xFB5D, 0xFB5B, },
334	{ /*0x0681*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
335	{ /*0x0682*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
336	{ /*0x0683*/ 0xFB76, 0xFB78, 0xFB79, 0xFB77, },
337	{ /*0x0684*/ 0xFB72, 0xFB74, 0xFB75, 0xFB73, },
338	{ /*0x0685*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
339	{ /*0x0686*/ 0xFB7A, 0xFB7C, 0xFB7D, 0xFB7B, },
340	{ /*0x0687*/ 0xFB7E, 0xFB80, 0xFB81, 0xFB7F, },
341	{ /*0x0688*/ 0xFB88, 0x0000, 0x0000, 0xFB89, },
342	{ /*0x0689*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
343	{ /*0x068A*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
344	{ /*0x068B*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
345	{ /*0x068C*/ 0xFB84, 0x0000, 0x0000, 0xFB85, },
346	{ /*0x068D*/ 0xFB82, 0x0000, 0x0000, 0xFB83, },
347	{ /*0x068E*/ 0xFB86, 0x0000, 0x0000, 0xFB87, },
348	{ /*0x068F*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
349	{ /*0x0690*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
350	{ /*0x0691*/ 0xFB8C, 0x0000, 0x0000, 0xFB8D, },
351	{ /*0x0692*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
352	{ /*0x0693*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
353	{ /*0x0694*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
354	{ /*0x0695*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
355	{ /*0x0696*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
356	{ /*0x0697*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
357	{ /*0x0698*/ 0xFB8A, 0x0000, 0x0000, 0xFB8B, },
358	{ /*0x0699*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
359	{ /*0x069A*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
360	{ /*0x069B*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
361	{ /*0x069C*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
362	{ /*0x069D*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
363	{ /*0x069E*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
364	{ /*0x069F*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
365	{ /*0x06A0*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
366	{ /*0x06A1*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
367	{ /*0x06A2*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
368	{ /*0x06A3*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
369	{ /*0x06A4*/ 0xFB6A, 0xFB6C, 0xFB6D, 0xFB6B, },
370	{ /*0x06A5*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
371	{ /*0x06A6*/ 0xFB6E, 0xFB70, 0xFB71, 0xFB6F, },
372	{ /*0x06A7*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
373	{ /*0x06A8*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
374	{ /*0x06A9*/ 0xFB8E, 0xFB90, 0xFB91, 0xFB8F, },
375	{ /*0x06AA*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
376	{ /*0x06AB*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
377	{ /*0x06AC*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
378	{ /*0x06AD*/ 0xFBD3, 0xFBD5, 0xFBD6, 0xFBD4, },
379	{ /*0x06AE*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
380	{ /*0x06AF*/ 0xFB92, 0xFB94, 0xFB95, 0xFB93, },
381	{ /*0x06B0*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
382	{ /*0x06B1*/ 0xFB9A, 0xFB9C, 0xFB9D, 0xFB9B, },
383	{ /*0x06B2*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
384	{ /*0x06B3*/ 0xFB96, 0xFB98, 0xFB99, 0xFB97, },
385	{ /*0x06B4*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
386	{ /*0x06B5*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
387	{ /*0x06B6*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
388	{ /*0x06B7*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
389	{ /*0x06B8*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
390	{ /*0x06B9*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
391	{ /*0x06BA*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
392	{ /*0x06BB*/ 0xFBA0, 0xFBA2, 0xFBA3, 0xFBA1, },
393	{ /*0x06BC*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
394	{ /*0x06BD*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
395	{ /*0x06BE*/ 0xFBAA, 0xFBAC, 0xFBAD, 0xFBAB, },
396	{ /*0x06BF*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
397	{ /*0x06C0*/ 0xFBA4, 0x0000, 0x0000, 0xFBA5, },
398	{ /*0x06C1*/ 0xFBA6, 0xFBA8, 0xFBA9, 0xFBA7, },
399	{ /*0x06C2*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
400	{ /*0x06C3*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
401	{ /*0x06C4*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
402	{ /*0x06C5*/ 0xFBE0, 0x0000, 0x0000, 0xFBE1, },
403	{ /*0x06C6*/ 0xFBD9, 0x0000, 0x0000, 0xFBDA, },
404	{ /*0x06C7*/ 0xFBD7, 0x0000, 0x0000, 0xFBD8, },
405	{ /*0x06C8*/ 0xFBDB, 0x0000, 0x0000, 0xFBDC, },
406	{ /*0x06C9*/ 0xFBE2, 0x0000, 0x0000, 0xFBE3, },
407	{ /*0x06CA*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
408	{ /*0x06CB*/ 0xFBDE, 0x0000, 0x0000, 0xFBDF, },
409	{ /*0x06CC*/ 0xFBFC, 0xFBFE, 0xFBFF, 0xFBFD, },
410	{ /*0x06CD*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
411	{ /*0x06CE*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
412	{ /*0x06CF*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
413	{ /*0x06D0*/ 0xFBE4, 0xFBE6, 0xFBE7, 0xFBE5, },
414	{ /*0x06D1*/ 0x0000, 0x0000, 0x0000, 0x0000, },  /* dummy filler */
415	{ /*0x06D2*/ 0xFBAE, 0x0000, 0x0000, 0xFBAF, },
416	{ /*0x06D3*/ 0xFBB0, 0x0000, 0x0000, 0xFBB1, },
417};
418
419#define SHAPED_TABLE2_START	0xFEF5
420#define SHAPED_TABLE2_TOP	0xFEFB
421
422/*
423 * The second table is for special ligatures
424 */
425static const chr_shpjoin_t shaped_table2[] =
426{
427	{ /*0xFEF5*/ 0xFEF5, 0x0000, 0x0000, 0xFEF6, }, /* LAM_ALEF_MADDA */
428	{ /*0xFEF6*/ 0x0000, 0x0000, 0x0000, 0x0000, }, /* dummy filler */
429	{ /*0xFEF7*/ 0xFEF7, 0x0000, 0x0000, 0xFEF8, }, /* LAM_ALEF_HAMZA_ABOVE */
430	{ /*0xFEF8*/ 0x0000, 0x0000, 0x0000, 0x0000, }, /* dummy filler */
431	{ /*0xFEF9*/ 0xFEF9, 0x0000, 0x0000, 0xFEFA, }, /* LAM_ALEF_HAMZA_BELOW */
432	{ /*0xFEFA*/ 0x0000, 0x0000, 0x0000, 0x0000, }, /* dummy filler */
433	{ /*0xFEFB*/ 0xFEFB, 0x0000, 0x0000, 0xFEFC, }, /* LAM_ALEF */
434};
435
436#define assignShape(chr)	( ((chr) >= SHAPED_TABLE_START  && (chr) <= SHAPED_TABLE_TOP)? \
437				    &shaped_table[(chr)-SHAPED_TABLE_START] : \
438                                  ((chr) >= SHAPED_TABLE2_START && (chr) <= SHAPED_TABLE2_TOP)? \
439				    &shaped_table2[(chr)-SHAPED_TABLE2_START] : NULL) 
440
441#define assignShapeUtf(txt, i) ( (utf8_len_map[(unsigned char)((txt)[(i)])] > 1)? \
442					doAssignShapeUtf((txt)+(i)) : NULL)
443
444static const chr_shpjoin_t *
445doAssignShapeUtf(const char *txt)
446{
447	unsigned short fs;
448
449	utf8_to_utf16((const unsigned char *) txt,
450		      utf8_len_map[(unsigned char) txt[0]], &fs);
451	return assignShape(fs);
452}
453
454
455static void
456storeUc_2_Utf8(char *dest, int *psz, unsigned short wch)
457{
458	int cb = uc16_to_utf8(&wch, 1, dest + (*psz));
459
460	*psz = *psz + cb;
461}
462
463
464static void
465store_Utf8(char *dest, int *psz, const char *txt)
466{
467	int cb = utf8_len_map[(unsigned char) txt[0]];
468
469	memcpy(dest + (*psz), txt, cb);
470	*psz = *psz + cb;
471}
472
473/*
474 * Note that text is currently left to right
475 */
476static unsigned short *
477arabicJoin_UC16(const unsigned short *text, int len, unsigned long *pAttrib)
478{
479	int i;
480	unsigned short *new_str;
481	const chr_shpjoin_t *prev = NULL;
482	const chr_shpjoin_t *curr = NULL;
483	const chr_shpjoin_t *next = NULL;
484	unsigned long attrib = 0;
485
486	new_str = (unsigned short *) malloc((1 + len) * sizeof(unsigned short));
487	if (new_str == NULL)
488		return NULL;
489
490	for (i = 0; i < len; i++) {
491		if ((curr = assignShape(text[i])) != NULL) {
492			if (i < len - 1)
493				next = assignShape(text[i + 1]);
494			else
495				next = NULL;
496			if (next) {
497				if (prev) {
498					if (!prev->initial || !prev->medial)
499						new_str[i] = curr->initial ?
500							curr->initial : curr->isolated;
501					else
502						new_str[i] = curr->medial ?
503							curr->medial : curr->final;
504				} else {
505					new_str[i] = curr->initial ?
506						curr->initial : curr->isolated;
507				}
508			} else {
509				if (prev) {
510					if (!prev->initial || !prev->medial)
511						new_str[i] = curr->isolated;
512					else
513						new_str[i] = curr->final ?
514							curr->final : curr->isolated;
515				} else {
516					new_str[i] = curr->isolated;
517				}
518			}
519			attrib |= (FTK_TEXT_ATTR_SHAPED | FTK_TEXT_ATTR_EXTENDED);
520		} else {
521			new_str[i] = text[i];
522			if (text[i] <= 0xFF)
523				attrib |= FTK_TEXT_ATTR_STANDARD;
524			else
525				attrib |= FTK_TEXT_ATTR_EXTENDED;
526		}
527
528		prev = curr;
529	}
530	new_str[i] = 0;
531	if (pAttrib)
532		*pAttrib = attrib;
533	return new_str;
534}
535
536/*
537 * Note that text is currently left to right
538 */
539char *
540arabicJoin_UTF8(const char *text, int len, int *pNewLen,
541		unsigned long *pAttrib)
542{
543	int i, sz;
544	char *new_str;
545	const chr_shpjoin_t *prev = NULL;
546	const chr_shpjoin_t *curr = NULL;
547	const chr_shpjoin_t *next = NULL;
548	unsigned long attrib = 0;
549
550	/* Note that shaping may result in three UTF-8 bytes, due to 06xx -> FBxx translation*/
551	/* two times the original buffer should be enough...*/
552	new_str = (char *) malloc((1 + 2 * len) * sizeof(char));
553	if (new_str == NULL)
554		return NULL;
555
556	sz = 0;
557
558	for (i = 0; i < len;) {
559		int b = utf8_len_map[(unsigned char) text[i]];
560		if ((curr = assignShapeUtf(text, i)) != NULL) {
561			if (i < len - b)
562				next = assignShapeUtf(text, i + b);
563			else
564				next = NULL;
565			if (next) {
566				if (prev) {
567					if (!prev->initial || !prev->medial)
568						storeUc_2_Utf8(new_str, &sz,
569							       (curr->initial ? curr->initial :
570								curr->isolated));
571					else
572						storeUc_2_Utf8(new_str, &sz,
573							       (curr->medial ? curr->medial :
574								curr->final));
575				} else {
576					storeUc_2_Utf8(new_str, &sz, (curr->initial ?
577							curr->initial : curr-> isolated));
578				}
579			} else {
580				if (prev) {
581					if (!prev->initial || !prev->medial)
582						storeUc_2_Utf8(new_str, &sz, curr->isolated);
583					else
584						storeUc_2_Utf8(new_str, &sz,
585							       (curr->final ? curr->final :
586							       curr->isolated));
587				} else {
588					storeUc_2_Utf8(new_str, &sz, curr->isolated);
589				}
590			}
591			attrib |= (FTK_TEXT_ATTR_SHAPED | FTK_TEXT_ATTR_EXTENDED);
592		} else {
593			store_Utf8(new_str, &sz, text + i);
594			if ((unsigned char) text[i] < 0xC0)
595				attrib |= FTK_TEXT_ATTR_STANDARD;
596			else
597				attrib |= FTK_TEXT_ATTR_EXTENDED;
598		}
599
600		i += b;
601		prev = curr;
602	}
603	new_str[sz] = 0;
604	if (pNewLen)
605		*pNewLen = sz;
606	if (pAttrib)
607		*pAttrib = attrib;
608#ifdef DEBUG_TEXT_SHAPING
609#endif
610	return new_str;
611}
612
613unsigned short *
614doCharShape_UC16(const unsigned short *text, int len, int *pNewLen,
615	unsigned long *pAttrib)
616{
617	unsigned short *conv = arabicJoin_UC16(text, len, pAttrib);
618
619	if (pNewLen)
620		*pNewLen = len;
621	return conv;
622}
623
624char *
625doCharShape_UTF8(const char *text, int len, int *pNewLen, unsigned long *pAttrib)
626{
627	return arabicJoin_UTF8(text, len, pNewLen, pAttrib);
628}
629
630#else /* HAVE_SHAPEJOINING_SUPPORT */
631/* DUMMY FUNCTIONS */
632unsigned short *
633doCharShape_UC16(const unsigned short *text, int len, int *pNewLen,
634	unsigned long *pAttrib)
635{
636	unsigned short *conv = malloc((len + 1) * sizeof(unsigned short));
637
638	if (conv == NULL)
639		return NULL;
640	memcpy(conv, text, len * sizeof(unsigned short));
641	conv[len] = 0;
642	if (pNewLen)
643		*pNewLen = len;
644	if (pAttrib)
645		*pAttrib = 0;
646	return conv;
647}
648
649char *
650doCharShape_UTF8(const char *text, int len, int *pNewLen, unsigned long *pAttrib)
651{
652	char *conv = malloc((len + 1) * sizeof(char));
653
654	if (conv == NULL)
655		return NULL;
656	memcpy(conv, text, len * sizeof(char));
657	conv[len] = 0;
658	if (pNewLen)
659		*pNewLen = len;
660	if (pAttrib)
661		*pAttrib = 0;
662	return conv;
663}
664#endif /* HAVE_SHAPEJOINING_SUPPORT */
665
666#define HAVE_FRIBIDI_SUPPORT 1
667#if HAVE_FRIBIDI_SUPPORT
668#include <fribidi/fribidi.h>
669
670char *
671doCharBidi_UTF8(const char *text, int len, int *v2lPos, char *pDirection,
672	unsigned long *pAttrib)
673{
674	FriBidiChar *ftxt, *fvirt;
675	FriBidiChar localBuff[128];
676	FriBidiCharType basedir;
677	int cc;
678	int isLocal = 0;
679	char *new_str;
680	int new_len;
681
682	new_str = (char *) malloc(len + 1);
683	if (new_str == NULL)
684		return NULL;
685
686	/* len may be greather than real char count, but it's ok.
687	   if will fit in localBuff, we use it to improve speed */
688	if (len < sizeof(localBuff) / sizeof(localBuff[0]) / 2) {
689		ftxt = localBuff;
690		fvirt = localBuff +
691			sizeof(localBuff) / sizeof(localBuff[0]) / 2;
692		isLocal = 1;
693	} else {
694		ftxt = (FriBidiChar *) malloc((len + 1) * sizeof(FriBidiChar));
695		fvirt = (FriBidiChar *) malloc((len + 1) * sizeof(FriBidiChar));
696	}
697
698	if (ftxt == NULL)
699		return NULL;
700	if (fvirt == NULL) {
701		free(ftxt);
702		return NULL;
703	}
704
705	cc = fribidi_utf8_to_unicode((char *) text, len, ftxt);
706	basedir = FRIBIDI_TYPE_N;
707	fribidi_log2vis(ftxt, cc, &basedir, fvirt, v2lPos, NULL, pDirection);
708	new_len = fribidi_unicode_to_utf8(fvirt, cc, new_str);
709
710	if (pAttrib) {
711		if (basedir & FRIBIDI_MASK_RTL)
712			*pAttrib |= FTK_TEXT_ATTR_RTOL;
713	}
714
715	if (!isLocal) {
716		free(fvirt);
717		free(ftxt);
718	}
719	new_str[new_len] = 0;
720	return new_str;
721}
722
723
724unsigned short *
725doCharBidi_UC16(const unsigned short *text, int len, int *v2lPos,
726	char *pDirection, unsigned long *pAttrib)
727{
728	FriBidiChar *ftxt, *fvirt;
729	FriBidiChar localBuff[128];
730	FriBidiCharType basedir;
731	int cc;
732	int isLocal = 0;
733	unsigned short *new_str;
734
735	new_str = (unsigned short *) malloc((len + 1) * sizeof(unsigned short));
736	if (new_str == NULL)
737		return NULL;
738
739	/* len may be greather than real char count, but it's ok.
740	   if will fit in localBuff, we use it to improve speed */
741	if (len < sizeof(localBuff) / sizeof(localBuff[0]) / 2) {
742		ftxt = localBuff;
743		fvirt = localBuff +
744			sizeof(localBuff) / sizeof(localBuff[0]) / 2;
745		isLocal = 1;
746	} else {
747		ftxt = (FriBidiChar *) malloc((len + 1) * sizeof(FriBidiChar));
748		fvirt = (FriBidiChar *) malloc((len + 1) * sizeof(FriBidiChar));
749	}
750
751	if (ftxt == NULL)
752		return NULL;
753	if (fvirt == NULL) {
754		free(ftxt);
755		return NULL;
756	}
757
758	for (cc = 0; cc < len; cc++)
759		ftxt[cc] = text[cc];
760	basedir = FRIBIDI_TYPE_N;
761	fribidi_log2vis(ftxt, cc, &basedir, fvirt, v2lPos, NULL, pDirection);
762	for (cc = 0; cc < len; cc++)
763		new_str[cc] = (unsigned short) fvirt[cc];
764	new_str[cc] = 0;
765
766	if (pAttrib) {
767		if (basedir & FRIBIDI_MASK_RTL)
768			*pAttrib |= FTK_TEXT_ATTR_RTOL;
769	}
770
771	if (!isLocal) {
772		free(fvirt);
773		free(ftxt);
774	}
775	return new_str;
776}
777
778#else
779/* DUMMY FUNCTIONS */
780char *
781doCharBidi_UTF8(const char *text, int len, int *v2lPos, char *pDirection,
782	unsigned long *pAttrib)
783{
784	int i;
785	unsigned short *conv = malloc((len + 1) * sizeof(unsigned short));
786
787	if (conv == NULL)
788		return NULL;
789	memcpy(conv, text, len * sizeof(unsigned short));
790	conv[len] = 0;
791	if (v2lPos)
792		for (i = 0; i < len; i++)
793			v2lPos[i] = i;
794	if (pDirection)
795		memset(pDirection, 0, len * sizeof(pDirection[0]));
796	return (char *) conv;
797}
798unsigned short *
799doCharBidi_UC16(const unsigned short *text, int len, int *v2lPos,
800	char *pDirection, unsigned long *pAttrib)
801{
802	int i;
803	char *conv = malloc((len + 1) * sizeof(char));
804
805	if (conv == NULL)
806		return NULL;
807	memcpy(conv, text, len * sizeof(char));
808	conv[len] = 0;
809	if (v2lPos)
810		for (i = 0; i < len; i++)
811			v2lPos[i] = i;
812	if (pDirection)
813		memset(pDirection, 0, len * sizeof(pDirection[0]));
814	return (unsigned short *) conv;
815}
816#endif /* HAVE_FRIBIDI_SUPPORT */
817
818struct _FtkTextLayout
819{
820	int pos;
821	int len;
822	size_t width;
823	FtkFont* font;
824	char* text;
825	int line_len;
826	int line_pos;
827	char* bidi_line;
828	unsigned long line_attr;
829	FtkWrapMode wrap_mode;
830	int v2l_v_line[FTK_LINE_CHAR_NR+1];
831	int v2l_l_line[8 * FTK_LINE_CHAR_NR+1];
832};
833
834FtkTextLayout* ftk_text_layout_create(void)
835{
836	FtkTextLayout* thiz = FTK_NEW(FtkTextLayout);
837
838	return thiz;
839}
840
841Ret ftk_text_layout_set_font(FtkTextLayout* thiz, FtkFont* font)
842{
843	return_val_if_fail(thiz != NULL && font != NULL, RET_FAIL);
844
845	thiz->font = font;
846
847	return RET_OK;
848}
849
850Ret ftk_text_layout_set_width(FtkTextLayout* thiz, size_t width)
851{
852	return_val_if_fail(thiz != NULL && width > 0, RET_FAIL);
853
854	thiz->width = width;
855
856	return RET_OK;
857}
858
859Ret ftk_text_layout_set_text(FtkTextLayout* thiz, const char* text, int len)
860{
861	unsigned long attr = 0;
862	return_val_if_fail(thiz != NULL && text != NULL, RET_FAIL);
863
864	if(thiz->text != NULL)
865	{
866		free(thiz->text);
867		thiz->text = NULL;
868	}
869
870	thiz->pos  = 0;
871	thiz->len  = len < 0 ? strlen(text) : len;
872	thiz->text = doCharShape_UTF8(text, thiz->len, &thiz->len, &attr);
873
874	return RET_OK;
875}
876
877Ret ftk_text_layout_set_wrap_mode(FtkTextLayout* thiz, FtkWrapMode wrap_mode)
878{
879	return_val_if_fail(thiz != NULL, RET_FAIL);
880
881	thiz->wrap_mode = wrap_mode;
882
883	return RET_OK;
884}
885
886Ret ftk_text_layout_init(FtkTextLayout* thiz, const char* text, int len, FtkFont* font, size_t width)
887{
888	return_val_if_fail(thiz != NULL && text != NULL && font != NULL && width > 0, RET_FAIL);
889
890	thiz->pos   = 0;
891	thiz->font  = font;
892	thiz->width = width;
893	ftk_text_layout_set_text(thiz, text, len);
894
895	return RET_OK;
896}
897
898Ret ftk_text_layout_skip_to(FtkTextLayout* thiz, int pos)
899{
900	return_val_if_fail(thiz != NULL && pos >= 0 && pos < thiz->len, RET_FAIL);
901
902	thiz->pos = pos;
903
904	return RET_OK;
905}
906
907Ret ftk_text_layout_get_visual_line(FtkTextLayout* thiz, FtkTextLine* line)
908{
909	int i = 0;
910	int extent = 0;
911	unsigned long attr = 0;
912	const char* end = NULL;
913	
914	return_val_if_fail(thiz != NULL && line != NULL, RET_FAIL);
915
916	if(thiz->pos >= thiz->len && thiz->line_pos >= thiz->line_len)
917	{
918		return RET_EOF;
919	}
920
921	line->pos_v2l = thiz->v2l_v_line;
922	line->attr = FTK_TEXT_ATTR_NORMAL;
923
924	if(thiz->line_pos >= thiz->line_len)
925	{
926		int line_len = 0;
927		const char* line_start = thiz->text + thiz->pos;
928		const char* line_end = line_start;
929	
930		thiz->line_pos = 0;
931		while((line_end - thiz->text) <= thiz->len)
932		{
933			if(*line_end == '\n' || *line_end == '\0') break;
934			line_end++;
935		}
936		line_len = line_end - line_start;
937		thiz->pos += line_len;
938		
939		if(thiz->bidi_line != NULL)
940		{
941			free(thiz->bidi_line);
942		}
943
944		thiz->bidi_line = doCharBidi_UTF8(line_start, line_len, thiz->v2l_l_line, NULL, &attr);
945		thiz->line_len = strlen(thiz->bidi_line);
946		thiz->line_attr = attr;
947	}
948
949	line->text = thiz->bidi_line + thiz->line_pos;
950	end = ftk_font_calc_str_visible_range(thiz->font, line->text, 0, -1, thiz->width, &extent);
951	line->len = end - line->text;
952	line->extent = extent;
953
954	line->attr = thiz->line_attr;
955	if(thiz->line_attr & FTK_TEXT_ATTR_RTOL)
956	{
957		line->xoffset = thiz->width - extent;
958		for(i = 0; i < line->len && i < FTK_LINE_CHAR_NR; i++)
959		{
960			line->pos_v2l[i] =  thiz->v2l_v_line[thiz->line_pos + i] + thiz->pos - thiz->line_len;
961		}
962	}
963	else
964	{
965		line->xoffset = 0;
966		for(i = 0; i < line->len && i < FTK_LINE_CHAR_NR; i++)
967		{
968			line->pos_v2l[i] = thiz->pos - thiz->line_len + 1;
969		}
970	}
971
972	ftk_logd("%s: line_pos=%d line_len=%d line.len=%d attr=%x\n", __func__, thiz->line_pos, thiz->line_len, line->len, attr);
973	thiz->line_pos += line->len;
974
975	for(i = line->len - 1; i >= 0; i--)
976	{
977		if(line->text[i] == '\r' || line->text[i] == '\n')
978			line->len--;
979	}
980
981	return RET_OK;
982}
983
984void ftk_text_layout_destroy(FtkTextLayout* thiz)
985{
986	if(thiz != NULL)
987	{
988		FTK_FREE(thiz);
989	}
990
991	return;
992}