gvtextlayout_gd.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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 <cgraph/strview.h>
  12. #include "gd_psfontResolve.h"
  13. #include <stdbool.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <gvc/gvplugin_textlayout.h>
  18. #include <gd.h>
  19. #include <common/const.h>
  20. #include <util/strcasecmp.h>
  21. #ifdef HAVE_GD_FREETYPE
  22. /* fontsize at which text is omitted entirely */
  23. #define FONTSIZE_MUCH_TOO_SMALL 0.15
  24. /* fontsize at which text is rendered by a simple line */
  25. #define FONTSIZE_TOO_SMALL 1.5
  26. #ifndef HAVE_GD_FONTCONFIG
  27. /* gd_alternate_fontlist;
  28. * Sometimes fonts are stored under a different name,
  29. * especially on Windows. Without fontconfig, we provide
  30. * here some rudimentary name mapping.
  31. */
  32. char *gd_alternate_fontlist(const char *font) {
  33. char *p;
  34. /* fontbuf to contain font without style descriptions like -Roman or -Italic */
  35. strview_t fontlist = strview(font, '\0');
  36. if ((p = strchr(font, '-')) || (p = strchr(font, '_')))
  37. fontlist.size = (size_t)(p - font);
  38. if ((strcasecmp(font, "times-bold") == 0)
  39. || strview_case_str_eq(fontlist, "timesbd")
  40. || strview_case_str_eq(fontlist, "timesb"))
  41. fontlist = strview("timesbd;Timesbd;TIMESBD;timesb;Timesb;TIMESB", '\0');
  42. else if ((strcasecmp(font, "times-italic") == 0)
  43. || strview_case_str_eq(fontlist, "timesi"))
  44. fontlist = strview("timesi;Timesi;TIMESI", '\0');
  45. else if ((strcasecmp(font, "timesnewroman") == 0)
  46. || (strcasecmp(font, "timesnew") == 0)
  47. || (strcasecmp(font, "timesroman") == 0)
  48. || strview_case_str_eq(fontlist, "times"))
  49. fontlist = strview("times;Times;TIMES", '\0');
  50. else if ((strcasecmp(font, "arial-bold") == 0)
  51. || strview_case_str_eq(fontlist, "arialb"))
  52. fontlist = strview("arialb;Arialb;ARIALB", '\0');
  53. else if ((strcasecmp(font, "arial-italic") == 0)
  54. || strview_case_str_eq(fontlist, "ariali"))
  55. fontlist = strview("ariali;Ariali;ARIALI", '\0');
  56. else if (strview_case_str_eq(fontlist, "helvetica"))
  57. fontlist = strview("helvetica;Helvetica;HELVETICA;arial;Arial;ARIAL", '\0');
  58. else if (strview_case_str_eq(fontlist, "arial"))
  59. fontlist = strview("arial;Arial;ARIAL", '\0');
  60. else if (strview_case_str_eq(fontlist, "courier"))
  61. fontlist = strview("courier;Courier;COURIER;cour", '\0');
  62. return strview_str(fontlist);
  63. }
  64. #endif /* HAVE_GD_FONTCONFIG */
  65. /* gd_psfontResolve:
  66. * * Construct alias for postscript fontname.
  67. * * NB. Uses a static array - non-reentrant.
  68. * */
  69. #define ADD_ATTR(a) \
  70. if (a) { \
  71. strcat(buf, comma ? " " : ", "); \
  72. comma = 1; \
  73. strcat(buf, a); \
  74. }
  75. char* gd_psfontResolve (PostscriptAlias* pa)
  76. {
  77. static char buf[1024];
  78. int comma=0;
  79. strcpy(buf, pa->family);
  80. ADD_ATTR(pa->weight);
  81. ADD_ATTR(pa->stretch);
  82. ADD_ATTR(pa->style);
  83. return buf;
  84. }
  85. static bool gd_textlayout(textspan_t * span, char **fontpath)
  86. {
  87. char *err, *fontlist, *fontname;
  88. double fontsize;
  89. int brect[8];
  90. gdFTStringExtra strex;
  91. #ifdef HAVE_GD_FONTCONFIG
  92. PostscriptAlias *pA;
  93. #endif
  94. fontname = span->font->name;
  95. fontsize = span->font->size;
  96. strex.fontpath = NULL;
  97. strex.flags = gdFTEX_RETURNFONTPATHNAME | gdFTEX_RESOLUTION;
  98. strex.hdpi = strex.vdpi = POINTS_PER_INCH;
  99. if (strchr(fontname, '/'))
  100. strex.flags |= gdFTEX_FONTPATHNAME;
  101. else
  102. strex.flags |= gdFTEX_FONTCONFIG;
  103. span->size.x = 0.0;
  104. span->size.y = 0.0;
  105. span->yoffset_layout = 0.0;
  106. span->layout = NULL;
  107. span->free_layout = NULL;
  108. span->yoffset_centerline = 0.05 * fontsize;
  109. if (fontname) {
  110. if (fontsize <= FONTSIZE_MUCH_TOO_SMALL) {
  111. return true; /* OK, but ignore text entirely */
  112. } else if (fontsize <= FONTSIZE_TOO_SMALL) {
  113. /* draw line in place of text */
  114. /* fake a finite fontsize so that line length is calculated */
  115. fontsize = FONTSIZE_TOO_SMALL;
  116. }
  117. /* call gdImageStringFT with null *im to get brect and to set font cache */
  118. #ifdef HAVE_GD_FONTCONFIG
  119. gdFTUseFontConfig(1); /* tell gd that we really want to use fontconfig, 'cos it s not the default */
  120. pA = span->font->postscript_alias;
  121. if (pA)
  122. fontlist = gd_psfontResolve (pA);
  123. else
  124. fontlist = fontname;
  125. #else
  126. fontlist = gd_alternate_fontlist(fontname);
  127. #endif
  128. err = gdImageStringFTEx(NULL, brect, -1, fontlist,
  129. fontsize, 0, 0, 0, span->str, &strex);
  130. #ifndef HAVE_GD_FONTCONFIG
  131. free(fontlist);
  132. #endif
  133. if (err) {
  134. agerrorf("%s\n", err);
  135. return false; /* indicate error */
  136. }
  137. if (fontpath)
  138. *fontpath = strex.fontpath;
  139. else
  140. free (strex.fontpath); /* strup'ed in libgd */
  141. if (span->str && span->str[0]) {
  142. /* can't use brect on some archtectures if strlen 0 */
  143. span->size.x = (double) (brect[4] - brect[0]);
  144. // LINESPACING specifies how much extra space to leave between lines
  145. span->size.y = fontsize * LINESPACING;
  146. }
  147. }
  148. return true;
  149. }
  150. static gvtextlayout_engine_t gd_textlayout_engine = {
  151. gd_textlayout,
  152. };
  153. #endif
  154. gvplugin_installed_t gvtextlayout_gd_types[] = {
  155. #ifdef HAVE_GD_FREETYPE
  156. {0, "textlayout", 2, &gd_textlayout_engine, NULL},
  157. #endif
  158. {0, NULL, 0, NULL, NULL}
  159. };