123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- #include "config.h"
- #include <assert.h>
- #include <limits.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <string.h>
- #include <gvc/gvplugin_render.h>
- #include <cgraph/gv_math.h>
- #include <common/utils.h>
- #include <gvc/gvplugin_textlayout.h>
- #include <util/agxbuf.h>
- #include <util/alloc.h>
- #include <pango/pangocairo.h>
- #include "gvgetfontlist.h"
- #ifdef HAVE_PANGO_FC_FONT_LOCK_FACE
- #include <pango/pangofc-font.h>
- #endif
- static void pango_free_layout (void *layout)
- {
- g_object_unref(layout);
- }
- static char* pango_psfontResolve (PostscriptAlias* pa)
- {
- agxbuf buf = {0};
- agxbprint(&buf, "%s,", pa->family);
- if (pa->weight) {
- agxbprint(&buf, " %s", pa->weight);
- }
- if (pa->stretch) {
- agxbprint(&buf, " %s", pa->stretch);
- }
- if (pa->style) {
- agxbprint(&buf, " %s", pa->style);
- }
- return agxbdisown(&buf);
- }
- #define FONT_DPI 96.
- #define ENABLE_PANGO_MARKUP
- // wrapper to handle difference in calling conventions between `agxbput` and
- // `xml_escape`’s `cb`
- static int agxbput_int(void *buffer, const char *s) {
- size_t len = agxbput(buffer, s);
- assert(len <= INT_MAX);
- return (int)len;
- }
- static bool pango_textlayout(textspan_t * span, char **fontpath)
- {
- static agxbuf buf; // returned in fontpath, only good until next call
- static PangoFontMap *fontmap;
- static PangoContext *context;
- static PangoFontDescription *desc;
- static char *fontname;
- static double fontsize;
- static gv_font_map* gv_fmap;
- char *fnt, *psfnt = NULL;
- PangoFont *font;
- #ifdef ENABLE_PANGO_MARKUP
- PangoAttrList *attrs;
- GError *error = NULL;
- int flags;
- #endif
- char *text;
- if (!context) {
- fontmap = pango_cairo_font_map_new();
- gv_fmap = get_font_mapping(fontmap);
- context = pango_font_map_create_context (fontmap);
- cairo_font_options_t* options = cairo_font_options_create();
- cairo_font_options_set_antialias(options,CAIRO_ANTIALIAS_GRAY);
- cairo_font_options_set_hint_style(options,CAIRO_HINT_STYLE_FULL);
- cairo_font_options_set_hint_metrics(options,CAIRO_HINT_METRICS_ON);
- cairo_font_options_set_subpixel_order(options,CAIRO_SUBPIXEL_ORDER_BGR);
- pango_cairo_context_set_font_options(context, options);
- pango_cairo_context_set_resolution(context, FONT_DPI);
- cairo_font_options_destroy(options);
- g_object_unref(fontmap);
- }
- if (!fontname || strcmp(fontname, span->font->name) != 0 ||
- !is_exactly_equal(fontsize, span->font->size)) {
- /* check if the conversion to Pango units below will overflow */
- if (INT_MAX / PANGO_SCALE < span->font->size) {
- return false;
- }
- free(fontname);
- fontname = gv_strdup(span->font->name);
- fontsize = span->font->size;
- pango_font_description_free (desc);
- PostscriptAlias *pA = span->font->postscript_alias;
- bool psfnt_needs_free = false;
- if (pA) {
- psfnt = fnt = gv_fmap[pA->xfig_code].gv_font;
- if(!psfnt) {
- psfnt = fnt = pango_psfontResolve (pA);
- psfnt_needs_free = true;
- }
- }
- else
- fnt = fontname;
- desc = pango_font_description_from_string(fnt);
- /* all text layout is done at a scale of FONT_DPI (nominaly 96.) */
- pango_font_description_set_size (desc, (int)(fontsize * PANGO_SCALE));
- if (fontpath && (font = pango_font_map_load_font(fontmap, context, desc))) { /* -v support */
- const char *fontclass = G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(font));
- agxbclear(&buf);
- if (psfnt) {
- agxbprint(&buf, "(ps:pango %s) ", psfnt);
- }
- agxbprint(&buf, "(%s) ", fontclass);
- #ifdef HAVE_PANGO_FC_FONT_LOCK_FACE
- if (strcmp(fontclass, "PangoCairoFcFont") == 0) {
- PangoFcFont *fcfont = PANGO_FC_FONT(font);
- FT_Face face = pango_fc_font_lock_face(fcfont);
- if (face) {
- agxbprint(&buf, "\"%s, %s\" ", face->family_name, face->style_name);
- FT_Stream stream = face->stream;
- if (stream) {
- FT_StreamDesc streamdesc = stream->pathname;
- if (streamdesc.pointer)
- agxbput(&buf, streamdesc.pointer);
- else
- agxbput(&buf, "*no pathname available*");
- }
- else
- agxbput(&buf, "*no stream available*");
- }
- pango_fc_font_unlock_face(fcfont);
- }
- else
- #endif
- {
- PangoFontDescription *tdesc = pango_font_describe(font);
- char *tfont = pango_font_description_to_string(tdesc);
- agxbprint(&buf, "\"%s\" ", tfont);
- g_free(tfont);
- }
- *fontpath = agxbuse(&buf);
- }
- if (psfnt_needs_free) {
- free(psfnt);
- }
- }
- #ifdef ENABLE_PANGO_MARKUP
- if ((span->font) && (flags = span->font->flags)) {
- agxbuf xb = {0};
- agxbput(&xb,"<span");
- if (flags & HTML_BF)
- agxbput(&xb," weight=\"bold\"");
- if (flags & HTML_IF)
- agxbput(&xb," style=\"italic\"");
- if (flags & HTML_UL)
- agxbput(&xb," underline=\"single\"");
- if (flags & HTML_S)
- agxbput(&xb," strikethrough=\"true\"");
- agxbput (&xb,">");
- if (flags & HTML_SUP)
- agxbput(&xb,"<sup>");
- if (flags & HTML_SUB)
- agxbput(&xb,"<sub>");
- const xml_flags_t xml_flags = {.raw = 1, .dash = 1, .nbsp = 1};
- xml_escape(span->str, xml_flags, agxbput_int, &xb);
- if (flags & HTML_SUB)
- agxbput(&xb,"</sub>");
- if (flags & HTML_SUP)
- agxbput(&xb,"</sup>");
- agxbput (&xb,"</span>");
- if (!pango_parse_markup (agxbuse(&xb), -1, 0, &attrs, &text, NULL, &error)) {
- fprintf (stderr, "Error - pango_parse_markup: %s\n", error->message);
- text = span->str;
- attrs = NULL;
- }
- agxbfree (&xb);
- }
- else {
- text = span->str;
- attrs = NULL;
- }
- #else
- text = span->str;
- #endif
- PangoLayout *layout = pango_layout_new (context);
- span->layout = layout; /* layout free with textspan - see labels.c */
- span->free_layout = pango_free_layout; /* function for freeing pango layout */
- pango_layout_set_text (layout, text, -1);
- pango_layout_set_font_description (layout, desc);
- #ifdef ENABLE_PANGO_MARKUP
- if (attrs)
- pango_layout_set_attributes (layout, attrs);
- #endif
- PangoRectangle logical_rect;
- pango_layout_get_extents (layout, NULL, &logical_rect);
- /* if pango doesn't like the font then it sets width=0 but height = garbage */
- if (logical_rect.width == 0)
- logical_rect.height = 0;
- const double textlayout_scale = POINTS_PER_INCH / (FONT_DPI * PANGO_SCALE);
- span->size.x = logical_rect.width * textlayout_scale;
- span->size.y = logical_rect.height * textlayout_scale;
- /* The y offset from baseline to 0,0 of the bitmap representation */
- span->yoffset_layout = pango_layout_get_baseline (layout) * textlayout_scale;
- /* The distance below midline for y centering of text strings */
- span->yoffset_centerline = 0.05 * span->font->size;
- return logical_rect.width != 0 || strcmp(text, "") == 0;
- }
- static gvtextlayout_engine_t pango_textlayout_engine = {
- pango_textlayout,
- };
- gvplugin_installed_t gvtextlayout_pango_types[] = {
- {0, "textlayout", 10, &pango_textlayout_engine, NULL},
- {0, NULL, 0, NULL, NULL}
- };
|