[HarfBuzz] harfbuzz: Branch 'master' - 2 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Tue Oct 23 09:52:35 UTC 2018


 src/HBIndicVowelConstraints.txt                                               |   97 ++
 src/Makefile.am                                                               |    7 
 src/Makefile.sources                                                          |    2 
 src/gen-vowel-constraints.py                                                  |  216 ++++
 src/hb-ot-shape-complex-indic.cc                                              |  278 ------
 src/hb-ot-shape-complex-use.cc                                                |   12 
 src/hb-ot-shape-complex-vowel-constraints.cc                                  |  433 ++++++++++
 src/hb-ot-shape-complex-vowel-constraints.hh                                  |   39 
 test/shaping/README.md                                                        |    4 
 test/shaping/data/in-house/fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf |binary
 test/shaping/data/in-house/tests/use-vowel-letter-spoofing.tests              |   94 ++
 11 files changed, 909 insertions(+), 273 deletions(-)

New commits:
commit 6d40eb8372a2c74a6e1294b44a2b19c99d11e7da
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Oct 23 02:51:42 2018 -0700

    Touch up on previous commit
    
    https://github.com/harfbuzz/harfbuzz/pull/1273

diff --git a/src/HBIndicVowelConstraints.txt b/src/HBIndicVowelConstraints.txt
new file mode 100644
index 00000000..146ae1cb
--- /dev/null
+++ b/src/HBIndicVowelConstraints.txt
@@ -0,0 +1,97 @@
+# Copied from https://docs.microsoft.com/en-us/typography/script-development/use
+# On October 23, 2018; with documentd dated 02/07/2018.
+
+  0905 0946       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT E
+  0905 093E       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AA
+  0930 094D 0907  ; # DEVANAGARI LETTER RA, DEVANAGARI SIGN VIRAMA, DEVANAGARI LETTER I
+  0909 0941       ; # DEVANAGARI LETTER U, DEVANAGARI VOWEL SIGN U
+  090F 0945       ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN CANDRA E
+  090F 0946       ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN SHORT E
+  090F 0947       ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN E
+  0905 0949       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN CANDRA O
+  0906 0945       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN CANDRA E
+  0905 094A       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT O
+  0906 0946       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN SHORT E
+  0905 094B       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN O
+  0906 0947       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN E
+  0905 094C       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AU
+  0906 0948       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN AI
+  0905 0945       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN CANDRA E
+  0905 093A       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN OE
+  0905 093B       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN OOE
+  0906 093A       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN OE
+  0905 094F       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AW
+  0905 0956       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN UE
+  0905 0957       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN UUE
+  0985 09BE       ; # BENGALI LETTER A, BENGALI VOWEL SIGN AA
+  098B 09C3       ; # BENGALI LETTER VOCALIC R, BENGALI VOWEL SIGN VOCALIC R
+  098C 09E2       ; # BENGALI LETTER VOCALIC L, BENGALI VOWEL SIGN VOCALIC L
+  0A05 0A3E       ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AA
+  0A72 0A3F       ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN I
+  0A72 0A40       ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN II
+  0A73 0A41       ; # GURMUKHI URA, GURMUKHI VOWEL SIGN U
+  0A73 0A42       ; # GURMUKHI URA, GURMUKHI VOWEL SIGN UU
+  0A72 0A47       ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN EE
+  0A05 0A48       ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AI
+  0A73 0A4B       ; # GURMUKHI URA, GURMUKHI VOWEL SIGN OO
+  0A05 0A4C       ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AU
+  0A85 0ABE       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA
+  0A85 0AC5       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN CANDRA E
+  0A85 0AC7       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN E
+  0A85 0AC8       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AI
+  0A85 0AC9       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN CANDRA O
+  0A85 0ACB       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN O
+  0A85 0ABE 0AC5  ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA, GUJARATI VOWEL SIGN CANDRA E
+  0A85 0ACC       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AU
+  0A85 0ABE 0AC8  ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA, GUJARATI VOWEL SIGN AI
+  0AC5 0ABE       ; # GUJARATI VOWEL SIGN CANDRA E, GUJARATI VOWEL SIGN AA
+  0B05 0B3E       ; # ORIYA LETTER A, ORIYA VOWEL SIGN AA
+  0B0F 0B57       ; # ORIYA LETTER E, ORIYA AU LENGTH MARK
+  0B13 0B57       ; # ORIYA LETTER O, ORIYA AU LENGTH MARK
+  0C12 0C55       ; # TELUGU LETTER O, TELUGU LENGTH MARK
+  0C12 0C4C       ; # TELUGU LETTER O, TELUGU VOWEL SIGN AU
+  0C3F 0C55       ; # TELUGU VOWEL SIGN I, TELUGU LENGTH MARK
+  0C46 0C55       ; # TELUGU VOWEL SIGN E, TELUGU LENGTH MARK
+  0C4A 0C55       ; # TELUGU VOWEL SIGN O, TELUGU LENGTH MARK
+  0C89 0CBE       ; # KANNADA LETTER U, KANNADA VOWEL SIGN AA
+  0C92 0CCC       ; # KANNADA LETTER O, KANNADA VOWEL SIGN AU
+  0C8B 0CBE       ; # KANNADA LETTER VOCALIC R, KANNADA VOWEL SIGN AA
+  0D07 0D57       ; # MALAYALAM LETTER I, MALAYALAM AU LENGTH MARK
+  0D09 0D57       ; # MALAYALAM LETTER U, MALAYALAM AU LENGTH MARK
+  0D0E 0D46       ; # MALAYALAM LETTER E, MALAYALAM VOWEL SIGN E
+  0D12 0D3E       ; # MALAYALAM LETTER O, MALAYALAM VOWEL SIGN AA
+  0D12 0D57       ; # MALAYALAM LETTER O, MALAYALAM AU LENGTH MARK
+  0D85 0DCF       ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN AELA-PILLA
+  0D85 0DD0       ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN KETTI AEDA-PILLA
+  0D85 0DD1       ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN DIGA AEDA-PILLA
+  0D8B 0DDF       ; # SINHALA LETTER UYANNA, SINHALA VOWEL SIGN GAYANUKITTA
+  0D8D 0DD8       ; # SINHALA LETTER IRUYANNA, SINHALA VOWEL SIGN GAETTA-PILLA
+  0D8F 0DDF       ; # SINHALA LETTER ILUYANNA, SINHALA VOWEL SIGN GAYANUKITTA
+  0D91 0DCA       ; # SINHALA LETTER EYANNA, SINHALA SIGN AL-LAKUNA
+  0D91 0DD9       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA
+  0D91 0DDA       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN DIGA KOMBUVA
+  0D91 0DDC       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA
+  0D91 0DDD       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA
+  0D91 0DDD       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA
+  0D94 0DDF       ; # SINHALA LETTER OYANNA, SINHALA VOWEL SIGN GAYANUKITTA
+  11005 11038     ; # BRAHMI LETTER A, BRAHMI VOWEL SIGN AA
+  1100B 1103E     ; # BRAHMI LETTER VOCALIC R, BRAHMI VOWEL SIGN VOCALIC R
+  1100F 11042     ; # BRAHMI LETTER E, BRAHMI VOWEL SIGN E
+  11680 116AD     ; # TAKRI LETTER A, TAKRI VOWEL SIGN AA
+  11686 116B2     ; # TAKRI LETTER E, TAKRI VOWEL SIGN E
+  11680 116B4     ; # TAKRI LETTER A, TAKRI VOWEL SIGN O
+  11680 116B5     ; # TAKRI LETTER A, TAKRI VOWEL SIGN AU
+  112B0 112E0     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AA
+  112B0 112E5     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN E
+  112B0 112E6     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AI
+  112B0 112E7     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN O
+  112B0 112E8     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AU
+  11481 114B0     ; # TIRHUTA LETTER A, TIRHUTA VOWEL SIGN AA
+  114AA 114B5     ; # TIRHUTA LETTER LA, TIRHUTA VOWEL SIGN VOCALIC R
+  114AA 114B6     ; # TIRHUTA LETTER LA, TIRHUTA VOWEL SIGN VOCALIC RR
+  1148B 114BA     ; # TIRHUTA LETTER E, TIRHUTA VOWEL SIGN SHORT E
+  1148D 114BA     ; # TIRHUTA LETTER O, TIRHUTA VOWEL SIGN SHORT E
+  11600 11639     ; # MODI LETTER A, MODI VOWEL SIGN E
+  11600 1163A     ; # MODI LETTER A, MODI VOWEL SIGN AI
+  11601 11639     ; # MODI LETTER AA, MODI VOWEL SIGN E
+  11601 1163A     ; # MODI LETTER AA, MODI VOWEL SIGN AI
diff --git a/src/Makefile.am b/src/Makefile.am
index 782992d1..ac03890b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -317,9 +317,9 @@ use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.tx
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-use-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-use-table.cc; false)
 
