ce8797322b1bd6790e669c2ea137bfa830e0866f
1 /*
2 * TextWrapper.cpp
3 * testICU
4 *
5 */
7 #ifdef HAVE_CONFIG_H
8 # include <config.h>
9 #endif
10 #include "TextWrapper.h"
12 #include <libnrtype/font-instance.h>
13 #include "libnrtype/text-boundary.h"
14 #include "libnrtype/one-glyph.h"
15 #include "libnrtype/one-box.h"
16 #include "libnrtype/one-para.h"
18 #include <svg/svg.h>
20 text_wrapper::text_wrapper(void)
21 {
22 // voids everything
23 utf8_text = NULL;
24 uni32_text = NULL;
25 glyph_text = NULL;
26 utf8_length = 0;
27 uni32_length = 0;
28 glyph_length = 0;
29 utf8_codepoint = NULL;
30 uni32_codepoint = NULL;
31 default_font = NULL;
32 bounds = NULL;
33 nbBound = maxBound = 0;
34 boxes = NULL;
35 nbBox = maxBox = 0;
36 paras = NULL;
37 nbPara = maxPara = 0;
38 kern_x = kern_y = NULL;
39 last_addition = -1;
40 // inits the pangolayout with default params
41 font_factory *font_src = font_factory::Default();
42 pLayout = pango_layout_new(font_src->fontContext);
43 pango_layout_set_single_paragraph_mode(pLayout, true);
44 pango_layout_set_width(pLayout, -1);
45 }
46 text_wrapper::~text_wrapper(void)
47 {
48 // frees everything
49 //printf("delete\n");
50 g_object_unref(pLayout);
51 if ( utf8_text ) free(utf8_text);
52 if ( uni32_text ) free(uni32_text);
53 if ( glyph_text ) free(glyph_text);
54 if ( utf8_codepoint ) free(utf8_codepoint);
55 if ( uni32_codepoint ) free(uni32_codepoint);
56 if ( default_font ) default_font->Unref();
57 if ( boxes ) free(boxes);
58 if ( paras ) free(paras);
59 if ( kern_x ) free(kern_x);
60 if ( kern_y ) free(kern_y);
61 for (unsigned i = 0; i < nbBound; i++) {
62 switch ( bounds[i].type ) {
63 default:
64 break;
65 }
66 }
67 if ( bounds ) free(bounds);
68 default_font = NULL;
70 }
72 void text_wrapper::SetDefaultFont(font_instance *iFont)
73 {
74 // refcounts the font for our internal uses
75 if ( iFont ) iFont->Ref();
76 if ( default_font ) default_font->Unref();
77 default_font = iFont;
78 }
80 void text_wrapper::AppendUTF8(char const *text, int len)
81 {
82 // appends text to what needs to be handled
83 if ( utf8_length <= 0 ) {
84 // a first check to prevent the text from containing a leading line return (which
85 // is probably a bug anyway)
86 if ( text[0] == '\n' || text[0] == '\r' ) {
87 /* fixme: Should the below be `0 <= len' ? The existing code looks wrong
88 * for the case that len==0.
89 * TODO: Document the meaning of the len parameter. */
90 if ( len > 0 ) {
91 while ( len > 0 && ( *text == '\n' || *text == '\r' ) ) {text++; len--;}
92 } else {
93 while ( *text == '\n' || *text == '\r' ) text++;
94 }
95 }
96 }
97 if ( len == 0 || text == NULL || *text == 0 ) return;
98 g_return_if_fail(g_utf8_validate(text, len, NULL));
100 // compute the length
101 int const nlen = ( len < 0
102 ? strlen(text)
103 : len );
104 /* effic: Use g_utf8_validate's last param to do this. */
106 // prepare to store the additional text
107 /* effic: (Not an issue for the sole caller at the time of writing.) This implementation
108 takes quadratic time if the text is composed of n appends. Use a proper data structure.
109 STL vector would suffice. */
110 utf8_text = (char*)realloc(utf8_text, (utf8_length + nlen + 1) * sizeof(char));
111 uni32_codepoint = (int*)realloc(uni32_codepoint, (utf8_length + nlen + 1) * sizeof(int));
113 // copy the source text in the newly lengthened array
114 memcpy(utf8_text + utf8_length, text, nlen * sizeof(char));
115 utf8_length += nlen;
116 utf8_text[utf8_length] = 0;
117 // remember where the text ended, before we recompute it, for the dx/dy we'll add after that (if any)
118 last_addition = uni32_length;
119 // free old uni32 structures (instead of incrementally putting the text)
120 if ( uni32_text ) free(uni32_text);
121 if ( utf8_codepoint ) free(utf8_codepoint);
122 uni32_text = NULL;
123 utf8_codepoint = NULL;
124 uni32_length = 0;
125 {
126 // recompute length of uni32 text
127 char *p = utf8_text;
128 while ( *p ) {
129 p = g_utf8_next_char(p); // since we validated the input text, we can use this 'fast' macro
130 uni32_length++;
131 }
132 }
133 // realloc the arrays
134 uni32_text = (gunichar*)malloc((uni32_length + 1) * sizeof(gunichar));
135 utf8_codepoint = (int*)malloc((uni32_length + 1) * sizeof(int));
136 {
137 // read the utf8 string and compute codepoints positions
138 char *p = utf8_text;
139 int i = 0;
140 int l_o = 0;
141 while ( *p ) {
142 // get the new codepoint
143 uni32_text[i] = g_utf8_get_char(p);
144 // compute the offset in the utf8_string
145 unsigned int n_o = (unsigned int)(p - utf8_text);
146 // record the codepoint's start
147 utf8_codepoint[i] = n_o;
148 // record the codepoint's correspondance in the utf8 string
149 for (unsigned int j = l_o; j < n_o; j++) uni32_codepoint[j] = i - 1;
150 // and move on
151 l_o = n_o;
152 p = g_utf8_next_char(p);
153 i++;
154 }
155 // the termination of the loop
156 for (int j = l_o; j < utf8_length; j++) uni32_codepoint[j] = uni32_length - 1;
157 uni32_codepoint[utf8_length] = uni32_length;
158 uni32_text[uni32_length] = 0;
159 utf8_codepoint[uni32_length] = utf8_length;
160 }
161 // if needed, fill the dx/dy arrays with 0 for the newly created part
162 // these will be filled by a KernXForLastAddition() right after this function
163 // note that the SVG spec doesn't require you to give a dx for each codepoint,
164 // so setting the dx to 0 is mandatory
165 if ( uni32_length > last_addition ) {
166 if ( kern_x ) {
167 kern_x = (double*)realloc(kern_x, (uni32_length + 1) * sizeof(double));
168 for (int i = last_addition; i <= uni32_length; i++) kern_x[i] = 0;
169 }
170 if ( kern_y ) {
171 kern_y = (double*)realloc(kern_y, (uni32_length + 1) * sizeof(double));
172 for (int i = last_addition; i <= uni32_length; i++) kern_y[i] = 0;
173 }
174 }
175 }
177 void text_wrapper::DoLayout(void)
178 {
179 // THE function
180 // first some sanity checks
181 if ( default_font == NULL ) return;
182 if ( uni32_length <= 0 || utf8_length <= 0 ) return;
183 // prepare the pangolayout object
184 {
185 //char *tc = pango_font_description_to_string(default_font->descr);
186 //printf("layout with %s\n", tc);
187 //free(tc);
188 }
189 pango_layout_set_font_description(pLayout, default_font->descr);
190 pango_layout_set_text(pLayout, utf8_text, utf8_length);
191 // reset the glyph string
192 if ( glyph_text ) free(glyph_text);
193 glyph_text = NULL;
194 glyph_length = 0;
196 double pango_to_ink = (1.0 / ((double)PANGO_SCALE)); // utility
197 int max_g = 0;
198 PangoLayoutIter *pIter = pango_layout_get_iter(pLayout); // and go!
199 do {
200 PangoLayoutLine *pLine = pango_layout_iter_get_line(pIter); // no need for unref
201 int plOffset = pLine->start_index; // start of the line in the uni32_text
202 PangoRectangle ink_r, log_r;
203 pango_layout_iter_get_line_extents(pIter, &ink_r, &log_r);
204 double plY = (1.0 / ((double)PANGO_SCALE)) * ((double)log_r.y); // start position of this line of the layout
205 double plX = (1.0 / ((double)PANGO_SCALE)) * ((double)log_r.x);
206 GSList *curR = pLine->runs; // get ready to iterate over the runs of this line
207 while ( curR ) {
208 PangoLayoutRun *pRun = (PangoLayoutRun*)curR->data;
209 int prOffset = pRun->item->offset; // start of the run in the line
210 if ( pRun ) {
211 // a run has uniform font/directionality/etc...
212 int o_g_l = glyph_length; // save the index of the first glyph we'll add
213 for (int i = 0; i < pRun->glyphs->num_glyphs; i++) { // add glyph sequentially, reading them from the run
214 // realloc the structures
215 if ( glyph_length >= max_g ) {
216 max_g = 2 * glyph_length + 1;
217 glyph_text = (one_glyph*)realloc(glyph_text, (max_g + 1) * sizeof(one_glyph));
218 }
219 // fill the glyph info
220 glyph_text[glyph_length].font = pRun->item->analysis.font;
221 glyph_text[glyph_length].gl = pRun->glyphs->glyphs[i].glyph;
222 glyph_text[glyph_length].uni_st = plOffset + prOffset + pRun->glyphs->log_clusters[i];
223 // depending on the directionality, the last uni32 codepoint for this glyph is the first of the next char
224 // or the first of the previous
225 if ( pRun->item->analysis.level == 1 ) {
226 // rtl
227 if ( i < pRun->glyphs->num_glyphs - 1 ) {
228 glyph_text[glyph_length + 1].uni_en = glyph_text[glyph_length].uni_st;
229 }
230 glyph_text[glyph_length].uni_dir = 1;
231 glyph_text[glyph_length + 1].uni_dir = 1; // set the directionality for the next too, so that the last glyph in
232 // the array has the correct direction
233 } else {
234 // ltr
235 if ( i > 0 ) {
236 glyph_text[glyph_length - 1].uni_en = glyph_text[glyph_length].uni_st;
237 }
238 glyph_text[glyph_length].uni_dir = 0;
239 glyph_text[glyph_length + 1].uni_dir = 0;
240 }
241 // set the position
242 // the layout is an infinite line
243 glyph_text[glyph_length].x = plX + pango_to_ink * ((double)pRun->glyphs->glyphs[i].geometry.x_offset);
244 glyph_text[glyph_length].y = plY + pango_to_ink * ((double)pRun->glyphs->glyphs[i].geometry.y_offset);
245 // advance to the next glyph
246 plX += pango_to_ink * ((double)pRun->glyphs->glyphs[i].geometry.width);
247 // and set the next glyph's position, in case it's the terminating glyph
248 glyph_text[glyph_length + 1].x = plX;
249 glyph_text[glyph_length + 1].y = plY;
250 glyph_length++;
251 }
252 // and finish filling the info
253 // notably, the uni_en of the last char in ltr text and the uni_en of the first in rtl are still not set
254 if ( pRun->item->analysis.level == 1 ) {
255 // rtl
256 if ( glyph_length > o_g_l ) glyph_text[o_g_l].uni_en = plOffset + prOffset + pRun->item->length;
257 } else {
258 if ( glyph_length > 0 ) glyph_text[glyph_length - 1].uni_en = plOffset + prOffset + pRun->item->length;
259 }
260 // the terminating glyph has glyph_id=0 because it means 'no glyph'
261 glyph_text[glyph_length].gl = 0;
262 // and is associated with no text (but you cannot set uni_st=uni_en=0, because the termination
263 // is expected to be the glyph for the termination of the uni32_text)
264 glyph_text[glyph_length].uni_st = glyph_text[glyph_length].uni_en = plOffset + prOffset + pRun->item->length;
265 }
266 curR = curR->next;
267 }
268 } while ( pango_layout_iter_next_line(pIter) );
269 pango_layout_iter_free(pIter);
271 // grunt work done. now some additional info for layout: computing letters, mostly (one letter = several glyphs sometimes)
272 PangoLogAttr *pAttrs = NULL;
273 int nbAttr = 0;
274 // get the layout attrs, they hold the boundaries pango computed
275 pango_layout_get_log_attrs(pLayout, &pAttrs, &nbAttr);
276 // feed to MakeTextBoundaries which knows what to do with these
277 MakeTextBoundaries(pAttrs, nbAttr);
278 // the array of boundaries is full, but out-of-order
279 SortBoundaries();
280 // boundary array is ready to be used, call chunktext to fill the *_start fields of the glyphs, and compute
281 // the boxed version of the text for sp-typeset
282 ChunkText();
283 // get rid of the attributes
284 if ( pAttrs ) g_free(pAttrs);
286 // cleaning up
287 for (int i = 0; i < glyph_length; i++) {
288 glyph_text[i].uni_st = uni32_codepoint[glyph_text[i].uni_st];
289 glyph_text[i].uni_en = uni32_codepoint[glyph_text[i].uni_en];
290 glyph_text[i].x /= 512; // why is this not default_font->daddy->fontsize?
291 glyph_text[i].y /= 512;
292 }
293 if ( glyph_length > 0 ) {
294 glyph_text[glyph_length].x /= 512;
295 glyph_text[glyph_length].y /= 512;
296 }
297 }
299 void text_wrapper::ChunkText(void)
300 {
301 int c_st = -1, c_en = -1;
302 for (int i = 0; i < glyph_length; i++) {
303 int g_st = glyph_text[i].uni_st, g_en = glyph_text[i].uni_en;
304 glyph_text[i].char_start = false;
305 glyph_text[i].word_start = false;
306 glyph_text[i].para_start = false;
307 // boundaries depend on the directionality
308 // letter boundaries correspond to the glyphs starting one letter when you read them left to right (always)
309 // because that's the order they are stored into in the glyph_text array
310 if ( glyph_text[i].uni_dir == 0 ) {
311 if ( IsBound(bnd_char, g_st, c_st) ) { // check if there is a charcater (=letter in pango speak) at this position
312 // can be a 'start' boundary or a 'end' boundary, doesn't matter, as long
313 // as you get from one letter to the next at this position
314 if ( g_st == bounds[c_st].uni_pos ) glyph_text[i].char_start = true;
315 }
316 if ( IsBound(bnd_word, g_st, c_st) ) {
317 if ( g_st == bounds[c_st].uni_pos ) glyph_text[i].word_start = true;
318 }
319 if ( IsBound(bnd_para, g_st, c_st) ) {
320 if ( g_st == bounds[c_st].uni_pos ) glyph_text[i].para_start = true;
321 }
322 } else {
323 if ( IsBound(bnd_char, g_en, c_en) ) {
324 if ( g_en == bounds[c_en].uni_pos ) glyph_text[i].char_start = true;
325 }
326 if ( IsBound(bnd_word, g_en, c_en) ) {
327 if ( g_en == bounds[c_en].uni_pos ) glyph_text[i].word_start = true;
328 }
329 if ( IsBound(bnd_para, g_en, c_en) ) {
330 if ( g_en == bounds[c_en].uni_pos ) glyph_text[i].para_start = true;
331 }
332 }
333 }
335 if ( glyph_length > 0 ) {
336 glyph_text[glyph_length].char_start = true;
337 glyph_text[glyph_length].word_start = true;
338 glyph_text[glyph_length].para_start = true;
339 }
340 {
341 // doing little boxes
342 int g_st = -1, g_en = -1;
343 while ( NextWord(g_st, g_en) ) {
344 // check uniformity of fonts
345 if ( g_st < g_en ) {
346 int n_st = g_st;
347 int n_en = g_st;
348 bool first = true;
349 do {
350 n_st = n_en;
351 PangoFont *curPF = glyph_text[n_st].font;
352 do {
353 n_en++;
354 } while ( n_en < g_en && glyph_text[n_en].font == curPF );
355 if ( nbBox >= maxBox ) {
356 maxBox = 2 * nbBox + 1;
357 boxes = (one_box*)realloc(boxes, maxBox * sizeof(one_box));
358 }
359 boxes[nbBox].g_st = n_st;
360 boxes[nbBox].g_en = n_en;
361 boxes[nbBox].word_start = first;
362 boxes[nbBox].word_end = (n_en >= g_en);
363 nbBox++;
364 first = false;
365 } while ( n_en < g_en );
366 }
367 }
368 }
369 {
370 // doing little paras
371 int g_st = -1, g_en = -1;
372 while ( NextPara(g_st, g_en) ) {
373 int b_st = 0;
374 while ( b_st < nbBox && boxes[b_st].g_st < g_st ) b_st++;
375 if ( b_st < nbBox && boxes[b_st].g_st == g_st ) {
376 int b_en = b_st;
377 while ( b_en < nbBox && boxes[b_en].g_en < g_en ) b_en++;
378 if ( b_en < nbBox && boxes[b_en].g_en == g_en ) {
379 if ( nbPara >= maxPara ) {
380 maxPara = 2 * nbPara + 1;
381 paras = (one_para*)realloc(paras, maxPara * sizeof(one_para));
382 }
383 paras[nbPara].b_st = b_st;
384 paras[nbPara].b_en = b_en;
385 nbPara++;
386 }
387 }
388 }
389 }
390 }
392 void text_wrapper::MakeVertical(void)
393 {
394 if ( glyph_length <= 0 ) return;
395 font_factory *font_src = font_factory::Default();
397 // explanation: when laying out text vertically, you must keep the glyphs of a single letter together
398 double baseY = glyph_text[0].y;
399 double lastY = baseY;
400 int g_st = 0, g_en = 0;
401 int nbLetter = 0;
402 PangoFont *curPF = NULL;
403 font_instance *curF = NULL;
404 do {
405 // move to the next letter boundary
406 g_st = g_en;
407 do {
408 g_en++;
409 } while ( g_en < glyph_length && glyph_text[g_en].char_start == false );
410 // got a letter
411 if ( g_st < g_en && g_en <= glyph_length ) {
412 // we need to compute the letter's width (in case sometimes we implement the flushleft and flushright)
413 // and the total height for this letter. for example accents usually have 0 width, so this is not
414 // stupid
415 double n_adv = 0;
416 double minX = glyph_text[g_st].x, maxX = glyph_text[g_st].x;
417 for (int i = g_st; i < g_en; i++) {
418 if ( glyph_text[i].font != curPF ) { // font is not the same as the one of the previous glyph
419 // so we need to update curF
420 if ( curF ) curF->Unref();
421 curF = NULL;
422 curPF = glyph_text[i].font;
423 if ( curPF ) {
424 PangoFontDescription *pfd = pango_font_describe(curPF);
425 curF = font_src->Face(pfd);
426 pango_font_description_free(pfd);
427 }
428 }
429 double x = ( curF
430 ? curF->Advance(glyph_text[i].gl, true)
431 : 0 );
432 if ( x > n_adv ) n_adv = x;
433 if ( glyph_text[i].x < minX ) minX = glyph_text[i].x;
434 if ( glyph_text[i].x > maxX ) maxX = glyph_text[i].x;
435 }
436 lastY += n_adv;
437 // and put the glyphs of this letter at their new position
438 for (int i = g_st; i < g_en; i++) {
439 glyph_text[i].x -= minX;
440 glyph_text[i].y += lastY;
441 }
442 g_st = g_en;
443 }
444 nbLetter++;
445 } while ( g_st < glyph_length );
446 if ( curF ) curF->Unref();
447 }
449 void text_wrapper::MergeWhiteSpace(void)
450 {
451 if ( glyph_length <= 0 ) return;
452 // scans the glyphs and shifts them accordingly
453 double delta_x = 0, delta_y = 0;
454 bool inWhite = true;
455 int wpos = 0, rpos = 0; // wpos is the position where we read glyphs, rpos is the position where we write them back
456 // since we only eat whitespace, wpos <= rpos
457 for (rpos = 0; rpos < glyph_length; rpos++) {
458 // copy the glyph at its new position
459 glyph_text[wpos].gl = glyph_text[rpos].gl;
460 glyph_text[wpos].uni_st = glyph_text[rpos].uni_st;
461 glyph_text[wpos].uni_en = glyph_text[rpos].uni_en;
462 glyph_text[wpos].font = glyph_text[rpos].font;
463 glyph_text[wpos].x = glyph_text[rpos].x - delta_x;
464 glyph_text[wpos].y = glyph_text[rpos].y - delta_y;
465 wpos++; // move the write position
466 if ( g_unichar_isspace(uni32_text[glyph_text[rpos].uni_st]) ) {
467 if ( inWhite ) {
468 // eat me: 2 steps: first add the shift in position to the cumulated shift
469 delta_x += glyph_text[rpos + 1].x - glyph_text[rpos].x;
470 delta_y += glyph_text[rpos + 1].y - glyph_text[rpos].y;
471 // then move the write position back. this way, we'll overwrite the previous whitespace with the new glyph
472 // since this is only done after the first whitespace, we only keep the first whitespace
473 wpos--;
474 }
475 inWhite = true;
476 } else {
477 inWhite = false;
478 }
479 }
480 // and the terminating glyph (we should probably copy the rest of the glyph's info, too)
481 glyph_text[wpos].x = glyph_text[rpos].x - delta_x;
482 glyph_text[wpos].y = glyph_text[rpos].y - delta_y;
483 // sets the new length
484 glyph_length = wpos;
485 }
487 // utility: computes the number of letters in the layout
488 int text_wrapper::NbLetter(int g_st, int g_en)
489 {
490 if ( glyph_length <= 0 ) return 0;
491 if ( g_st < 0 || g_st >= g_en ) {
492 g_st = 0;
493 g_en = glyph_length;
494 }
495 int nbLetter = 0;
496 for (int i = g_st; i < g_en; i++) {
497 if ( glyph_text[i].char_start ) nbLetter++;
498 }
499 return nbLetter;
500 }
502 void text_wrapper::AddLetterSpacing(double dx, double dy, int g_st, int g_en)
503 {
504 if ( glyph_length <= 0 ) return;
505 if ( g_st < 0 || g_st >= g_en ) {
506 g_st = 0;
507 g_en = glyph_length;
508 }
509 int nbLetter = 0;
511 // letterspacing means: add 'dx * (nbLetter - 1)' to the x position
512 // so we just scan the glyph string
513 for (int i = g_st; i < g_en; i++) {
514 if ( i > g_st && glyph_text[i].char_start ) nbLetter++;
515 glyph_text[i].x += dx * nbLetter;
516 glyph_text[i].y += dy * nbLetter;
517 }
518 if ( glyph_text[g_en].char_start ) nbLetter++;
519 glyph_text[g_en].x += dx * nbLetter;
520 glyph_text[g_en].y += dy * nbLetter;
521 }
523 /** @name Movement commands
524 * Miscellaneous functions for moving about glyphs.
525 * \a st and \en are start and end glyph indices.
526 * The three methods differ only in whether they look for .char_start, .word_start or .para_start.
527 * \return True iff a next character was found. (False iff we've already reached the end.)
528 */
529 //@{
530 bool text_wrapper::NextChar(int &st, int &en) const
531 {
532 if ( st < 0 || en < 0 ) {st = 0; en = 0;}
533 if ( st >= en ) en = st;
534 if ( st >= glyph_length || en >= glyph_length ) return false; // finished
535 st = en;
536 do {
537 en++;
538 } while ( en < glyph_length && glyph_text[en].char_start == false );
539 return true;
540 }
541 bool text_wrapper::NextWord(int &st, int &en) const
542 {
543 if ( st < 0 || en < 0 ) {st = 0; en = 0;}
544 if ( st >= en ) en = st;
545 if ( st >= glyph_length || en >= glyph_length ) return false; // finished
546 st = en;
547 do {
548 en++;
549 } while ( en < glyph_length && glyph_text[en].word_start == false );
550 return true;
551 }
552 bool text_wrapper::NextPara(int &st, int &en) const
553 {
554 if ( st < 0 || en < 0 ) {st = 0; en = 0;}
555 if ( st >= en ) en = st;
556 if ( st >= glyph_length || en >= glyph_length ) return false; // finished
557 st = en;
558 do {
559 en++;
560 } while ( en < glyph_length && glyph_text[en].para_start == false );
561 return true;
562 }
563 //@}
565 // boundary handling
566 /**
567 * Append \a ib to our bounds array.
568 * \return The index of the new element.
569 */
570 unsigned text_wrapper::AddBoundary(text_boundary const &ib)
571 {
572 if ( nbBound >= maxBound ) {
573 maxBound = 2 * nbBound + 1;
574 bounds = (text_boundary*)realloc(bounds, maxBound * sizeof(text_boundary));
575 }
576 unsigned const ix = nbBound++;
577 bounds[ix] = ib;
578 return ix;
579 }
581 /**
582 * Add the start \& end boundaries \a is \& \a ie to bounds.
583 */
584 void text_wrapper::AddTwinBoundaries(text_boundary const &is, text_boundary const &ie)
585 {
586 unsigned const ns = AddBoundary(is);
587 unsigned const ne = AddBoundary(ie);
588 bounds[ns].start = true;
589 bounds[ns].other = ne;
590 bounds[ne].start = false;
591 bounds[ne].other = ns;
592 }
594 static int CmpBound(void const *a, void const *b) {
595 text_boundary const &ta = *reinterpret_cast<text_boundary const *>(a);
596 text_boundary const &tb = *reinterpret_cast<text_boundary const *>(b);
597 if ( ta.uni_pos < tb.uni_pos ) return -1;
598 if ( ta.uni_pos > tb.uni_pos ) return 1;
599 /* TODO: I'd guess that for a given uni_pos it would be better for the end boundary to precede the start boundary. */
600 if ( ta.start && !tb.start ) return -1;
601 if ( !ta.start && tb.start ) return 1;
602 return 0;
603 }
604 /**
605 * Sort this.bounds by b.uni_pos, updating the .other index values appropriately.
606 */
607 void text_wrapper::SortBoundaries(void)
608 {
609 /* effic: If this function (including descendents such as the qsort calll) ever takes
610 * non-negligible time, then we can fairly easily improve it by changing MakeBoundaries add in
611 * sorted order. It would just have to remember for itself the index of each start boundary
612 * for updating the .other fields appropriately.
613 *
614 * A simpler speedup is just to change qsort to std::sort, which can inline the comparison
615 * function.
616 */
618 /* The 'other' field needs to be updated after sorting by qsort, so we build the inverse
619 * permutation. */
620 for (unsigned i = 0; i < nbBound; i++) {
621 bounds[i].old_ix = i;
622 }
623 qsort(bounds, nbBound, sizeof(text_boundary), CmpBound);
624 unsigned *const old2new = g_new(unsigned, nbBound);
625 for (unsigned new_ix = 0; new_ix < nbBound; new_ix++) { // compute inverse permutation
626 old2new[bounds[new_ix].old_ix] = new_ix;
627 }
628 for (unsigned i = 0; i < nbBound; i++) { // update 'other'
629 if ( bounds[i].other < nbBound ) {
630 bounds[i].other = old2new[bounds[i].other];
631 }
632 }
633 g_free(old2new);
634 }
635 void text_wrapper::MakeTextBoundaries(PangoLogAttr *pAttrs, int nAttr)
636 {
637 if ( pAttrs == NULL || nAttr <= 0 || uni32_length <= 0 ) return;
638 if ( nAttr > uni32_length + 1 ) nAttr = uni32_length + 1;
639 int last_c_st = -1;
640 int last_w_st = -1;
641 int last_s_st = -1;
642 int last_p_st = 0;
643 // reads the text and adds a pair of boundaries each time we encounter a stop
644 // last_* are used to keep track of the start of new text chunk
645 for (int i = 0; i <= nAttr; i++) {
646 text_boundary nbs;
647 text_boundary nbe;
648 nbs.uni_pos = i;
649 nbs.start = true;
650 nbe.uni_pos = i;
651 nbe.start = false;
652 // letters
653 if ( i == nAttr || pAttrs[i].is_cursor_position ) {
654 if ( last_c_st >= 0 ) {
655 nbs.type = nbe.type = bnd_char;
656 nbs.uni_pos = last_c_st;
657 nbe.uni_pos = i;
658 AddTwinBoundaries(nbs, nbe);
659 }
660 last_c_st = i;
661 }
662 // words
663 if ( i == nAttr || pAttrs[i].is_word_start ) {
664 if ( last_w_st >= 0 ) {
665 nbs.type = nbe.type = bnd_word;
666 nbs.uni_pos = last_w_st;
667 nbe.uni_pos = i;
668 nbs.data.i = nbe.data.i = ( pAttrs[last_w_st].is_white ? 1 : 0 );
669 AddTwinBoundaries(nbs, nbe);
670 }
671 last_w_st = i;
672 }
673 if ( i < nAttr && pAttrs[i].is_word_end ) {
674 if ( last_w_st >= 0 ) {
675 nbs.type = nbe.type = bnd_word;
676 nbs.uni_pos = last_w_st;
677 nbe.uni_pos = i;
678 nbs.data.i = nbe.data.i = ( pAttrs[last_w_st].is_white ? 1 : 0 );
679 AddTwinBoundaries(nbs, nbe);
680 }
681 last_w_st = i;
682 }
683 // sentences
684 if ( i == nAttr || pAttrs[i].is_sentence_boundary ) {
685 if ( last_s_st >= 0 ) {
686 nbs.type = nbe.type = bnd_sent;
687 nbs.uni_pos = last_s_st;
688 nbe.uni_pos = i;
689 AddTwinBoundaries(nbs, nbe);
690 }
691 last_s_st = i;
692 }
693 // paragraphs
694 if ( uni32_text[i] == '\n' || uni32_text[i] == '\r' || i == nAttr ) { // too simple to be true?
695 nbs.type = nbe.type = bnd_para;
696 nbs.uni_pos = last_p_st;
697 nbe.uni_pos = i + 1;
698 AddTwinBoundaries(nbs, nbe);
699 last_p_st = i + 1;
700 }
701 }
702 }
704 bool text_wrapper::IsBound(BoundaryType const bnd_type, int g_st, int &c_st)
705 {
706 if ( c_st < 0 ) c_st = 0;
707 int scan_dir = 0;
708 while ( unsigned(c_st) < nbBound ) {
709 if ( bounds[c_st].uni_pos == g_st && bounds[c_st].type == bnd_type ) {
710 return true;
711 }
712 if ( bounds[c_st].uni_pos < g_st ) {
713 if ( scan_dir < 0 ) break;
714 c_st++;
715 scan_dir = 1;
716 } else if ( bounds[c_st].uni_pos > g_st ) {
717 if ( scan_dir > 0 ) break;
718 c_st--;
719 scan_dir = -1;
720 } else {
721 // good pos, wrong type
722 while ( c_st > 0 && bounds[c_st].uni_pos == g_st ) {
723 c_st--;
724 }
725 if ( bounds[c_st].uni_pos < g_st ) c_st++;
726 while ( unsigned(c_st) < nbBound && bounds[c_st].uni_pos == g_st ) {
727 if ( bounds[c_st].type == bnd_type ) {
728 return true;
729 }
730 c_st++;
731 }
732 break;
733 }
734 }
735 return false;
736 }
738 /* Unused. Retained only because I haven't asked cyreve (Richard Hughes) whether he intends ever
739 * to use it. You can probably safely remove it. */
740 //bool text_wrapper::Contains(BoundaryType const bnd_type, int g_st, int g_en, int &c_st, int &c_en)
741 //{
742 // if ( c_st < 0 ) c_st = 0;
743 // bool found = false;
744 // int scan_dir = 0;
745 // while ( unsigned(c_st) < nbBound ) {
746 // if ( bounds[c_st].type == bnd_type ) {
747 // if ( bounds[c_st].start ) {
748 // c_en = bounds[c_st].other;
749 // } else {
750 // }
751 // }
752 // if ( bounds[c_st].type == bnd_type && unsigned(c_en) == bounds[c_st].other ) {
753 // if ( g_st >= bounds[c_st].uni_pos && g_en <= bounds[c_en].uni_pos ) {
754 // // character found
755 // found = true;
756 // break;
757 // }
758 // }
759 // if ( bounds[c_st].uni_pos < g_st ) {
760 // if ( scan_dir < 0 ) break;
761 // c_st++;
762 // scan_dir = 1;
763 // } else if ( bounds[c_st].uni_pos > g_st ) {
764 // if ( scan_dir > 0 ) break;
765 // c_st--;
766 // scan_dir = -1;
767 // } else {
768 // // good pos, wrong type
769 // while ( c_st > 0 && bounds[c_st].uni_pos == g_st ) {
770 // c_st--;
771 // }
772 // if ( bounds[c_st].uni_pos < g_st ) c_st++;
773 // while ( unsigned(c_st) < nbBound && bounds[c_st].uni_pos == g_st ) {
774 // if ( bounds[c_st].type == bnd_type ) {
775 // if ( bounds[c_st].start ) {
776 // c_en = bounds[c_st].other;
777 // } else {
778 // }
779 // }
780 // if ( bounds[c_st].type == bnd_type && unsigned(c_en) == bounds[c_st].other ) {
781 // if ( g_st >= bounds[c_st].uni_pos && g_en <= bounds[c_en].uni_pos ) {
782 // // character found
783 // return true;
784 // }
785 // }
786 // c_st++;
787 // }
788 //
789 // break;
790 // }
791 // }
792 // return found;
793 //}
795 void text_wrapper::MeasureBoxes(void)
796 {
797 font_factory *f_src = font_factory::Default();
798 for (int i = 0; i < nbBox; i++) {
799 boxes[i].ascent = 0;
800 boxes[i].descent = 0;
801 boxes[i].leading = 0;
802 boxes[i].width = 0;
804 PangoFont *curPF = glyph_text[boxes[i].g_st].font;
805 if ( curPF ) {
806 PangoFontDescription *pfd = pango_font_describe(curPF);
807 font_instance *curF = f_src->Face(pfd);
808 if ( curF ) {
809 curF->FontMetrics(boxes[i].ascent, boxes[i].descent, boxes[i].leading);
810 curF->Unref();
811 }
812 pango_font_description_free(pfd);
813 boxes[i].width = glyph_text[boxes[i].g_en].x - glyph_text[boxes[i].g_st].x;
814 }
815 }
816 }
819 void text_wrapper::KernXForLastAddition(double *i_kern_x, int i_len, double scale)
820 {
821 if ( i_kern_x == NULL || i_len <= 0 || last_addition < 0 || last_addition >= uni32_length || uni32_length <= 0 ) return;
822 if ( kern_x == NULL ) {
823 kern_x = (double*)malloc((uni32_length + 1) * sizeof(double));
824 for (int i = 0; i <= uni32_length; i++) kern_x[i] = 0;
825 }
826 int last_len = uni32_length - last_addition;
827 if ( i_len > last_len ) i_len = last_len;
828 for (int i = 0; i < i_len; i++) kern_x[last_addition + i] = i_kern_x[i] * scale;
829 }
831 void text_wrapper::KernYForLastAddition(double *i_kern_y, int i_len, double scale)
832 {
833 if ( i_kern_y == NULL || i_len <= 0 || last_addition < 0 || last_addition >= uni32_length || uni32_length <= 0 ) return;
834 if ( kern_y == NULL ) {
835 kern_y = (double*)malloc((uni32_length + 1) * sizeof(double));
836 for (int i = 0; i <= uni32_length; i++) kern_y[i] = 0;
837 }
838 int last_len = uni32_length - last_addition;
839 if ( i_len > last_len ) i_len = last_len;
840 for (int i = 0; i < i_len; i++) kern_y[last_addition + i] = i_kern_y[i] * scale;
841 }
843 void text_wrapper::KernXForLastAddition(GList *i_kern_x, double scale)
844 {
845 if ( i_kern_x == NULL || last_addition < 0 || last_addition >= uni32_length || uni32_length <= 0 ) return;
846 if ( kern_x == NULL ) {
847 kern_x = (double*)malloc((uni32_length + 1) * sizeof(double));
848 for (int i = 0; i <= uni32_length; i++) kern_x[i] = 0;
849 }
850 int last_len = uni32_length - last_addition;
851 GList *l = i_kern_x;
852 for (int i = 0; i < last_len && l && l->data; i++, l = l->next) {
853 kern_x[last_addition + i] = ((SVGLength *) l->data)->computed * scale;
854 }
855 }
857 void text_wrapper::KernYForLastAddition(GList *i_kern_y, double scale)
858 {
859 if ( i_kern_y == NULL || last_addition < 0 || last_addition >= uni32_length || uni32_length <= 0 ) return;
860 if ( kern_y == NULL ) {
861 kern_y = (double*)malloc((uni32_length + 1) * sizeof(double));
862 for (int i = 0; i <= uni32_length; i++) kern_y[i] = 0;
863 }
864 int last_len = uni32_length - last_addition;
865 GList *l = i_kern_y;
866 for (int i = 0; i < last_len && l && l->data; i++, l = l->next) {
867 kern_y[last_addition + i] = ((SVGLength *) l->data)->computed * scale;
868 }
869 }
872 void text_wrapper::AddDxDy(void)
873 {
874 if ( glyph_length <= 0 ) return;
875 if ( kern_x ) {
876 double sum = 0;
877 int l_pos = -1;
878 for (int i = 0; i < glyph_length; i++) {
879 int n_pos = glyph_text[i].uni_st;
880 if ( l_pos < n_pos ) {
881 for (int j = l_pos + 1; j <= n_pos; j++) sum += kern_x[j];
882 } else if ( l_pos > n_pos ) {
883 for (int j = l_pos; j > n_pos; j--) sum -= kern_x[j];
884 }
885 l_pos = n_pos;
887 glyph_text[i].x += sum;
888 }
889 {
890 int n_pos = uni32_length;
891 if ( l_pos < n_pos ) {
892 for (int j = l_pos + 1; j <= n_pos; j++) sum += kern_x[j];
893 } else if ( l_pos > n_pos ) {
894 for (int j = l_pos; j > n_pos; j--) sum -= kern_x[j];
895 }
896 l_pos = n_pos;
897 glyph_text[glyph_length].x += sum;
898 }
899 }
900 if ( kern_y ) {
901 double sum = 0;
902 int l_pos = -1;
903 for (int i = 0; i < glyph_length; i++) {
904 int n_pos = glyph_text[i].uni_st;
905 if ( l_pos < n_pos ) {
906 for (int j = l_pos + 1; j <= n_pos; j++) sum += kern_y[j];
907 } else if ( l_pos > n_pos ) {
908 for (int j = l_pos; j > n_pos; j--) sum -= kern_y[j];
909 }
910 l_pos = n_pos;
912 glyph_text[i].y += sum;
913 }
914 {
915 int n_pos = uni32_length;
916 if ( l_pos < n_pos ) {
917 for (int j = l_pos + 1; j <= n_pos; j++) sum += kern_y[j];
918 } else if ( l_pos > n_pos ) {
919 for (int j = l_pos; j > n_pos; j--) sum -= kern_y[j];
920 }
921 l_pos = n_pos;
922 glyph_text[glyph_length].y += sum;
923 }
924 }
925 }
928 /*
929 Local Variables:
930 mode:c++
931 c-file-style:"stroustrup"
932 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
933 indent-tabs-mode:nil
934 fill-column:99
935 End:
936 */
937 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :