~ubuntu-branches/ubuntu/quantal/icu/quantal

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Yves Arrouye
  • Date: 2002-03-03 15:31:13 UTC
  • Revision ID: package-import@ubuntu.com-20020303153113-3ssceqlq45xbmbnc
Tags: upstream-2.0-2.1pre20020303
ImportĀ upstreamĀ versionĀ 2.0-2.1pre20020303

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/unicode.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
           (Unicode::isWhitespace(ch) ||
 
222
            Unicode::isControl(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
        dyStart = dyEnd;
 
407
    }
 
408
 
 
409
    return getRunWidth(firstGlyph, lastGlyph);
 
410
}
 
411
 
 
412
void Paragraph::draw(void *surface, int32_t firstLine, int32_t lastLine)
 
413
{
 
414
    int32_t line, x, y;
 
415
    int32_t prevRun = 0;
 
416
    UErrorCode bidiStatus = U_ZERO_ERROR;
 
417
    UBiDi  *lBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
 
418
 
 
419
    y = fAscent;
 
420
 
 
421
    for (line = firstLine; line <= lastLine; line += 1) {
 
422
        int32_t firstChar = fBreakArray[line];
 
423
        int32_t lastChar  = fBreakArray[line + 1] - 1;
 
424
        int32_t dirCount, dirRun;
 
425
 
 
426
        x = MARGIN;
 
427
 
 
428
        ubidi_setLine(fBidi, firstChar, lastChar + 1, lBidi, &bidiStatus);
 
429
 
 
430
        dirCount = ubidi_countRuns(lBidi, &bidiStatus);
 
431
 
 
432
        for (dirRun = 0; dirRun < dirCount; dirRun += 1) {
 
433
            UTextOffset relStart = 0, runLength = 0;
 
434
            UBiDiDirection runDirection = ubidi_getVisualRun(lBidi, dirRun, &relStart, &runLength);
 
435
            int32_t runStart  = relStart + firstChar;
 
436
            int32_t runEnd    = runStart + runLength - 1;
 
437
            int32_t firstRun  = getCharRun(runStart, prevRun, 1);
 
438
            int32_t lastRun   = getCharRun(runEnd,   firstRun, 1);
 
439
 
 
440
            for (int32_t run = firstRun; run <= lastRun; run += 1) {
 
441
                const RenderingFontInstance *fontInstance = fRunInfo[run].fontInstance;
 
442
                int32_t nextBase;
 
443
 
 
444
                if (run == lastRun) {
 
445
                    nextBase = runEnd + 1;
 
446
                } else {
 
447
                    nextBase = fRunInfo[run + 1].charBase;
 
448
                }
 
449
 
 
450
                x += drawRun(surface, fontInstance, runStart, nextBase - 1, x, y);
 
451
                runStart = nextBase;
 
452
            }
 
453
 
 
454
            prevRun = lastRun;
 
455
        }
 
456
 
 
457
        y += fLineHeight;
 
458
    }
 
459
 
 
460
    ubidi_close(lBidi);
 
461
}
 
462
 
 
463
Paragraph *Paragraph::paragraphFactory(const char *fileName, FontMap *fontMap, GUISupport *guiSupport, void *surface)
 
464
{
 
465
    RunParams params[64];
 
466
    int32_t paramCount = 0;
 
467
    int32_t charCount  = 0;
 
468
    int32_t dirCount   = 0;
 
469
    int32_t dirRun     = 0;
 
470
    RFIErrorCode fontStatus = RFI_NO_ERROR;
 
471
    UErrorCode bidiStatus = U_ZERO_ERROR;
 
472
    const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount);
 
473
    ScriptRun scriptRun(text, charCount);
 
474
 
 
475
    if (text == NULL) {
 
476
        return NULL;
 
477
    }
 
478
 
 
479
    UBiDi *pBidi = ubidi_openSized(charCount, 0, &bidiStatus);
 
480
 
 
481
    ubidi_setPara(pBidi, text, charCount, UBIDI_DEFAULT_LTR, NULL, &bidiStatus);
 
482
 
 
483
    dirCount = ubidi_countRuns(pBidi, &bidiStatus);
 
484
 
 
485
    for (dirRun = 0; dirRun < dirCount; dirRun += 1) {
 
486
        UTextOffset runStart = 0, runLength = 0;
 
487
        UBiDiDirection runDirection = ubidi_getVisualRun(pBidi, dirRun, &runStart, &runLength);
 
488
        
 
489
        scriptRun.reset(runStart, runLength);
 
490
 
 
491
        while (scriptRun.next()) {
 
492
            int32_t     start = scriptRun.getScriptStart();
 
493
            int32_t     end   = scriptRun.getScriptEnd();
 
494
            UScriptCode code  = scriptRun.getScriptCode();
 
495
 
 
496
            params[paramCount].text = &((UChar *) text)[start];
 
497
            params[paramCount].count = end - start;
 
498
            params[paramCount].scriptCode = (UScriptCode) code;
 
499
            params[paramCount].rightToLeft = runDirection == UBIDI_RTL;
 
500
 
 
501
            params[paramCount].fontInstance = fontMap->getScriptFont(code, fontStatus);
 
502
 
 
503
            if (params[paramCount].fontInstance == NULL) {
 
504
                ubidi_close(pBidi);
 
505
                return 0;
 
506
            }
 
507
 
 
508
            paramCount += 1;
 
509
        }
 
510
    }
 
511
 
 
512
    return new Paragraph(surface, params, paramCount, pBidi);
 
513
}
 
514