Home | History | Annotate | Download | only in layout
      1 /*
      2  *******************************************************************************
      3  *
      4  *   Copyright (C) 1999-2007, 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                 break; // return? something else?
    119             }
    120 
    121             if (fParagraphLevel == UBIDI_DEFAULT_LTR) {
    122                 fParagraphLevel = paragraphLayout->getParagraphLevel();
    123             }
    124 
    125             pAscent  = paragraphLayout->getAscent();
    126             pDescent = paragraphLayout->getDescent();
    127             pLeading = paragraphLayout->getLeading();
    128 
    129             if (pAscent > ascent) {
    130                 ascent = pAscent;
    131             }
    132 
    133             if (pDescent > descent) {
    134                 descent = pDescent;
    135             }
    136 
    137             if (pLeading > leading) {
    138                 leading = pLeading;
    139             }
    140         }
    141 
    142         if (fParagraphCount >= fParagraphMax) {
    143             fParagraphLayout = (ParagraphLayout **) LE_GROW_ARRAY(fParagraphLayout, fParagraphMax + fParagraphGrow);
    144             fParagraphMax += fParagraphGrow;
    145         }
    146 
    147         fParagraphLayout[fParagraphCount++] = paragraphLayout;
    148 
    149         if (*pEnd == 0) {
    150             break;
    151         }
    152 
    153         pStart = skipLineEnd(pEnd);
    154     }
    155 
    156     fLineHeight = ascent + descent + leading;
    157     fAscent     = ascent;
    158 }
    159 
    160 Paragraph::~Paragraph()
    161 {
    162     for (le_int32 line = 0; line < fLineCount; line += 1) {
    163         delete /*(LineInfo *)*/ fLines[line];
    164     }
    165 
    166     LE_DELETE_ARRAY(fLines);
    167     delete fParagraphLayout;
    168     LE_DELETE_ARRAY(fChars);
    169 }
    170 
    171 void Paragraph::addLine(const ParagraphLayout::Line *line)
    172 {
    173     if (fLineCount >= fLinesMax) {
    174         fLines = (const ParagraphLayout::Line **) LE_GROW_ARRAY(fLines, fLinesMax + fLinesGrow);
    175         fLinesMax += fLinesGrow;
    176     }
    177 
    178     fLines[fLineCount++] = line;
    179 }
    180 
    181 void Paragraph::breakLines(le_int32 width, le_int32 height)
    182 {
    183     fHeight = height;
    184 
    185     // don't re-break if the width hasn't changed
    186     if (fWidth == width) {
    187         return;
    188     }
    189 
    190     fWidth  = width;
    191 
    192     float lineWidth = (float) (width - 2 * MARGIN);
    193     const ParagraphLayout::Line *line;
    194 
    195     // Free the old LineInfo's...
    196     for (le_int32 li = 0; li < fLineCount; li += 1) {
    197         delete fLines[li];
    198     }
    199 
    200     fLineCount = 0;
    201 
    202     for (le_int32 p = 0; p < fParagraphCount; p += 1) {
    203         ParagraphLayout *paragraphLayout = fParagraphLayout[p];
    204 
    205         if (paragraphLayout != NULL) {
    206             paragraphLayout->reflow();
    207             while ((line = paragraphLayout->nextLine(lineWidth)) != NULL) {
    208                 addLine(line);
    209             }
    210         } else {
    211             addLine(NULL);
    212         }
    213     }
    214 }
    215 
    216 void Paragraph::draw(RenderingSurface *surface, le_int32 firstLine, le_int32 lastLine)
    217 {
    218     le_int32 li, x, y;
    219 
    220     x = MARGIN;
    221     y = fAscent;
    222 
    223     for (li = firstLine; li <= lastLine; li += 1) {
    224         const ParagraphLayout::Line *line = fLines[li];
    225 
    226         if (line != NULL) {
    227             le_int32 runCount = line->countRuns();
    228             le_int32 run;
    229 
    230 		    if (fParagraphLevel == UBIDI_RTL) {
    231 			    le_int32 lastX = line->getWidth();
    232 
    233 			    x = (fWidth - lastX - MARGIN);
    234 		    }
    235 
    236 
    237             for (run = 0; run < runCount; run += 1) {
    238                 const ParagraphLayout::VisualRun *visualRun = line->getVisualRun(run);
    239                 le_int32 glyphCount = visualRun->getGlyphCount();
    240                 const LEFontInstance *font = visualRun->getFont();
    241                 const LEGlyphID *glyphs = visualRun->getGlyphs();
    242                 const float *positions = visualRun->getPositions();
    243 
    244                 surface->drawGlyphs(font, glyphs, glyphCount, positions, x, y, fWidth, fHeight);
    245             }
    246         }
    247 
    248         y += fLineHeight;
    249     }
    250 }
    251 
    252 Paragraph *Paragraph::paragraphFactory(const char *fileName, const LEFontInstance *font, GUISupport *guiSupport)
    253 {
    254     LEErrorCode status  = LE_NO_ERROR;
    255     le_int32 charCount;
    256     const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount);
    257     Paragraph *result = NULL;
    258 
    259     if (text == NULL) {
    260         return NULL;
    261     }
    262 
    263     FontRuns  fontRuns(0);
    264 
    265     fontRuns.add(font, charCount);
    266 
    267     result = new Paragraph(text, charCount, &fontRuns, status);
    268 
    269 	if (LE_FAILURE(status)) {
    270 		delete result;
    271 		result = NULL;
    272 	}
    273 
    274     LE_DELETE_ARRAY(text);
    275 
    276     return result;
    277 }
    278 
    279