| /* |
| ******************************************************************************* |
| * |
| * Copyright (C) 1999-2001, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| * |
| ******************************************************************************* |
| * file name: Paragraph.cpp |
| * |
| * created on: 09/06/2000 |
| * created by: Eric R. Mader |
| */ |
| |
| #include "loengine.h" |
| |
| #include "RenderingFontInstance.h" |
| |
| #include "unicode/utypes.h" |
| #include "unicode/uchar.h" |
| #include "unicode/ubidi.h" |
| #include "usc_impl.h" /* this is currently private! */ |
| |
| #include "paragraph.h" |
| #include "UnicodeReader.h" |
| #include "FontMap.h" |
| |
| #include "ParagraphLayout.h" |
| |
| #define MARGIN 10 |
| #define LINE_GROW 32 |
| |
| class LineRun |
| { |
| public: |
| LineRun(ParagraphLayout *paragraphLayout, le_int32 runIndex); |
| ~LineRun(); |
| |
| le_int32 draw(RenderingSurface *surface, le_int32 x, le_int32 y, le_int32 width, le_int32 height) const; |
| |
| private: |
| le_int32 fGlyphCount; |
| const RenderingFontInstance *fFont; |
| UBiDiDirection fDirection; |
| const LEGlyphID *fGlyphs; |
| le_int32 *fDX; |
| le_int32 *fDY; |
| }; |
| |
| LineRun::LineRun(ParagraphLayout *paragraphLayout, le_int32 runIndex) |
| { |
| LEGlyphID *glyphs; |
| float *positions; |
| le_int32 i; |
| |
| fGlyphCount = paragraphLayout->getVisualRun(runIndex, NULL, NULL, NULL, NULL, NULL); |
| |
| if (fGlyphCount <= 0) { |
| return; |
| } |
| |
| fGlyphs = new LEGlyphID[fGlyphCount]; |
| fDX = new le_int32[fGlyphCount]; |
| fDY = new le_int32[fGlyphCount]; |
| positions = new float[fGlyphCount * 2 + 2]; |
| |
| glyphs = (LEGlyphID *) fGlyphs; |
| |
| paragraphLayout->getVisualRun(runIndex, glyphs, (float *) positions, NULL, (const LEFontInstance **) &fFont, &fDirection); |
| |
| for (i = 0; i < fGlyphCount; i += 1) { |
| // filter out deleted glyphs |
| if (glyphs[i] == 0xFFFE || glyphs[i] == 0xFFFF) { |
| glyphs[i] = 0x0002; |
| } |
| |
| fDX[i] = (le_int32) (positions[i * 2 + 2] - positions[i * 2]); |
| fDY[i] = (le_int32) positions[i * 2 + 1]; |
| } |
| |
| delete[] positions; |
| } |
| |
| |
| LineRun::~LineRun() |
| { |
| delete[] fDY; |
| delete[] fDX; |
| delete[] (LEGlyphID *) fGlyphs; |
| } |
| |
| le_int32 LineRun::draw(RenderingSurface *surface, le_int32 x, le_int32 y, le_int32 width, le_int32 height) const |
| { |
| le_int32 dyEnd, dyStart; |
| |
| dyStart = dyEnd = 0; |
| |
| while (dyEnd < fGlyphCount) { |
| while (dyEnd < fGlyphCount && fDY[dyStart] == fDY[dyEnd]) { |
| dyEnd += 1; |
| } |
| |
| surface->drawGlyphs(fFont, &fGlyphs[dyStart], dyEnd - dyStart, |
| &fDX[dyStart], x, y + fDY[dyStart], width, height); |
| |
| for (le_int32 i = dyStart; i < dyEnd; i += 1) { |
| x += fDX[i]; |
| } |
| |
| dyStart = dyEnd; |
| } |
| |
| return x; |
| } |
| |
| class LineInfo |
| { |
| public: |
| LineInfo(ParagraphLayout *paragraphLayout); |
| ~LineInfo(); |
| |
| le_int32 getRunCount() const {return fRunCount;}; |
| void draw(RenderingSurface *surface, le_int32 x, le_int32 y, le_int32 width, le_int32 height); |
| |
| private: |
| le_int32 fRunCount; |
| LineRun **fRuns; |
| }; |
| |
| LineInfo::LineInfo(ParagraphLayout *paragraphLayout) |
| { |
| fRunCount = paragraphLayout->countLineRuns(); |
| fRuns = new LineRun *[fRunCount]; |
| le_int32 run; |
| |
| for (run = 0; run < fRunCount; run += 1) { |
| fRuns[run] = new LineRun(paragraphLayout, run); |
| } |
| } |
| |
| LineInfo::~LineInfo() |
| { |
| le_int32 run; |
| |
| for (run = 0; run < fRunCount; run += 1) { |
| delete fRuns[run]; |
| } |
| |
| delete[] fRuns; |
| } |
| |
| void LineInfo::draw(RenderingSurface *surface, le_int32 x, le_int32 y, le_int32 width, le_int32 height) |
| { |
| le_int32 run; |
| |
| for (run = 0; run < fRunCount; run += 1) { |
| x = fRuns[run]->draw(surface, x, y, width, height); |
| } |
| } |
| |
| Paragraph::Paragraph(const LEUnicode chars[], int32_t charCount, |
| const RenderingFontInstance *runFonts[], const UScriptCode runScripts[], |
| const le_int32 runLimits[], le_int32 runCount) |
| : fParagraphLayout(NULL), fLineCount(0), fLinesMax(0), fLinesGrow(LINE_GROW), fLines(NULL), |
| fLineHeight(-1), fAscent(-1), fWidth(-1), fHeight(-1) |
| { |
| le_int32 run, maxAscent = -1, maxDescent = -1, maxLeading = -1; |
| |
| for (run = 0; run < runCount; run += 1) { |
| le_int32 runAscent = runFonts[run]->getAscent(); |
| le_int32 runDescent = runFonts[run]->getDescent(); |
| le_int32 runLeading = runFonts[run]->getLeading(); |
| |
| |
| if (runAscent > maxAscent) { |
| maxAscent = runAscent; |
| } |
| |
| if (runDescent > maxDescent) { |
| maxDescent = runDescent; |
| } |
| |
| if (runLeading > maxLeading) { |
| maxLeading = runLeading; |
| } |
| } |
| |
| fLineHeight = maxAscent + maxDescent + maxLeading; |
| fAscent = maxAscent; |
| |
| // NOTE: We're passing the same array in for both font and script run limits... |
| fParagraphLayout = new ParagraphLayout(chars, charCount, (const LEFontInstance **) runFonts, runLimits, runCount, NULL, NULL, 0, |
| runScripts, runLimits, runCount, UBIDI_LTR, false); |
| } |
| |
| Paragraph::~Paragraph() |
| { |
| for (le_int32 line = 0; line < fLineCount; line += 1) { |
| delete (LineInfo *) fLines[line]; |
| } |
| |
| delete[] fLines; |
| } |
| |
| void Paragraph::breakLines(le_int32 width, le_int32 height) |
| { |
| fHeight = height; |
| |
| // don't re-break if the width hasn't changed |
| if (fWidth == width) { |
| return; |
| } |
| |
| fWidth = width; |
| |
| float lineWidth = (float) (width - 2 * MARGIN); |
| le_int32 line; |
| |
| // Free the old LineInfo's... |
| for (line = 0; line < fLineCount; line += 1) { |
| delete fLines[line]; |
| } |
| |
| line = 0; |
| fParagraphLayout->reflow(); |
| while (fParagraphLayout->nextLineBreak(lineWidth) >= 0) { |
| LineInfo *lineInfo = new LineInfo(fParagraphLayout); |
| |
| // grow the line array, if we need to. |
| if (line >= fLinesMax) { |
| LineInfo **newLines = new LineInfo *[fLinesMax + fLinesGrow]; |
| |
| if (fLines != NULL) { |
| LE_ARRAY_COPY(newLines, fLines, fLinesMax); |
| |
| delete[] fLines; |
| } |
| |
| fLinesMax += fLinesGrow; |
| fLines = newLines; |
| } |
| |
| fLines[line++] = lineInfo; |
| } |
| |
| fLineCount = line; |
| } |
| |
| void Paragraph::draw(RenderingSurface *surface, le_int32 firstLine, le_int32 lastLine) |
| { |
| le_int32 line, x, y; |
| |
| x = MARGIN; |
| y = fAscent; |
| |
| for (line = firstLine; line <= lastLine; line += 1) { |
| fLines[line]->draw(surface, x, y, fWidth, fHeight); |
| |
| y += fLineHeight; |
| } |
| } |
| |
| Paragraph *Paragraph::paragraphFactory(const char *fileName, FontMap *fontMap, GUISupport *guiSupport) |
| { |
| RFIErrorCode fontStatus = RFI_NO_ERROR; |
| UErrorCode scriptStatus = U_ZERO_ERROR; |
| le_int32 charCount, runCount; |
| UScriptRun *sr; |
| const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount); |
| |
| if (text == NULL) { |
| return NULL; |
| } |
| |
| sr = uscript_openRun(text, charCount, &scriptStatus); |
| |
| runCount = 0; |
| |
| while (uscript_nextRun(sr, NULL, NULL, NULL)) { |
| runCount += 1; |
| } |
| |
| uscript_resetRun(sr); |
| |
| UScriptCode *scripts = new UScriptCode[runCount]; |
| RenderingFontInstance **fonts = new RenderingFontInstance *[runCount]; |
| le_int32 *runLimits = new le_int32[runCount]; |
| |
| le_int32 limit, run; |
| UScriptCode script; |
| |
| run = 0; |
| while (uscript_nextRun(sr, NULL, &limit, &script)) { |
| scripts[run] = script; |
| runLimits[run] = limit; |
| fonts[run] = (RenderingFontInstance *) fontMap->getScriptFont(script, fontStatus); |
| |
| if (fonts[run] == NULL) { |
| delete[] runLimits; |
| delete[] fonts; |
| delete[] scripts; |
| |
| uscript_closeRun(sr); |
| return NULL; |
| } |
| |
| run += 1; |
| } |
| |
| uscript_closeRun(sr); |
| |
| return new Paragraph(text, charCount, (const RenderingFontInstance **) fonts, scripts, runLimits, runCount); |
| } |
| |