| /* | |
| ******************************************************************************* | |
| * | |
| * © 2016 and later: Unicode, Inc. and others. | |
| * License & terms of use: http://www.unicode.org/copyright.html | |
| * | |
| ******************************************************************************* | |
| ******************************************************************************* | |
| * | |
| * Copyright (C) 1999-2015, International Business Machines | |
| * Corporation and others. All Rights Reserved. | |
| * | |
| ******************************************************************************* | |
| * file name: Paragraph.cpp | |
| * | |
| * created on: 09/06/2000 | |
| * created by: Eric R. Mader | |
| */ | |
| #include "unicode/utypes.h" | |
| #include "unicode/uchar.h" | |
| #include "unicode/ubidi.h" | |
| #include "unicode/ustring.h" | |
| #include "layout/ParagraphLayout.h" | |
| #include "RenderingSurface.h" | |
| #include "paragraph.h" | |
| #include "UnicodeReader.h" | |
| #define MARGIN 10 | |
| #define LINE_GROW 32 | |
| #define PARA_GROW 8 | |
| #define CH_LF 0x000A | |
| #define CH_CR 0x000D | |
| #define CH_LSEP 0x2028 | |
| #define CH_PSEP 0x2029 | |
| static LEUnicode *skipLineEnd(LEUnicode *ptr) | |
| { | |
| if (ptr[0] == CH_CR && ptr[1] == CH_LF) { | |
| ptr += 1; | |
| } | |
| return ptr + 1; | |
| } | |
| static le_int32 findRun(const RunArray *runArray, le_int32 offset) | |
| { | |
| le_int32 runCount = runArray->getCount(); | |
| for (le_int32 run = 0; run < runCount; run += 1) { | |
| if (runArray->getLimit(run) > offset) { | |
| return run; | |
| } | |
| } | |
| return -1; | |
| } | |
| static void subsetFontRuns(const FontRuns *fontRuns, le_int32 start, le_int32 limit, FontRuns *sub) | |
| { | |
| le_int32 startRun = findRun(fontRuns, start); | |
| le_int32 endRun = findRun(fontRuns, limit - 1); | |
| sub->reset(); | |
| for (le_int32 run = startRun; run <= endRun; run += 1) { | |
| const LEFontInstance *runFont = fontRuns->getFont(run); | |
| le_int32 runLimit = fontRuns->getLimit(run) - start; | |
| if (run == endRun) { | |
| runLimit = limit - start; | |
| } | |
| sub->add(runFont, runLimit); | |
| } | |
| } | |
| Paragraph::Paragraph(const LEUnicode chars[], int32_t charCount, const FontRuns *fontRuns, LEErrorCode &status) | |
| : fParagraphLayout(NULL), fParagraphCount(0), fParagraphMax(PARA_GROW), fParagraphGrow(PARA_GROW), | |
| fLineCount(0), fLinesMax(LINE_GROW), fLinesGrow(LINE_GROW), fLines(NULL), fChars(NULL), | |
| fLineHeight(-1), fAscent(-1), fWidth(-1), fHeight(-1), fParagraphLevel(UBIDI_DEFAULT_LTR) | |
| { | |
| static const LEUnicode separators[] = {CH_LF, CH_CR, CH_LSEP, CH_PSEP, 0x0000}; | |
| if (LE_FAILURE(status)) { | |
| return; | |
| } | |
| le_int32 ascent = 0; | |
| le_int32 descent = 0; | |
| le_int32 leading = 0; | |
| LocaleRuns *locales = NULL; | |
| FontRuns fr(0); | |
| fLines = LE_NEW_ARRAY(const ParagraphLayout::Line *, fLinesMax); | |
| fParagraphLayout = LE_NEW_ARRAY(ParagraphLayout *, fParagraphMax); | |
| fChars = LE_NEW_ARRAY(LEUnicode, charCount + 1); | |
| LE_ARRAY_COPY(fChars, chars, charCount); | |
| fChars[charCount] = 0; | |
| LEUnicode *pStart = &fChars[0]; | |
| while (*pStart != 0) { | |
| LEUnicode *pEnd = u_strpbrk(pStart, separators); | |
| le_int32 pAscent, pDescent, pLeading; | |
| ParagraphLayout *paragraphLayout = NULL; | |
| if (pEnd == NULL) { | |
| pEnd = &fChars[charCount]; | |
| } | |
| if (pEnd != pStart) { | |
| subsetFontRuns(fontRuns, pStart - fChars, pEnd - fChars, &fr); | |
| paragraphLayout = new ParagraphLayout(pStart, pEnd - pStart, &fr, NULL, NULL, locales, fParagraphLevel, FALSE, status); | |
| if (LE_FAILURE(status)) { | |
| delete paragraphLayout; | |
| break; // return? something else? | |
| } | |
| if (fParagraphLevel == UBIDI_DEFAULT_LTR) { | |
| fParagraphLevel = paragraphLayout->getParagraphLevel(); | |
| } | |
| pAscent = paragraphLayout->getAscent(); | |
| pDescent = paragraphLayout->getDescent(); | |
| pLeading = paragraphLayout->getLeading(); | |
| if (pAscent > ascent) { | |
| ascent = pAscent; | |
| } | |
| if (pDescent > descent) { | |
| descent = pDescent; | |
| } | |
| if (pLeading > leading) { | |
| leading = pLeading; | |
| } | |
| } | |
| if (fParagraphCount >= fParagraphMax) { | |
| fParagraphLayout = (ParagraphLayout **) LE_GROW_ARRAY(fParagraphLayout, fParagraphMax + fParagraphGrow); | |
| fParagraphMax += fParagraphGrow; | |
| } | |
| fParagraphLayout[fParagraphCount++] = paragraphLayout; | |
| if (*pEnd == 0) { | |
| break; | |
| } | |
| pStart = skipLineEnd(pEnd); | |
| } | |
| fLineHeight = ascent + descent + leading; | |
| fAscent = ascent; | |
| } | |
| Paragraph::~Paragraph() | |
| { | |
| for (le_int32 line = 0; line < fLineCount; line += 1) { | |
| delete /*(LineInfo *)*/ fLines[line]; | |
| } | |
| for (le_int32 paragraph = 0; paragraph < fParagraphCount; paragraph += 1) { | |
| delete fParagraphLayout[paragraph]; | |
| } | |
| LE_DELETE_ARRAY(fLines); | |
| LE_DELETE_ARRAY(fParagraphLayout); | |
| LE_DELETE_ARRAY(fChars); | |
| } | |
| void Paragraph::addLine(const ParagraphLayout::Line *line) | |
| { | |
| if (fLineCount >= fLinesMax) { | |
| fLines = (const ParagraphLayout::Line **) LE_GROW_ARRAY(fLines, fLinesMax + fLinesGrow); | |
| fLinesMax += fLinesGrow; | |
| } | |
| fLines[fLineCount++] = line; | |
| } | |
| 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); | |
| const ParagraphLayout::Line *line; | |
| // Free the old LineInfo's... | |
| for (le_int32 li = 0; li < fLineCount; li += 1) { | |
| delete fLines[li]; | |
| } | |
| fLineCount = 0; | |
| for (le_int32 p = 0; p < fParagraphCount; p += 1) { | |
| ParagraphLayout *paragraphLayout = fParagraphLayout[p]; | |
| if (paragraphLayout != NULL) { | |
| paragraphLayout->reflow(); | |
| while ((line = paragraphLayout->nextLine(lineWidth)) != NULL) { | |
| addLine(line); | |
| } | |
| } else { | |
| addLine(NULL); | |
| } | |
| } | |
| } | |
| void Paragraph::draw(RenderingSurface *surface, le_int32 firstLine, le_int32 lastLine) | |
| { | |
| le_int32 li, x, y; | |
| x = MARGIN; | |
| y = fAscent; | |
| for (li = firstLine; li <= lastLine; li += 1) { | |
| const ParagraphLayout::Line *line = fLines[li]; | |
| if (line != NULL) { | |
| le_int32 runCount = line->countRuns(); | |
| le_int32 run; | |
| if (fParagraphLevel == UBIDI_RTL) { | |
| le_int32 lastX = line->getWidth(); | |
| x = (fWidth - lastX - MARGIN); | |
| } | |
| for (run = 0; run < runCount; run += 1) { | |
| const ParagraphLayout::VisualRun *visualRun = line->getVisualRun(run); | |
| le_int32 glyphCount = visualRun->getGlyphCount(); | |
| const LEFontInstance *font = visualRun->getFont(); | |
| const LEGlyphID *glyphs = visualRun->getGlyphs(); | |
| const float *positions = visualRun->getPositions(); | |
| surface->drawGlyphs(font, glyphs, glyphCount, positions, x, y, fWidth, fHeight); | |
| } | |
| } | |
| y += fLineHeight; | |
| } | |
| } | |
| Paragraph *Paragraph::paragraphFactory(const char *fileName, const LEFontInstance *font, GUISupport *guiSupport) | |
| { | |
| LEErrorCode status = LE_NO_ERROR; | |
| le_int32 charCount; | |
| const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount); | |
| Paragraph *result = NULL; | |
| if (text == NULL) { | |
| return NULL; | |
| } | |
| FontRuns fontRuns(0); | |
| fontRuns.add(font, charCount); | |
| result = new Paragraph(text, charCount, &fontRuns, status); | |
| if (LE_FAILURE(status)) { | |
| delete result; | |
| result = NULL; | |
| } | |
| LE_DELETE_ARRAY(text); | |
| return result; | |
| } | |