|
1 /* |
|
2 * Copyright © 1998-2004 David Turner and Werner Lemberg |
|
3 * Copyright © 2004,2007,2009,2010 Red Hat, Inc. |
|
4 * Copyright © 2011,2012 Google, Inc. |
|
5 * |
|
6 * This is part of HarfBuzz, a text shaping library. |
|
7 * |
|
8 * Permission is hereby granted, without written agreement and without |
|
9 * license or royalty fees, to use, copy, modify, and distribute this |
|
10 * software and its documentation for any purpose, provided that the |
|
11 * above copyright notice and the following two paragraphs appear in |
|
12 * all copies of this software. |
|
13 * |
|
14 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
|
15 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
|
16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
|
17 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
|
18 * DAMAGE. |
|
19 * |
|
20 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
|
21 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
22 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
|
23 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
|
24 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
|
25 * |
|
26 * Red Hat Author(s): Owen Taylor, Behdad Esfahbod |
|
27 * Google Author(s): Behdad Esfahbod |
|
28 */ |
|
29 |
|
30 #include "hb-buffer-private.hh" |
|
31 #include "hb-utf-private.hh" |
|
32 |
|
33 |
|
34 #ifndef HB_DEBUG_BUFFER |
|
35 #define HB_DEBUG_BUFFER (HB_DEBUG+0) |
|
36 #endif |
|
37 |
|
38 |
|
39 hb_bool_t |
|
40 hb_segment_properties_equal (const hb_segment_properties_t *a, |
|
41 const hb_segment_properties_t *b) |
|
42 { |
|
43 return a->direction == b->direction && |
|
44 a->script == b->script && |
|
45 a->language == b->language && |
|
46 a->reserved1 == b->reserved1 && |
|
47 a->reserved2 == b->reserved2; |
|
48 |
|
49 } |
|
50 |
|
51 unsigned int |
|
52 hb_segment_properties_hash (const hb_segment_properties_t *p) |
|
53 { |
|
54 return (unsigned int) p->direction ^ |
|
55 (unsigned int) p->script ^ |
|
56 (intptr_t) (p->language); |
|
57 } |
|
58 |
|
59 |
|
60 |
|
61 /* Here is how the buffer works internally: |
|
62 * |
|
63 * There are two info pointers: info and out_info. They always have |
|
64 * the same allocated size, but different lengths. |
|
65 * |
|
66 * As an optimization, both info and out_info may point to the |
|
67 * same piece of memory, which is owned by info. This remains the |
|
68 * case as long as out_len doesn't exceed i at any time. |
|
69 * In that case, swap_buffers() is no-op and the glyph operations operate |
|
70 * mostly in-place. |
|
71 * |
|
72 * As soon as out_info gets longer than info, out_info is moved over |
|
73 * to an alternate buffer (which we reuse the pos buffer for!), and its |
|
74 * current contents (out_len entries) are copied to the new place. |
|
75 * This should all remain transparent to the user. swap_buffers() then |
|
76 * switches info and out_info. |
|
77 */ |
|
78 |
|
79 |
|
80 |
|
81 /* Internal API */ |
|
82 |
|
83 bool |
|
84 hb_buffer_t::enlarge (unsigned int size) |
|
85 { |
|
86 if (unlikely (in_error)) |
|
87 return false; |
|
88 |
|
89 unsigned int new_allocated = allocated; |
|
90 hb_glyph_position_t *new_pos = NULL; |
|
91 hb_glyph_info_t *new_info = NULL; |
|
92 bool separate_out = out_info != info; |
|
93 |
|
94 if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0])))) |
|
95 goto done; |
|
96 |
|
97 while (size >= new_allocated) |
|
98 new_allocated += (new_allocated >> 1) + 32; |
|
99 |
|
100 ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0])); |
|
101 if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0])))) |
|
102 goto done; |
|
103 |
|
104 new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0])); |
|
105 new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0])); |
|
106 |
|
107 done: |
|
108 if (unlikely (!new_pos || !new_info)) |
|
109 in_error = true; |
|
110 |
|
111 if (likely (new_pos)) |
|
112 pos = new_pos; |
|
113 |
|
114 if (likely (new_info)) |
|
115 info = new_info; |
|
116 |
|
117 out_info = separate_out ? (hb_glyph_info_t *) pos : info; |
|
118 if (likely (!in_error)) |
|
119 allocated = new_allocated; |
|
120 |
|
121 return likely (!in_error); |
|
122 } |
|
123 |
|
124 bool |
|
125 hb_buffer_t::make_room_for (unsigned int num_in, |
|
126 unsigned int num_out) |
|
127 { |
|
128 if (unlikely (!ensure (out_len + num_out))) return false; |
|
129 |
|
130 if (out_info == info && |
|
131 out_len + num_out > idx + num_in) |
|
132 { |
|
133 assert (have_output); |
|
134 |
|
135 out_info = (hb_glyph_info_t *) pos; |
|
136 memcpy (out_info, info, out_len * sizeof (out_info[0])); |
|
137 } |
|
138 |
|
139 return true; |
|
140 } |
|
141 |
|
142 bool |
|
143 hb_buffer_t::shift_forward (unsigned int count) |
|
144 { |
|
145 assert (have_output); |
|
146 if (unlikely (!ensure (len + count))) return false; |
|
147 |
|
148 memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); |
|
149 len += count; |
|
150 idx += count; |
|
151 |
|
152 return true; |
|
153 } |
|
154 |
|
155 hb_buffer_t::scratch_buffer_t * |
|
156 hb_buffer_t::get_scratch_buffer (unsigned int *size) |
|
157 { |
|
158 have_output = false; |
|
159 have_positions = false; |
|
160 |
|
161 out_len = 0; |
|
162 out_info = info; |
|
163 |
|
164 assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0); |
|
165 *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t); |
|
166 return (scratch_buffer_t *) (void *) pos; |
|
167 } |
|
168 |
|
169 |
|
170 |
|
171 /* HarfBuzz-Internal API */ |
|
172 |
|
173 void |
|
174 hb_buffer_t::reset (void) |
|
175 { |
|
176 if (unlikely (hb_object_is_inert (this))) |
|
177 return; |
|
178 |
|
179 hb_unicode_funcs_destroy (unicode); |
|
180 unicode = hb_unicode_funcs_get_default (); |
|
181 |
|
182 clear (); |
|
183 } |
|
184 |
|
185 void |
|
186 hb_buffer_t::clear (void) |
|
187 { |
|
188 if (unlikely (hb_object_is_inert (this))) |
|
189 return; |
|
190 |
|
191 hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; |
|
192 props = default_props; |
|
193 flags = HB_BUFFER_FLAG_DEFAULT; |
|
194 |
|
195 content_type = HB_BUFFER_CONTENT_TYPE_INVALID; |
|
196 in_error = false; |
|
197 have_output = false; |
|
198 have_positions = false; |
|
199 |
|
200 idx = 0; |
|
201 len = 0; |
|
202 out_len = 0; |
|
203 out_info = info; |
|
204 |
|
205 serial = 0; |
|
206 memset (allocated_var_bytes, 0, sizeof allocated_var_bytes); |
|
207 memset (allocated_var_owner, 0, sizeof allocated_var_owner); |
|
208 |
|
209 memset (context, 0, sizeof context); |
|
210 memset (context_len, 0, sizeof context_len); |
|
211 } |
|
212 |
|
213 void |
|
214 hb_buffer_t::add (hb_codepoint_t codepoint, |
|
215 unsigned int cluster) |
|
216 { |
|
217 hb_glyph_info_t *glyph; |
|
218 |
|
219 if (unlikely (!ensure (len + 1))) return; |
|
220 |
|
221 glyph = &info[len]; |
|
222 |
|
223 memset (glyph, 0, sizeof (*glyph)); |
|
224 glyph->codepoint = codepoint; |
|
225 glyph->mask = 1; |
|
226 glyph->cluster = cluster; |
|
227 |
|
228 len++; |
|
229 } |
|
230 |
|
231 void |
|
232 hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) |
|
233 { |
|
234 if (unlikely (!ensure (len + 1))) return; |
|
235 |
|
236 info[len] = glyph_info; |
|
237 |
|
238 len++; |
|
239 } |
|
240 |
|
241 |
|
242 void |
|
243 hb_buffer_t::remove_output (void) |
|
244 { |
|
245 if (unlikely (hb_object_is_inert (this))) |
|
246 return; |
|
247 |
|
248 have_output = false; |
|
249 have_positions = false; |
|
250 |
|
251 out_len = 0; |
|
252 out_info = info; |
|
253 } |
|
254 |
|
255 void |
|
256 hb_buffer_t::clear_output (void) |
|
257 { |
|
258 if (unlikely (hb_object_is_inert (this))) |
|
259 return; |
|
260 |
|
261 have_output = true; |
|
262 have_positions = false; |
|
263 |
|
264 out_len = 0; |
|
265 out_info = info; |
|
266 } |
|
267 |
|
268 void |
|
269 hb_buffer_t::clear_positions (void) |
|
270 { |
|
271 if (unlikely (hb_object_is_inert (this))) |
|
272 return; |
|
273 |
|
274 have_output = false; |
|
275 have_positions = true; |
|
276 |
|
277 out_len = 0; |
|
278 out_info = info; |
|
279 |
|
280 memset (pos, 0, sizeof (pos[0]) * len); |
|
281 } |
|
282 |
|
283 void |
|
284 hb_buffer_t::swap_buffers (void) |
|
285 { |
|
286 if (unlikely (in_error)) return; |
|
287 |
|
288 assert (have_output); |
|
289 have_output = false; |
|
290 |
|
291 if (out_info != info) |
|
292 { |
|
293 hb_glyph_info_t *tmp_string; |
|
294 tmp_string = info; |
|
295 info = out_info; |
|
296 out_info = tmp_string; |
|
297 pos = (hb_glyph_position_t *) out_info; |
|
298 } |
|
299 |
|
300 unsigned int tmp; |
|
301 tmp = len; |
|
302 len = out_len; |
|
303 out_len = tmp; |
|
304 |
|
305 idx = 0; |
|
306 } |
|
307 |
|
308 |
|
309 void |
|
310 hb_buffer_t::replace_glyphs (unsigned int num_in, |
|
311 unsigned int num_out, |
|
312 const uint32_t *glyph_data) |
|
313 { |
|
314 if (unlikely (!make_room_for (num_in, num_out))) return; |
|
315 |
|
316 merge_clusters (idx, idx + num_in); |
|
317 |
|
318 hb_glyph_info_t orig_info = info[idx]; |
|
319 hb_glyph_info_t *pinfo = &out_info[out_len]; |
|
320 for (unsigned int i = 0; i < num_out; i++) |
|
321 { |
|
322 *pinfo = orig_info; |
|
323 pinfo->codepoint = glyph_data[i]; |
|
324 pinfo++; |
|
325 } |
|
326 |
|
327 idx += num_in; |
|
328 out_len += num_out; |
|
329 } |
|
330 |
|
331 void |
|
332 hb_buffer_t::output_glyph (hb_codepoint_t glyph_index) |
|
333 { |
|
334 if (unlikely (!make_room_for (0, 1))) return; |
|
335 |
|
336 out_info[out_len] = info[idx]; |
|
337 out_info[out_len].codepoint = glyph_index; |
|
338 |
|
339 out_len++; |
|
340 } |
|
341 |
|
342 void |
|
343 hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info) |
|
344 { |
|
345 if (unlikely (!make_room_for (0, 1))) return; |
|
346 |
|
347 out_info[out_len] = glyph_info; |
|
348 |
|
349 out_len++; |
|
350 } |
|
351 |
|
352 void |
|
353 hb_buffer_t::copy_glyph (void) |
|
354 { |
|
355 if (unlikely (!make_room_for (0, 1))) return; |
|
356 |
|
357 out_info[out_len] = info[idx]; |
|
358 |
|
359 out_len++; |
|
360 } |
|
361 |
|
362 bool |
|
363 hb_buffer_t::move_to (unsigned int i) |
|
364 { |
|
365 if (!have_output) |
|
366 { |
|
367 assert (i <= len); |
|
368 idx = i; |
|
369 return true; |
|
370 } |
|
371 |
|
372 assert (i <= out_len + (len - idx)); |
|
373 |
|
374 if (out_len < i) |
|
375 { |
|
376 unsigned int count = i - out_len; |
|
377 if (unlikely (!make_room_for (count, count))) return false; |
|
378 |
|
379 memmove (out_info + out_len, info + idx, count * sizeof (out_info[0])); |
|
380 idx += count; |
|
381 out_len += count; |
|
382 } |
|
383 else if (out_len > i) |
|
384 { |
|
385 /* Tricky part: rewinding... */ |
|
386 unsigned int count = out_len - i; |
|
387 |
|
388 if (unlikely (idx < count && !shift_forward (count + 32))) return false; |
|
389 |
|
390 assert (idx >= count); |
|
391 |
|
392 idx -= count; |
|
393 out_len -= count; |
|
394 memmove (info + idx, out_info + out_len, count * sizeof (out_info[0])); |
|
395 } |
|
396 |
|
397 return true; |
|
398 } |
|
399 |
|
400 void |
|
401 hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index) |
|
402 { |
|
403 if (unlikely (out_info != info || out_len != idx)) { |
|
404 if (unlikely (!make_room_for (1, 1))) return; |
|
405 out_info[out_len] = info[idx]; |
|
406 } |
|
407 out_info[out_len].codepoint = glyph_index; |
|
408 |
|
409 idx++; |
|
410 out_len++; |
|
411 } |
|
412 |
|
413 |
|
414 void |
|
415 hb_buffer_t::set_masks (hb_mask_t value, |
|
416 hb_mask_t mask, |
|
417 unsigned int cluster_start, |
|
418 unsigned int cluster_end) |
|
419 { |
|
420 hb_mask_t not_mask = ~mask; |
|
421 value &= mask; |
|
422 |
|
423 if (!mask) |
|
424 return; |
|
425 |
|
426 if (cluster_start == 0 && cluster_end == (unsigned int)-1) { |
|
427 unsigned int count = len; |
|
428 for (unsigned int i = 0; i < count; i++) |
|
429 info[i].mask = (info[i].mask & not_mask) | value; |
|
430 return; |
|
431 } |
|
432 |
|
433 unsigned int count = len; |
|
434 for (unsigned int i = 0; i < count; i++) |
|
435 if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) |
|
436 info[i].mask = (info[i].mask & not_mask) | value; |
|
437 } |
|
438 |
|
439 void |
|
440 hb_buffer_t::reverse_range (unsigned int start, |
|
441 unsigned int end) |
|
442 { |
|
443 unsigned int i, j; |
|
444 |
|
445 if (start == end - 1) |
|
446 return; |
|
447 |
|
448 for (i = start, j = end - 1; i < j; i++, j--) { |
|
449 hb_glyph_info_t t; |
|
450 |
|
451 t = info[i]; |
|
452 info[i] = info[j]; |
|
453 info[j] = t; |
|
454 } |
|
455 |
|
456 if (pos) { |
|
457 for (i = start, j = end - 1; i < j; i++, j--) { |
|
458 hb_glyph_position_t t; |
|
459 |
|
460 t = pos[i]; |
|
461 pos[i] = pos[j]; |
|
462 pos[j] = t; |
|
463 } |
|
464 } |
|
465 } |
|
466 |
|
467 void |
|
468 hb_buffer_t::reverse (void) |
|
469 { |
|
470 if (unlikely (!len)) |
|
471 return; |
|
472 |
|
473 reverse_range (0, len); |
|
474 } |
|
475 |
|
476 void |
|
477 hb_buffer_t::reverse_clusters (void) |
|
478 { |
|
479 unsigned int i, start, count, last_cluster; |
|
480 |
|
481 if (unlikely (!len)) |
|
482 return; |
|
483 |
|
484 reverse (); |
|
485 |
|
486 count = len; |
|
487 start = 0; |
|
488 last_cluster = info[0].cluster; |
|
489 for (i = 1; i < count; i++) { |
|
490 if (last_cluster != info[i].cluster) { |
|
491 reverse_range (start, i); |
|
492 start = i; |
|
493 last_cluster = info[i].cluster; |
|
494 } |
|
495 } |
|
496 reverse_range (start, i); |
|
497 } |
|
498 |
|
499 void |
|
500 hb_buffer_t::merge_clusters (unsigned int start, |
|
501 unsigned int end) |
|
502 { |
|
503 if (unlikely (end - start < 2)) |
|
504 return; |
|
505 |
|
506 unsigned int cluster = info[start].cluster; |
|
507 |
|
508 for (unsigned int i = start + 1; i < end; i++) |
|
509 cluster = MIN (cluster, info[i].cluster); |
|
510 |
|
511 /* Extend end */ |
|
512 while (end < len && info[end - 1].cluster == info[end].cluster) |
|
513 end++; |
|
514 |
|
515 /* Extend start */ |
|
516 while (idx < start && info[start - 1].cluster == info[start].cluster) |
|
517 start--; |
|
518 |
|
519 /* If we hit the start of buffer, continue in out-buffer. */ |
|
520 if (idx == start) |
|
521 for (unsigned i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) |
|
522 out_info[i - 1].cluster = cluster; |
|
523 |
|
524 for (unsigned int i = start; i < end; i++) |
|
525 info[i].cluster = cluster; |
|
526 } |
|
527 void |
|
528 hb_buffer_t::merge_out_clusters (unsigned int start, |
|
529 unsigned int end) |
|
530 { |
|
531 if (unlikely (end - start < 2)) |
|
532 return; |
|
533 |
|
534 unsigned int cluster = out_info[start].cluster; |
|
535 |
|
536 for (unsigned int i = start + 1; i < end; i++) |
|
537 cluster = MIN (cluster, out_info[i].cluster); |
|
538 |
|
539 /* Extend start */ |
|
540 while (start && out_info[start - 1].cluster == out_info[start].cluster) |
|
541 start--; |
|
542 |
|
543 /* Extend end */ |
|
544 while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster) |
|
545 end++; |
|
546 |
|
547 /* If we hit the end of out-buffer, continue in buffer. */ |
|
548 if (end == out_len) |
|
549 for (unsigned i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++) |
|
550 info[i].cluster = cluster; |
|
551 |
|
552 for (unsigned int i = start; i < end; i++) |
|
553 out_info[i].cluster = cluster; |
|
554 } |
|
555 |
|
556 void |
|
557 hb_buffer_t::guess_segment_properties (void) |
|
558 { |
|
559 assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || |
|
560 (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); |
|
561 |
|
562 /* If script is set to INVALID, guess from buffer contents */ |
|
563 if (props.script == HB_SCRIPT_INVALID) { |
|
564 for (unsigned int i = 0; i < len; i++) { |
|
565 hb_script_t script = unicode->script (info[i].codepoint); |
|
566 if (likely (script != HB_SCRIPT_COMMON && |
|
567 script != HB_SCRIPT_INHERITED && |
|
568 script != HB_SCRIPT_UNKNOWN)) { |
|
569 props.script = script; |
|
570 break; |
|
571 } |
|
572 } |
|
573 } |
|
574 |
|
575 /* If direction is set to INVALID, guess from script */ |
|
576 if (props.direction == HB_DIRECTION_INVALID) { |
|
577 props.direction = hb_script_get_horizontal_direction (props.script); |
|
578 } |
|
579 |
|
580 /* If language is not set, use default language from locale */ |
|
581 if (props.language == HB_LANGUAGE_INVALID) { |
|
582 /* TODO get_default_for_script? using $LANGUAGE */ |
|
583 props.language = hb_language_get_default (); |
|
584 } |
|
585 } |
|
586 |
|
587 |
|
588 static inline void |
|
589 dump_var_allocation (const hb_buffer_t *buffer) |
|
590 { |
|
591 char buf[80]; |
|
592 for (unsigned int i = 0; i < 8; i++) |
|
593 buf[i] = '0' + buffer->allocated_var_bytes[7 - i]; |
|
594 buf[8] = '\0'; |
|
595 DEBUG_MSG (BUFFER, buffer, |
|
596 "Current var allocation: %s", |
|
597 buf); |
|
598 } |
|
599 |
|
600 void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const char *owner) |
|
601 { |
|
602 assert (byte_i < 8 && byte_i + count <= 8); |
|
603 |
|
604 if (DEBUG_ENABLED (BUFFER)) |
|
605 dump_var_allocation (this); |
|
606 DEBUG_MSG (BUFFER, this, |
|
607 "Allocating var bytes %d..%d for %s", |
|
608 byte_i, byte_i + count - 1, owner); |
|
609 |
|
610 for (unsigned int i = byte_i; i < byte_i + count; i++) { |
|
611 assert (!allocated_var_bytes[i]); |
|
612 allocated_var_bytes[i]++; |
|
613 allocated_var_owner[i] = owner; |
|
614 } |
|
615 } |
|
616 |
|
617 void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const char *owner) |
|
618 { |
|
619 if (DEBUG_ENABLED (BUFFER)) |
|
620 dump_var_allocation (this); |
|
621 |
|
622 DEBUG_MSG (BUFFER, this, |
|
623 "Deallocating var bytes %d..%d for %s", |
|
624 byte_i, byte_i + count - 1, owner); |
|
625 |
|
626 assert (byte_i < 8 && byte_i + count <= 8); |
|
627 for (unsigned int i = byte_i; i < byte_i + count; i++) { |
|
628 assert (allocated_var_bytes[i]); |
|
629 assert (0 == strcmp (allocated_var_owner[i], owner)); |
|
630 allocated_var_bytes[i]--; |
|
631 } |
|
632 } |
|
633 |
|
634 void hb_buffer_t::assert_var (unsigned int byte_i, unsigned int count, const char *owner) |
|
635 { |
|
636 if (DEBUG_ENABLED (BUFFER)) |
|
637 dump_var_allocation (this); |
|
638 |
|
639 DEBUG_MSG (BUFFER, this, |
|
640 "Asserting var bytes %d..%d for %s", |
|
641 byte_i, byte_i + count - 1, owner); |
|
642 |
|
643 assert (byte_i < 8 && byte_i + count <= 8); |
|
644 for (unsigned int i = byte_i; i < byte_i + count; i++) { |
|
645 assert (allocated_var_bytes[i]); |
|
646 assert (0 == strcmp (allocated_var_owner[i], owner)); |
|
647 } |
|
648 } |
|
649 |
|
650 void hb_buffer_t::deallocate_var_all (void) |
|
651 { |
|
652 memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes)); |
|
653 memset (allocated_var_owner, 0, sizeof (allocated_var_owner)); |
|
654 } |
|
655 |
|
656 /* Public API */ |
|
657 |
|
658 /** |
|
659 * hb_buffer_create: (Xconstructor) |
|
660 * |
|
661 * |
|
662 * |
|
663 * Return value: (transfer full) |
|
664 * |
|
665 * Since: 1.0 |
|
666 **/ |
|
667 hb_buffer_t * |
|
668 hb_buffer_create (void) |
|
669 { |
|
670 hb_buffer_t *buffer; |
|
671 |
|
672 if (!(buffer = hb_object_create<hb_buffer_t> ())) |
|
673 return hb_buffer_get_empty (); |
|
674 |
|
675 buffer->reset (); |
|
676 |
|
677 return buffer; |
|
678 } |
|
679 |
|
680 /** |
|
681 * hb_buffer_get_empty: |
|
682 * |
|
683 * |
|
684 * |
|
685 * Return value: (transfer full): |
|
686 * |
|
687 * Since: 1.0 |
|
688 **/ |
|
689 hb_buffer_t * |
|
690 hb_buffer_get_empty (void) |
|
691 { |
|
692 static const hb_buffer_t _hb_buffer_nil = { |
|
693 HB_OBJECT_HEADER_STATIC, |
|
694 |
|
695 const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil), |
|
696 HB_SEGMENT_PROPERTIES_DEFAULT, |
|
697 HB_BUFFER_FLAG_DEFAULT, |
|
698 |
|
699 HB_BUFFER_CONTENT_TYPE_INVALID, |
|
700 true, /* in_error */ |
|
701 true, /* have_output */ |
|
702 true /* have_positions */ |
|
703 |
|
704 /* Zero is good enough for everything else. */ |
|
705 }; |
|
706 |
|
707 return const_cast<hb_buffer_t *> (&_hb_buffer_nil); |
|
708 } |
|
709 |
|
710 /** |
|
711 * hb_buffer_reference: (skip) |
|
712 * @buffer: a buffer. |
|
713 * |
|
714 * |
|
715 * |
|
716 * Return value: (transfer full): |
|
717 * |
|
718 * Since: 1.0 |
|
719 **/ |
|
720 hb_buffer_t * |
|
721 hb_buffer_reference (hb_buffer_t *buffer) |
|
722 { |
|
723 return hb_object_reference (buffer); |
|
724 } |
|
725 |
|
726 /** |
|
727 * hb_buffer_destroy: (skip) |
|
728 * @buffer: a buffer. |
|
729 * |
|
730 * |
|
731 * |
|
732 * Since: 1.0 |
|
733 **/ |
|
734 void |
|
735 hb_buffer_destroy (hb_buffer_t *buffer) |
|
736 { |
|
737 if (!hb_object_destroy (buffer)) return; |
|
738 |
|
739 hb_unicode_funcs_destroy (buffer->unicode); |
|
740 |
|
741 free (buffer->info); |
|
742 free (buffer->pos); |
|
743 |
|
744 free (buffer); |
|
745 } |
|
746 |
|
747 /** |
|
748 * hb_buffer_set_user_data: (skip) |
|
749 * @buffer: a buffer. |
|
750 * @key: |
|
751 * @data: |
|
752 * @destroy: |
|
753 * @replace: |
|
754 * |
|
755 * |
|
756 * |
|
757 * Return value: |
|
758 * |
|
759 * Since: 1.0 |
|
760 **/ |
|
761 hb_bool_t |
|
762 hb_buffer_set_user_data (hb_buffer_t *buffer, |
|
763 hb_user_data_key_t *key, |
|
764 void * data, |
|
765 hb_destroy_func_t destroy, |
|
766 hb_bool_t replace) |
|
767 { |
|
768 return hb_object_set_user_data (buffer, key, data, destroy, replace); |
|
769 } |
|
770 |
|
771 /** |
|
772 * hb_buffer_get_user_data: (skip) |
|
773 * @buffer: a buffer. |
|
774 * @key: |
|
775 * |
|
776 * |
|
777 * |
|
778 * Return value: |
|
779 * |
|
780 * Since: 1.0 |
|
781 **/ |
|
782 void * |
|
783 hb_buffer_get_user_data (hb_buffer_t *buffer, |
|
784 hb_user_data_key_t *key) |
|
785 { |
|
786 return hb_object_get_user_data (buffer, key); |
|
787 } |
|
788 |
|
789 |
|
790 /** |
|
791 * hb_buffer_set_content_type: |
|
792 * @buffer: a buffer. |
|
793 * @content_type: |
|
794 * |
|
795 * |
|
796 * |
|
797 * Since: 1.0 |
|
798 **/ |
|
799 void |
|
800 hb_buffer_set_content_type (hb_buffer_t *buffer, |
|
801 hb_buffer_content_type_t content_type) |
|
802 { |
|
803 buffer->content_type = content_type; |
|
804 } |
|
805 |
|
806 /** |
|
807 * hb_buffer_get_content_type: |
|
808 * @buffer: a buffer. |
|
809 * |
|
810 * |
|
811 * |
|
812 * Return value: |
|
813 * |
|
814 * Since: 1.0 |
|
815 **/ |
|
816 hb_buffer_content_type_t |
|
817 hb_buffer_get_content_type (hb_buffer_t *buffer) |
|
818 { |
|
819 return buffer->content_type; |
|
820 } |
|
821 |
|
822 |
|
823 /** |
|
824 * hb_buffer_set_unicode_funcs: |
|
825 * @buffer: a buffer. |
|
826 * @unicode_funcs: |
|
827 * |
|
828 * |
|
829 * |
|
830 * Since: 1.0 |
|
831 **/ |
|
832 void |
|
833 hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, |
|
834 hb_unicode_funcs_t *unicode_funcs) |
|
835 { |
|
836 if (unlikely (hb_object_is_inert (buffer))) |
|
837 return; |
|
838 |
|
839 if (!unicode_funcs) |
|
840 unicode_funcs = hb_unicode_funcs_get_default (); |
|
841 |
|
842 |
|
843 hb_unicode_funcs_reference (unicode_funcs); |
|
844 hb_unicode_funcs_destroy (buffer->unicode); |
|
845 buffer->unicode = unicode_funcs; |
|
846 } |
|
847 |
|
848 /** |
|
849 * hb_buffer_get_unicode_funcs: |
|
850 * @buffer: a buffer. |
|
851 * |
|
852 * |
|
853 * |
|
854 * Return value: |
|
855 * |
|
856 * Since: 1.0 |
|
857 **/ |
|
858 hb_unicode_funcs_t * |
|
859 hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) |
|
860 { |
|
861 return buffer->unicode; |
|
862 } |
|
863 |
|
864 /** |
|
865 * hb_buffer_set_direction: |
|
866 * @buffer: a buffer. |
|
867 * @direction: |
|
868 * |
|
869 * |
|
870 * |
|
871 * Since: 1.0 |
|
872 **/ |
|
873 void |
|
874 hb_buffer_set_direction (hb_buffer_t *buffer, |
|
875 hb_direction_t direction) |
|
876 |
|
877 { |
|
878 if (unlikely (hb_object_is_inert (buffer))) |
|
879 return; |
|
880 |
|
881 buffer->props.direction = direction; |
|
882 } |
|
883 |
|
884 /** |
|
885 * hb_buffer_get_direction: |
|
886 * @buffer: a buffer. |
|
887 * |
|
888 * |
|
889 * |
|
890 * Return value: |
|
891 * |
|
892 * Since: 1.0 |
|
893 **/ |
|
894 hb_direction_t |
|
895 hb_buffer_get_direction (hb_buffer_t *buffer) |
|
896 { |
|
897 return buffer->props.direction; |
|
898 } |
|
899 |
|
900 /** |
|
901 * hb_buffer_set_script: |
|
902 * @buffer: a buffer. |
|
903 * @script: |
|
904 * |
|
905 * |
|
906 * |
|
907 * Since: 1.0 |
|
908 **/ |
|
909 void |
|
910 hb_buffer_set_script (hb_buffer_t *buffer, |
|
911 hb_script_t script) |
|
912 { |
|
913 if (unlikely (hb_object_is_inert (buffer))) |
|
914 return; |
|
915 |
|
916 buffer->props.script = script; |
|
917 } |
|
918 |
|
919 /** |
|
920 * hb_buffer_get_script: |
|
921 * @buffer: a buffer. |
|
922 * |
|
923 * |
|
924 * |
|
925 * Return value: |
|
926 * |
|
927 * Since: 1.0 |
|
928 **/ |
|
929 hb_script_t |
|
930 hb_buffer_get_script (hb_buffer_t *buffer) |
|
931 { |
|
932 return buffer->props.script; |
|
933 } |
|
934 |
|
935 /** |
|
936 * hb_buffer_set_language: |
|
937 * @buffer: a buffer. |
|
938 * @language: |
|
939 * |
|
940 * |
|
941 * |
|
942 * Since: 1.0 |
|
943 **/ |
|
944 void |
|
945 hb_buffer_set_language (hb_buffer_t *buffer, |
|
946 hb_language_t language) |
|
947 { |
|
948 if (unlikely (hb_object_is_inert (buffer))) |
|
949 return; |
|
950 |
|
951 buffer->props.language = language; |
|
952 } |
|
953 |
|
954 /** |
|
955 * hb_buffer_get_language: |
|
956 * @buffer: a buffer. |
|
957 * |
|
958 * |
|
959 * |
|
960 * Return value: |
|
961 * |
|
962 * Since: 1.0 |
|
963 **/ |
|
964 hb_language_t |
|
965 hb_buffer_get_language (hb_buffer_t *buffer) |
|
966 { |
|
967 return buffer->props.language; |
|
968 } |
|
969 |
|
970 /** |
|
971 * hb_buffer_set_segment_properties: |
|
972 * @buffer: a buffer. |
|
973 * @props: |
|
974 * |
|
975 * |
|
976 * |
|
977 * Since: 1.0 |
|
978 **/ |
|
979 void |
|
980 hb_buffer_set_segment_properties (hb_buffer_t *buffer, |
|
981 const hb_segment_properties_t *props) |
|
982 { |
|
983 if (unlikely (hb_object_is_inert (buffer))) |
|
984 return; |
|
985 |
|
986 buffer->props = *props; |
|
987 } |
|
988 |
|
989 /** |
|
990 * hb_buffer_get_segment_properties: |
|
991 * @buffer: a buffer. |
|
992 * @props: |
|
993 * |
|
994 * |
|
995 * |
|
996 * Since: 1.0 |
|
997 **/ |
|
998 void |
|
999 hb_buffer_get_segment_properties (hb_buffer_t *buffer, |
|
1000 hb_segment_properties_t *props) |
|
1001 { |
|
1002 *props = buffer->props; |
|
1003 } |
|
1004 |
|
1005 |
|
1006 /** |
|
1007 * hb_buffer_set_flags: |
|
1008 * @buffer: a buffer. |
|
1009 * @flags: |
|
1010 * |
|
1011 * |
|
1012 * |
|
1013 * Since: 1.0 |
|
1014 **/ |
|
1015 void |
|
1016 hb_buffer_set_flags (hb_buffer_t *buffer, |
|
1017 hb_buffer_flags_t flags) |
|
1018 { |
|
1019 if (unlikely (hb_object_is_inert (buffer))) |
|
1020 return; |
|
1021 |
|
1022 buffer->flags = flags; |
|
1023 } |
|
1024 |
|
1025 /** |
|
1026 * hb_buffer_get_flags: |
|
1027 * @buffer: a buffer. |
|
1028 * |
|
1029 * |
|
1030 * |
|
1031 * Return value: |
|
1032 * |
|
1033 * Since: 1.0 |
|
1034 **/ |
|
1035 hb_buffer_flags_t |
|
1036 hb_buffer_get_flags (hb_buffer_t *buffer) |
|
1037 { |
|
1038 return buffer->flags; |
|
1039 } |
|
1040 |
|
1041 |
|
1042 /** |
|
1043 * hb_buffer_reset: |
|
1044 * @buffer: a buffer. |
|
1045 * |
|
1046 * |
|
1047 * |
|
1048 * Since: 1.0 |
|
1049 **/ |
|
1050 void |
|
1051 hb_buffer_reset (hb_buffer_t *buffer) |
|
1052 { |
|
1053 buffer->reset (); |
|
1054 } |
|
1055 |
|
1056 /** |
|
1057 * hb_buffer_clear_contents: |
|
1058 * @buffer: a buffer. |
|
1059 * |
|
1060 * |
|
1061 * |
|
1062 * Since: 1.0 |
|
1063 **/ |
|
1064 void |
|
1065 hb_buffer_clear_contents (hb_buffer_t *buffer) |
|
1066 { |
|
1067 buffer->clear (); |
|
1068 } |
|
1069 |
|
1070 /** |
|
1071 * hb_buffer_pre_allocate: |
|
1072 * @buffer: a buffer. |
|
1073 * @size: |
|
1074 * |
|
1075 * |
|
1076 * |
|
1077 * Return value: |
|
1078 * |
|
1079 * Since: 1.0 |
|
1080 **/ |
|
1081 hb_bool_t |
|
1082 hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) |
|
1083 { |
|
1084 return buffer->ensure (size); |
|
1085 } |
|
1086 |
|
1087 /** |
|
1088 * hb_buffer_allocation_successful: |
|
1089 * @buffer: a buffer. |
|
1090 * |
|
1091 * |
|
1092 * |
|
1093 * Return value: |
|
1094 * |
|
1095 * Since: 1.0 |
|
1096 **/ |
|
1097 hb_bool_t |
|
1098 hb_buffer_allocation_successful (hb_buffer_t *buffer) |
|
1099 { |
|
1100 return !buffer->in_error; |
|
1101 } |
|
1102 |
|
1103 /** |
|
1104 * hb_buffer_add: |
|
1105 * @buffer: a buffer. |
|
1106 * @codepoint: |
|
1107 * @cluster: |
|
1108 * |
|
1109 * |
|
1110 * |
|
1111 * Since: 1.0 |
|
1112 **/ |
|
1113 void |
|
1114 hb_buffer_add (hb_buffer_t *buffer, |
|
1115 hb_codepoint_t codepoint, |
|
1116 unsigned int cluster) |
|
1117 { |
|
1118 buffer->add (codepoint, cluster); |
|
1119 buffer->clear_context (1); |
|
1120 } |
|
1121 |
|
1122 /** |
|
1123 * hb_buffer_set_length: |
|
1124 * @buffer: a buffer. |
|
1125 * @length: |
|
1126 * |
|
1127 * |
|
1128 * |
|
1129 * Return value: |
|
1130 * |
|
1131 * Since: 1.0 |
|
1132 **/ |
|
1133 hb_bool_t |
|
1134 hb_buffer_set_length (hb_buffer_t *buffer, |
|
1135 unsigned int length) |
|
1136 { |
|
1137 if (unlikely (hb_object_is_inert (buffer))) |
|
1138 return length == 0; |
|
1139 |
|
1140 if (!buffer->ensure (length)) |
|
1141 return false; |
|
1142 |
|
1143 /* Wipe the new space */ |
|
1144 if (length > buffer->len) { |
|
1145 memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); |
|
1146 if (buffer->have_positions) |
|
1147 memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); |
|
1148 } |
|
1149 |
|
1150 buffer->len = length; |
|
1151 |
|
1152 if (!length) |
|
1153 { |
|
1154 buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID; |
|
1155 buffer->clear_context (0); |
|
1156 } |
|
1157 buffer->clear_context (1); |
|
1158 |
|
1159 return true; |
|
1160 } |
|
1161 |
|
1162 /** |
|
1163 * hb_buffer_get_length: |
|
1164 * @buffer: a buffer. |
|
1165 * |
|
1166 * Returns the number of items in the buffer. |
|
1167 * |
|
1168 * Return value: buffer length. |
|
1169 * |
|
1170 * Since: 1.0 |
|
1171 **/ |
|
1172 unsigned int |
|
1173 hb_buffer_get_length (hb_buffer_t *buffer) |
|
1174 { |
|
1175 return buffer->len; |
|
1176 } |
|
1177 |
|
1178 /** |
|
1179 * hb_buffer_get_glyph_infos: |
|
1180 * @buffer: a buffer. |
|
1181 * @length: (out): output array length. |
|
1182 * |
|
1183 * Returns buffer glyph information array. Returned pointer |
|
1184 * is valid as long as buffer contents are not modified. |
|
1185 * |
|
1186 * Return value: (transfer none) (array length=length): buffer glyph information array. |
|
1187 * |
|
1188 * Since: 1.0 |
|
1189 **/ |
|
1190 hb_glyph_info_t * |
|
1191 hb_buffer_get_glyph_infos (hb_buffer_t *buffer, |
|
1192 unsigned int *length) |
|
1193 { |
|
1194 if (length) |
|
1195 *length = buffer->len; |
|
1196 |
|
1197 return (hb_glyph_info_t *) buffer->info; |
|
1198 } |
|
1199 |
|
1200 /** |
|
1201 * hb_buffer_get_glyph_positions: |
|
1202 * @buffer: a buffer. |
|
1203 * @length: (out): output length. |
|
1204 * |
|
1205 * Returns buffer glyph position array. Returned pointer |
|
1206 * is valid as long as buffer contents are not modified. |
|
1207 * |
|
1208 * Return value: (transfer none) (array length=length): buffer glyph position array. |
|
1209 * |
|
1210 * Since: 1.0 |
|
1211 **/ |
|
1212 hb_glyph_position_t * |
|
1213 hb_buffer_get_glyph_positions (hb_buffer_t *buffer, |
|
1214 unsigned int *length) |
|
1215 { |
|
1216 if (!buffer->have_positions) |
|
1217 buffer->clear_positions (); |
|
1218 |
|
1219 if (length) |
|
1220 *length = buffer->len; |
|
1221 |
|
1222 return (hb_glyph_position_t *) buffer->pos; |
|
1223 } |
|
1224 |
|
1225 /** |
|
1226 * hb_buffer_reverse: |
|
1227 * @buffer: a buffer. |
|
1228 * |
|
1229 * Reverses buffer contents. |
|
1230 * |
|
1231 * Since: 1.0 |
|
1232 **/ |
|
1233 void |
|
1234 hb_buffer_reverse (hb_buffer_t *buffer) |
|
1235 { |
|
1236 buffer->reverse (); |
|
1237 } |
|
1238 |
|
1239 /** |
|
1240 * hb_buffer_reverse_clusters: |
|
1241 * @buffer: a buffer. |
|
1242 * |
|
1243 * Reverses buffer clusters. That is, the buffer contents are |
|
1244 * reversed, then each cluster (consecutive items having the |
|
1245 * same cluster number) are reversed again. |
|
1246 * |
|
1247 * Since: 1.0 |
|
1248 **/ |
|
1249 void |
|
1250 hb_buffer_reverse_clusters (hb_buffer_t *buffer) |
|
1251 { |
|
1252 buffer->reverse_clusters (); |
|
1253 } |
|
1254 |
|
1255 /** |
|
1256 * hb_buffer_guess_segment_properties: |
|
1257 * @buffer: a buffer. |
|
1258 * |
|
1259 * Sets unset buffer segment properties based on buffer Unicode |
|
1260 * contents. If buffer is not empty, it must have content type |
|
1261 * %HB_BUFFER_CONTENT_TYPE_UNICODE. |
|
1262 * |
|
1263 * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it |
|
1264 * will be set to the Unicode script of the first character in |
|
1265 * the buffer that has a script other than %HB_SCRIPT_COMMON, |
|
1266 * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN. |
|
1267 * |
|
1268 * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID), |
|
1269 * it will be set to the natural horizontal direction of the |
|
1270 * buffer script as returned by hb_script_get_horizontal_direction(). |
|
1271 * |
|
1272 * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID), |
|
1273 * it will be set to the process's default language as returned by |
|
1274 * hb_language_get_default(). This may change in the future by |
|
1275 * taking buffer script into consideration when choosing a language. |
|
1276 * |
|
1277 * Since: 1.0 |
|
1278 **/ |
|
1279 void |
|
1280 hb_buffer_guess_segment_properties (hb_buffer_t *buffer) |
|
1281 { |
|
1282 buffer->guess_segment_properties (); |
|
1283 } |
|
1284 |
|
1285 template <typename T> |
|
1286 static inline void |
|
1287 hb_buffer_add_utf (hb_buffer_t *buffer, |
|
1288 const T *text, |
|
1289 int text_length, |
|
1290 unsigned int item_offset, |
|
1291 int item_length) |
|
1292 { |
|
1293 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || |
|
1294 (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); |
|
1295 |
|
1296 if (unlikely (hb_object_is_inert (buffer))) |
|
1297 return; |
|
1298 |
|
1299 if (text_length == -1) |
|
1300 text_length = hb_utf_strlen (text); |
|
1301 |
|
1302 if (item_length == -1) |
|
1303 item_length = text_length - item_offset; |
|
1304 |
|
1305 buffer->ensure (buffer->len + item_length * sizeof (T) / 4); |
|
1306 |
|
1307 /* If buffer is empty and pre-context provided, install it. |
|
1308 * This check is written this way, to make sure people can |
|
1309 * provide pre-context in one add_utf() call, then provide |
|
1310 * text in a follow-up call. See: |
|
1311 * |
|
1312 * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 |
|
1313 */ |
|
1314 if (!buffer->len && item_offset > 0) |
|
1315 { |
|
1316 /* Add pre-context */ |
|
1317 buffer->clear_context (0); |
|
1318 const T *prev = text + item_offset; |
|
1319 const T *start = text; |
|
1320 while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH) |
|
1321 { |
|
1322 hb_codepoint_t u; |
|
1323 prev = hb_utf_prev (prev, start, &u); |
|
1324 buffer->context[0][buffer->context_len[0]++] = u; |
|
1325 } |
|
1326 } |
|
1327 |
|
1328 const T *next = text + item_offset; |
|
1329 const T *end = next + item_length; |
|
1330 while (next < end) |
|
1331 { |
|
1332 hb_codepoint_t u; |
|
1333 const T *old_next = next; |
|
1334 next = hb_utf_next (next, end, &u); |
|
1335 buffer->add (u, old_next - (const T *) text); |
|
1336 } |
|
1337 |
|
1338 /* Add post-context */ |
|
1339 buffer->clear_context (1); |
|
1340 end = text + text_length; |
|
1341 while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH) |
|
1342 { |
|
1343 hb_codepoint_t u; |
|
1344 next = hb_utf_next (next, end, &u); |
|
1345 buffer->context[1][buffer->context_len[1]++] = u; |
|
1346 } |
|
1347 |
|
1348 buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; |
|
1349 } |
|
1350 |
|
1351 /** |
|
1352 * hb_buffer_add_utf8: |
|
1353 * @buffer: a buffer. |
|
1354 * @text: (array length=text_length): |
|
1355 * @text_length: |
|
1356 * @item_offset: |
|
1357 * @item_length: |
|
1358 * |
|
1359 * |
|
1360 * |
|
1361 * Since: 1.0 |
|
1362 **/ |
|
1363 void |
|
1364 hb_buffer_add_utf8 (hb_buffer_t *buffer, |
|
1365 const char *text, |
|
1366 int text_length, |
|
1367 unsigned int item_offset, |
|
1368 int item_length) |
|
1369 { |
|
1370 hb_buffer_add_utf (buffer, (const uint8_t *) text, text_length, item_offset, item_length); |
|
1371 } |
|
1372 |
|
1373 /** |
|
1374 * hb_buffer_add_utf16: |
|
1375 * @buffer: a buffer. |
|
1376 * @text: (array length=text_length): |
|
1377 * @text_length: |
|
1378 * @item_offset: |
|
1379 * @item_length: |
|
1380 * |
|
1381 * |
|
1382 * |
|
1383 * Since: 1.0 |
|
1384 **/ |
|
1385 void |
|
1386 hb_buffer_add_utf16 (hb_buffer_t *buffer, |
|
1387 const uint16_t *text, |
|
1388 int text_length, |
|
1389 unsigned int item_offset, |
|
1390 int item_length) |
|
1391 { |
|
1392 hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); |
|
1393 } |
|
1394 |
|
1395 /** |
|
1396 * hb_buffer_add_utf32: |
|
1397 * @buffer: a buffer. |
|
1398 * @text: (array length=text_length): |
|
1399 * @text_length: |
|
1400 * @item_offset: |
|
1401 * @item_length: |
|
1402 * |
|
1403 * |
|
1404 * |
|
1405 * Since: 1.0 |
|
1406 **/ |
|
1407 void |
|
1408 hb_buffer_add_utf32 (hb_buffer_t *buffer, |
|
1409 const uint32_t *text, |
|
1410 int text_length, |
|
1411 unsigned int item_offset, |
|
1412 int item_length) |
|
1413 { |
|
1414 hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); |
|
1415 } |
|
1416 |
|
1417 |
|
1418 static int |
|
1419 compare_info_codepoint (const hb_glyph_info_t *pa, |
|
1420 const hb_glyph_info_t *pb) |
|
1421 { |
|
1422 return (int) pb->codepoint - (int) pa->codepoint; |
|
1423 } |
|
1424 |
|
1425 static inline void |
|
1426 normalize_glyphs_cluster (hb_buffer_t *buffer, |
|
1427 unsigned int start, |
|
1428 unsigned int end, |
|
1429 bool backward) |
|
1430 { |
|
1431 hb_glyph_position_t *pos = buffer->pos; |
|
1432 |
|
1433 /* Total cluster advance */ |
|
1434 hb_position_t total_x_advance = 0, total_y_advance = 0; |
|
1435 for (unsigned int i = start; i < end; i++) |
|
1436 { |
|
1437 total_x_advance += pos[i].x_advance; |
|
1438 total_y_advance += pos[i].y_advance; |
|
1439 } |
|
1440 |
|
1441 hb_position_t x_advance = 0, y_advance = 0; |
|
1442 for (unsigned int i = start; i < end; i++) |
|
1443 { |
|
1444 pos[i].x_offset += x_advance; |
|
1445 pos[i].y_offset += y_advance; |
|
1446 |
|
1447 x_advance += pos[i].x_advance; |
|
1448 y_advance += pos[i].y_advance; |
|
1449 |
|
1450 pos[i].x_advance = 0; |
|
1451 pos[i].y_advance = 0; |
|
1452 } |
|
1453 |
|
1454 if (backward) |
|
1455 { |
|
1456 /* Transfer all cluster advance to the last glyph. */ |
|
1457 pos[end - 1].x_advance = total_x_advance; |
|
1458 pos[end - 1].y_advance = total_y_advance; |
|
1459 |
|
1460 hb_bubble_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); |
|
1461 } else { |
|
1462 /* Transfer all cluster advance to the first glyph. */ |
|
1463 pos[start].x_advance += total_x_advance; |
|
1464 pos[start].y_advance += total_y_advance; |
|
1465 for (unsigned int i = start + 1; i < end; i++) { |
|
1466 pos[i].x_offset -= total_x_advance; |
|
1467 pos[i].y_offset -= total_y_advance; |
|
1468 } |
|
1469 hb_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); |
|
1470 } |
|
1471 } |
|
1472 |
|
1473 /** |
|
1474 * hb_buffer_normalize_glyphs: |
|
1475 * @buffer: a buffer. |
|
1476 * |
|
1477 * |
|
1478 * |
|
1479 * Since: 1.0 |
|
1480 **/ |
|
1481 void |
|
1482 hb_buffer_normalize_glyphs (hb_buffer_t *buffer) |
|
1483 { |
|
1484 assert (buffer->have_positions); |
|
1485 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); |
|
1486 |
|
1487 bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); |
|
1488 |
|
1489 unsigned int count = buffer->len; |
|
1490 if (unlikely (!count)) return; |
|
1491 hb_glyph_info_t *info = buffer->info; |
|
1492 |
|
1493 unsigned int start = 0; |
|
1494 unsigned int end; |
|
1495 for (end = start + 1; end < count; end++) |
|
1496 if (info[start].cluster != info[end].cluster) { |
|
1497 normalize_glyphs_cluster (buffer, start, end, backward); |
|
1498 start = end; |
|
1499 } |
|
1500 normalize_glyphs_cluster (buffer, start, end, backward); |
|
1501 } |