2 * pango-ot-buffer.c: Buffer of glyphs for shaping/positioning
4 * Copyright (C) 2004 Red Hat Software
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
24 #include "pango-ot-private.h"
25 #include "pangofc-private.h"
26 #include "pango-impl-utils.h"
28 /* cache a single hb_buffer_t */
29 static hb_buffer_t *cached_buffer = NULL;
30 G_LOCK_DEFINE_STATIC (cached_buffer);
33 acquire_buffer (gboolean *free_buffer)
37 if (G_LIKELY (G_TRYLOCK (cached_buffer)))
39 if (G_UNLIKELY (!cached_buffer))
40 cached_buffer = hb_buffer_create (64);
42 buffer = cached_buffer;
47 buffer = hb_buffer_create (32);
55 release_buffer (hb_buffer_t *buffer, gboolean free_buffer)
57 if (G_LIKELY (!free_buffer) && hb_buffer_get_reference_count (buffer) == 1)
59 hb_buffer_clear (buffer);
60 G_UNLOCK (cached_buffer);
63 hb_buffer_destroy (buffer);
68 * @font: a #PangoFcFont
70 * Creates a new #PangoOTBuffer for the given OpenType font.
72 * Return value: the newly allocated #PangoOTBuffer, which should
73 * be freed with pango_ot_buffer_destroy().
78 pango_ot_buffer_new (PangoFcFont *font)
80 PangoOTBuffer *buffer = g_slice_new (PangoOTBuffer);
82 buffer->buffer = acquire_buffer (&buffer->should_free_hb_buffer);
83 buffer->font = g_object_ref (font);
84 buffer->applied_gpos = FALSE;
86 buffer->zero_width_marks = FALSE;
92 * pango_ot_buffer_destroy
93 * @buffer: a #PangoOTBuffer
95 * Destroys a #PangoOTBuffer and free all associated memory.
100 pango_ot_buffer_destroy (PangoOTBuffer *buffer)
102 release_buffer (buffer->buffer, buffer->should_free_hb_buffer);
103 g_object_unref (buffer->font);
104 g_slice_free (PangoOTBuffer, buffer);
108 * pango_ot_buffer_clear
109 * @buffer: a #PangoOTBuffer
111 * Empties a #PangoOTBuffer, make it ready to add glyphs to.
116 pango_ot_buffer_clear (PangoOTBuffer *buffer)
118 hb_buffer_clear (buffer->buffer);
119 buffer->applied_gpos = FALSE;
123 * pango_ot_buffer_add_glyph
124 * @buffer: a #PangoOTBuffer
125 * @glyph: the glyph index to add, like a #PangoGlyph
126 * @properties: the glyph properties
127 * @cluster: the cluster that this glyph belongs to
129 * Appends a glyph to a #PangoOTBuffer, with @properties identifying which
130 * features should be applied on this glyph. See pango_ruleset_add_feature().
135 pango_ot_buffer_add_glyph (PangoOTBuffer *buffer,
140 hb_buffer_add_glyph (buffer->buffer,
141 glyph, properties, cluster);
145 * pango_ot_buffer_set_rtl
146 * @buffer: a #PangoOTBuffer
147 * @rtl: %TRUE for right-to-left text
149 * Sets whether glyphs will be rendered right-to-left. This setting
150 * is needed for proper horizontal positioning of right-to-left scripts.
155 pango_ot_buffer_set_rtl (PangoOTBuffer *buffer,
158 buffer->rtl = rtl != FALSE;
159 hb_buffer_set_direction (buffer->buffer,
160 buffer->rtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
164 * pango_ot_buffer_set_zero_width_marks
165 * @buffer: a #PangoOTBuffer
166 * @zero_width_marks: %TRUE if characters with a mark class should
167 * be forced to zero width.
169 * Sets whether characters with a mark class should be forced to zero width.
170 * This setting is needed for proper positioning of Arabic accents,
171 * but will produce incorrect results with standard OpenType Indic
177 pango_ot_buffer_set_zero_width_marks (PangoOTBuffer *buffer,
178 gboolean zero_width_marks)
180 buffer->zero_width_marks = zero_width_marks != FALSE;
184 * pango_ot_buffer_get_glyphs
185 * @buffer: a #PangoOTBuffer
186 * @glyphs: location to store the array of glyphs, or %NULL
187 * @n_glyphs: location to store the number of glyphs, or %NULL
189 * Gets the glyph array contained in a #PangoOTBuffer. The glyphs are
190 * owned by the buffer and should not be freed, and are only valid as long
191 * as buffer is not modified.
196 pango_ot_buffer_get_glyphs (const PangoOTBuffer *buffer,
197 PangoOTGlyph **glyphs,
201 *glyphs = (PangoOTGlyph *) hb_buffer_get_glyph_infos (buffer->buffer);
204 *n_glyphs = hb_buffer_get_len (buffer->buffer);
208 apply_gpos_ltr (PangoGlyphString *glyphs,
209 hb_glyph_position_t *positions,
217 for (i = 0; i < glyphs->num_glyphs; i++)
219 FT_Pos x_pos = positions[i].x_pos;
220 FT_Pos y_pos = positions[i].y_pos;
226 adjustment = PANGO_UNITS_26_6(positions[i].x_advance);
229 adjustment = PANGO_UNITS_ROUND (adjustment);
230 if (G_UNLIKELY (scale))
231 adjustment *= xscale;
233 if (positions[i].new_advance)
234 glyphs->glyphs[i].geometry.width = adjustment;
236 glyphs->glyphs[i].geometry.width += adjustment;
239 while (positions[back].back != 0)
241 back -= positions[back].back;
242 x_pos += positions[back].x_pos;
243 y_pos += positions[back].y_pos;
246 for (j = back; j < i; j++)
247 glyphs->glyphs[i].geometry.x_offset -= glyphs->glyphs[j].geometry.width;
249 if (G_UNLIKELY (scale))
251 glyphs->glyphs[i].geometry.x_offset += xscale * PANGO_UNITS_26_6(x_pos);
252 glyphs->glyphs[i].geometry.y_offset -= yscale * PANGO_UNITS_26_6(y_pos);
256 glyphs->glyphs[i].geometry.x_offset += PANGO_UNITS_26_6(x_pos);
257 glyphs->glyphs[i].geometry.y_offset -= PANGO_UNITS_26_6(y_pos);
263 apply_gpos_rtl (PangoGlyphString *glyphs,
264 hb_glyph_position_t *positions,
272 for (i = 0; i < glyphs->num_glyphs; i++)
274 int i_rev = glyphs->num_glyphs - i - 1;
275 int back_rev = i_rev;
277 FT_Pos x_pos = positions[i_rev].x_pos;
278 FT_Pos y_pos = positions[i_rev].y_pos;
283 adjustment = PANGO_UNITS_26_6(positions[i_rev].x_advance);
286 adjustment = PANGO_UNITS_ROUND (adjustment);
287 if (G_UNLIKELY (scale))
288 adjustment *= xscale;
290 if (positions[i_rev].new_advance)
291 glyphs->glyphs[i].geometry.width = adjustment;
293 glyphs->glyphs[i].geometry.width += adjustment;
296 while (positions[back_rev].back != 0)
298 back_rev -= positions[back_rev].back;
299 x_pos += positions[back_rev].x_pos;
300 y_pos += positions[back_rev].y_pos;
303 back = glyphs->num_glyphs - back_rev - 1;
305 for (j = i; j < back; j++)
306 glyphs->glyphs[i].geometry.x_offset += glyphs->glyphs[j].geometry.width;
308 if (G_UNLIKELY (scale))
310 glyphs->glyphs[i].geometry.x_offset += xscale * PANGO_UNITS_26_6(x_pos);
311 glyphs->glyphs[i].geometry.y_offset -= yscale * PANGO_UNITS_26_6(y_pos);
315 glyphs->glyphs[i].geometry.x_offset += PANGO_UNITS_26_6(x_pos);
316 glyphs->glyphs[i].geometry.y_offset -= PANGO_UNITS_26_6(y_pos);
322 * pango_ot_buffer_output
323 * @buffer: a #PangoOTBuffer
324 * @glyphs: a #PangoGlyphString
326 * Exports the glyphs in a #PangoOTBuffer into a #PangoGlyphString. This is
327 * typically used after the OpenType layout processing is over, to convert the
328 * resulting glyphs into a generic Pango glyph string.
333 pango_ot_buffer_output (const PangoOTBuffer *buffer,
334 PangoGlyphString *glyphs)
342 PangoOTGlyph *otglyphs;
343 hb_glyph_position_t *positions;
345 face = pango_fc_font_lock_face (buffer->font);
348 pango_ot_buffer_get_glyphs (buffer, &otglyphs, (int *) &len);
350 /* Copy glyphs into output glyph string */
351 pango_glyph_string_set_size (glyphs, len);
354 for (i = 0; i < len; i++)
356 PangoOTGlyph *otglyph = &otglyphs[i];
358 glyphs->glyphs[i].glyph = otglyph->glyph;
360 glyphs->log_clusters[i] = otglyph->cluster;
361 if (glyphs->log_clusters[i] != last_cluster)
362 glyphs->glyphs[i].attr.is_cluster_start = 1;
364 glyphs->glyphs[i].attr.is_cluster_start = 0;
366 last_cluster = glyphs->log_clusters[i];
369 hb_face = _pango_ot_info_get_hb_face (pango_ot_info_get (face));
371 /* Apply default positioning */
372 for (i = 0; i < (unsigned int)glyphs->num_glyphs; i++)
374 if (glyphs->glyphs[i].glyph)
376 PangoRectangle logical_rect;
378 if (buffer->zero_width_marks &&
379 hb_ot_layout_get_glyph_class (hb_face, glyphs->glyphs[i].glyph) == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
381 glyphs->glyphs[i].geometry.width = 0;
385 pango_font_get_glyph_extents ((PangoFont *)buffer->font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
386 glyphs->glyphs[i].geometry.width = logical_rect.width;
390 glyphs->glyphs[i].geometry.width = 0;
392 glyphs->glyphs[i].geometry.x_offset = 0;
393 glyphs->glyphs[i].geometry.y_offset = 0;
398 /* Swap all glyphs */
399 pango_glyph_string_reverse_range (glyphs, 0, glyphs->num_glyphs);
402 positions = hb_buffer_get_glyph_positions (buffer->buffer);
403 if (buffer->applied_gpos)
405 gboolean scale = FALSE;
406 double xscale = 1, yscale = 1;
407 PangoFcFontKey *key = _pango_fc_font_get_font_key (buffer->font);
409 /* This is a kludge, and dupped in pango_fc_font_kern_glyphs().
410 * Should move the scale factor to PangoFcFont layer. */
412 const PangoMatrix *matrix = pango_fc_font_key_get_matrix (key);
413 PangoMatrix identity = PANGO_MATRIX_INIT;
414 if (G_UNLIKELY (matrix && 0 != memcmp (&identity, matrix, 4 * sizeof (double))))
417 pango_matrix_get_font_scale_factors (matrix, &xscale, &yscale);
418 if (xscale) xscale = 1 / xscale;
419 if (yscale) yscale = 1 / yscale;
424 apply_gpos_rtl (glyphs, positions, scale, xscale, yscale, buffer->font->is_hinted);
426 apply_gpos_ltr (glyphs, positions, scale, xscale, yscale, buffer->font->is_hinted);
430 /* FIXME we should only do this if the 'kern' feature was requested */
431 pango_fc_font_kern_glyphs (buffer->font, glyphs);
434 pango_fc_font_unlock_face (buffer->font);