gvtextlayout_gdiplus.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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 <memory>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <gvc/gvplugin_textlayout.h>
  15. #include "gvplugin_gdiplus.h"
  16. using namespace Gdiplus;
  17. static int CALLBACK fetch_first_font(const LOGFONTA *logFont,
  18. const TEXTMETRICA *, DWORD, LPARAM lParam)
  19. {
  20. /* save the first font we see in the font enumeration */
  21. *((LOGFONTA *)lParam) = *logFont;
  22. return 0;
  23. }
  24. Layout::Layout(char *fontname, double fontsize, char* string)
  25. {
  26. /* convert incoming UTF8 string to wide chars */
  27. /* NOTE: conversion is 1 or more UTF8 chars to 1 wide char */
  28. int len = strlen(string);
  29. text.resize(len);
  30. text.resize(MultiByteToWideChar(CP_UTF8, 0, string, len, &text[0], len));
  31. /* search for a font with this name. if we can't find it, use the generic serif instead */
  32. /* NOTE: GDI font search is more comprehensive than GDI+ and will look for variants e.g. Arial Bold */
  33. DeviceContext reference;
  34. LOGFONTA font_to_find;
  35. font_to_find.lfCharSet = ANSI_CHARSET;
  36. strncpy(font_to_find.lfFaceName, fontname, sizeof(font_to_find.lfFaceName) - 1);
  37. font_to_find.lfFaceName[sizeof(font_to_find.lfFaceName) - 1] = '\0';
  38. font_to_find.lfPitchAndFamily = 0;
  39. LOGFONTA found_font;
  40. if (EnumFontFamiliesExA(reference.hdc,
  41. &font_to_find,
  42. fetch_first_font,
  43. (LPARAM)&found_font,
  44. 0) == 0) {
  45. found_font.lfHeight = (LONG)-fontsize;
  46. found_font.lfWidth = 0;
  47. font = std::make_unique<Font>(reference.hdc, &found_font);
  48. }
  49. else
  50. font = std::make_unique<Font>(FontFamily::GenericSerif(), fontsize);
  51. }
  52. void gdiplus_free_layout(void *layout)
  53. {
  54. if (layout)
  55. delete reinterpret_cast<Layout*>(layout);
  56. };
  57. bool gdiplus_textlayout(textspan_t *span, char **)
  58. {
  59. /* ensure GDI+ is started up: since we get called outside of a job, we can't rely on GDI+ startup then */
  60. UseGdiplus();
  61. Layout* layout = new Layout(span->font->name, span->font->size, span->str);
  62. /* measure the text */
  63. /* NOTE: use TextRenderingHintAntiAlias + GetGenericTypographic to get a layout without extra space at beginning and end */
  64. RectF boundingBox;
  65. DeviceContext deviceContext;
  66. Graphics measureGraphics(deviceContext.hdc);
  67. measureGraphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
  68. measureGraphics.MeasureString(
  69. &layout->text[0],
  70. layout->text.size(),
  71. layout->font.get(),
  72. PointF(0.0f, 0.0f),
  73. GetGenericTypographic(),
  74. &boundingBox);
  75. FontFamily fontFamily;
  76. layout->font->GetFamily(&fontFamily);
  77. int style = layout->font->GetStyle();
  78. span->layout = layout;
  79. span->free_layout = &gdiplus_free_layout;
  80. span->size.x = boundingBox.Width;
  81. span->size.y = layout->font->GetHeight(&measureGraphics);
  82. span->yoffset_layout = fontFamily.GetCellAscent(style) * span->font->size / fontFamily.GetEmHeight(style); /* convert design units to pixels */
  83. span->yoffset_centerline = 0;
  84. return true;
  85. };
  86. static gvtextlayout_engine_t gdiplus_textlayout_engine = {
  87. gdiplus_textlayout
  88. };
  89. gvplugin_installed_t gvtextlayout_gdiplus_types[] = {
  90. {0, "textlayout", 8, &gdiplus_textlayout_engine, nullptr},
  91. {0, nullptr, 0, nullptr, nullptr}
  92. };