text:
/*
 * Copyright © 2013  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping 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.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
#define HB_BUFFER_DESERIALIZE_TEXT_HH

#include "hb.hh"

%%{

machine deserialize_text;
alphtype unsigned char;
write data;

action clear_item {
	memset (&info, 0, sizeof (info));
	memset (&pos , 0, sizeof (pos ));
}

action add_item {
	buffer->add_info (info);
	if (unlikely (!buffer->successful))
	  return false;
	buffer->pos[buffer->len - 1] = pos;
	*end_ptr = p;
}

action tok {
	tok = p;
}

action parse_glyph {
	if (!hb_font_glyph_from_string (font,
					tok, p - tok,
					&info.codepoint))
	  return false;
}

action parse_cluster   { if (!parse_uint (tok, p, &info.cluster )) return false; }
action parse_x_offset  { if (!parse_int  (tok, p, &pos.x_offset )) return false; }
action parse_y_offset  { if (!parse_int  (tok, p, &pos.y_offset )) return false; }
action parse_x_advance { if (!parse_int  (tok, p, &pos.x_advance)) return false; }
action parse_y_advance { if (!parse_int  (tok, p, &pos.y_advance)) return false; }

unum	= '0' | [1-9] digit*;
num	= '-'? unum;

glyph_id = unum;
glyph_name = alpha (alnum|'_'|'.'|'-')*;

glyph	= (glyph_id | glyph_name) >tok %parse_glyph;
cluster	= '=' (unum >tok %parse_cluster);
offsets	= '@' (num >tok %parse_x_offset)   ',' (num >tok %parse_y_offset );
advances= '+' (num >tok %parse_x_advance) (',' (num >tok %parse_y_advance))?;
item	=
	(
		glyph
		cluster?
		offsets?
		advances?
	)
	>clear_item
	%add_item
	;

main := space* item (space* '|' space* item)* space* ('|'|']')?;

}%%

static hb_bool_t
_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer,
				    const char *buf,
				    unsigned int buf_len,
				    const char **end_ptr,
				    hb_font_t *font)
{
  const char *p = buf, *pe = buf + buf_len;

  /* Ensure we have positions. */
  (void) hb_buffer_get_glyph_positions (buffer, nullptr);

  while (p < pe && ISSPACE (*p))
    p++;
  if (p < pe && *p == (buffer->len ? '|' : '['))
  {
    *end_ptr = ++p;
  }

  const char *eof = pe, *tok = nullptr;
  int cs;
  hb_glyph_info_t info = {0};
  hb_glyph_position_t pos = {0};
  %%{
    write init;
    write exec;
  }%%

  *end_ptr = p;

  return p == pe && *(p-1) != ']';
}

#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */


text:
/*
 * Copyright © 2016  Igalia S.L.
 *
 *  This is part of HarfBuzz, a text shaping 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.
 *
 * Igalia Author(s): Frédéric Wang
 */

#include "hb.hh"

#ifndef HB_NO_MATH

#include "hb-ot-math-table.hh"

/**
 * SECTION:hb-ot-math
 * @title: hb-ot-math
 * @short_description: OpenType Math information
 * @include: hb-ot.h
 *
 * Functions for fetching mathematics layout data from OpenType fonts.
 *
 * HarfBuzz itself does not implement a math layout solution. The
 * functions and types provided can be used by client programs to access
 * the font data necessary for typesetting OpenType Math layout.
 *
 **/

/*
 * OT::MATH
 */

/**
 * hb_ot_math_has_data:
 * @face: #hb_face_t to test
 *
 * Tests whether a face has a `MATH` table.
 *
 * Return value: true if the table is found, false otherwise
 *
 * Since: 1.3.3
 **/
hb_bool_t
hb_ot_math_has_data (hb_face_t *face)
{
  return face->table.MATH->has_data ();
}

/**
 * hb_ot_math_get_constant:
 * @font: #hb_font_t to work upon
 * @constant: #hb_ot_math_constant_t the constant to retrieve
 *
 * Fetches the specified math constant. For most constants, the value returned
 * is an #hb_position_t.
 *
 * However, if the requested constant is #HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN,
 * #HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or
 * #HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, then the return value is
 * an integer between 0 and 100 representing that percentage.
 *
 * Return value: the requested constant or zero
 *
 * Since: 1.3.3
 **/
hb_position_t
hb_ot_math_get_constant (hb_font_t *font,
			 hb_ot_math_constant_t constant)
{
  return font->face->table.MATH->get_constant(constant, font);
}

/**
 * hb_ot_math_get_glyph_italics_correction:
 * @font: #hb_font_t to work upon
 * @glyph: The glyph index from which to retrieve the value
 *
 * Fetches an italics-correction value (if one exists) for the specified
 * glyph index.
 *
  * Return value: the italics correction of the glyph or zero
 *
 * Since: 1.3.3
 **/
hb_position_t
hb_ot_math_get_glyph_italics_correction (hb_font_t *font,
					 hb_codepoint_t glyph)
{
  return font->face->table.MATH->get_glyph_info().get_italics_correction (glyph, font);
}

/**
 * hb_ot_math_get_glyph_top_accent_attachment:
 * @font: #hb_font_t to work upon
 * @glyph: The glyph index from which to retrieve the value
 *
 * Fetches a top-accent-attachment value (if one exists) for the specified
 * glyph index.
 *
 * For any glyph that does not have a top-accent-attachment value - that is,
 * a glyph not covered by the `MathTopAccentAttachment` table (or, when
 * @font has no `MathTopAccentAttachment` table or no `MATH` table, any
 * glyph) - the function synthesizes a value, returning the position at
 * one-half the glyph's advance width.
 *
 * Return value: the top accent attachment of the glyph or 0.5 * the advance
 *               width of @glyph
 *
 * Since: 1.3.3
 **/
hb_position_t
hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font,
					    hb_codepoint_t glyph)
{
  return font->face->table.MATH->get_glyph_info().get_top_accent_attachment (glyph, font);
}

/**
 * hb_ot_math_is_glyph_extended_shape:
 * @face: #hb_face_t to work upon
 * @glyph: The glyph index to test
 *
 * Tests whether the given glyph index is an extended shape in the face.
 *
 * Return value: true if the glyph is an extended shape, false otherwise
 *
 * Since: 1.3.3
 **/
hb_bool_t
hb_ot_math_is_glyph_extended_shape (hb_face_t *face,
				    hb_codepoint_t glyph)
{
  return face->table.MATH->get_glyph_info().is_extended_shape (glyph);
}

/**
 * hb_ot_math_get_glyph_kerning:
 * @font: #hb_font_t to work upon
 * @glyph: The glyph index from which to retrieve the value
 * @kern: The #hb_ot_math_kern_t from which to retrieve the value
 * @correction_height: the correction height to use to determine the kerning.
 *
 * Fetches the math kerning (cut-ins) value for the specified font, glyph index, and
 * @kern.
 *
 * If the MathKern table is found, the function examines it to find a height
 * value that is greater or equal to @correction_height. If such a height
 * value is found, corresponding kerning value from the table is returned. If
 * no such height value is found, the last kerning value is returned.
 *
 * Return value: requested kerning value or zero
 *
 * Since: 1.3.3
 **/
hb_position_t
hb_ot_math_get_glyph_kerning (hb_font_t *font,
			      hb_codepoint_t glyph,
			      hb_ot_math_kern_t kern,
			      hb_position_t correction_height)
{
  return font->face->table.MATH->get_glyph_info().get_kerning (glyph,
							       kern,
							       correction_height,
							       font);
}

/**
 * hb_ot_math_get_glyph_variants:
 * @font: #hb_font_t to work upon
 * @glyph: The index of the glyph to stretch
 * @direction: The direction of the stretching (horizontal or vertical)
 * @start_offset: offset of the first variant to retrieve
 * @variants_count: (inout): Input = the maximum number of variants to return;
 *                           Output = the actual number of variants returned
 * @variants: (out) (array length=variants_count): array of variants returned
 *
 * Fetches the MathGlyphConstruction for the specified font, glyph index, and
 * direction. The corresponding list of size variants is returned as a list of
 * #hb_ot_math_glyph_variant_t structs.
 *
 * <note>The @direction parameter is only used to select between horizontal
 * or vertical directions for the construction. Even though all #hb_direction_t
 * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is
 * considered.</note>
 *
 * Return value: the total number of size variants available or zero
 *
 * Since: 1.3.3
 **/
unsigned int
hb_ot_math_get_glyph_variants (hb_font_t *font,
			       hb_codepoint_t glyph,
			       hb_direction_t direction,
			       unsigned int start_offset,
			       unsigned int *variants_count, /* IN/OUT */
			       hb_ot_math_glyph_variant_t *variants /* OUT */)
{
  return font->face->table.MATH->get_variants().get_glyph_variants (glyph, direction, font,
								    start_offset,
								    variants_count,
								    variants);
}

/**
 * hb_ot_math_get_min_connector_overlap:
 * @font: #hb_font_t to work upon
 * @direction: direction of the stretching (horizontal or vertical)
 *
 * Fetches the MathVariants table for the specified font and returns the
 * minimum overlap of connecting glyphs that are required to draw a glyph
 * assembly in the specified direction.
 *
 * <note>The @direction parameter is only used to select between horizontal
 * or vertical directions for the construction. Even though all #hb_direction_t
 * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is
 * considered.</note>
 *
 * Return value: requested minimum connector overlap or zero
 *
 * Since: 1.3.3
 **/
hb_position_t
hb_ot_math_get_min_connector_overlap (hb_font_t *font,
				      hb_direction_t direction)
{
  return font->face->table.MATH->get_variants().get_min_connector_overlap (direction, font);
}

/**
 * hb_ot_math_get_glyph_assembly:
 * @font: #hb_font_t to work upon
 * @glyph: The index of the glyph to stretch
 * @direction: direction of the stretching (horizontal or vertical)
 * @start_offset: offset of the first glyph part to retrieve
 * @parts_count: (inout): Input = maximum number of glyph parts to return;
 *               Output = actual number of parts returned
 * @parts: (out) (array length=parts_count): the glyph parts returned
 * @italics_correction: (out): italics correction of the glyph assembly
 *
 * Fetches the GlyphAssembly for the specified font, glyph index, and direction.
 * Returned are a list of #hb_ot_math_glyph_part_t glyph parts that can be
 * used to draw the glyph and an italics-correction value (if one is defined
 * in the font).
 *
 * <note>The @direction parameter is only used to select between horizontal
 * or vertical directions for the construction. Even though all #hb_direction_t
 * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is
 * considered.</note>
 *
 * Return value: the total number of parts in the glyph assembly
 *
 * Since: 1.3.3
 **/
unsigned int
hb_ot_math_get_glyph_assembly (hb_font_t *font,
			       hb_codepoint_t glyph,
			       hb_direction_t direction,
			       unsigned int start_offset,
			       unsigned int *parts_count, /* IN/OUT */
			       hb_ot_math_glyph_part_t *parts, /* OUT */
			       hb_position_t *italics_correction /* OUT */)
{
  return font->face->table.MATH->get_variants().get_glyph_parts (glyph,
								 direction,
								 font,
								 start_offset,
								 parts_count,
								 parts,
								 italics_correction);
}

#endif

repo_language: C

mime_type: text/x-c


text:
#!/usr/bin/env python3
# flake8: noqa

import io
import sys

if len (sys.argv) != 5:
	print ("""usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt

Input file, as of Unicode 12:
* https://unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt
* https://unicode.org/Public/UCD/latest/ucd/IndicPositionalCategory.txt
* https://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt""", file=sys.stderr)
	sys.exit (1)

BLACKLISTED_BLOCKS = ["Thai", "Lao"]

files = [io.open (x, encoding='utf-8') for x in sys.argv[1:]]

headers = [[f.readline () for i in range (2)] for j,f in enumerate(files) if j != 2]
headers.append (["UnicodeData.txt does not have a header."])

data = [{} for f in files]
values = [{} for f in files]
for i, f in enumerate (files):
	for line in f:

		j = line.find ('#')
		if j >= 0:
			line = line[:j]

		fields = [x.strip () for x in line.split (';')]
		if len (fields) == 1:
			continue

		uu = fields[0].split ('..')
		start = int (uu[0], 16)
		if len (uu) == 1:
			end = start
		else:
			end = int (uu[1], 16)

		t = fields[1 if i != 2 else 2]

		for u in range (start, end + 1):
			data[i][u] = t
		values[i][t] = values[i].get (t, 0) + end - start + 1

defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block')

# TODO Characters that are not in Unicode Indic files, but used in USE
data[0][0x034F] = defaults[0]
data[0][0x1B61] = defaults[0]
data[0][0x1B63] = defaults[0]
data[0][0x1B64] = defaults[0]
data[0][0x1B65] = defaults[0]
data[0][0x1B66] = defaults[0]
data[0][0x1B67] = defaults[0]
data[0][0x1B69] = defaults[0]
data[0][0x1B6A] = defaults[0]
data[0][0x2060] = defaults[0]
# TODO https://github.com/harfbuzz/harfbuzz/pull/1685
data[0][0x1B5B] = 'Consonant_Placeholder'
data[0][0x1B5C] = 'Consonant_Placeholder'
data[0][0x1B5F] = 'Consonant_Placeholder'
data[0][0x1B62] = 'Consonant_Placeholder'
data[0][0x1B68] = 'Consonant_Placeholder'
# TODO https://github.com/harfbuzz/harfbuzz/issues/1035
data[0][0x11C44] = 'Consonant_Placeholder'
data[0][0x11C45] = 'Consonant_Placeholder'
# TODO https://github.com/harfbuzz/harfbuzz/pull/1399
data[0][0x111C8] = 'Consonant_Placeholder'
for u in range (0xFE00, 0xFE0F + 1):
	data[0][u] = defaults[0]

# Merge data into one dict:
for i,v in enumerate (defaults):
	values[i][v] = values[i].get (v, 0) + 1
combined = {}
for i,d in enumerate (data):
	for u,v in d.items ():
		if i >= 2 and not u in combined:
			continue
		if not u in combined:
			combined[u] = list (defaults)
		combined[u][i] = v
combined = {k:v for k,v in combined.items() if v[3] not in BLACKLISTED_BLOCKS}
data = combined
del combined
num = len (data)

property_names = [
	# General_Category
	'Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc',
	'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po',
	'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',
	# Indic_Syllabic_Category
	'Other',
	'Bindu',
	'Visarga',
	'Avagraha',
	'Nukta',
	'Virama',
	'Pure_Killer',
	'Invisible_Stacker',
	'Vowel_Independent',
	'Vowel_Dependent',
	'Vowel',
	'Consonant_Placeholder',
	'Consonant',
	'Consonant_Dead',
	'Consonant_With_Stacker',
	'Consonant_Prefixed',
	'Consonant_Preceding_Repha',
	'Consonant_Succeeding_Repha',
	'Consonant_Subjoined',
	'Consonant_Medial',
	'Consonant_Final',
	'Consonant_Head_Letter',
	'Consonant_Initial_Postfixed',
	'Modifying_Letter',
	'Tone_Letter',
	'Tone_Mark',
	'Gemination_Mark',
	'Cantillation_Mark',
	'Register_Shifter',
	'Syllable_Modifier',
	'Consonant_Killer',
	'Non_Joiner',
	'Joiner',
	'Number_Joiner',
	'Number',
	'Brahmi_Joining_Number',
	# Indic_Positional_Category
	'Not_Applicable',
	'Right',
	'Left',
	'Visual_Order_Left',
	'Left_And_Right',
	'Top',
	'Bottom',
	'Top_And_Bottom',
	'Top_And_Right',
	'Top_And_Left',
	'Top_And_Left_And_Right',
	'Bottom_And_Left',
	'Bottom_And_Right',
	'Top_And_Bottom_And_Right',
	'Overstruck',
]

class PropertyValue(object):
	def __init__(self, name_):
		self.name = name_
	def __str__(self):
		return self.name
	def __eq__(self, other):
		return self.name == (other if isinstance(other, str) else other.name)
	def __ne__(self, other):
		return not (self == other)
	def __hash__(self):
		return hash(str(self))

property_values = {}

for name in property_names:
	value = PropertyValue(name)
	assert value not in property_values
	assert value not in globals()
	property_values[name] = value
globals().update(property_values)

def is_BASE(U, UISC, UGC):
	return (UISC in [Number, Consonant, Consonant_Head_Letter,
			#SPEC-DRAFT Consonant_Placeholder,
			Tone_Letter,
			Vowel_Independent #SPEC-DRAFT
			] or
		(UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial,
					Consonant_Subjoined, Vowel, Vowel_Dependent]))
def is_BASE_IND(U, UISC, UGC):
	#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
	return (UISC in [Consonant_Dead, Modifying_Letter] or
		(UGC == Po and not U in [0x104B, 0x104E, 0x1B5B, 0x1B5C, 0x1B5F, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
		False # SPEC-DRAFT-OUTDATED! U == 0x002D
		)
def is_BASE_NUM(U, UISC, UGC):
	return UISC == Brahmi_Joining_Number
def is_BASE_OTHER(U, UISC, UGC):
	if UISC == Consonant_Placeholder: return True #SPEC-DRAFT
	#SPEC-DRAFT return U in [0x00A0, 0x00D7, 0x2015, 0x2022, 0x25CC, 0x25FB, 0x25FC, 0x25FD, 0x25FE]
	return U in [0x2015, 0x2022, 0x25FB, 0x25FC, 0x25FD, 0x25FE]
def is_CGJ(U, UISC, UGC):
	return U == 0x034F
def is_CONS_FINAL(U, UISC, UGC):
	return ((UISC == Consonant_Final and UGC != Lo) or
		UISC == Consonant_Succeeding_Repha)
def is_CONS_FINAL_MOD(U, UISC, UGC):
	#SPEC-DRAFT return  UISC in [Consonant_Final_Modifier, Syllable_Modifier]
	return  UISC == Syllable_Modifier
def is_CONS_MED(U, UISC, UGC):
	# Consonant_Initial_Postfixed is new in Unicode 11; not in the spec.
	return (UISC == Consonant_Medial and UGC != Lo or
		UISC == Consonant_Initial_Postfixed)
def is_CONS_MOD(U, UISC, UGC):
	return UISC in [Nukta, Gemination_Mark, Consonant_Killer]
def is_CONS_SUB(U, UISC, UGC):
	#SPEC-DRAFT return UISC == Consonant_Subjoined
	return UISC == Consonant_Subjoined and UGC != Lo
def is_CONS_WITH_STACKER(U, UISC, UGC):
	return UISC == Consonant_With_Stacker
def is_HALANT(U, UISC, UGC):
	return (UISC in [Virama, Invisible_Stacker]
		and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC)
		and not is_SAKOT(U, UISC, UGC))
def is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC):
	# https://github.com/harfbuzz/harfbuzz/issues/1102
	# https://github.com/harfbuzz/harfbuzz/issues/1379
	return U in [0x11046, 0x1134D]
def is_HALANT_NUM(U, UISC, UGC):
	return UISC == Number_Joiner
def is_ZWNJ(U, UISC, UGC):
	return UISC == Non_Joiner
def is_ZWJ(U, UISC, UGC):
	return UISC == Joiner
def is_Word_Joiner(U, UISC, UGC):
	return U == 0x2060
def is_OTHER(U, UISC, UGC):
	#SPEC-OUTDATED return UGC == Zs # or any other SCRIPT_COMMON characters
	return (UISC == Other
		and not is_SYM(U, UISC, UGC)
		and not is_SYM_MOD(U, UISC, UGC)
		and not is_CGJ(U, UISC, UGC)
		and not is_Word_Joiner(U, UISC, UGC)
		and not is_VARIATION_SELECTOR(U, UISC, UGC)
	)
def is_Reserved(U, UISC, UGC):
	return UGC == 'Cn'
def is_REPHA(U, UISC, UGC):
	return UISC in [Consonant_Preceding_Repha, Consonant_Prefixed]
def is_SAKOT(U, UISC, UGC):
	return U == 0x1A60
def is_SYM(U, UISC, UGC):
	if U == 0x25CC: return False #SPEC-DRAFT
	#SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter
	return UGC in [So, Sc] and U not in [0x1B62, 0x1B68]
def is_SYM_MOD(U, UISC, UGC):
	return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
def is_VARIATION_SELECTOR(U, UISC, UGC):
	return 0xFE00 <= U <= 0xFE0F
def is_VOWEL(U, UISC, UGC):
	# https://github.com/harfbuzz/harfbuzz/issues/376
	return (UISC == Pure_Killer or
		(UGC != Lo and UISC in [Vowel, Vowel_Dependent] and U not in [0xAA29]))
def is_VOWEL_MOD(U, UISC, UGC):
	# https://github.com/harfbuzz/harfbuzz/issues/376
	return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
		(UGC != Lo and (UISC == Bindu or U in [0xAA29])))

use_mapping = {
	'B':	is_BASE,
	'IND':	is_BASE_IND,
	'N':	is_BASE_NUM,
	'GB':	is_BASE_OTHER,
	'CGJ':	is_CGJ,
	'F':	is_CONS_FINAL,
	'FM':	is_CONS_FINAL_MOD,
	'M':	is_CONS_MED,
	'CM':	is_CONS_MOD,
	'SUB':	is_CONS_SUB,
	'CS':	is_CONS_WITH_STACKER,
	'H':	is_HALANT,
	'HVM':	is_HALANT_OR_VOWEL_MODIFIER,
	'HN':	is_HALANT_NUM,
	'ZWNJ':	is_ZWNJ,
	'ZWJ':	is_ZWJ,
	'WJ':	is_Word_Joiner,
	'O':	is_OTHER,
	'Rsv':	is_Reserved,
	'R':	is_REPHA,
	'S':	is_SYM,
	'Sk':	is_SAKOT,
	'SM':	is_SYM_MOD,
	'VS':	is_VARIATION_SELECTOR,
	'V':	is_VOWEL,
	'VM':	is_VOWEL_MOD,
}

use_positions = {
	'F': {
		'Abv': [Top],
		'Blw': [Bottom],
		'Pst': [Right],
	},
	'M': {
		'Abv': [Top],
		'Blw': [Bottom, Bottom_And_Left],
		'Pst': [Right],
		'Pre': [Left],
	},
	'CM': {
		'Abv': [Top],
		'Blw': [Bottom],
	},
	'V': {
		'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right],
		'Blw': [Bottom, Overstruck, Bottom_And_Right],
		'Pst': [Right, Top_And_Left, Top_And_Left_And_Right, Left_And_Right],
		'Pre': [Left],
	},
	'VM': {
		'Abv': [Top],
		'Blw': [Bottom, Overstruck],
		'Pst': [Right],
		'Pre': [Left],
	},
	'SM': {
		'Abv': [Top],
		'Blw': [Bottom],
	},
	'H': None,
	'HVM': None,
	'B': None,
	'FM': {
		'Abv': [Top],
		'Blw': [Bottom],
		'Pst': [Not_Applicable],
	},
	'SUB': None,
}

