text:
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TLRichTextEditSelection" inherits="Reference" category="Core" version="3.2">
	<brief_description>
		Selection and caret info of the [TLRichTextEdit]
	</brief_description>
	<description>
	</description>
	<tutorials>
	</tutorials>
	<methods>
	</methods>
	<members>
		<member name="caret_offset" type="int" setter="set_caret_offset" getter="get_caret_offset" default="0">
			Caret position in the paragraph [member caret_para].
		</member>
		<member name="caret_para" type="int" setter="set_caret_para" getter="get_caret_para" default="0">
			Paragraph where caret currently is in.
		</member>
		<member name="end_offset" type="int" setter="set_end_offset" getter="get_end_offset" default="0">
			Offset of the end of selection in the paragraph [member end_para].
		</member>
		<member name="end_para" type="int" setter="set_end_para" getter="get_end_para" default="0">
			Last paragraph in the selection.
		</member>
		<member name="start_offset" type="int" setter="set_start_offset" getter="get_start_offset" default="0">
			Offset of the start of selection in the paragraph [member start_para].
		</member>
		<member name="start_para" type="int" setter="set_start_para" getter="get_start_para" default="0">
			First paragraph in the selection.
		</member>
	</members>
	<signals>
		<signal name="selection_changed">
			<description>
				Emitted when selection range is changed or caret moved.
			</description>
		</signal>
	</signals>
	<constants>
	</constants>
</class>


text:
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TLLineEdit" inherits="Control" category="Core" version="3.2">
	<brief_description>
		Control that provides single-line string editing. For formatted or/and multiline text, use [TLRichTextEdit].
		Copy of Godot [LineEdit] with the shaping support. TLLineEdit uses the same theme items as the [LineEdit].
	</brief_description>
	<description>
		TLLineEdit provides a single-line string editor, used for text fields. It features many built-in shortcuts which will always be available:
		- Ctrl + C: Copy
		- Ctrl + X: Cut
		- Ctrl + V or Ctrl + Y: Paste/"yank"
		- Ctrl + Z: Undo
		- Ctrl + Shift + Z: Redo
		- Ctrl + U: Delete text from the cursor position to the beginning of the line
		- Ctrl + K: Delete text from the cursor position to the end of the line
		- Ctrl + A: Select all text
		- Ctrl + D: Swap current input direction (primary cursor)
		- Up/Down arrow: Move the cursor to the beginning/end of the line
	</description>
	<tutorials>
	</tutorials>
	<methods>
		<method name="append_at_cursor">
			<return type="void">
			</return>
			<argument index="0" name="text" type="String">
			</argument>
			<description>
				Adds [code]text[/code] after the cursor. If the resulting value is longer than [member max_length], nothing happens.
			</description>
		</method>
		<method name="clear">
			<return type="void">
			</return>
			<description>
				Erases the [TLLineEdit] text.
			</description>
		</method>
		<method name="deselect">
			<return type="void">
			</return>
			<description>
				Clears the current selection.
			</description>
		</method>
		<method name="get_menu" qualifiers="const">
			<return type="PopupMenu">
			</return>
			<description>
				Returns the [PopupMenu] of this [TLLineEdit]. By default, this menu is displayed when right-clicking on the [TLLineEdit].
			</description>
		</method>
		<method name="menu_option">
			<return type="void">
			</return>
			<argument index="0" name="option" type="int">
			</argument>
			<description>
				Executes a given action as defined in the[code]MENU_*[/code] enum.
			</description>
		</method>
		<method name="select">
			<return type="void">
			</return>
			<argument index="0" name="from" type="int" default="0">
			</argument>
			<argument index="1" name="to" type="int" default="-1">
			</argument>
			<description>
				Selects characters inside [TLLineEdit] between [code]from[/code] and [code]to[/code]. By default, [code]from[/code] is at the beginning and [code]to[/code] at the end.
				[codeblock]
				text = "Welcome"
				select()     # Will select "Welcome"
				select(4)    # Will select "ome"
				select(2, 5) # Will select "lco"
				[/codeblock]
			</description>
		</method>
		<method name="select_all">
			<return type="void">
			</return>
			<description>
				Selects the whole [String].
			</description>
		</method>
	</methods>
	<members>
		<member name="align" type="int" setter="set_align" getter="get_align" default="0">
			Text alignment as defined in the [code]ALIGN_*[/code] enum.
		</member>
		<member name="base_font" type="TLFontFamily" setter="set_base_font" getter="get_base_font">
			Base font.
		</member>
		<member name="base_font_size" type="int" setter="set_base_font_size" getter="get_base_font_size" default="12">
			Base font size.
		</member>
		<member name="base_font_style" type="String" setter="set_base_font_style" getter="get_base_font_style" default=""Regular"">
			Base font style.
		</member>
		<member name="caret_blink" type="bool" setter="cursor_set_blink_enabled" getter="cursor_get_blink_enabled" default="false">
			If [code]true[/code], the caret (visual cursor) blinks.
		</member>
		<member name="caret_blink_speed" type="float" setter="cursor_set_blink_speed" getter="cursor_get_blink_speed" default="0.65">
			Duration (in seconds) of a caret's blinking cycle.
		</member>
		<member name="caret_position" type="int" setter="set_cursor_position" getter="get_cursor_position" default="0">
			The cursor's position inside the [TLLineEdit]. When set, the text may scroll to accommodate it.
		</member>
		<member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled" default="false">
			If [code]true[/code], the [TLLineEdit] will show a clear button if [code]text[/code] is not empty.
		</member>
		<member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled" default="true">
			If [code]true[/code], the context menu will appear when right-clicked.
		</member>
		<member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true">
			If [code]false[/code], existing text cannot be modified and new text cannot be added.
		</member>
		<member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length" getter="get_expand_to_text_length" default="false">
			If [code]true[/code], the [TLLineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened.
		</member>
		<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" default="2" />
		<member name="language" type="String" setter="set_language" getter="get_language" default="""">
			Language code for line-breaking and text shaping algorithms.
		</member>
		<member name="max_length" type="int" setter="set_max_length" getter="get_max_length" default="0">
			Maximum amount of characters that can be entered inside the [LineEdit]. If [code]0[/code], there is no limit.
		</member>
		<member name="mouse_default_cursor_shape" type="int" setter="set_default_cursor_shape" getter="get_default_cursor_shape" override="true" enum="Control.CursorShape" default="1" />
		<member name="ot_features" type="String" setter="set_ot_features" getter="get_ot_features" default="""">
			Comma separated list of OpenType feature tags.
		</member>
		<member name="placeholder_alpha" type="float" setter="set_placeholder_alpha" getter="get_placeholder_alpha" default="0.6">
			Opacity of the [member placeholder_text]. From [code]0[/code] to [code]1[/code].
		</member>
		<member name="placeholder_text" type="String" setter="set_placeholder" getter="get_placeholder" default="""">
			Text shown when the [TLLineEdit] is empty. It is [b]not[/b] the [TLLineEdit]'s default value (see [member text]).
		</member>
		<member name="secret" type="bool" setter="set_secret" getter="is_secret" default="false">
			If [code]true[/code], every character is replaced with the secret character (see [member secret_character]).
		</member>
		<member name="secret_character" type="String" setter="set_secret_character" getter="get_secret_character" default=""*"">
			The character to use to mask secret input (defaults to "*"). Only a single character can be used as the secret character.
		</member>
		<member name="text" type="String" setter="set_text" getter="get_text" default="""">
			String value of the [TLLineEdit].
		</member>
		<member name="text_direction" type="int" setter="set_text_direction" getter="get_text_direction" default="3">
			Base direction of the text.
		</member>
	</members>
	<signals>
		<signal name="text_changed">
			<argument index="0" name="new_text" type="String">
			</argument>
			<description>
				Emitted when the text changes.
			</description>
		</signal>
		<signal name="text_entered">
			<argument index="0" name="new_text" type="String">
			</argument>
			<description>
				Emitted when the user presses [code]KEY_ENTER[/code] on the [TLLineEdit].
			</description>
		</signal>
	</signals>
	<constants>
		<constant name="ALIGN_LEFT" value="0" enum="Align">
			Aligns the text on the left-hand side of the [TLLineEdit].
		</constant>
		<constant name="ALIGN_CENTER" value="1" enum="Align">
			Centers the text in the middle of the [TLLineEdit].
		</constant>
		<constant name="ALIGN_RIGHT" value="2" enum="Align">
			Aligns the text on the right-hand side of the [TLLineEdit].
		</constant>
		<constant name="ALIGN_FILL" value="3" enum="Align">
			Stretches white spaces to fit the [TLLineEdit]'s width.
		</constant>
		<constant name="MENU_CUT" value="0" enum="MenuItems">
			Cuts (copies and clears) the selected text.
		</constant>
		<constant name="MENU_COPY" value="1" enum="MenuItems">
			Copies the selected text.
		</constant>
		<constant name="MENU_PASTE" value="2" enum="MenuItems">
			Pastes the clipboard text over the selected text (or at the cursor's position).
			Non-printable escape characters are automatically stripped from the OS clipboard via [code]String.strip_escapes[/code].
		</constant>
		<constant name="MENU_CLEAR" value="3" enum="MenuItems">
			Erases the whole [TLLineEdit] text.
		</constant>
		<constant name="MENU_SELECT_ALL" value="4" enum="MenuItems">
			Selects the whole [TLLineEdit] text.
		</constant>
		<constant name="MENU_UNDO" value="5" enum="MenuItems">
			Undoes the previous action.
		</constant>
		<constant name="MENU_REDO" value="6" enum="MenuItems">
			Reverse the last undo action.
		</constant>
		<constant name="MENU_MAX" value="7" enum="MenuItems">
			Represents the size of the [enum MenuItems] enum.
		</constant>
	</constants>
</class>

repo_language: C

mime_type: text/x-c


text:
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TLShapedParagraph" inherits="Resource" category="Core" version="3.2">
	<brief_description>
		Class for formatting entire paragraphs of text at once.
	</brief_description>
	<description>
	</description>
	<tutorials>
	</tutorials>
	<methods>
		<method name="copy_properties">
			<return type="void">
			</return>
			<argument index="0" name="source" type="TLShapedParagraph">
			</argument>
			<description>
				Copies properties of the another paragraph.
			</description>
		</method>
		<method name="get_line" qualifiers="const">
			<return type="TLShapedAttributedString">
			</return>
			<argument index="0" name="index" type="int">
			</argument>
			<description>
				Returns line of the pargraphs. NOTE: read only, changes to the line will be discarded on paragraph update!
			</description>
		</method>
		<method name="get_line_bounds" qualifiers="const">
			<return type="Array">
			</return>
			<description>
				Returns array of the line boundaries as [code]Vector2(start, end)[/code].
			</description>
		</method>
		<method name="get_lines" qualifiers="const">
			<return type="int">
			</return>
			<description>
				Returns number of lines in the paragraph.
			</description>
		</method>
		<method name="get_size" qualifiers="const">
			<return type="Vector2">
			</return>
			<description>
				Returns size of the paragraph in pixels.
			</description>
		</method>
		<method name="get_word_bounds" qualifiers="const">
			<return type="Array">
			</return>
			<description>
				Returns array of the word boundaries as [code]Vector2(start, end)[/code].
			</description>
		</method>
	</methods>
	<members>
		<member name="back_color" type="Color" setter="set_back_color" getter="get_back_color" default="Color( 1, 1, 1, 0 )">
			Background color of the paragraph.
		</member>
		<member name="brk_flags" type="int" setter="set_brk_flags" getter="get_brk_flags" default="2">
			Line breaking flags.
		</member>
		<member name="halign" type="int" setter="set_halign" getter="get_halign" default="0">
			Horizontal alignment.
		</member>
		<member name="indent" type="float" setter="set_indent" getter="get_indent" default="0.0">
			Indentation.
		</member>
		<member name="jst_flags" type="int" setter="set_jst_flags" getter="get_jst_flags" default="1">
			Justification flags.
		</member>
		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="1.0">
			Lien spacing in pixels.
		</member>
		<member name="string" type="TLShapedAttributedString" setter="set_string" getter="get_string">
			Text and text attributes of the paragraph.
		</member>
		<member name="width" type="float" setter="set_width" getter="get_width" default="-1.0">
			Max width of the paragraph.
		</member>
	</members>
	<signals>
		<signal name="paragraph_changed">
			<description>
				Emitted when text or attributes of the paragraph are changed.
			</description>
		</signal>
	</signals>
	<constants>
		<constant name="PARA_HALIGN_LEFT" value="0" enum="ParaHAlign">
			Align rows to the left (default).
		</constant>
		<constant name="PARA_HALIGN_CENTER" value="1" enum="ParaHAlign">
			Align rows centered.
		</constant>
		<constant name="PARA_HALIGN_RIGHT" value="2" enum="ParaHAlign">
			Align rows to the right.
		</constant>
		<constant name="PARA_HALIGN_FILL" value="3" enum="ParaHAlign">
			Expand row white spaces to fit the width.
		</constant>
	</constants>
</class>


text:
/*************************************************************************/
/*  Map.hpp                                                              */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#ifndef MAP_H
#define MAP_H

/**
	@author Juan Linietsky <reduzio@gmail.com>
*/

// based on the very nice implementation of rb-trees by:
// http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html

#ifndef _ALWAYS_INLINE_
#if defined(__GNUC__) && (__GNUC__ >= 4)
#define _ALWAYS_INLINE_ __attribute__((always_inline)) inline
#elif defined(__llvm__)
#define _ALWAYS_INLINE_ __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
#define _ALWAYS_INLINE_ __forceinline
#else
#define _ALWAYS_INLINE_ inline
#endif
#endif

template <class T>
struct Comparator {

	_ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); }
};

