Code

c818bcab3526fb7deb9f2f523589f6cae141053f
[inkscape.git] / src / libnrtype / TextWrapper.cpp
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     }
177 void text_wrapper::DoLayout(void)
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     }
299 void text_wrapper::ChunkText(void)
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     }
392 void text_wrapper::MakeVertical(void)
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();
449 void text_wrapper::MergeWhiteSpace(void)
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;
487 // utility: computes the number of letters in the layout
488 int text_wrapper::NbLetter(int g_st, int g_en)
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;
502 void text_wrapper::AddLetterSpacing(double dx, double dy, int g_st, int g_en)
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;
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
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;
541 bool text_wrapper::NextWord(int &st, int &en) const
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;
552 bool text_wrapper::NextPara(int &st, int &en) const
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;
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)
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;
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)
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;
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;
604 /**
605  * Sort this.bounds by b.uni_pos, updating the .other index values appropriately.
606  */
607 void text_wrapper::SortBoundaries(void)
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);
635 void text_wrapper::MakeTextBoundaries(PangoLogAttr *pAttrs, int nAttr)
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 ( i == nAttr || uni32_text[i] == '\n' || uni32_text[i] == '\r' ) { // 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     }
704 bool text_wrapper::IsBound(BoundaryType const bnd_type, int g_st, int &c_st)
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;
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)
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     }
819 void text_wrapper::KernXForLastAddition(double *i_kern_x, int i_len, double scale)
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;
831 void text_wrapper::KernYForLastAddition(double *i_kern_y, int i_len, double scale)
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;
843 void text_wrapper::KernXForLastAddition(GList *i_kern_x, double scale)
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     }
857 void text_wrapper::KernYForLastAddition(GList *i_kern_y, double scale)
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     }
872 void text_wrapper::AddDxDy(void)
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     }
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 :