1 #define HB_WASM_INTERFACE(ret_t, name) __attribute__((export_name(#name))) ret_t name
2
3 #include <hb-wasm-api.h>
4
5 #include <graphite2/Segment.h>
6
7 #include <stdlib.h>
8 #include <string.h>
9
10 void debugprint1 (char *s, int32_t);
11 void debugprint2 (char *s, int32_t, int32_t);
12
copy_table(const void * data,unsigned int tag,size_t * len)13 static const void *copy_table (const void *data, unsigned int tag, size_t *len)
14 {
15 face_t *face = (face_t *) data;
16 blob_t blob = BLOB_INIT;
17 if (!face_copy_table (face, tag, &blob))
18 abort ();
19
20 *len = blob.length;
21 return blob.data;
22 }
23
free_table(const void * data,const void * table_data)24 static void free_table (const void *data, const void *table_data)
25 {
26 blob_t blob;
27 blob.length = 0; // Doesn't matter
28 blob.data = (char *) table_data;
29 blob_free (&blob);
30 }
31
32 void *
shape_plan_create(face_t * face)33 shape_plan_create (face_t *face)
34 {
35 const gr_face_ops ops = {sizeof (gr_face_ops), ©_table, &free_table};
36 gr_face *grface = gr_make_face_with_ops (face, &ops, gr_face_preloadAll);
37 return grface;
38 }
39
40 void
shape_plan_destroy(void * data)41 shape_plan_destroy (void *data)
42 {
43 gr_face_destroy ((gr_face *) data);
44 }
45
46 bool_t
shape(void * shape_plan,font_t * font,buffer_t * buffer,const feature_t * features,uint32_t num_features)47 shape (void *shape_plan,
48 font_t *font,
49 buffer_t *buffer,
50 const feature_t *features,
51 uint32_t num_features)
52 {
53 face_t *face = font_get_face (font);
54 gr_face *grface = (gr_face *) shape_plan;
55
56 direction_t direction = buffer_get_direction (buffer);
57 direction_t horiz_dir = script_get_horizontal_direction (buffer_get_script (buffer));
58 /* TODO vertical:
59 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
60 * Ogham fonts are supposed to be implemented BTT or not. Need to research that
61 * first. */
62 if ((DIRECTION_IS_HORIZONTAL (direction) &&
63 direction != horiz_dir && horiz_dir != DIRECTION_INVALID) ||
64 (DIRECTION_IS_VERTICAL (direction) &&
65 direction != DIRECTION_TTB))
66 {
67 buffer_reverse_clusters (buffer);
68 direction = DIRECTION_REVERSE (direction);
69 }
70
71 buffer_contents_t contents = BUFFER_CONTENTS_INIT;
72 if (!buffer_copy_contents (buffer, &contents))
73 return false;
74
75 gr_segment *seg = nullptr;
76 const gr_slot *is;
77 unsigned int ci = 0, ic = 0;
78 unsigned int curradvx = 0, curradvy = 0;
79 unsigned length = contents.length;
80
81 uint32_t *chars = (uint32_t *) malloc (length * sizeof (uint32_t));
82 if (!chars)
83 return false;
84 for (unsigned int i = 0; i < contents.length; ++i)
85 chars[i] = contents.info[i].codepoint;
86
87 seg = gr_make_seg (nullptr, grface,
88 0, // https://github.com/harfbuzz/harfbuzz/issues/3439#issuecomment-1442650148
89 nullptr,
90 gr_utf32, chars, contents.length,
91 2 | (direction == DIRECTION_RTL ? 1 : 0));
92
93 free (chars);
94
95 if (!seg)
96 return false;
97
98 unsigned int glyph_count = gr_seg_n_slots (seg);
99
100 struct cluster_t {
101 unsigned int base_char;
102 unsigned int num_chars;
103 unsigned int base_glyph;
104 unsigned int num_glyphs;
105 unsigned int cluster;
106 int advance;
107 };
108
109 length = glyph_count;
110 if (!buffer_contents_realloc (&contents, length))
111 return false;
112 cluster_t *clusters = (cluster_t *) malloc (length * sizeof (cluster_t));
113 uint32_t *gids = (uint32_t *) malloc (length * sizeof (uint32_t));
114 if (!clusters || !gids)
115 {
116 free (clusters);
117 free (gids);
118 return false;
119 }
120
121 memset (clusters, 0, sizeof (clusters[0]) * length);
122 codepoint_t *pg = gids;
123 clusters[0].cluster = contents.info[0].cluster;
124 unsigned int upem = face_get_upem (face);
125 int32_t font_x_scale, font_y_scale;
126 font_get_scale (font, &font_x_scale, &font_y_scale);
127 float xscale = (float) font_x_scale / upem;
128 float yscale = (float) font_y_scale / upem;
129 yscale *= yscale / xscale;
130 unsigned int curradv = 0;
131 if (DIRECTION_IS_BACKWARD (direction))
132 {
133 curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale;
134 clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv;
135 }
136 else
137 clusters[0].advance = 0;
138 for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
139 {
140 unsigned int before = gr_slot_before (is);
141 unsigned int after = gr_slot_after (is);
142 *pg = gr_slot_gid (is);
143 pg++;
144 while (clusters[ci].base_char > before && ci)
145 {
146 clusters[ci-1].num_chars += clusters[ci].num_chars;
147 clusters[ci-1].num_glyphs += clusters[ci].num_glyphs;
148 clusters[ci-1].advance += clusters[ci].advance;
149 ci--;
150 }
151
152 if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars)
153 {
154 cluster_t *c = clusters + ci + 1;
155 c->base_char = clusters[ci].base_char + clusters[ci].num_chars;
156 c->cluster = contents.info[c->base_char].cluster;
157 c->num_chars = before - c->base_char;
158 c->base_glyph = ic;
159 c->num_glyphs = 0;
160 if (DIRECTION_IS_BACKWARD (direction))
161 {
162 c->advance = curradv - gr_slot_origin_X(is) * xscale;
163 curradv -= c->advance;
164 }
165 else
166 {
167 auto origin_X = gr_slot_origin_X (is) * xscale;
168 c->advance = 0;
169 clusters[ci].advance += origin_X - curradv;
170 curradv = origin_X;
171 }
172 ci++;
173 }
174 clusters[ci].num_glyphs++;
175
176 if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
177 clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
178 }
179
180 if (DIRECTION_IS_BACKWARD (direction))
181 clusters[ci].advance += curradv;
182 else
183 clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv;
184 ci++;
185
186 for (unsigned int i = 0; i < ci; ++i)
187 {
188 for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
189 {
190 glyph_info_t *info = &contents.info[clusters[i].base_glyph + j];
191 info->codepoint = gids[clusters[i].base_glyph + j];
192 info->cluster = clusters[i].cluster;
193 info->var1 = (unsigned) clusters[i].advance; // all glyphs in the cluster get the same advance
194 }
195 }
196 contents.length = glyph_count;
197
198 /* Positioning. */
199 unsigned int currclus = 0xFFFFFFFF;
200 const glyph_info_t *info = contents.info;
201 glyph_position_t *pPos = contents.pos;
202 if (!DIRECTION_IS_BACKWARD (direction))
203 {
204 curradvx = 0;
205 for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
206 {
207 pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx;
208 pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
209 if (info->cluster != currclus) {
210 pPos->x_advance = (int) info->var1;
211 curradvx += pPos->x_advance;
212 currclus = info->cluster;
213 } else
214 pPos->x_advance = 0.;
215
216 pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale;
217 curradvy += pPos->y_advance;
218 }
219 buffer_set_contents (buffer, &contents);
220 }
221 else
222 {
223 curradvx = gr_seg_advance_X(seg) * xscale;
224 for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is))
225 {
226 if (info->cluster != currclus)
227 {
228 pPos->x_advance = (int) info->var1;
229 curradvx -= pPos->x_advance;
230 currclus = info->cluster;
231 } else
232 pPos->x_advance = 0.;
233
234 pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale;
235 curradvy -= pPos->y_advance;
236 pPos->x_offset = gr_slot_origin_X (is) * xscale - (int) info->var1 - curradvx + pPos->x_advance;
237 pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
238 }
239 buffer_set_contents (buffer, &contents);
240 buffer_reverse_clusters (buffer);
241 }
242
243 gr_seg_destroy (seg);
244 free (clusters);
245 free (gids);
246
247 bool ret = glyph_count;
248
249 return ret;
250 }
251