2013-12-21 21:31:38 +01:00
|
|
|
/***************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* hbshim.c */
|
|
|
|
/* */
|
|
|
|
/* HarfBuzz interface for accessing OpenType features (body). */
|
|
|
|
/* */
|
2014-01-01 07:10:36 +01:00
|
|
|
/* Copyright 2013, 2014 by */
|
2013-12-21 21:31:38 +01:00
|
|
|
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
|
|
|
|
/* */
|
|
|
|
/* This file is part of the FreeType project, and may only be used, */
|
|
|
|
/* modified, and distributed under the terms of the FreeType project */
|
|
|
|
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
|
|
|
|
/* this file you indicate that you have read the license and */
|
|
|
|
/* understand and accept it fully. */
|
|
|
|
/* */
|
|
|
|
/***************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
#include <ft2build.h>
|
|
|
|
#include FT_FREETYPE_H
|
2014-02-17 08:38:31 +01:00
|
|
|
#include "afglobal.h"
|
|
|
|
#include "aftypes.h"
|
2013-12-28 08:55:24 +01:00
|
|
|
#include "hbshim.h"
|
2013-12-21 21:31:38 +01:00
|
|
|
|
|
|
|
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */
|
|
|
|
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
|
|
|
|
/* messages during execution. */
|
|
|
|
/* */
|
|
|
|
#undef FT_COMPONENT
|
|
|
|
#define FT_COMPONENT trace_afharfbuzz
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We use `sets' (in the HarfBuzz sense, which comes quite near to the
|
|
|
|
* usual mathematical meaning) to manage both lookups and glyph indices.
|
|
|
|
*
|
|
|
|
* 1. For each coverage, collect lookup IDs in a set. Note that an
|
2013-12-30 07:41:22 +01:00
|
|
|
* auto-hinter `coverage' is represented by one `feature', and a
|
|
|
|
* feature consists of an arbitrary number of (font specific) `lookup's
|
|
|
|
* that actually do the mapping job. Please check the OpenType
|
|
|
|
* specification for more details on features and lookups.
|
2013-12-21 21:31:38 +01:00
|
|
|
*
|
|
|
|
* 2. Create glyph ID sets from the corresponding lookup sets.
|
|
|
|
*
|
|
|
|
* 3. The glyph set corresponding to AF_COVERAGE_DEFAULT is computed
|
|
|
|
* with all lookups specific to the OpenType script activated. It
|
|
|
|
* relies on the order of AF_DEFINE_STYLE_CLASS entries so that
|
|
|
|
* special coverages (like `oldstyle figures') don't get overwritten.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* load coverage tags */
|
2013-12-30 07:41:22 +01:00
|
|
|
#undef COVERAGE
|
|
|
|
#define COVERAGE( name, NAME, description, \
|
|
|
|
tag1, tag2, tag3, tag4 ) \
|
2013-12-21 21:31:38 +01:00
|
|
|
static const hb_tag_t name ## _coverage[] = \
|
|
|
|
{ \
|
2013-12-30 07:41:22 +01:00
|
|
|
HB_TAG( tag1, tag2, tag3, tag4 ), \
|
2013-12-21 21:31:38 +01:00
|
|
|
HB_TAG_NONE \
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#include "afcover.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* define mapping between coverage tags and AF_Coverage */
|
2013-12-30 07:41:22 +01:00
|
|
|
#undef COVERAGE
|
|
|
|
#define COVERAGE( name, NAME, description, \
|
|
|
|
tag1, tag2, tag3, tag4 ) \
|
2013-12-21 21:31:38 +01:00
|
|
|
name ## _coverage,
|
|
|
|
|
|
|
|
|
|
|
|
static const hb_tag_t* coverages[] =
|
|
|
|
{
|
|
|
|
#include "afcover.h"
|
|
|
|
|
|
|
|
NULL /* AF_COVERAGE_DEFAULT */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* load HarfBuzz script tags */
|
|
|
|
#undef SCRIPT
|
2014-01-26 09:45:23 +01:00
|
|
|
#define SCRIPT( s, S, d, h, sc1, sc2, sc3 ) h,
|
2013-12-21 21:31:38 +01:00
|
|
|
|
|
|
|
|
2014-03-06 18:56:58 +01:00
|
|
|
static const hb_script_t scripts[] =
|
2013-12-21 21:31:38 +01:00
|
|
|
{
|
|
|
|
#include "afscript.h"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
FT_Error
|
2013-12-27 19:26:04 +01:00
|
|
|
af_get_coverage( AF_FaceGlobals globals,
|
|
|
|
AF_StyleClass style_class,
|
|
|
|
FT_Byte* gstyles )
|
2013-12-21 21:31:38 +01:00
|
|
|
{
|
|
|
|
hb_face_t* face;
|
|
|
|
|
2014-01-01 07:48:20 +01:00
|
|
|
hb_set_t* gsub_lookups; /* GSUB lookups for a given script */
|
|
|
|
hb_set_t* gsub_glyphs; /* glyphs covered by GSUB lookups */
|
2014-01-01 08:00:16 +01:00
|
|
|
hb_set_t* gpos_lookups; /* GPOS lookups for a given script */
|
|
|
|
hb_set_t* gpos_glyphs; /* glyphs covered by GPOS lookups */
|
2013-12-21 21:31:38 +01:00
|
|
|
|
2014-03-06 18:56:58 +01:00
|
|
|
hb_script_t script;
|
2013-12-21 21:31:38 +01:00
|
|
|
const hb_tag_t* coverage_tags;
|
|
|
|
hb_tag_t script_tags[] = { HB_TAG_NONE,
|
2013-12-27 19:26:04 +01:00
|
|
|
HB_TAG_NONE,
|
2013-12-21 21:31:38 +01:00
|
|
|
HB_TAG_NONE,
|
|
|
|
HB_TAG_NONE };
|
|
|
|
|
|
|
|
hb_codepoint_t idx;
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
int count;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2013-12-27 19:26:04 +01:00
|
|
|
if ( !globals || !style_class || !gstyles )
|
2013-12-21 21:31:38 +01:00
|
|
|
return FT_THROW( Invalid_Argument );
|
|
|
|
|
2013-12-28 08:55:24 +01:00
|
|
|
face = hb_font_get_face( globals->hb_font );
|
2013-12-21 21:31:38 +01:00
|
|
|
|
2014-01-01 07:48:20 +01:00
|
|
|
gsub_lookups = hb_set_create();
|
|
|
|
gsub_glyphs = hb_set_create();
|
2014-01-01 08:00:16 +01:00
|
|
|
gpos_lookups = hb_set_create();
|
|
|
|
gpos_glyphs = hb_set_create();
|
2013-12-21 21:31:38 +01:00
|
|
|
|
|
|
|
coverage_tags = coverages[style_class->coverage];
|
|
|
|
script = scripts[style_class->script];
|
|
|
|
|
|
|
|
/* Convert a HarfBuzz script tag into the corresponding OpenType */
|
|
|
|
/* tag or tags -- some Indic scripts like Devanagari have an old */
|
|
|
|
/* and a new set of features. */
|
|
|
|
hb_ot_tags_from_script( script,
|
|
|
|
&script_tags[0],
|
|
|
|
&script_tags[1] );
|
|
|
|
|
|
|
|
/* `hb_ot_tags_from_script' usually returns HB_OT_TAG_DEFAULT_SCRIPT */
|
2013-12-27 19:26:04 +01:00
|
|
|
/* as the second tag. We change that to HB_TAG_NONE except for the */
|
|
|
|
/* default script. */
|
|
|
|
if ( style_class->script == globals->module->default_script &&
|
|
|
|
style_class->coverage == AF_COVERAGE_DEFAULT )
|
|
|
|
{
|
|
|
|
if ( script_tags[0] == HB_TAG_NONE )
|
|
|
|
script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( script_tags[1] == HB_TAG_NONE )
|
|
|
|
script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
|
|
|
|
else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT )
|
|
|
|
script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT )
|
|
|
|
script_tags[1] = HB_TAG_NONE;
|
|
|
|
}
|
2013-12-21 21:31:38 +01:00
|
|
|
|
|
|
|
hb_ot_layout_collect_lookups( face,
|
|
|
|
HB_OT_TAG_GSUB,
|
|
|
|
script_tags,
|
|
|
|
NULL,
|
|
|
|
coverage_tags,
|
2014-01-01 07:48:20 +01:00
|
|
|
gsub_lookups );
|
2014-01-03 18:33:24 +01:00
|
|
|
|
|
|
|
if ( hb_set_is_empty( gsub_lookups ) )
|
2014-01-22 09:19:57 +01:00
|
|
|
goto Exit; /* nothing to do */
|
2014-01-03 18:33:24 +01:00
|
|
|
|
2014-01-01 08:00:16 +01:00
|
|
|
hb_ot_layout_collect_lookups( face,
|
|
|
|
HB_OT_TAG_GPOS,
|
|
|
|
script_tags,
|
|
|
|
NULL,
|
|
|
|
coverage_tags,
|
|
|
|
gpos_lookups );
|
2013-12-21 21:31:38 +01:00
|
|
|
|
2014-01-01 07:48:20 +01:00
|
|
|
FT_TRACE4(( "GSUB lookups (style `%s'):\n"
|
2013-12-21 21:31:38 +01:00
|
|
|
" ",
|
|
|
|
af_style_names[style_class->style] ));
|
|
|
|
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
count = 0;
|
|
|
|
#endif
|
|
|
|
|
2014-01-01 07:48:20 +01:00
|
|
|
for ( idx = -1; hb_set_next( gsub_lookups, &idx ); )
|
2013-12-21 21:31:38 +01:00
|
|
|
{
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
FT_TRACE4(( " %d", idx ));
|
|
|
|
count++;
|
|
|
|
#endif
|
|
|
|
|
2014-01-01 08:00:16 +01:00
|
|
|
/* get output coverage of GSUB feature */
|
2013-12-21 21:31:38 +01:00
|
|
|
hb_ot_layout_lookup_collect_glyphs( face,
|
|
|
|
HB_OT_TAG_GSUB,
|
|
|
|
idx,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
2014-01-01 07:48:20 +01:00
|
|
|
gsub_glyphs );
|
2013-12-21 21:31:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
if ( !count )
|
|
|
|
FT_TRACE4(( " (none)" ));
|
|
|
|
FT_TRACE4(( "\n\n" ));
|
2014-01-01 08:00:16 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
FT_TRACE4(( "GPOS lookups (style `%s'):\n"
|
|
|
|
" ",
|
|
|
|
af_style_names[style_class->style] ));
|
2013-12-21 21:31:38 +01:00
|
|
|
|
2014-01-01 08:00:16 +01:00
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
count = 0;
|
|
|
|
#endif
|
2013-12-21 21:31:38 +01:00
|
|
|
|
2014-01-01 08:00:16 +01:00
|
|
|
for ( idx = -1; hb_set_next( gpos_lookups, &idx ); )
|
|
|
|
{
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
FT_TRACE4(( " %d", idx ));
|
|
|
|
count++;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* get input coverage of GPOS feature */
|
|
|
|
hb_ot_layout_lookup_collect_glyphs( face,
|
|
|
|
HB_OT_TAG_GPOS,
|
|
|
|
idx,
|
|
|
|
NULL,
|
|
|
|
gpos_glyphs,
|
|
|
|
NULL,
|
|
|
|
NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
if ( !count )
|
|
|
|
FT_TRACE4(( " (none)" ));
|
|
|
|
FT_TRACE4(( "\n\n" ));
|
|
|
|
#endif
|
|
|
|
|
2014-01-03 18:33:24 +01:00
|
|
|
/*
|
|
|
|
* We now check whether we can construct blue zones, using glyphs
|
|
|
|
* covered by the feature only. In case there is not a single zone
|
|
|
|
* (this is, not a single character is covered), we skip this coverage.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
AF_Blue_Stringset bss = style_class->blue_stringset;
|
|
|
|
const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
|
|
|
|
|
|
|
|
FT_Bool found = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
|
|
|
|
{
|
|
|
|
const char* p = &af_blue_strings[bs->string];
|
|
|
|
|
|
|
|
|
|
|
|
while ( *p )
|
|
|
|
{
|
|
|
|
hb_codepoint_t ch;
|
|
|
|
|
|
|
|
|
|
|
|
GET_UTF8_CHAR( ch, p );
|
|
|
|
|
|
|
|
for ( idx = -1; hb_set_next( gsub_lookups, &idx ); )
|
|
|
|
{
|
|
|
|
hb_codepoint_t gidx = FT_Get_Char_Index( globals->face, ch );
|
|
|
|
|
|
|
|
|
|
|
|
if ( hb_ot_layout_lookup_would_substitute( face, idx,
|
|
|
|
&gidx, 1, 1 ) )
|
|
|
|
{
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !found )
|
|
|
|
{
|
|
|
|
FT_TRACE4(( " no blue characters found; style skipped\n" ));
|
2014-01-22 09:19:57 +01:00
|
|
|
goto Exit;
|
2014-01-03 18:33:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-01 08:00:16 +01:00
|
|
|
/*
|
|
|
|
* Various OpenType features might use the same glyphs at different
|
|
|
|
* vertical positions; for example, superscript and subscript glyphs
|
2014-01-15 09:46:23 +01:00
|
|
|
* could be the same. However, the auto-hinter is completely
|
2014-01-01 08:00:16 +01:00
|
|
|
* agnostic of OpenType features after the feature analysis has been
|
2014-01-15 09:46:23 +01:00
|
|
|
* completed: The engine then simply receives a glyph index and returns a
|
2014-01-01 08:00:16 +01:00
|
|
|
* hinted and usually rendered glyph.
|
|
|
|
*
|
|
|
|
* Consider the superscript feature of font `pala.ttf': Some of the
|
|
|
|
* glyphs are `real', this is, they have a zero vertical offset, but
|
|
|
|
* most of them are small caps glyphs shifted up to the superscript
|
|
|
|
* position (this is, the `sups' feature is present in both the GSUB and
|
|
|
|
* GPOS tables). The code for blue zones computation actually uses a
|
|
|
|
* feature's y offset so that the `real' glyphs get correct hints. But
|
|
|
|
* later on it is impossible to decide whether a glyph index belongs to,
|
|
|
|
* say, the small caps or superscript feature.
|
|
|
|
*
|
|
|
|
* For this reason, we don't assign a style to a glyph if the current
|
|
|
|
* feature covers the glyph in both the GSUB and the GPOS tables. This
|
|
|
|
* is quite a broad condition, assuming that
|
|
|
|
*
|
|
|
|
* (a) glyphs that get used in multiple features are present in a
|
|
|
|
* feature without vertical shift,
|
|
|
|
*
|
|
|
|
* and
|
|
|
|
*
|
|
|
|
* (b) a feature's GPOS data really moves the glyph vertically.
|
|
|
|
*
|
|
|
|
* Not fulfilling condition (a) makes a font larger; it would also
|
|
|
|
* reduce the number of glyphs that could be addressed directly without
|
|
|
|
* using OpenType features, so this assumption is rather strong.
|
|
|
|
*
|
|
|
|
* Condition (b) is much weaker, and there might be glyphs which get
|
|
|
|
* missed. However, the OpenType features we are going to handle are
|
|
|
|
* primarily located in GSUB, and HarfBuzz doesn't provide an API to
|
|
|
|
* directly get the necessary information from the GPOS table. A
|
|
|
|
* possible solution might be to directly parse the GPOS table to find
|
|
|
|
* out whether a glyph gets shifted vertically, but this is something I
|
|
|
|
* would like to avoid if not really necessary.
|
|
|
|
*
|
2014-04-14 07:58:19 +02:00
|
|
|
* Note that we don't follow this logic for the default coverage.
|
|
|
|
* Complex scripts like Devanagari have mandatory GPOS features to
|
|
|
|
* position many glyph elements, using mark-to-base or mark-to-ligature
|
|
|
|
* tables; the number of glyphs missed due to condition (b) would be far
|
|
|
|
* too large.
|
|
|
|
*
|
2014-01-01 08:00:16 +01:00
|
|
|
*/
|
2014-04-14 07:58:19 +02:00
|
|
|
if ( style_class->coverage != AF_COVERAGE_DEFAULT )
|
|
|
|
hb_set_subtract( gsub_glyphs, gpos_glyphs );
|
2014-01-01 08:00:16 +01:00
|
|
|
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
FT_TRACE4(( " glyphs without GPOS data (`*' means already assigned)" ));
|
2013-12-21 21:31:38 +01:00
|
|
|
count = 0;
|
|
|
|
#endif
|
|
|
|
|
2014-01-01 07:48:20 +01:00
|
|
|
for ( idx = -1; hb_set_next( gsub_glyphs, &idx ); )
|
2013-12-21 21:31:38 +01:00
|
|
|
{
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
if ( !( count % 10 ) )
|
|
|
|
FT_TRACE4(( "\n"
|
|
|
|
" " ));
|
|
|
|
|
|
|
|
FT_TRACE4(( " %d", idx ));
|
|
|
|
count++;
|
|
|
|
#endif
|
|
|
|
|
2014-03-17 07:33:14 +01:00
|
|
|
/* HarfBuzz 0.9.26 and older doesn't validate glyph indices */
|
|
|
|
/* returned by `hb_ot_layout_lookup_collect_glyphs'... */
|
|
|
|
if ( idx >= (hb_codepoint_t)globals->glyph_count )
|
|
|
|
continue;
|
|
|
|
|
2013-12-21 21:31:38 +01:00
|
|
|
if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
|
|
|
|
gstyles[idx] = (FT_Byte)style_class->style;
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
else
|
|
|
|
FT_TRACE4(( "*" ));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
if ( !count )
|
|
|
|
FT_TRACE4(( "\n"
|
|
|
|
" (none)" ));
|
|
|
|
FT_TRACE4(( "\n\n" ));
|
|
|
|
#endif
|
|
|
|
|
2014-01-22 09:19:57 +01:00
|
|
|
Exit:
|
2014-01-01 07:48:20 +01:00
|
|
|
hb_set_destroy( gsub_lookups );
|
|
|
|
hb_set_destroy( gsub_glyphs );
|
2014-01-01 08:00:16 +01:00
|
|
|
hb_set_destroy( gpos_lookups );
|
|
|
|
hb_set_destroy( gpos_glyphs );
|
2013-12-21 21:31:38 +01:00
|
|
|
|
|
|
|
return FT_Err_Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-01 07:10:36 +01:00
|
|
|
/* construct HarfBuzz features */
|
|
|
|
#undef COVERAGE
|
|
|
|
#define COVERAGE( name, NAME, description, \
|
|
|
|
tag1, tag2, tag3, tag4 ) \
|
|
|
|
static const hb_feature_t name ## _feature[] = \
|
|
|
|
{ \
|
|
|
|
{ \
|
|
|
|
HB_TAG( tag1, tag2, tag3, tag4 ), \
|
2014-03-06 18:56:58 +01:00
|
|
|
1, 0, (unsigned int)-1 \
|
2014-01-01 07:10:36 +01:00
|
|
|
} \
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#include "afcover.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* define mapping between HarfBuzz features and AF_Coverage */
|
|
|
|
#undef COVERAGE
|
|
|
|
#define COVERAGE( name, NAME, description, \
|
|
|
|
tag1, tag2, tag3, tag4 ) \
|
|
|
|
name ## _feature,
|
|
|
|
|
|
|
|
|
|
|
|
static const hb_feature_t* features[] =
|
|
|
|
{
|
|
|
|
#include "afcover.h"
|
|
|
|
|
|
|
|
NULL /* AF_COVERAGE_DEFAULT */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-12-31 08:16:57 +01:00
|
|
|
FT_Error
|
2013-12-28 10:33:01 +01:00
|
|
|
af_get_char_index( AF_StyleMetrics metrics,
|
2013-12-31 08:16:57 +01:00
|
|
|
FT_ULong charcode,
|
|
|
|
FT_ULong *codepoint,
|
|
|
|
FT_Long *y_offset )
|
2013-12-28 10:33:01 +01:00
|
|
|
{
|
2014-01-01 07:10:36 +01:00
|
|
|
AF_StyleClass style_class;
|
|
|
|
|
|
|
|
const hb_feature_t* feature;
|
2013-12-28 10:33:01 +01:00
|
|
|
|
2014-01-03 20:49:31 +01:00
|
|
|
FT_ULong in_idx, out_idx;
|
|
|
|
|
2013-12-28 10:33:01 +01:00
|
|
|
|
|
|
|
if ( !metrics )
|
|
|
|
return FT_THROW( Invalid_Argument );
|
|
|
|
|
2014-01-03 20:49:31 +01:00
|
|
|
in_idx = FT_Get_Char_Index( metrics->globals->face, charcode );
|
|
|
|
|
2014-01-01 07:10:36 +01:00
|
|
|
style_class = metrics->style_class;
|
2013-12-28 10:33:01 +01:00
|
|
|
|
2014-01-01 07:10:36 +01:00
|
|
|
feature = features[style_class->coverage];
|
|
|
|
|
|
|
|
if ( feature )
|
|
|
|
{
|
2014-01-03 18:09:36 +01:00
|
|
|
FT_UInt upem = metrics->globals->face->units_per_EM;
|
|
|
|
|
2014-01-01 07:10:36 +01:00
|
|
|
hb_font_t* font = metrics->globals->hb_font;
|
|
|
|
hb_buffer_t* buf = hb_buffer_create();
|
|
|
|
|
|
|
|
uint32_t c = (uint32_t)charcode;
|
|
|
|
|
|
|
|
hb_glyph_info_t* ginfo;
|
|
|
|
hb_glyph_position_t* gpos;
|
|
|
|
unsigned int gcount;
|
|
|
|
|
|
|
|
|
2014-01-03 18:09:36 +01:00
|
|
|
/* we shape at a size of units per EM; this means font units */
|
|
|
|
hb_font_set_scale( font, upem, upem );
|
|
|
|
|
2014-01-01 07:10:36 +01:00
|
|
|
/* XXX: is this sufficient for a single character of any script? */
|
|
|
|
hb_buffer_set_direction( buf, HB_DIRECTION_LTR );
|
|
|
|
hb_buffer_set_script( buf, scripts[style_class->script] );
|
|
|
|
|
|
|
|
/* we add one character to `buf' ... */
|
|
|
|
hb_buffer_add_utf32( buf, &c, 1, 0, 1 );
|
|
|
|
|
|
|
|
/* ... and apply one feature */
|
|
|
|
hb_shape( font, buf, feature, 1 );
|
|
|
|
|
|
|
|
ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
|
|
|
|
gpos = hb_buffer_get_glyph_positions( buf, &gcount );
|
|
|
|
|
2014-01-03 20:49:31 +01:00
|
|
|
out_idx = ginfo[0].codepoint;
|
|
|
|
|
|
|
|
/* getting the same index indicates no substitution, */
|
|
|
|
/* which means that the glyph isn't available in the feature */
|
|
|
|
if ( in_idx == out_idx )
|
|
|
|
{
|
|
|
|
*codepoint = 0;
|
|
|
|
*y_offset = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*codepoint = out_idx;
|
|
|
|
*y_offset = gpos[0].y_offset;
|
|
|
|
}
|
2014-01-01 07:10:36 +01:00
|
|
|
|
|
|
|
hb_buffer_destroy( buf );
|
|
|
|
|
|
|
|
#ifdef FT_DEBUG_LEVEL_TRACE
|
|
|
|
if ( gcount > 1 )
|
|
|
|
FT_TRACE1(( "af_get_char_index:"
|
|
|
|
" input character mapped to multiple glyphs\n" ));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-01-03 20:49:31 +01:00
|
|
|
*codepoint = in_idx;
|
2014-01-01 07:10:36 +01:00
|
|
|
*y_offset = 0;
|
|
|
|
}
|
2013-12-31 08:16:57 +01:00
|
|
|
|
|
|
|
return FT_Err_Ok;
|
2013-12-28 10:33:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-15 09:46:23 +01:00
|
|
|
#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
|
2013-12-28 10:33:01 +01:00
|
|
|
|
|
|
|
|
2013-12-28 12:26:21 +01:00
|
|
|
FT_Error
|
|
|
|
af_get_coverage( AF_FaceGlobals globals,
|
|
|
|
AF_StyleClass style_class,
|
|
|
|
FT_Byte* gstyles )
|
|
|
|
{
|
|
|
|
FT_UNUSED( globals );
|
|
|
|
FT_UNUSED( style_class );
|
|
|
|
FT_UNUSED( gstyles );
|
|
|
|
|
|
|
|
return FT_Err_Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-31 08:16:57 +01:00
|
|
|
FT_Error
|
2013-12-28 10:33:01 +01:00
|
|
|
af_get_char_index( AF_StyleMetrics metrics,
|
2013-12-31 08:16:57 +01:00
|
|
|
FT_ULong charcode,
|
|
|
|
FT_ULong *codepoint,
|
|
|
|
FT_Long *y_offset )
|
2013-12-28 10:33:01 +01:00
|
|
|
{
|
|
|
|
FT_Face face;
|
|
|
|
|
|
|
|
|
|
|
|
if ( !metrics )
|
|
|
|
return FT_THROW( Invalid_Argument );
|
|
|
|
|
|
|
|
face = metrics->globals->face;
|
|
|
|
|
2013-12-31 08:16:57 +01:00
|
|
|
*codepoint = FT_Get_Char_Index( face, charcode );
|
|
|
|
*y_offset = 0;
|
|
|
|
|
|
|
|
return FT_Err_Ok;
|
2013-12-28 10:33:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
|
2013-12-21 21:31:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* END */
|