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"
20
text_wrapper::text_wrapper(void)
29
utf8_codepoint = NULL;
30
uni32_codepoint = NULL;
33
nbBound = maxBound = 0;
38
kern_x = kern_y = NULL;
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);
46
text_wrapper::~text_wrapper(void)
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 ) {
67
if ( bounds ) free(bounds);
72
void text_wrapper::SetDefaultFont(font_instance *iFont)
74
// refcounts the font for our internal uses
75
if ( iFont ) iFont->Ref();
76
if ( default_font ) default_font->Unref();
80
void text_wrapper::AppendUTF8(char const *text, int len)
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. */
91
while ( len > 0 && ( *text == '\n' || *text == '\r' ) ) {text++; len--;}
93
while ( *text == '\n' || *text == '\r' ) text++;
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
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));
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);
123
utf8_codepoint = NULL;
126
// recompute length of uni32 text
129
p = g_utf8_next_char(p); // since we validated the input text, we can use this 'fast' macro
133
// realloc the arrays
134
uni32_text = (gunichar*)malloc((uni32_length + 1) * sizeof(gunichar));
135
utf8_codepoint = (int*)malloc((uni32_length + 1) * sizeof(int));
137
// read the utf8 string and compute codepoints positions
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;
152
p = g_utf8_next_char(p);
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;
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 ) {
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;
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;
177
void text_wrapper::DoLayout(void)
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
185
//char *tc = pango_font_description_to_string(default_font->descr);
186
//printf("layout with %s\n", tc);
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);
196
double pango_to_ink = (1.0 / ((double)PANGO_SCALE)); // utility
198
PangoLayoutIter *pIter = pango_layout_get_iter(pLayout); // and go!
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
208
PangoLayoutRun *pRun = (PangoLayoutRun*)curR->data;
209
int prOffset = pRun->item->offset; // start of the run in the line
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));
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 ) {
227
if ( i < pRun->glyphs->num_glyphs - 1 ) {
228
glyph_text[glyph_length + 1].uni_en = glyph_text[glyph_length].uni_st;
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
236
glyph_text[glyph_length - 1].uni_en = glyph_text[glyph_length].uni_st;
238
glyph_text[glyph_length].uni_dir = 0;
239
glyph_text[glyph_length + 1].uni_dir = 0;
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;
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 ) {
256
if ( glyph_length > o_g_l ) glyph_text[o_g_l].uni_en = plOffset + prOffset + pRun->item->length;
258
if ( glyph_length > 0 ) glyph_text[glyph_length - 1].uni_en = plOffset + prOffset + pRun->item->length;
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;
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;
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
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
283
// get rid of the attributes
284
if ( pAttrs ) g_free(pAttrs);
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;
293
if ( glyph_length > 0 ) {
294
glyph_text[glyph_length].x /= 512;
295
glyph_text[glyph_length].y /= 512;
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;
316
if ( IsBound(bnd_word, g_st, c_st) ) {
317
if ( g_st == bounds[c_st].uni_pos ) glyph_text[i].word_start = true;
319
if ( IsBound(bnd_para, g_st, c_st) ) {
320
if ( g_st == bounds[c_st].uni_pos ) glyph_text[i].para_start = true;
323
if ( IsBound(bnd_char, g_en, c_en) ) {
324
if ( g_en == bounds[c_en].uni_pos ) glyph_text[i].char_start = true;
326
if ( IsBound(bnd_word, g_en, c_en) ) {
327
if ( g_en == bounds[c_en].uni_pos ) glyph_text[i].word_start = true;
329
if ( IsBound(bnd_para, g_en, c_en) ) {
330
if ( g_en == bounds[c_en].uni_pos ) glyph_text[i].para_start = true;
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;
341
// doing little boxes
342
int g_st = -1, g_en = -1;
343
while ( NextWord(g_st, g_en) ) {
344
// check uniformity of fonts
351
PangoFont *curPF = glyph_text[n_st].font;
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));
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);
365
} while ( n_en < g_en );
370
// doing little paras
371
int g_st = -1, g_en = -1;
372
while ( NextPara(g_st, g_en) ) {
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 ) {
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));
383
paras[nbPara].b_st = b_st;
384
paras[nbPara].b_en = b_en;
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;
402
PangoFont *curPF = NULL;
403
font_instance *curF = NULL;
405
// move to the next letter boundary
409
} while ( g_en < glyph_length && glyph_text[g_en].char_start == false );
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
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();
422
curPF = glyph_text[i].font;
424
PangoFontDescription *pfd = pango_font_describe(curPF);
425
curF = font_src->Face(pfd);
426
pango_font_description_free(pfd);
430
? curF->Advance(glyph_text[i].gl, true)
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;
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;
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;
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]) ) {
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
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
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 ) {
496
for (int i = g_st; i < g_en; i++) {
497
if ( glyph_text[i].char_start ) 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 ) {
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;
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.)
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
538
} while ( en < glyph_length && glyph_text[en].char_start == false );
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
549
} while ( en < glyph_length && glyph_text[en].word_start == false );
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
560
} while ( en < glyph_length && glyph_text[en].para_start == false );
567
* Append \a ib to our bounds array.
568
* \return The index of the new element.
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));
576
unsigned const ix = nbBound++;
582
* Add the start \& end boundaries \a is \& \a ie to bounds.
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;
605
* Sort this.bounds by b.uni_pos, updating the .other index values appropriately.
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.
614
* A simpler speedup is just to change qsort to std::sort, which can inline the comparison
618
/* The 'other' field needs to be updated after sorting by qsort, so we build the inverse
620
for (unsigned i = 0; i < nbBound; i++) {
621
bounds[i].old_ix = i;
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;
628
for (unsigned i = 0; i < nbBound; i++) { // update 'other'
629
if ( bounds[i].other < nbBound ) {
630
bounds[i].other = old2new[bounds[i].other];
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;
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++) {
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;
658
AddTwinBoundaries(nbs, nbe);
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;
668
nbs.data.i = nbe.data.i = ( pAttrs[last_w_st].is_white ? 1 : 0 );
669
AddTwinBoundaries(nbs, nbe);
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;
678
nbs.data.i = nbe.data.i = ( pAttrs[last_w_st].is_white ? 1 : 0 );
679
AddTwinBoundaries(nbs, nbe);
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;
689
AddTwinBoundaries(nbs, nbe);
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;
698
AddTwinBoundaries(nbs, nbe);
704
bool text_wrapper::IsBound(BoundaryType const bnd_type, int g_st, int &c_st)
706
if ( c_st < 0 ) c_st = 0;
708
while ( unsigned(c_st) < nbBound ) {
709
if ( bounds[c_st].uni_pos == g_st && bounds[c_st].type == bnd_type ) {
712
if ( bounds[c_st].uni_pos < g_st ) {
713
if ( scan_dir < 0 ) break;
716
} else if ( bounds[c_st].uni_pos > g_st ) {
717
if ( scan_dir > 0 ) break;
721
// good pos, wrong type
722
while ( c_st > 0 && bounds[c_st].uni_pos == g_st ) {
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 ) {
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)
742
// if ( c_st < 0 ) c_st = 0;
743
// bool found = false;
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;
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
759
// if ( bounds[c_st].uni_pos < g_st ) {
760
// if ( scan_dir < 0 ) break;
763
// } else if ( bounds[c_st].uni_pos > g_st ) {
764
// if ( scan_dir > 0 ) break;
768
// // good pos, wrong type
769
// while ( c_st > 0 && bounds[c_st].uni_pos == g_st ) {
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;
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
795
void text_wrapper::MeasureBoxes(void)
797
font_factory *f_src = font_factory::Default();
798
for (int i = 0; i < nbBox; i++) {
800
boxes[i].descent = 0;
801
boxes[i].leading = 0;
804
PangoFont *curPF = glyph_text[boxes[i].g_st].font;
806
PangoFontDescription *pfd = pango_font_describe(curPF);
807
font_instance *curF = f_src->Face(pfd);
809
curF->FontMetrics(boxes[i].ascent, boxes[i].descent, boxes[i].leading);
812
pango_font_description_free(pfd);
813
boxes[i].width = glyph_text[boxes[i].g_en].x - glyph_text[boxes[i].g_st].x;
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;
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;
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;
850
int last_len = uni32_length - last_addition;
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;
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;
864
int last_len = uni32_length - last_addition;
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;
872
void text_wrapper::AddDxDy(void)
874
if ( glyph_length <= 0 ) return;
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];
887
glyph_text[i].x += sum;
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];
897
glyph_text[glyph_length].x += sum;
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];
912
glyph_text[i].y += sum;
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];
922
glyph_text[glyph_length].y += sum;
931
c-file-style:"stroustrup"
932
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
937
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :