Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. #include "fitz.h"
  2.  
  3. #define LINE_DIST 0.9f
  4. #define SPACE_DIST 0.2f
  5.  
  6. #include <ft2build.h>
  7. #include FT_FREETYPE_H
  8. #include FT_ADVANCES_H
  9.  
  10. typedef struct fz_text_device_s fz_text_device;
  11.  
  12. struct fz_text_device_s
  13. {
  14.         fz_point point;
  15.         fz_text_span *head;
  16.         fz_text_span *span;
  17. };
  18.  
  19. fz_text_span *
  20. fz_new_text_span(void)
  21. {
  22.         fz_text_span *span;
  23.         span = fz_malloc(sizeof(fz_text_span));
  24.         span->font = NULL;
  25.         span->wmode = 0;
  26.         span->size = 0;
  27.         span->len = 0;
  28.         span->cap = 0;
  29.         span->text = NULL;
  30.         span->next = NULL;
  31.         span->eol = 0;
  32.         return span;
  33. }
  34.  
  35. void
  36. fz_free_text_span(fz_text_span *span)
  37. {
  38.         if (span->font)
  39.                 fz_drop_font(span->font);
  40.         if (span->next)
  41.                 fz_free_text_span(span->next);
  42.         fz_free(span->text);
  43.         fz_free(span);
  44. }
  45.  
  46. static void
  47. fz_add_text_char_imp(fz_text_span *span, int c, fz_bbox bbox)
  48. {
  49.         if (span->len + 1 >= span->cap)
  50.         {
  51.                 span->cap = span->cap > 1 ? (span->cap * 3) / 2 : 80;
  52.                 span->text = fz_realloc(span->text, span->cap, sizeof(fz_text_char));
  53.         }
  54.         span->text[span->len].c = c;
  55.         span->text[span->len].bbox = bbox;
  56.         span->len ++;
  57. }
  58.  
  59. static fz_bbox
  60. fz_split_bbox(fz_bbox bbox, int i, int n)
  61. {
  62.         float w = (float)(bbox.x1 - bbox.x0) / n;
  63.         float x0 = bbox.x0;
  64.         bbox.x0 = x0 + i * w;
  65.         bbox.x1 = x0 + (i + 1) * w;
  66.         return bbox;
  67. }
  68.  
  69. static void
  70. fz_add_text_char(fz_text_span **last, fz_font *font, float size, int wmode, int c, fz_bbox bbox)
  71. {
  72.         fz_text_span *span = *last;
  73.  
  74.         if (!span->font)
  75.         {
  76.                 span->font = fz_keep_font(font);
  77.                 span->size = size;
  78.         }
  79.  
  80.         if ((span->font != font || span->size != size || span->wmode != wmode) && c != 32)
  81.         {
  82.                 span = fz_new_text_span();
  83.                 span->font = fz_keep_font(font);
  84.                 span->size = size;
  85.                 span->wmode = wmode;
  86.                 (*last)->next = span;
  87.                 *last = span;
  88.         }
  89.  
  90.         switch (c)
  91.         {
  92.         case -1: /* ignore when one unicode character maps to multiple glyphs */
  93.                 break;
  94.         case 0xFB00: /* ff */
  95.                 fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 2));
  96.                 fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 1, 2));
  97.                 break;
  98.         case 0xFB01: /* fi */
  99.                 fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 2));
  100.                 fz_add_text_char_imp(span, 'i', fz_split_bbox(bbox, 1, 2));
  101.                 break;
  102.         case 0xFB02: /* fl */
  103.                 fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 2));
  104.                 fz_add_text_char_imp(span, 'l', fz_split_bbox(bbox, 1, 2));
  105.                 break;
  106.         case 0xFB03: /* ffi */
  107.                 fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 3));
  108.                 fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 1, 3));
  109.                 fz_add_text_char_imp(span, 'i', fz_split_bbox(bbox, 2, 3));
  110.                 break;
  111.         case 0xFB04: /* ffl */
  112.                 fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 0, 3));
  113.                 fz_add_text_char_imp(span, 'f', fz_split_bbox(bbox, 1, 3));
  114.                 fz_add_text_char_imp(span, 'l', fz_split_bbox(bbox, 2, 3));
  115.                 break;
  116.         case 0xFB05: /* long st */
  117.         case 0xFB06: /* st */
  118.                 fz_add_text_char_imp(span, 's', fz_split_bbox(bbox, 0, 2));
  119.                 fz_add_text_char_imp(span, 't', fz_split_bbox(bbox, 1, 2));
  120.                 break;
  121.         default:
  122.                 fz_add_text_char_imp(span, c, bbox);
  123.                 break;
  124.         }
  125. }
  126.  
  127. static void
  128. fz_divide_text_chars(fz_text_span **last, int n, fz_bbox bbox)
  129. {
  130.         fz_text_span *span = *last;
  131.         int i, x;
  132.         x = span->len - n;
  133.         if (x >= 0)
  134.                 for (i = 0; i < n; i++)
  135.                         span->text[x + i].bbox = fz_split_bbox(bbox, i, n);
  136. }
  137.  
  138. static void
  139. fz_add_text_newline(fz_text_span **last, fz_font *font, float size, int wmode)
  140. {
  141.         fz_text_span *span;
  142.         span = fz_new_text_span();
  143.         span->font = fz_keep_font(font);
  144.         span->size = size;
  145.         span->wmode = wmode;
  146.         (*last)->eol = 1;
  147.         (*last)->next = span;
  148.         *last = span;
  149. }
  150.  
  151. void
  152. fz_debug_text_span_xml(fz_text_span *span)
  153. {
  154.         char buf[10];
  155.         int c, n, k, i;
  156.  
  157.         printf("<span font=\"%s\" size=\"%g\" wmode=\"%d\" eol=\"%d\">\n",
  158.                 span->font ? span->font->name : "NULL", span->size, span->wmode, span->eol);
  159.  
  160.         for (i = 0; i < span->len; i++)
  161.         {
  162.                 printf("\t<char ucs=\"");
  163.                 c = span->text[i].c;
  164.                 if (c < 128)
  165.                         putchar(c);
  166.                 else
  167.                 {
  168.                         n = runetochar(buf, &c);
  169.                         for (k = 0; k < n; k++)
  170.                                 putchar(buf[k]);
  171.                 }
  172.                 printf("\" bbox=\"%d %d %d %d\" />\n",
  173.                         span->text[i].bbox.x0,
  174.                         span->text[i].bbox.y0,
  175.                         span->text[i].bbox.x1,
  176.                         span->text[i].bbox.y1);
  177.         }
  178.  
  179.         printf("</span>\n");
  180.  
  181.         if (span->next)
  182.                 fz_debug_text_span_xml(span->next);
  183. }
  184.  
  185. void
  186. fz_debug_text_span(fz_text_span *span)
  187. {
  188.         char buf[10];
  189.         int c, n, k, i;
  190.  
  191.         for (i = 0; i < span->len; i++)
  192.         {
  193.                 c = span->text[i].c;
  194.                 if (c < 128)
  195.                         putchar(c);
  196.                 else
  197.                 {
  198.                         n = runetochar(buf, &c);
  199.                         for (k = 0; k < n; k++)
  200.                                 putchar(buf[k]);
  201.                 }
  202.         }
  203.  
  204.         if (span->eol)
  205.                 putchar('\n');
  206.  
  207.         if (span->next)
  208.                 fz_debug_text_span(span->next);
  209. }
  210.  
  211. static void
  212. fz_text_extract_span(fz_text_span **last, fz_text *text, fz_matrix ctm, fz_point *pen)
  213. {
  214.         fz_font *font = text->font;
  215.         FT_Face face = font->ft_face;
  216.         fz_matrix tm = text->trm;
  217.         fz_matrix trm;
  218.         float size;
  219.         float adv;
  220.         fz_rect rect;
  221.         fz_point dir, ndir;
  222.         fz_point delta, ndelta;
  223.         float dist, dot;
  224.         float ascender = 1;
  225.         float descender = 0;
  226.         int multi;
  227.         int i, err;
  228.  
  229.         if (text->len == 0)
  230.                 return;
  231.  
  232.         if (font->ft_face)
  233.         {
  234.                 err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72);
  235.                 if (err)
  236.                         fz_warn("freetype set character size: %s", ft_error_string(err));
  237.                 ascender = (float)face->ascender / face->units_per_EM;
  238.                 descender = (float)face->descender / face->units_per_EM;
  239.         }
  240.  
  241.         rect = fz_empty_rect;
  242.  
  243.         if (text->wmode == 0)
  244.         {
  245.                 dir.x = 1;
  246.                 dir.y = 0;
  247.         }
  248.         else
  249.         {
  250.                 dir.x = 0;
  251.                 dir.y = 1;
  252.         }
  253.  
  254.         tm.e = 0;
  255.         tm.f = 0;
  256.         trm = fz_concat(tm, ctm);
  257.         dir = fz_transform_vector(trm, dir);
  258.         dist = sqrtf(dir.x * dir.x + dir.y * dir.y);
  259.         ndir.x = dir.x / dist;
  260.         ndir.y = dir.y / dist;
  261.  
  262.         size = fz_matrix_expansion(trm);
  263.  
  264.         multi = 1;
  265.  
  266.         for (i = 0; i < text->len; i++)
  267.         {
  268.                 if (text->items[i].gid < 0)
  269.                 {
  270.                         fz_add_text_char(last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect));
  271.                         multi ++;
  272.                         fz_divide_text_chars(last, multi, fz_round_rect(rect));
  273.                         continue;
  274.                 }
  275.                 multi = 1;
  276.  
  277.                 /* Calculate new pen location and delta */
  278.                 tm.e = text->items[i].x;
  279.                 tm.f = text->items[i].y;
  280.                 trm = fz_concat(tm, ctm);
  281.  
  282.                 delta.x = pen->x - trm.e;
  283.                 delta.y = pen->y - trm.f;
  284.                 if (pen->x == -1 && pen->y == -1)
  285.                         delta.x = delta.y = 0;
  286.  
  287.                 dist = sqrtf(delta.x * delta.x + delta.y * delta.y);
  288.  
  289.                 /* Add space and newlines based on pen movement */
  290.                 if (dist > 0)
  291.                 {
  292.                         ndelta.x = delta.x / dist;
  293.                         ndelta.y = delta.y / dist;
  294.                         dot = ndelta.x * ndir.x + ndelta.y * ndir.y;
  295.  
  296.                         if (dist > size * LINE_DIST)
  297.                         {
  298.                                 fz_add_text_newline(last, font, size, text->wmode);
  299.                         }
  300.                         else if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST)
  301.                         {
  302.                                 if ((*last)->len > 0 && (*last)->text[(*last)->len - 1].c != ' ')
  303.                                 {
  304.                                         fz_rect spacerect;
  305.                                         spacerect.x0 = -0.2f;
  306.                                         spacerect.y0 = 0;
  307.                                         spacerect.x1 = 0;
  308.                                         spacerect.y1 = 1;
  309.                                         spacerect = fz_transform_rect(trm, spacerect);
  310.                                         fz_add_text_char(last, font, size, text->wmode, ' ', fz_round_rect(spacerect));
  311.                                 }
  312.                         }
  313.                 }
  314.  
  315.                 /* Calculate bounding box and new pen position based on font metrics */
  316.                 if (font->ft_face)
  317.                 {
  318.                         FT_Fixed ftadv = 0;
  319.                         int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM;
  320.  
  321.                         /* TODO: freetype returns broken vertical metrics */
  322.                         /* if (text->wmode) mask |= FT_LOAD_VERTICAL_LAYOUT; */
  323.  
  324.                         FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv);
  325.                         adv = ftadv / 65536.0f;
  326.  
  327.                         rect.x0 = 0;
  328.                         rect.y0 = descender;
  329.                         rect.x1 = adv;
  330.                         rect.y1 = ascender;
  331.                 }
  332.                 else
  333.                 {
  334.                         adv = font->t3widths[text->items[i].gid];
  335.                         rect.x0 = 0;
  336.                         rect.y0 = descender;
  337.                         rect.x1 = adv;
  338.                         rect.y1 = ascender;
  339.                 }
  340.  
  341.                 rect = fz_transform_rect(trm, rect);
  342.                 pen->x = trm.e + dir.x * adv;
  343.                 pen->y = trm.f + dir.y * adv;
  344.  
  345.                 fz_add_text_char(last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect));
  346.         }
  347. }
  348.  
  349. static void
  350. fz_text_fill_text(void *user, fz_text *text, fz_matrix ctm,
  351.         fz_colorspace *colorspace, float *color, float alpha)
  352. {
  353.         fz_text_device *tdev = user;
  354.         fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
  355. }
  356.  
  357. static void
  358. fz_text_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm,
  359.         fz_colorspace *colorspace, float *color, float alpha)
  360. {
  361.         fz_text_device *tdev = user;
  362.         fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
  363. }
  364.  
  365. static void
  366. fz_text_clip_text(void *user, fz_text *text, fz_matrix ctm, int accumulate)
  367. {
  368.         fz_text_device *tdev = user;
  369.         fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
  370. }
  371.  
  372. static void
  373. fz_text_clip_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm)
  374. {
  375.         fz_text_device *tdev = user;
  376.         fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
  377. }
  378.  
  379. static void
  380. fz_text_ignore_text(void *user, fz_text *text, fz_matrix ctm)
  381. {
  382.         fz_text_device *tdev = user;
  383.         fz_text_extract_span(&tdev->span, text, ctm, &tdev->point);
  384. }
  385.  
  386. static void
  387. fz_text_free_user(void *user)
  388. {
  389.         fz_text_device *tdev = user;
  390.  
  391.         tdev->span->eol = 1;
  392.  
  393.         /* TODO: unicode NFC normalization */
  394.         /* TODO: bidi logical reordering */
  395.  
  396.         fz_free(tdev);
  397. }
  398.  
  399. fz_device *
  400. fz_new_text_device(fz_text_span *root)
  401. {
  402.         fz_device *dev;
  403.         fz_text_device *tdev = fz_malloc(sizeof(fz_text_device));
  404.         tdev->head = root;
  405.         tdev->span = root;
  406.         tdev->point.x = -1;
  407.         tdev->point.y = -1;
  408.  
  409.         dev = fz_new_device(tdev);
  410.         dev->hints = FZ_IGNORE_IMAGE | FZ_IGNORE_SHADE;
  411.         dev->free_user = fz_text_free_user;
  412.         dev->fill_text = fz_text_fill_text;
  413.         dev->stroke_text = fz_text_stroke_text;
  414.         dev->clip_text = fz_text_clip_text;
  415.         dev->clip_stroke_text = fz_text_clip_stroke_text;
  416.         dev->ignore_text = fz_text_ignore_text;
  417.         return dev;
  418. }
  419.