template <class K, class V, class C = Comparator<K> >
class Map {

	enum Color {
		RED,
		BLACK
	};
	struct _Data;

public:
	class Element {

	private:
		friend class Map<K, V, C>;
		int color;
		Element *right;
		Element *left;
		Element *parent;
		Element *_next;
		Element *_prev;
		K _key;
		V _value;
		//_Data *data;

	public:
		const Element *next() const {

			return _next;
		}
		Element *next() {

			return _next;
		}
		const Element *prev() const {

			return _prev;
		}
		Element *prev() {

			return _prev;
		}
		const K &key() const {
			return _key;
		};
		V &value() {
			return _value;
		};
		const V &value() const {
			return _value;
		};
		V &get() {
			return _value;
		};
		const V &get() const {
			return _value;
		};
		Element() {
			color = RED;
			right = NULL;
			left = NULL;
			parent = NULL;
			_next = NULL;
			_prev = NULL;
		};
	};

private:
	struct _Data {

		Element *_root;
		Element *_nil;
		int size_cache;

		_ALWAYS_INLINE_ _Data() {

			_nil = new Element();
			_nil->parent = _nil->left = _nil->right = _nil;
			_nil->color = BLACK;

			_root = NULL;
			size_cache = 0;
		}

		void _create_root() {

			_root = new Element();
			_root->parent = _root->left = _root->right = _nil;
			_root->color = BLACK;
		}

		void _free_root() {

			if (_root) {
				delete _root;
				_root = NULL;
			}
		}

		~_Data() {

			_free_root();
			delete _nil;
		}
	};

	_Data _data;

	inline void _set_color(Element *p_node, int p_color) {

		ERR_FAIL_COND(p_node == _data._nil && p_color == RED);
		p_node->color = p_color;
	}

	inline void _rotate_left(Element *p_node) {

		Element *r = p_node->right;
		p_node->right = r->left;
		if (r->left != _data._nil)
			r->left->parent = p_node;
		r->parent = p_node->parent;
		if (p_node == p_node->parent->left)
			p_node->parent->left = r;
		else
			p_node->parent->right = r;

		r->left = p_node;
		p_node->parent = r;
	}

	inline void _rotate_right(Element *p_node) {

		Element *l = p_node->left;
		p_node->left = l->right;
		if (l->right != _data._nil)
			l->right->parent = p_node;
		l->parent = p_node->parent;
		if (p_node == p_node->parent->right)
			p_node->parent->right = l;
		else
			p_node->parent->left = l;

		l->right = p_node;
		p_node->parent = l;
	}

	inline Element *_successor(Element *p_node) const {

		Element *node = p_node;

		if (node->right != _data._nil) {

			node = node->right;
			while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */
				node = node->left;
			}
			return node;
		} else {

			while (node == node->parent->right) {
				node = node->parent;
			}

			if (node->parent == _data._root)
				return NULL; // No successor, as p_node = last node
			return node->parent;
		}
	}

	inline Element *_predecessor(Element *p_node) const {
		Element *node = p_node;

		if (node->left != _data._nil) {

			node = node->left;
			while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */
				node = node->right;
			}
			return node;
		} else {

			while (node == node->parent->left) {
				node = node->parent;
			}

			if (node == _data._root)
				return NULL; // No predecessor, as p_node = first node
			return node->parent;
		}
	}

	Element *_find(const K &p_key) const {

		Element *node = _data._root->left;
		C less;

		while (node != _data._nil) {
			if (less(p_key, node->_key))
				node = node->left;
			else if (less(node->_key, p_key))
				node = node->right;
			else
				return node; // found
		}

		return NULL;
	}

	Element *_find_closest(const K &p_key) const {

		Element *node = _data._root->left;
		Element *prev = NULL;
		C less;

		while (node != _data._nil) {
			prev = node;

			if (less(p_key, node->_key))
				node = node->left;
			else if (less(node->_key, p_key))
				node = node->right;
			else
				return node; // found
		}

		if (prev == NULL)
			return NULL; // tree empty

		if (less(p_key, prev->_key))
			prev = prev->_prev;

		return prev;
	}

	void _insert_rb_fix(Element *p_new_node) {

		Element *node = p_new_node;
		Element *nparent = node->parent;
		Element *ngrand_parent;

		while (nparent->color == RED) {
			ngrand_parent = nparent->parent;

			if (nparent == ngrand_parent->left) {
				if (ngrand_parent->right->color == RED) {
					_set_color(nparent, BLACK);
					_set_color(ngrand_parent->right, BLACK);
					_set_color(ngrand_parent, RED);
					node = ngrand_parent;
					nparent = node->parent;
				} else {
					if (node == nparent->right) {
						_rotate_left(nparent);
						node = nparent;
						nparent = node->parent;
					}
					_set_color(nparent, BLACK);
					_set_color(ngrand_parent, RED);
					_rotate_right(ngrand_parent);
				}
			} else {
				if (ngrand_parent->left->color == RED) {
					_set_color(nparent, BLACK);
					_set_color(ngrand_parent->left, BLACK);
					_set_color(ngrand_parent, RED);
					node = ngrand_parent;
					nparent = node->parent;
				} else {
					if (node == nparent->left) {
						_rotate_right(nparent);
						node = nparent;
						nparent = node->parent;
					}
					_set_color(nparent, BLACK);
					_set_color(ngrand_parent, RED);
					_rotate_left(ngrand_parent);
				}
			}
		}

		_set_color(_data._root->left, BLACK);
	}

	Element *_insert(const K &p_key, const V &p_value) {

		Element *new_parent = _data._root;
		Element *node = _data._root->left;
		C less;

		while (node != _data._nil) {

			new_parent = node;

			if (less(p_key, node->_key))
				node = node->left;
			else if (less(node->_key, p_key))
				node = node->right;
			else {
				node->_value = p_value;
				return node; // Return existing node with new value
			}
		}

		Element *new_node = new Element();
		new_node->parent = new_parent;
		new_node->right = _data._nil;
		new_node->left = _data._nil;
		new_node->_key = p_key;
		new_node->_value = p_value;
		//new_node->data=_data;

		if (new_parent == _data._root || less(p_key, new_parent->_key)) {
			new_parent->left = new_node;
		} else {
			new_parent->right = new_node;
		}

		new_node->_next = _successor(new_node);
		new_node->_prev = _predecessor(new_node);
		if (new_node->_next)
			new_node->_next->_prev = new_node;
		if (new_node->_prev)
			new_node->_prev->_next = new_node;

		_data.size_cache++;
		_insert_rb_fix(new_node);
		return new_node;
	}

	void _erase_fix_rb(Element *p_node) {

		Element *root = _data._root->left;
		Element *node = _data._nil;
		Element *sibling = p_node;
		Element *parent = sibling->parent;

		while (node != root) { // If red node found, will exit at a break
			if (sibling->color == RED) {
				_set_color(sibling, BLACK);
				_set_color(parent, RED);
				if (sibling == parent->right) {
					sibling = sibling->left;
					_rotate_left(parent);
				} else {
					sibling = sibling->right;
					_rotate_right(parent);
				}
			}
			if ((sibling->left->color == BLACK) && (sibling->right->color == BLACK)) {
				_set_color(sibling, RED);
				if (parent->color == RED) {
					_set_color(parent, BLACK);
					break;
				} else { // loop: haven't found any red nodes yet
					node = parent;
					parent = node->parent;
					sibling = (node == parent->left) ? parent->right : parent->left;
				}
			} else {
				if (sibling == parent->right) {
					if (sibling->right->color == BLACK) {
						_set_color(sibling->left, BLACK);
						_set_color(sibling, RED);
						_rotate_right(sibling);
						sibling = sibling->parent;
					}
					_set_color(sibling, parent->color);
					_set_color(parent, BLACK);
					_set_color(sibling->right, BLACK);
					_rotate_left(parent);
					break;
				} else {
					if (sibling->left->color == BLACK) {
						_set_color(sibling->right, BLACK);
						_set_color(sibling, RED);
						_rotate_left(sibling);
						sibling = sibling->parent;
					}

					_set_color(sibling, parent->color);
					_set_color(parent, BLACK);
					_set_color(sibling->left, BLACK);
					_rotate_right(parent);
					break;
				}
			}
		}

		ERR_FAIL_COND(_data._nil->color != BLACK);
	}

	void _erase(Element *p_node) {

		Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
		Element *node = (rp->left == _data._nil) ? rp->right : rp->left;

		Element *sibling;
		if (rp == rp->parent->left) {
			rp->parent->left = node;
			sibling = rp->parent->right;
		} else {
			rp->parent->right = node;
			sibling = rp->parent->left;
		}

		if (node->color == RED) {
			node->parent = rp->parent;
			_set_color(node, BLACK);
		} else if (rp->color == BLACK && rp->parent != _data._root) {
			_erase_fix_rb(sibling);
		}

		if (rp != p_node) {

			ERR_FAIL_COND(rp == _data._nil);

			rp->left = p_node->left;
			rp->right = p_node->right;
			rp->parent = p_node->parent;
			rp->color = p_node->color;
			if (p_node->left != _data._nil)
				p_node->left->parent = rp;
			if (p_node->right != _data._nil)
				p_node->right->parent = rp;

			if (p_node == p_node->parent->left) {
				p_node->parent->left = rp;
			} else {
				p_node->parent->right = rp;
			}
		}

		if (p_node->_next)
			p_node->_next->_prev = p_node->_prev;
		if (p_node->_prev)
			p_node->_prev->_next = p_node->_next;

		delete p_node;
		_data.size_cache--;
		ERR_FAIL_COND(_data._nil->color == RED);
	}

	void _calculate_depth(Element *p_element, int &max_d, int d) const {

		if (p_element == _data._nil)
			return;

		_calculate_depth(p_element->left, max_d, d + 1);
		_calculate_depth(p_element->right, max_d, d + 1);

		if (d > max_d)
			max_d = d;
	}

	void _cleanup_tree(Element *p_element) {

		if (p_element == _data._nil)
			return;

		_cleanup_tree(p_element->left);
		_cleanup_tree(p_element->right);
		delete p_element;
	}

	void _copy_from(const Map &p_map) {

		clear();
		// not the fastest way, but safeset to write.
		for (Element *I = p_map.front(); I; I = I->next()) {

			insert(I->key(), I->value());
		}
	}

