~ubuntu-branches/ubuntu/utopic/inkscape/utopic-proposed

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/libnrtype/TextWrapper.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  TextWrapper.cpp
 
3
 *  testICU
 
4
 *
 
5
 */
 
6
 
 
7
#ifdef HAVE_CONFIG_H
 
8
# include <config.h>
 
9
#endif
 
10
#include "TextWrapper.h"
 
11
 
 
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"
 
17
 
 
18
#include <svg/svg.h>
 
19
 
 
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;
 
69
 
 
70
}
 
71
 
 
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
}
 
79
 
 
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));
 
99
 
 
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. */
 
105
 
 
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));
 
112
 
 
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
}
 
176
 
 
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;
 
195
 
 
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);
 
270
 
 
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);
 
285
 
 
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
}
 
298
 
 
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
    }
 
334
 
 
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
}
 
391
 
 
392
void text_wrapper::MakeVertical(void)
 
393
{
 
394
    if ( glyph_length <= 0 ) return;
 
395
    font_factory *font_src = font_factory::Default();
 
396
 
 
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
}
 
448
 
 
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
}
 
486
 
 
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
}
 
501
 
 
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;
 
510
 
 
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
}
 
522
 
 
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
//@}
 
564
 
 
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
}
 
580
 
 
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
}
 
593
 
 
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
     */
 
617
 
 
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 ( 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
    }
 
702
}
 
703
 
 
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
}
 
737
 
 
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
//}
 
794
 
 
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;
 
803
 
 
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
}
 
817
 
 
818
 
 
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
}
 
830
 
 
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
}
 
842
 
 
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
}
 
856
 
 
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
}
 
870
 
 
871
 
 
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;
 
886
 
 
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;
 
911
 
 
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
}
 
926
 
 
927
 
 
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 :