-vowel-constraints: gen-vowel-constraints.py use Scripts.txt
-	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-vowel-constraints.hh \
-	|| ($(RM) $(srcdir)/hb-ot-shape-complex-vowel-constraints.hh; false)
+vowel-constraints: gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt
+	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc \
+	|| ($(RM) $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc; false)
 
 emoji-table: gen-emoji-table.py emoji-data.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-unicode-emoji-table.hh \
diff --git a/src/Makefile.sources b/src/Makefile.sources
index b3029105..72968aee 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -142,6 +142,7 @@ HB_OT_sources = \
 	hb-ot-shape-complex-use.cc \
 	hb-ot-shape-complex-use.hh \
 	hb-ot-shape-complex-use-table.cc \
+	hb-ot-shape-complex-vowel-constraints.cc \
 	hb-ot-shape-complex-vowel-constraints.hh \
 	hb-ot-shape-complex.hh \
 	hb-ot-shape-normalize.hh \
diff --git a/src/gen-vowel-constraints.py b/src/gen-vowel-constraints.py
index bcb5d27b..19629abe 100755
--- a/src/gen-vowel-constraints.py
+++ b/src/gen-vowel-constraints.py
@@ -2,12 +2,10 @@
 
 """Generator of the function to prohibit certain vowel sequences.
 
-It creates ``preprocess_text_vowel_constraints``, which inserts dotted
+It creates ``_hb_preprocess_text_vowel_constraints``, which inserts dotted
 circles into sequences prohibited by the USE script development spec.
 This function should be used as the ``preprocess_text`` of an
 ``hb_ot_complex_shaper_t``.
-
-It also creates the helper function ``_output_with_dotted_circle``.
 """
 
 from __future__ import absolute_import, division, print_function, unicode_literals
@@ -27,23 +25,9 @@ import io
 import sys
 
 if len (sys.argv) != 3:
-	print ('usage: ./gen-vowel-constraints.py use Scripts.txt', file=sys.stderr)
+	print ('usage: ./gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt', file=sys.stderr)
 	sys.exit (1)
 
-try:
-	from html import unescape
-	def html_unescape (parser, entity):
-		return unescape (entity)
-except ImportError:
-	def html_unescape (parser, entity):
-		return parser.unescape (entity)
-
-def expect (condition, message=None):
-	if not condition:
-		if message is None:
-			raise AssertionError
-		raise AssertionError (message)
-
 with io.open (sys.argv[2], encoding='utf-8') as f:
 	scripts_header = [f.readline () for i in range (2)]
 	scripts = {}
@@ -142,74 +126,22 @@ class ConstraintSet (object):
 			s.append ('{}}}\n'.format (indent))
 		return ''.join (s)
 
-class USESpecParser (HTMLParser):
-	"""A parser for the USE script development spec.
-
-	Attributes:
-		header (str): The ``updated_at`` timestamp of the spec.
-		constraints (Mapping[str, ConstraintSet]): A map of script names
-			to the scripts' prohibited sequences.
-	"""
-	def __init__ (self):
-		HTMLParser.__init__ (self)
-		self.header = ''
-		self.constraints = {}
-		# Whether the next <code> contains the vowel constraints.
-		self._primed = False
-		# Whether the parser is in the <code> element with the constraints.
-		self._in_constraints = False
-		# The text of the constraints.
-		self._constraints = ''
-
-	def handle_starttag (self, tag, attrs):
-		if tag == 'meta':
-			for attr, value in attrs:
-				if attr == 'name' and value == 'updated_at':
-					self.header = self.get_starttag_text ()
-					break
-		elif tag == 'a':
-			for attr, value in attrs:
-				if attr == 'id' and value == 'ivdvconstraints':
-					self._primed = True
-					break
-		elif self._primed and tag == 'code':
-			self._primed = False
-			self._in_constraints = True
-
-	def handle_endtag (self, tag):
-		self._in_constraints = False
-
-	def handle_data (self, data):
-		if self._in_constraints:
-			self._constraints += data
-
-	def handle_charref (self, name):
-		self.handle_data (html_unescape (self, '&#%s;' % name))
-
-	def handle_entityref (self, name):
-		self.handle_data (html_unescape (self, '&%s;' % name))
-
-	def parse (self, filename):
-		"""Parse the USE script development spec.
-
-		Args:
-			filename (str): The file name of the spec.
-		"""
-		with io.open (filename, encoding='utf-8') as f:
-			self.feed (f.read ())
-		expect (self.header, 'No header found')
-		for line in self._constraints.splitlines ():
-			constraint = [int (cp, 16) for cp in line.split (';')[0].strip ().split (' ')]
-			expect (2 <= len (constraint), 'Prohibited sequence is too short: {}'.format (constraint))
-			script = scripts[constraint[0]]
-			if script in self.constraints:
-				self.constraints[script].add (constraint)
-			else:
-				self.constraints[script] = ConstraintSet (constraint)
-		expect (self.constraints, 'No constraints found')
-
-use_parser = USESpecParser ()
-use_parser.parse (sys.argv[1])
+constraints = {}
+with io.open (sys.argv[1], encoding='utf-8') as f:
+	constraints_header = [f.readline ().strip () for i in range (2)]
+	for line in f:
+		j = line.find ('#')
+		if j >= 0:
+			line = line[:j]
+		constraint = [int (cp, 16) for cp in line.split (';')[0].split ()]
+		if not constraint: continue
+		assert 2 <= len (constraint), 'Prohibited sequence is too short: {}'.format (constraint)
+		script = scripts[constraint[0]]
+		if script in constraints:
+			constraints[script].add (constraint)
+		else:
+			constraints[script] = ConstraintSet (constraint)
+		assert constraints, 'No constraints found'
 
 print ('/* == Start of generated functions == */')
 print ('/*')
@@ -219,15 +151,15 @@ print (' *   %s use Scripts.txt' % sys.argv[0])
 print (' *')
 print (' * on files with these headers:')
 print (' *')
-print (' * %s' % use_parser.header.strip ())
+for line in constraints_header:
+	print (' * %s' % line.strip ())
+print (' *')
 for line in scripts_header:
 	print (' * %s' % line.strip ())
 print (' */')
 print ()
