1 /*
2  * Copyright (C) 1998-2004  David Turner and Werner Lemberg
3  * Copyright (C) 2004,2007  Red Hat, Inc.
4  *
5  * This is part of HarfBuzz, an OpenType Layout engine library.
6  *
7  * Permission is hereby granted, without written agreement and without
8  * license or royalty fees, to use, copy, modify, and distribute this
9  * software and its documentation for any purpose, provided that the
10  * above copyright notice and the following two paragraphs appear in
11  * all copies of this software.
12  *
13  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17  * DAMAGE.
18  *
19  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24  *
25  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
26  */
27 
28 #include "harfbuzz-impl.h"
29 #include "harfbuzz-buffer-private.h"
30 #include "harfbuzz-gsub-private.h"
31 #include "harfbuzz-gpos-private.h"
32 
33 /* Here is how the buffer works internally:
34  *
35  * There are two string pointers: in_string and out_string.  They
36  * always have same allocated size, but different length and positions.
37  *
38  * As an optimization, both in_string and out_string may point to the
39  * same piece of memory, which is owned by in_string.  This remains the
40  * case as long as:
41  *
42  *   - copy_glyph() is called
43  *   - replace_glyph() is called with inplace=TRUE
44  *   - add_output_glyph() and add_output_glyphs() are not called
45  *
46  * In that case swap(), and copy_glyph(), and replace_glyph() are all
47  * mostly no-op.
48  *
49  * As soon an add_output_glyph[s]() or replace_glyph() with inplace=FALSE is
50  * called, out_string is moved over to an alternate buffer (alt_string), and
51  * its current contents (out_length entries) are copied to the alt buffer.
52  * This should all remain transparent to the user.  swap() then switches
53  * in_string and alt_string.  alt_string is not allocated until its needed,
54  * but after that it's grown with in_string unconditionally.
55  *
56  * The buffer->separate_out boolean keeps status of whether out_string points
57  * to in_string (FALSE) or alt_string (TRUE).
58  */
59 
60 /* Internal API */
61 
62 static HB_Error
hb_buffer_ensure(HB_Buffer buffer,HB_UInt size)63 hb_buffer_ensure( HB_Buffer buffer,
64 		   HB_UInt   size )
65 {
66   HB_UInt new_allocated = buffer->allocated;
67 
68   if (size > new_allocated)
69     {
70       HB_Error error;
71 
72       while (size > new_allocated)
73 	new_allocated += (new_allocated >> 1) + 8;
74 
75       if ( buffer->positions )
76         {
77 	  if ( REALLOC_ARRAY( buffer->positions, new_allocated, HB_PositionRec ) )
78 	    return error;
79 	}
80 
81       if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) )
82 	return error;
83 
84       if ( buffer->separate_out )
85         {
86 	  if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
87 	    return error;
88 
89 	  buffer->out_string = buffer->alt_string;
90 	}
91       else
92         {
93 	  buffer->out_string = buffer->in_string;
94 
95 	  if ( buffer->alt_string )
96 	    {
97 	      if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
98 		return error;
99 	    }
100 	}
101 
102       buffer->allocated = new_allocated;
103     }
104 
105   return HB_Err_Ok;
106 }
107 
108 static HB_Error
hb_buffer_duplicate_out_buffer(HB_Buffer buffer)109 hb_buffer_duplicate_out_buffer( HB_Buffer buffer )
110 {
111   if ( !buffer->alt_string )
112     {
113       HB_Error error;
114 
115       if ( ALLOC_ARRAY( buffer->alt_string, buffer->allocated, HB_GlyphItemRec ) )
116 	return error;
117     }
118 
119   buffer->out_string = buffer->alt_string;
120   memcpy( buffer->out_string, buffer->in_string, buffer->out_length * sizeof (buffer->out_string[0]) );
121   buffer->separate_out = TRUE;
122 
123   return HB_Err_Ok;
124 }
125 
126 /* Public API */
127 
128 HB_Error
hb_buffer_new(HB_Buffer * pbuffer)129 hb_buffer_new( HB_Buffer *pbuffer )
130 {
131   HB_Buffer buffer;
132   HB_Error error;
133 
134   if ( ALLOC( buffer, sizeof( HB_BufferRec ) ) )
135     return error;
136 
137   buffer->allocated = 0;
138   buffer->in_string = NULL;
139   buffer->alt_string = NULL;
140   buffer->positions = NULL;
141 
142   hb_buffer_clear( buffer );
143 
144   *pbuffer = buffer;
145 
146   return HB_Err_Ok;
147 }
148 
149 void
hb_buffer_free(HB_Buffer buffer)150 hb_buffer_free( HB_Buffer buffer )
151 {
152   FREE( buffer->in_string );
153   FREE( buffer->alt_string );
154   buffer->out_string = NULL;
155   FREE( buffer->positions );
156   FREE( buffer );
157 }
158 
159 void
hb_buffer_clear(HB_Buffer buffer)160 hb_buffer_clear( HB_Buffer buffer )
161 {
162   buffer->in_length = 0;
163   buffer->out_length = 0;
164   buffer->in_pos = 0;
165   buffer->out_pos = 0;
166   buffer->out_string = buffer->in_string;
167   buffer->separate_out = FALSE;
168   buffer->max_ligID = 0;
169 }
170 
171 HB_Error
hb_buffer_add_glyph(HB_Buffer buffer,HB_UInt glyph_index,HB_UInt properties,HB_UInt cluster)172 hb_buffer_add_glyph( HB_Buffer buffer,
173 		      HB_UInt   glyph_index,
174 		      HB_UInt   properties,
175 		      HB_UInt   cluster )
176 {
177   HB_Error error;
178   HB_GlyphItem glyph;
179 
180   error = hb_buffer_ensure( buffer, buffer->in_length + 1 );
181   if ( error )
182     return error;
183 
184   glyph = &buffer->in_string[buffer->in_length];
185   glyph->gindex = glyph_index;
186   glyph->properties = properties;
187   glyph->cluster = cluster;
188   glyph->component = 0;
189   glyph->ligID = 0;
190   glyph->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
191 
192   buffer->in_length++;
193 
194   return HB_Err_Ok;
195 }
196 
197 /* HarfBuzz-Internal API */
198 
199 HB_INTERNAL void
_hb_buffer_clear_output(HB_Buffer buffer)200 _hb_buffer_clear_output( HB_Buffer buffer )
201 {
202   buffer->out_length = 0;
203   buffer->out_pos = 0;
204   buffer->out_string = buffer->in_string;
205   buffer->separate_out = FALSE;
206 }
207 
208 HB_INTERNAL HB_Error
_hb_buffer_clear_positions(HB_Buffer buffer)209 _hb_buffer_clear_positions( HB_Buffer buffer )
210 {
211   if ( !buffer->positions )
212     {
213       HB_Error error;
214 
215       if ( ALLOC_ARRAY( buffer->positions, buffer->allocated, HB_PositionRec ) )
216 	return error;
217     }
218 
219   memset (buffer->positions, 0, sizeof (buffer->positions[0]) * buffer->in_length);
220 
221   return HB_Err_Ok;
222 }
223 
224 HB_INTERNAL void
_hb_buffer_swap(HB_Buffer buffer)225 _hb_buffer_swap( HB_Buffer buffer )
226 {
227   HB_GlyphItem tmp_string;
228   int tmp_length;
229   int tmp_pos;
230 
231   if ( buffer->separate_out )
232     {
233       tmp_string = buffer->in_string;
234       buffer->in_string = buffer->out_string;
235       buffer->out_string = tmp_string;
236       buffer->alt_string = buffer->out_string;
237     }
238 
239   tmp_length = buffer->in_length;
240   buffer->in_length = buffer->out_length;
241   buffer->out_length = tmp_length;
242 
243   tmp_pos = buffer->in_pos;
244   buffer->in_pos = buffer->out_pos;
245   buffer->out_pos = tmp_pos;
246 }
247 
248 /* The following function copies `num_out' elements from `glyph_data'
249    to `buffer->out_string', advancing the in array pointer in the structure
250    by `num_in' elements, and the out array pointer by `num_out' elements.
251    Finally, it sets the `length' field of `out' equal to
252    `pos' of the `out' structure.
253 
254    If `component' is 0xFFFF, the component value from buffer->in_pos
255    will copied `num_out' times, otherwise `component' itself will
256    be used to fill the `component' fields.
257 
258    If `ligID' is 0xFFFF, the ligID value from buffer->in_pos
259    will copied `num_out' times, otherwise `ligID' itself will
260    be used to fill the `ligID' fields.
261 
262    The properties for all replacement glyphs are taken
263    from the glyph at position `buffer->in_pos'.
264 
265    The cluster value for the glyph at position buffer->in_pos is used
266    for all replacement glyphs */
267 HB_INTERNAL HB_Error
_hb_buffer_add_output_glyphs(HB_Buffer buffer,HB_UShort num_in,HB_UShort num_out,HB_UShort * glyph_data,HB_UShort component,HB_UShort ligID)268 _hb_buffer_add_output_glyphs( HB_Buffer  buffer,
269 			      HB_UShort  num_in,
270 			      HB_UShort  num_out,
271 			      HB_UShort *glyph_data,
272 			      HB_UShort  component,
273 			      HB_UShort  ligID )
274 {
275   HB_Error  error;
276   HB_UShort i;
277   HB_UInt properties;
278   HB_UInt cluster;
279 
280   error = hb_buffer_ensure( buffer, buffer->out_pos + num_out );
281   if ( error )
282     return error;
283 
284   if ( !buffer->separate_out )
285     {
286       error = hb_buffer_duplicate_out_buffer( buffer );
287       if ( error )
288 	return error;
289     }
290 
291   properties = buffer->in_string[buffer->in_pos].properties;
292   cluster = buffer->in_string[buffer->in_pos].cluster;
293   if ( component == 0xFFFF )
294     component = buffer->in_string[buffer->in_pos].component;
295   if ( ligID == 0xFFFF )
296     ligID = buffer->in_string[buffer->in_pos].ligID;
297 
298   for ( i = 0; i < num_out; i++ )
299   {
300     HB_GlyphItem item = &buffer->out_string[buffer->out_pos + i];
301 
302     item->gindex = glyph_data[i];
303     item->properties = properties;
304     item->cluster = cluster;
305     item->component = component;
306     item->ligID = ligID;
307     item->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
308   }
309 
310   buffer->in_pos  += num_in;
311   buffer->out_pos += num_out;
312 
313   buffer->out_length = buffer->out_pos;
314 
315   return HB_Err_Ok;
316 }
317 
318 HB_INTERNAL HB_Error
_hb_buffer_add_output_glyph(HB_Buffer buffer,HB_UInt glyph_index,HB_UShort component,HB_UShort ligID)319 _hb_buffer_add_output_glyph( HB_Buffer buffer,
320 			     HB_UInt   glyph_index,
321 			     HB_UShort component,
322 			     HB_UShort ligID )
323 {
324   HB_UShort glyph_data =  glyph_index;
325 
326   return _hb_buffer_add_output_glyphs ( buffer, 1, 1,
327 					&glyph_data, component, ligID );
328 }
329 
330 HB_INTERNAL HB_Error
_hb_buffer_copy_output_glyph(HB_Buffer buffer)331 _hb_buffer_copy_output_glyph ( HB_Buffer buffer )
332 {
333   HB_Error  error;
334 
335   error = hb_buffer_ensure( buffer, buffer->out_pos + 1 );
336   if ( error )
337     return error;
338 
339   if ( buffer->separate_out )
340     {
341       buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
342     }
343 
344   buffer->in_pos++;
345   buffer->out_pos++;
346   buffer->out_length = buffer->out_pos;
347 
348   return HB_Err_Ok;
349 }
350 
351 HB_INTERNAL HB_Error
_hb_buffer_replace_output_glyph(HB_Buffer buffer,HB_UInt glyph_index,HB_Bool inplace)352 _hb_buffer_replace_output_glyph( HB_Buffer buffer,
353 				 HB_UInt   glyph_index,
354 				 HB_Bool   inplace )
355 {
356 
357   HB_Error error;
358 
359   if ( inplace )
360     {
361       error = _hb_buffer_copy_output_glyph ( buffer );
362       if ( error )
363 	return error;
364 
365       buffer->out_string[buffer->out_pos-1].gindex = glyph_index;
366     }
367   else
368     {
369       return _hb_buffer_add_output_glyph( buffer, glyph_index, 0xFFFF, 0xFFFF );
370     }
371 
372   return HB_Err_Ok;
373 }
374 
375 HB_INTERNAL HB_UShort
_hb_buffer_allocate_ligid(HB_Buffer buffer)376 _hb_buffer_allocate_ligid( HB_Buffer buffer )
377 {
378   buffer->max_ligID++;
379   if (HB_UNLIKELY (buffer->max_ligID == 0))
380     buffer->max_ligID++;
381 
382   return buffer->max_ligID;
383 }
384