def map_to_use(data):
	out = {}
	items = use_mapping.items()
	for U,(UISC,UIPC,UGC,UBlock) in data.items():

		# Resolve Indic_Syllabic_Category

		# TODO: These don't have UISC assigned in Unicode 12.0, but have UIPC
		if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark

		# Tibetan:
		# TODO: These don't have UISC assigned in Unicode 12.0, but have UIPC
		if 0x0F18 <= U <= 0x0F19 or 0x0F3E <= U <= 0x0F3F: UISC = Vowel_Dependent
		if 0x0F86 <= U <= 0x0F87: UISC = Tone_Mark
		# Overrides to allow NFC order matching syllable
		# https://github.com/harfbuzz/harfbuzz/issues/1012
		if UBlock == 'Tibetan' and is_VOWEL (U, UISC, UGC):
			if UIPC == Top:
				UIPC = Bottom

		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
		# also  https://github.com/harfbuzz/harfbuzz/issues/1012
		if UBlock == 'Chakma' and is_VOWEL (U, UISC, UGC):
			if UIPC == Top:
				UIPC = Bottom
			elif UIPC == Bottom:
				UIPC = Top

		# TODO: https://github.com/harfbuzz/harfbuzz/pull/627
		if 0x1BF2 <= U <= 0x1BF3: UISC = Nukta; UIPC = Bottom

		# TODO: U+1CED should only be allowed after some of
		# the nasalization marks, maybe only for U+1CE9..U+1CF1.
		if U == 0x1CED: UISC = Tone_Mark

		# TODO: https://github.com/harfbuzz/harfbuzz/issues/1105
		if U == 0x11134: UISC = Gemination_Mark

		values = [k for k,v in items if v(U,UISC,UGC)]
		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
		USE = values[0]

		# Resolve Indic_Positional_Category

		# TODO: These should die, but have UIPC in Unicode 12.0
		if U in [0x953, 0x954]: UIPC = Not_Applicable

		# TODO: In USE's override list but not in Unicode 12.0
		if U == 0x103C: UIPC = Left

		# TODO: https://github.com/harfbuzz/harfbuzz/pull/2012
		if U == 0x1C29: UIPC = Left

		# TODO: These are not in USE's override list that we have, nor are they in Unicode 12.0
		if 0xA926 <= U <= 0xA92A: UIPC = Top
		# TODO: https://github.com/harfbuzz/harfbuzz/pull/1037
		#  and https://github.com/harfbuzz/harfbuzz/issues/1631
		if U in [0x11302, 0x11303, 0x114C1]: UIPC = Top
		if U == 0x1171E: UIPC = Left
		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top

		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)

		pos_mapping = use_positions.get(USE, None)
		if pos_mapping:
			values = [k for k,v in pos_mapping.items() if v and UIPC in v]
			assert len(values) == 1, "%s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC, values)
			USE = USE + values[0]

		out[U] = (USE, UBlock)
	return out

defaults = ('O', 'No_Block')
data = map_to_use(data)

print ("/* == Start of generated table == */")
print ("/*")
print (" * The following table is generated by running:")
print (" *")
print (" *   ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt")
print (" *")
print (" * on files with these headers:")
print (" *")
for h in headers:
	for l in h:
		print (" * %s" % (l.strip()))
print (" */")
print ()
print ('#include "hb.hh"')
print ()
print ('#ifndef HB_NO_OT_SHAPE')
print ()
print ('#include "hb-ot-shape-complex-use.hh"')
print ()

total = 0
used = 0
last_block = None
def print_block (block, start, end, data):
	global total, used, last_block
	if block and block != last_block:
		print ()
		print ()
		print ("  /* %s */" % block)
		if start % 16:
			print (' ' * (20 + (start % 16 * 6)), end='')
	num = 0
	assert start % 8 == 0
	assert (end+1) % 8 == 0
	for u in range (start, end+1):
		if u % 16 == 0:
			print ()
			print ("  /* %04X */" % u, end='')
		if u in data:
			num += 1
		d = data.get (u, defaults)
		print ("%6s," % d[0], end='')

	total += end - start + 1
	used += num
	if block:
		last_block = block

uu = sorted (data.keys ())

last = -100000
num = 0
offset = 0
starts = []
ends = []
print ('#pragma GCC diagnostic push')
print ('#pragma GCC diagnostic ignored "-Wunused-macros"')
for k,v in sorted(use_mapping.items()):
	if k in use_positions and use_positions[k]: continue
	print ("#define %s	USE_%s	/* %s */" % (k, k, v.__name__[3:]))
for k,v in sorted(use_positions.items()):
	if not v: continue
	for suf in v.keys():
		tag = k + suf
		print ("#define %s	USE_%s" % (tag, tag))
print ('#pragma GCC diagnostic pop')
print ("")
print ("static const USE_TABLE_ELEMENT_TYPE use_table[] = {")
for u in uu:
	if u <= last:
		continue
	block = data[u][1]

	start = u//8*8
	end = start+1
	while end in uu and block == data[end][1]:
		end += 1
	end = (end-1)//8*8 + 7

	if start != last + 1:
		if start - last <= 1+16*3:
			print_block (None, last+1, start-1, data)
			last = start-1
		else:
			if last >= 0:
				ends.append (last + 1)
				offset += ends[-1] - starts[-1]
			print ()
			print ()
			print ("#define use_offset_0x%04xu %d" % (start, offset))
			starts.append (start)

	print_block (block, start, end, data)
	last = end
ends.append (last + 1)
offset += ends[-1] - starts[-1]
print ()
print ()
occupancy = used * 100. / total
page_bits = 12
print ("}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy))
print ()
print ("USE_TABLE_ELEMENT_TYPE")
print ("hb_use_get_category (hb_codepoint_t u)")
print ("{")
print ("  switch (u >> %d)" % page_bits)
print ("  {")
pages = set([u>>page_bits for u in starts+ends])
for p in sorted(pages):
	print ("    case 0x%0Xu:" % p)
	for (start,end) in zip (starts, ends):
		if p not in [start>>page_bits, end>>page_bits]: continue
		offset = "use_offset_0x%04xu" % start
		print ("      if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset))
	print ("      break;")
	print ("")
print ("    default:")
print ("      break;")
print ("  }")
print ("  return USE_O;")
print ("}")
print ()
for k in sorted(use_mapping.keys()):
	if k in use_positions and use_positions[k]: continue
	print ("#undef %s" % k)
for k,v in sorted(use_positions.items()):
	if not v: continue
	for suf in v.keys():
		tag = k + suf
		print ("#undef %s" % tag)
print ()
print ()
print ('#endif')
print ("/* == End of generated table == */")

# Maintain at least 50% occupancy in the table */
if occupancy < 50:
	raise Exception ("Table too sparse, please investigate: ", occupancy)


text:
/*
 * Copyright © 2018 Adobe Inc.
 *
 *  This is part of HarfBuzz, a text shaping 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.
 *
 * Adobe Author(s): Michiharu Ariza
 */
#ifndef HB_OT_CFF_COMMON_HH
#define HB_OT_CFF_COMMON_HH

#include "hb-open-type.hh"
#include "hb-bimap.hh"
#include "hb-ot-layout-common.hh"
#include "hb-cff-interp-dict-common.hh"
#include "hb-subset-plan.hh"

namespace CFF {

using namespace OT;

#define CFF_UNDEF_CODE  0xFFFFFFFF

using objidx_t = hb_serialize_context_t::objidx_t;
using whence_t = hb_serialize_context_t::whence_t;

/* utility macro */
template<typename Type>
static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset)
{ return offset ? StructAtOffset<Type> (P, offset) : Null (Type); }

inline unsigned int calcOffSize (unsigned int dataSize)
{
  unsigned int size = 1;
  unsigned int offset = dataSize + 1;
  while (offset & ~0xFF)
  {
    size++;
    offset >>= 8;
  }
  /* format does not support size > 4; caller should handle it as an error */
  return size;
}

struct code_pair_t
{
  hb_codepoint_t code;
  hb_codepoint_t glyph;
};

typedef hb_vector_t<unsigned char> str_buff_t;
struct str_buff_vec_t : hb_vector_t<str_buff_t>
{
  void fini () { SUPER::fini_deep (); }

  unsigned int total_size () const
  {
    unsigned int size = 0;
    for (unsigned int i = 0; i < length; i++)
      size += (*this)[i].length;
    return size;
  }

  private:
  typedef hb_vector_t<str_buff_t> SUPER;
};

/* CFF INDEX */
template <typename COUNT>
struct CFFIndex
{
  static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
  { return offSize * (count + 1); }

  unsigned int offset_array_size () const
  { return calculate_offset_array_size (offSize, count); }

  CFFIndex *copy (hb_serialize_context_t *c) const
  {
    TRACE_SERIALIZE (this);
    unsigned int size = get_size ();
    CFFIndex *out = c->allocate_size<CFFIndex> (size);
    if (likely (out))
      memcpy (out, this, size);
    return_trace (out);
  }

  bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
  {
    TRACE_SERIALIZE (this);
    unsigned int size = src.get_size ();
    CFFIndex *dest = c->allocate_size<CFFIndex> (size);
    if (unlikely (!dest)) return_trace (false);
    memcpy (dest, &src, size);
    return_trace (true);
  }

  bool serialize (hb_serialize_context_t *c,
		  unsigned int offSize_,
		  const byte_str_array_t &byteArray)
  {
    TRACE_SERIALIZE (this);
    if (byteArray.length == 0)
    {
      COUNT *dest = c->allocate_min<COUNT> ();
      if (unlikely (!dest)) return_trace (false);
      *dest = 0;
    }
    else
    {
      /* serialize CFFIndex header */
      if (unlikely (!c->extend_min (*this))) return_trace (false);
      this->count = byteArray.length;
      this->offSize = offSize_;
      if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1))))
	return_trace (false);

      /* serialize indices */
      unsigned int  offset = 1;
      unsigned int  i = 0;
      for (; i < byteArray.length; i++)
      {
	set_offset_at (i, offset);
	offset += byteArray[i].get_size ();
      }
      set_offset_at (i, offset);

      /* serialize data */
      for (unsigned int i = 0; i < byteArray.length; i++)
      {
	const byte_str_t &bs = byteArray[i];
	unsigned char *dest = c->allocate_size<unsigned char> (bs.length);
	if (unlikely (!dest)) return_trace (false);
	memcpy (dest, &bs[0], bs.length);
      }
    }
    return_trace (true);
  }

  bool serialize (hb_serialize_context_t *c,
		  unsigned int offSize_,
		  const str_buff_vec_t &buffArray)
  {
    byte_str_array_t  byteArray;
    byteArray.init ();
    byteArray.resize (buffArray.length);
    for (unsigned int i = 0; i < byteArray.length; i++)
      byteArray[i] = byte_str_t (buffArray[i].arrayZ, buffArray[i].length);
    bool result = this->serialize (c, offSize_, byteArray);
    byteArray.fini ();
    return result;
  }

  template <typename Iterator,
	    hb_requires (hb_is_iterator (Iterator))>
  bool serialize (hb_serialize_context_t *c,
		  Iterator it)
  {
    TRACE_SERIALIZE (this);
    if (it.len () == 0)
    {
      COUNT *dest = c->allocate_min<COUNT> ();
      if (unlikely (!dest)) return_trace (false);
      *dest = 0;
    }
    else
    {
      serialize_header(c, + it | hb_map ([] (const byte_str_t &_) { return _.length; }));
      for (const byte_str_t &_ : +it)
	_.copy (c);
    }
    return_trace (true);
  }

  bool serialize (hb_serialize_context_t *c,
		  const byte_str_array_t &byteArray)
  { return serialize (c, + hb_iter (byteArray)); }

  bool serialize (hb_serialize_context_t *c,
		  const str_buff_vec_t &buffArray)
  {
    auto it =
    + hb_iter (buffArray)
    | hb_map ([] (const str_buff_t &_) { return byte_str_t (_.arrayZ, _.length); })
    ;
    return serialize (c, it);
  }

  template <typename Iterator,
	    hb_requires (hb_is_iterator (Iterator))>
  bool serialize_header (hb_serialize_context_t *c,
			Iterator it)
  {
    TRACE_SERIALIZE (this);

    unsigned total = + it | hb_reduce (hb_add, 0);
    unsigned off_size = calcOffSize (total);

    /* serialize CFFIndex header */
    if (unlikely (!c->extend_min (*this))) return_trace (false);
    this->count = it.len ();
    this->offSize = off_size;
    if (unlikely (!c->allocate_size<HBUINT8> (off_size * (it.len () + 1))))
      return_trace (false);

    /* serialize indices */
    unsigned int offset = 1;
    unsigned int i = 0;
    for (unsigned _ : +it)
    {
      CFFIndex<COUNT>::set_offset_at (i++, offset);
      offset += _;
    }
    CFFIndex<COUNT>::set_offset_at (i, offset);

    return_trace (true);
  }

  void set_offset_at (unsigned int index, unsigned int offset)
  {
    HBUINT8 *p = offsets + offSize * index + offSize;
    unsigned int size = offSize;
    for (; size; size--)
    {
      --p;
      *p = offset & 0xFF;
      offset >>= 8;
    }
  }

  unsigned int offset_at (unsigned int index) const
  {
    assert (index <= count);
    const HBUINT8 *p = offsets + offSize * index;
    unsigned int size = offSize;
    unsigned int offset = 0;
    for (; size; size--)
      offset = (offset << 8) + *p++;
    return offset;
  }

  unsigned int length_at (unsigned int index) const
  {
    if (unlikely ((offset_at (index + 1) < offset_at (index)) ||
		  (offset_at (index + 1) > offset_at (count))))
      return 0;
    return offset_at (index + 1) - offset_at (index);
  }

  const unsigned char *data_base () const
  { return (const unsigned char *) this + min_size + offset_array_size (); }

  unsigned int data_size () const { return HBINT8::static_size; }

  byte_str_t operator [] (unsigned int index) const
  {
    if (unlikely (index >= count)) return Null (byte_str_t);
    return byte_str_t (data_base () + offset_at (index) - 1, length_at (index));
  }

  unsigned int get_size () const
  {
    if (this == &Null (CFFIndex)) return 0;
    if (count > 0)
      return min_size + offset_array_size () + (offset_at (count) - 1);
    return count.static_size;  /* empty CFFIndex contains count only */
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (likely ((c->check_struct (this) && count == 0) || /* empty INDEX */
			  (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
			   c->check_array (offsets, offSize, count + 1) &&
			   c->check_array ((const HBUINT8*) data_base (), 1, max_offset () - 1))));
  }

  protected:
  unsigned int max_offset () const
  {
    unsigned int max = 0;
    for (unsigned int i = 0; i < count + 1u; i++)
    {
      unsigned int off = offset_at (i);
      if (off > max) max = off;
    }
    return max;
  }

  public:
  COUNT		count;		/* Number of object data. Note there are (count+1) offsets */
  HBUINT8	offSize;	/* The byte size of each offset in the offsets array. */
  HBUINT8	offsets[HB_VAR_ARRAY];
				/* The array of (count + 1) offsets into objects array (1-base). */
  /* HBUINT8 data[HB_VAR_ARRAY];	Object data */
  public:
  DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
};

