/* | |
* Copyright (C) 1998-2004 David Turner and Werner Lemberg | |
* Copyright (C) 2004,2007 Red Hat, Inc. | |
* | |
* This is part of HarfBuzz, an OpenType Layout engine library. | |
* | |
* Permission is hereby granted, without written agreement and without | |
* license or royalty fees, to use, copy, modify, and distribute this | |
* software and its documentation for any purpose, provided that the | |
* above copyright notice and the following two paragraphs appear in | |
* all copies of this software. | |
* | |
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | |
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | |
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | |
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |
* DAMAGE. | |
* | |
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | |
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | |
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
* | |
* Red Hat Author(s): Owen Taylor, Behdad Esfahbod | |
*/ | |
#include "harfbuzz-impl.h" | |
#include "harfbuzz-buffer-private.h" | |
#include "harfbuzz-gsub-private.h" | |
#include "harfbuzz-gpos-private.h" | |
/* Here is how the buffer works internally: | |
* | |
* There are two string pointers: in_string and out_string. They | |
* always have same allocated size, but different length and positions. | |
* | |
* As an optimization, both in_string and out_string may point to the | |
* same piece of memory, which is owned by in_string. This remains the | |
* case as long as: | |
* | |
* - copy_glyph() is called | |
* - replace_glyph() is called with inplace=TRUE | |
* - add_output_glyph() and add_output_glyphs() are not called | |
* | |
* In that case swap(), and copy_glyph(), and replace_glyph() are all | |
* mostly no-op. | |
* | |
* As soon an add_output_glyph[s]() or replace_glyph() with inplace=FALSE is | |
* called, out_string is moved over to an alternate buffer (alt_string), and | |
* its current contents (out_length entries) are copied to the alt buffer. | |
* This should all remain transparent to the user. swap() then switches | |
* in_string and alt_string. alt_string is not allocated until its needed, | |
* but after that it's grown with in_string unconditionally. | |
* | |
* The buffer->separate_out boolean keeps status of whether out_string points | |
* to in_string (FALSE) or alt_string (TRUE). | |
*/ | |
/* Internal API */ | |
static HB_Error | |
hb_buffer_ensure( HB_Buffer buffer, | |
HB_UInt size ) | |
{ | |
HB_UInt new_allocated = buffer->allocated; | |
if (size > new_allocated) | |
{ | |
HB_Error error; | |
while (size > new_allocated) | |
new_allocated += (new_allocated >> 1) + 8; | |
if ( buffer->positions ) | |
{ | |
if ( REALLOC_ARRAY( buffer->positions, new_allocated, HB_PositionRec ) ) | |
return error; | |
} | |
if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) ) | |
return error; | |
if ( buffer->separate_out ) | |
{ | |
if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) ) | |
return error; | |
buffer->out_string = buffer->alt_string; | |
} | |
else | |
{ | |
buffer->out_string = buffer->in_string; | |
if ( buffer->alt_string ) | |
{ | |
if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) ) | |
return error; | |
} | |
} | |
buffer->allocated = new_allocated; | |
} | |
return HB_Err_Ok; | |
} | |
static HB_Error | |
hb_buffer_duplicate_out_buffer( HB_Buffer buffer ) | |
{ | |
if ( !buffer->alt_string ) | |
{ | |
HB_Error error; | |
if ( ALLOC_ARRAY( buffer->alt_string, buffer->allocated, HB_GlyphItemRec ) ) | |
return error; | |
} | |
buffer->out_string = buffer->alt_string; | |
memcpy( buffer->out_string, buffer->in_string, buffer->out_length * sizeof (buffer->out_string[0]) ); | |
buffer->separate_out = TRUE; | |
return HB_Err_Ok; | |
} | |
/* Public API */ | |
HB_Error | |
hb_buffer_new( HB_Buffer *pbuffer ) | |
{ | |
HB_Buffer buffer; | |
HB_Error error; | |
if ( ALLOC( buffer, sizeof( HB_BufferRec ) ) ) | |
return error; | |
buffer->allocated = 0; | |
buffer->in_string = NULL; | |
buffer->alt_string = NULL; | |
buffer->positions = NULL; | |
hb_buffer_clear( buffer ); | |
*pbuffer = buffer; | |
return HB_Err_Ok; | |
} | |
void | |
hb_buffer_free( HB_Buffer buffer ) | |
{ | |
FREE( buffer->in_string ); | |
FREE( buffer->alt_string ); | |
buffer->out_string = NULL; | |
FREE( buffer->positions ); | |
FREE( buffer ); | |
} | |
void | |
hb_buffer_clear( HB_Buffer buffer ) | |
{ | |
buffer->in_length = 0; | |
buffer->out_length = 0; | |
buffer->in_pos = 0; | |
buffer->out_pos = 0; | |
buffer->out_string = buffer->in_string; | |
buffer->separate_out = FALSE; | |
buffer->max_ligID = 0; | |
} | |
HB_Error | |
hb_buffer_add_glyph( HB_Buffer buffer, | |
HB_UInt glyph_index, | |
HB_UInt properties, | |
HB_UInt cluster ) | |
{ | |
HB_Error error; | |
HB_GlyphItem glyph; | |
error = hb_buffer_ensure( buffer, buffer->in_length + 1 ); | |
if ( error ) | |
return error; | |
glyph = &buffer->in_string[buffer->in_length]; | |
glyph->gindex = glyph_index; | |
glyph->properties = properties; | |
glyph->cluster = cluster; | |
glyph->component = 0; | |
glyph->ligID = 0; | |
glyph->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN; | |
buffer->in_length++; | |
return HB_Err_Ok; | |
} | |
/* HarfBuzz-Internal API */ | |
HB_INTERNAL void | |
_hb_buffer_clear_output( HB_Buffer buffer ) | |
{ | |
buffer->out_length = 0; | |
buffer->out_pos = 0; | |
buffer->out_string = buffer->in_string; | |
buffer->separate_out = FALSE; | |
} | |
HB_INTERNAL HB_Error | |
_hb_buffer_clear_positions( HB_Buffer buffer ) | |
{ | |
if ( !buffer->positions ) | |
{ | |
HB_Error error; | |
if ( ALLOC_ARRAY( buffer->positions, buffer->allocated, HB_PositionRec ) ) | |
return error; | |
} | |
memset (buffer->positions, 0, sizeof (buffer->positions[0]) * buffer->in_length); | |
return HB_Err_Ok; | |
} | |
HB_INTERNAL void | |
_hb_buffer_swap( HB_Buffer buffer ) | |
{ | |
HB_GlyphItem tmp_string; | |
int tmp_length; | |
int tmp_pos; | |
if ( buffer->separate_out ) | |
{ | |
tmp_string = buffer->in_string; | |
buffer->in_string = buffer->out_string; | |
buffer->out_string = tmp_string; | |
buffer->alt_string = buffer->out_string; | |
} | |
tmp_length = buffer->in_length; | |
buffer->in_length = buffer->out_length; | |
buffer->out_length = tmp_length; | |
tmp_pos = buffer->in_pos; | |
buffer->in_pos = buffer->out_pos; | |
buffer->out_pos = tmp_pos; | |
} | |
/* The following function copies `num_out' elements from `glyph_data' | |
to `buffer->out_string', advancing the in array pointer in the structure | |
by `num_in' elements, and the out array pointer by `num_out' elements. | |
Finally, it sets the `length' field of `out' equal to | |
`pos' of the `out' structure. | |
If `component' is 0xFFFF, the component value from buffer->in_pos | |
will copied `num_out' times, otherwise `component' itself will | |
be used to fill the `component' fields. | |
If `ligID' is 0xFFFF, the ligID value from buffer->in_pos | |
will copied `num_out' times, otherwise `ligID' itself will | |
be used to fill the `ligID' fields. | |
The properties for all replacement glyphs are taken | |
from the glyph at position `buffer->in_pos'. | |
The cluster value for the glyph at position buffer->in_pos is used | |
for all replacement glyphs */ | |
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 ) | |
{ | |
HB_Error error; | |
HB_UShort i; | |
HB_UInt properties; | |
HB_UInt cluster; | |
error = hb_buffer_ensure( buffer, buffer->out_pos + num_out ); | |
if ( error ) | |
return error; | |
if ( !buffer->separate_out ) | |
{ | |
error = hb_buffer_duplicate_out_buffer( buffer ); | |
if ( error ) | |
return error; | |
} | |
properties = buffer->in_string[buffer->in_pos].properties; | |
cluster = buffer->in_string[buffer->in_pos].cluster; | |
if ( component == 0xFFFF ) | |
component = buffer->in_string[buffer->in_pos].component; | |
if ( ligID == 0xFFFF ) | |
ligID = buffer->in_string[buffer->in_pos].ligID; | |
for ( i = 0; i < num_out; i++ ) | |
{ | |
HB_GlyphItem item = &buffer->out_string[buffer->out_pos + i]; | |
item->gindex = glyph_data[i]; | |
item->properties = properties; | |
item->cluster = cluster; | |
item->component = component; | |
item->ligID = ligID; | |
item->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN; | |
} | |
buffer->in_pos += num_in; | |
buffer->out_pos += num_out; | |
buffer->out_length = buffer->out_pos; | |
return HB_Err_Ok; | |
} | |
HB_INTERNAL HB_Error | |
_hb_buffer_add_output_glyph( HB_Buffer buffer, | |
HB_UInt glyph_index, | |
HB_UShort component, | |
HB_UShort ligID ) | |
{ | |
HB_UShort glyph_data = glyph_index; | |
return _hb_buffer_add_output_glyphs ( buffer, 1, 1, | |
&glyph_data, component, ligID ); | |
} | |
HB_INTERNAL HB_Error | |
_hb_buffer_copy_output_glyph ( HB_Buffer buffer ) | |
{ | |
HB_Error error; | |
error = hb_buffer_ensure( buffer, buffer->out_pos + 1 ); | |
if ( error ) | |
return error; | |
if ( buffer->separate_out ) | |
{ | |
buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos]; | |
} | |
buffer->in_pos++; | |
buffer->out_pos++; | |
buffer->out_length = buffer->out_pos; | |
return HB_Err_Ok; | |
} | |
HB_INTERNAL HB_Error | |
_hb_buffer_replace_output_glyph( HB_Buffer buffer, | |
HB_UInt glyph_index, | |
HB_Bool inplace ) | |
{ | |
HB_Error error; | |
if ( inplace ) | |
{ | |
error = _hb_buffer_copy_output_glyph ( buffer ); | |
if ( error ) | |
return error; | |
buffer->out_string[buffer->out_pos-1].gindex = glyph_index; | |
} | |
else | |
{ | |
return _hb_buffer_add_output_glyph( buffer, glyph_index, 0xFFFF, 0xFFFF ); | |
} | |
return HB_Err_Ok; | |
} | |
HB_INTERNAL HB_UShort | |
_hb_buffer_allocate_ligid( HB_Buffer buffer ) | |
{ | |
buffer->max_ligID++; | |
if (HB_UNLIKELY (buffer->max_ligID == 0)) | |
buffer->max_ligID++; | |
return buffer->max_ligID; | |
} |