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