template <typename COUNT, typename TYPE>
struct CFFIndexOf : CFFIndex<COUNT>
{
  const byte_str_t operator [] (unsigned int index) const
  {
    if (likely (index < CFFIndex<COUNT>::count))
      return byte_str_t (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
    return Null (byte_str_t);
  }

  template <typename DATA, typename PARAM1, typename PARAM2>
  bool serialize (hb_serialize_context_t *c,
		  unsigned int offSize_,
		  const DATA *dataArray,
		  unsigned int dataArrayLen,
		  const hb_vector_t<unsigned int> &dataSizeArray,
		  const PARAM1 &param1,
		  const PARAM2 &param2)
  {
    TRACE_SERIALIZE (this);
    /* serialize CFFIndex header */
    if (unlikely (!c->extend_min (*this))) return_trace (false);
    this->count = dataArrayLen;
    this->offSize = offSize_;
    if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1))))
      return_trace (false);

    /* serialize indices */
    unsigned int  offset = 1;
    unsigned int  i = 0;
    for (; i < dataArrayLen; i++)
    {
      CFFIndex<COUNT>::set_offset_at (i, offset);
      offset += dataSizeArray[i];
    }
    CFFIndex<COUNT>::set_offset_at (i, offset);

    /* serialize data */
    for (unsigned int i = 0; i < dataArrayLen; i++)
    {
      TYPE *dest = c->start_embed<TYPE> ();
      if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2)))
	return_trace (false);
    }
    return_trace (true);
  }
};

/* Top Dict, Font Dict, Private Dict */
struct Dict : UnsizedByteStr
{
  template <typename DICTVAL, typename OP_SERIALIZER, typename ...Ts>
  bool serialize (hb_serialize_context_t *c,
		  const DICTVAL &dictval,
		  OP_SERIALIZER& opszr,
		  Ts&&... ds)
  {
    TRACE_SERIALIZE (this);
    for (unsigned int i = 0; i < dictval.get_count (); i++)
      if (unlikely (!opszr.serialize (c, dictval[i], hb_forward<Ts> (ds)...)))
	return_trace (false);

    return_trace (true);
  }

  template <typename T, typename V>
  static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp)
  {
    // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
    if (/*unlikely*/ (!serialize_int<T, V> (c, intOp, value)))
      return false;

    TRACE_SERIALIZE (this);
    /* serialize the opcode */
    HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op));
    if (unlikely (!p)) return_trace (false);
    if (Is_OpCode_ESC (op))
    {
      *p = OpCode_escape;
      op = Unmake_OpCode_ESC (op);
      p++;
    }
    *p = op;
    return_trace (true);
  }

  template <typename V>
  static bool serialize_int4_op (hb_serialize_context_t *c, op_code_t op, V value)
  { return serialize_int_op<HBINT32> (c, op, value, OpCode_longintdict); }

  template <typename V>
  static bool serialize_int2_op (hb_serialize_context_t *c, op_code_t op, V value)
  { return serialize_int_op<HBINT16> (c, op, value, OpCode_shortint); }

  template <typename T, int int_op>
  static bool serialize_link_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence)
  {
    T &ofs = *(T *) (c->head + OpCode_Size (int_op));
    if (unlikely (!serialize_int_op<T> (c, op, 0, int_op))) return false;
    c->add_link (ofs, link, whence);
    return true;
  }

  static bool serialize_link4_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head)
  { return serialize_link_op<HBINT32, OpCode_longintdict> (c, op, link, whence); }

  static bool serialize_link2_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head)
  { return serialize_link_op<HBINT16, OpCode_shortint> (c, op, link, whence); }
};

struct TopDict : Dict {};
struct FontDict : Dict {};
struct PrivateDict : Dict {};

struct table_info_t
{
  void init () { offset = size = 0; link = 0; }

  unsigned int    offset;
  unsigned int    size;
  objidx_t	  link;
};

template <typename COUNT>
struct FDArray : CFFIndexOf<COUNT, FontDict>
{
  template <typename DICTVAL, typename INFO, typename Iterator, typename OP_SERIALIZER>
  bool serialize (hb_serialize_context_t *c,
		  Iterator it,
		  OP_SERIALIZER& opszr)
  {
    TRACE_SERIALIZE (this);

    /* serialize INDEX data */
    hb_vector_t<unsigned> sizes;
    c->push ();
    + it
    | hb_map ([&] (const hb_pair_t<const DICTVAL&, const INFO&> &_)
    {
      FontDict *dict = c->start_embed<FontDict> ();
		dict->serialize (c, _.first, opszr, _.second);
		return c->head - (const char*)dict;
	      })
    | hb_sink (sizes)
    ;
    c->pop_pack (false);

    /* serialize INDEX header */
    return_trace (CFFIndex<COUNT>::serialize_header (c, hb_iter (sizes)));
  }
};

/* FDSelect */
struct FDSelect0 {
  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
  {
    TRACE_SANITIZE (this);
    if (unlikely (!(c->check_struct (this))))
      return_trace (false);
    for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
      if (unlikely (!fds[i].sanitize (c)))
	return_trace (false);

    return_trace (true);
  }

  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
  { return (hb_codepoint_t) fds[glyph]; }

  unsigned int get_size (unsigned int num_glyphs) const
  { return HBUINT8::static_size * num_glyphs; }

  HBUINT8     fds[HB_VAR_ARRAY];

  DEFINE_SIZE_MIN (0);
};

template <typename GID_TYPE, typename FD_TYPE>
struct FDSelect3_4_Range
{
  bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const
  {
    TRACE_SANITIZE (this);
    return_trace (first < c->get_num_glyphs () && (fd < fdcount));
  }

  GID_TYPE    first;
  FD_TYPE     fd;
  public:
  DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
};

template <typename GID_TYPE, typename FD_TYPE>
struct FDSelect3_4
{
  unsigned int get_size () const
  { return GID_TYPE::static_size * 2 + ranges.get_size (); }

  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
  {
    TRACE_SANITIZE (this);
    if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) ||
		  (nRanges () == 0) || ranges[0].first != 0))
      return_trace (false);

    for (unsigned int i = 1; i < nRanges (); i++)
      if (unlikely (ranges[i - 1].first >= ranges[i].first))
	return_trace (false);

    if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
      return_trace (false);

    return_trace (true);
  }

  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
  {
    unsigned int i;
    for (i = 1; i < nRanges (); i++)
      if (glyph < ranges[i].first)
	break;

    return (hb_codepoint_t) ranges[i - 1].fd;
  }

  GID_TYPE        &nRanges ()       { return ranges.len; }
  GID_TYPE         nRanges () const { return ranges.len; }
  GID_TYPE       &sentinel ()       { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
  const GID_TYPE &sentinel () const { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }

  ArrayOf<FDSelect3_4_Range<GID_TYPE, FD_TYPE>, GID_TYPE> ranges;
  /* GID_TYPE sentinel */

  DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges);
};

typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;

struct FDSelect
{
  bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
  {
    TRACE_SERIALIZE (this);
    unsigned int size = src.get_size (num_glyphs);
    FDSelect *dest = c->allocate_size<FDSelect> (size);
    if (unlikely (!dest)) return_trace (false);
    memcpy (dest, &src, size);
    return_trace (true);
  }

  unsigned int get_size (unsigned int num_glyphs) const
  {
    switch (format)
    {
    case 0: return format.static_size + u.format0.get_size (num_glyphs);
    case 3: return format.static_size + u.format3.get_size ();
    default:return 0;
    }
  }

  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
  {
    if (this == &Null (FDSelect)) return 0;

    switch (format)
    {
    case 0: return u.format0.get_fd (glyph);
    case 3: return u.format3.get_fd (glyph);
    default:return 0;
    }
  }

  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
  {
    TRACE_SANITIZE (this);
    if (unlikely (!c->check_struct (this)))
      return_trace (false);

    switch (format)
    {
    case 0: return_trace (u.format0.sanitize (c, fdcount));
    case 3: return_trace (u.format3.sanitize (c, fdcount));
    default:return_trace (false);
    }
  }

