2
*******************************************************************************
4
* Copyright (C) 1999-2001, International Business Machines
5
* Corporation and others. All Rights Reserved.
7
*******************************************************************************
8
* file name: Paragraph.cpp
10
* created on: 09/06/2000
11
* created by: Eric R. Mader
14
#include "unicode/loengine.h"
16
#include "RenderingFontInstance.h"
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"
25
#include "paragraph.h"
27
#include "UnicodeReader.h"
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)
41
fRunInfo = new RunInfo[count + 1];
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;
51
// Set charBase and rightToLeft for the
52
// fake run at the end.
53
fRunInfo[count].charBase = fCharCount;
54
fRunInfo[count].rightToLeft = false;
56
fBreakArray = new int32_t[fCharCount + 1];
57
fText = new LEUnicode[fCharCount];
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;
64
LE_ARRAY_COPY(&fText[charBase], params[i].text, charCount);
68
UCharCharacterIterator *iter = new UCharCharacterIterator(fText, fCharCount);
69
UErrorCode status = U_ZERO_ERROR;
72
fBrkiter = BreakIterator::createLineInstance(thai, status);
73
fBrkiter->adoptText(iter);
75
ICULayoutEngine **engines = new ICULayoutEngine *[count];
76
int32_t maxAscent = -1, maxDescent = -1, maxLeading = -1;
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;
88
fRunInfo[i].fontInstance = params[i].fontInstance;
90
fRunInfo[i].fontInstance->setFont(surface);
92
runAscent = fRunInfo[i].fontInstance->getAscent();
93
runDescent = fRunInfo[i].fontInstance->getDescent();
94
runLeading = fRunInfo[i].fontInstance->getLeading();
97
if (runAscent > maxAscent) {
98
maxAscent = runAscent;
101
if (runDescent > maxDescent) {
102
maxDescent = runDescent;
105
if (runLeading > maxLeading) {
106
maxLeading = runLeading;
109
engines[i] = ICULayoutEngine::createInstance(fRunInfo[i].fontInstance, params[i].scriptCode, dummyLocale, success);
111
glyphCount = engines[i]->layoutChars(fText, charBase, charBase + charCount, fCharCount,
112
fRunInfo[i].rightToLeft, x, y, success);
114
engines[i]->getGlyphPosition(glyphCount, x, y, success);
116
fRunInfo[i].glyphBase = fGlyphCount;
117
fGlyphCount += glyphCount;
120
fLineHeight = maxAscent + maxDescent + maxLeading;
123
// Set glyphBase for the fake run at the end
124
fRunInfo[count].glyphBase = fGlyphCount;
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];
133
float *positions = new float[fGlyphCount * 2 + 2];
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;
142
engine->getGlyphs(&fGlyphs[glyphBase], success);
143
engine->getCharIndices(&fCharIndices[glyphBase], charBase, success);
144
engine->getGlyphPositions(&positions[glyphBase * 2], success);
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) {
155
// compute the logical advance
156
fDX[i] = (int32_t) (positions[i * 2 + 2] - positions[i * 2]);
159
fDY[i] = (int32_t) positions[i * 2 + 1];
161
// set char to glyph map
162
fGlyphIndices[fCharIndices[i]] = i;
165
if (fRunInfo[count - 1].rightToLeft) {
166
fGlyphIndices[fCharCount] = fRunInfo[count - 1].glyphBase - 1;
168
fGlyphIndices[fCharCount] = fGlyphCount;
173
// Get rid of the LayoutEngine's:
174
for (i = 0; i < count; i += 1) {
181
Paragraph::~Paragraph()
185
delete[] fGlyphIndices;
186
delete[] fCharIndices;
192
delete[] fBreakArray;
198
int32_t Paragraph::getLineHeight()
203
int32_t Paragraph::getLineCount()
208
int32_t Paragraph::getAscent()
213
int32_t Paragraph::previousBreak(int32_t charIndex)
215
LEUnicode ch = fText[charIndex];
217
// skip over any whitespace or control
218
// characters, because they can hang in
220
while (charIndex < fCharCount &&
221
(Unicode::isWhitespace(ch) ||
222
Unicode::isControl(ch))) {
223
ch = fText[++charIndex];
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
230
return fBrkiter->preceding(charIndex + 1);
233
void Paragraph::breakLines(int32_t width, int32_t height)
235
int32_t lineWidth = width - (2 * MARGIN);
236
int32_t thisWidth = 0;
237
int32_t thisBreak = -1;
238
int32_t prevWidth = fWidth;
243
// don't re-break if the width hasn't changed
244
if (width == prevWidth) {
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;
256
if (fRunInfo[run].rightToLeft) {
258
stop = fRunInfo[run].glyphBase - 1;
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];
273
// Check to see if we fell off the
280
// Find a place before here to break,
281
thisBreak = previousBreak(fCharIndices[glyph]);
283
// If there wasn't one, force one
284
if (thisBreak <= fBreakArray[fBreakCount - 1]) {
285
thisBreak = fCharIndices[glyph];
288
// Save the break location.
289
fBreakArray[fBreakCount++] = thisBreak;
291
// Reset the accumulated width
294
// Map the character back to a glyph
295
glyph = fGlyphIndices[thisBreak];
297
// Check to see if the new glyph is off
298
// the end of the run.
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);
308
if (fRunInfo[run].rightToLeft) {
309
stop = fRunInfo[run].glyphBase - 1;
312
stop = fRunInfo[run + 1].glyphBase;
319
// Make sure the last break is after the last character
320
if (fBreakArray[--fBreakCount] != fCharCount) {
321
fBreakArray[++fBreakCount] = fCharCount;
327
int32_t Paragraph::getGlyphRun(int32_t glyph, int32_t startingRun, int32_t direction)
337
for (int32_t run = startingRun; run != limit; run += direction) {
338
if (glyph >= fRunInfo[run].glyphBase && glyph < fRunInfo[run + 1].glyphBase) {
346
int32_t Paragraph::getCharRun(int32_t ch, int32_t startingRun, int32_t direction)
356
for (int32_t run = startingRun; run != limit; run += direction) {
357
if (ch >= fRunInfo[run].charBase && ch < fRunInfo[run + 1].charBase) {
365
int32_t Paragraph::getRunWidth(int32_t startGlyph, int32_t endGlyph)
369
for (int32_t glyph = startGlyph; glyph <= endGlyph; glyph += 1) {
376
int32_t Paragraph::drawRun(void *surface, const RenderingFontInstance *fontInstance, int32_t firstChar, int32_t lastChar,
377
int32_t x, int32_t y)
379
int32_t firstGlyph = fGlyphIndices[firstChar];
380
int32_t lastGlyph = fGlyphIndices[lastChar];
382
for (int32_t ch = firstChar; ch <= lastChar; ch += 1) {
383
int32_t glyph = fGlyphIndices[ch];
385
if (glyph < firstGlyph) {
389
if (glyph > lastGlyph) {
394
int32_t dyStart = firstGlyph, dyEnd = dyStart;
396
fontInstance->setFont(surface);
398
while (dyEnd <= lastGlyph) {
399
while (dyEnd <= lastGlyph && fDY[dyStart] == fDY[dyEnd]) {
403
fontInstance->drawGlyphs(surface, &fGlyphs[dyStart], dyEnd - dyStart,
404
&fDX[dyStart], x, y + fDY[dyStart], fWidth, fHeight);
409
return getRunWidth(firstGlyph, lastGlyph);
412
void Paragraph::draw(void *surface, int32_t firstLine, int32_t lastLine)
416
UErrorCode bidiStatus = U_ZERO_ERROR;
417
UBiDi *lBidi = ubidi_openSized(fCharCount, 0, &bidiStatus);
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;
428
ubidi_setLine(fBidi, firstChar, lastChar + 1, lBidi, &bidiStatus);
430
dirCount = ubidi_countRuns(lBidi, &bidiStatus);
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);
440
for (int32_t run = firstRun; run <= lastRun; run += 1) {
441
const RenderingFontInstance *fontInstance = fRunInfo[run].fontInstance;
444
if (run == lastRun) {
445
nextBase = runEnd + 1;
447
nextBase = fRunInfo[run + 1].charBase;
450
x += drawRun(surface, fontInstance, runStart, nextBase - 1, x, y);
463
Paragraph *Paragraph::paragraphFactory(const char *fileName, FontMap *fontMap, GUISupport *guiSupport, void *surface)
465
RunParams params[64];
466
int32_t paramCount = 0;
467
int32_t charCount = 0;
468
int32_t dirCount = 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);
479
UBiDi *pBidi = ubidi_openSized(charCount, 0, &bidiStatus);
481
ubidi_setPara(pBidi, text, charCount, UBIDI_DEFAULT_LTR, NULL, &bidiStatus);
483
dirCount = ubidi_countRuns(pBidi, &bidiStatus);
485
for (dirRun = 0; dirRun < dirCount; dirRun += 1) {
486
UTextOffset runStart = 0, runLength = 0;
487
UBiDiDirection runDirection = ubidi_getVisualRun(pBidi, dirRun, &runStart, &runLength);
489
scriptRun.reset(runStart, runLength);
491
while (scriptRun.next()) {
492
int32_t start = scriptRun.getScriptStart();
493
int32_t end = scriptRun.getScriptEnd();
494
UScriptCode code = scriptRun.getScriptCode();
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;
501
params[paramCount].fontInstance = fontMap->getScriptFont(code, fontStatus);
503
if (params[paramCount].fontInstance == NULL) {
512
return new Paragraph(surface, params, paramCount, pBidi);