public:
	const Element *find(const K &p_key) const {

		if (!_data._root)
			return NULL;

		const Element *res = _find(p_key);
		return res;
	}

	Element *find(const K &p_key) {

		if (!_data._root)
			return NULL;

		Element *res = _find(p_key);
		return res;
	}

	const Element *find_closest(const K &p_key) const {

		if (!_data._root)
			return NULL;

		const Element *res = _find_closest(p_key);
		return res;
	}

	Element *find_closest(const K &p_key) {

		if (!_data._root)
			return NULL;

		Element *res = _find_closest(p_key);
		return res;
	}

	bool has(const K &p_key) const {

		return find(p_key) != NULL;
	}

	Element *insert(const K &p_key, const V &p_value) {

		if (!_data._root)
			_data._create_root();
		return _insert(p_key, p_value);
	}

	void erase(Element *p_element) {

		if (!_data._root || !p_element)
			return;

		_erase(p_element);
		if (_data.size_cache == 0 && _data._root)
			_data._free_root();
	}

	bool erase(const K &p_key) {

		if (!_data._root)
			return false;

		Element *e = find(p_key);
		if (!e)
			return false;

		_erase(e);
		if (_data.size_cache == 0 && _data._root)
			_data._free_root();
		return true;
	}

	const V &operator[](const K &p_key) const {

		//CRASH_COND(!_data._root);
		const Element *e = find(p_key);
		//CRASH_COND(!e);
		return e->_value;
	}

	V &operator[](const K &p_key) {

		if (!_data._root)
			_data._create_root();

		Element *e = find(p_key);
		if (!e)
			e = insert(p_key, V());

		return e->_value;
	}

	Element *front() const {

		if (!_data._root)
			return NULL;

		Element *e = _data._root->left;
		if (e == _data._nil)
			return NULL;

		while (e->left != _data._nil)
			e = e->left;

		return e;
	}

	Element *back() const {

		if (!_data._root)
			return NULL;

		Element *e = _data._root->left;
		if (e == _data._nil)
			return NULL;

		while (e->right != _data._nil)
			e = e->right;

		return e;
	}

	inline bool empty() const { return _data.size_cache == 0; }
	inline int size() const { return _data.size_cache; }

	int calculate_depth() const {
		// used for debug mostly
		if (!_data._root)
			return 0;

		int max_d = 0;
		_calculate_depth(_data._root->left, max_d, 0);
		return max_d;
	}

	void clear() {

		if (!_data._root)
			return;

		_cleanup_tree(_data._root->left);
		_data._root->left = _data._nil;
		_data.size_cache = 0;
		_data._free_root();
	}

	void operator=(const Map &p_map) {

		_copy_from(p_map);
	}

	Map(const Map &p_map) {

		_copy_from(p_map);
	}

	_ALWAYS_INLINE_ Map() {
	}

	~Map() {

		clear();
	}
};

#endif


text:
# Third party libraries

## graphite2 (optional)

- Upstream: http://site.icu-project.org/
- Version: 1.3.14
- License: LGPL 2.1+ or GPL 2 or MPL

## freetype

- Upstream: https://www.freetype.org
- Version: 2.10.2
- License: FreeType License (BSD-like) or GPL 2

## HarfBuzz

- Upstream: https://github.com/behdad/harfbuzz
- Version: 2.6.6
- License: MIT-like License

## ICU4C

- Upstream: http://site.icu-project.org/
- Version: 67.1
- License: Unicode, Inc. License
- Note: USTRING_H header guard is renamed to ICU_USTRING_H to avoid conflivt with Godot header.

## libpng

- Upstream: http://libpng.org/pub/png/libpng.html
- Version: 1.6.37
- License: libpng/zlib

## zlib

- Upstream: http://www.zlib.net
- Version: 1.2.11
- License: zlib


text:
project('harfbuzz', 'cpp')
library_version = '2.6.6'

src = [
	'src/hb-aat-layout.cc',
	'src/hb-aat-layout.cc',
	'src/hb-aat-map.cc',
	'src/hb-blob.cc',
	'src/hb-buffer-serialize.cc',
	'src/hb-buffer.cc',
	'src/hb-common.cc',
	'src/hb-coretext.cc',
	'src/hb-draw.cc',
	'src/hb-face.cc',
	'src/hb-fallback-shape.cc',
	'src/hb-font.cc',
	'src/hb-ft.cc',
	'src/hb-icu.cc',
	'src/hb-map.cc',
	'src/hb-number.cc',
	'src/hb-ot-cff1-table.cc',
	'src/hb-ot-cff2-table.cc',
	'src/hb-ot-color.cc',
	'src/hb-ot-face.cc',
	'src/hb-ot-font.cc',
	'src/hb-ot-layout.cc',
	'src/hb-ot-map.cc',
	'src/hb-ot-math.cc',
	'src/hb-ot-meta.cc',
	'src/hb-ot-metrics.cc',
	'src/hb-ot-name.cc',
	'src/hb-ot-shape-complex-arabic.cc',
	'src/hb-ot-shape-complex-default.cc',
	'src/hb-ot-shape-complex-hangul.cc',
	'src/hb-ot-shape-complex-hebrew.cc',
	'src/hb-ot-shape-complex-indic-table.cc',
	'src/hb-ot-shape-complex-indic.cc',
	'src/hb-ot-shape-complex-khmer.cc',
	'src/hb-ot-shape-complex-myanmar.cc',
	'src/hb-ot-shape-complex-thai.cc',
	'src/hb-ot-shape-complex-use-table.cc',
	'src/hb-ot-shape-complex-use.cc',
	'src/hb-ot-shape-complex-vowel-constraints.cc',
	'src/hb-ot-shape-fallback.cc',
	'src/hb-ot-shape-normalize.cc',
	'src/hb-ot-shape.cc',
	'src/hb-ot-tag.cc',
	'src/hb-ot-var.cc',
	'src/hb-set.cc',
	'src/hb-shape-plan.cc',
	'src/hb-shape.cc',
	'src/hb-shaper.cc',
	'src/hb-static.cc',
	'src/hb-subset-cff-common.cc',
	'src/hb-subset-cff1.cc',
	'src/hb-subset-cff2.cc',
	'src/hb-subset-input.cc',
	'src/hb-subset-plan.cc',
	'src/hb-subset.cc',
	'src/hb-ucd.cc',
	'src/hb-unicode.cc'
]

cpp_args = [
	'-DHAVE_OT',
	'-DHAVE_ICU_BUILTIN',
	'-DHAVE_ICU',
	'-DHAVE_FREETYPE',
	'-DHB_NO_MT',
	'-DHAVE_FT_FACE_GETCHARVARIANTINDEX'
]

if meson.get_compiler('cpp').get_id() != 'msvc'
	cpp_args += [
		'-std=c++11',
		'-Wno-unknown-pragmas',
	]
endif

if get_option('use-graphite2')
	src += ['src/hb-graphite2.cc']
	cpp_args += ['-DHAVE_GRAPHITE2', '-DGRAPHITE2_STATIC']
endif

harfbuzz = static_library(
	'harfbuzz',
	src,
	cpp_args : cpp_args,
	include_directories : include_directories('src', '../freetype2/include', '../graphite2/include', '../icu4c/source/common/'),
	pic : true
)

harfbuzz_dep = declare_dependency(
	include_directories : include_directories('src'),
	link_with : harfbuzz
)


text:
Behdad Esfahbod
David Corbett
David Turner
Ebrahim Byagowi
Garret Rieger
Jonathan Kew
Khaled Hosny
Lars Knoll
Martin Hosken
Owen Taylor
Roderick Sheeter
Roozbeh Pournader
Simon Hausmann
Werner Lemberg


text:
#!/usr/bin/env python

Import('env')
Import('env_modules')

from compat import isbasestring

env_harfbuzz = env_modules.Clone()