-print ('#ifndef HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH')
-print ('#define HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH')
+print ('#include "hb-ot-shape-complex-vowel-constraints.hh"')
 print ()
-
 print ('static void')
 print ('_output_with_dotted_circle (hb_buffer_t *buffer)')
 print ('{')
@@ -238,10 +170,10 @@ print ('  buffer->next_glyph ();')
 print ('}')
 print ()
 
-print ('static void')
-print ('preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,')
-print ('\t\t\t\t   hb_buffer_t              *buffer,')
-print ('\t\t\t\t   hb_font_t                *font)')
+print ('void')
+print ('_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,')
+print ('\t\t\t\t       hb_buffer_t              *buffer,')
+print ('\t\t\t\t       hb_font_t                *font)')
 print ('{')
 print ('  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of')
 print ('   * vowel-sequences that look like another vowel.  Data for each script')
@@ -255,7 +187,7 @@ print ('  unsigned int count = buffer->len;')
 print ('  switch ((unsigned) buffer->props.script)')
 print ('  {')
 
-for script, constraints in sorted (use_parser.constraints.items (), key=lambda s_c: script_order[s_c[0]]):
+for script, constraints in sorted (constraints.items (), key=lambda s_c: script_order[s_c[0]]):
 	print ('    case HB_SCRIPT_{}:'.format (script.upper ()))
 	print ('      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)')
 	print ('      {')
@@ -281,6 +213,4 @@ print ('  }')
 print ('}')
 
 print ()
-print ('#endif /* HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH */')
-print ()
 print ('/* == End of generated functions == */')
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 092ac684..3babbfec 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -1517,6 +1517,14 @@ clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
 }
 
 
