Skip to content

Instantly share code, notes, and snippets.

@ebraminio
Last active February 22, 2020 10:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ebraminio/4dd7b90a87edf0ed0624aee23e6b3645 to your computer and use it in GitHub Desktop.
Save ebraminio/4dd7b90a87edf0ed0624aee23e6b3645 to your computer and use it in GitHub Desktop.
Print SVG path from GDI's HFONT using GetGlyphOutlineW
// gcc a.cc -lgdi32 && a.exe
#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include <windows.h>
int
main ()
{
HFONT hFont = CreateFont (1000, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, TEXT ("Tahoma"));
HDC hdc = GetDC (NULL);
SelectObject (hdc, hFont);
GLYPHMETRICS gm = {0};
MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
unsigned gid = 34;
unsigned flags = GGO_GLYPH_INDEX | GGO_NATIVE;
DWORD buffer_size = GetGlyphOutlineW (hdc, gid, flags, &gm, 0, NULL, &mat2);
if (buffer_size == GDI_ERROR) return 1;
unsigned char *buffer = (unsigned char *) malloc (buffer_size);
buffer_size =
GetGlyphOutlineW (hdc, gid, flags, &gm, buffer_size, buffer, &mat2);
if (buffer_size < 0) return 1;
uint8_t *ptr = buffer;
/* https://github.com/freedesktop/cairo/blob/3a03c1b/src/win32/cairo-win32-font.c#L1690-L1804
*/
while (ptr < buffer + buffer_size)
{
auto &header = *(const TTPOLYGONHEADER *) ptr;
uint8_t *end_poly = ptr + header.cb;
ptr += sizeof (TTPOLYGONHEADER);
#define fixed_to_float(fixed) ((float) fixed.value + fixed.fract / 65536.f)
#define fixed_round(fixed) (int) roundf (fixed_to_float (fixed))
printf ("M%d,%d", fixed_round (header.pfxStart.x),
fixed_round (header.pfxStart.y));
while (ptr < end_poly)
{
auto &curve = *(const TTPOLYCURVE *) ptr;
ptr += sizeof (TTPOLYCURVE) + sizeof (POINTFX) * (curve.cpfx - 1);
switch (curve.wType)
{
case TT_PRIM_LINE:
for (int i = 0; i < curve.cpfx; ++i)
printf ("L%d,%d",
fixed_round (curve.apfx[i].x),
fixed_round (curve.apfx[i].y));
break;
case TT_PRIM_QSPLINE:
for (int i = 0; i < curve.cpfx - 1; ++i)
{
float cx = fixed_to_float (curve.apfx[i].x),
cy = fixed_to_float (curve.apfx[i].y), x, y;
if (i + 1 == curve.cpfx - 1)
{
x = fixed_to_float (curve.apfx[i + 1].x);
y = fixed_to_float (curve.apfx[i + 1].y);
}
else
{
x = (cx + fixed_to_float (curve.apfx[i + 1].x)) / 2.f;
y = (cy + fixed_to_float (curve.apfx[i + 1].y)) / 2.f;
}
printf ("Q%d,%d %d,%d",
(int) roundf (cx), (int) roundf (cy),
(int) roundf (x), (int) roundf (y));
}
break;
case TT_PRIM_CSPLINE:
for (int i = 0; i < curve.cpfx - 2; i += 2)
printf ("C%d,%d %d,%d %d,%d", fixed_round (curve.apfx[i + 0].x),
fixed_round (curve.apfx[i + 0].y),
fixed_round (curve.apfx[i + 1].x),
fixed_round (curve.apfx[i + 1].y),
fixed_round (curve.apfx[i + 2].x),
fixed_round (curve.apfx[i + 2].y));
#undef fixed_to_float
#undef fixed_round
break;
}
}
printf ("Z");
}
free (buffer);
printf ("\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment