~ubuntu-branches/ubuntu/gutsy/icu/gutsy-updates

« back to all changes in this revision

Viewing changes to source/samples/layout/paragraph.cpp

  • Committer: Package Import Robot
  • Author(s): Jay Berkenbilt
  • Date: 2005-11-19 11:29:31 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20051119112931-vcizkrp10tli4enw
Tags: 3.4-3
Explicitly build with g++ 3.4.  The current ICU fails its test suite
with 4.0 but not with 3.4.  Future versions should work properly with
4.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 *******************************************************************************
3
 
 *
4
 
 *   Copyright (C) 1999-2001, International Business Machines
5
 
 *   Corporation and others.  All Rights Reserved.
6
 
 *
7
 
 *******************************************************************************
8
 
 *   file name:  Paragraph.cpp
9
 
 *
10
 
 *   created on: 09/06/2000
11
 
 *   created by: Eric R. Mader
12
 
 */
13
 
 
14
 
#include "unicode/loengine.h"
15
 
 
16
 
#include "RenderingFontInstance.h"
17
 
 
18
 
#include "unicode/utypes.h"
19
 
#include "unicode/uchar.h"
20
 
#include "unicode/uchriter.h"
21
 
#include "unicode/brkiter.h"
22
 
#include "unicode/locid.h"
23
 
#include "unicode/ubidi.h"
24
 
 
25
 
#include "paragraph.h"
26
 
#include "scrptrun.h"
27
 
#include "UnicodeReader.h"
28
 
#include "FontMap.h"
29
 
 
30
 
#define MARGIN 10
31
 
 
32
 
Paragraph::Paragraph(void *surface, RunParams params[], int32_t count, UBiDi *bidi)
33
 
    : fBidi(bidi), fRunCount(count), fRunInfo(NULL), fCharCount(0), fText(NULL), fGlyphCount(0), fGlyphs(NULL),
34
 
      fCharIndices(NULL), fGlyphIndices(NULL), fDX(NULL), fBreakArray(NULL), fBreakCount(0),
35
 
      fLineHeight(-1), fAscent(-1)
36
 