# Thirdparty source files
sources = [
    'src/hb-aat-layout.cc',
    'src/hb-aat-layout.cc',
    'src/hb-aat-map.cc',
    'src/hb-blob.cc',
    'src/hb-buffer-serialize.cc',
    'src/hb-buffer.cc',
    'src/hb-common.cc',
    'src/hb-coretext.cc',
    'src/hb-draw.cc',
    'src/hb-face.cc',
    'src/hb-fallback-shape.cc',
    'src/hb-font.cc',
    'src/hb-ft.cc',
    'src/hb-icu.cc',
    'src/hb-map.cc',
    'src/hb-number.cc',
    'src/hb-ot-cff1-table.cc',
    'src/hb-ot-cff2-table.cc',
    'src/hb-ot-color.cc',
    'src/hb-ot-face.cc',
    'src/hb-ot-font.cc',
    'src/hb-ot-layout.cc',
    'src/hb-ot-map.cc',
    'src/hb-ot-math.cc',
    'src/hb-ot-meta.cc',
    'src/hb-ot-metrics.cc',
    'src/hb-ot-name.cc',
    'src/hb-ot-shape-complex-arabic.cc',
    'src/hb-ot-shape-complex-default.cc',
    'src/hb-ot-shape-complex-hangul.cc',
    'src/hb-ot-shape-complex-hebrew.cc',
    'src/hb-ot-shape-complex-indic-table.cc',
    'src/hb-ot-shape-complex-indic.cc',
    'src/hb-ot-shape-complex-khmer.cc',
    'src/hb-ot-shape-complex-myanmar.cc',
    'src/hb-ot-shape-complex-thai.cc',
    'src/hb-ot-shape-complex-use-table.cc',
    'src/hb-ot-shape-complex-use.cc',
    'src/hb-ot-shape-complex-vowel-constraints.cc',
    'src/hb-ot-shape-fallback.cc',
    'src/hb-ot-shape-normalize.cc',
    'src/hb-ot-shape.cc',
    'src/hb-ot-tag.cc',
    'src/hb-ot-var.cc',
    'src/hb-set.cc',
    'src/hb-shape-plan.cc',
    'src/hb-shape.cc',
    'src/hb-shaper.cc',
    'src/hb-static.cc',
    'src/hb-subset-cff-common.cc',
    'src/hb-subset-cff1.cc',
    'src/hb-subset-cff2.cc',
    'src/hb-subset-input.cc',
    'src/hb-subset-plan.cc',
    'src/hb-subset.cc',
    'src/hb-ucd.cc',
    'src/hb-unicode.cc'
]

env_harfbuzz.Append(CPPPATH=['src', '../graphite2/include', '../icu4c/source/common/'])
env_harfbuzz.Append(CPPPATH=["#thirdparty/freetype/include"])

env_harfbuzz.Append(CCFLAGS=[
    '-DHAVE_OT',
    '-DHAVE_ICU_BUILTIN',
    '-DHAVE_ICU',
    '-DHAVE_FREETYPE',
    '-DHB_NO_MT',
    '-DHAVE_FT_FACE_GETCHARVARIANTINDEX'
])

if (not env_harfbuzz.msvc):
    env_harfbuzz.Append(CXXFLAGS=["-std=c++11"])

if env['use_graphite2']:
    sources += ['src/hb-graphite2.cc']
    env_harfbuzz.Append(CCFLAGS=[
        '-DHAVE_GRAPHITE2',
        '-DGRAPHITE2_STATIC'
    ])

env_harfbuzz.disable_warnings()

env_thirdparty = env_harfbuzz.Clone()
env_thirdparty.disable_warnings()
lib = env_thirdparty.add_library("harfbuzz_builtin", sources)

# Needs to be appended to arrive after libscene in the linker call,
# but we don't want it to arrive *after* system libs, so manual hack
# LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
# and then plain strings for system library. We insert between the two.
inserted = False
for idx, linklib in enumerate(env["LIBS"]):
    if isbasestring(linklib): # first system lib such as "X11", otherwise SCons lib object
        env["LIBS"].insert(idx, lib)
        inserted = True
        break
if not inserted:
    env.Append(LIBS=[lib])


text:
def can_build(env, platform):
    return True

def configure(env):
    pass


text:
HarfBuzz is licensed under the so-called "Old MIT" license.  Details follow.
For parts of HarfBuzz that are licensed under different licenses see individual
files names COPYING in subdirectories where applicable.

Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020  Google, Inc.
Copyright © 2018,2019,2020  Ebrahim Byagowi
Copyright © 2019,2020  Facebook, Inc.
Copyright © 2012  Mozilla Foundation
Copyright © 2011  Codethink Limited
Copyright © 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
Copyright © 2009  Keith Stribley
Copyright © 2009  Martin Hosken and SIL International
Copyright © 2007  Chris Wilson
Copyright © 2006  Behdad Esfahbod
Copyright © 2005  David Turner
Copyright © 2004,2007,2008,2009,2010  Red Hat, Inc.
Copyright © 1998-2004  David Turner and Werner Lemberg

For full copyright notices consult the individual files in the package.

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.


text:
/*
 * Copyright © 2009  Red Hat, Inc.
 * 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.
 *
 * Red Hat Author(s): Behdad Esfahbod
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_FT_H
#define HB_FT_H

#include "hb.h"

#include <ft2build.h>
#include FT_FREETYPE_H

HB_BEGIN_DECLS

/*
 * Note: FreeType is not thread-safe.
 * Hence, these functions are not either.
 */

/*
 * hb-face from ft-face.
 */

/* This one creates a new hb-face for given ft-face.
 * When the returned hb-face is destroyed, the destroy
 * callback is called (if not NULL), with the ft-face passed
 * to it.
 *
 * The client is responsible to make sure that ft-face is
 * destroyed after hb-face is destroyed.
 *
 * Most often you don't want this function.  You should use either
 * hb_ft_face_create_cached(), or hb_ft_face_create_referenced().
 * In particular, if you are going to pass NULL as destroy, you
 * probably should use (the more recent) hb_ft_face_create_referenced()
 * instead.
 */
HB_EXTERN hb_face_t *
hb_ft_face_create (FT_Face           ft_face,
		   hb_destroy_func_t destroy);

/* This version is like hb_ft_face_create(), except that it caches
 * the hb-face using the generic pointer of the ft-face.  This means
 * that subsequent calls to this function with the same ft-face will
 * return the same hb-face (correctly referenced).
 *
 * Client is still responsible for making sure that ft-face is destroyed
 * after hb-face is.
 */
HB_EXTERN hb_face_t *
hb_ft_face_create_cached (FT_Face ft_face);

/* This version is like hb_ft_face_create(), except that it calls
 * FT_Reference_Face() on ft-face, as such keeping ft-face alive
 * as long as the hb-face is.
 *
 * This is the most convenient version to use.  Use it unless you have
 * very good reasons not to.
 */
HB_EXTERN hb_face_t *
hb_ft_face_create_referenced (FT_Face ft_face);

/*
 * hb-font from ft-face.
 */

/*
 * Note:
 *
 * Set face size on ft-face before creating hb-font from it.
 * Otherwise hb-ft would NOT pick up the font size correctly.
 */

/* See notes on hb_ft_face_create().  Same issues re lifecycle-management
 * apply here.  Use hb_ft_font_create_referenced() if you can. */
HB_EXTERN hb_font_t *
hb_ft_font_create (FT_Face           ft_face,
		   hb_destroy_func_t destroy);

/* See notes on hb_ft_face_create_referenced() re lifecycle-management
 * issues. */
HB_EXTERN hb_font_t *
hb_ft_font_create_referenced (FT_Face ft_face);

HB_EXTERN FT_Face
hb_ft_font_get_face (hb_font_t *font);

HB_EXTERN FT_Face
hb_ft_font_lock_face (hb_font_t *font);

HB_EXTERN void
hb_ft_font_unlock_face (hb_font_t *font);

HB_EXTERN void
hb_ft_font_set_load_flags (hb_font_t *font, int load_flags);

HB_EXTERN int
hb_ft_font_get_load_flags (hb_font_t *font);

/* Call when size or variations settings on underlying FT_Face change. */
HB_EXTERN void
hb_ft_font_changed (hb_font_t *font);

/* Makes an hb_font_t use FreeType internally to implement font functions.
 * Note: this internally creates an FT_Face.  Use it when you create your
 * hb_face_t using hb_face_create(). */
HB_EXTERN void
hb_ft_font_set_funcs (hb_font_t *font);

HB_END_DECLS

#endif /* HB_FT_H */


text:
/*
 * Copyright © 2018  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): Garret Rieger, Roderick Sheeter
 */

#ifndef HB_SUBSET_INPUT_HH
#define HB_SUBSET_INPUT_HH

#include "hb.hh"

#include "hb-subset.h"

#include "hb-font.hh"

struct hb_subset_input_t
{
  hb_object_header_t header;

  hb_set_t *unicodes;
  hb_set_t *glyphs;
  hb_set_t *name_ids;
  hb_set_t *name_languages;
  hb_set_t *drop_tables;

  bool drop_hints;
  bool desubroutinize;
  bool retain_gids;
  bool name_legacy;
  /* TODO
   *
   * features
   * lookups
   * name_ids
   * ...
   */
};

#endif /* HB_SUBSET_INPUT_HH */


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_CFF_INTERP_CS_COMMON_HH
#define HB_CFF_INTERP_CS_COMMON_HH

#include "hb.hh"
#include "hb-cff-interp-common.hh"

namespace CFF {

using namespace OT;

enum cs_type_t {
  CSType_CharString,
  CSType_GlobalSubr,
  CSType_LocalSubr
};

struct call_context_t
{
  void init (const byte_str_ref_t substr_=byte_str_ref_t (), cs_type_t type_=CSType_CharString, unsigned int subr_num_=0)
  {
    str_ref = substr_;
    type = type_;
    subr_num = subr_num_;
  }

  void fini () {}

  byte_str_ref_t  str_ref;
  cs_type_t	  type;
  unsigned int    subr_num;
};

/* call stack */
const unsigned int kMaxCallLimit = 10;
struct call_stack_t : cff_stack_t<call_context_t, kMaxCallLimit> {};

template <typename SUBRS>
struct biased_subrs_t
{
  void init (const SUBRS *subrs_)
  {
    subrs = subrs_;
    unsigned int  nSubrs = get_count ();
    if (nSubrs < 1240)
      bias = 107;
    else if (nSubrs < 33900)
      bias = 1131;
    else
      bias = 32768;
  }

  void fini () {}

  unsigned int get_count () const { return subrs ? subrs->count : 0; }
  unsigned int get_bias () const  { return bias; }

  byte_str_t operator [] (unsigned int index) const
  {
    if (unlikely (!subrs || index >= subrs->count))
      return Null (byte_str_t);
    else
      return (*subrs)[index];
  }