  HBUINT8	format;
  union {
  FDSelect0	format0;
  FDSelect3	format3;
  } u;
  public:
  DEFINE_SIZE_MIN (1);
};

template <typename COUNT>
struct Subrs : CFFIndex<COUNT>
{
  typedef COUNT count_type;
  typedef CFFIndex<COUNT> SUPER;
};

} /* namespace CFF */

#endif /* HB_OT_CFF_COMMON_HH */


text:
/*
 * Copyright © 2019  Facebook, Inc.
 *
 *  This is part of HarfBuzz, a text shaping 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.
 *
 * Facebook Author(s): Behdad Esfahbod
 */

#include "hb.hh"
#include "hb-meta.hh"

#include <type_traits>

template <typename T> struct U { typedef T type; };

int
main (int argc, char **argv)
{
  static_assert (hb_is_convertible (void, void), "");
  static_assert (hb_is_convertible (void, const void), "");
  static_assert (hb_is_convertible (const void, void), "");

  static_assert (hb_is_convertible (int,  int), "");
  static_assert (hb_is_convertible (char, int), "");
  static_assert (hb_is_convertible (long, int), "");

  static_assert (hb_is_convertible (int, int), "");

  static_assert (hb_is_convertible (const int, int), "");
  static_assert (hb_is_convertible (int, const int), "");
  static_assert (hb_is_convertible (const int, const int), "");

  static_assert (hb_is_convertible (int&, int), "");
  static_assert (!hb_is_convertible (int, int&), "");

  static_assert (hb_is_convertible (int, const int&), "");
  static_assert (!hb_is_convertible (const int, int&), "");
  static_assert (hb_is_convertible (const int, const int&), "");
  static_assert (hb_is_convertible (int&, const int), "");
  static_assert (hb_is_convertible (const int&, int), "");
  static_assert (hb_is_convertible (const int&, const int), "");
  static_assert (hb_is_convertible (const int&, const int), "");

  struct X {};
  struct Y : X {};

  static_assert (hb_is_convertible (const X &, const X), "");
  static_assert (hb_is_convertible (X &, const X), "");
  static_assert (hb_is_convertible (X &, const X &), "");
  static_assert (hb_is_convertible (X, const X &), "");
  static_assert (hb_is_convertible (const X, const X &), "");
  static_assert (!hb_is_convertible (const X, X &), "");
  static_assert (!hb_is_convertible (X, X &), "");
  static_assert (hb_is_convertible (X &, X &), "");

  static_assert (hb_is_convertible (int&, long), "");
  static_assert (!hb_is_convertible (int&, long&), "");

  static_assert (hb_is_convertible (int *, int *), "");
  static_assert (hb_is_convertible (int *, const int *), "");
  static_assert (!hb_is_convertible (const int *, int *), "");
  static_assert (!hb_is_convertible (int *, long *), "");
  static_assert (hb_is_convertible (int *, void *), "");
  static_assert (!hb_is_convertible (void *, int *), "");

  static_assert (hb_is_base_of (void, void), "");
  static_assert (hb_is_base_of (void, int), "");
  static_assert (!hb_is_base_of (int, void), "");

  static_assert (hb_is_base_of (int, int), "");
  static_assert (hb_is_base_of (const int, int), "");
  static_assert (hb_is_base_of (int, const int), "");

  static_assert (hb_is_base_of (X, X), "");
  static_assert (hb_is_base_of (X, Y), "");
  static_assert (hb_is_base_of (const X, Y), "");
  static_assert (hb_is_base_of (X, const Y), "");
  static_assert (!hb_is_base_of (Y, X), "");

  static_assert (hb_is_constructible (int), "");
  static_assert (hb_is_constructible (int, int), "");
  static_assert (hb_is_constructible (int, char), "");
  static_assert (hb_is_constructible (int, long), "");
  static_assert (!hb_is_constructible (int, X), "");
  static_assert (!hb_is_constructible (int, int, int), "");
  static_assert (hb_is_constructible (X), "");
  static_assert (!hb_is_constructible (X, int), "");
  static_assert (hb_is_constructible (X, X), "");
  static_assert (!hb_is_constructible (X, X, X), "");
  static_assert (hb_is_constructible (X, Y), "");
  static_assert (!hb_is_constructible (Y, X), "");

  static_assert (hb_is_trivially_default_constructible (X), "");
  static_assert (hb_is_trivially_default_constructible (Y), "");
  static_assert (hb_is_trivially_copy_constructible (X), "");
  static_assert (hb_is_trivially_copy_constructible (Y), "");
  static_assert (hb_is_trivially_move_constructible (X), "");
  static_assert (hb_is_trivially_move_constructible (Y), "");
  static_assert (hb_is_trivially_destructible (Y), "");

  static_assert (hb_is_trivially_copyable (int), "");
  static_assert (hb_is_trivially_copyable (X), "");
  static_assert (hb_is_trivially_copyable (Y), "");

  static_assert (hb_is_trivial (int), "");
  static_assert (hb_is_trivial (X), "");
  static_assert (hb_is_trivial (Y), "");

  static_assert (hb_is_signed (hb_unwrap_type (U<U<U<int>>>)), "");
  static_assert (hb_is_unsigned (hb_unwrap_type (U<U<U<U<unsigned>>>>)), "");

  /* TODO Add more meaningful tests. */

  return 0;
}


text:
/*
 * Copyright (C) 2011  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping 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.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_GOBJECT_H_IN
#error "Include <hb-gobject.h> instead."
#endif

#ifndef HB_GOBJECT_STRUCTS_H
#define HB_GOBJECT_STRUCTS_H

#include "hb.h"

#include <glib-object.h>

HB_BEGIN_DECLS

/* Object types */

/**
 * hb_gobject_blob_get_type:
 *
 * Since: 0.9.2
 **/
HB_EXTERN GType
hb_gobject_blob_get_type (void);
#define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ())

/**
 * hb_gobject_buffer_get_type:
 *
 * Since: 0.9.2
 **/
HB_EXTERN GType
hb_gobject_buffer_get_type (void);
#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ())

/**
 * hb_gobject_face_get_type:
 *
 * Since: 0.9.2
 **/
HB_EXTERN GType
hb_gobject_face_get_type (void);
#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ())

/**
 * hb_gobject_font_get_type:
 *
 * Since: 0.9.2
 **/
HB_EXTERN GType
hb_gobject_font_get_type (void);
#define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ())

/**
 * hb_gobject_font_funcs_get_type:
 *
 * Since: 0.9.2
 **/
HB_EXTERN GType
hb_gobject_font_funcs_get_type (void);
#define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ())

HB_EXTERN GType
hb_gobject_set_get_type (void);
#define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ())

HB_EXTERN GType
hb_gobject_map_get_type (void);
#define HB_GOBJECT_TYPE_MAP (hb_gobject_map_get_type ())

HB_EXTERN GType
hb_gobject_shape_plan_get_type (void);
#define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ())

/**
 * hb_gobject_unicode_funcs_get_type:
 *
 * Since: 0.9.2
 **/
HB_EXTERN GType
hb_gobject_unicode_funcs_get_type (void);
#define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ())

/* Value types */

HB_EXTERN GType
hb_gobject_feature_get_type (void);
#define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ())

HB_EXTERN GType
hb_gobject_glyph_info_get_type (void);
#define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ())

HB_EXTERN GType
hb_gobject_glyph_position_get_type (void);
#define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ())

HB_EXTERN GType
hb_gobject_segment_properties_get_type (void);
#define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ())

HB_EXTERN GType
hb_gobject_user_data_key_get_type (void);
#define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ())

HB_EXTERN GType
hb_gobject_ot_math_glyph_variant_get_type (void);
#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT (hb_gobject_ot_math_glyph_variant_get_type ())

HB_EXTERN GType
hb_gobject_ot_math_glyph_part_get_type (void);
#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART (hb_gobject_ot_math_glyph_part_get_type ())

HB_END_DECLS

#endif /* HB_GOBJECT_H */


text:
/*
 * Copyright © 2011,2012,2013  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping 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.
 *
 * Google Author(s): Behdad Esfahbod
 */

#include "hb.hh"

#ifdef HAVE_UNISCRIBE

#ifdef HB_NO_OT_TAG
#error "Cannot compile 'uniscribe' shaper with HB_NO_OT_TAG."
#endif

#include "hb-shaper-impl.hh"

#include <windows.h>
#include <usp10.h>
#include <rpc.h>

#ifndef E_NOT_SUFFICIENT_BUFFER
#define E_NOT_SUFFICIENT_BUFFER HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)
#endif

#include "hb-uniscribe.h"

#include "hb-open-file.hh"
#include "hb-ot-name-table.hh"
#include "hb-ot-layout.h"

/**
 * SECTION:hb-uniscribe
 * @title: hb-uniscribe
 * @short_description: Windows integration
 * @include: hb-uniscribe.h
 *
 * Functions for using HarfBuzz with Windows fonts.
 **/

typedef HRESULT (WINAPI *SIOT) /*ScriptItemizeOpenType*/(
  const WCHAR *pwcInChars,
  int cInChars,
  int cMaxItems,
  const SCRIPT_CONTROL *psControl,
  const SCRIPT_STATE *psState,
  SCRIPT_ITEM *pItems,
  OPENTYPE_TAG *pScriptTags,
  int *pcItems
);

typedef HRESULT (WINAPI *SSOT) /*ScriptShapeOpenType*/(
  HDC hdc,
  SCRIPT_CACHE *psc,
  SCRIPT_ANALYSIS *psa,
  OPENTYPE_TAG tagScript,
  OPENTYPE_TAG tagLangSys,
  int *rcRangeChars,
  TEXTRANGE_PROPERTIES **rpRangeProperties,
  int cRanges,
  const WCHAR *pwcChars,
  int cChars,
  int cMaxGlyphs,
  WORD *pwLogClust,
  SCRIPT_CHARPROP *pCharProps,
  WORD *pwOutGlyphs,
  SCRIPT_GLYPHPROP *pOutGlyphProps,
  int *pcGlyphs
);

typedef HRESULT (WINAPI *SPOT) /*ScriptPlaceOpenType*/(
  HDC hdc,
  SCRIPT_CACHE *psc,
  SCRIPT_ANALYSIS *psa,
  OPENTYPE_TAG tagScript,
  OPENTYPE_TAG tagLangSys,
  int *rcRangeChars,
  TEXTRANGE_PROPERTIES **rpRangeProperties,
  int cRanges,
  const WCHAR *pwcChars,
  WORD *pwLogClust,
  SCRIPT_CHARPROP *pCharProps,
  int cChars,
  const WORD *pwGlyphs,
  const SCRIPT_GLYPHPROP *pGlyphProps,
  int cGlyphs,
  int *piAdvance,
  GOFFSET *pGoffset,
  ABC *pABC
);

/* Fallback implementations. */

static HRESULT WINAPI
hb_ScriptItemizeOpenType(
  const WCHAR *pwcInChars,
  int cInChars,
  int cMaxItems,
  const SCRIPT_CONTROL *psControl,
  const SCRIPT_STATE *psState,
  SCRIPT_ITEM *pItems,
  OPENTYPE_TAG *pScriptTags,
  int *pcItems
)
{
{
  return ScriptItemize (pwcInChars,
			cInChars,
			cMaxItems,
			psControl,
			psState,
			pItems,
			pcItems);
}
}

