1#include <stdint.h>
2
3#include <ft2build.h>
4#include FT_FREETYPE_H
5#include FT_TRUETYPE_TABLES_H
6
7#if 0
8#include <freetype/freetype.h>
9#include <freetype/tttables.h>
10#endif
11
12#include <harfbuzz-shaper.h>
13#include "harfbuzz-unicode.h"
14
15static HB_Bool
16hb_freetype_string_to_glyphs(HB_Font font,
17                             const HB_UChar16 *chars, hb_uint32 len,
18                             HB_Glyph *glyphs, hb_uint32 *numGlyphs,
19                             HB_Bool is_rtl) {
20  FT_Face face = (FT_Face) font->userData;
21  if (len > *numGlyphs)
22    return 0;
23
24  size_t i = 0, j = 0;
25  while (i < len) {
26    const uint32_t cp = utf16_to_code_point(chars, len, &i);
27    glyphs[j++] = FT_Get_Char_Index(face, cp);
28  }
29
30  *numGlyphs = j;
31
32  return 1;
33}
34
35static void
36hb_freetype_advances_get(HB_Font font, const HB_Glyph *glyphs, hb_uint32 len,
37                         HB_Fixed *advances, int flags) {
38  FT_Face face = (FT_Face) font->userData;
39
40  hb_uint32 i;
41  for (i = 0; i < len; ++i) {
42    const FT_Error error = FT_Load_Glyph(face, glyphs[i], FT_LOAD_DEFAULT);
43    if (error) {
44      advances[i] = 0;
45      continue;
46    }
47
48    advances[i] = face->glyph->advance.x;
49  }
50}
51
52static HB_Bool
53hb_freetype_can_render(HB_Font font, const HB_UChar16 *chars, hb_uint32 len) {
54  FT_Face face = (FT_Face)font->userData;
55
56  size_t i = 0;
57  while (i < len) {
58    const uint32_t cp = utf16_to_code_point(chars, len, &i);
59    if (FT_Get_Char_Index(face, cp) == 0)
60      return 0;
61  }
62
63  return 1;
64}
65
66static HB_Error
67hb_freetype_outline_point_get(HB_Font font, HB_Glyph glyph, int flags,
68                              hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos,
69                              hb_uint32 *n_points) {
70  HB_Error error = HB_Err_Ok;
71  FT_Face face = (FT_Face) font->userData;
72
73  int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
74
75  if ((error = (HB_Error) FT_Load_Glyph(face, glyph, load_flags)))
76    return error;
77
78  if (face->glyph->format != ft_glyph_format_outline)
79    return (HB_Error)HB_Err_Invalid_SubTable;
80
81  *n_points = face->glyph->outline.n_points;
82  if (!(*n_points))
83    return HB_Err_Ok;
84
85  if (point > *n_points)
86    return (HB_Error)HB_Err_Invalid_SubTable;
87
88  *xpos = face->glyph->outline.points[point].x;
89  *ypos = face->glyph->outline.points[point].y;
90
91  return HB_Err_Ok;
92}
93
94static void
95hb_freetype_glyph_metrics_get(HB_Font font, HB_Glyph glyph,
96                              HB_GlyphMetrics *metrics) {
97  FT_Face face = (FT_Face) font->userData;
98
99  const FT_Error error = FT_Load_Glyph(face, glyph, FT_LOAD_DEFAULT);
100  if (error) {
101    metrics->x = metrics->y = metrics->width = metrics->height = 0;
102    metrics->xOffset = metrics->yOffset = 0;
103    return;
104  }
105
106  const FT_Glyph_Metrics *ftmetrics = &face->glyph->metrics;
107  metrics->width = ftmetrics->width;
108  metrics->height = ftmetrics->height;
109  metrics->x = ftmetrics->horiAdvance;
110  metrics->y = 0;  // unclear what this is
111  metrics->xOffset = ftmetrics->horiBearingX;
112  metrics->yOffset = ftmetrics->horiBearingY;
113}
114
115static HB_Fixed
116hb_freetype_font_metric_get(HB_Font font, HB_FontMetric metric) {
117  FT_Face face = (FT_Face) font->userData;
118
119  switch (metric) {
120  case HB_FontAscent:
121    // Note that we aren't scanning the VDMX table which we probably would in
122    // an ideal world.
123    return face->ascender;
124  default:
125    return 0;
126  }
127}
128
129const HB_FontClass hb_freetype_class = {
130  hb_freetype_string_to_glyphs,
131  hb_freetype_advances_get,
132  hb_freetype_can_render,
133  hb_freetype_outline_point_get,
134  hb_freetype_glyph_metrics_get,
135  hb_freetype_font_metric_get,
136};
137
138HB_Error
139hb_freetype_table_sfnt_get(void *voidface, const HB_Tag tag, HB_Byte *buffer, HB_UInt *len) {
140  FT_Face face = (FT_Face) voidface;
141  FT_ULong ftlen = *len;
142
143  if (!FT_IS_SFNT(face))
144    return HB_Err_Invalid_Argument;
145
146  const FT_Error error = FT_Load_Sfnt_Table(face, tag, 0, buffer, &ftlen);
147  *len = ftlen;
148  return (HB_Error) error;
149}
150