{
37
 
    int32_t i;
38
 
 
39
 
    fWidth = fHeight = 0;
40
 
 
41
 
    fRunInfo = new RunInfo[count + 1];
42
 
 
43
 
    // Set charBase and rightToLeft for
44
 
    // each run and count the total characters
45
 
    for (i = 0; i < count; i += 1) {
46
 
        fRunInfo[i].charBase = fCharCount;
47
 
        fRunInfo[i].rightToLeft = params[i].rightToLeft;
48
 
        fCharCount += params[i].count;
49
 
    }
50
 
 
51
 
    // Set charBase and rightToLeft for the
52
 
    // fake run at the end.
53
 
    fRunInfo[count].charBase = fCharCount;
54
 
    fRunInfo[count].rightToLeft = false;
55
 
 
56
 
    fBreakArray = new int32_t[fCharCount + 1];
57
 
    fText = new LEUnicode[fCharCount];
58
 
    
59
 
    // Copy the text runs into a single array
60
 
    for (i = 0; i < count; i += 1) {
61
 
        int32_t charBase = fRunInfo[i].charBase;
62
 
        int32_t charCount = fRunInfo[i + 1].charBase - charBase;
63
 
 
64
 
        LE_ARRAY_COPY(&fText[charBase], params[i].text, charCount);
65
 
    }
66
 
 
67
 
    Locale thai("th");
68
 
    UCharCharacterIterator *iter = new UCharCharacterIterator(fText, fCharCount);
69
 
    UErrorCode status = U_ZERO_ERROR;
70
 
    Locale dummyLocale;
71
 
 
72
 
    fBrkiter = BreakIterator::createLineInstance(thai, status);
73
 
    fBrkiter->adoptText(iter);
74
 
 
75
 
    ICULayoutEngine **engines = new ICULayoutEngine *[count];
76
 
    int32_t maxAscent = -1, maxDescent = -1, maxLeading = -1;
77
 
    float x = 0, y = 0;
78
 
 
79
 
    // Layout each run, set glyphBase and glyphCount
80
 
    // and count the total number of glyphs
81
 
    for (i = 0; i < count; i += 1) {
82
 
        int32_t charBase = fRunInfo[i].charBase;
83
 
        int32_t charCount = fRunInfo[i + 1].charBase - charBase;
84
 
        int32_t glyphCount = 0;
85
 
        int32_t runAscent = 0, runDescent = 0, runLeading = 0;
86
 
        UErrorCode success = U_ZERO_ERROR;
87
 
 
88
 
        fRunInfo[i].fontInstance = params[i].fontInstance;
89
 
 
90
 
        fRunInfo[i].fontInstance->setFont(surface);
91
 
 
92
 
        runAscent  = fRunInfo[i].fontInstance->getAscent();
93
 
        runDescent = fRunInfo[i].fontInstance->getDescent();
94
 
        runLeading = fRunInfo[i].fontInstance->getLeading();
95
 
 
96
 
 
97
 
        if (runAscent > maxAscent) {
98
 
            maxAscent = runAscent;
99
 
        }
100
 
 
101
 
        if (runDescent > maxDescent) {
102
 
            maxDescent = runDescent;
103
 
        }
104
 
 
105
 
        if (runLeading > maxLeading) {
106
 
            maxLeading = runLeading;
107
 
        }
108
 
 
109
 
        engines[i] = ICULayoutEngine::createInstance(fRunInfo[i].fontInstance, params[i].scriptCode, dummyLocale, success);
110
 
 
111
 
        glyphCount = engines[i]->layoutChars(fText, charBase, charBase + charCount, fCharCount,
112
 
            fRunInfo[i].rightToLeft, x, y, success);
113
 
 
114
 
        engines[i]->getGlyphPosition(glyphCount, x, y, success);
115
 
 
116
 
        fRunInfo[i].glyphBase = fGlyphCount;
117
 
        fGlyphCount += glyphCount;
118
 
    }
119
 
 
120
 
    fLineHeight = maxAscent + maxDescent + maxLeading;
121
 
    fAscent = maxAscent;
122
 
 
123
 
    // Set glyphBase for the fake run at the end
124
 
    fRunInfo[count].glyphBase = fGlyphCount;
125
 
 
126
 
    fGlyphs = new LEGlyphID[fGlyphCount];
127
 
    fCharIndices = new int32_t[fGlyphCount];
128
 
    fGlyphIndices = new int32_t[fCharCount + 1];
129
 
    fDX = new int32_t[fGlyphCount];
130
 
    fDY = new int32_t[fGlyphCount];
131
 
 
132
 
 
133
 
    float *positions = new float[fGlyphCount * 2 + 2];
134
 
 
135
 
    // Build the glyph, charIndices and positions arrays
136
 
    for (i = 0; i < count; i += 1) {
137
 
        ICULayoutEngine *engine = engines[i];
138
 
        int32_t charBase = fRunInfo[i].charBase;
139
 
        int32_t glyphBase = fRunInfo[i].glyphBase;
140
 
        UErrorCode success = U_ZERO_ERROR;
141
 
 
142
 
        engine->getGlyphs(&fGlyphs[glyphBase], success);
143
 
        engine->getCharIndices(&fCharIndices[glyphBase], charBase, success);
144
 
        engine->getGlyphPositions(&positions[glyphBase * 2], success);
145
 
    }
146
 
 
147
 
    // Filter deleted glyphs, compute logical advances
148
 
    // and set the char to glyph map
149
 
    for (i = 0; i < fGlyphCount; i += 1) {
150
 
        // Filter deleted glyphs
151
 
        if (fGlyphs[i] == 0xFFFE || fGlyphs[i] == 0xFFFF) {
152
 
            fGlyphs[i] = 0x0001;
153
 
        }
154
 
 
155
 
        // compute the logical advance
156
 
        fDX[i] = (int32_t) (positions[i * 2 + 2] - positions[i * 2]);
157
 
 
158
 
        // save the Y offset
159
 
        fDY[i] = (int32_t) positions[i * 2 + 1];
160
 
 
161
 
        // set char to glyph map
162
 
        fGlyphIndices[fCharIndices[i]] = i;
163
 
    }
164
 
 
165
 
    if (fRunInfo[count - 1].rightToLeft) {
166
 
        fGlyphIndices[fCharCount] = fRunInfo[count - 1].glyphBase - 1;
167
 
    } else {
168
 
        fGlyphIndices[fCharCount] = fGlyphCount;
169
 
    }
170
 
 
171
 
    delete[] positions;
172
 
 
173
 
    // Get rid of the LayoutEngine's:
174
 
    for (i = 0; i < count; i += 1) {
175
 
        delete engines[i];
176
 
    }
177
 
 
178
 
    delete[] engines;
179
 
}
180
 
 
181
 
Paragraph::~Paragraph()
182
 