+static void
+preprocess_text_indic (const hb_ot_shape_plan_t *plan,
+		       hb_buffer_t              *buffer,
+		       hb_font_t                *font)
+{
+  _hb_preprocess_text_vowel_constraints (plan, buffer, font);
+}
+
 static bool
 decompose_indic (const hb_ot_shape_normalize_context_t *c,
 		 hb_codepoint_t  ab,
@@ -1616,7 +1624,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
   override_features_indic,
   data_create_indic,
   data_destroy_indic,
-  preprocess_text_vowel_constraints,
+  preprocess_text_indic,
   nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   decompose_indic,
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index 8c44fe01..addfb87b 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -572,6 +572,15 @@ reorder (const hb_ot_shape_plan_t *plan,
   HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
 }
 
+
+static void
+preprocess_text_use (const hb_ot_shape_plan_t *plan,
+		     hb_buffer_t              *buffer,
+		     hb_font_t                *font)
+{
+  _hb_preprocess_text_vowel_constraints (plan, buffer, font);
+}
+
 static bool
 compose_use (const hb_ot_shape_normalize_context_t *c,
 	     hb_codepoint_t  a,
@@ -592,7 +601,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use =
   nullptr, /* override_features */
   data_create_use,
   data_destroy_use,
-  preprocess_text_vowel_constraints,
+  preprocess_text_use,
   nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   nullptr, /* decompose */
diff --git a/src/hb-ot-shape-complex-vowel-constraints.cc b/src/hb-ot-shape-complex-vowel-constraints.cc
new file mode 100644
index 00000000..e5023392
--- /dev/null
+++ b/src/hb-ot-shape-complex-vowel-constraints.cc
@@ -0,0 +1,433 @@
+/* == Start of generated functions == */
+/*
+ * The following functions are generated by running:
+ *
+ *   ./gen-vowel-constraints.py use Scripts.txt
+ *
+ * on files with these headers:
+ *
+ * # Copied from https://docs.microsoft.com/en-us/typography/script-development/use
+ * # On October 23, 2018; with documentd dated 02/07/2018.
+ *
+ * # Scripts-11.0.0.txt
+ * # Date: 2018-02-21, 05:34:31 GMT
+ */
+
+#include "hb-ot-shape-complex-vowel-constraints.hh"
+
+static void
+_output_with_dotted_circle (hb_buffer_t *buffer)
+{
+  hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);
+  _hb_glyph_info_reset_continuation (&dottedcircle);
+
+  buffer->next_glyph ();
+}
+
+void
+_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,
+				       hb_buffer_t              *buffer,
+				       hb_font_t                *font)
+{
+  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of
+   * vowel-sequences that look like another vowel.  Data for each script
+   * collected from the USE script development spec.
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/1019
+   */
+  bool processed = false;
+  buffer->clear_output ();
+  unsigned int count = buffer->len;
+  switch ((unsigned) buffer->props.script)
+  {
+    case HB_SCRIPT_DEVANAGARI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0905u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u:
+	      case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu:
+	      case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0906u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u:
+	      case 0x0948u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0909u:
+	    matched = 0x0941u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x090Fu:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0945u: case 0x0946u: case 0x0947u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0930u:
+	    if (0x094Du == buffer->cur (1).codepoint &&
+		buffer->idx + 2 < count &&
+		0x0907u == buffer->cur (2).codepoint)
+	    {
+	      buffer->next_glyph ();
+	      buffer->next_glyph ();
+	      buffer->output_glyph (0x25CCu);
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_BENGALI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0985u:
+	    matched = 0x09BEu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x098Bu:
+	    matched = 0x09C3u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x098Cu:
+	    matched = 0x09E2u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_GURMUKHI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0A05u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0A72u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A3Fu: case 0x0A40u: case 0x0A47u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0A73u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A41u: case 0x0A42u: case 0x0A4Bu:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_GUJARATI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0A85u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u:
+	      case 0x0AC9u: case 0x0ACBu: case 0x0ACCu:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0AC5u:
+	    matched = 0x0ABEu == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_ORIYA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0B05u:
+	    matched = 0x0B3Eu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0B0Fu: case 0x0B13u:
+	    matched = 0x0B57u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TELUGU:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0C12u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0C4Cu: case 0x0C55u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0C3Fu: case 0x0C46u: case 0x0C4Au:
+	    matched = 0x0C55u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_KANNADA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0C89u: case 0x0C8Bu:
+	    matched = 0x0CBEu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0C92u:
+	    matched = 0x0CCCu == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_MALAYALAM:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0D07u: case 0x0D09u:
+	    matched = 0x0D57u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D0Eu:
+	    matched = 0x0D46u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D12u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0D3Eu: case 0x0D57u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_SINHALA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0D85u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0DCFu: case 0x0DD0u: case 0x0DD1u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0D8Bu: case 0x0D8Fu: case 0x0D94u:
+	    matched = 0x0DDFu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D8Du:
+	    matched = 0x0DD8u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D91u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0DCAu: case 0x0DD9u: case 0x0DDAu: case 0x0DDCu:
+	      case 0x0DDDu:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_BRAHMI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11005u:
+	    matched = 0x11038u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1100Bu:
+	    matched = 0x1103Eu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1100Fu:
+	    matched = 0x11042u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_KHUDAWADI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x112B0u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x112E0u: case 0x112E5u: case 0x112E6u: case 0x112E7u:
+	      case 0x112E8u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TIRHUTA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11481u:
+	    matched = 0x114B0u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1148Bu: case 0x1148Du:
+	    matched = 0x114BAu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x114AAu:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x114B5u: case 0x114B6u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_MODI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11600u: case 0x11601u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x11639u: case 0x1163Au:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TAKRI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11680u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x116ADu: case 0x116B4u: case 0x116B5u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x11686u:
+	    matched = 0x116B2u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    default:
+      break;
+  }
+  if (processed)
+  {
+    if (buffer->idx < count)
+     buffer->next_glyph ();
+    if (likely (buffer->successful))
+      buffer->swap_buffers ();
+  }
+}
+
+/* == End of generated functions == */
diff --git a/src/hb-ot-shape-complex-vowel-constraints.hh b/src/hb-ot-shape-complex-vowel-constraints.hh
index 1b07c2f4..d9082d4e 100644
--- a/src/hb-ot-shape-complex-vowel-constraints.hh
+++ b/src/hb-ot-shape-complex-vowel-constraints.hh
@@ -1,434 +1,39 @@
-/* == Start of generated functions == */
 /*
- * The following functions are generated by running:
+ * Copyright © 2018  Google, Inc.
  *
- *   ./gen-vowel-constraints.py use Scripts.txt
+ *  This is part of HarfBuzz, a text shaping library.
  *
- * on files with these headers:
+ * 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.
  *
- * <meta name="updated_at" content="2018-03-27 12:21 AM" />
- * # Scripts-11.0.0.txt
- * # Date: 2018-02-21, 05:34:31 GMT
+ * 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_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
 #define HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
 
-static void
-_output_with_dotted_circle (hb_buffer_t *buffer)
-{
-  hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);
-  _hb_glyph_info_reset_continuation (&dottedcircle);
-
-  buffer->next_glyph ();
-}
-
-static void
-preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,
-				   hb_buffer_t              *buffer,
-				   hb_font_t                *font)
-{
-  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of
-   * vowel-sequences that look like another vowel.  Data for each script
-   * collected from the USE script development spec.
-   *
-   * https://github.com/harfbuzz/harfbuzz/issues/1019
-   */
-  bool processed = false;
-  buffer->clear_output ();
-  unsigned int count = buffer->len;
-  switch ((unsigned) buffer->props.script)
-  {
-    case HB_SCRIPT_DEVANAGARI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0905u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u:
-	      case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu:
-	      case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0906u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u:
-	      case 0x0948u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0909u:
-	    matched = 0x0941u == buffer->cur (1).codepoint;
-	    break;
-	  case 0x090Fu:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0945u: case 0x0946u: case 0x0947u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0930u:
-	    if (0x094Du == buffer->cur (1).codepoint &&
-		buffer->idx + 2 < count &&
-		0x0907u == buffer->cur (2).codepoint)
-	    {
-	      buffer->next_glyph ();
-	      buffer->next_glyph ();
-	      buffer->output_glyph (0x25CCu);
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_BENGALI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0985u:
-	    matched = 0x09BEu == buffer->cur (1).codepoint;
-	    break;
-	  case 0x098Bu:
-	    matched = 0x09C3u == buffer->cur (1).codepoint;
-	    break;
-	  case 0x098Cu:
-	    matched = 0x09E2u == buffer->cur (1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_GURMUKHI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0A05u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0A72u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0A3Fu: case 0x0A40u: case 0x0A47u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0A73u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0A41u: case 0x0A42u: case 0x0A4Bu:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_GUJARATI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0A85u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u:
-	      case 0x0AC9u: case 0x0ACBu: case 0x0ACCu:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0AC5u:
-	    matched = 0x0ABEu == buffer->cur (1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_ORIYA:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0B05u:
-	    matched = 0x0B3Eu == buffer->cur (1).codepoint;
-	    break;
-	  case 0x0B0Fu: case 0x0B13u:
-	    matched = 0x0B57u == buffer->cur (1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_TELUGU:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0C12u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0C4Cu: case 0x0C55u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0C3Fu: case 0x0C46u: case 0x0C4Au:
-	    matched = 0x0C55u == buffer->cur (1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
+#include "hb.hh"
 
-    case HB_SCRIPT_KANNADA:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0C89u: case 0x0C8Bu:
-	    matched = 0x0CBEu == buffer->cur (1).codepoint;
-	    break;
-	  case 0x0C92u:
-	    matched = 0x0CCCu == buffer->cur (1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
+#include "hb-ot-shape-complex.hh"
 
-    case HB_SCRIPT_MALAYALAM:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0D07u: case 0x0D09u:
-	    matched = 0x0D57u == buffer->cur (1).codepoint;
-	    break;
-	  case 0x0D0Eu:
-	    matched = 0x0D46u == buffer->cur (1).codepoint;
-	    break;
-	  case 0x0D12u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0D3Eu: case 0x0D57u:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_SINHALA:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x0D85u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0DCFu: case 0x0DD0u: case 0x0DD1u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0D8Bu: case 0x0D8Fu: case 0x0D94u:
-	    matched = 0x0DDFu == buffer->cur (1).codepoint;
-	    break;
-	  case 0x0D8Du:
-	    matched = 0x0DD8u == buffer->cur (1).codepoint;
-	    break;
-	  case 0x0D91u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x0DCAu: case 0x0DD9u: case 0x0DDAu: case 0x0DDCu:
-	      case 0x0DDDu:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_BRAHMI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x11005u:
-	    matched = 0x11038u == buffer->cur (1).codepoint;
-	    break;
-	  case 0x1100Bu:
-	    matched = 0x1103Eu == buffer->cur (1).codepoint;
-	    break;
-	  case 0x1100Fu:
-	    matched = 0x11042u == buffer->cur (1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_KHUDAWADI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x112B0u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x112E0u: case 0x112E5u: case 0x112E6u: case 0x112E7u:
-	      case 0x112E8u:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_TIRHUTA:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x11481u:
-	    matched = 0x114B0u == buffer->cur (1).codepoint;
-	    break;
-	  case 0x1148Bu: case 0x1148Du:
-	    matched = 0x114BAu == buffer->cur (1).codepoint;
-	    break;
-	  case 0x114AAu:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x114B5u: case 0x114B6u:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_MODI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x11600u: case 0x11601u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x11639u: case 0x1163Au:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_TAKRI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur ().codepoint)
-	{
-	  case 0x11680u:
-	    switch (buffer->cur (1).codepoint)
-	    {
-	      case 0x116ADu: case 0x116B4u: case 0x116B5u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x11686u:
-	    matched = 0x116B2u == buffer->cur (1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    default:
-      break;
-  }
-  if (processed)
-  {
-    if (buffer->idx < count)
-     buffer->next_glyph ();
-    if (likely (buffer->successful))
-      buffer->swap_buffers ();
-  }
-}
+HB_INTERNAL void
+_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,
+				       hb_buffer_t              *buffer,
+				       hb_font_t                *font);
 
 #endif /* HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH */
-
-/* == End of generated functions == */
commit 205737acdc268b1c90cf00bde2d2038519a8bf48
Author: David Corbett <corbett.dav at husky.neu.edu>
Date:   Fri Oct 12 16:54:54 2018 -0400

    [use] Prohibit visually ambiguous vowel sequences

diff --git a/src/Makefile.am b/src/Makefile.am
index e0ea1c5d..782992d1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -295,6 +295,7 @@ GENERATORS = \
 	gen-os2-unicode-ranges.py \
 	gen-tag-table.py \
 	gen-use-table.py \
+	gen-vowel-constraints.py \
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
@@ -316,13 +317,17 @@ use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.tx
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-use-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-use-table.cc; false)
 
+vowel-constraints: gen-vowel-constraints.py use Scripts.txt
+	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-vowel-constraints.hh \
+	|| ($(RM) $(srcdir)/hb-ot-shape-complex-vowel-constraints.hh; false)
+
 emoji-table: gen-emoji-table.py emoji-data.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-unicode-emoji-table.hh \
 	|| ($(RM) $(srcdir)/hb-unicode-emoji-table.hh; false)
 
 built-sources: $(BUILT_SOURCES)
 
-.PHONY: unicode-tables arabic-table indic-table tag-table use-table emoji-table built-sources
+.PHONY: unicode-tables arabic-table indic-table tag-table use-table vowel-constraints emoji-table built-sources
 
 RAGEL_GENERATED = \
 	$(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \
diff --git a/src/Makefile.sources b/src/Makefile.sources
index eed245bc..b3029105 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -142,6 +142,7 @@ HB_OT_sources = \
 	hb-ot-shape-complex-use.cc \
 	hb-ot-shape-complex-use.hh \
 	hb-ot-shape-complex-use-table.cc \
+	hb-ot-shape-complex-vowel-constraints.hh \
 	hb-ot-shape-complex.hh \
 	hb-ot-shape-normalize.hh \
 	hb-ot-shape-normalize.cc \
diff --git a/src/gen-vowel-constraints.py b/src/gen-vowel-constraints.py
new file mode 100755
index 00000000..bcb5d27b
--- /dev/null
+++ b/src/gen-vowel-constraints.py
@@ -0,0 +1,286 @@
+#!/usr/bin/python
+
+"""Generator of the function to prohibit certain vowel sequences.
+
+It creates ``preprocess_text_vowel_constraints``, which inserts dotted
+circles into sequences prohibited by the USE script development spec.
+This function should be used as the ``preprocess_text`` of an
+``hb_ot_complex_shaper_t``.
+
+It also creates the helper function ``_output_with_dotted_circle``.
+"""
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import collections
+try:
+	from HTMLParser import HTMLParser
+	def write (s):
+		print (s.encode ('utf-8'), end='')
+except ImportError:
+	from html.parser import HTMLParser
+	def write (s):
+		sys.stdout.flush ()
+		sys.stdout.buffer.write (s.encode ('utf-8'))
+import itertools
+import io
+import sys
+
+if len (sys.argv) != 3:
+	print ('usage: ./gen-vowel-constraints.py use Scripts.txt', file=sys.stderr)
+	sys.exit (1)
+
+try:
+	from html import unescape
+	def html_unescape (parser, entity):
+		return unescape (entity)
+except ImportError:
+	def html_unescape (parser, entity):
+		return parser.unescape (entity)
+
+def expect (condition, message=None):
+	if not condition:
+		if message is None:
+			raise AssertionError
+		raise AssertionError (message)
+
+with io.open (sys.argv[2], encoding='utf-8') as f:
+	scripts_header = [f.readline () for i in range (2)]
+	scripts = {}
+	script_order = {}
+	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)
+		script = fields[1]
+		for u in range (start, end + 1):
+			scripts[u] = script
+		if script not in script_order:
+			script_order[script] = start
+
+class ConstraintSet (object):
+	"""A set of prohibited code point sequences.
+
+	Args:
+		constraint (List[int]): A prohibited code point sequence.
+
+	"""
+	def __init__ (self, constraint):
+		# Either a list or a dictionary. As a list of code points, it
+		# represents a prohibited code point sequence. As a dictionary,
+		# it represents a set of prohibited sequences, where each item
+		# represents the set of prohibited sequences starting with the
+		# key (a code point) concatenated with any of the values
+		# (ConstraintSets).
+		self._c = constraint
+
+	def add (self, constraint):
+		"""Add a constraint to this set."""
+		if not constraint:
+			return
+		first = constraint[0]
+		rest = constraint[1:]
+		if isinstance (self._c, list):
+			if constraint == self._c[:len (constraint)]:
+				self._c = constraint
+			elif self._c != constraint[:len (self._c)]:
+				self._c = {self._c[0]: ConstraintSet (self._c[1:])}
+		if isinstance (self._c, dict):
+			if first in self._c:
+				self._c[first].add (rest)
+			else:
+				self._c[first] = ConstraintSet (rest)
+
+	def _indent (self, depth):
+		return ('  ' * depth).replace ('        ', '\t')
+
+	def __str__ (self, index=0, depth=4):
+		s = []
+		indent = self._indent (depth)
+		if isinstance (self._c, list):
+			if len (self._c) == 0:
+				s.append ('{}matched = true;\n'.format (indent))
+			elif len (self._c) == 1:
+				s.append ('{}matched = 0x{:04X}u == buffer->cur ({}).codepoint;\n'.format (indent, next (iter (self._c)), index or ''))
+			else:
+				s.append ('{}if (0x{:04X}u == buffer->cur ({}).codepoint &&\n'.format (indent, self._c[0], index))
+				s.append ('{}buffer->idx + {} < count &&\n'.format (self._indent (depth + 2), len (self._c)))
+				for i, cp in enumerate (self._c[1:], start=1):
+					s.append ('{}0x{:04X}u == buffer->cur ({}).codepoint{}\n'.format (
+						self._indent (depth + 2), cp, index + i, ')' if i == len (self._c) - 1 else ' &&'))
+				s.append ('{}{{\n'.format (indent))
+				for i in range (len (self._c)):
+					s.append ('{}buffer->next_glyph ();\n'.format (self._indent (depth + 1)))
+				s.append ('{}buffer->output_glyph (0x25CCu);\n'.format (self._indent (depth + 1)))
+				s.append ('{}}}\n'.format (indent))
+		else:
+			s.append ('{}switch (buffer->cur ({}).codepoint)\n'.format(indent, index or ''))
+			s.append ('{}{{\n'.format (indent))
+			cases = collections.defaultdict (set)
+			for first, rest in sorted (self._c.items ()):
+				cases[rest.__str__ (index + 1, depth + 2)].add (first)
+			for body, labels in sorted (cases.items (), key=lambda b_ls: sorted (b_ls[1])[0]):
+				for i, cp in enumerate (sorted (labels)):
+					if i % 4 == 0:
+						s.append (self._indent (depth + 1))
+					else:
+						s.append (' ')
+					s.append ('case 0x{:04X}u:{}'.format (cp, '\n' if i % 4 == 3 else ''))
+				if len (labels) % 4 != 0:
+					s.append ('\n')
+				s.append (body)
+				s.append ('{}break;\n'.format (self._indent (depth + 2)))
+			s.append ('{}}}\n'.format (indent))
+		return ''.join (s)
+
+class USESpecParser (HTMLParser):
+	"""A parser for the USE script development spec.
+
+	Attributes:
+		header (str): The ``updated_at`` timestamp of the spec.
+		constraints (Mapping[str, ConstraintSet]): A map of script names
+			to the scripts' prohibited sequences.
+	"""
+	def __init__ (self):
+		HTMLParser.__init__ (self)
+		self.header = ''
+		self.constraints = {}
+		# Whether the next <code> contains the vowel constraints.
+		self._primed = False
+		# Whether the parser is in the <code> element with the constraints.
+		self._in_constraints = False
+		# The text of the constraints.
+		self._constraints = ''
+
+	def handle_starttag (self, tag, attrs):
+		if tag == 'meta':
+			for attr, value in attrs:
+				if attr == 'name' and value == 'updated_at':
+					self.header = self.get_starttag_text ()
+					break
+		elif tag == 'a':
+			for attr, value in attrs:
+				if attr == 'id' and value == 'ivdvconstraints':
+					self._primed = True
+					break
+		elif self._primed and tag == 'code':
+			self._primed = False
+			self._in_constraints = True
+
+	def handle_endtag (self, tag):
+		self._in_constraints = False
+
+	def handle_data (self, data):
+		if self._in_constraints:
+			self._constraints += data
+
+	def handle_charref (self, name):
+		self.handle_data (html_unescape (self, '&#%s;' % name))
+
+	def handle_entityref (self, name):
+		self.handle_data (html_unescape (self, '&%s;' % name))
+
+	def parse (self, filename):
+		"""Parse the USE script development spec.
+
+		Args:
+			filename (str): The file name of the spec.
+		"""
+		with io.open (filename, encoding='utf-8') as f:
+			self.feed (f.read ())
+		expect (self.header, 'No header found')
+		for line in self._constraints.splitlines ():
+			constraint = [int (cp, 16) for cp in line.split (';')[0].strip ().split (' ')]
+			expect (2 <= len (constraint), 'Prohibited sequence is too short: {}'.format (constraint))
+			script = scripts[constraint[0]]
+			if script in self.constraints:
+				self.constraints[script].add (constraint)
+			else:
+				self.constraints[script] = ConstraintSet (constraint)
+		expect (self.constraints, 'No constraints found')
+
+use_parser = USESpecParser ()
+use_parser.parse (sys.argv[1])
+
+print ('/* == Start of generated functions == */')
+print ('/*')
+print (' * The following functions are generated by running:')
+print (' *')
+print (' *   %s use Scripts.txt' % sys.argv[0])
+print (' *')
+print (' * on files with these headers:')
+print (' *')
+print (' * %s' % use_parser.header.strip ())
+for line in scripts_header:
+	print (' * %s' % line.strip ())
+print (' */')
+print ()
+print ('#ifndef HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH')
+print ('#define HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH')
+print ()
+
+print ('static void')
+print ('_output_with_dotted_circle (hb_buffer_t *buffer)')
+print ('{')
+print ('  hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);')
+print ('  _hb_glyph_info_reset_continuation (&dottedcircle);')
+print ()
+print ('  buffer->next_glyph ();')
+print ('}')
+print ()
+
+print ('static void')
+print ('preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,')
+print ('\t\t\t\t   hb_buffer_t              *buffer,')
+print ('\t\t\t\t   hb_font_t                *font)')
+print ('{')
+print ('  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of')
+print ('   * vowel-sequences that look like another vowel.  Data for each script')
+print ('   * collected from the USE script development spec.')
+print ('   *')
+print ('   * https://github.com/harfbuzz/harfbuzz/issues/1019')
+print ('   */')
+print ('  bool processed = false;')
+print ('  buffer->clear_output ();')
+print ('  unsigned int count = buffer->len;')
+print ('  switch ((unsigned) buffer->props.script)')
+print ('  {')
+
+for script, constraints in sorted (use_parser.constraints.items (), key=lambda s_c: script_order[s_c[0]]):
+	print ('    case HB_SCRIPT_{}:'.format (script.upper ()))
+	print ('      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)')
+	print ('      {')
+	print ('\tbool matched = false;')
+	write (str (constraints))
+	print ('\tbuffer->next_glyph ();')
+	print ('\tif (matched) _output_with_dotted_circle (buffer);')
+	print ('      }')
+	print ('      processed = true;')
+	print ('      break;')
+	print ()
+
+print ('    default:')
+print ('      break;')
+print ('  }')
+print ('  if (processed)')
+print ('  {')
+print ('    if (buffer->idx < count)')
+print ('     buffer->next_glyph ();')
+print ('    if (likely (buffer->successful))')
+print ('      buffer->swap_buffers ();')
+print ('  }')
+print ('}')
+
+print ()
+print ('#endif /* HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH */')
+print ()
+print ('/* == End of generated functions == */')
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index f1ae303a..092ac684 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -25,6 +25,7 @@
  */
 
 #include "hb-ot-shape-complex-indic.hh"
+#include "hb-ot-shape-complex-vowel-constraints.hh"
 #include "hb-ot-layout.hh"
 
 
@@ -331,275 +332,6 @@ data_destroy_indic (void *data)
   free (data);
 }
 
-static void
-_output_with_dotted_circle (hb_buffer_t *buffer)
-{
-  hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);
-  _hb_glyph_info_reset_continuation (&dottedcircle);
-
-  buffer->next_glyph ();
-}
-
-static void
-preprocess_text_indic (const hb_ot_shape_plan_t *plan,
-		       hb_buffer_t              *buffer,
-		       hb_font_t                *font)
-{
-  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of
-   * vowel-sequences that look like another vowel.  Data for each script
-   * collected from Unicode 11 book, tables named "Vowel Letters" with
-   * "Use" and "Do Not Use" columns.
-   *
-   * https://github.com/harfbuzz/harfbuzz/issues/1019
-   */
-  bool processed = false;
-  buffer->clear_output ();
-  unsigned int count = buffer->len;
-  switch ((unsigned) buffer->props.script)
-  {
-    case HB_SCRIPT_DEVANAGARI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0905u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u:
-	      case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu:
-	      case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0906u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u:
-	      case 0x0948u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0909u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0941u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x090Fu:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0945u: case 0x0946u: case 0x0947u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0930u:
-	    if (0x094Du == buffer->cur(1).codepoint &&
-		buffer->idx + 2 < count &&
-	        0x0907u == buffer->cur(2).codepoint)
-	    {
-	      buffer->next_glyph ();
-	      buffer->next_glyph ();
-	      buffer->output_glyph (0x25CCu);
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_BENGALI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0985u:
-	    matched = 0x09BE == buffer->cur(1).codepoint;
-	    break;
-	  case 0x098Bu:
-	    matched = 0x09C3 == buffer->cur(1).codepoint;
-	    break;
-	  case 0x098Cu:
-	    matched = 0x09E2 == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_GURMUKHI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0A05u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0A72u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0A3Fu: case 0x0A40u: case 0x0A47u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0A73u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0A41u: case 0x0A42u: case 0x0A4Bu:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_GUJARATI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0A85u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u:
-	      case 0x0AC9u: case 0x0ACBu: case 0x0ACCu:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0AC5u:
-	    matched = 0x0ABE == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_ORIYA:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0B05u:
-	    matched = 0x0B3E == buffer->cur(1).codepoint;
-	    break;
-	  case 0x0B0Fu: case 0x0B13u:
-	    matched = 0x0B57 == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_TELUGU:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0C12u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0C4Cu: case 0x0C55u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0C3Fu: case 0x0C46u: case 0xC4Au:
-	    matched = 0x0C55 == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_KANNADA:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0C89u: case 0x0C8Bu:
-	    matched = 0x0CBE == buffer->cur(1).codepoint;
-	    break;
-	  case 0x0C92u:
-	    matched = 0x0CCC == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_MALAYALAM:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0D07u: case 0x0D09u:
-	    matched = 0x0D57 == buffer->cur(1).codepoint;
-	    break;
-	  case 0x0D0Eu:
-	    matched = 0x0D46 == buffer->cur(1).codepoint;
-	    break;
-	  case 0x0D12u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0D3Eu: case 0x0D57u:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    default:
-      break;
-  }
-  if (processed)
-  {
-    if (buffer->idx < count)
-      buffer->next_glyph ();
-    if (likely (buffer->successful))
-      buffer->swap_buffers ();
-  }
-}
-
 static indic_position_t
 consonant_position_from_face (const indic_shape_plan_t *indic_plan,
 			      const hb_codepoint_t consonant,
@@ -1884,7 +1616,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
   override_features_indic,
   data_create_indic,
   data_destroy_indic,
-  preprocess_text_indic,
+  preprocess_text_vowel_constraints,
   nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   decompose_indic,
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index f9a580ca..8c44fe01 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -28,6 +28,7 @@
 
 #include "hb-ot-shape-complex-use.hh"
 #include "hb-ot-shape-complex-arabic.hh"
+#include "hb-ot-shape-complex-vowel-constraints.hh"
 
 /* buffer var allocations */
 #define use_category() complex_var_u8_0()
@@ -591,7 +592,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use =
   nullptr, /* override_features */
   data_create_use,
   data_destroy_use,
-  nullptr, /* preprocess_text */
+  preprocess_text_vowel_constraints,
   nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   nullptr, /* decompose */
diff --git a/src/hb-ot-shape-complex-vowel-constraints.hh b/src/hb-ot-shape-complex-vowel-constraints.hh
new file mode 100644
index 00000000..1b07c2f4
--- /dev/null
+++ b/src/hb-ot-shape-complex-vowel-constraints.hh
@@ -0,0 +1,434 @@
+/* == Start of generated functions == */
+/*
+ * The following functions are generated by running:
+ *
+ *   ./gen-vowel-constraints.py use Scripts.txt
+ *
+ * on files with these headers:
+ *
+ * <meta name="updated_at" content="2018-03-27 12:21 AM" />
+ * # Scripts-11.0.0.txt
+ * # Date: 2018-02-21, 05:34:31 GMT
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
+#define HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
+
+static void
+_output_with_dotted_circle (hb_buffer_t *buffer)
+{
+  hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);
+  _hb_glyph_info_reset_continuation (&dottedcircle);
+
+  buffer->next_glyph ();
+}
+
+static void
+preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,
+				   hb_buffer_t              *buffer,
+				   hb_font_t                *font)
+{
+  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of
+   * vowel-sequences that look like another vowel.  Data for each script
+   * collected from the USE script development spec.
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/1019
+   */
+  bool processed = false;
+  buffer->clear_output ();
+  unsigned int count = buffer->len;
+  switch ((unsigned) buffer->props.script)
+  {
+    case HB_SCRIPT_DEVANAGARI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0905u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u:
+	      case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu:
+	      case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0906u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u:
+	      case 0x0948u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0909u:
+	    matched = 0x0941u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x090Fu:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0945u: case 0x0946u: case 0x0947u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0930u:
+	    if (0x094Du == buffer->cur (1).codepoint &&
+		buffer->idx + 2 < count &&
+		0x0907u == buffer->cur (2).codepoint)
+	    {
+	      buffer->next_glyph ();
+	      buffer->next_glyph ();
+	      buffer->output_glyph (0x25CCu);
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_BENGALI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0985u:
+	    matched = 0x09BEu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x098Bu:
+	    matched = 0x09C3u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x098Cu:
+	    matched = 0x09E2u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_GURMUKHI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0A05u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0A72u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A3Fu: case 0x0A40u: case 0x0A47u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0A73u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A41u: case 0x0A42u: case 0x0A4Bu:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_GUJARATI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0A85u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u:
+	      case 0x0AC9u: case 0x0ACBu: case 0x0ACCu:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0AC5u:
+	    matched = 0x0ABEu == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_ORIYA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0B05u:
+	    matched = 0x0B3Eu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0B0Fu: case 0x0B13u:
+	    matched = 0x0B57u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TELUGU:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0C12u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0C4Cu: case 0x0C55u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0C3Fu: case 0x0C46u: case 0x0C4Au:
+	    matched = 0x0C55u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_KANNADA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0C89u: case 0x0C8Bu:
+	    matched = 0x0CBEu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0C92u:
+	    matched = 0x0CCCu == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_MALAYALAM:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0D07u: case 0x0D09u:
+	    matched = 0x0D57u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D0Eu:
+	    matched = 0x0D46u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D12u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0D3Eu: case 0x0D57u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_SINHALA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0D85u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0DCFu: case 0x0DD0u: case 0x0DD1u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0D8Bu: case 0x0D8Fu: case 0x0D94u:
+	    matched = 0x0DDFu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D8Du:
+	    matched = 0x0DD8u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D91u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0DCAu: case 0x0DD9u: case 0x0DDAu: case 0x0DDCu:
+	      case 0x0DDDu:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_BRAHMI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11005u:
+	    matched = 0x11038u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1100Bu:
+	    matched = 0x1103Eu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1100Fu:
+	    matched = 0x11042u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_KHUDAWADI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x112B0u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x112E0u: case 0x112E5u: case 0x112E6u: case 0x112E7u:
+	      case 0x112E8u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TIRHUTA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11481u:
+	    matched = 0x114B0u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1148Bu: case 0x1148Du:
+	    matched = 0x114BAu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x114AAu:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x114B5u: case 0x114B6u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_MODI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11600u: case 0x11601u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x11639u: case 0x1163Au:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TAKRI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11680u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x116ADu: case 0x116B4u: case 0x116B5u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x11686u:
+	    matched = 0x116B2u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    default:
+      break;
+  }
+  if (processed)
+  {
+    if (buffer->idx < count)
+     buffer->next_glyph ();
+    if (likely (buffer->successful))
+      buffer->swap_buffers ();
+  }
+}
+
+#endif /* HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH */
+
+/* == End of generated functions == */
diff --git a/test/shaping/README.md b/test/shaping/README.md
index 99498e60..f386fb96 100644
--- a/test/shaping/README.md
+++ b/test/shaping/README.md
@@ -25,10 +25,10 @@ what this does is:
   * If the outputs differ, recording fails.  Otherwise, it will move the
     subset font file into `data/in-house/fonts` and name it after its
     hash, and print out the test case input, which you can then redirect
-    to an existing or new test file in `data/in-house/tests` using `-o=`,
+    to an existing or new test file in `data/in-house/tests` using `-o`,
     e.g.:
 ```sh
-$ ./hb-unicode-encode 41 42 43 627 | ./record-test.sh -o=data/in-house/tests/test-name.test ../../util/hb-shape font.ttf
+$ ./hb-unicode-encode 41 42 43 627 | ./record-test.sh -o data/in-house/tests/test-name.test ../../util/hb-shape font.ttf
 ```
 
 If you created a new test file, add it to `data/in-house/Makefile.sources`
diff --git a/test/shaping/data/in-house/fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf b/test/shaping/data/in-house/fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf
new file mode 100644
index 00000000..7d488a31
Binary files /dev/null and b/test/shaping/data/in-house/fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf differ
diff --git a/test/shaping/data/in-house/tests/use-vowel-letter-spoofing.tests b/test/shaping/data/in-house/tests/use-vowel-letter-spoofing.tests
new file mode 100644
index 00000000..45cf80e8
--- /dev/null
+++ b/test/shaping/data/in-house/tests/use-vowel-letter-spoofing.tests
@@ -0,0 +1,94 @@
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+093A:[uni0905=0+500|uni25CC=0+500|uni093A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+093B:[uni0905=0+500|uni25CC=0+500|uni093B=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+093E:[uni0905=0+500|uni25CC=0+500|uni093E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0945:[uni0905=0+500|uni25CC=0+500|uni0945=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0946:[uni0905=0+500|uni25CC=0+500|uni0946=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0949:[uni0905=0+500|uni25CC=0+500|uni0949=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+094A:[uni0905=0+500|uni25CC=0+500|uni094A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+094B:[uni0905=0+500|uni25CC=0+500|uni094B=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+094C:[uni0905=0+500|uni25CC=0+500|uni094C=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+094F:[uni0905=0+500|uni25CC=0+500|uni094F=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0956:[uni0905=0+500|uni25CC=0+500|uni0956=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0957:[uni0905=0+500|uni25CC=0+500|uni0957=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+093A:[uni0906=0+500|uni25CC=0+500|uni093A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+0945:[uni0906=0+500|uni25CC=0+500|uni0945=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+0946:[uni0906=0+500|uni25CC=0+500|uni0946=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+0947:[uni0906=0+500|uni25CC=0+500|uni0947=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+0948:[uni0906=0+500|uni25CC=0+500|uni0948=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0909,U+0941:[uni0909=0+500|uni25CC=0+500|uni0941=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+090F,U+0945:[uni090F=0+500|uni25CC=0+500|uni0945=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+090F,U+0946:[uni090F=0+500|uni25CC=0+500|uni0946=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+090F,U+0947:[uni090F=0+500|uni25CC=0+500|uni0947=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0930,U+094D,U+0907:[uni0930=0+500|uni094D=0+500|uni25CC=2+500|uni0907=2+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0985,U+09BE:[uni0985=0+500|uni25CC=0+500|.notdef=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+098B,U+09C3:[uni098B=0+500|uni25CC=0+500|uni09C3=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+098C,U+09E2:[uni098C=0+500|uni25CC=0+500|uni09E2=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A05,U+0A3E:[uni0A05=0+500|uni25CC=0+500|uni0A3E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A05,U+0A48:[uni0A05=0+500|uni25CC=0+500|uni0A48=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A05,U+0A4C:[uni0A05=0+500|uni25CC=0+500|uni0A4C=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A72,U+0A3F:[uni0A72=0+500|uni0A3F=0+500|uni25CC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A72,U+0A40:[uni0A72=0+500|uni25CC=0+500|uni0A40=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A72,U+0A47:[uni0A72=0+500|uni25CC=0+500|uni0A47=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A73,U+0A41:[uni0A73=0+500|uni25CC=0+500|uni0A41=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A73,U+0A42:[uni0A73=0+500|uni25CC=0+500|uni0A42=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A73,U+0A4B:[uni0A73=0+500|uni25CC=0+500|uni0A4B=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ABE,U+0AC5:[uni0A85=0+500|uni25CC=0+500|uni0ABE=0+500|uni25CC=0+500|uni0AC5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ABE,U+0AC8:[uni0A85=0+500|uni25CC=0+500|uni0ABE=0+500|uni25CC=0+500|uni0AC8=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ABE:[uni0A85=0+500|uni25CC=0+500|uni0ABE=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0AC5:[uni0A85=0+500|uni25CC=0+500|uni0AC5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0AC7:[uni0A85=0+500|uni25CC=0+500|uni0AC7=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0AC8:[uni0A85=0+500|uni25CC=0+500|uni0AC8=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0AC9:[uni0A85=0+500|uni25CC=0+500|uni0AC9=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ACB:[uni0A85=0+500|uni25CC=0+500|uni0ACB=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ACC:[uni0A85=0+500|uni25CC=0+500|uni0ACC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0AC5,U+0ABE:[uni25CC=0+500|uni0AC5=0+500|uni25CC=0+500|uni0ABE=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0B05,U+0B3E:[uni0B05=0+500|uni25CC=0+500|uni0B3E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0B0F,U+0B57:[uni0B0F=0+500|uni25CC=0+500|uni0B57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0B13,U+0B57:[uni0B13=0+500|uni25CC=0+500|uni0B57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C12,U+0C4C:[uni0C12=0+500|uni25CC=0+500|uni0C4C=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C12,U+0C55:[uni0C12=0+500|uni25CC=0+500|uni0C55=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C3F,U+0C55:[uni25CC=0+500|uni0C3F=0+500|uni25CC=0+500|uni0C55=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C46,U+0C55:[uni25CC=0+500|uni0C46=0+500|uni25CC=0+500|uni0C55=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C4A,U+0C55:[uni25CC=0+500|uni0C4A=0+500|uni25CC=0+500|uni0C55=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C89,U+0CBE:[uni0C89=0+500|uni25CC=0+500|uni0CBE=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C8B,U+0CBE:[uni0C8B=0+500|uni25CC=0+500|uni0CBE=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C92,U+0CCC:[uni0C92=0+500|uni25CC=0+500|uni0CCC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D07,U+0D57:[uni0D07=0+500|uni25CC=0+500|uni0D57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D09,U+0D57:[uni0D09=0+500|uni25CC=0+500|uni0D57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D0E,U+0D46:[uni0D0E=0+500|uni0D46=0+500|uni25CC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D12,U+0D3E:[uni0D12=0+500|uni25CC=0+500|uni0D3E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D12,U+0D57:[uni0D12=0+500|uni25CC=0+500|uni0D57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D85,U+0DCF:[uni0D85=0+500|uni25CC=0+500|uni0DCF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D85,U+0DD0:[uni0D85=0+500|uni25CC=0+500|uni0DD0=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D85,U+0DD1:[uni0D85=0+500|uni25CC=0+500|uni0DD1=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D8B,U+0DDF:[uni0D8B=0+500|uni25CC=0+500|uni0DDF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D8D,U+0DD8:[uni0D8D=0+500|uni25CC=0+500|uni0DD8=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D8F,U+0DDF:[uni0D8F=0+500|uni25CC=0+500|uni0DDF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DCA:[uni0D91=0+500|uni25CC=0+500|uni0DCA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DD9:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DDA:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500|uni0DCA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DDC:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500|uni0DCF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DDD:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500|uni0DCF=0+500|uni0DCA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DDD:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500|uni0DCF=0+500|uni0DCA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D94,U+0DDF:[uni0D94=0+500|uni25CC=0+500|uni0DDF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11005,U+11038:[u11005=0+500|uni25CC=0+500|u11038=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+1100B,U+1103E:[u1100B=0+500|uni25CC=0+500|u1103E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+1100F,U+11042:[u1100F=0+500|uni25CC=0+500|u11042=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E0:[u112B0=0+500|uni25CC=0+500|u112E0=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E5:[u112B0=0+500|uni25CC=0+500|u112E5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E6:[u112B0=0+500|uni25CC=0+500|u112E6=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E7:[u112B0=0+500|uni25CC=0+500|u112E7=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E8:[u112B0=0+500|uni25CC=0+500|u112E8=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11481,U+114B0:[u11481=0+500|uni25CC=0+500|u114B0=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+1148B,U+114BA:[u1148B=0+500|uni25CC=0+500|u114BA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+1148D,U+114BA:[u1148D=0+500|uni25CC=0+500|u114BA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+114AA,U+114B5:[u114AA=0+500|uni25CC=0+500|u114B5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+114AA,U+114B6:[u114AA=0+500|uni25CC=0+500|u114B6=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11600,U+11639:[u11600=0+500|uni25CC=0+500|u11639=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11600,U+1163A:[u11600=0+500|uni25CC=0+500|u1163A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11601,U+11639:[u11601=0+500|uni25CC=0+500|u11639=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11601,U+1163A:[u11601=0+500|uni25CC=0+500|u1163A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11680,U+116AD:[u11680=0+500|uni25CC=0+500|u116AD=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11680,U+116B4:[u11680=0+500|uni25CC=0+500|u116B4=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11680,U+116B5:[u11680=0+500|uni25CC=0+500|u116B5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11686,U+116B2:[u11686=0+500|uni25CC=0+500|u116B2=0+500]


More information about the HarfBuzz mailing list