Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. #include "fitz.h"
  2. #include "muxps.h"
  3.  
  4. #include <ft2build.h>
  5. #include FT_FREETYPE_H
  6. #include FT_ADVANCES_H
  7.  
  8. static inline int ishex(int a)
  9. {
  10.         return (a >= 'A' && a <= 'F') ||
  11.                 (a >= 'a' && a <= 'f') ||
  12.                 (a >= '0' && a <= '9');
  13. }
  14.  
  15. static inline int unhex(int a)
  16. {
  17.         if (a >= 'A' && a <= 'F') return a - 'A' + 0xA;
  18.         if (a >= 'a' && a <= 'f') return a - 'a' + 0xA;
  19.         if (a >= '0' && a <= '9') return a - '0';
  20.         return 0;
  21. }
  22.  
  23. int
  24. xps_count_font_encodings(fz_font *font)
  25. {
  26.         FT_Face face = font->ft_face;
  27.         return face->num_charmaps;
  28. }
  29.  
  30. void
  31. xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid)
  32. {
  33.         FT_Face face = font->ft_face;
  34.         *pid = face->charmaps[idx]->platform_id;
  35.         *eid = face->charmaps[idx]->encoding_id;
  36. }
  37.  
  38. void
  39. xps_select_font_encoding(fz_font *font, int idx)
  40. {
  41.         FT_Face face = font->ft_face;
  42.         FT_Set_Charmap(face, face->charmaps[idx]);
  43. }
  44.  
  45. int
  46. xps_encode_font_char(fz_font *font, int code)
  47. {
  48.         FT_Face face = font->ft_face;
  49.         int gid = FT_Get_Char_Index(face, code);
  50.         if (gid == 0 && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0)
  51.                 gid = FT_Get_Char_Index(face, 0xF000 | code);
  52.         return gid;
  53. }
  54.  
  55. void
  56. xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx)
  57. {
  58.         int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM;
  59.         FT_Face face = font->ft_face;
  60.         FT_Fixed hadv, vadv;
  61.  
  62.         FT_Set_Char_Size(face, 64, 64, 72, 72);
  63.         FT_Get_Advance(face, gid, mask, &hadv);
  64.         FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv);
  65.  
  66.         mtx->hadv = hadv / 65536.0f;
  67.         mtx->vadv = vadv / 65536.0f;
  68.         mtx->vorg = face->ascender / (float) face->units_per_EM;
  69. }
  70.  
  71. static fz_font *
  72. xps_lookup_font(xps_context *ctx, char *name)
  73. {
  74.         xps_font_cache *cache;
  75.         for (cache = ctx->font_table; cache; cache = cache->next)
  76.                 if (!xps_strcasecmp(cache->name, name))
  77.                         return fz_keep_font(cache->font);
  78.         return NULL;
  79. }
  80.  
  81. static void
  82. xps_insert_font(xps_context *ctx, char *name, fz_font *font)
  83. {
  84.         xps_font_cache *cache = fz_malloc(sizeof(xps_font_cache));
  85.         cache->name = fz_strdup(name);
  86.         cache->font = fz_keep_font(font);
  87.         cache->next = ctx->font_table;
  88.         ctx->font_table = cache;
  89. }
  90.  
  91. /*
  92.  * Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the
  93.  * data with the GUID in the fontname.
  94.  */
  95. static void
  96. xps_deobfuscate_font_resource(xps_context *ctx, xps_part *part)
  97. {
  98.         byte buf[33];
  99.         byte key[16];
  100.         char *p;
  101.         int i;
  102.  
  103.         p = strrchr(part->name, '/');
  104.         if (!p)
  105.                 p = part->name;
  106.  
  107.         for (i = 0; i < 32 && *p; p++)
  108.         {
  109.                 if (ishex(*p))
  110.                         buf[i++] = *p;
  111.         }
  112.         buf[i] = 0;
  113.  
  114.         if (i != 32)
  115.         {
  116.                 fz_warn("cannot extract GUID from obfuscated font part name");
  117.                 return;
  118.         }
  119.  
  120.         for (i = 0; i < 16; i++)
  121.                 key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]);
  122.  
  123.         for (i = 0; i < 16; i++)
  124.         {
  125.                 part->data[i] ^= key[15-i];
  126.                 part->data[i+16] ^= key[15-i];
  127.         }
  128. }
  129.  
  130. static void
  131. xps_select_best_font_encoding(fz_font *font)
  132. {
  133.         static struct { int pid, eid; } xps_cmap_list[] =
  134.         {
  135.                 { 3, 10 },              /* Unicode with surrogates */
  136.                 { 3, 1 },               /* Unicode without surrogates */
  137.                 { 3, 5 },               /* Wansung */
  138.                 { 3, 4 },               /* Big5 */
  139.                 { 3, 3 },               /* Prc */
  140.                 { 3, 2 },               /* ShiftJis */
  141.                 { 3, 0 },               /* Symbol */
  142.                 // { 0, * }, -- Unicode (deprecated)
  143.                 { 1, 0 },
  144.                 { -1, -1 },
  145.         };
  146.  
  147.         int i, k, n, pid, eid;
  148.  
  149.         n = xps_count_font_encodings(font);
  150.         for (k = 0; xps_cmap_list[k].pid != -1; k++)
  151.         {
  152.                 for (i = 0; i < n; i++)
  153.                 {
  154.                         xps_identify_font_encoding(font, i, &pid, &eid);
  155.                         if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid)
  156.                         {
  157.                                 xps_select_font_encoding(font, i);
  158.                                 return;
  159.                         }
  160.                 }
  161.         }
  162.  
  163.         fz_warn("cannot find a suitable cmap");
  164. }
  165.  
  166. /*
  167.  * Parse and draw an XPS <Glyphs> element.
  168.  *
  169.  * Indices syntax:
  170.  
  171.  GlyphIndices   = GlyphMapping ( ";" GlyphMapping )
  172.  GlyphMapping   = ( [ClusterMapping] GlyphIndex ) [GlyphMetrics]
  173.  ClusterMapping = "(" ClusterCodeUnitCount [":" ClusterGlyphCount] ")"
  174.  ClusterCodeUnitCount   = * DIGIT
  175.  ClusterGlyphCount              = * DIGIT
  176.  GlyphIndex             = * DIGIT
  177.  GlyphMetrics   = "," AdvanceWidth ["," uOffset ["," vOffset]]
  178.  AdvanceWidth   = ["+"] RealNum
  179.  uOffset                = ["+" | "-"] RealNum
  180.  vOffset                = ["+" | "-"] RealNum
  181.  RealNum                = ((DIGIT ["." DIGIT]) | ("." DIGIT)) [Exponent]
  182.  Exponent               = ( ("E"|"e") ("+"|"-") DIGIT )
  183.  
  184.  */
  185.  
  186. static char *
  187. xps_parse_digits(char *s, int *digit)
  188. {
  189.         *digit = 0;
  190.         while (*s >= '0' && *s <= '9')
  191.         {
  192.                 *digit = *digit * 10 + (*s - '0');
  193.                 s ++;
  194.         }
  195.         return s;
  196. }
  197.  
  198. static inline int is_real_num_char(int c)
  199. {
  200.         return (c >= '0' && c <= '9') ||
  201.                 c == 'e' || c == 'E' || c == '+' || c == '-' || c == '.';
  202. }
  203.  
  204. static char *
  205. xps_parse_real_num(char *s, float *number)
  206. {
  207.         char buf[64];
  208.         char *p = buf;
  209.         while (is_real_num_char(*s))
  210.                 *p++ = *s++;
  211.         *p = 0;
  212.         if (buf[0])
  213.                 *number = fz_atof(buf);
  214.         return s;
  215. }
  216.  
  217. static char *
  218. xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count)
  219. {
  220.         if (*s == '(')
  221.                 s = xps_parse_digits(s + 1, code_count);
  222.         if (*s == ':')
  223.                 s = xps_parse_digits(s + 1, glyph_count);
  224.         if (*s == ')')
  225.                 s ++;
  226.         return s;
  227. }
  228.  
  229. static char *
  230. xps_parse_glyph_index(char *s, int *glyph_index)
  231. {
  232.         if (*s >= '0' && *s <= '9')
  233.                 s = xps_parse_digits(s, glyph_index);
  234.         return s;
  235. }
  236.  
  237. static char *
  238. xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs)
  239. {
  240.         if (*s == ',')
  241.                 s = xps_parse_real_num(s + 1, advance);
  242.         if (*s == ',')
  243.                 s = xps_parse_real_num(s + 1, uofs);
  244.         if (*s == ',')
  245.                 s = xps_parse_real_num(s + 1, vofs);
  246.         return s;
  247. }
  248.  
  249. /*
  250.  * Parse unicode and indices strings and encode glyphs.
  251.  * Calculate metrics for positioning.
  252.  */
  253. static fz_text *
  254. xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm,
  255.         fz_font *font, float size, float originx, float originy,
  256.         int is_sideways, int bidi_level,
  257.         char *indices, char *unicode)
  258. {
  259.         xps_glyph_metrics mtx;
  260.         fz_text *text;
  261.         fz_matrix tm;
  262.         float e, f;
  263.         float x = originx;
  264.         float y = originy;
  265.         char *us = unicode;
  266.         char *is = indices;
  267.         int un = 0;
  268.  
  269.         if (!unicode && !indices)
  270.                 fz_warn("glyphs element with neither characters nor indices");
  271.  
  272.         if (us)
  273.         {
  274.                 if (us[0] == '{' && us[1] == '}')
  275.                         us = us + 2;
  276.                 un = strlen(us);
  277.         }
  278.  
  279.         if (is_sideways)
  280.                 tm = fz_concat(fz_scale(-size, size), fz_rotate(90));
  281.         else
  282.                 tm = fz_scale(size, -size);
  283.  
  284.         text = fz_new_text(font, tm, is_sideways);
  285.  
  286.         while ((us && un > 0) || (is && *is))
  287.         {
  288.                 int char_code = '?';
  289.                 int code_count = 1;
  290.                 int glyph_count = 1;
  291.  
  292.                 if (is && *is)
  293.                 {
  294.                         is = xps_parse_cluster_mapping(is, &code_count, &glyph_count);
  295.                 }
  296.  
  297.                 if (code_count < 1)
  298.                         code_count = 1;
  299.                 if (glyph_count < 1)
  300.                         glyph_count = 1;
  301.  
  302.                 /* TODO: add code chars with cluster mappings for text extraction */
  303.  
  304.                 while (code_count--)
  305.                 {
  306.                         if (us && un > 0)
  307.                         {
  308.                                 int t = chartorune(&char_code, us);
  309.                                 us += t; un -= t;
  310.                         }
  311.                 }
  312.  
  313.                 while (glyph_count--)
  314.                 {
  315.                         int glyph_index = -1;
  316.                         float u_offset = 0;
  317.                         float v_offset = 0;
  318.                         float advance;
  319.  
  320.                         if (is && *is)
  321.                                 is = xps_parse_glyph_index(is, &glyph_index);
  322.  
  323.                         if (glyph_index == -1)
  324.                                 glyph_index = xps_encode_font_char(font, char_code);
  325.  
  326.                         xps_measure_font_glyph(ctx, font, glyph_index, &mtx);
  327.                         if (is_sideways)
  328.                                 advance = mtx.vadv * 100;
  329.                         else if (bidi_level & 1)
  330.                                 advance = -mtx.hadv * 100;
  331.                         else
  332.                                 advance = mtx.hadv * 100;
  333.  
  334.                         if (is && *is)
  335.                         {
  336.                                 is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset);
  337.                                 if (*is == ';')
  338.                                         is ++;
  339.                         }
  340.  
  341.                         if (bidi_level & 1)
  342.                                 u_offset = -mtx.hadv * 100 - u_offset;
  343.  
  344.                         u_offset = u_offset * 0.01f * size;
  345.                         v_offset = v_offset * 0.01f * size;
  346.  
  347.                         if (is_sideways)
  348.                         {
  349.                                 e = x + u_offset + (mtx.vorg * size);
  350.                                 f = y - v_offset + (mtx.hadv * 0.5f * size);
  351.                         }
  352.                         else
  353.                         {
  354.                                 e = x + u_offset;
  355.                                 f = y - v_offset;
  356.                         }
  357.  
  358.                         fz_add_text(text, glyph_index, char_code, e, f);
  359.  
  360.                         x += advance * 0.01f * size;
  361.                 }
  362.         }
  363.  
  364.         return text;
  365. }
  366.  
  367. void
  368. xps_parse_glyphs(xps_context *ctx, fz_matrix ctm,
  369.                 char *base_uri, xps_resource *dict, xml_element *root)
  370. {
  371.         xml_element *node;
  372.         int code;
  373.  
  374.         char *fill_uri;
  375.         char *opacity_mask_uri;
  376.  
  377.         char *bidi_level_att;
  378.         char *caret_stops_att;
  379.         char *fill_att;
  380.         char *font_size_att;
  381.         char *font_uri_att;
  382.         char *origin_x_att;
  383.         char *origin_y_att;
  384.         char *is_sideways_att;
  385.         char *indices_att;
  386.         char *unicode_att;
  387.         char *style_att;
  388.         char *transform_att;
  389.         char *clip_att;
  390.         char *opacity_att;
  391.         char *opacity_mask_att;
  392.  
  393.         xml_element *transform_tag = NULL;
  394.         xml_element *clip_tag = NULL;
  395.         xml_element *fill_tag = NULL;
  396.         xml_element *opacity_mask_tag = NULL;
  397.  
  398.         char *fill_opacity_att = NULL;
  399.  
  400.         xps_part *part;
  401.         fz_font *font;
  402.  
  403.         char partname[1024];
  404.         char *subfont;
  405.  
  406.         float font_size = 10;
  407.         int subfontid = 0;
  408.         int is_sideways = 0;
  409.         int bidi_level = 0;
  410.  
  411.         fz_text *text;
  412.         fz_rect area;
  413.  
  414.         /*
  415.          * Extract attributes and extended attributes.
  416.          */
  417.  
  418.         bidi_level_att = xml_att(root, "BidiLevel");
  419.         caret_stops_att = xml_att(root, "CaretStops");
  420.         fill_att = xml_att(root, "Fill");
  421.         font_size_att = xml_att(root, "FontRenderingEmSize");
  422.         font_uri_att = xml_att(root, "FontUri");
  423.         origin_x_att = xml_att(root, "OriginX");
  424.         origin_y_att = xml_att(root, "OriginY");
  425.         is_sideways_att = xml_att(root, "IsSideways");
  426.         indices_att = xml_att(root, "Indices");
  427.         unicode_att = xml_att(root, "UnicodeString");
  428.         style_att = xml_att(root, "StyleSimulations");
  429.         transform_att = xml_att(root, "RenderTransform");
  430.         clip_att = xml_att(root, "Clip");
  431.         opacity_att = xml_att(root, "Opacity");
  432.         opacity_mask_att = xml_att(root, "OpacityMask");
  433.  
  434.         for (node = xml_down(root); node; node = xml_next(node))
  435.         {
  436.                 if (!strcmp(xml_tag(node), "Glyphs.RenderTransform"))
  437.                         transform_tag = xml_down(node);
  438.                 if (!strcmp(xml_tag(node), "Glyphs.OpacityMask"))
  439.                         opacity_mask_tag = xml_down(node);
  440.                 if (!strcmp(xml_tag(node), "Glyphs.Clip"))
  441.                         clip_tag = xml_down(node);
  442.                 if (!strcmp(xml_tag(node), "Glyphs.Fill"))
  443.                         fill_tag = xml_down(node);
  444.         }
  445.  
  446.         fill_uri = base_uri;
  447.         opacity_mask_uri = base_uri;
  448.  
  449.         xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
  450.         xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
  451.         xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri);
  452.         xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
  453.  
  454.         /*
  455.          * Check that we have all the necessary information.
  456.          */
  457.  
  458.         if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) {
  459.                 fz_warn("missing attributes in glyphs element");
  460.                 return;
  461.         }
  462.  
  463.         if (!indices_att && !unicode_att)
  464.                 return; /* nothing to draw */
  465.  
  466.         if (is_sideways_att)
  467.                 is_sideways = !strcmp(is_sideways_att, "true");
  468.  
  469.         if (bidi_level_att)
  470.                 bidi_level = atoi(bidi_level_att);
  471.  
  472.         /*
  473.          * Find and load the font resource
  474.          */
  475.  
  476.         xps_absolute_path(partname, base_uri, font_uri_att, sizeof partname);
  477.         subfont = strrchr(partname, '#');
  478.         if (subfont)
  479.         {
  480.                 subfontid = atoi(subfont + 1);
  481.                 *subfont = 0;
  482.         }
  483.  
  484.         font = xps_lookup_font(ctx, partname);
  485.         if (!font)
  486.         {
  487.                 part = xps_read_part(ctx, partname);
  488.                 if (!part) {
  489.                         fz_warn("cannot find font resource part '%s'", partname);
  490.                         return;
  491.                 }
  492.  
  493.                 /* deobfuscate if necessary */
  494.                 if (strstr(part->name, ".odttf"))
  495.                         xps_deobfuscate_font_resource(ctx, part);
  496.                 if (strstr(part->name, ".ODTTF"))
  497.                         xps_deobfuscate_font_resource(ctx, part);
  498.  
  499.                 code = fz_new_font_from_memory(&font, part->data, part->size, subfontid);
  500.                 if (code) {
  501.                         fz_catch(code, "cannot load font resource '%s'", partname);
  502.                         xps_free_part(ctx, part);
  503.                         return;
  504.                 }
  505.  
  506.                 xps_select_best_font_encoding(font);
  507.  
  508.                 xps_insert_font(ctx, part->name, font);
  509.  
  510.                 /* NOTE: we keep part->data in the font */
  511.                 font->ft_data = part->data;
  512.                 font->ft_size = part->size;
  513.                 fz_free(part->name);
  514.                 fz_free(part);
  515.         }
  516.  
  517.         /*
  518.          * Set up graphics state.
  519.          */
  520.  
  521.         if (transform_att || transform_tag)
  522.         {
  523.                 fz_matrix transform;
  524.                 if (transform_att)
  525.                         xps_parse_render_transform(ctx, transform_att, &transform);
  526.                 if (transform_tag)
  527.                         xps_parse_matrix_transform(ctx, transform_tag, &transform);
  528.                 ctm = fz_concat(transform, ctm);
  529.         }
  530.  
  531.         if (clip_att || clip_tag)
  532.                 xps_clip(ctx, ctm, dict, clip_att, clip_tag);
  533.  
  534.         font_size = fz_atof(font_size_att);
  535.  
  536.         text = xps_parse_glyphs_imp(ctx, ctm, font, font_size,
  537.                         fz_atof(origin_x_att), fz_atof(origin_y_att),
  538.                         is_sideways, bidi_level, indices_att, unicode_att);
  539.  
  540.         area = fz_bound_text(text, ctm);
  541.  
  542.         xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
  543.  
  544.         /* If it's a solid color brush fill/stroke do a simple fill */
  545.  
  546.         if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush"))
  547.         {
  548.                 fill_opacity_att = xml_att(fill_tag, "Opacity");
  549.                 fill_att = xml_att(fill_tag, "Color");
  550.                 fill_tag = NULL;
  551.         }
  552.  
  553.         if (fill_att)
  554.         {
  555.                 float samples[32];
  556.                 fz_colorspace *colorspace;
  557.  
  558.                 xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples);
  559.                 if (fill_opacity_att)
  560.                         samples[0] = fz_atof(fill_opacity_att);
  561.                 xps_set_color(ctx, colorspace, samples);
  562.  
  563.                 fz_fill_text(ctx->dev, text, ctm,
  564.                         ctx->colorspace, ctx->color, ctx->alpha);
  565.         }
  566.  
  567.         /* If it's a complex brush, use the charpath as a clip mask */
  568.  
  569.         if (fill_tag)
  570.         {
  571.                 fz_clip_text(ctx->dev, text, ctm, 0);
  572.                 xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag);
  573.                 fz_pop_clip(ctx->dev);
  574.         }
  575.  
  576.         xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
  577.  
  578.         fz_free_text(text);
  579.  
  580.         if (clip_att || clip_tag)
  581.                 fz_pop_clip(ctx->dev);
  582.  
  583.         fz_drop_font(font);
  584. }
  585.