  protected:
  unsigned int  bias;
  const SUBRS   *subrs;
};

struct point_t
{
  void init ()
  {
    x.init ();
    y.init ();
  }

  void set_int (int _x, int _y)
  {
    x.set_int (_x);
    y.set_int (_y);
  }

  void move_x (const number_t &dx) { x += dx; }
  void move_y (const number_t &dy) { y += dy; }
  void move (const number_t &dx, const number_t &dy) { move_x (dx); move_y (dy); }
  void move (const point_t &d) { move_x (d.x); move_y (d.y); }

  number_t  x;
  number_t  y;
};

template <typename ARG, typename SUBRS>
struct cs_interp_env_t : interp_env_t<ARG>
{
  void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_)
  {
    interp_env_t<ARG>::init (str);

    context.init (str, CSType_CharString);
    seen_moveto = true;
    seen_hintmask = false;
    hstem_count = 0;
    vstem_count = 0;
    hintmask_size = 0;
    pt.init ();
    callStack.init ();
    globalSubrs.init (globalSubrs_);
    localSubrs.init (localSubrs_);
  }
  void fini ()
  {
    interp_env_t<ARG>::fini ();

    callStack.fini ();
    globalSubrs.fini ();
    localSubrs.fini ();
  }

  bool in_error () const
  {
    return callStack.in_error () || SUPER::in_error ();
  }

  bool pop_subr_num (const biased_subrs_t<SUBRS>& biasedSubrs, unsigned int &subr_num)
  {
    subr_num = 0;
    int n = SUPER::argStack.pop_int ();
    n += biasedSubrs.get_bias ();
    if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ())))
      return false;

    subr_num = (unsigned int)n;
    return true;
  }

  void call_subr (const biased_subrs_t<SUBRS>& biasedSubrs, cs_type_t type)
  {
    unsigned int subr_num = 0;

    if (unlikely (!pop_subr_num (biasedSubrs, subr_num)
		 || callStack.get_count () >= kMaxCallLimit))
    {
      SUPER::set_error ();
      return;
    }
    context.str_ref = SUPER::str_ref;
    callStack.push (context);

    context.init ( biasedSubrs[subr_num], type, subr_num);
    SUPER::str_ref = context.str_ref;
  }

  void return_from_subr ()
  {
    if (unlikely (SUPER::str_ref.in_error ()))
      SUPER::set_error ();
    context = callStack.pop ();
    SUPER::str_ref = context.str_ref;
  }

  void determine_hintmask_size ()
  {
    if (!seen_hintmask)
    {
      vstem_count += SUPER::argStack.get_count() / 2;
      hintmask_size = (hstem_count + vstem_count + 7) >> 3;
      seen_hintmask = true;
    }
  }

  void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
  bool is_endchar () const { return endchar_flag; }

  const number_t &get_x () const { return pt.x; }
  const number_t &get_y () const { return pt.y; }
  const point_t &get_pt () const { return pt; }

  void moveto (const point_t &pt_ ) { pt = pt_; }

  public:
  call_context_t   context;
  bool	  endchar_flag;
  bool	  seen_moveto;
  bool	  seen_hintmask;

  unsigned int  hstem_count;
  unsigned int  vstem_count;
  unsigned int  hintmask_size;
  call_stack_t	callStack;
  biased_subrs_t<SUBRS>   globalSubrs;
  biased_subrs_t<SUBRS>   localSubrs;

  private:
  point_t	 pt;

  typedef interp_env_t<ARG> SUPER;
};

template <typename ENV, typename PARAM>
struct path_procs_null_t
{
  static void rmoveto (ENV &env, PARAM& param) {}
  static void hmoveto (ENV &env, PARAM& param) {}
  static void vmoveto (ENV &env, PARAM& param) {}
  static void rlineto (ENV &env, PARAM& param) {}
  static void hlineto (ENV &env, PARAM& param) {}
  static void vlineto (ENV &env, PARAM& param) {}
  static void rrcurveto (ENV &env, PARAM& param) {}
  static void rcurveline (ENV &env, PARAM& param) {}
  static void rlinecurve (ENV &env, PARAM& param) {}
  static void vvcurveto (ENV &env, PARAM& param) {}
  static void hhcurveto (ENV &env, PARAM& param) {}
  static void vhcurveto (ENV &env, PARAM& param) {}
  static void hvcurveto (ENV &env, PARAM& param) {}
  static void moveto (ENV &env, PARAM& param, const point_t &pt) {}
  static void line (ENV &env, PARAM& param, const point_t &pt1) {}
  static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) {}
  static void hflex (ENV &env, PARAM& param) {}
  static void flex (ENV &env, PARAM& param) {}
  static void hflex1 (ENV &env, PARAM& param) {}
  static void flex1 (ENV &env, PARAM& param) {}
};