{
183
 
    delete[] fDY;
184
 
    delete[] fDX;
185
 
    delete[] fGlyphIndices;
186
 
    delete[] fCharIndices;
187
 
    delete[] fGlyphs;
188
 
 
189
 
    delete fBrkiter;
190
 
    delete fText;
191
 
 
192
 
    delete[] fBreakArray;
193
 
    delete[] fRunInfo;
194
 
 
195
 
    ubidi_close(fBidi);
196
 
}
197
 
 
198
 
int32_t Paragraph::getLineHeight()
199
 
{
200
 
    return fLineHeight;
201
 
}
202
 
 
203
 
int32_t Paragraph::getLineCount()
204
 
{
205
 
    return fBreakCount;
206
 
}
207
 
 
208
 
int32_t Paragraph::getAscent()
209
 
{
210
 
    return fAscent;
211
 
}
212
 
 
213
 
int32_t Paragraph::previousBreak(int32_t charIndex)
214
 
{
215
 
    LEUnicode ch = fText[charIndex];
216
 
 
217
 
    // skip over any whitespace or control
218
 
    // characters, because they can hang in
219
 
    // the margin.
220
 
    while (charIndex < fCharCount &&
221
 
           (u_isWhitespace(ch) ||
222
 
            u_iscntrl(ch))) {
223
 
        ch = fText[++charIndex];
224
 
    }
225
 
 
226
 
    // return the break location that's at or before
227
 
    // the character we stopped on. Note: if we're
228
 
    // on a break, the "+ 1" will cause preceding to
229
 
    // back up to it.
230
 
    return fBrkiter->preceding(charIndex + 1);
231
 
}
232
 
 
233
 
void Paragraph::breakLines(int32_t width, int32_t height)
234
 
{
235
 
    int32_t lineWidth = width - (2 * MARGIN);
236
 
    int32_t thisWidth = 0;
237
 
    int32_t thisBreak = -1;
238
 
    int32_t prevWidth = fWidth;
239
 
 
240
 
    fWidth  = width;
241
 
    fHeight = height;
242
 
 
243
 
    // don't re-break if the width hasn't changed
244
 
    if (width == prevWidth) {
245
 
        return;
246
 
    }
247
 
 
248
 
    fBreakArray[0] = 0;
249
 
    fBreakCount = 1;
250
 
 
251
 
    for (int32_t run = 0; run < fRunCount; run += 1) {
252
 
        int32_t glyph = fRunInfo[run].glyphBase;
253
 
        int32_t stop = fRunInfo[run + 1].glyphBase;
254
 
        int32_t dir = 1;
255
 
 
256
 
        if (fRunInfo[run].rightToLeft) {
257
 
            glyph = stop - 1;
258
 
            stop = fRunInfo[run].glyphBase - 1;
259
 
            dir = -1;
260
 
        }
261
 
 
262
 
        while (glyph != stop) {
263
 
            // Find the first glyph that doesn't fit on the line
264
 
            while (thisWidth + fDX[glyph] <= lineWidth) {
265
 
                thisWidth += fDX[glyph];
266
 
                glyph += dir;
267
 
 
268
 
                if (glyph == stop) {
269
 
                    break;
270
 
                }
271
 
            }
272
 
 
273
 
            // Check to see if we fell off the
274
 
            // end of the run
275
 
            if (glyph == stop) {
276
 
                break;
277
 
            }
278
 
 
279
 
 
280
 
            // Find a place before here to break,
281
 
            thisBreak = previousBreak(fCharIndices[glyph]);
282
 
 
283
 
            // If there wasn't one, force one
284
 
            if (thisBreak <= fBreakArray[fBreakCount - 1]) {
285
 
                thisBreak = fCharIndices[glyph];
286
 
            }
287
 
 
288
 
            // Save the break location.
289
 
            fBreakArray[fBreakCount++] = thisBreak;
290
 
 
291
 
            // Reset the accumulated width
292
 
            thisWidth = 0;
293
 
 
294
 
            // Map the character back to a glyph
295
 
            glyph = fGlyphIndices[thisBreak];
296
 
 
297
 
            // Check to see if the new glyph is off
298
 
            // the end of the run.
299
 
            if (glyph == stop) {
300
 
                break;
301
 
            }
302
 
 
303
 
            // If the glyph's not in the run we stopped in, we
304
 
            // have to re-synch to the new run
305
 
            if (glyph < fRunInfo[run].glyphBase || glyph >= fRunInfo[run + 1].glyphBase) {
306
 
                run = getGlyphRun(glyph, 0, 1);
307
 
 
308
 
                if (fRunInfo[run].rightToLeft) {
309
 
                    stop = fRunInfo[run].glyphBase - 1;
310
 
                    dir = -1;
311
 
                } else {
312
 
                    stop = fRunInfo[run + 1].glyphBase;
313
 
                    dir = 1;
314
 
                }
315
 
            }
316
 
        }
317
 
    }
318
 
 
319
 
    // Make sure the last break is after the last character
320
 
    if (fBreakArray[--fBreakCount] != fCharCount) {
321
 
        fBreakArray[++fBreakCount] = fCharCount;
322
 
    }
323
 
 
324
 
    return;
325
 
}
326
 
 
327
 