static HRESULT WINAPI
hb_ScriptShapeOpenType(
  HDC hdc,
  SCRIPT_CACHE *psc,
  SCRIPT_ANALYSIS *psa,
  OPENTYPE_TAG tagScript,
  OPENTYPE_TAG tagLangSys,
  int *rcRangeChars,
  TEXTRANGE_PROPERTIES **rpRangeProperties,
  int cRanges,
  const WCHAR *pwcChars,
  int cChars,
  int cMaxGlyphs,
  WORD *pwLogClust,
  SCRIPT_CHARPROP *pCharProps,
  WORD *pwOutGlyphs,
  SCRIPT_GLYPHPROP *pOutGlyphProps,
  int *pcGlyphs
)
{
  SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pOutGlyphProps;
  return ScriptShape (hdc,
		      psc,
		      pwcChars,
		      cChars,
		      cMaxGlyphs,
		      psa,
		      pwOutGlyphs,
		      pwLogClust,
		      psva,
		      pcGlyphs);
}

static HRESULT WINAPI
hb_ScriptPlaceOpenType(
  HDC hdc,
  SCRIPT_CACHE *psc,
  SCRIPT_ANALYSIS *psa,
  OPENTYPE_TAG tagScript,
  OPENTYPE_TAG tagLangSys,
  int *rcRangeChars,
  TEXTRANGE_PROPERTIES **rpRangeProperties,
  int cRanges,
  const WCHAR *pwcChars,
  WORD *pwLogClust,
  SCRIPT_CHARPROP *pCharProps,
  int cChars,
  const WORD *pwGlyphs,
  const SCRIPT_GLYPHPROP *pGlyphProps,
  int cGlyphs,
  int *piAdvance,
  GOFFSET *pGoffset,
  ABC *pABC
)
{
  SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pGlyphProps;
  return ScriptPlace (hdc,
		      psc,
		      pwGlyphs,
		      cGlyphs,
		      psva,
		      psa,
		      piAdvance,
		      pGoffset,
		      pABC);
}

struct hb_uniscribe_shaper_funcs_t
{
  SIOT ScriptItemizeOpenType;
  SSOT ScriptShapeOpenType;
  SPOT ScriptPlaceOpenType;

  void init ()
  {
    HMODULE hinstLib;
    this->ScriptItemizeOpenType = nullptr;
    this->ScriptShapeOpenType   = nullptr;
    this->ScriptPlaceOpenType   = nullptr;

    hinstLib = GetModuleHandle (TEXT ("usp10.dll"));
    if (hinstLib)
    {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
      this->ScriptItemizeOpenType = (SIOT) GetProcAddress (hinstLib, "ScriptItemizeOpenType");
      this->ScriptShapeOpenType   = (SSOT) GetProcAddress (hinstLib, "ScriptShapeOpenType");
      this->ScriptPlaceOpenType   = (SPOT) GetProcAddress (hinstLib, "ScriptPlaceOpenType");
#pragma GCC diagnostic pop
    }
    if (!this->ScriptItemizeOpenType ||
	!this->ScriptShapeOpenType   ||
	!this->ScriptPlaceOpenType)
    {
      DEBUG_MSG (UNISCRIBE, nullptr, "OpenType versions of functions not found; falling back.");
      this->ScriptItemizeOpenType = hb_ScriptItemizeOpenType;
      this->ScriptShapeOpenType   = hb_ScriptShapeOpenType;
      this->ScriptPlaceOpenType   = hb_ScriptPlaceOpenType;
    }
  }
};

#if HB_USE_ATEXIT
static void free_static_uniscribe_shaper_funcs ();
#endif

static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t<hb_uniscribe_shaper_funcs_t,
									 hb_uniscribe_shaper_funcs_lazy_loader_t>
{
  static hb_uniscribe_shaper_funcs_t *create ()
  {
    hb_uniscribe_shaper_funcs_t *funcs = (hb_uniscribe_shaper_funcs_t *) calloc (1, sizeof (hb_uniscribe_shaper_funcs_t));
    if (unlikely (!funcs))
      return nullptr;

    funcs->init ();

#if HB_USE_ATEXIT
    atexit (free_static_uniscribe_shaper_funcs);
#endif

    return funcs;
  }
  static void destroy (hb_uniscribe_shaper_funcs_t *p)
  {
    free ((void *) p);
  }
  static hb_uniscribe_shaper_funcs_t *get_null ()
  {
    return nullptr;
  }
} static_uniscribe_shaper_funcs;

#if HB_USE_ATEXIT
static
void free_static_uniscribe_shaper_funcs ()
{
  static_uniscribe_shaper_funcs.free_instance ();
}
#endif

static hb_uniscribe_shaper_funcs_t *
hb_uniscribe_shaper_get_funcs ()
{
  return static_uniscribe_shaper_funcs.get_unconst ();
}

struct active_feature_t {
  OPENTYPE_FEATURE_RECORD rec;
  unsigned int order;

  HB_INTERNAL static int cmp (const void *pa, const void *pb) {
    const active_feature_t *a = (const active_feature_t *) pa;
    const active_feature_t *b = (const active_feature_t *) pb;
    return a->rec.tagFeature < b->rec.tagFeature ? -1 : a->rec.tagFeature > b->rec.tagFeature ? 1 :
	   a->order < b->order ? -1 : a->order > b->order ? 1 :
	   a->rec.lParameter < b->rec.lParameter ? -1 : a->rec.lParameter > b->rec.lParameter ? 1 :
	   0;
  }
  bool operator== (const active_feature_t *f)
  { return cmp (this, f) == 0; }
};

struct feature_event_t {
  unsigned int index;
  bool start;
  active_feature_t feature;

  HB_INTERNAL static int cmp (const void *pa, const void *pb)
  {
    const feature_event_t *a = (const feature_event_t *) pa;
    const feature_event_t *b = (const feature_event_t *) pb;
    return a->index < b->index ? -1 : a->index > b->index ? 1 :
	   a->start < b->start ? -1 : a->start > b->start ? 1 :
	   active_feature_t::cmp (&a->feature, &b->feature);
  }
};

struct range_record_t {
  TEXTRANGE_PROPERTIES props;
  unsigned int index_first; /* == start */
  unsigned int index_last;  /* == end - 1 */
};

/*
 * shaper face data
 */

struct hb_uniscribe_face_data_t {
  HANDLE fh;
  hb_uniscribe_shaper_funcs_t *funcs;
  wchar_t face_name[LF_FACESIZE];
};

/* face_name should point to a wchar_t[LF_FACESIZE] object. */
static void
_hb_generate_unique_face_name (wchar_t *face_name, unsigned int *plen)
{
  /* We'll create a private name for the font from a UUID using a simple,
   * somewhat base64-like encoding scheme */
  const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
  UUID id;
  UuidCreate ((UUID*) &id);
  static_assert ((2 + 3 * (16/2) < LF_FACESIZE), "");
  unsigned int name_str_len = 0;
  face_name[name_str_len++] = 'F';
  face_name[name_str_len++] = '_';
  unsigned char *p = (unsigned char *) &id;
  for (unsigned int i = 0; i < 16; i += 2)
  {
    /* Spread the 16 bits from two bytes of the UUID across three chars of face_name,
     * using the bits in groups of 5,5,6 to select chars from enc.
     * This will generate 24 characters; with the 'F_' prefix we already provided,
     * the name will be 26 chars (plus the NUL terminator), so will always fit within
     * face_name (LF_FACESIZE = 32). */
    face_name[name_str_len++] = enc[p[i] >> 3];
    face_name[name_str_len++] = enc[((p[i] << 2) | (p[i + 1] >> 6)) & 0x1f];
    face_name[name_str_len++] = enc[p[i + 1] & 0x3f];
  }
  face_name[name_str_len] = 0;
  if (plen)
    *plen = name_str_len;
}

/* Destroys blob. */
static hb_blob_t *
_hb_rename_font (hb_blob_t *blob, wchar_t *new_name)
{
  /* Create a copy of the font data, with the 'name' table replaced by a
   * table that names the font with our private F_* name created above.
   * For simplicity, we just append a new 'name' table and update the
   * sfnt directory; the original table is left in place, but unused.
   *
   * The new table will contain just 5 name IDs: family, style, unique,
   * full, PS. All of them point to the same name data with our unique name.
   */

  blob = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (blob);

  unsigned int length, new_length, name_str_len;
  const char *orig_sfnt_data = hb_blob_get_data (blob, &length);

  _hb_generate_unique_face_name (new_name, &name_str_len);

  static const uint16_t name_IDs[] = { 1, 2, 3, 4, 6 };

  unsigned int name_table_length = OT::name::min_size +
				   ARRAY_LENGTH (name_IDs) * OT::NameRecord::static_size +
				   name_str_len * 2; /* for name data in UTF16BE form */
  unsigned int padded_name_table_length = ((name_table_length + 3) & ~3);
  unsigned int name_table_offset = (length + 3) & ~3;

  new_length = name_table_offset + padded_name_table_length;
  void *new_sfnt_data = calloc (1, new_length);
  if (!new_sfnt_data)
  {
    hb_blob_destroy (blob);
    return nullptr;
  }

  memcpy(new_sfnt_data, orig_sfnt_data, length);

  OT::name &name = StructAtOffset<OT::name> (new_sfnt_data, name_table_offset);
  name.format = 0;
  name.count = ARRAY_LENGTH (name_IDs);
  name.stringOffset = name.get_size ();
  for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++)
  {
    OT::NameRecord &record = name.nameRecordZ[i];
    record.platformID = 3;
    record.encodingID = 1;
    record.languageID = 0x0409u; /* English */
    record.nameID = name_IDs[i];
    record.length = name_str_len * 2;
    record.offset = 0;
  }

  /* Copy string data from new_name, converting wchar_t to UTF16BE. */
  unsigned char *p = &StructAfter<unsigned char> (name);
  for (unsigned int i = 0; i < name_str_len; i++)
  {
    *p++ = new_name[i] >> 8;
    *p++ = new_name[i] & 0xff;
  }

  /* Adjust name table entry to point to new name table */
  const OT::OpenTypeFontFile &file = * (OT::OpenTypeFontFile *) (new_sfnt_data);
  unsigned int face_count = file.get_face_count ();
  for (unsigned int face_index = 0; face_index < face_count; face_index++)
  {
    /* Note: doing multiple edits (ie. TTC) can be unsafe.  There may be
     * toe-stepping.  But we don't really care. */
    const OT::OpenTypeFontFace &face = file.get_face (face_index);
    unsigned int index;
    if (face.find_table_index (HB_OT_TAG_name, &index))
    {
      OT::TableRecord &record = const_cast<OT::TableRecord &> (face.get_table (index));
      record.checkSum.set_for_data (&name, padded_name_table_length);
      record.offset = name_table_offset;
      record.length = name_table_length;
    }
    else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */
    {
      free (new_sfnt_data);
      hb_blob_destroy (blob);
      return nullptr;
    }
  }

  /* The checkSumAdjustment field in the 'head' table is now wrong,
   * but that doesn't actually seem to cause any problems so we don't
   * bother. */

  hb_blob_destroy (blob);
  return hb_blob_create ((const char *) new_sfnt_data, new_length,
			 HB_MEMORY_MODE_WRITABLE, new_sfnt_data, free);
}

hb_uniscribe_face_data_t *
_hb_uniscribe_shaper_face_data_create (hb_face_t *face)
{
  hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) calloc (1, sizeof (hb_uniscribe_face_data_t));
  if (unlikely (!data))
    return nullptr;

  data->funcs = hb_uniscribe_shaper_get_funcs ();
  if (unlikely (!data->funcs))
  {
    free (data);
    return nullptr;
  }

  hb_blob_t *blob = hb_face_reference_blob (face);
  if (unlikely (!hb_blob_get_length (blob)))
    DEBUG_MSG (UNISCRIBE, face, "Face has empty blob");

  blob = _hb_rename_font (blob, data->face_name);
  if (unlikely (!blob))
  {
    free (data);
    return nullptr;
  }

  DWORD num_fonts_installed;
  data->fh = AddFontMemResourceEx ((void *) hb_blob_get_data (blob, nullptr),
				   hb_blob_get_length (blob),
				   0, &num_fonts_installed);
  if (unlikely (!data->fh))
  {
    DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed");
    free (data);
    return nullptr;
  }

  return data;
}

