Home | History | Annotate | Download | only in layout
      1 /*
      2  *
      3  * (C) Copyright IBM Corp. 1998-2007 - All Rights Reserved
      4  *
      5  */
      6 
      7 #include "unicode/utypes.h"
      8 #include "unicode/uchar.h"
      9 #include "unicode/ubidi.h"
     10 #include "unicode/ustring.h"
     11 
     12 #include "layout/LETypes.h"
     13 
     14 #include "layout/loengine.h"
     15 #include "layout/playout.h"
     16 #include "layout/plruns.h"
     17 
     18 #include "pflow.h"
     19 
     20 #include "arraymem.h"
     21 #include "ucreader.h"
     22 
     23 /*
     24  * Move the line below out of this comment
     25  * to add a locale run to the pl_paragraphs
     26  * that are created.
     27 #define TEST_LOCALE "zh_TW"
     28  */
     29 
     30 #define MARGIN 10
     31 #define LINE_GROW 32
     32 #define PARA_GROW 8
     33 
     34 #define CH_LF 0x000A
     35 #define CH_CR 0x000D
     36 #define CH_LSEP 0x2028
     37 #define CH_PSEP 0x2029
     38 
     39 struct pf_object
     40 {
     41     pl_paragraph    **fParagraphLayout;
     42 
     43     le_int32          fParagraphCount;
     44     le_int32          fParagraphMax;
     45     le_int32          fParagraphGrow;
     46 
     47     le_int32          fLineCount;
     48     le_int32          fLinesMax;
     49     le_int32          fLinesGrow;
     50 
     51     pl_line         **fLines;
     52 
     53    LEUnicode         *fChars;
     54 
     55     le_int32          fLineHeight;
     56     le_int32          fAscent;
     57     le_int32          fWidth;
     58     le_int32          fHeight;
     59     UBiDiLevel        fParagraphLevel;
     60 };
     61 
     62 typedef struct pf_object pf_object;
     63 
     64 
     65 static LEUnicode *skipLineEnd(LEUnicode *ptr)
     66 {
     67     if (ptr[0] == CH_CR && ptr[1] == CH_LF) {
     68         ptr += 1;
     69     }
     70 
     71     return ptr + 1;
     72 }
     73 
     74 static le_int32 findFontRun(const pl_fontRuns *fontRuns, le_int32 offset)
     75 {
     76     le_int32 runCount = pl_getFontRunCount(fontRuns);
     77     le_int32 run;
     78 
     79     for (run = 0; run < runCount; run += 1) {
     80         if (pl_getFontRunLimit(fontRuns, run) > offset) {
     81             return run;
     82         }
     83     }
     84 
     85     return -1;
     86 }
     87 
     88 static void subsetFontRuns(const pl_fontRuns *fontRuns, le_int32 start, le_int32 limit, pl_fontRuns *sub)
     89 {
     90     le_int32 startRun = findFontRun(fontRuns, start);
     91     le_int32 endRun   = findFontRun(fontRuns, limit - 1);
     92     le_int32 run;
     93 
     94     pl_resetFontRuns(sub);
     95 
     96     for (run = startRun; run <= endRun; run += 1) {
     97         const le_font *runFont = pl_getFontRunFont(fontRuns, run);
     98         le_int32 runLimit = pl_getFontRunLimit(fontRuns, run) - start;
     99 
    100         if (run == endRun) {
    101             runLimit = limit - start;
    102         }
    103 
    104         pl_addFontRun(sub, runFont, runLimit);
    105     }
    106 }
    107 
    108 pf_flow *pf_create(const LEUnicode chars[], le_int32 charCount, const pl_fontRuns *fontRuns, LEErrorCode *status)
    109 {
    110     pf_object *flow;
    111     le_int32 ascent  = 0;
    112     le_int32 descent = 0;
    113     le_int32 leading = 0;
    114 	pl_localeRuns *locales = NULL;
    115     pl_fontRuns *fr;
    116     LEUnicode *pStart;
    117     static const LEUnicode separators[] = {CH_LF, CH_CR, CH_LSEP, CH_PSEP, 0x0000};
    118 
    119 	if (LE_FAILURE(*status)) {
    120 		return NULL;
    121 	}
    122 
    123     flow = NEW_ARRAY(pf_object, 1);
    124 
    125     flow->fParagraphLayout = NULL;
    126     flow->fParagraphCount  = 0;
    127     flow->fParagraphMax    = PARA_GROW;
    128     flow->fParagraphGrow   = PARA_GROW;
    129     flow->fLineCount       = 0;
    130     flow->fLinesMax        = LINE_GROW;
    131     flow->fLinesGrow       = LINE_GROW;
    132     flow->fLines           = NULL;
    133     flow->fChars           = NULL;
    134     flow->fLineHeight      = -1;
    135     flow->fAscent          = -1;
    136     flow->fWidth           = -1;
    137     flow->fHeight          = -1;
    138     flow->fParagraphLevel  = UBIDI_DEFAULT_LTR;
    139 
    140     fr = pl_openEmptyFontRuns(0);
    141 
    142 #ifdef TEST_LOCALE
    143     locales = pl_openEmptyLocaleRuns(0);
    144 #endif
    145 
    146     flow->fLines = NEW_ARRAY(pl_line *, flow->fLinesMax);
    147     flow->fParagraphLayout = NEW_ARRAY(pl_paragraph *, flow->fParagraphMax);
    148 
    149     flow->fChars = NEW_ARRAY(LEUnicode, charCount + 1);
    150     LE_ARRAY_COPY(flow->fChars, chars, charCount);
    151     flow->fChars[charCount] = 0;
    152 
    153     pStart = &flow->fChars[0];
    154 
    155     while (*pStart != 0) {
    156         LEUnicode *pEnd = u_strpbrk(pStart, separators);
    157         le_int32 pAscent, pDescent, pLeading;
    158         pl_paragraph *paragraphLayout = NULL;
    159 
    160         if (pEnd == NULL) {
    161             pEnd = &flow->fChars[charCount];
    162         }
    163 
    164         if (pEnd != pStart) {
    165             subsetFontRuns(fontRuns, pStart - flow->fChars, pEnd - flow->fChars, fr);
    166 
    167 #ifdef TEST_LOCALE
    168             pl_resetLocaleRuns(locales);
    169             pl_addLocaleRun(locales, TEST_LOCALE, pEnd - pStart);
    170 #endif
    171 
    172             paragraphLayout = pl_create(pStart, pEnd - pStart, fr, NULL, NULL, locales, flow->fParagraphLevel, FALSE, status);
    173 
    174             if (LE_FAILURE(*status)) {
    175                 break; /* return? something else? */
    176             }
    177 
    178             if (flow->fParagraphLevel == UBIDI_DEFAULT_LTR) {
    179                 flow->fParagraphLevel = pl_getParagraphLevel(paragraphLayout);
    180             }
    181 
    182             pAscent  = pl_getAscent(paragraphLayout);
    183             pDescent = pl_getDescent(paragraphLayout);
    184             pLeading = pl_getLeading(paragraphLayout);
    185 
    186             if (pAscent > ascent) {
    187                 ascent = pAscent;
    188             }
    189 
    190             if (pDescent > descent) {
    191                 descent = pDescent;
    192             }
    193 
    194             if (pLeading > leading) {
    195                 leading = pLeading;
    196             }
    197         }
    198 
    199         if (flow->fParagraphCount >= flow->fParagraphMax) {
    200             flow->fParagraphLayout = (pl_paragraph **) GROW_ARRAY(flow->fParagraphLayout, flow->fParagraphMax + flow->fParagraphGrow);
    201             flow->fParagraphMax += flow->fParagraphGrow;
    202         }
    203 
    204         flow->fParagraphLayout[flow->fParagraphCount++] = paragraphLayout;
    205 
    206         if (*pEnd == 0) {
    207             break;
    208         }
    209 
    210         pStart = skipLineEnd(pEnd);
    211     }
    212 
    213     flow->fLineHeight = ascent + descent + leading;
    214     flow->fAscent     = ascent;
    215 
    216     pl_closeLocaleRuns(locales);
    217     pl_closeFontRuns(fr);
    218 
    219     return (pf_flow *) flow;
    220 }
    221 
    222 void pf_close(pf_flow *flow)
    223 {
    224     pf_object *obj = (pf_object *) flow;
    225     le_int32 i;
    226 
    227     for (i = 0; i < obj->fLineCount; i += 1) {
    228         DELETE_ARRAY(obj->fLines[i]);
    229     }
    230 
    231     DELETE_ARRAY(obj->fLines);
    232 
    233     for (i = 0; i < obj->fParagraphCount; i += 1) {
    234         pl_close(obj->fParagraphLayout[i]);
    235     }
    236 
    237     DELETE_ARRAY(obj->fParagraphLayout);
    238 
    239     DELETE_ARRAY(obj->fChars);
    240 
    241     DELETE_ARRAY(obj);
    242 }
    243 
    244 
    245 le_int32 pf_getAscent(pf_flow *flow)
    246 {
    247     pf_object *obj = (pf_object *) flow;
    248 
    249     return obj->fAscent;
    250 }
    251 
    252 le_int32 pf_getLineHeight(pf_flow *flow)
    253 {
    254     pf_object *obj = (pf_object *) flow;
    255 
    256     return obj->fLineHeight;
    257 }
    258 
    259 le_int32 pf_getLineCount(pf_flow *flow)
    260 {
    261     pf_object *obj = (pf_object *) flow;
    262 
    263     return obj->fLineCount;
    264 }
    265 
    266 static void addLine(pf_object *obj, pl_line *line)
    267 {
    268     if (obj->fLineCount >= obj->fLinesMax) {
    269         obj->fLines = (pl_line **) GROW_ARRAY(obj->fLines, obj->fLinesMax + obj->fLinesGrow);
    270         obj->fLinesMax += obj->fLinesGrow;
    271     }
    272 
    273     obj->fLines[obj->fLineCount++] = line;
    274 }
    275 
    276 void pf_breakLines(pf_flow *flow, le_int32 width, le_int32 height)
    277 {
    278     pf_object *obj = (pf_object *) flow;
    279     le_int32 li, p;
    280     float lineWidth;
    281     pl_line *line;
    282 
    283     obj->fHeight = height;
    284 
    285     /* don't re-break if the width hasn't changed */
    286     if (obj->fWidth == width) {
    287         return;
    288     }
    289 
    290     obj->fWidth  = width;
    291 
    292     lineWidth = (float) (width - 2 * MARGIN);
    293 
    294     /* Free the old Lines... */
    295     for (li = 0; li < obj->fLineCount; li += 1) {
    296         pl_closeLine(obj->fLines[li]);
    297     }
    298 
    299     obj->fLineCount = 0;
    300 
    301     for (p = 0; p < obj->fParagraphCount; p += 1) {
    302         pl_paragraph *paragraphLayout = obj->fParagraphLayout[p];
    303 
    304         if (paragraphLayout != NULL) {
    305             pl_reflow(paragraphLayout);
    306             while ((line = pl_nextLine(paragraphLayout, lineWidth)) != NULL) {
    307                 addLine(obj, line);
    308             }
    309         } else {
    310             addLine(obj, NULL);
    311         }
    312     }
    313 }
    314 
    315 void pf_draw(pf_flow *flow, rs_surface *surface, le_int32 firstLine, le_int32 lastLine)
    316 {
    317     pf_object *obj = (pf_object *) flow;
    318     le_int32 li, x, y;
    319 
    320     x = MARGIN;
    321     y = obj->fAscent;
    322 
    323     for (li = firstLine; li <= lastLine; li += 1) {
    324         const pl_line *line = obj->fLines[li];
    325 
    326         if (line != NULL) {
    327             le_int32 runCount = pl_countLineRuns(line);
    328             le_int32 run;
    329 
    330 		    if (obj->fParagraphLevel == UBIDI_RTL) {
    331 			    le_int32 lastX = pl_getLineWidth(line);
    332 
    333 			    x = (obj->fWidth - lastX - MARGIN);
    334 		    }
    335 
    336 
    337             for (run = 0; run < runCount; run += 1) {
    338                 const pl_visualRun *visualRun = pl_getLineVisualRun(line, run);
    339                 le_int32 glyphCount = pl_getVisualRunGlyphCount(visualRun);
    340                 const le_font *font = pl_getVisualRunFont(visualRun);
    341                 const LEGlyphID *glyphs = pl_getVisualRunGlyphs(visualRun);
    342                 const float *positions = pl_getVisualRunPositions(visualRun);
    343 
    344                 rs_drawGlyphs(surface, font, glyphs, glyphCount, positions, x, y, obj->fWidth, obj->fHeight);
    345             }
    346         }
    347 
    348         y += obj->fLineHeight;
    349     }
    350 }
    351 
    352 pf_flow *pf_factory(const char *fileName, const le_font *font, gs_guiSupport *guiSupport)
    353 {
    354     LEErrorCode status  = LE_NO_ERROR;
    355     le_int32 charCount;
    356     const UChar *text = uc_readFile(fileName, guiSupport, &charCount);
    357     pl_fontRuns *fontRuns;
    358     pf_flow *result = NULL;
    359 
    360     if (text == NULL) {
    361         return NULL;
    362     }
    363 
    364     fontRuns = pl_openEmptyFontRuns(0);
    365 
    366     pl_addFontRun(fontRuns, font, charCount);
    367 
    368     result = pf_create(text, charCount, fontRuns, &status);
    369 
    370 	if (LE_FAILURE(status)) {
    371 		pf_close(result);
    372 		result = NULL;
    373 	}
    374 
    375     pl_closeFontRuns(fontRuns);
    376 
    377     DELETE_ARRAY(text);
    378 
    379     return result;
    380 }
    381 
    382