int32_t Paragraph::getGlyphRun(int32_t glyph, int32_t startingRun, int32_t direction)
328
 
{
329
 
    int32_t limit;
330
 
 
331
 
    if (direction < 0) {
332
 
        limit = -1;
333
 
    } else {
334
 
        limit = fRunCount;
335
 
    }
336
 
 
337
 
    for (int32_t run = startingRun; run != limit; run += direction) {
338
 
        if (glyph >= fRunInfo[run].glyphBase && glyph < fRunInfo[run + 1].glyphBase) {
339
 
            return run;
340
 
        }
341
 
    }
342
 
 
343
 
    return limit;
344
 
}
345
 
 
346
 
int32_t Paragraph::getCharRun(int32_t ch, int32_t startingRun, int32_t direction)
347
 
{
348
 
    int32_t limit;
349
 
 
350
 
    if (direction < 0) {
351
 
        limit = -1;
352
 
    } else {
353
 
        limit = fRunCount;
354
 
    }
355
 
 
356
 
    for (int32_t run = startingRun; run != limit; run += direction) {
357
 
        if (ch >= fRunInfo[run].charBase && ch < fRunInfo[run + 1].charBase) {
358
 
            return run;
359
 
        }
360
 
    }
361
 
 
362
 
    return limit;
363
 
}
364
 
 
365
 
int32_t Paragraph::getRunWidth(int32_t startGlyph, int32_t endGlyph)
366
 
{
367
 
    int32_t width = 0;
368
 
 
369
 
    for (int32_t glyph = startGlyph; glyph <= endGlyph; glyph += 1) {
370
 
        width += fDX[glyph];
371
 
    }
372
 
 
373
 
    return width;
374
 
}
375
 
 
376
 
int32_t Paragraph::drawRun(void *surface, const RenderingFontInstance *fontInstance, int32_t firstChar, int32_t lastChar,
377
 
                         int32_t x, int32_t y)
378
 
{
379
 
    int32_t firstGlyph = fGlyphIndices[firstChar];
380
 
    int32_t lastGlyph  = fGlyphIndices[lastChar];
381
 
 
382
 
    for (int32_t ch = firstChar; ch <= lastChar; ch += 1) {
383
 
        int32_t glyph = fGlyphIndices[ch];
384
 
 
385
 
        if (glyph < firstGlyph) {
386
 
            firstGlyph = glyph;
387
 
        }
388
 
 
389
 
        if (glyph > lastGlyph) {
390
 
            lastGlyph = glyph;
391
 
        }
392
 
    }
393
 
 
394
 
    int32_t dyStart = firstGlyph, dyEnd = dyStart;
395
 
 
396
 
    fontInstance->setFont(surface);
397
 
 
398
 
    while (dyEnd <= lastGlyph) {
399
 
        while (dyEnd <= lastGlyph && fDY[dyStart] == fDY[dyEnd]) {
400
 
            dyEnd += 1;
401
 
        }
402
 
 
403
 
        fontInstance->drawGlyphs(surface, &fGlyphs[dyStart], dyEnd - dyStart,
404
 
            &fDX[dyStart], x, y + fDY[dyStart], fWidth, fHeight);
405
 
 
406
 
        for (int32_t i = dyStart; i < dyEnd; i += 1) {
407
 
            x += fDX[i];
408
 
        }
409
 
 
410
 
        dyStart = dyEnd;
411
 
    }
412
 
 
413
 
    return getRunWidth(firstGlyph, lastGlyph);
414
 
}
415
 
 
416
 
void Paragraph::draw(void *surface, int32_t firstLine, int32_t lastLine)
417
 