void
_hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_face_data_t *data)
{
  RemoveFontMemResourceEx (data->fh);
  free (data);
}

/*
 * shaper font data
 */

struct hb_uniscribe_font_data_t
{
  HDC hdc;
  mutable LOGFONTW log_font;
  HFONT hfont;
  mutable SCRIPT_CACHE script_cache;
  double x_mult, y_mult; /* From LOGFONT space to HB space. */
};

static bool
populate_log_font (LOGFONTW  *lf,
		   hb_font_t *font,
		   unsigned int font_size)
{
  memset (lf, 0, sizeof (*lf));
  lf->lfHeight = - (int) font_size;
  lf->lfCharSet = DEFAULT_CHARSET;

  memcpy (lf->lfFaceName, font->face->data.uniscribe->face_name, sizeof (lf->lfFaceName));

  return true;
}

hb_uniscribe_font_data_t *
_hb_uniscribe_shaper_font_data_create (hb_font_t *font)
{
  hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t));
  if (unlikely (!data))
    return nullptr;

  int font_size = font->face->get_upem (); /* Default... */
  /* No idea if the following is even a good idea. */
  if (font->y_ppem)
    font_size = font->y_ppem;

  if (font_size < 0)
    font_size = -font_size;
  data->x_mult = (double) font->x_scale / font_size;
  data->y_mult = (double) font->y_scale / font_size;

  data->hdc = GetDC (nullptr);

  if (unlikely (!populate_log_font (&data->log_font, font, font_size))) {
    DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed");
    _hb_uniscribe_shaper_font_data_destroy (data);
    return nullptr;
  }

  data->hfont = CreateFontIndirectW (&data->log_font);
  if (unlikely (!data->hfont)) {
    DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed");
    _hb_uniscribe_shaper_font_data_destroy (data);
     return nullptr;
  }

  if (!SelectObject (data->hdc, data->hfont)) {
    DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed");
    _hb_uniscribe_shaper_font_data_destroy (data);
     return nullptr;
  }

  return data;
}

void
_hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_font_data_t *data)
{
  if (data->hdc)
    ReleaseDC (nullptr, data->hdc);
  if (data->hfont)
    DeleteObject (data->hfont);
  if (data->script_cache)
    ScriptFreeCache (&data->script_cache);
  free (data);
}

/**
 * hb_uniscribe_font_get_logfontw:
 * @font: The #hb_font_t to work upon
 *
 * Fetches the LOGFONTW structure that corresponds to the
 * specified #hb_font_t font.
 *
 * Return value: a pointer to the LOGFONTW retrieved
 *
 **/
LOGFONTW *
hb_uniscribe_font_get_logfontw (hb_font_t *font)
{
  const hb_uniscribe_font_data_t *data =  font->data.uniscribe;
  return data ? &data->log_font : nullptr;
}

/**
 * hb_uniscribe_font_get_hfont:
 * @font: The #hb_font_t to work upon
 *
 * Fetches the HFONT handle that corresponds to the
 * specified #hb_font_t font.
 *
 * Return value: the HFONT retreieved
 *
 **/
HFONT
hb_uniscribe_font_get_hfont (hb_font_t *font)
{
  const hb_uniscribe_font_data_t *data =  font->data.uniscribe;
  return data ? data->hfont : nullptr;
}

/*
 * shaper
 */

hb_bool_t
_hb_uniscribe_shape (hb_shape_plan_t    *shape_plan,
		     hb_font_t          *font,
		     hb_buffer_t        *buffer,
		     const hb_feature_t *features,
		     unsigned int        num_features)
{
  hb_face_t *face = font->face;
  const hb_uniscribe_face_data_t *face_data = face->data.uniscribe;
  const hb_uniscribe_font_data_t *font_data = font->data.uniscribe;
  hb_uniscribe_shaper_funcs_t *funcs = face_data->funcs;

  /*
   * Set up features.
   */
  hb_vector_t<OPENTYPE_FEATURE_RECORD> feature_records;
  hb_vector_t<range_record_t> range_records;
  if (num_features)
  {
    /* Sort features by start/end events. */
    hb_vector_t<feature_event_t> feature_events;
    for (unsigned int i = 0; i < num_features; i++)
    {
      active_feature_t feature;
      feature.rec.tagFeature = hb_uint32_swap (features[i].tag);
      feature.rec.lParameter = features[i].value;
      feature.order = i;

      feature_event_t *event;

      event = feature_events.push ();
      event->index = features[i].start;
      event->start = true;
      event->feature = feature;

      event = feature_events.push ();
      event->index = features[i].end;
      event->start = false;
      event->feature = feature;
    }
    feature_events.qsort ();
    /* Add a strategic final event. */
    {
      active_feature_t feature;
      feature.rec.tagFeature = 0;
      feature.rec.lParameter = 0;
      feature.order = num_features + 1;

      feature_event_t *event = feature_events.push ();
      event->index = 0; /* This value does magic. */
      event->start = false;
      event->feature = feature;
    }

    /* Scan events and save features for each range. */
    hb_vector_t<active_feature_t> active_features;
    unsigned int last_index = 0;
    for (unsigned int i = 0; i < feature_events.length; i++)
    {
      feature_event_t *event = &feature_events[i];

      if (event->index != last_index)
      {
	/* Save a snapshot of active features and the range. */
	range_record_t *range = range_records.push ();

	unsigned int offset = feature_records.length;

	active_features.qsort ();
	for (unsigned int j = 0; j < active_features.length; j++)
	{
	  if (!j || active_features[j].rec.tagFeature != feature_records[feature_records.length - 1].tagFeature)
	  {
	    feature_records.push (active_features[j].rec);
	  }
	  else
	  {
	    /* Overrides value for existing feature. */
	    feature_records[feature_records.length - 1].lParameter = active_features[j].rec.lParameter;
	  }
	}

	/* Will convert to pointer after all is ready, since feature_records.array
	 * may move as we grow it. */
	range->props.potfRecords = reinterpret_cast<OPENTYPE_FEATURE_RECORD *> (offset);
	range->props.cotfRecords = feature_records.length - offset;
	range->index_first = last_index;
	range->index_last  = event->index - 1;

	last_index = event->index;
      }

      if (event->start)
      {
	active_features.push (event->feature);
      }
      else
      {
	active_feature_t *feature = active_features.find (&event->feature);
	if (feature)
	  active_features.remove (feature - active_features.arrayZ);
      }
    }

    if (!range_records.length) /* No active feature found. */
      num_features = 0;

    /* Fixup the pointers. */
    for (unsigned int i = 0; i < range_records.length; i++)
    {
      range_record_t *range = &range_records[i];
      range->props.potfRecords = (OPENTYPE_FEATURE_RECORD *) feature_records + reinterpret_cast<uintptr_t> (range->props.potfRecords);
    }
  }

#define FAIL(...) \
  HB_STMT_START { \
    DEBUG_MSG (UNISCRIBE, nullptr, __VA_ARGS__); \
    return false; \
  } HB_STMT_END

  HRESULT hr;

retry:

  unsigned int scratch_size;
  hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);

#define ALLOCATE_ARRAY(Type, name, len) \
  Type *name = (Type *) scratch; \
  do { \
    unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
    assert (_consumed <= scratch_size); \
    scratch += _consumed; \
    scratch_size -= _consumed; \
  } while (0)

#define utf16_index() var1.u32

  ALLOCATE_ARRAY (WCHAR, pchars, buffer->len * 2);

  unsigned int chars_len = 0;
  for (unsigned int i = 0; i < buffer->len; i++)
  {
    hb_codepoint_t c = buffer->info[i].codepoint;
    buffer->info[i].utf16_index() = chars_len;
    if (likely (c <= 0xFFFFu))
      pchars[chars_len++] = c;
    else if (unlikely (c > 0x10FFFFu))
      pchars[chars_len++] = 0xFFFDu;
    else {
      pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
      pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
    }
  }

  ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
  ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len);

  if (num_features)
  {
    /* Need log_clusters to assign features. */
    chars_len = 0;
    for (unsigned int i = 0; i < buffer->len; i++)
    {
      hb_codepoint_t c = buffer->info[i].codepoint;
      unsigned int cluster = buffer->info[i].cluster;
      log_clusters[chars_len++] = cluster;
      if (hb_in_range (c, 0x10000u, 0x10FFFFu))
	log_clusters[chars_len++] = cluster; /* Surrogates. */
    }
  }

  /* The -2 in the following is to compensate for possible
   * alignment needed after the WORD array.  sizeof(WORD) == 2. */
  unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
			   / (sizeof (WORD) +
			      sizeof (SCRIPT_GLYPHPROP) +
			      sizeof (int) +
			      sizeof (GOFFSET) +
			      sizeof (uint32_t));

  ALLOCATE_ARRAY (WORD, glyphs, glyphs_size);
  ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size);
  ALLOCATE_ARRAY (int, advances, glyphs_size);
  ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size);
  ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);

  /* Note:
   * We can't touch the contents of glyph_props.  Our fallback
   * implementations of Shape and Place functions use that buffer
   * by casting it to a different type.  It works because they
   * both agree about it, but if we want to access it here we
   * need address that issue first.
   */

#undef ALLOCATE_ARRAY

#define MAX_ITEMS 256

  SCRIPT_ITEM items[MAX_ITEMS + 1];
  SCRIPT_CONTROL bidi_control = {0};
  SCRIPT_STATE bidi_state = {0};
  ULONG script_tags[MAX_ITEMS];
  int item_count;

  /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */
  //bidi_control.fMergeNeutralItems = true;
  *(uint32_t*)&bidi_control |= 1u<<24;

  bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
  bidi_state.fOverrideDirection = 1;

  hr = funcs->ScriptItemizeOpenType (pchars,
				     chars_len,
				     MAX_ITEMS,
				     &bidi_control,
				     &bidi_state,
				     items,
				     script_tags,
				     &item_count);
  if (unlikely (FAILED (hr)))
    FAIL ("ScriptItemizeOpenType() failed: 0x%08lx", hr);

