blob: bde7624519eb470e65df77bfa927257bbd9db9f6 [file] [log] [blame]
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -05001/*
2 * Copyright © 2012,2013 Google, Inc.
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Google Author(s): Behdad Esfahbod
25 */
26
Behdad Esfahbodbe1c0ab2019-06-18 13:26:03 -070027#include "hb.hh"
28
29#ifndef HB_NO_BUFFER_SERIALIZE
30
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070031#include "hb-buffer.hh"
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050032
33
34static const char *serialize_formats[] = {
35 "text",
36 "json",
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +020037 nullptr
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050038};
39
Behdad Esfahbod288f2892013-09-06 15:40:22 -040040/**
41 * hb_buffer_serialize_list_formats:
42 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +040043 * Returns a list of supported buffer serialization formats.
Behdad Esfahbod288f2892013-09-06 15:40:22 -040044 *
45 * Return value: (transfer none):
Khaled Hosny9ab9f972016-01-01 20:38:21 +040046 * A string array of buffer serialization formats. Should not be freed.
Behdad Esfahbod288f2892013-09-06 15:40:22 -040047 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +040048 * Since: 0.9.7
Behdad Esfahbod288f2892013-09-06 15:40:22 -040049 **/
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050050const char **
Ebrahim Byagowie4120082018-12-17 21:31:01 +033051hb_buffer_serialize_list_formats ()
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050052{
53 return serialize_formats;
54}
55
Behdad Esfahbod288f2892013-09-06 15:40:22 -040056/**
57 * hb_buffer_serialize_format_from_string:
Khaled Hosny9ab9f972016-01-01 20:38:21 +040058 * @str: (array length=len) (element-type uint8_t): a string to parse
59 * @len: length of @str, or -1 if string is %NULL terminated
Behdad Esfahbod288f2892013-09-06 15:40:22 -040060 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +040061 * Parses a string into an #hb_buffer_serialize_format_t. Does not check if
62 * @str is a valid buffer serialization format, use
63 * hb_buffer_serialize_list_formats() to get the list of supported formats.
Behdad Esfahbod288f2892013-09-06 15:40:22 -040064 *
Ebrahim Byagowi687f6792018-10-20 17:50:39 +033065 * Return value:
Khaled Hosny9ab9f972016-01-01 20:38:21 +040066 * The parsed #hb_buffer_serialize_format_t.
Behdad Esfahbod288f2892013-09-06 15:40:22 -040067 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +040068 * Since: 0.9.7
Behdad Esfahbod288f2892013-09-06 15:40:22 -040069 **/
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050070hb_buffer_serialize_format_t
71hb_buffer_serialize_format_from_string (const char *str, int len)
72{
73 /* Upper-case it. */
Behdad Esfahbod76271002014-07-11 14:54:42 -040074 return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020u);
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050075}
76
Behdad Esfahbod288f2892013-09-06 15:40:22 -040077/**
78 * hb_buffer_serialize_format_to_string:
Khaled Hosny9ab9f972016-01-01 20:38:21 +040079 * @format: an #hb_buffer_serialize_format_t to convert.
Behdad Esfahbod288f2892013-09-06 15:40:22 -040080 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +040081 * Converts @format to the string corresponding it, or %NULL if it is not a valid
82 * #hb_buffer_serialize_format_t.
Behdad Esfahbod288f2892013-09-06 15:40:22 -040083 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +040084 * Return value: (transfer none):
85 * A %NULL terminated string corresponding to @format. Should not be freed.
Behdad Esfahbod288f2892013-09-06 15:40:22 -040086 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +040087 * Since: 0.9.7
Behdad Esfahbod288f2892013-09-06 15:40:22 -040088 **/
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050089const char *
90hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
91{
Behdad Esfahboddba1ac12019-05-12 15:33:31 -070092 switch ((unsigned) format)
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050093 {
Simon Cozens57a528a2020-09-18 09:35:05 +010094 case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0];
95 case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1];
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050096 default:
Simon Cozens57a528a2020-09-18 09:35:05 +010097 case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr;
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -050098 }
99}
100
101static unsigned int
102_hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
Simon Cozens57a528a2020-09-18 09:35:05 +0100103 unsigned int start,
104 unsigned int end,
105 char *buf,
106 unsigned int buf_size,
107 unsigned int *buf_consumed,
108 hb_font_t *font,
109 hb_buffer_serialize_flags_t flags)
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500110{
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200111 hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
Behdad Esfahbod75504a52015-08-18 18:47:02 +0100112 hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ?
Simon Cozens57a528a2020-09-18 09:35:05 +0100113 nullptr : hb_buffer_get_glyph_positions (buffer, nullptr);
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500114
115 *buf_consumed = 0;
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100116 hb_position_t x = 0, y = 0;
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500117 for (unsigned int i = start; i < end; i++)
118 {
119 char b[1024];
120 char *p = b;
121
122 /* In the following code, we know b is large enough that no overflow can happen. */
123
124#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END
125
126 if (i)
127 *p++ = ',';
Simon Cozensc0716bb2020-09-18 10:31:28 +0100128 else
129 *p++ = '[';
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500130
131 *p++ = '{';
132
133 APPEND ("\"g\":");
134 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
135 {
136 char g[128];
137 hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g));
138 *p++ = '"';
139 for (char *q = g; *q; q++) {
Simon Cozens57a528a2020-09-18 09:35:05 +0100140 if (*q == '"')
141 *p++ = '\\';
142 *p++ = *q;
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500143 }
144 *p++ = '"';
145 }
146 else
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700147 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500148
149 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700150 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500151 }
152
153 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
154 {
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700155 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
Simon Cozens57a528a2020-09-18 09:35:05 +0100156 x+pos[i].x_offset, y+pos[i].y_offset));
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100157 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
Simon Cozens57a528a2020-09-18 09:35:05 +0100158 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
159 pos[i].x_advance, pos[i].y_advance));
Behdad Esfahbod40bd7e92016-05-02 14:47:45 +0200160 }
161
162 if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
163 {
Jonathan Kew331d66c2014-07-19 23:09:09 +0100164 if (info[i].mask & HB_GLYPH_FLAG_DEFINED)
Simon Cozens57a528a2020-09-18 09:35:05 +0100165 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED));
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500166 }
167
Behdad Esfahbodfdd17702015-08-24 13:49:55 +0100168 if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
169 {
170 hb_glyph_extents_t extents;
171 hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700172 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d",
Simon Cozens57a528a2020-09-18 09:35:05 +0100173 extents.x_bearing, extents.y_bearing));
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700174 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
Simon Cozens57a528a2020-09-18 09:35:05 +0100175 extents.width, extents.height));
Behdad Esfahbodfdd17702015-08-24 13:49:55 +0100176 }
177
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500178 *p++ = '}';
Simon Cozensc0716bb2020-09-18 10:31:28 +0100179 if (i == end-1)
180 *p++ = ']';
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500181
Behdad Esfahbod176fd172013-10-30 17:27:24 +0000182 unsigned int l = p - b;
183 if (buf_size > l)
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500184 {
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500185 memcpy (buf, b, l);
186 buf += l;
187 buf_size -= l;
188 *buf_consumed += l;
189 *buf = '\0';
190 } else
191 return i - start;
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100192
Behdad Esfahbod994eb772018-01-11 11:37:12 +0100193 if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100194 {
195 x += pos[i].x_advance;
196 y += pos[i].y_advance;
197 }
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500198 }
199
200 return end - start;
201}
202
203static unsigned int
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100204_hb_buffer_serialize_unicode_json (hb_buffer_t *buffer,
Simon Cozens57a528a2020-09-18 09:35:05 +0100205 unsigned int start,
206 unsigned int end,
207 char *buf,
208 unsigned int buf_size,
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100209 unsigned int *buf_consumed,
210 hb_buffer_serialize_flags_t flags)
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100211{
Simon Cozens57a528a2020-09-18 09:35:05 +0100212 hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100213
Simon Cozens57a528a2020-09-18 09:35:05 +0100214 *buf_consumed = 0;
215 for (unsigned int i = start; i < end; i++)
216 {
217 char b[1024];
218 char *p = b;
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100219
Simon Cozens57a528a2020-09-18 09:35:05 +0100220 if (i)
221 *p++ = ',';
Simon Cozensc0716bb2020-09-18 10:31:28 +0100222 else
223 *p++ = '[';
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100224
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100225 *p++ = '{';
226
227 APPEND ("\"u\":");
228
Simon Cozens57a528a2020-09-18 09:35:05 +0100229 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100230
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100231 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
232 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
233 }
234
235 *p++ = '}';
236
Simon Cozensc0716bb2020-09-18 10:31:28 +0100237 if (i == end-1)
238 *p++ = ']';
239
Simon Cozens57a528a2020-09-18 09:35:05 +0100240 unsigned int l = p - b;
241 if (buf_size > l)
242 {
243 memcpy (buf, b, l);
244 buf += l;
245 buf_size -= l;
246 *buf_consumed += l;
247 *buf = '\0';
248 } else
249 return i - start;
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100250
Simon Cozens57a528a2020-09-18 09:35:05 +0100251 }
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100252
Simon Cozens57a528a2020-09-18 09:35:05 +0100253 return end - start;
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100254}
255
256static unsigned int
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500257_hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
Simon Cozens57a528a2020-09-18 09:35:05 +0100258 unsigned int start,
259 unsigned int end,
260 char *buf,
261 unsigned int buf_size,
262 unsigned int *buf_consumed,
263 hb_font_t *font,
264 hb_buffer_serialize_flags_t flags)
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500265{
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200266 hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
Behdad Esfahbod75504a52015-08-18 18:47:02 +0100267 hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ?
Simon Cozens57a528a2020-09-18 09:35:05 +0100268 nullptr : hb_buffer_get_glyph_positions (buffer, nullptr);
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500269
270 *buf_consumed = 0;
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100271 hb_position_t x = 0, y = 0;
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500272 for (unsigned int i = start; i < end; i++)
273 {
274 char b[1024];
275 char *p = b;
276
277 /* In the following code, we know b is large enough that no overflow can happen. */
278
279 if (i)
280 *p++ = '|';
Simon Cozensc0716bb2020-09-18 10:31:28 +0100281 else
282 *p++ = '[';
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500283
284 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
285 {
286 hb_font_glyph_to_string (font, info[i].codepoint, p, 128);
287 p += strlen (p);
288 }
289 else
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700290 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500291
292 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700293 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500294 }
295
296 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
297 {
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100298 if (x+pos[i].x_offset || y+pos[i].y_offset)
Simon Cozens57a528a2020-09-18 09:35:05 +0100299 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset));
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500300
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100301 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
302 {
Simon Cozens57a528a2020-09-18 09:35:05 +0100303 *p++ = '+';
304 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
305 if (pos[i].y_advance)
306 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100307 }
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500308 }
309
Behdad Esfahbod40bd7e92016-05-02 14:47:45 +0200310 if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
311 {
Behdad Esfahbod7251c772018-12-15 21:00:55 -0500312 if (info[i].mask & HB_GLYPH_FLAG_DEFINED)
Simon Cozens57a528a2020-09-18 09:35:05 +0100313 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED));
Behdad Esfahbod40bd7e92016-05-02 14:47:45 +0200314 }
315
Behdad Esfahbodfdd17702015-08-24 13:49:55 +0100316 if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
317 {
318 hb_glyph_extents_t extents;
319 hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
Behdad Esfahbod41248cc2019-05-07 20:54:31 -0700320 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height));
Behdad Esfahbodfdd17702015-08-24 13:49:55 +0100321 }
322
Simon Cozensc0716bb2020-09-18 10:31:28 +0100323 if (i == end-1) {
324 *p++ = ']';
325 }
326
Behdad Esfahbod176fd172013-10-30 17:27:24 +0000327 unsigned int l = p - b;
328 if (buf_size > l)
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500329 {
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500330 memcpy (buf, b, l);
331 buf += l;
332 buf_size -= l;
333 *buf_consumed += l;
334 *buf = '\0';
335 } else
336 return i - start;
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100337
Behdad Esfahbod994eb772018-01-11 11:37:12 +0100338 if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
Behdad Esfahbod71fd6322018-01-10 02:20:14 +0100339 {
340 x += pos[i].x_advance;
341 y += pos[i].y_advance;
342 }
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500343 }
344
345 return end - start;
346}
347
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100348
349static unsigned int
350_hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
Simon Cozens57a528a2020-09-18 09:35:05 +0100351 unsigned int start,
352 unsigned int end,
353 char *buf,
354 unsigned int buf_size,
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100355 unsigned int *buf_consumed,
356 hb_buffer_serialize_flags_t flags)
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100357{
Simon Cozensaff6a362020-09-18 09:27:27 +0100358 hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
359 *buf_consumed = 0;
360 for (unsigned int i = start; i < end; i++)
361 {
362 char b[1024];
363 char *p = b;
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100364
Simon Cozensaff6a362020-09-18 09:27:27 +0100365 if (i)
366 *p++ = '|';
Simon Cozensc0716bb2020-09-18 10:31:28 +0100367 else
368 *p++ = '<';
Simon Cozensaff6a362020-09-18 09:27:27 +0100369
370 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "U+%04X", info[i].codepoint));
Simon Cozensc0716bb2020-09-18 10:31:28 +0100371
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100372 if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
373 p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
374 }
375
Simon Cozensc0716bb2020-09-18 10:31:28 +0100376 if (i == end-1)
377 *p++ = '>';
378
Simon Cozensaff6a362020-09-18 09:27:27 +0100379 unsigned int l = p - b;
380 if (buf_size > l)
381 {
382 memcpy (buf, b, l);
383 buf += l;
384 buf_size -= l;
385 *buf_consumed += l;
386 *buf = '\0';
387 } else
388 return i - start;
389 }
390 return end - start;
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100391}
392
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400393/**
394 * hb_buffer_serialize_glyphs:
Khaled Hosny9ab9f972016-01-01 20:38:21 +0400395 * @buffer: an #hb_buffer_t buffer.
396 * @start: the first item in @buffer to serialize.
397 * @end: the last item in @buffer to serialize.
398 * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
399 * write serialized buffer into.
400 * @buf_size: the size of @buf.
401 * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf.
402 * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to
403 * read glyph names and extents. If %NULL, and empty font will be used.
404 * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
405 * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties
406 * to serialize.
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400407 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +0400408 * Serializes @buffer into a textual representation of its glyph content,
409 * useful for showing the contents of the buffer, for example during debugging.
410 * There are currently two supported serialization formats:
411 *
412 * ## text
413 * A human-readable, plain text format.
414 * The serialized glyphs will look something like:
415 *
416 * ```
417 * [uni0651=0@518,0+0|uni0628=0+1897]
418 * ```
419 * - The serialized glyphs are delimited with `[` and `]`.
420 * - Glyphs are separated with `|`
421 * - Each glyph starts with glyph name, or glyph index if
422 * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. Then,
423 * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, `=` then #hb_glyph_info_t.cluster.
424 * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format:
425 * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then,
426 * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then,
427 * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the
428 * #hb_glyph_extents_t in the format
429 * `&lt;x_bearing,y_bearing,width,height&gt;`
430 *
431 * ## json
Simon Cozens36ede562020-09-18 10:24:29 +0100432 * A machine-readable, structured format.
433 * The serialized glyphs will look something like:
434 *
435 * ```
436 * [{"g":"uni0651","cl":0,"dx":518,"dy":0,"ax":0,"ay":0},
437 * {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}]
438 * ```
439 * Each glyph is a JSON object, with the following properties:
440 * - `g`: the glyph name or glyph index if
441 * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set.
442 * - `cl`: #hb_glyph_info_t.cluster if
443 * #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set.
444 * - `dx`,`dy`,`ax`,`ay`: #hb_glyph_position_t.x_offset, #hb_glyph_position_t.y_offset,
445 * #hb_glyph_position_t.x_advance and #hb_glyph_position_t.y_advance
446 * respectively, if #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set.
447 * - `xb`,`yb`,`w`,`h`: #hb_glyph_extents_t.x_bearing, #hb_glyph_extents_t.y_bearing,
448 * #hb_glyph_extents_t.width and #hb_glyph_extents_t.height respectively if
449 * #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set.
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400450 *
Ebrahim Byagowi687f6792018-10-20 17:50:39 +0330451 * Return value:
Khaled Hosny9ab9f972016-01-01 20:38:21 +0400452 * The number of serialized items.
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400453 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +0400454 * Since: 0.9.7
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400455 **/
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500456unsigned int
457hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
Simon Cozens57a528a2020-09-18 09:35:05 +0100458 unsigned int start,
459 unsigned int end,
460 char *buf,
461 unsigned int buf_size,
462 unsigned int *buf_consumed,
463 hb_font_t *font,
464 hb_buffer_serialize_format_t format,
465 hb_buffer_serialize_flags_t flags)
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500466{
467 assert (start <= end && end <= buffer->len);
468
Behdad Esfahbodbcc8f322013-02-27 12:02:42 -0500469 unsigned int sconsumed;
470 if (!buf_consumed)
471 buf_consumed = &sconsumed;
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500472 *buf_consumed = 0;
Behdad Esfahbod3c080a72017-03-07 18:13:28 -0800473 if (buf_size)
474 *buf = '\0';
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500475
Ebrahim Byagowi48ad7452020-07-29 08:09:08 +0430476 assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) ||
Simon Cozens57a528a2020-09-18 09:35:05 +0100477 (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS));
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500478
Behdad Esfahbod9ea0aa42015-12-18 17:30:18 +0000479 if (!buffer->have_positions)
480 flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS;
481
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500482 if (unlikely (start == end))
483 return 0;
484
485 if (!font)
486 font = hb_font_get_empty ();
487
488 switch (format)
489 {
490 case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
491 return _hb_buffer_serialize_glyphs_text (buffer, start, end,
Simon Cozens57a528a2020-09-18 09:35:05 +0100492 buf, buf_size, buf_consumed,
493 font, flags);
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500494
495 case HB_BUFFER_SERIALIZE_FORMAT_JSON:
496 return _hb_buffer_serialize_glyphs_json (buffer, start, end,
Simon Cozens57a528a2020-09-18 09:35:05 +0100497 buf, buf_size, buf_consumed,
498 font, flags);
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500499
500 default:
501 case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
502 return 0;
503
504 }
505}
506
Simon Cozens36ede562020-09-18 10:24:29 +0100507/**
508 * hb_buffer_serialize_unicode:
509 * @buffer: an #hb_buffer_t buffer.
510 * @start: the first item in @buffer to serialize.
511 * @end: the last item in @buffer to serialize.
512 * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
513 * write serialized buffer into.
514 * @buf_size: the size of @buf.
515 * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf.
516 * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
517 *
518 * Serializes @buffer into a textual representation of its content,
519 * when the buffer contains Unicode codepoints (i.e., before shaping). This is
520 * useful for showing the contents of the buffer, for example during debugging.
521 * There are currently two supported serialization formats:
522 *
523 * ## text
524 * A human-readable, plain text format.
525 * The serialized codepoints will look something like:
526 *
527 * ```
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100528 * <U+0651=0|U+0628=1>
Simon Cozens36ede562020-09-18 10:24:29 +0100529 * ```
530 * - Glyphs are separated with `|`
531 * - Unicode codepoints are expressed as zero-padded four (or more)
532 * digit hexadecimal numbers preceded by `U+`
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100533 * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, the cluster
534 * will be indicated with a `=` then #hb_glyph_info_t.cluster.
Simon Cozens36ede562020-09-18 10:24:29 +0100535 *
536 * ## json
537 * A machine-readable, structured format.
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100538 * The serialized codepoints will be a list of objects with the following
539 * properties:
540 * - `g`: the Unicode codepoint as a decimal integer
541 * - `cl`: #hb_glyph_info_t.cluster if
542 * #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set.
543 *
544 * For example:
Simon Cozens36ede562020-09-18 10:24:29 +0100545 * ```
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100546 * [{g:1617,cl:0},{g:1576,cl:1}]
Simon Cozens36ede562020-09-18 10:24:29 +0100547 * ```
548 *
549 * Return value:
550 * The number of serialized items.
551 *
552 * Since: 2.7.3
553 **/
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100554unsigned int
555hb_buffer_serialize_unicode (hb_buffer_t *buffer,
Simon Cozens57a528a2020-09-18 09:35:05 +0100556 unsigned int start,
557 unsigned int end,
558 char *buf,
559 unsigned int buf_size,
560 unsigned int *buf_consumed,
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100561 hb_buffer_serialize_format_t format,
562 hb_buffer_serialize_flags_t flags)
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100563{
Simon Cozens57a528a2020-09-18 09:35:05 +0100564 assert (start <= end && end <= buffer->len);
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100565
Simon Cozens57a528a2020-09-18 09:35:05 +0100566 unsigned int sconsumed;
567 if (!buf_consumed)
568 buf_consumed = &sconsumed;
569 *buf_consumed = 0;
570 if (buf_size)
571 *buf = '\0';
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100572
Simon Cozens57a528a2020-09-18 09:35:05 +0100573 assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) ||
574 (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE));
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100575
Simon Cozens57a528a2020-09-18 09:35:05 +0100576 if (unlikely (start == end))
577 return 0;
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100578
Simon Cozens57a528a2020-09-18 09:35:05 +0100579 switch (format)
580 {
581 case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
582 return _hb_buffer_serialize_unicode_text (buffer, start, end,
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100583 buf, buf_size, buf_consumed, flags);
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100584
Simon Cozens57a528a2020-09-18 09:35:05 +0100585 case HB_BUFFER_SERIALIZE_FORMAT_JSON:
586 return _hb_buffer_serialize_unicode_json (buffer, start, end,
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100587 buf, buf_size, buf_consumed, flags);
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100588
Simon Cozens57a528a2020-09-18 09:35:05 +0100589 default:
590 case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
591 return 0;
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100592
Simon Cozens57a528a2020-09-18 09:35:05 +0100593 }
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100594}
595/**
596 * hb_buffer_serialize:
597 * @buffer: an #hb_buffer_t buffer.
598 * @start: the first item in @buffer to serialize.
599 * @end: the last item in @buffer to serialize.
600 * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
601 * write serialized buffer into.
602 * @buf_size: the size of @buf.
603 * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf.
604 * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to
605 * read glyph names and extents. If %NULL, and empty font will be used.
606 * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
607 * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties
608 * to serialize.
609 *
610 * Serializes @buffer into a textual representation of its content, whether
611 * Unicode codepoints or glyph identifiers and positioning information. This is
612 * useful for showing the contents of the buffer, for example during debugging.
Simon Cozens57a528a2020-09-18 09:35:05 +0100613 * See the documentation of hb_buffer_serialize_unicode() and
614 * hb_buffer_serialize_glyphs() for a description of the output format.
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100615 *
616 * Return value:
617 * The number of serialized items.
618 *
619 * Since: 2.7.3
620 **/
621unsigned int
622hb_buffer_serialize (hb_buffer_t *buffer,
Simon Cozens57a528a2020-09-18 09:35:05 +0100623 unsigned int start,
624 unsigned int end,
625 char *buf,
626 unsigned int buf_size,
627 unsigned int *buf_consumed,
628 hb_font_t *font,
629 hb_buffer_serialize_format_t format,
630 hb_buffer_serialize_flags_t flags)
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100631{
Simon Cozens57a528a2020-09-18 09:35:05 +0100632 if (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)
633 return hb_buffer_serialize_glyphs(buffer, start, end, buf, buf_size,
634 buf_consumed, font, format, flags);
635 else
636 return hb_buffer_serialize_unicode(buffer, start, end, buf, buf_size,
Simon Cozens3d3c87e2020-09-21 14:35:05 +0100637 buf_consumed, format, flags);
Simon Cozens58bcc1c2020-09-17 16:29:40 +0100638}
639
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430640static bool
641parse_int (const char *pp, const char *end, int32_t *pv)
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500642{
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430643 int v;
644 const char *p = pp;
Ebrahim Byagowi65690b52019-09-03 23:09:47 +0430645 if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */)))
646 return false;
Behdad Esfahbod20b817a2013-02-27 18:39:37 -0500647
648 *pv = v;
649 return true;
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500650}
651
Ebrahim Byagowi43372fb2019-09-03 01:02:40 +0430652static bool
653parse_uint (const char *pp, const char *end, uint32_t *pv)
Behdad Esfahbod20b817a2013-02-27 18:39:37 -0500654{
Ebrahim Byagowi005389b2019-09-03 17:00:46 +0430655 unsigned int v;
656 const char *p = pp;
Ebrahim Byagowi65690b52019-09-03 23:09:47 +0430657 if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */)))
658 return false;
Behdad Esfahbod20b817a2013-02-27 18:39:37 -0500659
660 *pv = v;
661 return true;
662}
663
Simon Cozensc03a2002020-09-18 14:19:17 +0100664static bool
665parse_hex (const char *pp, const char *end, uint32_t *pv)
666{
667 unsigned int v;
668 const char *p = pp;
669 if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, 16)))
670 return false;
671
672 *pv = v;
673 return true;
674}
675
Behdad Esfahbod20b817a2013-02-27 18:39:37 -0500676#include "hb-buffer-deserialize-json.hh"
Behdad Esfahbod847794e2013-02-27 17:59:28 -0500677#include "hb-buffer-deserialize-text.hh"
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500678
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400679/**
680 * hb_buffer_deserialize_glyphs:
Khaled Hosny9ab9f972016-01-01 20:38:21 +0400681 * @buffer: an #hb_buffer_t buffer.
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400682 * @buf: (array length=buf_len):
Ebrahim Byagowi687f6792018-10-20 17:50:39 +0330683 * @buf_len:
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400684 * @end_ptr: (out):
Ebrahim Byagowi687f6792018-10-20 17:50:39 +0330685 * @font:
686 * @format:
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400687 *
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400688 *
Ebrahim Byagowi687f6792018-10-20 17:50:39 +0330689 *
690 * Return value:
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400691 *
Khaled Hosny9ab9f972016-01-01 20:38:21 +0400692 * Since: 0.9.7
Behdad Esfahbod288f2892013-09-06 15:40:22 -0400693 **/
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500694hb_bool_t
695hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
Simon Cozens57a528a2020-09-18 09:35:05 +0100696 const char *buf,
697 int buf_len, /* -1 means nul-terminated */
698 const char **end_ptr, /* May be NULL */
699 hb_font_t *font, /* May be NULL */
700 hb_buffer_serialize_format_t format)
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500701{
Behdad Esfahbod847794e2013-02-27 17:59:28 -0500702 const char *end;
703 if (!end_ptr)
704 end_ptr = &end;
705 *end_ptr = buf;
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500706
Ebrahim Byagowi48ad7452020-07-29 08:09:08 +0430707 assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) ||
Simon Cozens57a528a2020-09-18 09:35:05 +0100708 (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS));
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500709
Behdad Esfahboda1f4d282020-09-21 16:50:11 -0600710 if (unlikely (hb_object_is_immutable (buffer)))
711 {
712 *end_ptr = buf;
713 return false;
714 }
715
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500716 if (buf_len == -1)
717 buf_len = strlen (buf);
718
719 if (!buf_len)
720 {
Behdad Esfahbod847794e2013-02-27 17:59:28 -0500721 *end_ptr = buf;
722 return false;
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500723 }
724
725 hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS);
726
727 if (!font)
728 font = hb_font_get_empty ();
729
730 switch (format)
731 {
732 case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
Simon Cozensc03a2002020-09-18 14:19:17 +0100733 return _hb_buffer_deserialize_text (buffer,
734 buf, buf_len, end_ptr,
735 font);
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500736
737 case HB_BUFFER_SERIALIZE_FORMAT_JSON:
Simon Cozensc03a2002020-09-18 14:19:17 +0100738 return _hb_buffer_deserialize_json (buffer,
739 buf, buf_len, end_ptr,
740 font);
Behdad Esfahbode54dd262013-02-27 13:01:02 -0500741
742 default:
743 case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
744 return false;
745
746 }
Behdad Esfahbodd3e14aa2013-02-27 11:06:36 -0500747}
Behdad Esfahbodbe1c0ab2019-06-18 13:26:03 -0700748
749
Simon Cozensc03a2002020-09-18 14:19:17 +0100750/**
751 * hb_buffer_deserialize_unicode:
752 * @buffer: an #hb_buffer_t buffer.
753 * @buf: (array length=buf_len):
754 * @buf_len:
755 * @end_ptr: (out):
756 * @format:
757 *
758 *
759 *
760 * Return value:
761 *
762 * Since: 2.7.3
763 **/
764hb_bool_t
765hb_buffer_deserialize_unicode (hb_buffer_t *buffer,
766 const char *buf,
767 int buf_len, /* -1 means nul-terminated */
768 const char **end_ptr, /* May be NULL */
769 hb_buffer_serialize_format_t format)
770{
771 const char *end;
772 if (!end_ptr)
773 end_ptr = &end;
774 *end_ptr = buf;
775
776 assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) ||
777 (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE));
778
779 if (buf_len == -1)
780 buf_len = strlen (buf);
781
782 if (!buf_len)
783 {
784 *end_ptr = buf;
785 return false;
786 }
787
788 hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
789
790 hb_font_t* font = hb_font_get_empty ();
791
792 switch (format)
793 {
794 case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
795 return _hb_buffer_deserialize_text (buffer,
796 buf, buf_len, end_ptr,
797 font);
798
799 case HB_BUFFER_SERIALIZE_FORMAT_JSON:
800 return _hb_buffer_deserialize_json (buffer,
801 buf, buf_len, end_ptr,
802 font);
803
804 default:
805 case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
806 printf("Invalid!\n");
807 return false;
808
809 }
810}
811
812
Behdad Esfahbodbe1c0ab2019-06-18 13:26:03 -0700813#endif