{
418
 
    int32_t line, x, y;
419
 
    int32_t prevRun = 0;
420
 
    UErrorCode bidiStatus = U_ZERO_ERROR;
421
 
    UBiDi  *lBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
422
 
 
423
 
    y = fAscent;
424
 
 
425
 
    for (line = firstLine; line <= lastLine; line += 1) {
426
 
        int32_t firstChar = fBreakArray[line];
427
 
        int32_t lastChar  = fBreakArray[line + 1] - 1;
428
 
        int32_t dirCount, dirRun;
429
 
 
430
 
        x = MARGIN;
431
 
 
432
 
        ubidi_setLine(fBidi, firstChar, lastChar + 1, lBidi, &bidiStatus);
433
 
 
434
 
        dirCount = ubidi_countRuns(lBidi, &bidiStatus);
435
 
 
436
 
        for (dirRun = 0; dirRun < dirCount; dirRun += 1) {
437
 
            int32_t relStart = 0, runLength = 0;
438
 
            UBiDiDirection runDirection = ubidi_getVisualRun(lBidi, dirRun, &relStart, &runLength);
439
 
            int32_t runStart  = relStart + firstChar;
440
 
            int32_t runEnd    = runStart + runLength - 1;
441
 
            int32_t firstRun  = getCharRun(runStart, prevRun, 1);
442
 
            int32_t lastRun   = getCharRun(runEnd,   firstRun, 1);
443
 
 
444
 
            for (int32_t run = firstRun; run <= lastRun; run += 1) {
445
 
                const RenderingFontInstance *fontInstance = fRunInfo[run].fontInstance;
446
 
                int32_t nextBase;
447
 
 
448
 
                if (run == lastRun) {
449
 
                    nextBase = runEnd + 1;
450
 
                } else {
451
 
                    nextBase = fRunInfo[run + 1].charBase;
452
 
                }
453
 
 
454
 
                x += drawRun(surface, fontInstance, runStart, nextBase - 1, x, y);
455
 
                runStart = nextBase;
456
 
            }
457
 
 
458
 
            prevRun = lastRun;
459
 
        }
460
 
 
461
 
        y += fLineHeight;
462
 
    }
463
 
 
464
 
    ubidi_close(lBidi);
465
 
}
466
 
 
467
 
Paragraph *Paragraph::paragraphFactory(const char *fileName, FontMap *fontMap, GUISupport *guiSupport, void *surface)
468
 
{
469
 
    RunParams params[64];
470
 
    int32_t paramCount = 0;
471
 
    int32_t charCount  = 0;
472
 
    int32_t dirCount   = 0;
473
 
    int32_t dirRun     = 0;
474
 
    RFIErrorCode fontStatus = RFI_NO_ERROR;
475
 
    UErrorCode bidiStatus = U_ZERO_ERROR;
476
 
    const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount);
477
 
    ScriptRun scriptRun(text, charCount);
478
 
 
479
 
    if (text == NULL) {
480
 
        return NULL;
481
 
    }
482
 
 
483
 
    UBiDi *pBidi = ubidi_openSized(charCount, 0, &bidiStatus);
484
 
 
485
 
    ubidi_setPara(pBidi, text, charCount, UBIDI_DEFAULT_LTR, NULL, &bidiStatus);
486
 
 
487
 
    dirCount = ubidi_countRuns(pBidi, &bidiStatus);
488
 
 
489
 
    for (dirRun = 0; dirRun < dirCount; dirRun += 1) {
490
 
        int32_t runStart = 0, runLength = 0;
491
 
        UBiDiDirection runDirection = ubidi_getVisualRun(pBidi, dirRun, &runStart, &runLength);
492
 
        
493
 
        scriptRun.reset(runStart, runLength);
494
 
 
495
 
        while (scriptRun.next()) {
496
 
            int32_t     start = scriptRun.getScriptStart();
497
 
            int32_t     end   = scriptRun.getScriptEnd();
498
 
            UScriptCode code  = scriptRun.getScriptCode();
499
 
 
500
 
            params[paramCount].text = &((UChar *) text)[start];
501
 
            params[paramCount].count = end - start;
502
 
            params[paramCount].scriptCode = (UScriptCode) code;
503
 
            params[paramCount].rightToLeft = runDirection == UBIDI_RTL;
504
 
 
505
 
            params[paramCount].fontInstance = fontMap->getScriptFont(code, fontStatus);
506
 
 
507
 
            if (params[paramCount].fontInstance == NULL) {
508
 
                ubidi_close(pBidi);
509
 
                return 0;
510
 
            }
511
 
 
512
 
            paramCount += 1;
513
 
        }
514
 
    }
515
 
 
516
 
    return new Paragraph(surface, params, paramCount, pBidi);
517
 
}
518