Home | History | Annotate | Download | only in layout
      1 /*
      2  *******************************************************************************
      3  *
      4  *   Copyright (C) 1999-2015, 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/utypes.h"
     15 #include "unicode/uchar.h"
     16 #include "unicode/ubidi.h"
     17 #include "unicode/ustring.h"
     18 
     19 #include "layout/ParagraphLayout.h"
     20 
     21 #include "RenderingSurface.h"
     22 
     23 #include "paragraph.h"
     24 #include "UnicodeReader.h"
     25 
     26 #define MARGIN 10
     27 #define LINE_GROW 32
     28 #define PARA_GROW 8
     29 
     30 #define CH_LF 0x000A
     31 #define CH_CR 0x000D
     32 #define CH_LSEP 0x2028
     33 #define CH_PSEP 0x2029
     34 
     35 static LEUnicode *skipLineEnd(LEUnicode *ptr)
     36 {
     37     if (ptr[0] == CH_CR && ptr[1] == CH_LF) {
     38         ptr += 1;
     39     }
     40 
     41     return ptr + 1;
     42 }
     43 
     44 static le_int32 findRun(const RunArray *runArray, le_int32 offset)
     45 {
     46     le_int32 runCount = runArray->getCount();
     47 
     48     for (le_int32 run = 0; run < runCount; run += 1) {
     49         if (runArray->getLimit(run) > offset) {
     50             return run;
     51         }
     52     }
     53 
     54     return -1;
     55 }
     56 
     57 static void subsetFontRuns(const FontRuns *fontRuns, le_int32 start, le_int32 limit, FontRuns *sub)
     58 {
     59     le_int32 startRun = findRun(fontRuns, start);
     60     le_int32 endRun   = findRun(fontRuns, limit - 1);
     61 
     62     sub->reset();
     63 
     64     for (le_int32 run = startRun; run <= endRun; run += 1) {
     65         const LEFontInstance *runFont = fontRuns->getFont(run);
     66         le_int32 runLimit = fontRuns->getLimit(run) - start;
     67 
     68         if (run == endRun) {
     69             runLimit = limit - start;
     70         }
     71 
     72         sub->add(runFont, runLimit);
     73     }
     74 }
     75 
     76 Paragraph::Paragraph(const LEUnicode chars[], int32_t charCount, const FontRuns *fontRuns, LEErrorCode &status)
     77   : fParagraphLayout(NULL), fParagraphCount(0), fParagraphMax(PARA_GROW), fParagraphGrow(PARA_GROW),
     78     fLineCount(0), fLinesMax(LINE_GROW), fLinesGrow(LINE_GROW), fLines(NULL), fChars(NULL),
     79     fLineHeight(-1), fAscent(-1), fWidth(-1), fHeight(-1), fParagraphLevel(UBIDI_DEFAULT_LTR)
     80 {
     81     static const LEUnicode separators[] = {CH_LF, CH_CR, CH_LSEP, CH_PSEP, 0x0000};
     82 
     83 	if (LE_FAILURE(status)) {
     84 		return;
     85 	}
     86 
     87     le_int32 ascent  = 0;
     88     le_int32 descent = 0;
     89     le_int32 leading = 0;
     90 
     91 	LocaleRuns *locales = NULL;
     92     FontRuns fr(0);
     93 
     94     fLines = LE_NEW_ARRAY(const ParagraphLayout::Line *, fLinesMax);
     95     fParagraphLayout = LE_NEW_ARRAY(ParagraphLayout *, fParagraphMax);
     96 
     97     fChars = LE_NEW_ARRAY(LEUnicode, charCount + 1);
     98     LE_ARRAY_COPY(fChars, chars, charCount);
     99     fChars[charCount] = 0;
    100 
    101     LEUnicode *pStart = &fChars[0];
    102 
    103     while (*pStart != 0) {
    104         LEUnicode *pEnd = u_strpbrk(pStart, separators);
    105         le_int32 pAscent, pDescent, pLeading;
    106         ParagraphLayout *paragraphLayout = NULL;
    107 
    108         if (pEnd == NULL) {
    109             pEnd = &fChars[charCount];
    110         }
    111 
    112         if (pEnd != pStart) {
    113             subsetFontRuns(fontRuns, pStart - fChars, pEnd - fChars, &fr);
    114 
    115             paragraphLayout = new ParagraphLayout(pStart, pEnd - pStart, &fr, NULL, NULL, locales, fParagraphLevel, FALSE, status);
    116 
    117             if (LE_FAILURE(status)) {
    118                 delete paragraphLayout;
    119                 break; // return? something else?
    120             }
    121 
    122             if (fParagraphLevel == UBIDI_DEFAULT_LTR) {
    123                 fParagraphLevel = paragraphLayout->getParagraphLevel();
    124             }
    125 
    126             pAscent  = paragraphLayout->getAscent();
    127             pDescent = paragraphLayout->getDescent();
    128             pLeading = paragraphLayout->getLeading();
    129 
    130             if (pAscent > ascent) {
    131                 ascent = pAscent;
    132             }
    133 
    134             if (pDescent > descent) {
    135                 descent = pDescent;
    136             }
    137 
    138             if (pLeading > leading) {
    139                 leading = pLeading;
    140             }
    141         }
    142 
    143         if (fParagraphCount >= fParagraphMax) {
    144             fParagraphLayout = (ParagraphLayout **) LE_GROW_ARRAY(fParagraphLayout, fParagraphMax + fParagraphGrow);
    145             fParagraphMax += fParagraphGrow;
    146         }
    147 
    148         fParagraphLayout[fParagraphCount++] = paragraphLayout;
    149 
    150         if (*pEnd == 0) {
    151             break;
    152         }
    153 
    154         pStart = skipLineEnd(pEnd);
    155     }
    156 
    157     fLineHeight = ascent + descent + leading;
    158     fAscent     = ascent;
    159 }
    160 
    161 Paragraph::~Paragraph()
    162 {
    163     for (le_int32 line = 0; line < fLineCount; line += 1) {
    164         delete /*(LineInfo *)*/ fLines[line];
    165     }
    166 
    167     for (le_int32 paragraph = 0; paragraph < fParagraphCount; paragraph += 1) {
    168         delete fParagraphLayout[paragraph];
    169     }
    170 
    171     LE_DELETE_ARRAY(fLines);
    172     LE_DELETE_ARRAY(fParagraphLayout);
    173     LE_DELETE_ARRAY(fChars);
    174 }
    175 
    176 void Paragraph::addLine(const ParagraphLayout::Line *line)
    177 {
    178     if (fLineCount >= fLinesMax) {
    179         fLines = (const ParagraphLayout::Line **) LE_GROW_ARRAY(fLines, fLinesMax + fLinesGrow);
    180         fLinesMax += fLinesGrow;
    181     }
    182 
    183     fLines[fLineCount++] = line;
    184 }
    185 
    186 void Paragraph::breakLines(le_int32 width, le_int32 height)
    187 {
    188     fHeight = height;
    189 
    190     // don't re-break if the width hasn't changed
    191     if (fWidth == width) {
    192         return;
    193     }
    194 
    195     fWidth  = width;
    196 
    197     float lineWidth = (float) (width - 2 * MARGIN);
    198     const ParagraphLayout::Line *line;
    199 
    200     // Free the old LineInfo's...
    201     for (le_int32 li = 0; li < fLineCount; li += 1) {
    202         delete fLines[li];
    203     }
    204 
    205     fLineCount = 0;
    206 
    207     for (le_int32 p = 0; p < fParagraphCount; p += 1) {
    208         ParagraphLayout *paragraphLayout = fParagraphLayout[p];
    209 
    210         if (paragraphLayout != NULL) {
    211             paragraphLayout->reflow();
    212             while ((line = paragraphLayout->nextLine(lineWidth)) != NULL) {
    213                 addLine(line);
    214             }
    215         } else {
    216             addLine(NULL);
    217         }
    218     }
    219 }
    220 
    221 void Paragraph::draw(RenderingSurface *surface, le_int32 firstLine, le_int32 lastLine)
    222 {
    223     le_int32 li, x, y;
    224 
    225     x = MARGIN;
    226     y = fAscent;
    227 
    228     for (li = firstLine; li <= lastLine; li += 1) {
    229         const ParagraphLayout::Line *line = fLines[li];
    230 
    231         if (line != NULL) {
    232             le_int32 runCount = line->countRuns();
    233             le_int32 run;
    234 
    235 		    if (fParagraphLevel == UBIDI_RTL) {
    236 			    le_int32 lastX = line->getWidth();
    237 
    238 			    x = (fWidth - lastX - MARGIN);
    239 		    }
    240 
    241 
    242             for (run = 0; run < runCount; run += 1) {
    243                 const ParagraphLayout::VisualRun *visualRun = line->getVisualRun(run);
    244                 le_int32 glyphCount = visualRun->getGlyphCount();
    245                 const LEFontInstance *font = visualRun->getFont();
    246                 const LEGlyphID *glyphs = visualRun->getGlyphs();
    247                 const float *positions = visualRun->getPositions();
    248 
    249                 surface->drawGlyphs(font, glyphs, glyphCount, positions, x, y, fWidth, fHeight);
    250             }
    251         }
    252 
    253         y += fLineHeight;
    254     }
    255 }
    256 
    257 Paragraph *Paragraph::paragraphFactory(const char *fileName, const LEFontInstance *font, GUISupport *guiSupport)
    258 {
    259     LEErrorCode status  = LE_NO_ERROR;
    260     le_int32 charCount;
    261     const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount);
    262     Paragraph *result = NULL;
    263 
    264     if (text == NULL) {
    265         return NULL;
    266     }
    267 
    268     FontRuns  fontRuns(0);
    269 
    270     fontRuns.add(font, charCount);
    271 
    272     result = new Paragraph(text, charCount, &fontRuns, status);
    273 
    274 	if (LE_FAILURE(status)) {
    275 		delete result;
    276 		result = NULL;
    277 	}
    278 
    279     LE_DELETE_ARRAY(text);
    280 
    281     return result;
    282 }
    283 
    284