template <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=path_procs_null_t<ENV, PARAM>>
struct cs_opset_t : opset_t<ARG>
{
  static void process_op (op_code_t op, ENV &env, PARAM& param)
  {
    switch (op) {

      case OpCode_return:
	env.return_from_subr ();
	break;
      case OpCode_endchar:
	OPSET::check_width (op, env, param);
	env.set_endchar (true);
	OPSET::flush_args_and_op (op, env, param);
	break;

      case OpCode_fixedcs:
	env.argStack.push_fixed_from_substr (env.str_ref);
	break;

      case OpCode_callsubr:
	env.call_subr (env.localSubrs, CSType_LocalSubr);
	break;

      case OpCode_callgsubr:
	env.call_subr (env.globalSubrs, CSType_GlobalSubr);
	break;

      case OpCode_hstem:
      case OpCode_hstemhm:
	OPSET::check_width (op, env, param);
	OPSET::process_hstem (op, env, param);
	break;
      case OpCode_vstem:
      case OpCode_vstemhm:
	OPSET::check_width (op, env, param);
	OPSET::process_vstem (op, env, param);
	break;
      case OpCode_hintmask:
      case OpCode_cntrmask:
	OPSET::check_width (op, env, param);
	OPSET::process_hintmask (op, env, param);
	break;
      case OpCode_rmoveto:
	OPSET::check_width (op, env, param);
	PATH::rmoveto (env, param);
	OPSET::process_post_move (op, env, param);
	break;
      case OpCode_hmoveto:
	OPSET::check_width (op, env, param);
	PATH::hmoveto (env, param);
	OPSET::process_post_move (op, env, param);
	break;
      case OpCode_vmoveto:
	OPSET::check_width (op, env, param);
	PATH::vmoveto (env, param);
	OPSET::process_post_move (op, env, param);
	break;
      case OpCode_rlineto:
	PATH::rlineto (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_hlineto:
	PATH::hlineto (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_vlineto:
	PATH::vlineto (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_rrcurveto:
	PATH::rrcurveto (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_rcurveline:
	PATH::rcurveline (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_rlinecurve:
	PATH::rlinecurve (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_vvcurveto:
	PATH::vvcurveto (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_hhcurveto:
	PATH::hhcurveto (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_vhcurveto:
	PATH::vhcurveto (env, param);
	process_post_path (op, env, param);
	break;
      case OpCode_hvcurveto:
	PATH::hvcurveto (env, param);
	process_post_path (op, env, param);
	break;

      case OpCode_hflex:
	PATH::hflex (env, param);
	OPSET::process_post_flex (op, env, param);
	break;

      case OpCode_flex:
	PATH::flex (env, param);
	OPSET::process_post_flex (op, env, param);
	break;

      case OpCode_hflex1:
	PATH::hflex1 (env, param);
	OPSET::process_post_flex (op, env, param);
	break;

      case OpCode_flex1:
	PATH::flex1 (env, param);
	OPSET::process_post_flex (op, env, param);
	break;

      default:
	SUPER::process_op (op, env);
	break;
    }
  }

  static void process_hstem (op_code_t op, ENV &env, PARAM& param)
  {
    env.hstem_count += env.argStack.get_count () / 2;
    OPSET::flush_args_and_op (op, env, param);
  }

  static void process_vstem (op_code_t op, ENV &env, PARAM& param)
  {
    env.vstem_count += env.argStack.get_count () / 2;
    OPSET::flush_args_and_op (op, env, param);
  }

  static void process_hintmask (op_code_t op, ENV &env, PARAM& param)
  {
    env.determine_hintmask_size ();
    if (likely (env.str_ref.avail (env.hintmask_size)))
    {
      OPSET::flush_hintmask (op, env, param);
      env.str_ref.inc (env.hintmask_size);
    }
  }

  static void process_post_flex (op_code_t op, ENV &env, PARAM& param)
  {
    OPSET::flush_args_and_op (op, env, param);
  }

  static void check_width (op_code_t op, ENV &env, PARAM& param)
  {}

  static void process_post_move (op_code_t op, ENV &env, PARAM& param)
  {
    if (!env.seen_moveto)
    {
      env.determine_hintmask_size ();
      env.seen_moveto = true;
    }
    OPSET::flush_args_and_op (op, env, param);
  }

  static void process_post_path (op_code_t op, ENV &env, PARAM& param)
  {
    OPSET::flush_args_and_op (op, env, param);
  }

  static void flush_args_and_op (op_code_t op, ENV &env, PARAM& param)
  {
    OPSET::flush_args (env, param);
    OPSET::flush_op (op, env, param);
  }

  static void flush_args (ENV &env, PARAM& param)
  {
    env.pop_n_args (env.argStack.get_count ());
  }

  static void flush_op (op_code_t op, ENV &env, PARAM& param)
  {
  }

  static void flush_hintmask (op_code_t op, ENV &env, PARAM& param)
  {
    OPSET::flush_args_and_op (op, env, param);
  }

  static bool is_number_op (op_code_t op)
  {
    switch (op)
    {
      case OpCode_shortint:
      case OpCode_fixedcs:
      case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
      case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
      case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
      case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
	return true;

      default:
	/* 1-byte integer */
	return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast);
    }
  }

  protected:
  typedef opset_t<ARG>  SUPER;
};

template <typename PATH, typename ENV, typename PARAM>
struct path_procs_t
{
  static void rmoveto (ENV &env, PARAM& param)
  {
    point_t pt1 = env.get_pt ();
    const number_t &dy = env.pop_arg ();
    const number_t &dx = env.pop_arg ();
    pt1.move (dx, dy);
    PATH::moveto (env, param, pt1);
  }

  static void hmoveto (ENV &env, PARAM& param)
  {
    point_t pt1 = env.get_pt ();
    pt1.move_x (env.pop_arg ());
    PATH::moveto (env, param, pt1);
  }

  static void vmoveto (ENV &env, PARAM& param)
  {
    point_t pt1 = env.get_pt ();
    pt1.move_y (env.pop_arg ());
    PATH::moveto (env, param, pt1);
  }

  static void rlineto (ENV &env, PARAM& param)
  {
    for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2)
    {
      point_t pt1 = env.get_pt ();
      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
      PATH::line (env, param, pt1);
    }
  }

  static void hlineto (ENV &env, PARAM& param)
  {
    point_t pt1;
    unsigned int i = 0;
    for (; i + 2 <= env.argStack.get_count (); i += 2)
    {
      pt1 = env.get_pt ();
      pt1.move_x (env.eval_arg (i));
      PATH::line (env, param, pt1);
      pt1.move_y (env.eval_arg (i+1));
      PATH::line (env, param, pt1);
    }
    if (i < env.argStack.get_count ())
    {
      pt1 = env.get_pt ();
      pt1.move_x (env.eval_arg (i));
      PATH::line (env, param, pt1);
    }
  }

  static void vlineto (ENV &env, PARAM& param)
  {
    point_t pt1;
    unsigned int i = 0;
    for (; i + 2 <= env.argStack.get_count (); i += 2)
    {
      pt1 = env.get_pt ();
      pt1.move_y (env.eval_arg (i));
      PATH::line (env, param, pt1);
      pt1.move_x (env.eval_arg (i+1));
      PATH::line (env, param, pt1);
    }
    if (i < env.argStack.get_count ())
    {
      pt1 = env.get_pt ();
      pt1.move_y (env.eval_arg (i));
      PATH::line (env, param, pt1);
    }
  }

  static void rrcurveto (ENV &env, PARAM& param)
  {
    for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6)
    {
      point_t pt1 = env.get_pt ();
      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
      point_t pt3 = pt2;
      pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
      PATH::curve (env, param, pt1, pt2, pt3);
    }
  }

  static void rcurveline (ENV &env, PARAM& param)
  {
    unsigned int arg_count = env.argStack.get_count ();
    if (unlikely (arg_count < 8))
      return;

    unsigned int i = 0;
    unsigned int curve_limit = arg_count - 2;
    for (; i + 6 <= curve_limit; i += 6)
    {
      point_t pt1 = env.get_pt ();
      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
      point_t pt3 = pt2;
      pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
      PATH::curve (env, param, pt1, pt2, pt3);
    }

    point_t pt1 = env.get_pt ();
    pt1.move (env.eval_arg (i), env.eval_arg (i+1));
    PATH::line (env, param, pt1);
  }

  static void rlinecurve (ENV &env, PARAM& param)
  {
    unsigned int arg_count = env.argStack.get_count ();
    if (unlikely (arg_count < 8))
      return;

    unsigned int i = 0;
    unsigned int line_limit = arg_count - 6;
    for (; i + 2 <= line_limit; i += 2)
    {
      point_t pt1 = env.get_pt ();
      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
      PATH::line (env, param, pt1);
    }

    point_t pt1 = env.get_pt ();
    pt1.move (env.eval_arg (i), env.eval_arg (i+1));
    point_t pt2 = pt1;
    pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
    point_t pt3 = pt2;
    pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
    PATH::curve (env, param, pt1, pt2, pt3);
  }

  static void vvcurveto (ENV &env, PARAM& param)
  {
    unsigned int i = 0;
    point_t pt1 = env.get_pt ();
    if ((env.argStack.get_count () & 1) != 0)
      pt1.move_x (env.eval_arg (i++));
    for (; i + 4 <= env.argStack.get_count (); i += 4)
    {
      pt1.move_y (env.eval_arg (i));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
      point_t pt3 = pt2;
      pt3.move_y (env.eval_arg (i+3));
      PATH::curve (env, param, pt1, pt2, pt3);
      pt1 = env.get_pt ();
    }
  }

  static void hhcurveto (ENV &env, PARAM& param)
  {
    unsigned int i = 0;
    point_t pt1 = env.get_pt ();
    if ((env.argStack.get_count () & 1) != 0)
      pt1.move_y (env.eval_arg (i++));
    for (; i + 4 <= env.argStack.get_count (); i += 4)
    {
      pt1.move_x (env.eval_arg (i));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
      point_t pt3 = pt2;
      pt3.move_x (env.eval_arg (i+3));
      PATH::curve (env, param, pt1, pt2, pt3);
      pt1 = env.get_pt ();
    }
  }

  static void vhcurveto (ENV &env, PARAM& param)
  {
    point_t pt1, pt2, pt3;
    unsigned int i = 0;
    if ((env.argStack.get_count () % 8) >= 4)
    {
      point_t pt1 = env.get_pt ();
      pt1.move_y (env.eval_arg (i));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
      point_t pt3 = pt2;
      pt3.move_x (env.eval_arg (i+3));
      i += 4;

      for (; i + 8 <= env.argStack.get_count (); i += 8)
      {
	PATH::curve (env, param, pt1, pt2, pt3);
	pt1 = env.get_pt ();
	pt1.move_x (env.eval_arg (i));
	pt2 = pt1;
	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
	pt3 = pt2;
	pt3.move_y (env.eval_arg (i+3));
	PATH::curve (env, param, pt1, pt2, pt3);

	pt1 = pt3;
	pt1.move_y (env.eval_arg (i+4));
	pt2 = pt1;
	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
	pt3 = pt2;
	pt3.move_x (env.eval_arg (i+7));
      }
      if (i < env.argStack.get_count ())
	pt3.move_y (env.eval_arg (i));
      PATH::curve (env, param, pt1, pt2, pt3);
    }
    else
    {
      for (; i + 8 <= env.argStack.get_count (); i += 8)
      {
	pt1 = env.get_pt ();
	pt1.move_y (env.eval_arg (i));
	pt2 = pt1;
	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
	pt3 = pt2;
	pt3.move_x (env.eval_arg (i+3));
	PATH::curve (env, param, pt1, pt2, pt3);

	pt1 = pt3;
	pt1.move_x (env.eval_arg (i+4));
	pt2 = pt1;
	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
	pt3 = pt2;
	pt3.move_y (env.eval_arg (i+7));
	if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
	  pt3.move_x (env.eval_arg (i+8));
	PATH::curve (env, param, pt1, pt2, pt3);
      }
    }
  }

  static void hvcurveto (ENV &env, PARAM& param)
  {
    point_t pt1, pt2, pt3;
    unsigned int i = 0;
    if ((env.argStack.get_count () % 8) >= 4)
    {
      point_t pt1 = env.get_pt ();
      pt1.move_x (env.eval_arg (i));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
      point_t pt3 = pt2;
      pt3.move_y (env.eval_arg (i+3));
      i += 4;

      for (; i + 8 <= env.argStack.get_count (); i += 8)
      {
	PATH::curve (env, param, pt1, pt2, pt3);
	pt1 = env.get_pt ();
	pt1.move_y (env.eval_arg (i));
	pt2 = pt1;
	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
	pt3 = pt2;
	pt3.move_x (env.eval_arg (i+3));
	PATH::curve (env, param, pt1, pt2, pt3);

	pt1 = pt3;
	pt1.move_x (env.eval_arg (i+4));
	pt2 = pt1;
	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
	pt3 = pt2;
	pt3.move_y (env.eval_arg (i+7));
      }
      if (i < env.argStack.get_count ())
	pt3.move_x (env.eval_arg (i));
      PATH::curve (env, param, pt1, pt2, pt3);
    }
    else
    {
      for (; i + 8 <= env.argStack.get_count (); i += 8)
      {
	pt1 = env.get_pt ();
	pt1.move_x (env.eval_arg (i));
	pt2 = pt1;
	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
	pt3 = pt2;
	pt3.move_y (env.eval_arg (i+3));
	PATH::curve (env, param, pt1, pt2, pt3);

	pt1 = pt3;
	pt1.move_y (env.eval_arg (i+4));
	pt2 = pt1;
	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
	pt3 = pt2;
	pt3.move_x (env.eval_arg (i+7));
	if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
	  pt3.move_y (env.eval_arg (i+8));
	PATH::curve (env, param, pt1, pt2, pt3);
      }
    }
  }

  /* default actions to be overridden */
  static void moveto (ENV &env, PARAM& param, const point_t &pt)
  { env.moveto (pt); }

  static void line (ENV &env, PARAM& param, const point_t &pt1)
  { PATH::moveto (env, param, pt1); }

  static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
  { PATH::moveto (env, param, pt3); }

  static void hflex (ENV &env, PARAM& param)
  {
    if (likely (env.argStack.get_count () == 7))
    {
      point_t pt1 = env.get_pt ();
      pt1.move_x (env.eval_arg (0));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (1), env.eval_arg (2));
      point_t pt3 = pt2;
      pt3.move_x (env.eval_arg (3));
      point_t pt4 = pt3;
      pt4.move_x (env.eval_arg (4));
      point_t pt5 = pt4;
      pt5.move_x (env.eval_arg (5));
      pt5.y = pt1.y;
      point_t pt6 = pt5;
      pt6.move_x (env.eval_arg (6));

      curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
    }
    else
      env.set_error ();
  }

  static void flex (ENV &env, PARAM& param)
  {
    if (likely (env.argStack.get_count () == 13))
    {
      point_t pt1 = env.get_pt ();
      pt1.move (env.eval_arg (0), env.eval_arg (1));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (2), env.eval_arg (3));
      point_t pt3 = pt2;
      pt3.move (env.eval_arg (4), env.eval_arg (5));
      point_t pt4 = pt3;
      pt4.move (env.eval_arg (6), env.eval_arg (7));
      point_t pt5 = pt4;
      pt5.move (env.eval_arg (8), env.eval_arg (9));
      point_t pt6 = pt5;
      pt6.move (env.eval_arg (10), env.eval_arg (11));

      curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
    }
    else
      env.set_error ();
  }

  static void hflex1 (ENV &env, PARAM& param)
  {
    if (likely (env.argStack.get_count () == 9))
    {
      point_t pt1 = env.get_pt ();
      pt1.move (env.eval_arg (0), env.eval_arg (1));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (2), env.eval_arg (3));
      point_t pt3 = pt2;
      pt3.move_x (env.eval_arg (4));
      point_t pt4 = pt3;
      pt4.move_x (env.eval_arg (5));
      point_t pt5 = pt4;
      pt5.move (env.eval_arg (6), env.eval_arg (7));
      point_t pt6 = pt5;
      pt6.move_x (env.eval_arg (8));
      pt6.y = env.get_pt ().y;

      curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
    }
    else
      env.set_error ();
  }

  static void flex1 (ENV &env, PARAM& param)
  {
    if (likely (env.argStack.get_count () == 11))
    {
      point_t d;
      d.init ();
      for (unsigned int i = 0; i < 10; i += 2)
	d.move (env.eval_arg (i), env.eval_arg (i+1));

      point_t pt1 = env.get_pt ();
      pt1.move (env.eval_arg (0), env.eval_arg (1));
      point_t pt2 = pt1;
      pt2.move (env.eval_arg (2), env.eval_arg (3));
      point_t pt3 = pt2;
      pt3.move (env.eval_arg (4), env.eval_arg (5));
      point_t pt4 = pt3;
      pt4.move (env.eval_arg (6), env.eval_arg (7));
      point_t pt5 = pt4;
      pt5.move (env.eval_arg (8), env.eval_arg (9));
      point_t pt6 = pt5;

      if (fabs (d.x.to_real ()) > fabs (d.y.to_real ()))
      {
	pt6.move_x (env.eval_arg (10));
	pt6.y = env.get_pt ().y;
      }
      else
      {
	pt6.x = env.get_pt ().x;
	pt6.move_y (env.eval_arg (10));
      }

      curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
    }
    else
      env.set_error ();
  }

  protected:
  static void curve2 (ENV &env, PARAM& param,
		      const point_t &pt1, const point_t &pt2, const point_t &pt3,
		      const point_t &pt4, const point_t &pt5, const point_t &pt6)
  {
    PATH::curve (env, param, pt1, pt2, pt3);
    PATH::curve (env, param, pt4, pt5, pt6);
  }
};

template <typename ENV, typename OPSET, typename PARAM>
struct cs_interpreter_t : interpreter_t<ENV>
{
  bool interpret (PARAM& param)
  {
    SUPER::env.set_endchar (false);

    for (;;) {
      OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
      if (unlikely (SUPER::env.in_error ()))
	return false;
      if (SUPER::env.is_endchar ())
	break;
    }

    return true;
  }

  private:
  typedef interpreter_t<ENV> SUPER;
};

} /* namespace CFF */

#endif /* HB_CFF_INTERP_CS_COMMON_HH */


text:
#!/bin/sh

LC_ALL=C
export LC_ALL

test -z "$srcdir" && srcdir=.
test -z "$libs" && libs=.libs
stat=0

if which ldd 2>/dev/null >/dev/null; then
	LDD=ldd
else
	# macOS specific tool
	if which otool 2>/dev/null >/dev/null; then
		LDD="otool -L"
	else
		echo "check-libstdc++.sh: 'ldd' not found; skipping test"
		exit 77
	fi
fi

tested=false
# harfbuzz-icu links to libstdc++ because icu does.
for soname in harfbuzz harfbuzz-subset harfbuzz-gobject; do
	for suffix in so dylib; do
		so=$libs/lib$soname.$suffix
		if ! test -f "$so"; then continue; fi

		echo "Checking that we are not linking to libstdc++ or libc++ in $so"
		if $LDD $so | grep 'libstdc[+][+]\|libc[+][+]'; then
			echo "Ouch, linked to libstdc++ or libc++"
			stat=1
		fi
		tested=true
	done
done
if ! $tested; then
	echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test"
	exit 77
fi

exit $stat


text:
/*
 * Copyright © 1998-2004  David Turner and Werner Lemberg
 * Copyright © 2004,2007,2009  Red Hat, Inc.
 * Copyright © 2011,2012  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.
 *
 * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_H_IN
#error "Include <hb.h> instead."
#endif

#ifndef HB_BUFFER_H
#define HB_BUFFER_H

#include "hb-common.h"
#include "hb-unicode.h"
#include "hb-font.h"

HB_BEGIN_DECLS

/**
 * hb_glyph_info_t:
 * @codepoint: either a Unicode code point (before shaping) or a glyph index
 *             (after shaping).
 * @cluster: the index of the character in the original text that corresponds
 *           to this #hb_glyph_info_t, or whatever the client passes to
 *           hb_buffer_add(). More than one #hb_glyph_info_t can have the same
 *           @cluster value, if they resulted from the same character (e.g. one
 *           to many glyph substitution), and when more than one character gets
 *           merged in the same glyph (e.g. many to one glyph substitution) the
 *           #hb_glyph_info_t will have the smallest cluster value of them.
 *           By default some characters are merged into the same cluster
 *           (e.g. combining marks have the same cluster as their bases)
 *           even if they are separate glyphs, hb_buffer_set_cluster_level()
 *           allow selecting more fine-grained cluster handling.
 *
 * The #hb_glyph_info_t is the structure that holds information about the
 * glyphs and their relation to input text.
 */
typedef struct hb_glyph_info_t
{
  hb_codepoint_t codepoint;
  /*< private >*/
  hb_mask_t      mask;
  /*< public >*/
  uint32_t       cluster;

  /*< private >*/
  hb_var_int_t   var1;
  hb_var_int_t   var2;
} hb_glyph_info_t;

/**
 * hb_glyph_flags_t:
 * @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the
 * 				   beginning of the cluster this glyph is part of,
 * 				   then both sides need to be re-shaped, as the
 * 				   result might be different.  On the flip side,
 * 				   it means that when this flag is not present,
 * 				   then it's safe to break the glyph-run at the
 * 				   beginning of this cluster, and the two sides
 * 				   represent the exact same result one would get
 * 				   if breaking input text at the beginning of
 * 				   this cluster and shaping the two sides
 * 				   separately.  This can be used to optimize
 * 				   paragraph layout, by avoiding re-shaping
 * 				   of each line after line-breaking, or limiting
 * 				   the reshaping to a small piece around the
 * 				   breaking point only.
 * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags.
 *
 * Since: 1.5.0
 */
typedef enum { /*< flags >*/
  HB_GLYPH_FLAG_UNSAFE_TO_BREAK		= 0x00000001,

  HB_GLYPH_FLAG_DEFINED			= 0x00000001 /* OR of all defined flags */
} hb_glyph_flags_t;

HB_EXTERN hb_glyph_flags_t
hb_glyph_info_get_glyph_flags (const hb_glyph_info_t *info);

#define hb_glyph_info_get_glyph_flags(info) \
	((hb_glyph_flags_t) ((unsigned int) (info)->mask & HB_GLYPH_FLAG_DEFINED))

/**
 * hb_glyph_position_t:
 * @x_advance: how much the line advances after drawing this glyph when setting
 *             text in horizontal direction.
 * @y_advance: how much the line advances after drawing this glyph when setting
 *             text in vertical direction.
 * @x_offset: how much the glyph moves on the X-axis before drawing it, this
 *            should not affect how much the line advances.
 * @y_offset: how much the glyph moves on the Y-axis before drawing it, this
 *            should not affect how much the line advances.
 *
 * The #hb_glyph_position_t is the structure that holds the positions of the
 * glyph in both horizontal and vertical directions. All positions in
 * #hb_glyph_position_t are relative to the current point.
 *
 */
typedef struct hb_glyph_position_t {
  hb_position_t  x_advance;
  hb_position_t  y_advance;
  hb_position_t  x_offset;
  hb_position_t  y_offset;

  /*< private >*/
  hb_var_int_t   var;
} hb_glyph_position_t;

/**
 * hb_segment_properties_t:
 * @direction: the #hb_direction_t of the buffer, see hb_buffer_set_direction().
 * @script: the #hb_script_t of the buffer, see hb_buffer_set_script().
 * @language: the #hb_language_t of the buffer, see hb_buffer_set_language().
 *
 * The structure that holds various text properties of an #hb_buffer_t. Can be
 * set and retrieved using hb_buffer_set_segment_properties() and
 * hb_buffer_get_segment_properties(), respectively.
 */
typedef struct hb_segment_properties_t {
  hb_direction_t  direction;
  hb_script_t     script;
  hb_language_t   language;
  /*< private >*/
  void           *reserved1;
  void           *reserved2;
} hb_segment_properties_t;

#define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \
				       HB_SCRIPT_INVALID, \
				       HB_LANGUAGE_INVALID, \
				       (void *) 0, \
				       (void *) 0}

HB_EXTERN hb_bool_t
hb_segment_properties_equal (const hb_segment_properties_t *a,
			     const hb_segment_properties_t *b);

HB_EXTERN unsigned int
hb_segment_properties_hash (const hb_segment_properties_t *p);

/**
 * hb_buffer_t:
 *
 * The main structure holding the input text and its properties before shaping,
 * and output glyphs and their information after shaping.
 */

typedef struct hb_buffer_t hb_buffer_t;

HB_EXTERN hb_buffer_t *
hb_buffer_create (void);

HB_EXTERN hb_buffer_t *
hb_buffer_get_empty (void);

HB_EXTERN hb_buffer_t *
hb_buffer_reference (hb_buffer_t *buffer);

HB_EXTERN void
hb_buffer_destroy (hb_buffer_t *buffer);

HB_EXTERN hb_bool_t
hb_buffer_set_user_data (hb_buffer_t        *buffer,
			 hb_user_data_key_t *key,
			 void *              data,
			 hb_destroy_func_t   destroy,
			 hb_bool_t           replace);

HB_EXTERN void *
hb_buffer_get_user_data (hb_buffer_t        *buffer,
			 hb_user_data_key_t *key);

/**
 * hb_buffer_content_type_t:
 * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer.
 * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping).
 * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping).
 */
typedef enum {
  HB_BUFFER_CONTENT_TYPE_INVALID = 0,
  HB_BUFFER_CONTENT_TYPE_UNICODE,
  HB_BUFFER_CONTENT_TYPE_GLYPHS
} hb_buffer_content_type_t;

HB_EXTERN void
hb_buffer_set_content_type (hb_buffer_t              *buffer,
			    hb_buffer_content_type_t  content_type);

HB_EXTERN hb_buffer_content_type_t
hb_buffer_get_content_type (hb_buffer_t *buffer);

HB_EXTERN void
hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
			     hb_unicode_funcs_t *unicode_funcs);

HB_EXTERN hb_unicode_funcs_t *
hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer);

HB_EXTERN void
hb_buffer_set_direction (hb_buffer_t    *buffer,
			 hb_direction_t  direction);

HB_EXTERN hb_direction_t
hb_buffer_get_direction (hb_buffer_t *buffer);

HB_EXTERN void
hb_buffer_set_script (hb_buffer_t *buffer,
		      hb_script_t  script);

HB_EXTERN hb_script_t
hb_buffer_get_script (hb_buffer_t *buffer);

HB_EXTERN void
hb_buffer_set_language (hb_buffer_t   *buffer,
			hb_language_t  language);

HB_EXTERN hb_language_t
hb_buffer_get_language (hb_buffer_t *buffer);

HB_EXTERN void
hb_buffer_set_segment_properties (hb_buffer_t *buffer,
				  const hb_segment_properties_t *props);

HB_EXTERN void
hb_buffer_get_segment_properties (hb_buffer_t *buffer,
				  hb_segment_properties_t *props);

HB_EXTERN void
hb_buffer_guess_segment_properties (hb_buffer_t *buffer);

/**
 * hb_buffer_flags_t:
 * @HB_BUFFER_FLAG_DEFAULT: the default buffer flag.
 * @HB_BUFFER_FLAG_BOT: flag indicating that special handling of the beginning
 *                      of text paragraph can be applied to this buffer. Should usually
 *                      be set, unless you are passing to the buffer only part
 *                      of the text without the full context.
 * @HB_BUFFER_FLAG_EOT: flag indicating that special handling of the end of text
 *                      paragraph can be applied to this buffer, similar to
 *                      @HB_BUFFER_FLAG_BOT.
 * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES:
 *                      flag indication that character with Default_Ignorable
 *                      Unicode property should use the corresponding glyph
 *                      from the font, instead of hiding them (done by
 *                      replacing them with the space glyph and zeroing the
 *                      advance width.)  This flag takes precedence over
 *                      @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES.
 * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES:
 *                      flag indication that character with Default_Ignorable
 *                      Unicode property should be removed from glyph string
 *                      instead of hiding them (done by replacing them with the
 *                      space glyph and zeroing the advance width.)
 *                      @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes
 *                      precedence over this flag. Since: 1.8.0
 * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE:
 *                      flag indicating that a dotted circle should
 *                      not be inserted in the rendering of incorrect
 *                      character sequences (such at <0905 093E>). Since: 2.4
 *
 * Since: 0.9.20
 */
typedef enum { /*< flags >*/
  HB_BUFFER_FLAG_DEFAULT			= 0x00000000u,
  HB_BUFFER_FLAG_BOT				= 0x00000001u, /* Beginning-of-text */
  HB_BUFFER_FLAG_EOT				= 0x00000002u, /* End-of-text */
  HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES	= 0x00000004u,
  HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES	= 0x00000008u,
  HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE	= 0x00000010u
} hb_buffer_flags_t;

HB_EXTERN void
hb_buffer_set_flags (hb_buffer_t       *buffer,
		     hb_buffer_flags_t  flags);

HB_EXTERN hb_buffer_flags_t
hb_buffer_get_flags (hb_buffer_t *buffer);

/**
 * hb_buffer_cluster_level_t:
 * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES: Return cluster values grouped by graphemes into
 *   monotone order.
 * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS: Return cluster values grouped into monotone order.
 * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values.
 * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level,
 *   equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES.
 *
 * Since: 0.9.42
 */
typedef enum {
  HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES	= 0,
  HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS	= 1,
  HB_BUFFER_CLUSTER_LEVEL_CHARACTERS		= 2,
  HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES
} hb_buffer_cluster_level_t;

HB_EXTERN void
hb_buffer_set_cluster_level (hb_buffer_t               *buffer,
			     hb_buffer_cluster_level_t  cluster_level);

HB_EXTERN hb_buffer_cluster_level_t
hb_buffer_get_cluster_level (hb_buffer_t *buffer);

/**
 * HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT:
 *
 * The default code point for replacing invalid characters in a given encoding.
 * Set to U+FFFD REPLACEMENT CHARACTER.
 *
 * Since: 0.9.31
 */
#define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu

HB_EXTERN void
hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
				     hb_codepoint_t  replacement);

HB_EXTERN hb_codepoint_t
hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer);

HB_EXTERN void
hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
			       hb_codepoint_t  invisible);

HB_EXTERN hb_codepoint_t
hb_buffer_get_invisible_glyph (hb_buffer_t    *buffer);

HB_EXTERN void
hb_buffer_reset (hb_buffer_t *buffer);

HB_EXTERN void
hb_buffer_clear_contents (hb_buffer_t *buffer);

HB_EXTERN hb_bool_t
hb_buffer_pre_allocate (hb_buffer_t  *buffer,
			unsigned int  size);

HB_EXTERN hb_bool_t
hb_buffer_allocation_successful (hb_buffer_t  *buffer);

HB_EXTERN void
hb_buffer_reverse (hb_buffer_t *buffer);

HB_EXTERN void
hb_buffer_reverse_range (hb_buffer_t *buffer,
			 unsigned int start, unsigned int end);

HB_EXTERN void
hb_buffer_reverse_clusters (hb_buffer_t *buffer);

/* Filling the buffer in */

HB_EXTERN void
hb_buffer_add (hb_buffer_t    *buffer,
	       hb_codepoint_t  codepoint,
	       unsigned int    cluster);

HB_EXTERN void
hb_buffer_add_utf8 (hb_buffer_t  *buffer,
		    const char   *text,
		    int           text_length,
		    unsigned int  item_offset,
		    int           item_length);

HB_EXTERN void
hb_buffer_add_utf16 (hb_buffer_t    *buffer,
		     const uint16_t *text,
		     int             text_length,
		     unsigned int    item_offset,
		     int             item_length);

HB_EXTERN void
hb_buffer_add_utf32 (hb_buffer_t    *buffer,
		     const uint32_t *text,
		     int             text_length,
		     unsigned int    item_offset,
		     int             item_length);

HB_EXTERN void
hb_buffer_add_latin1 (hb_buffer_t   *buffer,
		      const uint8_t *text,
		      int            text_length,
		      unsigned int   item_offset,
		      int            item_length);

HB_EXTERN void
hb_buffer_add_codepoints (hb_buffer_t          *buffer,
			  const hb_codepoint_t *text,
			  int                   text_length,
			  unsigned int          item_offset,
			  int                   item_length);

HB_EXTERN void
hb_buffer_append (hb_buffer_t *buffer,
		  hb_buffer_t *source,
		  unsigned int start,
		  unsigned int end);

HB_EXTERN hb_bool_t
hb_buffer_set_length (hb_buffer_t  *buffer,
		      unsigned int  length);

HB_EXTERN unsigned int
hb_buffer_get_length (hb_buffer_t *buffer);

/* Getting glyphs out of the buffer */

HB_EXTERN hb_glyph_info_t *
hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
			   unsigned int *length);

HB_EXTERN hb_glyph_position_t *
hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
			       unsigned int *length);

HB_EXTERN void
hb_buffer_normalize_glyphs (hb_buffer_t *buffer);

/*
 * Serialize
 */

/**
 * hb_buffer_serialize_flags_t:
 * @HB_BUFFER_SERIALIZE_FLAG_DEFAULT: serialize glyph names, clusters and positions.
 * @HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS: do not serialize glyph cluster.
 * @HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS: do not serialize glyph position information.
 * @HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES: do no serialize glyph name.
 * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents.
 * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0
 * @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances,
 *  glyph offsets will reflect absolute glyph positions. Since: 1.8.0
 *
 * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs().
 *
 * Since: 0.9.20
 */
typedef enum { /*< flags >*/
  HB_BUFFER_SERIALIZE_FLAG_DEFAULT		= 0x00000000u,
  HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS		= 0x00000001u,
  HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS		= 0x00000002u,
  HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES	= 0x00000004u,
  HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u,
  HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS		= 0x00000010u,
  HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES		= 0x00000020u
} hb_buffer_serialize_flags_t;

/**
 * hb_buffer_serialize_format_t:
 * @HB_BUFFER_SERIALIZE_FORMAT_TEXT: a human-readable, plain text format.
 * @HB_BUFFER_SERIALIZE_FORMAT_JSON: a machine-readable JSON format.
 * @HB_BUFFER_SERIALIZE_FORMAT_INVALID: invalid format.
 *
 * The buffer serialization and de-serialization format used in
 * hb_buffer_serialize_glyphs() and hb_buffer_deserialize_glyphs().
 *
 * Since: 0.9.2
 */
typedef enum {
  HB_BUFFER_SERIALIZE_FORMAT_TEXT	= HB_TAG('T','E','X','T'),
  HB_BUFFER_SERIALIZE_FORMAT_JSON	= HB_TAG('J','S','O','N'),
  HB_BUFFER_SERIALIZE_FORMAT_INVALID	= HB_TAG_NONE
} hb_buffer_serialize_format_t;

HB_EXTERN hb_buffer_serialize_format_t
hb_buffer_serialize_format_from_string (const char *str, int len);

HB_EXTERN const char *
hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format);

HB_EXTERN const char **
hb_buffer_serialize_list_formats (void);

HB_EXTERN unsigned int
hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
			    unsigned int start,
			    unsigned int end,
			    char *buf,
			    unsigned int buf_size,
			    unsigned int *buf_consumed,
			    hb_font_t *font,
			    hb_buffer_serialize_format_t format,
			    hb_buffer_serialize_flags_t flags);

HB_EXTERN hb_bool_t
hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
			      const char *buf,
			      int buf_len,
			      const char **end_ptr,
			      hb_font_t *font,
			      hb_buffer_serialize_format_t format);

/*
 * Compare buffers
 */

typedef enum { /*< flags >*/
  HB_BUFFER_DIFF_FLAG_EQUAL			= 0x0000,

  /* Buffers with different content_type cannot be meaningfully compared
   * in any further detail. */
  HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH	= 0x0001,

  /* For buffers with differing length, the per-glyph comparison is not
   * attempted, though we do still scan reference for dottedcircle / .notdef
   * glyphs. */
  HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH		= 0x0002,

  /* We want to know if dottedcircle / .notdef glyphs are present in the
   * reference, as we may not care so much about other differences in this
   * case. */
  HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT		= 0x0004,
  HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT	= 0x0008,

  /* If the buffers have the same length, we compare them glyph-by-glyph
   * and report which aspect(s) of the glyph info/position are different. */
  HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH	= 0x0010,
  HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH		= 0x0020,
  HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH	= 0x0040,
  HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH		= 0x0080

} hb_buffer_diff_flags_t;

/* Compare the contents of two buffers, report types of differences. */
HB_EXTERN hb_buffer_diff_flags_t
hb_buffer_diff (hb_buffer_t *buffer,
		hb_buffer_t *reference,
		hb_codepoint_t dottedcircle_glyph,
		unsigned int position_fuzz);

/*
 * Debugging.
 */

typedef hb_bool_t	(*hb_buffer_message_func_t)	(hb_buffer_t *buffer,
							 hb_font_t   *font,
							 const char  *message,
							 void        *user_data);

HB_EXTERN void
hb_buffer_set_message_func (hb_buffer_t *buffer,
			    hb_buffer_message_func_t func,
			    void *user_data, hb_destroy_func_t destroy);

HB_END_DECLS

#endif /* HB_BUFFER_H */