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 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 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 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 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 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 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 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 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 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 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 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 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 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 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