gvtextlayout_pango.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. #include "config.h"
  11. #include <assert.h>
  12. #include <limits.h>
  13. #include <stdbool.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <gvc/gvplugin_render.h>
  17. #include <cgraph/gv_math.h>
  18. #include <common/utils.h>
  19. #include <gvc/gvplugin_textlayout.h>
  20. #include <util/agxbuf.h>
  21. #include <util/alloc.h>
  22. #include <pango/pangocairo.h>
  23. #include "gvgetfontlist.h"
  24. #ifdef HAVE_PANGO_FC_FONT_LOCK_FACE
  25. #include <pango/pangofc-font.h>
  26. #endif
  27. static void pango_free_layout (void *layout)
  28. {
  29. g_object_unref(layout);
  30. }
  31. static char* pango_psfontResolve (PostscriptAlias* pa)
  32. {
  33. agxbuf buf = {0};
  34. agxbprint(&buf, "%s,", pa->family);
  35. if (pa->weight) {
  36. agxbprint(&buf, " %s", pa->weight);
  37. }
  38. if (pa->stretch) {
  39. agxbprint(&buf, " %s", pa->stretch);
  40. }
  41. if (pa->style) {
  42. agxbprint(&buf, " %s", pa->style);
  43. }
  44. return agxbdisown(&buf);
  45. }
  46. #define FONT_DPI 96.
  47. #define ENABLE_PANGO_MARKUP
  48. // wrapper to handle difference in calling conventions between `agxbput` and
  49. // `xml_escape`’s `cb`
  50. static int agxbput_int(void *buffer, const char *s) {
  51. size_t len = agxbput(buffer, s);
  52. assert(len <= INT_MAX);
  53. return (int)len;
  54. }
  55. static bool pango_textlayout(textspan_t * span, char **fontpath)
  56. {
  57. static agxbuf buf; // returned in fontpath, only good until next call
  58. static PangoFontMap *fontmap;
  59. static PangoContext *context;
  60. static PangoFontDescription *desc;
  61. static char *fontname;
  62. static double fontsize;
  63. static gv_font_map* gv_fmap;
  64. char *fnt, *psfnt = NULL;
  65. PangoFont *font;
  66. #ifdef ENABLE_PANGO_MARKUP
  67. PangoAttrList *attrs;
  68. GError *error = NULL;
  69. int flags;
  70. #endif
  71. char *text;
  72. if (!context) {
  73. fontmap = pango_cairo_font_map_new();
  74. gv_fmap = get_font_mapping(fontmap);
  75. context = pango_font_map_create_context (fontmap);
  76. cairo_font_options_t* options = cairo_font_options_create();
  77. cairo_font_options_set_antialias(options,CAIRO_ANTIALIAS_GRAY);
  78. cairo_font_options_set_hint_style(options,CAIRO_HINT_STYLE_FULL);
  79. cairo_font_options_set_hint_metrics(options,CAIRO_HINT_METRICS_ON);
  80. cairo_font_options_set_subpixel_order(options,CAIRO_SUBPIXEL_ORDER_BGR);
  81. pango_cairo_context_set_font_options(context, options);
  82. pango_cairo_context_set_resolution(context, FONT_DPI);
  83. cairo_font_options_destroy(options);
  84. g_object_unref(fontmap);
  85. }
  86. if (!fontname || strcmp(fontname, span->font->name) != 0 ||
  87. !is_exactly_equal(fontsize, span->font->size)) {
  88. /* check if the conversion to Pango units below will overflow */
  89. if (INT_MAX / PANGO_SCALE < span->font->size) {
  90. return false;
  91. }
  92. free(fontname);
  93. fontname = gv_strdup(span->font->name);
  94. fontsize = span->font->size;
  95. pango_font_description_free (desc);
  96. PostscriptAlias *pA = span->font->postscript_alias;
  97. bool psfnt_needs_free = false;
  98. if (pA) {
  99. psfnt = fnt = gv_fmap[pA->xfig_code].gv_font;
  100. if(!psfnt) {
  101. psfnt = fnt = pango_psfontResolve (pA);
  102. psfnt_needs_free = true;
  103. }
  104. }
  105. else
  106. fnt = fontname;
  107. desc = pango_font_description_from_string(fnt);
  108. /* all text layout is done at a scale of FONT_DPI (nominaly 96.) */
  109. pango_font_description_set_size (desc, (int)(fontsize * PANGO_SCALE));
  110. if (fontpath && (font = pango_font_map_load_font(fontmap, context, desc))) { /* -v support */
  111. const char *fontclass = G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(font));
  112. agxbclear(&buf);
  113. if (psfnt) {
  114. agxbprint(&buf, "(ps:pango %s) ", psfnt);
  115. }
  116. agxbprint(&buf, "(%s) ", fontclass);
  117. #ifdef HAVE_PANGO_FC_FONT_LOCK_FACE
  118. if (strcmp(fontclass, "PangoCairoFcFont") == 0) {
  119. PangoFcFont *fcfont = PANGO_FC_FONT(font);
  120. FT_Face face = pango_fc_font_lock_face(fcfont);
  121. if (face) {
  122. agxbprint(&buf, "\"%s, %s\" ", face->family_name, face->style_name);
  123. FT_Stream stream = face->stream;
  124. if (stream) {
  125. FT_StreamDesc streamdesc = stream->pathname;
  126. if (streamdesc.pointer)
  127. agxbput(&buf, streamdesc.pointer);
  128. else
  129. agxbput(&buf, "*no pathname available*");
  130. }
  131. else
  132. agxbput(&buf, "*no stream available*");
  133. }
  134. pango_fc_font_unlock_face(fcfont);
  135. }
  136. else
  137. #endif
  138. {
  139. PangoFontDescription *tdesc = pango_font_describe(font);
  140. char *tfont = pango_font_description_to_string(tdesc);
  141. agxbprint(&buf, "\"%s\" ", tfont);
  142. g_free(tfont);
  143. }
  144. *fontpath = agxbuse(&buf);
  145. }
  146. if (psfnt_needs_free) {
  147. free(psfnt);
  148. }
  149. }
  150. #ifdef ENABLE_PANGO_MARKUP
  151. if ((span->font) && (flags = span->font->flags)) {
  152. agxbuf xb = {0};
  153. agxbput(&xb,"<span");
  154. if (flags & HTML_BF)
  155. agxbput(&xb," weight=\"bold\"");
  156. if (flags & HTML_IF)
  157. agxbput(&xb," style=\"italic\"");
  158. if (flags & HTML_UL)
  159. agxbput(&xb," underline=\"single\"");
  160. if (flags & HTML_S)
  161. agxbput(&xb," strikethrough=\"true\"");
  162. agxbput (&xb,">");
  163. if (flags & HTML_SUP)
  164. agxbput(&xb,"<sup>");
  165. if (flags & HTML_SUB)
  166. agxbput(&xb,"<sub>");
  167. const xml_flags_t xml_flags = {.raw = 1, .dash = 1, .nbsp = 1};
  168. xml_escape(span->str, xml_flags, agxbput_int, &xb);
  169. if (flags & HTML_SUB)
  170. agxbput(&xb,"</sub>");
  171. if (flags & HTML_SUP)
  172. agxbput(&xb,"</sup>");
  173. agxbput (&xb,"</span>");
  174. if (!pango_parse_markup (agxbuse(&xb), -1, 0, &attrs, &text, NULL, &error)) {
  175. fprintf (stderr, "Error - pango_parse_markup: %s\n", error->message);
  176. text = span->str;
  177. attrs = NULL;
  178. }
  179. agxbfree (&xb);
  180. }
  181. else {
  182. text = span->str;
  183. attrs = NULL;
  184. }
  185. #else
  186. text = span->str;
  187. #endif
  188. PangoLayout *layout = pango_layout_new (context);
  189. span->layout = layout; /* layout free with textspan - see labels.c */
  190. span->free_layout = pango_free_layout; /* function for freeing pango layout */
  191. pango_layout_set_text (layout, text, -1);
  192. pango_layout_set_font_description (layout, desc);
  193. #ifdef ENABLE_PANGO_MARKUP
  194. if (attrs)
  195. pango_layout_set_attributes (layout, attrs);
  196. #endif
  197. PangoRectangle logical_rect;
  198. pango_layout_get_extents (layout, NULL, &logical_rect);
  199. /* if pango doesn't like the font then it sets width=0 but height = garbage */
  200. if (logical_rect.width == 0)
  201. logical_rect.height = 0;
  202. const double textlayout_scale = POINTS_PER_INCH / (FONT_DPI * PANGO_SCALE);
  203. span->size.x = logical_rect.width * textlayout_scale;
  204. span->size.y = logical_rect.height * textlayout_scale;
  205. /* The y offset from baseline to 0,0 of the bitmap representation */
  206. span->yoffset_layout = pango_layout_get_baseline (layout) * textlayout_scale;
  207. /* The distance below midline for y centering of text strings */
  208. span->yoffset_centerline = 0.05 * span->font->size;
  209. return logical_rect.width != 0 || strcmp(text, "") == 0;
  210. }
  211. static gvtextlayout_engine_t pango_textlayout_engine = {
  212. pango_textlayout,
  213. };
  214. gvplugin_installed_t gvtextlayout_pango_types[] = {
  215. {0, "textlayout", 10, &pango_textlayout_engine, NULL},
  216. {0, NULL, 0, NULL, NULL}
  217. };