Graphviz 14.1.2~dev.20260104.2039
Loading...
Searching...
No Matches
gvtextlayout_gd.c
Go to the documentation of this file.
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
11#include "config.h"
12#include "gd_psfontResolve.h"
13#include <stdbool.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
18#include <gd.h>
19#include <common/const.h>
20#include <util/agxbuf.h>
21#include <util/strcasecmp.h>
22#include <util/strview.h>
23
24#ifdef HAVE_GD_FREETYPE
25
26/* fontsize at which text is omitted entirely */
27#define FONTSIZE_MUCH_TOO_SMALL 0.15
28/* fontsize at which text is rendered by a simple line */
29#define FONTSIZE_TOO_SMALL 1.5
30
31#ifndef HAVE_GD_FONTCONFIG
32/* gd_alternate_fontlist;
33 * Sometimes fonts are stored under a different name,
34 * especially on Windows. Without fontconfig, we provide
35 * here some rudimentary name mapping.
36 */
37char *gd_alternate_fontlist(const char *font) {
38 char *p;
39
40 /* fontbuf to contain font without style descriptions like -Roman or -Italic */
41 strview_t fontlist = strview(font, '\0');
42 if ((p = strchr(font, '-')) || (p = strchr(font, '_')))
43 fontlist.size = (size_t)(p - font);
44
45 if (strcasecmp(font, "times-bold") == 0
46 || strview_case_str_eq(fontlist, "timesbd")
47 || strview_case_str_eq(fontlist, "timesb"))
48 fontlist = strview("timesbd;Timesbd;TIMESBD;timesb;Timesb;TIMESB", '\0');
49
50 else if (strcasecmp(font, "times-italic") == 0
51 || strview_case_str_eq(fontlist, "timesi"))
52 fontlist = strview("timesi;Timesi;TIMESI", '\0');
53
54 else if (strcasecmp(font, "timesnewroman") == 0
55 || strcasecmp(font, "timesnew") == 0
56 || strcasecmp(font, "timesroman") == 0
57 || strview_case_str_eq(fontlist, "times"))
58 fontlist = strview("times;Times;TIMES", '\0');
59
60 else if (strcasecmp(font, "arial-bold") == 0
61 || strview_case_str_eq(fontlist, "arialb"))
62 fontlist = strview("arialb;Arialb;ARIALB", '\0');
63
64 else if (strcasecmp(font, "arial-italic") == 0
65 || strview_case_str_eq(fontlist, "ariali"))
66 fontlist = strview("ariali;Ariali;ARIALI", '\0');
67
68 else if (strview_case_str_eq(fontlist, "helvetica"))
69 fontlist = strview("helvetica;Helvetica;HELVETICA;arial;Arial;ARIAL", '\0');
70
71 else if (strview_case_str_eq(fontlist, "arial"))
72 fontlist = strview("arial;Arial;ARIAL", '\0');
73
74 else if (strview_case_str_eq(fontlist, "courier"))
75 fontlist = strview("courier;Courier;COURIER;cour", '\0');
76
77 return strview_str(fontlist);
78}
79#endif /* HAVE_GD_FONTCONFIG */
80
83{
84 agxbuf buf = {0};
85 agxbput(&buf, pa->family);
86
87 const char *separator = " ";
88 const char *const attributes[] = {pa->weight, pa->stretch, pa->style};
89 for (size_t i = 0; i < sizeof(attributes) / sizeof(attributes[0]); ++i) {
90 const char *const a = attributes[i];
91 if (a != NULL) {
92 agxbprint(&buf, "%s%s", separator, a);
93 separator = ", ";
94 }
95 }
96
97 return agxbdisown(&buf);
98}
99
100static bool gd_textlayout(textspan_t * span, char **fontpath)
101{
102 char *err, *fontlist, *fontname;
103 double fontsize;
104 int brect[8];
105 gdFTStringExtra strex;
106#ifdef HAVE_GD_FONTCONFIG
107 PostscriptAlias *pA;
108#endif
109
110 fontname = span->font->name;
111 fontsize = span->font->size;
112
113 strex.fontpath = NULL;
114 strex.flags = gdFTEX_RETURNFONTPATHNAME | gdFTEX_RESOLUTION;
115 strex.hdpi = strex.vdpi = POINTS_PER_INCH;
116
117 if (strchr(fontname, '/'))
118 strex.flags |= gdFTEX_FONTPATHNAME;
119 else
120 strex.flags |= gdFTEX_FONTCONFIG;
121
122 span->size.x = 0.0;
123 span->size.y = 0.0;
124 span->yoffset_layout = 0.0;
125
126 span->layout = NULL;
127 span->free_layout = NULL;
128
129 span->yoffset_centerline = 0.05 * fontsize;
130
131 if (fontname) {
132 if (fontsize <= FONTSIZE_MUCH_TOO_SMALL) {
133 return true; /* OK, but ignore text entirely */
134 } else if (fontsize <= FONTSIZE_TOO_SMALL) {
135 /* draw line in place of text */
136 /* fake a finite fontsize so that line length is calculated */
137 fontsize = FONTSIZE_TOO_SMALL;
138 }
139 /* call gdImageStringFT with null *im to get brect and to set font cache */
140 bool fontlist_needs_free = false;
141#ifdef HAVE_GD_FONTCONFIG
142 gdFTUseFontConfig(1); /* tell gd that we really want to use fontconfig, 'cos it s not the default */
143 pA = span->font->postscript_alias;
144 if (pA)
145 fontlist = gd_psfontResolve (pA);
146 else
147 fontlist = fontname;
148#else
149 fontlist = gd_alternate_fontlist(fontname);
150 fontlist_needs_free = true;
151#endif
152
153 err = gdImageStringFTEx(NULL, brect, -1, fontlist,
154 fontsize, 0, 0, 0, span->str, &strex);
155 if (fontlist_needs_free) {
156 free(fontlist);
157 }
158
159 if (err) {
160 agerrorf("%s\n", err);
161 return false; /* indicate error */
162 }
163
164 if (fontpath)
165 *fontpath = strex.fontpath;
166 else
167 free (strex.fontpath); /* strup'ed in libgd */
168
169 if (span->str && span->str[0]) {
170 /* can't use brect on some archtectures if strlen 0 */
171 span->size.x = (double) (brect[4] - brect[0]);
172 // LINESPACING specifies how much extra space to leave between lines
173 span->size.y = fontsize * LINESPACING;
174 }
175 }
176 return true;
177}
178
179static gvtextlayout_engine_t gd_textlayout_engine = {
180 gd_textlayout,
181};
182#endif
183
185#ifdef HAVE_GD_FREETYPE
186 {0, "textlayout", 2, &gd_textlayout_engine, NULL},
187#endif
188 {0, NULL, 0, NULL, NULL}
189};
Dynamically expanding string buffers.
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:345
#define LINESPACING
Definition const.h:70
#define FONTSIZE_MUCH_TOO_SMALL
Definition const.h:254
#define FONTSIZE_TOO_SMALL
Definition const.h:256
static char * err
Definition delaunay.c:532
char * gd_psfontResolve(PostscriptAlias *pa)
#define POINTS_PER_INCH
Definition geom.h:58
void free(void *)
node NULL
Definition grammar.y:181
void agerrorf(const char *fmt,...)
Definition agerror.c:165
agxbput(xb, staging)
gvplugin_installed_t gvtextlayout_gd_types[]
$2 font
Definition htmlparse.y:294
platform abstraction for case-insensitive string functions
ingroup plugin_api
Definition gvplugin.h:35
double x
Definition geom.h:29
double y
Definition geom.h:29
a non-owning string reference
Definition strview.h:20
size_t size
extent of the string in bytes
Definition strview.h:22
char * name
Definition textspan.h:54
PostscriptAlias * postscript_alias
Definition textspan.h:56
double size
Definition textspan.h:57
double yoffset_layout
Definition textspan.h:69
char * str
Definition textspan.h:65
void * layout
Definition textspan.h:67
pointf size
Definition textspan.h:70
textfont_t * font
Definition textspan.h:66
double yoffset_centerline
Definition textspan.h:69
void(* free_layout)(void *layout)
Definition textspan.h:68
Non-owning string references.
static char * strview_str(strview_t source)
make a heap-allocated string from this string view
Definition strview.h:41
static strview_t strview(const char *referent, char terminator)
create a string reference
Definition strview.h:26
static bool strview_case_str_eq(strview_t a, const char *b)
compare a string reference to a string for case insensitive equality
Definition strview.h:62