#undef MAX_ITEMS

  hb_tag_t lang_tag;
  unsigned int lang_count = 1;
  hb_ot_tags_from_script_and_language (buffer->props.script,
				       buffer->props.language,
				       nullptr, nullptr,
				       &lang_count, &lang_tag);
  OPENTYPE_TAG language_tag = hb_uint32_swap (lang_count ? lang_tag : HB_TAG_NONE);
  hb_vector_t<TEXTRANGE_PROPERTIES*> range_properties;
  hb_vector_t<int> range_char_counts;

  unsigned int glyphs_offset = 0;
  unsigned int glyphs_len;
  bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
  for (int i = 0; i < item_count; i++)
  {
    unsigned int chars_offset = items[i].iCharPos;
    unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset;

    if (num_features)
    {
      range_properties.shrink (0);
      range_char_counts.shrink (0);

      range_record_t *last_range = &range_records[0];

      for (unsigned int k = chars_offset; k < chars_offset + item_chars_len; k++)
      {
	range_record_t *range = last_range;
	while (log_clusters[k] < range->index_first)
	  range--;
	while (log_clusters[k] > range->index_last)
	  range++;
	if (!range_properties.length ||
	    &range->props != range_properties[range_properties.length - 1])
	{
	  TEXTRANGE_PROPERTIES **props = range_properties.push ();
	  int *c = range_char_counts.push ();
	  if (unlikely (!props || !c))
	  {
	    range_properties.shrink (0);
	    range_char_counts.shrink (0);
	    break;
	  }
	  *props = &range->props;
	  *c = 1;
	}
	else
	{
	  range_char_counts[range_char_counts.length - 1]++;
	}

	last_range = range;
      }
    }

    /* Asking for glyphs in logical order circumvents at least
     * one bug in Uniscribe. */
    items[i].a.fLogicalOrder = true;

  retry_shape:
    hr = funcs->ScriptShapeOpenType (font_data->hdc,
				     &font_data->script_cache,
				     &items[i].a,
				     script_tags[i],
				     language_tag,
				     range_char_counts.arrayZ,
				     range_properties.arrayZ,
				     range_properties.length,
				     pchars + chars_offset,
				     item_chars_len,
				     glyphs_size - glyphs_offset,
				     /* out */
				     log_clusters + chars_offset,
				     char_props + chars_offset,
				     glyphs + glyphs_offset,
				     glyph_props + glyphs_offset,
				     (int *) &glyphs_len);

    if (unlikely (items[i].a.fNoGlyphIndex))
      FAIL ("ScriptShapeOpenType() set fNoGlyphIndex");
    if (unlikely (hr == E_OUTOFMEMORY || hr == E_NOT_SUFFICIENT_BUFFER))
    {
      if (unlikely (!buffer->ensure (buffer->allocated * 2)))
	FAIL ("Buffer resize failed");
      goto retry;
    }
    if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT))
    {
      if (items[i].a.eScript == SCRIPT_UNDEFINED)
	FAIL ("ScriptShapeOpenType() failed: Font doesn't support script");
      items[i].a.eScript = SCRIPT_UNDEFINED;
      goto retry_shape;
    }
    if (unlikely (FAILED (hr)))
    {
      FAIL ("ScriptShapeOpenType() failed: 0x%08lx", hr);
    }

    for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++)
      log_clusters[j] += glyphs_offset;

    hr = funcs->ScriptPlaceOpenType (font_data->hdc,
				     &font_data->script_cache,
				     &items[i].a,
				     script_tags[i],
				     language_tag,
				     range_char_counts.arrayZ,
				     range_properties.arrayZ,
				     range_properties.length,
				     pchars + chars_offset,
				     log_clusters + chars_offset,
				     char_props + chars_offset,
				     item_chars_len,
				     glyphs + glyphs_offset,
				     glyph_props + glyphs_offset,
				     glyphs_len,
				     /* out */
				     advances + glyphs_offset,
				     offsets + glyphs_offset,
				     nullptr);
    if (unlikely (FAILED (hr)))
      FAIL ("ScriptPlaceOpenType() failed: 0x%08lx", hr);

    if (DEBUG_ENABLED (UNISCRIBE))
      fprintf (stderr, "Item %d RTL %d LayoutRTL %d LogicalOrder %d ScriptTag %c%c%c%c\n",
	       i,
	       items[i].a.fRTL,
	       items[i].a.fLayoutRTL,
	       items[i].a.fLogicalOrder,
	       HB_UNTAG (hb_uint32_swap (script_tags[i])));

    glyphs_offset += glyphs_len;
  }
  glyphs_len = glyphs_offset;

  /* Ok, we've got everything we need, now compose output buffer,
   * very, *very*, carefully! */

  /* Calculate visual-clusters.  That's what we ship. */
  for (unsigned int i = 0; i < glyphs_len; i++)
    vis_clusters[i] = (uint32_t) -1;
  for (unsigned int i = 0; i < buffer->len; i++) {
    uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]];
    *p = hb_min (*p, buffer->info[i].cluster);
  }
  for (unsigned int i = 1; i < glyphs_len; i++)
    if (vis_clusters[i] == (uint32_t) -1)
      vis_clusters[i] = vis_clusters[i - 1];

#undef utf16_index

  if (unlikely (!buffer->ensure (glyphs_len)))
    FAIL ("Buffer in error");

#undef FAIL

  /* Set glyph infos */
  buffer->len = 0;
  for (unsigned int i = 0; i < glyphs_len; i++)
  {
    hb_glyph_info_t *info = &buffer->info[buffer->len++];

    info->codepoint = glyphs[i];
    info->cluster = vis_clusters[i];

    /* The rest is crap.  Let's store position info there for now. */
    info->mask = advances[i];
    info->var1.i32 = offsets[i].du;
    info->var2.i32 = offsets[i].dv;
  }

  /* Set glyph positions */
  buffer->clear_positions ();
  double x_mult = font_data->x_mult, y_mult = font_data->y_mult;
  for (unsigned int i = 0; i < glyphs_len; i++)
  {
    hb_glyph_info_t *info = &buffer->info[i];
    hb_glyph_position_t *pos = &buffer->pos[i];

    /* TODO vertical */
    pos->x_advance = x_mult * (int32_t) info->mask;
    pos->x_offset = x_mult * (backward ? -info->var1.i32 : info->var1.i32);
    pos->y_offset = y_mult * info->var2.i32;
  }

  if (backward)
    hb_buffer_reverse (buffer);

  buffer->unsafe_to_break_all ();

  /* Wow, done! */
  return true;
}

#endif


text:
/*
 * Copyright © 2015  Mozilla Foundation.
 * Copyright © 2015  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping 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.
 *
 * Mozilla Author(s): Jonathan Kew
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH

#include "hb.hh"

%%{
  machine use_syllable_machine;
  alphtype unsigned char;
  write data;
}%%

%%{

# Same order as enum use_category_t.  Not sure how to avoid duplication.

O	= 0; # OTHER

B	= 1; # BASE
IND	= 3; # BASE_IND
N	= 4; # BASE_NUM
GB	= 5; # BASE_OTHER
CGJ	= 6; # CGJ
#F	= 7; # CONS_FINAL
#FM	= 8; # CONS_FINAL_MOD
#M	= 9; # CONS_MED
#CM	= 10; # CONS_MOD
SUB	= 11; # CONS_SUB
H	= 12; # HALANT

HN	= 13; # HALANT_NUM
ZWNJ	= 14; # Zero width non-joiner
ZWJ	= 15; # Zero width joiner
WJ	= 16; # Word joiner
Rsv	= 17; # Reserved characters
R	= 18; # REPHA
S	= 19; # SYM
#SM	= 20; # SYM_MOD
VS	= 21; # VARIATION_SELECTOR
#V	= 36; # VOWEL
#VM	= 40; # VOWEL_MOD
CS	= 43; # CONS_WITH_STACKER
HVM	= 44; # HALANT_OR_VOWEL_MODIFIER
Sk	= 48; # SAKOT

FAbv	= 24; # CONS_FINAL_ABOVE
FBlw	= 25; # CONS_FINAL_BELOW
FPst	= 26; # CONS_FINAL_POST
MAbv	= 27; # CONS_MED_ABOVE
MBlw	= 28; # CONS_MED_BELOW
MPst	= 29; # CONS_MED_POST
MPre	= 30; # CONS_MED_PRE
CMAbv	= 31; # CONS_MOD_ABOVE
CMBlw	= 32; # CONS_MOD_BELOW
VAbv	= 33; # VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST
VBlw	= 34; # VOWEL_BELOW / VOWEL_BELOW_POST
VPst	= 35; # VOWEL_POST	UIPC = Right
VPre	= 22; # VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST
VMAbv	= 37; # VOWEL_MOD_ABOVE
VMBlw	= 38; # VOWEL_MOD_BELOW
VMPst	= 39; # VOWEL_MOD_POST
VMPre	= 23; # VOWEL_MOD_PRE
SMAbv	= 41; # SYM_MOD_ABOVE
SMBlw	= 42; # SYM_MOD_BELOW
FMAbv	= 45; # CONS_FINAL_MOD	UIPC = Top
FMBlw	= 46; # CONS_FINAL_MOD	UIPC = Bottom
FMPst	= 47; # CONS_FINAL_MOD	UIPC = Not_Applicable

h = H | HVM | Sk;

# Override: Adhoc ZWJ placement. https://github.com/harfbuzz/harfbuzz/issues/542#issuecomment-353169729
consonant_modifiers = CMAbv* CMBlw* ((ZWJ?.h.ZWJ? B | SUB) VS? CMAbv? CMBlw*)*;
# Override: Allow two MBlw. https://github.com/harfbuzz/harfbuzz/issues/376
medial_consonants = MPre? MAbv? MBlw?.MBlw? MPst?;
dependent_vowels = VPre* VAbv* VBlw* VPst*;
vowel_modifiers = HVM? VMPre* VMAbv* VMBlw* VMPst*;
final_consonants = FAbv* FBlw* FPst*;
final_modifiers = FMAbv* FMBlw* | FMPst?;

complex_syllable_start = (R | CS)? (B | GB) VS?;
complex_syllable_middle =
	consonant_modifiers
	medial_consonants
	dependent_vowels
	vowel_modifiers
	(Sk B)*
;
complex_syllable_tail =
	complex_syllable_middle
	final_consonants
	final_modifiers
;
number_joiner_terminated_cluster_tail = (HN N VS?)* HN;
numeral_cluster_tail = (HN N VS?)+;
symbol_cluster_tail = SMAbv+ SMBlw* | SMBlw+;

virama_terminated_cluster =
	complex_syllable_start
	consonant_modifiers
	ZWJ?.h.ZWJ?
;
sakot_terminated_cluster =
	complex_syllable_start
	complex_syllable_middle
	Sk
;
standard_cluster =
	complex_syllable_start
	complex_syllable_tail
;
broken_cluster =
	R?
	(complex_syllable_tail | number_joiner_terminated_cluster_tail | numeral_cluster_tail | symbol_cluster_tail)
;

number_joiner_terminated_cluster = N VS? number_joiner_terminated_cluster_tail;
numeral_cluster = N VS? numeral_cluster_tail?;
symbol_cluster = (S | GB) VS? symbol_cluster_tail?;
independent_cluster = (IND | O | Rsv | WJ) VS?;
other = any;

main := |*
	independent_cluster			=> { found_syllable (independent_cluster); };
	virama_terminated_cluster		=> { found_syllable (virama_terminated_cluster); };
	sakot_terminated_cluster		=> { found_syllable (sakot_terminated_cluster); };
	standard_cluster			=> { found_syllable (standard_cluster); };
	number_joiner_terminated_cluster	=> { found_syllable (number_joiner_terminated_cluster); };
	numeral_cluster				=> { found_syllable (numeral_cluster); };
	symbol_cluster				=> { found_syllable (symbol_cluster); };
	broken_cluster				=> { found_syllable (broken_cluster); };
	other					=> { found_syllable (non_cluster); };
*|;

}%%

#define found_syllable(syllable_type) \
  HB_STMT_START { \
    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
    for (unsigned int i = ts; i < te; i++) \
      info[i].syllable() = (syllable_serial << 4) | use_##syllable_type; \
    syllable_serial++; \
    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
  } HB_STMT_END

static void
find_syllables_use (hb_buffer_t *buffer)
{
  unsigned int p, pe, eof, ts, te, act;
  int cs;
  hb_glyph_info_t *info = buffer->info;
  %%{
    write init;
    getkey info[p].use_category();
  }%%

  p = 0;
  pe = eof = buffer->len;

  unsigned int syllable_serial = 1;
  %%{
    write exec;
  }%%
}

#undef found_syllable

#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */