/* kb_text_shape - v2.06 - text segmentation and shaping by Jimmy Lefevre SECURITY This library provides NO SECURITY GUARANTEE whatsoever. DO NOT use it on untrusted font files. WHAT DOES THIS LIBRARY DO? Before computers had monitors, the main way of inspecting the output of a command was to print it out on real paper. When monitors appeared, most computer graphics were text-based, meaning the display was arranged in a hardcoded grid in which each cell could hold one of several hardcoded characters. As simple as it is, this kind of text handling is sufficient for displaying almost any document written in the Latin alphabet and a few other writing systems that happen to fit particularly well on a grid, like Chinese and Japanese. Handwritten Latin characters do not all have the same width, however. As computers became more powerful, glyphs started having different widths to fit better together. After that came kerning, which allows for packing glyphs closer together in pairs. This is, of course, N-squared in the number of glyphs you want to handle, but this is fine for the Latin alphabet, because there really aren't that many glyphs. This is where TrueType stops: it is a good and simple, format for displaying text using the Latin alphabet or writing systems that happen to work similarly to it. All is well and good. What about the rest of the writing systems? Arabic is a cursive writing system. Much like when we write cursive ourselves, the letters need to join together visually. Furthermore, a given letter in Arabic has a different appearance depending on whether it is the first letter of a word, the last letter of a word, or in the middle, and Unicode does not differenciate between any of these. Also, Arabic features a beautiful set of marks that attach to letters, much like accents in the Latin alphabet. You would usually want to align these marks in some way depending on which other marks are present in the immediate vicinity. Indic scripts, like Devanagari, have even less in common with Latin than Arabic does. To try to support the plethora of writing systems out there, OpenType was introduced. OpenType fonts contain rules that allow modifying a sequence of glyphs through pattern matching. These rules can modify both the content of the sequence (ligatures replace multiple glyphs with a single one, for instance) and its visual appearance by e.g. attaching marks to letters. This is the meat of text shaping. OpenType rules have limitations, though. They don't work with mixed direction text, because, when going from one text direction to the other, there is a visual jump that breaks pattern matching. To illustrate, the logical string "0123456789" might have a display order like this: 01234 765 89 ^LTR ^RTL ^LTR continued As you can see, there is a visual jump from 4 all the way to 5, and similarly from 7 to 8. This kind of discontinuity cannot work with OpenType rules, which want to work with "neighboring" glyphs in the visual sense. OpenType rules don't work with mixed script text, either. They are designed to work with a single writing system, and ideally a single language. A typographic rule that is correct in writing system A might not be in writing system B, and vice versa. So, for all of these reasons, we need to split our text before sending it to the shaper. This is what the text processing pipeline looks like: Your text A Text runs with B Sequence of glyphs C (Probably ------------> uniform direction ------------> ready to rasterize ------------> Pixels UTF-8) and script We call arrow A text segmentation, arrow B text shaping, and arrow C rasterization. This library does A and B. FEATURE OVERVIEW This library provides: - Unicode segmentation LTR/RTL breaking Script breaking Line breaking Word breaking Grapheme breaking - OpenType text shaping Open and parse TTF and OTF fonts Apply OpenType features such as ligatures and contextual typographic rules All OpenType shapers are supported, which means most languages in the world are supported (see LANGUAGE_SUPPORT for known non-supported cases) COMPILING & LINKING This library uses declare-anywhere, so it will not compile as C89/VC6 C. In one C/C++ file that #includes this file, do this: #define KB_TEXT_SHAPE_IMPLEMENTATION before the #include. That will create the implementation in that file. If you also do this: #define KB_TEXT_SHAPE_STATIC then all functions will be declared as static. If you do this: #define KB_TEXT_SHAPE_NO_CRT then we do not use the C runtime library. In that case, these functions are compiled out: kbts_ShapePushFontFromFile() kbts_FontFromFile() Additionally, there are some functions that you will want to #define yourself: KBTS_MEMSET defaults to memset otherwise. KBTS_MEMCPY defaults to memcpy otherwise. You can redefine the default allocator by redefining these: KBTS_MALLOC(AllocatorData, Size) defaults to 0 if KB_TEXT_SHAPE_NO_CRT is defined, defaults to malloc(Size) otherwise. KBTS_FREE(AllocatorData, Pointer) defaults to a no-op if KB_TEXT_SHAPE_NO_CRT is defined, defaults to free(Pointer) otherwise. In other words, if you do not redefine the default allocator, and you #define KB_TEXT_SHAPE_NO_CRT, then the default allocator always returns 0. EXAMPLES Basic kbts_shape_context *Context = kbts_CreateShapeContext(0, 0); kbts_ShapePushFontFromFile(Context, "myfont.ttf", 0); kbts_ShapeBegin(Context, KBTS_DIRECTION_DONT_KNOW, KBTS_LANGUAGE_DONT_KNOW); kbts_ShapeUtf8(Context, "Let's shape something!", sizeof("Let's shape something!") - 1, KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX); kbts_ShapeEnd(Context); // Layout runs naively left to right. kbts_run Run; int CursorX = 0, CursorY = 0; while(kbts_ShapeRun(Context, &Run)) { kbts_glyph *Glyph; while(kbts_GlyphIteratorNext(&Run.Glyphs, &Glyph)) { int GlyphX = CursorX + Glyph->OffsetX; int GlyphY = CursorY + Glyph->OffsetY; DisplayGlyph(Glyph->Id, GlyphX, GlyphY); CursorX += Glyph->AdvanceX; CursorY += Glyph->AdvanceY; } } Font collections void *FontData; int FontSize; kbts_font Font = kbts_FontFromFile("myfonts.ttc", 0, 0, 0, &FontData, &FontSize); kbts_ShapePushFont(Context, &Font); int FontCount = kbts_FontCount(FontData, FontSize); for(int FontIndex = 1; FontIndex < FontCount; ++FontIndex) { kbts_ShapePushFontFromMemory(Context, FontData, FontSize, FontIndex); } Feature control kbts_ShapeBegin(Context, KBTS_DIRECTION_DONT_KNOW, KBTS_LANGUAGE_DONT_KNOW); kbts_ShapePushFeature(Context, KBTS_FEATURE_TAG_kern, 0); kbts_ShapeUtf8(Context, "Without kerning", sizeof("Without kerning") - 1, KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX); kbts_ShapePopFeature(Context, KBTS_FEATURE_TAG_kern); kbts_ShapeUtf8(Context, "With kerning", sizeof("With kerning") - 1, KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX); kbts_ShapeEnd(Context); @Todo: Write more examples API The shaping API is broken down into two parts: the context API and the direct API. The context API is the higher-level API of the two and is meant to be the default API. It exposes an immediate-mode, procedural interface somewhat inspired by Dear Imgui and covers most of the functionality present in the library. It notably includes automatic segmentation into paragraphs and runs, shaping, and font fallback. The direct API, in contrast, is all of the tools you can use to directly manage and manipulate shaping data. With it, you can interact directly with the lower-level parts of the library, giving you very granular control. It is also very explicit about memory. As a result, it is also a lot more verbose than the context API. The library also contains several miscellaneous utility functions that are not obviously part of any of the two aforementioned APIs. In the documentation below, all functions (as well as some structs/enums) are marked with "search tags". As an example, this hypothetical function: int kbts_Foo(int X); Will be presented like this: :kbts_Foo :Foo int kbts_Foo(int X); Allowing you to easily search for its documentation by searching for either ":Foo" or ":kbts_Foo". MEMORY MANAGEMENT kb_text_shape takes manual memory management seriously, and tries to give the user as much control over memory as possible. Whenever it is possible for you to pass your own buffer into a function, we allow it. Whenever it is not possible, we allow specifying a custom allocator. An allocator is simply a function that manages memory: :kbts_allocator_function :allocator_function typedef void kbts_allocator_function(void *Data, kbts_allocator_op *Op); [Data] the custom data pointer you passed in along with your allocator. [Op] the memory request. It is of this type: :kbts_allocator_op :allocator_op typedef struct kbts_allocator_op { kbts_allocator_op_kind Kind; union { kbts_allocator_op_allocate Allocate; kbts_allocator_op_free Free; }; } kbts_allocator_op; And the possible op kinds are: KBTS_ALLOCATOR_OP_KIND_ALLOCATE KBTS_ALLOCATOR_OP_KIND_FREE ALLOCATE expects you to fill in Op->Allocate.Pointer. The allocation does not need to be aligned. FREE expects you to free Op->Free.Pointer. THE CONTEXT API CONTEXT:CREATION :kbts_SizeOfShapeContext :SizeOfShapeContext int kbts_SizeOfShapeContext() Tells you how big of a buffer you need to provide to kbts_PlaceShapeContext. :kbts_PlaceShapeContext :PlaceShapeContext kbts_shape_context *kbts_PlaceShapeContext(kbts_allocator_function *Allocator, void *AllocatorData, void *Memory) Places a context at Memory and initializes it. [Allocator] will be used for subsequent allocations. :kbts_PlaceShapeContextFixedMemory :PlaceShapeContextFixedMemory kbts_shape_context *kbts_PlaceShapeContextFixedMemory(void *Memory, int Size) Places a context at Memory and initializes it. This context will only use the [Size] bytes located at [Memory] for its allocations. :kbts_CreateShapeContext :CreateShapeContext kbts_shape_context *kbts_CreateShapeContext(kbts_allocator_function *Allocator, void *AllocatorData) Allocates a context using [Allocator] and initializes it. :kbts_DestroyShapeContext :DestroyShapeContext void kbts_DestroyShapeContext(kbts_shape_context *Context) Frees all context memory. If the Context was allocated by kbts_CreateShapeContext, then it is also freed. CONTEXT:FONT HANDLING The context is capable of managing multiple fonts through a font stack. The font stack will hold references to all fonts in use by the context. Whenever you try to shape some text, the context will check to see if it is supported by the font at the top of the stack. If it is not, it will try the next font down, and so on, until all fonts have been tried. As such, you should push your fallback fonts first, and your preferred fonts last. :kbts_ShapePushFontFromFile :ShapePushFontFromFile kbts_font *kbts_ShapePushFontFromFile(kbts_shape_context *Context, const char *FileName, int FontIndex) (This function is not available if KB_TEXT_SHAPE_NO_CRT is defined.) Opens the file corresponding to [FileName], parses the [FontIndex]th font within it, and, if successful, pushes the result onto the stack. A [return value] of 0 could mean that the stack is out of space (see KBTS_CONTEXT_MAX_FONT_COUNT), that the file could not be found or opened, or that the parse has failed. :kbts_ShapePushFontFromMemory :ShapePushFontFromMemory kbts_font *kbts_ShapePushFontFromMemory(kbts_shape_context *Context, void *Memory, int Size, int FontIndex) Parses the [FontIndex]th font in [Memory] and pushes the result to the font stack. A [return value] of 0 could mean that the stack is out of space (see KBTS_CONTEXT_MAX_FONT_COUNT) or that the font could not be parsed. :kbts_ShapePushFont :ShapePushFont kbts_font *kbts_ShapePushFont(kbts_shape_context *Context, kbts_font *Font) Pushes the pre-parsed [Font] onto the stack. A [return value] of 0 means that the stack has run out of space (see KBTS_CONTEXT_MAX_FONT_COUNT). :kbts_ShapePopFont :ShapePopFont kbts_font *kbts_ShapePopFont(kbts_shape_context *Context) Removes the topmost font from the stack. A [return value] of 0 means that there is no font to remove. A non-null [return value] is the original font pointer that was pushed. If the context allocated the font itself, using kbts_ShapePushFontFromFile or kbts_ShapePushFontFromMemory, then the pointer is still returned, but it points to freed memory. CONTEXT:SHAPING :kbts_ShapeBegin :ShapeBegin void kbts_ShapeBegin(kbts_shape_context *Context, kbts_direction ParagraphDirection, kbts_language Language) Begins a shaping pass. [ParagraphDirection] is sometimes called the "document direction". It can significantly affect segmentation. Bidirectionality in text works like a stack: the default direction is at the bottom of the stack, and, sometimes, text can _temporarily_ take a different direction. In the end, though, it will always go back to the document direction. To illustrate, a period followed by a space ". " typically ends up resetting the current direction to the paragraph direction. This means that, if my paragraph direction is left-to-right, and I am shaping Arabic text, then each Arabic sentence will be right-to-left, but the sentences themselves will be sequenced left-to-right. If [ParagraphDirection] is KBTS_DIRECTION_DONT_KNOW, then the context takes the first directional hint in the text as the paragraph direction. [Language] is used to select which font rules are used. Knowing this allows access to language-specific typographical features. If [Language] is KBTS_LANGUAGE_DONT_KNOW, then the default, language-agnostic font rules are used. :kbts_ShapeEnd :ShapeEnd void kbts_ShapeEnd(kbts_shape_context *Context) Ends a shaping pass. This means you are done providing input to the context, and, in turn, that you can start getting results from it with kbts_ShapeRun(). :kbts_ShapeRun :ShapeRun int kbts_ShapeRun(kbts_shape_context *Context, kbts_run *Run) Once you've called kbts_ShapeEnd, you can get the resulting runs by calling this function repeatedly. !!! CAREFUL !!! Memory is reused from one run to the next, so you cannot trivially store [Run] and reuse it later. Instead, you should traverse the glyphs using the iterator provided in Run.Glyphs and extract whatever data you need before calling kbts_ShapeRun again. The [return value] is non-zero if a run was shaped. When there is no text left to shape, the [return value] is 0. kbts_ShapeEnd(Context); kbts_run Run; while(kbts_ShapeRun(Context, &Run)) { // Handle Run } :kbts_ShapePushFeature :ShapePushFeature void kbts_ShapePushFeature(kbts_shape_context *Context, kbts_u32 FeatureTag, int Value) The context has a feature stack that allows you to manipulate font features hierarchically. When you give text to the context, it will apply all feature overrides that are on the stack at the time. If two feature overrides use the same tag, then only the latest one, i.e. the one higher in the stack, is applied. :kbts_ShapePopFeature :ShapePopFeature int kbts_ShapePopFeature(kbts_shape_context *Context, kbts_u32 FeatureTag) Removes the latest feature override with tag [FeatureTag]. The [return value] is non-zero if an override was found and removed, 0 if not. :kbts_ShapeCodepointWithUserId :ShapeCodepointWithUserId void kbts_ShapeCodepointWithUserId(kbts_shape_context *Context, int Codepoint, int UserId) Inputs a codepoint to shape. [Codepoint] is a Unicode codepoint. [UserId] is an arbitrary identifier that you will get back when reading the results. This is often some kind of index into the input text so that you can perform hit-testing. If an automatic codepoint index is fine for you, consider using kbts_ShapeCodepoint. :kbts_ShapeCodepoint :ShapeCodepoint void kbts_ShapeCodepoint(kbts_shape_context *Context, int Codepoint) Inputs a codepoint to shape. The codepoint's user ID will be an implicit codepoint index assigned by the context. :kbts_ShapeUtf32WithUserId :ShapeUtf32WithUserId void kbts_ShapeUtf32WithUserId(kbts_shape_context *Context, int *Utf32, int Length, int UserId, int UserIdIncrement); Inputs a block of UTF-32 text to shape. User IDs for each codepoint start at [UserId] and increment by [UserIdIncrement] for every codepoint. :kbts_ShapeUtf32 :ShapeUtf32 void kbts_ShapeUtf32(kbts_shape_context *Context, int *Utf32, int Length) Same as kbts_ShapeUtf8WithUserId, but using the context's implicit user ID counter. :kbts_ShapeUtf8WithUserId :ShapeUtf8WithUserId void kbts_ShapeUtf8WithUserId(kbts_shape_context *Context, const char *Utf8, int Length, int UserId, kbts_user_id_generation_mode UserIdGenerationMode); Inputs a block of UTF-8 text to shape. User IDs for the corresponding codepoints start at [UserId]. If [UserIdGenerationMode] is KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX, each codepoint will increment the user ID by 1. If [UserIdGenerationMode] is KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX, each codepoint will increment the user ID by the length of its encoding in UTF-8. :kbts_ShapeUtf8 :ShapeUtf8 void kbts_ShapeUtf8(kbts_shape_context *Context, const char *Utf8, int Length, kbts_user_id_generation_mode UserIdGenerationMode) Same as kbts_ShapeUtf8WithUserId, but using the context's implicit user ID counter. User IDs for the corresponding codepoints start at the context's implicit user ID counter. If [UserIdGenerationMode] is KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX, each codepoint will increment the user ID by 1. If [UserIdGenerationMode] is KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX, each codepoint will increment the user ID by the length of its encoding in bytes in UTF-8. :kbts_ShapeCurrentCodepointsIterator :ShapeCurrentCodepointsIterator kbts_shape_codepoint_iterator kbts_ShapeCurrentCodepointsIterator(kbts_shape_context *Context) The [return value] is an iterator that goes over all of the codepoints fed to [Context] so far. These codepoints are tagged with user IDs, segmentation info and more. See the definition of kbts_shape_codepoint for details. !!! WARNING !!! Remember that segmentation is buffered, so, until you call kbts_ShapeEnd, some codepoints might not be completely filled in yet! Call kbts_ShapeCodepointIteratorNext repeatedly to loop through the corresponding codepoints. :kbts_ShapeCodepointIteratorIsValid :ShapeCodepointIteratorIsValid int kbts_ShapeCodepointIteratorIsValid(kbts_shape_codepoint_iterator *It) The [return value] is non-zero if there is still a codepoint to iterate, zero if not. :kbts_ShapeCodepointIteratorNext :ShapeCodepointIteratorNext int kbts_ShapeCodepointIteratorNext(kbts_shape_codepoint_iterator *It, kbts_shape_codepoint *Codepoint, int *CodepointIndex) Gets the next codepoint from the context [It] was initialized from and writes it to [Codepoint]. If [CodepointIndex] is non-zero, then it is filled with [Codepoint]'s index. The [return value] is non-zero if a codepoint was found, 0 if not. :kbts_ShapeGetShapeCodepoint :ShapeGetShapeCodepoint int kbts_ShapeGetShapeCodepoint(kbts_shape_context *Context, int CodepointIndex, kbts_shape_codepoint *Codepoint) Gets the [CodepointIndex]th codepoint from [Context] and writes it to [Codepoint]. If you are reading glyphs back from the context, then you can use the UserIdOrCodepointIndex field of kbts_glyph here. !!! WARNING !!! When using the context API, UserIdOrCodepointIndex will _always_ be a codepoint index. To get your original user ID, you need to do: kbts_shape_codepoint ShapeCodepoint; kbts_ShapeGetShapeCodepoint(Context, Glyph->UserIdOrCodepointIndex, &ShapeCodepoint); int MyUserId = ShapeCodepoint.UserId; The [return value] is non-zero if [CodepointIndex] is in-bounds, 0 if not. CONTEXT:MISCELLANEOUS :kbts_ShapeError :ShapeError kbts_shape_error kbts_ShapeError(kbts_shape_context *Context); Get the first error that occurred on [Context]. Once a context is tagged with an error, most operations on it will do nothing. Obviously, you can always destroy it. :kbts_ShapeManualBreak :ShapeManualBreak void kbts_ShapeManualBreak(kbts_shape_context *Context); Forces a run break at the current position in the Context. This will flush the current segmentation state just like an end-of-text would, and restart it as if it was at a start-of-text. This will also generate a KBTS_BREAK_FLAG_MANUAL at the current position. You do not need to be in manual break mode for this function to work. :kbts_ShapeBeginManualRuns :ShapeBeginManualRuns void kbts_ShapeBeginManualRuns(kbts_shape_context *Context); Disables the context's automatic segmentation, and enters a manual break mode. :kbts_ShapeNextManualRun :ShapeNextManualRun void kbts_ShapeNextManualRun(kbts_shape_context *Context, kbts_direction Direction, kbts_script Script); Add a run break at the current place in the input stream. Since the context's segmentation is disabled, it cannot know which direction and script to use, so you need to provide them with [Direction] and [Script]. Outside of manual break mode, this function is a no-op. :kbts_ShapeEndManualRuns :ShapeEndManualRuns void kbts_ShapeEndManualRuns(kbts_shape_context *Context); Ends manual break mode and re-enables the context's automatic segmentation. Note that this will force natural break barriers too, just like an end-of-text would. Outside of manual break mode, this function is a no-op. DIRECT SHAPING API When trying to shape things yourself, there are four main pieces of state you will need: - Font data (kbts_font) - A shaping configuration (kbts_shape_config) A shaping configuration holds a bunch of precomputed data for a given combination of font, script and language. You can think of it as a pipeline state in a modern graphics API. In practice, you are only ever shaping text with a single active configuration. - Glyph storage (kbts_glyph_storage) Glyph storage fills two roles: it allocates and holds glyph data, and it also manages a set of active glyphs. The active glyph set part is used by the library. As a user, you only need to care about the memory allocation part. - Scratch memory Unfortunately, shaping can have a very unpredictable memory footprint, so all shaping operations require some amount of scratch space that we cannot compute beforehand. The central function to call is this: :kbts_ShapeDirect :ShapeDirect kbts_shape_error kbts_ShapeDirect(kbts_shape_config *Config, kbts_glyph_storage *Storage, kbts_direction RunDirection, kbts_allocator_function *Allocator, void *AllocatorData, kbts_glyph_iterator *Output) [RunDirection] is the direction of the specific run being shaped. If the [return value] is KBTS_SHAPE_ERROR_NONE, then the shaping operation completed successfully. Shaping output is returned in [Output]. You can go through the resulting glyphs with kbts_GlyphIteratorNext. Note that kbts_ShapeDirect does not care about the paragraph direction. Glyphs are always returned in left-to-right order. In other words, RTL runs are flipped so that visual order is consistent. :kbts_ShapeDirectFixedMemory :ShapeDirectFixedMemory kbts_shape_error kbts_ShapeDirectFixedMemory(kbts_shape_config *Config, kbts_glyph_storage *Storage, kbts_direction RunDirection, void *Memory, int Size, kbts_glyph_iterator *Output) Same as kbts_ShapeDirect, but only uses the [Size] bytes at [Memory] for allocations. The rest of the direct API is more or less about preparing the data you need to call kbts_ShapeDirect. DIRECT:FONT HANDLING :kbts_FontCount :FontCount int kbts_FontCount(void *Data, int Size) Parses the beginning of the file and returns the number of fonts contained within the file data. While most font files contain single fonts, font collections contain several. This function will return 0 if [Data] is invalid, 1 if it represents a single font, and possibly more if it represents a collection. For all functions that require a font index, passing 0 is always safe no matter the kind of file. :kbts_FontFromFile :FontFromFile kbts_font kbts_FontFromFile(const char *FileName, int FontIndex, kbts_allocator_function *Allocator, void *AllocatorData, void **FileData, int *FileSize) (This function is not available if KB_TEXT_SHAPE_NO_CRT is defined.) Opens the file at [FileName], parses it and returns the [FontIndex]th font. You can call kbts_FontIsValid to check if the [return value] is usable. If [FileData] is non-zero, it is filled with a pointer to the file's contents. This pointer is allocated using [Allocator]. If [FileData] is 0, it is freed before the function returns. If [FileSize] is non-zero, it is filled with the size of the file's contents. If [FontIndex] is out of range, the [return value] is invalid. :kbts_FontFromMemory :FontFromMemory kbts_font kbts_FontFromMemory(void *FileData, int FileSize, int FontIndex, kbts_allocator_function *Allocator, void *AllocatorData) Parses the [FontIndex]th font in [FileData]. You can call kbts_FontIsValid to check if the [return value] is usable. :kbts_FontIsValid :FontIsValid int kbts_FontIsValid(kbts_font *Font) Returns whether a font is usable. :kbts_LoadFont :LoadFont kbts_load_font_error kbts_LoadFont(kbts_font *Font, kbts_load_font_state *State, void *FontData, int FontDataSize, int FontIndex, int *ScratchSize, int *OutputSize) Parses the [FontIndex]th font in [FontData] and puts the result into [Font] and [State]. [State] needs to be zeroed before calling this function. If the data represents a TrueType/OpenType font, we need to extract the data we need and create some additional data structures. In this case, the [return value] is KBTS_LOAD_FONT_ERROR_NEED_TO_CREATE_BLOB, and [ScratchSize] and [OutputSize] are filled with the amount of memory we need to create our blob. You can then initialize this blob with kbts_PlaceBlob. If the data represents a kbts blob, then nothing needs to be done, and [Font] is immediately usable. Any value of [FontIndex] less than kbts_FontCount(FontData, FontDataSize) is acceptable. If we could not find any useful font data, the [return value] is KBTS_LOAD_FONT_ERROR_INVALID_FONT. :kbts_PlaceBlob :PlaceBlob kbts_load_font_error kbts_PlaceBlob(kbts_font *Font, kbts_load_font_state *State, void *ScratchMemory, void *OutputMemory) Creates a kbts blob from font data, and places it in [OutputMemory]. [Font] is the resulting font. [State] is the state you passed into kbts_LoadFont. [ScratchMemory] needs to be as big as the [ScratchSize] returned by kbts_LoadFont. You can free this buffer once this function returns. [OutputMemory] needs to be as big as the [OutputSize] returned by kbts_LoadFont. This buffer will be used by [Font] until it is freed by kbts_FreeFont. :kbts_FreeFont :FreeFont void kbts_FreeFont(kbts_font *Font) If [Font] used allocators to allocate its data (for instance, if [Font] was returned by kbts_FontFromFile), frees all of [Font]'s buffers. Otherwise, does nothing. :kbts_GetFontInfo :GetFontInfo void kbts_GetFontInfo(kbts_font *Font, kbts_font_info *Info) Writes a bunch of useful metadata about [Font] into [Info]. You can use this function to extract styling, name and licensing information from a font. We use a simplified representation for font weight and width that is fine for classic font selection, e.g. "I need a bold font". OpenType fonts may feature finer-grained metrics, and we currently do not expose/support those. :kbts_font_style_flags :font_style_flags [Info]->StyleFlags can be: KBTS_FONT_STYLE_FLAG_NONE (no useful style flags have been found) KBTS_FONT_STYLE_FLAG_REGULAR KBTS_FONT_STYLE_FLAG_BOLD KBTS_FONT_STYLE_FLAG_ITALIC A given font can be bold and italic at the same time, but probably not regular and bold and probably not regular and italic. If [Font] is not a valid font, then [Info] will be zeroed. DIRECT:SHAPE CONFIG :kbts_SizeOfShapeConfig :SizeOfShapeConfig int kbts_SizeOfShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language) Returns how large the buffer you pass into kbts_PlaceShapeConfig needs to be. :kbts_PlaceShapeConfig :PlaceShapeConfig kbts_shape_config *kbts_PlaceShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language, void *Memory) Writes a shape config into [Memory] and returns a pointer to it. [Memory] needs to be at least kbts_SizeOfShapeConfig([Font], [Script], [Language]) bytes. :kbts_CreateShapeConfig :CreateShapeConfig kbts_shape_config *kbts_CreateShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language, kbts_allocator_function *Allocator, void *AllocatorData) Allocates and initializes a shape config. :kbts_DestroyShapeConfig :DestroyShapeConfig void kbts_DestroyShapeConfig(kbts_shape_config *Config) If [Config] was allocated in kbts_CreateShapeConfig, frees all of [Config]'s data. Otherwise, nothing is done. DIRECT:GLYPH STORAGE kbts_glyph_storage is a public struct: :kbts_glyph_storage :glyph_storage typedef struct kbts_glyph_storage { kbts_arena Arena; kbts_glyph GlyphSentinel; kbts_glyph FreeGlyphSentinel; } kbts_glyph_storage; A zeroed kbts_glyph_storage will auto-initialize itself when you try to use it. Arena requires an allocator. By default, it will be initialized to KBTS_MALLOC and KBTS_FREE. You can specify your own allocator by writing to Arena.Allocator and Arena.AllocatorData before trying to use a kbts_glyph_storage. Alternatively, you can use kbts_InitializeGlyphStorage() to accomplish the same thing. :kbts_InitializeGlyphStorage :InitializeGlyphStorage int kbts_InitializeGlyphStorage(kbts_glyph_storage *Storage, kbts_allocator_function *Allocator, void *AllocatorData) Initializes [Storage] to use [Allocator] and [AllocatorData]. This is equivalent to setting [Storage]->Arena.Allocator and [Storage]->Arena.AllocatorData, and setting all other members to 0. The [return value] is non-zero if [Storage] is non-null. :kbts_InitializeGlyphStorageFixedMemory :InitializeGlyphStorageFixedMemory int kbts_InitializeGlyphStorageFixedMemory(kbts_glyph_storage *Storage, void *Memory, int MemorySize) Initializes [Storage] to use a fixed-size buffer of size [MemorySize] located at [Memory]. If [Storage] needs more memory than [MemorySize], allocations will fail. The [return value] is non-zero if [Storage] is non-null and [MemorySize] is large enough to initialize [Storage]'s arena. (Currently, this is 5 pointers' worth of bytes.) :kbts_PushGlyph :PushGlyph kbts_glyph *kbts_PushGlyph(kbts_glyph_storage *Storage, kbts_font *Font, int Codepoint, kbts_glyph_config *Config, int UserId) Adds a glyph to [Storage]'s active glyph set and returns a pointer to it. [Font] is used to initialize the glyph's glyph ID. It is assumed that [Font] is the same as the kbts_shape_config's Font field passed into kbts_ShapeDirect. [Config] is the glyph's configuration and needs to stay live until kbts_ShapeDirect completes. See DIRECT:GLYPH CONFIG for more details. [UserId] is a user-provided unique identifier that you can get back once shaping is done. The [return value] might be zero if [Storage]'s allocator fails. :kbts_ClearActiveGlyphs :ClearActiveGlyphs void kbts_ClearActiveGlyphs(kbts_glyph_storage *Storage) Clears [Storage]'s active glyph set. This does not free any memory; rather, it puts the active glyphs in a free list. :kbts_FreeAllGlyphs :FreeAllGlyphs void kbts_FreeAllGlyphs(kbts_glyph_storage *Storage) Frees all memory allocated by [Storage]. :kbts_CodepointToGlyph :CodepointToGlyph kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, int Codepoint, kbts_glyph_config *Config, int UserId) You can create glyphs without a glyph storage at all with this function. :kbts_CodepointToGlyphId :CodepointToGlyphId int kbts_CodepointToGlyphId(kbts_font *Font, int Codepoint) Gets the glyph ID corresponding to [Codepoint] from [Font]. A glyph ID of 0 means that the codepoint is not present in the font. Note that this is not thorough enough to be a good font coverage test! See OTHER:FONT COVERAGE TEST for this. :kbts_ActiveGlyphIterator :ActiveGlyphIterator kbts_glyph_iterator kbts_ActiveGlyphIterator(kbts_glyph_storage *Storage) Returns an iterator to traverse [Storage]'s active glyph set. See OTHER:GLYPH ITERATION for more details on glyph iterators. DIRECT:GLYPH CONFIG The shaper figures out most of the work it needs to do based on the writing system it is shaping. However, some fonts support optional, toggleable features, like "make this text smallcaps". For things like this, you will want to create a kbts_glyph_config. You can then pass it to glyph creation functions or write it to the Config field of a kbts_glyph. A kbts_glyph_config can hold any number of feature overrides. A feature override is a feature tag and a value. Most of the time, you only care whether the value is 0 or 1, but a few features actually care about the exact value. (You can think of a feature that is like "when I am enabled, change this letter to one of these alternatives". In that case, the value you provide in the feature override is used as an index into the array of alternatives.) :kbts_SizeOfGlyphConfig :SizeOfGlyphConfig int kbts_SizeOfGlyphConfig(kbts_feature_override *Overrides, int OverrideCount) Returns the buffer size needed to hold a kbts_glyph_config that describes [Overrides]. This size can vary a lot depending on the kind of feature overrides you specify. Built-in OpenType features with values of 0 or 1 are "free"; they are packed in a fixed-size representation which does not change the config's memory footprint. On the other hand, if you need non-binary values, or non-standard features, then we need to store a description of the override itself, which requires memory. :kbts_PlaceGlyphConfig :PlaceGlyphConfig kbts_glyph_config *kbts_PlaceGlyphConfig(kbts_feature_override *Overrides, int OverrideCount, void *Memory) Writes a kbts_glyph_config that describes [Overrides] into [Memory], and returns a pointer to it. The kbts_glyph_config uses its own representation for overrides, so you can modify [Overrides] once this function returns. :kbts_CreateGlyphConfig :CreateGlyphConfig kbts_glyph_config *kbts_CreateGlyphConfig(kbts_feature_override *Overrides, int OverrideCount, kbts_allocator_function *Allocator, void *AllocatorData) Allocates a kbts_glyph_config that describes [Overrides] and returns a pointer to it. The kbts_glyph_config uses its own representation for overrides, so you can modify [Overrides] once this function returns. :kbts_DestroyGlyphConfig :DestroyGlyphConfig void kbts_DestroyGlyphConfig(kbts_glyph_config *Config) If [Config] was allocated in kbts_CreateGlyphConfig, frees all of its data. Otherwise, does nothing. DIRECT:SEGMENTATION kbts_break_state is the central struct used for segmentation. It contains all of the state needed to perform fixed-memory segmentation of text. :kbts_BreakBegin :BreakBegin void kbts_BreakBegin(kbts_break_state *State, kbts_direction ParagraphDirection, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags) Initializes [State] for segmentation. [ParagraphDirection] is the top-level flow direction of your document or layout. If [ParagraphDirection] is KBTS_DIRECTION_DONT_KNOW, then [State]'s ParagraphDirection will be initialized to the first direction we find while segmenting. :kbts_japanese_line_break_style :japanese_line_break_style [JapaneseLineBreakStyle] can be one of the following: KBTS_JAPANESE_LINE_BREAK_STYLE_STRICT KBTS_JAPANESE_LINE_BREAK_STYLE_NORMAL KBTS_JAPANESE_LINE_BREAK_STYLE_LOOSE Japanese text contains "kinsoku" characters, around which breaking a line is forbidden. Exactly which characters are "kinsoku" or not depends on the context: - Strict style has the largest amount of kinsoku characters, which leads to longer lines. The Unicode standard does not define what strict style is used for. Supposedly, it is used for anything that does not fall into the other two categories of text. - Loose style has the smallest amount of kinsoku characters, which leads to smaller lines. According to the Unicode standard, loose style is used for newspapers. I assume it is also used for any other narrow column format. - Normal style is somewhere in the middle. According to the Unicode standard, normal style is used for books and documents. Note that, while the Unicode standard mentions all three of these styles, it does not mention any differences between the normal and loose styles. As such, normal and loose styles currently behave the same. :kbts_kbts_break_config_flags :kbts_break_config_flags [ConfigFlags] can be a combination of the following: KBTS_BREAK_CONFIG_FLAG_END_OF_TEXT_GENERATES_HARD_LINE_BREAK The Unicode standard specifies that the end of a text should generate a hard line break. However, this is an awkward rule to uphold in practical contexts, because it makes the case where the text ends in a newline ambiguous. So, by default, we disable it. Without this flag (default behavior): \n generates a hard line break at position 1 A generates no hard line break With this flag (Unicode behavior): \n generates a hard line break at position 1 A generates a hard line break at position 1 :kbts_BreakAddCodepoint :BreakAddCodepoint void kbts_BreakAddCodepoint(kbts_break_state *State, int Codepoint, int PositionIncrement, int EndOfText) Feeds [Codepoint] to [State]. [PositionIncrement] is used to update an internal cursor and fill out kbts_break's Position field. If you only care about codepoint indices, pass 1. Maybe you want to pass in the number of bytes it took to decode the codepoint, though, to be able to directly index UTF-8 text. If [EndOfText] is non-zero, kbts_BreakEnd is called after adding [Codepoint]. Every time you call kbts_BreakAddCodepoint, you need to empty the break buffer by calling kbts_Break repeatedly. :kbts_BreakEnd :BreakEnd void kbts_BreakEnd(kbts_break_state *State) Flushes all pending breaks and finishes segmentation. You then obtain breaks by repeatedly calling kbts_Break, just as you would after kbts_BreakAddCodepoint. :kbts_Break :Break int kbts_Break(kbts_break_state *State, kbts_break *Break) If any breaks have been found, writes one to [Break] and returns a non-zero value. If not, returns 0. kbts_break looks like this: typedef struct kbts_break { int Position; kbts_break_flags Flags; kbts_direction Direction; // Only valid if (Flags & KBTS_BREAK_FLAG_DIRECTION). kbts_script Script; // Only valid if (Flags & KBTS_BREAK_FLAG_SCRIPT). } kbts_break; Position is the position of the break, informed by the PositionIncrement you passed to kbts_BreakAddCodepoint. Flags can be any combination of: KBTS_BREAK_FLAG_DIRECTION Indicates a change of direction. KBTS_BREAK_FLAG_SCRIPT Indicates a change of script. KBTS_BREAK_FLAG_GRAPHEME Indicates the start of a grapheme. Unicode describes a grapheme as a visual unit. In practice, you care about graphemes for font coverage testing and caret positioning. The way you do grapheme-aware font coverage testing is you split your text into graphemes, then, for each grapheme, check if it is supported by your font. Grapheme boundaries are nice because they group codepoints that may want to combine together, but it separates codepoints that probably won't recombine, so they work as an synchronization point for font coverage. Caret positioning typically works in graphemes, too. When the user presses the right arrow, you would go to the next grapheme boundary instead of naively going to the next codepoint. KBTS_BREAK_FLAG_WORD Indicates the start of a word. KBTS_BREAK_FLAG_LINE_SOFT A soft line break tells you where you are able to break lines. In Unicode land, you cannot break a line without one of these! KBTS_BREAK_FLAG_LINE_HARD A hard line break should always be respected. KBTS_BREAK_FLAG_MANUAL This is used internally by the kbts_shape_context for manual segmentation. (See kbts_ShapeBeginManualRuns for more details.) !CAREFUL! For a given break type, breaks are guaranteed to be returned in order. However, there is no such ordering guarantee between different types of breaks. Each type of break is processed separately, and the corresponding Unicode algorithms all require some kind of buffering scheme to work in fixed memory, so, while any given buffer is consistent with itself, we cannot order multiple buffers together. :kbts_BreakEntireString :BreakEntireString void kbts_BreakEntireString(kbts_direction ParagraphDirection, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags, void *Input, int InputSizeInBytes, kbts_text_format InputFormat, kbts_break *Breaks, int BreakCapacity, int *BreakCount, kbts_break_flags *BreakFlags, int BreakFlagCapacity, int *BreakFlagCount) Goes through the entire buffer at [Input] and finds all breaks. [Input] is of type [InputFormat], which can be one of: KBTS_TEXT_FORMAT_UTF32 KBTS_TEXT_FORMAT_UTF8 Breaks will be written to [Breaks], up to [BreakCapacity]. Regardless of whether [BreakCapacity] is large enough or not, the amount of breaks found will be written to [BreakCount]. Unlike kbts_Break, here, [Breaks] are guaranteed to be ordered. [BreakFlags] is a parallel array to the input sequence. If a break is found at position X, then BreakFlags[X] will be filled with the appropriate flags, up to [BreakFlagCapacity]. Regardless of whether [BreakFlagCapacity] is large enough or not, the required capacity is written to [BreakFlagCount]. :kbts_BreakEntireStringUtf32 :BreakEntireStringUtf32 void kbts_BreakEntireStringUtf32(kbts_direction ParagraphDirection, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags, int *Utf32, int Utf32Count, kbts_break *Breaks, int BreakCapacity, int *BreakCount, kbts_break_flags *BreakFlags, int BreakFlagCapacity, int *BreakFlagCount) Convenience wrapper for kbts_BreakEntireString for UTF-32 text. :kbts_BreakEntireStringUtf8 :BreakEntireStringUtf8 void kbts_BreakEntireStringUtf8(kbts_direction ParagraphDirection, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags, const char *Utf8, int Utf8Length, kbts_break *Breaks, int BreakCapacity, int *BreakCount, kbts_break_flags *BreakFlags, int BreakFlagCapacity, int *BreakFlagCount) Convenience wrapper for kbts_BreakEntireString for UTF-8 text. This wrapper passes the amount of bytes used to decode each codepoint into kbts_BreakAddCodepoint's PositionIncrement argument. This means that break positions written to [Breaks] point into the UTF-8 stream. :kbts_GuessTextProperties :GuessTextProperties void kbts_GuessTextProperties(void *Text, int TextSizeInBytes, kbts_text_format Format, kbts_direction *Direction, kbts_script *Script) Goes through the input sequence at [Text], finds the first direction and script, and writes them to [Direction] and [Script] respectively. This is a quick-and-dirty way of finding out simple facts about your text. However, the results only really make sense when you know [Input] is mono-script and mono-direction. :kbts_GuessTextPropertiesUtf32 :GuessTextPropertiesUtf32 void kbts_GuessTextPropertiesUtf32(const int *Utf32, int Utf32Count, kbts_direction *Direction, kbts_script *Script) Convenience wrapper for kbts_GuessTextProperties for UTF-32 text. :kbts_GuessTextPropertiesUtf8 :GuessTextPropertiesUtf8 void kbts_GuessTextPropertiesUtf8(const char *Utf8, int Utf8Length, kbts_direction *Direction, kbts_script *Script) Convenience wrapper for kbts_GuessTextProperties for UTF-8 text. OTHER APIS OTHER:GLYPH ITERATION :kbts_GlyphIteratorNext :GlyphIteratorNext int kbts_GlyphIteratorNext(kbts_glyph_iterator *It, kbts_glyph **Glyph) Writes the next glyph to iterate over in [Glyph]. Once shaping is done, the interesting members of a glyph are: - Id: the glyph index/id in the font. - UserId: the user ID you passed in when creating the glyph. This is typically some kind of codepoint index you can use to trace back the glyph to your source text. - AdvanceX/Y and OffsetX/Y: positioning data. Here is how you might use them: kbts_glyph *Glyph; int CursorX = 0, CursorY = 0; while(kbts_GlyphIteratorNext(&It, &Glyph)) { int GlyphX = CursorX + Glyph->OffsetX; int GlyphY = CursorY + Glyph->OffsetY; CursorX += Glyph->AdvanceX; CursorY += Glyph->AdvanceY; } You cannot assume that [Glyph] will stay valid if you free its glyph storage, begin another shaping operation using the same glyph storage, or do any kind of manipulation involving the glyph storage that holds this glyph. Likewise, you should probably not assume that [Glyph] will stay valid after the next call to kbts_GlyphIteratorNext. The [return value] is 1 if we found a glyph to return, and 0 if we did not. Once kbts_GlyphIteratorNext has returned 0, you can keep calling it and it will keep returning 0. :kbts_GlyphIteratorIsValid :GlyphIteratorIsValid int kbts_GlyphIteratorIsValid(kbts_glyph_iterator *It) Returns whether there are still glyphs left to iterate over. If this returns a non-zero value, then the next call to kbts_GlyphIteratorNext will also return a non-zero value and write a valid glyph. OTHER:FONT COVERAGE TEST To implement font fallback, you need to be able to know if a given span of text is supported by a given font. However, this process is not as simple as it sounds. Some Unicode codepoints have "canonical decompositions" and "canonical recompositions" that are meant to describe different ways to represent the same text, but with different codepoints. At the beginning of the shaping process, shapers try all combinations until one is found that is fully supported by the font. A font coverage test does this within a fixed memory footprint. :kbts_FontCoverageTestBegin :FontCoverageTestBegin void kbts_FontCoverageTestBegin(kbts_font_coverage_test *Test, kbts_font *Font) Initializes [Test] to test coverage with [Font]. :kbts_FontCoverageTestCodepoint :FontCoverageTestCodepoint void kbts_FontCoverageTestCodepoint(kbts_font_coverage_test *Test, int Codepoint) Feeds [Codepoint] into [Test] and updates coverage information. :kbts_FontCoverageTestEnd :FontCoverageTestEnd int kbts_FontCoverageTestEnd(kbts_font_coverage_test *Test) Flushes the pending combinations not yet tested by [Test] and ends the coverage test. The [return value] is non-zero if the text is fully supported by the font, whereas it is 0 if any glyph was not supported. You can also check Test->Error to see if any glyph was unsupported. OTHER:OTHER OTHER:MISC :kbts_DecodeUtf8 :DecodeUtf8 kbts_decode kbts_DecodeUtf8(const char *Utf8, kbts_un Length) Tries to decode a single codepoint from [Utf8]. kbts_decode looks like this: typedef struct kbts_decode { int Codepoint; int SourceCharactersConsumed; int Valid; } kbts_decode; Codepoint is the decoded codepoint. SourceCharactersConsumed is the amount of bytes that were read from [Utf8]. If decoding was successful, Valid is non-zero. Otherwise, it is zero. Valid is zero if we run out of characters, or if the characters in [Utf8] are invalid. :kbts_EncodeUtf8 :EncodeUtf8 kbts_encode_utf8 kbts_EncodeUtf8(int Codepoint) Tries to encode a single codepoint into a UTF-8 sequence of bytes. kbts_encode looks like this: typedef struct kbts_encode_utf8 { char Encoded[4]; int EncodedLength; int Valid; } kbts_encode_utf8; Encoded is the encoded sequence. EncodedLength is the number of bytes needed to encode [Codepoint]. Valid is whether or not [Codepoint] is a valid codepoint to encode. (All codepoints up to 0x10FFFF inclusive can be encoded.) When Valid is 0, EncodedLength is also 0. :kbts_ScriptDirection :ScriptDirection kbts_direction kbts_ScriptDirection(kbts_script Script) Returns the default direction for a given script. :kbts_ScriptIsComplex :ScriptIsComplex int kbts_ScriptIsComplex(kbts_script Script) Returns whether a script is complex, i.e. if it requires complex shaper support. :kbts_ScriptTagToScript :ScriptTagToScript kbts_script kbts_ScriptTagToScript(kbts_script_tag Tag) Returns a given script from a four-character tag. A kbts_script_tag can be obtained either through the KBTS_SCRIPT_TAG_* constants, or through the KBTS_FOURCC() macro, which creates a tag from four characters. LANGUAGE SUPPORT Shaping is NOT supported for the following scripts: Zawgyi: some fonts exist, but no standardized OpenType feature set seems to exist as of writing. Syriac: Syriac Abbreviation Mark (0x070F) is not supported. Egyptian Hieroglyphs, I think, although example text is hard to come by. Word breaking is NOT supported for languages that require word dictionaries, like CJK. FONT SUPPORT Indic fonts using the Indic1 shaping model are not supported. e.g., 'bng2' will work, but 'beng' will not. The Indic v2 shaping model was released with OpenType 1.5 in May 2008. Traditional Arabic Windows 3.1 fonts are not supported. https://github.com/harfbuzz/harfbuzz/issues/681 Thai/Lao PUA fonts are not supported. These are old fonts that use OS-specific codepages (PUA stands for [Unicode] "Private Use Area") and pre-OpenType shaping. https://linux.thai.net/~thep/th-otf/shaping.html More generally, we try to be compatible with most well-formed fonts, but we try less hard than Harfbuzz to be compatible with every font under the sun. OTHER LIMITATIONS Explicit direction control characters are not supported. This includes: 0x202A Left-to-right embedding 0x202B Right-to-left embedding 0x202D Left-to-right override 0x202E Right-to-left override 0x202C Pop directional formatting 0x2066 Left-to-right isolate 0x2067 Right-to-left isolate 0x2068 First strong isolate 0x2069 Pop directional isolate See https://unicode.org/reports/tr9 for more information. VERSION HISTORY 2.06 - Faster GSUB and GPOS feature culling. 2.05 - Fix custom allocator initialization for kbts_shape_context.PermanentArena. 2.04 - Fix Indic syllable logic for small/single-character syllables. Fix wrong indirection in pointer code in Indic syllable logic. 2.03 - Fix loading blobs directly, fix a parsing edge case in GPOS format 2 subtables. 2.02 - Improve globbing of cursive attachments. 2.01 - Add kbts_InitializeGlyphStorage and kbts_ScriptDirection. Rename some private functions for better namespacing. Delete some deprecated functions. Bounds check in kbts_ScriptIsComplex. Fix a couple pointer iteration bugs. Fix some pedantic MSVC warnings. Extend mirroring logic from brackets to any codepoint that has a Unicode mirror. 2.0 - Completely new API and implementation. 1.03 - New functions: kbts_FeatureTagToId(), kbts_FeatureOverrideFromTag(), kbts_EmptyGlyphConfig(), kbts_GlyphConfigOverrideFeature(), kbts_GlyphConfigOverrideFeatureFromTag(), kbts_ScriptTagToScript() Unregistered features can now be overriden using their tags. This is slower than overriding registered features, i.e. those that have a kbts_feature_id. Compiler warning cleanup 1.02b - Feature control for GPOS features Bounds checking in ReadFontHeader 1.02a - Positioning fix for format 2 GPOS pair adjustments 1.02 - Added per-glyph manual feature control through kbts_FeatureOverride(), kbts_GlyphConfig() Added enum definitions for features cv01-cv99 and ss01-ss20 1.01 - Header cleanup and glyph output documentation 1.0 - Initial release TODO Word dictionaries for word breaking: CJK, etc. 'stch' feature. LICENSE zlib License (C) Copyright 2024-2025 Jimmy Lefevre This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef KB_TEXT_SHAPE_INCLUDED # define KB_TEXT_SHAPE_INCLUDED # ifndef kbts_s64 # if defined(_MSC_VER) || defined(__BORLANDC__) # define kbts_s64 __int64 # else # define kbts_s64 long long # endif # endif # ifndef kbts_u64 # if defined(_MSC_VER) || defined(__BORLANDC__) # define kbts_u64 unsigned __int64 # else # define kbts_u64 unsigned long long # endif # endif # ifndef kbts_u32 # define kbts_u32 unsigned # endif # ifndef kbts_u16 # define kbts_u16 unsigned short # endif # ifndef kbts_s32 # define kbts_s32 int # endif # ifndef kbts_s16 # define kbts_s16 short # endif # ifndef kbts_u8 # define kbts_u8 unsigned char # endif # ifndef kbts_s8 # define kbts_s8 signed char # endif # ifndef KB_TEXT_SHAPE_POINTER_SIZE # if defined(i386) || defined(__i386__) || defined(_M_IX86) || defined(_M_ARM) || defined(__arm__) || defined(__x86) || (defined(__APPLE__) && defined(__ppc)) || \ (defined(__TOS_AIX__) && !defined(__64BIT)) # define KB_TEXT_SHAPE_POINTER_SIZE 4 # else # define KB_TEXT_SHAPE_POINTER_SIZE 8 # endif # endif # if KB_TEXT_SHAPE_POINTER_SIZE == 4 # define kbts_un kbts_u32 # define kbts_sn kbts_s32 # else # define kbts_un kbts_u64 # define kbts_sn kbts_s64 # endif # ifdef __has_attribute # if __has_attribute(fallthrough) # define KBTS_FALLTHROUGH __attribute__((fallthrough)) # endif # endif # ifndef KBTS_FALLTHROUGH # define KBTS_FALLTHROUGH # endif # ifndef KBTS_EXPORT # ifdef KB_TEXT_SHAPE_STATIC # define KBTS_EXPORT static # else # ifdef __cplusplus # define KBTS_EXPORT extern "C" # else # define KBTS_EXPORT extern # endif # endif # endif # ifdef _MSC_VER # define KBTS_INLINE static __forceinline # define KBTS_NOINLINE static __declspec(noinline) # define KBTS_ALIGNOF __alignof # else # ifdef __has_attribute # if __has_attribute(always_inline) # define KBTS_INLINE static inline __attribute__((always_inline)) # endif # if __has_attribute(noinline) # define KBTS_NOINLINE static __attribute__((noinline)) # endif # endif # define KBTS_ALIGNOF __alignof__ # endif # ifndef KBTS_INLINE # define KBTS_INLINE static inline # endif # define KBTS_FOURCC(A, B, C, D) ((A) | ((B) << 8) | ((C) << 16) | ((D) << 24)) typedef kbts_u32 kbts_language; enum kbts_language_enum { KBTS_LANGUAGE_DONT_KNOW = 0, KBTS_LANGUAGE_A_HMAO = KBTS_FOURCC('H', 'M', 'D', ' '), KBTS_LANGUAGE_AARI = KBTS_FOURCC('A', 'R', 'I', ' '), KBTS_LANGUAGE_ABAZA = KBTS_FOURCC('A', 'B', 'A', ' '), KBTS_LANGUAGE_ABKHAZIAN = KBTS_FOURCC('A', 'B', 'K', ' '), KBTS_LANGUAGE_ACHI = KBTS_FOURCC('A', 'C', 'R', ' '), KBTS_LANGUAGE_ACHOLI = KBTS_FOURCC('A', 'C', 'H', ' '), KBTS_LANGUAGE_ADYGHE = KBTS_FOURCC('A', 'D', 'Y', ' '), KBTS_LANGUAGE_AFAR = KBTS_FOURCC('A', 'F', 'R', ' '), KBTS_LANGUAGE_AFRIKAANS = KBTS_FOURCC('A', 'F', 'K', ' '), KBTS_LANGUAGE_AGAW = KBTS_FOURCC('A', 'G', 'W', ' '), KBTS_LANGUAGE_AITON = KBTS_FOURCC('A', 'I', 'O', ' '), KBTS_LANGUAGE_AKAN = KBTS_FOURCC('A', 'K', 'A', ' '), KBTS_LANGUAGE_ALBANIAN = KBTS_FOURCC('S', 'Q', 'I', ' '), KBTS_LANGUAGE_ALSATIAN = KBTS_FOURCC('A', 'L', 'S', ' '), KBTS_LANGUAGE_ALTAI = KBTS_FOURCC('A', 'L', 'T', ' '), KBTS_LANGUAGE_ALUO = KBTS_FOURCC('Y', 'N', 'A', ' '), KBTS_LANGUAGE_AMERICAN_PHONETIC = KBTS_FOURCC('A', 'P', 'P', 'H'), KBTS_LANGUAGE_AMHARIC = KBTS_FOURCC('A', 'M', 'H', ' '), KBTS_LANGUAGE_ANGLO_SAXON = KBTS_FOURCC('A', 'N', 'G', ' '), KBTS_LANGUAGE_ARABIC = KBTS_FOURCC('A', 'R', 'A', ' '), KBTS_LANGUAGE_ARAGONESE = KBTS_FOURCC('A', 'R', 'G', ' '), KBTS_LANGUAGE_ARAKANESE = KBTS_FOURCC('A', 'R', 'K', ' '), KBTS_LANGUAGE_ARAKWAL = KBTS_FOURCC('R', 'K', 'W', ' '), KBTS_LANGUAGE_ARMENIAN = KBTS_FOURCC('H', 'Y', 'E', ' '), KBTS_LANGUAGE_ARMENIAN_EAST = KBTS_FOURCC('H', 'Y', 'E', '0'), KBTS_LANGUAGE_AROMANIAN = KBTS_FOURCC('R', 'U', 'P', ' '), KBTS_LANGUAGE_ARPITAN = KBTS_FOURCC('F', 'R', 'P', ' '), KBTS_LANGUAGE_ASSAMESE = KBTS_FOURCC('A', 'S', 'M', ' '), KBTS_LANGUAGE_ASTURIAN = KBTS_FOURCC('A', 'S', 'T', ' '), KBTS_LANGUAGE_ATHAPASKAN = KBTS_FOURCC('A', 'T', 'H', ' '), KBTS_LANGUAGE_ATSINA = KBTS_FOURCC('A', 'T', 'S', ' '), KBTS_LANGUAGE_AVAR = KBTS_FOURCC('A', 'V', 'R', ' '), KBTS_LANGUAGE_AVATIME = KBTS_FOURCC('A', 'V', 'N', ' '), KBTS_LANGUAGE_AWADHI = KBTS_FOURCC('A', 'W', 'A', ' '), KBTS_LANGUAGE_AYMARA = KBTS_FOURCC('A', 'Y', 'M', ' '), KBTS_LANGUAGE_AZERBAIDJANI = KBTS_FOURCC('A', 'Z', 'E', ' '), KBTS_LANGUAGE_BADAGA = KBTS_FOURCC('B', 'A', 'D', ' '), KBTS_LANGUAGE_BAGHELKHANDI = KBTS_FOURCC('B', 'A', 'G', ' '), KBTS_LANGUAGE_BAGRI = KBTS_FOURCC('B', 'G', 'Q', ' '), KBTS_LANGUAGE_BALANTE = KBTS_FOURCC('B', 'L', 'N', ' '), KBTS_LANGUAGE_BALINESE = KBTS_FOURCC('B', 'A', 'N', ' '), KBTS_LANGUAGE_BALKAR = KBTS_FOURCC('B', 'A', 'L', ' '), KBTS_LANGUAGE_BALTI = KBTS_FOURCC('B', 'L', 'T', ' '), KBTS_LANGUAGE_BALUCHI = KBTS_FOURCC('B', 'L', 'I', ' '), KBTS_LANGUAGE_BAMBARA = KBTS_FOURCC('B', 'M', 'B', ' '), KBTS_LANGUAGE_BAMILEKE = KBTS_FOURCC('B', 'M', 'L', ' '), KBTS_LANGUAGE_BANDA = KBTS_FOURCC('B', 'A', 'D', '0'), KBTS_LANGUAGE_BANDJALANG = KBTS_FOURCC('B', 'D', 'Y', ' '), KBTS_LANGUAGE_BANGLA = KBTS_FOURCC('B', 'E', 'N', ' '), KBTS_LANGUAGE_BASHKIR = KBTS_FOURCC('B', 'S', 'H', ' '), KBTS_LANGUAGE_BASQUE = KBTS_FOURCC('E', 'U', 'Q', ' '), KBTS_LANGUAGE_BATAK = KBTS_FOURCC('B', 'T', 'K', ' '), KBTS_LANGUAGE_BATAK_ALAS_KLUET = KBTS_FOURCC('B', 'T', 'Z', ' '), KBTS_LANGUAGE_BATAK_ANGKOLA = KBTS_FOURCC('A', 'K', 'B', ' '), KBTS_LANGUAGE_BATAK_DAIRI = KBTS_FOURCC('B', 'T', 'D', ' '), KBTS_LANGUAGE_BATAK_KARO = KBTS_FOURCC('B', 'T', 'X', ' '), KBTS_LANGUAGE_BATAK_MANDAILING = KBTS_FOURCC('B', 'T', 'M', ' '), KBTS_LANGUAGE_BATAK_SIMALUNGUN = KBTS_FOURCC('B', 'T', 'S', ' '), KBTS_LANGUAGE_BATAK_TOBA = KBTS_FOURCC('B', 'B', 'C', ' '), KBTS_LANGUAGE_BAULE = KBTS_FOURCC('B', 'A', 'U', ' '), KBTS_LANGUAGE_BAVARIAN = KBTS_FOURCC('B', 'A', 'R', ' '), KBTS_LANGUAGE_BELARUSIAN = KBTS_FOURCC('B', 'E', 'L', ' '), KBTS_LANGUAGE_BEMBA = KBTS_FOURCC('B', 'E', 'M', ' '), KBTS_LANGUAGE_BENCH = KBTS_FOURCC('B', 'C', 'H', ' '), KBTS_LANGUAGE_BERBER = KBTS_FOURCC('B', 'B', 'R', ' '), KBTS_LANGUAGE_BETI = KBTS_FOURCC('B', 'T', 'I', ' '), KBTS_LANGUAGE_BETTE_KURUMA = KBTS_FOURCC('X', 'U', 'B', ' '), KBTS_LANGUAGE_BHILI = KBTS_FOURCC('B', 'H', 'I', ' '), KBTS_LANGUAGE_BHOJPURI = KBTS_FOURCC('B', 'H', 'O', ' '), KBTS_LANGUAGE_BHUTANESE = KBTS_FOURCC('D', 'Z', 'N', ' '), KBTS_LANGUAGE_BIBLE_CREE = KBTS_FOURCC('B', 'C', 'R', ' '), KBTS_LANGUAGE_BIKOL = KBTS_FOURCC('B', 'I', 'K', ' '), KBTS_LANGUAGE_BILEN = KBTS_FOURCC('B', 'I', 'L', ' '), KBTS_LANGUAGE_BISHNUPRIYA_MANIPURI = KBTS_FOURCC('B', 'P', 'Y', ' '), KBTS_LANGUAGE_BISLAMA = KBTS_FOURCC('B', 'I', 'S', ' '), KBTS_LANGUAGE_BLACKFOOT = KBTS_FOURCC('B', 'K', 'F', ' '), KBTS_LANGUAGE_BODO = KBTS_FOURCC('B', 'R', 'X', ' '), KBTS_LANGUAGE_BOSNIAN = KBTS_FOURCC('B', 'O', 'S', ' '), KBTS_LANGUAGE_BOUYEI = KBTS_FOURCC('P', 'C', 'C', ' '), KBTS_LANGUAGE_BRAHUI = KBTS_FOURCC('B', 'R', 'H', ' '), KBTS_LANGUAGE_BRAJ_BHASHA = KBTS_FOURCC('B', 'R', 'I', ' '), KBTS_LANGUAGE_BRETON = KBTS_FOURCC('B', 'R', 'E', ' '), KBTS_LANGUAGE_BUGIS = KBTS_FOURCC('B', 'U', 'G', ' '), KBTS_LANGUAGE_BULGARIAN = KBTS_FOURCC('B', 'G', 'R', ' '), KBTS_LANGUAGE_BUMTHANGKHA = KBTS_FOURCC('K', 'J', 'Z', ' '), KBTS_LANGUAGE_BURMESE = KBTS_FOURCC('B', 'R', 'M', ' '), KBTS_LANGUAGE_BURUSHASKI = KBTS_FOURCC('B', 'S', 'K', ' '), KBTS_LANGUAGE_CAJUN_FRENCH = KBTS_FOURCC('F', 'R', 'C', ' '), KBTS_LANGUAGE_CARRIER = KBTS_FOURCC('C', 'R', 'R', ' '), KBTS_LANGUAGE_CATALAN = KBTS_FOURCC('C', 'A', 'T', ' '), KBTS_LANGUAGE_CAYUGA = KBTS_FOURCC('C', 'A', 'Y', ' '), KBTS_LANGUAGE_CEBUANO = KBTS_FOURCC('C', 'E', 'B', ' '), KBTS_LANGUAGE_CENTRAL_YUPIK = KBTS_FOURCC('E', 'S', 'U', ' '), KBTS_LANGUAGE_CHAHA_GURAGE = KBTS_FOURCC('C', 'H', 'G', ' '), KBTS_LANGUAGE_CHAMORRO = KBTS_FOURCC('C', 'H', 'A', ' '), KBTS_LANGUAGE_CHATTISGARHI = KBTS_FOURCC('C', 'H', 'H', ' '), KBTS_LANGUAGE_CHECHEN = KBTS_FOURCC('C', 'H', 'E', ' '), KBTS_LANGUAGE_CHEROKEE = KBTS_FOURCC('C', 'H', 'R', ' '), KBTS_LANGUAGE_CHEYENNE = KBTS_FOURCC('C', 'H', 'Y', ' '), KBTS_LANGUAGE_CHICHEWA = KBTS_FOURCC('C', 'H', 'I', ' '), KBTS_LANGUAGE_CHIGA = KBTS_FOURCC('C', 'G', 'G', ' '), KBTS_LANGUAGE_CHIMILA = KBTS_FOURCC('C', 'B', 'G', ' '), KBTS_LANGUAGE_CHIN = KBTS_FOURCC('Q', 'I', 'N', ' '), KBTS_LANGUAGE_CHINANTEC = KBTS_FOURCC('C', 'C', 'H', 'N'), KBTS_LANGUAGE_CHINESE_PHONETIC = KBTS_FOURCC('Z', 'H', 'P', ' '), KBTS_LANGUAGE_CHINESE_SIMPLIFIED = KBTS_FOURCC('Z', 'H', 'S', ' '), KBTS_LANGUAGE_CHINESE_TRADITIONAL = KBTS_FOURCC('Z', 'H', 'T', ' '), KBTS_LANGUAGE_CHINESE_TRADITIONAL_HONG_KONG = KBTS_FOURCC('Z', 'H', 'H', ' '), KBTS_LANGUAGE_CHINESE_TRADITIONAL_MACAO = KBTS_FOURCC('Z', 'H', 'T', 'M'), KBTS_LANGUAGE_CHIPEWYAN = KBTS_FOURCC('C', 'H', 'P', ' '), KBTS_LANGUAGE_CHITTAGONIAN = KBTS_FOURCC('C', 'T', 'G', ' '), KBTS_LANGUAGE_CHOCTAW = KBTS_FOURCC('C', 'H', 'O', ' '), KBTS_LANGUAGE_CHUKCHI = KBTS_FOURCC('C', 'H', 'K', ' '), KBTS_LANGUAGE_CHURCH_SLAVONIC = KBTS_FOURCC('C', 'S', 'L', ' '), KBTS_LANGUAGE_CHUUKESE = KBTS_FOURCC('C', 'H', 'K', '0'), KBTS_LANGUAGE_CHUVASH = KBTS_FOURCC('C', 'H', 'U', ' '), KBTS_LANGUAGE_COMORIAN = KBTS_FOURCC('C', 'M', 'R', ' '), KBTS_LANGUAGE_COMOX = KBTS_FOURCC('C', 'O', 'O', ' '), KBTS_LANGUAGE_COPTIC = KBTS_FOURCC('C', 'O', 'P', ' '), KBTS_LANGUAGE_CORNISH = KBTS_FOURCC('C', 'O', 'R', ' '), KBTS_LANGUAGE_CORSICAN = KBTS_FOURCC('C', 'O', 'S', ' '), KBTS_LANGUAGE_CREE = KBTS_FOURCC('C', 'R', 'E', ' '), KBTS_LANGUAGE_CREOLES = KBTS_FOURCC('C', 'P', 'P', ' '), KBTS_LANGUAGE_CRIMEAN_TATAR = KBTS_FOURCC('C', 'R', 'T', ' '), KBTS_LANGUAGE_CRIOULO = KBTS_FOURCC('K', 'E', 'A', ' '), KBTS_LANGUAGE_CROATIAN = KBTS_FOURCC('H', 'R', 'V', ' '), KBTS_LANGUAGE_CYPRIOT_ARABIC = KBTS_FOURCC('A', 'C', 'Y', ' '), KBTS_LANGUAGE_CZECH = KBTS_FOURCC('C', 'S', 'Y', ' '), KBTS_LANGUAGE_DAGBANI = KBTS_FOURCC('D', 'A', 'G', ' '), KBTS_LANGUAGE_DAN = KBTS_FOURCC('D', 'N', 'J', ' '), KBTS_LANGUAGE_DANGME = KBTS_FOURCC('D', 'N', 'G', ' '), KBTS_LANGUAGE_DANISH = KBTS_FOURCC('D', 'A', 'N', ' '), KBTS_LANGUAGE_DARGWA = KBTS_FOURCC('D', 'A', 'R', ' '), KBTS_LANGUAGE_DARI = KBTS_FOURCC('D', 'R', 'I', ' '), KBTS_LANGUAGE_DAYI = KBTS_FOURCC('D', 'A', 'X', ' '), KBTS_LANGUAGE_DEFAULT = KBTS_FOURCC('d', 'f', 'l', 't'), // Can be DFLT too... KBTS_LANGUAGE_DEHONG_DAI = KBTS_FOURCC('T', 'D', 'D', ' '), KBTS_LANGUAGE_DHANGU = KBTS_FOURCC('D', 'H', 'G', ' '), KBTS_LANGUAGE_DHIVEHI = KBTS_FOURCC('D', 'I', 'V', ' '), KBTS_LANGUAGE_DHUWAL = KBTS_FOURCC('D', 'U', 'J', ' '), KBTS_LANGUAGE_DIMLI = KBTS_FOURCC('D', 'I', 'Q', ' '), KBTS_LANGUAGE_DINKA = KBTS_FOURCC('D', 'N', 'K', ' '), KBTS_LANGUAGE_DIVEHI = KBTS_FOURCC('D', 'I', 'V', ' '), KBTS_LANGUAGE_DJAMBARRPUYNGU = KBTS_FOURCC('D', 'J', 'R', '0'), KBTS_LANGUAGE_DOGRI = KBTS_FOURCC('D', 'G', 'O', ' '), KBTS_LANGUAGE_DOGRI_MACROLANGUAGE = KBTS_FOURCC('D', 'G', 'R', ' '), KBTS_LANGUAGE_DUNGAN = KBTS_FOURCC('D', 'U', 'N', ' '), KBTS_LANGUAGE_DUTCH = KBTS_FOURCC('N', 'L', 'D', ' '), KBTS_LANGUAGE_DZONGKHA = KBTS_FOURCC('D', 'Z', 'N', ' '), KBTS_LANGUAGE_EASTERN_ABENAKI = KBTS_FOURCC('A', 'A', 'Q', ' '), KBTS_LANGUAGE_EASTERN_CHAM = KBTS_FOURCC('C', 'J', 'M', ' '), KBTS_LANGUAGE_EASTERN_CREE = KBTS_FOURCC('E', 'C', 'R', ' '), KBTS_LANGUAGE_EASTERN_MANINKAKAN = KBTS_FOURCC('E', 'M', 'K', ' '), KBTS_LANGUAGE_EASTERN_PWO_KAREN = KBTS_FOURCC('K', 'J', 'P', ' '), KBTS_LANGUAGE_EBIRA = KBTS_FOURCC('E', 'B', 'I', ' '), KBTS_LANGUAGE_EDO = KBTS_FOURCC('E', 'D', 'O', ' '), KBTS_LANGUAGE_EFIK = KBTS_FOURCC('E', 'F', 'I', ' '), KBTS_LANGUAGE_EMBERA_BAUDO = KBTS_FOURCC('B', 'D', 'C', ' '), KBTS_LANGUAGE_EMBERA_CATIO = KBTS_FOURCC('C', 'T', 'O', ' '), KBTS_LANGUAGE_EMBERA_CHAMI = KBTS_FOURCC('C', 'M', 'I', ' '), KBTS_LANGUAGE_EMBERA_TADO = KBTS_FOURCC('T', 'D', 'C', ' '), KBTS_LANGUAGE_ENGLISH = KBTS_FOURCC('E', 'N', 'G', ' '), KBTS_LANGUAGE_EPENA = KBTS_FOURCC('S', 'J', 'A', ' '), KBTS_LANGUAGE_ERZYA = KBTS_FOURCC('E', 'R', 'Z', ' '), KBTS_LANGUAGE_KB_TEXT_SHAPEANTO = KBTS_FOURCC('N', 'T', 'O', ' '), KBTS_LANGUAGE_ESTONIAN = KBTS_FOURCC('E', 'T', 'I', ' '), KBTS_LANGUAGE_EVEN = KBTS_FOURCC('E', 'V', 'N', ' '), KBTS_LANGUAGE_EVENKI = KBTS_FOURCC('E', 'V', 'K', ' '), KBTS_LANGUAGE_EWE = KBTS_FOURCC('E', 'W', 'E', ' '), KBTS_LANGUAGE_FALAM_CHIN = KBTS_FOURCC('H', 'A', 'L', ' '), KBTS_LANGUAGE_FANG = KBTS_FOURCC('F', 'A', 'N', '0'), KBTS_LANGUAGE_FANTI = KBTS_FOURCC('F', 'A', 'T', ' '), KBTS_LANGUAGE_FAROESE = KBTS_FOURCC('F', 'O', 'S', ' '), KBTS_LANGUAGE_FEFE = KBTS_FOURCC('F', 'M', 'P', ' '), KBTS_LANGUAGE_FIJIAN = KBTS_FOURCC('F', 'J', 'I', ' '), KBTS_LANGUAGE_FILIPINO = KBTS_FOURCC('P', 'I', 'L', ' '), KBTS_LANGUAGE_FINNISH = KBTS_FOURCC('F', 'I', 'N', ' '), KBTS_LANGUAGE_FLEMISH = KBTS_FOURCC('F', 'L', 'E', ' '), KBTS_LANGUAGE_FON = KBTS_FOURCC('F', 'O', 'N', ' '), KBTS_LANGUAGE_FOREST_ENETS = KBTS_FOURCC('F', 'N', 'E', ' '), KBTS_LANGUAGE_FRENCH = KBTS_FOURCC('F', 'R', 'A', ' '), KBTS_LANGUAGE_FRENCH_ANTILLEAN = KBTS_FOURCC('F', 'A', 'N', ' '), KBTS_LANGUAGE_FRISIAN = KBTS_FOURCC('F', 'R', 'I', ' '), KBTS_LANGUAGE_FRIULIAN = KBTS_FOURCC('F', 'R', 'L', ' '), KBTS_LANGUAGE_FULAH = KBTS_FOURCC('F', 'U', 'L', ' '), KBTS_LANGUAGE_FUTA = KBTS_FOURCC('F', 'T', 'A', ' '), KBTS_LANGUAGE_GA = KBTS_FOURCC('G', 'A', 'D', ' '), KBTS_LANGUAGE_GAGAUZ = KBTS_FOURCC('G', 'A', 'G', ' '), KBTS_LANGUAGE_GALICIAN = KBTS_FOURCC('G', 'A', 'L', ' '), KBTS_LANGUAGE_GANDA = KBTS_FOURCC('L', 'U', 'G', ' '), KBTS_LANGUAGE_GARHWALI = KBTS_FOURCC('G', 'A', 'W', ' '), KBTS_LANGUAGE_GARO = KBTS_FOURCC('G', 'R', 'O', ' '), KBTS_LANGUAGE_GARSHUNI = KBTS_FOURCC('G', 'A', 'R', ' '), KBTS_LANGUAGE_GEBA_KAREN = KBTS_FOURCC('K', 'V', 'Q', ' '), KBTS_LANGUAGE_GEEZ = KBTS_FOURCC('G', 'E', 'Z', ' '), KBTS_LANGUAGE_GEORGIAN = KBTS_FOURCC('K', 'A', 'T', ' '), KBTS_LANGUAGE_GEPO = KBTS_FOURCC('Y', 'G', 'P', ' '), KBTS_LANGUAGE_GERMAN = KBTS_FOURCC('D', 'E', 'U', ' '), KBTS_LANGUAGE_GIKUYU = KBTS_FOURCC('K', 'I', 'K', ' '), KBTS_LANGUAGE_GILAKI = KBTS_FOURCC('G', 'L', 'K', ' '), KBTS_LANGUAGE_GILBERTESE = KBTS_FOURCC('G', 'I', 'L', '0'), KBTS_LANGUAGE_GILYAK = KBTS_FOURCC('G', 'I', 'L', ' '), KBTS_LANGUAGE_GITHABUL = KBTS_FOURCC('G', 'I', 'H', ' '), KBTS_LANGUAGE_GOGO = KBTS_FOURCC('G', 'O', 'G', ' '), KBTS_LANGUAGE_GONDI = KBTS_FOURCC('G', 'O', 'N', ' '), KBTS_LANGUAGE_GREEK = KBTS_FOURCC('E', 'L', 'L', ' '), KBTS_LANGUAGE_GREENLANDIC = KBTS_FOURCC('G', 'R', 'N', ' '), KBTS_LANGUAGE_GUARANI = KBTS_FOURCC('G', 'U', 'A', ' '), KBTS_LANGUAGE_GUINEA = KBTS_FOURCC('G', 'K', 'P', ' '), KBTS_LANGUAGE_GUJARATI = KBTS_FOURCC('G', 'U', 'J', ' '), KBTS_LANGUAGE_GUMATJ = KBTS_FOURCC('G', 'N', 'N', ' '), KBTS_LANGUAGE_GUMUZ = KBTS_FOURCC('G', 'M', 'Z', ' '), KBTS_LANGUAGE_GUPAPUYNGU = KBTS_FOURCC('G', 'U', 'F', ' '), KBTS_LANGUAGE_GUSII = KBTS_FOURCC('G', 'U', 'Z', ' '), KBTS_LANGUAGE_HAIDA = KBTS_FOURCC('H', 'A', 'I', '0'), KBTS_LANGUAGE_HAITIAN_CREOLE = KBTS_FOURCC('H', 'A', 'I', ' '), KBTS_LANGUAGE_HALKOMELEM = KBTS_FOURCC('H', 'U', 'R', ' '), KBTS_LANGUAGE_HAMMER_BANNA = KBTS_FOURCC('H', 'B', 'N', ' '), KBTS_LANGUAGE_HARARI = KBTS_FOURCC('H', 'R', 'I', ' '), KBTS_LANGUAGE_HARAUTI = KBTS_FOURCC('H', 'A', 'R', ' '), KBTS_LANGUAGE_HARYANVI = KBTS_FOURCC('B', 'G', 'C', ' '), KBTS_LANGUAGE_HAUSA = KBTS_FOURCC('H', 'A', 'U', ' '), KBTS_LANGUAGE_HAVASUPAI_WALAPAI_YAVAPAI = KBTS_FOURCC('Y', 'U', 'F', ' '), KBTS_LANGUAGE_HAWAIIAN = KBTS_FOURCC('H', 'A', 'W', ' '), KBTS_LANGUAGE_HAYA = KBTS_FOURCC('H', 'A', 'Y', ' '), KBTS_LANGUAGE_HAZARAGI = KBTS_FOURCC('H', 'A', 'Z', ' '), KBTS_LANGUAGE_HEBREW = KBTS_FOURCC('I', 'W', 'R', ' '), KBTS_LANGUAGE_HEILTSUK = KBTS_FOURCC('H', 'E', 'I', ' '), KBTS_LANGUAGE_HERERO = KBTS_FOURCC('H', 'E', 'R', ' '), KBTS_LANGUAGE_HIGH_MARI = KBTS_FOURCC('H', 'M', 'A', ' '), KBTS_LANGUAGE_HILIGAYNON = KBTS_FOURCC('H', 'I', 'L', ' '), KBTS_LANGUAGE_HINDI = KBTS_FOURCC('H', 'I', 'N', ' '), KBTS_LANGUAGE_HINDKO = KBTS_FOURCC('H', 'N', 'D', ' '), KBTS_LANGUAGE_HIRI_MOTU = KBTS_FOURCC('H', 'M', 'O', ' '), KBTS_LANGUAGE_HMONG = KBTS_FOURCC('H', 'M', 'N', ' '), KBTS_LANGUAGE_HMONG_DAW = KBTS_FOURCC('M', 'W', 'W', ' '), KBTS_LANGUAGE_HMONG_SHUAT = KBTS_FOURCC('H', 'M', 'Z', ' '), KBTS_LANGUAGE_HO = KBTS_FOURCC('H', 'O', ' ', ' '), KBTS_LANGUAGE_HUNGARIAN = KBTS_FOURCC('H', 'U', 'N', ' '), KBTS_LANGUAGE_IBAN = KBTS_FOURCC('I', 'B', 'A', ' '), KBTS_LANGUAGE_IBIBIO = KBTS_FOURCC('I', 'B', 'B', ' '), KBTS_LANGUAGE_ICELANDIC = KBTS_FOURCC('I', 'S', 'L', ' '), KBTS_LANGUAGE_IDO = KBTS_FOURCC('I', 'D', 'O', ' '), KBTS_LANGUAGE_IGBO = KBTS_FOURCC('I', 'B', 'O', ' '), KBTS_LANGUAGE_IJO = KBTS_FOURCC('I', 'J', 'O', ' '), KBTS_LANGUAGE_ILOKANO = KBTS_FOURCC('I', 'L', 'O', ' '), KBTS_LANGUAGE_INARI_SAMI = KBTS_FOURCC('I', 'S', 'M', ' '), KBTS_LANGUAGE_INDONESIAN = KBTS_FOURCC('I', 'N', 'D', ' '), KBTS_LANGUAGE_INGUSH = KBTS_FOURCC('I', 'N', 'G', ' '), KBTS_LANGUAGE_INTERLINGUA = KBTS_FOURCC('I', 'N', 'A', ' '), KBTS_LANGUAGE_INTERLINGUE = KBTS_FOURCC('I', 'L', 'E', ' '), KBTS_LANGUAGE_INUKTITUT = KBTS_FOURCC('I', 'N', 'U', ' '), KBTS_LANGUAGE_INUPIAT = KBTS_FOURCC('I', 'P', 'K', ' '), KBTS_LANGUAGE_IPA_PHONETIC = KBTS_FOURCC('I', 'P', 'P', ' '), KBTS_LANGUAGE_IRISH = KBTS_FOURCC('I', 'R', 'I', ' '), KBTS_LANGUAGE_IRISH_TRADITIONAL = KBTS_FOURCC('I', 'R', 'T', ' '), KBTS_LANGUAGE_IRULA = KBTS_FOURCC('I', 'R', 'U', ' '), KBTS_LANGUAGE_ITALIAN = KBTS_FOURCC('I', 'T', 'A', ' '), KBTS_LANGUAGE_JAMAICAN_CREOLE = KBTS_FOURCC('J', 'A', 'M', ' '), KBTS_LANGUAGE_JAPANESE = KBTS_FOURCC('J', 'A', 'N', ' '), KBTS_LANGUAGE_JAVANESE = KBTS_FOURCC('J', 'A', 'V', ' '), KBTS_LANGUAGE_JENNU_KURUMA = KBTS_FOURCC('X', 'U', 'J', ' '), KBTS_LANGUAGE_JUDEO_TAT = KBTS_FOURCC('J', 'D', 'T', ' '), KBTS_LANGUAGE_JULA = KBTS_FOURCC('J', 'U', 'L', ' '), KBTS_LANGUAGE_KABARDIAN = KBTS_FOURCC('K', 'A', 'B', ' '), KBTS_LANGUAGE_KABYLE = KBTS_FOURCC('K', 'A', 'B', '0'), KBTS_LANGUAGE_KACHCHI = KBTS_FOURCC('K', 'A', 'C', ' '), KBTS_LANGUAGE_KADIWEU = KBTS_FOURCC('K', 'B', 'C', ' '), KBTS_LANGUAGE_KALENJIN = KBTS_FOURCC('K', 'A', 'L', ' '), KBTS_LANGUAGE_KALMYK = KBTS_FOURCC('K', 'L', 'M', ' '), KBTS_LANGUAGE_KAMBA = KBTS_FOURCC('K', 'M', 'B', ' '), KBTS_LANGUAGE_KANAUJI = KBTS_FOURCC('B', 'J', 'J', ' '), KBTS_LANGUAGE_KANNADA = KBTS_FOURCC('K', 'A', 'N', ' '), KBTS_LANGUAGE_KANURI = KBTS_FOURCC('K', 'N', 'R', ' '), KBTS_LANGUAGE_KAQCHIKEL = KBTS_FOURCC('C', 'A', 'K', ' '), KBTS_LANGUAGE_KARACHAY = KBTS_FOURCC('K', 'A', 'R', ' '), KBTS_LANGUAGE_KARAIM = KBTS_FOURCC('K', 'R', 'M', ' '), KBTS_LANGUAGE_KARAKALPAK = KBTS_FOURCC('K', 'R', 'K', ' '), KBTS_LANGUAGE_KARELIAN = KBTS_FOURCC('K', 'R', 'L', ' '), KBTS_LANGUAGE_KAREN = KBTS_FOURCC('K', 'R', 'N', ' '), KBTS_LANGUAGE_KASHMIRI = KBTS_FOURCC('K', 'S', 'H', ' '), KBTS_LANGUAGE_KASHUBIAN = KBTS_FOURCC('C', 'S', 'B', ' '), KBTS_LANGUAGE_KATE = KBTS_FOURCC('K', 'M', 'G', ' '), KBTS_LANGUAGE_KAZAKH = KBTS_FOURCC('K', 'A', 'Z', ' '), KBTS_LANGUAGE_KEBENA = KBTS_FOURCC('K', 'E', 'B', ' '), KBTS_LANGUAGE_KEKCHI = KBTS_FOURCC('K', 'E', 'K', ' '), KBTS_LANGUAGE_KHAKASS = KBTS_FOURCC('K', 'H', 'A', ' '), KBTS_LANGUAGE_KHAMTI_SHAN = KBTS_FOURCC('K', 'H', 'T', ' '), KBTS_LANGUAGE_KHAMYANG = KBTS_FOURCC('K', 'S', 'U', ' '), KBTS_LANGUAGE_KHANTY_KAZIM = KBTS_FOURCC('K', 'H', 'K', ' '), KBTS_LANGUAGE_KHANTY_SHURISHKAR = KBTS_FOURCC('K', 'H', 'S', ' '), KBTS_LANGUAGE_KHANTY_VAKHI = KBTS_FOURCC('K', 'H', 'V', ' '), KBTS_LANGUAGE_KHASI = KBTS_FOURCC('K', 'S', 'I', ' '), KBTS_LANGUAGE_KHENGKHA = KBTS_FOURCC('X', 'K', 'F', ' '), KBTS_LANGUAGE_KHINALUG = KBTS_FOURCC('K', 'J', 'J', ' '), KBTS_LANGUAGE_KHMER = KBTS_FOURCC('K', 'H', 'M', ' '), KBTS_LANGUAGE_KHORASANI_TURKIC = KBTS_FOURCC('K', 'M', 'Z', ' '), KBTS_LANGUAGE_KHOWAR = KBTS_FOURCC('K', 'H', 'W', ' '), KBTS_LANGUAGE_KHUTSURI_GEORGIAN = KBTS_FOURCC('K', 'G', 'E', ' '), KBTS_LANGUAGE_KICHE = KBTS_FOURCC('Q', 'U', 'C', ' '), KBTS_LANGUAGE_KIKONGO = KBTS_FOURCC('K', 'O', 'N', ' '), KBTS_LANGUAGE_KILDIN_SAMI = KBTS_FOURCC('K', 'S', 'M', ' '), KBTS_LANGUAGE_KINYARWANDA = KBTS_FOURCC('R', 'U', 'A', ' '), KBTS_LANGUAGE_KIRMANJKI = KBTS_FOURCC('K', 'I', 'U', ' '), KBTS_LANGUAGE_KISII = KBTS_FOURCC('K', 'I', 'S', ' '), KBTS_LANGUAGE_KITUBA = KBTS_FOURCC('M', 'K', 'W', ' '), KBTS_LANGUAGE_KODAGU = KBTS_FOURCC('K', 'O', 'D', ' '), KBTS_LANGUAGE_KOKNI = KBTS_FOURCC('K', 'K', 'N', ' '), KBTS_LANGUAGE_KOMI = KBTS_FOURCC('K', 'O', 'M', ' '), KBTS_LANGUAGE_KOMI_PERMYAK = KBTS_FOURCC('K', 'O', 'P', ' '), KBTS_LANGUAGE_KOMI_ZYRIAN = KBTS_FOURCC('K', 'O', 'Z', ' '), KBTS_LANGUAGE_KOMO = KBTS_FOURCC('K', 'M', 'O', ' '), KBTS_LANGUAGE_KOMSO = KBTS_FOURCC('K', 'M', 'S', ' '), KBTS_LANGUAGE_KONGO = KBTS_FOURCC('K', 'O', 'N', '0'), KBTS_LANGUAGE_KONKANI = KBTS_FOURCC('K', 'O', 'K', ' '), KBTS_LANGUAGE_KOORETE = KBTS_FOURCC('K', 'R', 'T', ' '), KBTS_LANGUAGE_KOREAN = KBTS_FOURCC('K', 'O', 'R', ' '), KBTS_LANGUAGE_KOREAO_OLD_HANGUL = KBTS_FOURCC('K', 'O', 'H', ' '), KBTS_LANGUAGE_KORYAK = KBTS_FOURCC('K', 'Y', 'K', ' '), KBTS_LANGUAGE_KOSRAEAN = KBTS_FOURCC('K', 'O', 'S', ' '), KBTS_LANGUAGE_KPELLE = KBTS_FOURCC('K', 'P', 'L', ' '), KBTS_LANGUAGE_KPELLE_LIBERIA = KBTS_FOURCC('X', 'P', 'E', ' '), KBTS_LANGUAGE_KRIO = KBTS_FOURCC('K', 'R', 'I', ' '), KBTS_LANGUAGE_KRYMCHAK = KBTS_FOURCC('J', 'C', 'T', ' '), KBTS_LANGUAGE_KUANYAMA = KBTS_FOURCC('K', 'U', 'A', ' '), KBTS_LANGUAGE_KUBE = KBTS_FOURCC('K', 'G', 'F', ' '), KBTS_LANGUAGE_KUI = KBTS_FOURCC('K', 'U', 'I', ' '), KBTS_LANGUAGE_KULVI = KBTS_FOURCC('K', 'U', 'K', ' '), KBTS_LANGUAGE_KUMAONI = KBTS_FOURCC('K', 'M', 'N', ' '), KBTS_LANGUAGE_KUMYK = KBTS_FOURCC('K', 'U', 'M', ' '), KBTS_LANGUAGE_KURDISH = KBTS_FOURCC('K', 'U', 'R', ' '), KBTS_LANGUAGE_KURUKH = KBTS_FOURCC('K', 'U', 'U', ' '), KBTS_LANGUAGE_KUY = KBTS_FOURCC('K', 'U', 'Y', ' '), KBTS_LANGUAGE_KWAKWALA = KBTS_FOURCC('K', 'W', 'K', ' '), KBTS_LANGUAGE_KYRGYZ = KBTS_FOURCC('K', 'I', 'R', ' '), KBTS_LANGUAGE_L_CREE = KBTS_FOURCC('L', 'C', 'R', ' '), KBTS_LANGUAGE_LADAKHI = KBTS_FOURCC('L', 'D', 'K', ' '), KBTS_LANGUAGE_LADIN = KBTS_FOURCC('L', 'A', 'D', ' '), KBTS_LANGUAGE_LADINO = KBTS_FOURCC('J', 'U', 'D', ' '), KBTS_LANGUAGE_LAHULI = KBTS_FOURCC('L', 'A', 'H', ' '), KBTS_LANGUAGE_LAK = KBTS_FOURCC('L', 'A', 'K', ' '), KBTS_LANGUAGE_LAKI = KBTS_FOURCC('L', 'K', 'I', ' '), KBTS_LANGUAGE_LAMBANI = KBTS_FOURCC('L', 'A', 'M', ' '), KBTS_LANGUAGE_LAMPUNG = KBTS_FOURCC('L', 'J', 'P', ' '), KBTS_LANGUAGE_LAO = KBTS_FOURCC('L', 'A', 'O', ' '), KBTS_LANGUAGE_LATIN = KBTS_FOURCC('L', 'A', 'T', ' '), KBTS_LANGUAGE_LATVIAN = KBTS_FOURCC('L', 'V', 'I', ' '), KBTS_LANGUAGE_LAZ = KBTS_FOURCC('L', 'A', 'Z', ' '), KBTS_LANGUAGE_LELEMI = KBTS_FOURCC('L', 'E', 'F', ' '), KBTS_LANGUAGE_LEZGI = KBTS_FOURCC('L', 'E', 'Z', ' '), KBTS_LANGUAGE_LIGURIAN = KBTS_FOURCC('L', 'I', 'J', ' '), KBTS_LANGUAGE_LIMBU = KBTS_FOURCC('L', 'M', 'B', ' '), KBTS_LANGUAGE_LIMBURGISH = KBTS_FOURCC('L', 'I', 'M', ' '), KBTS_LANGUAGE_LINGALA = KBTS_FOURCC('L', 'I', 'N', ' '), KBTS_LANGUAGE_LIPO = KBTS_FOURCC('L', 'P', 'O', ' '), KBTS_LANGUAGE_LISU = KBTS_FOURCC('L', 'I', 'S', ' '), KBTS_LANGUAGE_LITHUANIAN = KBTS_FOURCC('L', 'T', 'H', ' '), KBTS_LANGUAGE_LIV = KBTS_FOURCC('L', 'I', 'V', ' '), KBTS_LANGUAGE_LOJBAN = KBTS_FOURCC('J', 'B', 'O', ' '), KBTS_LANGUAGE_LOMA = KBTS_FOURCC('L', 'O', 'M', ' '), KBTS_LANGUAGE_LOMBARD = KBTS_FOURCC('L', 'M', 'O', ' '), KBTS_LANGUAGE_LOMWE = KBTS_FOURCC('L', 'M', 'W', ' '), KBTS_LANGUAGE_LOW_MARI = KBTS_FOURCC('L', 'M', 'A', ' '), KBTS_LANGUAGE_LOW_SAXON = KBTS_FOURCC('N', 'D', 'S', ' '), KBTS_LANGUAGE_LOWER_SORBIAN = KBTS_FOURCC('L', 'S', 'B', ' '), KBTS_LANGUAGE_LU = KBTS_FOURCC('X', 'B', 'D', ' '), KBTS_LANGUAGE_LUBA_KATANGA = KBTS_FOURCC('L', 'U', 'B', ' '), KBTS_LANGUAGE_LUBA_LULUA = KBTS_FOURCC('L', 'U', 'A', ' '), KBTS_LANGUAGE_LULE_SAMI = KBTS_FOURCC('L', 'S', 'M', ' '), KBTS_LANGUAGE_LUO = KBTS_FOURCC('L', 'U', 'O', ' '), KBTS_LANGUAGE_LURI = KBTS_FOURCC('L', 'R', 'C', ' '), KBTS_LANGUAGE_LUSHOOTSEED = KBTS_FOURCC('L', 'U', 'T', ' '), KBTS_LANGUAGE_LUXEMBOURGISH = KBTS_FOURCC('L', 'T', 'Z', ' '), KBTS_LANGUAGE_LUYIA = KBTS_FOURCC('L', 'U', 'H', ' '), KBTS_LANGUAGE_MACEDONIAN = KBTS_FOURCC('M', 'K', 'D', ' '), KBTS_LANGUAGE_MADURA = KBTS_FOURCC('M', 'A', 'D', ' '), KBTS_LANGUAGE_MAGAHI = KBTS_FOURCC('M', 'A', 'G', ' '), KBTS_LANGUAGE_MAITHILI = KBTS_FOURCC('M', 'T', 'H', ' '), KBTS_LANGUAGE_MAJANG = KBTS_FOURCC('M', 'A', 'J', ' '), KBTS_LANGUAGE_MAKASAR = KBTS_FOURCC('M', 'K', 'R', ' '), KBTS_LANGUAGE_MAKHUWA = KBTS_FOURCC('M', 'A', 'K', ' '), KBTS_LANGUAGE_MAKONDE = KBTS_FOURCC('K', 'D', 'E', ' '), KBTS_LANGUAGE_MALAGASY = KBTS_FOURCC('M', 'L', 'G', ' '), KBTS_LANGUAGE_MALAY = KBTS_FOURCC('M', 'L', 'Y', ' '), KBTS_LANGUAGE_MALAYALAM = KBTS_FOURCC('M', 'A', 'L', ' '), KBTS_LANGUAGE_MALAYALAM_REFORMED = KBTS_FOURCC('M', 'L', 'R', ' '), KBTS_LANGUAGE_MALE = KBTS_FOURCC('M', 'L', 'E', ' '), KBTS_LANGUAGE_MALINKE = KBTS_FOURCC('M', 'L', 'N', ' '), KBTS_LANGUAGE_MALTESE = KBTS_FOURCC('M', 'T', 'S', ' '), KBTS_LANGUAGE_MAM = KBTS_FOURCC('M', 'A', 'M', ' '), KBTS_LANGUAGE_MANCHU = KBTS_FOURCC('M', 'C', 'H', ' '), KBTS_LANGUAGE_MANDAR = KBTS_FOURCC('M', 'D', 'R', ' '), KBTS_LANGUAGE_MANDINKA = KBTS_FOURCC('M', 'N', 'D', ' '), KBTS_LANGUAGE_MANINKA = KBTS_FOURCC('M', 'N', 'K', ' '), KBTS_LANGUAGE_MANIPURI = KBTS_FOURCC('M', 'N', 'I', ' '), KBTS_LANGUAGE_MANO = KBTS_FOURCC('M', 'E', 'V', ' '), KBTS_LANGUAGE_MANSI = KBTS_FOURCC('M', 'A', 'N', ' '), KBTS_LANGUAGE_MANX = KBTS_FOURCC('M', 'N', 'X', ' '), KBTS_LANGUAGE_MAORI = KBTS_FOURCC('M', 'R', 'I', ' '), KBTS_LANGUAGE_MAPUDUNGUN = KBTS_FOURCC('M', 'A', 'P', ' '), KBTS_LANGUAGE_MARATHI = KBTS_FOURCC('M', 'A', 'R', ' '), KBTS_LANGUAGE_MARSHALLESE = KBTS_FOURCC('M', 'A', 'H', ' '), KBTS_LANGUAGE_MARWARI = KBTS_FOURCC('M', 'A', 'W', ' '), KBTS_LANGUAGE_MAYAN = KBTS_FOURCC('M', 'Y', 'N', ' '), KBTS_LANGUAGE_MAZANDERANI = KBTS_FOURCC('M', 'Z', 'N', ' '), KBTS_LANGUAGE_MBEMBE_TIGON = KBTS_FOURCC('N', 'Z', 'A', ' '), KBTS_LANGUAGE_MBO = KBTS_FOURCC('M', 'B', 'O', ' '), KBTS_LANGUAGE_MBUNDU = KBTS_FOURCC('M', 'B', 'N', ' '), KBTS_LANGUAGE_MEDUMBA = KBTS_FOURCC('B', 'Y', 'V', ' '), KBTS_LANGUAGE_MEEN = KBTS_FOURCC('M', 'E', 'N', ' '), KBTS_LANGUAGE_MENDE = KBTS_FOURCC('M', 'D', 'E', ' '), KBTS_LANGUAGE_MERU = KBTS_FOURCC('M', 'E', 'R', ' '), KBTS_LANGUAGE_MEWATI = KBTS_FOURCC('W', 'T', 'M', ' '), KBTS_LANGUAGE_MINANGKABAU = KBTS_FOURCC('M', 'I', 'N', ' '), KBTS_LANGUAGE_MINJANGBAL = KBTS_FOURCC('X', 'J', 'B', ' '), KBTS_LANGUAGE_MIRANDESE = KBTS_FOURCC('M', 'W', 'L', ' '), KBTS_LANGUAGE_MIZO = KBTS_FOURCC('M', 'I', 'Z', ' '), KBTS_LANGUAGE_MOHAWK = KBTS_FOURCC('M', 'O', 'H', ' '), KBTS_LANGUAGE_MOKSHA = KBTS_FOURCC('M', 'O', 'K', ' '), KBTS_LANGUAGE_MOLDAVIAN = KBTS_FOURCC('M', 'O', 'L', ' '), KBTS_LANGUAGE_MON = KBTS_FOURCC('M', 'O', 'N', ' '), KBTS_LANGUAGE_MONGOLIAN = KBTS_FOURCC('M', 'N', 'G', ' '), KBTS_LANGUAGE_MOOSE_CREE = KBTS_FOURCC('M', 'C', 'R', ' '), KBTS_LANGUAGE_MORISYEN = KBTS_FOURCC('M', 'F', 'E', ' '), KBTS_LANGUAGE_MOROCCAN = KBTS_FOURCC('M', 'O', 'R', ' '), KBTS_LANGUAGE_MOSSI = KBTS_FOURCC('M', 'P', 'S', ' '), KBTS_LANGUAGE_MUNDARI = KBTS_FOURCC('M', 'U', 'N', ' '), KBTS_LANGUAGE_MUSCOGEE = KBTS_FOURCC('M', 'U', 'S', ' '), KBTS_LANGUAGE_N_CREE = KBTS_FOURCC('N', 'C', 'R', ' '), KBTS_LANGUAGE_NAGA_ASSAMESE = KBTS_FOURCC('N', 'A', 'G', ' '), KBTS_LANGUAGE_NAGARI = KBTS_FOURCC('N', 'G', 'R', ' '), KBTS_LANGUAGE_NAHUATL = KBTS_FOURCC('N', 'A', 'H', ' '), KBTS_LANGUAGE_NANAI = KBTS_FOURCC('N', 'A', 'N', ' '), KBTS_LANGUAGE_NASKAPI = KBTS_FOURCC('N', 'A', 'S', ' '), KBTS_LANGUAGE_NAURUAN = KBTS_FOURCC('N', 'A', 'U', ' '), KBTS_LANGUAGE_NAVAJO = KBTS_FOURCC('N', 'A', 'V', ' '), KBTS_LANGUAGE_NDAU = KBTS_FOURCC('N', 'D', 'C', ' '), KBTS_LANGUAGE_NDEBELE = KBTS_FOURCC('N', 'D', 'B', ' '), KBTS_LANGUAGE_NDONGA = KBTS_FOURCC('N', 'D', 'G', ' '), KBTS_LANGUAGE_NEAPOLITAN = KBTS_FOURCC('N', 'A', 'P', ' '), KBTS_LANGUAGE_NEPALI = KBTS_FOURCC('N', 'E', 'P', ' '), KBTS_LANGUAGE_NEWARI = KBTS_FOURCC('N', 'E', 'W', ' '), KBTS_LANGUAGE_NGBAKA = KBTS_FOURCC('N', 'G', 'A', ' '), KBTS_LANGUAGE_NIGERIAN_FULFULDE = KBTS_FOURCC('F', 'U', 'V', ' '), KBTS_LANGUAGE_NIMADI = KBTS_FOURCC('N', 'O', 'E', ' '), KBTS_LANGUAGE_NISI = KBTS_FOURCC('N', 'I', 'S', ' '), KBTS_LANGUAGE_NIUEAN = KBTS_FOURCC('N', 'I', 'U', ' '), KBTS_LANGUAGE_NKO = KBTS_FOURCC('N', 'K', 'O', ' '), KBTS_LANGUAGE_NOGAI = KBTS_FOURCC('N', 'O', 'G', ' '), KBTS_LANGUAGE_NORFOLK = KBTS_FOURCC('P', 'I', 'H', ' '), KBTS_LANGUAGE_NORTH_SLAVEY = KBTS_FOURCC('S', 'C', 'S', ' '), KBTS_LANGUAGE_NORTHERN_EMBERA = KBTS_FOURCC('E', 'M', 'P', ' '), KBTS_LANGUAGE_NORTHERN_SAMI = KBTS_FOURCC('N', 'S', 'M', ' '), KBTS_LANGUAGE_NORTHERN_SOTHO = KBTS_FOURCC('N', 'S', 'O', ' '), KBTS_LANGUAGE_NORTHERN_TAI = KBTS_FOURCC('N', 'T', 'A', ' '), KBTS_LANGUAGE_NORWAY_HOUSE_CREE = KBTS_FOURCC('N', 'H', 'C', ' '), KBTS_LANGUAGE_NORWEGIAN = KBTS_FOURCC('N', 'O', 'R', ' '), KBTS_LANGUAGE_NORWEGIAN_NYNORSK = KBTS_FOURCC('N', 'Y', 'N', ' '), KBTS_LANGUAGE_NOVIAL = KBTS_FOURCC('N', 'O', 'V', ' '), KBTS_LANGUAGE_NUMANGGANG = KBTS_FOURCC('N', 'O', 'P', ' '), KBTS_LANGUAGE_NUNAVIK_INUKTITUT = KBTS_FOURCC('I', 'N', 'U', ' '), KBTS_LANGUAGE_NUU_CHAH_NULTH = KBTS_FOURCC('N', 'U', 'K', ' '), KBTS_LANGUAGE_NYAMWEZI = KBTS_FOURCC('N', 'Y', 'M', ' '), KBTS_LANGUAGE_NYANKOLE = KBTS_FOURCC('N', 'K', 'L', ' '), KBTS_LANGUAGE_OCCITAN = KBTS_FOURCC('O', 'C', 'I', ' '), KBTS_LANGUAGE_ODIA = KBTS_FOURCC('O', 'R', 'I', ' '), KBTS_LANGUAGE_OJI_CREE = KBTS_FOURCC('O', 'C', 'R', ' '), KBTS_LANGUAGE_OJIBWAY = KBTS_FOURCC('O', 'J', 'B', ' '), KBTS_LANGUAGE_OLD_IRISH = KBTS_FOURCC('S', 'G', 'A', ' '), KBTS_LANGUAGE_OLD_JAVANESE = KBTS_FOURCC('K', 'A', 'W', ' '), KBTS_LANGUAGE_ONEIDA = KBTS_FOURCC('O', 'N', 'E', ' '), KBTS_LANGUAGE_ONONDAGA = KBTS_FOURCC('O', 'N', 'O', ' '), KBTS_LANGUAGE_OROMO = KBTS_FOURCC('O', 'R', 'O', ' '), KBTS_LANGUAGE_OSSETIAN = KBTS_FOURCC('O', 'S', 'S', ' '), KBTS_LANGUAGE_PA_O_KAREN = KBTS_FOURCC('B', 'L', 'K', ' '), KBTS_LANGUAGE_PALAUAN = KBTS_FOURCC('P', 'A', 'U', ' '), KBTS_LANGUAGE_PALAUNG = KBTS_FOURCC('P', 'L', 'G', ' '), KBTS_LANGUAGE_PALESTINIAN_ARAMAIC = KBTS_FOURCC('P', 'A', 'A', ' '), KBTS_LANGUAGE_PALI = KBTS_FOURCC('P', 'A', 'L', ' '), KBTS_LANGUAGE_PALPA = KBTS_FOURCC('P', 'A', 'P', ' '), KBTS_LANGUAGE_PAMPANGAN = KBTS_FOURCC('P', 'A', 'M', ' '), KBTS_LANGUAGE_PANGASINAN = KBTS_FOURCC('P', 'A', 'G', ' '), KBTS_LANGUAGE_PAPIAMENTU = KBTS_FOURCC('P', 'A', 'P', '0'), KBTS_LANGUAGE_PASHTO = KBTS_FOURCC('P', 'A', 'S', ' '), KBTS_LANGUAGE_PATTANI_MALAY = KBTS_FOURCC('M', 'F', 'A', ' '), KBTS_LANGUAGE_PENNSYLVANIA_GERMAN = KBTS_FOURCC('P', 'D', 'C', ' '), KBTS_LANGUAGE_PERSIAN = KBTS_FOURCC('F', 'A', 'R', ' '), KBTS_LANGUAGE_PHAKE = KBTS_FOURCC('P', 'J', 'K', ' '), KBTS_LANGUAGE_PICARD = KBTS_FOURCC('P', 'C', 'D', ' '), KBTS_LANGUAGE_PIEMONTESE = KBTS_FOURCC('P', 'M', 'S', ' '), KBTS_LANGUAGE_PILAGA = KBTS_FOURCC('P', 'L', 'G', ' '), KBTS_LANGUAGE_PITE_SAMI = KBTS_FOURCC('S', 'J', 'E', ' '), KBTS_LANGUAGE_POCOMCHI = KBTS_FOURCC('P', 'O', 'H', ' '), KBTS_LANGUAGE_POHNPEIAN = KBTS_FOURCC('P', 'O', 'N', ' '), KBTS_LANGUAGE_POLISH = KBTS_FOURCC('P', 'L', 'K', ' '), KBTS_LANGUAGE_POLYTONIC_GREEK = KBTS_FOURCC('P', 'G', 'R', ' '), KBTS_LANGUAGE_PORTUGUESE = KBTS_FOURCC('P', 'T', 'G', ' '), KBTS_LANGUAGE_PROVENCAL = KBTS_FOURCC('P', 'R', 'O', ' '), KBTS_LANGUAGE_PUNJABI = KBTS_FOURCC('P', 'A', 'N', ' '), KBTS_LANGUAGE_QUECHUA = KBTS_FOURCC('Q', 'U', 'Z', ' '), KBTS_LANGUAGE_QUECHUA_BOLIVIA = KBTS_FOURCC('Q', 'U', 'H', ' '), KBTS_LANGUAGE_QUECHUA_ECUADOR = KBTS_FOURCC('Q', 'V', 'I', ' '), KBTS_LANGUAGE_QUECHUA_PERU = KBTS_FOURCC('Q', 'W', 'H', ' '), KBTS_LANGUAGE_R_CREE = KBTS_FOURCC('R', 'C', 'R', ' '), KBTS_LANGUAGE_RAJASTHANI = KBTS_FOURCC('R', 'A', 'J', ' '), KBTS_LANGUAGE_RAKHINE = KBTS_FOURCC('A', 'R', 'K', ' '), KBTS_LANGUAGE_RAROTONGAN = KBTS_FOURCC('R', 'A', 'R', ' '), KBTS_LANGUAGE_REJANG = KBTS_FOURCC('R', 'E', 'J', ' '), KBTS_LANGUAGE_RIANG = KBTS_FOURCC('R', 'I', 'A', ' '), KBTS_LANGUAGE_RIPUARIAN = KBTS_FOURCC('K', 'S', 'H', ' '), KBTS_LANGUAGE_RITARUNGO = KBTS_FOURCC('R', 'I', 'T', ' '), KBTS_LANGUAGE_ROHINGYA = KBTS_FOURCC('R', 'H', 'G', ' '), KBTS_LANGUAGE_ROMANIAN = KBTS_FOURCC('R', 'O', 'M', ' '), KBTS_LANGUAGE_ROMANSH = KBTS_FOURCC('R', 'M', 'S', ' '), KBTS_LANGUAGE_ROMANY = KBTS_FOURCC('R', 'O', 'Y', ' '), KBTS_LANGUAGE_ROTUMAN = KBTS_FOURCC('R', 'T', 'M', ' '), KBTS_LANGUAGE_RUNDI = KBTS_FOURCC('R', 'U', 'N', ' '), KBTS_LANGUAGE_RUSSIAN = KBTS_FOURCC('R', 'U', 'S', ' '), KBTS_LANGUAGE_RUSSIAN_BURIAT = KBTS_FOURCC('R', 'B', 'U', ' '), KBTS_LANGUAGE_RUSYN = KBTS_FOURCC('R', 'S', 'Y', ' '), KBTS_LANGUAGE_SADRI = KBTS_FOURCC('S', 'A', 'D', ' '), KBTS_LANGUAGE_SAKHA = KBTS_FOURCC('Y', 'A', 'K', ' '), KBTS_LANGUAGE_SAMOAN = KBTS_FOURCC('S', 'M', 'O', ' '), KBTS_LANGUAGE_SAMOGITIAN = KBTS_FOURCC('S', 'G', 'S', ' '), KBTS_LANGUAGE_SAN_BLAS_KUNA = KBTS_FOURCC('C', 'U', 'K', ' '), KBTS_LANGUAGE_SANGO = KBTS_FOURCC('S', 'G', 'O', ' '), KBTS_LANGUAGE_SANSKRIT = KBTS_FOURCC('S', 'A', 'N', ' '), KBTS_LANGUAGE_SANTALI = KBTS_FOURCC('S', 'A', 'T', ' '), KBTS_LANGUAGE_SARAIKI = KBTS_FOURCC('S', 'R', 'K', ' '), KBTS_LANGUAGE_SARDINIAN = KBTS_FOURCC('S', 'R', 'D', ' '), KBTS_LANGUAGE_SASAK = KBTS_FOURCC('S', 'A', 'S', ' '), KBTS_LANGUAGE_SATERLAND_FRISIAN = KBTS_FOURCC('S', 'T', 'Q', ' '), KBTS_LANGUAGE_SAYISI = KBTS_FOURCC('S', 'A', 'Y', ' '), KBTS_LANGUAGE_SCOTS = KBTS_FOURCC('S', 'C', 'I', ' '), KBTS_LANGUAGE_SCOTTISH_GAELIC = KBTS_FOURCC('G', 'A', 'E', ' '), KBTS_LANGUAGE_SEKOTA = KBTS_FOURCC('S', 'E', 'J', ' '), KBTS_LANGUAGE_SELKUP = KBTS_FOURCC('S', 'E', 'L', ' '), KBTS_LANGUAGE_SENA = KBTS_FOURCC('S', 'N', 'A', ' '), KBTS_LANGUAGE_SENECA = KBTS_FOURCC('S', 'E', 'E', ' '), KBTS_LANGUAGE_SERBIAN = KBTS_FOURCC('S', 'R', 'B', ' '), KBTS_LANGUAGE_SERER = KBTS_FOURCC('S', 'R', 'R', ' '), KBTS_LANGUAGE_SGAW_KAREN = KBTS_FOURCC('K', 'S', 'W', ' '), KBTS_LANGUAGE_SHAN = KBTS_FOURCC('S', 'H', 'N', ' '), KBTS_LANGUAGE_SHONA = KBTS_FOURCC('S', 'N', 'A', ' '), KBTS_LANGUAGE_SIBE = KBTS_FOURCC('S', 'I', 'B', ' '), KBTS_LANGUAGE_SICILIAN = KBTS_FOURCC('S', 'C', 'N', ' '), KBTS_LANGUAGE_SIDAMO = KBTS_FOURCC('S', 'I', 'D', ' '), KBTS_LANGUAGE_SILESIAN = KBTS_FOURCC('S', 'Z', 'L', ' '), KBTS_LANGUAGE_SILTE_GURAGE = KBTS_FOURCC('S', 'I', 'G', ' '), KBTS_LANGUAGE_SINDHI = KBTS_FOURCC('S', 'N', 'D', ' '), KBTS_LANGUAGE_SINHALA = KBTS_FOURCC('S', 'N', 'H', ' '), KBTS_LANGUAGE_SKOLT_SAMI = KBTS_FOURCC('S', 'K', 'S', ' '), KBTS_LANGUAGE_SLAVEY = KBTS_FOURCC('S', 'L', 'A', ' '), KBTS_LANGUAGE_SLOVAK = KBTS_FOURCC('S', 'K', 'Y', ' '), KBTS_LANGUAGE_SLOVENIAN = KBTS_FOURCC('S', 'L', 'V', ' '), KBTS_LANGUAGE_SMALL_FLOWERY_MIAO = KBTS_FOURCC('S', 'F', 'M', ' '), KBTS_LANGUAGE_SODO_GURAGE = KBTS_FOURCC('S', 'O', 'G', ' '), KBTS_LANGUAGE_SOGA = KBTS_FOURCC('X', 'O', 'G', ' '), KBTS_LANGUAGE_SOMALI = KBTS_FOURCC('S', 'M', 'L', ' '), KBTS_LANGUAGE_SONGE = KBTS_FOURCC('S', 'O', 'P', ' '), KBTS_LANGUAGE_SONINKE = KBTS_FOURCC('S', 'N', 'K', ' '), KBTS_LANGUAGE_SOUTH_SLAVEY = KBTS_FOURCC('S', 'S', 'L', ' '), KBTS_LANGUAGE_SOUTHERN_KIWAI = KBTS_FOURCC('K', 'J', 'D', ' '), KBTS_LANGUAGE_SOUTHERN_SAMI = KBTS_FOURCC('S', 'S', 'M', ' '), KBTS_LANGUAGE_SOUTHERN_SOTHO = KBTS_FOURCC('S', 'O', 'T', ' '), KBTS_LANGUAGE_SPANISH = KBTS_FOURCC('E', 'S', 'P', ' '), KBTS_LANGUAGE_STANDARD_MOROCCAN_TAMAZIGHT = KBTS_FOURCC('Z', 'G', 'H', ' '), KBTS_LANGUAGE_STRAITS_SALISH = KBTS_FOURCC('S', 'T', 'R', ' '), KBTS_LANGUAGE_SUKUMA = KBTS_FOURCC('S', 'U', 'K', ' '), KBTS_LANGUAGE_SUNDANESE = KBTS_FOURCC('S', 'U', 'N', ' '), KBTS_LANGUAGE_SURI = KBTS_FOURCC('S', 'U', 'R', ' '), KBTS_LANGUAGE_SUTU = KBTS_FOURCC('S', 'X', 'T', ' '), KBTS_LANGUAGE_SVAN = KBTS_FOURCC('S', 'V', 'A', ' '), KBTS_LANGUAGE_SWADAYA_ARAMAIC = KBTS_FOURCC('S', 'W', 'A', ' '), KBTS_LANGUAGE_SWAHILI = KBTS_FOURCC('S', 'W', 'K', ' '), KBTS_LANGUAGE_SWATI = KBTS_FOURCC('S', 'W', 'Z', ' '), KBTS_LANGUAGE_SWEDISH = KBTS_FOURCC('S', 'V', 'E', ' '), KBTS_LANGUAGE_SYLHETI = KBTS_FOURCC('S', 'Y', 'L', ' '), KBTS_LANGUAGE_SYRIAC = KBTS_FOURCC('S', 'Y', 'R', ' '), KBTS_LANGUAGE_SYRIAC_EASTERN = KBTS_FOURCC('S', 'Y', 'R', 'N'), KBTS_LANGUAGE_SYRIAC_ESTRANGELA = KBTS_FOURCC('S', 'Y', 'R', 'E'), KBTS_LANGUAGE_SYRIAC_WESTERN = KBTS_FOURCC('S', 'Y', 'R', 'J'), KBTS_LANGUAGE_TABASARAN = KBTS_FOURCC('T', 'A', 'B', ' '), KBTS_LANGUAGE_TACHELHIT = KBTS_FOURCC('S', 'H', 'I', ' '), KBTS_LANGUAGE_TAGALOG = KBTS_FOURCC('T', 'G', 'L', ' '), KBTS_LANGUAGE_TAHAGGART_TAMAHAQ = KBTS_FOURCC('T', 'H', 'V', ' '), KBTS_LANGUAGE_TAHITIAN = KBTS_FOURCC('T', 'H', 'T', ' '), KBTS_LANGUAGE_TAI_LAING = KBTS_FOURCC('T', 'J', 'L', ' '), KBTS_LANGUAGE_TAJIKI = KBTS_FOURCC('T', 'A', 'J', ' '), KBTS_LANGUAGE_TALYSH = KBTS_FOURCC('T', 'L', 'Y', ' '), KBTS_LANGUAGE_TAMASHEK = KBTS_FOURCC('T', 'M', 'H', ' '), KBTS_LANGUAGE_TAMASHEQ = KBTS_FOURCC('T', 'A', 'Q', ' '), KBTS_LANGUAGE_TAMAZIGHT = KBTS_FOURCC('T', 'Z', 'M', ' '), KBTS_LANGUAGE_TAMIL = KBTS_FOURCC('T', 'A', 'M', ' '), KBTS_LANGUAGE_TARIFIT = KBTS_FOURCC('R', 'I', 'F', ' '), KBTS_LANGUAGE_TATAR = KBTS_FOURCC('T', 'A', 'T', ' '), KBTS_LANGUAGE_TAWALLAMMAT_TAMAJAQ = KBTS_FOURCC('T', 'T', 'Q', ' '), KBTS_LANGUAGE_TAY = KBTS_FOURCC('T', 'Y', 'Z', ' '), KBTS_LANGUAGE_TAYART_TAMAJEQ = KBTS_FOURCC('T', 'H', 'Z', ' '), KBTS_LANGUAGE_TELUGU = KBTS_FOURCC('T', 'E', 'L', ' '), KBTS_LANGUAGE_TEMNE = KBTS_FOURCC('T', 'M', 'N', ' '), KBTS_LANGUAGE_TETUM = KBTS_FOURCC('T', 'E', 'T', ' '), KBTS_LANGUAGE_TH_CREE = KBTS_FOURCC('T', 'C', 'R', ' '), KBTS_LANGUAGE_THAI = KBTS_FOURCC('T', 'H', 'A', ' '), KBTS_LANGUAGE_THAILAND_MON = KBTS_FOURCC('M', 'O', 'N', 'T'), KBTS_LANGUAGE_THOMPSON = KBTS_FOURCC('T', 'H', 'P', ' '), KBTS_LANGUAGE_TIBETAN = KBTS_FOURCC('T', 'I', 'B', ' '), KBTS_LANGUAGE_TIGRE = KBTS_FOURCC('T', 'G', 'R', ' '), KBTS_LANGUAGE_TIGRINYA = KBTS_FOURCC('T', 'G', 'Y', ' '), KBTS_LANGUAGE_TIV = KBTS_FOURCC('T', 'I', 'V', ' '), KBTS_LANGUAGE_TLINGIT = KBTS_FOURCC('T', 'L', 'I', ' '), KBTS_LANGUAGE_TOBO = KBTS_FOURCC('T', 'B', 'V', ' '), KBTS_LANGUAGE_TODO = KBTS_FOURCC('T', 'O', 'D', ' '), KBTS_LANGUAGE_TOK_PISIN = KBTS_FOURCC('T', 'P', 'I', ' '), KBTS_LANGUAGE_TOMA = KBTS_FOURCC('T', 'O', 'D', '0'), KBTS_LANGUAGE_TONGA = KBTS_FOURCC('T', 'N', 'G', ' '), KBTS_LANGUAGE_TONGAN = KBTS_FOURCC('T', 'G', 'N', ' '), KBTS_LANGUAGE_TORKI = KBTS_FOURCC('A', 'Z', 'B', ' '), KBTS_LANGUAGE_TSHANGLA = KBTS_FOURCC('T', 'S', 'J', ' '), KBTS_LANGUAGE_TSONGA = KBTS_FOURCC('T', 'S', 'G', ' '), KBTS_LANGUAGE_TSWANA = KBTS_FOURCC('T', 'N', 'A', ' '), KBTS_LANGUAGE_TULU = KBTS_FOURCC('T', 'U', 'L', ' '), KBTS_LANGUAGE_TUMBUKA = KBTS_FOURCC('T', 'U', 'M', ' '), KBTS_LANGUAGE_TUNDRA_ENETS = KBTS_FOURCC('T', 'N', 'E', ' '), KBTS_LANGUAGE_TURKISH = KBTS_FOURCC('T', 'R', 'K', ' '), KBTS_LANGUAGE_TURKMEN = KBTS_FOURCC('T', 'K', 'M', ' '), KBTS_LANGUAGE_TUROYO_ARAMAIC = KBTS_FOURCC('T', 'U', 'A', ' '), KBTS_LANGUAGE_TUSCARORA = KBTS_FOURCC('T', 'U', 'S', ' '), KBTS_LANGUAGE_TUVALU = KBTS_FOURCC('T', 'V', 'L', ' '), KBTS_LANGUAGE_TUVIN = KBTS_FOURCC('T', 'U', 'V', ' '), KBTS_LANGUAGE_TWI = KBTS_FOURCC('T', 'W', 'I', ' '), KBTS_LANGUAGE_TZOTZIL = KBTS_FOURCC('T', 'Z', 'O', ' '), KBTS_LANGUAGE_UDI = KBTS_FOURCC('U', 'D', 'I', ' '), KBTS_LANGUAGE_UDMURT = KBTS_FOURCC('U', 'D', 'M', ' '), KBTS_LANGUAGE_UKRAINIAN = KBTS_FOURCC('U', 'K', 'R', ' '), KBTS_LANGUAGE_UMBUNDU = KBTS_FOURCC('U', 'M', 'B', ' '), KBTS_LANGUAGE_UME_SAMI = KBTS_FOURCC('S', 'J', 'U', ' '), KBTS_LANGUAGE_UPPER_SAXON = KBTS_FOURCC('S', 'X', 'U', ' '), KBTS_LANGUAGE_UPPER_SORBIAN = KBTS_FOURCC('U', 'S', 'B', ' '), KBTS_LANGUAGE_URALIC_PHONETIC = KBTS_FOURCC('U', 'P', 'P', ' '), KBTS_LANGUAGE_URDU = KBTS_FOURCC('U', 'R', 'D', ' '), KBTS_LANGUAGE_UYGHUR = KBTS_FOURCC('U', 'Y', 'G', ' '), KBTS_LANGUAGE_UZBEK = KBTS_FOURCC('U', 'Z', 'B', ' '), KBTS_LANGUAGE_VENDA = KBTS_FOURCC('V', 'E', 'N', ' '), KBTS_LANGUAGE_VENETIAN = KBTS_FOURCC('V', 'E', 'C', ' '), KBTS_LANGUAGE_VIETNAMESE = KBTS_FOURCC('V', 'I', 'T', ' '), KBTS_LANGUAGE_VLAX_ROMANI = KBTS_FOURCC('R', 'M', 'Y', ' '), KBTS_LANGUAGE_VOLAPUK = KBTS_FOURCC('V', 'O', 'L', ' '), KBTS_LANGUAGE_VORO = KBTS_FOURCC('V', 'R', 'O', ' '), KBTS_LANGUAGE_WA = KBTS_FOURCC('W', 'A', ' ', ' '), KBTS_LANGUAGE_WACI_GBE = KBTS_FOURCC('W', 'C', 'I', ' '), KBTS_LANGUAGE_WAGDI = KBTS_FOURCC('W', 'A', 'G', ' '), KBTS_LANGUAGE_WAKHI = KBTS_FOURCC('W', 'B', 'L', ' '), KBTS_LANGUAGE_WALLOON = KBTS_FOURCC('W', 'L', 'N', ' '), KBTS_LANGUAGE_WARAY_WARAY = KBTS_FOURCC('W', 'A', 'R', ' '), KBTS_LANGUAGE_WAYANAD_CHETTI = KBTS_FOURCC('C', 'T', 'T', ' '), KBTS_LANGUAGE_WAYUU = KBTS_FOURCC('G', 'U', 'C', ' '), KBTS_LANGUAGE_WELSH = KBTS_FOURCC('W', 'E', 'L', ' '), KBTS_LANGUAGE_WENDAT = KBTS_FOURCC('W', 'D', 'T', ' '), KBTS_LANGUAGE_WEST_CREE = KBTS_FOURCC('W', 'C', 'R', ' '), KBTS_LANGUAGE_WESTERN_CHAM = KBTS_FOURCC('C', 'J', 'A', ' '), KBTS_LANGUAGE_WESTERN_KAYAH = KBTS_FOURCC('K', 'Y', 'U', ' '), KBTS_LANGUAGE_WESTERN_PANJABI = KBTS_FOURCC('P', 'N', 'B', ' '), KBTS_LANGUAGE_WESTERN_PWO_KAREN = KBTS_FOURCC('P', 'W', 'O', ' '), KBTS_LANGUAGE_WOLOF = KBTS_FOURCC('W', 'L', 'F', ' '), KBTS_LANGUAGE_WOODS_CREE = KBTS_FOURCC('D', 'C', 'R', ' '), KBTS_LANGUAGE_WUDING_LUQUAN_YI = KBTS_FOURCC('Y', 'W', 'Q', ' '), KBTS_LANGUAGE_WYANDOT = KBTS_FOURCC('W', 'Y', 'N', ' '), KBTS_LANGUAGE_XHOSA = KBTS_FOURCC('X', 'H', 'S', ' '), KBTS_LANGUAGE_Y_CREE = KBTS_FOURCC('Y', 'C', 'R', ' '), KBTS_LANGUAGE_YAO = KBTS_FOURCC('Y', 'A', 'O', ' '), KBTS_LANGUAGE_YAPESE = KBTS_FOURCC('Y', 'A', 'P', ' '), KBTS_LANGUAGE_YI_CLASSIC = KBTS_FOURCC('Y', 'I', 'C', ' '), KBTS_LANGUAGE_YI_MODERN = KBTS_FOURCC('Y', 'I', 'M', ' '), KBTS_LANGUAGE_YIDDISH = KBTS_FOURCC('J', 'I', 'I', ' '), KBTS_LANGUAGE_YORUBA = KBTS_FOURCC('Y', 'B', 'A', ' '), KBTS_LANGUAGE_ZAMBOANGA_CHAVACANO = KBTS_FOURCC('C', 'B', 'K', ' '), KBTS_LANGUAGE_ZANDE = KBTS_FOURCC('Z', 'N', 'D', ' '), KBTS_LANGUAGE_ZARMA = KBTS_FOURCC('D', 'J', 'R', ' '), KBTS_LANGUAGE_ZAZAKI = KBTS_FOURCC('Z', 'Z', 'A', ' '), KBTS_LANGUAGE_ZEALANDIC = KBTS_FOURCC('Z', 'E', 'A', ' '), KBTS_LANGUAGE_ZHUANG = KBTS_FOURCC('Z', 'H', 'A', ' '), KBTS_LANGUAGE_ZULU = KBTS_FOURCC('Z', 'U', 'L', ' '), }; typedef kbts_u32 kbts_break_flags; enum kbts_break_flags_enum { // Direction changes from left-to-right to right-to-left, or vice versa. KBTS_BREAK_FLAG_DIRECTION = 1 << 0, // Script changes. // Note that some characters, such as digits, are used in multiple // scripts and, as such, will not produce script breaks. KBTS_BREAK_FLAG_SCRIPT = 1 << 1, // Graphemes are "visual units". They may be composed of more than one codepoint. // They are used as interaction boundaries in graphical interfaces, e.g. moving the // caret. KBTS_BREAK_FLAG_GRAPHEME = 1 << 2, // In most scripts, words are broken up by whitespace, but Unicode word breaking has // better script coverage and also handles some special cases that a simple stateless // loop cannot handle. KBTS_BREAK_FLAG_WORD = 1 << 3, // By default, you are not allowed to break a line. // Soft line breaks allow for line breaking, but do not require it. // This is useful for when you are doing line wrapping. KBTS_BREAK_FLAG_LINE_SOFT = 1 << 4, // Hard line breaks are required. They signal the end of a paragraph. // (In Unicode, there is no meaningful distinction between a line and a paragraph. // a paragraph is pretty much just a line of text that can wrap.) KBTS_BREAK_FLAG_LINE_HARD = 1 << 5, // Used for manual segmentation in the context. KBTS_BREAK_FLAG_MANUAL = 1 << 6, KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION = 1 << 7, KBTS_BREAK_FLAG_LINE = KBTS_BREAK_FLAG_LINE_SOFT | KBTS_BREAK_FLAG_LINE_HARD, KBTS_BREAK_FLAG_ANY = KBTS_BREAK_FLAG_DIRECTION | KBTS_BREAK_FLAG_SCRIPT | KBTS_BREAK_FLAG_GRAPHEME | KBTS_BREAK_FLAG_WORD | KBTS_BREAK_FLAG_LINE_SOFT | KBTS_BREAK_FLAG_LINE_HARD, }; // Japanese text contains "kinsoku" characters, around which breaking a line is forbidden. // Exactly which characters are "kinsoku" or not depends on the context: // - Strict style has the largest amount of kinsoku characters, which leads to longer lines. // - Loose style has the smallest amount of kinsoku characters, which leads to smaller lines. // - Normal style is somewhere in the middle. // Note that, while the Unicode standard mentions all three of these styles, it does not mention // any differences between the normal and loose styles. // As such, normal and loose styles currently behave the same. typedef kbts_u8 kbts_japanese_line_break_style; enum kbts_japanese_line_break_style_enum { // The Unicode standard does not define what strict style is used for. // Supposedly, it is used for anything that does not fall into the other two categories of text. KBTS_JAPANESE_LINE_BREAK_STYLE_STRICT, // Normal style is used for books and documents. KBTS_JAPANESE_LINE_BREAK_STYLE_NORMAL, // Loose style is used for newspapers, and (I assume) any other narrow column format. KBTS_JAPANESE_LINE_BREAK_STYLE_LOOSE, KBTS_JAPANESE_LINE_BREAK_STYLE_COUNT, }; typedef kbts_u32 kbts_break_state_flags; enum kbts_break_state_flags_enum { KBTS_BREAK_STATE_FLAG_STARTED = 1, KBTS_BREAK_STATE_FLAG_END = 2, // Bidirectional flags KBTS_BREAK_STATE_FLAG_SAW_R_AFTER_L = 8, KBTS_BREAK_STATE_FLAG_SAW_AL_AFTER_LR = 0x10, KBTS_BREAK_STATE_FLAG_LAST_WAS_BRACKET = 0x20, }; typedef kbts_u32 kbts_text_format; enum kbts_text_format_enum { KBTS_TEXT_FORMAT_NONE, KBTS_TEXT_FORMAT_UTF32, KBTS_TEXT_FORMAT_UTF8, KBTS_TEXT_FORMAT_COUNT, }; typedef kbts_u32 kbts_direction; enum kbts_direction_enum { KBTS_DIRECTION_DONT_KNOW, KBTS_DIRECTION_LTR, KBTS_DIRECTION_RTL, KBTS_DIRECTION_COUNT, }; typedef kbts_u32 kbts_orientation; enum kbts_orientation_enum { KBTS_ORIENTATION_HORIZONTAL, KBTS_ORIENTATION_VERTICAL, KBTS_ORIENTATION_COUNT, }; typedef kbts_u8 kbts_shaping_table; enum kbts_shaping_table_enum { KBTS_SHAPING_TABLE_GSUB, KBTS_SHAPING_TABLE_GPOS, KBTS_SHAPING_TABLE_COUNT, }; typedef kbts_u32 kbts_shape_error; enum kbts_shape_error_enum { KBTS_SHAPE_ERROR_NONE, KBTS_SHAPE_ERROR_INVALID_FONT, KBTS_SHAPE_ERROR_GAVE_TEXT_BEFORE_CALLING_BEGIN, KBTS_SHAPE_ERROR_OUT_OF_MEMORY, KBTS_SHAPE_ERROR_COUNT, }; typedef kbts_u32 kbts_allocator_op_kind; enum kbts_allocator_op_kind_enum { KBTS_ALLOCATOR_OP_KIND_NONE, KBTS_ALLOCATOR_OP_KIND_ALLOCATE, KBTS_ALLOCATOR_OP_KIND_FREE, KBTS_ALLOCATOR_OP_KIND_COUNT, }; typedef kbts_u32 kbts_blob_table_id; enum kbts_blob_table_id_enum { KBTS_BLOB_TABLE_ID_NONE, KBTS_BLOB_TABLE_ID_HEAD, KBTS_BLOB_TABLE_ID_CMAP, KBTS_BLOB_TABLE_ID_GDEF, KBTS_BLOB_TABLE_ID_GSUB, KBTS_BLOB_TABLE_ID_GPOS, KBTS_BLOB_TABLE_ID_HHEA, KBTS_BLOB_TABLE_ID_VHEA, KBTS_BLOB_TABLE_ID_HMTX, KBTS_BLOB_TABLE_ID_VMTX, KBTS_BLOB_TABLE_ID_MAXP, KBTS_BLOB_TABLE_ID_OS2, KBTS_BLOB_TABLE_ID_NAME, KBTS_BLOB_TABLE_ID_COUNT, }; typedef kbts_u32 kbts_load_font_error; enum kbts_load_font_error_enum { KBTS_LOAD_FONT_ERROR_NONE, KBTS_LOAD_FONT_ERROR_NEED_TO_CREATE_BLOB, KBTS_LOAD_FONT_ERROR_INVALID_FONT, KBTS_LOAD_FONT_ERROR_OUT_OF_MEMORY, KBTS_LOAD_FONT_ERROR_COULD_NOT_OPEN_FILE, KBTS_LOAD_FONT_ERROR_READ_ERROR, KBTS_LOAD_FONT_ERROR_COUNT, }; typedef kbts_u32 kbts_version; enum kbts_version_enum { KBTS_VERSION_1_X, KBTS_VERSION_2_0, KBTS_VERSION_CURRENT = KBTS_VERSION_2_0, }; typedef kbts_u32 kbts_blob_version; enum kbts_blob_version_enum { KBTS_BLOB_VERSION_INVALID, KBTS_BLOB_VERSION_INITIAL, KBTS_BLOB_VERSION_CURRENT = KBTS_BLOB_VERSION_INITIAL, }; typedef kbts_u32 kbts_font_style_flags; enum kbts_font_style_flags_enum { KBTS_FONT_STYLE_FLAG_NONE, KBTS_FONT_STYLE_FLAG_REGULAR = (1 << 0), KBTS_FONT_STYLE_FLAG_ITALIC = (1 << 1), KBTS_FONT_STYLE_FLAG_BOLD = (1 << 2), }; typedef kbts_u32 kbts_font_weight; enum kbts_font_weight_enum { KBTS_FONT_WEIGHT_UNKNOWN, KBTS_FONT_WEIGHT_THIN, KBTS_FONT_WEIGHT_EXTRA_LIGHT, KBTS_FONT_WEIGHT_LIGHT, KBTS_FONT_WEIGHT_NORMAL, KBTS_FONT_WEIGHT_MEDIUM, KBTS_FONT_WEIGHT_SEMI_BOLD, KBTS_FONT_WEIGHT_BOLD, KBTS_FONT_WEIGHT_EXTRA_BOLD, KBTS_FONT_WEIGHT_BLACK, KBTS_FONT_WEIGHT_COUNT, }; typedef kbts_u32 kbts_font_width; enum kbts_font_width_enum { KBTS_FONT_WIDTH_UNKNOWN, KBTS_FONT_WIDTH_ULTRA_CONDENSED, KBTS_FONT_WIDTH_EXTRA_CONDENSED, KBTS_FONT_WIDTH_CONDENSED, KBTS_FONT_WIDTH_SEMI_CONDENSED, KBTS_FONT_WIDTH_NORMAL, KBTS_FONT_WIDTH_SEMI_EXPANDED, KBTS_FONT_WIDTH_EXPANDED, KBTS_FONT_WIDTH_EXTRA_EXPANDED, KBTS_FONT_WIDTH_ULTRA_EXPANDED, KBTS_FONT_WIDTH_COUNT, }; typedef kbts_u32 kbts_glyph_flags; enum kbts_glyph_flags_enum { // These feature flags must coincide with kbts_joining_feature _and_ KBTS_FEATURE_FLAG! KBTS_GLYPH_FLAG_ISOL = (1 << 0), KBTS_GLYPH_FLAG_FINA = (1 << 1), KBTS_GLYPH_FLAG_FIN2 = (1 << 2), KBTS_GLYPH_FLAG_FIN3 = (1 << 3), KBTS_GLYPH_FLAG_MEDI = (1 << 4), KBTS_GLYPH_FLAG_MED2 = (1 << 5), KBTS_GLYPH_FLAG_INIT = (1 << 6), // These feature flags must coincide with FEATURE_FLAG! KBTS_GLYPH_FLAG_LJMO = (1 << 7), KBTS_GLYPH_FLAG_VJMO = (1 << 8), KBTS_GLYPH_FLAG_TJMO = (1 << 9), KBTS_GLYPH_FLAG_RPHF = (1 << 10), KBTS_GLYPH_FLAG_BLWF = (1 << 11), KBTS_GLYPH_FLAG_HALF = (1 << 12), KBTS_GLYPH_FLAG_PSTF = (1 << 13), KBTS_GLYPH_FLAG_ABVF = (1 << 14), KBTS_GLYPH_FLAG_PREF = (1 << 15), KBTS_GLYPH_FLAG_NUMR = (1 << 16), KBTS_GLYPH_FLAG_FRAC = (1 << 17), KBTS_GLYPH_FLAG_DNOM = (1 << 18), KBTS_GLYPH_FLAG_CFAR = (1 << 19), // These can be anything. KBTS_GLYPH_FLAG_DO_NOT_DECOMPOSE = (1 << 21), KBTS_GLYPH_FLAG_FIRST_IN_MULTIPLE_SUBSTITUTION = (1 << 22), KBTS_GLYPH_FLAG_NO_BREAK = (1 << 23), KBTS_GLYPH_FLAG_CURSIVE = (1 << 24), KBTS_GLYPH_FLAG_GENERATED_BY_GSUB = (1 << 25), KBTS_GLYPH_FLAG_USED_IN_GPOS = (1 << 26), KBTS_GLYPH_FLAG_STCH_ENDPOINT = (1 << 27), KBTS_GLYPH_FLAG_STCH_EXTENSION = (1 << 28), KBTS_GLYPH_FLAG_LIGATURE = (1 << 29), KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION = (1 << 30), }; typedef kbts_u8 kbts_joining_feature; enum kbts_joining_feature_enum { KBTS_JOINING_FEATURE_NONE, // These must correspond with glyph_flags and FEATURE_IDs. KBTS_JOINING_FEATURE_ISOL, KBTS_JOINING_FEATURE_FINA, KBTS_JOINING_FEATURE_FIN2, KBTS_JOINING_FEATURE_FIN3, KBTS_JOINING_FEATURE_MEDI, KBTS_JOINING_FEATURE_MED2, KBTS_JOINING_FEATURE_INIT, KBTS_JOINING_FEATURE_COUNT, }; typedef kbts_u32 kbts_user_id_generation_mode; enum kbts_user_id_generation_mode_enum { KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX, KBTS_USER_ID_GENERATION_MODE_SOURCE_INDEX, KBTS_USER_ID_GENERATION_MODE_COUNT, }; typedef kbts_u32 kbts_break_config_flags; enum kbts_break_config_flags_enum { KBTS_BREAK_CONFIG_FLAG_NONE, KBTS_BREAK_CONFIG_FLAG_END_OF_TEXT_GENERATES_HARD_LINE_BREAK = 1, }; typedef kbts_u32 kbts_font_info_string_id; enum kbts_font_info_string_id_enum { KBTS_FONT_INFO_STRING_ID_NONE, KBTS_FONT_INFO_STRING_ID_COPYRIGHT, KBTS_FONT_INFO_STRING_ID_FAMILY, KBTS_FONT_INFO_STRING_ID_SUBFAMILY, KBTS_FONT_INFO_STRING_ID_UID, KBTS_FONT_INFO_STRING_ID_FULL_NAME, KBTS_FONT_INFO_STRING_ID_VERSION, KBTS_FONT_INFO_STRING_ID_POSTSCRIPT_NAME, KBTS_FONT_INFO_STRING_ID_TRADEMARK, KBTS_FONT_INFO_STRING_ID_MANUFACTURER, KBTS_FONT_INFO_STRING_ID_DESIGNER, KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_FAMILY, KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_SUBFAMILY, KBTS_FONT_INFO_STRING_ID_COUNT, }; typedef kbts_u8 kbts_unicode_joining_type; enum kbts_unicode_joining_type_enum { KBTS_UNICODE_JOINING_TYPE_NONE, KBTS_UNICODE_JOINING_TYPE_LEFT, KBTS_UNICODE_JOINING_TYPE_DUAL, KBTS_UNICODE_JOINING_TYPE_FORCE, KBTS_UNICODE_JOINING_TYPE_RIGHT, KBTS_UNICODE_JOINING_TYPE_TRANSPARENT, KBTS_UNICODE_JOINING_TYPE_COUNT, }; typedef kbts_u8 kbts_unicode_flags; enum kbts_unicode_flag_enum { KBTS_UNICODE_FLAG_MODIFIER_COMBINING_MARK = (1 << 0), KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE = (1 << 1), KBTS_UNICODE_FLAG_OPEN_BRACKET = (1 << 2), KBTS_UNICODE_FLAG_CLOSE_BRACKET = (1 << 3), KBTS_UNICODE_FLAG_PART_OF_WORD = (1 << 4), KBTS_UNICODE_FLAG_DECIMAL_DIGIT = (1 << 5), KBTS_UNICODE_FLAG_NON_SPACING_MARK = (1 << 6), KBTS_UNICODE_FLAG_MIRRORED = KBTS_UNICODE_FLAG_OPEN_BRACKET | KBTS_UNICODE_FLAG_CLOSE_BRACKET, }; typedef kbts_u8 kbts_unicode_bidirectional_class; enum kbts_unicode_bidirectional_class_enum { KBTS_UNICODE_BIDIRECTIONAL_CLASS_NI, KBTS_UNICODE_BIDIRECTIONAL_CLASS_BN, // Formatting characters need to be ignored. KBTS_UNICODE_BIDIRECTIONAL_CLASS_L, KBTS_UNICODE_BIDIRECTIONAL_CLASS_R, KBTS_UNICODE_BIDIRECTIONAL_CLASS_NSM, KBTS_UNICODE_BIDIRECTIONAL_CLASS_AL, KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN, KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN, KBTS_UNICODE_BIDIRECTIONAL_CLASS_ES, KBTS_UNICODE_BIDIRECTIONAL_CLASS_ET, KBTS_UNICODE_BIDIRECTIONAL_CLASS_CS, KBTS_UNICODE_BIDIRECTIONAL_CLASS_COUNT, }; typedef kbts_u8 kbts_line_break_class; enum kbts_line_break_class_enum { /* 0 */ KBTS_LINE_BREAK_CLASS_Onea, /* 1 */ KBTS_LINE_BREAK_CLASS_Oea, /* 2 */ KBTS_LINE_BREAK_CLASS_Ope, /* 3 */ KBTS_LINE_BREAK_CLASS_BK, /* 4 */ KBTS_LINE_BREAK_CLASS_CR, /* 5 */ KBTS_LINE_BREAK_CLASS_LF, /* 6 */ KBTS_LINE_BREAK_CLASS_NL, /* 7 */ KBTS_LINE_BREAK_CLASS_SP, /* 8 */ KBTS_LINE_BREAK_CLASS_ZW, /* 9 */ KBTS_LINE_BREAK_CLASS_WJ, /* 10 */ KBTS_LINE_BREAK_CLASS_GLnea, /* 11 */ KBTS_LINE_BREAK_CLASS_GLea, /* 12 */ KBTS_LINE_BREAK_CLASS_CLnea, /* 13 */ KBTS_LINE_BREAK_CLASS_CLea, /* 14 */ KBTS_LINE_BREAK_CLASS_CPnea, /* 15 */ KBTS_LINE_BREAK_CLASS_CPea, /* 16 */ KBTS_LINE_BREAK_CLASS_EXnea, /* 17 */ KBTS_LINE_BREAK_CLASS_EXea, /* 18 */ KBTS_LINE_BREAK_CLASS_SY, /* 19 */ KBTS_LINE_BREAK_CLASS_BAnea, /* 20 */ KBTS_LINE_BREAK_CLASS_BAea, /* 21 */ KBTS_LINE_BREAK_CLASS_OPnea, /* 22 */ KBTS_LINE_BREAK_CLASS_OPea, /* 23 */ KBTS_LINE_BREAK_CLASS_QU, /* 24 */ KBTS_LINE_BREAK_CLASS_QUPi, /* 25 */ KBTS_LINE_BREAK_CLASS_QUPf, /* 26 */ KBTS_LINE_BREAK_CLASS_IS, /* 27 */ KBTS_LINE_BREAK_CLASS_NSnea, /* 28 */ KBTS_LINE_BREAK_CLASS_NSea, /* 29 */ KBTS_LINE_BREAK_CLASS_B2, /* 30 */ KBTS_LINE_BREAK_CLASS_CB, /* 31 */ KBTS_LINE_BREAK_CLASS_HY, /* 32 */ KBTS_LINE_BREAK_CLASS_HYPHEN, /* 33 */ KBTS_LINE_BREAK_CLASS_INnea, /* 34 */ KBTS_LINE_BREAK_CLASS_INea, /* 35 */ KBTS_LINE_BREAK_CLASS_BB, /* 36 */ KBTS_LINE_BREAK_CLASS_HL, /* 37 */ KBTS_LINE_BREAK_CLASS_ALnea, /* 38 */ KBTS_LINE_BREAK_CLASS_ALea, /* 39 */ KBTS_LINE_BREAK_CLASS_NU, /* 40 */ KBTS_LINE_BREAK_CLASS_PRnea, /* 41 */ KBTS_LINE_BREAK_CLASS_PRea, /* 42 */ KBTS_LINE_BREAK_CLASS_IDnea, /* 43 */ KBTS_LINE_BREAK_CLASS_IDea, /* 44 */ KBTS_LINE_BREAK_CLASS_IDpe, /* 45 */ KBTS_LINE_BREAK_CLASS_EBnea, /* 46 */ KBTS_LINE_BREAK_CLASS_EBea, /* 47 */ KBTS_LINE_BREAK_CLASS_EM, /* 48 */ KBTS_LINE_BREAK_CLASS_POnea, /* 49 */ KBTS_LINE_BREAK_CLASS_POea, /* 50 */ KBTS_LINE_BREAK_CLASS_JL, /* 51 */ KBTS_LINE_BREAK_CLASS_JV, /* 52 */ KBTS_LINE_BREAK_CLASS_JT, /* 53 */ KBTS_LINE_BREAK_CLASS_H2, /* 54 */ KBTS_LINE_BREAK_CLASS_H3, /* 55 */ KBTS_LINE_BREAK_CLASS_AP, /* 56 */ KBTS_LINE_BREAK_CLASS_AK, /* 57 */ KBTS_LINE_BREAK_CLASS_DOTTED_CIRCLE, /* 58 */ KBTS_LINE_BREAK_CLASS_AS, /* 59 */ KBTS_LINE_BREAK_CLASS_VF, /* 60 */ KBTS_LINE_BREAK_CLASS_VI, /* 61 */ KBTS_LINE_BREAK_CLASS_RI, /* 62 */ KBTS_LINE_BREAK_CLASS_COUNT, /* 63 */ KBTS_LINE_BREAK_CLASS_CM, /* 64 */ KBTS_LINE_BREAK_CLASS_ZWJ, // CJ resolves to either NS or ID depending on the (Japanese) line break style. // NS is strict line breaking, used for long lines. // ID is normal line breaking, used for normal body text. /* 65 */ KBTS_LINE_BREAK_CLASS_CJ, /* 66 */ KBTS_LINE_BREAK_CLASS_SOT, /* 67 */ KBTS_LINE_BREAK_CLASS_EOT, }; // @Cleanup: Merge EX and FO. typedef kbts_u8 kbts_word_break_class; enum kbts_word_break_class_enum { KBTS_WORD_BREAK_CLASS_Onep, KBTS_WORD_BREAK_CLASS_Oep, KBTS_WORD_BREAK_CLASS_CR, KBTS_WORD_BREAK_CLASS_LF, KBTS_WORD_BREAK_CLASS_NL, KBTS_WORD_BREAK_CLASS_EX, KBTS_WORD_BREAK_CLASS_ZWJ, KBTS_WORD_BREAK_CLASS_RI, KBTS_WORD_BREAK_CLASS_FO, KBTS_WORD_BREAK_CLASS_KA, KBTS_WORD_BREAK_CLASS_HL, KBTS_WORD_BREAK_CLASS_ALnep, KBTS_WORD_BREAK_CLASS_ALep, KBTS_WORD_BREAK_CLASS_SQ, KBTS_WORD_BREAK_CLASS_DQ, KBTS_WORD_BREAK_CLASS_MNL, KBTS_WORD_BREAK_CLASS_ML, KBTS_WORD_BREAK_CLASS_MN, KBTS_WORD_BREAK_CLASS_NM, KBTS_WORD_BREAK_CLASS_ENL, KBTS_WORD_BREAK_CLASS_WSS, KBTS_WORD_BREAK_CLASS_SOT, }; // Unicode defines scripts and languages. // A language belongs to a single script, and a script belongs to a single writing system. // On top of these, OpenType defines shapers, which are basically just designations for // specific code paths that are taken depending on which script is being shapen. // // Some scripts, like Latin and Cyrillic, need relatively few operations, while complex // scripts like Arabic and Indic scripts have specific processing steps that need to happen // in order to obtain a correct result. // // These sequences of operations are _not_ described in the font file itself. The shaping // code needs to know which script it is shaping, and implement all of those passes itself. // That is why you, as a user, have to care about this. // // When creating shape_config, you can either pass in a known script, or you can specify // SCRIPT_DONT_KNOW and let the library figure it out. // While SCRIPT_DONT_KNOW may look appealing, it is worth noting that we can only infer // the _script_, and not the language, of the text you pass in. // This means that you might miss out on language-specific features when you use it. typedef kbts_u32 kbts_shaper; enum kbts_shaper_enum { KBTS_SHAPER_DEFAULT, KBTS_SHAPER_ARABIC, KBTS_SHAPER_HANGUL, KBTS_SHAPER_HEBREW, KBTS_SHAPER_INDIC, KBTS_SHAPER_KHMER, KBTS_SHAPER_MYANMAR, KBTS_SHAPER_TIBETAN, KBTS_SHAPER_USE, KBTS_SHAPER_COUNT, }; #define KBTS_MAXIMUM_RECOMPOSITION_PARENTS 19 #define KBTS_MAXIMUM_CODEPOINT_SCRIPTS 23 typedef kbts_u32 kbts_script_tag; enum kbts_script_tag_enum { KBTS_SCRIPT_TAG_DONT_KNOW = KBTS_FOURCC(' ', ' ', ' ', ' '), KBTS_SCRIPT_TAG_ADLAM = KBTS_FOURCC('a', 'd', 'l', 'm'), KBTS_SCRIPT_TAG_AHOM = KBTS_FOURCC('a', 'h', 'o', 'm'), KBTS_SCRIPT_TAG_ANATOLIAN_HIEROGLYPHS = KBTS_FOURCC('h', 'l', 'u', 'w'), KBTS_SCRIPT_TAG_ARABIC = KBTS_FOURCC('a', 'r', 'a', 'b'), KBTS_SCRIPT_TAG_ARMENIAN = KBTS_FOURCC('a', 'r', 'm', 'n'), KBTS_SCRIPT_TAG_AVESTAN = KBTS_FOURCC('a', 'v', 's', 't'), KBTS_SCRIPT_TAG_BALINESE = KBTS_FOURCC('b', 'a', 'l', 'i'), KBTS_SCRIPT_TAG_BAMUM = KBTS_FOURCC('b', 'a', 'm', 'u'), KBTS_SCRIPT_TAG_BASSA_VAH = KBTS_FOURCC('b', 'a', 's', 's'), KBTS_SCRIPT_TAG_BATAK = KBTS_FOURCC('b', 'a', 't', 'k'), KBTS_SCRIPT_TAG_BENGALI = KBTS_FOURCC('b', 'n', 'g', '2'), KBTS_SCRIPT_TAG_BHAIKSUKI = KBTS_FOURCC('b', 'h', 'k', 's'), KBTS_SCRIPT_TAG_BOPOMOFO = KBTS_FOURCC('b', 'o', 'p', 'o'), KBTS_SCRIPT_TAG_BRAHMI = KBTS_FOURCC('b', 'r', 'a', 'h'), KBTS_SCRIPT_TAG_BUGINESE = KBTS_FOURCC('b', 'u', 'g', 'i'), KBTS_SCRIPT_TAG_BUHID = KBTS_FOURCC('b', 'u', 'h', 'd'), KBTS_SCRIPT_TAG_CANADIAN_SYLLABICS = KBTS_FOURCC('c', 'a', 'n', 's'), KBTS_SCRIPT_TAG_CARIAN = KBTS_FOURCC('c', 'a', 'r', 'i'), KBTS_SCRIPT_TAG_CAUCASIAN_ALBANIAN = KBTS_FOURCC('a', 'g', 'h', 'b'), KBTS_SCRIPT_TAG_CHAKMA = KBTS_FOURCC('c', 'a', 'k', 'm'), KBTS_SCRIPT_TAG_CHAM = KBTS_FOURCC('c', 'h', 'a', 'm'), KBTS_SCRIPT_TAG_CHEROKEE = KBTS_FOURCC('c', 'h', 'e', 'r'), KBTS_SCRIPT_TAG_CHORASMIAN = KBTS_FOURCC('c', 'h', 'r', 's'), KBTS_SCRIPT_TAG_CJK_IDEOGRAPHIC = KBTS_FOURCC('h', 'a', 'n', 'i'), KBTS_SCRIPT_TAG_COPTIC = KBTS_FOURCC('c', 'o', 'p', 't'), KBTS_SCRIPT_TAG_CYPRIOT_SYLLABARY = KBTS_FOURCC('c', 'p', 'r', 't'), KBTS_SCRIPT_TAG_CYPRO_MINOAN = KBTS_FOURCC('c', 'p', 'm', 'n'), KBTS_SCRIPT_TAG_CYRILLIC = KBTS_FOURCC('c', 'y', 'r', 'l'), KBTS_SCRIPT_TAG_DEFAULT = KBTS_FOURCC('D', 'F', 'L', 'T'), KBTS_SCRIPT_TAG_DEFAULT2 = KBTS_FOURCC('D', 'F', 'L', 'T'), KBTS_SCRIPT_TAG_DESERET = KBTS_FOURCC('d', 's', 'r', 't'), KBTS_SCRIPT_TAG_DEVANAGARI = KBTS_FOURCC('d', 'e', 'v', '2'), KBTS_SCRIPT_TAG_DIVES_AKURU = KBTS_FOURCC('d', 'i', 'a', 'k'), KBTS_SCRIPT_TAG_DOGRA = KBTS_FOURCC('d', 'o', 'g', 'r'), KBTS_SCRIPT_TAG_DUPLOYAN = KBTS_FOURCC('d', 'u', 'p', 'l'), KBTS_SCRIPT_TAG_EGYPTIAN_HIEROGLYPHS = KBTS_FOURCC('e', 'g', 'y', 'p'), KBTS_SCRIPT_TAG_ELBASAN = KBTS_FOURCC('e', 'l', 'b', 'a'), KBTS_SCRIPT_TAG_ELYMAIC = KBTS_FOURCC('e', 'l', 'y', 'm'), KBTS_SCRIPT_TAG_ETHIOPIC = KBTS_FOURCC('e', 't', 'h', 'i'), KBTS_SCRIPT_TAG_GARAY = KBTS_FOURCC('g', 'a', 'r', 'a'), KBTS_SCRIPT_TAG_GEORGIAN = KBTS_FOURCC('g', 'e', 'o', 'r'), KBTS_SCRIPT_TAG_GLAGOLITIC = KBTS_FOURCC('g', 'l', 'a', 'g'), KBTS_SCRIPT_TAG_GOTHIC = KBTS_FOURCC('g', 'o', 't', 'h'), KBTS_SCRIPT_TAG_GRANTHA = KBTS_FOURCC('g', 'r', 'a', 'n'), KBTS_SCRIPT_TAG_GREEK = KBTS_FOURCC('g', 'r', 'e', 'k'), KBTS_SCRIPT_TAG_GUJARATI = KBTS_FOURCC('g', 'j', 'r', '2'), KBTS_SCRIPT_TAG_GUNJALA_GONDI = KBTS_FOURCC('g', 'o', 'n', 'g'), KBTS_SCRIPT_TAG_GURMUKHI = KBTS_FOURCC('g', 'u', 'r', '2'), KBTS_SCRIPT_TAG_GURUNG_KHEMA = KBTS_FOURCC('g', 'u', 'k', 'h'), KBTS_SCRIPT_TAG_HANGUL = KBTS_FOURCC('h', 'a', 'n', 'g'), KBTS_SCRIPT_TAG_HANIFI_ROHINGYA = KBTS_FOURCC('r', 'o', 'h', 'g'), KBTS_SCRIPT_TAG_HANUNOO = KBTS_FOURCC('h', 'a', 'n', 'o'), KBTS_SCRIPT_TAG_HATRAN = KBTS_FOURCC('h', 'a', 't', 'r'), KBTS_SCRIPT_TAG_HEBREW = KBTS_FOURCC('h', 'e', 'b', 'r'), KBTS_SCRIPT_TAG_HIRAGANA = KBTS_FOURCC('k', 'a', 'n', 'a'), KBTS_SCRIPT_TAG_IMPERIAL_ARAMAIC = KBTS_FOURCC('a', 'r', 'm', 'i'), KBTS_SCRIPT_TAG_INSCRIPTIONAL_PAHLAVI = KBTS_FOURCC('p', 'h', 'l', 'i'), KBTS_SCRIPT_TAG_INSCRIPTIONAL_PARTHIAN = KBTS_FOURCC('p', 'r', 't', 'i'), KBTS_SCRIPT_TAG_JAVANESE = KBTS_FOURCC('j', 'a', 'v', 'a'), KBTS_SCRIPT_TAG_KAITHI = KBTS_FOURCC('k', 't', 'h', 'i'), KBTS_SCRIPT_TAG_KANNADA = KBTS_FOURCC('k', 'n', 'd', '2'), KBTS_SCRIPT_TAG_KATAKANA = KBTS_FOURCC('k', 'a', 'n', 'a'), KBTS_SCRIPT_TAG_KAWI = KBTS_FOURCC('k', 'a', 'w', 'i'), KBTS_SCRIPT_TAG_KAYAH_LI = KBTS_FOURCC('k', 'a', 'l', 'i'), KBTS_SCRIPT_TAG_KHAROSHTHI = KBTS_FOURCC('k', 'h', 'a', 'r'), KBTS_SCRIPT_TAG_KHITAN_SMALL_SCRIPT = KBTS_FOURCC('k', 'i', 't', 's'), KBTS_SCRIPT_TAG_KHMER = KBTS_FOURCC('k', 'h', 'm', 'r'), KBTS_SCRIPT_TAG_KHOJKI = KBTS_FOURCC('k', 'h', 'o', 'j'), KBTS_SCRIPT_TAG_KHUDAWADI = KBTS_FOURCC('s', 'i', 'n', 'd'), KBTS_SCRIPT_TAG_KIRAT_RAI = KBTS_FOURCC('k', 'r', 'a', 'i'), KBTS_SCRIPT_TAG_LAO = KBTS_FOURCC('l', 'a', 'o', ' '), KBTS_SCRIPT_TAG_LATIN = KBTS_FOURCC('l', 'a', 't', 'n'), KBTS_SCRIPT_TAG_LEPCHA = KBTS_FOURCC('l', 'e', 'p', 'c'), KBTS_SCRIPT_TAG_LIMBU = KBTS_FOURCC('l', 'i', 'm', 'b'), KBTS_SCRIPT_TAG_LINEAR_A = KBTS_FOURCC('l', 'i', 'n', 'a'), KBTS_SCRIPT_TAG_LINEAR_B = KBTS_FOURCC('l', 'i', 'n', 'b'), KBTS_SCRIPT_TAG_LISU = KBTS_FOURCC('l', 'i', 's', 'u'), KBTS_SCRIPT_TAG_LYCIAN = KBTS_FOURCC('l', 'y', 'c', 'i'), KBTS_SCRIPT_TAG_LYDIAN = KBTS_FOURCC('l', 'y', 'd', 'i'), KBTS_SCRIPT_TAG_MAHAJANI = KBTS_FOURCC('m', 'a', 'h', 'j'), KBTS_SCRIPT_TAG_MAKASAR = KBTS_FOURCC('m', 'a', 'k', 'a'), KBTS_SCRIPT_TAG_MALAYALAM = KBTS_FOURCC('m', 'l', 'm', '2'), KBTS_SCRIPT_TAG_MANDAIC = KBTS_FOURCC('m', 'a', 'n', 'd'), KBTS_SCRIPT_TAG_MANICHAEAN = KBTS_FOURCC('m', 'a', 'n', 'i'), KBTS_SCRIPT_TAG_MARCHEN = KBTS_FOURCC('m', 'a', 'r', 'c'), KBTS_SCRIPT_TAG_MASARAM_GONDI = KBTS_FOURCC('g', 'o', 'n', 'm'), KBTS_SCRIPT_TAG_MEDEFAIDRIN = KBTS_FOURCC('m', 'e', 'd', 'f'), KBTS_SCRIPT_TAG_MEETEI_MAYEK = KBTS_FOURCC('m', 't', 'e', 'i'), KBTS_SCRIPT_TAG_MENDE_KIKAKUI = KBTS_FOURCC('m', 'e', 'n', 'd'), KBTS_SCRIPT_TAG_MEROITIC_CURSIVE = KBTS_FOURCC('m', 'e', 'r', 'c'), KBTS_SCRIPT_TAG_MEROITIC_HIEROGLYPHS = KBTS_FOURCC('m', 'e', 'r', 'o'), KBTS_SCRIPT_TAG_MIAO = KBTS_FOURCC('p', 'l', 'r', 'd'), KBTS_SCRIPT_TAG_MODI = KBTS_FOURCC('m', 'o', 'd', 'i'), KBTS_SCRIPT_TAG_MONGOLIAN = KBTS_FOURCC('m', 'o', 'n', 'g'), KBTS_SCRIPT_TAG_MRO = KBTS_FOURCC('m', 'r', 'o', 'o'), KBTS_SCRIPT_TAG_MULTANI = KBTS_FOURCC('m', 'u', 'l', 't'), KBTS_SCRIPT_TAG_MYANMAR = KBTS_FOURCC('m', 'y', 'm', '2'), KBTS_SCRIPT_TAG_NABATAEAN = KBTS_FOURCC('n', 'b', 'a', 't'), KBTS_SCRIPT_TAG_NAG_MUNDARI = KBTS_FOURCC('n', 'a', 'g', 'm'), KBTS_SCRIPT_TAG_NANDINAGARI = KBTS_FOURCC('n', 'a', 'n', 'd'), KBTS_SCRIPT_TAG_NEWA = KBTS_FOURCC('n', 'e', 'w', 'a'), KBTS_SCRIPT_TAG_NEW_TAI_LUE = KBTS_FOURCC('t', 'a', 'l', 'u'), KBTS_SCRIPT_TAG_NKO = KBTS_FOURCC('n', 'k', 'o', ' '), KBTS_SCRIPT_TAG_NUSHU = KBTS_FOURCC('n', 's', 'h', 'u'), KBTS_SCRIPT_TAG_NYIAKENG_PUACHUE_HMONG = KBTS_FOURCC('h', 'm', 'n', 'p'), KBTS_SCRIPT_TAG_OGHAM = KBTS_FOURCC('o', 'g', 'a', 'm'), KBTS_SCRIPT_TAG_OL_CHIKI = KBTS_FOURCC('o', 'l', 'c', 'k'), KBTS_SCRIPT_TAG_OL_ONAL = KBTS_FOURCC('o', 'n', 'a', 'o'), KBTS_SCRIPT_TAG_OLD_ITALIC = KBTS_FOURCC('i', 't', 'a', 'l'), KBTS_SCRIPT_TAG_OLD_HUNGARIAN = KBTS_FOURCC('h', 'u', 'n', 'g'), KBTS_SCRIPT_TAG_OLD_NORTH_ARABIAN = KBTS_FOURCC('n', 'a', 'r', 'b'), KBTS_SCRIPT_TAG_OLD_PERMIC = KBTS_FOURCC('p', 'e', 'r', 'm'), KBTS_SCRIPT_TAG_OLD_PERSIAN_CUNEIFORM = KBTS_FOURCC('x', 'p', 'e', 'o'), KBTS_SCRIPT_TAG_OLD_SOGDIAN = KBTS_FOURCC('s', 'o', 'g', 'o'), KBTS_SCRIPT_TAG_OLD_SOUTH_ARABIAN = KBTS_FOURCC('s', 'a', 'r', 'b'), KBTS_SCRIPT_TAG_OLD_TURKIC = KBTS_FOURCC('o', 'r', 'k', 'h'), KBTS_SCRIPT_TAG_OLD_UYGHUR = KBTS_FOURCC('o', 'u', 'g', 'r'), KBTS_SCRIPT_TAG_ODIA = KBTS_FOURCC('o', 'r', 'y', '2'), KBTS_SCRIPT_TAG_OSAGE = KBTS_FOURCC('o', 's', 'g', 'e'), KBTS_SCRIPT_TAG_OSMANYA = KBTS_FOURCC('o', 's', 'm', 'a'), KBTS_SCRIPT_TAG_PAHAWH_HMONG = KBTS_FOURCC('h', 'm', 'n', 'g'), KBTS_SCRIPT_TAG_PALMYRENE = KBTS_FOURCC('p', 'a', 'l', 'm'), KBTS_SCRIPT_TAG_PAU_CIN_HAU = KBTS_FOURCC('p', 'a', 'u', 'c'), KBTS_SCRIPT_TAG_PHAGS_PA = KBTS_FOURCC('p', 'h', 'a', 'g'), KBTS_SCRIPT_TAG_PHOENICIAN = KBTS_FOURCC('p', 'h', 'n', 'x'), KBTS_SCRIPT_TAG_PSALTER_PAHLAVI = KBTS_FOURCC('p', 'h', 'l', 'p'), KBTS_SCRIPT_TAG_REJANG = KBTS_FOURCC('r', 'j', 'n', 'g'), KBTS_SCRIPT_TAG_RUNIC = KBTS_FOURCC('r', 'u', 'n', 'r'), KBTS_SCRIPT_TAG_SAMARITAN = KBTS_FOURCC('s', 'a', 'm', 'r'), KBTS_SCRIPT_TAG_SAURASHTRA = KBTS_FOURCC('s', 'a', 'u', 'r'), KBTS_SCRIPT_TAG_SHARADA = KBTS_FOURCC('s', 'h', 'r', 'd'), KBTS_SCRIPT_TAG_SHAVIAN = KBTS_FOURCC('s', 'h', 'a', 'w'), KBTS_SCRIPT_TAG_SIDDHAM = KBTS_FOURCC('s', 'i', 'd', 'd'), KBTS_SCRIPT_TAG_SIGN_WRITING = KBTS_FOURCC('s', 'g', 'n', 'w'), KBTS_SCRIPT_TAG_SOGDIAN = KBTS_FOURCC('s', 'o', 'g', 'd'), KBTS_SCRIPT_TAG_SINHALA = KBTS_FOURCC('s', 'i', 'n', 'h'), KBTS_SCRIPT_TAG_SORA_SOMPENG = KBTS_FOURCC('s', 'o', 'r', 'a'), KBTS_SCRIPT_TAG_SOYOMBO = KBTS_FOURCC('s', 'o', 'y', 'o'), KBTS_SCRIPT_TAG_SUMERO_AKKADIAN_CUNEIFORM = KBTS_FOURCC('x', 's', 'u', 'x'), KBTS_SCRIPT_TAG_SUNDANESE = KBTS_FOURCC('s', 'u', 'n', 'd'), KBTS_SCRIPT_TAG_SUNUWAR = KBTS_FOURCC('s', 'u', 'n', 'u'), KBTS_SCRIPT_TAG_SYLOTI_NAGRI = KBTS_FOURCC('s', 'y', 'l', 'o'), KBTS_SCRIPT_TAG_SYRIAC = KBTS_FOURCC('s', 'y', 'r', 'c'), KBTS_SCRIPT_TAG_TAGALOG = KBTS_FOURCC('t', 'g', 'l', 'g'), KBTS_SCRIPT_TAG_TAGBANWA = KBTS_FOURCC('t', 'a', 'g', 'b'), KBTS_SCRIPT_TAG_TAI_LE = KBTS_FOURCC('t', 'a', 'l', 'e'), KBTS_SCRIPT_TAG_TAI_THAM = KBTS_FOURCC('l', 'a', 'n', 'a'), KBTS_SCRIPT_TAG_TAI_VIET = KBTS_FOURCC('t', 'a', 'v', 't'), KBTS_SCRIPT_TAG_TAKRI = KBTS_FOURCC('t', 'a', 'k', 'r'), KBTS_SCRIPT_TAG_TAMIL = KBTS_FOURCC('t', 'm', 'l', '2'), KBTS_SCRIPT_TAG_TANGSA = KBTS_FOURCC('t', 'n', 's', 'a'), KBTS_SCRIPT_TAG_TANGUT = KBTS_FOURCC('t', 'a', 'n', 'g'), KBTS_SCRIPT_TAG_TELUGU = KBTS_FOURCC('t', 'e', 'l', '2'), KBTS_SCRIPT_TAG_THAANA = KBTS_FOURCC('t', 'h', 'a', 'a'), KBTS_SCRIPT_TAG_THAI = KBTS_FOURCC('t', 'h', 'a', 'i'), KBTS_SCRIPT_TAG_TIBETAN = KBTS_FOURCC('t', 'i', 'b', 't'), KBTS_SCRIPT_TAG_TIFINAGH = KBTS_FOURCC('t', 'f', 'n', 'g'), KBTS_SCRIPT_TAG_TIRHUTA = KBTS_FOURCC('t', 'i', 'r', 'h'), KBTS_SCRIPT_TAG_TODHRI = KBTS_FOURCC('t', 'o', 'd', 'r'), KBTS_SCRIPT_TAG_TOTO = KBTS_FOURCC('t', 'o', 't', 'o'), KBTS_SCRIPT_TAG_TULU_TIGALARI = KBTS_FOURCC('t', 'u', 't', 'g'), KBTS_SCRIPT_TAG_UGARITIC_CUNEIFORM = KBTS_FOURCC('u', 'g', 'a', 'r'), KBTS_SCRIPT_TAG_VAI = KBTS_FOURCC('v', 'a', 'i', ' '), KBTS_SCRIPT_TAG_VITHKUQI = KBTS_FOURCC('v', 'i', 't', 'h'), KBTS_SCRIPT_TAG_WANCHO = KBTS_FOURCC('w', 'c', 'h', 'o'), KBTS_SCRIPT_TAG_WARANG_CITI = KBTS_FOURCC('w', 'a', 'r', 'a'), KBTS_SCRIPT_TAG_YEZIDI = KBTS_FOURCC('y', 'e', 'z', 'i'), KBTS_SCRIPT_TAG_YI = KBTS_FOURCC('y', 'i', ' ', ' '), KBTS_SCRIPT_TAG_ZANABAZAR_SQUARE = KBTS_FOURCC('z', 'a', 'n', 'b'), }; typedef kbts_u32 kbts_script; enum kbts_script_enum { KBTS_SCRIPT_DONT_KNOW, KBTS_SCRIPT_ADLAM, KBTS_SCRIPT_AHOM, KBTS_SCRIPT_ANATOLIAN_HIEROGLYPHS, KBTS_SCRIPT_ARABIC, KBTS_SCRIPT_ARMENIAN, KBTS_SCRIPT_AVESTAN, KBTS_SCRIPT_BALINESE, KBTS_SCRIPT_BAMUM, KBTS_SCRIPT_BASSA_VAH, KBTS_SCRIPT_BATAK, KBTS_SCRIPT_BENGALI, KBTS_SCRIPT_BHAIKSUKI, KBTS_SCRIPT_BOPOMOFO, KBTS_SCRIPT_BRAHMI, KBTS_SCRIPT_BUGINESE, KBTS_SCRIPT_BUHID, KBTS_SCRIPT_CANADIAN_SYLLABICS, KBTS_SCRIPT_CARIAN, KBTS_SCRIPT_CAUCASIAN_ALBANIAN, KBTS_SCRIPT_CHAKMA, KBTS_SCRIPT_CHAM, KBTS_SCRIPT_CHEROKEE, KBTS_SCRIPT_CHORASMIAN, KBTS_SCRIPT_CJK_IDEOGRAPHIC, KBTS_SCRIPT_COPTIC, KBTS_SCRIPT_CYPRIOT_SYLLABARY, KBTS_SCRIPT_CYPRO_MINOAN, KBTS_SCRIPT_CYRILLIC, KBTS_SCRIPT_DEFAULT, KBTS_SCRIPT_DEFAULT2, KBTS_SCRIPT_DESERET, KBTS_SCRIPT_DEVANAGARI, KBTS_SCRIPT_DIVES_AKURU, KBTS_SCRIPT_DOGRA, KBTS_SCRIPT_DUPLOYAN, KBTS_SCRIPT_EGYPTIAN_HIEROGLYPHS, KBTS_SCRIPT_ELBASAN, KBTS_SCRIPT_ELYMAIC, KBTS_SCRIPT_ETHIOPIC, KBTS_SCRIPT_GARAY, KBTS_SCRIPT_GEORGIAN, KBTS_SCRIPT_GLAGOLITIC, KBTS_SCRIPT_GOTHIC, KBTS_SCRIPT_GRANTHA, KBTS_SCRIPT_GREEK, KBTS_SCRIPT_GUJARATI, KBTS_SCRIPT_GUNJALA_GONDI, KBTS_SCRIPT_GURMUKHI, KBTS_SCRIPT_GURUNG_KHEMA, KBTS_SCRIPT_HANGUL, KBTS_SCRIPT_HANIFI_ROHINGYA, KBTS_SCRIPT_HANUNOO, KBTS_SCRIPT_HATRAN, KBTS_SCRIPT_HEBREW, KBTS_SCRIPT_HIRAGANA, KBTS_SCRIPT_IMPERIAL_ARAMAIC, KBTS_SCRIPT_INSCRIPTIONAL_PAHLAVI, KBTS_SCRIPT_INSCRIPTIONAL_PARTHIAN, KBTS_SCRIPT_JAVANESE, KBTS_SCRIPT_KAITHI, KBTS_SCRIPT_KANNADA, KBTS_SCRIPT_KATAKANA, KBTS_SCRIPT_KAWI, KBTS_SCRIPT_KAYAH_LI, KBTS_SCRIPT_KHAROSHTHI, KBTS_SCRIPT_KHITAN_SMALL_SCRIPT, KBTS_SCRIPT_KHMER, KBTS_SCRIPT_KHOJKI, KBTS_SCRIPT_KHUDAWADI, KBTS_SCRIPT_KIRAT_RAI, KBTS_SCRIPT_LAO, KBTS_SCRIPT_LATIN, KBTS_SCRIPT_LEPCHA, KBTS_SCRIPT_LIMBU, KBTS_SCRIPT_LINEAR_A, KBTS_SCRIPT_LINEAR_B, KBTS_SCRIPT_LISU, KBTS_SCRIPT_LYCIAN, KBTS_SCRIPT_LYDIAN, KBTS_SCRIPT_MAHAJANI, KBTS_SCRIPT_MAKASAR, KBTS_SCRIPT_MALAYALAM, KBTS_SCRIPT_MANDAIC, KBTS_SCRIPT_MANICHAEAN, KBTS_SCRIPT_MARCHEN, KBTS_SCRIPT_MASARAM_GONDI, KBTS_SCRIPT_MEDEFAIDRIN, KBTS_SCRIPT_MEETEI_MAYEK, KBTS_SCRIPT_MENDE_KIKAKUI, KBTS_SCRIPT_MEROITIC_CURSIVE, KBTS_SCRIPT_MEROITIC_HIEROGLYPHS, KBTS_SCRIPT_MIAO, KBTS_SCRIPT_MODI, KBTS_SCRIPT_MONGOLIAN, KBTS_SCRIPT_MRO, KBTS_SCRIPT_MULTANI, KBTS_SCRIPT_MYANMAR, KBTS_SCRIPT_NABATAEAN, KBTS_SCRIPT_NAG_MUNDARI, KBTS_SCRIPT_NANDINAGARI, KBTS_SCRIPT_NEWA, KBTS_SCRIPT_NEW_TAI_LUE, KBTS_SCRIPT_NKO, KBTS_SCRIPT_NUSHU, KBTS_SCRIPT_NYIAKENG_PUACHUE_HMONG, KBTS_SCRIPT_OGHAM, KBTS_SCRIPT_OL_CHIKI, KBTS_SCRIPT_OL_ONAL, KBTS_SCRIPT_OLD_ITALIC, KBTS_SCRIPT_OLD_HUNGARIAN, KBTS_SCRIPT_OLD_NORTH_ARABIAN, KBTS_SCRIPT_OLD_PERMIC, KBTS_SCRIPT_OLD_PERSIAN_CUNEIFORM, KBTS_SCRIPT_OLD_SOGDIAN, KBTS_SCRIPT_OLD_SOUTH_ARABIAN, KBTS_SCRIPT_OLD_TURKIC, KBTS_SCRIPT_OLD_UYGHUR, KBTS_SCRIPT_ODIA, KBTS_SCRIPT_OSAGE, KBTS_SCRIPT_OSMANYA, KBTS_SCRIPT_PAHAWH_HMONG, KBTS_SCRIPT_PALMYRENE, KBTS_SCRIPT_PAU_CIN_HAU, KBTS_SCRIPT_PHAGS_PA, KBTS_SCRIPT_PHOENICIAN, KBTS_SCRIPT_PSALTER_PAHLAVI, KBTS_SCRIPT_REJANG, KBTS_SCRIPT_RUNIC, KBTS_SCRIPT_SAMARITAN, KBTS_SCRIPT_SAURASHTRA, KBTS_SCRIPT_SHARADA, KBTS_SCRIPT_SHAVIAN, KBTS_SCRIPT_SIDDHAM, KBTS_SCRIPT_SIGN_WRITING, KBTS_SCRIPT_SOGDIAN, KBTS_SCRIPT_SINHALA, KBTS_SCRIPT_SORA_SOMPENG, KBTS_SCRIPT_SOYOMBO, KBTS_SCRIPT_SUMERO_AKKADIAN_CUNEIFORM, KBTS_SCRIPT_SUNDANESE, KBTS_SCRIPT_SUNUWAR, KBTS_SCRIPT_SYLOTI_NAGRI, KBTS_SCRIPT_SYRIAC, KBTS_SCRIPT_TAGALOG, KBTS_SCRIPT_TAGBANWA, KBTS_SCRIPT_TAI_LE, KBTS_SCRIPT_TAI_THAM, KBTS_SCRIPT_TAI_VIET, KBTS_SCRIPT_TAKRI, KBTS_SCRIPT_TAMIL, KBTS_SCRIPT_TANGSA, KBTS_SCRIPT_TANGUT, KBTS_SCRIPT_TELUGU, KBTS_SCRIPT_THAANA, KBTS_SCRIPT_THAI, KBTS_SCRIPT_TIBETAN, KBTS_SCRIPT_TIFINAGH, KBTS_SCRIPT_TIRHUTA, KBTS_SCRIPT_TODHRI, KBTS_SCRIPT_TOTO, KBTS_SCRIPT_TULU_TIGALARI, KBTS_SCRIPT_UGARITIC_CUNEIFORM, KBTS_SCRIPT_VAI, KBTS_SCRIPT_VITHKUQI, KBTS_SCRIPT_WANCHO, KBTS_SCRIPT_WARANG_CITI, KBTS_SCRIPT_YEZIDI, KBTS_SCRIPT_YI, KBTS_SCRIPT_ZANABAZAR_SQUARE, KBTS_SCRIPT_COUNT, }; typedef kbts_u32 kbts_feature_tag; enum kbts_feature_tag_enum { KBTS_FEATURE_TAG_UNREGISTERED = KBTS_FOURCC(0, 0, 0, 0), // Features that aren't pre-defined in the OpenType spec KBTS_FEATURE_TAG_isol = KBTS_FOURCC('i', 's', 'o', 'l'), // Isolated Forms KBTS_FEATURE_TAG_fina = KBTS_FOURCC('f', 'i', 'n', 'a'), // Terminal Forms KBTS_FEATURE_TAG_fin2 = KBTS_FOURCC('f', 'i', 'n', '2'), // Terminal Forms #2 KBTS_FEATURE_TAG_fin3 = KBTS_FOURCC('f', 'i', 'n', '3'), // Terminal Forms #3 KBTS_FEATURE_TAG_medi = KBTS_FOURCC('m', 'e', 'd', 'i'), // Medial Forms KBTS_FEATURE_TAG_med2 = KBTS_FOURCC('m', 'e', 'd', '2'), // Medial Forms #2 KBTS_FEATURE_TAG_init = KBTS_FOURCC('i', 'n', 'i', 't'), // Initial Forms KBTS_FEATURE_TAG_ljmo = KBTS_FOURCC('l', 'j', 'm', 'o'), // Leading Jamo Forms KBTS_FEATURE_TAG_vjmo = KBTS_FOURCC('v', 'j', 'm', 'o'), // Vowel Jamo Forms KBTS_FEATURE_TAG_tjmo = KBTS_FOURCC('t', 'j', 'm', 'o'), // Trailing Jamo Forms KBTS_FEATURE_TAG_rphf = KBTS_FOURCC('r', 'p', 'h', 'f'), // Reph Form KBTS_FEATURE_TAG_blwf = KBTS_FOURCC('b', 'l', 'w', 'f'), // Below-base Forms KBTS_FEATURE_TAG_half = KBTS_FOURCC('h', 'a', 'l', 'f'), // Half Forms KBTS_FEATURE_TAG_pstf = KBTS_FOURCC('p', 's', 't', 'f'), // Post-base Forms KBTS_FEATURE_TAG_abvf = KBTS_FOURCC('a', 'b', 'v', 'f'), // Above-base Forms KBTS_FEATURE_TAG_pref = KBTS_FOURCC('p', 'r', 'e', 'f'), // Pre-base Forms KBTS_FEATURE_TAG_numr = KBTS_FOURCC('n', 'u', 'm', 'r'), // Numerators KBTS_FEATURE_TAG_frac = KBTS_FOURCC('f', 'r', 'a', 'c'), // Fractions KBTS_FEATURE_TAG_dnom = KBTS_FOURCC('d', 'n', 'o', 'm'), // Denominators KBTS_FEATURE_TAG_cfar = KBTS_FOURCC('c', 'f', 'a', 'r'), // Conjunct Form After Ro KBTS_FEATURE_TAG_aalt = KBTS_FOURCC('a', 'a', 'l', 't'), // Access All Alternates KBTS_FEATURE_TAG_abvm = KBTS_FOURCC('a', 'b', 'v', 'm'), // Above-base Mark Positioning KBTS_FEATURE_TAG_abvs = KBTS_FOURCC('a', 'b', 'v', 's'), // Above-base Substitutions KBTS_FEATURE_TAG_afrc = KBTS_FOURCC('a', 'f', 'r', 'c'), // Alternative Fractions KBTS_FEATURE_TAG_akhn = KBTS_FOURCC('a', 'k', 'h', 'n'), // Akhand KBTS_FEATURE_TAG_apkn = KBTS_FOURCC('a', 'p', 'k', 'n'), // Kerning for Alternate Proportional Widths KBTS_FEATURE_TAG_blwm = KBTS_FOURCC('b', 'l', 'w', 'm'), // Below-base Mark Positioning KBTS_FEATURE_TAG_blws = KBTS_FOURCC('b', 'l', 'w', 's'), // Below-base Substitutions KBTS_FEATURE_TAG_calt = KBTS_FOURCC('c', 'a', 'l', 't'), // Contextual Alternates KBTS_FEATURE_TAG_case = KBTS_FOURCC('c', 'a', 's', 'e'), // Case-sensitive Forms KBTS_FEATURE_TAG_ccmp = KBTS_FOURCC('c', 'c', 'm', 'p'), // Glyph Composition / Decomposition KBTS_FEATURE_TAG_chws = KBTS_FOURCC('c', 'h', 'w', 's'), // Contextual Half-width Spacing KBTS_FEATURE_TAG_cjct = KBTS_FOURCC('c', 'j', 'c', 't'), // Conjunct Forms KBTS_FEATURE_TAG_clig = KBTS_FOURCC('c', 'l', 'i', 'g'), // Contextual Ligatures KBTS_FEATURE_TAG_cpct = KBTS_FOURCC('c', 'p', 'c', 't'), // Centered CJK Punctuation KBTS_FEATURE_TAG_cpsp = KBTS_FOURCC('c', 'p', 's', 'p'), // Capital Spacing KBTS_FEATURE_TAG_cswh = KBTS_FOURCC('c', 's', 'w', 'h'), // Contextual Swash KBTS_FEATURE_TAG_curs = KBTS_FOURCC('c', 'u', 'r', 's'), // Cursive Positioning KBTS_FEATURE_TAG_cv01 = KBTS_FOURCC('c', 'v', '0', '1'), // Character Variant 1 KBTS_FEATURE_TAG_cv02 = KBTS_FOURCC('c', 'v', '0', '2'), // Character Variant 2 KBTS_FEATURE_TAG_cv03 = KBTS_FOURCC('c', 'v', '0', '3'), // Character Variant 3 KBTS_FEATURE_TAG_cv04 = KBTS_FOURCC('c', 'v', '0', '4'), // Character Variant 4 KBTS_FEATURE_TAG_cv05 = KBTS_FOURCC('c', 'v', '0', '5'), // Character Variant 5 KBTS_FEATURE_TAG_cv06 = KBTS_FOURCC('c', 'v', '0', '6'), // Character Variant 6 KBTS_FEATURE_TAG_cv07 = KBTS_FOURCC('c', 'v', '0', '7'), // Character Variant 7 KBTS_FEATURE_TAG_cv08 = KBTS_FOURCC('c', 'v', '0', '8'), // Character Variant 8 KBTS_FEATURE_TAG_cv09 = KBTS_FOURCC('c', 'v', '0', '9'), // Character Variant 9 KBTS_FEATURE_TAG_cv10 = KBTS_FOURCC('c', 'v', '1', '0'), // Character Variant 10 KBTS_FEATURE_TAG_cv11 = KBTS_FOURCC('c', 'v', '1', '1'), // Character Variant 11 KBTS_FEATURE_TAG_cv12 = KBTS_FOURCC('c', 'v', '1', '2'), // Character Variant 12 KBTS_FEATURE_TAG_cv13 = KBTS_FOURCC('c', 'v', '1', '3'), // Character Variant 13 KBTS_FEATURE_TAG_cv14 = KBTS_FOURCC('c', 'v', '1', '4'), // Character Variant 14 KBTS_FEATURE_TAG_cv15 = KBTS_FOURCC('c', 'v', '1', '5'), // Character Variant 15 KBTS_FEATURE_TAG_cv16 = KBTS_FOURCC('c', 'v', '1', '6'), // Character Variant 16 KBTS_FEATURE_TAG_cv17 = KBTS_FOURCC('c', 'v', '1', '7'), // Character Variant 17 KBTS_FEATURE_TAG_cv18 = KBTS_FOURCC('c', 'v', '1', '8'), // Character Variant 18 KBTS_FEATURE_TAG_cv19 = KBTS_FOURCC('c', 'v', '1', '9'), // Character Variant 19 KBTS_FEATURE_TAG_cv20 = KBTS_FOURCC('c', 'v', '2', '0'), // Character Variant 20 KBTS_FEATURE_TAG_cv21 = KBTS_FOURCC('c', 'v', '2', '1'), // Character Variant 21 KBTS_FEATURE_TAG_cv22 = KBTS_FOURCC('c', 'v', '2', '2'), // Character Variant 22 KBTS_FEATURE_TAG_cv23 = KBTS_FOURCC('c', 'v', '2', '3'), // Character Variant 23 KBTS_FEATURE_TAG_cv24 = KBTS_FOURCC('c', 'v', '2', '4'), // Character Variant 24 KBTS_FEATURE_TAG_cv25 = KBTS_FOURCC('c', 'v', '2', '5'), // Character Variant 25 KBTS_FEATURE_TAG_cv26 = KBTS_FOURCC('c', 'v', '2', '6'), // Character Variant 26 KBTS_FEATURE_TAG_cv27 = KBTS_FOURCC('c', 'v', '2', '7'), // Character Variant 27 KBTS_FEATURE_TAG_cv28 = KBTS_FOURCC('c', 'v', '2', '8'), // Character Variant 28 KBTS_FEATURE_TAG_cv29 = KBTS_FOURCC('c', 'v', '2', '9'), // Character Variant 29 KBTS_FEATURE_TAG_cv30 = KBTS_FOURCC('c', 'v', '3', '0'), // Character Variant 30 KBTS_FEATURE_TAG_cv31 = KBTS_FOURCC('c', 'v', '3', '1'), // Character Variant 31 KBTS_FEATURE_TAG_cv32 = KBTS_FOURCC('c', 'v', '3', '2'), // Character Variant 32 KBTS_FEATURE_TAG_cv33 = KBTS_FOURCC('c', 'v', '3', '3'), // Character Variant 33 KBTS_FEATURE_TAG_cv34 = KBTS_FOURCC('c', 'v', '3', '4'), // Character Variant 34 KBTS_FEATURE_TAG_cv35 = KBTS_FOURCC('c', 'v', '3', '5'), // Character Variant 35 KBTS_FEATURE_TAG_cv36 = KBTS_FOURCC('c', 'v', '3', '6'), // Character Variant 36 KBTS_FEATURE_TAG_cv37 = KBTS_FOURCC('c', 'v', '3', '7'), // Character Variant 37 KBTS_FEATURE_TAG_cv38 = KBTS_FOURCC('c', 'v', '3', '8'), // Character Variant 38 KBTS_FEATURE_TAG_cv39 = KBTS_FOURCC('c', 'v', '3', '9'), // Character Variant 39 KBTS_FEATURE_TAG_cv40 = KBTS_FOURCC('c', 'v', '4', '0'), // Character Variant 40 KBTS_FEATURE_TAG_cv41 = KBTS_FOURCC('c', 'v', '4', '1'), // Character Variant 41 KBTS_FEATURE_TAG_cv42 = KBTS_FOURCC('c', 'v', '4', '2'), // Character Variant 42 KBTS_FEATURE_TAG_cv43 = KBTS_FOURCC('c', 'v', '4', '3'), // Character Variant 43 KBTS_FEATURE_TAG_cv44 = KBTS_FOURCC('c', 'v', '4', '4'), // Character Variant 44 KBTS_FEATURE_TAG_cv45 = KBTS_FOURCC('c', 'v', '4', '5'), // Character Variant 45 KBTS_FEATURE_TAG_cv46 = KBTS_FOURCC('c', 'v', '4', '6'), // Character Variant 46 KBTS_FEATURE_TAG_cv47 = KBTS_FOURCC('c', 'v', '4', '7'), // Character Variant 47 KBTS_FEATURE_TAG_cv48 = KBTS_FOURCC('c', 'v', '4', '8'), // Character Variant 48 KBTS_FEATURE_TAG_cv49 = KBTS_FOURCC('c', 'v', '4', '9'), // Character Variant 49 KBTS_FEATURE_TAG_cv50 = KBTS_FOURCC('c', 'v', '5', '0'), // Character Variant 50 KBTS_FEATURE_TAG_cv51 = KBTS_FOURCC('c', 'v', '5', '1'), // Character Variant 51 KBTS_FEATURE_TAG_cv52 = KBTS_FOURCC('c', 'v', '5', '2'), // Character Variant 52 KBTS_FEATURE_TAG_cv53 = KBTS_FOURCC('c', 'v', '5', '3'), // Character Variant 53 KBTS_FEATURE_TAG_cv54 = KBTS_FOURCC('c', 'v', '5', '4'), // Character Variant 54 KBTS_FEATURE_TAG_cv55 = KBTS_FOURCC('c', 'v', '5', '5'), // Character Variant 55 KBTS_FEATURE_TAG_cv56 = KBTS_FOURCC('c', 'v', '5', '6'), // Character Variant 56 KBTS_FEATURE_TAG_cv57 = KBTS_FOURCC('c', 'v', '5', '7'), // Character Variant 57 KBTS_FEATURE_TAG_cv58 = KBTS_FOURCC('c', 'v', '5', '8'), // Character Variant 58 KBTS_FEATURE_TAG_cv59 = KBTS_FOURCC('c', 'v', '5', '9'), // Character Variant 59 KBTS_FEATURE_TAG_cv60 = KBTS_FOURCC('c', 'v', '6', '0'), // Character Variant 60 KBTS_FEATURE_TAG_cv61 = KBTS_FOURCC('c', 'v', '6', '1'), // Character Variant 61 KBTS_FEATURE_TAG_cv62 = KBTS_FOURCC('c', 'v', '6', '2'), // Character Variant 62 KBTS_FEATURE_TAG_cv63 = KBTS_FOURCC('c', 'v', '6', '3'), // Character Variant 63 KBTS_FEATURE_TAG_cv64 = KBTS_FOURCC('c', 'v', '6', '4'), // Character Variant 64 KBTS_FEATURE_TAG_cv65 = KBTS_FOURCC('c', 'v', '6', '5'), // Character Variant 65 KBTS_FEATURE_TAG_cv66 = KBTS_FOURCC('c', 'v', '6', '6'), // Character Variant 66 KBTS_FEATURE_TAG_cv67 = KBTS_FOURCC('c', 'v', '6', '7'), // Character Variant 67 KBTS_FEATURE_TAG_cv68 = KBTS_FOURCC('c', 'v', '6', '8'), // Character Variant 68 KBTS_FEATURE_TAG_cv69 = KBTS_FOURCC('c', 'v', '6', '9'), // Character Variant 69 KBTS_FEATURE_TAG_cv70 = KBTS_FOURCC('c', 'v', '7', '0'), // Character Variant 70 KBTS_FEATURE_TAG_cv71 = KBTS_FOURCC('c', 'v', '7', '1'), // Character Variant 71 KBTS_FEATURE_TAG_cv72 = KBTS_FOURCC('c', 'v', '7', '2'), // Character Variant 72 KBTS_FEATURE_TAG_cv73 = KBTS_FOURCC('c', 'v', '7', '3'), // Character Variant 73 KBTS_FEATURE_TAG_cv74 = KBTS_FOURCC('c', 'v', '7', '4'), // Character Variant 74 KBTS_FEATURE_TAG_cv75 = KBTS_FOURCC('c', 'v', '7', '5'), // Character Variant 75 KBTS_FEATURE_TAG_cv76 = KBTS_FOURCC('c', 'v', '7', '6'), // Character Variant 76 KBTS_FEATURE_TAG_cv77 = KBTS_FOURCC('c', 'v', '7', '7'), // Character Variant 77 KBTS_FEATURE_TAG_cv78 = KBTS_FOURCC('c', 'v', '7', '8'), // Character Variant 78 KBTS_FEATURE_TAG_cv79 = KBTS_FOURCC('c', 'v', '7', '9'), // Character Variant 79 KBTS_FEATURE_TAG_cv80 = KBTS_FOURCC('c', 'v', '8', '0'), // Character Variant 80 KBTS_FEATURE_TAG_cv81 = KBTS_FOURCC('c', 'v', '8', '1'), // Character Variant 81 KBTS_FEATURE_TAG_cv82 = KBTS_FOURCC('c', 'v', '8', '2'), // Character Variant 82 KBTS_FEATURE_TAG_cv83 = KBTS_FOURCC('c', 'v', '8', '3'), // Character Variant 83 KBTS_FEATURE_TAG_cv84 = KBTS_FOURCC('c', 'v', '8', '4'), // Character Variant 84 KBTS_FEATURE_TAG_cv85 = KBTS_FOURCC('c', 'v', '8', '5'), // Character Variant 85 KBTS_FEATURE_TAG_cv86 = KBTS_FOURCC('c', 'v', '8', '6'), // Character Variant 86 KBTS_FEATURE_TAG_cv87 = KBTS_FOURCC('c', 'v', '8', '7'), // Character Variant 87 KBTS_FEATURE_TAG_cv88 = KBTS_FOURCC('c', 'v', '8', '8'), // Character Variant 88 KBTS_FEATURE_TAG_cv89 = KBTS_FOURCC('c', 'v', '8', '9'), // Character Variant 89 KBTS_FEATURE_TAG_cv90 = KBTS_FOURCC('c', 'v', '9', '0'), // Character Variant 90 KBTS_FEATURE_TAG_cv91 = KBTS_FOURCC('c', 'v', '9', '1'), // Character Variant 91 KBTS_FEATURE_TAG_cv92 = KBTS_FOURCC('c', 'v', '9', '2'), // Character Variant 92 KBTS_FEATURE_TAG_cv93 = KBTS_FOURCC('c', 'v', '9', '3'), // Character Variant 93 KBTS_FEATURE_TAG_cv94 = KBTS_FOURCC('c', 'v', '9', '4'), // Character Variant 94 KBTS_FEATURE_TAG_cv95 = KBTS_FOURCC('c', 'v', '9', '5'), // Character Variant 95 KBTS_FEATURE_TAG_cv96 = KBTS_FOURCC('c', 'v', '9', '6'), // Character Variant 96 KBTS_FEATURE_TAG_cv97 = KBTS_FOURCC('c', 'v', '9', '7'), // Character Variant 97 KBTS_FEATURE_TAG_cv98 = KBTS_FOURCC('c', 'v', '9', '8'), // Character Variant 98 KBTS_FEATURE_TAG_cv99 = KBTS_FOURCC('c', 'v', '9', '9'), // Character Variant 99 KBTS_FEATURE_TAG_c2pc = KBTS_FOURCC('c', '2', 'p', 'c'), // Petite Capitals From Capitals KBTS_FEATURE_TAG_c2sc = KBTS_FOURCC('c', '2', 's', 'c'), // Small Capitals From Capitals KBTS_FEATURE_TAG_dist = KBTS_FOURCC('d', 'i', 's', 't'), // Distances KBTS_FEATURE_TAG_dlig = KBTS_FOURCC('d', 'l', 'i', 'g'), // Discretionary Ligatures KBTS_FEATURE_TAG_dtls = KBTS_FOURCC('d', 't', 'l', 's'), // Dotless Forms KBTS_FEATURE_TAG_expt = KBTS_FOURCC('e', 'x', 'p', 't'), // Expert Forms KBTS_FEATURE_TAG_falt = KBTS_FOURCC('f', 'a', 'l', 't'), // Final Glyph on Line Alternates KBTS_FEATURE_TAG_flac = KBTS_FOURCC('f', 'l', 'a', 'c'), // Flattened Accent Forms KBTS_FEATURE_TAG_fwid = KBTS_FOURCC('f', 'w', 'i', 'd'), // Full Widths KBTS_FEATURE_TAG_haln = KBTS_FOURCC('h', 'a', 'l', 'n'), // Halant Forms KBTS_FEATURE_TAG_halt = KBTS_FOURCC('h', 'a', 'l', 't'), // Alternate Half Widths KBTS_FEATURE_TAG_hist = KBTS_FOURCC('h', 'i', 's', 't'), // Historical Forms KBTS_FEATURE_TAG_hkna = KBTS_FOURCC('h', 'k', 'n', 'a'), // Horizontal Kana Alternates KBTS_FEATURE_TAG_hlig = KBTS_FOURCC('h', 'l', 'i', 'g'), // Historical Ligatures KBTS_FEATURE_TAG_hngl = KBTS_FOURCC('h', 'n', 'g', 'l'), // Hangul KBTS_FEATURE_TAG_hojo = KBTS_FOURCC('h', 'o', 'j', 'o'), // Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) KBTS_FEATURE_TAG_hwid = KBTS_FOURCC('h', 'w', 'i', 'd'), // Half Widths KBTS_FEATURE_TAG_ital = KBTS_FOURCC('i', 't', 'a', 'l'), // Italics KBTS_FEATURE_TAG_jalt = KBTS_FOURCC('j', 'a', 'l', 't'), // Justification Alternates KBTS_FEATURE_TAG_jp78 = KBTS_FOURCC('j', 'p', '7', '8'), // JIS78 Forms KBTS_FEATURE_TAG_jp83 = KBTS_FOURCC('j', 'p', '8', '3'), // JIS83 Forms KBTS_FEATURE_TAG_jp90 = KBTS_FOURCC('j', 'p', '9', '0'), // JIS90 Forms KBTS_FEATURE_TAG_jp04 = KBTS_FOURCC('j', 'p', '0', '4'), // JIS2004 Forms KBTS_FEATURE_TAG_kern = KBTS_FOURCC('k', 'e', 'r', 'n'), // Kerning KBTS_FEATURE_TAG_lfbd = KBTS_FOURCC('l', 'f', 'b', 'd'), // Left Bounds KBTS_FEATURE_TAG_liga = KBTS_FOURCC('l', 'i', 'g', 'a'), // Standard Ligatures KBTS_FEATURE_TAG_lnum = KBTS_FOURCC('l', 'n', 'u', 'm'), // Lining Figures KBTS_FEATURE_TAG_locl = KBTS_FOURCC('l', 'o', 'c', 'l'), // Localized Forms KBTS_FEATURE_TAG_ltra = KBTS_FOURCC('l', 't', 'r', 'a'), // Left-to-right Alternates KBTS_FEATURE_TAG_ltrm = KBTS_FOURCC('l', 't', 'r', 'm'), // Left-to-right Mirrored Forms KBTS_FEATURE_TAG_mark = KBTS_FOURCC('m', 'a', 'r', 'k'), // Mark Positioning KBTS_FEATURE_TAG_mgrk = KBTS_FOURCC('m', 'g', 'r', 'k'), // Mathematical Greek KBTS_FEATURE_TAG_mkmk = KBTS_FOURCC('m', 'k', 'm', 'k'), // Mark to Mark Positioning KBTS_FEATURE_TAG_mset = KBTS_FOURCC('m', 's', 'e', 't'), // Mark Positioning via Substitution KBTS_FEATURE_TAG_nalt = KBTS_FOURCC('n', 'a', 'l', 't'), // Alternate Annotation Forms KBTS_FEATURE_TAG_nlck = KBTS_FOURCC('n', 'l', 'c', 'k'), // NLC Kanji Forms KBTS_FEATURE_TAG_nukt = KBTS_FOURCC('n', 'u', 'k', 't'), // Nukta Forms KBTS_FEATURE_TAG_onum = KBTS_FOURCC('o', 'n', 'u', 'm'), // Oldstyle Figures KBTS_FEATURE_TAG_opbd = KBTS_FOURCC('o', 'p', 'b', 'd'), // Optical Bounds KBTS_FEATURE_TAG_ordn = KBTS_FOURCC('o', 'r', 'd', 'n'), // Ordinals KBTS_FEATURE_TAG_ornm = KBTS_FOURCC('o', 'r', 'n', 'm'), // Ornaments KBTS_FEATURE_TAG_palt = KBTS_FOURCC('p', 'a', 'l', 't'), // Proportional Alternate Widths KBTS_FEATURE_TAG_pcap = KBTS_FOURCC('p', 'c', 'a', 'p'), // Petite Capitals KBTS_FEATURE_TAG_pkna = KBTS_FOURCC('p', 'k', 'n', 'a'), // Proportional Kana KBTS_FEATURE_TAG_pnum = KBTS_FOURCC('p', 'n', 'u', 'm'), // Proportional Figures KBTS_FEATURE_TAG_pres = KBTS_FOURCC('p', 'r', 'e', 's'), // Pre-base Substitutions KBTS_FEATURE_TAG_psts = KBTS_FOURCC('p', 's', 't', 's'), // Post-base Substitutions KBTS_FEATURE_TAG_pwid = KBTS_FOURCC('p', 'w', 'i', 'd'), // Proportional Widths KBTS_FEATURE_TAG_qwid = KBTS_FOURCC('q', 'w', 'i', 'd'), // Quarter Widths KBTS_FEATURE_TAG_rand = KBTS_FOURCC('r', 'a', 'n', 'd'), // Randomize KBTS_FEATURE_TAG_rclt = KBTS_FOURCC('r', 'c', 'l', 't'), // Required Contextual Alternates KBTS_FEATURE_TAG_rkrf = KBTS_FOURCC('r', 'k', 'r', 'f'), // Rakar Forms KBTS_FEATURE_TAG_rlig = KBTS_FOURCC('r', 'l', 'i', 'g'), // Required Ligatures KBTS_FEATURE_TAG_rtbd = KBTS_FOURCC('r', 't', 'b', 'd'), // Right Bounds KBTS_FEATURE_TAG_rtla = KBTS_FOURCC('r', 't', 'l', 'a'), // Right-to-left Alternates KBTS_FEATURE_TAG_rtlm = KBTS_FOURCC('r', 't', 'l', 'm'), // Right-to-left Mirrored Forms KBTS_FEATURE_TAG_ruby = KBTS_FOURCC('r', 'u', 'b', 'y'), // Ruby Notation Forms KBTS_FEATURE_TAG_rvrn = KBTS_FOURCC('r', 'v', 'r', 'n'), // Required Variation Alternates KBTS_FEATURE_TAG_salt = KBTS_FOURCC('s', 'a', 'l', 't'), // Stylistic Alternates KBTS_FEATURE_TAG_sinf = KBTS_FOURCC('s', 'i', 'n', 'f'), // Scientific Inferiors KBTS_FEATURE_TAG_size = KBTS_FOURCC('s', 'i', 'z', 'e'), // Optical size KBTS_FEATURE_TAG_smcp = KBTS_FOURCC('s', 'm', 'c', 'p'), // Small Capitals KBTS_FEATURE_TAG_smpl = KBTS_FOURCC('s', 'm', 'p', 'l'), // Simplified Forms KBTS_FEATURE_TAG_ss01 = KBTS_FOURCC('s', 's', '0', '1'), // Stylistic Set 1 KBTS_FEATURE_TAG_ss02 = KBTS_FOURCC('s', 's', '0', '2'), // Stylistic Set 2 KBTS_FEATURE_TAG_ss03 = KBTS_FOURCC('s', 's', '0', '3'), // Stylistic Set 3 KBTS_FEATURE_TAG_ss04 = KBTS_FOURCC('s', 's', '0', '4'), // Stylistic Set 4 KBTS_FEATURE_TAG_ss05 = KBTS_FOURCC('s', 's', '0', '5'), // Stylistic Set 5 KBTS_FEATURE_TAG_ss06 = KBTS_FOURCC('s', 's', '0', '6'), // Stylistic Set 6 KBTS_FEATURE_TAG_ss07 = KBTS_FOURCC('s', 's', '0', '7'), // Stylistic Set 7 KBTS_FEATURE_TAG_ss08 = KBTS_FOURCC('s', 's', '0', '8'), // Stylistic Set 8 KBTS_FEATURE_TAG_ss09 = KBTS_FOURCC('s', 's', '0', '9'), // Stylistic Set 9 KBTS_FEATURE_TAG_ss10 = KBTS_FOURCC('s', 's', '1', '0'), // Stylistic Set 10 KBTS_FEATURE_TAG_ss11 = KBTS_FOURCC('s', 's', '1', '1'), // Stylistic Set 11 KBTS_FEATURE_TAG_ss12 = KBTS_FOURCC('s', 's', '1', '2'), // Stylistic Set 12 KBTS_FEATURE_TAG_ss13 = KBTS_FOURCC('s', 's', '1', '3'), // Stylistic Set 13 KBTS_FEATURE_TAG_ss14 = KBTS_FOURCC('s', 's', '1', '4'), // Stylistic Set 14 KBTS_FEATURE_TAG_ss15 = KBTS_FOURCC('s', 's', '1', '5'), // Stylistic Set 15 KBTS_FEATURE_TAG_ss16 = KBTS_FOURCC('s', 's', '1', '6'), // Stylistic Set 16 KBTS_FEATURE_TAG_ss17 = KBTS_FOURCC('s', 's', '1', '7'), // Stylistic Set 17 KBTS_FEATURE_TAG_ss18 = KBTS_FOURCC('s', 's', '1', '8'), // Stylistic Set 18 KBTS_FEATURE_TAG_ss19 = KBTS_FOURCC('s', 's', '1', '9'), // Stylistic Set 19 KBTS_FEATURE_TAG_ss20 = KBTS_FOURCC('s', 's', '2', '0'), // Stylistic Set 20 KBTS_FEATURE_TAG_ssty = KBTS_FOURCC('s', 's', 't', 'y'), // Math Script-style Alternates KBTS_FEATURE_TAG_stch = KBTS_FOURCC('s', 't', 'c', 'h'), // Stretching Glyph Decomposition KBTS_FEATURE_TAG_subs = KBTS_FOURCC('s', 'u', 'b', 's'), // Subscript KBTS_FEATURE_TAG_sups = KBTS_FOURCC('s', 'u', 'p', 's'), // Superscript KBTS_FEATURE_TAG_swsh = KBTS_FOURCC('s', 'w', 's', 'h'), // Swash KBTS_FEATURE_TAG_test = KBTS_FOURCC('t', 'e', 's', 't'), // Test features, only for development KBTS_FEATURE_TAG_titl = KBTS_FOURCC('t', 'i', 't', 'l'), // Titling KBTS_FEATURE_TAG_tnam = KBTS_FOURCC('t', 'n', 'a', 'm'), // Traditional Name Forms KBTS_FEATURE_TAG_tnum = KBTS_FOURCC('t', 'n', 'u', 'm'), // Tabular Figures KBTS_FEATURE_TAG_trad = KBTS_FOURCC('t', 'r', 'a', 'd'), // Traditional Forms KBTS_FEATURE_TAG_twid = KBTS_FOURCC('t', 'w', 'i', 'd'), // Third Widths KBTS_FEATURE_TAG_unic = KBTS_FOURCC('u', 'n', 'i', 'c'), // Unicase KBTS_FEATURE_TAG_valt = KBTS_FOURCC('v', 'a', 'l', 't'), // Alternate Vertical Metrics KBTS_FEATURE_TAG_vapk = KBTS_FOURCC('v', 'a', 'p', 'k'), // Kerning for Alternate Proportional Vertical Metrics KBTS_FEATURE_TAG_vatu = KBTS_FOURCC('v', 'a', 't', 'u'), // Vattu Variants KBTS_FEATURE_TAG_vchw = KBTS_FOURCC('v', 'c', 'h', 'w'), // Vertical Contextual Half-width Spacing KBTS_FEATURE_TAG_vert = KBTS_FOURCC('v', 'e', 'r', 't'), // Vertical Alternates KBTS_FEATURE_TAG_vhal = KBTS_FOURCC('v', 'h', 'a', 'l'), // Alternate Vertical Half Metrics KBTS_FEATURE_TAG_vkna = KBTS_FOURCC('v', 'k', 'n', 'a'), // Vertical Kana Alternates KBTS_FEATURE_TAG_vkrn = KBTS_FOURCC('v', 'k', 'r', 'n'), // Vertical Kerning KBTS_FEATURE_TAG_vpal = KBTS_FOURCC('v', 'p', 'a', 'l'), // Proportional Alternate Vertical Metrics KBTS_FEATURE_TAG_vrt2 = KBTS_FOURCC('v', 'r', 't', '2'), // Vertical Alternates and Rotation KBTS_FEATURE_TAG_vrtr = KBTS_FOURCC('v', 'r', 't', 'r'), // Vertical Alternates for Rotation KBTS_FEATURE_TAG_zero = KBTS_FOURCC('z', 'e', 'r', 'o'), // Slashed Zero }; typedef struct kbts__gdef kbts__gdef; typedef struct kbts__cmap_14 kbts__cmap_14; typedef struct kbts__gsub_gpos kbts__gsub_gpos; typedef struct kbts__maxp kbts__maxp; typedef struct kbts__hea kbts__hea; typedef struct kbts_shaper_properties kbts_shaper_properties; typedef struct kbts__feature kbts__feature; typedef struct kbts__head kbts__head; typedef struct kbts__langsys kbts__langsys; typedef struct kbts_shape_config kbts_shape_config; typedef struct kbts_glyph kbts_glyph; typedef struct kbts_glyph_config kbts_glyph_config; typedef struct kbts_shape_context kbts_shape_context; typedef struct kbts_glyph_storage kbts_glyph_storage; typedef struct kbts_allocator_op_allocate { void *Pointer; kbts_u32 Size; } kbts_allocator_op_allocate; typedef struct kbts_allocator_op_free { void *Pointer; } kbts_allocator_op_free; typedef struct kbts_allocator_op { kbts_allocator_op_kind Kind; union { kbts_allocator_op_allocate Allocate; kbts_allocator_op_free Free; }; } kbts_allocator_op; typedef void kbts_allocator_function(void *Data, kbts_allocator_op *Op); typedef struct kbts_lookup_subtable_info { kbts_u16 MinimumBacktrackPlusOne; kbts_u16 MinimumFollowupPlusOne; } kbts_lookup_subtable_info; typedef struct kbts_blob_table { kbts_u32 OffsetFromStartOfFile; kbts_u32 Length; } kbts_blob_table; typedef struct kbts_load_font_state { void *FontData; kbts_u32 FontDataSize; kbts_blob_table Tables[KBTS_BLOB_TABLE_ID_COUNT]; kbts_u32 LookupCount; kbts_u32 LookupSubtableCount; kbts_u32 GlyphCount; kbts_u32 ScratchSize; kbts_u32 GlyphLookupMatrixSizeInBytes; kbts_u32 GlyphLookupSubtableMatrixSizeInBytes; kbts_u32 TotalSize; } kbts_load_font_state; typedef struct kbts_blob_header { kbts_u32 Magic; kbts_u32 Version; kbts_u32 LookupCount; kbts_u32 LookupSubtableCount; kbts_u32 GlyphCount; kbts_u32 GposLookupIndexOffset; kbts_u32 GlyphLookupMatrixOffsetFromStartOfFile; kbts_u32 GlyphLookupSubtableMatrixOffsetFromStartOfFile; kbts_u32 LookupSubtableIndexOffsetsOffsetFromStartOfFile; kbts_u32 SubtableInfosOffsetFromStartOfFile; kbts_blob_table Tables[KBTS_BLOB_TABLE_ID_COUNT]; } kbts_blob_header; typedef struct kbts_font { kbts_allocator_function *Allocator; void *AllocatorData; kbts_blob_header *Blob; kbts_u16 *Cmap; kbts__cmap_14 *Cmap14; kbts__gsub_gpos *ShapingTables[KBTS_SHAPING_TABLE_COUNT]; void *UserData; kbts_load_font_error Error; } kbts_font; typedef struct kbts_font_info { char *Strings[KBTS_FONT_INFO_STRING_ID_COUNT]; kbts_u16 StringLengths[KBTS_FONT_INFO_STRING_ID_COUNT]; kbts_font_style_flags StyleFlags; kbts_font_weight Weight; kbts_font_width Width; } kbts_font_info; typedef struct kbts_feature_override { kbts_feature_tag Tag; int Value; } kbts_feature_override; typedef struct kbts_break { // The break code mostly works in relative positions, but we convert to absolute positions for the user. // That way, breaks can be trivially stored and compared and such and it just works. int Position; kbts_break_flags Flags; kbts_direction Direction; // Only valid if (Flags & KBTS_BREAK_FLAG_DIRECTION). kbts_direction ParagraphDirection; // Only valid if (Flags & KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION). kbts_script Script; // Only valid if (Flags & KBTS_BREAK_FLAG_SCRIPT). } kbts_break; typedef struct kbts_bracket { kbts_u32 Codepoint; kbts_u32 Position; kbts_u8 Direction; kbts_u8 Script; } kbts_bracket; // In the worst case, a single call to BreakAddCodepoint would generate 4 breaks. // We buffer breaks to reorder them before returning them to the user. // This potentially requires infinite memory, which we don't have, so you may want to tweak this constant, // although, really, if the defaults don't work, then you have likely found very strange/adversarial text. typedef struct kbts_break_state { kbts_break Breaks[8]; kbts_u32 BreakCount; kbts_direction ParagraphDirection; kbts_direction UserParagraphDirection; kbts_u32 CurrentPosition; kbts_u32 ParagraphStartPosition; kbts_u32 LastScriptBreakPosition; kbts_u32 LastDirectionBreakPosition; kbts_u8 LastScriptBreakScript; kbts_u8 LastDirectionBreakDirection; kbts_s16 ScriptPositionOffset; kbts_u32 ScriptCount; kbts_u8 ScriptSet[KBTS_MAXIMUM_CODEPOINT_SCRIPTS]; kbts_bracket Brackets[64]; kbts_u32 BracketCount; kbts_break_state_flags Flags; kbts_u32 FlagState; // u8(kbts_break_flags)x4 kbts_s16 PositionOffset2; kbts_s16 PositionOffset3; kbts_u32 WordBreakHistory; // u8x4 kbts_u16 WordBreaks; // u4x4 kbts_u16 WordUnbreaks; // u4x4 kbts_s16 WordBreak2PositionOffset; kbts_u64 LineBreaks; // u16x4 // Instead of staying synchronized with LineBreaks/LineUnbreaks, // this advances every character always. // (This is only needed because ZWJ can create an unbreak while simultaneously being ignored.) kbts_u64 LineUnbreaksAsync; // u16x4 kbts_u64 LineUnbreaks; // u16x4 kbts_u32 LineBreakHistory; // u8(line_break_class)x4 kbts_s16 LineBreak2PositionOffset; kbts_s16 LineBreak3PositionOffset; kbts_u8 LastDirection; kbts_u8 BidirectionalClass2; kbts_u8 BidirectionalClass1; kbts_s16 Bidirectional1PositionOffset; kbts_s16 Bidirectional2PositionOffset; kbts_japanese_line_break_style JapaneseLineBreakStyle; kbts_break_config_flags ConfigFlags; kbts_u8 GraphemeBreakState; kbts_u8 LastLineBreakClass; kbts_u8 LastWordBreakClass; kbts_u8 LastWordBreakClassIncludingIgnored; } kbts_break_state; typedef struct kbts_decode { int Codepoint; int SourceCharactersConsumed; int Valid; } kbts_decode; typedef struct kbts_encode_utf8 { char Encoded[4]; int EncodedLength; int Valid; } kbts_encode_utf8; typedef struct kbts_glyph_classes { kbts_u16 Class; kbts_u16 MarkAttachmentClass; } kbts_glyph_classes; typedef struct kbts_glyph { kbts_glyph *Prev; kbts_glyph *Next; kbts_u32 Codepoint; kbts_u16 Id; // Glyph index. This is what you want to use to query outline data. kbts_u16 Uid; // This field is kept and returned as-is throughout the shaping process. // When you are using the context API, it contains a codepoint index always! // To get the original user ID with the context API, you need to get the corresponding kbts_shape_codepoint // with kbts_ShapeGetShapeCodepoint(Context, Glyph->UserIdOrCodepointIndex, ...); int UserIdOrCodepointIndex; // Used by GPOS kbts_s32 OffsetX; kbts_s32 OffsetY; kbts_s32 AdvanceX; kbts_s32 AdvanceY; // Earlier on, we used to assume that, if a glyph had no advance, or had the MARK glyph class, then // it could be handled as a mark in layout operations. This is inaccurate. // Unicode makes a distinction between attached marks and standalone marks. For our purposes, attached // marks are marks that have found a valid base character to attach to. In practice, this means that the // font contains a valid display position/configuration for it in the current context. // In contrast, standalone marks are marks that aren't attached to anything. Fonts may still have glyphs // for them, in which case we want to display those just like regular glyphs that take up horizontal space // on the line. When fonts don't have glyphs for them, they simply stay around as zero-width glyphs. // Standalone marks have notably different behavior compared to attached marks, and so, once we start // applying positioning features, it becomes worthwhile to track exactly which glyph has attached to which. struct kbts_glyph *AttachGlyph; // Set by GPOS attachments. kbts_glyph_config *Config; kbts_u64 Decomposition; kbts_glyph_classes Classes; kbts_glyph_flags Flags; kbts_u32 ParentInfo; // This is set by GSUB and used by GPOS. // A 0-index means that we should attach to the last component in the ligature. // // From the Microsoft docs: // To correctly access the subtables, the client must keep track of the component associated with the mark. // // For a given mark assigned to a particular class, the appropriate base attachment point is determined by which // ligature component the mark is associated with. This is dependent on the original character string and subsequent // character- or glyph-sequence processing, not the font data alone. While a text-layout client is performing any // character-based preprocessing or any glyph-substitution operations using the GSUB table, the text-layout client // must keep track of associations of marks to particular ligature-glyph components. kbts_u16 LigatureUid; kbts_u16 LigatureComponentIndexPlusOne; kbts_u16 LigatureComponentCount; // Set in GSUB and used in GPOS, for STCH. kbts_joining_feature JoiningFeature; // Unicode properties filled in by CodepointToGlyph. kbts_unicode_joining_type JoiningType; kbts_u8 UnicodeFlags; kbts_u8 SyllabicClass; kbts_u8 SyllabicPosition; kbts_u8 UseClass; kbts_u8 CombiningClass; kbts_u8 MarkOrdering; // Only used temporarily in NORMALIZE for Arabic mark reordering. } kbts_glyph; typedef struct kbts_shape_codepoint { kbts_font *Font; // Only set when (BreakFlags & KBTS_BREAK_FLAG_GRAPHEME) != 0. kbts_glyph_config *Config; int Codepoint; int UserId; kbts_break_flags BreakFlags; kbts_script Script; // Only set when (BreakFlags & KBTS_BREAK_FLAG_SCRIPT) != 0. kbts_direction Direction; // Only set when (BreakFlags & KBTS_BREAK_FLAG_DIRECTION) != 0. kbts_direction ParagraphDirection; // Only set when (BreakFlags & KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION) != 0. } kbts_shape_codepoint; typedef struct kbts_shape_codepoint_iterator { kbts_shape_codepoint *Codepoint; kbts_shape_context *Context; kbts_u32 EndBlockIndex; kbts_u32 OnePastLastCodepointIndex; kbts_u32 BlockIndex; kbts_u32 CodepointIndex; kbts_u32 CurrentBlockCodepointCount; kbts_u32 FlatCodepointIndex; } kbts_shape_codepoint_iterator; typedef struct kbts_glyph_iterator { kbts_glyph_storage *GlyphStorage; kbts_glyph *CurrentGlyph; int LastAdvanceX; int X; int Y; } kbts_glyph_iterator; typedef struct kbts_arena_block_header { struct kbts_arena_block_header *Prev; struct kbts_arena_block_header *Next; } kbts_arena_block_header; typedef struct kbts_arena { kbts_allocator_function *Allocator; void *AllocatorData; kbts_arena_block_header BlockSentinel; kbts_arena_block_header FreeBlockSentinel; int Error; } kbts_arena; typedef struct kbts_glyph_storage { kbts_arena Arena; kbts_glyph GlyphSentinel; kbts_glyph FreeGlyphSentinel; int Error; } kbts_glyph_storage; typedef struct kbts_glyph_parent { kbts_u64 Decomposition; kbts_u32 Codepoint; } kbts_glyph_parent; typedef struct kbts_font_coverage_test { kbts_font *Font; kbts_u32 BaseCodepoint; int CurrentBaseError; int Error; kbts_glyph_parent BaseParents[KBTS_MAXIMUM_RECOMPOSITION_PARENTS]; kbts_u32 BaseParentCount; } kbts_font_coverage_test; typedef struct kbts_run { kbts_font *Font; kbts_script Script; kbts_direction ParagraphDirection; kbts_direction Direction; kbts_break_flags Flags; kbts_glyph_iterator Glyphs; } kbts_run; // // Context API // The context can do everything for you. It is pretty convenient! // KBTS_EXPORT int kbts_SizeOfShapeContext(void); KBTS_EXPORT kbts_shape_context *kbts_PlaceShapeContext(kbts_allocator_function *Allocator, void *AllocatorData, void *Memory); KBTS_EXPORT kbts_shape_context *kbts_PlaceShapeContextFixedMemory(void *Memory, int Size); KBTS_EXPORT kbts_shape_context *kbts_CreateShapeContext(kbts_allocator_function *Allocator, void *AllocatorData); KBTS_EXPORT void kbts_DestroyShapeContext(kbts_shape_context *Context); #ifndef KB_TEXT_SHAPE_NO_CRT KBTS_EXPORT kbts_font *kbts_ShapePushFontFromFile(kbts_shape_context *Context, const char *FileName, int FontIndex); #endif KBTS_EXPORT kbts_font *kbts_ShapePushFontFromMemory(kbts_shape_context *Context, void *Memory, int Size, int FontIndex); KBTS_EXPORT kbts_font *kbts_ShapePushFont(kbts_shape_context *Context, kbts_font *Font); KBTS_EXPORT kbts_font *kbts_ShapePopFont(kbts_shape_context *Context); KBTS_EXPORT void kbts_ShapeBegin(kbts_shape_context *Context, kbts_direction ParagraphDirection, kbts_language Language); KBTS_EXPORT void kbts_ShapeEnd(kbts_shape_context *Context); KBTS_EXPORT int kbts_ShapeRun(kbts_shape_context *Context, kbts_run *Run); KBTS_EXPORT void kbts_ShapePushFeature(kbts_shape_context *Context, kbts_u32 FeatureTag, int Value); KBTS_EXPORT int kbts_ShapePopFeature(kbts_shape_context *Context, kbts_u32 FeatureTag); KBTS_EXPORT void kbts_ShapeCodepoint(kbts_shape_context *Context, int Codepoint); KBTS_EXPORT void kbts_ShapeCodepointWithUserId(kbts_shape_context *Context, int Codepoint, int UserId); KBTS_EXPORT void kbts_ShapeUtf32(kbts_shape_context *Context, int *Utf32, int Length); KBTS_EXPORT void kbts_ShapeUtf32WithUserId(kbts_shape_context *Context, int *Utf32, int Length, int UserId, int UserIdIncrement); KBTS_EXPORT void kbts_ShapeUtf8(kbts_shape_context *Context, const char *Utf8, int Length, kbts_user_id_generation_mode UserIdGenerationMode); KBTS_EXPORT void kbts_ShapeUtf8WithUserId(kbts_shape_context *Context, const char *Utf8, int Length, int UserId, kbts_user_id_generation_mode UserIdGenerationMode); KBTS_EXPORT kbts_shape_error kbts_ShapeError(kbts_shape_context *Context); KBTS_EXPORT void kbts_ShapeBeginManualRuns(kbts_shape_context *Context); KBTS_EXPORT void kbts_ShapeNextManualRun(kbts_shape_context *Context, kbts_direction Direction, kbts_script Script); KBTS_EXPORT void kbts_ShapeEndManualRuns(kbts_shape_context *Context); KBTS_EXPORT void kbts_ShapeManualBreak(kbts_shape_context *Context); KBTS_EXPORT kbts_shape_codepoint_iterator kbts_ShapeCurrentCodepointsIterator(kbts_shape_context *Context); KBTS_EXPORT int kbts_ShapeCodepointIteratorIsValid(kbts_shape_codepoint_iterator *It); KBTS_EXPORT int kbts_ShapeCodepointIteratorNext(kbts_shape_codepoint_iterator *It, kbts_shape_codepoint *Codepoint, int *CodepointIndex); KBTS_EXPORT int kbts_ShapeGetShapeCodepoint(kbts_shape_context *Context, int CodepointIndex, kbts_shape_codepoint *Codepoint); // // Direct API // KBTS_EXPORT kbts_shape_error kbts_ShapeDirect(kbts_shape_config *Config, kbts_glyph_storage *Storage, kbts_direction RunDirection, kbts_allocator_function *Allocator, void *AllocatorData, kbts_glyph_iterator *Output); KBTS_EXPORT kbts_shape_error kbts_ShapeDirectFixedMemory(kbts_shape_config *Config, kbts_glyph_storage *Storage, kbts_direction RunDirection, void *Memory, int MemorySize, kbts_glyph_iterator *Output); // A font holds all data that corresponds to a given font file. #ifndef KB_TEXT_SHAPE_NO_CRT KBTS_EXPORT kbts_font kbts_FontFromFile(const char *FileName, int FontIndex, kbts_allocator_function *Allocator, void *AllocatorData, void **FileData, int *FileSize); #endif KBTS_EXPORT int kbts_FontCount(void *FileData, int FileSize); KBTS_EXPORT kbts_font kbts_FontFromMemory(void *FileData, int FileSize, int FontIndex, kbts_allocator_function *Allocator, void *AllocatorData); KBTS_EXPORT void kbts_FreeFont(kbts_font *Font); KBTS_EXPORT int kbts_FontIsValid(kbts_font *Font); KBTS_EXPORT kbts_load_font_error kbts_LoadFont(kbts_font *Font, kbts_load_font_state *State, void *FontData, int FontDataSize, int FontIndex, int *ScratchSize_, int *OutputSize_); KBTS_EXPORT kbts_load_font_error kbts_PlaceBlob(kbts_font *Font, kbts_load_font_state *State, void *ScratchMemory, void *OutputMemory); KBTS_EXPORT void kbts_GetFontInfo(kbts_font *Font, kbts_font_info *Info); // A shape_config is a bag of pre-computed data for a specific shaping setup. KBTS_EXPORT int kbts_SizeOfShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language); KBTS_EXPORT kbts_shape_config *kbts_PlaceShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language, void *Memory); KBTS_EXPORT kbts_shape_config *kbts_CreateShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language, kbts_allocator_function *Allocator, void *AllocatorData); KBTS_EXPORT void kbts_DestroyShapeConfig(kbts_shape_config *Config); // A glyph_storage holds and recycles glyph data. KBTS_EXPORT int kbts_InitializeGlyphStorage(kbts_glyph_storage *Storage, kbts_allocator_function *Allocator, void *AllocatorData); KBTS_EXPORT int kbts_InitializeGlyphStorageFixedMemory(kbts_glyph_storage *Storage, void *Memory, int MemorySize); KBTS_EXPORT kbts_glyph *kbts_PushGlyph(kbts_glyph_storage *Storage, kbts_font *Font, int Codepoint, kbts_glyph_config *Config, int UserId); KBTS_EXPORT void kbts_ClearActiveGlyphs(kbts_glyph_storage *Storage); KBTS_EXPORT void kbts_FreeAllGlyphs(kbts_glyph_storage *Storage); KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, int Codepoint, kbts_glyph_config *Config, int UserId); KBTS_EXPORT int kbts_CodepointToGlyphId(kbts_font *Font, int Codepoint); KBTS_EXPORT kbts_glyph_iterator kbts_ActiveGlyphIterator(kbts_glyph_storage *Storage); // A glyph_config specifies glyph-specific shaping parameters. // A single glyph_config can be shared by multiple glyphs. KBTS_EXPORT int kbts_SizeOfGlyphConfig(kbts_feature_override *Overrides, int OverrideCount); KBTS_EXPORT kbts_glyph_config *kbts_PlaceGlyphConfig(kbts_feature_override *Overrides, int OverrideCount, void *Memory); KBTS_EXPORT kbts_glyph_config *kbts_CreateGlyphConfig(kbts_feature_override *Overrides, int OverrideCount, kbts_allocator_function *Allocator, void *AllocatorData); KBTS_EXPORT void kbts_DestroyGlyphConfig(kbts_glyph_config *Config); // // Glyph iterator // KBTS_EXPORT int kbts_GlyphIteratorNext(kbts_glyph_iterator *It, kbts_glyph **Glyph); KBTS_EXPORT int kbts_GlyphIteratorIsValid(kbts_glyph_iterator *It); // // Segmentation // // This is a quick guess that stops at the first glyph that has a strong script/direction associated to it. // It is convenient, but only works if you are sure your input text is mono-script and mono-direction. KBTS_EXPORT void kbts_GuessTextProperties(void *Text, int TextSizeInBytes, kbts_text_format Format, kbts_direction *Direction, kbts_script *Script); KBTS_EXPORT void kbts_GuessTextPropertiesUtf32(const int *Utf32, int Utf32Count, kbts_direction *Direction, kbts_script *Script); KBTS_EXPORT void kbts_GuessTextPropertiesUtf8(const char *Utf8, int Utf8Length, kbts_direction *Direction, kbts_script *Script); KBTS_EXPORT void kbts_BreakBegin(kbts_break_state *State, kbts_direction ParagraphDirection, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags); KBTS_EXPORT void kbts_BreakAddCodepoint(kbts_break_state *State, int Codepoint, int PositionIncrement, int EndOfText); KBTS_EXPORT void kbts_BreakEnd(kbts_break_state *State); KBTS_EXPORT int kbts_Break(kbts_break_state *State, kbts_break *Break); KBTS_EXPORT void kbts_BreakEntireString(kbts_direction Direction, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags, const void *Input, int InputSizeInBytes, kbts_text_format InputFormat, kbts_break *Breaks, int BreakCapacity, int *BreakCount, kbts_break_flags *BreakFlags, int BreakFlagCapacity, int *BreakFlagCount); KBTS_EXPORT void kbts_BreakEntireStringUtf32(kbts_direction Direction, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags, const int *Utf32, int Utf32Count, kbts_break *Breaks, int BreakCapacity, int *BreakCount, kbts_break_flags *BreakFlags, int BreakFlagCapacity, int *BreakFlagCount); KBTS_EXPORT void kbts_BreakEntireStringUtf8(kbts_direction Direction, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags, const char *Utf8, int Utf8Length, kbts_break *Breaks, int BreakCapacity, int *BreakCount, kbts_break_flags *BreakFlags, int BreakFlagCapacity, int *BreakFlagCount); // // Other stuff // // Quick test for font support of a sequence of codepoints. KBTS_EXPORT void kbts_FontCoverageTestBegin(kbts_font_coverage_test *Test, kbts_font *Font); KBTS_EXPORT void kbts_FontCoverageTestCodepoint(kbts_font_coverage_test *Test, int Codepoint); KBTS_EXPORT int kbts_FontCoverageTestEnd(kbts_font_coverage_test *Test); KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, kbts_un Length); KBTS_EXPORT kbts_encode_utf8 kbts_EncodeUtf8(int Codepoint); KBTS_EXPORT kbts_direction kbts_ScriptDirection(kbts_script Script); KBTS_EXPORT int kbts_ScriptIsComplex(kbts_script Script); KBTS_EXPORT kbts_script kbts_ScriptTagToScript(kbts_script_tag Tag); #endif