From a87f602cd04be0286591bf8137ff24939e0e9f9f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Dec 2025 11:39:22 +0000 Subject: [PATCH 1/8] Update kb_text_shape to v2.03 --- vendor/kb_text_shape/kb_text_shape_procs.odin | 443 +- vendor/kb_text_shape/kb_text_shape_types.odin | 3188 +-- vendor/kb_text_shape/lib/kb_text_shape.lib | Bin 714902 -> 852874 bytes vendor/kb_text_shape/src/kb_text_shape.h | 22039 ++++++++++------ 4 files changed, 15750 insertions(+), 9920 deletions(-) diff --git a/vendor/kb_text_shape/kb_text_shape_procs.odin b/vendor/kb_text_shape/kb_text_shape_procs.odin index a033402bc..9e6d02212 100644 --- a/vendor/kb_text_shape/kb_text_shape_procs.odin +++ b/vendor/kb_text_shape/kb_text_shape_procs.odin @@ -11,175 +11,314 @@ when ODIN_OS == .Windows { } } -import "core:mem" +import "core:c" + +// +// Context API +// The context can do everything for you. It is pretty convenient! +// +@(default_calling_convention="c", link_prefix="kbts_", require_results) +foreign lib { + SizeOfShapeContext :: proc() -> c.int --- + PlaceShapeContext :: proc(Allocator: allocator_function, AllocatorData: rawptr, Memory: rawptr) -> ^shape_context --- + PlaceShapeContextFixedMemory :: proc(Memory: rawptr, Size: c.int) -> ^shape_context --- + CreateShapeContext :: proc(Allocator: allocator_function, AllocatorData: rawptr) -> ^shape_context --- + DestroyShapeContext :: proc(Context: ^shape_context) --- + ShapePushFontFromMemory :: proc(Context: ^shape_context, Memory: rawptr, Size: c.int, FontIndex: c.int) -> ^font --- + ShapePushFont :: proc(Context: ^shape_context, Font: ^font) -> ^font --- + ShapePopFont :: proc(Context: ^shape_context) -> ^font --- + ShapeBegin :: proc(Context: ^shape_context, ParagraphDirection: direction, Language: language) --- + ShapeEnd :: proc(Context: ^shape_context) --- + ShapePushFeature :: proc(Context: ^shape_context, FeatureTag: u32, Value: c.int) --- + ShapePopFeature :: proc(Context: ^shape_context, FeatureTag: u32) -> b32 --- + ShapeCodepoint :: proc(Context: ^shape_context, Codepoint: rune) --- + ShapeCodepointWithUserId :: proc(Context: ^shape_context, Codepoint: rune, UserId: c.int) --- + ShapeError :: proc(Context: ^shape_context) -> shape_error --- + ShapeBeginManualRuns :: proc(Context: ^shape_context) --- + ShapeNextManualRun :: proc(Context: ^shape_context, Direction: direction, Script: script) --- + ShapeEndManualRuns :: proc(Context: ^shape_context) --- + ShapeManualBreak :: proc(Context: ^shape_context) --- +} + +@(require_results) +ShapeRun :: proc "contextless" (Context: ^shape_context) -> (Run: run, ok: b32) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + ShapeRun :: proc(Context: ^shape_context, Run: ^run) -> b32 --- + } + ok = ShapeRun(Context, &Run) + return +} + +ShapeUtf32 :: proc "c" (Context: ^shape_context, Utf32: []rune) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + ShapeUtf32 :: proc(Context: ^shape_context, Utf32: [^]rune, Length: c.int) --- + } + ShapeUtf32(Context, raw_data(Utf32), c.int(len(Utf32))) +} +ShapeUtf32WithUserId :: proc "c" (Context: ^shape_context, Utf32: []rune, UserId: c.int, UserIdIncrement: c.int) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + ShapeUtf32WithUserId :: proc(Context: ^shape_context, Utf32: [^]rune, Length: c.int, UserId: c.int, UserIdIncrement: c.int) --- + } + ShapeUtf32WithUserId(Context, raw_data(Utf32), c.int(len(Utf32)), UserId, UserIdIncrement) +} + +ShapeUtf8 :: proc(Context: ^shape_context, Utf8: string, UserIdGenerationMode: user_id_generation_mode) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + ShapeUtf8 :: proc(Context: ^shape_context, Utf8: [^]byte, Length: c.int, UserIdGenerationMode: user_id_generation_mode) --- + } + ShapeUtf8(Context, raw_data(Utf8), c.int(len(Utf8)), UserIdGenerationMode) +} +ShapeUtf8WithUserId :: proc(Context: ^shape_context, Utf8: string, UserId: c.int, UserIdGenerationMode: user_id_generation_mode) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + ShapeUtf8WithUserId :: proc(Context: ^shape_context, Utf8: [^]byte, Length: c.int, UserId: c.int, UserIdGenerationMode: user_id_generation_mode) --- + } + ShapeUtf8WithUserId(Context, raw_data(Utf8), c.int(len(Utf8)), UserId, UserIdGenerationMode) +} + @(default_calling_convention="c", link_prefix="kbts_", require_results) foreign lib { - FeatureTagToId :: proc(Tag: feature_tag) -> feature_id --- - FeatureOverride :: proc(Id: feature_id, Alternate: b32, Value: u32) -> feature_override --- - FeatureOverrideFromTag :: proc(Tag: feature_tag, Alternate: b32, Value: u32) -> feature_override --- - GlyphConfigOverrideFeature :: proc(Config: ^glyph_config, Id: feature_id, Alternate: b32, Value: u32) -> b32 --- - GlyphConfigOverrideFeatureFromTag :: proc(Config: ^glyph_config, Tag: feature_tag, Alternate: b32, Value: u32) -> b32 --- - - FontIsValid :: proc(Font: ^font) -> b32 --- - SizeOfShapeState :: proc(Font: ^font) -> un --- - - ResetShapeState :: proc(State: ^shape_state) --- - - ShapeConfig :: proc(Font: ^font, Script: script, Language: language) -> shape_config --- - ShaperIsComplex :: proc(Shaper: shaper) -> b32 --- - ScriptTagToScript :: proc(Tag: script_tag) -> script --- - - Shape :: proc(State: ^shape_state, Config: ^shape_config, - MainDirection, RunDirection: direction, - Glyphs: [^]glyph, GlyphCount: ^u32, GlyphCapacity: u32) -> b32 --- - - Cursor :: proc(Direction: direction) -> cursor --- - BeginBreak :: proc(State: ^break_state, MainDirection: direction, JapaneseLineBreakStyle: japanese_line_break_style) --- - BreakStateIsValid :: proc(State: ^break_state) -> b32 --- - BreakAddCodepoint :: proc(State: ^break_state, Codepoint: rune, PositionIncrement: u32, EndOfText: b32) --- - BreakFlush :: proc(State: ^break_state) --- - Break :: proc(State: ^break_state, Break: ^break_type) -> b32 --- - CodepointToGlyph :: proc(Font: ^font, Codepoint: rune) -> glyph --- - InferScript :: proc(Direction: ^direction, Script: ^script, GlyphScript: script) --- + ShapeCurrentCodepointsIterator :: proc(Context: ^shape_context) -> shape_codepoint_iterator --- + ShapeCodepointIteratorIsValid :: proc(It: ^shape_codepoint_iterator) -> b32 --- + ShapeGetShapeCodepoint :: proc(Context: ^shape_context, CodepointIndex: c.int, Codepoint: ^shape_codepoint) -> b32 --- } - @(require_results) -GlyphConfig :: proc "c" (FeatureOverrides: []feature_override) -> glyph_config { - @(default_calling_convention="c", require_results) +ShapeCodepointIteratorNext :: proc "contextless" (It: ^shape_codepoint_iterator) -> (Codepoint: shape_codepoint, CodepointIndex: c.int, ok: b32) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) foreign lib { - kbts_GlyphConfig :: proc(FeatureOverrides: [^]feature_override, FeatureOverrideCount: u32) -> glyph_config --- + ShapeCodepointIteratorNext :: proc(It: ^shape_codepoint_iterator, Codepoint: ^shape_codepoint, CodepointIndex: ^c.int) -> b32 --- } - return kbts_GlyphConfig(raw_data(FeatureOverrides), u32(len(FeatureOverrides))) - -} - -@(require_results) -EmptyGlyphConfig :: proc(FeatureOverrides: []feature_override) -> glyph_config { - @(default_calling_convention="c", require_results) - foreign lib { - kbts_EmptyGlyphConfig :: proc(FeatureOverrides: [^]feature_override, FeatureOverrideCapacity: u32) -> glyph_config --- - } - return kbts_EmptyGlyphConfig(raw_data(FeatureOverrides), u32(len(FeatureOverrides))) -} - -@(require_results) -PlaceShapeState :: proc "c" (Memory: []byte) -> ^shape_state { - @(default_calling_convention="c", require_results) - foreign lib { - kbts_PlaceShapeState :: proc(Address: rawptr, Size: un) -> ^shape_state --- - } - - return kbts_PlaceShapeState(raw_data(Memory), un(len(Memory))) -} - -@(require_results) -DecodeUtf8 :: proc "contextless" (String: string) -> (Codepoint: rune, SourceCharactersConsumed: u32, Valid: bool) { - decode :: struct { - Codepoint: rune, - - SourceCharactersConsumed: u32, - Valid: b32, - } - - @(default_calling_convention="c", require_results) - foreign lib { - kbts_DecodeUtf8 :: proc(Utf8: [^]byte, Length: un) -> decode --- - } - - Decode := kbts_DecodeUtf8(raw_data(String), un(len(String))) - return Decode.Codepoint, Decode.SourceCharactersConsumed, bool(Decode.Valid) -} - - -@(require_results) -ReadFontHeader :: proc "c" (Font: ^font, Data: []byte) -> un { - @(default_calling_convention="c", require_results) - foreign lib { - kbts_ReadFontHeader :: proc(Font: ^font, Data: rawptr, Size: un) -> un --- - } - - return kbts_ReadFontHeader(Font, raw_data(Data), un(len(Data))) -} -@(require_results) -ReadFontData :: proc "c" (Font: ^font, Scratch: []byte) -> un { - @(default_calling_convention="c", require_results) - foreign lib { - kbts_ReadFontData :: proc(Font: ^font, Scratch: rawptr, ScratchSize: un) -> un --- - } - - return kbts_ReadFontData(Font, raw_data(Scratch), un(len(Scratch))) -} -@(require_results) -PostReadFontInitialize :: proc "c" (Font: ^font, Memory: []byte) -> b32 { - @(default_calling_convention="c", require_results) - foreign lib { - kbts_PostReadFontInitialize :: proc(Font: ^font, Memory: rawptr, MemorySize: un) -> b32 --- - } - - return kbts_PostReadFontInitialize(Font, raw_data(Memory), un(len(Memory))) -} - -@(require_results) -FontFromMemory :: proc(Data: []byte, allocator: mem.Allocator) -> (Result: font, Err: mem.Allocator_Error) { - ClonedData := mem.make_aligned([]byte, len(Data), 16, allocator) or_return - defer if Err != nil { - delete(ClonedData, allocator) - } - copy(ClonedData, Data) - - ScratchSize := ReadFontHeader(&Result, ClonedData) - Scratch := mem.make_aligned([]byte, ScratchSize, 16, allocator) or_return - MemorySize := ReadFontData(&Result, Scratch) - - Memory := Scratch - if MemorySize > ScratchSize { - delete(Scratch, allocator) - Memory = mem.make_aligned([]byte, MemorySize, 16, allocator) or_return - } - defer if Err != nil { - delete(Memory, allocator) - } - - _ = PostReadFontInitialize(&Result, Memory) - return - -} -FreeFont :: proc(Font: ^font, allocator: mem.Allocator) { - free(Font.FileBase, allocator) - free(Font.GlyphLookupMatrix, allocator) - Font^ = {} -} - -@(require_results) -CreateShapeState :: proc(Font: ^font, allocator: mem.Allocator) -> (Result: ^shape_state, Err: mem.Allocator_Error) { - Size := SizeOfShapeState(Font) - Memory := mem.make_aligned([]byte, Size, 16, allocator) or_return - Result = PlaceShapeState(Memory) + ok = ShapeCodepointIteratorNext(It, &Codepoint, &CodepointIndex) return } -FreeShapeState :: proc(State: ^shape_state, allocator: mem.Allocator) { - free(State, allocator) + + + +// +// Direct API +// +@(default_calling_convention="c", link_prefix="kbts_", require_results) +foreign lib { + FontCount :: proc(FileData: rawptr, FileSize: c.int) -> b32 --- + FontFromMemory :: proc(FileData: rawptr, FileSize: c.int, FontIndex: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> font --- + FreeFont :: proc(Font: ^font) --- + FontIsValid :: proc(Font: ^font) -> b32 --- + LoadFont :: proc(Font: ^font, State: ^load_font_state, FontData: rawptr, FontDataSize: c.int, FontIndex: c.int, ScratchSize_: ^c.int, OutputSize_: ^c.int) -> load_font_error --- + PlaceBlob :: proc(Font: ^font, State: ^load_font_state, ScratchMemory: rawptr, OutputMemory: rawptr) -> load_font_error --- + GetFontInfo :: proc(Font: ^font, Info: ^font_info) --- + + // A shape_config is a bag of pre-computed data for a specific shaping setup. + SizeOfShapeConfig :: proc(Font: ^font, Script: script, Language: language) -> b32 --- + PlaceShapeConfig :: proc(Font: ^font, Script: script, Language: language, Memory: rawptr) -> ^shape_config --- + CreateShapeConfig :: proc(Font: ^font, Script: script, Language: language, Allocator: allocator_function, AllocatorData: rawptr) -> ^shape_config --- + DestroyShapeConfig :: proc(Config: ^shape_config) --- + + // A glyph_storage holds and recycles glyph data. + InitializeGlyphStorage :: proc(Storage: ^glyph_storage, Allocator: allocator_function, AllocatorData: rawptr) -> b32 --- + InitializeGlyphStorageFixedMemory :: proc(Storage: ^glyph_storage, Memory: rawptr, MemorySize: c.int) -> b32 --- + PushGlyph :: proc(Storage: ^glyph_storage, Font: ^font, Codepoint: rune, Config: ^glyph_config, UserId: c.int) -> ^glyph --- + ClearActiveGlyphs :: proc(Storage: ^glyph_storage) --- + FreeAllGlyphs :: proc(Storage: ^glyph_storage) --- + CodepointToGlyph :: proc(Font: ^font, Codepoint: rune, Config: ^glyph_config, UserId: c.int) -> glyph --- + CodepointToGlyphId :: proc(Font: ^font, Codepoint: rune) -> c.int --- + ActiveGlyphIterator :: proc(Storage: ^glyph_storage) -> glyph_iterator --- + + // A glyph_config specifies glyph-specific shaping parameters. + // A single glyph_config can be shared by multiple glyphs. + + DestroyGlyphConfig :: proc(Config: ^glyph_config) --- } + @(require_results) -PositionGlyph :: proc(Cursor: ^cursor, Glyph: ^glyph) -> (X, Y: i32) { - @(default_calling_convention="c", require_results) +ShapeDirect :: proc "contextless" (Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Allocator: allocator_function, AllocatorData: rawptr) -> (Output: glyph_iterator, err: shape_error) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) foreign lib { - kbts_PositionGlyph :: proc(Cursor: ^cursor, Glyph: ^glyph, X, Y: ^i32) --- + ShapeDirect :: proc(Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Allocator: allocator_function, AllocatorData: rawptr, Output: ^glyph_iterator) -> shape_error --- } - kbts_PositionGlyph(Cursor, Glyph, &X, &Y) + err = ShapeDirect(Config, Storage, RunDirection, Allocator, AllocatorData, &Output) return } @(require_results) -ShapeDynamic :: proc(State: ^shape_state, Config: ^shape_config, - MainDirection, RunDirection: direction, - Glyphs: ^[dynamic]glyph) -> b32 { - GlyphCount := u32(len(Glyphs^)) - GlyphCapacity := u32(cap(Glyphs^)) - Res := Shape(State, Config, MainDirection, RunDirection, raw_data(Glyphs^), &GlyphCount, GlyphCapacity) - resize(Glyphs, int(GlyphCount)) - return Res +ShapeDirectFixedMemory :: proc "contextless" (Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Memory: rawptr, MemorySize: c.int) -> (Output: glyph_iterator, err: shape_error) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + ShapeDirectFixedMemory :: proc(Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Memory: rawptr, MemorySize: c.int, Output: ^glyph_iterator) -> shape_error --- + } + err = ShapeDirectFixedMemory(Config, Storage, RunDirection, Memory, MemorySize, &Output) + return +} + + +@(require_results) +SizeOfGlyphConfig :: proc "c" (Overrides: []feature_override) -> c.int { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + SizeOfGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int) -> c.int --- + } + return SizeOfGlyphConfig(raw_data(Overrides), c.int(len(Overrides))) +} + +@(require_results) +PlaceGlyphConfig :: proc "c" (Overrides: []feature_override, Memory: rawptr) -> ^glyph_config { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + PlaceGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int, Memory: rawptr) -> ^glyph_config --- + } + return PlaceGlyphConfig(raw_data(Overrides), c.int(len(Overrides)), Memory) +} + +@(require_results) +CreateGlyphConfig :: proc(Overrides: []feature_override, Allocator: allocator_function, AllocatorData: rawptr) -> ^glyph_config { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + CreateGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> ^glyph_config --- + } + return CreateGlyphConfig(raw_data(Overrides), c.int(len(Overrides)), Allocator, AllocatorData) +} + +@(default_calling_convention="c", link_prefix="kbts_", require_results) +foreign lib { + GlyphIteratorIsValid :: proc(It: ^glyph_iterator) -> b32 --- +} + +@(require_results) +GlyphIteratorNext :: proc "contextless" (It: ^glyph_iterator) -> (Glyph: ^glyph, ok: b32) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + GlyphIteratorNext :: proc(It: ^glyph_iterator, Glyph: ^^glyph) -> b32 --- + } + ok = GlyphIteratorNext(It, &Glyph) + return +} + + +// +// Segmentation +// +@(default_calling_convention="c", link_prefix="kbts_", require_results) +foreign lib { + BreakBegin :: proc(State: ^break_state, ParagraphDirection: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags) --- + BreakAddCodepoint :: proc(State: ^break_state, Codepoint: rune, PositionIncrement: c.int, EndOfText: c.int) --- + BreakEnd :: proc(State: ^break_state) --- +} + +@(require_results) +Break :: proc "contextless" (State: ^break_state) -> (Break: break_type, ok: b32) { + @(default_calling_convention="c", require_results) + foreign lib { + kbts_Break :: proc(State: ^break_state, Break: ^break_type) -> b32 --- + } + ok = kbts_Break(State, &Break) + return +} + + +BreakEntireString :: proc "c" (Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Input: []byte, InputFormat: text_format, + Breaks: []break_type, BreakCount: ^c.int, + BreakFlags: []break_flags, BreakFlagCount: ^c.int) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + BreakEntireString :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Input: rawptr, InputSizeInBytes: c.int, InputFormat: text_format, + Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, + BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- + } + BreakEntireString(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Input), c.int(len(Input)), InputFormat, raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) +} + +BreakEntireStringUtf32 :: proc "c" (Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Utf32: []rune, + Breaks: []break_type, BreakCount: ^c.int, + BreakFlags: []break_flags, BreakFlagCount: ^c.int) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + BreakEntireStringUtf32 :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Utf32: [^]rune, Utf32Count: c.int, + Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, + BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- + } + BreakEntireStringUtf32(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Utf32), c.int(len(Utf32)), raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) +} + +BreakEntireStringUtf8 :: proc "c" (Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Utf8: string, + Breaks: []break_type, BreakCount: ^c.int, + BreakFlags: []break_flags, BreakFlagCount: ^c.int) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + BreakEntireStringUtf8 :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Utf8: [^]byte, Utf8Length: c.int, + Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, + BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- + } + BreakEntireStringUtf8(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Utf8), c.int(len(Utf8)), raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) +} + + + +@(default_calling_convention="c", link_prefix="kbts_", require_results) +foreign lib { + // Quick test for font support of a sequence of codepoints. + FontCoverageTestBegin :: proc(Test: ^font_coverage_test, Font: ^font) --- + FontCoverageTestCodepoint :: proc(Test: ^font_coverage_test, Codepoint: rune) --- + FontCoverageTestEnd :: proc(Test: ^font_coverage_test) -> b32 --- + + EncodeUtf8 :: proc(Codepoint: rune) -> encode_utf8 --- + ScriptDirection :: proc(Script: script) -> direction --- + ScriptIsComplex :: proc(Script: script) -> b32 --- + ScriptTagToScript :: proc(Tag: script_tag) -> script --- +} + +@(require_results) +DecodeUtf8 :: proc "c" (Utf8: string) -> decode { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + DecodeUtf8 :: proc(Utf8: [^]byte, Length: un) -> decode --- + } + return DecodeUtf8(raw_data(Utf8), un(len(Utf8))) +} + +// 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. +@(require_results) +GuessTextProperties :: proc "contextless" (Text: []byte, Format: text_format) -> (Direction: direction, Script: script) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + GuessTextProperties :: proc(Text: rawptr, TextSizeInBytes: c.int, Format: text_format, Direction: ^direction, Script: ^script) --- + } + GuessTextProperties(raw_data(Text), c.int(len(Text)), Format, &Direction, &Script) + return +} + +// 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. +@(require_results) +GuessTextPropertiesUtf32 :: proc "contextless" (Utf32: []rune) -> (Direction: direction, Script: script) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + GuessTextPropertiesUtf32 :: proc(Utf32: [^]rune, Utf32Count: c.int, Direction: ^direction, Script: ^script) --- + } + GuessTextPropertiesUtf32(raw_data(Utf32), c.int(len(Utf32)), &Direction, &Script) + return +} + +// 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._results) +@(require_results) +GuessTextPropertiesUtf8 :: proc "contextless" (Utf8: string) -> (Direction: direction, Script: script) { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + GuessTextPropertiesUtf8 :: proc(Utf8: cstring, Utf8Length: c.int, Direction: ^direction, Script: ^script) --- + } + GuessTextPropertiesUtf8(cstring(raw_data(Utf8)), c.int(len(Utf8)), &Direction, &Script) + return } \ No newline at end of file diff --git a/vendor/kb_text_shape/kb_text_shape_types.odin b/vendor/kb_text_shape/kb_text_shape_types.odin index 929af64d2..6c4465319 100644 --- a/vendor/kb_text_shape/kb_text_shape_types.odin +++ b/vendor/kb_text_shape/kb_text_shape_types.odin @@ -17,743 +17,693 @@ un :: distinct ( // i64 // ) -joining_feature :: enum u8 { - NONE, - ISOL, - FINA, - FIN2, - FIN3, - MEDI, - MED2, - INIT, -} - -reph_position :: enum u8 { - AFTER_POST, - BEFORE_POST, - BEFORE_SUBJOINED, - AFTER_SUBJOINED, - AFTER_MAIN, -} - -reph_encoding :: enum u8 { - IMPLICIT, - EXPLICIT, - LOGICAL_REPHA, - VISUAL_REPHA, -} - -syllabic_position :: enum u8 { - NONE, - - RA_TO_BECOME_REPH, - - PREBASE_MATRA, - PREBASE_CONSONANT, - - SYLLABLE_BASE, - AFTER_MAIN, - - ABOVEBASE_CONSONANT, - - BEFORE_SUBJOINED, - BELOWBASE_CONSONANT, - AFTER_SUBJOINED, - - BEFORE_POST, - POSTBASE_CONSONANT, - AFTER_POST, - - FINAL_CONSONANT, - SMVD, -} language :: enum u32 { DONT_KNOW = 0, - A_HMAO = ('H' | 'M'<<8 | 'D'<<16 | ' '<<24), - AARI = ('A' | 'R'<<8 | 'I'<<16 | ' '<<24), - ABAZA = ('A' | 'B'<<8 | 'A'<<16 | ' '<<24), - ABKHAZIAN = ('A' | 'B'<<8 | 'K'<<16 | ' '<<24), - ACHI = ('A' | 'C'<<8 | 'R'<<16 | ' '<<24), - ACHOLI = ('A' | 'C'<<8 | 'H'<<16 | ' '<<24), - ADYGHE = ('A' | 'D'<<8 | 'Y'<<16 | ' '<<24), - AFAR = ('A' | 'F'<<8 | 'R'<<16 | ' '<<24), - AFRIKAANS = ('A' | 'F'<<8 | 'K'<<16 | ' '<<24), - AGAW = ('A' | 'G'<<8 | 'W'<<16 | ' '<<24), - AITON = ('A' | 'I'<<8 | 'O'<<16 | ' '<<24), - AKAN = ('A' | 'K'<<8 | 'A'<<16 | ' '<<24), - ALBANIAN = ('S' | 'Q'<<8 | 'I'<<16 | ' '<<24), - ALSATIAN = ('A' | 'L'<<8 | 'S'<<16 | ' '<<24), - ALTAI = ('A' | 'L'<<8 | 'T'<<16 | ' '<<24), - ALUO = ('Y' | 'N'<<8 | 'A'<<16 | ' '<<24), - AMERICAN_PHONETIC = ('A' | 'P'<<8 | 'P'<<16 | 'H'<<24), - AMHARIC = ('A' | 'M'<<8 | 'H'<<16 | ' '<<24), - ANGLO_SAXON = ('A' | 'N'<<8 | 'G'<<16 | ' '<<24), - ARABIC = ('A' | 'R'<<8 | 'A'<<16 | ' '<<24), - ARAGONESE = ('A' | 'R'<<8 | 'G'<<16 | ' '<<24), - ARAKANESE = ('A' | 'R'<<8 | 'K'<<16 | ' '<<24), - ARAKWAL = ('R' | 'K'<<8 | 'W'<<16 | ' '<<24), - ARMENIAN = ('H' | 'Y'<<8 | 'E'<<16 | ' '<<24), - ARMENIAN_EAST = ('H' | 'Y'<<8 | 'E'<<16 | '0'<<24), - AROMANIAN = ('R' | 'U'<<8 | 'P'<<16 | ' '<<24), - ARPITAN = ('F' | 'R'<<8 | 'P'<<16 | ' '<<24), - ASSAMESE = ('A' | 'S'<<8 | 'M'<<16 | ' '<<24), - ASTURIAN = ('A' | 'S'<<8 | 'T'<<16 | ' '<<24), - ATHAPASKAN = ('A' | 'T'<<8 | 'H'<<16 | ' '<<24), - ATSINA = ('A' | 'T'<<8 | 'S'<<16 | ' '<<24), - AVAR = ('A' | 'V'<<8 | 'R'<<16 | ' '<<24), - AVATIME = ('A' | 'V'<<8 | 'N'<<16 | ' '<<24), - AWADHI = ('A' | 'W'<<8 | 'A'<<16 | ' '<<24), - AYMARA = ('A' | 'Y'<<8 | 'M'<<16 | ' '<<24), - AZERBAIDJANI = ('A' | 'Z'<<8 | 'E'<<16 | ' '<<24), - BADAGA = ('B' | 'A'<<8 | 'D'<<16 | ' '<<24), - BAGHELKHANDI = ('B' | 'A'<<8 | 'G'<<16 | ' '<<24), - BAGRI = ('B' | 'G'<<8 | 'Q'<<16 | ' '<<24), - BALANTE = ('B' | 'L'<<8 | 'N'<<16 | ' '<<24), - BALINESE = ('B' | 'A'<<8 | 'N'<<16 | ' '<<24), - BALKAR = ('B' | 'A'<<8 | 'L'<<16 | ' '<<24), - BALTI = ('B' | 'L'<<8 | 'T'<<16 | ' '<<24), - BALUCHI = ('B' | 'L'<<8 | 'I'<<16 | ' '<<24), - BAMBARA = ('B' | 'M'<<8 | 'B'<<16 | ' '<<24), - BAMILEKE = ('B' | 'M'<<8 | 'L'<<16 | ' '<<24), - BANDA = ('B' | 'A'<<8 | 'D'<<16 | '0'<<24), - BANDJALANG = ('B' | 'D'<<8 | 'Y'<<16 | ' '<<24), - BANGLA = ('B' | 'E'<<8 | 'N'<<16 | ' '<<24), - BASHKIR = ('B' | 'S'<<8 | 'H'<<16 | ' '<<24), - BASQUE = ('E' | 'U'<<8 | 'Q'<<16 | ' '<<24), - BATAK = ('B' | 'T'<<8 | 'K'<<16 | ' '<<24), - BATAK_ALAS_KLUET = ('B' | 'T'<<8 | 'Z'<<16 | ' '<<24), - BATAK_ANGKOLA = ('A' | 'K'<<8 | 'B'<<16 | ' '<<24), - BATAK_DAIRI = ('B' | 'T'<<8 | 'D'<<16 | ' '<<24), - BATAK_KARO = ('B' | 'T'<<8 | 'X'<<16 | ' '<<24), - BATAK_MANDAILING = ('B' | 'T'<<8 | 'M'<<16 | ' '<<24), - BATAK_SIMALUNGUN = ('B' | 'T'<<8 | 'S'<<16 | ' '<<24), - BATAK_TOBA = ('B' | 'B'<<8 | 'C'<<16 | ' '<<24), - BAULE = ('B' | 'A'<<8 | 'U'<<16 | ' '<<24), - BAVARIAN = ('B' | 'A'<<8 | 'R'<<16 | ' '<<24), - BELARUSIAN = ('B' | 'E'<<8 | 'L'<<16 | ' '<<24), - BEMBA = ('B' | 'E'<<8 | 'M'<<16 | ' '<<24), - BENCH = ('B' | 'C'<<8 | 'H'<<16 | ' '<<24), - BERBER = ('B' | 'B'<<8 | 'R'<<16 | ' '<<24), - BETI = ('B' | 'T'<<8 | 'I'<<16 | ' '<<24), - BETTE_KURUMA = ('X' | 'U'<<8 | 'B'<<16 | ' '<<24), - BHILI = ('B' | 'H'<<8 | 'I'<<16 | ' '<<24), - BHOJPURI = ('B' | 'H'<<8 | 'O'<<16 | ' '<<24), - BHUTANESE = ('D' | 'Z'<<8 | 'N'<<16 | ' '<<24), - BIBLE_CREE = ('B' | 'C'<<8 | 'R'<<16 | ' '<<24), - BIKOL = ('B' | 'I'<<8 | 'K'<<16 | ' '<<24), - BILEN = ('B' | 'I'<<8 | 'L'<<16 | ' '<<24), - BISHNUPRIYA_MANIPURI = ('B' | 'P'<<8 | 'Y'<<16 | ' '<<24), - BISLAMA = ('B' | 'I'<<8 | 'S'<<16 | ' '<<24), - BLACKFOOT = ('B' | 'K'<<8 | 'F'<<16 | ' '<<24), - BODO = ('B' | 'R'<<8 | 'X'<<16 | ' '<<24), - BOSNIAN = ('B' | 'O'<<8 | 'S'<<16 | ' '<<24), - BOUYEI = ('P' | 'C'<<8 | 'C'<<16 | ' '<<24), - BRAHUI = ('B' | 'R'<<8 | 'H'<<16 | ' '<<24), - BRAJ_BHASHA = ('B' | 'R'<<8 | 'I'<<16 | ' '<<24), - BRETON = ('B' | 'R'<<8 | 'E'<<16 | ' '<<24), - BUGIS = ('B' | 'U'<<8 | 'G'<<16 | ' '<<24), - BULGARIAN = ('B' | 'G'<<8 | 'R'<<16 | ' '<<24), - BUMTHANGKHA = ('K' | 'J'<<8 | 'Z'<<16 | ' '<<24), - BURMESE = ('B' | 'R'<<8 | 'M'<<16 | ' '<<24), - BURUSHASKI = ('B' | 'S'<<8 | 'K'<<16 | ' '<<24), - CAJUN_FRENCH = ('F' | 'R'<<8 | 'C'<<16 | ' '<<24), - CARRIER = ('C' | 'R'<<8 | 'R'<<16 | ' '<<24), - CATALAN = ('C' | 'A'<<8 | 'T'<<16 | ' '<<24), - CAYUGA = ('C' | 'A'<<8 | 'Y'<<16 | ' '<<24), - CEBUANO = ('C' | 'E'<<8 | 'B'<<16 | ' '<<24), - CENTRAL_YUPIK = ('E' | 'S'<<8 | 'U'<<16 | ' '<<24), - CHAHA_GURAGE = ('C' | 'H'<<8 | 'G'<<16 | ' '<<24), - CHAMORRO = ('C' | 'H'<<8 | 'A'<<16 | ' '<<24), - CHATTISGARHI = ('C' | 'H'<<8 | 'H'<<16 | ' '<<24), - CHECHEN = ('C' | 'H'<<8 | 'E'<<16 | ' '<<24), - CHEROKEE = ('C' | 'H'<<8 | 'R'<<16 | ' '<<24), - CHEYENNE = ('C' | 'H'<<8 | 'Y'<<16 | ' '<<24), - CHICHEWA = ('C' | 'H'<<8 | 'I'<<16 | ' '<<24), - CHIGA = ('C' | 'G'<<8 | 'G'<<16 | ' '<<24), - CHIMILA = ('C' | 'B'<<8 | 'G'<<16 | ' '<<24), - CHIN = ('Q' | 'I'<<8 | 'N'<<16 | ' '<<24), - CHINANTEC = ('C' | 'C'<<8 | 'H'<<16 | 'N'<<24), - CHINESE_PHONETIC = ('Z' | 'H'<<8 | 'P'<<16 | ' '<<24), - CHINESE_SIMPLIFIED = ('Z' | 'H'<<8 | 'S'<<16 | ' '<<24), - CHINESE_TRADITIONAL = ('Z' | 'H'<<8 | 'T'<<16 | ' '<<24), - CHINESE_TRADITIONAL_HONG_KONG = ('Z' | 'H'<<8 | 'H'<<16 | ' '<<24), - CHINESE_TRADITIONAL_MACAO = ('Z' | 'H'<<8 | 'T'<<16 | 'M'<<24), - CHIPEWYAN = ('C' | 'H'<<8 | 'P'<<16 | ' '<<24), - CHITTAGONIAN = ('C' | 'T'<<8 | 'G'<<16 | ' '<<24), - CHOCTAW = ('C' | 'H'<<8 | 'O'<<16 | ' '<<24), - CHUKCHI = ('C' | 'H'<<8 | 'K'<<16 | ' '<<24), - CHURCH_SLAVONIC = ('C' | 'S'<<8 | 'L'<<16 | ' '<<24), - CHUUKESE = ('C' | 'H'<<8 | 'K'<<16 | '0'<<24), - CHUVASH = ('C' | 'H'<<8 | 'U'<<16 | ' '<<24), - COMORIAN = ('C' | 'M'<<8 | 'R'<<16 | ' '<<24), - COMOX = ('C' | 'O'<<8 | 'O'<<16 | ' '<<24), - COPTIC = ('C' | 'O'<<8 | 'P'<<16 | ' '<<24), - CORNISH = ('C' | 'O'<<8 | 'R'<<16 | ' '<<24), - CORSICAN = ('C' | 'O'<<8 | 'S'<<16 | ' '<<24), - CREE = ('C' | 'R'<<8 | 'E'<<16 | ' '<<24), - CREOLES = ('C' | 'P'<<8 | 'P'<<16 | ' '<<24), - CRIMEAN_TATAR = ('C' | 'R'<<8 | 'T'<<16 | ' '<<24), - CRIOULO = ('K' | 'E'<<8 | 'A'<<16 | ' '<<24), - CROATIAN = ('H' | 'R'<<8 | 'V'<<16 | ' '<<24), - CYPRIOT_ARABIC = ('A' | 'C'<<8 | 'Y'<<16 | ' '<<24), - CZECH = ('C' | 'S'<<8 | 'Y'<<16 | ' '<<24), - DAGBANI = ('D' | 'A'<<8 | 'G'<<16 | ' '<<24), - DAN = ('D' | 'N'<<8 | 'J'<<16 | ' '<<24), - DANGME = ('D' | 'N'<<8 | 'G'<<16 | ' '<<24), - DANISH = ('D' | 'A'<<8 | 'N'<<16 | ' '<<24), - DARGWA = ('D' | 'A'<<8 | 'R'<<16 | ' '<<24), - DARI = ('D' | 'R'<<8 | 'I'<<16 | ' '<<24), - DAYI = ('D' | 'A'<<8 | 'X'<<16 | ' '<<24), - DEFAULT = ('d' | 'f'<<8 | 'l'<<16 | 't'<<24), // Can be DFLT too... - DEHONG_DAI = ('T' | 'D'<<8 | 'D'<<16 | ' '<<24), - DHANGU = ('D' | 'H'<<8 | 'G'<<16 | ' '<<24), - DHIVEHI = ('D' | 'I'<<8 | 'V'<<16 | ' '<<24), - DHUWAL = ('D' | 'U'<<8 | 'J'<<16 | ' '<<24), - DIMLI = ('D' | 'I'<<8 | 'Q'<<16 | ' '<<24), - DINKA = ('D' | 'N'<<8 | 'K'<<16 | ' '<<24), - DIVEHI = ('D' | 'I'<<8 | 'V'<<16 | ' '<<24), - DJAMBARRPUYNGU = ('D' | 'J'<<8 | 'R'<<16 | '0'<<24), - DOGRI = ('D' | 'G'<<8 | 'O'<<16 | ' '<<24), - DOGRI_MACROLANGUAGE = ('D' | 'G'<<8 | 'R'<<16 | ' '<<24), - DUNGAN = ('D' | 'U'<<8 | 'N'<<16 | ' '<<24), - DUTCH = ('N' | 'L'<<8 | 'D'<<16 | ' '<<24), - DZONGKHA = ('D' | 'Z'<<8 | 'N'<<16 | ' '<<24), - EASTERN_ABENAKI = ('A' | 'A'<<8 | 'Q'<<16 | ' '<<24), - EASTERN_CHAM = ('C' | 'J'<<8 | 'M'<<16 | ' '<<24), - EASTERN_CREE = ('E' | 'C'<<8 | 'R'<<16 | ' '<<24), - EASTERN_MANINKAKAN = ('E' | 'M'<<8 | 'K'<<16 | ' '<<24), - EASTERN_PWO_KAREN = ('K' | 'J'<<8 | 'P'<<16 | ' '<<24), - EBIRA = ('E' | 'B'<<8 | 'I'<<16 | ' '<<24), - EDO = ('E' | 'D'<<8 | 'O'<<16 | ' '<<24), - EFIK = ('E' | 'F'<<8 | 'I'<<16 | ' '<<24), - EMBERA_BAUDO = ('B' | 'D'<<8 | 'C'<<16 | ' '<<24), - EMBERA_CATIO = ('C' | 'T'<<8 | 'O'<<16 | ' '<<24), - EMBERA_CHAMI = ('C' | 'M'<<8 | 'I'<<16 | ' '<<24), - EMBERA_TADO = ('T' | 'D'<<8 | 'C'<<16 | ' '<<24), - ENGLISH = ('E' | 'N'<<8 | 'G'<<16 | ' '<<24), - EPENA = ('S' | 'J'<<8 | 'A'<<16 | ' '<<24), - ERZYA = ('E' | 'R'<<8 | 'Z'<<16 | ' '<<24), - KB_TEXT_SHAPEANTO = ('N' | 'T'<<8 | 'O'<<16 | ' '<<24), - ESTONIAN = ('E' | 'T'<<8 | 'I'<<16 | ' '<<24), - EVEN = ('E' | 'V'<<8 | 'N'<<16 | ' '<<24), - EVENKI = ('E' | 'V'<<8 | 'K'<<16 | ' '<<24), - EWE = ('E' | 'W'<<8 | 'E'<<16 | ' '<<24), - FALAM_CHIN = ('H' | 'A'<<8 | 'L'<<16 | ' '<<24), - FANG = ('F' | 'A'<<8 | 'N'<<16 | '0'<<24), - FANTI = ('F' | 'A'<<8 | 'T'<<16 | ' '<<24), - FAROESE = ('F' | 'O'<<8 | 'S'<<16 | ' '<<24), - FEFE = ('F' | 'M'<<8 | 'P'<<16 | ' '<<24), - FIJIAN = ('F' | 'J'<<8 | 'I'<<16 | ' '<<24), - FILIPINO = ('P' | 'I'<<8 | 'L'<<16 | ' '<<24), - FINNISH = ('F' | 'I'<<8 | 'N'<<16 | ' '<<24), - FLEMISH = ('F' | 'L'<<8 | 'E'<<16 | ' '<<24), - FON = ('F' | 'O'<<8 | 'N'<<16 | ' '<<24), - FOREST_ENETS = ('F' | 'N'<<8 | 'E'<<16 | ' '<<24), - FRENCH = ('F' | 'R'<<8 | 'A'<<16 | ' '<<24), - FRENCH_ANTILLEAN = ('F' | 'A'<<8 | 'N'<<16 | ' '<<24), - FRISIAN = ('F' | 'R'<<8 | 'I'<<16 | ' '<<24), - FRIULIAN = ('F' | 'R'<<8 | 'L'<<16 | ' '<<24), - FULAH = ('F' | 'U'<<8 | 'L'<<16 | ' '<<24), - FUTA = ('F' | 'T'<<8 | 'A'<<16 | ' '<<24), - GA = ('G' | 'A'<<8 | 'D'<<16 | ' '<<24), - GAGAUZ = ('G' | 'A'<<8 | 'G'<<16 | ' '<<24), - GALICIAN = ('G' | 'A'<<8 | 'L'<<16 | ' '<<24), - GANDA = ('L' | 'U'<<8 | 'G'<<16 | ' '<<24), - GARHWALI = ('G' | 'A'<<8 | 'W'<<16 | ' '<<24), - GARO = ('G' | 'R'<<8 | 'O'<<16 | ' '<<24), - GARSHUNI = ('G' | 'A'<<8 | 'R'<<16 | ' '<<24), - GEBA_KAREN = ('K' | 'V'<<8 | 'Q'<<16 | ' '<<24), - GEEZ = ('G' | 'E'<<8 | 'Z'<<16 | ' '<<24), - GEORGIAN = ('K' | 'A'<<8 | 'T'<<16 | ' '<<24), - GEPO = ('Y' | 'G'<<8 | 'P'<<16 | ' '<<24), - GERMAN = ('D' | 'E'<<8 | 'U'<<16 | ' '<<24), - GIKUYU = ('K' | 'I'<<8 | 'K'<<16 | ' '<<24), - GILAKI = ('G' | 'L'<<8 | 'K'<<16 | ' '<<24), - GILBERTESE = ('G' | 'I'<<8 | 'L'<<16 | '0'<<24), - GILYAK = ('G' | 'I'<<8 | 'L'<<16 | ' '<<24), - GITHABUL = ('G' | 'I'<<8 | 'H'<<16 | ' '<<24), - GOGO = ('G' | 'O'<<8 | 'G'<<16 | ' '<<24), - GONDI = ('G' | 'O'<<8 | 'N'<<16 | ' '<<24), - GREEK = ('E' | 'L'<<8 | 'L'<<16 | ' '<<24), - GREENLANDIC = ('G' | 'R'<<8 | 'N'<<16 | ' '<<24), - GUARANI = ('G' | 'U'<<8 | 'A'<<16 | ' '<<24), - GUINEA = ('G' | 'K'<<8 | 'P'<<16 | ' '<<24), - GUJARATI = ('G' | 'U'<<8 | 'J'<<16 | ' '<<24), - GUMATJ = ('G' | 'N'<<8 | 'N'<<16 | ' '<<24), - GUMUZ = ('G' | 'M'<<8 | 'Z'<<16 | ' '<<24), - GUPAPUYNGU = ('G' | 'U'<<8 | 'F'<<16 | ' '<<24), - GUSII = ('G' | 'U'<<8 | 'Z'<<16 | ' '<<24), - HAIDA = ('H' | 'A'<<8 | 'I'<<16 | '0'<<24), - HAITIAN_CREOLE = ('H' | 'A'<<8 | 'I'<<16 | ' '<<24), - HALKOMELEM = ('H' | 'U'<<8 | 'R'<<16 | ' '<<24), - HAMMER_BANNA = ('H' | 'B'<<8 | 'N'<<16 | ' '<<24), - HARARI = ('H' | 'R'<<8 | 'I'<<16 | ' '<<24), - HARAUTI = ('H' | 'A'<<8 | 'R'<<16 | ' '<<24), - HARYANVI = ('B' | 'G'<<8 | 'C'<<16 | ' '<<24), - HAUSA = ('H' | 'A'<<8 | 'U'<<16 | ' '<<24), - HAVASUPAI_WALAPAI_YAVAPAI = ('Y' | 'U'<<8 | 'F'<<16 | ' '<<24), - HAWAIIAN = ('H' | 'A'<<8 | 'W'<<16 | ' '<<24), - HAYA = ('H' | 'A'<<8 | 'Y'<<16 | ' '<<24), - HAZARAGI = ('H' | 'A'<<8 | 'Z'<<16 | ' '<<24), - HEBREW = ('I' | 'W'<<8 | 'R'<<16 | ' '<<24), - HEILTSUK = ('H' | 'E'<<8 | 'I'<<16 | ' '<<24), - HERERO = ('H' | 'E'<<8 | 'R'<<16 | ' '<<24), - HIGH_MARI = ('H' | 'M'<<8 | 'A'<<16 | ' '<<24), - HILIGAYNON = ('H' | 'I'<<8 | 'L'<<16 | ' '<<24), - HINDI = ('H' | 'I'<<8 | 'N'<<16 | ' '<<24), - HINDKO = ('H' | 'N'<<8 | 'D'<<16 | ' '<<24), - HIRI_MOTU = ('H' | 'M'<<8 | 'O'<<16 | ' '<<24), - HMONG = ('H' | 'M'<<8 | 'N'<<16 | ' '<<24), - HMONG_DAW = ('M' | 'W'<<8 | 'W'<<16 | ' '<<24), - HMONG_SHUAT = ('H' | 'M'<<8 | 'Z'<<16 | ' '<<24), - HO = ('H' | 'O'<<8 | ' '<<16 | ' '<<24), - HUNGARIAN = ('H' | 'U'<<8 | 'N'<<16 | ' '<<24), - IBAN = ('I' | 'B'<<8 | 'A'<<16 | ' '<<24), - IBIBIO = ('I' | 'B'<<8 | 'B'<<16 | ' '<<24), - ICELANDIC = ('I' | 'S'<<8 | 'L'<<16 | ' '<<24), - IDO = ('I' | 'D'<<8 | 'O'<<16 | ' '<<24), - IGBO = ('I' | 'B'<<8 | 'O'<<16 | ' '<<24), - IJO = ('I' | 'J'<<8 | 'O'<<16 | ' '<<24), - ILOKANO = ('I' | 'L'<<8 | 'O'<<16 | ' '<<24), - INARI_SAMI = ('I' | 'S'<<8 | 'M'<<16 | ' '<<24), - INDONESIAN = ('I' | 'N'<<8 | 'D'<<16 | ' '<<24), - INGUSH = ('I' | 'N'<<8 | 'G'<<16 | ' '<<24), - INTERLINGUA = ('I' | 'N'<<8 | 'A'<<16 | ' '<<24), - INTERLINGUE = ('I' | 'L'<<8 | 'E'<<16 | ' '<<24), - INUKTITUT = ('I' | 'N'<<8 | 'U'<<16 | ' '<<24), - INUPIAT = ('I' | 'P'<<8 | 'K'<<16 | ' '<<24), - IPA_PHONETIC = ('I' | 'P'<<8 | 'P'<<16 | ' '<<24), - IRISH = ('I' | 'R'<<8 | 'I'<<16 | ' '<<24), - IRISH_TRADITIONAL = ('I' | 'R'<<8 | 'T'<<16 | ' '<<24), - IRULA = ('I' | 'R'<<8 | 'U'<<16 | ' '<<24), - ITALIAN = ('I' | 'T'<<8 | 'A'<<16 | ' '<<24), - JAMAICAN_CREOLE = ('J' | 'A'<<8 | 'M'<<16 | ' '<<24), - JAPANESE = ('J' | 'A'<<8 | 'N'<<16 | ' '<<24), - JAVANESE = ('J' | 'A'<<8 | 'V'<<16 | ' '<<24), - JENNU_KURUMA = ('X' | 'U'<<8 | 'J'<<16 | ' '<<24), - JUDEO_TAT = ('J' | 'D'<<8 | 'T'<<16 | ' '<<24), - JULA = ('J' | 'U'<<8 | 'L'<<16 | ' '<<24), - KABARDIAN = ('K' | 'A'<<8 | 'B'<<16 | ' '<<24), - KABYLE = ('K' | 'A'<<8 | 'B'<<16 | '0'<<24), - KACHCHI = ('K' | 'A'<<8 | 'C'<<16 | ' '<<24), - KADIWEU = ('K' | 'B'<<8 | 'C'<<16 | ' '<<24), - KALENJIN = ('K' | 'A'<<8 | 'L'<<16 | ' '<<24), - KALMYK = ('K' | 'L'<<8 | 'M'<<16 | ' '<<24), - KAMBA = ('K' | 'M'<<8 | 'B'<<16 | ' '<<24), - KANAUJI = ('B' | 'J'<<8 | 'J'<<16 | ' '<<24), - KANNADA = ('K' | 'A'<<8 | 'N'<<16 | ' '<<24), - KANURI = ('K' | 'N'<<8 | 'R'<<16 | ' '<<24), - KAQCHIKEL = ('C' | 'A'<<8 | 'K'<<16 | ' '<<24), - KARACHAY = ('K' | 'A'<<8 | 'R'<<16 | ' '<<24), - KARAIM = ('K' | 'R'<<8 | 'M'<<16 | ' '<<24), - KARAKALPAK = ('K' | 'R'<<8 | 'K'<<16 | ' '<<24), - KARELIAN = ('K' | 'R'<<8 | 'L'<<16 | ' '<<24), - KAREN = ('K' | 'R'<<8 | 'N'<<16 | ' '<<24), - KASHMIRI = ('K' | 'S'<<8 | 'H'<<16 | ' '<<24), - KASHUBIAN = ('C' | 'S'<<8 | 'B'<<16 | ' '<<24), - KATE = ('K' | 'M'<<8 | 'G'<<16 | ' '<<24), - KAZAKH = ('K' | 'A'<<8 | 'Z'<<16 | ' '<<24), - KEBENA = ('K' | 'E'<<8 | 'B'<<16 | ' '<<24), - KEKCHI = ('K' | 'E'<<8 | 'K'<<16 | ' '<<24), - KHAKASS = ('K' | 'H'<<8 | 'A'<<16 | ' '<<24), - KHAMTI_SHAN = ('K' | 'H'<<8 | 'T'<<16 | ' '<<24), - KHAMYANG = ('K' | 'S'<<8 | 'U'<<16 | ' '<<24), - KHANTY_KAZIM = ('K' | 'H'<<8 | 'K'<<16 | ' '<<24), - KHANTY_SHURISHKAR = ('K' | 'H'<<8 | 'S'<<16 | ' '<<24), - KHANTY_VAKHI = ('K' | 'H'<<8 | 'V'<<16 | ' '<<24), - KHASI = ('K' | 'S'<<8 | 'I'<<16 | ' '<<24), - KHENGKHA = ('X' | 'K'<<8 | 'F'<<16 | ' '<<24), - KHINALUG = ('K' | 'J'<<8 | 'J'<<16 | ' '<<24), - KHMER = ('K' | 'H'<<8 | 'M'<<16 | ' '<<24), - KHORASANI_TURKIC = ('K' | 'M'<<8 | 'Z'<<16 | ' '<<24), - KHOWAR = ('K' | 'H'<<8 | 'W'<<16 | ' '<<24), - KHUTSURI_GEORGIAN = ('K' | 'G'<<8 | 'E'<<16 | ' '<<24), - KICHE = ('Q' | 'U'<<8 | 'C'<<16 | ' '<<24), - KIKONGO = ('K' | 'O'<<8 | 'N'<<16 | ' '<<24), - KILDIN_SAMI = ('K' | 'S'<<8 | 'M'<<16 | ' '<<24), - KINYARWANDA = ('R' | 'U'<<8 | 'A'<<16 | ' '<<24), - KIRMANJKI = ('K' | 'I'<<8 | 'U'<<16 | ' '<<24), - KISII = ('K' | 'I'<<8 | 'S'<<16 | ' '<<24), - KITUBA = ('M' | 'K'<<8 | 'W'<<16 | ' '<<24), - KODAGU = ('K' | 'O'<<8 | 'D'<<16 | ' '<<24), - KOKNI = ('K' | 'K'<<8 | 'N'<<16 | ' '<<24), - KOMI = ('K' | 'O'<<8 | 'M'<<16 | ' '<<24), - KOMI_PERMYAK = ('K' | 'O'<<8 | 'P'<<16 | ' '<<24), - KOMI_ZYRIAN = ('K' | 'O'<<8 | 'Z'<<16 | ' '<<24), - KOMO = ('K' | 'M'<<8 | 'O'<<16 | ' '<<24), - KOMSO = ('K' | 'M'<<8 | 'S'<<16 | ' '<<24), - KONGO = ('K' | 'O'<<8 | 'N'<<16 | '0'<<24), - KONKANI = ('K' | 'O'<<8 | 'K'<<16 | ' '<<24), - KOORETE = ('K' | 'R'<<8 | 'T'<<16 | ' '<<24), - KOREAN = ('K' | 'O'<<8 | 'R'<<16 | ' '<<24), - KOREAO_OLD_HANGUL = ('K' | 'O'<<8 | 'H'<<16 | ' '<<24), - KORYAK = ('K' | 'Y'<<8 | 'K'<<16 | ' '<<24), - KOSRAEAN = ('K' | 'O'<<8 | 'S'<<16 | ' '<<24), - KPELLE = ('K' | 'P'<<8 | 'L'<<16 | ' '<<24), - KPELLE_LIBERIA = ('X' | 'P'<<8 | 'E'<<16 | ' '<<24), - KRIO = ('K' | 'R'<<8 | 'I'<<16 | ' '<<24), - KRYMCHAK = ('J' | 'C'<<8 | 'T'<<16 | ' '<<24), - KUANYAMA = ('K' | 'U'<<8 | 'A'<<16 | ' '<<24), - KUBE = ('K' | 'G'<<8 | 'F'<<16 | ' '<<24), - KUI = ('K' | 'U'<<8 | 'I'<<16 | ' '<<24), - KULVI = ('K' | 'U'<<8 | 'K'<<16 | ' '<<24), - KUMAONI = ('K' | 'M'<<8 | 'N'<<16 | ' '<<24), - KUMYK = ('K' | 'U'<<8 | 'M'<<16 | ' '<<24), - KURDISH = ('K' | 'U'<<8 | 'R'<<16 | ' '<<24), - KURUKH = ('K' | 'U'<<8 | 'U'<<16 | ' '<<24), - KUY = ('K' | 'U'<<8 | 'Y'<<16 | ' '<<24), - KWAKWALA = ('K' | 'W'<<8 | 'K'<<16 | ' '<<24), - KYRGYZ = ('K' | 'I'<<8 | 'R'<<16 | ' '<<24), - L_CREE = ('L' | 'C'<<8 | 'R'<<16 | ' '<<24), - LADAKHI = ('L' | 'D'<<8 | 'K'<<16 | ' '<<24), - LADIN = ('L' | 'A'<<8 | 'D'<<16 | ' '<<24), - LADINO = ('J' | 'U'<<8 | 'D'<<16 | ' '<<24), - LAHULI = ('L' | 'A'<<8 | 'H'<<16 | ' '<<24), - LAK = ('L' | 'A'<<8 | 'K'<<16 | ' '<<24), - LAKI = ('L' | 'K'<<8 | 'I'<<16 | ' '<<24), - LAMBANI = ('L' | 'A'<<8 | 'M'<<16 | ' '<<24), - LAMPUNG = ('L' | 'J'<<8 | 'P'<<16 | ' '<<24), - LAO = ('L' | 'A'<<8 | 'O'<<16 | ' '<<24), - LATIN = ('L' | 'A'<<8 | 'T'<<16 | ' '<<24), - LATVIAN = ('L' | 'V'<<8 | 'I'<<16 | ' '<<24), - LAZ = ('L' | 'A'<<8 | 'Z'<<16 | ' '<<24), - LELEMI = ('L' | 'E'<<8 | 'F'<<16 | ' '<<24), - LEZGI = ('L' | 'E'<<8 | 'Z'<<16 | ' '<<24), - LIGURIAN = ('L' | 'I'<<8 | 'J'<<16 | ' '<<24), - LIMBU = ('L' | 'M'<<8 | 'B'<<16 | ' '<<24), - LIMBURGISH = ('L' | 'I'<<8 | 'M'<<16 | ' '<<24), - LINGALA = ('L' | 'I'<<8 | 'N'<<16 | ' '<<24), - LIPO = ('L' | 'P'<<8 | 'O'<<16 | ' '<<24), - LISU = ('L' | 'I'<<8 | 'S'<<16 | ' '<<24), - LITHUANIAN = ('L' | 'T'<<8 | 'H'<<16 | ' '<<24), - LIV = ('L' | 'I'<<8 | 'V'<<16 | ' '<<24), - LOJBAN = ('J' | 'B'<<8 | 'O'<<16 | ' '<<24), - LOMA = ('L' | 'O'<<8 | 'M'<<16 | ' '<<24), - LOMBARD = ('L' | 'M'<<8 | 'O'<<16 | ' '<<24), - LOMWE = ('L' | 'M'<<8 | 'W'<<16 | ' '<<24), - LOW_MARI = ('L' | 'M'<<8 | 'A'<<16 | ' '<<24), - LOW_SAXON = ('N' | 'D'<<8 | 'S'<<16 | ' '<<24), - LOWER_SORBIAN = ('L' | 'S'<<8 | 'B'<<16 | ' '<<24), - LU = ('X' | 'B'<<8 | 'D'<<16 | ' '<<24), - LUBA_KATANGA = ('L' | 'U'<<8 | 'B'<<16 | ' '<<24), - LUBA_LULUA = ('L' | 'U'<<8 | 'A'<<16 | ' '<<24), - LULE_SAMI = ('L' | 'S'<<8 | 'M'<<16 | ' '<<24), - LUO = ('L' | 'U'<<8 | 'O'<<16 | ' '<<24), - LURI = ('L' | 'R'<<8 | 'C'<<16 | ' '<<24), - LUSHOOTSEED = ('L' | 'U'<<8 | 'T'<<16 | ' '<<24), - LUXEMBOURGISH = ('L' | 'T'<<8 | 'Z'<<16 | ' '<<24), - LUYIA = ('L' | 'U'<<8 | 'H'<<16 | ' '<<24), - MACEDONIAN = ('M' | 'K'<<8 | 'D'<<16 | ' '<<24), - MADURA = ('M' | 'A'<<8 | 'D'<<16 | ' '<<24), - MAGAHI = ('M' | 'A'<<8 | 'G'<<16 | ' '<<24), - MAITHILI = ('M' | 'T'<<8 | 'H'<<16 | ' '<<24), - MAJANG = ('M' | 'A'<<8 | 'J'<<16 | ' '<<24), - MAKASAR = ('M' | 'K'<<8 | 'R'<<16 | ' '<<24), - MAKHUWA = ('M' | 'A'<<8 | 'K'<<16 | ' '<<24), - MAKONDE = ('K' | 'D'<<8 | 'E'<<16 | ' '<<24), - MALAGASY = ('M' | 'L'<<8 | 'G'<<16 | ' '<<24), - MALAY = ('M' | 'L'<<8 | 'Y'<<16 | ' '<<24), - MALAYALAM = ('M' | 'A'<<8 | 'L'<<16 | ' '<<24), - MALAYALAM_REFORMED = ('M' | 'L'<<8 | 'R'<<16 | ' '<<24), - MALE = ('M' | 'L'<<8 | 'E'<<16 | ' '<<24), - MALINKE = ('M' | 'L'<<8 | 'N'<<16 | ' '<<24), - MALTESE = ('M' | 'T'<<8 | 'S'<<16 | ' '<<24), - MAM = ('M' | 'A'<<8 | 'M'<<16 | ' '<<24), - MANCHU = ('M' | 'C'<<8 | 'H'<<16 | ' '<<24), - MANDAR = ('M' | 'D'<<8 | 'R'<<16 | ' '<<24), - MANDINKA = ('M' | 'N'<<8 | 'D'<<16 | ' '<<24), - MANINKA = ('M' | 'N'<<8 | 'K'<<16 | ' '<<24), - MANIPURI = ('M' | 'N'<<8 | 'I'<<16 | ' '<<24), - MANO = ('M' | 'E'<<8 | 'V'<<16 | ' '<<24), - MANSI = ('M' | 'A'<<8 | 'N'<<16 | ' '<<24), - MANX = ('M' | 'N'<<8 | 'X'<<16 | ' '<<24), - MAORI = ('M' | 'R'<<8 | 'I'<<16 | ' '<<24), - MAPUDUNGUN = ('M' | 'A'<<8 | 'P'<<16 | ' '<<24), - MARATHI = ('M' | 'A'<<8 | 'R'<<16 | ' '<<24), - MARSHALLESE = ('M' | 'A'<<8 | 'H'<<16 | ' '<<24), - MARWARI = ('M' | 'A'<<8 | 'W'<<16 | ' '<<24), - MAYAN = ('M' | 'Y'<<8 | 'N'<<16 | ' '<<24), - MAZANDERANI = ('M' | 'Z'<<8 | 'N'<<16 | ' '<<24), - MBEMBE_TIGON = ('N' | 'Z'<<8 | 'A'<<16 | ' '<<24), - MBO = ('M' | 'B'<<8 | 'O'<<16 | ' '<<24), - MBUNDU = ('M' | 'B'<<8 | 'N'<<16 | ' '<<24), - MEDUMBA = ('B' | 'Y'<<8 | 'V'<<16 | ' '<<24), - MEEN = ('M' | 'E'<<8 | 'N'<<16 | ' '<<24), - MENDE = ('M' | 'D'<<8 | 'E'<<16 | ' '<<24), - MERU = ('M' | 'E'<<8 | 'R'<<16 | ' '<<24), - MEWATI = ('W' | 'T'<<8 | 'M'<<16 | ' '<<24), - MINANGKABAU = ('M' | 'I'<<8 | 'N'<<16 | ' '<<24), - MINJANGBAL = ('X' | 'J'<<8 | 'B'<<16 | ' '<<24), - MIRANDESE = ('M' | 'W'<<8 | 'L'<<16 | ' '<<24), - MIZO = ('M' | 'I'<<8 | 'Z'<<16 | ' '<<24), - MOHAWK = ('M' | 'O'<<8 | 'H'<<16 | ' '<<24), - MOKSHA = ('M' | 'O'<<8 | 'K'<<16 | ' '<<24), - MOLDAVIAN = ('M' | 'O'<<8 | 'L'<<16 | ' '<<24), - MON = ('M' | 'O'<<8 | 'N'<<16 | ' '<<24), - MONGOLIAN = ('M' | 'N'<<8 | 'G'<<16 | ' '<<24), - MOOSE_CREE = ('M' | 'C'<<8 | 'R'<<16 | ' '<<24), - MORISYEN = ('M' | 'F'<<8 | 'E'<<16 | ' '<<24), - MOROCCAN = ('M' | 'O'<<8 | 'R'<<16 | ' '<<24), - MOSSI = ('M' | 'P'<<8 | 'S'<<16 | ' '<<24), - MUNDARI = ('M' | 'U'<<8 | 'N'<<16 | ' '<<24), - MUSCOGEE = ('M' | 'U'<<8 | 'S'<<16 | ' '<<24), - N_CREE = ('N' | 'C'<<8 | 'R'<<16 | ' '<<24), - NAGA_ASSAMESE = ('N' | 'A'<<8 | 'G'<<16 | ' '<<24), - NAGARI = ('N' | 'G'<<8 | 'R'<<16 | ' '<<24), - NAHUATL = ('N' | 'A'<<8 | 'H'<<16 | ' '<<24), - NANAI = ('N' | 'A'<<8 | 'N'<<16 | ' '<<24), - NASKAPI = ('N' | 'A'<<8 | 'S'<<16 | ' '<<24), - NAURUAN = ('N' | 'A'<<8 | 'U'<<16 | ' '<<24), - NAVAJO = ('N' | 'A'<<8 | 'V'<<16 | ' '<<24), - NDAU = ('N' | 'D'<<8 | 'C'<<16 | ' '<<24), - NDEBELE = ('N' | 'D'<<8 | 'B'<<16 | ' '<<24), - NDONGA = ('N' | 'D'<<8 | 'G'<<16 | ' '<<24), - NEAPOLITAN = ('N' | 'A'<<8 | 'P'<<16 | ' '<<24), - NEPALI = ('N' | 'E'<<8 | 'P'<<16 | ' '<<24), - NEWARI = ('N' | 'E'<<8 | 'W'<<16 | ' '<<24), - NGBAKA = ('N' | 'G'<<8 | 'A'<<16 | ' '<<24), - NIGERIAN_FULFULDE = ('F' | 'U'<<8 | 'V'<<16 | ' '<<24), - NIMADI = ('N' | 'O'<<8 | 'E'<<16 | ' '<<24), - NISI = ('N' | 'I'<<8 | 'S'<<16 | ' '<<24), - NIUEAN = ('N' | 'I'<<8 | 'U'<<16 | ' '<<24), - NKO = ('N' | 'K'<<8 | 'O'<<16 | ' '<<24), - NOGAI = ('N' | 'O'<<8 | 'G'<<16 | ' '<<24), - NORFOLK = ('P' | 'I'<<8 | 'H'<<16 | ' '<<24), - NORTH_SLAVEY = ('S' | 'C'<<8 | 'S'<<16 | ' '<<24), - NORTHERN_EMBERA = ('E' | 'M'<<8 | 'P'<<16 | ' '<<24), - NORTHERN_SAMI = ('N' | 'S'<<8 | 'M'<<16 | ' '<<24), - NORTHERN_SOTHO = ('N' | 'S'<<8 | 'O'<<16 | ' '<<24), - NORTHERN_TAI = ('N' | 'T'<<8 | 'A'<<16 | ' '<<24), - NORWAY_HOUSE_CREE = ('N' | 'H'<<8 | 'C'<<16 | ' '<<24), - NORWEGIAN = ('N' | 'O'<<8 | 'R'<<16 | ' '<<24), - NORWEGIAN_NYNORSK = ('N' | 'Y'<<8 | 'N'<<16 | ' '<<24), - NOVIAL = ('N' | 'O'<<8 | 'V'<<16 | ' '<<24), - NUMANGGANG = ('N' | 'O'<<8 | 'P'<<16 | ' '<<24), - NUNAVIK_INUKTITUT = ('I' | 'N'<<8 | 'U'<<16 | ' '<<24), - NUU_CHAH_NULTH = ('N' | 'U'<<8 | 'K'<<16 | ' '<<24), - NYAMWEZI = ('N' | 'Y'<<8 | 'M'<<16 | ' '<<24), - NYANKOLE = ('N' | 'K'<<8 | 'L'<<16 | ' '<<24), - OCCITAN = ('O' | 'C'<<8 | 'I'<<16 | ' '<<24), - ODIA = ('O' | 'R'<<8 | 'I'<<16 | ' '<<24), - OJI_CREE = ('O' | 'C'<<8 | 'R'<<16 | ' '<<24), - OJIBWAY = ('O' | 'J'<<8 | 'B'<<16 | ' '<<24), - OLD_IRISH = ('S' | 'G'<<8 | 'A'<<16 | ' '<<24), - OLD_JAVANESE = ('K' | 'A'<<8 | 'W'<<16 | ' '<<24), - ONEIDA = ('O' | 'N'<<8 | 'E'<<16 | ' '<<24), - ONONDAGA = ('O' | 'N'<<8 | 'O'<<16 | ' '<<24), - OROMO = ('O' | 'R'<<8 | 'O'<<16 | ' '<<24), - OSSETIAN = ('O' | 'S'<<8 | 'S'<<16 | ' '<<24), - PA_O_KAREN = ('B' | 'L'<<8 | 'K'<<16 | ' '<<24), - PALAUAN = ('P' | 'A'<<8 | 'U'<<16 | ' '<<24), - PALAUNG = ('P' | 'L'<<8 | 'G'<<16 | ' '<<24), - PALESTINIAN_ARAMAIC = ('P' | 'A'<<8 | 'A'<<16 | ' '<<24), - PALI = ('P' | 'A'<<8 | 'L'<<16 | ' '<<24), - PALPA = ('P' | 'A'<<8 | 'P'<<16 | ' '<<24), - PAMPANGAN = ('P' | 'A'<<8 | 'M'<<16 | ' '<<24), - PANGASINAN = ('P' | 'A'<<8 | 'G'<<16 | ' '<<24), - PAPIAMENTU = ('P' | 'A'<<8 | 'P'<<16 | '0'<<24), - PASHTO = ('P' | 'A'<<8 | 'S'<<16 | ' '<<24), - PATTANI_MALAY = ('M' | 'F'<<8 | 'A'<<16 | ' '<<24), - PENNSYLVANIA_GERMAN = ('P' | 'D'<<8 | 'C'<<16 | ' '<<24), - PERSIAN = ('F' | 'A'<<8 | 'R'<<16 | ' '<<24), - PHAKE = ('P' | 'J'<<8 | 'K'<<16 | ' '<<24), - PICARD = ('P' | 'C'<<8 | 'D'<<16 | ' '<<24), - PIEMONTESE = ('P' | 'M'<<8 | 'S'<<16 | ' '<<24), - PILAGA = ('P' | 'L'<<8 | 'G'<<16 | ' '<<24), - PITE_SAMI = ('S' | 'J'<<8 | 'E'<<16 | ' '<<24), - POCOMCHI = ('P' | 'O'<<8 | 'H'<<16 | ' '<<24), - POHNPEIAN = ('P' | 'O'<<8 | 'N'<<16 | ' '<<24), - POLISH = ('P' | 'L'<<8 | 'K'<<16 | ' '<<24), - POLYTONIC_GREEK = ('P' | 'G'<<8 | 'R'<<16 | ' '<<24), - PORTUGUESE = ('P' | 'T'<<8 | 'G'<<16 | ' '<<24), - PROVENCAL = ('P' | 'R'<<8 | 'O'<<16 | ' '<<24), - PUNJABI = ('P' | 'A'<<8 | 'N'<<16 | ' '<<24), - QUECHUA = ('Q' | 'U'<<8 | 'Z'<<16 | ' '<<24), - QUECHUA_BOLIVIA = ('Q' | 'U'<<8 | 'H'<<16 | ' '<<24), - QUECHUA_ECUADOR = ('Q' | 'V'<<8 | 'I'<<16 | ' '<<24), - QUECHUA_PERU = ('Q' | 'W'<<8 | 'H'<<16 | ' '<<24), - R_CREE = ('R' | 'C'<<8 | 'R'<<16 | ' '<<24), - RAJASTHANI = ('R' | 'A'<<8 | 'J'<<16 | ' '<<24), - RAKHINE = ('A' | 'R'<<8 | 'K'<<16 | ' '<<24), - RAROTONGAN = ('R' | 'A'<<8 | 'R'<<16 | ' '<<24), - REJANG = ('R' | 'E'<<8 | 'J'<<16 | ' '<<24), - RIANG = ('R' | 'I'<<8 | 'A'<<16 | ' '<<24), - RIPUARIAN = ('K' | 'S'<<8 | 'H'<<16 | ' '<<24), - RITARUNGO = ('R' | 'I'<<8 | 'T'<<16 | ' '<<24), - ROHINGYA = ('R' | 'H'<<8 | 'G'<<16 | ' '<<24), - ROMANIAN = ('R' | 'O'<<8 | 'M'<<16 | ' '<<24), - ROMANSH = ('R' | 'M'<<8 | 'S'<<16 | ' '<<24), - ROMANY = ('R' | 'O'<<8 | 'Y'<<16 | ' '<<24), - ROTUMAN = ('R' | 'T'<<8 | 'M'<<16 | ' '<<24), - RUNDI = ('R' | 'U'<<8 | 'N'<<16 | ' '<<24), - RUSSIAN = ('R' | 'U'<<8 | 'S'<<16 | ' '<<24), - RUSSIAN_BURIAT = ('R' | 'B'<<8 | 'U'<<16 | ' '<<24), - RUSYN = ('R' | 'S'<<8 | 'Y'<<16 | ' '<<24), - SADRI = ('S' | 'A'<<8 | 'D'<<16 | ' '<<24), - SAKHA = ('Y' | 'A'<<8 | 'K'<<16 | ' '<<24), - SAMOAN = ('S' | 'M'<<8 | 'O'<<16 | ' '<<24), - SAMOGITIAN = ('S' | 'G'<<8 | 'S'<<16 | ' '<<24), - SAN_BLAS_KUNA = ('C' | 'U'<<8 | 'K'<<16 | ' '<<24), - SANGO = ('S' | 'G'<<8 | 'O'<<16 | ' '<<24), - SANSKRIT = ('S' | 'A'<<8 | 'N'<<16 | ' '<<24), - SANTALI = ('S' | 'A'<<8 | 'T'<<16 | ' '<<24), - SARAIKI = ('S' | 'R'<<8 | 'K'<<16 | ' '<<24), - SARDINIAN = ('S' | 'R'<<8 | 'D'<<16 | ' '<<24), - SASAK = ('S' | 'A'<<8 | 'S'<<16 | ' '<<24), - SATERLAND_FRISIAN = ('S' | 'T'<<8 | 'Q'<<16 | ' '<<24), - SAYISI = ('S' | 'A'<<8 | 'Y'<<16 | ' '<<24), - SCOTS = ('S' | 'C'<<8 | 'I'<<16 | ' '<<24), - SCOTTISH_GAELIC = ('G' | 'A'<<8 | 'E'<<16 | ' '<<24), - SEKOTA = ('S' | 'E'<<8 | 'J'<<16 | ' '<<24), - SELKUP = ('S' | 'E'<<8 | 'L'<<16 | ' '<<24), - SENA = ('S' | 'N'<<8 | 'A'<<16 | ' '<<24), - SENECA = ('S' | 'E'<<8 | 'E'<<16 | ' '<<24), - SERBIAN = ('S' | 'R'<<8 | 'B'<<16 | ' '<<24), - SERER = ('S' | 'R'<<8 | 'R'<<16 | ' '<<24), - SGAW_KAREN = ('K' | 'S'<<8 | 'W'<<16 | ' '<<24), - SHAN = ('S' | 'H'<<8 | 'N'<<16 | ' '<<24), - SHONA = ('S' | 'N'<<8 | 'A'<<16 | ' '<<24), - SIBE = ('S' | 'I'<<8 | 'B'<<16 | ' '<<24), - SICILIAN = ('S' | 'C'<<8 | 'N'<<16 | ' '<<24), - SIDAMO = ('S' | 'I'<<8 | 'D'<<16 | ' '<<24), - SILESIAN = ('S' | 'Z'<<8 | 'L'<<16 | ' '<<24), - SILTE_GURAGE = ('S' | 'I'<<8 | 'G'<<16 | ' '<<24), - SINDHI = ('S' | 'N'<<8 | 'D'<<16 | ' '<<24), - SINHALA = ('S' | 'N'<<8 | 'H'<<16 | ' '<<24), - SKOLT_SAMI = ('S' | 'K'<<8 | 'S'<<16 | ' '<<24), - SLAVEY = ('S' | 'L'<<8 | 'A'<<16 | ' '<<24), - SLOVAK = ('S' | 'K'<<8 | 'Y'<<16 | ' '<<24), - SLOVENIAN = ('S' | 'L'<<8 | 'V'<<16 | ' '<<24), - SMALL_FLOWERY_MIAO = ('S' | 'F'<<8 | 'M'<<16 | ' '<<24), - SODO_GURAGE = ('S' | 'O'<<8 | 'G'<<16 | ' '<<24), - SOGA = ('X' | 'O'<<8 | 'G'<<16 | ' '<<24), - SOMALI = ('S' | 'M'<<8 | 'L'<<16 | ' '<<24), - SONGE = ('S' | 'O'<<8 | 'P'<<16 | ' '<<24), - SONINKE = ('S' | 'N'<<8 | 'K'<<16 | ' '<<24), - SOUTH_SLAVEY = ('S' | 'S'<<8 | 'L'<<16 | ' '<<24), - SOUTHERN_KIWAI = ('K' | 'J'<<8 | 'D'<<16 | ' '<<24), - SOUTHERN_SAMI = ('S' | 'S'<<8 | 'M'<<16 | ' '<<24), - SOUTHERN_SOTHO = ('S' | 'O'<<8 | 'T'<<16 | ' '<<24), - SPANISH = ('E' | 'S'<<8 | 'P'<<16 | ' '<<24), - STANDARD_MOROCCAN_TAMAZIGHT = ('Z' | 'G'<<8 | 'H'<<16 | ' '<<24), - STRAITS_SALISH = ('S' | 'T'<<8 | 'R'<<16 | ' '<<24), - SUKUMA = ('S' | 'U'<<8 | 'K'<<16 | ' '<<24), - SUNDANESE = ('S' | 'U'<<8 | 'N'<<16 | ' '<<24), - SURI = ('S' | 'U'<<8 | 'R'<<16 | ' '<<24), - SUTU = ('S' | 'X'<<8 | 'T'<<16 | ' '<<24), - SVAN = ('S' | 'V'<<8 | 'A'<<16 | ' '<<24), - SWADAYA_ARAMAIC = ('S' | 'W'<<8 | 'A'<<16 | ' '<<24), - SWAHILI = ('S' | 'W'<<8 | 'K'<<16 | ' '<<24), - SWATI = ('S' | 'W'<<8 | 'Z'<<16 | ' '<<24), - SWEDISH = ('S' | 'V'<<8 | 'E'<<16 | ' '<<24), - SYLHETI = ('S' | 'Y'<<8 | 'L'<<16 | ' '<<24), - SYRIAC = ('S' | 'Y'<<8 | 'R'<<16 | ' '<<24), - SYRIAC_EASTERN = ('S' | 'Y'<<8 | 'R'<<16 | 'N'<<24), - SYRIAC_ESTRANGELA = ('S' | 'Y'<<8 | 'R'<<16 | 'E'<<24), - SYRIAC_WESTERN = ('S' | 'Y'<<8 | 'R'<<16 | 'J'<<24), - TABASARAN = ('T' | 'A'<<8 | 'B'<<16 | ' '<<24), - TACHELHIT = ('S' | 'H'<<8 | 'I'<<16 | ' '<<24), - TAGALOG = ('T' | 'G'<<8 | 'L'<<16 | ' '<<24), - TAHAGGART_TAMAHAQ = ('T' | 'H'<<8 | 'V'<<16 | ' '<<24), - TAHITIAN = ('T' | 'H'<<8 | 'T'<<16 | ' '<<24), - TAI_LAING = ('T' | 'J'<<8 | 'L'<<16 | ' '<<24), - TAJIKI = ('T' | 'A'<<8 | 'J'<<16 | ' '<<24), - TALYSH = ('T' | 'L'<<8 | 'Y'<<16 | ' '<<24), - TAMASHEK = ('T' | 'M'<<8 | 'H'<<16 | ' '<<24), - TAMASHEQ = ('T' | 'A'<<8 | 'Q'<<16 | ' '<<24), - TAMAZIGHT = ('T' | 'Z'<<8 | 'M'<<16 | ' '<<24), - TAMIL = ('T' | 'A'<<8 | 'M'<<16 | ' '<<24), - TARIFIT = ('R' | 'I'<<8 | 'F'<<16 | ' '<<24), - TATAR = ('T' | 'A'<<8 | 'T'<<16 | ' '<<24), - TAWALLAMMAT_TAMAJAQ = ('T' | 'T'<<8 | 'Q'<<16 | ' '<<24), - TAY = ('T' | 'Y'<<8 | 'Z'<<16 | ' '<<24), - TAYART_TAMAJEQ = ('T' | 'H'<<8 | 'Z'<<16 | ' '<<24), - TELUGU = ('T' | 'E'<<8 | 'L'<<16 | ' '<<24), - TEMNE = ('T' | 'M'<<8 | 'N'<<16 | ' '<<24), - TETUM = ('T' | 'E'<<8 | 'T'<<16 | ' '<<24), - TH_CREE = ('T' | 'C'<<8 | 'R'<<16 | ' '<<24), - THAI = ('T' | 'H'<<8 | 'A'<<16 | ' '<<24), - THAILAND_MON = ('M' | 'O'<<8 | 'N'<<16 | 'T'<<24), - THOMPSON = ('T' | 'H'<<8 | 'P'<<16 | ' '<<24), - TIBETAN = ('T' | 'I'<<8 | 'B'<<16 | ' '<<24), - TIGRE = ('T' | 'G'<<8 | 'R'<<16 | ' '<<24), - TIGRINYA = ('T' | 'G'<<8 | 'Y'<<16 | ' '<<24), - TIV = ('T' | 'I'<<8 | 'V'<<16 | ' '<<24), - TLINGIT = ('T' | 'L'<<8 | 'I'<<16 | ' '<<24), - TOBO = ('T' | 'B'<<8 | 'V'<<16 | ' '<<24), - TODO = ('T' | 'O'<<8 | 'D'<<16 | ' '<<24), - TOK_PISIN = ('T' | 'P'<<8 | 'I'<<16 | ' '<<24), - TOMA = ('T' | 'O'<<8 | 'D'<<16 | '0'<<24), - TONGA = ('T' | 'N'<<8 | 'G'<<16 | ' '<<24), - TONGAN = ('T' | 'G'<<8 | 'N'<<16 | ' '<<24), - TORKI = ('A' | 'Z'<<8 | 'B'<<16 | ' '<<24), - TSHANGLA = ('T' | 'S'<<8 | 'J'<<16 | ' '<<24), - TSONGA = ('T' | 'S'<<8 | 'G'<<16 | ' '<<24), - TSWANA = ('T' | 'N'<<8 | 'A'<<16 | ' '<<24), - TULU = ('T' | 'U'<<8 | 'L'<<16 | ' '<<24), - TUMBUKA = ('T' | 'U'<<8 | 'M'<<16 | ' '<<24), - TUNDRA_ENETS = ('T' | 'N'<<8 | 'E'<<16 | ' '<<24), - TURKISH = ('T' | 'R'<<8 | 'K'<<16 | ' '<<24), - TURKMEN = ('T' | 'K'<<8 | 'M'<<16 | ' '<<24), - TUROYO_ARAMAIC = ('T' | 'U'<<8 | 'A'<<16 | ' '<<24), - TUSCARORA = ('T' | 'U'<<8 | 'S'<<16 | ' '<<24), - TUVALU = ('T' | 'V'<<8 | 'L'<<16 | ' '<<24), - TUVIN = ('T' | 'U'<<8 | 'V'<<16 | ' '<<24), - TWI = ('T' | 'W'<<8 | 'I'<<16 | ' '<<24), - TZOTZIL = ('T' | 'Z'<<8 | 'O'<<16 | ' '<<24), - UDI = ('U' | 'D'<<8 | 'I'<<16 | ' '<<24), - UDMURT = ('U' | 'D'<<8 | 'M'<<16 | ' '<<24), - UKRAINIAN = ('U' | 'K'<<8 | 'R'<<16 | ' '<<24), - UMBUNDU = ('U' | 'M'<<8 | 'B'<<16 | ' '<<24), - UME_SAMI = ('S' | 'J'<<8 | 'U'<<16 | ' '<<24), - UPPER_SAXON = ('S' | 'X'<<8 | 'U'<<16 | ' '<<24), - UPPER_SORBIAN = ('U' | 'S'<<8 | 'B'<<16 | ' '<<24), - URALIC_PHONETIC = ('U' | 'P'<<8 | 'P'<<16 | ' '<<24), - URDU = ('U' | 'R'<<8 | 'D'<<16 | ' '<<24), - UYGHUR = ('U' | 'Y'<<8 | 'G'<<16 | ' '<<24), - UZBEK = ('U' | 'Z'<<8 | 'B'<<16 | ' '<<24), - VENDA = ('V' | 'E'<<8 | 'N'<<16 | ' '<<24), - VENETIAN = ('V' | 'E'<<8 | 'C'<<16 | ' '<<24), - VIETNAMESE = ('V' | 'I'<<8 | 'T'<<16 | ' '<<24), - VLAX_ROMANI = ('R' | 'M'<<8 | 'Y'<<16 | ' '<<24), - VOLAPUK = ('V' | 'O'<<8 | 'L'<<16 | ' '<<24), - VORO = ('V' | 'R'<<8 | 'O'<<16 | ' '<<24), - WA = ('W' | 'A'<<8 | ' '<<16 | ' '<<24), - WACI_GBE = ('W' | 'C'<<8 | 'I'<<16 | ' '<<24), - WAGDI = ('W' | 'A'<<8 | 'G'<<16 | ' '<<24), - WAKHI = ('W' | 'B'<<8 | 'L'<<16 | ' '<<24), - WALLOON = ('W' | 'L'<<8 | 'N'<<16 | ' '<<24), - WARAY_WARAY = ('W' | 'A'<<8 | 'R'<<16 | ' '<<24), - WAYANAD_CHETTI = ('C' | 'T'<<8 | 'T'<<16 | ' '<<24), - WAYUU = ('G' | 'U'<<8 | 'C'<<16 | ' '<<24), - WELSH = ('W' | 'E'<<8 | 'L'<<16 | ' '<<24), - WENDAT = ('W' | 'D'<<8 | 'T'<<16 | ' '<<24), - WEST_CREE = ('W' | 'C'<<8 | 'R'<<16 | ' '<<24), - WESTERN_CHAM = ('C' | 'J'<<8 | 'A'<<16 | ' '<<24), - WESTERN_KAYAH = ('K' | 'Y'<<8 | 'U'<<16 | ' '<<24), - WESTERN_PANJABI = ('P' | 'N'<<8 | 'B'<<16 | ' '<<24), - WESTERN_PWO_KAREN = ('P' | 'W'<<8 | 'O'<<16 | ' '<<24), - WOLOF = ('W' | 'L'<<8 | 'F'<<16 | ' '<<24), - WOODS_CREE = ('D' | 'C'<<8 | 'R'<<16 | ' '<<24), - WUDING_LUQUAN_YI = ('Y' | 'W'<<8 | 'Q'<<16 | ' '<<24), - WYANDOT = ('W' | 'Y'<<8 | 'N'<<16 | ' '<<24), - XHOSA = ('X' | 'H'<<8 | 'S'<<16 | ' '<<24), - Y_CREE = ('Y' | 'C'<<8 | 'R'<<16 | ' '<<24), - YAO = ('Y' | 'A'<<8 | 'O'<<16 | ' '<<24), - YAPESE = ('Y' | 'A'<<8 | 'P'<<16 | ' '<<24), - YI_CLASSIC = ('Y' | 'I'<<8 | 'C'<<16 | ' '<<24), - YI_MODERN = ('Y' | 'I'<<8 | 'M'<<16 | ' '<<24), - YIDDISH = ('J' | 'I'<<8 | 'I'<<16 | ' '<<24), - YORUBA = ('Y' | 'B'<<8 | 'A'<<16 | ' '<<24), - ZAMBOANGA_CHAVACANO = ('C' | 'B'<<8 | 'K'<<16 | ' '<<24), - ZANDE = ('Z' | 'N'<<8 | 'D'<<16 | ' '<<24), - ZARMA = ('D' | 'J'<<8 | 'R'<<16 | ' '<<24), - ZAZAKI = ('Z' | 'Z'<<8 | 'A'<<16 | ' '<<24), - ZEALANDIC = ('Z' | 'E'<<8 | 'A'<<16 | ' '<<24), - ZHUANG = ('Z' | 'H'<<8 | 'A'<<16 | ' '<<24), - ZULU = ('Z' | 'U'<<8 | 'L'<<16 | ' '<<24), + A_HMAO = 'H' | 'M'<<8 | 'D'<<16 | ' '<<24, + AARI = 'A' | 'R'<<8 | 'I'<<16 | ' '<<24, + ABAZA = 'A' | 'B'<<8 | 'A'<<16 | ' '<<24, + ABKHAZIAN = 'A' | 'B'<<8 | 'K'<<16 | ' '<<24, + ACHI = 'A' | 'C'<<8 | 'R'<<16 | ' '<<24, + ACHOLI = 'A' | 'C'<<8 | 'H'<<16 | ' '<<24, + ADYGHE = 'A' | 'D'<<8 | 'Y'<<16 | ' '<<24, + AFAR = 'A' | 'F'<<8 | 'R'<<16 | ' '<<24, + AFRIKAANS = 'A' | 'F'<<8 | 'K'<<16 | ' '<<24, + AGAW = 'A' | 'G'<<8 | 'W'<<16 | ' '<<24, + AITON = 'A' | 'I'<<8 | 'O'<<16 | ' '<<24, + AKAN = 'A' | 'K'<<8 | 'A'<<16 | ' '<<24, + ALBANIAN = 'S' | 'Q'<<8 | 'I'<<16 | ' '<<24, + ALSATIAN = 'A' | 'L'<<8 | 'S'<<16 | ' '<<24, + ALTAI = 'A' | 'L'<<8 | 'T'<<16 | ' '<<24, + ALUO = 'Y' | 'N'<<8 | 'A'<<16 | ' '<<24, + AMERICAN_PHONETIC = 'A' | 'P'<<8 | 'P'<<16 | 'H'<<24, + AMHARIC = 'A' | 'M'<<8 | 'H'<<16 | ' '<<24, + ANGLO_SAXON = 'A' | 'N'<<8 | 'G'<<16 | ' '<<24, + ARABIC = 'A' | 'R'<<8 | 'A'<<16 | ' '<<24, + ARAGONESE = 'A' | 'R'<<8 | 'G'<<16 | ' '<<24, + ARAKANESE = 'A' | 'R'<<8 | 'K'<<16 | ' '<<24, + ARAKWAL = 'R' | 'K'<<8 | 'W'<<16 | ' '<<24, + ARMENIAN = 'H' | 'Y'<<8 | 'E'<<16 | ' '<<24, + ARMENIAN_EAST = 'H' | 'Y'<<8 | 'E'<<16 | '0'<<24, + AROMANIAN = 'R' | 'U'<<8 | 'P'<<16 | ' '<<24, + ARPITAN = 'F' | 'R'<<8 | 'P'<<16 | ' '<<24, + ASSAMESE = 'A' | 'S'<<8 | 'M'<<16 | ' '<<24, + ASTURIAN = 'A' | 'S'<<8 | 'T'<<16 | ' '<<24, + ATHAPASKAN = 'A' | 'T'<<8 | 'H'<<16 | ' '<<24, + ATSINA = 'A' | 'T'<<8 | 'S'<<16 | ' '<<24, + AVAR = 'A' | 'V'<<8 | 'R'<<16 | ' '<<24, + AVATIME = 'A' | 'V'<<8 | 'N'<<16 | ' '<<24, + AWADHI = 'A' | 'W'<<8 | 'A'<<16 | ' '<<24, + AYMARA = 'A' | 'Y'<<8 | 'M'<<16 | ' '<<24, + AZERBAIDJANI = 'A' | 'Z'<<8 | 'E'<<16 | ' '<<24, + BADAGA = 'B' | 'A'<<8 | 'D'<<16 | ' '<<24, + BAGHELKHANDI = 'B' | 'A'<<8 | 'G'<<16 | ' '<<24, + BAGRI = 'B' | 'G'<<8 | 'Q'<<16 | ' '<<24, + BALANTE = 'B' | 'L'<<8 | 'N'<<16 | ' '<<24, + BALINESE = 'B' | 'A'<<8 | 'N'<<16 | ' '<<24, + BALKAR = 'B' | 'A'<<8 | 'L'<<16 | ' '<<24, + BALTI = 'B' | 'L'<<8 | 'T'<<16 | ' '<<24, + BALUCHI = 'B' | 'L'<<8 | 'I'<<16 | ' '<<24, + BAMBARA = 'B' | 'M'<<8 | 'B'<<16 | ' '<<24, + BAMILEKE = 'B' | 'M'<<8 | 'L'<<16 | ' '<<24, + BANDA = 'B' | 'A'<<8 | 'D'<<16 | '0'<<24, + BANDJALANG = 'B' | 'D'<<8 | 'Y'<<16 | ' '<<24, + BANGLA = 'B' | 'E'<<8 | 'N'<<16 | ' '<<24, + BASHKIR = 'B' | 'S'<<8 | 'H'<<16 | ' '<<24, + BASQUE = 'E' | 'U'<<8 | 'Q'<<16 | ' '<<24, + BATAK = 'B' | 'T'<<8 | 'K'<<16 | ' '<<24, + BATAK_ALAS_KLUET = 'B' | 'T'<<8 | 'Z'<<16 | ' '<<24, + BATAK_ANGKOLA = 'A' | 'K'<<8 | 'B'<<16 | ' '<<24, + BATAK_DAIRI = 'B' | 'T'<<8 | 'D'<<16 | ' '<<24, + BATAK_KARO = 'B' | 'T'<<8 | 'X'<<16 | ' '<<24, + BATAK_MANDAILING = 'B' | 'T'<<8 | 'M'<<16 | ' '<<24, + BATAK_SIMALUNGUN = 'B' | 'T'<<8 | 'S'<<16 | ' '<<24, + BATAK_TOBA = 'B' | 'B'<<8 | 'C'<<16 | ' '<<24, + BAULE = 'B' | 'A'<<8 | 'U'<<16 | ' '<<24, + BAVARIAN = 'B' | 'A'<<8 | 'R'<<16 | ' '<<24, + BELARUSIAN = 'B' | 'E'<<8 | 'L'<<16 | ' '<<24, + BEMBA = 'B' | 'E'<<8 | 'M'<<16 | ' '<<24, + BENCH = 'B' | 'C'<<8 | 'H'<<16 | ' '<<24, + BERBER = 'B' | 'B'<<8 | 'R'<<16 | ' '<<24, + BETI = 'B' | 'T'<<8 | 'I'<<16 | ' '<<24, + BETTE_KURUMA = 'X' | 'U'<<8 | 'B'<<16 | ' '<<24, + BHILI = 'B' | 'H'<<8 | 'I'<<16 | ' '<<24, + BHOJPURI = 'B' | 'H'<<8 | 'O'<<16 | ' '<<24, + BHUTANESE = 'D' | 'Z'<<8 | 'N'<<16 | ' '<<24, + BIBLE_CREE = 'B' | 'C'<<8 | 'R'<<16 | ' '<<24, + BIKOL = 'B' | 'I'<<8 | 'K'<<16 | ' '<<24, + BILEN = 'B' | 'I'<<8 | 'L'<<16 | ' '<<24, + BISHNUPRIYA_MANIPURI = 'B' | 'P'<<8 | 'Y'<<16 | ' '<<24, + BISLAMA = 'B' | 'I'<<8 | 'S'<<16 | ' '<<24, + BLACKFOOT = 'B' | 'K'<<8 | 'F'<<16 | ' '<<24, + BODO = 'B' | 'R'<<8 | 'X'<<16 | ' '<<24, + BOSNIAN = 'B' | 'O'<<8 | 'S'<<16 | ' '<<24, + BOUYEI = 'P' | 'C'<<8 | 'C'<<16 | ' '<<24, + BRAHUI = 'B' | 'R'<<8 | 'H'<<16 | ' '<<24, + BRAJ_BHASHA = 'B' | 'R'<<8 | 'I'<<16 | ' '<<24, + BRETON = 'B' | 'R'<<8 | 'E'<<16 | ' '<<24, + BUGIS = 'B' | 'U'<<8 | 'G'<<16 | ' '<<24, + BULGARIAN = 'B' | 'G'<<8 | 'R'<<16 | ' '<<24, + BUMTHANGKHA = 'K' | 'J'<<8 | 'Z'<<16 | ' '<<24, + BURMESE = 'B' | 'R'<<8 | 'M'<<16 | ' '<<24, + BURUSHASKI = 'B' | 'S'<<8 | 'K'<<16 | ' '<<24, + CAJUN_FRENCH = 'F' | 'R'<<8 | 'C'<<16 | ' '<<24, + CARRIER = 'C' | 'R'<<8 | 'R'<<16 | ' '<<24, + CATALAN = 'C' | 'A'<<8 | 'T'<<16 | ' '<<24, + CAYUGA = 'C' | 'A'<<8 | 'Y'<<16 | ' '<<24, + CEBUANO = 'C' | 'E'<<8 | 'B'<<16 | ' '<<24, + CENTRAL_YUPIK = 'E' | 'S'<<8 | 'U'<<16 | ' '<<24, + CHAHA_GURAGE = 'C' | 'H'<<8 | 'G'<<16 | ' '<<24, + CHAMORRO = 'C' | 'H'<<8 | 'A'<<16 | ' '<<24, + CHATTISGARHI = 'C' | 'H'<<8 | 'H'<<16 | ' '<<24, + CHECHEN = 'C' | 'H'<<8 | 'E'<<16 | ' '<<24, + CHEROKEE = 'C' | 'H'<<8 | 'R'<<16 | ' '<<24, + CHEYENNE = 'C' | 'H'<<8 | 'Y'<<16 | ' '<<24, + CHICHEWA = 'C' | 'H'<<8 | 'I'<<16 | ' '<<24, + CHIGA = 'C' | 'G'<<8 | 'G'<<16 | ' '<<24, + CHIMILA = 'C' | 'B'<<8 | 'G'<<16 | ' '<<24, + CHIN = 'Q' | 'I'<<8 | 'N'<<16 | ' '<<24, + CHINANTEC = 'C' | 'C'<<8 | 'H'<<16 | 'N'<<24, + CHINESE_PHONETIC = 'Z' | 'H'<<8 | 'P'<<16 | ' '<<24, + CHINESE_SIMPLIFIED = 'Z' | 'H'<<8 | 'S'<<16 | ' '<<24, + CHINESE_TRADITIONAL = 'Z' | 'H'<<8 | 'T'<<16 | ' '<<24, + CHINESE_TRADITIONAL_HONG_KONG = 'Z' | 'H'<<8 | 'H'<<16 | ' '<<24, + CHINESE_TRADITIONAL_MACAO = 'Z' | 'H'<<8 | 'T'<<16 | 'M'<<24, + CHIPEWYAN = 'C' | 'H'<<8 | 'P'<<16 | ' '<<24, + CHITTAGONIAN = 'C' | 'T'<<8 | 'G'<<16 | ' '<<24, + CHOCTAW = 'C' | 'H'<<8 | 'O'<<16 | ' '<<24, + CHUKCHI = 'C' | 'H'<<8 | 'K'<<16 | ' '<<24, + CHURCH_SLAVONIC = 'C' | 'S'<<8 | 'L'<<16 | ' '<<24, + CHUUKESE = 'C' | 'H'<<8 | 'K'<<16 | '0'<<24, + CHUVASH = 'C' | 'H'<<8 | 'U'<<16 | ' '<<24, + COMORIAN = 'C' | 'M'<<8 | 'R'<<16 | ' '<<24, + COMOX = 'C' | 'O'<<8 | 'O'<<16 | ' '<<24, + COPTIC = 'C' | 'O'<<8 | 'P'<<16 | ' '<<24, + CORNISH = 'C' | 'O'<<8 | 'R'<<16 | ' '<<24, + CORSICAN = 'C' | 'O'<<8 | 'S'<<16 | ' '<<24, + CREE = 'C' | 'R'<<8 | 'E'<<16 | ' '<<24, + CREOLES = 'C' | 'P'<<8 | 'P'<<16 | ' '<<24, + CRIMEAN_TATAR = 'C' | 'R'<<8 | 'T'<<16 | ' '<<24, + CRIOULO = 'K' | 'E'<<8 | 'A'<<16 | ' '<<24, + CROATIAN = 'H' | 'R'<<8 | 'V'<<16 | ' '<<24, + CYPRIOT_ARABIC = 'A' | 'C'<<8 | 'Y'<<16 | ' '<<24, + CZECH = 'C' | 'S'<<8 | 'Y'<<16 | ' '<<24, + DAGBANI = 'D' | 'A'<<8 | 'G'<<16 | ' '<<24, + DAN = 'D' | 'N'<<8 | 'J'<<16 | ' '<<24, + DANGME = 'D' | 'N'<<8 | 'G'<<16 | ' '<<24, + DANISH = 'D' | 'A'<<8 | 'N'<<16 | ' '<<24, + DARGWA = 'D' | 'A'<<8 | 'R'<<16 | ' '<<24, + DARI = 'D' | 'R'<<8 | 'I'<<16 | ' '<<24, + DAYI = 'D' | 'A'<<8 | 'X'<<16 | ' '<<24, + DEFAULT = 'd' | 'f'<<8 | 'l'<<16 | 't'<<24, // Can be DFLT too. + DEHONG_DAI = 'T' | 'D'<<8 | 'D'<<16 | ' '<<24, + DHANGU = 'D' | 'H'<<8 | 'G'<<16 | ' '<<24, + DHIVEHI = 'D' | 'I'<<8 | 'V'<<16 | ' '<<24, + DHUWAL = 'D' | 'U'<<8 | 'J'<<16 | ' '<<24, + DIMLI = 'D' | 'I'<<8 | 'Q'<<16 | ' '<<24, + DINKA = 'D' | 'N'<<8 | 'K'<<16 | ' '<<24, + DIVEHI = 'D' | 'I'<<8 | 'V'<<16 | ' '<<24, + DJAMBARRPUYNGU = 'D' | 'J'<<8 | 'R'<<16 | '0'<<24, + DOGRI = 'D' | 'G'<<8 | 'O'<<16 | ' '<<24, + DOGRI_MACROLANGUAGE = 'D' | 'G'<<8 | 'R'<<16 | ' '<<24, + DUNGAN = 'D' | 'U'<<8 | 'N'<<16 | ' '<<24, + DUTCH = 'N' | 'L'<<8 | 'D'<<16 | ' '<<24, + DZONGKHA = 'D' | 'Z'<<8 | 'N'<<16 | ' '<<24, + EASTERN_ABENAKI = 'A' | 'A'<<8 | 'Q'<<16 | ' '<<24, + EASTERN_CHAM = 'C' | 'J'<<8 | 'M'<<16 | ' '<<24, + EASTERN_CREE = 'E' | 'C'<<8 | 'R'<<16 | ' '<<24, + EASTERN_MANINKAKAN = 'E' | 'M'<<8 | 'K'<<16 | ' '<<24, + EASTERN_PWO_KAREN = 'K' | 'J'<<8 | 'P'<<16 | ' '<<24, + EBIRA = 'E' | 'B'<<8 | 'I'<<16 | ' '<<24, + EDO = 'E' | 'D'<<8 | 'O'<<16 | ' '<<24, + EFIK = 'E' | 'F'<<8 | 'I'<<16 | ' '<<24, + EMBERA_BAUDO = 'B' | 'D'<<8 | 'C'<<16 | ' '<<24, + EMBERA_CATIO = 'C' | 'T'<<8 | 'O'<<16 | ' '<<24, + EMBERA_CHAMI = 'C' | 'M'<<8 | 'I'<<16 | ' '<<24, + EMBERA_TADO = 'T' | 'D'<<8 | 'C'<<16 | ' '<<24, + ENGLISH = 'E' | 'N'<<8 | 'G'<<16 | ' '<<24, + EPENA = 'S' | 'J'<<8 | 'A'<<16 | ' '<<24, + ERZYA = 'E' | 'R'<<8 | 'Z'<<16 | ' '<<24, + KB_TEXT_SHAPEANTO = 'N' | 'T'<<8 | 'O'<<16 | ' '<<24, + ESTONIAN = 'E' | 'T'<<8 | 'I'<<16 | ' '<<24, + EVEN = 'E' | 'V'<<8 | 'N'<<16 | ' '<<24, + EVENKI = 'E' | 'V'<<8 | 'K'<<16 | ' '<<24, + EWE = 'E' | 'W'<<8 | 'E'<<16 | ' '<<24, + FALAM_CHIN = 'H' | 'A'<<8 | 'L'<<16 | ' '<<24, + FANG = 'F' | 'A'<<8 | 'N'<<16 | '0'<<24, + FANTI = 'F' | 'A'<<8 | 'T'<<16 | ' '<<24, + FAROESE = 'F' | 'O'<<8 | 'S'<<16 | ' '<<24, + FEFE = 'F' | 'M'<<8 | 'P'<<16 | ' '<<24, + FIJIAN = 'F' | 'J'<<8 | 'I'<<16 | ' '<<24, + FILIPINO = 'P' | 'I'<<8 | 'L'<<16 | ' '<<24, + FINNISH = 'F' | 'I'<<8 | 'N'<<16 | ' '<<24, + FLEMISH = 'F' | 'L'<<8 | 'E'<<16 | ' '<<24, + FON = 'F' | 'O'<<8 | 'N'<<16 | ' '<<24, + FOREST_ENETS = 'F' | 'N'<<8 | 'E'<<16 | ' '<<24, + FRENCH = 'F' | 'R'<<8 | 'A'<<16 | ' '<<24, + FRENCH_ANTILLEAN = 'F' | 'A'<<8 | 'N'<<16 | ' '<<24, + FRISIAN = 'F' | 'R'<<8 | 'I'<<16 | ' '<<24, + FRIULIAN = 'F' | 'R'<<8 | 'L'<<16 | ' '<<24, + FULAH = 'F' | 'U'<<8 | 'L'<<16 | ' '<<24, + FUTA = 'F' | 'T'<<8 | 'A'<<16 | ' '<<24, + GA = 'G' | 'A'<<8 | 'D'<<16 | ' '<<24, + GAGAUZ = 'G' | 'A'<<8 | 'G'<<16 | ' '<<24, + GALICIAN = 'G' | 'A'<<8 | 'L'<<16 | ' '<<24, + GANDA = 'L' | 'U'<<8 | 'G'<<16 | ' '<<24, + GARHWALI = 'G' | 'A'<<8 | 'W'<<16 | ' '<<24, + GARO = 'G' | 'R'<<8 | 'O'<<16 | ' '<<24, + GARSHUNI = 'G' | 'A'<<8 | 'R'<<16 | ' '<<24, + GEBA_KAREN = 'K' | 'V'<<8 | 'Q'<<16 | ' '<<24, + GEEZ = 'G' | 'E'<<8 | 'Z'<<16 | ' '<<24, + GEORGIAN = 'K' | 'A'<<8 | 'T'<<16 | ' '<<24, + GEPO = 'Y' | 'G'<<8 | 'P'<<16 | ' '<<24, + GERMAN = 'D' | 'E'<<8 | 'U'<<16 | ' '<<24, + GIKUYU = 'K' | 'I'<<8 | 'K'<<16 | ' '<<24, + GILAKI = 'G' | 'L'<<8 | 'K'<<16 | ' '<<24, + GILBERTESE = 'G' | 'I'<<8 | 'L'<<16 | '0'<<24, + GILYAK = 'G' | 'I'<<8 | 'L'<<16 | ' '<<24, + GITHABUL = 'G' | 'I'<<8 | 'H'<<16 | ' '<<24, + GOGO = 'G' | 'O'<<8 | 'G'<<16 | ' '<<24, + GONDI = 'G' | 'O'<<8 | 'N'<<16 | ' '<<24, + GREEK = 'E' | 'L'<<8 | 'L'<<16 | ' '<<24, + GREENLANDIC = 'G' | 'R'<<8 | 'N'<<16 | ' '<<24, + GUARANI = 'G' | 'U'<<8 | 'A'<<16 | ' '<<24, + GUINEA = 'G' | 'K'<<8 | 'P'<<16 | ' '<<24, + GUJARATI = 'G' | 'U'<<8 | 'J'<<16 | ' '<<24, + GUMATJ = 'G' | 'N'<<8 | 'N'<<16 | ' '<<24, + GUMUZ = 'G' | 'M'<<8 | 'Z'<<16 | ' '<<24, + GUPAPUYNGU = 'G' | 'U'<<8 | 'F'<<16 | ' '<<24, + GUSII = 'G' | 'U'<<8 | 'Z'<<16 | ' '<<24, + HAIDA = 'H' | 'A'<<8 | 'I'<<16 | '0'<<24, + HAITIAN_CREOLE = 'H' | 'A'<<8 | 'I'<<16 | ' '<<24, + HALKOMELEM = 'H' | 'U'<<8 | 'R'<<16 | ' '<<24, + HAMMER_BANNA = 'H' | 'B'<<8 | 'N'<<16 | ' '<<24, + HARARI = 'H' | 'R'<<8 | 'I'<<16 | ' '<<24, + HARAUTI = 'H' | 'A'<<8 | 'R'<<16 | ' '<<24, + HARYANVI = 'B' | 'G'<<8 | 'C'<<16 | ' '<<24, + HAUSA = 'H' | 'A'<<8 | 'U'<<16 | ' '<<24, + HAVASUPAI_WALAPAI_YAVAPAI = 'Y' | 'U'<<8 | 'F'<<16 | ' '<<24, + HAWAIIAN = 'H' | 'A'<<8 | 'W'<<16 | ' '<<24, + HAYA = 'H' | 'A'<<8 | 'Y'<<16 | ' '<<24, + HAZARAGI = 'H' | 'A'<<8 | 'Z'<<16 | ' '<<24, + HEBREW = 'I' | 'W'<<8 | 'R'<<16 | ' '<<24, + HEILTSUK = 'H' | 'E'<<8 | 'I'<<16 | ' '<<24, + HERERO = 'H' | 'E'<<8 | 'R'<<16 | ' '<<24, + HIGH_MARI = 'H' | 'M'<<8 | 'A'<<16 | ' '<<24, + HILIGAYNON = 'H' | 'I'<<8 | 'L'<<16 | ' '<<24, + HINDI = 'H' | 'I'<<8 | 'N'<<16 | ' '<<24, + HINDKO = 'H' | 'N'<<8 | 'D'<<16 | ' '<<24, + HIRI_MOTU = 'H' | 'M'<<8 | 'O'<<16 | ' '<<24, + HMONG = 'H' | 'M'<<8 | 'N'<<16 | ' '<<24, + HMONG_DAW = 'M' | 'W'<<8 | 'W'<<16 | ' '<<24, + HMONG_SHUAT = 'H' | 'M'<<8 | 'Z'<<16 | ' '<<24, + HO = 'H' | 'O'<<8 | ' '<<16 | ' '<<24, + HUNGARIAN = 'H' | 'U'<<8 | 'N'<<16 | ' '<<24, + IBAN = 'I' | 'B'<<8 | 'A'<<16 | ' '<<24, + IBIBIO = 'I' | 'B'<<8 | 'B'<<16 | ' '<<24, + ICELANDIC = 'I' | 'S'<<8 | 'L'<<16 | ' '<<24, + IDO = 'I' | 'D'<<8 | 'O'<<16 | ' '<<24, + IGBO = 'I' | 'B'<<8 | 'O'<<16 | ' '<<24, + IJO = 'I' | 'J'<<8 | 'O'<<16 | ' '<<24, + ILOKANO = 'I' | 'L'<<8 | 'O'<<16 | ' '<<24, + INARI_SAMI = 'I' | 'S'<<8 | 'M'<<16 | ' '<<24, + INDONESIAN = 'I' | 'N'<<8 | 'D'<<16 | ' '<<24, + INGUSH = 'I' | 'N'<<8 | 'G'<<16 | ' '<<24, + INTERLINGUA = 'I' | 'N'<<8 | 'A'<<16 | ' '<<24, + INTERLINGUE = 'I' | 'L'<<8 | 'E'<<16 | ' '<<24, + INUKTITUT = 'I' | 'N'<<8 | 'U'<<16 | ' '<<24, + INUPIAT = 'I' | 'P'<<8 | 'K'<<16 | ' '<<24, + IPA_PHONETIC = 'I' | 'P'<<8 | 'P'<<16 | ' '<<24, + IRISH = 'I' | 'R'<<8 | 'I'<<16 | ' '<<24, + IRISH_TRADITIONAL = 'I' | 'R'<<8 | 'T'<<16 | ' '<<24, + IRULA = 'I' | 'R'<<8 | 'U'<<16 | ' '<<24, + ITALIAN = 'I' | 'T'<<8 | 'A'<<16 | ' '<<24, + JAMAICAN_CREOLE = 'J' | 'A'<<8 | 'M'<<16 | ' '<<24, + JAPANESE = 'J' | 'A'<<8 | 'N'<<16 | ' '<<24, + JAVANESE = 'J' | 'A'<<8 | 'V'<<16 | ' '<<24, + JENNU_KURUMA = 'X' | 'U'<<8 | 'J'<<16 | ' '<<24, + JUDEO_TAT = 'J' | 'D'<<8 | 'T'<<16 | ' '<<24, + JULA = 'J' | 'U'<<8 | 'L'<<16 | ' '<<24, + KABARDIAN = 'K' | 'A'<<8 | 'B'<<16 | ' '<<24, + KABYLE = 'K' | 'A'<<8 | 'B'<<16 | '0'<<24, + KACHCHI = 'K' | 'A'<<8 | 'C'<<16 | ' '<<24, + KADIWEU = 'K' | 'B'<<8 | 'C'<<16 | ' '<<24, + KALENJIN = 'K' | 'A'<<8 | 'L'<<16 | ' '<<24, + KALMYK = 'K' | 'L'<<8 | 'M'<<16 | ' '<<24, + KAMBA = 'K' | 'M'<<8 | 'B'<<16 | ' '<<24, + KANAUJI = 'B' | 'J'<<8 | 'J'<<16 | ' '<<24, + KANNADA = 'K' | 'A'<<8 | 'N'<<16 | ' '<<24, + KANURI = 'K' | 'N'<<8 | 'R'<<16 | ' '<<24, + KAQCHIKEL = 'C' | 'A'<<8 | 'K'<<16 | ' '<<24, + KARACHAY = 'K' | 'A'<<8 | 'R'<<16 | ' '<<24, + KARAIM = 'K' | 'R'<<8 | 'M'<<16 | ' '<<24, + KARAKALPAK = 'K' | 'R'<<8 | 'K'<<16 | ' '<<24, + KARELIAN = 'K' | 'R'<<8 | 'L'<<16 | ' '<<24, + KAREN = 'K' | 'R'<<8 | 'N'<<16 | ' '<<24, + KASHMIRI = 'K' | 'S'<<8 | 'H'<<16 | ' '<<24, + KASHUBIAN = 'C' | 'S'<<8 | 'B'<<16 | ' '<<24, + KATE = 'K' | 'M'<<8 | 'G'<<16 | ' '<<24, + KAZAKH = 'K' | 'A'<<8 | 'Z'<<16 | ' '<<24, + KEBENA = 'K' | 'E'<<8 | 'B'<<16 | ' '<<24, + KEKCHI = 'K' | 'E'<<8 | 'K'<<16 | ' '<<24, + KHAKASS = 'K' | 'H'<<8 | 'A'<<16 | ' '<<24, + KHAMTI_SHAN = 'K' | 'H'<<8 | 'T'<<16 | ' '<<24, + KHAMYANG = 'K' | 'S'<<8 | 'U'<<16 | ' '<<24, + KHANTY_KAZIM = 'K' | 'H'<<8 | 'K'<<16 | ' '<<24, + KHANTY_SHURISHKAR = 'K' | 'H'<<8 | 'S'<<16 | ' '<<24, + KHANTY_VAKHI = 'K' | 'H'<<8 | 'V'<<16 | ' '<<24, + KHASI = 'K' | 'S'<<8 | 'I'<<16 | ' '<<24, + KHENGKHA = 'X' | 'K'<<8 | 'F'<<16 | ' '<<24, + KHINALUG = 'K' | 'J'<<8 | 'J'<<16 | ' '<<24, + KHMER = 'K' | 'H'<<8 | 'M'<<16 | ' '<<24, + KHORASANI_TURKIC = 'K' | 'M'<<8 | 'Z'<<16 | ' '<<24, + KHOWAR = 'K' | 'H'<<8 | 'W'<<16 | ' '<<24, + KHUTSURI_GEORGIAN = 'K' | 'G'<<8 | 'E'<<16 | ' '<<24, + KICHE = 'Q' | 'U'<<8 | 'C'<<16 | ' '<<24, + KIKONGO = 'K' | 'O'<<8 | 'N'<<16 | ' '<<24, + KILDIN_SAMI = 'K' | 'S'<<8 | 'M'<<16 | ' '<<24, + KINYARWANDA = 'R' | 'U'<<8 | 'A'<<16 | ' '<<24, + KIRMANJKI = 'K' | 'I'<<8 | 'U'<<16 | ' '<<24, + KISII = 'K' | 'I'<<8 | 'S'<<16 | ' '<<24, + KITUBA = 'M' | 'K'<<8 | 'W'<<16 | ' '<<24, + KODAGU = 'K' | 'O'<<8 | 'D'<<16 | ' '<<24, + KOKNI = 'K' | 'K'<<8 | 'N'<<16 | ' '<<24, + KOMI = 'K' | 'O'<<8 | 'M'<<16 | ' '<<24, + KOMI_PERMYAK = 'K' | 'O'<<8 | 'P'<<16 | ' '<<24, + KOMI_ZYRIAN = 'K' | 'O'<<8 | 'Z'<<16 | ' '<<24, + KOMO = 'K' | 'M'<<8 | 'O'<<16 | ' '<<24, + KOMSO = 'K' | 'M'<<8 | 'S'<<16 | ' '<<24, + KONGO = 'K' | 'O'<<8 | 'N'<<16 | '0'<<24, + KONKANI = 'K' | 'O'<<8 | 'K'<<16 | ' '<<24, + KOORETE = 'K' | 'R'<<8 | 'T'<<16 | ' '<<24, + KOREAN = 'K' | 'O'<<8 | 'R'<<16 | ' '<<24, + KOREAO_OLD_HANGUL = 'K' | 'O'<<8 | 'H'<<16 | ' '<<24, + KORYAK = 'K' | 'Y'<<8 | 'K'<<16 | ' '<<24, + KOSRAEAN = 'K' | 'O'<<8 | 'S'<<16 | ' '<<24, + KPELLE = 'K' | 'P'<<8 | 'L'<<16 | ' '<<24, + KPELLE_LIBERIA = 'X' | 'P'<<8 | 'E'<<16 | ' '<<24, + KRIO = 'K' | 'R'<<8 | 'I'<<16 | ' '<<24, + KRYMCHAK = 'J' | 'C'<<8 | 'T'<<16 | ' '<<24, + KUANYAMA = 'K' | 'U'<<8 | 'A'<<16 | ' '<<24, + KUBE = 'K' | 'G'<<8 | 'F'<<16 | ' '<<24, + KUI = 'K' | 'U'<<8 | 'I'<<16 | ' '<<24, + KULVI = 'K' | 'U'<<8 | 'K'<<16 | ' '<<24, + KUMAONI = 'K' | 'M'<<8 | 'N'<<16 | ' '<<24, + KUMYK = 'K' | 'U'<<8 | 'M'<<16 | ' '<<24, + KURDISH = 'K' | 'U'<<8 | 'R'<<16 | ' '<<24, + KURUKH = 'K' | 'U'<<8 | 'U'<<16 | ' '<<24, + KUY = 'K' | 'U'<<8 | 'Y'<<16 | ' '<<24, + KWAKWALA = 'K' | 'W'<<8 | 'K'<<16 | ' '<<24, + KYRGYZ = 'K' | 'I'<<8 | 'R'<<16 | ' '<<24, + L_CREE = 'L' | 'C'<<8 | 'R'<<16 | ' '<<24, + LADAKHI = 'L' | 'D'<<8 | 'K'<<16 | ' '<<24, + LADIN = 'L' | 'A'<<8 | 'D'<<16 | ' '<<24, + LADINO = 'J' | 'U'<<8 | 'D'<<16 | ' '<<24, + LAHULI = 'L' | 'A'<<8 | 'H'<<16 | ' '<<24, + LAK = 'L' | 'A'<<8 | 'K'<<16 | ' '<<24, + LAKI = 'L' | 'K'<<8 | 'I'<<16 | ' '<<24, + LAMBANI = 'L' | 'A'<<8 | 'M'<<16 | ' '<<24, + LAMPUNG = 'L' | 'J'<<8 | 'P'<<16 | ' '<<24, + LAO = 'L' | 'A'<<8 | 'O'<<16 | ' '<<24, + LATIN = 'L' | 'A'<<8 | 'T'<<16 | ' '<<24, + LATVIAN = 'L' | 'V'<<8 | 'I'<<16 | ' '<<24, + LAZ = 'L' | 'A'<<8 | 'Z'<<16 | ' '<<24, + LELEMI = 'L' | 'E'<<8 | 'F'<<16 | ' '<<24, + LEZGI = 'L' | 'E'<<8 | 'Z'<<16 | ' '<<24, + LIGURIAN = 'L' | 'I'<<8 | 'J'<<16 | ' '<<24, + LIMBU = 'L' | 'M'<<8 | 'B'<<16 | ' '<<24, + LIMBURGISH = 'L' | 'I'<<8 | 'M'<<16 | ' '<<24, + LINGALA = 'L' | 'I'<<8 | 'N'<<16 | ' '<<24, + LIPO = 'L' | 'P'<<8 | 'O'<<16 | ' '<<24, + LISU = 'L' | 'I'<<8 | 'S'<<16 | ' '<<24, + LITHUANIAN = 'L' | 'T'<<8 | 'H'<<16 | ' '<<24, + LIV = 'L' | 'I'<<8 | 'V'<<16 | ' '<<24, + LOJBAN = 'J' | 'B'<<8 | 'O'<<16 | ' '<<24, + LOMA = 'L' | 'O'<<8 | 'M'<<16 | ' '<<24, + LOMBARD = 'L' | 'M'<<8 | 'O'<<16 | ' '<<24, + LOMWE = 'L' | 'M'<<8 | 'W'<<16 | ' '<<24, + LOW_MARI = 'L' | 'M'<<8 | 'A'<<16 | ' '<<24, + LOW_SAXON = 'N' | 'D'<<8 | 'S'<<16 | ' '<<24, + LOWER_SORBIAN = 'L' | 'S'<<8 | 'B'<<16 | ' '<<24, + LU = 'X' | 'B'<<8 | 'D'<<16 | ' '<<24, + LUBA_KATANGA = 'L' | 'U'<<8 | 'B'<<16 | ' '<<24, + LUBA_LULUA = 'L' | 'U'<<8 | 'A'<<16 | ' '<<24, + LULE_SAMI = 'L' | 'S'<<8 | 'M'<<16 | ' '<<24, + LUO = 'L' | 'U'<<8 | 'O'<<16 | ' '<<24, + LURI = 'L' | 'R'<<8 | 'C'<<16 | ' '<<24, + LUSHOOTSEED = 'L' | 'U'<<8 | 'T'<<16 | ' '<<24, + LUXEMBOURGISH = 'L' | 'T'<<8 | 'Z'<<16 | ' '<<24, + LUYIA = 'L' | 'U'<<8 | 'H'<<16 | ' '<<24, + MACEDONIAN = 'M' | 'K'<<8 | 'D'<<16 | ' '<<24, + MADURA = 'M' | 'A'<<8 | 'D'<<16 | ' '<<24, + MAGAHI = 'M' | 'A'<<8 | 'G'<<16 | ' '<<24, + MAITHILI = 'M' | 'T'<<8 | 'H'<<16 | ' '<<24, + MAJANG = 'M' | 'A'<<8 | 'J'<<16 | ' '<<24, + MAKASAR = 'M' | 'K'<<8 | 'R'<<16 | ' '<<24, + MAKHUWA = 'M' | 'A'<<8 | 'K'<<16 | ' '<<24, + MAKONDE = 'K' | 'D'<<8 | 'E'<<16 | ' '<<24, + MALAGASY = 'M' | 'L'<<8 | 'G'<<16 | ' '<<24, + MALAY = 'M' | 'L'<<8 | 'Y'<<16 | ' '<<24, + MALAYALAM = 'M' | 'A'<<8 | 'L'<<16 | ' '<<24, + MALAYALAM_REFORMED = 'M' | 'L'<<8 | 'R'<<16 | ' '<<24, + MALE = 'M' | 'L'<<8 | 'E'<<16 | ' '<<24, + MALINKE = 'M' | 'L'<<8 | 'N'<<16 | ' '<<24, + MALTESE = 'M' | 'T'<<8 | 'S'<<16 | ' '<<24, + MAM = 'M' | 'A'<<8 | 'M'<<16 | ' '<<24, + MANCHU = 'M' | 'C'<<8 | 'H'<<16 | ' '<<24, + MANDAR = 'M' | 'D'<<8 | 'R'<<16 | ' '<<24, + MANDINKA = 'M' | 'N'<<8 | 'D'<<16 | ' '<<24, + MANINKA = 'M' | 'N'<<8 | 'K'<<16 | ' '<<24, + MANIPURI = 'M' | 'N'<<8 | 'I'<<16 | ' '<<24, + MANO = 'M' | 'E'<<8 | 'V'<<16 | ' '<<24, + MANSI = 'M' | 'A'<<8 | 'N'<<16 | ' '<<24, + MANX = 'M' | 'N'<<8 | 'X'<<16 | ' '<<24, + MAORI = 'M' | 'R'<<8 | 'I'<<16 | ' '<<24, + MAPUDUNGUN = 'M' | 'A'<<8 | 'P'<<16 | ' '<<24, + MARATHI = 'M' | 'A'<<8 | 'R'<<16 | ' '<<24, + MARSHALLESE = 'M' | 'A'<<8 | 'H'<<16 | ' '<<24, + MARWARI = 'M' | 'A'<<8 | 'W'<<16 | ' '<<24, + MAYAN = 'M' | 'Y'<<8 | 'N'<<16 | ' '<<24, + MAZANDERANI = 'M' | 'Z'<<8 | 'N'<<16 | ' '<<24, + MBEMBE_TIGON = 'N' | 'Z'<<8 | 'A'<<16 | ' '<<24, + MBO = 'M' | 'B'<<8 | 'O'<<16 | ' '<<24, + MBUNDU = 'M' | 'B'<<8 | 'N'<<16 | ' '<<24, + MEDUMBA = 'B' | 'Y'<<8 | 'V'<<16 | ' '<<24, + MEEN = 'M' | 'E'<<8 | 'N'<<16 | ' '<<24, + MENDE = 'M' | 'D'<<8 | 'E'<<16 | ' '<<24, + MERU = 'M' | 'E'<<8 | 'R'<<16 | ' '<<24, + MEWATI = 'W' | 'T'<<8 | 'M'<<16 | ' '<<24, + MINANGKABAU = 'M' | 'I'<<8 | 'N'<<16 | ' '<<24, + MINJANGBAL = 'X' | 'J'<<8 | 'B'<<16 | ' '<<24, + MIRANDESE = 'M' | 'W'<<8 | 'L'<<16 | ' '<<24, + MIZO = 'M' | 'I'<<8 | 'Z'<<16 | ' '<<24, + MOHAWK = 'M' | 'O'<<8 | 'H'<<16 | ' '<<24, + MOKSHA = 'M' | 'O'<<8 | 'K'<<16 | ' '<<24, + MOLDAVIAN = 'M' | 'O'<<8 | 'L'<<16 | ' '<<24, + MON = 'M' | 'O'<<8 | 'N'<<16 | ' '<<24, + MONGOLIAN = 'M' | 'N'<<8 | 'G'<<16 | ' '<<24, + MOOSE_CREE = 'M' | 'C'<<8 | 'R'<<16 | ' '<<24, + MORISYEN = 'M' | 'F'<<8 | 'E'<<16 | ' '<<24, + MOROCCAN = 'M' | 'O'<<8 | 'R'<<16 | ' '<<24, + MOSSI = 'M' | 'P'<<8 | 'S'<<16 | ' '<<24, + MUNDARI = 'M' | 'U'<<8 | 'N'<<16 | ' '<<24, + MUSCOGEE = 'M' | 'U'<<8 | 'S'<<16 | ' '<<24, + N_CREE = 'N' | 'C'<<8 | 'R'<<16 | ' '<<24, + NAGA_ASSAMESE = 'N' | 'A'<<8 | 'G'<<16 | ' '<<24, + NAGARI = 'N' | 'G'<<8 | 'R'<<16 | ' '<<24, + NAHUATL = 'N' | 'A'<<8 | 'H'<<16 | ' '<<24, + NANAI = 'N' | 'A'<<8 | 'N'<<16 | ' '<<24, + NASKAPI = 'N' | 'A'<<8 | 'S'<<16 | ' '<<24, + NAURUAN = 'N' | 'A'<<8 | 'U'<<16 | ' '<<24, + NAVAJO = 'N' | 'A'<<8 | 'V'<<16 | ' '<<24, + NDAU = 'N' | 'D'<<8 | 'C'<<16 | ' '<<24, + NDEBELE = 'N' | 'D'<<8 | 'B'<<16 | ' '<<24, + NDONGA = 'N' | 'D'<<8 | 'G'<<16 | ' '<<24, + NEAPOLITAN = 'N' | 'A'<<8 | 'P'<<16 | ' '<<24, + NEPALI = 'N' | 'E'<<8 | 'P'<<16 | ' '<<24, + NEWARI = 'N' | 'E'<<8 | 'W'<<16 | ' '<<24, + NGBAKA = 'N' | 'G'<<8 | 'A'<<16 | ' '<<24, + NIGERIAN_FULFULDE = 'F' | 'U'<<8 | 'V'<<16 | ' '<<24, + NIMADI = 'N' | 'O'<<8 | 'E'<<16 | ' '<<24, + NISI = 'N' | 'I'<<8 | 'S'<<16 | ' '<<24, + NIUEAN = 'N' | 'I'<<8 | 'U'<<16 | ' '<<24, + NKO = 'N' | 'K'<<8 | 'O'<<16 | ' '<<24, + NOGAI = 'N' | 'O'<<8 | 'G'<<16 | ' '<<24, + NORFOLK = 'P' | 'I'<<8 | 'H'<<16 | ' '<<24, + NORTH_SLAVEY = 'S' | 'C'<<8 | 'S'<<16 | ' '<<24, + NORTHERN_EMBERA = 'E' | 'M'<<8 | 'P'<<16 | ' '<<24, + NORTHERN_SAMI = 'N' | 'S'<<8 | 'M'<<16 | ' '<<24, + NORTHERN_SOTHO = 'N' | 'S'<<8 | 'O'<<16 | ' '<<24, + NORTHERN_TAI = 'N' | 'T'<<8 | 'A'<<16 | ' '<<24, + NORWAY_HOUSE_CREE = 'N' | 'H'<<8 | 'C'<<16 | ' '<<24, + NORWEGIAN = 'N' | 'O'<<8 | 'R'<<16 | ' '<<24, + NORWEGIAN_NYNORSK = 'N' | 'Y'<<8 | 'N'<<16 | ' '<<24, + NOVIAL = 'N' | 'O'<<8 | 'V'<<16 | ' '<<24, + NUMANGGANG = 'N' | 'O'<<8 | 'P'<<16 | ' '<<24, + NUNAVIK_INUKTITUT = 'I' | 'N'<<8 | 'U'<<16 | ' '<<24, + NUU_CHAH_NULTH = 'N' | 'U'<<8 | 'K'<<16 | ' '<<24, + NYAMWEZI = 'N' | 'Y'<<8 | 'M'<<16 | ' '<<24, + NYANKOLE = 'N' | 'K'<<8 | 'L'<<16 | ' '<<24, + OCCITAN = 'O' | 'C'<<8 | 'I'<<16 | ' '<<24, + ODIA = 'O' | 'R'<<8 | 'I'<<16 | ' '<<24, + OJI_CREE = 'O' | 'C'<<8 | 'R'<<16 | ' '<<24, + OJIBWAY = 'O' | 'J'<<8 | 'B'<<16 | ' '<<24, + OLD_IRISH = 'S' | 'G'<<8 | 'A'<<16 | ' '<<24, + OLD_JAVANESE = 'K' | 'A'<<8 | 'W'<<16 | ' '<<24, + ONEIDA = 'O' | 'N'<<8 | 'E'<<16 | ' '<<24, + ONONDAGA = 'O' | 'N'<<8 | 'O'<<16 | ' '<<24, + OROMO = 'O' | 'R'<<8 | 'O'<<16 | ' '<<24, + OSSETIAN = 'O' | 'S'<<8 | 'S'<<16 | ' '<<24, + PA_O_KAREN = 'B' | 'L'<<8 | 'K'<<16 | ' '<<24, + PALAUAN = 'P' | 'A'<<8 | 'U'<<16 | ' '<<24, + PALAUNG = 'P' | 'L'<<8 | 'G'<<16 | ' '<<24, + PALESTINIAN_ARAMAIC = 'P' | 'A'<<8 | 'A'<<16 | ' '<<24, + PALI = 'P' | 'A'<<8 | 'L'<<16 | ' '<<24, + PALPA = 'P' | 'A'<<8 | 'P'<<16 | ' '<<24, + PAMPANGAN = 'P' | 'A'<<8 | 'M'<<16 | ' '<<24, + PANGASINAN = 'P' | 'A'<<8 | 'G'<<16 | ' '<<24, + PAPIAMENTU = 'P' | 'A'<<8 | 'P'<<16 | '0'<<24, + PASHTO = 'P' | 'A'<<8 | 'S'<<16 | ' '<<24, + PATTANI_MALAY = 'M' | 'F'<<8 | 'A'<<16 | ' '<<24, + PENNSYLVANIA_GERMAN = 'P' | 'D'<<8 | 'C'<<16 | ' '<<24, + PERSIAN = 'F' | 'A'<<8 | 'R'<<16 | ' '<<24, + PHAKE = 'P' | 'J'<<8 | 'K'<<16 | ' '<<24, + PICARD = 'P' | 'C'<<8 | 'D'<<16 | ' '<<24, + PIEMONTESE = 'P' | 'M'<<8 | 'S'<<16 | ' '<<24, + PILAGA = 'P' | 'L'<<8 | 'G'<<16 | ' '<<24, + PITE_SAMI = 'S' | 'J'<<8 | 'E'<<16 | ' '<<24, + POCOMCHI = 'P' | 'O'<<8 | 'H'<<16 | ' '<<24, + POHNPEIAN = 'P' | 'O'<<8 | 'N'<<16 | ' '<<24, + POLISH = 'P' | 'L'<<8 | 'K'<<16 | ' '<<24, + POLYTONIC_GREEK = 'P' | 'G'<<8 | 'R'<<16 | ' '<<24, + PORTUGUESE = 'P' | 'T'<<8 | 'G'<<16 | ' '<<24, + PROVENCAL = 'P' | 'R'<<8 | 'O'<<16 | ' '<<24, + PUNJABI = 'P' | 'A'<<8 | 'N'<<16 | ' '<<24, + QUECHUA = 'Q' | 'U'<<8 | 'Z'<<16 | ' '<<24, + QUECHUA_BOLIVIA = 'Q' | 'U'<<8 | 'H'<<16 | ' '<<24, + QUECHUA_ECUADOR = 'Q' | 'V'<<8 | 'I'<<16 | ' '<<24, + QUECHUA_PERU = 'Q' | 'W'<<8 | 'H'<<16 | ' '<<24, + R_CREE = 'R' | 'C'<<8 | 'R'<<16 | ' '<<24, + RAJASTHANI = 'R' | 'A'<<8 | 'J'<<16 | ' '<<24, + RAKHINE = 'A' | 'R'<<8 | 'K'<<16 | ' '<<24, + RAROTONGAN = 'R' | 'A'<<8 | 'R'<<16 | ' '<<24, + REJANG = 'R' | 'E'<<8 | 'J'<<16 | ' '<<24, + RIANG = 'R' | 'I'<<8 | 'A'<<16 | ' '<<24, + RIPUARIAN = 'K' | 'S'<<8 | 'H'<<16 | ' '<<24, + RITARUNGO = 'R' | 'I'<<8 | 'T'<<16 | ' '<<24, + ROHINGYA = 'R' | 'H'<<8 | 'G'<<16 | ' '<<24, + ROMANIAN = 'R' | 'O'<<8 | 'M'<<16 | ' '<<24, + ROMANSH = 'R' | 'M'<<8 | 'S'<<16 | ' '<<24, + ROMANY = 'R' | 'O'<<8 | 'Y'<<16 | ' '<<24, + ROTUMAN = 'R' | 'T'<<8 | 'M'<<16 | ' '<<24, + RUNDI = 'R' | 'U'<<8 | 'N'<<16 | ' '<<24, + RUSSIAN = 'R' | 'U'<<8 | 'S'<<16 | ' '<<24, + RUSSIAN_BURIAT = 'R' | 'B'<<8 | 'U'<<16 | ' '<<24, + RUSYN = 'R' | 'S'<<8 | 'Y'<<16 | ' '<<24, + SADRI = 'S' | 'A'<<8 | 'D'<<16 | ' '<<24, + SAKHA = 'Y' | 'A'<<8 | 'K'<<16 | ' '<<24, + SAMOAN = 'S' | 'M'<<8 | 'O'<<16 | ' '<<24, + SAMOGITIAN = 'S' | 'G'<<8 | 'S'<<16 | ' '<<24, + SAN_BLAS_KUNA = 'C' | 'U'<<8 | 'K'<<16 | ' '<<24, + SANGO = 'S' | 'G'<<8 | 'O'<<16 | ' '<<24, + SANSKRIT = 'S' | 'A'<<8 | 'N'<<16 | ' '<<24, + SANTALI = 'S' | 'A'<<8 | 'T'<<16 | ' '<<24, + SARAIKI = 'S' | 'R'<<8 | 'K'<<16 | ' '<<24, + SARDINIAN = 'S' | 'R'<<8 | 'D'<<16 | ' '<<24, + SASAK = 'S' | 'A'<<8 | 'S'<<16 | ' '<<24, + SATERLAND_FRISIAN = 'S' | 'T'<<8 | 'Q'<<16 | ' '<<24, + SAYISI = 'S' | 'A'<<8 | 'Y'<<16 | ' '<<24, + SCOTS = 'S' | 'C'<<8 | 'I'<<16 | ' '<<24, + SCOTTISH_GAELIC = 'G' | 'A'<<8 | 'E'<<16 | ' '<<24, + SEKOTA = 'S' | 'E'<<8 | 'J'<<16 | ' '<<24, + SELKUP = 'S' | 'E'<<8 | 'L'<<16 | ' '<<24, + SENA = 'S' | 'N'<<8 | 'A'<<16 | ' '<<24, + SENECA = 'S' | 'E'<<8 | 'E'<<16 | ' '<<24, + SERBIAN = 'S' | 'R'<<8 | 'B'<<16 | ' '<<24, + SERER = 'S' | 'R'<<8 | 'R'<<16 | ' '<<24, + SGAW_KAREN = 'K' | 'S'<<8 | 'W'<<16 | ' '<<24, + SHAN = 'S' | 'H'<<8 | 'N'<<16 | ' '<<24, + SHONA = 'S' | 'N'<<8 | 'A'<<16 | ' '<<24, + SIBE = 'S' | 'I'<<8 | 'B'<<16 | ' '<<24, + SICILIAN = 'S' | 'C'<<8 | 'N'<<16 | ' '<<24, + SIDAMO = 'S' | 'I'<<8 | 'D'<<16 | ' '<<24, + SILESIAN = 'S' | 'Z'<<8 | 'L'<<16 | ' '<<24, + SILTE_GURAGE = 'S' | 'I'<<8 | 'G'<<16 | ' '<<24, + SINDHI = 'S' | 'N'<<8 | 'D'<<16 | ' '<<24, + SINHALA = 'S' | 'N'<<8 | 'H'<<16 | ' '<<24, + SKOLT_SAMI = 'S' | 'K'<<8 | 'S'<<16 | ' '<<24, + SLAVEY = 'S' | 'L'<<8 | 'A'<<16 | ' '<<24, + SLOVAK = 'S' | 'K'<<8 | 'Y'<<16 | ' '<<24, + SLOVENIAN = 'S' | 'L'<<8 | 'V'<<16 | ' '<<24, + SMALL_FLOWERY_MIAO = 'S' | 'F'<<8 | 'M'<<16 | ' '<<24, + SODO_GURAGE = 'S' | 'O'<<8 | 'G'<<16 | ' '<<24, + SOGA = 'X' | 'O'<<8 | 'G'<<16 | ' '<<24, + SOMALI = 'S' | 'M'<<8 | 'L'<<16 | ' '<<24, + SONGE = 'S' | 'O'<<8 | 'P'<<16 | ' '<<24, + SONINKE = 'S' | 'N'<<8 | 'K'<<16 | ' '<<24, + SOUTH_SLAVEY = 'S' | 'S'<<8 | 'L'<<16 | ' '<<24, + SOUTHERN_KIWAI = 'K' | 'J'<<8 | 'D'<<16 | ' '<<24, + SOUTHERN_SAMI = 'S' | 'S'<<8 | 'M'<<16 | ' '<<24, + SOUTHERN_SOTHO = 'S' | 'O'<<8 | 'T'<<16 | ' '<<24, + SPANISH = 'E' | 'S'<<8 | 'P'<<16 | ' '<<24, + STANDARD_MOROCCAN_TAMAZIGHT = 'Z' | 'G'<<8 | 'H'<<16 | ' '<<24, + STRAITS_SALISH = 'S' | 'T'<<8 | 'R'<<16 | ' '<<24, + SUKUMA = 'S' | 'U'<<8 | 'K'<<16 | ' '<<24, + SUNDANESE = 'S' | 'U'<<8 | 'N'<<16 | ' '<<24, + SURI = 'S' | 'U'<<8 | 'R'<<16 | ' '<<24, + SUTU = 'S' | 'X'<<8 | 'T'<<16 | ' '<<24, + SVAN = 'S' | 'V'<<8 | 'A'<<16 | ' '<<24, + SWADAYA_ARAMAIC = 'S' | 'W'<<8 | 'A'<<16 | ' '<<24, + SWAHILI = 'S' | 'W'<<8 | 'K'<<16 | ' '<<24, + SWATI = 'S' | 'W'<<8 | 'Z'<<16 | ' '<<24, + SWEDISH = 'S' | 'V'<<8 | 'E'<<16 | ' '<<24, + SYLHETI = 'S' | 'Y'<<8 | 'L'<<16 | ' '<<24, + SYRIAC = 'S' | 'Y'<<8 | 'R'<<16 | ' '<<24, + SYRIAC_EASTERN = 'S' | 'Y'<<8 | 'R'<<16 | 'N'<<24, + SYRIAC_ESTRANGELA = 'S' | 'Y'<<8 | 'R'<<16 | 'E'<<24, + SYRIAC_WESTERN = 'S' | 'Y'<<8 | 'R'<<16 | 'J'<<24, + TABASARAN = 'T' | 'A'<<8 | 'B'<<16 | ' '<<24, + TACHELHIT = 'S' | 'H'<<8 | 'I'<<16 | ' '<<24, + TAGALOG = 'T' | 'G'<<8 | 'L'<<16 | ' '<<24, + TAHAGGART_TAMAHAQ = 'T' | 'H'<<8 | 'V'<<16 | ' '<<24, + TAHITIAN = 'T' | 'H'<<8 | 'T'<<16 | ' '<<24, + TAI_LAING = 'T' | 'J'<<8 | 'L'<<16 | ' '<<24, + TAJIKI = 'T' | 'A'<<8 | 'J'<<16 | ' '<<24, + TALYSH = 'T' | 'L'<<8 | 'Y'<<16 | ' '<<24, + TAMASHEK = 'T' | 'M'<<8 | 'H'<<16 | ' '<<24, + TAMASHEQ = 'T' | 'A'<<8 | 'Q'<<16 | ' '<<24, + TAMAZIGHT = 'T' | 'Z'<<8 | 'M'<<16 | ' '<<24, + TAMIL = 'T' | 'A'<<8 | 'M'<<16 | ' '<<24, + TARIFIT = 'R' | 'I'<<8 | 'F'<<16 | ' '<<24, + TATAR = 'T' | 'A'<<8 | 'T'<<16 | ' '<<24, + TAWALLAMMAT_TAMAJAQ = 'T' | 'T'<<8 | 'Q'<<16 | ' '<<24, + TAY = 'T' | 'Y'<<8 | 'Z'<<16 | ' '<<24, + TAYART_TAMAJEQ = 'T' | 'H'<<8 | 'Z'<<16 | ' '<<24, + TELUGU = 'T' | 'E'<<8 | 'L'<<16 | ' '<<24, + TEMNE = 'T' | 'M'<<8 | 'N'<<16 | ' '<<24, + TETUM = 'T' | 'E'<<8 | 'T'<<16 | ' '<<24, + TH_CREE = 'T' | 'C'<<8 | 'R'<<16 | ' '<<24, + THAI = 'T' | 'H'<<8 | 'A'<<16 | ' '<<24, + THAILAND_MON = 'M' | 'O'<<8 | 'N'<<16 | 'T'<<24, + THOMPSON = 'T' | 'H'<<8 | 'P'<<16 | ' '<<24, + TIBETAN = 'T' | 'I'<<8 | 'B'<<16 | ' '<<24, + TIGRE = 'T' | 'G'<<8 | 'R'<<16 | ' '<<24, + TIGRINYA = 'T' | 'G'<<8 | 'Y'<<16 | ' '<<24, + TIV = 'T' | 'I'<<8 | 'V'<<16 | ' '<<24, + TLINGIT = 'T' | 'L'<<8 | 'I'<<16 | ' '<<24, + TOBO = 'T' | 'B'<<8 | 'V'<<16 | ' '<<24, + TODO = 'T' | 'O'<<8 | 'D'<<16 | ' '<<24, + TOK_PISIN = 'T' | 'P'<<8 | 'I'<<16 | ' '<<24, + TOMA = 'T' | 'O'<<8 | 'D'<<16 | '0'<<24, + TONGA = 'T' | 'N'<<8 | 'G'<<16 | ' '<<24, + TONGAN = 'T' | 'G'<<8 | 'N'<<16 | ' '<<24, + TORKI = 'A' | 'Z'<<8 | 'B'<<16 | ' '<<24, + TSHANGLA = 'T' | 'S'<<8 | 'J'<<16 | ' '<<24, + TSONGA = 'T' | 'S'<<8 | 'G'<<16 | ' '<<24, + TSWANA = 'T' | 'N'<<8 | 'A'<<16 | ' '<<24, + TULU = 'T' | 'U'<<8 | 'L'<<16 | ' '<<24, + TUMBUKA = 'T' | 'U'<<8 | 'M'<<16 | ' '<<24, + TUNDRA_ENETS = 'T' | 'N'<<8 | 'E'<<16 | ' '<<24, + TURKISH = 'T' | 'R'<<8 | 'K'<<16 | ' '<<24, + TURKMEN = 'T' | 'K'<<8 | 'M'<<16 | ' '<<24, + TUROYO_ARAMAIC = 'T' | 'U'<<8 | 'A'<<16 | ' '<<24, + TUSCARORA = 'T' | 'U'<<8 | 'S'<<16 | ' '<<24, + TUVALU = 'T' | 'V'<<8 | 'L'<<16 | ' '<<24, + TUVIN = 'T' | 'U'<<8 | 'V'<<16 | ' '<<24, + TWI = 'T' | 'W'<<8 | 'I'<<16 | ' '<<24, + TZOTZIL = 'T' | 'Z'<<8 | 'O'<<16 | ' '<<24, + UDI = 'U' | 'D'<<8 | 'I'<<16 | ' '<<24, + UDMURT = 'U' | 'D'<<8 | 'M'<<16 | ' '<<24, + UKRAINIAN = 'U' | 'K'<<8 | 'R'<<16 | ' '<<24, + UMBUNDU = 'U' | 'M'<<8 | 'B'<<16 | ' '<<24, + UME_SAMI = 'S' | 'J'<<8 | 'U'<<16 | ' '<<24, + UPPER_SAXON = 'S' | 'X'<<8 | 'U'<<16 | ' '<<24, + UPPER_SORBIAN = 'U' | 'S'<<8 | 'B'<<16 | ' '<<24, + URALIC_PHONETIC = 'U' | 'P'<<8 | 'P'<<16 | ' '<<24, + URDU = 'U' | 'R'<<8 | 'D'<<16 | ' '<<24, + UYGHUR = 'U' | 'Y'<<8 | 'G'<<16 | ' '<<24, + UZBEK = 'U' | 'Z'<<8 | 'B'<<16 | ' '<<24, + VENDA = 'V' | 'E'<<8 | 'N'<<16 | ' '<<24, + VENETIAN = 'V' | 'E'<<8 | 'C'<<16 | ' '<<24, + VIETNAMESE = 'V' | 'I'<<8 | 'T'<<16 | ' '<<24, + VLAX_ROMANI = 'R' | 'M'<<8 | 'Y'<<16 | ' '<<24, + VOLAPUK = 'V' | 'O'<<8 | 'L'<<16 | ' '<<24, + VORO = 'V' | 'R'<<8 | 'O'<<16 | ' '<<24, + WA = 'W' | 'A'<<8 | ' '<<16 | ' '<<24, + WACI_GBE = 'W' | 'C'<<8 | 'I'<<16 | ' '<<24, + WAGDI = 'W' | 'A'<<8 | 'G'<<16 | ' '<<24, + WAKHI = 'W' | 'B'<<8 | 'L'<<16 | ' '<<24, + WALLOON = 'W' | 'L'<<8 | 'N'<<16 | ' '<<24, + WARAY_WARAY = 'W' | 'A'<<8 | 'R'<<16 | ' '<<24, + WAYANAD_CHETTI = 'C' | 'T'<<8 | 'T'<<16 | ' '<<24, + WAYUU = 'G' | 'U'<<8 | 'C'<<16 | ' '<<24, + WELSH = 'W' | 'E'<<8 | 'L'<<16 | ' '<<24, + WENDAT = 'W' | 'D'<<8 | 'T'<<16 | ' '<<24, + WEST_CREE = 'W' | 'C'<<8 | 'R'<<16 | ' '<<24, + WESTERN_CHAM = 'C' | 'J'<<8 | 'A'<<16 | ' '<<24, + WESTERN_KAYAH = 'K' | 'Y'<<8 | 'U'<<16 | ' '<<24, + WESTERN_PANJABI = 'P' | 'N'<<8 | 'B'<<16 | ' '<<24, + WESTERN_PWO_KAREN = 'P' | 'W'<<8 | 'O'<<16 | ' '<<24, + WOLOF = 'W' | 'L'<<8 | 'F'<<16 | ' '<<24, + WOODS_CREE = 'D' | 'C'<<8 | 'R'<<16 | ' '<<24, + WUDING_LUQUAN_YI = 'Y' | 'W'<<8 | 'Q'<<16 | ' '<<24, + WYANDOT = 'W' | 'Y'<<8 | 'N'<<16 | ' '<<24, + XHOSA = 'X' | 'H'<<8 | 'S'<<16 | ' '<<24, + Y_CREE = 'Y' | 'C'<<8 | 'R'<<16 | ' '<<24, + YAO = 'Y' | 'A'<<8 | 'O'<<16 | ' '<<24, + YAPESE = 'Y' | 'A'<<8 | 'P'<<16 | ' '<<24, + YI_CLASSIC = 'Y' | 'I'<<8 | 'C'<<16 | ' '<<24, + YI_MODERN = 'Y' | 'I'<<8 | 'M'<<16 | ' '<<24, + YIDDISH = 'J' | 'I'<<8 | 'I'<<16 | ' '<<24, + YORUBA = 'Y' | 'B'<<8 | 'A'<<16 | ' '<<24, + ZAMBOANGA_CHAVACANO = 'C' | 'B'<<8 | 'K'<<16 | ' '<<24, + ZANDE = 'Z' | 'N'<<8 | 'D'<<16 | ' '<<24, + ZARMA = 'D' | 'J'<<8 | 'R'<<16 | ' '<<24, + ZAZAKI = 'Z' | 'Z'<<8 | 'A'<<16 | ' '<<24, + ZEALANDIC = 'Z' | 'E'<<8 | 'A'<<16 | ' '<<24, + ZHUANG = 'Z' | 'H'<<8 | 'A'<<16 | ' '<<24, + ZULU = 'Z' | 'U'<<8 | 'L'<<16 | ' '<<24, } break_flags :: distinct bit_set[break_flag; u32] @@ -780,42 +730,173 @@ break_flag :: enum u32 { // (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.) LINE_HARD = 5, + // Used for manual segmentation in the context. + MANUAL = 6, + + PARAGRAPH_DIRECTION = 7, } -BREAK_FLAGS_DIRECTION :: break_flags{.DIRECTION} -BREAK_FLAGS_SCRIPT :: break_flags{.SCRIPT} -BREAK_FLAGS_GRAPHEME :: break_flags{.GRAPHEME} -BREAK_FLAGS_WORD :: break_flags{.WORD} -BREAK_FLAGS_LINE_SOFT :: break_flags{.LINE_SOFT} -BREAK_FLAGS_LINE_HARD :: break_flags{.LINE_HARD} -BREAK_FLAGS_LINE :: break_flags{.LINE_SOFT, .LINE_HARD} -BREAK_FLAGS_ANY :: break_flags{.DIRECTION, .SCRIPT, .GRAPHEME, .WORD, .LINE_SOFT, .LINE_HARD} +BREAK_FLAG_DIRECTION :: break_flags{.DIRECTION} +BREAK_FLAG_SCRIPT :: break_flags{.SCRIPT} +BREAK_FLAG_GRAPHEME :: break_flags{.GRAPHEME} +BREAK_FLAG_WORD :: break_flags{.WORD} +BREAK_FLAG_LINE_SOFT :: break_flags{.LINE_SOFT} +BREAK_FLAG_LINE_HARD :: break_flags{.LINE_HARD} +BREAK_FLAG_MANUAL :: break_flags{.MANUAL} +BREAK_FLAG_PARAGRAPH_DIRECTION :: break_flags{.PARAGRAPH_DIRECTION} + +BREAK_FLAG_LINE :: break_flags{.LINE_SOFT, .LINE_HARD} +BREAK_FLAG_ANY :: break_flags{.DIRECTION, .SCRIPT, .GRAPHEME, .WORD, .LINE_SOFT, .LINE_HARD} -op_kind :: enum u8 { - END, +// 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. +japanese_line_break_style :: enum u8 { + // 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. + STRICT, - // Substitution ops. - PRE_NORMALIZE_DOTTED_CIRCLES, - NORMALIZE, - NORMALIZE_HANGUL, - FLAG_JOINING_LETTERS, - GSUB_FEATURES, - GSUB_FEATURES_WITH_USER, + // Normal style is used for books and documents. + NORMAL, - // Positioning ops. - GPOS_METRICS, - GPOS_FEATURES, + // Loose style is used for newspapers, and (I assume) any other narrow column format. + LOOSE, +} - POST_GPOS_FIXUP, - STCH_POSTPASS, +break_state_flags :: distinct bit_set[break_state_flag; u32] +break_state_flag :: enum u32 { + STARTED = 0, + END = 1, + + _ = 2, + + // Bidirectional flags + SAW_R_AFTER_L = 3, + SAW_AL_AFTER_LR = 4, + LAST_WAS_BRACKET = 5, } +text_format :: enum u32 { + NONE, + + UTF32, + UTF8, +} + +direction :: enum u32 { + DONT_KNOW, + KBTS_DIRECTION_LTR, + KBTS_DIRECTION_RTL, +} + +orientation :: enum u32 { + HORIZONTAL, + VERTICAL, +} + +shaping_table :: enum u8 { + GSUB, + GPOS, +} + +shape_error :: enum u32 { + NONE, + INVALID_FONT, + GAVE_TEXT_BEFORE_CALLING_BEGIN, + OUT_OF_MEMORY, +} + +allocator_op_kind :: enum u32 { + NONE, + ALLOCATE, + FREE, +} + +blob_table_id :: enum u32 { + NONE, + HEAD, + CMAP, + GDEF, + GSUB, + GPOS, + HHEA, + VHEA, + HMTX, + VMTX, + MAXP, + OS2, + NAME, +} + +load_font_error :: enum u32 { + NONE, + NEED_TO_CREATE_BLOB, + INVALID_FONT, + OUT_OF_MEMORY, + COULD_NOT_OPEN_FILE, + READ_ERROR, +} + +version :: enum u32 { + _1_X, + _2_0, + + CURRENT = _2_0, +} + +blob_version :: enum u32 { + INVALID, + INITIAL, + + CURRENT = INITIAL, +} + +font_style_flags :: distinct bit_set[font_style_flag; u32] +font_style_flag :: enum u32 { + REGULAR = 0, + ITALIC = 1, + BOLD = 2, +} + +font_weight :: enum u32 { + UNKNOWN, + + THIN, + EXTRA_LIGHT, + LIGHT, + NORMAL, + MEDIUM, + SEMI_BOLD, + BOLD, + EXTRA_BOLD, + BLACK, +} + +font_width :: enum u32 { + UNKNOWN, + + ULTRA_CONDENSED, + EXTRA_CONDENSED, + CONDENSED, + SEMI_CONDENSED, + NORMAL, + SEMI_EXPANDED, + EXPANDED, + EXTRA_EXPANDED, + ULTRA_EXPANDED, +} + glyph_flags :: distinct bit_set[glyph_flag; u32] glyph_flag :: enum u32 { - // These feature flags must coincide with joining_feature _and_ FEATURE_FLAG! + // These feature flags must coincide with kbts_joining_feature _and_ KBTS_FEATURE_FLAG! ISOL = 0, FINA = 1, FIN2 = 2, @@ -840,66 +921,60 @@ glyph_flag :: enum u32 { CFAR = 19, // These can be anything. - DO_NOT_DECOMPOSE = 21, + DO_NOT_DECOMPOSE = 21, FIRST_IN_MULTIPLE_SUBSTITUTION = 22, - NO_BREAK = 23, - CURSIVE = 24, - GENERATED_BY_GSUB = 25, - USED_IN_GPOS = 26, + NO_BREAK = 23, + CURSIVE = 24, + GENERATED_BY_GSUB = 25, + USED_IN_GPOS = 26, - STCH_ENDPOINT = 27, - STCH_EXTENSION = 28, + STCH_ENDPOINT = 27, + STCH_EXTENSION = 28, - LIGATURE = 29, - MULTIPLE_SUBSTITUTION = 30, + LIGATURE = 29, + MULTIPLE_SUBSTITUTION = 30, } -GLYPH_FEATURE_MASK :: glyph_flags{.ISOL, .FINA, .FIN2, .FIN3, .MEDI, .MED2, .INIT, .LJMO, .VJMO, .TJMO, .RPHF, .BLWF, .HALF, .PSTF, .ABVF, .PREF, .NUMR, .FRAC, .DNOM, .CFAR} - -// In USE, glyphs are mostly not pre-flagged for feature application. -// However, we do want to flag rphf/pref results for reordering, so we want to -// keep all of the flags as usual, and only use these feature flags for filtering. - -USE_GLYPH_FEATURE_MASK :: glyph_flags{ - .ISOL, .FINA, .FIN2, .FIN3, .MEDI, .MED2, .INIT, - .NUMR, .DNOM, .FRAC, -} - -JOINING_FEATURE_MASK :: glyph_flags{.ISOL, .FINA, .FIN2, .FIN3, .MEDI, .MED2, .INIT} - - -// 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. -japanese_line_break_style :: enum u8 { - // 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. - STRICT, - - // Normal style is used for books and documents. - NORMAL, - - // Loose style is used for newspapers, and (I assume) any other narrow column format. - LOOSE, -} - - -orientation :: enum u32 { - HORIZONTAL, - VERTICAL, -} - -direction :: enum u32 { +joining_feature :: enum u8 { NONE, - LTR, - RTL, + + // These must correspond with glyph_flags and FEATURE_IDs. + ISOL, + FINA, + FIN2, + FIN3, + MEDI, + MED2, + INIT, } +user_id_generation_mode :: enum u32 { + CODEPOINT_INDEX, + SOURCE_INDEX, +} + +break_config_flags :: distinct bit_set[break_config_flag; u32] +break_config_flag :: enum u32 { + END_OF_TEXT_GENERATES_HARD_LINE_BREAK = 0, +} + +font_info_string_id :: enum u32 { + NONE, + COPYRIGHT, + FAMILY, + SUBFAMILY, + UID, + FULL_NAME, + VERSION, + POSTSCRIPT_NAME, + TRADEMARK, + MANUFACTURER, + DESIGNER, + TYPOGRAPHIC_FAMILY, + TYPOGRAPHIC_SUBFAMILY, +} + + unicode_joining_type :: enum u8 { NONE, LEFT, @@ -920,8 +995,18 @@ unicode_flag :: enum u8 { NON_SPACING_MARK = 6, } +UNICODE_FLAG_MODIFIER_COMBINING_MARK :: unicode_flags{.MODIFIER_COMBINING_MARK} +UNICODE_FLAG_DEFAULT_IGNORABLE :: unicode_flags{.DEFAULT_IGNORABLE} +UNICODE_FLAG_OPEN_BRACKET :: unicode_flags{.OPEN_BRACKET} +UNICODE_FLAG_CLOSE_BRACKET :: unicode_flags{.CLOSE_BRACKET} +UNICODE_FLAG_PART_OF_WORD :: unicode_flags{.PART_OF_WORD} +UNICODE_FLAG_DECIMAL_DIGIT :: unicode_flags{.DECIMAL_DIGIT} +UNICODE_FLAG_NON_SPACING_MARK :: unicode_flags{.NON_SPACING_MARK} +UNICODE_FLAG_MIRRORED :: unicode_flags{.OPEN_BRACKET, .CLOSE_BRACKET} + unicode_bidirectional_class :: enum u8 { NI, + BN, // Formatting characters need to be ignored. L, R, NSM, @@ -1068,177 +1153,180 @@ shaper :: enum u32 { USE, } +MAXIMUM_RECOMPOSITION_PARENTS :: 19 +MAXIMUM_CODEPOINT_SCRIPTS :: 23 + script_tag :: enum u32 { - DONT_KNOW = (' ' | ' '<<8 | ' '<<16 | ' '<<24), - ADLAM = ('a' | 'd'<<8 | 'l'<<16 | 'm'<<24), - AHOM = ('a' | 'h'<<8 | 'o'<<16 | 'm'<<24), - ANATOLIAN_HIEROGLYPHS = ('h' | 'l'<<8 | 'u'<<16 | 'w'<<24), - ARABIC = ('a' | 'r'<<8 | 'a'<<16 | 'b'<<24), - ARMENIAN = ('a' | 'r'<<8 | 'm'<<16 | 'n'<<24), - AVESTAN = ('a' | 'v'<<8 | 's'<<16 | 't'<<24), - BALINESE = ('b' | 'a'<<8 | 'l'<<16 | 'i'<<24), - BAMUM = ('b' | 'a'<<8 | 'm'<<16 | 'u'<<24), - BASSA_VAH = ('b' | 'a'<<8 | 's'<<16 | 's'<<24), - BATAK = ('b' | 'a'<<8 | 't'<<16 | 'k'<<24), - BENGALI = ('b' | 'n'<<8 | 'g'<<16 | '2'<<24), - BHAIKSUKI = ('b' | 'h'<<8 | 'k'<<16 | 's'<<24), - BOPOMOFO = ('b' | 'o'<<8 | 'p'<<16 | 'o'<<24), - BRAHMI = ('b' | 'r'<<8 | 'a'<<16 | 'h'<<24), - BUGINESE = ('b' | 'u'<<8 | 'g'<<16 | 'i'<<24), - BUHID = ('b' | 'u'<<8 | 'h'<<16 | 'd'<<24), - CANADIAN_SYLLABICS = ('c' | 'a'<<8 | 'n'<<16 | 's'<<24), - CARIAN = ('c' | 'a'<<8 | 'r'<<16 | 'i'<<24), - CAUCASIAN_ALBANIAN = ('a' | 'g'<<8 | 'h'<<16 | 'b'<<24), - CHAKMA = ('c' | 'a'<<8 | 'k'<<16 | 'm'<<24), - CHAM = ('c' | 'h'<<8 | 'a'<<16 | 'm'<<24), - CHEROKEE = ('c' | 'h'<<8 | 'e'<<16 | 'r'<<24), - CHORASMIAN = ('c' | 'h'<<8 | 'r'<<16 | 's'<<24), - CJK_IDEOGRAPHIC = ('h' | 'a'<<8 | 'n'<<16 | 'i'<<24), - COPTIC = ('c' | 'o'<<8 | 'p'<<16 | 't'<<24), - CYPRIOT_SYLLABARY = ('c' | 'p'<<8 | 'r'<<16 | 't'<<24), - CYPRO_MINOAN = ('c' | 'p'<<8 | 'm'<<16 | 'n'<<24), - CYRILLIC = ('c' | 'y'<<8 | 'r'<<16 | 'l'<<24), - DEFAULT = ('D' | 'F'<<8 | 'L'<<16 | 'T'<<24), - DEFAULT2 = ('D' | 'F'<<8 | 'L'<<16 | 'T'<<24), - DESERET = ('d' | 's'<<8 | 'r'<<16 | 't'<<24), - DEVANAGARI = ('d' | 'e'<<8 | 'v'<<16 | '2'<<24), - DIVES_AKURU = ('d' | 'i'<<8 | 'a'<<16 | 'k'<<24), - DOGRA = ('d' | 'o'<<8 | 'g'<<16 | 'r'<<24), - DUPLOYAN = ('d' | 'u'<<8 | 'p'<<16 | 'l'<<24), - EGYPTIAN_HIEROGLYPHS = ('e' | 'g'<<8 | 'y'<<16 | 'p'<<24), - ELBASAN = ('e' | 'l'<<8 | 'b'<<16 | 'a'<<24), - ELYMAIC = ('e' | 'l'<<8 | 'y'<<16 | 'm'<<24), - ETHIOPIC = ('e' | 't'<<8 | 'h'<<16 | 'i'<<24), - GARAY = ('g' | 'a'<<8 | 'r'<<16 | 'a'<<24), - GEORGIAN = ('g' | 'e'<<8 | 'o'<<16 | 'r'<<24), - GLAGOLITIC = ('g' | 'l'<<8 | 'a'<<16 | 'g'<<24), - GOTHIC = ('g' | 'o'<<8 | 't'<<16 | 'h'<<24), - GRANTHA = ('g' | 'r'<<8 | 'a'<<16 | 'n'<<24), - GREEK = ('g' | 'r'<<8 | 'e'<<16 | 'k'<<24), - GUJARATI = ('g' | 'j'<<8 | 'r'<<16 | '2'<<24), - GUNJALA_GONDI = ('g' | 'o'<<8 | 'n'<<16 | 'g'<<24), - GURMUKHI = ('g' | 'u'<<8 | 'r'<<16 | '2'<<24), - GURUNG_KHEMA = ('g' | 'u'<<8 | 'k'<<16 | 'h'<<24), - HANGUL = ('h' | 'a'<<8 | 'n'<<16 | 'g'<<24), - HANIFI_ROHINGYA = ('r' | 'o'<<8 | 'h'<<16 | 'g'<<24), - HANUNOO = ('h' | 'a'<<8 | 'n'<<16 | 'o'<<24), - HATRAN = ('h' | 'a'<<8 | 't'<<16 | 'r'<<24), - HEBREW = ('h' | 'e'<<8 | 'b'<<16 | 'r'<<24), - HIRAGANA = ('k' | 'a'<<8 | 'n'<<16 | 'a'<<24), - IMPERIAL_ARAMAIC = ('a' | 'r'<<8 | 'm'<<16 | 'i'<<24), - INSCRIPTIONAL_PAHLAVI = ('p' | 'h'<<8 | 'l'<<16 | 'i'<<24), - INSCRIPTIONAL_PARTHIAN = ('p' | 'r'<<8 | 't'<<16 | 'i'<<24), - JAVANESE = ('j' | 'a'<<8 | 'v'<<16 | 'a'<<24), - KAITHI = ('k' | 't'<<8 | 'h'<<16 | 'i'<<24), - KANNADA = ('k' | 'n'<<8 | 'd'<<16 | '2'<<24), - KATAKANA = ('k' | 'a'<<8 | 'n'<<16 | 'a'<<24), - KAWI = ('k' | 'a'<<8 | 'w'<<16 | 'i'<<24), - KAYAH_LI = ('k' | 'a'<<8 | 'l'<<16 | 'i'<<24), - KHAROSHTHI = ('k' | 'h'<<8 | 'a'<<16 | 'r'<<24), - KHITAN_SMALL_SCRIPT = ('k' | 'i'<<8 | 't'<<16 | 's'<<24), - KHMER = ('k' | 'h'<<8 | 'm'<<16 | 'r'<<24), - KHOJKI = ('k' | 'h'<<8 | 'o'<<16 | 'j'<<24), - KHUDAWADI = ('s' | 'i'<<8 | 'n'<<16 | 'd'<<24), - KIRAT_RAI = ('k' | 'r'<<8 | 'a'<<16 | 'i'<<24), - LAO = ('l' | 'a'<<8 | 'o'<<16 | ' '<<24), - LATIN = ('l' | 'a'<<8 | 't'<<16 | 'n'<<24), - LEPCHA = ('l' | 'e'<<8 | 'p'<<16 | 'c'<<24), - LIMBU = ('l' | 'i'<<8 | 'm'<<16 | 'b'<<24), - LINEAR_A = ('l' | 'i'<<8 | 'n'<<16 | 'a'<<24), - LINEAR_B = ('l' | 'i'<<8 | 'n'<<16 | 'b'<<24), - LISU = ('l' | 'i'<<8 | 's'<<16 | 'u'<<24), - LYCIAN = ('l' | 'y'<<8 | 'c'<<16 | 'i'<<24), - LYDIAN = ('l' | 'y'<<8 | 'd'<<16 | 'i'<<24), - MAHAJANI = ('m' | 'a'<<8 | 'h'<<16 | 'j'<<24), - MAKASAR = ('m' | 'a'<<8 | 'k'<<16 | 'a'<<24), - MALAYALAM = ('m' | 'l'<<8 | 'm'<<16 | '2'<<24), - MANDAIC = ('m' | 'a'<<8 | 'n'<<16 | 'd'<<24), - MANICHAEAN = ('m' | 'a'<<8 | 'n'<<16 | 'i'<<24), - MARCHEN = ('m' | 'a'<<8 | 'r'<<16 | 'c'<<24), - MASARAM_GONDI = ('g' | 'o'<<8 | 'n'<<16 | 'm'<<24), - MEDEFAIDRIN = ('m' | 'e'<<8 | 'd'<<16 | 'f'<<24), - MEETEI_MAYEK = ('m' | 't'<<8 | 'e'<<16 | 'i'<<24), - MENDE_KIKAKUI = ('m' | 'e'<<8 | 'n'<<16 | 'd'<<24), - MEROITIC_CURSIVE = ('m' | 'e'<<8 | 'r'<<16 | 'c'<<24), - MEROITIC_HIEROGLYPHS = ('m' | 'e'<<8 | 'r'<<16 | 'o'<<24), - MIAO = ('p' | 'l'<<8 | 'r'<<16 | 'd'<<24), - MODI = ('m' | 'o'<<8 | 'd'<<16 | 'i'<<24), - MONGOLIAN = ('m' | 'o'<<8 | 'n'<<16 | 'g'<<24), - MRO = ('m' | 'r'<<8 | 'o'<<16 | 'o'<<24), - MULTANI = ('m' | 'u'<<8 | 'l'<<16 | 't'<<24), - MYANMAR = ('m' | 'y'<<8 | 'm'<<16 | '2'<<24), - NABATAEAN = ('n' | 'b'<<8 | 'a'<<16 | 't'<<24), - NAG_MUNDARI = ('n' | 'a'<<8 | 'g'<<16 | 'm'<<24), - NANDINAGARI = ('n' | 'a'<<8 | 'n'<<16 | 'd'<<24), - NEWA = ('n' | 'e'<<8 | 'w'<<16 | 'a'<<24), - NEW_TAI_LUE = ('t' | 'a'<<8 | 'l'<<16 | 'u'<<24), - NKO = ('n' | 'k'<<8 | 'o'<<16 | ' '<<24), - NUSHU = ('n' | 's'<<8 | 'h'<<16 | 'u'<<24), - NYIAKENG_PUACHUE_HMONG = ('h' | 'm'<<8 | 'n'<<16 | 'p'<<24), - OGHAM = ('o' | 'g'<<8 | 'a'<<16 | 'm'<<24), - OL_CHIKI = ('o' | 'l'<<8 | 'c'<<16 | 'k'<<24), - OL_ONAL = ('o' | 'n'<<8 | 'a'<<16 | 'o'<<24), - OLD_ITALIC = ('i' | 't'<<8 | 'a'<<16 | 'l'<<24), - OLD_HUNGARIAN = ('h' | 'u'<<8 | 'n'<<16 | 'g'<<24), - OLD_NORTH_ARABIAN = ('n' | 'a'<<8 | 'r'<<16 | 'b'<<24), - OLD_PERMIC = ('p' | 'e'<<8 | 'r'<<16 | 'm'<<24), - OLD_PERSIAN_CUNEIFORM = ('x' | 'p'<<8 | 'e'<<16 | 'o'<<24), - OLD_SOGDIAN = ('s' | 'o'<<8 | 'g'<<16 | 'o'<<24), - OLD_SOUTH_ARABIAN = ('s' | 'a'<<8 | 'r'<<16 | 'b'<<24), - OLD_TURKIC = ('o' | 'r'<<8 | 'k'<<16 | 'h'<<24), - OLD_UYGHUR = ('o' | 'u'<<8 | 'g'<<16 | 'r'<<24), - ODIA = ('o' | 'r'<<8 | 'y'<<16 | '2'<<24), - OSAGE = ('o' | 's'<<8 | 'g'<<16 | 'e'<<24), - OSMANYA = ('o' | 's'<<8 | 'm'<<16 | 'a'<<24), - PAHAWH_HMONG = ('h' | 'm'<<8 | 'n'<<16 | 'g'<<24), - PALMYRENE = ('p' | 'a'<<8 | 'l'<<16 | 'm'<<24), - PAU_CIN_HAU = ('p' | 'a'<<8 | 'u'<<16 | 'c'<<24), - PHAGS_PA = ('p' | 'h'<<8 | 'a'<<16 | 'g'<<24), - PHOENICIAN = ('p' | 'h'<<8 | 'n'<<16 | 'x'<<24), - PSALTER_PAHLAVI = ('p' | 'h'<<8 | 'l'<<16 | 'p'<<24), - REJANG = ('r' | 'j'<<8 | 'n'<<16 | 'g'<<24), - RUNIC = ('r' | 'u'<<8 | 'n'<<16 | 'r'<<24), - SAMARITAN = ('s' | 'a'<<8 | 'm'<<16 | 'r'<<24), - SAURASHTRA = ('s' | 'a'<<8 | 'u'<<16 | 'r'<<24), - SHARADA = ('s' | 'h'<<8 | 'r'<<16 | 'd'<<24), - SHAVIAN = ('s' | 'h'<<8 | 'a'<<16 | 'w'<<24), - SIDDHAM = ('s' | 'i'<<8 | 'd'<<16 | 'd'<<24), - SIGN_WRITING = ('s' | 'g'<<8 | 'n'<<16 | 'w'<<24), - SOGDIAN = ('s' | 'o'<<8 | 'g'<<16 | 'd'<<24), - SINHALA = ('s' | 'i'<<8 | 'n'<<16 | 'h'<<24), - SORA_SOMPENG = ('s' | 'o'<<8 | 'r'<<16 | 'a'<<24), - SOYOMBO = ('s' | 'o'<<8 | 'y'<<16 | 'o'<<24), - SUMERO_AKKADIAN_CUNEIFORM = ('x' | 's'<<8 | 'u'<<16 | 'x'<<24), - SUNDANESE = ('s' | 'u'<<8 | 'n'<<16 | 'd'<<24), - SUNUWAR = ('s' | 'u'<<8 | 'n'<<16 | 'u'<<24), - SYLOTI_NAGRI = ('s' | 'y'<<8 | 'l'<<16 | 'o'<<24), - SYRIAC = ('s' | 'y'<<8 | 'r'<<16 | 'c'<<24), - TAGALOG = ('t' | 'g'<<8 | 'l'<<16 | 'g'<<24), - TAGBANWA = ('t' | 'a'<<8 | 'g'<<16 | 'b'<<24), - TAI_LE = ('t' | 'a'<<8 | 'l'<<16 | 'e'<<24), - TAI_THAM = ('l' | 'a'<<8 | 'n'<<16 | 'a'<<24), - TAI_VIET = ('t' | 'a'<<8 | 'v'<<16 | 't'<<24), - TAKRI = ('t' | 'a'<<8 | 'k'<<16 | 'r'<<24), - TAMIL = ('t' | 'm'<<8 | 'l'<<16 | '2'<<24), - TANGSA = ('t' | 'n'<<8 | 's'<<16 | 'a'<<24), - TANGUT = ('t' | 'a'<<8 | 'n'<<16 | 'g'<<24), - TELUGU = ('t' | 'e'<<8 | 'l'<<16 | '2'<<24), - THAANA = ('t' | 'h'<<8 | 'a'<<16 | 'a'<<24), - THAI = ('t' | 'h'<<8 | 'a'<<16 | 'i'<<24), - TIBETAN = ('t' | 'i'<<8 | 'b'<<16 | 't'<<24), - TIFINAGH = ('t' | 'f'<<8 | 'n'<<16 | 'g'<<24), - TIRHUTA = ('t' | 'i'<<8 | 'r'<<16 | 'h'<<24), - TODHRI = ('t' | 'o'<<8 | 'd'<<16 | 'r'<<24), - TOTO = ('t' | 'o'<<8 | 't'<<16 | 'o'<<24), - TULU_TIGALARI = ('t' | 'u'<<8 | 't'<<16 | 'g'<<24), - UGARITIC_CUNEIFORM = ('u' | 'g'<<8 | 'a'<<16 | 'r'<<24), - VAI = ('v' | 'a'<<8 | 'i'<<16 | ' '<<24), - VITHKUQI = ('v' | 'i'<<8 | 't'<<16 | 'h'<<24), - WANCHO = ('w' | 'c'<<8 | 'h'<<16 | 'o'<<24), - WARANG_CITI = ('w' | 'a'<<8 | 'r'<<16 | 'a'<<24), - YEZIDI = ('y' | 'e'<<8 | 'z'<<16 | 'i'<<24), - YI = ('y' | 'i'<<8 | ' '<<16 | ' '<<24), - ZANABAZAR_SQUARE = ('z' | 'a'<<8 | 'n'<<16 | 'b'<<24), + DONT_KNOW = ' ' | ' '<<8 | ' '<<16 | ' '<<24, + ADLAM = 'a' | 'd'<<8 | 'l'<<16 | 'm'<<24, + AHOM = 'a' | 'h'<<8 | 'o'<<16 | 'm'<<24, + ANATOLIAN_HIEROGLYPHS = 'h' | 'l'<<8 | 'u'<<16 | 'w'<<24, + ARABIC = 'a' | 'r'<<8 | 'a'<<16 | 'b'<<24, + ARMENIAN = 'a' | 'r'<<8 | 'm'<<16 | 'n'<<24, + AVESTAN = 'a' | 'v'<<8 | 's'<<16 | 't'<<24, + BALINESE = 'b' | 'a'<<8 | 'l'<<16 | 'i'<<24, + BAMUM = 'b' | 'a'<<8 | 'm'<<16 | 'u'<<24, + BASSA_VAH = 'b' | 'a'<<8 | 's'<<16 | 's'<<24, + BATAK = 'b' | 'a'<<8 | 't'<<16 | 'k'<<24, + BENGALI = 'b' | 'n'<<8 | 'g'<<16 | '2'<<24, + BHAIKSUKI = 'b' | 'h'<<8 | 'k'<<16 | 's'<<24, + BOPOMOFO = 'b' | 'o'<<8 | 'p'<<16 | 'o'<<24, + BRAHMI = 'b' | 'r'<<8 | 'a'<<16 | 'h'<<24, + BUGINESE = 'b' | 'u'<<8 | 'g'<<16 | 'i'<<24, + BUHID = 'b' | 'u'<<8 | 'h'<<16 | 'd'<<24, + CANADIAN_SYLLABICS = 'c' | 'a'<<8 | 'n'<<16 | 's'<<24, + CARIAN = 'c' | 'a'<<8 | 'r'<<16 | 'i'<<24, + CAUCASIAN_ALBANIAN = 'a' | 'g'<<8 | 'h'<<16 | 'b'<<24, + CHAKMA = 'c' | 'a'<<8 | 'k'<<16 | 'm'<<24, + CHAM = 'c' | 'h'<<8 | 'a'<<16 | 'm'<<24, + CHEROKEE = 'c' | 'h'<<8 | 'e'<<16 | 'r'<<24, + CHORASMIAN = 'c' | 'h'<<8 | 'r'<<16 | 's'<<24, + CJK_IDEOGRAPHIC = 'h' | 'a'<<8 | 'n'<<16 | 'i'<<24, + COPTIC = 'c' | 'o'<<8 | 'p'<<16 | 't'<<24, + CYPRIOT_SYLLABARY = 'c' | 'p'<<8 | 'r'<<16 | 't'<<24, + CYPRO_MINOAN = 'c' | 'p'<<8 | 'm'<<16 | 'n'<<24, + CYRILLIC = 'c' | 'y'<<8 | 'r'<<16 | 'l'<<24, + DEFAULT = 'D' | 'F'<<8 | 'L'<<16 | 'T'<<24, + DEFAULT2 = 'D' | 'F'<<8 | 'L'<<16 | 'T'<<24, + DESERET = 'd' | 's'<<8 | 'r'<<16 | 't'<<24, + DEVANAGARI = 'd' | 'e'<<8 | 'v'<<16 | '2'<<24, + DIVES_AKURU = 'd' | 'i'<<8 | 'a'<<16 | 'k'<<24, + DOGRA = 'd' | 'o'<<8 | 'g'<<16 | 'r'<<24, + DUPLOYAN = 'd' | 'u'<<8 | 'p'<<16 | 'l'<<24, + EGYPTIAN_HIEROGLYPHS = 'e' | 'g'<<8 | 'y'<<16 | 'p'<<24, + ELBASAN = 'e' | 'l'<<8 | 'b'<<16 | 'a'<<24, + ELYMAIC = 'e' | 'l'<<8 | 'y'<<16 | 'm'<<24, + ETHIOPIC = 'e' | 't'<<8 | 'h'<<16 | 'i'<<24, + GARAY = 'g' | 'a'<<8 | 'r'<<16 | 'a'<<24, + GEORGIAN = 'g' | 'e'<<8 | 'o'<<16 | 'r'<<24, + GLAGOLITIC = 'g' | 'l'<<8 | 'a'<<16 | 'g'<<24, + GOTHIC = 'g' | 'o'<<8 | 't'<<16 | 'h'<<24, + GRANTHA = 'g' | 'r'<<8 | 'a'<<16 | 'n'<<24, + GREEK = 'g' | 'r'<<8 | 'e'<<16 | 'k'<<24, + GUJARATI = 'g' | 'j'<<8 | 'r'<<16 | '2'<<24, + GUNJALA_GONDI = 'g' | 'o'<<8 | 'n'<<16 | 'g'<<24, + GURMUKHI = 'g' | 'u'<<8 | 'r'<<16 | '2'<<24, + GURUNG_KHEMA = 'g' | 'u'<<8 | 'k'<<16 | 'h'<<24, + HANGUL = 'h' | 'a'<<8 | 'n'<<16 | 'g'<<24, + HANIFI_ROHINGYA = 'r' | 'o'<<8 | 'h'<<16 | 'g'<<24, + HANUNOO = 'h' | 'a'<<8 | 'n'<<16 | 'o'<<24, + HATRAN = 'h' | 'a'<<8 | 't'<<16 | 'r'<<24, + HEBREW = 'h' | 'e'<<8 | 'b'<<16 | 'r'<<24, + HIRAGANA = 'k' | 'a'<<8 | 'n'<<16 | 'a'<<24, + IMPERIAL_ARAMAIC = 'a' | 'r'<<8 | 'm'<<16 | 'i'<<24, + INSCRIPTIONAL_PAHLAVI = 'p' | 'h'<<8 | 'l'<<16 | 'i'<<24, + INSCRIPTIONAL_PARTHIAN = 'p' | 'r'<<8 | 't'<<16 | 'i'<<24, + JAVANESE = 'j' | 'a'<<8 | 'v'<<16 | 'a'<<24, + KAITHI = 'k' | 't'<<8 | 'h'<<16 | 'i'<<24, + KANNADA = 'k' | 'n'<<8 | 'd'<<16 | '2'<<24, + KATAKANA = 'k' | 'a'<<8 | 'n'<<16 | 'a'<<24, + KAWI = 'k' | 'a'<<8 | 'w'<<16 | 'i'<<24, + KAYAH_LI = 'k' | 'a'<<8 | 'l'<<16 | 'i'<<24, + KHAROSHTHI = 'k' | 'h'<<8 | 'a'<<16 | 'r'<<24, + KHITAN_SMALL_SCRIPT = 'k' | 'i'<<8 | 't'<<16 | 's'<<24, + KHMER = 'k' | 'h'<<8 | 'm'<<16 | 'r'<<24, + KHOJKI = 'k' | 'h'<<8 | 'o'<<16 | 'j'<<24, + KHUDAWADI = 's' | 'i'<<8 | 'n'<<16 | 'd'<<24, + KIRAT_RAI = 'k' | 'r'<<8 | 'a'<<16 | 'i'<<24, + LAO = 'l' | 'a'<<8 | 'o'<<16 | ' '<<24, + LATIN = 'l' | 'a'<<8 | 't'<<16 | 'n'<<24, + LEPCHA = 'l' | 'e'<<8 | 'p'<<16 | 'c'<<24, + LIMBU = 'l' | 'i'<<8 | 'm'<<16 | 'b'<<24, + LINEAR_A = 'l' | 'i'<<8 | 'n'<<16 | 'a'<<24, + LINEAR_B = 'l' | 'i'<<8 | 'n'<<16 | 'b'<<24, + LISU = 'l' | 'i'<<8 | 's'<<16 | 'u'<<24, + LYCIAN = 'l' | 'y'<<8 | 'c'<<16 | 'i'<<24, + LYDIAN = 'l' | 'y'<<8 | 'd'<<16 | 'i'<<24, + MAHAJANI = 'm' | 'a'<<8 | 'h'<<16 | 'j'<<24, + MAKASAR = 'm' | 'a'<<8 | 'k'<<16 | 'a'<<24, + MALAYALAM = 'm' | 'l'<<8 | 'm'<<16 | '2'<<24, + MANDAIC = 'm' | 'a'<<8 | 'n'<<16 | 'd'<<24, + MANICHAEAN = 'm' | 'a'<<8 | 'n'<<16 | 'i'<<24, + MARCHEN = 'm' | 'a'<<8 | 'r'<<16 | 'c'<<24, + MASARAM_GONDI = 'g' | 'o'<<8 | 'n'<<16 | 'm'<<24, + MEDEFAIDRIN = 'm' | 'e'<<8 | 'd'<<16 | 'f'<<24, + MEETEI_MAYEK = 'm' | 't'<<8 | 'e'<<16 | 'i'<<24, + MENDE_KIKAKUI = 'm' | 'e'<<8 | 'n'<<16 | 'd'<<24, + MEROITIC_CURSIVE = 'm' | 'e'<<8 | 'r'<<16 | 'c'<<24, + MEROITIC_HIEROGLYPHS = 'm' | 'e'<<8 | 'r'<<16 | 'o'<<24, + MIAO = 'p' | 'l'<<8 | 'r'<<16 | 'd'<<24, + MODI = 'm' | 'o'<<8 | 'd'<<16 | 'i'<<24, + MONGOLIAN = 'm' | 'o'<<8 | 'n'<<16 | 'g'<<24, + MRO = 'm' | 'r'<<8 | 'o'<<16 | 'o'<<24, + MULTANI = 'm' | 'u'<<8 | 'l'<<16 | 't'<<24, + MYANMAR = 'm' | 'y'<<8 | 'm'<<16 | '2'<<24, + NABATAEAN = 'n' | 'b'<<8 | 'a'<<16 | 't'<<24, + NAG_MUNDARI = 'n' | 'a'<<8 | 'g'<<16 | 'm'<<24, + NANDINAGARI = 'n' | 'a'<<8 | 'n'<<16 | 'd'<<24, + NEWA = 'n' | 'e'<<8 | 'w'<<16 | 'a'<<24, + NEW_TAI_LUE = 't' | 'a'<<8 | 'l'<<16 | 'u'<<24, + NKO = 'n' | 'k'<<8 | 'o'<<16 | ' '<<24, + NUSHU = 'n' | 's'<<8 | 'h'<<16 | 'u'<<24, + NYIAKENG_PUACHUE_HMONG = 'h' | 'm'<<8 | 'n'<<16 | 'p'<<24, + OGHAM = 'o' | 'g'<<8 | 'a'<<16 | 'm'<<24, + OL_CHIKI = 'o' | 'l'<<8 | 'c'<<16 | 'k'<<24, + OL_ONAL = 'o' | 'n'<<8 | 'a'<<16 | 'o'<<24, + OLD_ITALIC = 'i' | 't'<<8 | 'a'<<16 | 'l'<<24, + OLD_HUNGARIAN = 'h' | 'u'<<8 | 'n'<<16 | 'g'<<24, + OLD_NORTH_ARABIAN = 'n' | 'a'<<8 | 'r'<<16 | 'b'<<24, + OLD_PERMIC = 'p' | 'e'<<8 | 'r'<<16 | 'm'<<24, + OLD_PERSIAN_CUNEIFORM = 'x' | 'p'<<8 | 'e'<<16 | 'o'<<24, + OLD_SOGDIAN = 's' | 'o'<<8 | 'g'<<16 | 'o'<<24, + OLD_SOUTH_ARABIAN = 's' | 'a'<<8 | 'r'<<16 | 'b'<<24, + OLD_TURKIC = 'o' | 'r'<<8 | 'k'<<16 | 'h'<<24, + OLD_UYGHUR = 'o' | 'u'<<8 | 'g'<<16 | 'r'<<24, + ODIA = 'o' | 'r'<<8 | 'y'<<16 | '2'<<24, + OSAGE = 'o' | 's'<<8 | 'g'<<16 | 'e'<<24, + OSMANYA = 'o' | 's'<<8 | 'm'<<16 | 'a'<<24, + PAHAWH_HMONG = 'h' | 'm'<<8 | 'n'<<16 | 'g'<<24, + PALMYRENE = 'p' | 'a'<<8 | 'l'<<16 | 'm'<<24, + PAU_CIN_HAU = 'p' | 'a'<<8 | 'u'<<16 | 'c'<<24, + PHAGS_PA = 'p' | 'h'<<8 | 'a'<<16 | 'g'<<24, + PHOENICIAN = 'p' | 'h'<<8 | 'n'<<16 | 'x'<<24, + PSALTER_PAHLAVI = 'p' | 'h'<<8 | 'l'<<16 | 'p'<<24, + REJANG = 'r' | 'j'<<8 | 'n'<<16 | 'g'<<24, + RUNIC = 'r' | 'u'<<8 | 'n'<<16 | 'r'<<24, + SAMARITAN = 's' | 'a'<<8 | 'm'<<16 | 'r'<<24, + SAURASHTRA = 's' | 'a'<<8 | 'u'<<16 | 'r'<<24, + SHARADA = 's' | 'h'<<8 | 'r'<<16 | 'd'<<24, + SHAVIAN = 's' | 'h'<<8 | 'a'<<16 | 'w'<<24, + SIDDHAM = 's' | 'i'<<8 | 'd'<<16 | 'd'<<24, + SIGN_WRITING = 's' | 'g'<<8 | 'n'<<16 | 'w'<<24, + SOGDIAN = 's' | 'o'<<8 | 'g'<<16 | 'd'<<24, + SINHALA = 's' | 'i'<<8 | 'n'<<16 | 'h'<<24, + SORA_SOMPENG = 's' | 'o'<<8 | 'r'<<16 | 'a'<<24, + SOYOMBO = 's' | 'o'<<8 | 'y'<<16 | 'o'<<24, + SUMERO_AKKADIAN_CUNEIFORM = 'x' | 's'<<8 | 'u'<<16 | 'x'<<24, + SUNDANESE = 's' | 'u'<<8 | 'n'<<16 | 'd'<<24, + SUNUWAR = 's' | 'u'<<8 | 'n'<<16 | 'u'<<24, + SYLOTI_NAGRI = 's' | 'y'<<8 | 'l'<<16 | 'o'<<24, + SYRIAC = 's' | 'y'<<8 | 'r'<<16 | 'c'<<24, + TAGALOG = 't' | 'g'<<8 | 'l'<<16 | 'g'<<24, + TAGBANWA = 't' | 'a'<<8 | 'g'<<16 | 'b'<<24, + TAI_LE = 't' | 'a'<<8 | 'l'<<16 | 'e'<<24, + TAI_THAM = 'l' | 'a'<<8 | 'n'<<16 | 'a'<<24, + TAI_VIET = 't' | 'a'<<8 | 'v'<<16 | 't'<<24, + TAKRI = 't' | 'a'<<8 | 'k'<<16 | 'r'<<24, + TAMIL = 't' | 'm'<<8 | 'l'<<16 | '2'<<24, + TANGSA = 't' | 'n'<<8 | 's'<<16 | 'a'<<24, + TANGUT = 't' | 'a'<<8 | 'n'<<16 | 'g'<<24, + TELUGU = 't' | 'e'<<8 | 'l'<<16 | '2'<<24, + THAANA = 't' | 'h'<<8 | 'a'<<16 | 'a'<<24, + THAI = 't' | 'h'<<8 | 'a'<<16 | 'i'<<24, + TIBETAN = 't' | 'i'<<8 | 'b'<<16 | 't'<<24, + TIFINAGH = 't' | 'f'<<8 | 'n'<<16 | 'g'<<24, + TIRHUTA = 't' | 'i'<<8 | 'r'<<16 | 'h'<<24, + TODHRI = 't' | 'o'<<8 | 'd'<<16 | 'r'<<24, + TOTO = 't' | 'o'<<8 | 't'<<16 | 'o'<<24, + TULU_TIGALARI = 't' | 'u'<<8 | 't'<<16 | 'g'<<24, + UGARITIC_CUNEIFORM = 'u' | 'g'<<8 | 'a'<<16 | 'r'<<24, + VAI = 'v' | 'a'<<8 | 'i'<<16 | ' '<<24, + VITHKUQI = 'v' | 'i'<<8 | 't'<<16 | 'h'<<24, + WANCHO = 'w' | 'c'<<8 | 'h'<<16 | 'o'<<24, + WARANG_CITI = 'w' | 'a'<<8 | 'r'<<16 | 'a'<<24, + YEZIDI = 'y' | 'e'<<8 | 'z'<<16 | 'i'<<24, + YI = 'y' | 'i'<<8 | ' '<<16 | ' '<<24, + ZANABAZAR_SQUARE = 'z' | 'a'<<8 | 'n'<<16 | 'b'<<24, } script :: enum u32 { @@ -1415,324 +1503,446 @@ script :: enum u32 { } feature_tag :: enum u32 { - UNREGISTERED = 0, - isol = ('i' | 's'<<8 | 'o'<<16 | 'l'<<24), /* Isolated Forms */ - fina = ('f' | 'i'<<8 | 'n'<<16 | 'a'<<24), /* Terminal Forms */ - fin2 = ('f' | 'i'<<8 | 'n'<<16 | '2'<<24), /* Terminal Forms #2 */ - fin3 = ('f' | 'i'<<8 | 'n'<<16 | '3'<<24), /* Terminal Forms #3 */ - medi = ('m' | 'e'<<8 | 'd'<<16 | 'i'<<24), /* Medial Forms */ - med2 = ('m' | 'e'<<8 | 'd'<<16 | '2'<<24), /* Medial Forms #2 */ - init = ('i' | 'n'<<8 | 'i'<<16 | 't'<<24), /* Initial Forms */ - ljmo = ('l' | 'j'<<8 | 'm'<<16 | 'o'<<24), /* Leading Jamo Forms */ - vjmo = ('v' | 'j'<<8 | 'm'<<16 | 'o'<<24), /* Vowel Jamo Forms */ - tjmo = ('t' | 'j'<<8 | 'm'<<16 | 'o'<<24), /* Trailing Jamo Forms */ - rphf = ('r' | 'p'<<8 | 'h'<<16 | 'f'<<24), /* Reph Form */ - blwf = ('b' | 'l'<<8 | 'w'<<16 | 'f'<<24), /* Below-base Forms */ - half = ('h' | 'a'<<8 | 'l'<<16 | 'f'<<24), /* Half Forms */ - pstf = ('p' | 's'<<8 | 't'<<16 | 'f'<<24), /* Post-base Forms */ - abvf = ('a' | 'b'<<8 | 'v'<<16 | 'f'<<24), /* Above-base Forms */ - pref = ('p' | 'r'<<8 | 'e'<<16 | 'f'<<24), /* Pre-base Forms */ - numr = ('n' | 'u'<<8 | 'm'<<16 | 'r'<<24), /* Numerators */ - frac = ('f' | 'r'<<8 | 'a'<<16 | 'c'<<24), /* Fractions */ - dnom = ('d' | 'n'<<8 | 'o'<<16 | 'm'<<24), /* Denominators */ - cfar = ('c' | 'f'<<8 | 'a'<<16 | 'r'<<24), /* Conjunct Form After Ro */ - aalt = ('a' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Access All Alternates */ - abvm = ('a' | 'b'<<8 | 'v'<<16 | 'm'<<24), /* Above-base Mark Positioning */ - abvs = ('a' | 'b'<<8 | 'v'<<16 | 's'<<24), /* Above-base Substitutions */ - afrc = ('a' | 'f'<<8 | 'r'<<16 | 'c'<<24), /* Alternative Fractions */ - akhn = ('a' | 'k'<<8 | 'h'<<16 | 'n'<<24), /* Akhand */ - apkn = ('a' | 'p'<<8 | 'k'<<16 | 'n'<<24), /* Kerning for Alternate Proportional Widths */ - blwm = ('b' | 'l'<<8 | 'w'<<16 | 'm'<<24), /* Below-base Mark Positioning */ - blws = ('b' | 'l'<<8 | 'w'<<16 | 's'<<24), /* Below-base Substitutions */ - calt = ('c' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Contextual Alternates */ - Case = ('c' | 'a'<<8 | 's'<<16 | 'e'<<24), /* Case-sensitive Forms */ - ccmp = ('c' | 'c'<<8 | 'm'<<16 | 'p'<<24), /* Glyph Composition / Decomposition */ - chws = ('c' | 'h'<<8 | 'w'<<16 | 's'<<24), /* Contextual Half-width Spacing */ - cjct = ('c' | 'j'<<8 | 'c'<<16 | 't'<<24), /* Conjunct Forms */ - clig = ('c' | 'l'<<8 | 'i'<<16 | 'g'<<24), /* Contextual Ligatures */ - cpct = ('c' | 'p'<<8 | 'c'<<16 | 't'<<24), /* Centered CJK Punctuation */ - cpsp = ('c' | 'p'<<8 | 's'<<16 | 'p'<<24), /* Capital Spacing */ - cswh = ('c' | 's'<<8 | 'w'<<16 | 'h'<<24), /* Contextual Swash */ - curs = ('c' | 'u'<<8 | 'r'<<16 | 's'<<24), /* Cursive Positioning */ - cv01 = ('c' | 'v'<<8 | '0'<<16 | '1'<<24), /* 'cv99' Character Variant 1 – Character Variant 99 */ - c2pc = ('c' | '2'<<8 | 'p'<<16 | 'c'<<24), /* Petite Capitals From Capitals */ - c2sc = ('c' | '2'<<8 | 's'<<16 | 'c'<<24), /* Small Capitals From Capitals */ - dist = ('d' | 'i'<<8 | 's'<<16 | 't'<<24), /* Distances */ - dlig = ('d' | 'l'<<8 | 'i'<<16 | 'g'<<24), /* Discretionary Ligatures */ - dtls = ('d' | 't'<<8 | 'l'<<16 | 's'<<24), /* Dotless Forms */ - expt = ('e' | 'x'<<8 | 'p'<<16 | 't'<<24), /* Expert Forms */ - falt = ('f' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Final Glyph on Line Alternates */ - flac = ('f' | 'l'<<8 | 'a'<<16 | 'c'<<24), /* Flattened Accent Forms */ - fwid = ('f' | 'w'<<8 | 'i'<<16 | 'd'<<24), /* Full Widths */ - haln = ('h' | 'a'<<8 | 'l'<<16 | 'n'<<24), /* Halant Forms */ - halt = ('h' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Alternate Half Widths */ - hist = ('h' | 'i'<<8 | 's'<<16 | 't'<<24), /* Historical Forms */ - hkna = ('h' | 'k'<<8 | 'n'<<16 | 'a'<<24), /* Horizontal Kana Alternates */ - hlig = ('h' | 'l'<<8 | 'i'<<16 | 'g'<<24), /* Historical Ligatures */ - hngl = ('h' | 'n'<<8 | 'g'<<16 | 'l'<<24), /* Hangul */ - hojo = ('h' | 'o'<<8 | 'j'<<16 | 'o'<<24), /* Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) */ - hwid = ('h' | 'w'<<8 | 'i'<<16 | 'd'<<24), /* Half Widths */ - ital = ('i' | 't'<<8 | 'a'<<16 | 'l'<<24), /* Italics */ - jalt = ('j' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Justification Alternates */ - jp78 = ('j' | 'p'<<8 | '7'<<16 | '8'<<24), /* JIS78 Forms */ - jp83 = ('j' | 'p'<<8 | '8'<<16 | '3'<<24), /* JIS83 Forms */ - jp90 = ('j' | 'p'<<8 | '9'<<16 | '0'<<24), /* JIS90 Forms */ - jp04 = ('j' | 'p'<<8 | '0'<<16 | '4'<<24), /* JIS2004 Forms */ - kern = ('k' | 'e'<<8 | 'r'<<16 | 'n'<<24), /* Kerning */ - lfbd = ('l' | 'f'<<8 | 'b'<<16 | 'd'<<24), /* Left Bounds */ - liga = ('l' | 'i'<<8 | 'g'<<16 | 'a'<<24), /* Standard Ligatures */ - lnum = ('l' | 'n'<<8 | 'u'<<16 | 'm'<<24), /* Lining Figures */ - locl = ('l' | 'o'<<8 | 'c'<<16 | 'l'<<24), /* Localized Forms */ - ltra = ('l' | 't'<<8 | 'r'<<16 | 'a'<<24), /* Left-to-right Alternates */ - ltrm = ('l' | 't'<<8 | 'r'<<16 | 'm'<<24), /* Left-to-right Mirrored Forms */ - mark = ('m' | 'a'<<8 | 'r'<<16 | 'k'<<24), /* Mark Positioning */ - mgrk = ('m' | 'g'<<8 | 'r'<<16 | 'k'<<24), /* Mathematical Greek */ - mkmk = ('m' | 'k'<<8 | 'm'<<16 | 'k'<<24), /* Mark to Mark Positioning */ - mset = ('m' | 's'<<8 | 'e'<<16 | 't'<<24), /* Mark Positioning via Substitution */ - nalt = ('n' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Alternate Annotation Forms */ - nlck = ('n' | 'l'<<8 | 'c'<<16 | 'k'<<24), /* NLC Kanji Forms */ - nukt = ('n' | 'u'<<8 | 'k'<<16 | 't'<<24), /* Nukta Forms */ - onum = ('o' | 'n'<<8 | 'u'<<16 | 'm'<<24), /* Oldstyle Figures */ - opbd = ('o' | 'p'<<8 | 'b'<<16 | 'd'<<24), /* Optical Bounds */ - ordn = ('o' | 'r'<<8 | 'd'<<16 | 'n'<<24), /* Ordinals */ - ornm = ('o' | 'r'<<8 | 'n'<<16 | 'm'<<24), /* Ornaments */ - palt = ('p' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Proportional Alternate Widths */ - pcap = ('p' | 'c'<<8 | 'a'<<16 | 'p'<<24), /* Petite Capitals */ - pkna = ('p' | 'k'<<8 | 'n'<<16 | 'a'<<24), /* Proportional Kana */ - pnum = ('p' | 'n'<<8 | 'u'<<16 | 'm'<<24), /* Proportional Figures */ - pres = ('p' | 'r'<<8 | 'e'<<16 | 's'<<24), /* Pre-base Substitutions */ - psts = ('p' | 's'<<8 | 't'<<16 | 's'<<24), /* Post-base Substitutions */ - pwid = ('p' | 'w'<<8 | 'i'<<16 | 'd'<<24), /* Proportional Widths */ - qwid = ('q' | 'w'<<8 | 'i'<<16 | 'd'<<24), /* Quarter Widths */ - rand = ('r' | 'a'<<8 | 'n'<<16 | 'd'<<24), /* Randomize */ - rclt = ('r' | 'c'<<8 | 'l'<<16 | 't'<<24), /* Required Contextual Alternates */ - rkrf = ('r' | 'k'<<8 | 'r'<<16 | 'f'<<24), /* Rakar Forms */ - rlig = ('r' | 'l'<<8 | 'i'<<16 | 'g'<<24), /* Required Ligatures */ - rtbd = ('r' | 't'<<8 | 'b'<<16 | 'd'<<24), /* Right Bounds */ - rtla = ('r' | 't'<<8 | 'l'<<16 | 'a'<<24), /* Right-to-left Alternates */ - rtlm = ('r' | 't'<<8 | 'l'<<16 | 'm'<<24), /* Right-to-left Mirrored Forms */ - ruby = ('r' | 'u'<<8 | 'b'<<16 | 'y'<<24), /* Ruby Notation Forms */ - rvrn = ('r' | 'v'<<8 | 'r'<<16 | 'n'<<24), /* Required Variation Alternates */ - salt = ('s' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Stylistic Alternates */ - sinf = ('s' | 'i'<<8 | 'n'<<16 | 'f'<<24), /* Scientific Inferiors */ - size = ('s' | 'i'<<8 | 'z'<<16 | 'e'<<24), /* Optical size */ - smcp = ('s' | 'm'<<8 | 'c'<<16 | 'p'<<24), /* Small Capitals */ - smpl = ('s' | 'm'<<8 | 'p'<<16 | 'l'<<24), /* Simplified Forms */ - ss01 = ('s' | 's'<<8 | '0'<<16 | '1'<<24), /* 'ss20' Stylistic Set 1 – Stylistic Set 20 */ - ssty = ('s' | 's'<<8 | 't'<<16 | 'y'<<24), /* Math Script-style Alternates */ - stch = ('s' | 't'<<8 | 'c'<<16 | 'h'<<24), /* Stretching Glyph Decomposition */ - subs = ('s' | 'u'<<8 | 'b'<<16 | 's'<<24), /* Subscript */ - sups = ('s' | 'u'<<8 | 'p'<<16 | 's'<<24), /* Superscript */ - swsh = ('s' | 'w'<<8 | 's'<<16 | 'h'<<24), /* Swash */ - test = ('t' | 'e'<<8 | 's'<<16 | 't'<<24), /* Test features, only for development */ - titl = ('t' | 'i'<<8 | 't'<<16 | 'l'<<24), /* Titling */ - tnam = ('t' | 'n'<<8 | 'a'<<16 | 'm'<<24), /* Traditional Name Forms */ - tnum = ('t' | 'n'<<8 | 'u'<<16 | 'm'<<24), /* Tabular Figures */ - trad = ('t' | 'r'<<8 | 'a'<<16 | 'd'<<24), /* Traditional Forms */ - twid = ('t' | 'w'<<8 | 'i'<<16 | 'd'<<24), /* Third Widths */ - unic = ('u' | 'n'<<8 | 'i'<<16 | 'c'<<24), /* Unicase */ - valt = ('v' | 'a'<<8 | 'l'<<16 | 't'<<24), /* Alternate Vertical Metrics */ - vapk = ('v' | 'a'<<8 | 'p'<<16 | 'k'<<24), /* Kerning for Alternate Proportional Vertical Metrics */ - vatu = ('v' | 'a'<<8 | 't'<<16 | 'u'<<24), /* Vattu Variants */ - vchw = ('v' | 'c'<<8 | 'h'<<16 | 'w'<<24), /* Vertical Contextual Half-width Spacing */ - vert = ('v' | 'e'<<8 | 'r'<<16 | 't'<<24), /* Vertical Alternates */ - vhal = ('v' | 'h'<<8 | 'a'<<16 | 'l'<<24), /* Alternate Vertical Half Metrics */ - vkna = ('v' | 'k'<<8 | 'n'<<16 | 'a'<<24), /* Vertical Kana Alternates */ - vkrn = ('v' | 'k'<<8 | 'r'<<16 | 'n'<<24), /* Vertical Kerning */ - vpal = ('v' | 'p'<<8 | 'a'<<16 | 'l'<<24), /* Proportional Alternate Vertical Metrics */ - vrt2 = ('v' | 'r'<<8 | 't'<<16 | '2'<<24), /* Vertical Alternates and Rotation */ - vrtr = ('v' | 'r'<<8 | 't'<<16 | 'r'<<24), /* Vertical Alternates for Rotation */ - zero = ('z' | 'e'<<8 | 'r'<<16 | 'o'<<24), /* Slashed Zero */ + UNREGISTERED = 0, // Features that aren't pre-defined in the OpenType spec + isol = 'i' | 's'<<8 | 'o'<<16 | 'l'<<24, // Isolated Forms + fina = 'f' | 'i'<<8 | 'n'<<16 | 'a'<<24, // Terminal Forms + fin2 = 'f' | 'i'<<8 | 'n'<<16 | '2'<<24, // Terminal Forms #2 + fin3 = 'f' | 'i'<<8 | 'n'<<16 | '3'<<24, // Terminal Forms #3 + medi = 'm' | 'e'<<8 | 'd'<<16 | 'i'<<24, // Medial Forms + med2 = 'm' | 'e'<<8 | 'd'<<16 | '2'<<24, // Medial Forms #2 + init = 'i' | 'n'<<8 | 'i'<<16 | 't'<<24, // Initial Forms + ljmo = 'l' | 'j'<<8 | 'm'<<16 | 'o'<<24, // Leading Jamo Forms + vjmo = 'v' | 'j'<<8 | 'm'<<16 | 'o'<<24, // Vowel Jamo Forms + tjmo = 't' | 'j'<<8 | 'm'<<16 | 'o'<<24, // Trailing Jamo Forms + rphf = 'r' | 'p'<<8 | 'h'<<16 | 'f'<<24, // Reph Form + blwf = 'b' | 'l'<<8 | 'w'<<16 | 'f'<<24, // Below-base Forms + half = 'h' | 'a'<<8 | 'l'<<16 | 'f'<<24, // Half Forms + pstf = 'p' | 's'<<8 | 't'<<16 | 'f'<<24, // Post-base Forms + abvf = 'a' | 'b'<<8 | 'v'<<16 | 'f'<<24, // Above-base Forms + pref = 'p' | 'r'<<8 | 'e'<<16 | 'f'<<24, // Pre-base Forms + numr = 'n' | 'u'<<8 | 'm'<<16 | 'r'<<24, // Numerators + frac = 'f' | 'r'<<8 | 'a'<<16 | 'c'<<24, // Fractions + dnom = 'd' | 'n'<<8 | 'o'<<16 | 'm'<<24, // Denominators + cfar = 'c' | 'f'<<8 | 'a'<<16 | 'r'<<24, // Conjunct Form After Ro + aalt = 'a' | 'a'<<8 | 'l'<<16 | 't'<<24, // Access All Alternates + abvm = 'a' | 'b'<<8 | 'v'<<16 | 'm'<<24, // Above-base Mark Positioning + abvs = 'a' | 'b'<<8 | 'v'<<16 | 's'<<24, // Above-base Substitutions + afrc = 'a' | 'f'<<8 | 'r'<<16 | 'c'<<24, // Alternative Fractions + akhn = 'a' | 'k'<<8 | 'h'<<16 | 'n'<<24, // Akhand + apkn = 'a' | 'p'<<8 | 'k'<<16 | 'n'<<24, // Kerning for Alternate Proportional Widths + blwm = 'b' | 'l'<<8 | 'w'<<16 | 'm'<<24, // Below-base Mark Positioning + blws = 'b' | 'l'<<8 | 'w'<<16 | 's'<<24, // Below-base Substitutions + calt = 'c' | 'a'<<8 | 'l'<<16 | 't'<<24, // Contextual Alternates + Case = 'c' | 'a'<<8 | 's'<<16 | 'e'<<24, // Case-sensitive Forms + ccmp = 'c' | 'c'<<8 | 'm'<<16 | 'p'<<24, // Glyph Composition / Decomposition + chws = 'c' | 'h'<<8 | 'w'<<16 | 's'<<24, // Contextual Half-width Spacing + cjct = 'c' | 'j'<<8 | 'c'<<16 | 't'<<24, // Conjunct Forms + clig = 'c' | 'l'<<8 | 'i'<<16 | 'g'<<24, // Contextual Ligatures + cpct = 'c' | 'p'<<8 | 'c'<<16 | 't'<<24, // Centered CJK Punctuation + cpsp = 'c' | 'p'<<8 | 's'<<16 | 'p'<<24, // Capital Spacing + cswh = 'c' | 's'<<8 | 'w'<<16 | 'h'<<24, // Contextual Swash + curs = 'c' | 'u'<<8 | 'r'<<16 | 's'<<24, // Cursive Positioning + cv01 = 'c' | 'v'<<8 | '0'<<16 | '1'<<24, // Character Variant 1 + cv02 = 'c' | 'v'<<8 | '0'<<16 | '2'<<24, // Character Variant 2 + cv03 = 'c' | 'v'<<8 | '0'<<16 | '3'<<24, // Character Variant 3 + cv04 = 'c' | 'v'<<8 | '0'<<16 | '4'<<24, // Character Variant 4 + cv05 = 'c' | 'v'<<8 | '0'<<16 | '5'<<24, // Character Variant 5 + cv06 = 'c' | 'v'<<8 | '0'<<16 | '6'<<24, // Character Variant 6 + cv07 = 'c' | 'v'<<8 | '0'<<16 | '7'<<24, // Character Variant 7 + cv08 = 'c' | 'v'<<8 | '0'<<16 | '8'<<24, // Character Variant 8 + cv09 = 'c' | 'v'<<8 | '0'<<16 | '9'<<24, // Character Variant 9 + cv10 = 'c' | 'v'<<8 | '1'<<16 | '0'<<24, // Character Variant 10 + cv11 = 'c' | 'v'<<8 | '1'<<16 | '1'<<24, // Character Variant 11 + cv12 = 'c' | 'v'<<8 | '1'<<16 | '2'<<24, // Character Variant 12 + cv13 = 'c' | 'v'<<8 | '1'<<16 | '3'<<24, // Character Variant 13 + cv14 = 'c' | 'v'<<8 | '1'<<16 | '4'<<24, // Character Variant 14 + cv15 = 'c' | 'v'<<8 | '1'<<16 | '5'<<24, // Character Variant 15 + cv16 = 'c' | 'v'<<8 | '1'<<16 | '6'<<24, // Character Variant 16 + cv17 = 'c' | 'v'<<8 | '1'<<16 | '7'<<24, // Character Variant 17 + cv18 = 'c' | 'v'<<8 | '1'<<16 | '8'<<24, // Character Variant 18 + cv19 = 'c' | 'v'<<8 | '1'<<16 | '9'<<24, // Character Variant 19 + cv20 = 'c' | 'v'<<8 | '2'<<16 | '0'<<24, // Character Variant 20 + cv21 = 'c' | 'v'<<8 | '2'<<16 | '1'<<24, // Character Variant 21 + cv22 = 'c' | 'v'<<8 | '2'<<16 | '2'<<24, // Character Variant 22 + cv23 = 'c' | 'v'<<8 | '2'<<16 | '3'<<24, // Character Variant 23 + cv24 = 'c' | 'v'<<8 | '2'<<16 | '4'<<24, // Character Variant 24 + cv25 = 'c' | 'v'<<8 | '2'<<16 | '5'<<24, // Character Variant 25 + cv26 = 'c' | 'v'<<8 | '2'<<16 | '6'<<24, // Character Variant 26 + cv27 = 'c' | 'v'<<8 | '2'<<16 | '7'<<24, // Character Variant 27 + cv28 = 'c' | 'v'<<8 | '2'<<16 | '8'<<24, // Character Variant 28 + cv29 = 'c' | 'v'<<8 | '2'<<16 | '9'<<24, // Character Variant 29 + cv30 = 'c' | 'v'<<8 | '3'<<16 | '0'<<24, // Character Variant 30 + cv31 = 'c' | 'v'<<8 | '3'<<16 | '1'<<24, // Character Variant 31 + cv32 = 'c' | 'v'<<8 | '3'<<16 | '2'<<24, // Character Variant 32 + cv33 = 'c' | 'v'<<8 | '3'<<16 | '3'<<24, // Character Variant 33 + cv34 = 'c' | 'v'<<8 | '3'<<16 | '4'<<24, // Character Variant 34 + cv35 = 'c' | 'v'<<8 | '3'<<16 | '5'<<24, // Character Variant 35 + cv36 = 'c' | 'v'<<8 | '3'<<16 | '6'<<24, // Character Variant 36 + cv37 = 'c' | 'v'<<8 | '3'<<16 | '7'<<24, // Character Variant 37 + cv38 = 'c' | 'v'<<8 | '3'<<16 | '8'<<24, // Character Variant 38 + cv39 = 'c' | 'v'<<8 | '3'<<16 | '9'<<24, // Character Variant 39 + cv40 = 'c' | 'v'<<8 | '4'<<16 | '0'<<24, // Character Variant 40 + cv41 = 'c' | 'v'<<8 | '4'<<16 | '1'<<24, // Character Variant 41 + cv42 = 'c' | 'v'<<8 | '4'<<16 | '2'<<24, // Character Variant 42 + cv43 = 'c' | 'v'<<8 | '4'<<16 | '3'<<24, // Character Variant 43 + cv44 = 'c' | 'v'<<8 | '4'<<16 | '4'<<24, // Character Variant 44 + cv45 = 'c' | 'v'<<8 | '4'<<16 | '5'<<24, // Character Variant 45 + cv46 = 'c' | 'v'<<8 | '4'<<16 | '6'<<24, // Character Variant 46 + cv47 = 'c' | 'v'<<8 | '4'<<16 | '7'<<24, // Character Variant 47 + cv48 = 'c' | 'v'<<8 | '4'<<16 | '8'<<24, // Character Variant 48 + cv49 = 'c' | 'v'<<8 | '4'<<16 | '9'<<24, // Character Variant 49 + cv50 = 'c' | 'v'<<8 | '5'<<16 | '0'<<24, // Character Variant 50 + cv51 = 'c' | 'v'<<8 | '5'<<16 | '1'<<24, // Character Variant 51 + cv52 = 'c' | 'v'<<8 | '5'<<16 | '2'<<24, // Character Variant 52 + cv53 = 'c' | 'v'<<8 | '5'<<16 | '3'<<24, // Character Variant 53 + cv54 = 'c' | 'v'<<8 | '5'<<16 | '4'<<24, // Character Variant 54 + cv55 = 'c' | 'v'<<8 | '5'<<16 | '5'<<24, // Character Variant 55 + cv56 = 'c' | 'v'<<8 | '5'<<16 | '6'<<24, // Character Variant 56 + cv57 = 'c' | 'v'<<8 | '5'<<16 | '7'<<24, // Character Variant 57 + cv58 = 'c' | 'v'<<8 | '5'<<16 | '8'<<24, // Character Variant 58 + cv59 = 'c' | 'v'<<8 | '5'<<16 | '9'<<24, // Character Variant 59 + cv60 = 'c' | 'v'<<8 | '6'<<16 | '0'<<24, // Character Variant 60 + cv61 = 'c' | 'v'<<8 | '6'<<16 | '1'<<24, // Character Variant 61 + cv62 = 'c' | 'v'<<8 | '6'<<16 | '2'<<24, // Character Variant 62 + cv63 = 'c' | 'v'<<8 | '6'<<16 | '3'<<24, // Character Variant 63 + cv64 = 'c' | 'v'<<8 | '6'<<16 | '4'<<24, // Character Variant 64 + cv65 = 'c' | 'v'<<8 | '6'<<16 | '5'<<24, // Character Variant 65 + cv66 = 'c' | 'v'<<8 | '6'<<16 | '6'<<24, // Character Variant 66 + cv67 = 'c' | 'v'<<8 | '6'<<16 | '7'<<24, // Character Variant 67 + cv68 = 'c' | 'v'<<8 | '6'<<16 | '8'<<24, // Character Variant 68 + cv69 = 'c' | 'v'<<8 | '6'<<16 | '9'<<24, // Character Variant 69 + cv70 = 'c' | 'v'<<8 | '7'<<16 | '0'<<24, // Character Variant 70 + cv71 = 'c' | 'v'<<8 | '7'<<16 | '1'<<24, // Character Variant 71 + cv72 = 'c' | 'v'<<8 | '7'<<16 | '2'<<24, // Character Variant 72 + cv73 = 'c' | 'v'<<8 | '7'<<16 | '3'<<24, // Character Variant 73 + cv74 = 'c' | 'v'<<8 | '7'<<16 | '4'<<24, // Character Variant 74 + cv75 = 'c' | 'v'<<8 | '7'<<16 | '5'<<24, // Character Variant 75 + cv76 = 'c' | 'v'<<8 | '7'<<16 | '6'<<24, // Character Variant 76 + cv77 = 'c' | 'v'<<8 | '7'<<16 | '7'<<24, // Character Variant 77 + cv78 = 'c' | 'v'<<8 | '7'<<16 | '8'<<24, // Character Variant 78 + cv79 = 'c' | 'v'<<8 | '7'<<16 | '9'<<24, // Character Variant 79 + cv80 = 'c' | 'v'<<8 | '8'<<16 | '0'<<24, // Character Variant 80 + cv81 = 'c' | 'v'<<8 | '8'<<16 | '1'<<24, // Character Variant 81 + cv82 = 'c' | 'v'<<8 | '8'<<16 | '2'<<24, // Character Variant 82 + cv83 = 'c' | 'v'<<8 | '8'<<16 | '3'<<24, // Character Variant 83 + cv84 = 'c' | 'v'<<8 | '8'<<16 | '4'<<24, // Character Variant 84 + cv85 = 'c' | 'v'<<8 | '8'<<16 | '5'<<24, // Character Variant 85 + cv86 = 'c' | 'v'<<8 | '8'<<16 | '6'<<24, // Character Variant 86 + cv87 = 'c' | 'v'<<8 | '8'<<16 | '7'<<24, // Character Variant 87 + cv88 = 'c' | 'v'<<8 | '8'<<16 | '8'<<24, // Character Variant 88 + cv89 = 'c' | 'v'<<8 | '8'<<16 | '9'<<24, // Character Variant 89 + cv90 = 'c' | 'v'<<8 | '9'<<16 | '0'<<24, // Character Variant 90 + cv91 = 'c' | 'v'<<8 | '9'<<16 | '1'<<24, // Character Variant 91 + cv92 = 'c' | 'v'<<8 | '9'<<16 | '2'<<24, // Character Variant 92 + cv93 = 'c' | 'v'<<8 | '9'<<16 | '3'<<24, // Character Variant 93 + cv94 = 'c' | 'v'<<8 | '9'<<16 | '4'<<24, // Character Variant 94 + cv95 = 'c' | 'v'<<8 | '9'<<16 | '5'<<24, // Character Variant 95 + cv96 = 'c' | 'v'<<8 | '9'<<16 | '6'<<24, // Character Variant 96 + cv97 = 'c' | 'v'<<8 | '9'<<16 | '7'<<24, // Character Variant 97 + cv98 = 'c' | 'v'<<8 | '9'<<16 | '8'<<24, // Character Variant 98 + cv99 = 'c' | 'v'<<8 | '9'<<16 | '9'<<24, // Character Variant 99 + c2pc = 'c' | '2'<<8 | 'p'<<16 | 'c'<<24, // Petite Capitals From Capitals + c2sc = 'c' | '2'<<8 | 's'<<16 | 'c'<<24, // Small Capitals From Capitals + dist = 'd' | 'i'<<8 | 's'<<16 | 't'<<24, // Distances + dlig = 'd' | 'l'<<8 | 'i'<<16 | 'g'<<24, // Discretionary Ligatures + dtls = 'd' | 't'<<8 | 'l'<<16 | 's'<<24, // Dotless Forms + expt = 'e' | 'x'<<8 | 'p'<<16 | 't'<<24, // Expert Forms + falt = 'f' | 'a'<<8 | 'l'<<16 | 't'<<24, // Final Glyph on Line Alternates + flac = 'f' | 'l'<<8 | 'a'<<16 | 'c'<<24, // Flattened Accent Forms + fwid = 'f' | 'w'<<8 | 'i'<<16 | 'd'<<24, // Full Widths + haln = 'h' | 'a'<<8 | 'l'<<16 | 'n'<<24, // Halant Forms + halt = 'h' | 'a'<<8 | 'l'<<16 | 't'<<24, // Alternate Half Widths + hist = 'h' | 'i'<<8 | 's'<<16 | 't'<<24, // Historical Forms + hkna = 'h' | 'k'<<8 | 'n'<<16 | 'a'<<24, // Horizontal Kana Alternates + hlig = 'h' | 'l'<<8 | 'i'<<16 | 'g'<<24, // Historical Ligatures + hngl = 'h' | 'n'<<8 | 'g'<<16 | 'l'<<24, // Hangul + hojo = 'h' | 'o'<<8 | 'j'<<16 | 'o'<<24, // Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) + hwid = 'h' | 'w'<<8 | 'i'<<16 | 'd'<<24, // Half Widths + ital = 'i' | 't'<<8 | 'a'<<16 | 'l'<<24, // Italics + jalt = 'j' | 'a'<<8 | 'l'<<16 | 't'<<24, // Justification Alternates + jp78 = 'j' | 'p'<<8 | '7'<<16 | '8'<<24, // JIS78 Forms + jp83 = 'j' | 'p'<<8 | '8'<<16 | '3'<<24, // JIS83 Forms + jp90 = 'j' | 'p'<<8 | '9'<<16 | '0'<<24, // JIS90 Forms + jp04 = 'j' | 'p'<<8 | '0'<<16 | '4'<<24, // JIS2004 Forms + kern = 'k' | 'e'<<8 | 'r'<<16 | 'n'<<24, // Kerning + lfbd = 'l' | 'f'<<8 | 'b'<<16 | 'd'<<24, // Left Bounds + liga = 'l' | 'i'<<8 | 'g'<<16 | 'a'<<24, // Standard Ligatures + lnum = 'l' | 'n'<<8 | 'u'<<16 | 'm'<<24, // Lining Figures + locl = 'l' | 'o'<<8 | 'c'<<16 | 'l'<<24, // Localized Forms + ltra = 'l' | 't'<<8 | 'r'<<16 | 'a'<<24, // Left-to-right Alternates + ltrm = 'l' | 't'<<8 | 'r'<<16 | 'm'<<24, // Left-to-right Mirrored Forms + mark = 'm' | 'a'<<8 | 'r'<<16 | 'k'<<24, // Mark Positioning + mgrk = 'm' | 'g'<<8 | 'r'<<16 | 'k'<<24, // Mathematical Greek + mkmk = 'm' | 'k'<<8 | 'm'<<16 | 'k'<<24, // Mark to Mark Positioning + mset = 'm' | 's'<<8 | 'e'<<16 | 't'<<24, // Mark Positioning via Substitution + nalt = 'n' | 'a'<<8 | 'l'<<16 | 't'<<24, // Alternate Annotation Forms + nlck = 'n' | 'l'<<8 | 'c'<<16 | 'k'<<24, // NLC Kanji Forms + nukt = 'n' | 'u'<<8 | 'k'<<16 | 't'<<24, // Nukta Forms + onum = 'o' | 'n'<<8 | 'u'<<16 | 'm'<<24, // Oldstyle Figures + opbd = 'o' | 'p'<<8 | 'b'<<16 | 'd'<<24, // Optical Bounds + ordn = 'o' | 'r'<<8 | 'd'<<16 | 'n'<<24, // Ordinals + ornm = 'o' | 'r'<<8 | 'n'<<16 | 'm'<<24, // Ornaments + palt = 'p' | 'a'<<8 | 'l'<<16 | 't'<<24, // Proportional Alternate Widths + pcap = 'p' | 'c'<<8 | 'a'<<16 | 'p'<<24, // Petite Capitals + pkna = 'p' | 'k'<<8 | 'n'<<16 | 'a'<<24, // Proportional Kana + pnum = 'p' | 'n'<<8 | 'u'<<16 | 'm'<<24, // Proportional Figures + pres = 'p' | 'r'<<8 | 'e'<<16 | 's'<<24, // Pre-base Substitutions + psts = 'p' | 's'<<8 | 't'<<16 | 's'<<24, // Post-base Substitutions + pwid = 'p' | 'w'<<8 | 'i'<<16 | 'd'<<24, // Proportional Widths + qwid = 'q' | 'w'<<8 | 'i'<<16 | 'd'<<24, // Quarter Widths + rand = 'r' | 'a'<<8 | 'n'<<16 | 'd'<<24, // Randomize + rclt = 'r' | 'c'<<8 | 'l'<<16 | 't'<<24, // Required Contextual Alternates + rkrf = 'r' | 'k'<<8 | 'r'<<16 | 'f'<<24, // Rakar Forms + rlig = 'r' | 'l'<<8 | 'i'<<16 | 'g'<<24, // Required Ligatures + rtbd = 'r' | 't'<<8 | 'b'<<16 | 'd'<<24, // Right Bounds + rtla = 'r' | 't'<<8 | 'l'<<16 | 'a'<<24, // Right-to-left Alternates + rtlm = 'r' | 't'<<8 | 'l'<<16 | 'm'<<24, // Right-to-left Mirrored Forms + ruby = 'r' | 'u'<<8 | 'b'<<16 | 'y'<<24, // Ruby Notation Forms + rvrn = 'r' | 'v'<<8 | 'r'<<16 | 'n'<<24, // Required Variation Alternates + salt = 's' | 'a'<<8 | 'l'<<16 | 't'<<24, // Stylistic Alternates + sinf = 's' | 'i'<<8 | 'n'<<16 | 'f'<<24, // Scientific Inferiors + size = 's' | 'i'<<8 | 'z'<<16 | 'e'<<24, // Optical size + smcp = 's' | 'm'<<8 | 'c'<<16 | 'p'<<24, // Small Capitals + smpl = 's' | 'm'<<8 | 'p'<<16 | 'l'<<24, // Simplified Forms + ss01 = 's' | 's'<<8 | '0'<<16 | '1'<<24, // Stylistic Set 1 + ss02 = 's' | 's'<<8 | '0'<<16 | '2'<<24, // Stylistic Set 2 + ss03 = 's' | 's'<<8 | '0'<<16 | '3'<<24, // Stylistic Set 3 + ss04 = 's' | 's'<<8 | '0'<<16 | '4'<<24, // Stylistic Set 4 + ss05 = 's' | 's'<<8 | '0'<<16 | '5'<<24, // Stylistic Set 5 + ss06 = 's' | 's'<<8 | '0'<<16 | '6'<<24, // Stylistic Set 6 + ss07 = 's' | 's'<<8 | '0'<<16 | '7'<<24, // Stylistic Set 7 + ss08 = 's' | 's'<<8 | '0'<<16 | '8'<<24, // Stylistic Set 8 + ss09 = 's' | 's'<<8 | '0'<<16 | '9'<<24, // Stylistic Set 9 + ss10 = 's' | 's'<<8 | '1'<<16 | '0'<<24, // Stylistic Set 10 + ss11 = 's' | 's'<<8 | '1'<<16 | '1'<<24, // Stylistic Set 11 + ss12 = 's' | 's'<<8 | '1'<<16 | '2'<<24, // Stylistic Set 12 + ss13 = 's' | 's'<<8 | '1'<<16 | '3'<<24, // Stylistic Set 13 + ss14 = 's' | 's'<<8 | '1'<<16 | '4'<<24, // Stylistic Set 14 + ss15 = 's' | 's'<<8 | '1'<<16 | '5'<<24, // Stylistic Set 15 + ss16 = 's' | 's'<<8 | '1'<<16 | '6'<<24, // Stylistic Set 16 + ss17 = 's' | 's'<<8 | '1'<<16 | '7'<<24, // Stylistic Set 17 + ss18 = 's' | 's'<<8 | '1'<<16 | '8'<<24, // Stylistic Set 18 + ss19 = 's' | 's'<<8 | '1'<<16 | '9'<<24, // Stylistic Set 19 + ss20 = 's' | 's'<<8 | '2'<<16 | '0'<<24, // Stylistic Set 20 + ssty = 's' | 's'<<8 | 't'<<16 | 'y'<<24, // Math Script-style Alternates + stch = 's' | 't'<<8 | 'c'<<16 | 'h'<<24, // Stretching Glyph Decomposition + subs = 's' | 'u'<<8 | 'b'<<16 | 's'<<24, // Subscript + sups = 's' | 'u'<<8 | 'p'<<16 | 's'<<24, // Superscript + swsh = 's' | 'w'<<8 | 's'<<16 | 'h'<<24, // Swash + test = 't' | 'e'<<8 | 's'<<16 | 't'<<24, // Test features, only for development + titl = 't' | 'i'<<8 | 't'<<16 | 'l'<<24, // Titling + tnam = 't' | 'n'<<8 | 'a'<<16 | 'm'<<24, // Traditional Name Forms + tnum = 't' | 'n'<<8 | 'u'<<16 | 'm'<<24, // Tabular Figures + trad = 't' | 'r'<<8 | 'a'<<16 | 'd'<<24, // Traditional Forms + twid = 't' | 'w'<<8 | 'i'<<16 | 'd'<<24, // Third Widths + unic = 'u' | 'n'<<8 | 'i'<<16 | 'c'<<24, // Unicase + valt = 'v' | 'a'<<8 | 'l'<<16 | 't'<<24, // Alternate Vertical Metrics + vapk = 'v' | 'a'<<8 | 'p'<<16 | 'k'<<24, // Kerning for Alternate Proportional Vertical Metrics + vatu = 'v' | 'a'<<8 | 't'<<16 | 'u'<<24, // Vattu Variants + vchw = 'v' | 'c'<<8 | 'h'<<16 | 'w'<<24, // Vertical Contextual Half-width Spacing + vert = 'v' | 'e'<<8 | 'r'<<16 | 't'<<24, // Vertical Alternates + vhal = 'v' | 'h'<<8 | 'a'<<16 | 'l'<<24, // Alternate Vertical Half Metrics + vkna = 'v' | 'k'<<8 | 'n'<<16 | 'a'<<24, // Vertical Kana Alternates + vkrn = 'v' | 'k'<<8 | 'r'<<16 | 'n'<<24, // Vertical Kerning + vpal = 'v' | 'p'<<8 | 'a'<<16 | 'l'<<24, // Proportional Alternate Vertical Metrics + vrt2 = 'v' | 'r'<<8 | 't'<<16 | '2'<<24, // Vertical Alternates and Rotation + vrtr = 'v' | 'r'<<8 | 't'<<16 | 'r'<<24, // Vertical Alternates for Rotation + zero = 'z' | 'e'<<8 | 'r'<<16 | 'o'<<24, // Slashed Zero } -feature_id :: enum u32 { - UNREGISTERED = 0, - isol, /* Isolated Forms */ - fina, /* Terminal Forms */ - fin2, /* Terminal Forms #2 */ - fin3, /* Terminal Forms #3 */ - medi, /* Medial Forms */ - med2, /* Medial Forms #2 */ - init, /* Initial Forms */ - ljmo, /* Leading Jamo Forms */ - vjmo, /* Vowel Jamo Forms */ - tjmo, /* Trailing Jamo Forms */ - rphf, /* Reph Form */ - blwf, /* Below-base Forms */ - half, /* Half Forms */ - pstf, /* Post-base Forms */ - abvf, /* Above-base Forms */ - pref, /* Pre-base Forms */ - numr, /* Numerators */ - frac, /* Fractions */ - dnom, /* Denominators */ - cfar, /* Conjunct Form After Ro */ - aalt, /* Access All Alternates */ - abvm, /* Above-base Mark Positioning */ - abvs, /* Above-base Substitutions */ - afrc, /* Alternative Fractions */ - akhn, /* Akhand */ - apkn, /* Kerning for Alternate Proportional Widths */ - blwm, /* Below-base Mark Positioning */ - blws, /* Below-base Substitutions */ - calt, /* Contextual Alternates */ - Case, /* Case-sensitive Forms */ - ccmp, /* Glyph Composition / Decomposition */ - chws, /* Contextual Half-width Spacing */ - cjct, /* Conjunct Forms */ - clig, /* Contextual Ligatures */ - cpct, /* Centered CJK Punctuation */ - cpsp, /* Capital Spacing */ - cswh, /* Contextual Swash */ - curs, /* Cursive Positioning */ - cv01, /* 'cv99' Character Variant 1 – Character Variant 99 */ - c2pc, /* Petite Capitals From Capitals */ - c2sc, /* Small Capitals From Capitals */ - dist, /* Distances */ - dlig, /* Discretionary Ligatures */ - dtls, /* Dotless Forms */ - expt, /* Expert Forms */ - falt, /* Final Glyph on Line Alternates */ - flac, /* Flattened Accent Forms */ - fwid, /* Full Widths */ - haln, /* Halant Forms */ - halt, /* Alternate Half Widths */ - hist, /* Historical Forms */ - hkna, /* Horizontal Kana Alternates */ - hlig, /* Historical Ligatures */ - hngl, /* Hangul */ - hojo, /* Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) */ - hwid, /* Half Widths */ - ital, /* Italics */ - jalt, /* Justification Alternates */ - jp78, /* JIS78 Forms */ - jp83, /* JIS83 Forms */ - jp90, /* JIS90 Forms */ - jp04, /* JIS2004 Forms */ - kern, /* Kerning */ - lfbd, /* Left Bounds */ - liga, /* Standard Ligatures */ - lnum, /* Lining Figures */ - locl, /* Localized Forms */ - ltra, /* Left-to-right Alternates */ - ltrm, /* Left-to-right Mirrored Forms */ - mark, /* Mark Positioning */ - mgrk, /* Mathematical Greek */ - mkmk, /* Mark to Mark Positioning */ - mset, /* Mark Positioning via Substitution */ - nalt, /* Alternate Annotation Forms */ - nlck, /* NLC Kanji Forms */ - nukt, /* Nukta Forms */ - onum, /* Oldstyle Figures */ - opbd, /* Optical Bounds */ - ordn, /* Ordinals */ - ornm, /* Ornaments */ - palt, /* Proportional Alternate Widths */ - pcap, /* Petite Capitals */ - pkna, /* Proportional Kana */ - pnum, /* Proportional Figures */ - pres, /* Pre-base Substitutions */ - psts, /* Post-base Substitutions */ - pwid, /* Proportional Widths */ - qwid, /* Quarter Widths */ - rand, /* Randomize */ - rclt, /* Required Contextual Alternates */ - rkrf, /* Rakar Forms */ - rlig, /* Required Ligatures */ - rtbd, /* Right Bounds */ - rtla, /* Right-to-left Alternates */ - rtlm, /* Right-to-left Mirrored Forms */ - ruby, /* Ruby Notation Forms */ - rvrn, /* Required Variation Alternates */ - salt, /* Stylistic Alternates */ - sinf, /* Scientific Inferiors */ - size, /* Optical size */ - smcp, /* Small Capitals */ - smpl, /* Simplified Forms */ - ss01, /* 'ss20' Stylistic Set 1 – Stylistic Set 20 */ - ssty, /* Math Script-style Alternates */ - stch, /* Stretching Glyph Decomposition */ - subs, /* Subscript */ - sups, /* Superscript */ - swsh, /* Swash */ - test, /* Test features, only for development */ - titl, /* Titling */ - tnam, /* Traditional Name Forms */ - tnum, /* Tabular Figures */ - trad, /* Traditional Forms */ - twid, /* Third Widths */ - unic, /* Unicase */ - valt, /* Alternate Vertical Metrics */ - vapk, /* Kerning for Alternate Proportional Vertical Metrics */ - vatu, /* Vattu Variants */ - vchw, /* Vertical Contextual Half-width Spacing */ - vert, /* Vertical Alternates */ - vhal, /* Alternate Vertical Half Metrics */ - vkna, /* Vertical Kana Alternates */ - vkrn, /* Vertical Kerning */ - vpal, /* Proportional Alternate Vertical Metrics */ - vrt2, /* Vertical Alternates and Rotation */ - vrtr, /* Vertical Alternates for Rotation */ - zero, /* Slashed Zero */ -} - - -shaping_table :: enum u8 { - GSUB, - GPOS, -} - -lookup_info :: struct { - MaximumBacktrackWithoutSkippingGlyphs: u32, - MaximumLookaheadWithoutSkippingGlyphs: u32, - MaximumSubstitutionOutputSize: u32, - MaximumInputSequenceLength: u32, - MaximumLookupStackSize: u32, -} - -gdef :: struct {} -cmap_14 :: struct {} -gsub_gpos :: struct {} -maxp :: struct {} -hea :: struct {} -iterate_features :: struct {} +_gdef :: struct {} +_cmap_14 :: struct {} +_gsub_gpos :: struct {} +_maxp :: struct {} +_hea :: struct {} shaper_properties :: struct {} -feature :: struct {} -head :: struct {} +_feature :: struct {} +_head :: struct {} +_langsys :: struct {} +shape_config :: struct {} +glyph_config :: struct {} +shape_context :: struct {} + +allocator_op_allocate :: struct { + Pointer: rawptr, + Size: u32, +} + +allocator_op_free :: struct { + Pointer: rawptr, +} + +allocator_op :: struct { + Kind: allocator_op_kind, + + using op: struct #raw_union { + Allocate: allocator_op_allocate, + Free: allocator_op_free, + }, +} + +allocator_function :: #type proc "c" (Data: rawptr, Op: ^allocator_op) lookup_subtable_info :: struct { - MinimumBacktrackPlusOne: u32, - MinimumFollowupPlusOne: u32, + MinimumBacktrackPlusOne: u16, + MinimumFollowupPlusOne: u16, } -font :: struct { - FileBase: [^]byte, - FileSize: un, - Head: ^head, - Cmap: ^u16, - Gdef: ^gdef, - Cmap14: ^cmap_14, - ShapingTables: [shaping_table]^gsub_gpos, - Fvar: rawptr, - Maxp: ^maxp, +blob_table :: struct { + OffsetFromStartOfFile: u32, + Length: u32, +} - Hea: [orientation]^hea, - Mtx: [orientation]^u16, +load_font_state :: struct { + FontData: rawptr, + FontDataSize: u32, - LookupInfo: lookup_info, + Tables: [blob_table_id]blob_table, + LookupCount: u32, + LookupSubtableCount: u32, + GlyphCount: u32, + ScratchSize: u32, - GlyphCount: u32, - LookupCount: u32, - SubtableCount: u32, + GlyphLookupMatrixSizeInBytes: u32, + GlyphLookupSubtableMatrixSizeInBytes: u32, + TotalSize: u32, +} - GlyphLookupMatrix: [^]u32, // [LookupCount * GlyphCount] bitmap - GlyphLookupSubtableMatrix: [^]u32, // [LookupSubtableCount * GlyphCount] bitmap - LookupSubtableIndexOffsets: [^]u32, // [LookupCount] - SubtableInfos: [^]lookup_subtable_info, // [LookupSubtableCount] +blob_header :: struct { + Magic: u32, + Version: u32, + + LookupCount: u32, + LookupSubtableCount: u32, + GlyphCount: u32, GposLookupIndexOffset: u32, - Error: c.int, + GlyphLookupMatrixOffsetFromStartOfFile: u32, + GlyphLookupSubtableMatrixOffsetFromStartOfFile: u32, + LookupSubtableIndexOffsetsOffsetFromStartOfFile: u32, + SubtableInfosOffsetFromStartOfFile: u32, + + Tables: [blob_table_id]blob_table, +} + +font :: struct { + Allocator: allocator_function, + AllocatorData: rawptr, + + Blob: ^blob_header, + Cmap: ^u16, + Cmap14: ^_cmap_14, + + ShapingTables: [shaping_table]^_gsub_gpos, + + UserData: rawptr, + + Error: load_font_error, +} + +font_info :: struct { + Strings: [font_info_string_id]cstring, + StringLengths: [font_info_string_id]u16, + + StyleFlags: font_style_flags, + Weight: font_weight, + Width: font_width, +} + +feature_override :: struct { + Tag: feature_tag, + Value: c.int, +} + +break_type :: struct { + // 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. + Position: c.int, + Flags: break_flags, + Direction: direction, // Only valid if (DIRECTION in Flags). + ParagraphDirection: direction, // Only valid if (PARAGRAPH_DIRECTION in Flags). + Script: script, // Only valid if (SCRIPT in Flags). +} + +bracket :: struct { + Codepoint: rune, + Position: u32, + Direction: u8, + Script: u8, +} + +// 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. +break_state :: struct { + Breaks: [8]break_type `fmt:"v,BreakCount"`, + BreakCount: u32, + + ParagraphDirection: direction, + UserParagraphDirection: direction, + + CurrentPosition: u32, + ParagraphStartPosition: u32, + + LastScriptBreakPosition: u32, + LastDirectionBreakPosition: u32, + LastScriptBreakScript: u8, + LastDirectionBreakDirection: u8, + + ScriptPositionOffset: i16, + ScriptCount: u32, + ScriptSet: [MAXIMUM_CODEPOINT_SCRIPTS]u8 `fmt:"v,ScriptCount"`, + + Brackets: [64]bracket `fmt:"v,BracketCount"`, + BracketCount: u32, + Flags: break_state_flags, + + FlagState: u32, // u8(break_flags)x4 + PositionOffset2: i16, + PositionOffset3: i16, + + WordBreakHistory: u32, // u8x4 + WordBreaks: u16, // u4x4 + WordUnbreaks: u16, // u4x4 + WordBreak2PositionOffset: i16, + + LineBreaks: u64, // 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.) + LineUnbreaksAsync: u64, // u16x4 + LineUnbreaks: u64, // u16x4 + LineBreakHistory: u32, // u8(line_break_class)x4 + LineBreak2PositionOffset: i16, + LineBreak3PositionOffset: i16, + + LastDirection: u8, + BidirectionalClass2: u8, + BidirectionalClass1: u8, + Bidirectional1PositionOffset: i16, + Bidirectional2PositionOffset: i16, + + JapaneseLineBreakStyle: japanese_line_break_style, + ConfigFlags: break_config_flags, + GraphemeBreakState: u8, + LastLineBreakClass: u8, + LastWordBreakClass: u8, + LastWordBreakClassIncludingIgnored: u8, +} + +decode :: struct { + Codepoint: rune, + + SourceCharactersConsumed: c.int, + Valid: b32, +} + +encode_utf8 :: struct { + Encoded: [4]u8 `fmt:"q,EncodedLength"`, + EncodedLength: c.int, + Valid: b32, } glyph_classes :: struct { @@ -1740,35 +1950,46 @@ glyph_classes :: struct { MarkAttachmentClass: u16, } -glyph_config :: struct { - EnabledFeatures: feature_set, - DisabledFeatures: feature_set, - FeatureOverrideCount: u32, - FeatureOverrideCapacity: u32, - RequiredFeatureOverrideCapacity: u32, - FeatureOverrides: [^]feature_override `fmt:"v,FeatureOverrideCount"`, -} - glyph :: struct { + Prev: ^glyph, + Next: ^glyph, + Codepoint: rune, Id: u16, // Glyph index. This is what you want to use to query outline data. Uid: u16, - Classes: glyph_classes, - Decomposition: u64, + // 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 shape_codepoint + // with ShapeGetShapeCodepoint(Context, Glyph^.UserIdOrCodepointIndex, ...); + UserIdOrCodepointIndex: c.int, - Config: ^glyph_config, - - Flags: glyph_flags, - - // These fields are the glyph's final positioning data. - // For normal usage, you should not have to use these directly yourself. - // In case you are curious or have a specific need, see kbts_PositionGlyph() to see how these are used. + // Used by GPOS OffsetX: i32, OffsetY: i32, AdvanceX: i32, AdvanceY: i32, + // 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. + AttachGlyph: ^glyph, // Set by GPOS attachments. + + Config: ^glyph_config, + + Decomposition: u64, + + Classes: glyph_classes, + + Flags: glyph_flags, + ParentInfo: u32, // This is set by GSUB and used by GPOS. @@ -1784,315 +2005,102 @@ glyph :: struct { // must keep track of associations of marks to particular ligature-glyph components. LigatureUid: u16, LigatureComponentIndexPlusOne: u16, - - // 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. - AttachGlyphIndexPlusOne: u16, // Set by GPOS attachments. + LigatureComponentCount: u16, // Set in GSUB and used in GPOS, for STCH. JoiningFeature: joining_feature, // Unicode properties filled in by CodepointToGlyph. JoiningType: unicode_joining_type, - using ScriptBitField: bit_field u8 { - Script: script | 8, - }, - UnicodeFlags: unicode_flags, + UnicodeFlags: u8, SyllabicClass: u8, SyllabicPosition: u8, UseClass: u8, CombiningClass: u8, - MarkOrdering: u8, // Only used temporarily in NORMALIZE for Arabic mark reordering. + MarkOrdering: u8, // Only used temporarily in NORMALIZE for Arabic mark reordering. } -glyph_array :: struct { - Glyphs: [^]glyph `fmt:"v,Count"`, - Count: u32, - TotalCount: u32, - Capacity: u32, - RequiredCapacity: u32, -} +shape_codepoint :: struct { + Font: ^font, // Only set when (.GRAPHEME in BreakFlags) + Config: ^glyph_config, -op_state_normalize :: struct { - CodepointsToDecomposeCount: un, - AboveBaseGlyphCount: un, -} - - -skip_flags :: distinct bit_set[skip_flag; u32] -skip_flag :: enum u32 { - ZWNJ = 0, - ZWJ = 1, -} - -op_state_gsub :: struct { - LookupFeatures: feature_set, - LookupIndex: un, - GlyphFilter: glyph_flags, - SkipFlags: skip_flags, -} - -op_state_normalize_hangul :: struct { - LvtGlyphs: [4]glyph `fmt:"v,LvtGlyphCount"`, - LvtGlyphCount: un, -} - -op_state_op_specific :: struct #raw_union { - Normalize: op_state_normalize, - Gsub: op_state_gsub, - NormalizeHangul: op_state_normalize_hangul, -} - -lookup_indices :: struct { - FeatureTag: feature_tag, - FeatureId: feature_id, - SkipFlags: skip_flags, - GlyphFilter: glyph_flags, - Count: u32, - Indices: [^]u16 `fmt:"v,Count"`, -} - -feature_set :: struct { - Flags: [(uint(len(feature_id)) + 63) / 64]u64, -} - -feature_override :: struct { - Id: feature_id, - Tag: feature_tag, - EnabledOrAlternatePlusOne: u32, -} - -op :: struct { - Kind: op_kind, - Features: feature_set, -} - -// This needs to be updated when we change the op lists! -MAX_SIMULTANEOUS_FEATURES :: 16 -op_state :: struct { - WrittenCount: un, - GlyphIndex: un, - FrameCount: u32, - ResumePoint: u32, - - FeatureCount: u32, - FeatureLookupIndices: [MAX_SIMULTANEOUS_FEATURES]lookup_indices `fmt:"v,FeatureCount"`, - - UnregisteredFeatureCount: u32, - UnregisteredFeatureTags: [MAX_SIMULTANEOUS_FEATURES]feature_tag `fmt:"v,UnregisteredFeatureCount"`, - - OpSpecific: op_state_op_specific, - - // Ops are free to use the following as they please: - // LeftoverMemory: [LeftoverMemorySize]u8, -} - -op_list :: struct { // TODO(bill): is this actually a slice? e.g. `op_list :: []op_kind` - Ops: [^]op_kind, - Length: un, -} - -indic_script_properties :: struct { - ViramaCodepoint: rune, - BlwfPostOnly: bool, // b8 - RephPosition: reph_position, - RephEncoding: reph_encoding, - RightSideMatraPosition: syllabic_position, - AboveBaseMatraPosition: syllabic_position, - BelowBaseMatraPosition: syllabic_position, -} - -langsys :: struct {} - -shape_config :: struct { - Font: ^font, - Script: script, - Language: language, - Langsys: [shaping_table]^langsys, - OpLists: [4]op_list, - - Features: ^feature_set, - - Shaper: shaper, - ShaperProperties: ^shaper_properties, - - IndicScriptProperties: indic_script_properties, - Blwf: ^feature, - Pref: ^feature, - Pstf: ^feature, - Locl: ^feature, - Rphf: ^feature, - Half: ^feature, - Vatu: ^feature, - - // Indic - Virama: glyph, - - DottedCircle: glyph, - Whitespace: glyph, - - // Thai - Nikhahit: glyph, - SaraAa: glyph, -} - -shape_state :: struct { - Op: op, - Config: ^shape_config, - MainDirection: direction, - RunDirection: direction, - - UserFeatures: feature_set, - - GlyphArray: glyph_array, - ClusterGlyphArray: glyph_array, - - DottedCircleInsertIndex: u32, - - GlyphCountStartingFromCurrentCluster: u32, - - At: u32, - ResumePoint: u32, - OpGlyphOffset: u32, - ClusterGlyphCount: u32, - Ip: u32, - NextGlyphUid: u32, - - RequiredGlyphCapacity: u32, - - RealCluster: c.int, - ClusterAtStartOfWord: c.int, - WordBreak: c.int, - - // This must always be the last member! - OpState: op_state, -} - -cursor :: struct { - Direction: direction, - LastAdvanceX: i32, - X: i32, - Y: i32, -} - -break_type :: struct { - // 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. - Position: u32, - Flags: break_flags, - Direction: direction, // Only valid if (Flags & BREAK_FLAG_DIRECTION). - Script: script, // Only valid if (Flags & BREAK_FLAG_SCRIPT). -} - -bracket :: struct { Codepoint: rune, - using DirectionBitField: bit_field u8 { - Direction: direction | 8, - }, - using ScriptBitField: bit_field u8 { - Script: script | 8, - }, + UserId: c.int, + + BreakFlags: break_flags, + Script: script, // Only set when (BreakFlags & KBTS_BREAK_FLAG_SCRIPT) != 0. + Direction: direction, // Only set when (BreakFlags & KBTS_BREAK_FLAG_DIRECTION) != 0. + ParagraphDirection: direction, // Only set when (BreakFlags & KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION) != 0. } -break_state_flags :: distinct bit_set[break_state_flag; u32] -break_state_flag :: enum u32 { - STARTED = 0, - END = 1, - RAN_OUT_OF_REORDER_BUFFER_SPACE = 2, +shape_codepoint_iterator :: struct { + Codepoint: ^shape_codepoint, + Context: ^shape_context, - // Bidirectional flags - SAW_R_AFTER_L = 3, - SAW_AL_AFTER_LR = 4, - LAST_WAS_BRACKET = 5, + EndBlockIndex: u32, + OnePastLastCodepointIndex: u32, + BlockIndex: u32, + CodepointIndex: u32, + CurrentBlockCodepointCount: u32, + FlatCodepointIndex: u32, } -// 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. -BREAK_REORDER_BUFFER_FLUSH_THRESHOLD :: 4 -BREAK_REORDER_BUFFER_SIZE :: BREAK_REORDER_BUFFER_FLUSH_THRESHOLD * 2 -break_state :: struct { - Breaks: [BREAK_REORDER_BUFFER_SIZE]break_type `fmt:"v,Breaks"`, - BreakCount: u32, - MainDirection: direction, +glyph_iterator :: struct { + GlyphStorage: ^glyph_storage, + CurrentGlyph: ^glyph, - LastFlushedBreakPosition: u32, - CurrentPosition: u32, - - LastScripts: [2]u8, - - Brackets: [64]bracket `fmt:"v,BracketCount"`, - BracketCount: u32, - Flags: break_state_flags, - - FlagState: bit_field u32 { - _0: u32 | 8, // break_flags - _1: u32 | 8, // break_flags - _2: u32 | 8, // break_flags - _3: u32 | 8, // break_flags - }, - PositionOffset2: i16, - PositionOffset3: i16, - - WordBreakHistory: bit_field u32 { - _0: u32 | 8, - _1: u32 | 8, - _2: u32 | 8, - _3: u32 | 8, - }, - WordBreaks, - WordUnbreaks: bit_field u16 { - _0: u16 | 4, - _1: u16 | 4, - _2: u16 | 4, - _3: u16 | 4, - }, - WordBreak2PositionOffset: i16, - - LineBreaks: bit_field u64 { - _0: u64 | 16, - _1: u64 | 16, - _2: u64 | 16, - _3: u64 | 16, - }, - // 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.) - LineUnbreaksAsync, - LineUnbreaks: bit_field u64 { - _0: u64 | 16, - _1: u64 | 16, - _2: u64 | 16, - _3: u64 | 16, - }, - LineBreakHistory: bit_field u32 { - _0: u32 | 8, // break_flags - _1: u32 | 8, // break_flags - _2: u32 | 8, // break_flags - _3: u32 | 8, // break_flags - }, - LineBreak2PositionOffset: i16, - LineBreak3PositionOffset: i16, - - using LastDirectionBitField: bit_field u8 { - LastDirection: direction | 8, - }, - BidirectionalClass2: unicode_bidirectional_class, - BidirectionalClass1: unicode_bidirectional_class, - - JapaneseLineBreakStyle: japanese_line_break_style, - GraphemeBreakState: u8, - LastLineBreakClass: line_break_class, - LastWordBreakClass: word_break_class, - LastWordBreakClassIncludingIgnored: word_break_class, + LastAdvanceX: c.int, + X: c.int, + Y: c.int, } + +arena_block_header :: struct { + Prev: ^arena_block_header, + Next: ^arena_block_header, +} + +arena :: struct { + Allocator: allocator_function, + AllocatorData: rawptr, + + BlockSentinel: arena_block_header, + FreeBlockSentinel: arena_block_header, + + Error: c.int, +} + +glyph_storage :: struct { + Arena: arena, + + GlyphSentinel: glyph, + FreeGlyphSentinel: glyph, + + Error: c.int, +} + +glyph_parent :: struct { + Decomposition: u64, + Codepoint: rune, +} + +font_coverage_test :: struct { + Font: ^font, + BaseCodepoint: rune, + + CurrentBaseError: c.int, + Error: c.int, + + BaseParents: [MAXIMUM_RECOMPOSITION_PARENTS]glyph_parent `fmt:"v,BaseParentCount"`, + BaseParentCount: u32, +} + +run :: struct { + Font: ^font, + Script: script, + ParagraphDirection: direction, + Direction: direction, + Flags: break_flags, + + Glyphs: glyph_iterator, +} \ No newline at end of file diff --git a/vendor/kb_text_shape/lib/kb_text_shape.lib b/vendor/kb_text_shape/lib/kb_text_shape.lib index 1c3b94779ba2baaa1216b84525fbdae75b28103c..000ae09b2f50c6e9b3745a77f0026c67263de59f 100644 GIT binary patch literal 852874 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

uS;o`g(W`R`bnvxDx-xtM34)3}enDS> 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≦p8t<| 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

&z2koOT z_U#Vf@agrn@a(?iYkjfA&T}`zABN}h3=9mQ&M16bkbj$3Jh;i^aruE~>)}$d?@pkx ze1%e$6%n9@V-Sa@^~KVgp4|*SodF!4-3*@H9vmK>jGmnV93I_Fo}COnolIa^2M(X^ zPz#UVS_@a>+pgV*9lLM)SYLkRlogqgIJpUhf9m~J# zp=?q3w?h#nL7v^We7jGB60Re(w)W}{Ib#4?&C|{0(e1&qM3ik!1MoM4{YZ9 zbaHuiJ97ARpF%hPM$s|H=KY|SB14I>N9*lUy)?^tpnK>`RDF8ez~y-Nbq{D09<*eq z^+2hEFU9*svp$;=UaVn(;SM5m*~ z>%+}EAr|lQ?5zMr7U;kP*Y3}r-8aCI3v#jb@e*6t-eN`%>+>ZhuD!)f9@fW8v>h8N zV)9FIA~v3hp@IOZ6~>d_s+0pfzf$)o$Sr}fPeZJ*vy z3s37`r6T;>ykcRA&0{574+}#nw?{W5iQRk63_8=l*VO{FyMV{j`UZas=!|{Pz2^`? z{ua;~exMcaKE1UTAVW8?Y-aVe{?Fe6T65#mn`)6Zfq$EStPB6P!#>^q9L+B|OKO^5 z@{}ZjuB;aYak)XWH9ju<+YY^UbKJoE+R7d@>{mOck%B4LkC1CLHO4&P2M4&P3}*UUcM*BgFwlxn}0ZTQVv%Idg*hWc2}Qnt;UAW@eN_c+h)pN{<7E`a)nt=|9t|MzVE$H?EN0q#P% zI(YQPT6lC@K=KF3ACBFpeZi4(n7@S?I-wxUwj42IU0r`wisq z1lnzvy%fn#qP=PauJlBJwbMKa(H$#`gD45 zcy>E*_;fNrv-WFd-|iE>-G@N!%pTB;d&>X+|6i8<{r}&y+rh!JH^9Qf`VfChEofr3 zdmgBs?E0zJtH&n*5fU+J$%g5L3#~kBW ze7Y|-*GUAG8u@|>N^l4q02N2R7QWU8OJpGF5tQCyn_sYk!qXCT2a&J!!O~Ztpad;X zY)SYBN)cQjK4?0uB^9(#tf4B7g}*HdJlTKoWe`{hRB)X1><$1`FC_<%!w(c)VB_IM z!|T`c;U&lA2R_}0Rx&k!c1x!I|NpO5eSf_36IhX??1Mhku)6tPZOUqYuB^P2bkr{H=!=7#JFUF_)TyN>6CT9S(|R zGk91ZEd2y3*KRmARL8QE>Un~T=gTFk z(E1c9B33piFfx=d`*ur1oN&4Hk#Fnm5-rbeaNz-}YQe=3xV_-pDdE}e%z+xV-~}36 zK`WiQgE%~TeJwn?V;wxKFY>p5x;^0V>vjT7b@LbTf$Z?GzF7M1C1|Xbf7b&^{^lxh z_nZk7T0Y&kL8;Wqk$;;9QU+vXDCPC-zTn%<07|EiUMu-_-vaf{r67d}OR1nE|2BAv zWAW{d;=oapM{#(928ozFJB2}Y1i1O*>Htbj{H<+2{{MGu-Vd6iV&HG70Cl81d>mfp z|M>r32)<6yfQf-Z7*UjnMda@+LvANQKp)}E>JCFl3Qz_`#>nPx1eW8TI zquWuyqti(NlrH(@89*aJ)}Ek~EUXWf7zz5yDF2c7=Y85+R9tqpXD*?-W?>;&-r0Pj^ncN5G} z=>VAnUC-CKN5um)Fx$CC#Q{ujQLz9?&j8&{U;x^np}9pxft`V&y9K)L@6i7Tp4|}+ zzP$w&#{a>~jnYBp?y}=`0F}ZFp8Ksh4}kid44!+fI2S-zJ05X17&3wq>P|;a1w}Ax z*FMe#poSVlx3_>t_wmMu|NqM~cpQ8n+Wpgm`-jI4(9u1h`%porY%8Dev_4;M!{4$F z)R@;k>(TsEmcP9Tv=_Y>+yUU<)&g4N!r;*h8n|NU?rCHI^$sAC9?U2HKlHFZ#ov+- z(y_zoYyA3upou!>gC5O4_{yam_c!J-FnIDWK8|obf0Gl)F6$HgEtX(iyT8eUPH6rQ z+Is=I@1RWyDr|orVHh)i8|W@oPwSKXEuiaKJwdjD?t%5#ahNj!90cHU9dwbU#}0Qc z2hi$8@D7|7&|RJ$I}Uw~-;Xd3?6?;sz=c4YeLZ%B zb3aIg3rT`ig>xr>MwuaIv4MpSaW5!;W04@YNe$it` zIIjZewkL?|J;18mcn=_iOu<4I-q!893r{(W{B3ezVY5&7eG%~>0!mPf4YC*s>LViq zgU5~=ybCtMEx!jA3Ww$$aG0M43mJZj_eU7X#NW0BEPS6g0ihY>I*%Rqc^@Ez`oOB3 z1RgSi7I=frs|E|*=WPHT-w5$v23W{KU?BrsRVY}+KR=59^zt ziqKiXqto@qt^&|lE@)*118j%7Pq%M_PxlSbq(uSbYA?_3KnLI6ObegRTn10;tNbmA z%t#lZeRFbP=5MiNW?!aNyT$1y#;Y`~of#3jCVApaChcyaB&vE2xM9%LMRi z_I_YuVE6=0Sf#A5f1zU z2?6|q-ZS_$T?_aH91J|VTYrES_iKVzqIz_KM`*iSZ!j@1bh=jXYq%D8bh{St3p%d= z&G++bfR&}S9^e-Y-T^uV>;Ex+LFW@L+z-K#$1fOtgI_TD#V3A2=N~@S-}svxK!sy> zsRtw-fkv|{Ji22KK$2MK4j<4)f0xb_l?0#e)PU{~l?2z;x82A2rySru#NP@!;=qG{ z*F&9RuEt)_!UF~d1qP6SE=V8+6eJ7^46fZrT|tZRLHFD{_WJ(!$Ud0ik$pMCqqi3n zlpf3{Jee^=#J zg-+KUpiVVt&ZJ)mltV!|3v`GHsCNSD?fZ1+GJsZ=fqMm4`CB)D7xTtG@aZn)@NNB8 zlHh1rXHXgi%C~}`Yj-@XZ-DYHC^tJRfX)u|?e!A?jh~r=TI)vO{y_i-sJ#jrdh+aM z0A~b01%i?Fj+0&-2UM`x{uXZJ~u&RPYJ?)%^cN}xq6py_6CAUJ}i zFdTOuBf8J;U|?Wl;Gc33bc9=Xp+oCQ$LP?W-;u+I`vr)b;PL$e*al@s0Uz!QAfXJfQ0NU0 zWk(4gZqS+-gXUTRCjM5?@tBU^++uhjJMA62-2`m^{TE;;)&lKy1vg$j4!&S-epK(o zzYW|2V(>Wlkllm(lxuejD3LO}W_Ia54k{F&`5ttVQ;iB_6{$~mYzN3YDX970fL|cN zfnOj4RK^x0fF@@SfbuhF@usC`x0q+QABSVNn}j33AfF?@pc|;n4g;0faiFp~4OC9& z1%OKCvINggG0#pv4#!S636Ji}KHcv@XGwR)IyhR?CGfW%2bEpTH7W%hprc(%1sogd zV%bYMJvwV=yu1WD>#I910NNyXk`&kwnM-%e zT#!-F=mEtPauk7zUC>HjegOe~fd~bRm^lH88GcBC3@T|`?}L)_|HGcvcZ-&|fDWqY zj_m-YAO-#wab^Yv%Nmu05`%765l2opACGR(lHrpjG9KNo8r`8AK=lV_fDd>;5Oky6 zXOB)-4Uf)R3D7=EP}#2F+xpg%-{qL+|D&L-Vg(+Zt_m+%nHd;5YZpM`g#)xN9+WRK zz{3lC;8jt+5k9@(GeSJF4`qOt>BM$;HXob;QtQKf!lyIzfd}Xi5?>1s<^w+6x4J`A zQaV9%fxRt9K^q4>I$bZo!ppOp36zcwz7SUh)nA>p3mh%o1o&HhL8E!yCtSPVJ6?Y3 z()xdsc0WiS>;aGn*asjHh!;RY37*X_Ge9+Qf=Bb~44+P43(x-t zJev;*cr+i*0IdrJMUoHrdTr2Asb=7@XExXFbWaJlM z_Usn+?Dpnx>~;nf7R>wt-k`Dq6sXRi^1@kxU%(qwW`Kg#8B}h7!qpqzzH^4Q?>sw& zJv+TQ96OyQUK)ZecC!H63@(bG%@9zD1Z{CR@(YSN@(cQbEQLrhEXrbz}ol) zz;&4iL@B6d@#q8_>!EzYqZ4cozd$dj^5YkD)8H5MvjB}7rZ`$w8-T9gbzK2!AP9Q) zIt%z%9{|o zx+6JS4)C{}XJBCHa7^^IzFQ&(%kPf-+nf_x4wUdZb~q>cc1LsgTHh^w<=g!kq>9D2 zyO_hHTN3Gp0H1De4$xt)jvSyt=jsWdA@fA&6is4T1h~yZ{1lC2_jyt#Xnd_Nm-c}s zXh0X+dUl@!ow;`!DTJIwPnptj*1uuUJbO4*3cmUvTU` zZhaD@SlqGueDhBxM~G@p*Omh%q9Au5MF4+mDQKL(+bC^-mrW|J#BZ5d7_+!<0QB!<+ouTELwK3{c-R_diOPWvmMl4zt09*a4pcHk^!oI zyM;YKsnA&hl-cXF?NLw(DA1w;%HN=B1uO%~`k=bSkzb%i1+-KGRKb8{ zK=(uQ3wncEQvxk2po5tqMRcdIN2fPOr?bRsUf$<|Fx1Q0ZJ6OPT_xh52hnYJr?e*y{ z=5Xvj<9EK5husHVx*vLIAM)HW zS;cb_2zgw5;lT{rrpoElJA*-xfx#p50O%YJpU&7BKH3jFyAODNf8yDFz(@O$=fyWZ z%#XmPcpiMi0y3uYNFqoJ%nTNf>;p0$+9!N^JC_SGF!=DhocHPU?RYKVXnmZ&HJb&r zSn!rlXXp%YdoUW*2<)s;De!H5QflI3UAlq4#hrzL!L>U~z_A;0GX}^n1!}iVkl&!*bj&`A12T?{FXp&uFn~P>3vx*7 z+JIjm!+~F*0MxJxNO;ZhiC^7f4Xx7sxPx<_1uqhVbaF7o3og;)gg8o;asK5~tuC zNWv6c!V317;2KDR6l`Gy#o;G@0W68}b&gB7vjA+&s5^85)P3OC0Hq45W@FE8F;F?& z&F#}2+W;Bk1Pxs>!`4xCyRPu)WcKU^-)0J4>jauBjo^SN6#zA3bv%0Kg7PA$zCP>; zs=Od)41%UK)j-CAP6li~oZ)NzwCJv9_dQT`0Vy+&b22czeDL%C|Kn{9jG$o|28NfL zIY9~8Q^5Ft_i@WgpAxoiM-R}%7HB{x7Igkv2{-@t2o4Ws&@l22k8VC6Q0v)Izy~z3 z+Ij$VCM#&XsP$3_Cuji&i%++gf=B06P^AbNCECRRz6re-)c*D91UFW}LF)=y*L=mJ z`M7_*?8585kybbUSl6?)?9sp8+=V-0eD{b026_&=EYg!N1*G#HTwzz?I+Sgp0C| zN{VZDlmuuQx`AW2lYsS!Vhxw>1K@_w`eQ!K2Oasho$x&Pg0w3VYvqZ(gxBGl& zjEaS4_m$2X(0Ov_KsBZYsOt|2P7ll41Eps{r_Mpg{(ZVjR4P2WU00+vTYv*i`Zc>p z_dL*uJT#y|+k-u#S-T-z-{%rx#4Zm1R${iajqZvR=GzLe0 zmqU&n4pE-n#~t~%c}Dm0obtH*z+)wYjEqc)E=VZKqxf@4I9UEV$iV+69lITTe7jFve$@P+f%}4EXMhj5 zCuV(^zZG;L=Sy`^%d^`>rC>h;0|P_z3%2h2;8QPLAX$!o>S4=@m=boEZjT5b<`W*> z=PfI8O4zzRGCY`1_;erlZ9Q4S?$LVEhu`&}<1Pjk1_sLtlM=yh4+9V86D6E3{M!SC z(i|_o^6d6@0EMI{Xr0t%&{5;y+>U1#J@dIq?GHMV+>OX zOZWBHp!NNZO4b*OLEFbgCcI_??T&2tlh$16!Q6e=k>B-r>wywB{_Tf49aTKKJvltB zPnWKE>BkBxWE=&I|GRV_@4gOdqFPpjl$5)4djxcQ1o$wYaOs`~8fN$HKIqvUqoU#X z|5WEb@YMs%CrZLVgW!Rn-j<1v^@U=0&+Zl#(A11~FHcaKwap2iUXc^UY976PpeqJI z<6f}-W?H8u=%i3b(Eg_Xhdit=6uouqzW$mGVl=32xE0j01Qm5LDit7KgVy9naCm|W zyBL*(Hz`+aAPN{UPOb)W9<{DQ3uKs|(B9tW6<_yt=xfSNSD zEGN4AK+Xs4G-dMXbUk5xg1;G@UgaGa;PVll-3|giouw;WI&(MhZx``!=|1Io@FA-& zvj>MyuS}RD|MqT>mpr=}L9(u`Z%bKSyWhWN^|ZcLS_`@f#Hag{W$gx!-l-smICh_K z>^|Vb?^?Q|`Juflcc6&J!53^Ey&{J|lVAL+Pk3}PxwalCmFsQ;`K9@XLa{JNiz82G zOCKoP{0EEwZv#0z&GCOT$ZpW4btJD?pWttD19yL88$f%IQ@|52pxezrXZ`tf9|s+% z2`X(bgJ%%B9Rw^ZTtF$-!NG_50RMI-{_S3%vpBs#XAXP-UC9F)i1p~KeUa8#`z@{0 z_Y-LE@fXnEV;xYz?ZpO)6-7`mf)5-JPjj^NVPNv?6=5jj?)K?o@nAmX(dqjFG-m7D zo$UZRApvyQzz5LO8)#evbcP;u0s(Y@bBpG`|NoJ~A9OJoc#aWL%7eyd1RVLd`-=E< zdkDC82THiMUMewh>~;hd?mDjBjv}Dao%xs#|M~-13V1TjC?RqREmR+qT_G<4k~!NV^k_YO^279fB*l7%-ev=1V?^>DJnBSWBc9rjsJuDv7ptP zmmfRw3(Qg3!N|bS{Fu9WA84|M$&p`R33$J^Bfr2Jl^Y;U`~q84UVxVLY3@-GU}s-ic7bn24jGX z3!{SqXeis$`e5lz@IE?dx6iXX*ufWcCZhE<{+9coQ56^dqYs%N;R#uR&jik{P8|HL zpk+|soMM>xTV{dRyPZgLZG8gT+QQ}9eZAXBf?rUGU(ic{Uoc96UogplUogvoU$7|P z6Te_pf=4HpPp1<{rxPeil;*(Zso8xx`#@ny`W$sPsBP=fi8)98n!ojS33pm^l?QVv zi$`abif8u)U+ZK1t-AmJ|L50$grraR9u?54EYNaw&^j&9rSJ}**|~1WIxf(Gf1u@E z{2Gu|TOQrL44^6yb{=5k5zrzs$C$X-!)YM*gDM;%F(V@bBN#9+fF`cNB$yAH-$pfy z3DhcMU}9ndVJ0YmSp((LkYd1VKWGO>3HZ9AbohPupr&32_?QRKN)5a97pO_j!^6Ok17>OP!1P|?fvJ1X!@v*-mgVJziFxocFa(3e!g(1OLcpxkyfD40 z{4kcAC`^Z+C<8+u*!7{J3=E*9V$2M=qA=ITh%qqS1G`y69LCCkv(}5l%=3|ev2-P2 ztgUd?2RQ4Y6il{G8phI+f!TIb1{NBxWEdDOgYA`)Wng#)X8n|9V0aE@Wy&!yfbQjG zVYnm*ll>vbz;F^Qdrldq&RPX_+=sj>1H%ijY?vBsnP`R@14Ad+=Q(N&4834hff@tD zBrvN)je!AlTQM_3g&G6HWUyF`8Uq8U`^?PHpvJ&36)e`G#=tNQ%<52MV3-bO^{6p0 z^nh6t)EF3Mf>~SC7#JpkSv%Ai7(g9mW`;d#3=FftVxX&+!A;%P10_7ozZmP;jSqmX zeLM^*RvEx7(4;Wr9xdYojX#siOfC7_*MZhzwk%?2U;vAl@VA5e+%0FAAtEM5{OzF2 z*!f#-flhGwnOtUQ$lv}2)JSdtSqIW(W>8*Ik^^EH8I+rq=r#Tb;8k7iu z_(lf&t!wxg7tR)M7ff2$QpH)uh};T@orT8DS!fb{JEO)neb#0J}F2=lF}A;h=IWkyhQjKSt4mzfyy zx9;O*VA$ag(zU}COj?7_tg05;hyu$`0 zzas!df>fCrqsDJpQHc@E#}-iglgmt@_M1V?G&kaJJ;TGmupL@E69`hV!yQb5c3~Xe5e;I4(}W4! zw@|y2%gmtWn1jv9DzGr-Z@tIOz_0^!3B};hy>Yf3D%RAnFrNlV8Y+J z3Zw^AQy<>J0#dgFbn@`w9iZ!_!0~Hj0`YBfnSlw&x5dSVCLrG?ml>Myx8{I!n}GFQ z1Cv@{(hfv|Y&3?dH8J6DbpWXi0?F*~0FyBw668NabMzc-X^I--$z_J7urRQMyT}ae zqU17js4W&I{H?dR7#MbdR{I~`Q2;W3M;e#}?F4}2FH?{&lFJNCLB1$1HZlYVXTfY!5DD^ysVOWBjG(%W!Mc;nOicM(GeEk7L27q+f=PQY84Dsoy3L?^ z%%OTLO!-?)Kzcy8z#iTKy7c$(4p0q!ctM;iENiH)n<8R#o(h~wwu>*8N=iwa=V0Ijc z1nIGa+L&Bs2Gwm2)oo$M-&zII4Z247@QxyojvX0b5;Tth314%N>ypb1%=ugWKx%ct z@}Sw$!#hCt>w#01u?2rSX#X;Q%PsINXUSz2<}e?dm_vM=TxMtv@v#xqOk;EY)?XY9 z3_HBRW;%jNb1)eSB0;VM=LxZ#@RmQw&nEBNI%DB|O-2v+09^TOoW`i6K_KP7@t&s(PYYa%O6Ik8?OoDdELc+xo;=il{ zbGU6L77+iLLQOEU;BS=znUDq6n*b)mz$9qG4{Vz`RF4HzkEI2F>oax+h8>^BYOa9gsAl;T=9k;=xKA3a{k)RMYhN?BO zEuBQ{zijw); zL1(h@w>$;MdPa6$A|zCj%Piq;Gy}Uaxy&4DuZ1OlD+|b8(7n)yca(x`+>s3?K`W{b zH~!4YOiu*)tEeO=5#+D5%)CVY)(dP5kO8m5J3x~nhj%On>jCY{JKXp)EhjO#+_*%$ z@nu1FUSf%C^Gi06)^8jv4E(KAL0XrAmA?m*pq)pDcYw|V1!*lxOa@t3nwOaj(vxgd zkj&qj2+{+(mG|%tP%Gr{4$yv{!#hB?dmnE6Sx}ytQeIe++4wWRASneJ9Y)1qb4rR5 zQ$XhAq$Q>Bw@QM{Sp>EfltT{h0G(HJcn7EldbsgrQAtutiEQJ`FHGzVCH&1lnfO~@ zure_00QL6{@0bME1{z#DyaROS1Ss|si&G)~EG*AV0r`^|WY@o!Ab+j}Y26Lh^c+m? z0+VtevhineW>qRkZ9!3L8h>jeNUZ=!W(Vlb(!)ErLF~q#c{$11{Oymy%`Z?H+W0di zCo>%qtp&v;X%N3=B<7@n!mKzmFAZd7QFc)pf2%IY%)KCUb{qwhpe-$jcYrPhKiv2; zF{uo!Cn={KtS31qGo8QnCkq3^4$z|Y!#jAudicR4Xu$y}qm>tDK)joglbH_kE*Ho; zMvh+${H^;yTJM3CgZ7pm-T^u&Kr8bO z?*N_W1xjoA$vF_u<)#;9Lp+z8otq8vTv=j4HrRy3qHKQf{gFFBL*a*afUa^pykix} zo*kf5o(?zuEXzpD0qZG9%mL}iEJ@7aZ9NzH`tY;~RZ2Vc0S&{>> zF(WS>tUI$fAFR7Lw;+eV^&T?=!w%2_UPuf3@D5OK@9+*#+#GKFS(2BS3$m%GBqx`@ z6_iVN90ZxW1GIPg@QzI&cH__D(t=|CcF=hV{4M9f`8FdzD<3uACgqgpLKAadX)eS? zDS7$1Aphi)6oGBY$t%s}Z}kDW2sA2jc*k;(`8$NcBxrNP;l`H*d8N6aC}IIQ>+NR- z2L4uQkOncZB2XD|cn9c$^}{=)K_ZPmGZJ(1AlBsTm;?FEe5GgDak43Zv}0! z+0h4<2dx%4yaP0u0Loj5IVJqJ2g+-k}JRZTy*B zkX!;XC$~7Y1Y}NTUSk`ndCm!L}{OC%d#eqd%`C;^`%;J#xjNbwHP zs^`NyKv&ow-tiVJ@)|@o{wzz*C@+y|{8?0*R9V6YYLt{zmJ05;3zFRd+8%Ux2WUId zVbBOD2k4YWfpCxJHwMPHUHP|N@aX;sTKHt?*!+sAMANbP6=R8{WAiKK5-!K)UyKm< zgGW2N@4uGZ59+%0#y7s^hq4-8vw|h9rAib#r5rafI`Z$o=+b@qxQhy?&&B|{CKEKI z1*+8;99sTYS~c$jWod>|L;mgHW$doTC)2dsKq2b5p@Dxt=x$h;=FS-C^6k!fAU&Yj zHwTaI5)}`R<|792hd~X=|KK4EPz?)VA-o701(tx^)7$;Vqwx)>lMLRD>eCDAAu)J# zUo4e}-0O~YV=A*xrzLoqN1(tbegV*84%ZW)HKw2&P(dg4{@@n`9po8$!^6@`qD0c8 z+x3P=ukQ^H(2fES-R=6oqm$#cfJZNASAy#UD1F1D*Ga*{()9ya%L|WQ-xp9VFFd+k zKftuS@aS~?;L+;}qF;ctx*C52-4yEB{S7L*0z8T7(OvU(fk(GH2k77o`!vuXu>k07 z43BQt6QIFj6_4Ytph*`7pU%<)pu>kj^WglR*2h5`#eFY$^oDNuf5D^su!r^i(!-#0 z>ze;D6|Dl@W)2E3Pw;K##TuU7)dtX;%wv&u>H1iMclnlbgSM!9bb>iP){Ysa?BE?1 zju}4Ijs>7osT@Fy*$PlLY=cgI0PVu?=mlR6jcp@_XZL}_{M%gu**usJf*DLO26(oE z1;jIkD1pncfmW?xk#S`6u)a{t2Qm(P*$_;L6aRLXLo8{Xj9>;^TBjqEQ@6_@4$xvQ z@V2RghxxZTCLRWzAk=yZvV0I+SUK{$p76N*0=zW)aPvdvZjcihk!|+|-2&Om;?(Wp z#MB8oBHw`x$y6j+7Kkj0o!eak;eG-$K=YGqzWnPif==-CVZH!zZxHAN^%52j=0k^} z;eZ-cpaj6dzul1o8d83}B1~SrEWMyLG(Mezp3H|po9Fqr2QYyS4Pd_H(HSUk*rS(a zDo6oH59F4S!~EM%dO)`b_p)>x1`TY0hFMt{kUW9Ki=e8j0X?z z>M}4eB!F2|Fftiz5Xi_BFbk`Z_Mo{$n6o?}dnr7+TR=rG11M@xT?rYE1{nz%j>c*v zX&#RSyA5Py9GFD~BO}2EfsBj-v#4NXBG@31kx5_{6^slA8w4^k0?dLMsYx&qK{x7S z#tvkn0W=^2nP|XjqP)k!7p(l-TR^>aP_kt8;68}c#8j}$KqjVvSx^&+Na#=tFddl* zHVEYHEHH}-Mn;1T0vQ&P8SseM@t8nk}OxySrN=PN)8;-j` z!PD&_(R#AP!lgSxz}5P6vA$0)3kT@Fm2MA_*G$GIyDxe$Uv}(1>B+zTGUy(QZKpgA zz5*@ha=iS=Ras;L$T|+kZcydq(R|PXbl}R=1BX5N*I)ExzTnY)sQD2%8-Z85AN1%9 zP^C8df1CGrPkR*LN9XSp=1bZ~UQ2<4g2l%}1G}qRXB_aIVorGNZw;goh zUwoiQ!4VVy4j?f{$WgbBtp`d(AjjM~dX;b)pX@&9(R@T9`Y?FOAms4qK=={k#wS}3 z@VngXJ`7O<8tDVAQfFZRhYjd}aD`ClG4`Od^_t%}cz|w^=ziH9AOl)3Q^dr;(Cwk( z(<>s-9iZUBe4;x-z=QchcLopWF2BwY6$_9-1|FR`DjJ|YG;A*24kE9aUAqrDb|3Iy zZczb^rMq;?H2E;kQ33V-eLCl;fX3#W__wpn1XrB@PrPP!>^|s|`~Z}mXF^&4Fu`uf z*?u0$KR~Tt1yE`f=|17Xzy7Qz^J$Oni_H(^K~dykeVD)b7^n*disj1>99vl?@wb3Z zNAT>H@a?U&aOvKn0_ux-c7FsPiwx>vd0KzrZ!Q51$!&KQ>Hgutzy7Z$^KXyt_s!4c zJr2HQ^{{qPF({7b-`1i68Zm2ejwuatgxm%Xy;IfM5po+m^iEY1kM1cdpg{)2#Y!a- z9^L1`6MUB+fHp;2w6d(^Zvh>j>(dPh2#;=QP%E`NM@0kVvevZ!pesNi-UhiGboQ_U zX#2PI7yhRE3=9mO-4PrxZ-9ENkhKrs!<|7LG0+{ZI6V`M>={2)&)B1S#sG_FK)1xB zd8Wi4;gM(#uup1K6g=SG0UdYdVO^r4;0PLlV{qi3e9*D`;A>kZW930~v;~=M3#vTS8KLWYNLJxjDMOyPu&Jv!q=3mSuY+%ix3MQ@j zCp(59D7kWgI*SS(-M7+QI-Nuu`L|zi?EVQZkdV$sKMp<TP6r|NlSe1_6QQAMxeVjX#A289*IF&?-a*{z->A!H4|2s0bYHZCvo~|NrJ6?&TWY zhd`GogBqfZKT8D}82G0hX#AN7VoW}ye5lg}bT0rzO9WU;c)48T&%=TY4340s+6*qB z`<2nn4g;AjQ_cr68IyqM%tUh-hzPfCfmp7(_Y9AutJ3jsO24 zwsiZb2sHmNaO8Ib-JJuv&sjy_u2RQz@wAV$1+f&R0wjIR-lAOC-^`uk4_=aUQc$9=7&Fg zv`_jnpYUw{sZpfv(J2V3VLdvzJh~Y{0}h_OjyF6y9VNi|A7ly0@WY_m8q~ep4<1$l z=YIHjk%BAe(EVQ?-8Wq<8)f)G1FoQ|iGS~TN6-$p=AUfk9F99dY5KJYoqy-(LK!rwBWV%QmK<;_d&=qc+hV7UXccmgO8ZOC5uP*1&`JP zrBcV6K^B4L(?N|11CQYb1_?ec}VU znf#RTNmu^87acn}TtG)rHveKP7k2IRU;#BZjyrIGT<+M(aoDr_6u9+s`H^F<*RSqF zj-3G<-!FjHqjx%h)pl|mb_nASKVW>oxBDje1l^1Lf{grv;9(>M@Udi|;VR1}klXpe zM~C=YpWtr;4f%nBQ~*}E?>zu&2}n5hMlwR{YnOxFCpi+fpx})+BNB+Hs9rKf=EQP5$*w>-K(`dHs65%&e{cLp7^=fvUL9Sl0M&a;#0wcsvLYtp6HX#qGAeY$Ue zHr$qawEi!#=NDji1n)NY2W?gc?TmI;0G(;0z%Sqr+O!PX8SM_+8SM|>8SM_;8QuD} zM9i}@8gwm!XD72yr?h7`vrlIP$7@B;PHFIFz|Lq6Bw?=OZJt*a`<+8ad?7` zCT7I+_yynAOC>p=mD%?E0?eSz?a(dZUIP3A;4R@!3eYX#UIzRE;4R@!4*UY(E#Y3E zolKA|;Z6yjokkwrZXBMSP8>d+0-!SiIzi_n9|FZSN`B}D`|=RTgBHiz;ht|kq7Z)= zG?7eGuVz!nt3@0h-E5wnOg^1r6#LZpfQO}{0RL3bVT4WspsjO)jIJ#Qe7Y}!a;i^v zgaCMr5WgU3&z_?Jzo4VTP7lyTq{~64<{AkWh7xn9ZXO9w=F=s5{M#Mb9Qg$pJ(v%A zc5*p_P9Ok9h$H{D1CIQHPM|&iUK$?VK8F-MJG}%vAzhwMaCsmB8aeC~^a1r?T27X- zr8WOzf6bEC{EM@MH_fs62XYe(lny08c~Aj#RaO`%iXD8rKYDim03BA?{Skc7L?pNg z&J613^0$K4+qfEEN^`LeX5nwP1E2TqqY?l*Kgh%L;6u>ufzC{zRp-HMzRZVwtq&H9 zf&1;R*i`I6+i$vAE_?7VK2faX!@vHxFY^&lQOLgywEYj%kMrmiX#>{}mmhF4@o#r# z@%(=X)XiN9av3D?ImS80#Dja#{H>s=W2j@8_?tnqXQ1Lu!KXKx4V3o+d^&4XG<>>i zR6INmK4k+PlFGk5n90NXDu$!3gB=y(%D6zw+t+ z>D&5^zoi|#pnE$TBw9dA^jwW^r@30kG4VHNGcquMu2RWJo8Zj9JtU3=)b&&9KIOr` z{=5hCSx^Ew_<|MGH|u0k0WDeQ@aSbxd9Ccxeb$42{b|r5e(*pnM2QQinD$YT@aT0> z5dfW~VSTw+2DEAO0B9>9hi9)yJBW7>9&zAv{5}64_OZUf-&Fno|Nobu)hD1q4}S0_ zA%sIg$eg) z&>=?$U$TN6F5uaH66_DK`+QUcUMqTZU-bZw4_xr*J_U*xP_Ms}M+MX$;PB|>Q2}Ku z>%+yeU`Ou)71+MLEd5}=fQ7(u=g7aE3v{pe|ARiv=RK?s7PY~A<=K3Q4eYZ(@R3?2 zDh{AH^Z>NJ76+&;ZY7RLWwyMOw0f9!q_ zzJUug;|y9p4w?`I?Ilof>0Sfwl7j*$!^3(Hxa(S6;>f?4oYnX;Pw?L;15F=DTCIFLY5|jd?5iEGztYL zIR#ha{~o=C7LMINJ-cuCbiZ=!e(z&_tR&Z`Geso;bZJ)sXp2e&Xaok7lqR^eB*&EM zI-n#bIfrk@nZRiae1jLiXZKaa7)q(AYwI`UL}PugbQkE*``4gbxdK3GCjvCKQsL41 z7UcK@NB*hD9YIG;X+rZ$Y&WQX z=_ui7?I=*n=Gg5h@|vspAUilnEt14A>~pz=TgXqeu5e&`u{S(4IMu z){`ZMF5O--{M#LwT&zzNtAMsbd9pb6Ix?npdVvOBeLR>vnNM^&$#`^n3Bb((72}W= zE+j35fJ*f4M$kBytMP46xOn_O;nF!1wEfGs`vLgqwV#gNza6_j`}DFnxOQLm=xze- ztaIcSWCI@u=GCinz@@tpwElvByDO86^#T57(2^5SoGVNKU2W#reZhr)J4=8o=t}m7 ztUk=X9A3RLj{MsvgH{B1c7ra=W(ja@y{fVtt6eDHL38#(+kld{i79A?;)h-%b$~$4+}t!nWu%1SNL^NTG80 z5a@8ZJE4xDj$xj?t2-cPE+>FD*ML?$F?bjs0c}EM1KlPPE#PYW&9k%kfg|YB&X1t$ zML^X=g=_aakM7S$)E{ z^`uYlTF}OI#~q+!7=8F%Ux50`9bispj!FT@D+VQ`!W_Zhptzpwmzt`M1}zbRRtULc){zZ}Sgh*X{?P^PCx6_+4%|@=rO^ zebAW6MfnEk-YG%QMrxkMpDu1bA^feND?IqOv-|Xlu&1>iC}sBTzTgQ?YVJ&+jAG!? z>(7EX2@W)7&f>#--cquJ4|1ay8|X%_7ogh~E`l8hDi8uZT2H$2yLRJs|Vhpe^MNoh~XK-L4JI2fo@u#s>d_`W)LZ58wei?RD>Q z7ZnH4*x)f26<5Zz<1Q-hoSes8R6IFff`+d_vY-({kSy~t7ZrCNh#aW02g`9WFo5JB zI(fi4d0v7Rnu26OqlF+@Ca}3YV7)xA^FdOe0mIjkAeJvL0|Q9jm4yjz0nckMuoO4M zbQX}jI}a<^?$`Ptsk7V=UG7jjUrT~SeL=&8E-G$d=XkKQA9GQ0VeEEM@%RL~w9Nr@ zlp^Fxw$4`2&=UCgU02YtKcJ!4h;9cK$4=J*&=5}#sHw`pFTe=e({BLkkMW0tx?;W# zj@_XqAVJUwf@?zSr4n=Flc2g5e9Rl@j;-e78lZdBJULwhJh~kPJULw>Kn)vDPS*sF zZbt=APS*?{>qA9nJ-b~6jQ@lBji4h}F7b!ocIp1kAO6InJ3xS6(Def7xB-5_&=35A zu?&_~J|*GZULGFIr%L=mokh?Y5uLslKqm=%cDpL@3xEy{b$#Fgs%#h(7#IXRI$eK2 zc#s1iTp9QUVi`P+yS`wkU}WGI@O|LXS^I%sAoK&jCivb_$338?u}Ak-(5d5~Lvg@o zJInyZHK&3O6X(~Q3p&LDoZmb;_kwn*g8JymF8terSzN786-Oe< z2gmM%{M&sXttuD(?JkgJl@IeV$NvXG%_^|D;FeYQ1&@QT*j&1AfV+<#-7QzaXJmu# z_yrwn?`nOjXrrrTYC)+i=(r{cegSZuz%RfHTEGE{7tmM_D3auiuy&< zZUyB`kKU;u&mwNTcTv#*6&ea4FM(ahFTmi)FW3rNj>|9L0U9yu1#Q#-xeFu?Qo}Fc z0Se3BAE5F>zyUOL)(RT+0i6xw$S(-KbQ0o3kPgrY^GnboK+x5VoqIu{-26knT*R^0 z`@duNk;@NMK?i$uw)TKl?fwTXnl8^TiEsQ5TE1Q4+4#SxAS12BxbZ)DDYk6mf6(G= zP=B9+zjfZ<|NnhE!Dr&UoCINmlDYB!mn{$;X!W@<nS|9>e8 zz7;4&B?FWqK*w`9cGpTY{s*myF5ztc@6O)}+VQ%>mydw~oJ1UVoaJL+@aXLY-Sr4j z=h1!5h2Q0Xt1;xDVu&DA@g{^~kM3*G7-9gObq0+g0WjMEG_2XG0A@pC2y`O{=*S{a zJUBqaL8Vh~0O*)(egTJs9Y!E0@w*%V7k~*&3=A*_KyAr@=mMt$@abv-5L-a!$iUJ8 z_-r+3Iso9pDlP>JD(`5%2)}7Le5Q-{p`;cL?i(9iU~-pu4er zyN~&G>u7*y2|(-BeL-h*3o(ORPDO0S2SCj+79ZwM-9JF)GvcC;&W|CXPDp=W+#f z4eW_xX3zfzJpLc|wEkH%)A+zXkSjfUgV}wokAaT5@4n&5@ASc=`Oy#XAqpib2_6R@ zvVsPrIzv1ShdrAQDfn35D;9L*-|or+Ij4fZX%6^M3LljSP?IwN6gr?H&#RY3 z&ZAex(Wh6W5S*NSEK4Www}7^uICkFy&BO>f@^3%p+5OA2`*hmp)u0Au1gL9~x8whR z1_q>)MP2x}AMouy=h=M@q{|pI3TWd6I$zlON3n=UHxvJMmWnh->jTB?AeBDdCqbiw zpsQc4PZwQ=80P$+#S3CVcZf+zaZgdvyEG09`)~vM~X4ogw%hpYA;>u=5~5Cp~y{Z&8T= zcVQvd{(ufb1{Ma;<|)W308ra10KCf3m>G6FL@4OKSMXdtXnnv+ zFsp@yfuRVj?i(xY_Euds1_pmFka;F>7HFFv#Ev311_lW002}Pshd6cyhF9R}>ZkC7 zG153;vNzx?4=$Kk8#l~0OCA_&EBrW&y*vyIo5Aio!o$F@1}70kahN85q`rSt|St4C}xw4SoiO^7l&u3mjkFI@pSO9_L3-J179J_=+nu`AASIO@g%<>6ThI70B9kgg->sg1$Y^v zlLKf)JY@HR0BDfXw=2(4%5K7EEt*@0BdVtOh>^|o4 z{f5W?lb*~cJebdc4!G_<=&60clli!ZwUb0Sk4N`U572SUjNoozu)+=o0njaGIVuSr zy?Oec%gK((kx>wyw+(9uLfpfi9ydP7tcKwC*%R5V_*gN`f(b!u7gGZ;6fQR-WkLCk6JeZGqbUI1A=JM>mcH9}%$YcP`-z4~S z7J*J#_vmEu>=c8|FZ4PKz>ToJP@>?`Tle4N`#n&Yc{2a-Q2tlK>(PDQQ~QJ`^LdX> zFYtU_2I$Hm(48uf)p7B0he35u0Z0_QTrNKDFle(F=yn3=T2D|5QNY#swj=+xA0FMe zKy@5wfhTD9S2y_n9BVHJe#kMD-A*1Z{M$qr!6V|}Cx?M z;n+P9)F<-j<(cHu%W}fA*Mq~evlG-8>~&&v>;w-&J2w1cE8%c#_{CA``kKpe6D#y8 zM9*$V4bR>{4-an8Z9Sk<(LA{w4Lmy?13bGuBs{r2Ej&9tBRso36g;^-9XvZd6FfV? z3m9CiuDfo( z);1e52bZ7rZxZID;4(Wbrb<9cLFUm zvGwTWe{JE}T*bjq%Ed3hq5@ic@md|EvRILS`w6E88$*V2_B6-l-^`@~Fa@Ay5OZ25 z8)SC)pl9;|4sdHC7&<#F0Gb|<0C^2Oy=dUseF&67p=*&qJn%XT&|xU544^WuoWpilQP575QXr@@lVwV=gbL7*G4 zK$@;2O*UO?*bPcx-2AP^pws@k!yLMsK`Gs*Ge;%DrF%7acD1uaCBdh+%mO^e1xgMI zoh~XF$D2VL8bKwAr}Z`d7SQks{OmcGPKeI#Q=Jgwx{vvETY*-Ibzetv66j)EpKbxq zZg&pw(Xnn0uKe4QVthbnv4gHoF5>X%H1O;MotWyx;dt;3i{tkzj?GUPeL4j^I)gJj zy080mIu|%@;PL77uJGvW28~G}JXc~2cMrDk&;yNEZQwx)r`7|d*HBeDZs0|cFEN1h zkWf`Y!WXo+99@0sHIHsT2aj&&3ebf`-FzP1&0sfzx2Ablcyzltcyv!j2!Vaz+06!8 z9cW!#0jdnW3p_ggz?Gq&gOBxQklGSfmC=mwdVpT22Htf=V-8x;sHDKOLKYDip_nW*Hs3 zPdausfW*M}M}y9!?DZ1xWIo~BebA$GB4`lKvl}#j;KT2F05l!L3A%)e#j`s=!KZT~ zXgt%Sw-Kbur?U~XLe!&kBB+!-?9qJ)viJV-gXRa^hm8-ko&+sl>UI1NKIasatr+;H z960>{WaCe8w>A`MO)+S+fx)r62Nb^yo}imNK|NZQUZQMS2Bb_d z3$)TP2h0MU!IceWfzAxd0<%D8sN{lKpwpuAz%0O)wv?2i2Q}!K0JI_@qa7D`*4|G!qM&a^)BB zUEtB_I)h)pbppSjk4gdP?w}e_>oWmVhGb0eXs%I7VCcSBV&>cW1k^-!QK=@DP~`bjSJ%rc!Z`BLf(~t6xg@`EFV|@a& zbjG#8qtn^JquaH^6Lj~7vje!3;cVg43EC%pz@xht9E-5?DZA&W6o9JR#v`EE22ae# zI>vyOZGg@;f%IqRGQy4o1RWa=x@eY#0i2XBKTrkTYkru2yAu1)H^bHb~;Lc+jyOhY9QrsyVX3w zty3nS-XJE(Lim$uosJ41HDX}*bvi2WZ$IG3zg@&B?cgbn*Q^kxV`rd}XRjBNM=y^f z|Mo+kotz%Mpe-vtj{m@RQ^j4NN#B#8^qtn}2s-hZA+6I9bS|y~Q(C7ZXpGH+#k2VU z8^}Tt-%iFA0wvm@B`|9I+nvB0*JVNB<>A46!L$2dnxz+089(%_Ue8WXj>8b2JF!5b zh7B4u5+JX`gGYjY`vDJV)OhrYIC*vkf+Ir;stOV{Qm7#<01asws3J%#%Yf|*Ki~^m z7Ykm*4q62UE}=j{$mrV}2=W#axR3&gFnKl~0*SCVemlTYf-J)3`0W4(iU^0}w*vwt zpa_8I0+nizJ@5jK-wr5Xm634#cEACvjDl;+fl6$KY4~bi1npqI_$$q&Q}D1uD5y9* z04WYZ6D}ylA*lZS*2uulz`*F!eG}5VZ3PWTcy!KDi2&uQ&MhjSIT(-5J>W}jd^(q? zSb*nbp7?aGQ32&RpUy2R8la=BJNKx7XL{rv7?7t^n>)A}7(gfRt1&Y${6FE?2^Mhd zo()=)1KO|vx&Wv91sCW>(=FXUxEL5byH9tPsA#0|*B#&&blt#wqx*7ajEX|{Z*XnE zFW|eu2eiN&v;`D&(s3Gp{z1?ZNtQH6{<;I8%}y%d0A+XVoDEtD1d1E}mV8i)y?ZjK zr2=YtM}XF=IDls!!2ao;3>x2p9;@xyco1Y2=-6rtU*^-G0aXuZuqD z8@zkoRjkeN+lScT>2il<*7qs1$UDHn?<#w(tx3&T#2Y zozY#oq`P(jzo2W!Cw>9fhEM#0t}D7@VP|N#E&x{>hl>>dA9w8j*jc;4(fUs*7wB3u zkAtrjUUT*Ovw`=VUViLo{fECD)T`+}_WxM-_0G~Ip!fux?+sha=F;tQLIAXCVYy@D z5Acx&em;-|AdcM^njh7J_Q4!{!4BF;4Vn~(U1RBHu?2J>4?~H9BWN+tSx{^+xOSgQ zYdyf<8VOo#(p$**G62Nr{ti*%3f+eXIzk1c$`~ZsTgmuR3&i052Tn4e+A&1M0W>$_ z;qm`ecZ`aHXY)%1m+lZ1i|*gxBms_WP`YRp{P+L=YgTaQt-C};!`1kSOSg-P3TPxh z0HhO?5Iwt_L1!I-<{lzEIt4sCyFpyX&e`D9#OVmiz)U5~orN;sQiKndRyor6>l{IO zn7;M{`vInbs z%f!F{VS%oUfmEFip}|Nye|y)cSU}1b(1a>V`2t!E;L$x7R807S78HZ0d5ypMf<^@& zd4QISpDZc$>23vIp5E(P;iG-Pqt~~>v-_eWzW@t|M{lFS|No%F#CG zJem(?cr?FaEM4H+9cSU$8)o6sy%rQk9=)!hMwWm_ukQlS?1LGgtAzx8C-`*MHh{8M zg>QG91Bd}ySjW1-qt{o!r`v~h1E_uG(d)yy0<^vnvZu@iG|K2I0BOC23P3Jq0k!VH zi|aT%x?4fzO}FnHk51PPQ1;^&bZzjkEN$R#0i9#y)9u>f3o0!^i#I?+LrkFi04_5y zFo4b;@D=!fu+v8+1T>52z~a&CDv$=s@Bu#7r%HAs3SDSJ4zxOk0kqFZ&>5u8nJ2BY zn6I;#!=sZKbV9mEXFF)dqjNiG_yp9jgZJ=3mwy3)Upilus+1!n)Uzx|85_Zke5RL|Nq|y zG76-?v)j!9G)tQR%8(hLT_ljPX;14*C7d3h>n%Jx-5flv!50gIZ**bu?Dlg2IYJP0 zDK9w0yCGLNBhox56(X8}(Xod?6H<`m&&vdB2To^#jrcQiu`qC@urqKOv4Q$544`uc zVY5J-;LZwYBP@gk>R3v%FfcGPD6%p!u!1Ht7#QF>K(Y{Zp!4(~EKsQi(E&0SbWkQf zvlSs`LzRGZfS9QEL(B!)05KP2Cg@~CkV+7S$fN2}2b;^t#>Bv8!~{B@nE`a77)Uh; zbHRNHVS)Ssx*84Y%M#EgNCvnLkSs(U$PEw{$d^zZ3=9mQOb3tZ;4%P#i3BnM0R6Ukpa~VZB7(`2W8AMZf7(_$38AOW&L3xP* zbQCB^CkS)HeGFlNTxAc|4T)DSkRrGakSs(U$UP7i$j1;JAag;*Dp(5x0|P`JLs>d5_E+ZEc16L6nI9@?rY^VkX(3M`y zpvzsLERahA!8#JEsxppb)@3o?5%5Ee-HWQf_qYz)F*co{gZ@G-EI@Po~V=>(YvQ4dPp5EdwP zLv(^vNq|>;f~{bH$fMdZ6JiG_kAC50V7S7EVm_+NA^Jf!LCgo4y@r7K^P%QLTmbSl zh=u9~h`AuUAYlVCa}NP?mx9e@#F9Ed2dskZ0AXQxTtir(P+bkyDJjRoz{4odz?uLl zdq8VDK`ua)J)i^iL1uuk2;3Y93uMklusMoSJPcA_q!`4WNHYjskzwFFBFn&4BFDfI z0y+VdfdOU;XqOH|FQ|Nius|vxrhrrxf(Cj)mN76eK;%K81Yv>f+YYf0ssf|~#6(pC zQ4R73#9WY>-2}|t3pSULn}vb9goAcVt~k_>NyWK7ZUr546Fq#pp&^7K&yE{<{)Apba+3^95J~6AuN#lu7b@0 z*F0Zj7&xBDGO%0$4M#IDz;uFq4p9&CIfMnun-HBKRj^tZWDi6hj^WXElU9Y^FD zI6@Q{SWMKw=EHR2vIC+Mr0ObC>_bdOwc{bg4hD$3K{`OLK~)3M2MQmExgay&5-|5U z++2`(Am4&$T;@W;1Y{;VNC`+O0|NuZUXTEU1qze5U~|FwK7@yX--Mfizla+ghk8(5 zAW9q_V-OZd=V!1^aO(=x6lY*yfaw6qLezo$2w{Oj7NP^B3UrPw$XpPH$b(!3VS#jm z4zgy3w@*PjKq^tyKvaWlfS3z16LelTNF@kEbQ4dO+5EdwHLUe*ufwp{t z%m-nJJji7b7RU~6h#eql286k&Y9OjXHbcw>nF%_q8>)!`B9E#^5Ns}TKLK>H0ZfM^ z+_w-G$R*+!IzaROFdb5G9S{~shb)E;P>BT70a^wOspTOokPc&%QW!wwQT1el z%?0;1K==1R>nf1hAgl!UC4>cXNg+fBXk5jJ?E}jJNO*x}<3KtMb3|uLk46G&~IR*xpZcunb^nlV3gavBdL3D#u$$*Y016jhrzyOg4`3J%R znNW+>)PX_>!UDwsLi6(Pua z1_lNdczS`bKruBNY$iBeLSr1cuCjsafUrP17J_wv>yZ#h0}7@CBnwdo@)v{!ig}0* zkSfrXT~Nm`K;%K80%3u4FNc^7YAcj5FtDbuFd*wfH5H-@l#(I3KxQM4;6db3%?DjD z0v(StVPZhm0kR#U1JxFY4v@K7NI4v$64h+b#lDdC6SBFeY9RVRHbBe;nORA|+?|jx zVPHTB3tZ+x!UAL_=o&wKVZ9%2E=Uz9oIx}$b0KjEG81&EJxC=8L;QoP=P1}*aEb!m zL5I}y0qL-Xrzi*u6z-=HI-q4EC=G*DgRmW32ZROEaS@_Jf{Q^SMSwvfgr7megpWaD zlE??42asAAbTtx4HwfFqO@OdKCR~S@02=*)+6g|wAEcatfdS-4Nc#!G0_gx2qYxuE@w5R(}gAo8Htg|I-n zze3E0Dgo&LF;Ue(RD*1Qm1KzR4OIft0b-)6 zfv5)A05KP2CTNisNF@kEZ&K*|{y7+m4Lgs?y^5r*i%l6D>-g{T`` zCxivk3A(t6nSqgqm4PRPg@MO}nSlp9D#E}3o(G27=?>QkVS#kYgY5*D@FkGQfaw6q zLezo$4Pk*o7NP^B$_dF;5P47tKv*E%st~iGwu5wln5b$XszEkD%mtYVnwSNt1Yw9g zsvd2yx#08z8Zu;HV1PC+K^ho5;J$>gKrS)F&;h!@4Xhk=$S+)nCzu7&VGh;-4i^)U z?F0U{CrCGh1#*=qSU1?mpc!oj1_qc8kSs(U z$UP7i$j1;JAXT7QYkX$=L(GOM0qFoSQSFDA3$g)fE+YfONhBXb24RRi$Y&51$ozD$ zI~75roKGe*upF5J9#e$r!eu^07f2Q8s#K8KAPg}T)%;wD`E@J|bs^IjSiVeRK-PuJ ze26ZP*^iK72AlcC5c3%rP|QVj14JLlE{M4xGeH+jf!qwj5P4KRm2h*BLlIRCL?6gp zNO*wEZfQD1-%y`ECpypxzotH3<8`bwF4k9TOosWH}gQQ@9vpLpT{^O*j~2gCxOe8+79n zNGAyU!*xSgAl=g;xe#h_!8An6~%0_mCy)+K4k%OIaJhe1AMHiNv$ zECzYTDGU-H#2$!T5IO*9n=vpA*hGvs;1 zXt+HP7D&f#unusk3awvZIzX}zb)XalVS!2uhz^h{SUVl$B8WUFJwjL@-3K9NgQOV{ zGn%MsAgVz&K+FZ12|8gKs)+$2kE-W5*j#YwvI8Ixd5CAcqS~2S^s84&-VG3luI89UxW6s~RBkAlE=xAl)}1W`|miYcfjhBl7*-PxdXxi`4pl9qzZI>GCs3kL(GOM0qFoSK|Y0;hH3-ET#%Wd!=FGZK^USE zRnJGTxs1H547^F41snB40` zL>EX^0mwLz;S3B65K}<{5EjUMW_D2O28}!;y9QMaL^a4Rh`As$r-GE=F_#l=E=Uo` zY!D671ThU{H-rT;7ZM&IGZ!I^6G7xr_3(qu#nw|ro>!g+k0}TXWSb~NCt3*zIyejD zG7z2**9BpLbV*~?1!@t1bb#;zxGo3_q)QQ^3pCba#QK0a0n)n#UDXZJ0m2L6x*#l& zE_JXjMNog}3pWE-2`2+<2uPfP0j3iaju7>rR0Lsx#uFeqL8`!2Ajkp+1_p>cDEuHS zkXgDAJ3wQbN4UV|!gPRSA?iRe0%3vdgXjRM0$q>_H5#kg#t^d^Anpa}0I5c`A7U=Z z28g*JGyj5=K`}%gRgWdyT#zD=FF`b_8i+oSxsb2`naPIKW`oG1>aho#3n||aErAG- z;ZVE?9!n4wD2!Yoy0}>xxEHY(uqChtKvpOq))Ig;FNW)cus}M!!8#Q=SQt322r{r7 z0fjC?7s%}pwIFc_3lz%`U7+;@$ZdUyJjg!~7RdZSi22~MNQi;u3m15G156h#^C7xG zs&bLS3t}p&`QZ@rL5Da!5kfH=WIIF$sx1(+LFOJIV0J9jY)E*3TmuS4RQnM&K+FZ1 zDTLIvg2AG6d)vY0G72y=2srUP;DQ!Oka;zTwx#e` zg0MiL*bCOBsKCac@P&neH-(FVH3Sk&Fn1}~T=a4nbRs^;d=Op+k0l5T z6h^BdxrQ&rl5vpv|Tb9S{~s$6kmI&{#aQZvmyinm|26 zhzG7uKXgp&{x zxY@v~#XxJQm;)d!kQSIW1_p*raGekqNauO5PH@{DwAzq?fdQriBnwdoidzT^lqw-Q zK&n7zn!${R$b(V_gay)l6=F81_8`e@khxPK(E~OfVj`;9w;^VO+IClX7+8;RF(B(g zbumOgDBnVKfy@S-iVQX!N4PwMn9sm~VlJv1Am)PXf|v_36Ld*BK69VL%>}6fg(8T? zWiBMFL1w-{s+}PAqUw1IHkXl;m4VXOkQLVS(&}=m4qu2$_}z8O;EZM>YFD#B2tLPeD3BN>SB7^nq-Em5bM+!7+_-sAO~)Nrv?ZMXG2&ZDOreJ zP!%8@ASS9Bh-y&GLCgi2c?F~yq!fp_%3yPmM|VJLK%g2JK-ca;N?-^JWSAyc2e=f3 z&V|5qfMg-+KyHArK=A|70a8^5Gnjz^tJ(Sxv!SX$IzUX2FCnI(+5j;ZWG3k98jwm5 zhNwi2n%G3HCTrtX!UvtWPcNEw-QVz$Y&7spp*w;f#MmW z6Ql}sm<`B$5QfNuTm@l)>~MtG!NA7AP{P8%kiyQu0NoJ<(hbsu-wu%Zk3q_y7-BN2 z9qv#&AWC57qN;)D1H~D{?I1HjXM#ZVFfc&mQT6zO%|&mOSirO}FfiL|BouP3>(s2;31HuC7h{VtV>*0bl9)jzDus}NEAv!>{oe4K+Uk|ulD-BW(#Yf<} zAS{rsRIn~^s)o*Y!E}IRA?iRO1Yv>V1)>9_3UuxY)My5XJSeOnERgPOh}lpjARQnk zsv3xDkPQ%XL1wBU)kY9`R6T`Ya~XNr7A&4uXz z$wJhjnhntbQUy9n8fr8HL?x=(%@DI0Anpa}0I5b*1JMVv0b(x5Oz@6LkWvN)tmbyY z%>^j}`4U9qG8YmSAT!S+#Sp|^R6YG*a~b8h801RC801n!8RSev800>ISF3eIA}JgUA|dPyA|`C09soEWdz>Z7w8fKu-@}~Fy#yk442_{L0BLin;|;H*%`#0gg*#A0Jr}GxS>lbkV}XwaNQ6VNcT># zZbcy$1|iVu@++V?gN(|7tOcbUh+0s(hp<2;JVY1B?7JXkPz;d=nE+vd%-;_&AF2YR z1H?pC15pjK3t}$F%%31-Pz;er)pHbVuA~7sgS^QW26@Ic44Az-28IAu1_n?o12Mz) zMHp%*0|Ub~cz8fqAordIo69J`!60D5!ysV9^MUIEqMxXN(98g8wL@YL!UE~O2-dC0 z&%(g}gd5zu0BL~f0{IZ478IHg7AQ0!x}a82n%HXb%^;;6(AiTCaM~U zYLHzJb3tbA04alFh&-yEyI^w}HP{(67_}H=K1e-~xFD7w3eJ@b40EA6K@_M@0r542 z1#;D6ux>?ZHU{Yu4F=&O>I@tvpd*zKIzj%0s0aBM!UDAqAUZ+jgU9A$EYILGu$(8dVKMHOS`>b3tYz@0o(gqw0ANHJ6Qn;fOi|Lx~0h14(WNn-8fr zz;423$5*f&j2fH_8jMy9k{`q`h#n9w5K0g<5P*g-=nx{Xb_U3va!3e20;f-q?|*|$ z0Iy|xVhL6V(*cr&r~~;M!UBahLaRf7(?()lPrHE$ zvM(Kxi`FCcDxZQ~2{IGH0-4DUHj`15lR>nEok28(jX^Yol>t0H0lH~`fdRfB8sw~J za1$UbkO{nC6Tr1Hv}K(IQp&&py4HgMq7LL^2n*z6hz^h{kguVNp(;VHgQx_VEetUm zssyA1#6(pCQ4O*IVlK!`cSwN@QjFDHNwB$+#=H!QCDIIvDN+oIA(9M=CK3#aj3NwD zA0!@#L46N8D*@E!L4@cXB(p*5OCc!$!UDNj9%8mGD}xNa^fGlecBNFK)W?63K@`ffLsGH5fsu87RWCU9Uyb1 zk*auzJSd(ZERb$Ph}jGbDCVN7fv5)A05KP2rZ)j|&Ee*PJO*+zh{k0uBrHH?#;JqU zK`_K#R6Vv}b0PJXAA>xj8`gRYw9XF{?ua;*M9OiXv#cRu3}J!7!5Lz%Dhq=M<5UK= z2Q1*!k$|y!3v}x?#B~e|pmQG~=0I2=b3DQ3C^qvjG=J%5sJ=3Rq4>x|2CJB_vEB zERg-tV6(tGM4w17a2%0jU?~CJXbxGy1=0yJ527Aazd=}_`VFEJWPUtS{6XYF?uD>G zb|gaV02u~d83tmas)48mg#*M~keM|E%uR=y3p$%Xf`Q?PBm)CUZU>ov5#$Vza~QCP zPcGCBh!T)*K`f9ah>Jn~gRl^82buW+GF1Xnj5XYg!RAV;voeS=z5(r+x&TSl3=9VB zko6XbI7G^J44_>wkT`^}K=xOH%~BL)V-PKQ#lW5Nnt=sc&cSqo;t--96o(KNC=MYy zL8?GUQ-U4Kz<||`dWapMoz!1mFt8kX$$+d2xyCW6fckBgLm zPPlMZciV+ICD>k6!a;Tzm82n(cRC0K`|EE|Jt2@8Wr2r~n#2_y@` zbbK$U=WfS928fS3ky2ZRNRXNb8VGeKwh zf>eSqL>^VoRJ`5Zt-XP836DU9oQ0)NG52_s?EKu(SViL$c(2jAC zN)U#~gWL{bf$Tg8u@fZCfH;{0RSiTn$S)9cL1waolt6Vs zfv8{bP=K&NIziV1GBYU3b1=w%VPTLu!UdTFxxx+7zyQ+?N&^r*pfmtsfoe^NZjh>3 zkmLi_1(6525W)i4a~bSH(7G#-5|9oMjj9Hs8su|`xgaw^YZX8$K^P*Bs^=!yT<9(g z@OTJpZ7)d2AGnVpERaj?gLNnhurLUOU_U7Wq85|`AS{sYA-X_j!`5oxGXE*We0eqo z`7cZi!cUkPSW`H_tGi)3QB8)}0SZ@$PLL`cNFf8Vf&n5A@->77^22M09nk$)ARQp3 zsA?dpL7@pT7i6YA0dqgX%>|hU@-2wQWiBL4KxTRnF!wvyTt;CI2H_Ac24NFU24N%q z54;Z`JEcE?EP>*`@EC)zK)(G8)(!4OrGWSh3@{xa9z-3;pAZ%(bRjxGs^=UgatBR6kM@3!3sM2{ zC5T2<1JMTx6-Zcs%>0WKGZ1-HJ(gf|!SiKM$NGZ|hGF>1b_OsDz($KIVV+}P zU|@!u0%3tn@dlg109vSnjL`&?+0#VTrS?nh^=uTnuwmk#2+^6>PR*TlZll1#w1v;k zKHvA>H23Q%hYfjq|7ok&vDke$?9?#-X{M&L#ix0Fm;57rju&_2C#?-~bUC>oVN*WS zLcY&4!*;pqY%`ne!SZ;*<~P@D1$K+8NSWCkJHPMUx^iWYb5lRQ>NKC#GwWN-Oy8T& z?G~KUzj{!~UAuqTU(=_vE-4Br99m__4VTVzdQc!o|Jgm+eV<(dWVD;&(0OjVpsh4&$z5Dpdud?HG{G1 zpWyzldOAwa>U$lSrhVgjqwuSL^5%a~M=*YbI&hhbQ>BW^rLS3hM zKNVEUTsf9KcQM!RHCI?GRFv*L`1tIM&pyVr=@QK12QKMM2Ht4?$@1X?WmsU6TeSKicsxBtiP_!Yn za@E4xcY%jHGqinrYIP1QuT8nP^J2a3H*awX;Tu9hOMHHbFf}bnnry#p-@3ZPENr^jGS|PZlGy)x^#Y}%oJWk3 zoA+Gm`*C%}{VdrVr!$^vx z&@L6u!ab*-Z@y@jV1NHxRPHOutGSL|rxqSkk`#Ft@Y?DZlh&g6Q`hf{O5QldvmyHW z!HH`XquPDBcQTsgsg@#eXu7fM$wzo-?V_xo1$w;=f) zvagqHGpUX-Th6~eiqB$7)+n}`+36_lS$bNZs^E7e6xI~)0M+4 z%$tmM`1-s&zP`%q?mJ0wvBL|ll`Km*rD5QDMB{*M$#vGA=efDLU(a_vsfz8Lr7W8* z5o^4lAfsfmcYVG4-DAE{a;5v{_D`Gi>|^=OV!le|oaYCo7&FDM>5Vu&<@KTJ+D4z#YONl< z+~QKZ`rbdwFZ-gyKKoO5^VtZeip0+Ism;dfaw6C37tfkbqt>zz$ULDmR z{A@DQ1ektkij#-Bx#q?f&VDdwq9&`FH%z4ZC|wF9p5brWjEi(RgOD!&!$- z->;=TzFWjH|9h=a2I|)47_orY~O^bmaNj!YKFr`t2ge zX5X7%b=C0G-*UE!&g)hdb;VuY=W`!?xcqzFr!DuYuL>O4cKXemj~nVr9!$TY0w^zYj{q`|a0A3l&cAj9OgL z%I9DEzP0|Aa}z5m|O@GNeX~v5F8>^PzFJBln_1cL)tWPi9v*jth z-`E#r^2*8HvA#X|EPvkqoKJmxa_g2p3XodPB z>`lL;jiH!D)Hd-48!9$A&5#vZrN+bkk$LO!1OcWHcKhqznPnH7YZUV4uV1p(^`gvC zrEg-MGj}OZ>g9If(qpgEc&=5Vbjx<9P?Lu8#ZCU7n@k1H?^(onM60+XNA=62KXh3|MTn9w!bKT-RT!!+ZChpl4~h!#h<~v5hUPIgS4JmFD=y@2n_~D_?na z^P6Kw+a+GjNVuW5ORd(8q08Dsw#C2m;6{60>4PsnbV|Q@pccY)eV@ASBWGro759>* z8c$2X>`Trse8QP1tQs0`ZgiI=FQRRdaNml@MGWU>OTL+;yNunjKkw=S>l>+V*Ce+J zU*b~ux$O*7LgbzK4U5B%mouz5`dUC%Ro6yk?&C;8SaIey^b z(<#mB9D&Q9o?SVYv2Mvh?Iryx7r$j2ovpuRX1t}_?49M$Is>f*zfLcDJU8=D%e;*1 z^SN~7>px{lD3>q)_9*z9*^vi*#|5u1|Fbo+IiWfK^aPgT^!BBv*XRkaS+;wg{9(m6 zuksqU{jTZ>7q(%Ww{c6G;OD-*vnsaDV^u8@4FA$Qb%WER*dF7vrm*z3xvc4NtZvUU z7=Lx!sxMVxA0uIWnE$;ePrI+>m9Wh3myh4;TQ2M{|3985COWfdMm9`6Ed8$eY^wEf zC%?yi)r}i`7?Z6fgR&eQCunHqXh&f81`E`RnJoZL99* zto(V-?Apc`#>P(7`eNcMOTXJ|)H(d>lzzm=v4*oCcBkgmbOVVO?CUojRqF9^x6zm! zuesiFg39w1d&M%e4JTUk_|5PBx9z(}+PiW^%UO$6KYq>dx|y?m!3FQHmtgS|v~hx_ z?491FvzZUvT{7(_|KcOleD?i&{bSd*i7INlc`LfAw*21xMzQ#v>)!cmUzyDm6wuH2 zQq<5mGdE~$dQEF#@#>CM5;q^GeYnq&kvS{s@7?!@Ti(t59$p$RI+yAC`$qP)K8tIO zOFCceE8oHs-c`DCrp@u~mKnEU;k_WZ<09({Dc2`cPP&?SesFi}=RCZ8$^@_8M;s!v zJVf_)AAWK@zubLV`d?<@9q04>OqazT+II$Z>m|r z%n@*^=h^P-N=phNY{UE71hiM2NbL#KyJiQAKbZev<{lLmTr{2{F?q8~ww})ax9`K8 z!z^6050sxZ+n-Xx!Q^yx-nGX9kN-|Q7HD&SAI$&SD%+JduS+>QA?Q9=hg9qDq}wq9 z_fGH8H4xjWCy{zoELr1-y=>wJAy|5m?XQ<}-T8EpPP2S{#fbcb5p-}y;)Ia*ps0@PwdqeL0EajpzXJQL1v>B zj9lFO%c)#lmG9+9&QuAW#10EjSowNNdrQ`B zw!0><_|OpjYWd-e`H!nlXZ*;$nW@ka_Vvam7q!RPN-KU_)s_l0-sU?I$Z~s~5m(;c zO{=@g?@Im9NPjTt>Dg(Xv)kJyCOA8%%!~I@J)vLp&wTC8cVU^JH zKLr^tBDKpUCLe*-2R09QgVZK&T@}Zkv-M}G1*e0i!k<~Uz9{D0i!b`p*w+P%57VFp z&lgFwor3AFJ*fC@FSCj5bD6*AH?OH(#&q-C-D!zO_k^VS#g(c&(hbU;pecGMIw7-W z&$eEcwkt6Ez9eaV>o4^Sb5!3rajPx24$Qvyu74$jFK^a*?JZ+ttt7AE`t88KISI=X zH&zHeI8d`F^yfom7Vj!~{#W0ZdEXEeJZtf4(N?BM$5(be+*o(FiS1Rk)xI#+clk1( zI57ye+jhr?%FQ)w>~6X8_2t&1pHiP4nWR#e(XJ#K7RKtaf;;&_m2}?v z>6)#Vk9*a_>c=l@_TFM+d;Rr>ZS(fi%a)}X>(uWxDSUnNXARR1+vOj+dW?T7>+38@ z;rZ&u-)8*3W~1Sxm>D~t-D@qH-oMxJSK8gIoRuweXTSaL&M39aOg+nM=X3ENPizHl zwCEH`zS}c1%bMr>Yg_&^Uvy8b@#bH;kH6{lm(Oi~<(P~YKXy7&Fn6Nh1It!t{*uIu z?az-)ZBTz1;U~Fx(+v-W%d?aVgjpg^W^=Gij9s=C79P1W)l28fT-wWW^xwXCBLS=R z@4L&Y96Tq$>L;NCCvyHfSe}BFH?aB#mL6BISx4__&OdD@`1)tx!~=IqUaXZ~z3`X- z=bE`GkC@)abbX#?QZ318=)>xK;WOub_5ChQKebD)tnXT`5L~oMhga>?#K!OiFYkmE zJ1TyuTlcH}aA;4-i=YzuBk}h$_l0n;{&9XUbM|Vj{7ci49J;re?PWh7A^rB(mrp(C zUOx3KERFtnSDNv~>kP}BSGUgJx%)39Y0I|vQtd8FLwGix)p2}0QS~RAM#8BzrXCO8 zMn1^0`COp*d-?V2(b|#^`U{dpO+ERp7%F#&24}xX+CFKG2(NJ9>N@)goT7Y)q&Vw=M@uxb zhc~SLyDYIx(rRDwDVTa#`(@V;|2U(K`#PxXf37vqbl1yVMOgaWc-8CBr}>U7iebWTvW9&&F#mFF;(p}6 zG&@8r`TF^V=X2J;$^)^ei@t$X3AZ0?ySwxGv2QB>Zx-&3HjgQ}zVEIBzhV$uW2Wie z8GiqX>yxfcyJ=F`U9dN;bH&%9&{7{$PfnW!A!l=CZs)1x-#E>;ZBg9*>2CYp z{O)x&h|yLvE}pmciT<3U{d??h{`2+V|8w%rN}YQS-!S?6*J9c zw7zh^?S==IwRwayCCn z4)(L?bYqUp{aR5HebDZzaFxScv$P#ik`6+@rJu{Z_~gg5dg`C26>DDw)aocnSWIzE zTk+Uo+dBcyjKJHPS>5?_r)#%G7KkxV$XcADI;HA!%l0{sf@XC1?~zst%{$ts>84V* z)cSIW3Ev?luH!b(cKo-SIq&^?rxVd${BtfFEEM~D;!)?he2vbO?*ggE&$OI#z3)^p zQSXkcFL!rH8cbgQtJd0l_c_P){kDH&<(&D`HXta*Z;`%>a%A(jsF|5lFRbMePl~gC z-Er}wcF~#1cbaVz_bvGG_j}D856cUaHav-6vL@&5kB#503>Z=>9G_}m=Ulg7x6wvn zv5>-fvV2c@gbsV2x0v_(2rR$C+6&3h{@VWT4-Hd!^w(ss)~r+#WC)Zx*V4H&HSCx7 zm)i%_rd6z2yGQJt4V%4gyNHdvu|rqPN%OOfyyZUAk6zo{&8B{h9oC;y?YRV$WgeyFmSzdC@=oEL&}$x#&J!VOHQC#WcwAzB%yLZo^VRR(Y|cef zZ+=+lFV5HY%*8!u`S<<(4M`PKVC|{RFJCU(!(F5HSl4=b&U+To=^i&78S-+bH{U+; zm!T{%zLZ(*oY>l>?as0?v+qw%dkAYU3ETldkW-E{LhGSFWiWV~e+jA}WqDxw9 zVPDgoIc~dp^tCE>PSQH`ruR_NvWrvdWWsK0ELdzTG~;}?NT|Y0&6N))h5apBc#zl6 zxk&Fx^o{jLe_Br4!&fCAvRgr&L3N>1K&@ZKx!wmq!-{(DAA1MupPAhGaB|+Qr-Cu_ zI25+r^_>{@>H)Jdzees}t<&;V_N!!s!}Z*Cu84O<&Uifi>?NNfSpQ9Hp3|Jifl;4e z?K!UXwJ7z}X`jVa&K&QAZU{S_RDO9<=VL7UT#vx0hn;T{Oaruj zv&}i7WgfH?HWkX?ker`epqH6a*~r59|37HWsY6<7Vu_xC!30Le2B=6`evV#l3TWpi z14KkGr6@JIq%0LQal**Jz`)FiH2DD%1fBk#lA2VSt`ZF5f+o|M8DaY)K_U>Wmy%eL z2w{RIY8aRqcW7}kNHZ`nxG6ZowvmHx7>6hVZ5{++1_sb2wV>HE5D$bQJK8~~fq-}* z3^{Qhbm=^Z2f_>tptB)BE6qSW5N2Q~W?*2@UxyA&Ky%R#P((nt zetkd@DPm+`*a4o)h3RT#WMH^~A~KzkfdRC(3*>JQW?gf zMh1ok6gA%%85k6Jkacl0F)%owh=A5bCZLEIGBGfm;DzbZD?&s(Xs(T!5w;kraN;GT zi))-f2?rYg%nW)ZsTCzExp|I`9RL!f! zn~!6vVMj^5&vzb_##F-rRs%X=4CI`aT5&9Pffmg%GlI@E0I3laQ?bW12edDgnGv)^ z5|n4e4L?r7R0CS4!psQTO%AfvKuLxLQw?aQl$jB9#u&())P!eiG1Y(;zcVv}P8$HJ zxpC9s9;O;lm@_kiPRam62s-l0O+d zYCtl~jGz^?AajmPvA&C`1{5;PjG!|VL8-CjqgyGa8qh`&W=7Bnx*#?GSEm2NR0CQ& zz|07`Sql^wJGAah#8e{+_7~`6F_5jdF4t9IssWWT%#5JbD4;N8;CXikQ;j&-9MJi^ zAT_NYnWQk)fY!n=GlK5z0;O7OzdS7Nkp!Cq+SdpQpBTZb2QbY6t!`yzGzTeQU|=|F zF`*Sx4XEs7W(0*2NSSh^as;Lt&`Kv}M$me4kb6Xq7_7on1KJ(T%m|8okU3Wty?KnO z26VzMGb8A9d5}3S)+f(lssU9r%%IH-AY&Nh=lxiYsRpzTjhPX&yaE)4u1n`U!c+rV z(Zb9Ky5|I>Ms2IzGE6m~-MGw*pi_}SA-<|0eIceA(5gjdMv%)v4x0E*i4Ri^Xf+%& zBV?}(1H-XHR|PTEfX*&sW(1w42+BKA@rztB)qobWGc$s01i5*#(34I~HK28!%#5HI z0ja4!;hKi22DB!RnGv+w9i*o4qCXb*fLzPW7zbLxz`($epJu-o(;U!=AIyxPliWe( z{JtmtA5#rztv53x=wx;fD?4j17Q1x7X%%$3E65yYL#-v4=IDaefZ_q9MqE<59#aiy zuP-wr==^d}K27hJal}*uYF98bf=;^w<#)qn0^*ozKq-fr5p;?($QoPmLXf2mYCrW(+Z&&-UVThu^q zwwZSMC#D)xusNX94MA?sC^?Ts4QS0aGb1Q>fZU_-!jploLVjNiw;afT;#_LK8D1 z=$;W!zA*ivSB{9P|63XQEhR-(v}DT+XXuvbm`fzSjsw3%NbJEf!uR1&Pf*2t}w7U zpjr%Mmw;456sDSRuo_S+2xN}N5}_bWHK1M>Gou=`<@ly`#ZgQ(pj-@TBZ16`V$s0T z#s;14#LNiVV+8WQ`p*2RnC3)--2>a59qzInOZ^V2Ntqczr9H?lGnE`H?eJKzIiMCG zNX@Y#H7s_4YFlPTP$>;kqnIJMAJaYYU~@n%Qji*_B|KQlG0-LlNSX(!ac+yAjcE?3 zm(0uvT4M;>Q?Px)t<#ulKno$689}8h$Q-tGX)jDQpp!3|89`+{NKM6-b|VIGzaF`7 zl>&A%s7(h_W4Zn37ECpuu0Jy)$TUzMl%B)(3Dd4LusLTyhA=QNm~yx(_%K3HZ5pRDWu&CQe)VVhNbQX^_Z9$ zLA`m9nja6}uEew}2W%JUbV^X)RdM--+n8!V_pC58g3fsY_14&SSiHkj1M0{!Gm0@X zfDRgBSi^IFC#D+E%?-?qptICK_0EeN6BSG~plt%oj2+OnpQOo5223@e9SY2hpqvQG z%ky`%Z^Bdqs+E`-L1*oP{J!j&36?Yu8ZTjH1fAIfYT@<0G5m;W4yd=t%m_LO7F1uv z8AmYfjx0q@`=L#`1f?7%-qpH_mz>){+ z!FIt`Q!`EHHNrHf0jvg8(}DcHt^E&{G}Z`K13ISx)Yc5^IErOF0d!yvGa~~pS#morm6@u)lE7^~wzUTzo1=?o}YE724 zUfqo8o-VK&SbuQqJO?cO6i{!7nNc0u@fLbK6-yc312zY=Y8{kMJJx_k|3I!sF2j4l zYCvbVfMg^%CY;1{Pai}L$Wsgq45#D&W3j6rtOnFJ1SzY$Vuq#61nrwg^znBVZEM1` zYa-Yj&?pedoDIL4PhhH<1XcsH3%fa!!D?XUOq+8DOPUAmh9=}5(D@t8jGz$}kb5S1 zh+_$#X<)lR@dZ*7zGg9&v7PB)HL$v}XT_|on04h0uo~EyL-rXdK}2z~(@c217f4&@ap|oDEh3v+K)iiEd0apz&%~m@v%&&*%^^2i$@ppawk3 zL_p1E6!(~8_16}#8t^C(=q&ZNq(DqJgYN2OW`wB`{dqP4Qw?Z;6*JsD1{EGRFx7xZ zBnYShwVWa2#-Mh~mj_GIFwFt=77=z~_t!44zd)e`N*ktf^RU!$pq@K3BdC@K)lIwp zdc45QUwgpjfO>NvbI#7G|Awh%FIWwzy#`Xl_2U+nz5{3k05Wz8QnTLeYb>TY`@!bG z+GF*CJFw(;(0+8tm<%YNMzf}286!FfHV1UhAjn_Gd$-=gwCfO94Qz~&ueMYQGn@~j zsA93(njr;+PrLnr%b0!#jQ~LAK|t>TQw=DUGBbisU;vFvc*VZngQ?~< z*gc@0KFF>PU-M8*HE+ObKr{6qHA44by)5MZ*ITd}SQy@Y#m$as4(RR_M0@PblFeA= zm*0cUNoRzNncti-1;uT}J&x{J#&SWkz08cDbs3;^Z(DQ#%ls**HeqIj z)wypM+|-kKt;UaB9EaWK5&sgP!W5$2oJQ| zVG0*fgNiu8MeLv=c5sm}sE8w6BnK*D1s4I`=?C(cIb38jRE-5(aB@QG$!~Kt=rE zB8#CSx^NND9o-#jN1yv&m7nuYVVS|gThKl&YMGiqlbl@U4pdvzWk@rv$F}Mgjw1ch! z7mP^E6-j1L0I%a@V7LzzNr8)iRxE<-x&{~Ff(}=7 z!$m;nX@bZ%-H}Jxd>IW7B2Dy zDzXVKA^`0itb>c_Lq)d2MZBOQJK!S8P?2qLk!GmK7P!b_sK`vX$UdmZ47kWWsK{!# z$XBSy8n_5Qv>CDtE}{t)Sqc|%f{Hwbi^M`jp1?&ap(1bKBFmv7$KWD|p&}V@kw;LG zG`PqQs7N+kL>$_I$b^gNLPe_KBF<2eK)6Td!&4G(3LPci7MU0>#i{K(|P?3djkua#pD!52KRAePw zq!lVM6)v(EDl!EwvJWaU87^`UDl!Q!@*65L5iX(tYUVO9Fie1pI6+0G!9^mVA}in` zSx}LFxJUz3WC2`cDpX`HTx2;^WC>hkH&kRET;wuTWIkNvBUEHDT!a}qyqyjgQGkkM z!9{GLA{XHzF;J1iaFKeb$RW7MBB)3RTx2^`qzEo@7AjH#7kLF0DT9mrhl&)yMZ};( z&Z%$_ZKy~NT*MhFQV17`fQsVq!cQ07B12Y6*&VJSppU5gNtl|iadvl+=Plm!$sag zMXta_IH7}f32+e&s7NDR#2zXV3Kt23irj#Uq(Ma@;UX1Kkr!~08BmewaFKOTk!Ns` zBT$hhxX4|o$Wge+H>k)bxQHlp2r3pXVgwawgNyh=MVjFvNl=joaFH^o$bGm-CsgDo zTx32}tQP>~aG5l+zZ2nGg* zQ*aSAsK`yYh&@!~2V5iwD)JI8k`5Jl1sAD=ip0T1`k*3D;UdeSBA4MJ2cROC;37An zBK2^Q=TMObxX4eaND^E`0y_NG0~gVSiX_5CT%jUw;Udvc5e9fqAqPbyH#Il80JQW5 z)HDaH$xY2IP6aI>f{Up5_!)xc$v^_2bsB68Dn5Qjpf%qh5zzW@u!u2C1eD3aB1W(k zejv|4L_l+mAYGtUuV4|-BnwD@0kl>YECO0k4Hf~d!Uc;MgVv6Ngg~wUix|R82Ccn; zh?s#SK_ku#3=9wvr~m_KJtsuO0O~~0T5GU~kuk_v1_lPu>RGUeA2A}}T38=}D?@X!FQWCn}C3}yhW>jsO!-2)01un627 z(5e=&2wV+lr8ii_7^Voc@(?Uy3R@EiT897@F@yyxXq7Bf1gZ_RUK%W7WM&Q#0_{8i zi||xd*iF4C$0nNsMMT|gx2MK{f1tJ1+Ge`u~KnIH$g5*FVp!KC-5mQk3fJ8tIVz3A} zje$f!4N$O%At-HtL_llE!6GK0@BxW{cD{f`EMT^Snt)&txGz8z8(72)W)5hz30MRc zU<{x&)ugArg6m@y2XAr*)S%pg!_8!Q47U;uSxQAI%ALa+#|hyvfe z1Qsy@1uJN_5Y(gqr#i4*pt1)f0#*Zd4`_Y{Ap%kaTC2II}P^Fu+B?{sIvo5wLqeN*F*TA6x_+V4#@Rk2 z1mt7}(E2jC2*}9{pcOiB5pZe&ttmi=fMOXm3yKf{c?>j<1Q!7bF#KbHxEbt16OhrM zHJ;$P8<4+DLFo{*iWx2f@)G#I0FVe+ml4Ptpxq$|5s)`Pvz>4e6OcC0x(S2`NEc|0 zIb6ih(tv^C01K$!1lny2sw+Wi8W=vI*Kt>OW#E{2!` z3Ng@}Cqe||_fkfPE{GaXIE%6%iI{-gEX#^40@9VohAaYdA?Vm`xXC7<&;Z?32p2In zFlAtPz{9`*UeOAQL1U2LLl_|<2;1)=ijuS;T2UJmiKo&7Juw-E17ei8G3~DxI$e@Ur zFfcI6qlj2CFnmD~0Ts|^6_C|{8WZIzNFs&?7Df!xP#1#zWoT%~z_1?Kr^W^r3=F@J zU1$QTET;%EFo5z8IMo?~+G8_0ARH6q7;O zDV-PD9Ags(h9XoEQwD}+R1pgXh8d_LmJAHbQA9v#tQ^^PV^Hc{kF3VX!jyr*hM$1} zyoLdkevB;47#Kv5MGQeAIm{3>5WicPGca_aidZl(EJGEsWMBZD4*)V5q6<{o+(%Jk zX~@9v5>>>Afq{hu*)B_C1_pUl5fcUmQ)Ce%3u6X`2gq(V0%esm$Rgl;afJujWKgm7 z0YwB<_=0voA>3?i%)sy)MGeSgKV;htLFtf}7ujSpV+)276$S?IP8?9E8iN|*t5_f+ z;LtDzm35#ybUiY#IdD#Nu=L_oeUK^6hG zzHCuMK;^s}iU`Pde`FC*qGO0b5dqnriY#IXD*f_MMNAnO%27l>rA-5}h!Lpb`HLa~ zN@L2rNIo?*Gd5)CKn@>6Gh-u$5)=_oV+(W?20Z;3f}+rm56K)OP-Ap2Kaz-{nUN*9 zE&%C*#f1SwgBnB)RK&=f;SQRJ1;Y^(5l|U*S`eZO96Ls!TIMc_2&mcp9#sUiQ& zqN*`sP(TqewX|f=(L+^Zz#xMnVrps5P=F?4!H|F^V#=@vO~j0037Uv8!xuCW6NWcv zB8Ch$`pE7vwKQTdK^8GGGh$%Kmxri~{by$(g4Ww;rME5L6df4Fj@Wf53KuM%zHTetf;HkI?l1stc?J#jd|#T_6{NbUDjA z+9Gr@K)2(7)u7k~>R&N4g8J+rT}1~t#Spp_pt`_nQ0xL-vw`r}G|4Sn5V{;fGlgxDdwc*V^x`tUGJfa(IPL9vSwbUqY#&j`qcCz9@%B6M{@b%E8O*u@0a1q;K= z#}?HibZv&}0;@r>iy5p77KWE|BzGWm-Gk}^t3j~~)UQT_p-M-04?-6w=-3$0IZGgu zQS4#`iNO7}xL1e`q00oS3#YM51|Wu^a}$616U1;T|8i2pfMj%7_K&r%$0zL zp%Uo87zPFguo@J*K&PM}(!6Td)_R04XQ(c)8Wg+uz;=Pg$w4bf?RUicxx?*BhUx;V zL9vS;tP2)~d`E?rBXo5@b%E8O*d+kg1(F8E*OI6InGw3yfP@(s7{F>!>;jFkBK%df zJ>oh-*CnVfuo@J*gur&e%8f_*u5t)nKcKq6YEbMF2J3?PYx@UT0fa6&=vET28Wg)g zXGuYJqk#OiKjCf=LYE6v7g!C7U7{#sgs^KHR2Ntcid_<5U9fa`(efz28$4~?f$9RQL9t5`tP2)~Mmf_qB6NL+ z>H@1lu}cc93)wCgxLtyv$pi)l2Cy0wyFjPtA=1YE3*t5iUD{AxU^OUq$$;$wr43O2 zTHfgY)&y>s3se_a4T@c|U|leO&3Cy_htL%R)dg0AVwW6P7tCLWzb@3Zh1*pO)dg0A zVwXHv7tF4y?>ZJBbag^?fz_bc1-cU(k~Tp8@(X(q=K;5C9#j`t4T@cgD0Zbpar7f} zZGq|nt3k0#39JiN_I_ICxeB4{Bvcnz4T@c$GvpBQB`w9tjnMS~stc?J#V!@FU7$=0 z%3onK!zUtieTV7-t3k0#6|4&uhG9B(iU?hNprc^htQzrej0A8A4YuR2Ntcid~v0{+hD$ z)P01mET}H98Wg*%Mh#! z7KYs#TO<*>YM{EnYEbL~%@IOU6euqX9lpZu2@k`?P+ed(D0Ug6*wwB)dnrQKZm2G> z8Wg)sz`8(f0Fb|QtS5Xz=(+^e1y+M%7wEiFhzmiw63+2{M(BDC)dg0AVwV}%E?5|5 zpFAUj(8U6pA_m>I4N`+*mpMoT9$)w?ACm(vl-2wf3SU0^jRc3FXS!OEy}-)2Z6bX7uifz_bc1wJVb?k}E4 z7g!Ox=0J6U)u7mAgJRd$l>z$@y7ohLfz_bcWee5?^Hy1b#f zz-mzJas=yw`HR;xXd^;b9#j`t4T@b(U|k@8f%2Ex{<1gT@U$@zstc?J#V%*CE|^^@ z1%Hrqt$^wRt3k2L1*{8{4na93$hEu&Vb?yWF0dLDyIjG#V0PJjR%b)#x(3w+R)b=f z8(0^}Um&|WW~~lI=z0g$1y+M%7iiZcBEFvA`q6~Y#RfeF1FQzcF3=u8h%QhVx}N4- z?gI})IjAnM8Wg)c!7haP>sLXT0z#KPR2Ntcid|k{U9kLhyI1)dLRUOg7g!C7U7*tl zAua@khS!|UUl6()p}N3oQ0(#n+XV|lv002W5xN#bb%E8O*yRh>1@l+pD?@#RuA@+0 zU^OUq`GIx8+J~Sq2UB<$K8NZ8t3k2LAFK;zm+HOP9SB{V(Bo>rYEbM70P8}w%MNar z7E~8l4T@cXU|le~-oD86Lg?~>>H@1lu`39y3)wD5xLui0U0^jRb_IiV!R!LnUkF|8 zP+ed(D0YEnXCdtkP%O`BfBGJwYY9{rSPhC@p(u8p>|}IB=sFD51y+M%R~T3qENy`5 zFNCf~P+ed(D0YQ|b;0~~y!n$KLKo~v60jN+yFjOXqmD;e!NX7iw6uhQfdQ-r#jZ#c zyFlYn2wgrF+%YxLxH?U0^jRc144A!Tja&yVn|_Yd%yLSPhC@ zF<@OVyFh8)0dChZs4lP?6uV-zzc2HelH7Ir^ zqS*CzTIoB4t|X`~uo@J*lEAt^{sN_W#!ox`B6PJvb%E8O*ah0Jj3^KP&oAGJ(6tJx z3#!NX7zv{Z_LfdQ-r#V*h;X@tML9GWcH@1lu`3U*3zWZ@ z=PIyk!R>kv)dg0AVpl#~7bx{=rv~pv=wbzR=0GP|gVdndRR9tJpNb696%>1}0HI3? zstc?J#jZlIE||Zx3K~5Sx(uPZz-mzJ0-daes8cFS}uo@J*O2E2cY2(k?EK zSPhC@m0(>kyY2>`W<}_F3DpHwgJM?|SQp4&pmM{->#ChLJRLHCmX0zoFo4yd*i{YI z1+(iWzpXk#mpD`xSPhC@HDFz^{3W<0zFrA#mo8KnSPhC@wP0N^yFhI=gf2IzF0dLD zyXwHYU}e-E||Z>m$WJ(bRC1*1y+M%S2I`_%wL){ zoZk?-9zb=0)u7nb0@ejfhi!7d84yvh z4T@czU|le~u62vlAndAv>H@1lv8xNL3zp^$>pRT-;AwsuR2Ntcie24cT`;>q?RkW* ztx#QHH7IuVfOWy_0@Yu-aJw!;b%E8O*wqWx1+xn@myOW%5vmKU2F0#EurAQp7bu;& z_a8ni47ZC9v>kzgfdQ-r#jbv^E|^_1WwLAtUAj{{b?LJpxT8>$Pe2E{JWSyG6(0MHzV2HdVLs4lP?6uTy)*wy*0lL?_~9aI-s z4T@b;z`8)`5EOvVyG^#8Wg)`fpvkx5L7RJJ(+d^p{pCJ3#K9I#!mbO@@y_~Bu=8>$Pe2E{JWO#ukI-X5B(iO_Wmstc?J#jbf^yI^7X z#XUd+q3Z`!7g!C7UGu@ZU}5;2b%iBDmxL}T<{22kYEbN20M-QyLq?5L6A-#Apt`_n zQ0!U=)&;XmIOk3gLRSP-7g!C7U5miFU||UA!z#eTumY+JtOmud#b8}9yJ~~awjy** zhUx;VL9uHISQp4&AfJQsmjK+ZwNPDPH7Is11?z&@byaVUvBLAYHnpt`_nQ0!U`)&;Zcik)cyLKg>U>mCCG16U1;T`R!4U}@uu=yPU-E_J9b zuo@J*R)TfG>@wON){M~Q0o4UogJRbzur83lK>5q!O43V&u1u&duo@J*R)clH?7Glu zUyRVz1=R&sgJRbjur65I0L`2G!ozSiR2Ntcid}2Lx?px4`>Ym<&~*x`3#p}N3oQ0&?Yb|K7P%ddUAhS0SSstc?J#jb5& zU9d2`S0n!kq3aG*7g!C7U7(w`5dQk!lFx$B^#`g8tOmud9bmg)Vd!nMFdv~y2DHtF zfq?<62F0$OU|leO^<4Z{hR|gL)dg0AV%ILPE?7DQwb{hrVHg9|1y+M%7wCjigug)R zh!DDJpt`_nQ0&?RwhI)7puXP!HzyiY;daf2>H@1lv1>0_7tCLvwkSf^9;hy`8Wg+s zfpvk}hoEvpcJA+XgswYKU0^jRcI^l2g4tEBp=XQG^$)5GtOmud17KY+ySl}<@*#97 zfHtHtFff4CpxAW~tP5t>9+QHl2whH4U0^jRb{zuig4xC8Hgf_(R|-@YSPhC@hrzmF zb_GoLvq0!-gX#jSL9y!ySQjiE2HaDZLFifm)dg0AV%JfyF1TGGKg|%jPC<2n)u7mQ z46F-g*X%=;EeKt2pt`_nQ0zJm)&;Z6;dWm!LKh!sTLJ?E16U1;U7)jAA#H3>+5pW3 zAasFt6M#w+uo@J*z_%R2=X?u!Ho72m1wrist3k2r6xfBZ`~~WNsln5H2~-zY4T@c- z!Mb4n+M4>a9HDC#R2Ntcid|>Gx{&QshugIustc?J#jdkpT`;>+R7A89x}HFFfz_bc zbq=fxl$SxFwK0|NtC4T@c$(@+uRhK$nJg9u#;P+ed(D0W={+XZqV z$S&2hk*XYUyUd}wz-mzJx(L?=ie)3Wq)P~0VNhLQH7Isn0_y?|SAoi?{_fQ~x#4z| zKy`uDpxAX8tPA8qkXK6|gQ?d<80fc*g^`Ya>(_SPhC@SHZeq zc4f_%(m?3C3e^QxgJRb;ur6e~c;R;afa(IPL9y#PSQpH$w|g3U5xS&78+aHP7{F>! z?79Kh1&c2kxLvkTU0^jRcHM;Q0)^ofk%PtvT`5pqU^OUqfo{7+%(Zd8=av(N+tmft z1y+M%*KHKLKx-Wlx)woofz_bc1-?fHJWm92p^>}GBZRJems$|ISU{Vy7#JA9YEbL~-2;mV zLr}Xn9Ug|VP+ed(D0V$Wu`4X8>?cB(6I2&i4T@clz`9`R5ENe_aJ$l=y1;5s?0O8= z1xtq)vnA~jx|*T7z-mzJdIHu3Dx*O0b&uU^OUqJqPQ8+4Y?(ClI0QCsY?$4T@ba;JQHht3i(S zT@gGC#X;MZ85kJAYEbNY3D*S*4N#g#=mPE71(^j_gJRb!ur62_riFLyN9YQI+67jF zV%KZ1E||Z9)q0H)x(cDXz-mzJdIQ!43qw$vN9dXW)dg0AV%J-+E|^^_KJmLFbghT# z0;@r>>m67Z$S#n-r$;Ipl)%IA98?!r4T@dw!Mb2}X&dgYLFoDn)dg0AV%G<-E?Ao_ zuWQPDgf4N=rbh+_2Cy0wyFP+-!R)$Y)F^|{Wee2>R)b>KC$KJ7NIKzstc?J#jam)T_C$aZJPqPT@6rO zU^OUq{f6rTg?#?JdkqL(^P#%HYEbO@1J?z!D-~|nUZ^gx8Wg)gH<2OQY!S;-;t;xS zL3M%EpxE^f#V#LNmH#<#yS_nnfz_bc^&hSaw=Ys*90DKM(8Ss>H@1l zv5Oh33zjyPZxh&r&@}_93#w?*pd-2p! zgsxjqU0^jRcCmqV!R%VRT0j$_>o-&vSPhC@>|k9myH38f+J(?113ILNfq?<62E{H8 zxGq>aEQF^GTc|Fu8Wg)Y;krPfQT*a?8$wqUR2Ntcid|e_T_Asf?E2z=b^{;Wt}>`D zuo@J*xWT$${sOION9dXk)dg0AViyls7p&|(y5yP}Lf3YvF0dLDyLiF6V0N*DuRMg% zbrY%!tOms{KCmvBUGgo#j}W?kLv?}GpxDI^)&;Zc-tClC2wk$Ei_RV=(30E0;@r>OAxFJW>@@&7y1ZYiBMf&H7Irofpvl63lz&Q_Wr9z=xT=Q z0;@r>OBk#RX4eI2U2BA{rBGd9H7It8fOWy_0__Vx=sFJ71y+M%mnc{l%&tj49=aiP zy@cult3k0#46X~54(oDWN0!6WAvfq4HUCR2Ntcie2EFCg9^6LVmMKxm+tR~G=#1ms4lP? z6uYFsx?o|bQq}nep=&Kv7g!C7T{2)@u=x77y*Ey&zuo@J*WWl;%cJU>;&PM2Z z57h-$gJPE)SQpH$H~aS2A#@3V4iRKvU;wK@u}dDT3uc$?VnYjrE<>m;uo@J*6yUl* zX~XBGplBXEZ3IDefz_bcr3lvrN*nX)wq+o6)kAfG)u7m=1l9#wrw7ut&Tw-pLf2}j zF0dLDyOhDYVE!uUZ?8w_x(U?JX+S29!=SPhC@>R??kyB=peK?;qDP+ed(D0XRp zb;0Z^n)2`>!mj;LU0^jRc4>lj!R$I_FM|{sZ=t%tYEbObg6jgM4N&_qAD%X(?LlG; z3}7`Vc4@z4Px?K%k61y+M%mp+PJeD9wfLFjr4)dg0AVwV9} z7i`P{H0OH&ZWjmWm`nx+2Cy0wy9~j)VEzKF??vd+gX#jSL9xpStP5rrXe{Le+^%4# zF0dLDyNtoQV0KM7x+5E*D;uf{tOms{6R<8=e2GaN4?GCBs|l(LtOms{Q?M?WU2eBm zUO?zt57h-$gJPE%SQjYGgVM&Ij<<7j;db4I>H@1lvCAB+3uc!>cj0w}E&H@1lvCA5)3uf0WHtzt0u4hnPU^OUq*?@I{;tP~3Th_G4A#}-rE`wrVU;wK@ zvC9^$3uc#3*wk`_t{|u`uo@J*?7+G}@ddK0?2XJzgsu*#F0dLDyFlj&BKkz;9nl;J zUHhQAz-mzJazL@`OrlyILf03lF0dLDyBxu~VE(dObTt;COABH@1lvC9Rl3szTd?eTq$(6te& z3#jL=;ln#A_ma7)S(_tu77g!C7 zUH)KQFuOqG*a%&9P+ed(D0T&ab%8c1f#U0PLPID**D|Osuo@J*0>Qdqc5&W(@(Q8r z0#p}R4T@bsU|k@8f$Rc}V;)w17gf11(LEQ`t3}7`Vc7=d- zA=j0=;bG_p)dg0AVi)LyWkeYT+Jk`5l?l}aR)b(6t+?3#7+!~m;b*8Wuo@J*qQSagc7gW8B6RVBj#_76U;wK@u`33w3zomQa+_KZ zy40Y$z-mzJ0-b-2h_49`+3zECIYM=T)u7lFhho=4hv;O4t{A8;uo@J*;=#IL{(8E0 zeHlVmIaC)|4T@a}U|q2I>X@}U6rpPpR2Ntcid~6dT`;>gzL3^M=voWa1y+M%R}x$o zsN4Xx=a0bS>jYF6SPhC@$#7kuTzSGnBMhPIAygMw4T@bUU|q1Z0b1J=1h?xaR2Ntc zid~@7$r16@d8RfIp-U2UxH0IaTad{pcBO$t7?JG?hTCNU)dg0AVplp?7c2}xs{pJE7KS@n7Dym;`9gJp)u7l_2-XF&>yJv)7lf`H@1lv8xQlu5h`KbqHN|p}N3oQ0yuP>w@`9lzTxWLf2QQF0dLD zyDGrCK>ZX@+Bhe%(6kqxHh4it%`-4CfYqSbRSDJwv+Kq?I?tH@1lv8x%Z3uYJhJEa_iE(fSCuo@J*TEMzs@s(xoIqeGEu4t$(uo@J* zK(}Hb;tN!#AavD3b%E8O*wqHM3zX(TwZoZRin<70i=eu|YEbNI2kV0Q>v|hQ0z%hu zs4lP?6uUaWxH_Nm z^&Pw@`9 zN=UW^p=&Kv7g!C7T{FPCVE$V7*LNX8*LkQeuo@J*W`cFW?9xAz@DicxBUBey4T@c} zz`8)`5LC{yd|9Z|4395i(4{&I3=Cj3D0a;T>w?+!D3dWBq00cO3#`ptJ!h zH#8e|-a+W{f$9RQL9uHtSQpH$St`$x5W2FUy1;5s?3xGG1>2W>H`7hyC){5hP+ed( zD0a;U>w?+E`Q+0Rgsx>!U0^jRb}azwf`y?#Na~#1aJvpeb%E8O*tHO>3uYJS>{*1a zhfrN$H7Is10_%c>;g0186cD=pL3M%EpxCt-tP5sW_uG`Y2wk%NpqOW10INZ{{^B^Q>Jj?l#px*CarfdQ-r z#jZ7AU9hylG(qVmLYD$m7g!C7U2DO*V0Q7_e*2 z2&xOL2F0%RU|le~s_$8zM(E0h>H@1lv1}&Rz-mzJ+6dMKv#V!A zUMfP@T&OOv8Wg)Wfpx*!8!|S_?|g)(jcrg}U^OUqZ3gRt*`;Zn{RN@x0#p}R4T@b` zz`8*31xknikDbkng4^{Hstc?J#jdSjT`;>C>SUH6bg=|N@-kQrie1~lx?t&0%xtSH zLYEv=7g!C7UE9IBV0Oj+X zR2Ntcie3A`x?pyBy?k7S(8U`BNgH4_D0UqH>w@`fw|HPXLYF#J7g!C7T?fIsV0Njh zUvWq1a)jyvt3k2r5Lg#>e?>!efz_bcbr`G*X4i~pV_Ae>kL>I%&x_a zPrMMidZ4<%YEbMt3)Tg*YoXVftq5Ifpt`_nQ0zJf*98i%(8A1}kKk$J3{)3b4T@c$ z+vyN}2Ucx^(+FJ;p}N3oQ0%$@whK1q09sSg1-I)fR2Ntcid`4Mx?uhS?V&^Hk_rL2 znt=hV2F0#RU|pav1gQXx+nj*gWe3#-R)b>KWw0)oUFPn~st~%Op}N3oQ0%$_)&=v| z&V$>FPQmRehw1{WL9y#9SQpH$Fps8L2wiiay1;5s?79Zl1 z>pEB$%r5bO!wLvp&!D=%YEbOD0oDZyLs0(OwdkrcLKjacC|nsBz-mzJx(U_=vy0;o z_jZIXBd9L08Wg*3fpx*!05;nip8SA^VFXkcSPhC@x52t#c7e`LN9Zbs>H@1lvFi?4 z7wGT{kiW#GZ)G5K^+I)l)u7mQ7px0r*RroYo(Nqlp}N3oQ0%$~)&&bg&>7uqzO~osC82*Lo0;@r>>mgVd%r3r( zzY-9-#KS-)F))DDpxE^YtP9ky1cl**H&?bHbQwT(fz_bc^%$%RW>TP? z8Wg*pfOUcV1+wc=_1c>VU1?BVU^OUqJq7E6+4W25W-dZkBUBey4T@dQz`9_0+4v=E z(^GgD&VlLzt3k2rIan9WF3{d~gs#0%U0^jRcD(@Wg4JKFErwqay6!@Cfz_bc^%ATL zW>@p}c{dTd{zG+v)u7n*3akrOZiuP&$RKnnhC^Z*tOmud*I->RyVgnu`y+HYLv?}G zpx6bvHyW|WCqMRN0zy|RR2Ntcid}C}?7G9OuZGao4%G!#gJRb^ur65I0PO)s=voQY z1y+M%*L$!on7<;I*!)K5It|qYR)b>K2e2+s83hWjWuk8)F2mEtTc|Fu8Wg)ef_1^{ zG8gvSi_pav0Wyh!0jvhau1{cHAiF?m{&DTe3WP2_s4lP?6uUlyb;0aPT3j20&=mmH z1y+M%*B7uZSQy^^DanG+RRq-qR)b>KSFkRaUAeDns@>ktMhe(93?@(P}H7Iud z1nYv?HQ_@+0YaB#B*@ha3}7`VcKrhDf`wt;oCn1SUDi-tU^OUq{RZoT+0{O6K^sC> zEL0a*4T@cVz`9^%l(I|osd{)A)u|7 z9j^2C(nIK41=R&sgJRczur8Qg{*SYNAaoss>H@1lv5SEjw37>Fm(nsReT1$XP+ed( zD0VS|b;0b~uKVE%Lf3n!F0dLDyO_YbVDYt>)5Z~@iw$&9Edv7sSPhC@%wSzGyB@#d zI)Tt73)KZygJKs8SQpGL(0S$vT_#XnU^OUqv4VBM>?*m`WQ@?|4b=r!gJKsOSQjj9 zfX=CCfTxWls4lP?6ua2Lx?pxWUfBBqp{oX}3#Qgivdh=MdrKnRu5(abU^OUqaf5Zi>^gV& zRTo0nJE$(O8jzt3ywMB{FL*(BcSA4(gL*Urg8(0d2TK1840h2B3<<)>A`#IH3=4FT zMe;#v^pQoTfkcXtMYe%Nlo>(E8H^bi?tw&HkwyN4L^dLesK+ocBwR)o@rYqycwo&0 zHUr|q5|BtNvdB!3t~JOa`#~b-kVWo+M0A;v%=rrv@kbUBjb&hXZ~|Gx5hU^oStJc4 z632pMPB%!T0a;`vNMskX$VHII9b}PDAQ2u`By+^#7#JSNupx;U$1yM@q_882gn~p$ zkwpqXBHNKgW`aa+B8%(>iJap=GUo|MvyL9N`$T#P+cH3ObmL-8QH~Vkb^4tz`6=R6)Mzq z5Q;y6fgwAoq&Pm_zo0nYB{eOvG^YeA;+C3NQd*Q6T#}fciZ170;FDQgf~Emvghygt zdT9<;6Hug}2B3)*$2%4!CS@iECF5`fhBVY1EaEV;{PGEyhN1xE2hY5e%w()yK$d~p zj4D(d@12pGT7*?IvJ6x+s!(yfZ)IX$ZX$N=sB%yP&;^U*Lo$<6OA_<2x&T!UY6QAq zaeQcTDpu_Xai~^=fOBqQfm?o2ZemG5QEGB(N@`v*)QsTFs#O29;EcqAROkG>lGF-l z@(RdFOim?2*e$anHN`hIH@~P7;wa~$)Wi}jI$ctWON#O5n7yDD}K`X=U; zCguc{<`p9u;g?!b0+&D%cg;(I3nQzC2s#y|CT1h4b}lU{O3f>Qd$ZWHB(*591jP+- zNtm2xaadwbCUQVvC;_<+$r|_660mpR>X77IGK*4^k;5D!fRWljiNHC(6c#-oE_xDj zD@siTi9<{Wu~3}qlb@IZ5`;*Dlf6?;eiDT1o>~Hu^UO=jhX|l%m$b}uh%i_i)H8W- zQPive7e>noaB|qQrD)HpMQ75ged|4b}{CmUB*OVv%EV zNoES>MPcb2$w*lALo`5w2W*0KJ|ZB%!Gj?R z2_6h#Xz*Z&g9TvLA(9e`FiOn2m!=jMhon}N1Qg{Lq!yKArb0c7MFN~jA%4Iv3(ctD ztOCw|5XE4=V@e7<{E&pXMqApOP9{k_NTHH4l{^oLrPyPy)$~nfb6n6)fmk?3|xl zkds;g@hU`RNMd?OK7Et8+q&Q?cuT#FcABNorm(Jc5y|^vwj-Mrg?iB##pKpn?Dq0Vq;f zw88=o%|$R_SYkjJi13(8YEDTaQn(^ykgA1vG*97?@=r@EPK8!H@!%4@2waL5`z97; zJEoK+<|U^Vd*%h?7nisfW)-MWjwvaq0-<>YiOJbvi8-aIL8;04MKG5@ z#Dh}HQj3aHoih?M^MXs0ic2y}N902kL>hd zc&vk*f$1mr)Dm#=#Hj~E7GV#TkV3Hzi#&#A4CAqxfT0M!N)YG#++0{a3NHsB)dtw#i`H;b;|^$cBp+IlfWvV zd@viOMHvqkf*9$WSW=W(;hC2LYwsXS!rX{MQ8BvvVCtbZ!-Ei1{vt|P>;V8%3`$TC zr{ORSl-jT>$jMIv$An8_Ng~tLEdmb2k`+ zPlk4(-h_kVd4261a@=$xKTv$;^eh$TcqoO~g094B{H7At0wB z8wzH_JpxsPa5++!5v&rfpg6Ut1RUv@B2KAk`9&}ndFH`YI;NE%#?p#XA$?4!_TW^A zX#qv4#i>PQsXm!`*~N}|DagYm@n9cAlLIX3eG^NPGr(m*VtOhp>q8B7DoV^t&d5nE z2KO`)LD2$kPC=zz@?q*x(vD+6K~5!RAqPrh$R38&L&ze*uvUbBUTQ#Mafwf2F~n0) z`#?>C(xT$bvQ*>(C^)sm8B^3HKR7clJtx&MC9AZ!BsUc{Vi6A+Vudtyb5fD2Jcv+c zGB^{0`c`?x`FV+Oec-Tz_!H#8%;ZF*1{x^YfLm6WQZD&e8g!tP2=+Nrdnq8l09`0N zzceQWt{s*lKmiGH2y$r%>HmWgVmYiW3X5WwoSe+!5@;JUxHJjGg9fQA?|_-2c;&aAnXDSf`asb zI>%52klYUr2k3YVA}hfP0cZmDFH0>d%1lZ1EOxCZDT0-wjwvZ%E}9~!@t%1FrHFAh zDq)7=VTZ%FrgHe24Vxq4t5pIv}wqF&)(Tg$f3z z=AKRaL0aD??vM*Ezk_4#d?UI@Xm2k-icevp^co!Th0+tO1mHNRYiA5#o zE^*5#EzW>A7*WH3^#IU}(sF}WnQNX5v=+?YY7G_O1}FGU4BO@ormz=P>3 zpukVWDVq$c;Z+Q9%0qPHmWSv!B%mL+JYL5m)fEhQ{ehwo;v`&7Mp262cPL6BrsDDl zic*NFL@7n~K4dr&nlm6XA;_M_CJk{E)T`JOK%9a{0mKBTTXCC!M**@IphKrP{eV>- z*#NLkSP=+nphM?kkOKmz8i-$vAZZb&DoBtJQUx)~m?*Odse+hgLX=sAR6)!#CCV&9 zs&EE8N@ha|NYv5>A_biZ!;B)B1cp7}Vja^Gs4#|d(AY4B>0m(&rJ!jx45eT}NWejJ z0@&eTVMuDg5QeBmj#wns7{U9UPY0_&< z1dT&L8^RbyU{!?aW~|B}p^5AVtjZv!nIR=&tjZv!5mbiZO~~8@hR2}75X+$j8aN?9 zB_P&ek$~txE-_GaV3EM?Bz&O`HW66?4oi`hKpc!=J+cyrDaa9mVG+a>yho`oos{~unT40tX;0YuZh?U5>8zK(zGp0C1Jz5fgn~o`t)iKE753vZ` zG=n8IYP7@ z0cg?~+98&MTho})iy{dL0OZ(2k%ojIPHBj4cjevvIFozz6|6N->Rv&4fdS ztMZFLy)DdeftRDG$}o%rjl6?;n{f4*MqyEcVGN}2gQ*uLjza^M)-ocCK=h(0fkY8< z3}R7)8O>M}K}@`q-9YAr|A1gy@923cF4mk|<7v zEk;33ji4bskcp_RJNUvBWTgl}h z1T1>6$w2f#BL#~dY%&l%(5S(p2b&B;4>W?X=)oof(F1KcV9|q3205TX1K5ZKgrLPt z$ia<87Fjbm^1wASbXX2qFQz22R@BZiy1iIrku{@rgwb_llSbB#7z0Ee8AK_EaVa5e z3`)@pNr&Ll8M}!ns*w!`bw$9gLva?8C?w*cNgPQWVkM?HL_M^O!%&YYj_er7)CclH zG~|fJA`7t$>If|ID2~J;577^G6c%}ie%$iNjt3Rbh~;v~4o8zg)&#HFp-bmbVgXYU zpH|NlyqZDM5bwf*3)WVFS`AVF@h=_)5EEblg3|;%3Xr`3$@bvnhb3cUkww-F&iA4)q$QG>XqLr6IbZ-oliI=*B6H>}*JpgUj7mWFb~V9gamFVmEGih<>QsaO%e` zkL-A8iHFPe*rbuQV_7qal4h~WqqrZd3P=b*eSlR3#0*&diDkhjG`r$c0WkwsH{vz} zp9+W>u(}er8TeE{%z!qSu=*Af9{5y1%z!qd@R)&51%ZeKj~Wn)U8o|$M!|dc;5rVA zi$R@f)P^y*PYUk;qPPNC7};L1ILd?&G);hQ#3GBK8F^tYrj@9&cr`;q1rm)2>yb@> zDj{SHYOsMlfo6&YYM_Do7qDqL&{AP|6@=m;JgSgg1X|AtT_lVgmdL`8ga|FXkR>3V z$07mI0nO}~IleK^&i7=*T#3&j9*If$E~o{2}7Me!TJ-`9?;Sx=t4M@ z@P=7{t_Hg^&{d&^A%3%<6$CuIu$x6l6~tZ8t{DNd2&saY1+8=lm_41B1uC+4%!iiha8duNW#FQ0Ad1qY-5;! zM*+kH^f<;a0gnQR3FtA4VFDfnSc4I|0u*Z)!sL)O!m&>+TR20j%KGoXQt#|(TbAZ9>=8IKwGR6xvtHvaIKflmc;q`(t3XlV_&#>1W$ zk)$C`fCe~j{dg2WOn?SAZWHh*fS3RcY}_W`Q2;Rk8q~N=z@q?bFydIyjGWHlmf=zh zaT| zim)05TT6<*>Kdz&I2EH94enl}ul+{Nxj2+y8iQpSJBrJ|24Pc#ViaP{JBnr`VHD+H zLvgQOM=^ttS`>p}Wg=SAAS&z$>479d^d>DKU62e)sxF9K1hO>_Uy-T{Vi$p|h{G;Y zbwTVRkTr4GMXD}{UC^o!SFwx3E>d-2O@yGfKcoQ+U*U?9vSEo8n>vW&(W4TZN=O1D zMkT~d^zt0HnZ&4sn2DYYaGObtN{E^0(T&?oVpKxRM2}Y7W)hDYBu;{`n143Nh3@emv+bwGhDi%vM8Ft^8(O<0j*d=lS0vkeb*U^#W<8eA`zAta43SL zFanAoCc%;kUXutYLh%~Lt~eB5ValRt29v02QA|n;B8uj$H+cA&9+=C_0gZAufhBAdn;=KE)yd(E)4p zV(7pk0nq`SLypJLfkgtM13F`jsRN4yino!saH4n|MFK?&xTJ<{=|s_kDg$vDELc$G zP=gFr4x$ehIH+p_w)^M)Ni^r6P^e>=i8#{szqDDCz8pqLl(6b>S2LqxU7YNY+ zKk5*6I0jS$XbBqpFv<9U#Pn3~0e|r0kgzI4Izb64k9z7PZgbGoVHyOF0Px5xZiCR& zVH$*ak|l1#u&Bi}5Oz`x=z?OoJe47JslJslhY=cFHGiH^5b3YDUW*xXnOUh-nnsp`o~qLRW|| z3MD0m6eZ>rgAa&>oD>7jnXm+gE(tw^9hWrtkat{?p~b1_#z9Y#fs{kV4Ds=vxryni mpmkX&N60`9kBN`3$jx;yFfcGkO9PSEX$F`we2NSV7`OnRk1W~% literal 714902 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

m41|_hdGML09KqrJVFfgcVU`h>F z5lt-y25lW(JqCRSgTd`bL$LFVz$Ak)gNZ4G1trbErxQCN5c9;J~{!3+%13=9l0 z43NXvWdFRwA z5CtJYtUGt^FoFPx4b%JK*_}Igo;`bZ=MDpyymRN-i#vCoy?pfwgkY+`G!{4G633#H z4niP*JOdH;Km^n~Am812_Uzu1XJY&Ug5pBLBBFeJ5TooCPPpc{f~xe&TK%J^XGpFLv$C5LBsz%&Ck*I=u|<>e`)CASOZl;>nXcx1PPcL$z!M za}L9^XN-&>>lr~5NEGB*M#g7%o-u-h1;hmj4-f`L&j4AC)mkF6E>^un34-DRA12BW zu%cT}Ufg@|0L-KZ!NEZfTj*^EIQb*ugOK&qDkTv1U^kJtWJsJUgkAJv;d2)vtYIv& zT}AAO5@Y&LuRk8QLDAfyr0kxiF5(&BSjAL+hxpv|j1lZJl2bIMrF0OXu$c)8 zEs9+UGK8$M3T`vB%!BhOXAoPX5VQ%@4FydCkz*i8A3+NSxdP&|0h&rQE(zY`M5%^J z(1@y(j1CE<9cbz}9O?~308-UhsOdN;Duq1`(`h5jjtD6#S-_4F;yA4U7k=RK3(TUD z12B!@q%>_}8c9$DGz=s+_fEv)mIG~m@Fff1upFsG~LV7^FJwSfN6Mm4~ zhAZ*o(MUfjS_B#vGqDI!=tt_CNTGvpX{X6l71@(ZHceouystkBGf>cdN z>cQ;?a7zM7a0oHk*hq0Kc-Dp#4TDqdjT?{&s7E(I0Om&Mn9?24YB$iDH1L1|OcIj@ z#XP3yz>AR4gF>0VuP)5+vj|>b9EDWp+Y~Ymw9N=yR7Xt$W zHv!Ir^}!JfeZhgpsc3=B>T z3=GZ;E)1>=ZVc`W9ynCePuPbokSUg*97>vlnjW2 zDM1H8hJkg3Gej^%GDLyd2m*4rBZMEz5XZp406O6@0V2SV$iTpm1Z6|0WH6NiCXtC$ zFeeR6!iaQ+42DdGECvP!$l}=?1_p*)m?W6aW5@^73=9Qex{#rWp%~0!U?^c=U?>H% z%fKXrC}*f(s01^s7^)c<7;3<528LROIw*@63bMSl0jji-fq|h3%7(071#wzH1QKp# zXan=w!6by}VCZD%VqjqCX6Rw)W$1$lGW0V{V3-JIPhyzNz`!ttVJZUy!!(BJV6hnt zGZ|(v%x0LwFqdH-!+eGX3=0_+F)U_S!myNK8N+gh6$~rE>Q*tVW>~|pmSG*kdWH=Q z8yOfFHZg2wU|`t7u$5sO!*+%p3_BTiF)%RfX4nH(&%m&kVIP#WpMinl0K-9sLkx!* zj)29EG8|(#&TxX^B*Q6&(_oP^3}+e6F`Q>$V7S1*z;F>Pc8TFKn7+cmz;Kn}8pCyl z8w?B#HyLg*FfiO^fGnuH%W#i@f#E&_0|RKe$U_DOhDQtx438O}Fg#^=#_*is1;a~* zR}8Ni-Y~pncn3D^J;MhE28NFepBO$fFfe>!_{#8&;XA_*hMx?-7=APS0jv4T@Q>j? zh{edr$i&FZ$im3V$i~Rd$ic|T$i>Lb$iTn@ZLae&@-gx=GB5}*3Ni{YGB5};iZF^Y ziZO~aN-#d~(TLHQ(S*^I(TvfY(Sp&E(Tb6Q!J5&A(U#GUk%7UU z(SgyC(TS0P!I{y8k%7S#tk;dvoza8QlhKROo6(2Sm(dR_%D~{y7{D0F$iNW9$iNWH z7{VCJ7{(aR7{M6H7{$oI5Y5QI5W^VD7{?gT$iR@mn8=vKn9P{M$iR@w$iR@sn9i8N zmw8z);Ru!N|Z+$ymi$ z%~-=&%UH))&)C4oz|hFZz|h3l%-F)%%E-Xb2G`dPW_2(!Fmy6@F?KWdF!nO`G4?Y~ zV4TRvz%Yq%GUF7+sf^PYr-RjjYy#mKj0_Aj8D}xhW@KQP!#J0bfngrwe8vTg3&E-u zF)jwvAeBoPmohE`^OiHNU|h+_z_5yOHRBq_wT$Z+85q_xZeZNV$iT3PaWhyANXHh& ztzh;xFu9#^2jfo0U5vXK_b~2d+{ehku%Gb&<3UCShC_^p8ILeBFdSt(#>l{Mobd!$ z{Yl1CjHekH7|t-BWjqHK0htNI=NT_BUSzz)c$x7EBLl-#B>8J#_I1V^j0_An!R%X% z3=Fp!?=ap4^I#_21M}`PGB7+~e8~6+%wu49%*ep-gz+gO1H&`M=Zr7FqAwX=F}`Me z17^QvWMFs)X1!-*VEDku!0-{w1DX1X@iUwa;v?`E#;;JBZ;am=e=z=J{Kfd2@ed;d z!(YaKP?`UX3{0T=Pnnru`dFA47+9e^HYRo^4k()wN;5EUfmz&O5~hm>&f|r$_?Q?N z_?Z|O1egTDB0@~UOd?F8OkzypOcG3zObiTCU~!lUqx5J9jE2By2#kinPzV9(AyRhB z3=wyN;#`(V4(eKYC=C+DjzQxR3QPlM6~S4UCKN;EN}x1CMJa{| zLJk|NjA-@cM5wGlQd5b<#%41%u_}Uks=+F1z$7*ks4Z5@RENc`dMsk(+T4J{tVSH- z#M#pX)!od*z|aC^p*m2ZR62zBikA_zH3SRFJnmw};^iGiUD&Gc?4zXwXgT;7Yu zhl!)peMG85*Eh-^4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c z4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R z7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7 zfzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c1*0J_8UmvsFd71*Aut*OqaiRF z0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71* zAut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@? z8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*O zqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs zFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF z0;3@?8UlkU1QM7T8OoU%8A_QM8OoR$;ZBQS5MZ!mVrH1f$jVU1$i`sKAjnX}%*epS z$j+d~$j;Egz|SDW$jA`F$i@)G$i`sL#LRGzfs>(vL4cuyi3x1ZZw3wq2}X7XYesek zVFqD_2~123EsSgolbDzoPJz@ivVqm16D7=y3?2*u48}~%3{x0c8HyO$7}OX9848&h z8JHQ_86GomFidCQW2j(aVpz+_%HYJv#$dz5%&>!jlOcmafFX<#V*h0Z4hBI+c81ps z91IK$!VEo3ObjNB>B(`!o;eFsmozzWB_3pA4az`F*1NKjE_#|GBYwDV{~~+ z_<78X3@aE}!I&I#7BjLkfG{q-zDVf@ml|TlConQHfH1M@DOG3B%*X)3smzQFAdGGv z$Soj@E@MdIW&}C#t*3NJXTE@*}WCe@MqsfB>85kHYLDi*0&4h`2 zpw(4=Nclru^l1Qv)P<2gc z<>qZP^U@gE7&w`j8F-kO84{Tp8IqV885+>^^h4`mB;zS!_Cv#)kRh`fAoV{;oij8X z<(QZmN|=}!7BVt29D@3BCYpIsP_-a^FuV(@7RI+`fYf2={5T}>0;F*8fSRer#LQ5{ z#KbV2k&)pP)J#vP9L%f;G(JfD8dNQa4Z>PT_8{~9(8NJ<=$P2>L)S+sA5*mUSe+F$ui#Qgia0-sZEsS{CRVKzG@rs~V)c=tjv9Ft*^FIiWgRJQ zp@!OEW<~}WMmH}I8lK4F{>+RFAdD;qV^2Td<}eUGdbrk|EHy8YC)YZ8^~RBF2q zpLwLjKR$Di<rMTBf6mNLo2 zAUs(70dfln<8se!21W)D#wAZH@jNCb1`wu|`RYuJ3?NJ`^YfS(89A!vM%{m3|+fIPBV zO4!7dr|NjxEyU=j#C^2ZOUV6fXnqIv6G50%cM-CORP#tx?*>gzu29+?N|S01x$3*1 zeMA`TLV^9*!V*~wnN6;H@#%w^52Nv^C08yD8eZ7+!|X+_r(t5q>S6N?$YPYRk?jE0 zZ6Hjlc_24~Fiana4Z<)!sBHnlVa$vSAPf_S(IB-TjEfHw$3}-TGctfMHaTL&VEG7L zJt)3F7$lBwJj9QgkpUTl)DKqNgC`B5yV;hRkpYC!#c9pQR*%BcDXnObpKzHCl0(PH zbDpHK(aoYKALd6&?YzUl$bgKAHIG{M6Kfwfb-irVpRdAZ4@j&BYF8hW28j&@+)X3* zbkWFMfIN=S4&A@j(QFDB6qMI@?6cM~fO*2SUQL5Oca zMj9xj(BFjIL~Hp1GTfg}h8|k`1E2YMRCI4H70truW>VyG=-{SoI@m$T&sl@O4TRiC ztbFDm^h*Yv-IR`IUkVzZ*sw%bmqsOX(9I>5pE|(yCl9dw#D)_gbxCOcNJQh~(w{)2 zI$ZkcEgnBe+#5${cf``!K62tUh6p!C6QPD2yJ@W_iVE(Iq=Ff=^k)PC_k|OXr?%U| z=xiT0_lMHa4Iy;23!7hq2hjc?66_8nK`l0ak}DQKLwkR+(0=al7m0R!!J*!tjgjFS znw%e6*!ZIH$qjQ#^nFBg<99SZC1%q??`JBx?+1zI_|Q16K9T4iO2gKh#{MDXzOOj^ z`wolv2P|TQ%%_#S9vNeLUT9(c7Rf)JXmYf2Hznr2M{+ME<_v^hj{yu{cN*F4LV~%^ z$cQ^FGSWDyar+dDyIx@tLpE0pkGq^m2oq#8VQdE^{f=ym3@=$28SbIE=Ls4grWc*I zM=}dt4#s~@q**ZaAleR#xo${eu28lulm_V`7TXZeV@*IFWRD6oey)?@H;{gGd>hG} zTS#nlb;R&*K=nU_(!}T?RgD!9VT8?0OEi5-&~U`2mKd>{RPy5;DwuPZg^|Gm&42j( zU`~QseCE(g&J2rtO|giPy1OmTgkp=-G^!XnUZ};?ZXSHJem7;Zv`V!^{&n+@Xgplb2!{d$n5iE>LX+>HhFC#`~lK;8i$*-h|~`K0YKf zL2@^sXUu`vq+n$N;iE)A9-Cdqp?*{(LoYV-$Q8Q*t&_>sPi=iXXmLhu^Qmc`0uAHt z7&Ltl>t1;px`kLbpsPDXq94%plf#!~V`Pw{a#+Yv**tRm1=Axvh};5m6Nr|ggS#Z@ zUh)UCo3PCr>089*49 zUTTZ~AR!FClc1JZcYY($%&#P|9QRP7=M9m5c}=7`O5B1??<}a5 zo4Y__Gl^6OQcEtLOQIbwsTe+NR4m6{P{Cd3X#rc9P9-tCu$e(Cu^DuB+hjERo}3Y232I^fz(r5M;qFN|#BrH35aRc#7!Dx!-=ng5 zATyD1D~>SjKojp}VPv>VW49o?iyZbHsQcQ{+)j>J+EWY0+? zf0SU62dRPK6G&=dVzj373AhPn|8Xk0t$;}TVD?f<=issL7?u1)iW{@=xCdm;Q7XFW z2nA+kptRX7>R3WfvBE zlb~^#2Bil>_#nGsCl)s&%j07wlISNtCT4~JD(K@r zX2xI@+eSwCMzb(7Y$ZbeVpo0PC2q;@kFd$H-+grv@!3T+Q; zc2G-fBP%0AC>i1Pfr*J>1Cl?9bP^ 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 - GlyphCapacity = State->RequiredGlyphCapacity; - } - Once Shape() returns 0, you are done shaping. Glyph indices are in the kbts_glyph.Id field. - Please note that, while the glyphs do also contain a Codepoint field, this field will mostly - be meaningless whenever complex shaping operations occur. This is because fonts exclusively - work on glyph indices, and a lot of ligatures are obviously a combination of several codepoints - and do not have a corresponding codepoint in the Unicode world. - The same is true when a single glyph is split into multiple glyphs. A font might decide to - decompose a letter-with-accent into a letter glyph + an accent glyph. In that case, we will - know what the accent glyph's index is, but we are not told what its codepoint is. + 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. - There is currently no way to track where in the source text each glyph originates from. - One thing we might try is to have a "void *UserData" member on each glyph, and flow it through - the different substitutions, but I personally have not needed this yet and I do not have good - test cases for it. If you are interested, let me know! + 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. - Final positions are in font units and can be extracted with Cursor() and PositionGlyph(). - To convert font units to fractional pixels in FreeType: - (FontX * FtSizeMetrics.x_scale) >> 16 - (FontY * FtSizeMetrics.y_scale) >> 16 - This will give you 26.6 fractional pixel units. - See https://freetype.org/freetype2/docs/reference/ft2-sizing_and_scaling.html for more info. - kbts_ResetShapeState() - Shaping - feature control - kbts_FeatureOverride() -- Describe a manual override for a font feature - kbts_FeatureOverrideFromTag() -- This also works on features that do not have a kbts_feature_id, but they will be slower. - (Calling this with a feature that has a kbts_feature_id will not be slower.) - kbts_GlyphConfig() -- Bake per-glyph parameters (for now, only feature overrides) - Shaping - feature control - incremental API - With this API, you provide a buffer first, and gradually construct the glyph_config. - kbts_EmptyGlyphConfig() - kbts_GlyphConfigOverrideFeature() - kbts_GlyphConfigOverrideFeatureFromTag() - Layout - kbts_Cursor() - kbts_PositionGlyph() - Manual memory management - kbts_SizeOfShapeState() - kbts_PlaceShapeState() - kbts_ReadFontHeader() -- Read and byteswap the top of the file. - kbts_ReadFontData() -- Read and byteswap the rest. - kbts_PostReadFontInitialize() -- Initialize auxiliary structures - Example code for reading a font file with this API looks like this: - size_t ScratchSize = kbts_ReadFontHeader(&Font, Data, Size); - size_t PermanentMemorySize = kbts_ReadFontData(&Font, malloc(ScratchSize), ScratchSize); - kbts_PostReadFontInitialize(&Font, malloc(PermanentMemorySize), PermanentMemorySize); + 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: - Please note that, AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE. - AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE. - AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE. - AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE! - If you need to open the same font with another library, you need to copy the data BEFORE - calling ReadFontHeader(). + Your text A Text runs with B Sequence of glyphs C + (Probably ------------> uniform direction ------------> ready to rasterize ------------> Pixels + UTF-8) and script - The buffer you pass to ReadFontData() is temporary and can be freed once the function returns. - The buffer you pass to PostReadFontInitialize() is persistent and can only be freed once you - are done with the font. - Utility, etc. - kbts_ShaperIsComplex() - kbts_ScriptIsComplex() - kbts_InferScript() -- Stupid script detection. Do not ship this! Use script breaks instead. - kbts_DecodeUtf8() - kbts_ScriptTagToScript() - kbts_FeatureTagToId() + We call arrow A text segmentation, arrow B text shaping, and arrow C rasterization. + This library does A and B. - EXAMPLE USAGE - Complete example: - const char *String = "..."; - size_t Length = strlen(String); - // Open a font file - kbts_font Font = kbts_FontFromFile("..."); - // Make some glyphs - kbts_glyph *Glyphs = (kbts_glyph *)malloc(sizeof(kbts_glyph) * Length); - uint32_t GlyphCount = 0; - kbts_script Script = KBTS_SCRIPT_DONT_KNOW; - kbts_direction Direction = KBTS_DIRECTION_NONE; - for(size_t StringAt = 0; StringAt < Length;) - { - kbts_decode Decode = kbts_DecodeUtf8(String + StringAt, Length - StringAt); - StringAt += Decode.SourceCharactersConsumed; - if(Decode.Valid) - { - kbts_glyph Glyph = kbts_CodepointToGlyph(&Font, Decode.Codepoint); - // Easy script inference for simple cases. (This is similar to hb_buffer_guess_segment_properties.) - // If you have already segmented String with our API, you already have a script! - // So no need to pass it in that case. - // Only use this as a shorthand, when you are pretty sure String is a single - // script. - kbts_InferScript(&Direction, &Script, Glyph.Script); - Glyphs[GlyphCount++] = Glyph; - } - } - // Shape - uint32_t GlyphCapacity = Length; - kbts_shape_state *State = kbts_CreateShapeState(&Font); - // A shape_config is immutable once created. You can freely share/hash it, etc. - kbts_shape_config Config = kbts_ShapeConfig(&Font, Script, KBTS_LANGUAGE_DONT_KNOW); - while(kbts_Shape(State, &Config, Direction, Direction, Glyphs, &GlyphCount, GlyphCapacity)) - { - Glyphs = realloc(Glyphs, sizeof(kbts_glyph) * State->RequiredGlyphCapacity); - GlyphCapacity = State->RequiredGlyphCapacity; - } - // Get final positions - kbts_cursor Cursor = kbts_Cursor(Direction); - for(size_t GlyphIndex = 0; GlyphIndex < GlyphCount; ++GlyphIndex) - { - int X, Y; - kbts_PositionGlyph(&Cursor, &Glyphs[GlyphIndex], &X, &Y); - } + 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) - Breaking Unicode text - finding the end of the current line: - uint32_t LineLength = 0; - kbts_break_state BreakState; - kbts_BeginBreak(&BreakState, KBTS_DIRECTION_NONE, KBTS_JAPANESE_LINE_BREAK_STYLE_NORMAL); - for(size_t CodepointIndex = 0; (CodepointIndex < CodepointCount) && !LineLength; ++CodepointIndex) - { - // We use a fixed-width encoding (codepoints) -- just pass a PositionIncrement of 1. - // If we wanted to directly segment UTF-8 in-place, we would pass in however many bytes each UTF-8 character was. + COMPILING & LINKING + This library uses declare-anywhere, so it will not compile as C89/VC6 C. - // The last parameter to AddCodepoint is a boolean signifying the end of text. - // Alternatively, we could have written: - // kbts_BreakAddCodepoint(..., 0); - // if((CodepointIndex + 1) == CodepointCount) kbts_BreakFlush(); + 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. - kbts_BreakAddCodepoint(&BreakState, Codepoints[CodepointIndex], 1, (CodepointIndex + 1) == CodepointCount); - kbts_break Break; - while(kbts_Break(&BreakState, &Break)) - { - // We could just as easily check for any other kind of break here. - // See kbts_break_flags for the kind of breaks we can find. - if(Break.Flags & KBTS_BREAK_FLAG_LINE_HARD) - { - LineLength = Break.Position; - } - } - } + If you also do this: + #define KB_TEXT_SHAPE_STATIC + then all functions will be declared as static. - Control which font features apply to which glyphs: - kbts_feature_override Ss03FeatureOverrides[] = { - kbts_FeatureOverride(KBTS_FEATURE_ID_ccmp, 0, 0), // Disable ccmp - kbts_FeatureOverride(KBTS_FEATURE_ID_ss03, 0, 1), // Enable ss03 - }; - kbts_glyph_config Ss03Config = kbts_GlyphConfig(Ss03FeatureOverrides, 2); - kbts_feature_override SaltFeatureOverrides[] = { - kbts_FeatureOverride(KBTS_FEATURE_ID_salt, 1, 3), // Pick alternate glyph number 3 from feature 'salt' - }; - kbts_glyph_config SaltConfig = kbts_GlyphConfig(SaltFeatureOverrides, 1); + 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. - // Then, do this before calling kbts_Shape(): - MyGlyphs[0].Config = &Ss03Config; - MyGlyphs[1].Config = &SaltConfig; + EXAMPLES + Basic + kbts_shape_context *Context = kbts_CreateShapeContext(0, 0); + kbts_ShapePushFontFromFile(Context, "myfont.ttf", 0); - Open a font with your own memory: - kbts_font Font; - // Be careful: ReadFontHeader() and ReadFontData() both byteswap font data in-place! - size_t ScratchSize = kbts_ReadFontHeader(&Font, Data, Size); - size_t PermanentMemorySize = kbts_ReadFontData(&Font, malloc(ScratchSize), ScratchSize); - kbts_PostReadFontInitialize(&Font, malloc(PermanentMemorySize), PermanentMemorySize); - // At any point, you can call kbts_FontIsValid(&Font) to check if the read went well. - // You do not need to check for font validity in between API calls here; once a font is flagged - // with an error, subsequent API calls simply do nothing. - // If you have provided real font data, kbts_FontIsValid(&Font) will most likely be true. - // We may still fail to parse some technically valid fonts that contain very deeply-nested or - // self-referential lookups. - // In those cases, Harfbuzz might open the font, but, at the time of writing, it silently limits - // the recursion depth to 64 when applying the lookups anyway. + 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); - Allocate a shape_state with your own memory: - kbts_shape_state *State = kbts_PlaceShapeState(malloc(kbts_SizeOfShapeState(Font)), kbts_SizeOfShapeState(Font)); + // 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; - Use a single shape_state across multiple runs: - while(kbts_Shape(State, ...)) {...} - // If the previous shaping operation completed successfully, ResetShapeState is not necessary. - // If, for any reason, you have decided to stop shaping in the middle of an operation, though, - // you need to call it. - // (This is just setting a state counter, so this is very cheap.) - kbts_ResetShapeState(State); - while(kbts_Shape(State, ...)) {...} + DisplayGlyph(Glyph->Id, GlyphX, GlyphY); - Use a single shape_state across multiple fonts: - // Different fonts have different memory requirements for shaping. However, beyond that, there - // is nothing that ties a shape_state to a font. You may use it freely with multiple fonts, as - // long as it is large enough. - size_t Size0 = kbts_SizeOfShapeState(Font0); - size_t Size1 = kbts_SizeOfShapeState(Font1); - size_t Size = MAX(Size0, Size1) - kbts_shape_state *State = kbts_PlaceShapeState(malloc(Size), Size); + CursorX += Glyph->AdvanceX; + CursorY += Glyph->AdvanceY; + } + } - Use a single shape_config across multiple runs: - // Once a shape_config has been created, it is assumed to be immutable and can be trivially shared - // between runs/operations that have the same parameters. + Font collections + void *FontData; + int FontSize; + kbts_font Font = kbts_FontFromFile("myfonts.ttc", 0, 0, 0, &FontData, &FontSize); - PERFORMANCE - Just like most libraries that interact with font files, we use the file as an in-memory database. - There are a few issues with this approach: - - Font files can be arbitrarily complex, making it difficult to predict system behavior at runtime. - - Font files are encoded in big endian byte order, which is stupid and slow. - We compensate for this by pre-processing as much as we can when opening the file. Notably, we - byteswap everything we need in-place, we precompute some useful runtime memory bounds, and we - allocate a few auxiliary acceleration structures. + kbts_ShapePushFont(Context, &Font); - Since we byteswap everything in-place, you cannot pass the same font data to kbts and to another - library, because the other library will expect everything to be big endian. + int FontCount = kbts_FontCount(FontData, FontSize); + for(int FontIndex = 1; FontIndex < FontCount; ++FontIndex) + { + kbts_ShapePushFontFromMemory(Context, FontData, FontSize, FontIndex); + } - As a result of this approach, opening fonts is slow and shaping is fast. This is very much intentional. - At the time of writing (2025-07-13), on Harfbuzz's test suite, we are, on average, 4.5x faster than - Harfbuzz on my laptop (Ryzen 9 5900HX). As fonts are complex, and Harfbuzz's test suite is quite varied, - the speedup numbers are rather spread out: - Best: 22x - 10th percentile: 6.5x - Median: 4.5x - 90th percentile: 2.5x - Worst: 1.05x - So, aside from a few extreme cases, you should expect a small integer speedup factor compared to Harfbuzz. + 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: @@ -317,7 +1219,6 @@ Word breaking is NOT supported for languages that require word dictionaries, like CJK. FONT SUPPORT - Font collections (.ttc) are not supported. 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. @@ -344,6 +1245,16 @@ See https://unicode.org/reports/tr9 for more information. VERSION HISTORY + 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. @@ -359,8 +1270,6 @@ TODO Word dictionaries for word breaking: CJK, etc. 'stch' feature. - Precompute GPOS nesting in advance and replace recursion with explicit frames. - (Either that or limit to a hard depth for now.) LICENSE zlib License @@ -384,7 +1293,6 @@ 3. This notice may not be removed or altered from any source distribution. */ - #ifndef KB_TEXT_SHAPE_INCLUDED # define KB_TEXT_SHAPE_INCLUDED @@ -462,6 +1370,7 @@ # 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) @@ -471,6 +1380,7 @@ # define KBTS_NOINLINE static __attribute__((noinline)) # endif # endif +# define KBTS_ALIGNOF __alignof__ # endif # ifndef KBTS_INLINE # define KBTS_INLINE static inline @@ -478,75 +1388,6 @@ # define KBTS_FOURCC(A, B, C, D) ((A) | ((B) << 8) | ((C) << 16) | ((D) << 24)) -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_u8 kbts_reph_position; -enum kbts_reph_position_enum -{ - KBTS_REPH_POSITION_AFTER_POST, - KBTS_REPH_POSITION_BEFORE_POST, - KBTS_REPH_POSITION_BEFORE_SUBJOINED, - KBTS_REPH_POSITION_AFTER_SUBJOINED, - KBTS_REPH_POSITION_AFTER_MAIN, - - KBTS_REPH_POSITION_COUNT, -}; - -typedef kbts_u8 kbts_reph_encoding; -enum kbts_reph_encoding_enum -{ - KBTS_REPH_ENCODING_IMPLICIT, - KBTS_REPH_ENCODING_EXPLICIT, - KBTS_REPH_ENCODING_LOGICAL_REPHA, - KBTS_REPH_ENCODING_VISUAL_REPHA, - - KBTS_REPH_ENCODING_COUNT, -}; - -typedef kbts_u8 kbts_syllabic_position; -enum kbts_syllabic_position_enum -{ - KBTS_SYLLABIC_POSITION_NONE, - - KBTS_SYLLABIC_POSITION_RA_TO_BECOME_REPH, - - KBTS_SYLLABIC_POSITION_PREBASE_MATRA, - KBTS_SYLLABIC_POSITION_PREBASE_CONSONANT, - - KBTS_SYLLABIC_POSITION_SYLLABLE_BASE, - KBTS_SYLLABIC_POSITION_AFTER_MAIN, - - KBTS_SYLLABIC_POSITION_ABOVEBASE_CONSONANT, - - KBTS_SYLLABIC_POSITION_BEFORE_SUBJOINED, - KBTS_SYLLABIC_POSITION_BELOWBASE_CONSONANT, - KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED, - - KBTS_SYLLABIC_POSITION_BEFORE_POST, - KBTS_SYLLABIC_POSITION_POSTBASE_CONSONANT, - KBTS_SYLLABIC_POSITION_AFTER_POST, - - KBTS_SYLLABIC_POSITION_FINAL_CONSONANT, - KBTS_SYLLABIC_POSITION_SMVD, - - KBTS_SYLLABIC_POSITION_COUNT, -}; - typedef kbts_u32 kbts_language; enum kbts_language_enum { @@ -1262,32 +2103,205 @@ enum kbts_break_flags_enum // (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, }; -typedef kbts_u8 kbts_op_kind; -enum kbts_op_kind_enum +// 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 { - KBTS_OP_KIND_END, + // 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, - // Substitution ops. - KBTS_OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, - KBTS_OP_KIND_NORMALIZE, - KBTS_OP_KIND_NORMALIZE_HANGUL, - KBTS_OP_KIND_FLAG_JOINING_LETTERS, - KBTS_OP_KIND_GSUB_FEATURES, - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, + // Normal style is used for books and documents. + KBTS_JAPANESE_LINE_BREAK_STYLE_NORMAL, - // Positioning ops. - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, + // Loose style is used for newspapers, and (I assume) any other narrow column format. + KBTS_JAPANESE_LINE_BREAK_STYLE_LOOSE, - KBTS_OP_KIND_POST_GPOS_FIXUP, - KBTS_OP_KIND_STCH_POSTPASS, + KBTS_JAPANESE_LINE_BREAK_STYLE_COUNT, +}; - KBTS_OP_KIND_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; @@ -1331,60 +2345,62 @@ enum kbts_glyph_flags_enum KBTS_GLYPH_FLAG_LIGATURE = (1 << 29), KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION = (1 << 30), }; -#define KBTS_GLYPH_FEATURE_MASK ((KBTS_GLYPH_FLAG_CFAR << 1) - 1) -// In USE, glyphs are mostly not pre-flagged for feature application. -// However, we do want to flag rphf/pref results for reordering, so we want to -// keep all of the flags as usual, and only use these feature flags for filtering. -#define KBTS_USE_GLYPH_FEATURE_MASK (KBTS_GLYPH_FLAG_ISOL | KBTS_GLYPH_FLAG_FINA | KBTS_GLYPH_FLAG_FIN2 | KBTS_GLYPH_FLAG_FIN3 | \ - KBTS_GLYPH_FLAG_MEDI | KBTS_GLYPH_FLAG_MED2 | KBTS_GLYPH_FLAG_INIT | KBTS_GLYPH_FLAG_NUMR | \ - KBTS_GLYPH_FLAG_DNOM | KBTS_GLYPH_FLAG_FRAC) -#define KBTS_JOINING_FEATURE_MASK (KBTS_GLYPH_FLAG_ISOL | KBTS_GLYPH_FLAG_FINA | KBTS_GLYPH_FLAG_FIN2 | KBTS_GLYPH_FLAG_FIN3 | \ - KBTS_GLYPH_FLAG_MEDI | KBTS_GLYPH_FLAG_MED2 | KBTS_GLYPH_FLAG_INIT) -#define KBTS_JOINING_FEATURE_TO_GLYPH_FLAG(Feature) (1 << ((Feature) - 1)) -// 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 +typedef kbts_u8 kbts_joining_feature; +enum kbts_joining_feature_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, + KBTS_JOINING_FEATURE_NONE, - // Normal style is used for books and documents. - KBTS_JAPANESE_LINE_BREAK_STYLE_NORMAL, + // 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, - // 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, + KBTS_JOINING_FEATURE_COUNT, }; -typedef kbts_u32 kbts_orientation; -enum kbts_orientation_enum +typedef kbts_u32 kbts_user_id_generation_mode; +enum kbts_user_id_generation_mode_enum { - KBTS_ORIENTATION_HORIZONTAL, - KBTS_ORIENTATION_VERTICAL, + KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX, + KBTS_USER_ID_GENERATION_MODE_SOURCE_INDEX, - KBTS_ORIENTATION_COUNT, + KBTS_USER_ID_GENERATION_MODE_COUNT, }; -typedef kbts_u32 kbts_direction; -enum kbts_direction_enum +typedef kbts_u32 kbts_break_config_flags; +enum kbts_break_config_flags_enum { - KBTS_DIRECTION_NONE, - KBTS_DIRECTION_LTR, - KBTS_DIRECTION_RTL, + KBTS_BREAK_CONFIG_FLAG_NONE, - KBTS_DIRECTION_COUNT, + 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 { @@ -1407,12 +2423,15 @@ enum kbts_unicode_flag_enum 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, @@ -1567,6 +2586,8 @@ enum kbts_shaper_enum 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 { @@ -2168,624 +3189,166 @@ enum kbts_feature_tag_enum KBTS_FEATURE_TAG_zero = KBTS_FOURCC('z', 'e', 'r', 'o'), // Slashed Zero }; -typedef kbts_u32 kbts_feature_id; -enum kbts_feature_id_enum -{ - KBTS_FEATURE_ID_UNREGISTERED, // Features that aren't pre-defined in the OpenType spec - KBTS_FEATURE_ID_isol, // Isolated Forms - KBTS_FEATURE_ID_fina, // Terminal Forms - KBTS_FEATURE_ID_fin2, // Terminal Forms #2 - KBTS_FEATURE_ID_fin3, // Terminal Forms #3 - KBTS_FEATURE_ID_medi, // Medial Forms - KBTS_FEATURE_ID_med2, // Medial Forms #2 - KBTS_FEATURE_ID_init, // Initial Forms - KBTS_FEATURE_ID_ljmo, // Leading Jamo Forms - KBTS_FEATURE_ID_vjmo, // Vowel Jamo Forms - KBTS_FEATURE_ID_tjmo, // Trailing Jamo Forms - KBTS_FEATURE_ID_rphf, // Reph Form - KBTS_FEATURE_ID_blwf, // Below-base Forms - KBTS_FEATURE_ID_half, // Half Forms - KBTS_FEATURE_ID_pstf, // Post-base Forms - KBTS_FEATURE_ID_abvf, // Above-base Forms - KBTS_FEATURE_ID_pref, // Pre-base Forms - KBTS_FEATURE_ID_numr, // Numerators - KBTS_FEATURE_ID_frac, // Fractions - KBTS_FEATURE_ID_dnom, // Denominators - KBTS_FEATURE_ID_cfar, // Conjunct Form After Ro - KBTS_FEATURE_ID_aalt, // Access All Alternates - KBTS_FEATURE_ID_abvm, // Above-base Mark Positioning - KBTS_FEATURE_ID_abvs, // Above-base Substitutions - KBTS_FEATURE_ID_afrc, // Alternative Fractions - KBTS_FEATURE_ID_akhn, // Akhand - KBTS_FEATURE_ID_apkn, // Kerning for Alternate Proportional Widths - KBTS_FEATURE_ID_blwm, // Below-base Mark Positioning - KBTS_FEATURE_ID_blws, // Below-base Substitutions - KBTS_FEATURE_ID_calt, // Contextual Alternates - KBTS_FEATURE_ID_case, // Case-sensitive Forms - KBTS_FEATURE_ID_ccmp, // Glyph Composition / Decomposition - KBTS_FEATURE_ID_chws, // Contextual Half-width Spacing - KBTS_FEATURE_ID_cjct, // Conjunct Forms - KBTS_FEATURE_ID_clig, // Contextual Ligatures - KBTS_FEATURE_ID_cpct, // Centered CJK Punctuation - KBTS_FEATURE_ID_cpsp, // Capital Spacing - KBTS_FEATURE_ID_cswh, // Contextual Swash - KBTS_FEATURE_ID_curs, // Cursive Positioning - KBTS_FEATURE_ID_cv01, // Character Variant 1 - KBTS_FEATURE_ID_cv02, // Character Variant 2 - KBTS_FEATURE_ID_cv03, // Character Variant 3 - KBTS_FEATURE_ID_cv04, // Character Variant 4 - KBTS_FEATURE_ID_cv05, // Character Variant 5 - KBTS_FEATURE_ID_cv06, // Character Variant 6 - KBTS_FEATURE_ID_cv07, // Character Variant 7 - KBTS_FEATURE_ID_cv08, // Character Variant 8 - KBTS_FEATURE_ID_cv09, // Character Variant 9 - KBTS_FEATURE_ID_cv10, // Character Variant 10 - KBTS_FEATURE_ID_cv11, // Character Variant 11 - KBTS_FEATURE_ID_cv12, // Character Variant 12 - KBTS_FEATURE_ID_cv13, // Character Variant 13 - KBTS_FEATURE_ID_cv14, // Character Variant 14 - KBTS_FEATURE_ID_cv15, // Character Variant 15 - KBTS_FEATURE_ID_cv16, // Character Variant 16 - KBTS_FEATURE_ID_cv17, // Character Variant 17 - KBTS_FEATURE_ID_cv18, // Character Variant 18 - KBTS_FEATURE_ID_cv19, // Character Variant 19 - KBTS_FEATURE_ID_cv20, // Character Variant 20 - KBTS_FEATURE_ID_cv21, // Character Variant 21 - KBTS_FEATURE_ID_cv22, // Character Variant 22 - KBTS_FEATURE_ID_cv23, // Character Variant 23 - KBTS_FEATURE_ID_cv24, // Character Variant 24 - KBTS_FEATURE_ID_cv25, // Character Variant 25 - KBTS_FEATURE_ID_cv26, // Character Variant 26 - KBTS_FEATURE_ID_cv27, // Character Variant 27 - KBTS_FEATURE_ID_cv28, // Character Variant 28 - KBTS_FEATURE_ID_cv29, // Character Variant 29 - KBTS_FEATURE_ID_cv30, // Character Variant 30 - KBTS_FEATURE_ID_cv31, // Character Variant 31 - KBTS_FEATURE_ID_cv32, // Character Variant 32 - KBTS_FEATURE_ID_cv33, // Character Variant 33 - KBTS_FEATURE_ID_cv34, // Character Variant 34 - KBTS_FEATURE_ID_cv35, // Character Variant 35 - KBTS_FEATURE_ID_cv36, // Character Variant 36 - KBTS_FEATURE_ID_cv37, // Character Variant 37 - KBTS_FEATURE_ID_cv38, // Character Variant 38 - KBTS_FEATURE_ID_cv39, // Character Variant 39 - KBTS_FEATURE_ID_cv40, // Character Variant 40 - KBTS_FEATURE_ID_cv41, // Character Variant 41 - KBTS_FEATURE_ID_cv42, // Character Variant 42 - KBTS_FEATURE_ID_cv43, // Character Variant 43 - KBTS_FEATURE_ID_cv44, // Character Variant 44 - KBTS_FEATURE_ID_cv45, // Character Variant 45 - KBTS_FEATURE_ID_cv46, // Character Variant 46 - KBTS_FEATURE_ID_cv47, // Character Variant 47 - KBTS_FEATURE_ID_cv48, // Character Variant 48 - KBTS_FEATURE_ID_cv49, // Character Variant 49 - KBTS_FEATURE_ID_cv50, // Character Variant 50 - KBTS_FEATURE_ID_cv51, // Character Variant 51 - KBTS_FEATURE_ID_cv52, // Character Variant 52 - KBTS_FEATURE_ID_cv53, // Character Variant 53 - KBTS_FEATURE_ID_cv54, // Character Variant 54 - KBTS_FEATURE_ID_cv55, // Character Variant 55 - KBTS_FEATURE_ID_cv56, // Character Variant 56 - KBTS_FEATURE_ID_cv57, // Character Variant 57 - KBTS_FEATURE_ID_cv58, // Character Variant 58 - KBTS_FEATURE_ID_cv59, // Character Variant 59 - KBTS_FEATURE_ID_cv60, // Character Variant 60 - KBTS_FEATURE_ID_cv61, // Character Variant 61 - KBTS_FEATURE_ID_cv62, // Character Variant 62 - KBTS_FEATURE_ID_cv63, // Character Variant 63 - KBTS_FEATURE_ID_cv64, // Character Variant 64 - KBTS_FEATURE_ID_cv65, // Character Variant 65 - KBTS_FEATURE_ID_cv66, // Character Variant 66 - KBTS_FEATURE_ID_cv67, // Character Variant 67 - KBTS_FEATURE_ID_cv68, // Character Variant 68 - KBTS_FEATURE_ID_cv69, // Character Variant 69 - KBTS_FEATURE_ID_cv70, // Character Variant 70 - KBTS_FEATURE_ID_cv71, // Character Variant 71 - KBTS_FEATURE_ID_cv72, // Character Variant 72 - KBTS_FEATURE_ID_cv73, // Character Variant 73 - KBTS_FEATURE_ID_cv74, // Character Variant 74 - KBTS_FEATURE_ID_cv75, // Character Variant 75 - KBTS_FEATURE_ID_cv76, // Character Variant 76 - KBTS_FEATURE_ID_cv77, // Character Variant 77 - KBTS_FEATURE_ID_cv78, // Character Variant 78 - KBTS_FEATURE_ID_cv79, // Character Variant 79 - KBTS_FEATURE_ID_cv80, // Character Variant 80 - KBTS_FEATURE_ID_cv81, // Character Variant 81 - KBTS_FEATURE_ID_cv82, // Character Variant 82 - KBTS_FEATURE_ID_cv83, // Character Variant 83 - KBTS_FEATURE_ID_cv84, // Character Variant 84 - KBTS_FEATURE_ID_cv85, // Character Variant 85 - KBTS_FEATURE_ID_cv86, // Character Variant 86 - KBTS_FEATURE_ID_cv87, // Character Variant 87 - KBTS_FEATURE_ID_cv88, // Character Variant 88 - KBTS_FEATURE_ID_cv89, // Character Variant 89 - KBTS_FEATURE_ID_cv90, // Character Variant 90 - KBTS_FEATURE_ID_cv91, // Character Variant 91 - KBTS_FEATURE_ID_cv92, // Character Variant 92 - KBTS_FEATURE_ID_cv93, // Character Variant 93 - KBTS_FEATURE_ID_cv94, // Character Variant 94 - KBTS_FEATURE_ID_cv95, // Character Variant 95 - KBTS_FEATURE_ID_cv96, // Character Variant 96 - KBTS_FEATURE_ID_cv97, // Character Variant 97 - KBTS_FEATURE_ID_cv98, // Character Variant 98 - KBTS_FEATURE_ID_cv99, // Character Variant 99 - KBTS_FEATURE_ID_c2pc, // Petite Capitals From Capitals - KBTS_FEATURE_ID_c2sc, // Small Capitals From Capitals - KBTS_FEATURE_ID_dist, // Distances - KBTS_FEATURE_ID_dlig, // Discretionary Ligatures - KBTS_FEATURE_ID_dtls, // Dotless Forms - KBTS_FEATURE_ID_expt, // Expert Forms - KBTS_FEATURE_ID_falt, // Final Glyph on Line Alternates - KBTS_FEATURE_ID_flac, // Flattened Accent Forms - KBTS_FEATURE_ID_fwid, // Full Widths - KBTS_FEATURE_ID_haln, // Halant Forms - KBTS_FEATURE_ID_halt, // Alternate Half Widths - KBTS_FEATURE_ID_hist, // Historical Forms - KBTS_FEATURE_ID_hkna, // Horizontal Kana Alternates - KBTS_FEATURE_ID_hlig, // Historical Ligatures - KBTS_FEATURE_ID_hngl, // Hangul - KBTS_FEATURE_ID_hojo, // Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) - KBTS_FEATURE_ID_hwid, // Half Widths - KBTS_FEATURE_ID_ital, // Italics - KBTS_FEATURE_ID_jalt, // Justification Alternates - KBTS_FEATURE_ID_jp78, // JIS78 Forms - KBTS_FEATURE_ID_jp83, // JIS83 Forms - KBTS_FEATURE_ID_jp90, // JIS90 Forms - KBTS_FEATURE_ID_jp04, // JIS2004 Forms - KBTS_FEATURE_ID_kern, // Kerning - KBTS_FEATURE_ID_lfbd, // Left Bounds - KBTS_FEATURE_ID_liga, // Standard Ligatures - KBTS_FEATURE_ID_lnum, // Lining Figures - KBTS_FEATURE_ID_locl, // Localized Forms - KBTS_FEATURE_ID_ltra, // Left-to-right Alternates - KBTS_FEATURE_ID_ltrm, // Left-to-right Mirrored Forms - KBTS_FEATURE_ID_mark, // Mark Positioning - KBTS_FEATURE_ID_mgrk, // Mathematical Greek - KBTS_FEATURE_ID_mkmk, // Mark to Mark Positioning - KBTS_FEATURE_ID_mset, // Mark Positioning via Substitution - KBTS_FEATURE_ID_nalt, // Alternate Annotation Forms - KBTS_FEATURE_ID_nlck, // NLC Kanji Forms - KBTS_FEATURE_ID_nukt, // Nukta Forms - KBTS_FEATURE_ID_onum, // Oldstyle Figures - KBTS_FEATURE_ID_opbd, // Optical Bounds - KBTS_FEATURE_ID_ordn, // Ordinals - KBTS_FEATURE_ID_ornm, // Ornaments - KBTS_FEATURE_ID_palt, // Proportional Alternate Widths - KBTS_FEATURE_ID_pcap, // Petite Capitals - KBTS_FEATURE_ID_pkna, // Proportional Kana - KBTS_FEATURE_ID_pnum, // Proportional Figures - KBTS_FEATURE_ID_pres, // Pre-base Substitutions - KBTS_FEATURE_ID_psts, // Post-base Substitutions - KBTS_FEATURE_ID_pwid, // Proportional Widths - KBTS_FEATURE_ID_qwid, // Quarter Widths - KBTS_FEATURE_ID_rand, // Randomize - KBTS_FEATURE_ID_rclt, // Required Contextual Alternates - KBTS_FEATURE_ID_rkrf, // Rakar Forms - KBTS_FEATURE_ID_rlig, // Required Ligatures - KBTS_FEATURE_ID_rtbd, // Right Bounds - KBTS_FEATURE_ID_rtla, // Right-to-left Alternates - KBTS_FEATURE_ID_rtlm, // Right-to-left Mirrored Forms - KBTS_FEATURE_ID_ruby, // Ruby Notation Forms - KBTS_FEATURE_ID_rvrn, // Required Variation Alternates - KBTS_FEATURE_ID_salt, // Stylistic Alternates - KBTS_FEATURE_ID_sinf, // Scientific Inferiors - KBTS_FEATURE_ID_size, // Optical size - KBTS_FEATURE_ID_smcp, // Small Capitals - KBTS_FEATURE_ID_smpl, // Simplified Forms - KBTS_FEATURE_ID_ss01, // Stylistic Set 1 - KBTS_FEATURE_ID_ss02, // Stylistic Set 2 - KBTS_FEATURE_ID_ss03, // Stylistic Set 3 - KBTS_FEATURE_ID_ss04, // Stylistic Set 4 - KBTS_FEATURE_ID_ss05, // Stylistic Set 5 - KBTS_FEATURE_ID_ss06, // Stylistic Set 6 - KBTS_FEATURE_ID_ss07, // Stylistic Set 7 - KBTS_FEATURE_ID_ss08, // Stylistic Set 8 - KBTS_FEATURE_ID_ss09, // Stylistic Set 9 - KBTS_FEATURE_ID_ss10, // Stylistic Set 10 - KBTS_FEATURE_ID_ss11, // Stylistic Set 11 - KBTS_FEATURE_ID_ss12, // Stylistic Set 12 - KBTS_FEATURE_ID_ss13, // Stylistic Set 13 - KBTS_FEATURE_ID_ss14, // Stylistic Set 14 - KBTS_FEATURE_ID_ss15, // Stylistic Set 15 - KBTS_FEATURE_ID_ss16, // Stylistic Set 16 - KBTS_FEATURE_ID_ss17, // Stylistic Set 17 - KBTS_FEATURE_ID_ss18, // Stylistic Set 18 - KBTS_FEATURE_ID_ss19, // Stylistic Set 19 - KBTS_FEATURE_ID_ss20, // Stylistic Set 20 - KBTS_FEATURE_ID_ssty, // Math Script-style Alternates - KBTS_FEATURE_ID_stch, // Stretching Glyph Decomposition - KBTS_FEATURE_ID_subs, // Subscript - KBTS_FEATURE_ID_sups, // Superscript - KBTS_FEATURE_ID_swsh, // Swash - KBTS_FEATURE_ID_test, // Test features, only for development - KBTS_FEATURE_ID_titl, // Titling - KBTS_FEATURE_ID_tnam, // Traditional Name Forms - KBTS_FEATURE_ID_tnum, // Tabular Figures - KBTS_FEATURE_ID_trad, // Traditional Forms - KBTS_FEATURE_ID_twid, // Third Widths - KBTS_FEATURE_ID_unic, // Unicase - KBTS_FEATURE_ID_valt, // Alternate Vertical Metrics - KBTS_FEATURE_ID_vapk, // Kerning for Alternate Proportional Vertical Metrics - KBTS_FEATURE_ID_vatu, // Vattu Variants - KBTS_FEATURE_ID_vchw, // Vertical Contextual Half-width Spacing - KBTS_FEATURE_ID_vert, // Vertical Alternates - KBTS_FEATURE_ID_vhal, // Alternate Vertical Half Metrics - KBTS_FEATURE_ID_vkna, // Vertical Kana Alternates - KBTS_FEATURE_ID_vkrn, // Vertical Kerning - KBTS_FEATURE_ID_vpal, // Proportional Alternate Vertical Metrics - KBTS_FEATURE_ID_vrt2, // Vertical Alternates and Rotation - KBTS_FEATURE_ID_vrtr, // Vertical Alternates for Rotation - KBTS_FEATURE_ID_zero, // Slashed Zero - KBTS_FEATURE_ID_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 struct kbts_lookup_info -{ - kbts_u32 MaximumBacktrackWithoutSkippingGlyphs; - kbts_u32 MaximumLookaheadWithoutSkippingGlyphs; - kbts_u32 MaximumSubstitutionOutputSize; - kbts_u32 MaximumInputSequenceLength; - kbts_u32 MaximumLookupStackSize; -} kbts_lookup_info; - -typedef struct kbts_op_state kbts_op_state; -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_iterate_features kbts_iterate_features; -typedef struct kbts_shape_state kbts_shape_state; +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__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_u32 MinimumBacktrackPlusOne; - kbts_u32 MinimumFollowupPlusOne; + 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 { - char *FileBase; - kbts_un FileSize; - kbts_head *Head; + kbts_allocator_function *Allocator; + void *AllocatorData; + + kbts_blob_header *Blob; kbts_u16 *Cmap; - kbts_gdef *Gdef; - kbts_cmap_14 *Cmap14; - kbts_gsub_gpos *ShapingTables[KBTS_SHAPING_TABLE_COUNT]; - void *Fvar; - kbts_maxp *Maxp; + kbts__cmap_14 *Cmap14; - kbts_hea *Hea[KBTS_ORIENTATION_COUNT]; - kbts_u16 *Mtx[KBTS_ORIENTATION_COUNT]; + kbts__gsub_gpos *ShapingTables[KBTS_SHAPING_TABLE_COUNT]; - kbts_lookup_info LookupInfo; + void *UserData; - kbts_u32 GlyphCount; - kbts_u32 LookupCount; - kbts_u32 SubtableCount; - - kbts_u32 *GlyphLookupMatrix; // [LookupCount * GlyphCount] bitmap - kbts_u32 *GlyphLookupSubtableMatrix; // [LookupSubtableCount * GlyphCount] bitmap - kbts_u32 *LookupSubtableIndexOffsets; // [LookupCount] - kbts_lookup_subtable_info *SubtableInfos; // [LookupSubtableCount] - kbts_u32 GposLookupIndexOffset; - - int Error; + kbts_load_font_error Error; } kbts_font; -typedef struct kbts_glyph_classes +typedef struct kbts_font_info { - kbts_u16 Class; - kbts_u16 MarkAttachmentClass; -} kbts_glyph_classes; + char *Strings[KBTS_FONT_INFO_STRING_ID_COUNT]; + kbts_u16 StringLengths[KBTS_FONT_INFO_STRING_ID_COUNT]; -typedef struct kbts_feature_set -{ - kbts_u64 Flags[(KBTS_FEATURE_ID_COUNT + 63) / 64]; -} kbts_feature_set; + kbts_font_style_flags StyleFlags; + kbts_font_weight Weight; + kbts_font_width Width; +} kbts_font_info; typedef struct kbts_feature_override { - kbts_feature_id Id; kbts_feature_tag Tag; - kbts_u32 EnabledOrAlternatePlusOne; + int Value; } kbts_feature_override; -typedef struct kbts_glyph_config -{ - kbts_feature_set EnabledFeatures; - kbts_feature_set DisabledFeatures; - kbts_u32 FeatureOverrideCount; - kbts_u32 FeatureOverrideCapacity; - kbts_u32 RequiredFeatureOverrideCapacity; - kbts_feature_override *FeatureOverrides; // [FeatureOverrideCount] -} kbts_glyph_config; - -typedef struct kbts_glyph -{ - kbts_u32 Codepoint; - kbts_u16 Id; // Glyph index. This is what you want to use to query outline data. - kbts_u16 Uid; - kbts_glyph_classes Classes; - - kbts_u64 Decomposition; - - kbts_glyph_config *Config; - - kbts_glyph_flags Flags; - - // These fields are the glyph's final positioning data. - // For normal usage, you should not have to use these directly yourself. - // In case you are curious or have a specific need, see kbts_PositionGlyph() to see how these are used. - kbts_s32 OffsetX; - kbts_s32 OffsetY; - kbts_s32 AdvanceX; - kbts_s32 AdvanceY; - - 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; - - // 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. - kbts_u16 AttachGlyphIndexPlusOne; // Set by GPOS attachments. - - // 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 Script; - 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_glyph_array -{ - kbts_glyph *Glyphs; - kbts_u32 Count; - kbts_u32 TotalCount; - kbts_u32 Capacity; - kbts_u32 RequiredCapacity; -} kbts_glyph_array; - -typedef struct kbts_op_state_normalize -{ - kbts_un CodepointsToDecomposeCount; - kbts_un AboveBaseGlyphCount; -} kbts_op_state_normalize; - -typedef struct kbts_op_state_gsub -{ - kbts_feature_set LookupFeatures; - kbts_un LookupIndex; - kbts_u32 GlyphFilter; - kbts_u32 SkipFlags; -} kbts_op_state_gsub; - -typedef struct kbts_op_state_normalize_hangul -{ - kbts_glyph LvtGlyphs[4]; - kbts_un LvtGlyphCount; -} kbts_op_state_normalize_hangul; - -typedef union kbts_op_state_op_specific -{ - kbts_op_state_normalize Normalize; - kbts_op_state_gsub Gsub; - kbts_op_state_normalize_hangul NormalizeHangul; -} kbts_op_state_op_specific; - -typedef struct kbts_lookup_indices -{ - kbts_u32 FeatureTag; - kbts_u32 FeatureId; - kbts_u32 SkipFlags; - kbts_u32 GlyphFilter; - kbts_u32 Count; - kbts_u16 *Indices; -} kbts_lookup_indices; - -typedef struct kbts_op -{ - kbts_op_kind Kind; - kbts_feature_set Features; -} kbts_op; - -// This needs to be updated when we change the op lists! -#define KBTS_MAX_SIMULTANEOUS_FEATURES 16 -typedef struct kbts_op_state -{ - kbts_un WrittenCount; - kbts_un GlyphIndex; - kbts_u32 FrameCount; - kbts_u32 ResumePoint; - - kbts_u32 FeatureCount; - kbts_lookup_indices FeatureLookupIndices[KBTS_MAX_SIMULTANEOUS_FEATURES]; - kbts_u32 UnregisteredFeatureCount; - kbts_feature_tag UnregisteredFeatureTags[KBTS_MAX_SIMULTANEOUS_FEATURES]; - - kbts_op_state_op_specific OpSpecific; - - // Ops are free to use the following as they please: - // kbts_u8 LeftoverMemory[LeftoverMemorySize]; -} kbts_op_state; - -typedef struct kbts_op_list -{ - kbts_u8 *Ops; - kbts_un Length; -} kbts_op_list; - -typedef struct kbts_indic_script_properties -{ - kbts_u32 ViramaCodepoint; - kbts_u8 BlwfPostOnly; - kbts_reph_position RephPosition; - kbts_reph_encoding RephEncoding; - kbts_syllabic_position RightSideMatraPosition; - kbts_syllabic_position AboveBaseMatraPosition; - kbts_syllabic_position BelowBaseMatraPosition; -} kbts_indic_script_properties; - -typedef struct kbts_langsys kbts_langsys; -typedef struct kbts_shape_config -{ - kbts_font *Font; - kbts_script Script; - kbts_language Language; - kbts_langsys *Langsys[KBTS_SHAPING_TABLE_COUNT]; - kbts_op_list OpLists[4]; - - kbts_feature_set *Features; - - kbts_shaper Shaper; - kbts_shaper_properties *ShaperProperties; - - kbts_indic_script_properties IndicScriptProperties; - kbts_feature *Blwf; - kbts_feature *Pref; - kbts_feature *Pstf; - kbts_feature *Locl; - kbts_feature *Rphf; - kbts_feature *Half; - kbts_feature *Vatu; - - // Indic - kbts_glyph Virama; - - kbts_glyph DottedCircle; - kbts_glyph Whitespace; - - // Thai - kbts_glyph Nikhahit; - kbts_glyph SaraAa; -} kbts_shape_config; - -typedef struct kbts_shape_state -{ - kbts_op Op; - kbts_shape_config *Config; - kbts_direction MainDirection; - kbts_direction RunDirection; - - kbts_feature_set UserFeatures; - - kbts_glyph_array GlyphArray; - kbts_glyph_array ClusterGlyphArray; - - kbts_u32 DottedCircleInsertIndex; - - kbts_u32 GlyphCountStartingFromCurrentCluster; - - kbts_u32 At; - kbts_u32 ResumePoint; - kbts_u32 OpGlyphOffset; - kbts_u32 ClusterGlyphCount; - kbts_u32 Ip; - kbts_u32 NextGlyphUid; - - kbts_u32 RequiredGlyphCapacity; - - int RealCluster; - int ClusterAtStartOfWord; - int WordBreak; - - // This must always be the last member! - kbts_op_state OpState; -} kbts_shape_state; - -typedef struct kbts_cursor -{ - kbts_direction Direction; - kbts_s32 LastAdvanceX; - kbts_s32 X; - kbts_s32 Y; -} kbts_cursor; - 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. - kbts_u32 Position; + 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; -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, - KBTS_BREAK_STATE_FLAG_RAN_OUT_OF_REORDER_BUFFER_SPACE = 4, - - // 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, -}; - // 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. -#define KBTS_BREAK_REORDER_BUFFER_FLUSH_THRESHOLD 4 -#define KBTS_BREAK_REORDER_BUFFER_SIZE (KBTS_BREAK_REORDER_BUFFER_FLUSH_THRESHOLD * 2) typedef struct kbts_break_state { - kbts_break Breaks[KBTS_BREAK_REORDER_BUFFER_SIZE]; + kbts_break Breaks[8]; kbts_u32 BreakCount; - kbts_direction MainDirection; - kbts_u32 LastFlushedBreakPosition; + kbts_direction ParagraphDirection; + kbts_direction UserParagraphDirection; + kbts_u32 CurrentPosition; + kbts_u32 ParagraphStartPosition; - kbts_u8 LastScripts[2]; + 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; @@ -2813,8 +3376,11 @@ typedef struct kbts_break_state 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; @@ -2823,84 +3389,352 @@ typedef struct kbts_break_state typedef struct kbts_decode { - kbts_u32 Codepoint; + int Codepoint; - kbts_u32 SourceCharactersConsumed; - kbts_u32 Valid; + 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_FontFromFile(const char *FileName); -KBTS_EXPORT void kbts_FreeFont(kbts_font *Font); -KBTS_EXPORT kbts_shape_state *kbts_CreateShapeState(kbts_font *Font); -KBTS_EXPORT void kbts_FreeShapeState(kbts_shape_state *State); +KBTS_EXPORT kbts_font *kbts_ShapePushFontFromFile(kbts_shape_context *Context, const char *FileName, int FontIndex); #endif -KBTS_EXPORT kbts_feature_id kbts_FeatureTagToId(kbts_feature_tag Tag); -KBTS_EXPORT kbts_feature_override kbts_FeatureOverride(kbts_feature_id Id, int Alternate, kbts_u32 Value); -KBTS_EXPORT kbts_feature_override kbts_FeatureOverrideFromTag(kbts_feature_tag Tag, int Alternate, kbts_u32 Value); -KBTS_EXPORT kbts_glyph_config kbts_GlyphConfig(kbts_feature_override *FeatureOverrides, kbts_u32 FeatureOverrideCount); -KBTS_EXPORT kbts_glyph_config kbts_EmptyGlyphConfig(kbts_feature_override *FeatureOverrides, kbts_u32 FeatureOverrideCapacity); -KBTS_EXPORT int kbts_GlyphConfigOverrideFeature(kbts_glyph_config *Config, kbts_feature_id Id, int Alternate, kbts_u32 Value); -KBTS_EXPORT int kbts_GlyphConfigOverrideFeatureFromTag(kbts_glyph_config *Config, kbts_feature_tag Tag, int Alternate, kbts_u32 Value); +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_un kbts_ReadFontHeader(kbts_font *Font, void *Data, kbts_un Size); -KBTS_EXPORT kbts_un kbts_ReadFontData(kbts_font *Font, void *Scratch, kbts_un ScratchSize); -KBTS_EXPORT int kbts_PostReadFontInitialize(kbts_font *Font, void *Memory, kbts_un MemorySize); -KBTS_EXPORT kbts_un kbts_SizeOfShapeState(kbts_font *Font); -KBTS_EXPORT kbts_shape_state *kbts_PlaceShapeState(void *Address, kbts_un Size); -KBTS_EXPORT void kbts_ResetShapeState(kbts_shape_state *State); -KBTS_EXPORT kbts_shape_config kbts_ShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language); -KBTS_EXPORT int kbts_ShaperIsComplex(kbts_shaper Shaper); -KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, kbts_direction MainDirection, kbts_direction RunDirection, kbts_glyph *Glyphs, kbts_u32 *GlyphCount, kbts_u32 GlyphCapacity); -KBTS_EXPORT kbts_cursor kbts_Cursor(kbts_direction Direction); -KBTS_EXPORT void kbts_PositionGlyph(kbts_cursor *Cursor, kbts_glyph *Glyph, kbts_s32 *X, kbts_s32 *Y); -KBTS_EXPORT void kbts_BeginBreak(kbts_break_state *State, kbts_direction MainDirection, kbts_japanese_line_break_style JapaneseLineBreakStyle); -KBTS_EXPORT int kbts_BreakStateIsValid(kbts_break_state *State); -KBTS_EXPORT void kbts_BreakAddCodepoint(kbts_break_state *State, kbts_u32 Codepoint, kbts_u32 PositionIncrement, int EndOfText); -KBTS_EXPORT void kbts_BreakFlush(kbts_break_state *State); +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_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint); -KBTS_EXPORT void kbts_InferScript(kbts_direction *Direction, kbts_script *Script, kbts_script GlyphScript); +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 #ifdef KB_TEXT_SHAPE_IMPLEMENTATION #ifdef _MSC_VER -#define KBTS_UNUSED(X) (void)sizeof((X)) +#define KBTS__UNUSED(X) (void)sizeof((X)) #else -#define KBTS_UNUSED(X) (void)(X) +#define KBTS__UNUSED(X) (void)(X) #endif -# define KBTS_FOR(I, Start, End) for(kbts_un I = (Start); I < (End); ++I) +# define KBTS__FOR(I, Start, End) for(kbts_un I = (Start); I < (End); ++I) # ifdef __cplusplus -# define KBTS_ZERO {} -# define KBTS_ZERO_TYPE(T) T{} +# define KBTS__ZERO {} +# define KBTS__ZERO_TYPE(T) T{} # else -# define KBTS_ZERO {0} -# define KBTS_ZERO_TYPE(T) (T){0} +# define KBTS__ZERO {0} +# define KBTS__ZERO_TYPE(T) (T){0} # endif -# define KBTS_MAX(A, B) (((A) < (B)) ? (B) : (A)) -# define KBTS_MIN(A, B) (((A) < (B)) ? (A) : (B)) -# define KBTS_ARRAY_LENGTH(A) (sizeof(A)/sizeof(*(A))) -# define KBTS_POINTER_AFTER(Type, X) ((Type *)((X) + 1)) -# define KBTS_POINTER_OFFSET(Type, Base, Offset) ((Type *)((char *)(Base) + (Offset))) -# define KBTS_POINTER_DIFF32(Pointer, Base) ((kbts_u32)((char *)(Pointer) - (char *)(Base))) -# define KBTS_PASTE_1(A, B) A##B -# define KBTS_PASTE_0(A, B) KBTS_PASTE_1(A, B) -# define KBTS_PASTE(A, B) KBTS_PASTE_0(A, B) -# define KBTS_IN_SET(X, Set) ((1u << (X)) & (Set)) -# define KBTS_SET32_0(Arg) | (1u << (Arg)) KBTS_SET32_1 -# define KBTS_SET32_1(Arg) | (1u << (Arg)) KBTS_SET32_0 -# define KBTS_SET32_0End -# define KBTS_SET32_1End -# define KBTS_SET32(Args) (0u KBTS_PASTE(KBTS_SET32_0 Args, End)) -# define KBTS_IN_SET64(X, Set) ((1ull << (X)) & (Set)) -# define KBTS_SET64_0(Arg) | (1ull << (Arg)) KBTS_SET64_1 -# define KBTS_SET64_1(Arg) | (1ull << (Arg)) KBTS_SET64_0 -# define KBTS_SET64_0End -# define KBTS_SET64_1End -# define KBTS_SET64(Args) (0u KBTS_PASTE(KBTS_SET64_0 Args, End)) +# define KBTS__MAX(A, B) (((A) < (B)) ? (B) : (A)) +# define KBTS__MIN(A, B) (((A) < (B)) ? (A) : (B)) +# define KBTS__ARRAY_LENGTH(A) (sizeof(A)/sizeof(*(A))) +# define KBTS__POINTER_AFTER(Type, X) ((Type *)((X) + 1)) +# define KBTS__POINTER_OFFSET(Type, Base, Offset) ((Type *)((char *)(Base) + (Offset))) +# define KBTS__POINTER_DIFF32(Pointer, Base) ((kbts_u32)((char *)(Pointer) - (char *)(Base))) +# define KBTS__POINTER_DIFF(Pointer, Base) ((kbts_un)((char *)(Pointer) - (char *)(Base))) +# define KBTS__ALIGN_POINTER(Type, Pointer, Align) (Type *)(((kbts_un)(Pointer) + ((Align) - 1)) & ~((Align) - 1)) +# define KBTS__PASTE_1(A, B) A##B +# define KBTS__PASTE_0(A, B) KBTS__PASTE_1(A, B) +# define KBTS__PASTE(A, B) KBTS__PASTE_0(A, B) +# define KBTS__IN_SET(X, Set) ((1u << (X)) & (Set)) +# define KBTS__SET32_0(Arg) | (1u << (Arg)) KBTS__SET32_1 +# define KBTS__SET32_1(Arg) | (1u << (Arg)) KBTS__SET32_0 +# define KBTS__SET32_0End +# define KBTS__SET32_1End +# define KBTS__SET32(Args) (0u KBTS__PASTE(KBTS__SET32_0 Args, End)) +# define KBTS__IN_SET64(X, Set) ((1ull << (X)) & (Set)) +# define KBTS__SET64_0(Arg) | (1ull << (Arg)) KBTS__SET64_1 +# define KBTS__SET64_1(Arg) | (1ull << (Arg)) KBTS__SET64_0 +# define KBTS__SET64_0End +# define KBTS__SET64_1End +# define KBTS__SET64(Args) (0u KBTS__PASTE(KBTS__SET64_0 Args, End)) +#define KBTS__U32BE(X) kbts__ByteSwap32((X)) +#define KBTS__U32LE(X) (X) + +#define KBTS_LOOKUP_STACK_SIZE 64 # ifndef KBTS_ASSERT #ifndef KB_TEXT_SHAPE_NO_CRT @@ -2920,47 +3754,706 @@ KBTS_EXPORT kbts_script kbts_ScriptTagToScript(kbts_script_tag Tag); #define KBTS_MEMSET memset #endif -#ifndef kbts_ByteSwap16 +#ifndef KBTS_MEMCPY +#include +#define KBTS_MEMCPY memcpy +#endif + +#ifndef KB_TEXT_SHAPE_NO_CRT +#ifndef KBTS_MALLOC +#include +#define KBTS_MALLOC(Data, Size) malloc((Size)) +#define KBTS_FREE(Data, Pointer) free((Pointer)) +#endif +#else +#ifndef KBTS_MALLOC +// Nerf the default allocator to a null allocator. +#define KBTS_MALLOC(Data, Size) 0 +#define KBTS_FREE(Data, Pointer) +#endif +#endif + +#ifndef kbts__ByteSwap16 # if defined(_MSC_VER) && !defined(__clang__) -# define kbts_ByteSwap16(X) _byteswap_ushort(X) -# define kbts_ByteSwap32(X) _byteswap_ulong(X) -# define kbts_PopCount32(X) (kbts_un)__popcnt(X) +#include +KBTS_INLINE kbts_u16 kbts__ByteSwap16(kbts_u16 X) +{ + kbts_u16 Result = (kbts_u16)((X >> 8) | (X << 8)); + return Result; +} +KBTS_INLINE kbts_u32 kbts__ByteSwap32(kbts_u32 X) +{ + kbts_u32 Result = (X >> 24) | + ((X & 0xFF0000) >> 8) | + ((X & 0xFF00) << 8) | + ((X & 0xFF) << 24); + return Result; +} +# define kbts__PopCount32(X) (kbts_un)__popcnt(X) # elif defined(__clang__) || defined(__GNUC__) -# define kbts_ByteSwap16(X) __builtin_bswap16(X) -# define kbts_ByteSwap32(X) __builtin_bswap32(X) -# define kbts_PopCount32(X) (kbts_un)__builtin_popcount(X) +# define kbts__ByteSwap16(X) __builtin_bswap16(X) +# define kbts__ByteSwap32(X) __builtin_bswap32(X) +# define kbts__PopCount32(X) (kbts_un)__builtin_popcount(X) # else # error Unsupported compiler! # endif #endif -#define KBTS_FEATURE_FLAG0(Feature) (1ull << KBTS_FEATURE_ID_##Feature) -#define KBTS_FEATURE_FLAG1(Feature) (1ull << (KBTS_FEATURE_ID_##Feature - 64)) -#define KBTS_FEATURE_FLAG2(Feature) (1ull << (KBTS_FEATURE_ID_##Feature - 128)) -#define KBTS_FEATURE_FLAG3(Feature) (1ull << (KBTS_FEATURE_ID_##Feature - 192)) +#ifndef kbts__MsbPositionOrZero32 +# if defined(_MSC_VER) && !defined(__clang__) +#include +KBTS_INLINE kbts_u32 kbts__MsbPositionOrZero32(kbts_u32 X) +{ + unsigned long Result; + if(!_BitScanReverse(&Result, X)) + { + Result = 0; + } + return (kbts_u32)Result; +} +# elif defined(__clang__) || defined(__GNUC__) +KBTS_INLINE kbts_u32 kbts__MsbPositionOrZero32(kbts_u32 X) +{ + kbts_u32 Result = 0; + if(X) + { + Result = 31 - __builtin_clz(X); + } + return Result; +} +# else +# error Unsupported compiler! +# endif +#endif + +#define KBTS__FEATURE_FLAG0(Feature) (1ull << KBTS__FEATURE_ID_##Feature) +#define KBTS__FEATURE_FLAG1(Feature) (1ull << (KBTS__FEATURE_ID_##Feature - 64)) +#define KBTS__FEATURE_FLAG2(Feature) (1ull << (KBTS__FEATURE_ID_##Feature - 128)) +#define KBTS__FEATURE_FLAG3(Feature) (1ull << (KBTS__FEATURE_ID_##Feature - 192)) + +// #define KBTS_SANITY_CHECK +// #define KBTS_DUMP -// # define KBTS_DUMP # ifdef KBTS_DUMP # define KBTS_DUMPF(...) printf(__VA_ARGS__), fflush(stdout) # else # define KBTS_DUMPF(...) # endif -# define KBTS_GROW_BUFFER_MARGIN 16 - #ifndef KBTS_INSTRUMENT_BLOCK_BEGIN #define KBTS_INSTRUMENT_BLOCK_BEGIN(Name) +#define KBTS_INSTRUMENT_BLOCK_END(Name) #define KBTS_INSTRUMENT_FUNCTION_BEGIN -#define KBTS_INSTRUMENT_END +#define KBTS_INSTRUMENT_FUNCTION_END #endif -#define KBTS_MAXIMUM_DECOMPOSITION_CODEPOINTS 6 -typedef struct kbts_script_properties { +#define KBTS__GLYPH_FEATURE_MASK ((KBTS_GLYPH_FLAG_CFAR << 1) - 1) +// In USE, glyphs are mostly not pre-flagged for feature application. +// However, we do want to flag rphf/pref results for reordering, so we want to +// keep all of the flags as usual, and only use these feature flags for filtering. +#define KBTS__USE_GLYPH_FEATURE_MASK (KBTS_GLYPH_FLAG_ISOL | KBTS_GLYPH_FLAG_FINA | KBTS_GLYPH_FLAG_FIN2 | KBTS_GLYPH_FLAG_FIN3 | \ + KBTS_GLYPH_FLAG_MEDI | KBTS_GLYPH_FLAG_MED2 | KBTS_GLYPH_FLAG_INIT | KBTS_GLYPH_FLAG_NUMR | \ + KBTS_GLYPH_FLAG_DNOM | KBTS_GLYPH_FLAG_FRAC) +#define KBTS__JOINING_FEATURE_MASK (KBTS_GLYPH_FLAG_ISOL | KBTS_GLYPH_FLAG_FINA | KBTS_GLYPH_FLAG_FIN2 | KBTS_GLYPH_FLAG_FIN3 | \ + KBTS_GLYPH_FLAG_MEDI | KBTS_GLYPH_FLAG_MED2 | KBTS_GLYPH_FLAG_INIT) +#define KBTS__JOINING_FEATURE_TO_GLYPH_FLAG(Feature) (1 << ((Feature) - 1)) + +typedef kbts_u8 kbts__reph_position; +enum kbts__reph_position_enum +{ + KBTS__REPH_POSITION_AFTER_POST, + KBTS__REPH_POSITION_BEFORE_POST, + KBTS__REPH_POSITION_BEFORE_SUBJOINED, + KBTS__REPH_POSITION_AFTER_SUBJOINED, + KBTS__REPH_POSITION_AFTER_MAIN, + + KBTS__REPH_POSITION_COUNT, +}; + +typedef kbts_u8 kbts__reph_encoding; +enum kbts__reph_encoding_enum +{ + KBTS__REPH_ENCODING_IMPLICIT, + KBTS__REPH_ENCODING_EXPLICIT, + KBTS__REPH_ENCODING_LOGICAL_REPHA, + KBTS__REPH_ENCODING_VISUAL_REPHA, + + KBTS__REPH_ENCODING_COUNT, +}; + +typedef kbts_u8 kbts__syllabic_position; +enum kbts__syllabic_position_enum +{ + KBTS__SYLLABIC_POSITION_NONE, + + KBTS__SYLLABIC_POSITION_RA_TO_BECOME_REPH, + + KBTS__SYLLABIC_POSITION_PREBASE_MATRA, + KBTS__SYLLABIC_POSITION_PREBASE_CONSONANT, + + KBTS__SYLLABIC_POSITION_SYLLABLE_BASE, + KBTS__SYLLABIC_POSITION_AFTER_MAIN, + + KBTS__SYLLABIC_POSITION_ABOVEBASE_CONSONANT, + + KBTS__SYLLABIC_POSITION_BEFORE_SUBJOINED, + KBTS__SYLLABIC_POSITION_BELOWBASE_CONSONANT, + KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED, + + KBTS__SYLLABIC_POSITION_BEFORE_POST, + KBTS__SYLLABIC_POSITION_POSTBASE_CONSONANT, + KBTS__SYLLABIC_POSITION_AFTER_POST, + + KBTS__SYLLABIC_POSITION_FINAL_CONSONANT, + KBTS__SYLLABIC_POSITION_SMVD, + + KBTS__SYLLABIC_POSITION_COUNT, +}; + + +typedef kbts_u32 kbts__feature_id; +enum kbts__feature_id_enum +{ + KBTS__FEATURE_ID_UNREGISTERED, // Features that aren't pre-defined in the OpenType spec + KBTS__FEATURE_ID_isol, // Isolated Forms + KBTS__FEATURE_ID_fina, // Terminal Forms + KBTS__FEATURE_ID_fin2, // Terminal Forms #2 + KBTS__FEATURE_ID_fin3, // Terminal Forms #3 + KBTS__FEATURE_ID_medi, // Medial Forms + KBTS__FEATURE_ID_med2, // Medial Forms #2 + KBTS__FEATURE_ID_init, // Initial Forms + KBTS__FEATURE_ID_ljmo, // Leading Jamo Forms + KBTS__FEATURE_ID_vjmo, // Vowel Jamo Forms + KBTS__FEATURE_ID_tjmo, // Trailing Jamo Forms + KBTS__FEATURE_ID_rphf, // Reph Form + KBTS__FEATURE_ID_blwf, // Below-base Forms + KBTS__FEATURE_ID_half, // Half Forms + KBTS__FEATURE_ID_pstf, // Post-base Forms + KBTS__FEATURE_ID_abvf, // Above-base Forms + KBTS__FEATURE_ID_pref, // Pre-base Forms + KBTS__FEATURE_ID_numr, // Numerators + KBTS__FEATURE_ID_frac, // Fractions + KBTS__FEATURE_ID_dnom, // Denominators + KBTS__FEATURE_ID_cfar, // Conjunct Form After Ro + KBTS__FEATURE_ID_aalt, // Access All Alternates + KBTS__FEATURE_ID_abvm, // Above-base Mark Positioning + KBTS__FEATURE_ID_abvs, // Above-base Substitutions + KBTS__FEATURE_ID_afrc, // Alternative Fractions + KBTS__FEATURE_ID_akhn, // Akhand + KBTS__FEATURE_ID_apkn, // Kerning for Alternate Proportional Widths + KBTS__FEATURE_ID_blwm, // Below-base Mark Positioning + KBTS__FEATURE_ID_blws, // Below-base Substitutions + KBTS__FEATURE_ID_calt, // Contextual Alternates + KBTS__FEATURE_ID_case, // Case-sensitive Forms + KBTS__FEATURE_ID_ccmp, // Glyph Composition / Decomposition + KBTS__FEATURE_ID_chws, // Contextual Half-width Spacing + KBTS__FEATURE_ID_cjct, // Conjunct Forms + KBTS__FEATURE_ID_clig, // Contextual Ligatures + KBTS__FEATURE_ID_cpct, // Centered CJK Punctuation + KBTS__FEATURE_ID_cpsp, // Capital Spacing + KBTS__FEATURE_ID_cswh, // Contextual Swash + KBTS__FEATURE_ID_curs, // Cursive Positioning + KBTS__FEATURE_ID_cv01, // Character Variant 1 + KBTS__FEATURE_ID_cv02, // Character Variant 2 + KBTS__FEATURE_ID_cv03, // Character Variant 3 + KBTS__FEATURE_ID_cv04, // Character Variant 4 + KBTS__FEATURE_ID_cv05, // Character Variant 5 + KBTS__FEATURE_ID_cv06, // Character Variant 6 + KBTS__FEATURE_ID_cv07, // Character Variant 7 + KBTS__FEATURE_ID_cv08, // Character Variant 8 + KBTS__FEATURE_ID_cv09, // Character Variant 9 + KBTS__FEATURE_ID_cv10, // Character Variant 10 + KBTS__FEATURE_ID_cv11, // Character Variant 11 + KBTS__FEATURE_ID_cv12, // Character Variant 12 + KBTS__FEATURE_ID_cv13, // Character Variant 13 + KBTS__FEATURE_ID_cv14, // Character Variant 14 + KBTS__FEATURE_ID_cv15, // Character Variant 15 + KBTS__FEATURE_ID_cv16, // Character Variant 16 + KBTS__FEATURE_ID_cv17, // Character Variant 17 + KBTS__FEATURE_ID_cv18, // Character Variant 18 + KBTS__FEATURE_ID_cv19, // Character Variant 19 + KBTS__FEATURE_ID_cv20, // Character Variant 20 + KBTS__FEATURE_ID_cv21, // Character Variant 21 + KBTS__FEATURE_ID_cv22, // Character Variant 22 + KBTS__FEATURE_ID_cv23, // Character Variant 23 + KBTS__FEATURE_ID_cv24, // Character Variant 24 + KBTS__FEATURE_ID_cv25, // Character Variant 25 + KBTS__FEATURE_ID_cv26, // Character Variant 26 + KBTS__FEATURE_ID_cv27, // Character Variant 27 + KBTS__FEATURE_ID_cv28, // Character Variant 28 + KBTS__FEATURE_ID_cv29, // Character Variant 29 + KBTS__FEATURE_ID_cv30, // Character Variant 30 + KBTS__FEATURE_ID_cv31, // Character Variant 31 + KBTS__FEATURE_ID_cv32, // Character Variant 32 + KBTS__FEATURE_ID_cv33, // Character Variant 33 + KBTS__FEATURE_ID_cv34, // Character Variant 34 + KBTS__FEATURE_ID_cv35, // Character Variant 35 + KBTS__FEATURE_ID_cv36, // Character Variant 36 + KBTS__FEATURE_ID_cv37, // Character Variant 37 + KBTS__FEATURE_ID_cv38, // Character Variant 38 + KBTS__FEATURE_ID_cv39, // Character Variant 39 + KBTS__FEATURE_ID_cv40, // Character Variant 40 + KBTS__FEATURE_ID_cv41, // Character Variant 41 + KBTS__FEATURE_ID_cv42, // Character Variant 42 + KBTS__FEATURE_ID_cv43, // Character Variant 43 + KBTS__FEATURE_ID_cv44, // Character Variant 44 + KBTS__FEATURE_ID_cv45, // Character Variant 45 + KBTS__FEATURE_ID_cv46, // Character Variant 46 + KBTS__FEATURE_ID_cv47, // Character Variant 47 + KBTS__FEATURE_ID_cv48, // Character Variant 48 + KBTS__FEATURE_ID_cv49, // Character Variant 49 + KBTS__FEATURE_ID_cv50, // Character Variant 50 + KBTS__FEATURE_ID_cv51, // Character Variant 51 + KBTS__FEATURE_ID_cv52, // Character Variant 52 + KBTS__FEATURE_ID_cv53, // Character Variant 53 + KBTS__FEATURE_ID_cv54, // Character Variant 54 + KBTS__FEATURE_ID_cv55, // Character Variant 55 + KBTS__FEATURE_ID_cv56, // Character Variant 56 + KBTS__FEATURE_ID_cv57, // Character Variant 57 + KBTS__FEATURE_ID_cv58, // Character Variant 58 + KBTS__FEATURE_ID_cv59, // Character Variant 59 + KBTS__FEATURE_ID_cv60, // Character Variant 60 + KBTS__FEATURE_ID_cv61, // Character Variant 61 + KBTS__FEATURE_ID_cv62, // Character Variant 62 + KBTS__FEATURE_ID_cv63, // Character Variant 63 + KBTS__FEATURE_ID_cv64, // Character Variant 64 + KBTS__FEATURE_ID_cv65, // Character Variant 65 + KBTS__FEATURE_ID_cv66, // Character Variant 66 + KBTS__FEATURE_ID_cv67, // Character Variant 67 + KBTS__FEATURE_ID_cv68, // Character Variant 68 + KBTS__FEATURE_ID_cv69, // Character Variant 69 + KBTS__FEATURE_ID_cv70, // Character Variant 70 + KBTS__FEATURE_ID_cv71, // Character Variant 71 + KBTS__FEATURE_ID_cv72, // Character Variant 72 + KBTS__FEATURE_ID_cv73, // Character Variant 73 + KBTS__FEATURE_ID_cv74, // Character Variant 74 + KBTS__FEATURE_ID_cv75, // Character Variant 75 + KBTS__FEATURE_ID_cv76, // Character Variant 76 + KBTS__FEATURE_ID_cv77, // Character Variant 77 + KBTS__FEATURE_ID_cv78, // Character Variant 78 + KBTS__FEATURE_ID_cv79, // Character Variant 79 + KBTS__FEATURE_ID_cv80, // Character Variant 80 + KBTS__FEATURE_ID_cv81, // Character Variant 81 + KBTS__FEATURE_ID_cv82, // Character Variant 82 + KBTS__FEATURE_ID_cv83, // Character Variant 83 + KBTS__FEATURE_ID_cv84, // Character Variant 84 + KBTS__FEATURE_ID_cv85, // Character Variant 85 + KBTS__FEATURE_ID_cv86, // Character Variant 86 + KBTS__FEATURE_ID_cv87, // Character Variant 87 + KBTS__FEATURE_ID_cv88, // Character Variant 88 + KBTS__FEATURE_ID_cv89, // Character Variant 89 + KBTS__FEATURE_ID_cv90, // Character Variant 90 + KBTS__FEATURE_ID_cv91, // Character Variant 91 + KBTS__FEATURE_ID_cv92, // Character Variant 92 + KBTS__FEATURE_ID_cv93, // Character Variant 93 + KBTS__FEATURE_ID_cv94, // Character Variant 94 + KBTS__FEATURE_ID_cv95, // Character Variant 95 + KBTS__FEATURE_ID_cv96, // Character Variant 96 + KBTS__FEATURE_ID_cv97, // Character Variant 97 + KBTS__FEATURE_ID_cv98, // Character Variant 98 + KBTS__FEATURE_ID_cv99, // Character Variant 99 + KBTS__FEATURE_ID_c2pc, // Petite Capitals From Capitals + KBTS__FEATURE_ID_c2sc, // Small Capitals From Capitals + KBTS__FEATURE_ID_dist, // Distances + KBTS__FEATURE_ID_dlig, // Discretionary Ligatures + KBTS__FEATURE_ID_dtls, // Dotless Forms + KBTS__FEATURE_ID_expt, // Expert Forms + KBTS__FEATURE_ID_falt, // Final Glyph on Line Alternates + KBTS__FEATURE_ID_flac, // Flattened Accent Forms + KBTS__FEATURE_ID_fwid, // Full Widths + KBTS__FEATURE_ID_haln, // Halant Forms + KBTS__FEATURE_ID_halt, // Alternate Half Widths + KBTS__FEATURE_ID_hist, // Historical Forms + KBTS__FEATURE_ID_hkna, // Horizontal Kana Alternates + KBTS__FEATURE_ID_hlig, // Historical Ligatures + KBTS__FEATURE_ID_hngl, // Hangul + KBTS__FEATURE_ID_hojo, // Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) + KBTS__FEATURE_ID_hwid, // Half Widths + KBTS__FEATURE_ID_ital, // Italics + KBTS__FEATURE_ID_jalt, // Justification Alternates + KBTS__FEATURE_ID_jp78, // JIS78 Forms + KBTS__FEATURE_ID_jp83, // JIS83 Forms + KBTS__FEATURE_ID_jp90, // JIS90 Forms + KBTS__FEATURE_ID_jp04, // JIS2004 Forms + KBTS__FEATURE_ID_kern, // Kerning + KBTS__FEATURE_ID_lfbd, // Left Bounds + KBTS__FEATURE_ID_liga, // Standard Ligatures + KBTS__FEATURE_ID_lnum, // Lining Figures + KBTS__FEATURE_ID_locl, // Localized Forms + KBTS__FEATURE_ID_ltra, // Left-to-right Alternates + KBTS__FEATURE_ID_ltrm, // Left-to-right Mirrored Forms + KBTS__FEATURE_ID_mark, // Mark Positioning + KBTS__FEATURE_ID_mgrk, // Mathematical Greek + KBTS__FEATURE_ID_mkmk, // Mark to Mark Positioning + KBTS__FEATURE_ID_mset, // Mark Positioning via Substitution + KBTS__FEATURE_ID_nalt, // Alternate Annotation Forms + KBTS__FEATURE_ID_nlck, // NLC Kanji Forms + KBTS__FEATURE_ID_nukt, // Nukta Forms + KBTS__FEATURE_ID_onum, // Oldstyle Figures + KBTS__FEATURE_ID_opbd, // Optical Bounds + KBTS__FEATURE_ID_ordn, // Ordinals + KBTS__FEATURE_ID_ornm, // Ornaments + KBTS__FEATURE_ID_palt, // Proportional Alternate Widths + KBTS__FEATURE_ID_pcap, // Petite Capitals + KBTS__FEATURE_ID_pkna, // Proportional Kana + KBTS__FEATURE_ID_pnum, // Proportional Figures + KBTS__FEATURE_ID_pres, // Pre-base Substitutions + KBTS__FEATURE_ID_psts, // Post-base Substitutions + KBTS__FEATURE_ID_pwid, // Proportional Widths + KBTS__FEATURE_ID_qwid, // Quarter Widths + KBTS__FEATURE_ID_rand, // Randomize + KBTS__FEATURE_ID_rclt, // Required Contextual Alternates + KBTS__FEATURE_ID_rkrf, // Rakar Forms + KBTS__FEATURE_ID_rlig, // Required Ligatures + KBTS__FEATURE_ID_rtbd, // Right Bounds + KBTS__FEATURE_ID_rtla, // Right-to-left Alternates + KBTS__FEATURE_ID_rtlm, // Right-to-left Mirrored Forms + KBTS__FEATURE_ID_ruby, // Ruby Notation Forms + KBTS__FEATURE_ID_rvrn, // Required Variation Alternates + KBTS__FEATURE_ID_salt, // Stylistic Alternates + KBTS__FEATURE_ID_sinf, // Scientific Inferiors + KBTS__FEATURE_ID_size, // Optical size + KBTS__FEATURE_ID_smcp, // Small Capitals + KBTS__FEATURE_ID_smpl, // Simplified Forms + KBTS__FEATURE_ID_ss01, // Stylistic Set 1 + KBTS__FEATURE_ID_ss02, // Stylistic Set 2 + KBTS__FEATURE_ID_ss03, // Stylistic Set 3 + KBTS__FEATURE_ID_ss04, // Stylistic Set 4 + KBTS__FEATURE_ID_ss05, // Stylistic Set 5 + KBTS__FEATURE_ID_ss06, // Stylistic Set 6 + KBTS__FEATURE_ID_ss07, // Stylistic Set 7 + KBTS__FEATURE_ID_ss08, // Stylistic Set 8 + KBTS__FEATURE_ID_ss09, // Stylistic Set 9 + KBTS__FEATURE_ID_ss10, // Stylistic Set 10 + KBTS__FEATURE_ID_ss11, // Stylistic Set 11 + KBTS__FEATURE_ID_ss12, // Stylistic Set 12 + KBTS__FEATURE_ID_ss13, // Stylistic Set 13 + KBTS__FEATURE_ID_ss14, // Stylistic Set 14 + KBTS__FEATURE_ID_ss15, // Stylistic Set 15 + KBTS__FEATURE_ID_ss16, // Stylistic Set 16 + KBTS__FEATURE_ID_ss17, // Stylistic Set 17 + KBTS__FEATURE_ID_ss18, // Stylistic Set 18 + KBTS__FEATURE_ID_ss19, // Stylistic Set 19 + KBTS__FEATURE_ID_ss20, // Stylistic Set 20 + KBTS__FEATURE_ID_ssty, // Math Script-style Alternates + KBTS__FEATURE_ID_stch, // Stretching Glyph Decomposition + KBTS__FEATURE_ID_subs, // Subscript + KBTS__FEATURE_ID_sups, // Superscript + KBTS__FEATURE_ID_swsh, // Swash + KBTS__FEATURE_ID_test, // Test features, only for development + KBTS__FEATURE_ID_titl, // Titling + KBTS__FEATURE_ID_tnam, // Traditional Name Forms + KBTS__FEATURE_ID_tnum, // Tabular Figures + KBTS__FEATURE_ID_trad, // Traditional Forms + KBTS__FEATURE_ID_twid, // Third Widths + KBTS__FEATURE_ID_unic, // Unicase + KBTS__FEATURE_ID_valt, // Alternate Vertical Metrics + KBTS__FEATURE_ID_vapk, // Kerning for Alternate Proportional Vertical Metrics + KBTS__FEATURE_ID_vatu, // Vattu Variants + KBTS__FEATURE_ID_vchw, // Vertical Contextual Half-width Spacing + KBTS__FEATURE_ID_vert, // Vertical Alternates + KBTS__FEATURE_ID_vhal, // Alternate Vertical Half Metrics + KBTS__FEATURE_ID_vkna, // Vertical Kana Alternates + KBTS__FEATURE_ID_vkrn, // Vertical Kerning + KBTS__FEATURE_ID_vpal, // Proportional Alternate Vertical Metrics + KBTS__FEATURE_ID_vrt2, // Vertical Alternates and Rotation + KBTS__FEATURE_ID_vrtr, // Vertical Alternates for Rotation + KBTS__FEATURE_ID_zero, // Slashed Zero + KBTS__FEATURE_ID_COUNT, +}; + +typedef struct kbts__feature_set +{ + kbts_u64 Flags[(KBTS__FEATURE_ID_COUNT + 63) / 64]; +} kbts__feature_set; +typedef kbts_u8 kbts__op_kind; +enum kbts__op_kind_enum +{ + KBTS__OP_KIND_END, + KBTS__OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_NORMALIZE_HANGUL, + KBTS__OP_KIND_FLAG_JOINING_LETTERS, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_POST_GPOS_FIXUP, + KBTS__OP_KIND_STCH_POSTPASS, + KBTS__OP_KIND_BEGIN_CLUSTER, + KBTS__OP_KIND_END_CLUSTER, + KBTS__OP_KIND_END_SYLLABLE, + KBTS__OP_KIND_COUNT, +}; +typedef struct kbts__feature_stage +{ + kbts_u32 FeatureCount; + kbts__feature_set Features; +} kbts__feature_stage; +typedef struct kbts__op_list +{ + kbts_u32 TotalFeatureCount; + kbts_u32 FeatureStageCount; + kbts__feature_stage *FeatureStages; + kbts_u32 OpCount; + kbts__op_kind *Ops; +} kbts__op_list; +static kbts__op_kind kbts__Ops_Default[] = { + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_Default[] = { + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rvrn)}}}, + {12, {{0ull | KBTS__FEATURE_FLAG0(frac) | KBTS__FEATURE_FLAG0(numr) | KBTS__FEATURE_FLAG0(dnom) | KBTS__FEATURE_FLAG0(ccmp) | KBTS__FEATURE_FLAG0(clig) | KBTS__FEATURE_FLAG0(calt), 0ull, 0ull | KBTS__FEATURE_FLAG2(ltra) | KBTS__FEATURE_FLAG2(ltrm) | KBTS__FEATURE_FLAG2(locl) | KBTS__FEATURE_FLAG2(rlig) | KBTS__FEATURE_FLAG2(liga) | KBTS__FEATURE_FLAG2(rclt), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm) | KBTS__FEATURE_FLAG0(curs), 0ull, 0ull | KBTS__FEATURE_FLAG2(mark) | KBTS__FEATURE_FLAG2(mkmk) | KBTS__FEATURE_FLAG2(dist) | KBTS__FEATURE_FLAG2(kern), 0ull}}}, +}; +static kbts__op_list kbts__OpList_Default = {20, KBTS__ARRAY_LENGTH(kbts__FeatureStages_Default), kbts__FeatureStages_Default, KBTS__ARRAY_LENGTH(kbts__Ops_Default), kbts__Ops_Default}; +static kbts__op_kind kbts__Ops_Hangul[] = { + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_NORMALIZE_HANGUL, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_Hangul[] = { + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rvrn)}}}, + {14, {{0ull | KBTS__FEATURE_FLAG0(frac) | KBTS__FEATURE_FLAG0(numr) | KBTS__FEATURE_FLAG0(dnom) | KBTS__FEATURE_FLAG0(ljmo) | KBTS__FEATURE_FLAG0(vjmo) | KBTS__FEATURE_FLAG0(tjmo) | KBTS__FEATURE_FLAG0(ccmp) | KBTS__FEATURE_FLAG0(clig), 0ull, 0ull | KBTS__FEATURE_FLAG2(ltra) | KBTS__FEATURE_FLAG2(ltrm) | KBTS__FEATURE_FLAG2(rlig) | KBTS__FEATURE_FLAG2(liga) | KBTS__FEATURE_FLAG2(locl) | KBTS__FEATURE_FLAG2(rclt), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm) | KBTS__FEATURE_FLAG0(curs), 0ull, 0ull | KBTS__FEATURE_FLAG2(mkmk) | KBTS__FEATURE_FLAG2(mark) | KBTS__FEATURE_FLAG2(dist) | KBTS__FEATURE_FLAG2(kern), 0ull}}}, +}; +static kbts__op_list kbts__OpList_Hangul = {22, KBTS__ARRAY_LENGTH(kbts__FeatureStages_Hangul), kbts__FeatureStages_Hangul, KBTS__ARRAY_LENGTH(kbts__Ops_Hangul), kbts__Ops_Hangul}; +static kbts__op_kind kbts__Ops_ArabicRclt[] = { + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_FLAG_JOINING_LETTERS, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_STCH_POSTPASS, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_ArabicRclt[] = { + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rvrn)}}}, + {5, {{0ull | KBTS__FEATURE_FLAG0(frac) | KBTS__FEATURE_FLAG0(numr) | KBTS__FEATURE_FLAG0(dnom), 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rtla) | KBTS__FEATURE_FLAG3(rtlm)}}}, + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(stch)}}}, + {2, {{0ull | KBTS__FEATURE_FLAG0(ccmp), 0ull, 0ull | KBTS__FEATURE_FLAG2(locl), 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(isol), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(fina), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(fin2), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(fin3), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(medi), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(med2), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(init), 0ull, 0ull, 0ull}}}, + {1, {{0ull, 0ull, 0ull | KBTS__FEATURE_FLAG2(rlig), 0ull}}}, + {5, {{0ull | KBTS__FEATURE_FLAG0(calt) | KBTS__FEATURE_FLAG0(clig), 0ull, 0ull | KBTS__FEATURE_FLAG2(liga) | KBTS__FEATURE_FLAG2(mset) | KBTS__FEATURE_FLAG2(rclt), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm) | KBTS__FEATURE_FLAG0(curs), 0ull, 0ull | KBTS__FEATURE_FLAG2(mark) | KBTS__FEATURE_FLAG2(mkmk) | KBTS__FEATURE_FLAG2(dist) | KBTS__FEATURE_FLAG2(kern), 0ull}}}, +}; +static kbts__op_list kbts__OpList_ArabicRclt = {29, KBTS__ARRAY_LENGTH(kbts__FeatureStages_ArabicRclt), kbts__FeatureStages_ArabicRclt, KBTS__ARRAY_LENGTH(kbts__Ops_ArabicRclt), kbts__Ops_ArabicRclt}; +static kbts__op_kind kbts__Ops_ArabicNoRclt[] = { + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_FLAG_JOINING_LETTERS, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_STCH_POSTPASS, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_ArabicNoRclt[] = { + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rvrn)}}}, + {5, {{0ull | KBTS__FEATURE_FLAG0(frac) | KBTS__FEATURE_FLAG0(numr) | KBTS__FEATURE_FLAG0(dnom), 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rtla) | KBTS__FEATURE_FLAG3(rtlm)}}}, + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(stch)}}}, + {2, {{0ull | KBTS__FEATURE_FLAG0(ccmp), 0ull, 0ull | KBTS__FEATURE_FLAG2(locl), 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(isol), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(fina), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(fin2), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(fin3), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(medi), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(med2), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(init), 0ull, 0ull, 0ull}}}, + {1, {{0ull, 0ull, 0ull | KBTS__FEATURE_FLAG2(rlig), 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(calt), 0ull, 0ull, 0ull}}}, + {3, {{0ull | KBTS__FEATURE_FLAG0(clig), 0ull, 0ull | KBTS__FEATURE_FLAG2(liga) | KBTS__FEATURE_FLAG2(mset), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm) | KBTS__FEATURE_FLAG0(curs), 0ull, 0ull | KBTS__FEATURE_FLAG2(mark) | KBTS__FEATURE_FLAG2(mkmk) | KBTS__FEATURE_FLAG2(dist) | KBTS__FEATURE_FLAG2(kern), 0ull}}}, +}; +static kbts__op_list kbts__OpList_ArabicNoRclt = {28, KBTS__ARRAY_LENGTH(kbts__FeatureStages_ArabicNoRclt), kbts__FeatureStages_ArabicNoRclt, KBTS__ARRAY_LENGTH(kbts__Ops_ArabicNoRclt), kbts__Ops_ArabicNoRclt}; +static kbts__op_kind kbts__Ops_Indic[] = { + KBTS__OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_BEGIN_CLUSTER, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_END_CLUSTER, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_END_SYLLABLE, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_Indic[] = { + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rvrn)}}}, + {5, {{0ull | KBTS__FEATURE_FLAG0(frac) | KBTS__FEATURE_FLAG0(numr) | KBTS__FEATURE_FLAG0(dnom), 0ull, 0ull | KBTS__FEATURE_FLAG2(ltra) | KBTS__FEATURE_FLAG2(ltrm), 0ull}}}, + {1, {{0ull, 0ull, 0ull | KBTS__FEATURE_FLAG2(nukt), 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(akhn), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(rphf), 0ull, 0ull, 0ull}}}, + {1, {{0ull, 0ull, 0ull | KBTS__FEATURE_FLAG2(rkrf), 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(pref), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(blwf), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(abvf), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(half), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(pstf), 0ull, 0ull, 0ull}}}, + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(vatu)}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(cjct), 0ull, 0ull, 0ull}}}, + {6, {{0ull | KBTS__FEATURE_FLAG0(abvs) | KBTS__FEATURE_FLAG0(blws) | KBTS__FEATURE_FLAG0(init), 0ull, 0ull | KBTS__FEATURE_FLAG2(haln) | KBTS__FEATURE_FLAG2(pres) | KBTS__FEATURE_FLAG2(psts), 0ull}}}, + {5, {{0ull | KBTS__FEATURE_FLAG0(calt) | KBTS__FEATURE_FLAG0(clig), 0ull, 0ull | KBTS__FEATURE_FLAG2(locl) | KBTS__FEATURE_FLAG2(rlig) | KBTS__FEATURE_FLAG2(rclt), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm) | KBTS__FEATURE_FLAG0(curs), 0ull, 0ull | KBTS__FEATURE_FLAG2(mark) | KBTS__FEATURE_FLAG2(mkmk) | KBTS__FEATURE_FLAG2(dist) | KBTS__FEATURE_FLAG2(kern), 0ull}}}, +}; +static kbts__op_list kbts__OpList_Indic = {35, KBTS__ARRAY_LENGTH(kbts__FeatureStages_Indic), kbts__FeatureStages_Indic, KBTS__ARRAY_LENGTH(kbts__Ops_Indic), kbts__Ops_Indic}; +static kbts__op_kind kbts__Ops_Khmer[] = { + KBTS__OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_BEGIN_CLUSTER, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_END_CLUSTER, + KBTS__OP_KIND_END_SYLLABLE, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_Khmer[] = { + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rvrn)}}}, + {5, {{0ull | KBTS__FEATURE_FLAG0(frac) | KBTS__FEATURE_FLAG0(numr) | KBTS__FEATURE_FLAG0(dnom), 0ull, 0ull | KBTS__FEATURE_FLAG2(ltra) | KBTS__FEATURE_FLAG2(ltrm), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(ccmp) | KBTS__FEATURE_FLAG0(pref) | KBTS__FEATURE_FLAG0(blwf) | KBTS__FEATURE_FLAG0(abvf) | KBTS__FEATURE_FLAG0(pstf) | KBTS__FEATURE_FLAG0(cfar), 0ull, 0ull | KBTS__FEATURE_FLAG2(locl), 0ull}}}, + {8, {{0ull | KBTS__FEATURE_FLAG0(abvs) | KBTS__FEATURE_FLAG0(blws) | KBTS__FEATURE_FLAG0(calt) | KBTS__FEATURE_FLAG0(clig), 0ull, 0ull | KBTS__FEATURE_FLAG2(pres) | KBTS__FEATURE_FLAG2(psts) | KBTS__FEATURE_FLAG2(rclt) | KBTS__FEATURE_FLAG2(rlig), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm) | KBTS__FEATURE_FLAG0(curs), 0ull, 0ull | KBTS__FEATURE_FLAG2(dist) | KBTS__FEATURE_FLAG2(kern) | KBTS__FEATURE_FLAG2(mark) | KBTS__FEATURE_FLAG2(mkmk), 0ull}}}, +}; +static kbts__op_list kbts__OpList_Khmer = {28, KBTS__ARRAY_LENGTH(kbts__FeatureStages_Khmer), kbts__FeatureStages_Khmer, KBTS__ARRAY_LENGTH(kbts__Ops_Khmer), kbts__Ops_Khmer}; +static kbts__op_kind kbts__Ops_Myanmar[] = { + KBTS__OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_BEGIN_CLUSTER, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_END_CLUSTER, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_END_SYLLABLE, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_Myanmar[] = { + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rvrn)}}}, + {5, {{0ull | KBTS__FEATURE_FLAG0(frac) | KBTS__FEATURE_FLAG0(numr) | KBTS__FEATURE_FLAG0(dnom), 0ull, 0ull | KBTS__FEATURE_FLAG2(ltra) | KBTS__FEATURE_FLAG2(ltrm), 0ull}}}, + {2, {{0ull | KBTS__FEATURE_FLAG0(ccmp), 0ull, 0ull | KBTS__FEATURE_FLAG2(locl), 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(rphf), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(pref), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(blwf), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(pstf), 0ull, 0ull, 0ull}}}, + {9, {{0ull | KBTS__FEATURE_FLAG0(abvs) | KBTS__FEATURE_FLAG0(blws) | KBTS__FEATURE_FLAG0(calt) | KBTS__FEATURE_FLAG0(clig), 0ull, 0ull | KBTS__FEATURE_FLAG2(pres) | KBTS__FEATURE_FLAG2(psts) | KBTS__FEATURE_FLAG2(rlig) | KBTS__FEATURE_FLAG2(liga) | KBTS__FEATURE_FLAG2(rclt), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm) | KBTS__FEATURE_FLAG0(curs), 0ull, 0ull | KBTS__FEATURE_FLAG2(dist) | KBTS__FEATURE_FLAG2(kern) | KBTS__FEATURE_FLAG2(mark) | KBTS__FEATURE_FLAG2(mkmk), 0ull}}}, +}; +static kbts__op_list kbts__OpList_Myanmar = {28, KBTS__ARRAY_LENGTH(kbts__FeatureStages_Myanmar), kbts__FeatureStages_Myanmar, KBTS__ARRAY_LENGTH(kbts__Ops_Myanmar), kbts__Ops_Myanmar}; +static kbts__op_kind kbts__Ops_Tibetan[] = { + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_Tibetan[] = { + {1, {{0ull, 0ull, 0ull | KBTS__FEATURE_FLAG2(locl), 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(ccmp), 0ull, 0ull, 0ull}}}, + {4, {{0ull | KBTS__FEATURE_FLAG0(abvs) | KBTS__FEATURE_FLAG0(blws) | KBTS__FEATURE_FLAG0(calt), 0ull, 0ull | KBTS__FEATURE_FLAG2(liga), 0ull}}}, + {4, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm), 0ull, 0ull | KBTS__FEATURE_FLAG2(kern) | KBTS__FEATURE_FLAG2(mkmk), 0ull}}}, +}; +static kbts__op_list kbts__OpList_Tibetan = {10, KBTS__ARRAY_LENGTH(kbts__FeatureStages_Tibetan), kbts__FeatureStages_Tibetan, KBTS__ARRAY_LENGTH(kbts__Ops_Tibetan), kbts__Ops_Tibetan}; +static kbts__op_kind kbts__Ops_Use[] = { + KBTS__OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, + KBTS__OP_KIND_NORMALIZE, + KBTS__OP_KIND_FLAG_JOINING_LETTERS, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_BEGIN_CLUSTER, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_END_CLUSTER, + KBTS__OP_KIND_END_SYLLABLE, + KBTS__OP_KIND_GSUB_FEATURES, + KBTS__OP_KIND_GSUB_FEATURES_WITH_USER, + KBTS__OP_KIND_GPOS_METRICS, + KBTS__OP_KIND_GPOS_FEATURES, + KBTS__OP_KIND_POST_GPOS_FIXUP, +}; +static kbts__feature_stage kbts__FeatureStages_Use[] = { + {1, {{0ull, 0ull, 0ull, 0ull | KBTS__FEATURE_FLAG3(rvrn)}}}, + {5, {{0ull | KBTS__FEATURE_FLAG0(frac) | KBTS__FEATURE_FLAG0(numr) | KBTS__FEATURE_FLAG0(dnom), 0ull, 0ull | KBTS__FEATURE_FLAG2(ltra) | KBTS__FEATURE_FLAG2(ltrm), 0ull}}}, + {4, {{0ull | KBTS__FEATURE_FLAG0(ccmp) | KBTS__FEATURE_FLAG0(akhn), 0ull, 0ull | KBTS__FEATURE_FLAG2(locl) | KBTS__FEATURE_FLAG2(nukt), 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(rphf), 0ull, 0ull, 0ull}}}, + {1, {{0ull | KBTS__FEATURE_FLAG0(pref), 0ull, 0ull, 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvf) | KBTS__FEATURE_FLAG0(blwf) | KBTS__FEATURE_FLAG0(cjct) | KBTS__FEATURE_FLAG0(half) | KBTS__FEATURE_FLAG0(pstf), 0ull, 0ull | KBTS__FEATURE_FLAG2(rkrf), 0ull | KBTS__FEATURE_FLAG3(vatu)}}}, + {4, {{0ull | KBTS__FEATURE_FLAG0(fina) | KBTS__FEATURE_FLAG0(init) | KBTS__FEATURE_FLAG0(isol) | KBTS__FEATURE_FLAG0(medi), 0ull, 0ull, 0ull}}}, + {10, {{0ull | KBTS__FEATURE_FLAG0(abvs) | KBTS__FEATURE_FLAG0(blws) | KBTS__FEATURE_FLAG0(calt) | KBTS__FEATURE_FLAG0(clig), 0ull, 0ull | KBTS__FEATURE_FLAG2(haln) | KBTS__FEATURE_FLAG2(pres) | KBTS__FEATURE_FLAG2(psts) | KBTS__FEATURE_FLAG2(liga) | KBTS__FEATURE_FLAG2(rclt) | KBTS__FEATURE_FLAG2(rlig), 0ull}}}, + {7, {{0ull | KBTS__FEATURE_FLAG0(abvm) | KBTS__FEATURE_FLAG0(blwm) | KBTS__FEATURE_FLAG0(curs), 0ull, 0ull | KBTS__FEATURE_FLAG2(dist) | KBTS__FEATURE_FLAG2(kern) | KBTS__FEATURE_FLAG2(mark) | KBTS__FEATURE_FLAG2(mkmk), 0ull}}}, +}; +static kbts__op_list kbts__OpList_Use = {40, KBTS__ARRAY_LENGTH(kbts__FeatureStages_Use), kbts__FeatureStages_Use, KBTS__ARRAY_LENGTH(kbts__Ops_Use), kbts__Ops_Use}; +#define KBTS__MAXIMUM_DECOMPOSITION_CODEPOINTS 6 +typedef struct kbts__script_properties { kbts_u32 Tag; kbts_shaper Shaper; -} kbts_script_properties; +} kbts__script_properties; -static kbts_script_properties kbts_ScriptProperties[KBTS_SCRIPT_COUNT] = { +static kbts__script_properties kbts__ScriptProperties[KBTS_SCRIPT_COUNT] = { {KBTS_FOURCC(' ', ' ', ' ', ' '), KBTS_SHAPER_DEFAULT}, {KBTS_FOURCC('a', 'd', 'l', 'm'), KBTS_SHAPER_USE}, {KBTS_FOURCC('a', 'h', 'o', 'm'), KBTS_SHAPER_USE}, @@ -3311,261 +4804,261 @@ KBTS_EXPORT kbts_script kbts_ScriptTagToScript(kbts_script_tag Tag) return Result; } -KBTS_EXPORT kbts_feature_id kbts_FeatureTagToId(kbts_feature_tag Tag) +static kbts__feature_id kbts__FeatureTagToId(kbts_feature_tag Tag) { - kbts_feature_id Result = 0; + kbts__feature_id Result = 0; switch(Tag) { - case KBTS_FEATURE_TAG_isol: Result = KBTS_FEATURE_ID_isol; break; - case KBTS_FEATURE_TAG_fina: Result = KBTS_FEATURE_ID_fina; break; - case KBTS_FEATURE_TAG_fin2: Result = KBTS_FEATURE_ID_fin2; break; - case KBTS_FEATURE_TAG_fin3: Result = KBTS_FEATURE_ID_fin3; break; - case KBTS_FEATURE_TAG_medi: Result = KBTS_FEATURE_ID_medi; break; - case KBTS_FEATURE_TAG_med2: Result = KBTS_FEATURE_ID_med2; break; - case KBTS_FEATURE_TAG_init: Result = KBTS_FEATURE_ID_init; break; - case KBTS_FEATURE_TAG_ljmo: Result = KBTS_FEATURE_ID_ljmo; break; - case KBTS_FEATURE_TAG_vjmo: Result = KBTS_FEATURE_ID_vjmo; break; - case KBTS_FEATURE_TAG_tjmo: Result = KBTS_FEATURE_ID_tjmo; break; - case KBTS_FEATURE_TAG_rphf: Result = KBTS_FEATURE_ID_rphf; break; - case KBTS_FEATURE_TAG_blwf: Result = KBTS_FEATURE_ID_blwf; break; - case KBTS_FEATURE_TAG_half: Result = KBTS_FEATURE_ID_half; break; - case KBTS_FEATURE_TAG_pstf: Result = KBTS_FEATURE_ID_pstf; break; - case KBTS_FEATURE_TAG_abvf: Result = KBTS_FEATURE_ID_abvf; break; - case KBTS_FEATURE_TAG_pref: Result = KBTS_FEATURE_ID_pref; break; - case KBTS_FEATURE_TAG_numr: Result = KBTS_FEATURE_ID_numr; break; - case KBTS_FEATURE_TAG_frac: Result = KBTS_FEATURE_ID_frac; break; - case KBTS_FEATURE_TAG_dnom: Result = KBTS_FEATURE_ID_dnom; break; - case KBTS_FEATURE_TAG_cfar: Result = KBTS_FEATURE_ID_cfar; break; - case KBTS_FEATURE_TAG_aalt: Result = KBTS_FEATURE_ID_aalt; break; - case KBTS_FEATURE_TAG_abvm: Result = KBTS_FEATURE_ID_abvm; break; - case KBTS_FEATURE_TAG_abvs: Result = KBTS_FEATURE_ID_abvs; break; - case KBTS_FEATURE_TAG_afrc: Result = KBTS_FEATURE_ID_afrc; break; - case KBTS_FEATURE_TAG_akhn: Result = KBTS_FEATURE_ID_akhn; break; - case KBTS_FEATURE_TAG_apkn: Result = KBTS_FEATURE_ID_apkn; break; - case KBTS_FEATURE_TAG_blwm: Result = KBTS_FEATURE_ID_blwm; break; - case KBTS_FEATURE_TAG_blws: Result = KBTS_FEATURE_ID_blws; break; - case KBTS_FEATURE_TAG_calt: Result = KBTS_FEATURE_ID_calt; break; - case KBTS_FEATURE_TAG_case: Result = KBTS_FEATURE_ID_case; break; - case KBTS_FEATURE_TAG_ccmp: Result = KBTS_FEATURE_ID_ccmp; break; - case KBTS_FEATURE_TAG_chws: Result = KBTS_FEATURE_ID_chws; break; - case KBTS_FEATURE_TAG_cjct: Result = KBTS_FEATURE_ID_cjct; break; - case KBTS_FEATURE_TAG_clig: Result = KBTS_FEATURE_ID_clig; break; - case KBTS_FEATURE_TAG_cpct: Result = KBTS_FEATURE_ID_cpct; break; - case KBTS_FEATURE_TAG_cpsp: Result = KBTS_FEATURE_ID_cpsp; break; - case KBTS_FEATURE_TAG_cswh: Result = KBTS_FEATURE_ID_cswh; break; - case KBTS_FEATURE_TAG_curs: Result = KBTS_FEATURE_ID_curs; break; - case KBTS_FEATURE_TAG_cv01: Result = KBTS_FEATURE_ID_cv01; break; - case KBTS_FEATURE_TAG_cv02: Result = KBTS_FEATURE_ID_cv02; break; - case KBTS_FEATURE_TAG_cv03: Result = KBTS_FEATURE_ID_cv03; break; - case KBTS_FEATURE_TAG_cv04: Result = KBTS_FEATURE_ID_cv04; break; - case KBTS_FEATURE_TAG_cv05: Result = KBTS_FEATURE_ID_cv05; break; - case KBTS_FEATURE_TAG_cv06: Result = KBTS_FEATURE_ID_cv06; break; - case KBTS_FEATURE_TAG_cv07: Result = KBTS_FEATURE_ID_cv07; break; - case KBTS_FEATURE_TAG_cv08: Result = KBTS_FEATURE_ID_cv08; break; - case KBTS_FEATURE_TAG_cv09: Result = KBTS_FEATURE_ID_cv09; break; - case KBTS_FEATURE_TAG_cv10: Result = KBTS_FEATURE_ID_cv10; break; - case KBTS_FEATURE_TAG_cv11: Result = KBTS_FEATURE_ID_cv11; break; - case KBTS_FEATURE_TAG_cv12: Result = KBTS_FEATURE_ID_cv12; break; - case KBTS_FEATURE_TAG_cv13: Result = KBTS_FEATURE_ID_cv13; break; - case KBTS_FEATURE_TAG_cv14: Result = KBTS_FEATURE_ID_cv14; break; - case KBTS_FEATURE_TAG_cv15: Result = KBTS_FEATURE_ID_cv15; break; - case KBTS_FEATURE_TAG_cv16: Result = KBTS_FEATURE_ID_cv16; break; - case KBTS_FEATURE_TAG_cv17: Result = KBTS_FEATURE_ID_cv17; break; - case KBTS_FEATURE_TAG_cv18: Result = KBTS_FEATURE_ID_cv18; break; - case KBTS_FEATURE_TAG_cv19: Result = KBTS_FEATURE_ID_cv19; break; - case KBTS_FEATURE_TAG_cv20: Result = KBTS_FEATURE_ID_cv20; break; - case KBTS_FEATURE_TAG_cv21: Result = KBTS_FEATURE_ID_cv21; break; - case KBTS_FEATURE_TAG_cv22: Result = KBTS_FEATURE_ID_cv22; break; - case KBTS_FEATURE_TAG_cv23: Result = KBTS_FEATURE_ID_cv23; break; - case KBTS_FEATURE_TAG_cv24: Result = KBTS_FEATURE_ID_cv24; break; - case KBTS_FEATURE_TAG_cv25: Result = KBTS_FEATURE_ID_cv25; break; - case KBTS_FEATURE_TAG_cv26: Result = KBTS_FEATURE_ID_cv26; break; - case KBTS_FEATURE_TAG_cv27: Result = KBTS_FEATURE_ID_cv27; break; - case KBTS_FEATURE_TAG_cv28: Result = KBTS_FEATURE_ID_cv28; break; - case KBTS_FEATURE_TAG_cv29: Result = KBTS_FEATURE_ID_cv29; break; - case KBTS_FEATURE_TAG_cv30: Result = KBTS_FEATURE_ID_cv30; break; - case KBTS_FEATURE_TAG_cv31: Result = KBTS_FEATURE_ID_cv31; break; - case KBTS_FEATURE_TAG_cv32: Result = KBTS_FEATURE_ID_cv32; break; - case KBTS_FEATURE_TAG_cv33: Result = KBTS_FEATURE_ID_cv33; break; - case KBTS_FEATURE_TAG_cv34: Result = KBTS_FEATURE_ID_cv34; break; - case KBTS_FEATURE_TAG_cv35: Result = KBTS_FEATURE_ID_cv35; break; - case KBTS_FEATURE_TAG_cv36: Result = KBTS_FEATURE_ID_cv36; break; - case KBTS_FEATURE_TAG_cv37: Result = KBTS_FEATURE_ID_cv37; break; - case KBTS_FEATURE_TAG_cv38: Result = KBTS_FEATURE_ID_cv38; break; - case KBTS_FEATURE_TAG_cv39: Result = KBTS_FEATURE_ID_cv39; break; - case KBTS_FEATURE_TAG_cv40: Result = KBTS_FEATURE_ID_cv40; break; - case KBTS_FEATURE_TAG_cv41: Result = KBTS_FEATURE_ID_cv41; break; - case KBTS_FEATURE_TAG_cv42: Result = KBTS_FEATURE_ID_cv42; break; - case KBTS_FEATURE_TAG_cv43: Result = KBTS_FEATURE_ID_cv43; break; - case KBTS_FEATURE_TAG_cv44: Result = KBTS_FEATURE_ID_cv44; break; - case KBTS_FEATURE_TAG_cv45: Result = KBTS_FEATURE_ID_cv45; break; - case KBTS_FEATURE_TAG_cv46: Result = KBTS_FEATURE_ID_cv46; break; - case KBTS_FEATURE_TAG_cv47: Result = KBTS_FEATURE_ID_cv47; break; - case KBTS_FEATURE_TAG_cv48: Result = KBTS_FEATURE_ID_cv48; break; - case KBTS_FEATURE_TAG_cv49: Result = KBTS_FEATURE_ID_cv49; break; - case KBTS_FEATURE_TAG_cv50: Result = KBTS_FEATURE_ID_cv50; break; - case KBTS_FEATURE_TAG_cv51: Result = KBTS_FEATURE_ID_cv51; break; - case KBTS_FEATURE_TAG_cv52: Result = KBTS_FEATURE_ID_cv52; break; - case KBTS_FEATURE_TAG_cv53: Result = KBTS_FEATURE_ID_cv53; break; - case KBTS_FEATURE_TAG_cv54: Result = KBTS_FEATURE_ID_cv54; break; - case KBTS_FEATURE_TAG_cv55: Result = KBTS_FEATURE_ID_cv55; break; - case KBTS_FEATURE_TAG_cv56: Result = KBTS_FEATURE_ID_cv56; break; - case KBTS_FEATURE_TAG_cv57: Result = KBTS_FEATURE_ID_cv57; break; - case KBTS_FEATURE_TAG_cv58: Result = KBTS_FEATURE_ID_cv58; break; - case KBTS_FEATURE_TAG_cv59: Result = KBTS_FEATURE_ID_cv59; break; - case KBTS_FEATURE_TAG_cv60: Result = KBTS_FEATURE_ID_cv60; break; - case KBTS_FEATURE_TAG_cv61: Result = KBTS_FEATURE_ID_cv61; break; - case KBTS_FEATURE_TAG_cv62: Result = KBTS_FEATURE_ID_cv62; break; - case KBTS_FEATURE_TAG_cv63: Result = KBTS_FEATURE_ID_cv63; break; - case KBTS_FEATURE_TAG_cv64: Result = KBTS_FEATURE_ID_cv64; break; - case KBTS_FEATURE_TAG_cv65: Result = KBTS_FEATURE_ID_cv65; break; - case KBTS_FEATURE_TAG_cv66: Result = KBTS_FEATURE_ID_cv66; break; - case KBTS_FEATURE_TAG_cv67: Result = KBTS_FEATURE_ID_cv67; break; - case KBTS_FEATURE_TAG_cv68: Result = KBTS_FEATURE_ID_cv68; break; - case KBTS_FEATURE_TAG_cv69: Result = KBTS_FEATURE_ID_cv69; break; - case KBTS_FEATURE_TAG_cv70: Result = KBTS_FEATURE_ID_cv70; break; - case KBTS_FEATURE_TAG_cv71: Result = KBTS_FEATURE_ID_cv71; break; - case KBTS_FEATURE_TAG_cv72: Result = KBTS_FEATURE_ID_cv72; break; - case KBTS_FEATURE_TAG_cv73: Result = KBTS_FEATURE_ID_cv73; break; - case KBTS_FEATURE_TAG_cv74: Result = KBTS_FEATURE_ID_cv74; break; - case KBTS_FEATURE_TAG_cv75: Result = KBTS_FEATURE_ID_cv75; break; - case KBTS_FEATURE_TAG_cv76: Result = KBTS_FEATURE_ID_cv76; break; - case KBTS_FEATURE_TAG_cv77: Result = KBTS_FEATURE_ID_cv77; break; - case KBTS_FEATURE_TAG_cv78: Result = KBTS_FEATURE_ID_cv78; break; - case KBTS_FEATURE_TAG_cv79: Result = KBTS_FEATURE_ID_cv79; break; - case KBTS_FEATURE_TAG_cv80: Result = KBTS_FEATURE_ID_cv80; break; - case KBTS_FEATURE_TAG_cv81: Result = KBTS_FEATURE_ID_cv81; break; - case KBTS_FEATURE_TAG_cv82: Result = KBTS_FEATURE_ID_cv82; break; - case KBTS_FEATURE_TAG_cv83: Result = KBTS_FEATURE_ID_cv83; break; - case KBTS_FEATURE_TAG_cv84: Result = KBTS_FEATURE_ID_cv84; break; - case KBTS_FEATURE_TAG_cv85: Result = KBTS_FEATURE_ID_cv85; break; - case KBTS_FEATURE_TAG_cv86: Result = KBTS_FEATURE_ID_cv86; break; - case KBTS_FEATURE_TAG_cv87: Result = KBTS_FEATURE_ID_cv87; break; - case KBTS_FEATURE_TAG_cv88: Result = KBTS_FEATURE_ID_cv88; break; - case KBTS_FEATURE_TAG_cv89: Result = KBTS_FEATURE_ID_cv89; break; - case KBTS_FEATURE_TAG_cv90: Result = KBTS_FEATURE_ID_cv90; break; - case KBTS_FEATURE_TAG_cv91: Result = KBTS_FEATURE_ID_cv91; break; - case KBTS_FEATURE_TAG_cv92: Result = KBTS_FEATURE_ID_cv92; break; - case KBTS_FEATURE_TAG_cv93: Result = KBTS_FEATURE_ID_cv93; break; - case KBTS_FEATURE_TAG_cv94: Result = KBTS_FEATURE_ID_cv94; break; - case KBTS_FEATURE_TAG_cv95: Result = KBTS_FEATURE_ID_cv95; break; - case KBTS_FEATURE_TAG_cv96: Result = KBTS_FEATURE_ID_cv96; break; - case KBTS_FEATURE_TAG_cv97: Result = KBTS_FEATURE_ID_cv97; break; - case KBTS_FEATURE_TAG_cv98: Result = KBTS_FEATURE_ID_cv98; break; - case KBTS_FEATURE_TAG_cv99: Result = KBTS_FEATURE_ID_cv99; break; - case KBTS_FEATURE_TAG_c2pc: Result = KBTS_FEATURE_ID_c2pc; break; - case KBTS_FEATURE_TAG_c2sc: Result = KBTS_FEATURE_ID_c2sc; break; - case KBTS_FEATURE_TAG_dist: Result = KBTS_FEATURE_ID_dist; break; - case KBTS_FEATURE_TAG_dlig: Result = KBTS_FEATURE_ID_dlig; break; - case KBTS_FEATURE_TAG_dtls: Result = KBTS_FEATURE_ID_dtls; break; - case KBTS_FEATURE_TAG_expt: Result = KBTS_FEATURE_ID_expt; break; - case KBTS_FEATURE_TAG_falt: Result = KBTS_FEATURE_ID_falt; break; - case KBTS_FEATURE_TAG_flac: Result = KBTS_FEATURE_ID_flac; break; - case KBTS_FEATURE_TAG_fwid: Result = KBTS_FEATURE_ID_fwid; break; - case KBTS_FEATURE_TAG_haln: Result = KBTS_FEATURE_ID_haln; break; - case KBTS_FEATURE_TAG_halt: Result = KBTS_FEATURE_ID_halt; break; - case KBTS_FEATURE_TAG_hist: Result = KBTS_FEATURE_ID_hist; break; - case KBTS_FEATURE_TAG_hkna: Result = KBTS_FEATURE_ID_hkna; break; - case KBTS_FEATURE_TAG_hlig: Result = KBTS_FEATURE_ID_hlig; break; - case KBTS_FEATURE_TAG_hngl: Result = KBTS_FEATURE_ID_hngl; break; - case KBTS_FEATURE_TAG_hojo: Result = KBTS_FEATURE_ID_hojo; break; - case KBTS_FEATURE_TAG_hwid: Result = KBTS_FEATURE_ID_hwid; break; - case KBTS_FEATURE_TAG_ital: Result = KBTS_FEATURE_ID_ital; break; - case KBTS_FEATURE_TAG_jalt: Result = KBTS_FEATURE_ID_jalt; break; - case KBTS_FEATURE_TAG_jp78: Result = KBTS_FEATURE_ID_jp78; break; - case KBTS_FEATURE_TAG_jp83: Result = KBTS_FEATURE_ID_jp83; break; - case KBTS_FEATURE_TAG_jp90: Result = KBTS_FEATURE_ID_jp90; break; - case KBTS_FEATURE_TAG_jp04: Result = KBTS_FEATURE_ID_jp04; break; - case KBTS_FEATURE_TAG_kern: Result = KBTS_FEATURE_ID_kern; break; - case KBTS_FEATURE_TAG_lfbd: Result = KBTS_FEATURE_ID_lfbd; break; - case KBTS_FEATURE_TAG_liga: Result = KBTS_FEATURE_ID_liga; break; - case KBTS_FEATURE_TAG_lnum: Result = KBTS_FEATURE_ID_lnum; break; - case KBTS_FEATURE_TAG_locl: Result = KBTS_FEATURE_ID_locl; break; - case KBTS_FEATURE_TAG_ltra: Result = KBTS_FEATURE_ID_ltra; break; - case KBTS_FEATURE_TAG_ltrm: Result = KBTS_FEATURE_ID_ltrm; break; - case KBTS_FEATURE_TAG_mark: Result = KBTS_FEATURE_ID_mark; break; - case KBTS_FEATURE_TAG_mgrk: Result = KBTS_FEATURE_ID_mgrk; break; - case KBTS_FEATURE_TAG_mkmk: Result = KBTS_FEATURE_ID_mkmk; break; - case KBTS_FEATURE_TAG_mset: Result = KBTS_FEATURE_ID_mset; break; - case KBTS_FEATURE_TAG_nalt: Result = KBTS_FEATURE_ID_nalt; break; - case KBTS_FEATURE_TAG_nlck: Result = KBTS_FEATURE_ID_nlck; break; - case KBTS_FEATURE_TAG_nukt: Result = KBTS_FEATURE_ID_nukt; break; - case KBTS_FEATURE_TAG_onum: Result = KBTS_FEATURE_ID_onum; break; - case KBTS_FEATURE_TAG_opbd: Result = KBTS_FEATURE_ID_opbd; break; - case KBTS_FEATURE_TAG_ordn: Result = KBTS_FEATURE_ID_ordn; break; - case KBTS_FEATURE_TAG_ornm: Result = KBTS_FEATURE_ID_ornm; break; - case KBTS_FEATURE_TAG_palt: Result = KBTS_FEATURE_ID_palt; break; - case KBTS_FEATURE_TAG_pcap: Result = KBTS_FEATURE_ID_pcap; break; - case KBTS_FEATURE_TAG_pkna: Result = KBTS_FEATURE_ID_pkna; break; - case KBTS_FEATURE_TAG_pnum: Result = KBTS_FEATURE_ID_pnum; break; - case KBTS_FEATURE_TAG_pres: Result = KBTS_FEATURE_ID_pres; break; - case KBTS_FEATURE_TAG_psts: Result = KBTS_FEATURE_ID_psts; break; - case KBTS_FEATURE_TAG_pwid: Result = KBTS_FEATURE_ID_pwid; break; - case KBTS_FEATURE_TAG_qwid: Result = KBTS_FEATURE_ID_qwid; break; - case KBTS_FEATURE_TAG_rand: Result = KBTS_FEATURE_ID_rand; break; - case KBTS_FEATURE_TAG_rclt: Result = KBTS_FEATURE_ID_rclt; break; - case KBTS_FEATURE_TAG_rkrf: Result = KBTS_FEATURE_ID_rkrf; break; - case KBTS_FEATURE_TAG_rlig: Result = KBTS_FEATURE_ID_rlig; break; - case KBTS_FEATURE_TAG_rtbd: Result = KBTS_FEATURE_ID_rtbd; break; - case KBTS_FEATURE_TAG_rtla: Result = KBTS_FEATURE_ID_rtla; break; - case KBTS_FEATURE_TAG_rtlm: Result = KBTS_FEATURE_ID_rtlm; break; - case KBTS_FEATURE_TAG_ruby: Result = KBTS_FEATURE_ID_ruby; break; - case KBTS_FEATURE_TAG_rvrn: Result = KBTS_FEATURE_ID_rvrn; break; - case KBTS_FEATURE_TAG_salt: Result = KBTS_FEATURE_ID_salt; break; - case KBTS_FEATURE_TAG_sinf: Result = KBTS_FEATURE_ID_sinf; break; - case KBTS_FEATURE_TAG_size: Result = KBTS_FEATURE_ID_size; break; - case KBTS_FEATURE_TAG_smcp: Result = KBTS_FEATURE_ID_smcp; break; - case KBTS_FEATURE_TAG_smpl: Result = KBTS_FEATURE_ID_smpl; break; - case KBTS_FEATURE_TAG_ss01: Result = KBTS_FEATURE_ID_ss01; break; - case KBTS_FEATURE_TAG_ss02: Result = KBTS_FEATURE_ID_ss02; break; - case KBTS_FEATURE_TAG_ss03: Result = KBTS_FEATURE_ID_ss03; break; - case KBTS_FEATURE_TAG_ss04: Result = KBTS_FEATURE_ID_ss04; break; - case KBTS_FEATURE_TAG_ss05: Result = KBTS_FEATURE_ID_ss05; break; - case KBTS_FEATURE_TAG_ss06: Result = KBTS_FEATURE_ID_ss06; break; - case KBTS_FEATURE_TAG_ss07: Result = KBTS_FEATURE_ID_ss07; break; - case KBTS_FEATURE_TAG_ss08: Result = KBTS_FEATURE_ID_ss08; break; - case KBTS_FEATURE_TAG_ss09: Result = KBTS_FEATURE_ID_ss09; break; - case KBTS_FEATURE_TAG_ss10: Result = KBTS_FEATURE_ID_ss10; break; - case KBTS_FEATURE_TAG_ss11: Result = KBTS_FEATURE_ID_ss11; break; - case KBTS_FEATURE_TAG_ss12: Result = KBTS_FEATURE_ID_ss12; break; - case KBTS_FEATURE_TAG_ss13: Result = KBTS_FEATURE_ID_ss13; break; - case KBTS_FEATURE_TAG_ss14: Result = KBTS_FEATURE_ID_ss14; break; - case KBTS_FEATURE_TAG_ss15: Result = KBTS_FEATURE_ID_ss15; break; - case KBTS_FEATURE_TAG_ss16: Result = KBTS_FEATURE_ID_ss16; break; - case KBTS_FEATURE_TAG_ss17: Result = KBTS_FEATURE_ID_ss17; break; - case KBTS_FEATURE_TAG_ss18: Result = KBTS_FEATURE_ID_ss18; break; - case KBTS_FEATURE_TAG_ss19: Result = KBTS_FEATURE_ID_ss19; break; - case KBTS_FEATURE_TAG_ss20: Result = KBTS_FEATURE_ID_ss20; break; - case KBTS_FEATURE_TAG_ssty: Result = KBTS_FEATURE_ID_ssty; break; - case KBTS_FEATURE_TAG_stch: Result = KBTS_FEATURE_ID_stch; break; - case KBTS_FEATURE_TAG_subs: Result = KBTS_FEATURE_ID_subs; break; - case KBTS_FEATURE_TAG_sups: Result = KBTS_FEATURE_ID_sups; break; - case KBTS_FEATURE_TAG_swsh: Result = KBTS_FEATURE_ID_swsh; break; - case KBTS_FEATURE_TAG_test: Result = KBTS_FEATURE_ID_test; break; - case KBTS_FEATURE_TAG_titl: Result = KBTS_FEATURE_ID_titl; break; - case KBTS_FEATURE_TAG_tnam: Result = KBTS_FEATURE_ID_tnam; break; - case KBTS_FEATURE_TAG_tnum: Result = KBTS_FEATURE_ID_tnum; break; - case KBTS_FEATURE_TAG_trad: Result = KBTS_FEATURE_ID_trad; break; - case KBTS_FEATURE_TAG_twid: Result = KBTS_FEATURE_ID_twid; break; - case KBTS_FEATURE_TAG_unic: Result = KBTS_FEATURE_ID_unic; break; - case KBTS_FEATURE_TAG_valt: Result = KBTS_FEATURE_ID_valt; break; - case KBTS_FEATURE_TAG_vapk: Result = KBTS_FEATURE_ID_vapk; break; - case KBTS_FEATURE_TAG_vatu: Result = KBTS_FEATURE_ID_vatu; break; - case KBTS_FEATURE_TAG_vchw: Result = KBTS_FEATURE_ID_vchw; break; - case KBTS_FEATURE_TAG_vert: Result = KBTS_FEATURE_ID_vert; break; - case KBTS_FEATURE_TAG_vhal: Result = KBTS_FEATURE_ID_vhal; break; - case KBTS_FEATURE_TAG_vkna: Result = KBTS_FEATURE_ID_vkna; break; - case KBTS_FEATURE_TAG_vkrn: Result = KBTS_FEATURE_ID_vkrn; break; - case KBTS_FEATURE_TAG_vpal: Result = KBTS_FEATURE_ID_vpal; break; - case KBTS_FEATURE_TAG_vrt2: Result = KBTS_FEATURE_ID_vrt2; break; - case KBTS_FEATURE_TAG_vrtr: Result = KBTS_FEATURE_ID_vrtr; break; - case KBTS_FEATURE_TAG_zero: Result = KBTS_FEATURE_ID_zero; break; + case KBTS_FEATURE_TAG_isol: Result = KBTS__FEATURE_ID_isol; break; + case KBTS_FEATURE_TAG_fina: Result = KBTS__FEATURE_ID_fina; break; + case KBTS_FEATURE_TAG_fin2: Result = KBTS__FEATURE_ID_fin2; break; + case KBTS_FEATURE_TAG_fin3: Result = KBTS__FEATURE_ID_fin3; break; + case KBTS_FEATURE_TAG_medi: Result = KBTS__FEATURE_ID_medi; break; + case KBTS_FEATURE_TAG_med2: Result = KBTS__FEATURE_ID_med2; break; + case KBTS_FEATURE_TAG_init: Result = KBTS__FEATURE_ID_init; break; + case KBTS_FEATURE_TAG_ljmo: Result = KBTS__FEATURE_ID_ljmo; break; + case KBTS_FEATURE_TAG_vjmo: Result = KBTS__FEATURE_ID_vjmo; break; + case KBTS_FEATURE_TAG_tjmo: Result = KBTS__FEATURE_ID_tjmo; break; + case KBTS_FEATURE_TAG_rphf: Result = KBTS__FEATURE_ID_rphf; break; + case KBTS_FEATURE_TAG_blwf: Result = KBTS__FEATURE_ID_blwf; break; + case KBTS_FEATURE_TAG_half: Result = KBTS__FEATURE_ID_half; break; + case KBTS_FEATURE_TAG_pstf: Result = KBTS__FEATURE_ID_pstf; break; + case KBTS_FEATURE_TAG_abvf: Result = KBTS__FEATURE_ID_abvf; break; + case KBTS_FEATURE_TAG_pref: Result = KBTS__FEATURE_ID_pref; break; + case KBTS_FEATURE_TAG_numr: Result = KBTS__FEATURE_ID_numr; break; + case KBTS_FEATURE_TAG_frac: Result = KBTS__FEATURE_ID_frac; break; + case KBTS_FEATURE_TAG_dnom: Result = KBTS__FEATURE_ID_dnom; break; + case KBTS_FEATURE_TAG_cfar: Result = KBTS__FEATURE_ID_cfar; break; + case KBTS_FEATURE_TAG_aalt: Result = KBTS__FEATURE_ID_aalt; break; + case KBTS_FEATURE_TAG_abvm: Result = KBTS__FEATURE_ID_abvm; break; + case KBTS_FEATURE_TAG_abvs: Result = KBTS__FEATURE_ID_abvs; break; + case KBTS_FEATURE_TAG_afrc: Result = KBTS__FEATURE_ID_afrc; break; + case KBTS_FEATURE_TAG_akhn: Result = KBTS__FEATURE_ID_akhn; break; + case KBTS_FEATURE_TAG_apkn: Result = KBTS__FEATURE_ID_apkn; break; + case KBTS_FEATURE_TAG_blwm: Result = KBTS__FEATURE_ID_blwm; break; + case KBTS_FEATURE_TAG_blws: Result = KBTS__FEATURE_ID_blws; break; + case KBTS_FEATURE_TAG_calt: Result = KBTS__FEATURE_ID_calt; break; + case KBTS_FEATURE_TAG_case: Result = KBTS__FEATURE_ID_case; break; + case KBTS_FEATURE_TAG_ccmp: Result = KBTS__FEATURE_ID_ccmp; break; + case KBTS_FEATURE_TAG_chws: Result = KBTS__FEATURE_ID_chws; break; + case KBTS_FEATURE_TAG_cjct: Result = KBTS__FEATURE_ID_cjct; break; + case KBTS_FEATURE_TAG_clig: Result = KBTS__FEATURE_ID_clig; break; + case KBTS_FEATURE_TAG_cpct: Result = KBTS__FEATURE_ID_cpct; break; + case KBTS_FEATURE_TAG_cpsp: Result = KBTS__FEATURE_ID_cpsp; break; + case KBTS_FEATURE_TAG_cswh: Result = KBTS__FEATURE_ID_cswh; break; + case KBTS_FEATURE_TAG_curs: Result = KBTS__FEATURE_ID_curs; break; + case KBTS_FEATURE_TAG_cv01: Result = KBTS__FEATURE_ID_cv01; break; + case KBTS_FEATURE_TAG_cv02: Result = KBTS__FEATURE_ID_cv02; break; + case KBTS_FEATURE_TAG_cv03: Result = KBTS__FEATURE_ID_cv03; break; + case KBTS_FEATURE_TAG_cv04: Result = KBTS__FEATURE_ID_cv04; break; + case KBTS_FEATURE_TAG_cv05: Result = KBTS__FEATURE_ID_cv05; break; + case KBTS_FEATURE_TAG_cv06: Result = KBTS__FEATURE_ID_cv06; break; + case KBTS_FEATURE_TAG_cv07: Result = KBTS__FEATURE_ID_cv07; break; + case KBTS_FEATURE_TAG_cv08: Result = KBTS__FEATURE_ID_cv08; break; + case KBTS_FEATURE_TAG_cv09: Result = KBTS__FEATURE_ID_cv09; break; + case KBTS_FEATURE_TAG_cv10: Result = KBTS__FEATURE_ID_cv10; break; + case KBTS_FEATURE_TAG_cv11: Result = KBTS__FEATURE_ID_cv11; break; + case KBTS_FEATURE_TAG_cv12: Result = KBTS__FEATURE_ID_cv12; break; + case KBTS_FEATURE_TAG_cv13: Result = KBTS__FEATURE_ID_cv13; break; + case KBTS_FEATURE_TAG_cv14: Result = KBTS__FEATURE_ID_cv14; break; + case KBTS_FEATURE_TAG_cv15: Result = KBTS__FEATURE_ID_cv15; break; + case KBTS_FEATURE_TAG_cv16: Result = KBTS__FEATURE_ID_cv16; break; + case KBTS_FEATURE_TAG_cv17: Result = KBTS__FEATURE_ID_cv17; break; + case KBTS_FEATURE_TAG_cv18: Result = KBTS__FEATURE_ID_cv18; break; + case KBTS_FEATURE_TAG_cv19: Result = KBTS__FEATURE_ID_cv19; break; + case KBTS_FEATURE_TAG_cv20: Result = KBTS__FEATURE_ID_cv20; break; + case KBTS_FEATURE_TAG_cv21: Result = KBTS__FEATURE_ID_cv21; break; + case KBTS_FEATURE_TAG_cv22: Result = KBTS__FEATURE_ID_cv22; break; + case KBTS_FEATURE_TAG_cv23: Result = KBTS__FEATURE_ID_cv23; break; + case KBTS_FEATURE_TAG_cv24: Result = KBTS__FEATURE_ID_cv24; break; + case KBTS_FEATURE_TAG_cv25: Result = KBTS__FEATURE_ID_cv25; break; + case KBTS_FEATURE_TAG_cv26: Result = KBTS__FEATURE_ID_cv26; break; + case KBTS_FEATURE_TAG_cv27: Result = KBTS__FEATURE_ID_cv27; break; + case KBTS_FEATURE_TAG_cv28: Result = KBTS__FEATURE_ID_cv28; break; + case KBTS_FEATURE_TAG_cv29: Result = KBTS__FEATURE_ID_cv29; break; + case KBTS_FEATURE_TAG_cv30: Result = KBTS__FEATURE_ID_cv30; break; + case KBTS_FEATURE_TAG_cv31: Result = KBTS__FEATURE_ID_cv31; break; + case KBTS_FEATURE_TAG_cv32: Result = KBTS__FEATURE_ID_cv32; break; + case KBTS_FEATURE_TAG_cv33: Result = KBTS__FEATURE_ID_cv33; break; + case KBTS_FEATURE_TAG_cv34: Result = KBTS__FEATURE_ID_cv34; break; + case KBTS_FEATURE_TAG_cv35: Result = KBTS__FEATURE_ID_cv35; break; + case KBTS_FEATURE_TAG_cv36: Result = KBTS__FEATURE_ID_cv36; break; + case KBTS_FEATURE_TAG_cv37: Result = KBTS__FEATURE_ID_cv37; break; + case KBTS_FEATURE_TAG_cv38: Result = KBTS__FEATURE_ID_cv38; break; + case KBTS_FEATURE_TAG_cv39: Result = KBTS__FEATURE_ID_cv39; break; + case KBTS_FEATURE_TAG_cv40: Result = KBTS__FEATURE_ID_cv40; break; + case KBTS_FEATURE_TAG_cv41: Result = KBTS__FEATURE_ID_cv41; break; + case KBTS_FEATURE_TAG_cv42: Result = KBTS__FEATURE_ID_cv42; break; + case KBTS_FEATURE_TAG_cv43: Result = KBTS__FEATURE_ID_cv43; break; + case KBTS_FEATURE_TAG_cv44: Result = KBTS__FEATURE_ID_cv44; break; + case KBTS_FEATURE_TAG_cv45: Result = KBTS__FEATURE_ID_cv45; break; + case KBTS_FEATURE_TAG_cv46: Result = KBTS__FEATURE_ID_cv46; break; + case KBTS_FEATURE_TAG_cv47: Result = KBTS__FEATURE_ID_cv47; break; + case KBTS_FEATURE_TAG_cv48: Result = KBTS__FEATURE_ID_cv48; break; + case KBTS_FEATURE_TAG_cv49: Result = KBTS__FEATURE_ID_cv49; break; + case KBTS_FEATURE_TAG_cv50: Result = KBTS__FEATURE_ID_cv50; break; + case KBTS_FEATURE_TAG_cv51: Result = KBTS__FEATURE_ID_cv51; break; + case KBTS_FEATURE_TAG_cv52: Result = KBTS__FEATURE_ID_cv52; break; + case KBTS_FEATURE_TAG_cv53: Result = KBTS__FEATURE_ID_cv53; break; + case KBTS_FEATURE_TAG_cv54: Result = KBTS__FEATURE_ID_cv54; break; + case KBTS_FEATURE_TAG_cv55: Result = KBTS__FEATURE_ID_cv55; break; + case KBTS_FEATURE_TAG_cv56: Result = KBTS__FEATURE_ID_cv56; break; + case KBTS_FEATURE_TAG_cv57: Result = KBTS__FEATURE_ID_cv57; break; + case KBTS_FEATURE_TAG_cv58: Result = KBTS__FEATURE_ID_cv58; break; + case KBTS_FEATURE_TAG_cv59: Result = KBTS__FEATURE_ID_cv59; break; + case KBTS_FEATURE_TAG_cv60: Result = KBTS__FEATURE_ID_cv60; break; + case KBTS_FEATURE_TAG_cv61: Result = KBTS__FEATURE_ID_cv61; break; + case KBTS_FEATURE_TAG_cv62: Result = KBTS__FEATURE_ID_cv62; break; + case KBTS_FEATURE_TAG_cv63: Result = KBTS__FEATURE_ID_cv63; break; + case KBTS_FEATURE_TAG_cv64: Result = KBTS__FEATURE_ID_cv64; break; + case KBTS_FEATURE_TAG_cv65: Result = KBTS__FEATURE_ID_cv65; break; + case KBTS_FEATURE_TAG_cv66: Result = KBTS__FEATURE_ID_cv66; break; + case KBTS_FEATURE_TAG_cv67: Result = KBTS__FEATURE_ID_cv67; break; + case KBTS_FEATURE_TAG_cv68: Result = KBTS__FEATURE_ID_cv68; break; + case KBTS_FEATURE_TAG_cv69: Result = KBTS__FEATURE_ID_cv69; break; + case KBTS_FEATURE_TAG_cv70: Result = KBTS__FEATURE_ID_cv70; break; + case KBTS_FEATURE_TAG_cv71: Result = KBTS__FEATURE_ID_cv71; break; + case KBTS_FEATURE_TAG_cv72: Result = KBTS__FEATURE_ID_cv72; break; + case KBTS_FEATURE_TAG_cv73: Result = KBTS__FEATURE_ID_cv73; break; + case KBTS_FEATURE_TAG_cv74: Result = KBTS__FEATURE_ID_cv74; break; + case KBTS_FEATURE_TAG_cv75: Result = KBTS__FEATURE_ID_cv75; break; + case KBTS_FEATURE_TAG_cv76: Result = KBTS__FEATURE_ID_cv76; break; + case KBTS_FEATURE_TAG_cv77: Result = KBTS__FEATURE_ID_cv77; break; + case KBTS_FEATURE_TAG_cv78: Result = KBTS__FEATURE_ID_cv78; break; + case KBTS_FEATURE_TAG_cv79: Result = KBTS__FEATURE_ID_cv79; break; + case KBTS_FEATURE_TAG_cv80: Result = KBTS__FEATURE_ID_cv80; break; + case KBTS_FEATURE_TAG_cv81: Result = KBTS__FEATURE_ID_cv81; break; + case KBTS_FEATURE_TAG_cv82: Result = KBTS__FEATURE_ID_cv82; break; + case KBTS_FEATURE_TAG_cv83: Result = KBTS__FEATURE_ID_cv83; break; + case KBTS_FEATURE_TAG_cv84: Result = KBTS__FEATURE_ID_cv84; break; + case KBTS_FEATURE_TAG_cv85: Result = KBTS__FEATURE_ID_cv85; break; + case KBTS_FEATURE_TAG_cv86: Result = KBTS__FEATURE_ID_cv86; break; + case KBTS_FEATURE_TAG_cv87: Result = KBTS__FEATURE_ID_cv87; break; + case KBTS_FEATURE_TAG_cv88: Result = KBTS__FEATURE_ID_cv88; break; + case KBTS_FEATURE_TAG_cv89: Result = KBTS__FEATURE_ID_cv89; break; + case KBTS_FEATURE_TAG_cv90: Result = KBTS__FEATURE_ID_cv90; break; + case KBTS_FEATURE_TAG_cv91: Result = KBTS__FEATURE_ID_cv91; break; + case KBTS_FEATURE_TAG_cv92: Result = KBTS__FEATURE_ID_cv92; break; + case KBTS_FEATURE_TAG_cv93: Result = KBTS__FEATURE_ID_cv93; break; + case KBTS_FEATURE_TAG_cv94: Result = KBTS__FEATURE_ID_cv94; break; + case KBTS_FEATURE_TAG_cv95: Result = KBTS__FEATURE_ID_cv95; break; + case KBTS_FEATURE_TAG_cv96: Result = KBTS__FEATURE_ID_cv96; break; + case KBTS_FEATURE_TAG_cv97: Result = KBTS__FEATURE_ID_cv97; break; + case KBTS_FEATURE_TAG_cv98: Result = KBTS__FEATURE_ID_cv98; break; + case KBTS_FEATURE_TAG_cv99: Result = KBTS__FEATURE_ID_cv99; break; + case KBTS_FEATURE_TAG_c2pc: Result = KBTS__FEATURE_ID_c2pc; break; + case KBTS_FEATURE_TAG_c2sc: Result = KBTS__FEATURE_ID_c2sc; break; + case KBTS_FEATURE_TAG_dist: Result = KBTS__FEATURE_ID_dist; break; + case KBTS_FEATURE_TAG_dlig: Result = KBTS__FEATURE_ID_dlig; break; + case KBTS_FEATURE_TAG_dtls: Result = KBTS__FEATURE_ID_dtls; break; + case KBTS_FEATURE_TAG_expt: Result = KBTS__FEATURE_ID_expt; break; + case KBTS_FEATURE_TAG_falt: Result = KBTS__FEATURE_ID_falt; break; + case KBTS_FEATURE_TAG_flac: Result = KBTS__FEATURE_ID_flac; break; + case KBTS_FEATURE_TAG_fwid: Result = KBTS__FEATURE_ID_fwid; break; + case KBTS_FEATURE_TAG_haln: Result = KBTS__FEATURE_ID_haln; break; + case KBTS_FEATURE_TAG_halt: Result = KBTS__FEATURE_ID_halt; break; + case KBTS_FEATURE_TAG_hist: Result = KBTS__FEATURE_ID_hist; break; + case KBTS_FEATURE_TAG_hkna: Result = KBTS__FEATURE_ID_hkna; break; + case KBTS_FEATURE_TAG_hlig: Result = KBTS__FEATURE_ID_hlig; break; + case KBTS_FEATURE_TAG_hngl: Result = KBTS__FEATURE_ID_hngl; break; + case KBTS_FEATURE_TAG_hojo: Result = KBTS__FEATURE_ID_hojo; break; + case KBTS_FEATURE_TAG_hwid: Result = KBTS__FEATURE_ID_hwid; break; + case KBTS_FEATURE_TAG_ital: Result = KBTS__FEATURE_ID_ital; break; + case KBTS_FEATURE_TAG_jalt: Result = KBTS__FEATURE_ID_jalt; break; + case KBTS_FEATURE_TAG_jp78: Result = KBTS__FEATURE_ID_jp78; break; + case KBTS_FEATURE_TAG_jp83: Result = KBTS__FEATURE_ID_jp83; break; + case KBTS_FEATURE_TAG_jp90: Result = KBTS__FEATURE_ID_jp90; break; + case KBTS_FEATURE_TAG_jp04: Result = KBTS__FEATURE_ID_jp04; break; + case KBTS_FEATURE_TAG_kern: Result = KBTS__FEATURE_ID_kern; break; + case KBTS_FEATURE_TAG_lfbd: Result = KBTS__FEATURE_ID_lfbd; break; + case KBTS_FEATURE_TAG_liga: Result = KBTS__FEATURE_ID_liga; break; + case KBTS_FEATURE_TAG_lnum: Result = KBTS__FEATURE_ID_lnum; break; + case KBTS_FEATURE_TAG_locl: Result = KBTS__FEATURE_ID_locl; break; + case KBTS_FEATURE_TAG_ltra: Result = KBTS__FEATURE_ID_ltra; break; + case KBTS_FEATURE_TAG_ltrm: Result = KBTS__FEATURE_ID_ltrm; break; + case KBTS_FEATURE_TAG_mark: Result = KBTS__FEATURE_ID_mark; break; + case KBTS_FEATURE_TAG_mgrk: Result = KBTS__FEATURE_ID_mgrk; break; + case KBTS_FEATURE_TAG_mkmk: Result = KBTS__FEATURE_ID_mkmk; break; + case KBTS_FEATURE_TAG_mset: Result = KBTS__FEATURE_ID_mset; break; + case KBTS_FEATURE_TAG_nalt: Result = KBTS__FEATURE_ID_nalt; break; + case KBTS_FEATURE_TAG_nlck: Result = KBTS__FEATURE_ID_nlck; break; + case KBTS_FEATURE_TAG_nukt: Result = KBTS__FEATURE_ID_nukt; break; + case KBTS_FEATURE_TAG_onum: Result = KBTS__FEATURE_ID_onum; break; + case KBTS_FEATURE_TAG_opbd: Result = KBTS__FEATURE_ID_opbd; break; + case KBTS_FEATURE_TAG_ordn: Result = KBTS__FEATURE_ID_ordn; break; + case KBTS_FEATURE_TAG_ornm: Result = KBTS__FEATURE_ID_ornm; break; + case KBTS_FEATURE_TAG_palt: Result = KBTS__FEATURE_ID_palt; break; + case KBTS_FEATURE_TAG_pcap: Result = KBTS__FEATURE_ID_pcap; break; + case KBTS_FEATURE_TAG_pkna: Result = KBTS__FEATURE_ID_pkna; break; + case KBTS_FEATURE_TAG_pnum: Result = KBTS__FEATURE_ID_pnum; break; + case KBTS_FEATURE_TAG_pres: Result = KBTS__FEATURE_ID_pres; break; + case KBTS_FEATURE_TAG_psts: Result = KBTS__FEATURE_ID_psts; break; + case KBTS_FEATURE_TAG_pwid: Result = KBTS__FEATURE_ID_pwid; break; + case KBTS_FEATURE_TAG_qwid: Result = KBTS__FEATURE_ID_qwid; break; + case KBTS_FEATURE_TAG_rand: Result = KBTS__FEATURE_ID_rand; break; + case KBTS_FEATURE_TAG_rclt: Result = KBTS__FEATURE_ID_rclt; break; + case KBTS_FEATURE_TAG_rkrf: Result = KBTS__FEATURE_ID_rkrf; break; + case KBTS_FEATURE_TAG_rlig: Result = KBTS__FEATURE_ID_rlig; break; + case KBTS_FEATURE_TAG_rtbd: Result = KBTS__FEATURE_ID_rtbd; break; + case KBTS_FEATURE_TAG_rtla: Result = KBTS__FEATURE_ID_rtla; break; + case KBTS_FEATURE_TAG_rtlm: Result = KBTS__FEATURE_ID_rtlm; break; + case KBTS_FEATURE_TAG_ruby: Result = KBTS__FEATURE_ID_ruby; break; + case KBTS_FEATURE_TAG_rvrn: Result = KBTS__FEATURE_ID_rvrn; break; + case KBTS_FEATURE_TAG_salt: Result = KBTS__FEATURE_ID_salt; break; + case KBTS_FEATURE_TAG_sinf: Result = KBTS__FEATURE_ID_sinf; break; + case KBTS_FEATURE_TAG_size: Result = KBTS__FEATURE_ID_size; break; + case KBTS_FEATURE_TAG_smcp: Result = KBTS__FEATURE_ID_smcp; break; + case KBTS_FEATURE_TAG_smpl: Result = KBTS__FEATURE_ID_smpl; break; + case KBTS_FEATURE_TAG_ss01: Result = KBTS__FEATURE_ID_ss01; break; + case KBTS_FEATURE_TAG_ss02: Result = KBTS__FEATURE_ID_ss02; break; + case KBTS_FEATURE_TAG_ss03: Result = KBTS__FEATURE_ID_ss03; break; + case KBTS_FEATURE_TAG_ss04: Result = KBTS__FEATURE_ID_ss04; break; + case KBTS_FEATURE_TAG_ss05: Result = KBTS__FEATURE_ID_ss05; break; + case KBTS_FEATURE_TAG_ss06: Result = KBTS__FEATURE_ID_ss06; break; + case KBTS_FEATURE_TAG_ss07: Result = KBTS__FEATURE_ID_ss07; break; + case KBTS_FEATURE_TAG_ss08: Result = KBTS__FEATURE_ID_ss08; break; + case KBTS_FEATURE_TAG_ss09: Result = KBTS__FEATURE_ID_ss09; break; + case KBTS_FEATURE_TAG_ss10: Result = KBTS__FEATURE_ID_ss10; break; + case KBTS_FEATURE_TAG_ss11: Result = KBTS__FEATURE_ID_ss11; break; + case KBTS_FEATURE_TAG_ss12: Result = KBTS__FEATURE_ID_ss12; break; + case KBTS_FEATURE_TAG_ss13: Result = KBTS__FEATURE_ID_ss13; break; + case KBTS_FEATURE_TAG_ss14: Result = KBTS__FEATURE_ID_ss14; break; + case KBTS_FEATURE_TAG_ss15: Result = KBTS__FEATURE_ID_ss15; break; + case KBTS_FEATURE_TAG_ss16: Result = KBTS__FEATURE_ID_ss16; break; + case KBTS_FEATURE_TAG_ss17: Result = KBTS__FEATURE_ID_ss17; break; + case KBTS_FEATURE_TAG_ss18: Result = KBTS__FEATURE_ID_ss18; break; + case KBTS_FEATURE_TAG_ss19: Result = KBTS__FEATURE_ID_ss19; break; + case KBTS_FEATURE_TAG_ss20: Result = KBTS__FEATURE_ID_ss20; break; + case KBTS_FEATURE_TAG_ssty: Result = KBTS__FEATURE_ID_ssty; break; + case KBTS_FEATURE_TAG_stch: Result = KBTS__FEATURE_ID_stch; break; + case KBTS_FEATURE_TAG_subs: Result = KBTS__FEATURE_ID_subs; break; + case KBTS_FEATURE_TAG_sups: Result = KBTS__FEATURE_ID_sups; break; + case KBTS_FEATURE_TAG_swsh: Result = KBTS__FEATURE_ID_swsh; break; + case KBTS_FEATURE_TAG_test: Result = KBTS__FEATURE_ID_test; break; + case KBTS_FEATURE_TAG_titl: Result = KBTS__FEATURE_ID_titl; break; + case KBTS_FEATURE_TAG_tnam: Result = KBTS__FEATURE_ID_tnam; break; + case KBTS_FEATURE_TAG_tnum: Result = KBTS__FEATURE_ID_tnum; break; + case KBTS_FEATURE_TAG_trad: Result = KBTS__FEATURE_ID_trad; break; + case KBTS_FEATURE_TAG_twid: Result = KBTS__FEATURE_ID_twid; break; + case KBTS_FEATURE_TAG_unic: Result = KBTS__FEATURE_ID_unic; break; + case KBTS_FEATURE_TAG_valt: Result = KBTS__FEATURE_ID_valt; break; + case KBTS_FEATURE_TAG_vapk: Result = KBTS__FEATURE_ID_vapk; break; + case KBTS_FEATURE_TAG_vatu: Result = KBTS__FEATURE_ID_vatu; break; + case KBTS_FEATURE_TAG_vchw: Result = KBTS__FEATURE_ID_vchw; break; + case KBTS_FEATURE_TAG_vert: Result = KBTS__FEATURE_ID_vert; break; + case KBTS_FEATURE_TAG_vhal: Result = KBTS__FEATURE_ID_vhal; break; + case KBTS_FEATURE_TAG_vkna: Result = KBTS__FEATURE_ID_vkna; break; + case KBTS_FEATURE_TAG_vkrn: Result = KBTS__FEATURE_ID_vkrn; break; + case KBTS_FEATURE_TAG_vpal: Result = KBTS__FEATURE_ID_vpal; break; + case KBTS_FEATURE_TAG_vrt2: Result = KBTS__FEATURE_ID_vrt2; break; + case KBTS_FEATURE_TAG_vrtr: Result = KBTS__FEATURE_ID_vrtr; break; + case KBTS_FEATURE_TAG_zero: Result = KBTS__FEATURE_ID_zero; break; default: break; } return Result; } -static kbts_s32 kbts_UnicodeParentDeltas[1679] = { +static kbts_s32 kbts__UnicodeParentDeltas[1679] = { 132,133,134,135,244,246,248,250,252,254,315,351,416,418,7678,7680,7682,7792,7794,132,133,134,135,275,277,279,281,283,285,346,382,447, 449,7709,7711,7713,7823,7825,131,132,133,134,174,176,178,180,182,416,418,452,7604,7606,7764,7766,7768,131,132,133,134,205,207,209,211,213, 447,449,483,7635,7637,7795,7797,7799,127,128,129,130,131,132,160,162,164,365,416,418,454,7584,7744,7746,127,128,129,130,131,132,191,193, @@ -3621,7 +5114,24 @@ static kbts_s32 kbts_UnicodeParentDeltas[1679] = { 180071,180102,180152,180175,180238,180262,180308,180469,180588,180601,181007,181056,181082,181102,181519, }; -static kbts_u8 kbts_UnicodeDecomposition_PageIndices[17407] = { +static kbts_u8 kbts__ScriptExtensions[447] = { + 6,18,24,25,35,37,41,42,43,45,47,72,79,80,112,132,11,28,32,72,77,155,160,13,72,72,155,22,25,28,45,72, + 112,141,146,22,28,45,72,119,141,146,159,22,28,72,157,42,72,141,143,155,19,22,25,28,43,45,72,119,143,157,159,25, + 37,42,43,62,72,25,35,54,72,112,143,146,157,159,5,28,35,43,45,54,72,112,143,146,35,72,143,22,28,72,119,22, + 72,146,39,72,28,72,159,45,72,112,159,22,35,62,72,143,22,35,72,143,22,72,143,19,22,43,72,141,155,19,72,159, + 25,45,28,112,28,42,5,41,42,4,40,51,103,143,154,167,4,143,154,1,4,40,51,103,143,154,167,1,4,51,83,84, + 117,126,135,143,4,154,167,11,32,44,46,48,61,72,82,118,131,150,153,158,11,32,44,46,48,61,72,82,118,150,153,158, + 11,32,34,44,46,47,48,61,69,80,82,86,100,108,118,136,142,149,150,153,158,11,32,34,44,46,47,48,49,61,69,74, + 80,82,86,100,108,118,136,142,149,150,153,158,32,34,60,80,11,20,142,48,96,46,68,44,150,61,100,161,20,97,146,41, + 42,72,16,52,144,145,94,124,11,32,44,61,32,131,32,61,82,118,150,153,32,100,11,32,44,61,82,100,118,136,153,158, + 161,32,44,61,161,28,72,143,72,94,124,18,41,42,78,110,116,18,45,91,110,32,44,72,25,72,6,116,6,18,41,60, + 79,110,129,1,4,110,24,152,13,24,50,55,62,94,168,13,24,50,55,62,94,124,168,13,24,50,55,62,94,156,168,13, + 24,50,55,62,77,94,156,168,13,24,50,55,62,168,24,55,62,24,72,32,34,46,48,60,61,68,69,80,82,93,100,131, + 149,158,161,32,34,46,48,60,61,68,69,80,93,100,131,149,158,161,32,34,46,48,60,68,69,80,93,149,158,32,34,46, + 48,60,68,69,80,93,131,149,158,11,32,161,32,150,64,72,97,15,59,4,103,26,27,76,26,76,26,75,76,4,25, +}; + +static kbts_u8 kbts__UnicodeDecomposition_PageIndices[17407] = { 0,1,1,2,3,4,5,6,7,1,1,1,1,8,9,10,11,12,1,13,1,1,1,1,14,1,1,15,1,1,1,1, 1,1,1,1,16,17,1,18,19,20,1,1,1,21,1,22,1,23,1,24,1,25,1,26,1,1,1,1,1,27,28,1, 29,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,30,31, @@ -4168,9 +5678,9 @@ static kbts_u8 kbts_UnicodeDecomposition_PageIndices[17407] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, }; -static kbts_u64 kbts_UnicodeDecomposition_Data[5632] = { +static kbts_u64 kbts__UnicodeDecomposition_Data[5632] = { 52776558133248ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull, - 0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,343932928ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull, + 0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull, 0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull, 0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull, 6442451206ull,6450839814ull,6459228422ull,6467617030ull,6509560070ull,6526337286ull,0ull,6769606926ull,6442451222ull,6450839830ull,6459228438ull,6509560086ull,6442451238ull,6450839846ull,6459228454ull,6509560102ull,0ull,6467617082ull,6442451262ull,6450839870ull,6459228478ull,6467617086ull,6509560126ull,0ull,0ull,6442451286ull,6450839894ull,6459228502ull,6509560150ull,6450839910ull,0ull,0ull, @@ -4347,12 +5857,12 @@ static kbts_u64 kbts_UnicodeDecomposition_Data[5632] = { 0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull,0ull, }; -KBTS_INLINE kbts_u64 kbts_GetUnicodeDecomposition(kbts_u32 Codepoint) +KBTS_INLINE kbts_u64 kbts__GetUnicodeDecomposition(kbts_u32 Codepoint) { - return kbts_UnicodeDecomposition_Data[((kbts_un)kbts_UnicodeDecomposition_PageIndices[Codepoint/64] * 64) | (Codepoint & 63)]; + return (Codepoint < 1114110) ? kbts__UnicodeDecomposition_Data[((kbts_un)kbts__UnicodeDecomposition_PageIndices[Codepoint/64] * 64) | (Codepoint & 63)] : 0; } -static kbts_u8 kbts_UnicodeWordBreakClass_PageIndices[8703] = { +static kbts_u8 kbts__UnicodeWordBreakClass_PageIndices[8703] = { 0,1,2,2,2,3,4,5,2,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, 29,30,2,2,31,32,33,34,35,2,2,2,36,37,38,39,40,41,42,43,44,45,46,47,48,49,2,50,2,2,51,52, 53,54,55,56,57,57,58,59,57,60,57,61,62,63,64,65,57,57,66,57,57,57,67,57,2,68,69,70,71,57,57,57, @@ -4627,7 +6137,7 @@ static kbts_u8 kbts_UnicodeWordBreakClass_PageIndices[8703] = { 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, }; -static kbts_u8 kbts_UnicodeWordBreakClass_Data[27648] = { +static kbts_u8 kbts__UnicodeWordBreakClass_Data[27648] = { 0,0,0,0,0,0,0,0,0,0,3,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,14,0,0,0,0,13,0,0,0,0,17,0,15,0,18,18,18,18,18,18,18,18,18,18,16,17,0,0,0,0, 0,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,0,0,19,0,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,0,0,0, 0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,11,0,0,8,1,0,0,0,0,0,0,11,0,16,0,0,11,0,0,0,0,0, @@ -5062,12 +6572,12 @@ static kbts_u8 kbts_UnicodeWordBreakClass_Data[27648] = { 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; -KBTS_INLINE kbts_u8 kbts_GetUnicodeWordBreakClass(kbts_u32 Codepoint) +KBTS_INLINE kbts_u8 kbts__GetUnicodeWordBreakClass(kbts_u32 Codepoint) { - return kbts_UnicodeWordBreakClass_Data[((kbts_un)kbts_UnicodeWordBreakClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; + return (Codepoint < 1114110) ? kbts__UnicodeWordBreakClass_Data[((kbts_un)kbts__UnicodeWordBreakClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; } -static kbts_u8 kbts_UnicodeLineBreakClass_PageIndices[8703] = { +static kbts_u8 kbts__UnicodeLineBreakClass_PageIndices[8703] = { 0,1,2,2,2,3,4,2,2,5,2,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, 27,28,29,30,2,2,31,2,32,2,2,2,2,33,34,35,36,37,38,39,40,41,42,43,44,45,2,46,2,2,2,47, 48,49,50,2,51,52,53,54,2,2,2,55,56,57,58,59,2,2,2,60,2,2,61,2,2,62,63,64,65,66,67,68, @@ -5342,7 +6852,7 @@ static kbts_u8 kbts_UnicodeLineBreakClass_PageIndices[8703] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, }; -static kbts_u8 kbts_UnicodeLineBreakClass_Data[26752] = { +static kbts_u8 kbts__UnicodeLineBreakClass_Data[26752] = { 63,63,63,63,63,63,63,63,63,19,5,3,3,4,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,7,16,23,37,40,48,37,23,21,14,37,40,26,31,26,18,39,39,39,39,39,39,39,39,39,39,26,26,37,37,37,16, 37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,21,40,14,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,21,19,12,37,63, 63,63,63,63,63,6,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,10,21,48,40,40,40,37,37,37,37,37,24,37,19,37,37,48,40,37,37,35,37,37,37,37,37,37,25,37,37,37,21, @@ -5763,12 +7273,12 @@ static kbts_u8 kbts_UnicodeLineBreakClass_Data[26752] = { 63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, }; -KBTS_INLINE kbts_u8 kbts_GetUnicodeLineBreakClass(kbts_u32 Codepoint) +KBTS_INLINE kbts_u8 kbts__GetUnicodeLineBreakClass(kbts_u32 Codepoint) { - return kbts_UnicodeLineBreakClass_Data[((kbts_un)kbts_UnicodeLineBreakClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; + return (Codepoint < 1114110) ? kbts__UnicodeLineBreakClass_Data[((kbts_un)kbts__UnicodeLineBreakClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; } -static kbts_u8 kbts_UnicodeGraphemeBreakClass_PageIndices[8703] = { +static kbts_u8 kbts__UnicodeGraphemeBreakClass_PageIndices[8703] = { 0,1,2,2,2,2,3,2,2,4,2,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25, 26,27,28,29,2,2,30,2,2,2,2,2,2,2,31,32,33,34,35,2,36,37,38,39,40,41,2,42,2,2,2,2, 43,44,45,46,2,2,47,48,2,49,2,50,51,52,53,54,2,2,55,2,2,2,56,2,2,57,58,59,2,2,2,2, @@ -6043,7 +7553,7 @@ static kbts_u8 kbts_UnicodeGraphemeBreakClass_PageIndices[8703] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, }; -static kbts_u8 kbts_UnicodeGraphemeBreakClass_Data[20096] = { +static kbts_u8 kbts__UnicodeGraphemeBreakClass_Data[20096] = { 3,3,3,3,3,3,3,3,3,3,2,0,3,1,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,16,0,0,0,3,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -6360,12 +7870,12 @@ static kbts_u8 kbts_UnicodeGraphemeBreakClass_Data[20096] = { 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, }; -KBTS_INLINE kbts_u8 kbts_GetUnicodeGraphemeBreakClass(kbts_u32 Codepoint) +KBTS_INLINE kbts_u8 kbts__GetUnicodeGraphemeBreakClass(kbts_u32 Codepoint) { - return kbts_UnicodeGraphemeBreakClass_Data[((kbts_un)kbts_UnicodeGraphemeBreakClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; + return (Codepoint < 1114110) ? kbts__UnicodeGraphemeBreakClass_Data[((kbts_un)kbts__UnicodeGraphemeBreakClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; } -static kbts_u8 kbts_UnicodeSyllabicInfo_PageIndices[8703] = { +static kbts_u8 kbts__UnicodeSyllabicInfo_PageIndices[8703] = { 0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,3,4,2,5,6,7,8,9,10,11,12,13,14,15,16,17,18, 19,20,2,2,2,2,2,2,2,2,2,2,2,2,21,22,23,24,25,26,27,28,29,30,31,32,2,33,2,2,2,2, 34,35,2,2,2,2,2,2,2,2,2,36,2,2,2,2,2,2,2,2,2,2,2,2,2,2,37,2,2,2,2,2, @@ -6640,7 +8150,7 @@ static kbts_u8 kbts_UnicodeSyllabicInfo_PageIndices[8703] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, }; -static kbts_u16 kbts_UnicodeSyllabicInfo_Data[12160] = { +static kbts_u16 kbts__UnicodeSyllabicInfo_Data[12160] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,1034,0,0,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -7023,816 +8533,45 @@ static kbts_u16 kbts_UnicodeSyllabicInfo_Data[12160] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; -KBTS_INLINE kbts_u16 kbts_GetUnicodeSyllabicInfo(kbts_u32 Codepoint) +KBTS_INLINE kbts_u16 kbts__GetUnicodeSyllabicInfo(kbts_u32 Codepoint) { - return kbts_UnicodeSyllabicInfo_Data[((kbts_un)kbts_UnicodeSyllabicInfo_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; + return (Codepoint < 1114110) ? kbts__UnicodeSyllabicInfo_Data[((kbts_un)kbts__UnicodeSyllabicInfo_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; } -static kbts_u8 kbts_UnicodeScript_PageIndices[8703] = { - 0,1,2,2,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, - 30,31,32,32,33,34,35,36,37,37,37,37,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,2,2,53,54, - 55,56,57,58,59,59,59,59,60,59,59,59,59,59,59,59,61,61,59,59,59,59,62,63,64,65,66,67,68,61,61,69, - 70,71,72,73,74,75,76,59,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,77,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 78,78,78,78,78,78,78,78,78,79,80,80,81,82,83,84,85,86,87,88,89,90,91,92,32,32,32,32,32,32,32,32, - 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, - 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, - 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,93,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,94,95,96,96,97,98,99,100,101,102, - 103,104,105,106,61,107,108,109,110,111,112,113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132, - 133,134,135,136,137,138,139,140,141,142,61,143,144,145,146,61,147,148,149,150,151,152,153,154,155,156,157,158,61,159,160,161, - 162,162,162,162,162,162,162,163,164,162,165,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,166, - 167,167,167,167,167,167,167,167,168,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, - 167,167,167,167,167,167,167,169,170,170,170,170,171,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,172,61,61,61,61,61,61,61,61,61,61,61,61,61,173,173,173,173,174,175,176,177,61,61,178,61,179,180,181,182, - 183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183, - 183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,184,183,183,183,183,183,183,185,185,185,186,187,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,188, - 189,190,191,192,192,193,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,194,195,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,59,196,59,59,59,197,198,199, - 59,200,201,202,203,204,205,61,206,207,208,59,59,209,59,210,211,211,211,211,211,212,61,61,61,61,61,61,61,61,213,61, - 214,215,216,61,61,217,61,61,61,218,61,219,61,61,61,220,221,222,223,61,61,61,61,61,224,225,226,61,227,228,61,61, - 229,230,59,231,232,61,59,59,59,59,59,59,59,233,234,235,236,237,59,59,238,239,59,240,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 241,61,242,243,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, -}; - -static kbts_u8 kbts_UnicodeScript_Data[31232] = { - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,29,29,29,29,29,29,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,72,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,72,29,29,29,29,29, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,29,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,29,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,72,72,72,72,72,29,29,29,29,29,13,13,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,45,45,45,45,29,45,45,45,0,0,45,45,45,45,29,45, - 0,0,0,0,45,29,45,29,45,45,45,0,45,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, - 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,25,25,25,25,25,25,25,25,25,25,25,25,25,25,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, - 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,30,30,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,0,0,5,5,5,0,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54, - 54,54,54,54,54,54,54,54,0,0,0,0,0,0,0,0,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,0,0,0,0,54,54,54,54,54,54,0,0,0,0,0,0,0,0,0,0,0, - 4,4,4,4,4,29,4,4,4,4,4,4,29,4,4,4,4,4,4,4,4,4,4,4,4,4,4,29,4,4,4,29,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 29,4,4,4,4,4,4,4,4,4,4,30,30,30,30,30,30,30,30,30,30,30,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,30,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,29,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 143,143,143,143,143,143,143,143,143,143,143,143,143,143,0,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, - 143,143,143,143,143,143,143,143,143,143,143,0,0,143,143,143,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,0,0,103,103,103, - 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,0,0,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,0, - 83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,0,0,83,0,143,143,143,143,143,143,143,143,143,143,143,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,4,4,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,29,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, - 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,30,30,30,30,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,29,29,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, - 11,11,11,11,0,11,11,11,11,11,11,11,11,0,0,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,0,11,11,11,11,11,11,11,0,11,0,0,0,11,11,11,11,0,0,11,11,11,11, - 11,11,11,11,11,0,0,11,11,0,0,11,11,11,11,0,0,0,0,0,0,0,0,11,0,0,0,0,11,11,0,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,0, - 0,48,48,48,0,48,48,48,48,48,48,0,0,0,0,48,48,0,0,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,0,48,48,48,48,48,48,48,0,48,48,0,48,48,0,48,48,0,0,48,0,48,48, - 48,48,48,0,0,0,0,48,48,0,0,48,48,48,0,0,0,48,0,0,0,0,0,0,0,48,48,48,48,0,48,0,0,0,0,0,0,0,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,0,0,0,0,0,0,0,0,0, - 0,46,46,46,0,46,46,46,46,46,46,46,46,46,0,46,46,46,0,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,0,46,46,46,46,46,46,46,0,46,46,0,46,46,46,46,46,0,0,46,46,46,46, - 46,46,46,46,46,46,0,46,46,46,0,46,46,46,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,46,46,46,0,0,46,46,46,46,46,46,46,46,46,46,46,46,0,0,0,0,0,0,0,46,46,46,46,46,46,46, - 0,118,118,118,0,118,118,118,118,118,118,118,118,0,0,118,118,0,0,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,0,118,118,118,118,118,118,118,0,118,118,0,118,118,118,118,118,0,0,118,118,118,118, - 118,118,118,118,118,0,0,118,118,0,0,118,118,118,0,0,0,0,0,0,0,118,118,118,0,0,0,0,118,118,0,118,118,118,118,118,0,0,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,0,0,0,0,0,0,0,0, - 0,0,150,150,0,150,150,150,150,150,150,0,0,0,150,150,150,0,150,150,150,150,0,0,0,150,150,0,150,0,150,150,0,0,0,150,150,0,0,0,150,150,150,0,0,0,150,150,150,150,150,150,150,150,150,150,150,150,0,0,0,0,150,150, - 150,150,150,0,0,0,150,150,150,0,150,150,150,150,0,0,150,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,0,0,0,0,0, - 153,153,153,153,153,153,153,153,153,153,153,153,153,0,153,153,153,0,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,0,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,0,0,153,153,153,153, - 153,153,153,153,153,0,153,153,153,0,153,153,153,153,0,0,0,0,0,0,0,153,153,0,153,153,153,0,0,153,0,0,153,153,153,153,0,0,153,153,153,153,153,153,153,153,153,153,0,0,0,0,0,0,0,153,153,153,153,153,153,153,153,153, - 61,61,61,61,61,61,61,61,61,61,61,61,61,0,61,61,61,0,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,0,61,61,61,61,61,61,61,61,61,61,0,61,61,61,61,61,0,0,61,61,61,61, - 61,61,61,61,61,0,61,61,61,0,61,61,61,61,0,0,0,0,0,0,0,61,61,0,0,0,0,0,0,61,61,0,61,61,61,61,0,0,61,61,61,61,61,61,61,61,61,61,0,61,61,61,0,0,0,0,0,0,0,0,0,0,0,0, - 82,82,82,82,82,82,82,82,82,82,82,82,82,0,82,82,82,0,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82, - 82,82,82,82,82,0,82,82,82,0,82,82,82,82,82,82,0,0,0,0,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,0,0,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82, - 0,136,136,136,0,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,0,0,0,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,0,136,136,136,136,136,136,136,136,136,0,136,0,0, - 136,136,136,136,136,136,136,0,0,0,136,0,0,0,0,136,136,136,136,136,136,0,136,0,136,136,136,136,136,136,136,136,0,0,0,0,0,0,136,136,136,136,136,136,136,136,136,136,0,0,136,136,136,0,0,0,0,0,0,0,0,0,0,0, - 0,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,0,0,0,0,29, - 155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,71,71,0,71,0,71,71,71,71,71,0,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,0,71,0,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,0,0, - 71,71,71,71,71,0,71,0,71,71,71,71,71,71,71,0,71,71,71,71,71,71,71,71,71,71,0,0,71,71,71,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, - 156,156,156,156,156,156,156,156,0,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,0,0,0,0,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, - 156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,0,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,0,156,156, - 156,156,156,156,156,156,156,156,156,156,156,156,156,0,156,156,156,156,156,156,156,29,29,29,29,156,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, - 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, - 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41, - 41,41,41,41,41,41,0,41,0,0,0,0,0,41,0,0,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,29,41,41,41,41, - 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, - 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, - 39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39, - 39,39,39,39,39,39,39,39,39,0,39,39,39,39,0,0,39,39,39,39,39,39,39,0,39,0,39,39,39,39,0,0,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39, - 39,39,39,39,39,39,39,39,39,0,39,39,39,39,0,0,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0,39,39,39,39,0,0,39,39,39,39,39,39,39,0, - 39,0,39,39,39,39,0,0,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39, - 39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0,39,39,39,39,0,0,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39, - 39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0,0,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0,0,0, - 39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0,0,0,0,0,0,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, - 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,0,0,22,22,22,22,22,22,0,0, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,0,0,0,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, - 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,29,29,29,128,128,128,128,128,128,128,128,128,128,128,0,0,0,0,0,0,0, - 144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,0,0,0,0,0,0,0,0,0,144,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,29,29,0,0,0,0,0,0,0,0,0, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,145,145,145,145,145,145,145,145,145,145,145,145,145,0,145,145,145,0,145,145,0,0,0,0,0,0,0,0,0,0,0,0, - 67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67, - 67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,0,0,67,67,67,67,67,67,67,67,67,67,0,0,0,0,0,0,67,67,67,67,67,67,67,67,67,67,0,0,0,0,0,0, - 94,94,29,29,94,29,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,0,0,0,0,0,0,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94, - 94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,0,0,0,0,0,0,0, - 94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,0,0,0,0,0,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,0,0,0,0,0,0,0,0,0,0, - 74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,0,74,74,74,74,74,74,74,74,74,74,74,74,0,0,0,0,74,74,74,74,74,74,74,74,74,74,74,74,0,0,0,0, - 74,0,0,0,74,74,74,74,74,74,74,74,74,74,74,74,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,0,0,146,146,146,146,146,0,0,0,0,0,0,0,0,0,0,0, - 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102, - 102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,102,102,102,102,102,102,102,102,102,102,102,0,0,0,102,102,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,15,15,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147, - 147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,0,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,0,0,147, - 147,147,147,147,147,147,147,147,147,147,0,0,0,0,0,0,147,147,147,147,147,147,147,147,147,147,0,0,0,0,0,0,147,147,147,147,147,147,147,147,147,147,147,147,147,147,0,0,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, - 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,10,10,10,10, - 73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,0,0,0,73,73,73,73,73, - 73,73,73,73,73,73,73,73,73,73,0,0,0,73,73,73,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, - 28,28,28,28,28,28,28,28,28,28,28,0,0,0,0,0,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,0,0,41,41,41, - 140,140,140,140,140,140,140,140,0,0,0,0,0,0,0,0,30,30,30,29,30,30,30,30,30,30,30,30,30,30,30,30,30,29,30,30,30,30,30,30,30,29,29,29,29,30,29,29,29,29,29,29,30,29,29,29,30,30,29,0,0,0,0,0, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,45,45,45,45,45,28,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,45,45,45,45,45,72,72,72,72,45,45,45,45,45,72,72,72,72,72,72,72,72,72,72,72,72,72,28,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,45, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, - 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,0,0,45,45,45,45,45,45,0,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, - 45,45,45,45,45,45,0,0,45,45,45,45,45,45,0,0,45,45,45,45,45,45,45,45,0,45,0,45,0,45,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,0,0, - 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,0,45,45,45,45,45,45,45,45,45,45, - 45,45,45,45,45,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,0,0,45,45,45,45,45,45,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,0,0,45,45,45,0,45,45,45,45,45,45,45,45,45,0, - 29,29,29,29,29,29,29,29,29,29,29,29,30,30,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,72,0,0,29,29,29,29,29,29,29,29,29,29,29,72, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,72,72,72,72,72,72,72,72,72,72,72,72,72,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,45,29,29,29,72,72,29,29,29,29,29,29,72,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,72,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,29,29,29,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42, - 42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, - 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,0,0,0,0,0,25,25,25,25,25,25,25, - 41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,0,41,0,0,0,0,0,41,0,0,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157, - 157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,0,0,0,0,0,0,0,157,157,0,0,0,0,0,0,0,0,0,0,0,0,0,0,157, - 39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0,0,0,0,0,0,0,0,0,39,39,39,39,39,39,39,0,39,39,39,39,39,39,39,0,39,39,39,39,39,39,39,0,39,39,39,39,39,39,39,0, - 39,39,39,39,39,39,39,0,39,39,39,39,39,39,39,0,39,39,39,39,39,39,39,0,39,39,39,39,39,39,39,0,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,0,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,30,30,30,30,50,50,29,29,29,29,29,29,29,29,0,0,0,0,29,29,29,29, - 0,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55, - 55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,0,0,30,30,29,29,55,55,55,29,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62, - 62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,29,29,62,62,62, - 0,0,0,0,0,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, - 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, - 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,29,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62, - 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,29, - 62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62, - 62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, - 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, - 168,168,168,168,168,168,168,168,168,168,168,168,168,0,0,0,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, - 168,168,168,168,168,168,168,0,0,0,0,0,0,0,0,0,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, - 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, - 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, - 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,29,29,29,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,0,0,72,72,0,72,0,72,72,72,72,72,72,72,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,0,0,0,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0, - 124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,0,0,0,0,0,0,0,0, - 130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130, - 130,130,130,130,130,130,0,0,0,0,0,0,0,0,130,130,130,130,130,130,130,130,130,130,130,130,0,0,0,0,0,0,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, - 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,29,64,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, - 127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,0,0,0,0,0,0,0,0,0,0,0,127,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0, - 59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59, - 59,59,59,59,59,59,59,59,59,59,59,59,59,59,0,29,59,59,59,59,59,59,59,59,59,59,0,0,0,0,59,59,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,0, - 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,0,0,0,0,0,0,0,0,0, - 21,21,21,21,21,21,21,21,21,21,21,21,21,21,0,0,21,21,21,21,21,21,21,21,21,21,0,0,21,21,21,21,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, - 148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, - 148,148,148,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,148,148,148,148,148,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,0,0,0,0,0,0,0,0,0, - 0,39,39,39,39,39,39,0,0,39,39,39,39,39,39,0,0,39,39,39,39,39,39,0,0,0,0,0,0,0,0,0,39,39,39,39,39,39,39,0,39,39,39,39,39,39,39,0,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,29,72,72,72,72,72,72,72,72,72,45,72,72,72,72,29,29,0,0,0,0,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, - 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, - 88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,0,0,88,88,88,88,88,88,88,88,88,88,0,0,0,0,0,0, - 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, - 50,50,50,50,50,50,50,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0, - 72,72,72,72,72,72,72,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,0,0,0,0,0,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,0,54,54,54,54,54,0,54,0, - 54,54,0,54,54,0,54,54,54,54,54,54,54,54,54,54,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,29,29, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,30,30,30,30,30,30,30,30,30,30,30,30,30,30,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,29,29,0,0,0,0,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,29, - 0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,29,29,29,29,29, - 29,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,29,29,29,29,29,29,29,29,29,29,29,62,62,62,62,62,62,62,62,62,62,29,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62, - 62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,29,29,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0, - 0,0,50,50,50,50,50,50,0,0,50,50,50,50,50,50,0,0,50,50,50,50,50,50,0,0,50,50,50,0,0,0,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,0,0, - 76,76,76,76,76,76,76,76,76,76,76,76,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,0,76,76,0,76, - 76,76,76,76,76,76,76,76,76,76,76,76,76,76,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76, - 76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,0,0,0,0,0, - 29,29,29,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,29,29,29,29,29,29,29,29,29, - 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, - 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,0,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,0,0, - 78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,0,0,0,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, - 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0, - 109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,0,0,0,0,0,0,0,0,0,109,109,109,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43, - 43,43,43,43,43,43,43,43,43,43,43,0,0,0,0,0,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,0,0,0,0,0, - 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,0,162,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, - 113,113,113,113,0,0,0,0,113,113,113,113,113,113,113,113,113,113,113,113,113,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31, - 31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, - 120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,0,0,120,120,120,120,120,120,120,120,120,120,0,0,0,0,0,0,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119, - 119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,0,0,0,0,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,0,0,0,0, - 37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,0,0,0,0,0,0,0,0,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, - 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,0,0,0,0,0,0,0,0,0,0,0,19,164,164,164,164,164,164,164,164,164,164,164,0,164,164,164,164, - 164,164,164,164,164,164,164,164,164,164,164,0,164,164,164,164,164,164,164,0,164,164,0,164,164,164,164,164,164,164,164,164,164,164,0,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,0,164,164,164,164,164,164,164,0,164,164,0,0,0, - 159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,0,0,0,0,0,0,0,0,0,0,0,0, - 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, - 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, - 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,0,0,0,0,0,0,0,0,0, - 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,0,0,0,0,0,0,0,0,0,0,75,75,75,75,75,75,75,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 72,72,72,72,72,72,0,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,0,72,72,72,72,72,72,72,72,72,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 26,26,26,26,26,26,0,0,26,0,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,0,26,26,0,0,0,26,0,0,26, - 56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,0,56,56,56,56,56,56,56,56,56,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, - 98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,0,0,0,0,0,0,0,0,98,98,98,98,98,98,98,98,98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,0,53,53,0,0,0,0,0,53,53,53,53,53, - 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,0,0,0,125,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,0,0,0,0,0,79, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,0,0,0,0,90,90,90,90, - 90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,0,0,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90, - 65,65,65,65,0,65,65,0,0,0,0,0,65,65,65,65,65,65,65,65,0,65,65,65,0,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,0,0,65,65,65,0,0,0,0,65, - 65,65,65,65,65,65,65,65,65,0,0,0,0,0,0,0,65,65,65,65,65,65,65,65,65,0,0,0,0,0,0,0,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,0,0,0,0,84,84,84,84,84,84,84,84,84,84,84,84,0,0,0,0,0,0,0,0,0, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,6,6,6,6,6,6,6, - 58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,0,0,58,58,58,58,58,58,58,58,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,0,0,0,0,0,57,57,57,57,57,57,57,57, - 126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,0,0,0,0,0,0,0,126,126,126,126,0,0,0,0,0,0,0,0,0,0,0,0,126,126,126,126,126,126,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, - 116,116,116,116,116,116,116,116,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,0,0,0,0,0,0,0,0,0,0,0,0,0, - 110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,0,0,0,0,0,0,0,110,110,110,110,110,110, - 51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,0,0,0,0,0,0,0,0,51,51,51,51,51,51,51,51,51,51,0,0,0,0,0,0, - 40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,0,0,0,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40, - 40,40,40,40,40,40,0,0,0,0,0,0,0,0,40,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0, - 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,0,167,167,167,0,0,167,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, - 114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,0,0,0,0,0,0,0,0,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135, - 135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117, - 117,117,117,117,117,117,117,117,117,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, - 23,23,23,23,23,23,23,23,23,23,23,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,0,0,0,0,0,0,0,0,0, - 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,0,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,0,0,0,0,0,0,14, - 60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60, - 60,60,60,0,0,0,0,0,0,0,0,0,0,60,0,0,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,0,0,0,0,0,0,0,137,137,137,137,137,137,137,137,137,137,0,0,0,0,0,0, - 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,0,20,20,20,20,20,20,20,20,20,20, - 20,20,20,20,20,20,20,20,0,0,0,0,0,0,0,0,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,0,0,0,0,0,0,0,0,0, - 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, - 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,0,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,0,0,0,0,0,0,0,0,0,0,0, - 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,0,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, - 68,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 96,96,96,96,96,96,96,0,96,0,96,96,96,96,0,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,0,96,96,96,96,96,96,96,96,96,96,96,0,0,0,0,0,0,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69, - 69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,0,0,0,0,0,69,69,69,69,69,69,69,69,69,69,0,0,0,0,0,0, - 44,44,44,44,0,44,44,44,44,44,44,44,44,0,0,44,44,0,0,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,0,44,44,44,44,44,44,44,0,44,44,0,44,44,44,44,44,0,30,44,44,44,44, - 44,44,44,44,44,0,0,44,44,0,0,44,44,44,0,0,44,0,0,0,0,0,0,44,0,0,0,0,0,44,44,44,44,44,44,44,0,0,44,44,44,44,44,44,44,0,0,0,44,44,44,44,44,0,0,0,0,0,0,0,0,0,0,0, - 161,161,161,161,161,161,161,161,161,161,0,161,0,0,161,0,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,0,161,161,161,161,161,161,161,161,161, - 161,0,161,0,0,161,0,161,161,161,161,0,161,161,161,161,161,161,161,161,161,161,0,161,161,0,0,0,0,0,0,0,0,161,161,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, - 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,0,101,101,101,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158, - 158,158,158,158,158,158,158,158,0,0,0,0,0,0,0,0,158,158,158,158,158,158,158,158,158,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,0,0,133,133,133,133,133,133,133,133, - 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93, - 93,93,93,93,93,0,0,0,0,0,0,0,0,0,0,0,93,93,93,93,93,93,93,93,93,93,0,0,0,0,0,0,94,94,94,94,94,94,94,94,94,94,94,94,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,0,0,0,0,0,0, - 149,149,149,149,149,149,149,149,149,149,0,0,0,0,0,0,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, - 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,0,0,0,0,0,0,0,0,0,0,0,0,166, - 33,33,33,33,33,33,33,0,0,33,0,0,33,33,33,33,33,33,33,33,0,33,33,0,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,0,33,33,0,0,33,33,33,33,33, - 33,33,33,33,33,33,33,0,0,0,0,0,0,0,0,0,33,33,33,33,33,33,33,33,33,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,100,100,100,0,0,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,0,0,100,100,100,100,100,100,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, - 169,169,169,169,169,169,169,169,0,0,0,0,0,0,0,0,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138, - 138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,0,0,0,0,0,0,0, - 32,32,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,141,141,141,141,141,141,141,141,141,0,0,0,0,0,0, - 12,12,12,12,12,12,12,12,12,0,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,0,12,12,12,12,12,12,12,12, - 12,12,12,12,12,12,0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,0,0,0,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85, - 85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,0,0,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,0,85,85,85,85,85,85,85,85,85,85,85,85,85,85,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 86,86,86,86,86,86,86,0,86,86,0,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,0,0,0,86,0,86,86,0,86, - 86,86,86,86,86,86,86,86,0,0,0,0,0,0,0,0,86,86,86,86,86,86,86,86,86,86,0,0,0,0,0,0,47,47,47,47,47,47,0,47,47,0,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47, - 47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,0,47,47,0,47,47,47,47,47,47,0,0,0,0,0,0,0,47,47,47,47,47,47,47,47,47,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,0,0,0,0,0,0,0, - 63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,0,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,0,0,0,63,63, - 63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,150, - 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, - 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, - 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, - 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,0,139,139,139,139,139,0,0,0,0,0,0,0,0,0,0,0, - 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, - 139,139,139,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, - 27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,0,0,0,0,0,0,0,0,0,0,0,0,0, - 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,0,0,0,0,0,0,0,0,0,0,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,0,0,0,0,0, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0, - 95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,0,95,95,95,95,95,95,95,95,95,95,0,0,0,0,95,95,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151, - 151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,0, - 151,151,151,151,151,151,151,151,151,151,0,0,0,0,0,0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,0,9,9,9,9,9,9,0,0,0,0,0,0,0,0,0,0, - 121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, - 121,121,121,121,121,121,0,0,0,0,0,0,0,0,0,0,121,121,121,121,121,121,121,121,121,121,0,121,121,121,121,121,121,121,0,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,0,0,0,0,0,121,121,121, - 121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87, - 87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92, - 92,92,92,92,92,92,92,92,92,92,92,0,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92, - 92,92,92,92,92,92,92,92,0,0,0,0,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,152,104,0,0,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, - 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, - 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, - 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,0,0,0,0,0,0,0,0, - 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, - 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, - 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, - 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66, - 152,152,152,152,152,152,152,152,152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,62,62,62,0,62,62,62,62,62,62,62,0,62,62,0, - 62,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55, - 55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55, - 55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55, - 55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55, - 55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,62,62,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,55,55,0,0,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,62,62,62,0,0,0,0,0,0,0,0,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, - 104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, - 104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, - 104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104, - 104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,0,0,0,0, - 35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35, - 35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,0,0,0,0,0,35,35,35,35,35,35,35,35,35,35,35,35,35,0,0,0, - 35,35,35,35,35,35,35,35,35,0,0,0,0,0,0,0,35,35,35,35,35,35,35,35,35,35,0,0,35,35,35,35,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,0,0,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, - 30,30,30,30,30,30,30,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,30,30,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30, - 30,30,30,29,29,30,30,30,30,30,30,30,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,30,30,30,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, - 45,45,45,45,45,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,0,0,29,0,0,29,29,0,0,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,0,29,0,29,29,29, - 29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,0,29,29,29,29,0,0,29,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,29,29,0, - 29,29,29,29,29,0,29,0,0,0,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134, - 134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134, - 134,134,134,134,134,134,134,134,134,134,134,134,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,134,134,134,134,134,0,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,0,0,0,0,0,0,72,72,72,72,72,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 42,42,42,42,42,42,42,0,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,0,0,42,42,42,42,42,42,42,0,42,42,0,42,42,42,42,42,0,0,0,0,0,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,0,0,0,105,105,105,105,105,105,105,105,105,105,105,105,105,105,0,0, - 105,105,105,105,105,105,105,105,105,105,0,0,0,0,105,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,0,0,0,0,0,165, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,0,0,0,0,108, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,39,39,39,39,39,39,0,39,39,39,39,0,39,39,0,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0, - 89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, - 89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, - 89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89, - 89,89,89,89,89,0,0,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,4,4,0,4,0,0,4,0,4,4,4,4,4,4,4,4,4,4,0,4,4,4,4,0,4,0,4,0,0,0,0, - 0,0,4,0,0,0,0,4,0,4,0,4,0,4,4,4,0,4,4,0,4,0,0,4,0,4,0,4,0,4,0,4,0,4,4,0,4,0,0,4,4,4,4,0,4,4,4,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,0, - 4,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,4,4,4,0,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 55,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0, - 29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0, - 29,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0, - 29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0,0, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,0,0,0,0,0,0, - 0,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, - 30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -}; - -KBTS_INLINE kbts_u8 kbts_GetUnicodeScript(kbts_u32 Codepoint) -{ - return kbts_UnicodeScript_Data[((kbts_un)kbts_UnicodeScript_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; -} - -static kbts_u8 kbts_UnicodeFlags_PageIndices[8703] = { +static kbts_u8 kbts__UnicodeFlags_PageIndices[8703] = { 0,1,2,3,2,4,5,6,2,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, 30,31,32,33,34,35,36,37,38,33,33,33,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,2,2,2,55, - 56,57,58,59,33,33,60,33,61,33,33,33,33,33,62,63,33,33,33,64,33,33,65,66,67,68,69,70,71,72,33,73, - 74,75,76,77,78,33,33,33,79,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,80,79,2,2,2, + 56,57,58,59,60,61,62,33,63,33,33,33,33,33,64,65,33,33,33,66,67,68,69,70,71,72,73,74,75,76,33,77, + 78,79,80,81,82,33,33,33,83,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,84,83,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,81, - 33,33,33,33,33,33,33,33,33,82,33,33,83,84,85,86,87,88,89,90,91,92,93,94,79,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,85, + 33,33,33,33,33,33,33,33,33,86,33,33,87,88,89,90,91,92,93,94,95,96,97,98,83,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,95,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 79,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,81,33,33,96,97,98,99,33,33,100,101,102,103,104,105, - 106,107,108,109,2,110,111,112,113,114,115,116,33,33,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, - 135,136,137,138,139,140,141,142,143,144,2,145,146,147,148,2,149,150,151,152,153,154,2,155,156,157,158,159,2,160,161,162, - 33,33,33,33,33,33,33,37,163,33,164,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,165, - 33,33,33,33,33,33,33,33,166,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, - 33,33,33,33,33,33,33,107,33,33,33,33,167,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,99,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 83,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,85,33,33,100,101,102,103,33,33,104,105,106,107,108,109, + 110,111,112,113,2,114,115,116,117,118,119,120,33,33,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138, + 139,140,141,142,143,144,145,146,147,148,2,149,150,151,152,2,153,154,155,156,157,158,2,159,160,161,162,163,2,164,165,166, + 33,33,33,33,33,33,33,37,167,33,168,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,169, + 33,33,33,33,33,33,33,33,170,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, + 33,33,33,33,33,33,33,111,33,33,33,33,171,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,168,2,2,2,2,2,2,2,2,2,2,2,2,2,33,33,33,33,169,170,171,172,2,2,173,2,2,174,175,176, - 79,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,177,33,33,33,33,33,33,33,33,33,178,179,2,2,2,2,2, + 2,2,172,2,2,2,2,2,2,2,2,2,2,2,2,2,33,33,33,33,173,174,175,176,2,2,177,2,2,178,179,180, + 83,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,181,33,33,33,33,33,33,33,33,33,182,183,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,180, - 33,33,181,33,33,182,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,183,184,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,33,185,33,33,33,186,187,164, - 33,188,189,190,191,192,193,2,2,2,2,2,2,194,195,196,33,33,33,33,197,198,2,2,2,2,2,2,2,2,199,2, - 200,201,202,2,2,203,2,2,2,204,2,205,2,2,2,206,33,207,208,2,2,2,2,2,209,210,211,2,212,213,2,2, - 214,215,33,216,217,2,33,33,33,33,33,33,33,218,219,220,221,222,33,33,223,224,33,225,2,2,2,2,2,2,2,2, - 79,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,184, + 33,33,185,33,33,186,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,187,188,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,33,189,33,33,33,190,191,168, + 33,192,193,194,195,196,197,2,2,2,2,2,2,198,199,200,33,33,33,33,201,202,2,2,2,2,2,2,2,2,203,2, + 204,205,206,2,2,207,2,2,2,208,2,209,2,2,2,210,33,211,212,2,2,2,2,2,213,214,215,2,216,217,2,2, + 218,219,33,220,221,2,33,33,33,33,33,33,33,222,223,224,225,226,33,33,227,228,33,229,2,2,2,2,2,2,2,2, + 83,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, @@ -7842,15 +8581,15 @@ static kbts_u8 kbts_UnicodeFlags_PageIndices[8703] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,226,79,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,227,2,228,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,229,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,230,83,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,231,2,232,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,233,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,230,2,2,2,2,231,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,33,33,33,33,232,2,2,2,2,2,2,2,2,2,2,2, - 79,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,233,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,234,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,234,2,2,2,2,235,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,33,33,33,33,236,2,2,2,2,2,2,2,2,2,2,2, + 83,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,237,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,238,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, @@ -8024,7 +8763,7 @@ static kbts_u8 kbts_UnicodeFlags_PageIndices[8703] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 235,235,236,237,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235, + 239,239,240,241,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239,239, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, @@ -8040,7 +8779,7 @@ static kbts_u8 kbts_UnicodeFlags_PageIndices[8703] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 79,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 83,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, @@ -8055,8 +8794,8 @@ static kbts_u8 kbts_UnicodeFlags_PageIndices[8703] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,238, - 79,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,242, + 83,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, @@ -8074,10 +8813,10 @@ static kbts_u8 kbts_UnicodeFlags_PageIndices[8703] = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, }; -static kbts_u8 kbts_UnicodeFlags_Data[30592] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,4,0,0,16,0,0,0,0,48,48,48,48,48,48,48,48,48,48,0,0,16,16,16,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,16,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,0,16,16,16,0,16,2,16,16,16,16,16,16,16,0,0,0,16,16,16,0,16,16,16,0, +static kbts_u8 kbts__UnicodeFlags_Data[31104] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,4,8,0,16,0,0,0,0,48,48,48,48,48,48,48,48,48,48,0,0,28,16,28,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,8,16,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,16,8,16,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,0,16,16,16,12,16,2,16,16,16,16,16,16,16,0,0,0,16,16,16,12,16,16,16,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -8131,7 +8870,7 @@ static kbts_u8 kbts_UnicodeFlags_Data[30592] = { 16,16,16,16,16,16,16,80,80,80,80,80,80,80,80,0,48,48,48,48,48,48,48,48,48,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,16,16,0,16,0,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,16,0,16,16,16,16,16,16,16,16,16,16,80,16,16,80,80,80,80,80,80,80,80,80,16,0,0, 16,16,16,16,16,0,16,0,80,80,80,80,80,80,80,0,48,48,48,48,48,48,48,48,48,48,0,0,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,16,16,16,80,80,16,16,16,16,16,16,48,48,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,16,16,16,80,16,80,16,80,0,0,0,0,16,16, + 16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,16,16,16,80,80,16,16,16,16,16,16,48,48,48,48,48,48,48,48,48,48,16,16,16,16,16,16,16,16,16,16,16,80,16,80,16,80,4,8,4,8,16,16, 16,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,80,80,80,80,80,80,80,80,80,80,80,80,80,80,16, 80,80,80,80,80,0,80,80,16,16,16,16,16,80,80,80,80,80,80,80,80,80,80,80,0,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,0,16,16, 16,16,16,16,16,16,80,16,16,16,16,16,16,0,16,16,0,0,0,0,0,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -8155,7 +8894,7 @@ static kbts_u8 kbts_UnicodeFlags_Data[30592] = { 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,4,8,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,80,80,80,16,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,80,80,16,0,0,0,0,0,0,0,0,0,0,0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,80,80,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16,16,0,80,80,0,0,0,0,0,0,0,0,0,0,0,0, @@ -8187,28 +8926,36 @@ static kbts_u8 kbts_UnicodeFlags_Data[30592] = { 80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,16, 16,16,0,0,0,0,0,0,0,0,0,0,0,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,0, - 0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,16,16,0,0,16,16,16,16,16,16,16,16,16,0,0,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,12,12,0,0,0,0,0, + 0,0,0,0,16,4,8,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,16,16,0,0,16,16,16,16,16,16,16,16,16,4,8,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,4,8,0,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,80,80,80,80,80,80,80,80,80,80,80,80,16,16,16,16,80,16,16,16,80,80,80,80,80,80,80,80,80,80,80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 16,16,0,16,16,16,16,0,16,16,0,0,0,0,0,0,0,0,0,0,16,0,16,16,16,0,0,0,0,0,16,16,16,16,16,16,0,16,0,16,0,16,0,0,0,0,16,0,0,0,0,0,0,16,16,16,16,0,16,16,0,0,0,0, 16,16,16,16,16,0,0,0,0,0,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,0,0,16,16,16,16,16,16,16,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,28,28,28,28,28,28,16,16,16,16,16,16,16,28,16,16,16,16,16,16,16,16,16,28,28,28,28,16,28,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,28,28,16,16, + 16,16,16,28,16,28,16,16,16,16,16,16,28,16,16,16,16,16,28,28,28,28,16,16,16,16,16,16,16,16,16,16,16,16,16,16,28,28,28,28,28,28,28,28,16,16,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, + 28,28,28,28,28,28,28,28,28,28,28,28,16,16,16,28,28,28,28,16,16,16,16,16,28,16,16,16,16,16,16,16,16,16,28,28,16,16,28,16,28,28,16,28,16,16,16,16,28,28,28,28,28,28,28,28,28,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,28,28,28,28,28,16,16,28,28,16,16,16,16,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,16,16,28,28,28,28,28,16,28,28,16,16,28,28,28,28,28,16, + 16,16,16,16,16,16,16,16,4,8,4,8,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,4,8,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,4,8,4,8,4,8,4,8,4,8,4,8,4,8,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,16,16, + 16,16,16,28,28,4,8,16,28,28,16,28,16,28,16,16,16,16,16,16,16,28,28,16,16,16,16,16,28,28,28,16,16,16,28,28,28,28,4,8,4,8,4,8,4,8,4,8,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,4,8,4,8,4,8,4,8,4,8,4,8,4,8,4,8,4,8,4,8,4,8,16,16,28,16,16,16,16,28,16,16,28,28,28,16,16,28,28,28,28,28,28,28,28,16,16,16,16,16,16,16,16,28,16,16,16,16,16,16,16, + 28,28,16,16,28,28,16,16,16,16,16,16,16,16,16,28,28,28,28,16,28,28,16,16,4,8,4,8,16,16,16,16,16,16,16,16,16,16,16,16,28,28,16,16,16,16,16,16,16,16,16,16,16,28,16,16,28,28,16,16,4,8,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,28,28,28,28,16,16,16,16,16,28,28,16,16,16,16,16,16,28,28,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,28,28,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,28,28,28,28,28,28,28, + 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,16,16,16,28,28,28,28,28,28,28,28,16,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, + 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,16,16,16,16,16,16,16,28,16,16,16,16,28,28,28,16,16,16,16,16,16,28,28,28,16,16,16,16,16,16,16,16,28,28,28,28,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,28,16, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -8217,13 +8964,13 @@ static kbts_u8 kbts_UnicodeFlags_Data[30592] = { 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,0, 16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,0,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,12,12,12,12,0,0,0,12,12,0,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,0,0,12,12,4,8,4,8,4,8,4,8,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,0,0,0,4,8,4,8,4,8,4,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 0,0,0,0,16,16,16,16,0,0,0,0,0,0,0,0,0,0,16,16,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,80,80,80,80,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16, + 0,0,0,0,16,16,16,16,4,8,4,8,4,8,4,8,4,8,16,16,4,8,4,8,4,8,4,8,0,0,0,0,16,16,16,16,16,16,16,16,16,16,80,80,80,80,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16, 0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,80,80,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16,16,16, @@ -8280,11 +9027,11 @@ static kbts_u8 kbts_UnicodeFlags_Data[30592] = { 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,16,16,16,0,0,16,0,0,0,0,0,0,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,4,8,4,8,0,0,0,16,0,28,28,16,0,0,16,0,0,0,0,0,0,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,0,2, - 0,0,0,0,16,0,0,0,0,0,0,16,0,0,0,0,48,48,48,48,48,48,48,48,48,48,0,0,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0, - 16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,16,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0,0,0,0,16,0,0,0,4,8,0,16,0,0,0,0,48,48,48,48,48,48,48,48,48,48,0,0,28,16,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,8,16,0, + 16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,16,8,16,4,8,0,4,8,0,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,18,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0, 0,0,16,16,16,16,16,16,0,0,16,16,16,16,16,16,0,0,16,16,16,16,16,16,0,0,16,16,16,0,0,0,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,0,2,2,2,2,2,2,2,2,2,0,0,0,16,16,0,0, 16,16,16,16,16,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,16,16,0,16, @@ -8555,12 +9302,12 @@ static kbts_u8 kbts_UnicodeFlags_Data[30592] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0, }; -KBTS_INLINE kbts_u8 kbts_GetUnicodeFlags(kbts_u32 Codepoint) +KBTS_INLINE kbts_u8 kbts__GetUnicodeFlags(kbts_u32 Codepoint) { - return kbts_UnicodeFlags_Data[((kbts_un)kbts_UnicodeFlags_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; + return (Codepoint < 1114110) ? kbts__UnicodeFlags_Data[((kbts_un)kbts__UnicodeFlags_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; } -static kbts_u8 kbts_UnicodeBidirectionalClass_PageIndices[8703] = { +static kbts_u8 kbts__UnicodeBidirectionalClass_PageIndices[8703] = { 0,1,2,2,2,3,4,5,2,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, 29,30,2,2,31,32,33,34,35,2,2,2,2,36,37,38,39,40,41,42,43,44,45,46,47,48,2,49,2,2,50,51, 52,53,54,55,56,57,58,59,57,60,57,57,57,61,57,57,2,2,57,57,57,57,57,57,2,62,63,64,57,57,57,57, @@ -8785,7 +9532,7 @@ static kbts_u8 kbts_UnicodeBidirectionalClass_PageIndices[8703] = { 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, - 57,57,228,229,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, + 228,57,229,230,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, @@ -8816,7 +9563,7 @@ static kbts_u8 kbts_UnicodeBidirectionalClass_PageIndices[8703] = { 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, - 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,230, + 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,231, 73,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, @@ -8835,477 +9582,479 @@ static kbts_u8 kbts_UnicodeBidirectionalClass_PageIndices[8703] = { 57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57, }; -static kbts_u8 kbts_UnicodeBidirectionalClass_Data[29568] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,0,0,0,0,0,7,9,7,9,9,6,6,6,6,6,6,6,6,6,6,9,0,0,0,0,0, - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,8,8,8,8,0,0,0,0,1,0,0,0,0,0,8,8,6,6,0,1,0,0,0,6,1,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1, - 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,0,0,1,1,0,0,1,1,1,1,0,1, - 0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1, - 1,1,1,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,8,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3, - 2,3,3,2,3,3,2,3,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0, - 5,5,5,5,5,5,0,0,4,8,8,4,9,4,0,0,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,5,5,8,5,5,4,4,4,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +static kbts_u8 kbts__UnicodeBidirectionalClass_Data[29696] = { + 1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,9,9,9,0,0,0,0,0,8,10,8,10,10,7,7,7,7,7,7,7,7,7,7,10,0,0,0,0,0, + 0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1, + 1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,10,0,9,9,9,9,0,0,0,0,2,0,0,1,0,0,9,9,7,7,0,2,0,0,0,7,2,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2, + 2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,5,0,3,3,3,3,3,3,4,4,3,3,0,3,3,3,3,4,4,6,6,6,6,6,6,6,6,6,6,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,4,4,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3,3,3,3,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,2,2,0,0,0,0,2,0,0,3,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,2,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,3,3,3,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,0,0,2,0,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,5,5,0,0,0,0,0,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1, - 1,3,3,3,3,3,3,3,3,1,1,1,1,3,1,1,1,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,3,1,1,0,1,1,1,1,1,1,1,1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,1,0,0,3,1,1,1, - 1,3,3,3,3,0,0,1,1,0,0,1,1,3,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,1,3,3,0,0,1,1,1,1,1,1,1,1,1,1,1,1,8,8,1,1,1,1,1,1,1,8,1,1,3,0, - 0,3,3,1,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,0,0,3,0,1,1, - 1,3,3,0,0,0,0,3,3,0,0,3,3,3,0,0,0,3,0,0,0,0,0,0,0,1,1,1,1,0,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,3,1,0,0,0,0,0,0,0,0,0, - 0,3,3,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,0,3,1,1,1, - 1,3,3,3,3,3,0,3,3,1,0,1,1,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,3,0,0,1,1,1,1,1,1,1,1,1,1,1,8,0,0,0,0,0,0,0,1,3,3,3,3,3,3, - 0,3,1,1,0,1,1,1,1,1,1,1,1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,0,3,1,1,3, - 1,3,3,3,3,0,0,1,1,0,0,1,1,3,0,0,0,0,0,0,0,3,3,1,0,0,0,0,1,1,0,1,1,1,3,3,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, - 0,0,3,1,0,1,1,1,1,1,1,0,0,0,1,1,1,0,1,1,1,1,0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1, - 3,1,1,0,0,0,1,1,1,0,1,1,1,3,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,8,0,0,0,0,0,0, - 3,1,1,1,3,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,3,1,3,3, - 3,1,1,1,1,0,3,3,3,0,3,3,3,3,0,0,0,0,0,0,0,3,3,0,1,1,1,0,0,1,0,0,1,1,3,3,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, - 1,3,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,3,1,1,1, - 1,1,1,1,1,0,1,1,1,0,1,1,3,3,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,1,1,3,3,0,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, - 3,3,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1, - 1,3,3,3,3,0,1,1,1,0,1,1,1,3,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 0,3,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,0,0, - 1,1,1,1,1,1,1,0,0,0,3,0,0,0,0,1,1,1,3,3,3,0,3,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0, - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,3,3,3,3,3,3,3,0,0,0,0,8, - 1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,1,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,3,1,1,3,3,3,3,3,3,3,3,3,1,0,0, - 1,1,1,1,1,0,1,0,3,3,3,3,3,3,3,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,3,0,0,0,0,1,1, - 1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1, - 3,3,3,3,3,1,3,3,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,1,1, - 1,1,1,1,1,1,3,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,1,3,3,3,3,3,3,1,3,3,1,1,3,3,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1, - 1,1,3,1,1,3,3,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,0,1,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,0, - 1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0, - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,3,3,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,3,3,3,3,3,3,3,1,1, - 1,1,1,1,1,1,3,1,1,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,8,1,3,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,3,3,3,0,3,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 1,1,1,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,3,3,3,1,1,1,1,3,3,1,1,1,0,0,0,0,1,1,3,1,1,1,1,1,1,3,3,3,0,0,0,0, - 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,3,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,3,3,3,3,3,3,0,3,1,3,1,1,3,3,3,3,3,3,3,3,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,0,0,3, - 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,3,3,3,3,1,3,1,1,1, - 1,1,3,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1, - 3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,1,1,3,3,1,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,3,1,1,1,3,1,3,3,3,1,1,0,0,0,0,0,0,0,0,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,1,1,3,3,0,0,0,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1, - 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,1,1,1,1,3,1,1,1,1,1,1,3,1,1,1,3,3,1,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0, - 0,0,1,1,1,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,1,0,0,6,6,6,6,6,6,7,7,0,0,0,1, - 6,6,6,6,6,6,6,6,6,6,7,7,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,0,1,0,1,0,1,1,1,1,8,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1, - 0,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,3,3,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0, - 1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,3,3,3,3,1,1,0,1,1,1,1,1,0,0,1,1,1,1,1,0,0,0, - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,3,3,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1, - 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, - 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,1,1,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,3,1,1,1,3,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,0,0,0,0,3,0,0,0,1,1,1,1,1,1,1,1,8,8,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,3,3,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,3, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0, - 3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,3,3,3,3,1,1,3,3,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,1,1,3,3,1,1,3,3,0,0,0,0,0,0,0,0,0, - 1,1,1,3,1,1,1,1,1,1,1,1,3,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,3,3,1,1,3,3,1,1,1,1,1,3,3, - 1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,1,1,1,1,3,0,0,0,0,0,0,0,0,0, - 0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,3,1,1,1,1,3,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,2,3,2,2,2,2,2,2,2,2,2,2,7,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,0,2,0, - 2,2,0,2,2,0,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,9,0,0,9,0,0,0,0,0,0,0,0,0,8,0,0,7,7,0,0,0,0,0,8,8,0,0,0,0,0,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0, - 0,0,0,8,8,8,0,0,0,0,0,7,9,7,9,9,6,6,6,6,6,6,6,6,6,6,9,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, - 0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,1,0,0,0,8,8,0,0,0,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 1,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,0,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,2,0,0,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,0,0,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,2, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,3,3,3,0,3,3,0,0,0,0,0,3,3,3,3,2,2,2,2,0,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,3,3,3,0,0,0,0,3, - 2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,0,0,2,2,0,0,2,2,2,2,0,2, + 0,0,0,0,0,0,2,0,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2, + 2,2,2,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,9,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,4, + 3,4,4,3,4,4,3,4,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0, + 6,6,6,6,6,6,0,0,5,9,9,5,10,5,0,0,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,6,6,6,6,9,6,6,5,5,5,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,6,0,4,4,4,4,4,4,5,5,4,4,0,4,4,4,4,5,5,7,7,7,7,7,7,7,7,7,7,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,5,5,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,3,3,0,0,0,0,3,0,0,4,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,3,4,4,4,4,4,4,4,4,4,3,4,4,4,3,4,4,4,4,4,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,0,0,3,0,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,6,6,0,0,0,0,0,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,4,2,2,2, + 2,4,4,4,4,4,4,4,4,2,2,2,2,4,2,2,2,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,4,2,2,0,2,2,2,2,2,2,2,2,0,0,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,0,0,0,2,2,2,2,0,0,4,2,2,2, + 2,4,4,4,4,0,0,2,2,0,0,2,2,4,2,0,0,0,0,0,0,0,0,2,0,0,0,0,2,2,0,2,2,2,4,4,0,0,2,2,2,2,2,2,2,2,2,2,2,2,9,9,2,2,2,2,2,2,2,9,2,2,4,0, + 0,4,4,2,0,2,2,2,2,2,2,0,0,0,0,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,0,2,2,0,2,2,0,0,4,0,2,2, + 2,4,4,0,0,0,0,4,4,0,0,4,4,4,0,0,0,4,0,0,0,0,0,0,0,2,2,2,2,0,2,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,4,2,0,0,0,0,0,0,0,0,0, + 0,4,4,2,0,2,2,2,2,2,2,2,2,2,0,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,0,0,4,2,2,2, + 2,4,4,4,4,4,0,4,4,2,0,2,2,4,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,4,4,0,0,2,2,2,2,2,2,2,2,2,2,2,9,0,0,0,0,0,0,0,2,4,4,4,4,4,4, + 0,4,2,2,0,2,2,2,2,2,2,2,2,0,0,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,0,0,4,2,2,4, + 2,4,4,4,4,0,0,2,2,0,0,2,2,4,0,0,0,0,0,0,0,4,4,2,0,0,0,0,2,2,0,2,2,2,4,4,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0, + 0,0,4,2,0,2,2,2,2,2,2,0,0,0,2,2,2,0,2,2,2,2,0,0,0,2,2,0,2,0,2,2,0,0,0,2,2,0,0,0,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2, + 4,2,2,0,0,0,2,2,2,0,2,2,2,4,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,9,0,0,0,0,0,0, + 4,2,2,2,4,2,2,2,2,2,2,2,2,0,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,2,4,4, + 4,2,2,2,2,0,4,4,4,0,4,4,4,4,0,0,0,0,0,0,0,4,4,0,2,2,2,0,0,2,0,0,2,2,4,4,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,2, + 2,4,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,0,0,4,2,2,2, + 2,2,2,2,2,0,2,2,2,0,2,2,4,4,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,2,2,0,2,2,4,4,0,0,2,2,2,2,2,2,2,2,2,2,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 4,4,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2, + 2,4,4,4,4,0,2,2,2,0,2,2,2,4,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 0,4,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,0,2,0,0, + 2,2,2,2,2,2,2,0,0,0,4,0,0,0,0,2,2,2,4,4,4,0,4,0,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0, + 0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,4,4,4,4,4,4,4,0,0,0,0,9, + 2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,2,0,2,0,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,4,2,2,4,4,4,4,4,4,4,4,4,2,0,0, + 2,2,2,2,2,0,2,0,4,4,4,4,4,4,4,0,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,4,2,4,0,0,0,0,2,2, + 2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2, + 4,4,4,4,4,2,4,4,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,2,2, + 2,2,2,2,2,2,4,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,2,4,4,4,4,4,4,2,4,4,2,2,4,4,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,2,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2, + 2,2,4,2,2,4,4,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,0,2,2,2,2,0,0,2,2,2,2,2,2,2,0,2,0,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,0,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,0,0,2,2,2,2,2,2,2,0, + 2,0,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,0, + 0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,0,4,4,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,4,4,4,4,4,4,4,2,2, + 2,2,2,2,2,2,4,2,2,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,9,2,4,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,4,4,4,1,4,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0, + 2,2,2,2,2,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,4,4,4,2,2,2,2,4,4,2,2,2,0,0,0,0,2,2,4,2,2,2,2,2,2,4,4,4,0,0,0,0, + 0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,4,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,4,4,4,4,4,4,4,0,4,2,4,2,2,4,4,4,4,4,4,4,4,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,0,0,4, + 2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,4,4,4,4,4,2,4,2,2,2, + 2,2,4,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2, + 4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,2,2,4,4,2,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,4,4,2,2,2,4,2,4,4,4,2,2,0,0,0,0,0,0,0,0,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,2,2,4,4,0,0,0,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2, + 2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,4,4,4,2,4,4,4,4,4,4,4,4,4,4,4,4,4,2,4,4,4,4,4,4,4,2,2,2,2,4,2,2,2,2,2,2,4,2,2,2,4,4,2,0,0,0,0,0, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,0,2,0,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,0, + 0,0,2,2,2,0,2,2,2,2,2,2,2,0,0,0,2,2,2,2,0,0,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,2,2,2,0,2,2,2,2,2,2,2,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,1,1,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,9,9,9,9,9,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,7,2,0,0,7,7,7,7,7,7,8,8,0,0,0,2, + 7,7,7,7,7,7,7,7,7,7,8,8,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,2,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2,2,2,0,2,0,0,0,2,2,2,2,2,0,0,0,0,0,0,2,0,2,0,2,0,2,2,2,2,9,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2, + 0,0,0,0,0,2,2,2,2,2,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,4,4,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0, + 2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,4,4,4,4,2,2,0,2,2,2,2,2,0,0,2,2,2,2,2,0,0,0, + 0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,4,0,0,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2, + 0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0, + 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,2,2,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,0,2,0,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,4,2,2,2,4,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,0,0,0,0,4,0,0,0,2,2,2,2,2,2,2,2,9,9,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,4,4,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,4, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,2,2,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0, + 4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,4,4,4,4,2,2,4,4,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,2,2,4,4,2,2,4,4,0,0,0,0,0,0,0,0,0, + 2,2,2,4,2,2,2,2,2,2,2,2,4,2,0,0,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,4,4,4,2,2,4,4,2,2,2,2,2,4,4, + 2,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,2,2,2,2,2,4,0,0,0,0,0,0,0,0,0, + 0,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,4,2,2,2,2,4,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0, + 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,3,4,3,3,3,3,3,3,3,3,3,3,8,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,0,3,0, + 3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,10,0,0,10,0,0,0,0,0,0,0,0,0,9,0,0,8,8,0,0,0,0,0,9,9,0,0,0,0,0,5,5,5,5,5,0,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,1, + 0,0,0,9,9,9,0,0,0,0,0,8,10,8,10,10,7,7,7,7,7,7,7,7,7,7,10,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0, + 0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0, + 0,0,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,0,2,2,2,0,0,0,9,9,0,0,0,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0, + 2,0,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,0,0,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0,0,0,3,0,0,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0,0,0,0,0,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,3, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,4,4,4,0,4,4,0,0,0,0,0,4,4,4,4,3,3,3,3,0,3,3,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,4,4,4,0,0,0,0,4, + 3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,3,3,3,3,3,3, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0, + 6,6,6,6,6,6,6,6,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,4,4,4,4,4,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,4,4,3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,4,4,4,4,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0, + 2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,4,2,2,4,4,2,0,0,0,0,0,0,0,0,0,4, + 4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,2,2,4,4,2,2,2,2,2, + 2,2,4,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0, + 4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,2,4,4,4,4,4,4,4,4,0,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,0,0,0,0,0,0,0,0,0, + 4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,2, + 2,2,2,2,2,2,2,2,2,4,4,4,4,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,2,2,4,2,4,4,2,2,2,2,2,2,4,2, + 2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,0,2,0,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,4,4,4,4,4,4,4,4,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0, + 4,4,2,2,0,2,2,2,2,2,2,2,2,0,0,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,0,4,4,2,2,2, + 4,2,2,2,2,0,0,2,2,0,0,2,2,2,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,2,2,2,2,2,2,2,0,0,4,4,4,4,4,4,4,0,0,0,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,0,2,0,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,4,4,4,4,4, + 4,0,2,0,0,2,0,2,2,2,2,0,2,2,4,2,4,2,4,2,2,2,0,2,2,0,0,0,0,0,0,0,0,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4, + 2,2,4,4,4,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,4,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,2,4,2,2,2,2,4, + 4,2,4,4,2,2,2,2,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,0,0,2,2,2,2,4,4,2,4, + 4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,2,2,4,2,4, + 4,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,4,2,2,4,4,4,4,4,4,2,4,2,2,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,2,4,2,2,4,4,4,4,2,4,4,4,4,4,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,2,4,4,2,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,2, + 2,2,2,2,2,2,2,0,0,2,0,0,2,2,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,4,4,2,4,2, + 2,2,2,4,2,2,2,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,0,0,4,4,2,2,2,2,4,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,4,4,4,4,4,4,2,2,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,2,2,4,4,4,4,2, + 2,2,2,2,2,2,2,4,0,0,0,0,0,0,0,0,2,4,4,4,4,4,4,2,2,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,2,4,4,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,0,4,4,4,4,4,4,2,2, + 2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,2,4,4,4,4,4,4,4,2,4,4,2,4,4,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,0,0,0,4,0,4,4,0,4, + 4,4,4,4,4,4,2,4,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,4,4,0,2,2,4,2,4,2,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,2,0,0,0,0,0,0,0, + 4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,0,0,0,2,2, + 4,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,9,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0, - 5,5,5,5,5,5,5,5,5,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,3,3,3,3,3,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,3,3,2,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,3,3,3,3,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0, - 1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,3,1,1,3,3,1,0,0,0,0,0,0,0,0,0,3, - 3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,1,1,3,3,1,1,1,1,1, - 1,1,3,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,1,3,3,3,3,3,3,3,3,0,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,0,0,0,0,0,0,0,0,0, - 3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,1, - 1,1,1,1,1,1,1,1,1,3,3,3,3,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,1,1,3,1,3,3,1,1,1,1,1,1,3,1, - 1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,3,3,3,3,3,3,3,3,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 3,3,1,1,0,1,1,1,1,1,1,1,1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,3,3,1,1,1, - 3,1,1,1,1,0,0,1,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,0,0,3,3,3,3,3,3,3,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,3,3,3,3,3, - 3,0,1,0,0,1,0,1,1,1,1,0,1,1,3,1,3,1,3,1,1,1,0,1,1,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3, - 1,1,3,3,3,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,3,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,1,3,1,1,1,1,3, - 3,1,3,3,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,0,0,1,1,1,1,3,3,1,3, - 3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,1,1,3,1,3, - 3,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,3,3,3,3,3,3,1,3,1,1,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,3,1,3,1,1,3,3,3,3,1,3,3,3,3,3,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,1,3,3,1,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1, - 1,1,1,1,1,1,1,0,0,1,0,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,3,3,1,3,1, - 1,1,1,3,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,0,0,3,3,1,1,1,1,3,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,3,3,3,3,3,3,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,1,1,3,3,3,3,1, - 1,1,1,1,1,1,1,3,0,0,0,0,0,0,0,0,1,3,3,3,3,3,3,1,1,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,0,3,3,3,3,3,3,1,1, - 1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,1,3,3,3,3,3,3,3,1,3,3,1,3,3,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,0,0,0,3,0,3,3,0,3, - 3,3,3,3,3,3,1,3,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,3,3,0,1,1,3,1,3,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,0,0,0,0,0,0,0, - 3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,0,0,0,1,1, - 3,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 3,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,3,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, - 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,3,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,3,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, - 1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0, - 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,3,3,3,3,3, - 3,3,3,1,1,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1, - 1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0, - 1,1,1,1,1,0,1,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,0,1,1,1,1,1,1,1,1,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,3,3,3,3,3, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,3,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,0,3,3,0,3,3,3,3,3,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,3,3,3,3,3,3,3,1,1,1,1,1,1,1,0,0, - 1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,8, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 4,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,4,4,4,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0, + 2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,4,4,4,4,2,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,3,3,3,3,3,3,3,2,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,2,4,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,4,4,0,4,0,0,4,0,4,4,4,4,4,4,4,4,4,4,0,4,4,4,4,0,4,0,4,0,0,0,0, - 0,0,4,0,0,0,0,4,0,4,0,4,0,4,4,4,0,4,4,0,4,0,0,4,0,4,0,4,0,4,0,4,0,4,4,0,4,0,0,4,4,4,4,0,4,4,4,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,0, - 4,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,4,4,4,0,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, - 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, + 2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0, + 2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,0,0,2,4,4,2,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,4,4,4,4,4, + 4,4,4,2,2,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,2,0,0,2,2,0,0,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2, + 2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,0,2,2,2,2,0,0,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,0, + 2,2,2,2,2,0,2,0,0,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,0,2,2,2,2,2,2,2,2,0,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,4,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,4,4,4,4,4,4,4,0,4,4,0,4,4,4,4,4,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,4,4,4,4,4,4,4,2,2,2,2,2,2,2,0,0, + 2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,9, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,2,2,2,2,0,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,4,4,4,4,4,4,4,3,0,0,0,0,3,3,3,3,3,3,3,3,3,3,0,0,0,0,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 5,5,5,5,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,5,5,0,5,0,0,5,0,5,5,5,5,5,5,5,5,5,5,0,5,5,5,5,0,5,0,5,0,0,0,0, + 0,0,5,0,0,0,0,5,0,5,0,5,0,5,5,5,0,5,5,0,5,0,0,5,0,5,0,5,0,5,0,5,0,5,5,0,5,0,0,5,5,5,5,0,5,5,5,5,5,5,5,0,5,5,5,5,0,5,5,5,5,0,5,0, + 5,5,5,5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,5,5,5,0,5,5,5,5,5,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0, + 2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, + 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0, }; -KBTS_INLINE kbts_u8 kbts_GetUnicodeBidirectionalClass(kbts_u32 Codepoint) +KBTS_INLINE kbts_u8 kbts__GetUnicodeBidirectionalClass(kbts_u32 Codepoint) { - return kbts_UnicodeBidirectionalClass_Data[((kbts_un)kbts_UnicodeBidirectionalClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; + return (Codepoint < 1114110) ? kbts__UnicodeBidirectionalClass_Data[((kbts_un)kbts__UnicodeBidirectionalClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; } -static kbts_u8 kbts_UnicodeJoiningType_PageIndices[8703] = { +static kbts_u8 kbts__UnicodeJoiningType_PageIndices[8703] = { 0,1,0,0,0,0,2,0,0,3,0,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 25,26,0,0,0,0,27,0,0,0,0,0,0,0,28,29,30,31,32,0,33,34,35,36,37,38,0,39,0,0,0,0, 40,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,43,44,0,0,0,0, @@ -9580,7 +10329,7 @@ static kbts_u8 kbts_UnicodeJoiningType_PageIndices[8703] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; -static kbts_u8 kbts_UnicodeJoiningType_Data[14848] = { +static kbts_u8 kbts__UnicodeJoiningType_Data[14848] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -9815,12 +10564,12 @@ static kbts_u8 kbts_UnicodeJoiningType_Data[14848] = { 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, }; -KBTS_INLINE kbts_u8 kbts_GetUnicodeJoiningType(kbts_u32 Codepoint) +KBTS_INLINE kbts_u8 kbts__GetUnicodeJoiningType(kbts_u32 Codepoint) { - return kbts_UnicodeJoiningType_Data[((kbts_un)kbts_UnicodeJoiningType_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; + return (Codepoint < 1114110) ? kbts__UnicodeJoiningType_Data[((kbts_un)kbts__UnicodeJoiningType_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; } -static kbts_u8 kbts_UnicodeCombiningClass_PageIndices[8703] = { +static kbts_u8 kbts__UnicodeCombiningClass_PageIndices[8703] = { 0,0,0,0,0,0,1,0,0,2,0,3,4,5,6,7,8,9,10,11,12,12,12,13,14,12,15,16,17,18,19,20, 21,22,0,0,0,0,23,0,0,0,0,0,0,0,24,25,0,26,27,0,28,29,30,31,32,33,0,34,0,0,0,0, 0,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,37,38,0,0,0,0, @@ -10095,7 +10844,7 @@ static kbts_u8 kbts_UnicodeCombiningClass_PageIndices[8703] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; -static kbts_u8 kbts_UnicodeCombiningClass_Data[12288] = { +static kbts_u8 kbts__UnicodeCombiningClass_Data[12288] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220,220,220,220,220,220,220,220,220,1,1,1,1,1,220,220,220,220,230,230,230, @@ -10290,12 +11039,12 @@ static kbts_u8 kbts_UnicodeCombiningClass_Data[12288] = { 0,0,0,0,230,230,230,230,230,230,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; -KBTS_INLINE kbts_u8 kbts_GetUnicodeCombiningClass(kbts_u32 Codepoint) +KBTS_INLINE kbts_u8 kbts__GetUnicodeCombiningClass(kbts_u32 Codepoint) { - return kbts_UnicodeCombiningClass_Data[((kbts_un)kbts_UnicodeCombiningClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)]; + return (Codepoint < 1114110) ? kbts__UnicodeCombiningClass_Data[((kbts_un)kbts__UnicodeCombiningClass_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; } -static kbts_u16 kbts_UnicodeParentInfo_PageIndices[34815] = { +static kbts_u16 kbts__UnicodeParentInfo_PageIndices[34815] = { 0,1,2,3,0,4,5,6,7,0,8,9,0,10,0,11,0,12,0,0,13,14,0,0,15,0,0,0,16,17,18,0, 19,20,21,22,0,0,23,24,0,0,0,0,0,0,25,26,0,27,28,0,0,0,29,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,30,31,0,0,0,32,33,0,34,35,0,0,0,0,0,0,0,36,37,0,0,0,38,0, @@ -11386,7 +12135,7 @@ static kbts_u16 kbts_UnicodeParentInfo_PageIndices[34815] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; -static kbts_u32 kbts_UnicodeParentInfo_Data[20960] = { +static kbts_u32 kbts__UnicodeParentInfo_Data[20960] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66400,66423,66421,66422,0, 0,1048664,197177,328094,393598,1114167,66418,459018,459046,983176,65810,393556,393562,197183,590017,1048696,131720,0,524539,459032,459067,1245203,131728,393604,131730,590026,393592,0,0,0,0,0, @@ -12044,12 +12793,12 @@ static kbts_u32 kbts_UnicodeParentInfo_Data[20960] = { 66424,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; -KBTS_INLINE kbts_u32 kbts_GetUnicodeParentInfo(kbts_u32 Codepoint) +KBTS_INLINE kbts_u32 kbts__GetUnicodeParentInfo(kbts_u32 Codepoint) { - return kbts_UnicodeParentInfo_Data[((kbts_un)kbts_UnicodeParentInfo_PageIndices[Codepoint/32] * 32) | (Codepoint & 31)]; + return (Codepoint < 1114110) ? kbts__UnicodeParentInfo_Data[((kbts_un)kbts__UnicodeParentInfo_PageIndices[Codepoint/32] * 32) | (Codepoint & 31)] : 0; } -static kbts_u8 kbts_UnicodeUseClass_PageIndices[4351] = { +static kbts_u8 kbts__UnicodeUseClass_PageIndices[4351] = { 0,1,1,1,1,1,2,3,4,5,6,7,8,9,10,11,12,1,1,1,1,1,1,13,14,15,16,17,18,19,1,1, 20,1,1,1,1,21,1,22,1,1,1,1,1,23,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, @@ -12188,7 +12937,7 @@ static kbts_u8 kbts_UnicodeUseClass_PageIndices[4351] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, }; -static kbts_u8 kbts_UnicodeUseClass_Data[16896] = { +static kbts_u8 kbts__UnicodeUseClass_Data[16896] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,21,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0, @@ -12455,53 +13204,1679 @@ static kbts_u8 kbts_UnicodeUseClass_Data[16896] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; -KBTS_INLINE kbts_u8 kbts_GetUnicodeUseClass(kbts_u32 Codepoint) +KBTS_INLINE kbts_u8 kbts__GetUnicodeUseClass(kbts_u32 Codepoint) { - return kbts_UnicodeUseClass_Data[((kbts_un)kbts_UnicodeUseClass_PageIndices[Codepoint/256] * 256) | (Codepoint & 255)]; + return (Codepoint < 1114110) ? kbts__UnicodeUseClass_Data[((kbts_un)kbts__UnicodeUseClass_PageIndices[Codepoint/256] * 256) | (Codepoint & 255)] : 0; } -KBTS_INLINE kbts_u32 kbts_GetDecompositionSize(kbts_u64 Decomposition) +static kbts_u8 kbts__UnicodeScriptExtension_PageIndices[8703] = { + 0,1,2,2,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, + 30,31,32,32,33,34,35,36,37,37,37,37,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,2,2,53,54, + 55,56,57,58,59,59,59,59,60,59,59,59,59,59,59,59,61,61,59,59,59,59,62,63,64,65,66,67,68,61,61,69, + 70,71,72,73,74,75,76,77,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,78,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 79,79,79,79,79,79,79,79,79,80,81,81,82,83,84,85,86,87,88,89,90,91,92,93,32,32,32,32,32,32,32,32, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,94,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,95,96,97,97,98,99,100,101,102,103, + 104,105,106,107,61,108,109,110,111,112,113,114,115,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133, + 134,135,136,137,138,139,140,141,142,143,61,144,145,146,147,61,148,149,150,151,152,153,154,155,156,157,158,159,61,160,161,162, + 163,163,163,163,163,163,163,164,165,163,166,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,167, + 168,168,168,168,168,168,168,168,169,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, + 168,168,168,168,168,168,168,170,171,171,171,171,172,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,173,61,61,61,61,61,61,61,61,61,61,61,61,61,174,174,174,174,175,176,177,178,61,61,179,61,180,181,182,183, + 184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, + 184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,185,184,184,184,184,184,184,186,186,186,187,188,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,189, + 190,191,192,193,193,194,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,195,196,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,59,197,59,59,59,198,199,200, + 59,201,202,203,204,205,206,61,207,208,209,59,59,210,59,211,212,212,212,212,212,213,61,61,61,61,61,61,61,61,214,61, + 215,216,217,61,61,218,61,61,61,219,61,220,61,61,61,221,222,223,224,61,61,61,61,61,225,226,227,61,228,229,61,61, + 230,231,59,232,233,61,59,59,59,59,59,59,59,234,235,236,237,238,59,59,239,240,59,241,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 242,61,243,244,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, +}; + +static kbts_u16 kbts__UnicodeScriptExtension_Data[31360] = { + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,929,929,929,929,929, + 929,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,2305,929,929,929,929,929,929,929,929,929,929,929,929,16,929,929,2305,929,929,929,929,929, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,929,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,929,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,929,929,929,519,929,929,929, + 929,929,929,929,929,929,929,738,929,738,738,738,929,610,929,929,929,929,929,929,929,929,929,802,929,738,929,929,929,929,929,929, + 2305,2305,2305,2305,2305,929,929,929,929,929,417,417,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 872,1128,1380,1509,1675,2022,932,2217,2506,1442,2819,2916,3043,1538,3138,961,1538,3203,961,3300,961,961,961,961,961,961,961,961,961,961,961,961, + 2850,961,961,3429,3588,2850,961,961,961,961,961,961,961,1539,2850,961,3715,3814,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,1441,961,961,1441,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,1218,961,961,961,961,961,4003,961, + 961,961,961,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,1441,1441,1441,1441,4098,4098,1441,1441,1,1,1441,1441,1441,1441,929,1441, + 1,1,1,1,1441,929,1441,929,1441,1441,1441,1,1441,1,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,801,801,801,801,801,801,801,801,801,801,801,801,801,801,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,4162,4226,1410,1410,4226,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,1,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,1,1,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, + 161,161,161,161,161,161,161,161,161,4291,161,1,1,161,161,161,1,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729, + 1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729, + 1729,1729,1729,1729,1729,1729,1729,1729,1,1,1,1,1,1,1,1,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729, + 1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1,1,1,1,1729,1729,1729,1729,1729,1729,1,1,1,1,1,1,1,1,1,1,1, + 129,129,129,129,129,929,129,129,129,129,129,129,4391,129,129,129,129,129,129,129,129,129,129,129,129,129,129,4391,4611,129,129,4712, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 4969,129,129,129,129,129,129,129,129,129,129,4610,4610,4610,4610,4610,4610,4610,4610,4610,4610,4610,129,129,129,129,129,129,129,129,129,129, + 5251,5251,5251,5251,5251,5251,5251,5251,5251,5251,129,129,129,129,129,129,4610,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,4994,129,129,129,129,129,129,129,129,929,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,1,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577, + 4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577, + 4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,1,1,4577,4577,4577,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929, + 4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,4929,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297, + 3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,3297,1,1,3297,3297,3297, + 4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129, + 4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,1,1,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,4129,1, + 2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,2657,1,1,2657,1, + 4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,4577,1,1,1,1,1,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,1,129,129,1,1,1,1,1,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,929,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025, + 1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025, + 1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,5357,5772,961,961,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025, + 1025,1025,1025,1025,6165,6839,7556,7556,7556,7556,7556,7556,7556,7556,7556,7556,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025, + 353,353,353,353,1,353,353,353,353,353,353,353,353,1,1,353,353,1,1,353,353,353,353,353,353,353,353,353,353,353,353,353, + 353,353,353,353,353,353,353,353,353,1,353,353,353,353,353,353,353,1,353,1,1,1,353,353,353,353,1,1,353,353,353,353, + 353,353,353,353,353,1,1,353,353,1,1,353,353,353,353,1,1,1,1,1,1,1,1,353,1,1,1,1,353,353,1,353, + 353,353,353,353,1,1,7683,7683,7683,7683,7683,7683,7683,7683,7683,7683,353,353,353,353,353,353,353,353,353,353,353,353,353,353,353,1, + 1,1537,1537,1537,1,1537,1537,1537,1537,1537,1537,1,1,1,1,1537,1537,1,1,1537,1537,1537,1537,1537,1537,1537,1537,1537,1537,1537,1537,1537, + 1537,1537,1537,1537,1537,1537,1537,1537,1537,1,1537,1537,1537,1537,1537,1537,1537,1,1537,1537,1,1537,1537,1,1537,1537,1,1,1537,1,1537,1537, + 1537,1537,1537,1,1,1,1,1537,1537,1,1,1537,1537,1537,1,1,1,1537,1,1,1,1,1,1,1,1537,1537,1537,1537,1,1537,1, + 1,1,1,1,1,1,7778,7778,7778,7778,7778,7778,7778,7778,7778,7778,1537,1537,1537,1537,1537,1537,1537,1,1,1,1,1,1,1,1,1, + 1,1473,1473,1473,1,1473,1473,1473,1473,1473,1473,1473,1473,1473,1,1473,1473,1473,1,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473, + 1473,1473,1473,1473,1473,1473,1473,1473,1473,1,1473,1473,1473,1473,1473,1473,1473,1,1473,1473,1,1473,1473,1473,1473,1473,1,1,1473,1473,1473,1473, + 1473,1473,1473,1473,1473,1473,1,1473,1473,1473,1,1473,1473,1473,1,1,1473,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1473,1473,1473,1473,1,1,7842,7842,7842,7842,7842,7842,7842,7842,7842,7842,1473,1473,1,1,1,1,1,1,1,1473,1473,1473,1473,1473,1473,1473, + 1,3777,3777,3777,1,3777,3777,3777,3777,3777,3777,3777,3777,1,1,3777,3777,1,1,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777, + 3777,3777,3777,3777,3777,3777,3777,3777,3777,1,3777,3777,3777,3777,3777,3777,3777,1,3777,3777,1,3777,3777,3777,3777,3777,1,1,3777,3777,3777,3777, + 3777,3777,3777,3777,3777,1,1,3777,3777,1,1,3777,3777,3777,1,1,1,1,1,1,1,3777,3777,3777,1,1,1,1,3777,3777,1,3777, + 3777,3777,3777,3777,1,1,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,3777,1,1,1,1,1,1,1,1, + 1,1,4801,4801,1,4801,4801,4801,4801,4801,4801,1,1,1,4801,4801,4801,1,4801,4801,4801,4801,1,1,1,4801,4801,1,4801,1,4801,4801, + 1,1,1,4801,4801,1,1,1,4801,4801,4801,1,1,1,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,1,1,1,1,4801,4801, + 4801,4801,4801,1,1,1,4801,4801,4801,1,4801,4801,4801,4801,1,1,4801,1,1,1,1,1,1,4801,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,7906,7906,7906,7906,7906,7906,7906,7906,7906,7906,7906,7906,7906,7906,4801,4801,4801,4801,4801,4801,4801,1,1,1,1,1, + 4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,1,4897,4897,4897,1,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897, + 4897,4897,4897,4897,4897,4897,4897,4897,4897,1,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,1,1,4897,4897,4897,4897, + 4897,4897,4897,4897,4897,1,4897,4897,4897,1,4897,4897,4897,4897,1,1,1,1,1,1,1,4897,4897,1,4897,4897,4897,1,1,4897,1,1, + 4897,4897,4897,4897,1,1,4897,4897,4897,4897,4897,4897,4897,4897,4897,4897,1,1,1,1,1,1,1,4897,4897,4897,4897,4897,4897,4897,4897,4897, + 1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1,1953,1953,1953,1,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953, + 1953,1953,1953,1953,1953,1953,1953,1953,1953,1,1953,1953,1953,1953,1953,1953,1953,1953,1953,1953,1,1953,1953,1953,1953,1953,1,1,1953,1953,1953,1953, + 1953,1953,1953,1953,1953,1,1953,1953,1953,1,1953,1953,1953,1953,1,1,1,1,1,1,1,1953,1953,1,1,1,1,1,1,1953,1953,1, + 1953,1953,1953,1953,1,1,7971,7971,7971,7971,7971,7971,7971,7971,7971,7971,1,1953,1953,1953,1,1,1,1,1,1,1,1,1,1,1,1, + 2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,1,2625,2625,2625,1,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625, + 2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625, + 2625,2625,2625,2625,2625,1,2625,2625,2625,1,2625,2625,2625,2625,2625,2625,1,1,1,1,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625, + 2625,2625,2625,2625,1,1,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625,2625, + 1,4353,4353,4353,1,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,1,1,1,4353,4353,4353,4353,4353,4353, + 4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,1,4353,4353,4353,4353,4353,4353,4353,4353,4353,1,4353,1,1, + 4353,4353,4353,4353,4353,4353,4353,1,1,1,4353,1,1,1,1,4353,4353,4353,4353,4353,4353,1,4353,1,4353,4353,4353,4353,4353,4353,4353,4353, + 1,1,1,1,1,1,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,1,1,4353,4353,4353,1,1,1,1,1,1,1,1,1,1,1, + 1,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961, + 4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,1,1,1,1,929, + 4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,4961,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,2273,2273,1,2273,1,2273,2273,2273,2273,2273,1,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273, + 2273,2273,2273,2273,1,2273,1,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,1,1, + 2273,2273,2273,2273,2273,1,2273,1,2273,2273,2273,2273,2273,2273,2273,1,2273,2273,2273,2273,2273,2273,2273,2273,2273,2273,1,1,2273,2273,2273,2273, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993, + 4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993, + 4993,4993,4993,4993,4993,4993,4993,4993,1,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993, + 4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,1,1,1,1,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993, + 4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,1,4993,4993,4993,4993,4993,4993,4993, + 4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,1,4993,4993, + 4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,4993,1,4993,4993,4993,4993,4993,4993,4993,929,929,929,929,4993,4993,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105, + 3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105, + 8067,8067,8067,8067,8067,8067,8067,8067,8067,8067,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105, + 3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105, + 3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105, + 1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313, + 1313,1313,1313,1313,1313,1313,1,1313,1,1,1,1,1,1313,1,1,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313, + 1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,8163,1313,1313,1313,1313, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1,1,1249,1249,1249,1249,1249,1249,1249,1,1249,1,1249,1249,1249,1249,1,1, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1,1,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1,1,1249,1249,1249,1249,1249,1249,1249,1, + 1249,1,1249,1249,1249,1249,1,1,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1,1,1249,1249,1249,1249,1249,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1,1249,1249,1249, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1,1, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1,1,1,1,1, + 705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, + 705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, + 705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,1,1,705,705,705,705,705,705,1,1, + 545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545, + 545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545, + 545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545, + 545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545, + 3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,3393,1,1,1, + 4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097, + 4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097, + 4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,4097,1,1,1,1,1,1,1, + 4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,4609,1,1,1,1,1,1,1,1,1,4609, + 1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,1665,8260,8260,1,1,1,1,1,1,1,1,1, + 513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,1,1,1,1,1,1,1,1,1,1,1,1, + 4641,4641,4641,4641,4641,4641,4641,4641,4641,4641,4641,4641,4641,1,4641,4641,4641,1,4641,4641,1,1,1,1,1,1,1,1,1,1,1,1, + 2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145, + 2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145, + 2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,1,1, + 2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,1,1,1,1,1,1,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,1,1,1,1,1,1, + 3009,3009,8386,8386,3009,8386,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,1,1,1,1,1,1, + 3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009, + 3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009, + 3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,1,1,1,1,1,1,1, + 3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009, + 3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,1,1,1,1,1,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545, + 545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545, + 545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,1,1,1,1,1,1,1,1,1,1, + 2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,1, + 2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,1,1,1,1,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,1,1,1,1, + 2369,1,1,1,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,2369,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673, + 4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,4673,1,1,4673,4673,4673,4673,4673,1,1,1,1,1,1,1,1,1,1,1, + 3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265, + 3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,1,1,1,1,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265, + 3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,1,1,1,1,1,1,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,3265,1,1,1,3265,3265, + 2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145,2145, + 481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,1,1,481,481, + 4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705, + 4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,1, + 4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,1,1,4705, + 4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,1,1,1,1,1,1,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,1,1,1,1,1,1, + 4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,4705,1,1,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, + 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, + 225,225,225,225,225,225,225,225,225,225,225,225,225,1,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, + 225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225, + 4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481, + 4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481,4481, + 321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321, + 321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,1,1,1,1,1,1,1,1,321,321,321,321, + 2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337, + 2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,1,1,1,2337,2337,2337,2337,2337, + 2337,2337,2337,2337,2337,2337,2337,2337,2337,2337,1,1,1,2337,2337,2337,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425, + 3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425,3425, + 897,897,897,897,897,897,897,897,897,897,897,1,1,1,1,1,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313, + 1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1,1,1313,1313,1313, + 4481,4481,4481,4481,4481,4481,4481,4481,1,1,1,1,1,1,1,1,8452,1025,8452,8483,1025,5346,5346,8578,5346,8578,8646,1025,8578,8578,1025,1025, + 8578,5346,1025,1025,1025,1025,1025,1025,1025,8834,5346,1025,1025,5346,1025,1025,1025,1025,8907,5378,9252,5346,5346,353,5378,5378,3201,1,1,1,1,1, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,1441,1441,1441,1441,1441,897,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,1441,1441,1441, + 1441,1441,2305,2305,2305,2305,1441,1441,1441,1441,1441,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,897,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,1441, + 1441,1441,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,9379,961,4577,961,961,961,961,961, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1,1,1441,1441,1441,1441,1441,1441,1,1, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1441,1441,1441,1441,1,1,1441,1441,1441,1441,1441,1441,1,1,1441,1441,1441,1441,1441,1441,1441,1441,1,1441,1,1441,1,1441,1,1441, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1,1, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1441,1441,1441,1,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1,1,1441,1441,1441,1441,1441,1441,1,1441,1441,1441, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1,1,1441,1441,1441,1,1441,1441,1441,1441,1441,1441,1441,1441,1441,1, + 929,929,929,929,929,929,929,929,929,929,929,929,961,961,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,9475,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,4706,929,929,929,929,929,929,929,929,929,929,9574,929,929,9764,929,929, + 929,929,929,929,929,1,929,929,929,929,929,929,929,929,929,929,929,2305,1,1,929,929,929,929,929,929,929,929,929,929,929,2305, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,9891,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,1441,929,929,929,2305,2305,929,929,929,929,929,929,2305,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,2305,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,929,929,929,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345, + 1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345, + 1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801, + 801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801, + 801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801, + 801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,801,1,1,1,1,1,801,801,801,801,801,801,801, + 1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,1313, + 1313,1313,1313,1313,1313,1313,1,1313,1,1,1,1,1,1313,1,1,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025, + 5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025,5025, + 5025,5025,5025,5025,5025,5025,5025,5025,1,1,1,1,1,1,1,5025,5025,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5025, + 1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1,1,1,1,1,1,1,1,1, + 1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1, + 1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,9986,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,10050,10119,929,929,929,929,929,929,929,929,929,929,1121,929,929,929, + 929,10339,929,4226,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,10434,10434,10434,10434,10434,10434,10434,10434,10434,10434,10434,10434,10434,10434,10434,10434, + 929,10503,10728,10501,929,1,769,1,10984,10984,11241,11241,11526,11526,11526,11526,11526,11526,929,10501,11526,11526,11526,11526,11526,11526,11526,11526,10501,10501,10501,10501, + 929,1,1,1,1,1,1,1,1,1,10498,10498,10498,10498,1601,1601,10501,10594,10594,10594,10594,10594,929,10501,1,1,1,1,11715,11715,769,769, + 1,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1,1,10594,10594,10594,10594,1761,1761,1761, + 10594,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985, + 1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985, + 1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,11526,10594,1985,1985,1985, + 1,1,1,1,1,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417, + 417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,1,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769, + 417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417,417, + 769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769, + 769,769,769,769,769,769,1,1,1,1,1,1,1,1,1,10434,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1, + 769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769, + 769,769,769,769,769,769,769,769,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,929, + 769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769, + 769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 769,769,769,769,769,769,769,769,769,769,769,769,929,929,929,929,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985, + 1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,769, + 1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985, + 1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985, + 1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,769,769,769,769,769,769,769,769, + 769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,929,929,929,929,929,929,929,929,929,929,769,769,769,769,769, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,929, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377, + 5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377, + 5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377, + 5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377, + 5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,1,1,1,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377, + 5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377,5377, + 5377,5377,5377,5377,5377,5377,5377,1,1,1,1,1,1,1,1,1,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465, + 2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465,2465, + 5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217, + 5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217, + 5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217, + 5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217, + 5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217, + 5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,5217,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,4226,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,1,1,1,1,1,1,1,1, + 11810,11810,11810,11810,11810,11810,11810,11810,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,929,929,929,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,1,1,2305,2305,1,2305,1,2305,2305,2305,2305,2305,2305,2305,2305,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545, + 4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,4545,1,1,1,11888,11888,11888,12399,12399,12399,12875,12875,13228,12875,1,1,1,1,1,1, + 3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969, + 3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,3969,1,1,1,1,1,1,1,1, + 4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161, + 4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161, + 4161,4161,4161,4161,4161,4161,1,1,1,1,1,1,1,1,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,4161,1,1,1,1,1,1, + 1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,13603,1025,13698,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025, + 2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049, + 2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,2049,13763,2049,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065, + 4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,4065,1,1,1,1,1,1,1,1,1,1,1,4065, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1,1,1, + 1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889, + 1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889, + 1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1,13858,1889,1889,1889,1889,1889,1889,1889,1889,1889,1889,1,1,1,1,1889,1889, + 3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,1, + 673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673, + 673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,673,1,1,1,1,1,1,1,1,1, + 673,673,673,673,673,673,673,673,673,673,673,673,673,673,1,1,673,673,673,673,673,673,673,673,673,673,1,1,673,673,673,673, + 3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105, + 4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737, + 4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737, + 4737,4737,4737,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4737,4737,4737,4737,4737, + 2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,1,1,1,1,1,1,1,1,1, + 1,1249,1249,1249,1249,1249,1249,1,1,1249,1249,1249,1249,1249,1249,1,1,1249,1249,1249,1249,1249,1249,1,1,1,1,1,1,1,1,1, + 1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,929,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,1441,2305,2305,2305,2305,929,929,1,1,1,1,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, + 705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, + 705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705,705, + 2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817, + 2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,1,1,2817,2817,2817,2817,2817,2817,2817,2817,2817,2817,1,1,1,1,1,1, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1,1,1,1,1,1,1,1,1,1,1,1,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1601,1601,1601,1,1,1,1,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1,1,1,1, + 2305,2305,2305,2305,2305,2305,2305,1,1,1,1,1,1,1,1,1,1,1,1,161,161,161,161,161,1,1,1,1,1,1729,1729,1729, + 1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,1,1729,1729,1729,1729,1729,1,1729,1, + 1729,1729,1,1729,1729,1,1729,1729,1729,1729,1729,1729,1729,1729,1729,1729,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,13922,13922, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,1,1,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,1,1,1,1,1,1,1,129,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,129,129,5250,129,129,129,129,129,129,129,129,129,129,5250,129,129, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,897,897,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,10501,10501,929,929,929,929,929,929,929,929,929,929,929,929,1,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,1,929,929,929,929,1,1,1,1,129,129,129,129,129,1,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,1,1,929, + 1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,929,929,929,929,929, + 929,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,929,929,929,929,929, + 929,11526,11526,11526,11526,11526,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,10594,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985, + 1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,1985,10594,10594, + 1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1601,1, + 1,1,1601,1601,1601,1601,1601,1601,1,1,1601,1601,1601,1601,1601,1601,1,1,1601,1601,1601,1601,1601,1601,1,1,1601,1601,1601,1,1,1, + 929,929,929,929,929,929,929,1,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,929,929,929,929,929,1,1, + 2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,1,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433, + 2433,2433,2433,2433,2433,2433,2433,1,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,1,2433,2433,1,2433, + 2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,1,1,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433, + 2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433, + 2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433, + 2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,2433,1,1,1,1,1, + 13987,13987,14082,1,1,1,1,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147, + 14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,14147,1,1,1,14082,14082,14082,14082,14082,14082,14082,14082,14082, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1, + 1441,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,961,1,1, + 2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,2497,1,1,1, + 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577, + 577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,14242,1,1,1,1, + 3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489,3489, + 3489,3489,3489,3489,1,1,1,1,1,1,1,1,1,3489,3489,3489,1377,1377,1377,1377,1377,1377,1377,1377,1377,1377,1377,1377,1377,1377,1377,1377, + 1377,1377,1377,1377,1377,1377,1377,1377,1377,1377,1377,1,1,1,1,1,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585, + 3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,3585,1,1,1,1,1, + 5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,5185,1,5185, + 3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617, + 3617,3617,3617,3617,1,1,1,1,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,3617,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993, + 993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993, + 993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,993,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225, + 4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225,4225, + 3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,1,1, + 3841,3841,3841,3841,3841,3841,3841,3841,3841,3841,1,1,1,1,1,1,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809, + 3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,1,1,1,1,3809,3809,3809,3809,3809,3809,3809,3809, + 3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,3809,1,1,1,1, + 1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185,1185, + 1185,1185,1185,1185,1185,1185,1185,1185,1,1,1,1,1,1,1,1,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609, + 609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609, + 609,609,609,609,1,1,1,1,1,1,1,1,1,1,1,609,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,1,5249,5249,5249,5249, + 5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,1,5249,5249,5249,5249,5249,5249,5249,1,5249,5249,1,5249,5249,5249,5249,5249,5249,5249,5249,5249, + 5249,5249,1,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,5249,1,5249,5249,5249,5249,5249,5249,5249,1,5249,5249,1,1,1, + 5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089, + 5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,5089,1,1,1,1,1,1,1,1,1,1,1,1, + 2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401, + 2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401, + 2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401, + 2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401, + 2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401, + 2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,1,1,1,1,1,1,1,1,1, + 2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,2401,1,1,1,1,1,1,1,1,1,1, + 2401,2401,2401,2401,2401,2401,2401,2401,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2305,2305,2305,2305,2305,2305,1,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,1,2305,2305,2305,2305,2305,2305,2305,2305,2305,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 833,833,833,833,833,833,1,1,833,1,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833, + 833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,1,833,833,1,1,1,833,1,1,833, + 1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1793,1,1793,1793,1793,1793,1793,1793,1793,1793,1793, + 3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905,3905, + 3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,3137,1, + 1,1,1,1,1,1,1,3137,3137,3137,3137,3137,3137,3137,3137,3137,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1697,1,1697,1697,1,1,1,1,1,1697,1697,1697,1697,1697, + 4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,4001,1,1,1,4001, + 2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,2529,1,1,1,1,1,2529, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913,2913, + 2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,1,1,1,1,2881,2881,2881,2881, + 2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,1,1,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881, + 2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881,2881, + 2081,2081,2081,2081,1,2081,2081,1,1,1,1,1,2081,2081,2081,2081,2081,2081,2081,2081,1,2081,2081,2081,1,2081,2081,2081,2081,2081,2081,2081, + 2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,2081,1,1,2081,2081,2081,1,1,1,1,2081, + 2081,2081,2081,2081,2081,2081,2081,2081,2081,1,1,1,1,1,1,1,2081,2081,2081,2081,2081,2081,2081,2081,2081,1,1,1,1,1,1,1, + 3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681,3681, + 3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553,3553, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689,2689, + 2689,2689,2689,2689,2689,2689,2689,1,1,1,1,2689,2689,2689,2689,2689,2689,2689,5090,2689,2689,2689,2689,1,1,1,1,1,1,1,1,1, + 193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193, + 193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,1,1,1,193,193,193,193,193,193,193, + 1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1857,1,1,1857,1857,1857,1857,1857,1857,1857,1857, + 1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1825,1,1,1,1,1,1825,1825,1825,1825,1825,1825,1825,1825, + 4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,4033,1,1,1,1,1,1,1,4033,4033,4033,4033,1,1,1, + 1,1,1,1,1,1,1,1,1,4033,4033,4033,4033,4033,4033,4033,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713, + 3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713,3713, + 3713,3713,3713,3713,3713,3713,3713,3713,3713,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521, + 3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521, + 3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,3521,1,1,1,1,1,1,1,3521,3521,3521,3521,3521,3521, + 1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633, + 1633,1633,1633,1633,1633,1633,1633,1633,1,1,1,1,1,1,1,1,1633,1633,1633,1633,1633,1633,1633,1633,1633,1633,1,1,1,1,1,1, + 1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281, + 1281,1281,1281,1281,1281,1281,1,1,1,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281, + 1281,1281,1281,1281,1281,1281,1,1,1,1,1,1,1,1,1281,1281,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,1, + 5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,5345, + 5345,5345,5345,5345,5345,5345,5345,5345,5345,5345,1,5345,5345,5345,1,1,5345,5345,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,129,129,129,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,129,129,129,129, + 3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649,3649, + 3649,3649,3649,3649,3649,3649,3649,3649,1,1,1,1,1,1,1,1,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321, + 4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,4321,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3745,3745,3745,3745,3745,3745,3745,3745,3745,3745,3745,3745,3745,3745,3745,3745, + 3745,3745,3745,3745,3745,3745,3745,3745,3745,3745,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737,737, + 737,737,737,737,737,737,737,737,737,737,737,737,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1217,1,1,1,1,1,1,1,1,1, + 449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, + 449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449, + 449,449,449,449,449,449,449,449,449,449,449,449,449,449,1,1,1,1,449,449,449,449,449,449,449,449,449,449,449,449,449,449, + 449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,449,1,1,1,1,1,1,1,1,1,449, + 1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921, + 1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921,1921, + 1921,1921,1921,1,1,1,1,1,1,1,1,1,1,1921,1,1,4385,4385,4385,4385,4385,4385,4385,4385,4385,4385,4385,4385,4385,4385,4385,4385, + 4385,4385,4385,4385,4385,4385,4385,4385,4385,1,1,1,1,1,1,1,4385,4385,4385,4385,4385,4385,4385,4385,4385,4385,1,1,1,1,1,1, + 641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641, + 641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,641,1,641,641,641,641,641,641,641,641,641,641, + 641,641,641,641,641,641,641,641,1,1,1,1,1,1,1,1,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561, + 2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,2561,1,1,1,1,1,1,1,1,1, + 4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193, + 4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193, + 4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193,4193, + 1,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,1,1,1,1,1,1,1,1,1,1,1, + 2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,1,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177, + 2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177,2177, + 2177,2177,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3073,3073,3073,3073,3073,3073,3073,1,3073,1,3073,3073,3073,3073,1,3073,3073,3073,3073,3073,3073,3073,3073,3073,3073,3073,3073,3073,3073,3073,1,3073, + 3073,3073,3073,3073,3073,3073,3073,3073,3073,3073,1,1,1,1,1,1,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209, + 2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209, + 2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,1,1,1,1,1,2209,2209,2209,2209,2209,2209,2209,2209,2209,2209,1,1,1,1,1,1, + 1409,7906,1409,7906,1,1409,1409,1409,1409,1409,1409,1409,1409,1,1,1409,1409,1,1,1409,1409,1409,1409,1409,1409,1409,1409,1409,1409,1409,1409,1409, + 1409,1409,1409,1409,1409,1409,1409,1409,1409,1,1409,1409,1409,1409,1409,1409,1409,1,1409,1409,1,1409,1409,1409,1409,1409,1,7906,7906,1409,1409,1409, + 1409,1409,1409,1409,1409,1,1,1409,1409,1,1,1409,1409,1409,1,1,1409,1,1,1,1,1,1,1409,1,1,1,1,1,1409,1409,1409, + 1409,1409,1409,1409,1,1,1409,1409,1409,1409,1409,1409,1409,1,1,1,1409,1409,1409,1409,1409,1,1,1,1,1,1,1,1,1,1,1, + 5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,1,5153,1,1,5153,1,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153, + 5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,1,5153,5153,5153,5153,5153,5153,5153,5153,5153, + 5153,1,5153,1,1,5153,1,5153,5153,5153,5153,1,5153,5153,5153,5153,5153,5153,5153,5153,5153,5153,1,5153,5153,1,1,1,1,1,1,1, + 1,5153,5153,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233, + 3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233, + 3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,3233,1,3233,3233,3233, + 3233,3233,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057, + 5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057, + 5057,5057,5057,5057,5057,5057,5057,5057,1,1,1,1,1,1,1,1,5057,5057,5057,5057,5057,5057,5057,5057,5057,5057,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257, + 4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,1,1,4257,4257,4257,4257,4257,4257,4257,4257, + 4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,4257,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977, + 2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977, + 2977,2977,2977,2977,2977,1,1,1,1,1,1,1,1,1,1,1,2977,2977,2977,2977,2977,2977,2977,2977,2977,2977,1,1,1,1,1,1, + 3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,3009,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769, + 4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,1,1,1,1,1,1, + 4769,4769,4769,4769,4769,4769,4769,4769,4769,4769,1,1,1,1,1,1,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105,3105, + 3105,3105,3105,3105,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,1,1,65,65,65, + 65,65,65,65,65,65,65,65,65,65,65,65,1,1,1,1,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, + 65,65,65,65,65,65,65,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089, + 1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313, + 5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313, + 5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,5313,1,1,1,1,1,1,1,1,1,1,1,1,5313, + 1057,1057,1057,1057,1057,1057,1057,1,1,1057,1,1,1057,1057,1057,1057,1057,1057,1057,1057,1,1057,1057,1,1057,1057,1057,1057,1057,1057,1057,1057, + 1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1,1057,1057,1,1,1057,1057,1057,1057,1057, + 1057,1057,1057,1057,1057,1057,1057,1,1,1,1,1,1,1,1,1,1057,1057,1057,1057,1057,1057,1057,1057,1057,1057,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3201,3201,3201,3201,3201,3201,3201,3201,1,1,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201, + 3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,3201,1,1,3201,3201,3201,3201,3201,3201, + 3201,3201,3201,3201,3201,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409, + 5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409,5409, + 5409,5409,5409,5409,5409,5409,5409,5409,1,1,1,1,1,1,1,1,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417, + 4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417, + 4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417,4417, + 4417,4417,4417,1,1,1,1,1,1,1,1,1,1,1,1,1,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545, + 3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937, + 3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,3937,1,1,1,1,1,1,1, + 1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513, + 4513,4513,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4513,4513,4513,4513,4513,4513,4513,4513,4513,4513,1,1,1,1,1,1, + 385,385,385,385,385,385,385,385,385,1,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385, + 385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,1,385,385,385,385,385,385,385,385, + 385,385,385,385,385,385,1,1,1,1,1,1,1,1,1,1,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385,385, + 385,385,385,385,385,385,385,385,385,385,385,385,385,1,1,1,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721, + 2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,1,1,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721, + 2721,2721,2721,2721,2721,2721,2721,2721,1,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,2721,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2753,2753,2753,2753,2753,2753,2753,1,2753,2753,1,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753, + 2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,1,1,1,2753,1,2753,2753,1,2753, + 2753,2753,2753,2753,2753,2753,2753,2753,1,1,1,1,1,1,1,1,2753,2753,2753,2753,2753,2753,2753,2753,2753,2753,1,1,1,1,1,1, + 1505,1505,1505,1505,1505,1505,1,1505,1505,1,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505, + 1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1,1505,1505,1,1505,1505,1505,1505,1505,1505,1,1,1,1,1,1,1, + 1505,1505,1505,1505,1505,1505,1505,1505,1505,1505,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,2593,1,1,1,1,1,1,1, + 2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,1,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017, + 2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,1,1,1,2017,2017, + 2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2465,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,7906,7906,4801,7906,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801, + 4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,4801,1,1,1,1,1,1,1,1,1,1,1,1,1,4801, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,1,4449,4449,4449,4449,4449,1,1,1,1,1,1,1,1,1,1,1, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449,4449, + 4449,4449,4449,4449,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865, + 865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865, + 865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865, + 865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,865,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1,1,1,1,1,1,1,1,1,1, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153, + 1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1153,1,1,1,1,1, + 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 97,97,97,97,97,97,97,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569, + 1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1569,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257, + 257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,257,1,1,1,1,1,1,1, + 3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,1, + 3041,3041,3041,3041,3041,3041,3041,3041,3041,3041,1,1,1,1,3041,3041,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833, + 4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833, + 4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,1, + 4833,4833,4833,4833,4833,4833,4833,4833,4833,4833,1,1,1,1,1,1,289,289,289,289,289,289,289,289,289,289,289,289,289,289,289,289, + 289,289,289,289,289,289,289,289,289,289,289,289,289,289,1,1,289,289,289,289,289,289,1,1,1,1,1,1,1,1,1,1, + 3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873, + 3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873, + 3873,3873,3873,3873,3873,3873,1,1,1,1,1,1,1,1,1,1,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,1,3873,3873,3873,3873,3873, + 3873,3873,1,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,1,1,1,1,1,3873,3873,3873, + 3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,3873,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241, + 2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,2241,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785, + 2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785, + 2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,2785,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945, + 2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945, + 2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,1,1,1,1,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945, + 2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945, + 2945,2945,2945,2945,2945,2945,2945,2945,1,1,1,1,1,1,1,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945,2945, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4865,3329,1,1,2113,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,4865,1,1,1,1,1,1,1,1, + 2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113, + 2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113, + 2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113, + 2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113, + 2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113, + 2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113, + 2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,2113,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2113, + 4865,4865,4865,4865,4865,4865,4865,4865,4865,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1985,1985,1985,1985,1,1985,1985,1985,1985,1985,1985,1985,1,1985,1985,1, + 1985,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761,1761, + 1985,1985,1985,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1761,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1761,1761,1761,1,1,1985,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1985,1985,1985,1985,1,1,1,1,1,1,1,1,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329, + 3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329, + 3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329, + 3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329, + 3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329, + 3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329, + 3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329, + 3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329, + 3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,3329,1,1,1,1, + 1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121, + 1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121, + 1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121, + 1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1,1,1,1,1,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1,1,1, + 1121,1121,1121,1121,1121,1121,1121,1121,1121,1,1,1,1,1,1,1,1121,1121,1121,1121,1121,1121,1121,1121,1121,1121,1,1,1121,1121,1121,1121, + 1121,1121,1121,1121,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,1,1,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,1,1,1,1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,961,961,961,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,961,961,961,961,961, + 961,961,961,929,929,961,961,961,961,961,961,961,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,961,961,961,961,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441,1441, + 1441,1441,1441,1441,1441,1441,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1, + 769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,769,929,929,929,929,929,929,929,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,929,929, + 1,1,929,1,1,929,929,1,1,929,929,929,929,1,929,929,929,929,929,929,929,929,929,929,929,929,1,929,1,929,929,929, + 929,929,929,929,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,1,929,929,929,929,1,1,929,929,929,929,929,929,929,929,1,929,929,929,929,929,929,929,1,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,929,929,929,929,1, + 929,929,929,929,929,1,929,1,1,1,929,929,929,929,929,929,929,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289, + 4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289, + 4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289, + 4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289, + 4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4289,4289,4289,4289,4289, + 1,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,4289,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,2305,1, + 1,1,1,1,1,2305,2305,2305,2305,2305,2305,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1345,1345,1345,1345,1345,1345,1345,1,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1,1,1345,1345,1345,1345,1345, + 1345,1345,1,1345,1345,1,1345,1345,1345,1345,1345,1,1,1,1,1,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897, + 897,897,897,897,897,897,897,897,897,897,897,897,897,897,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,897,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361, + 3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,1,1,1,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,1,1, + 3361,3361,3361,3361,3361,3361,3361,3361,3361,3361,1,1,1,1,3361,3361,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121, + 5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,5121,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281, + 5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,5281,1,1,1,1,1,5281, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169, + 3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,3169,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457, + 3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,3457,1,1,1,1,3457, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1249,1249,1249,1249,1249,1249,1249,1,1249,1249,1249,1249,1,1249,1249,1,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1249,1, + 2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849, + 2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849, + 2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849, + 2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849, + 2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849, + 2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849, + 2849,2849,2849,2849,2849,1,1,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,2849,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, + 33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, + 33,33,33,33,33,33,33,33,33,33,33,33,1,1,1,1,33,33,33,33,33,33,33,33,33,33,1,1,1,1,33,33, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 129,129,129,129,1,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, + 1,129,129,1,129,1,1,129,1,129,129,129,129,129,129,129,129,129,129,1,129,129,129,129,1,129,1,129,1,1,1,1, + 1,1,129,1,1,1,1,129,1,129,1,129,1,129,129,129,1,129,129,1,129,1,1,129,1,129,1,129,1,129,1,129, + 1,129,129,1,129,1,1,129,129,129,129,1,129,129,129,129,129,129,129,1,129,129,129,129,1,129,129,129,129,1,129,1, + 129,129,129,129,129,129,129,129,129,129,1,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,1,1,1,1, + 1,129,129,129,1,129,129,129,129,129,1,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,129,129,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 1761,929,929,1,1,1,1,1,1,1,1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1, + 929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,769,769,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1, + 929,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1, + 929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,1,1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,929, + 929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,1,1,1,1,1,1, + 1,929,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961, + 961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +}; + +KBTS_INLINE kbts_u16 kbts__GetUnicodeScriptExtension(kbts_u32 Codepoint) +{ + return (Codepoint < 1114110) ? kbts__UnicodeScriptExtension_Data[((kbts_un)kbts__UnicodeScriptExtension_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; +} + +static kbts_u8 kbts__UnicodeMirrorCodepoint_PageIndices[8703] = { + 0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 5,6,2,2,7,8,9,2,2,2,2,2,2,2,10,11,2,2,2,12,13,14,2,15,2,2,2,2,16,2,2,2, + 17,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,18,2,19,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +}; + +static kbts_u32 kbts__UnicodeMirrorCodepoint_Data[2560] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,41,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,0,60,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,0,91,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,0,123,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3899,3898,3901,3900,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5788,5787,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8250,8249,0,0,0,0,0, + 0,0,0,0,0,8262,8261,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8318,8317,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,8334,8333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,8715,8716,8717,8712,8713,8714,0,0,0,0,0,0,0,10741,0,0,0,0,0,0,0,0,0,11262, + 10659,10651,10656,0,10990,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8765,8764,0,0, + 0,0,0,8909,0,8780,0,0,0,0,0,0,8773,0,0,0,0,0,8787,8786,8789,8788,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,8805,8804,8807,8806,8809,8808,8811,8810,0,0,8815,8814,8817,8816,8819,8818,8821,8820,8823,8822,8825,8824,8827,8826,8829,8828,8831,8830, + 8833,8832,8835,8834,8837,8836,8839,8838,8841,8840,8843,8842,0,0,0,8848,8847,8850,8849,0,0,0,0,0,10680,0,0,0,0,0,0,0, + 0,0,8867,8866,0,0,10974,0,10980,10979,0,10981,0,0,0,0,8881,8880,8883,8882,8885,8884,8887,8886,10204,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,8906,8905,8908,8907,8771,0,0,8913,8912,0,0,0,0,8919,8918,8921,8920,8923,8922,8925,8924,8927,8926, + 8929,8928,8931,8930,8933,8932,8935,8934,8937,8936,8939,8938,8941,8940,0,0,8945,8944,8954,8955,8956,0,8957,8958,0,0,8946,8947,8948,8950,8951,0, + 0,0,0,0,0,0,0,0,8969,8968,8971,8970,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,9002,9001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,10089,10088,10091,10090,10093,10092,10095,10094,10097,10096,10099,10098,10101,10100,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,10180,10179,10182,10181,0,10185,10184,0,10189,0,10187,0,0,0,0,0,0,0,10198,10197,0,0,0,0,0,8888,10206,10205,0, + 0,0,10211,10210,10213,10212,10215,10214,10217,10216,10219,10218,10221,10220,10223,10222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,10628,10627,10630,10629,10632,10631,10634,10633,10636,10635,10640,10639,10638,10637,10642,10641,10644,10643,10646,10645,10648,10647,0,0,8737,0,0,0,0, + 8738,0,0,8736,10661,10660,0,0,10665,10664,10667,10666,10669,10668,10671,10670,0,0,0,0,0,0,0,0,8856,0,0,0,0,0,0,0, + 10689,10688,0,0,10693,10692,0,0,0,0,0,0,0,0,0,10704,10703,10706,10705,0,10709,10708,0,0,10713,10712,10715,10714,0,0,0,0, + 0,0,0,0,0,0,0,0,10729,10728,0,0,0,0,0,0,0,0,0,0,0,8725,0,0,10745,10744,0,0,10749,10748,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,10796,10795,10798,10797,0,0,0,0,0,10805,10804,0,0,0,0,0,0,10813,10812,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,10853,10852,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10874,10873,10876,10875,10878,10877,10880, + 10879,10882,10881,10884,10883,10886,10885,10888,10887,10890,10889,10892,10891,10894,10893,10896,10895,10898,10897,10900,10899,10902,10901,10904,10903,10906,10905,10908,10907,10910,10909,10912, + 10911,10914,10913,0,0,0,10919,10918,10921,10920,10923,10922,10925,10924,0,10928,10927,10930,10929,10932,10931,10934,10933,10936,10935,10938,10937,10940,10939,10942,10941,10944, + 10943,10946,10945,10948,10947,10950,10949,10952,10951,10954,10953,10956,10955,10958,10957,10960,10959,10962,10961,10964,10963,10966,10965,0,0,0,0,0,0,0,8870,0, + 0,0,0,8873,8872,8875,0,0,0,0,0,0,10989,10988,8740,0,0,0,0,0,0,0,0,11000,10999,11002,11001,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8735,0, + 0,0,11779,11778,11781,11780,0,0,0,11786,11785,0,11789,11788,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11805,11804,0,0, + 11809,11808,11811,11810,11813,11812,11815,11814,11817,11816,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11862,11861,11864,11863,11866,11865,11868,11867,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,12297,12296,12299,12298,12301,12300,12303,12302,12305,12304,0,0,12309,12308,12311,12310,12313,12312,12315,12314,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65114,65113,65116,65115,65118,65117,0, + 0,0,0,0,65125,65124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,65289,65288,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65310,0,65308,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65341,0,65339,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65373,0,65371,0,65376, + 65375,0,65379,65378,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +KBTS_INLINE kbts_u32 kbts__GetUnicodeMirrorCodepoint(kbts_u32 Codepoint) +{ + return (Codepoint < 1114110) ? kbts__UnicodeMirrorCodepoint_Data[((kbts_un)kbts__UnicodeMirrorCodepoint_PageIndices[Codepoint/128] * 128) | (Codepoint & 127)] : 0; +} + +KBTS_INLINE kbts_u32 kbts__GetDecompositionSize(kbts_u64 Decomposition) { return Decomposition & 3; } -KBTS_INLINE kbts_u32 kbts_GetDecompositionCodepoint(kbts_u64 Decomposition, kbts_un Index){ +KBTS_INLINE kbts_u32 kbts__GetDecompositionCodepoint(kbts_u64 Decomposition, kbts_un Index){ return (Decomposition >> (Index ? 23 : 2)) & 0x1FFFFF; } #define KBTS_UNICODE_DECOMPOSITION_DO_NOT_RECURSE0 (1ull << 44) #define KBTS_UNICODE_DECOMPOSITION_DO_NOT_RECURSE1 (1ull << 45) -KBTS_INLINE kbts_u32 kbts_GetMatchingBracket(kbts_u64 Decomposition) -{ - return (Decomposition >> 23) & 0x1FFFFF; -} - -KBTS_INLINE kbts_u32 kbts_GetUnicodeMatchingBracket(kbts_u32 Codepoint) -{ - kbts_u64 Decomposition = kbts_GetUnicodeDecomposition(Codepoint); return (Decomposition >> 23) & 0x1FFFFF; -} - -KBTS_INLINE kbts_u8 kbts_GetSyllabicClass(kbts_u16 SyllabicInfo) +KBTS_INLINE kbts_u8 kbts__GetSyllabicClass(kbts_u16 SyllabicInfo) { return SyllabicInfo & 0xFF; } -KBTS_INLINE kbts_u8 kbts_GetSyllabicPosition(kbts_u16 SyllabicInfo) +KBTS_INLINE kbts_u8 kbts__GetSyllabicPosition(kbts_u16 SyllabicInfo) { return SyllabicInfo >> 8; } -KBTS_INLINE kbts_s32 *kbts_GetParentInfoDeltas(kbts_u32 ParentInfo) +KBTS_INLINE kbts_s32 *kbts__GetParentInfoDeltas(kbts_u32 ParentInfo) { - return kbts_UnicodeParentDeltas + (ParentInfo & 0xFFFF); + return kbts__UnicodeParentDeltas + (ParentInfo & 0xFFFF); } -KBTS_INLINE kbts_un kbts_GetParentInfoCount(kbts_u32 ParentInfo) +KBTS_INLINE kbts_un kbts__GetParentInfoCount(kbts_u32 ParentInfo) { return ParentInfo >> 16; } +KBTS_INLINE kbts_un kbts__ScriptExtensionCount(kbts_u16 ScriptExtension) +{ + return (ScriptExtension & 0x1f); +} + +KBTS_INLINE kbts_un kbts__ScriptExtensionOffset(kbts_u16 ScriptExtension) +{ + return (ScriptExtension >> 5); +} + typedef kbts_u8 kbts_grapheme_break_class; enum kbts_grapheme_break_class_enum { /* 0 */ KBTS_GRAPHEME_BREAK_CLASS_DEFAULT, @@ -12836,56 +15211,423 @@ static kbts_u8 kbts_UseTransition[KBTS_USE_SYLLABIC_CLASS_COUNT][KBTS_USE_STATE_ /* p */ {13,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,}, }; -static const kbts_u8 kbts_CmapFormatPrecedence[14] = {1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 3, 0, 4, 5}; +KBTS_EXPORT void kbts_FontCoverageTestBegin(kbts_font_coverage_test *Test, kbts_font *Font) +{ + Test->Font = Font; + Test->BaseParentCount = 0; + Test->BaseCodepoint = 0; + Test->CurrentBaseError = 0; + Test->Error = 0; +} -static int kbts_ShaperClearsMarkAdvancesInPostGposFixup(kbts_u32 Shaper) +KBTS_EXPORT void kbts_FontCoverageTestCodepoint(kbts_font_coverage_test *Test, int Codepoint) +{ + if(!Test->Error) + { + kbts_u32 UCodepoint = (kbts_u32)Codepoint; + int RecomposeBase = 0; + + if(!kbts__GetUnicodeCombiningClass(UCodepoint)) + { + Test->Error |= Test->CurrentBaseError; + Test->CurrentBaseError = 0; + Test->BaseCodepoint = UCodepoint; + + RecomposeBase = 1; + } + else + { + kbts_u16 Id = (kbts_u16)kbts_CodepointToGlyphId(Test->Font, Codepoint); + + if(!Id) + { + KBTS__FOR(ParentIndex, 0, Test->BaseParentCount) + { + kbts_glyph_parent *Parent = &Test->BaseParents[ParentIndex]; + + if(kbts__GetDecompositionCodepoint(Parent->Decomposition, 1) == UCodepoint) + { + Test->BaseCodepoint = Parent->Codepoint; + RecomposeBase = 1; + + break; + } + } + + if(!RecomposeBase) + { + Test->Error = 1; + } + } + } + + if(RecomposeBase) + { + kbts_u32 BaseCodepoint = Test->BaseCodepoint; + kbts_u16 BaseId = (kbts_u16)kbts_CodepointToGlyphId(Test->Font, (int)Test->BaseCodepoint); + kbts_un BaseParentCount = 0; + + int Done = 0; + while(!Done) + { + kbts_u32 ParentInfo = kbts__GetUnicodeParentInfo(BaseCodepoint); + kbts_s32 *ParentDeltas = kbts__GetParentInfoDeltas(ParentInfo); + + BaseParentCount = 0; + + Done = 1; + + KBTS__FOR(ParentIndex, 0, BaseParentCount) + { + kbts_glyph_parent Parent = KBTS__ZERO; + Parent.Codepoint = BaseCodepoint + (kbts_u32)ParentDeltas[ParentIndex]; + Parent.Decomposition = kbts__GetUnicodeDecomposition(Parent.Codepoint); + kbts_u16 RecompositionId = (kbts_u16)kbts_CodepointToGlyphId(Test->Font, (int)Parent.Codepoint); + + if(RecompositionId) + { + if(kbts__GetDecompositionSize(Parent.Decomposition) == 1) + { + BaseCodepoint = Parent.Codepoint; + BaseId = RecompositionId; + + Done = 0; + + break; + } + else + { + Test->BaseParents[BaseParentCount++] = Parent; + } + } + } + } + + Test->BaseCodepoint = BaseCodepoint; + Test->BaseParentCount = (kbts_u32)BaseParentCount; + + if(!BaseId) + { + Test->CurrentBaseError = 1; + } + } + } +} + +KBTS_EXPORT int kbts_FontCoverageTestEnd(kbts_font_coverage_test *Test) +{ + int Result = Test->CurrentBaseError | Test->Error; + Test->Error = Result; + return !Result; +} + +typedef struct kbts__feature_override_header +{ + struct kbts__feature_override_header *Prev; + struct kbts__feature_override_header *Next; +} kbts__feature_override_header; + +typedef struct kbts__feature_override +{ + kbts__feature_override_header Header; + + kbts_u32 Tag; + int Value; +} kbts__feature_override; + +typedef struct kbts_glyph_config +{ + kbts__feature_set EnabledFeatures; + kbts__feature_set DisabledFeatures; + + kbts_u32 ExtraOverrideCount; + + kbts_allocator_function *Allocator; + void *AllocatorData; + + kbts__feature_override_header FeatureOverrideSentinel; +} kbts_glyph_config; + +typedef struct kbts__arena_block +{ + kbts_arena_block_header Header; + void *BaseAllocation; // For freeing + + kbts_un Size; + kbts_un Used; + + // char Memory[Size]; // if Memory is NULL +} kbts__arena_block; + +typedef struct kbts__arena_lifetime +{ + kbts_arena *Arena; + kbts_arena_block_header *BlockHeader; + kbts_un Used; +} kbts__arena_lifetime; + +typedef struct kbts__glyph_list +{ + kbts_glyph *SentinelPrev; + kbts_glyph *SentinelNext; + kbts_glyph *OneBeforeFirst; + kbts_glyph *OnePastLast; +} kbts__glyph_list; + +typedef struct kbts__baked_feature +{ + kbts_u16 *Indices; + kbts_u32 Count; + kbts_u32 FeatureTag; + kbts_u32 FeatureId; + kbts_u32 SkipFlags; + kbts_u32 GlyphFilter; +} kbts__baked_feature; + +typedef struct kbts__baked_feature_stage +{ + kbts_u16 FeatureCount; + kbts_u16 FeatureIndexCount; + kbts__baked_feature *Features; // [FeatureCount] + kbts_u16 *FeatureIndices; // [FeatureIndexCount] +} kbts__baked_feature_stage; + +#define KBTS_MAX_SIMULTANEOUS_FEATURES 32 +typedef struct kbts__shape_scratchpad +{ + kbts_arena *Arena; + + kbts__feature_set LookupFeatures; + kbts__feature_set UserFeatures; + kbts__feature_set ScratchFeatures; + + kbts__glyph_list Cluster; + int RealCluster; + int ClusterAtStartOfWord; + + kbts_glyph *LookupOnePastLastGlyph; + kbts_u32 LookupOnePastLastGlyphIndex; + + kbts_u32 NextGlyphUid; + + kbts_u32 Ip; + kbts__op_kind OpKind; + kbts__feature_set *OpFeatures; + + kbts_direction RunDirection; + + kbts_u32 FeatureIndexIndex; + kbts_u32 FeatureStagesRead; + kbts_u16 BakedLookupSubtablesRead[KBTS_MAX_SIMULTANEOUS_FEATURES]; + + kbts_u32 UnregisteredFeatureCount; + kbts_feature_tag UnregisteredFeatureTags[KBTS_MAX_SIMULTANEOUS_FEATURES]; + + kbts_shape_error Error; +} kbts__shape_scratchpad; + +#define KBTS_CONTEXT_MAX_FONT_COUNT 32 + +typedef struct kbts__config_params +{ + kbts_u32 FontIndex; + kbts_script Script; + kbts_language Language; +} kbts__config_params; + +#define KBTS__INPUT_CODEPOINT_FIRST_BLOCK_MSB 6 +#define KBTS__INPUT_CODEPOINT_ONE_PAST_LAST_BLOCK_MSB 27 + +typedef struct kbts__existing_shape_config +{ + kbts_shape_config *Config; + + kbts_font *Font; + kbts_script Script; +} kbts__existing_shape_config; + +typedef kbts_u32 kbts__context_flags; +enum kbts__context_flags_enum +{ + KBTS__CONTEXT_FLAG_NONE, + + KBTS__CONTEXT_FLAG_MANUAL_SEGMENTATION = 1, + KBTS__CONTEXT_FLAG_START_OF_MANUAL_RUN = 2, + KBTS__CONTEXT_FLAG_USE_MANUAL_BREAK_INFO = 4, +}; + +typedef struct kbts__context_font +{ + kbts_font *Font; + kbts__arena_lifetime Lifetime; +} kbts__context_font; + +typedef struct kbts_shape_context +{ + kbts_arena PermanentArena; + kbts_arena FontArena; + kbts_arena ConfigArena; + kbts_arena ScratchArena; + + kbts_allocator_function *SelfAllocator; + void *SelfAllocatorData; + + kbts_shape_codepoint *LastGraphemeBreak; + kbts_u32 LastGraphemeBreakIndex; + kbts_u32 LastLineBreakIndex; + kbts_u32 BreakStartIndex; + + kbts_u32 FontCount; + kbts__context_font Fonts[KBTS_CONTEXT_MAX_FONT_COUNT]; + + kbts__config_params *ConfigTableKeys; + kbts_shape_config **ConfigTable; + + kbts__feature_override_header FeatureOverrideSentinel; + kbts__feature_override_header FreeFeatureOverrideSentinel; + + kbts_un InputCodepointCount; + int NextUserId; + kbts_shape_codepoint *InputBlocks[KBTS__INPUT_CODEPOINT_ONE_PAST_LAST_BLOCK_MSB - KBTS__INPUT_CODEPOINT_FIRST_BLOCK_MSB]; + + kbts_glyph_storage GlyphStorage; + + kbts__context_flags Flags; + kbts_direction ManualRunDirection; + kbts_script ManualRunScript; + + kbts_u32 BeginClusterIp; + kbts_u32 BeginClusterFeatureStagesRead; + kbts_glyph *TopLevelGlyph; + + kbts__feature_set *OpFeatures; + kbts__feature_set ScratchFeatures; + kbts__feature_set UserFeatures; + kbts__feature_set UserFeaturesEnabled; + + kbts_glyph_config *CurrentGlyphConfig; + int NeedNewGlyphConfig; + + kbts_direction ParagraphDirection; + kbts_language Language; + + kbts_font *RunFont; + kbts_script RunScript; + // This may be different from ParagraphDirection if ParagraphDirection == KBTS_DIRECTION_DONT_KNOW. + kbts_direction RunParagraphDirection; + kbts_direction RunDirection; + kbts_shape_codepoint_iterator RunCodepointIterator; + int DoneShapingRuns; + + kbts_u32 ExistingShapeConfigCount; + kbts__existing_shape_config ExistingShapeConfigs[32]; + + kbts_break_state BreakState; + + kbts_u32 FrameCount; + + kbts_shape_error Error; +} kbts_shape_context; + +typedef struct kbts__indic_script_properties +{ + kbts_u32 ViramaCodepoint; + kbts_u8 BlwfPostOnly; + kbts__reph_position RephPosition; + kbts__reph_encoding RephEncoding; + kbts__syllabic_position RightSideMatraPosition; + kbts__syllabic_position AboveBaseMatraPosition; + kbts__syllabic_position BelowBaseMatraPosition; +} kbts__indic_script_properties; + +typedef struct kbts_shape_config +{ + kbts_allocator_function *Allocator; + void *AllocatorData; + + kbts_font *Font; + kbts_script Script; + kbts_language Language; + kbts__langsys *Langsys[KBTS_SHAPING_TABLE_COUNT]; + kbts__op_list OpList; + + kbts__feature_set Features; + + kbts_shaper Shaper; + kbts_shaper_properties *ShaperProperties; + + kbts__indic_script_properties IndicScriptProperties; + kbts__feature *Blwf; + kbts__feature *Pref; + kbts__feature *Pstf; + kbts__feature *Locl; + kbts__feature *Rphf; + kbts__feature *Half; + kbts__feature *Vatu; + + // Indic + kbts_glyph Virama; + + kbts_glyph DottedCircle; + kbts_glyph Whitespace; + + // Thai + kbts_glyph Nikhahit; + kbts_glyph SaraAa; + + kbts__baked_feature_stage *FeatureStages; // [OpList.FeatureStageCount] +} kbts_shape_config; + +static const kbts_u8 kbts__CmapFormatPrecedence[14] = {1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 3, 0, 4, 5}; + +static int kbts__ShaperClearsMarkAdvancesInPostGposFixup(kbts_u32 Shaper) { int Result = (1 << Shaper) & ((1 << KBTS_SHAPER_ARABIC) | (1 << KBTS_SHAPER_DEFAULT) | (1 << KBTS_SHAPER_HEBREW)); return Result; } -static kbts_indic_script_properties kbts_IndicScriptProperties(kbts_u32 Script) +static kbts__indic_script_properties kbts__IndicScriptProperties(kbts_u32 Script) { - kbts_indic_script_properties Result = KBTS_ZERO; + kbts__indic_script_properties Result = KBTS__ZERO; switch(Script) { case KBTS_SCRIPT_DEVANAGARI: { Result.ViramaCodepoint = 0x94D; - Result.RephPosition = KBTS_REPH_POSITION_BEFORE_POST; - Result.RightSideMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED; - Result.AboveBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED; + Result.RephPosition = KBTS__REPH_POSITION_BEFORE_POST; + Result.RightSideMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED; + Result.AboveBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED; } break; case KBTS_SCRIPT_BENGALI: { Result.ViramaCodepoint = 0x9CD; - Result.RephPosition = KBTS_REPH_POSITION_AFTER_SUBJOINED; - Result.RightSideMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED; + Result.RephPosition = KBTS__REPH_POSITION_AFTER_SUBJOINED; + Result.RightSideMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED; } break; case KBTS_SCRIPT_GUJARATI: { Result.ViramaCodepoint = 0xACD; - Result.RephPosition = KBTS_REPH_POSITION_BEFORE_POST; - Result.RightSideMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; - Result.AboveBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; + Result.RephPosition = KBTS__REPH_POSITION_BEFORE_POST; + Result.RightSideMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; + Result.AboveBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; } break; case KBTS_SCRIPT_GURMUKHI: { Result.ViramaCodepoint = 0xA4D; - Result.RephPosition = KBTS_REPH_POSITION_BEFORE_SUBJOINED; - Result.RightSideMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; - Result.AboveBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; + Result.RephPosition = KBTS__REPH_POSITION_BEFORE_SUBJOINED; + Result.RightSideMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; + Result.AboveBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; } break; @@ -12893,47 +15635,47 @@ static kbts_indic_script_properties kbts_IndicScriptProperties(kbts_u32 Script) { Result.ViramaCodepoint = 0xCCD; Result.BlwfPostOnly = 1; - Result.AboveBaseMatraPosition = KBTS_SYLLABIC_POSITION_BEFORE_SUBJOINED; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_BEFORE_SUBJOINED; + Result.AboveBaseMatraPosition = KBTS__SYLLABIC_POSITION_BEFORE_SUBJOINED; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_BEFORE_SUBJOINED; } break; case KBTS_SCRIPT_MALAYALAM: { Result.ViramaCodepoint = 0xD4D; - Result.RephPosition = KBTS_REPH_POSITION_AFTER_MAIN; - Result.RephEncoding = KBTS_REPH_ENCODING_LOGICAL_REPHA; - Result.RightSideMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; + Result.RephPosition = KBTS__REPH_POSITION_AFTER_MAIN; + Result.RephEncoding = KBTS__REPH_ENCODING_LOGICAL_REPHA; + Result.RightSideMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; } break; case KBTS_SCRIPT_ODIA: { Result.ViramaCodepoint = 0xB4D; - Result.RephPosition = KBTS_REPH_POSITION_AFTER_MAIN; - Result.RightSideMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; - Result.AboveBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_MAIN; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED; + Result.RephPosition = KBTS__REPH_POSITION_AFTER_MAIN; + Result.RightSideMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; + Result.AboveBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_MAIN; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED; } break; case KBTS_SCRIPT_TAMIL: { Result.ViramaCodepoint = 0xBCD; - Result.RightSideMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; - Result.AboveBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_AFTER_POST; + Result.RightSideMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; + Result.AboveBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_AFTER_POST; } break; case KBTS_SCRIPT_TELUGU: { Result.ViramaCodepoint = 0xC4D; - Result.RephEncoding = KBTS_REPH_ENCODING_EXPLICIT; + Result.RephEncoding = KBTS__REPH_ENCODING_EXPLICIT; Result.BlwfPostOnly = 1; - Result.AboveBaseMatraPosition = KBTS_SYLLABIC_POSITION_BEFORE_SUBJOINED; - Result.BelowBaseMatraPosition = KBTS_SYLLABIC_POSITION_BEFORE_SUBJOINED; + Result.AboveBaseMatraPosition = KBTS__SYLLABIC_POSITION_BEFORE_SUBJOINED; + Result.BelowBaseMatraPosition = KBTS__SYLLABIC_POSITION_BEFORE_SUBJOINED; } break; } @@ -12941,45 +15683,46 @@ static kbts_indic_script_properties kbts_IndicScriptProperties(kbts_u32 Script) return Result; } -static void kbts_ByteSwapArray16Unchecked(kbts_u16 *Array, kbts_un Count) +static void kbts__ByteSwapArray16Unchecked(kbts_u16 *Array, kbts_un Count) { - KBTS_FOR(It, 0, Count) + KBTS__FOR(It, 0, Count) { - Array[It] = kbts_ByteSwap16(Array[It]); + Array[It] = kbts__ByteSwap16(Array[It]); } } -static int kbts_ByteSwapArray16(kbts_u16 *Array, kbts_un Count, char *End) +static int kbts__ByteSwapArray16(kbts_u16 *Array, kbts_un Count, char *End) { int Result = 0; if((char *)(Array + Count) <= End) { - kbts_ByteSwapArray16Unchecked(Array, Count); + kbts__ByteSwapArray16Unchecked(Array, Count); + Result = 1; + } + + return Result; +} + +static void kbts__ByteSwapArray32Unchecked(kbts_u32 *Array, kbts_un Count) +{ + KBTS__FOR(It, 0, Count) + { + Array[It] = kbts__ByteSwap32(Array[It]); + } +} + +static int kbts__ByteSwapArray32(kbts_u32 *Array, kbts_un Count, char *End) +{ + int Result = 0; + if((char *)(Array + Count) <= End) + { + kbts__ByteSwapArray32Unchecked(Array, Count); Result = 1; } return Result; } -static void kbts_ByteSwapArray32Unchecked(kbts_u32 *Array, kbts_un Count) -{ - KBTS_FOR(It, 0, Count) - { - Array[It] = kbts_ByteSwap32(Array[It]); - } -} - -static int kbts_ByteSwapArray32(kbts_u32 *Array, kbts_un Count, char *End) -{ - int Result = 0; - if((char *)(Array + Count) <= End) - { - kbts_ByteSwapArray32Unchecked(Array, Count); - Result = 1; - } - return Result; -} - -static kbts_u64 kbts_ContainsFeature(kbts_feature_set *Set, kbts_feature_id Id) +static kbts_u64 kbts__ContainsFeature(kbts__feature_set *Set, kbts__feature_id Id) { kbts_un WordIndex = Id / 64; kbts_un BitIndex = Id % 64; @@ -12988,7 +15731,7 @@ static kbts_u64 kbts_ContainsFeature(kbts_feature_set *Set, kbts_feature_id Id) return Result; } -static void kbts_AddFeature(kbts_feature_set *Set, kbts_feature_id Id) +static void kbts__AddFeature(kbts__feature_set *Set, kbts__feature_id Id) { kbts_un WordIndex = Id / 64; kbts_un BitIndex = Id % 64; @@ -12996,202 +15739,123 @@ static void kbts_AddFeature(kbts_feature_set *Set, kbts_feature_id Id) Set->Flags[WordIndex] |= 1ull << BitIndex; } -static kbts_feature_override kbts_FeatureOverrideBase(kbts_feature_id Id, kbts_feature_tag Tag, int Alternate, kbts_u32 Value) -{ - kbts_feature_override Result = KBTS_ZERO; - Result.Id = Id; - Result.Tag = Tag; - - if(Alternate) - { - Result.EnabledOrAlternatePlusOne = Value + 1; - } - else - { - Result.EnabledOrAlternatePlusOne = Value; - } - - return Result; -} -KBTS_EXPORT kbts_feature_override kbts_FeatureOverrideFromTag(kbts_feature_tag Tag, int Alternate, kbts_u32 Value) -{ - kbts_feature_override Result = kbts_FeatureOverrideBase(kbts_FeatureTagToId(Tag), Tag, Alternate, Value); - return Result; -} -KBTS_EXPORT kbts_feature_override kbts_FeatureOverride(kbts_feature_id Id, int Alternate, kbts_u32 Value) -{ - kbts_feature_override Result = kbts_FeatureOverrideBase(Id, 0, Alternate, Value); - return Result; -} - -KBTS_EXPORT kbts_glyph_config kbts_GlyphConfig(kbts_feature_override *FeatureOverrides, kbts_u32 FeatureOverrideCount) -{ - kbts_glyph_config Result = KBTS_ZERO; - Result.FeatureOverrides = FeatureOverrides; - Result.FeatureOverrideCount = FeatureOverrideCount; - - KBTS_FOR(FeatureOverrideIndex, 0, FeatureOverrideCount) - { - kbts_feature_override *Override = &FeatureOverrides[FeatureOverrideIndex]; - if(Override->EnabledOrAlternatePlusOne) - { - kbts_AddFeature(&Result.EnabledFeatures, FeatureOverrides[FeatureOverrideIndex].Id); - } - else - { - kbts_AddFeature(&Result.DisabledFeatures, FeatureOverrides[FeatureOverrideIndex].Id); - } - } - - return Result; -} - -KBTS_EXPORT kbts_glyph_config kbts_EmptyGlyphConfig(kbts_feature_override *FeatureOverrides, kbts_u32 FeatureOverrideCapacity) -{ - kbts_glyph_config Result = KBTS_ZERO; - Result.FeatureOverrides = FeatureOverrides; - Result.FeatureOverrideCapacity = FeatureOverrideCapacity; - return Result; -} - -static int kbts_GlyphConfigOverrideFeatureBase(kbts_glyph_config *Config, kbts_feature_id Id, kbts_feature_tag Tag, int Alternate, kbts_u32 Value) -{ - kbts_feature_set *Set = &Config->EnabledFeatures; - if(!Value) - { - Set = &Config->DisabledFeatures; - } - kbts_AddFeature(Set, Id); - - if(!Id || Alternate) - { - if(Config->FeatureOverrideCount < Config->FeatureOverrideCapacity) - { - kbts_feature_override Override = kbts_FeatureOverrideFromTag(Tag, Alternate, Value); - Config->FeatureOverrides[Config->FeatureOverrideCount++] = Override; - } - Config->RequiredFeatureOverrideCapacity += 1; - } - - int Result = Config->RequiredFeatureOverrideCapacity <= Config->FeatureOverrideCapacity; - return Result; -} -KBTS_EXPORT int kbts_GlyphConfigOverrideFeature(kbts_glyph_config *Config, kbts_feature_id Id, int Alternate, kbts_u32 Value) -{ - int Result = kbts_GlyphConfigOverrideFeatureBase(Config, Id, 0, Alternate, Value); - return Result; -} -KBTS_EXPORT int kbts_GlyphConfigOverrideFeatureFromTag(kbts_glyph_config *Config, kbts_feature_tag Tag, int Alternate, kbts_u32 Value) -{ - int Result = kbts_GlyphConfigOverrideFeatureBase(Config, kbts_FeatureTagToId(Tag), Tag, Alternate, Value); - return Result; -} - // // TTF struct definitions. // enum { - KBTS_GLYPH_CLASS_BASE = 1, - KBTS_GLYPH_CLASS_LIGATURE = 2, - KBTS_GLYPH_CLASS_MARK = 3, - KBTS_GLYPH_CLASS_COMPONENT = 4, + KBTS__GLYPH_CLASS_BASE = 1, + KBTS__GLYPH_CLASS_LIGATURE = 2, + KBTS__GLYPH_CLASS_MARK = 3, + KBTS__GLYPH_CLASS_COMPONENT = 4, }; enum { - KBTS_LOOKUP_FLAG_RIGHT_TO_LEFT = 1 << 0, - KBTS_LOOKUP_FLAG_IGNORE_BASE_GLYPHS = 1 << 1, - KBTS_LOOKUP_FLAG_IGNORE_LIGATURES = 1 << 2, - KBTS_LOOKUP_FLAG_IGNORE_MARKS = 1 << 3, - KBTS_LOOKUP_FLAG_USE_MARK_FILTERING_SET = 1 << 4, + KBTS__LOOKUP_FLAG_RIGHT_TO_LEFT = 1 << 0, + KBTS__LOOKUP_FLAG_IGNORE_BASE_GLYPHS = 1 << 1, + KBTS__LOOKUP_FLAG_IGNORE_LIGATURES = 1 << 2, + KBTS__LOOKUP_FLAG_IGNORE_MARKS = 1 << 3, + KBTS__LOOKUP_FLAG_USE_MARK_FILTERING_SET = 1 << 4, - KBTS_LOOKUP_FLAG_MARK_ATTACHMENT_CLASS_FILTER = 0xFF00, + KBTS__LOOKUP_FLAG_MARK_ATTACHMENT_CLASS_FILTER = 0xFF00, }; enum { - KBTS_VALUE_FORMAT_X_PLACEMENT = 1 << 0, - KBTS_VALUE_FORMAT_Y_PLACEMENT = 1 << 1, - KBTS_VALUE_FORMAT_X_ADVANCE = 1 << 2, - KBTS_VALUE_FORMAT_Y_ADVANCE = 1 << 3, - KBTS_VALUE_FORMAT_X_PLACEMENT_DEVICE = 1 << 4, - KBTS_VALUE_FORMAT_Y_PLACEMENT_DEVICE = 1 << 5, - KBTS_VALUE_FORMAT_X_ADVANCE_DEVICE = 1 << 6, - KBTS_VALUE_FORMAT_Y_ADVANCE_DEVICE = 1 << 7, + KBTS__OS2_WIDTH_ULTRA_CONDENSED = 1, + KBTS__OS2_WIDTH_EXTRA_CONDENSED = 2, + KBTS__OS2_WIDTH_CONDENSED = 3, + KBTS__OS2_WIDTH_SEMI_CONDENSED = 4, + KBTS__OS2_WIDTH_MEDIUM = 5, + KBTS__OS2_WIDTH_SEMI_EXPANDED = 6, + KBTS__OS2_WIDTH_EXPANDED = 7, + KBTS__OS2_WIDTH_EXTRA_EXPANDED = 8, + KBTS__OS2_WIDTH_ULTRA_EXPANDED = 9, }; enum { - KBTS_DELTA_FORMAT_2_BIT = 1, - KBTS_DELTA_FORMAT_4_BIT = 2, - KBTS_DELTA_FORMAT_8_BIT = 3, - KBTS_DELTA_FORMAT_VARIATION_INDEX = 0x8000, + KBTS__VALUE_FORMAT_X_PLACEMENT = 1 << 0, + KBTS__VALUE_FORMAT_Y_PLACEMENT = 1 << 1, + KBTS__VALUE_FORMAT_X_ADVANCE = 1 << 2, + KBTS__VALUE_FORMAT_Y_ADVANCE = 1 << 3, + KBTS__VALUE_FORMAT_X_PLACEMENT_DEVICE = 1 << 4, + KBTS__VALUE_FORMAT_Y_PLACEMENT_DEVICE = 1 << 5, + KBTS__VALUE_FORMAT_X_ADVANCE_DEVICE = 1 << 6, + KBTS__VALUE_FORMAT_Y_ADVANCE_DEVICE = 1 << 7, +}; + +enum +{ + KBTS__OS2_SELECTION_FLAG_NONE, + + KBTS__OS2_SELECTION_FLAG_ITALIC = (1 << 0), + KBTS__OS2_SELECTION_FLAG_UNDERSCORE = (1 << 1), + KBTS__OS2_SELECTION_FLAG_NEGATIVE = (1 << 2), + KBTS__OS2_SELECTION_FLAG_OUTLINED = (1 << 3), + KBTS__OS2_SELECTION_FLAG_STRIKEOUT = (1 << 4), + KBTS__OS2_SELECTION_FLAG_BOLD = (1 << 5), + KBTS__OS2_SELECTION_FLAG_REGULAR = (1 << 6), + KBTS__OS2_SELECTION_FLAG_USE_TYPO_METRICS = (1 << 7), + KBTS__OS2_SELECTION_FLAG_WWS = (1 << 8), + KBTS__OS2_SELECTION_FLAG_OBLIQUE = (1 << 9), }; # pragma pack(push, 1) -typedef struct kbts_glyph_header -{ - kbts_s16 ContourCount; - kbts_s16 MinX; - kbts_s16 MinY; - kbts_s16 MaxX; - kbts_s16 MaxY; -} kbts_glyph_header; - -typedef struct kbts_script_record +typedef struct kbts__script_record { kbts_u32 Tag; kbts_u16 Offset; -} kbts_script_record; +} kbts__script_record; -typedef struct kbts_script_list +typedef struct kbts__script_list { kbts_u16 Count; - // kbts_script_record Records[Count]; -} kbts_script_list; + // kbts__script_record Records[Count]; +} kbts__script_list; -typedef struct kbts_ot_script +typedef struct kbts__ot_script { kbts_u16 DefaultLangsysOffset; kbts_u16 Count; - // kbts_langsys_record Records[Count]; -} kbts_ot_script; + // kbts__langsys_record Records[Count]; +} kbts__ot_script; -typedef struct kbts_langsys_record +typedef struct kbts__langsys_record { kbts_u32 Tag; kbts_u16 Offset; -} kbts_langsys_record; +} kbts__langsys_record; -typedef struct kbts_langsys +typedef struct kbts__langsys { kbts_u16 LookupOrderOffset; // reserved kbts_u16 RequiredFeatureIndex; kbts_u16 FeatureIndexCount; // kbts_u16 FeatureIndices[FeatureIndexCount]; -} kbts_langsys; +} kbts__langsys; -typedef struct kbts_feature_list +typedef struct kbts__feature_list { kbts_u16 Count; - // kbts_feature_record Records[Count]; -} kbts_feature_list; + // kbts__feature_record Records[Count]; +} kbts__feature_list; -typedef struct kbts_feature_record +typedef struct kbts__feature_record { kbts_u32 Tag; kbts_u16 Offset; -} kbts_feature_record; +} kbts__feature_record; -typedef struct kbts_feature +typedef struct kbts__feature { kbts_u16 FeatureParamsOffset; kbts_u16 LookupIndexCount; // kbts_u16 LookupIndices[LookupIndexCount]; -} kbts_feature; +} kbts__feature; typedef struct kbts_lookup_list { @@ -13201,7 +15865,7 @@ typedef struct kbts_lookup_list // Lookups are used both in GSUB and GPOS. // Type means either GSUB Lookup Type, or GPOS Lookup Type, depending on the table we are parsing. -typedef struct kbts_lookup +typedef struct kbts__lookup { kbts_u16 Type; kbts_u16 Flag; @@ -13209,122 +15873,122 @@ typedef struct kbts_lookup // kbts_u16 SubtableOffsets[SubtableCount]; // If USE_MARK_FILTERING_SET: // kbts_u16 MarkFilteringSet; -} kbts_lookup; +} kbts__lookup; -typedef struct kbts_coverage +typedef struct kbts__coverage { kbts_u16 Format; kbts_u16 Count; // If Format == 1: // kbts_u16 GlyphArray[Count]; // If Format == 2: - // kbts_range_record Ranges[Count]; -} kbts_coverage; + // kbts__range_record Ranges[Count]; +} kbts__coverage; -typedef struct kbts_range_record +typedef struct kbts__range_record { kbts_u16 StartGlyphId; kbts_u16 EndGlyphId; kbts_u16 StartCoverageIndex; -} kbts_range_record; +} kbts__range_record; -typedef struct kbts_class_definition_1 +typedef struct kbts__class_definition_1 { kbts_u16 Format; kbts_u16 StartGlyphId; kbts_u16 GlyphCount; // kbts_u16 ClassValues[GlyphCount]; -} kbts_class_definition_1; +} kbts__class_definition_1; -typedef struct kbts_class_definition_2 +typedef struct kbts__class_definition_2 { kbts_u16 Format; kbts_u16 Count; - // kbts_class_range_record Ranges[Count]; -} kbts_class_definition_2; + // kbts__class_range_record Ranges[Count]; +} kbts__class_definition_2; -typedef struct kbts_class_range_record +typedef struct kbts__class_range_record { kbts_u16 StartGlyphId; kbts_u16 EndGlyphId; kbts_u16 Class; -} kbts_class_range_record; +} kbts__class_range_record; -typedef struct kbts_sequence_lookup_record +typedef struct kbts__sequence_lookup_record { kbts_u16 SequenceIndex; kbts_u16 LookupListIndex; -} kbts_sequence_lookup_record; +} kbts__sequence_lookup_record; -typedef struct kbts_sequence_rule +typedef struct kbts__sequence_rule { kbts_u16 GlyphCount; kbts_u16 SequenceLookupCount; // kbts_u16 InputSequence[GlyphCount - 1]; - // kbts_sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; -} kbts_sequence_rule; + // kbts__sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; +} kbts__sequence_rule; -typedef struct kbts_sequence_rule_set +typedef struct kbts__sequence_rule_set { kbts_u16 Count; // kbts_u16 Offsets[Count]; -} kbts_sequence_rule_set; +} kbts__sequence_rule_set; -typedef struct kbts_sequence_context_1 +typedef struct kbts__sequence_context_1 { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 SeqRuleSetCount; // kbts_u16 SeqRuleSetOffsets[]; -} kbts_sequence_context_1; +} kbts__sequence_context_1; -typedef struct kbts_sequence_context_2 +typedef struct kbts__sequence_context_2 { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 ClassDefOffset; kbts_u16 ClassSequenceRuleSetCount; // kbts_u16 ClassSequenceRuleSetOffsets[ClassSequenceRuleSetCount]; May be NULL! -} kbts_sequence_context_2; +} kbts__sequence_context_2; -typedef struct kbts_class_sequence_rule_set +typedef struct kbts__class_sequence_rule_set { kbts_u16 Count; // kbts_u16 ClassSequenceRuleOffsets[Count]; -} kbts_class_sequence_rule_set; +} kbts__class_sequence_rule_set; -typedef struct kbts_class_sequence_rule +typedef struct kbts__class_sequence_rule { kbts_u16 GlyphCount; kbts_u16 SequenceLookupCount; // kbts_u16 InputSequence[GlyphCount-1]; - // kbts_sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; -} kbts_class_sequence_rule; + // kbts__sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; +} kbts__class_sequence_rule; -typedef struct kbts_sequence_context_3 +typedef struct kbts__sequence_context_3 { kbts_u16 Format; kbts_u16 GlyphCount; kbts_u16 SequenceLookupCount; // kbts_u16 CoverageOffsets[GlyphCount]; - // kbts_sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; -} kbts_sequence_context_3; + // kbts__sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; +} kbts__sequence_context_3; -typedef struct kbts_chained_sequence_context_1 +typedef struct kbts__chained_sequence_context_1 { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 ChainedSequenceRuleSetCount; // kbts_u16 ChainedSequenceRuleSetOffsets[ChainedSequenceRuleSetCount]; -} kbts_chained_sequence_context_1; +} kbts__chained_sequence_context_1; -typedef struct kbts_chained_sequence_rule_set +typedef struct kbts__chained_sequence_rule_set { kbts_u16 Count; // kbts_u16 Offsets[Count]; -} kbts_chained_sequence_rule_set; +} kbts__chained_sequence_rule_set; -typedef struct kbts_chained_sequence_rule +typedef struct kbts__chained_sequence_rule { kbts_u16 BacktrackGlyphCount; // kbts_u16 BacktrackSequence[BacktrackCount]; @@ -13333,10 +15997,10 @@ typedef struct kbts_chained_sequence_rule // kbts_u16 LookaheadGlyphCount; // kbts_u16 LookaheadSequence[LookaheadGlyphCount]; // kbts_u16 SequenceLookupCount; - // kbts_sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; -} kbts_chained_sequence_rule; + // kbts__sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; +} kbts__chained_sequence_rule; -typedef struct kbts_chained_sequence_context_2 +typedef struct kbts__chained_sequence_context_2 { kbts_u16 Format; kbts_u16 CoverageOffset; @@ -13345,9 +16009,9 @@ typedef struct kbts_chained_sequence_context_2 kbts_u16 LookaheadClassDefOffset; kbts_u16 ChainedClassSequenceRuleSetCount; // kbts_u16 ChainedClassSequenceRuleSetOffsets[ChainedClassSequenceRuleSetCount]; -} kbts_chained_sequence_context_2; +} kbts__chained_sequence_context_2; -typedef struct kbts_chained_sequence_context_3 +typedef struct kbts__chained_sequence_context_3 { kbts_u16 Format; kbts_u16 BacktrackGlyphCount; @@ -13357,10 +16021,10 @@ typedef struct kbts_chained_sequence_context_3 // kbts_u16 LookaheadGlyphCount; // kbts_u16 LookaheadCoverageOffsets[LookaheadGlyphCount]; // kbts_u16 SequenceLookupCount; - // kbts_sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; -} kbts_chained_sequence_context_3; + // kbts__sequence_lookup_record SequenceLookupRecords[SequenceLookupCount]; +} kbts__chained_sequence_context_3; -typedef struct kbts_device +typedef struct kbts__device { union { @@ -13378,81 +16042,95 @@ typedef struct kbts_device } U; kbts_u16 DeltaFormat; // kbts_u16 DeltaValue[]; -} kbts_device; +} kbts__device; -typedef struct kbts_feature_variations +typedef struct kbts__feature_variations { kbts_u16 Major; kbts_u16 Minor; kbts_u32 RecordCount; - // kbts_feature_variation_record Records[RecordCount]; -} kbts_feature_variations; + // kbts__feature_variation_record Records[RecordCount]; +} kbts__feature_variations; -typedef struct kbts_feature_variation_record +typedef struct kbts__feature_variation_record { kbts_u32 ConditionSetOffset; kbts_u32 FeatureTableSubstitutionOffset; -} kbts_feature_variation_record; +} kbts__feature_variation_record; -typedef struct kbts_condition_set +typedef struct kbts__condition_set { kbts_u16 Count; // kbts_u32 Offsets[Count]; -} kbts_condition_set; +} kbts__condition_set; -typedef struct kbts_condition_1 +typedef struct kbts__condition_1 { kbts_u16 Format; kbts_u16 AxisIndex; kbts_u16 FilterRangeMinValue; kbts_u16 FilterRangeMaxValue; -} kbts_condition_1; +} kbts__condition_1; -typedef struct kbts_feature_table_substitution +typedef struct kbts__feature_table_substitution { kbts_u16 Major; kbts_u16 Minor; kbts_u16 Count; - // kbts_feature_table_substitution_record Records[Count]; -} kbts_feature_table_substitution; + // kbts__feature_table_substitution_record Records[Count]; +} kbts__feature_table_substitution; -typedef struct kbts_feature_table_substitution_record +typedef struct kbts__feature_table_substitution_record { kbts_u16 FeatureIndex; kbts_u32 AlternateFeatureOffset; -} kbts_feature_table_substitution_record; +} kbts__feature_table_substitution_record; -typedef struct kbts_table_directory +typedef struct kbts__ttc_header +{ + kbts_u32 Magic; // ttcf + kbts_u16 Major; + kbts_u16 Minor; + kbts_u32 FontCount; + // kbts_u32 TableDirectoryOffsets[FontCount]; + + // If Major == 2: + // kbts_u32 DsigTag; + // kbts_u32 DsigLength; + // kbts_u32 DsigOffset; +} kbts__ttc_header; + +typedef struct kbts__table_directory { kbts_u32 Version; kbts_u16 TableCount; kbts_u16 SearchRange; kbts_u16 EntrySelector; kbts_u16 RangeShift; - // kbts_table_record Records[TableCount]; -} kbts_table_directory; + // kbts__table_record Records[TableCount]; +} kbts__table_directory; -typedef struct kbts_table_record +typedef struct kbts__table_record { kbts_u32 Tag; kbts_u32 Checksum; kbts_u32 Offset; kbts_u32 Length; -} kbts_table_record; +} kbts__table_record; -typedef struct kbts_cmap +typedef struct kbts__cmap { kbts_u16 Version; kbts_u16 TableCount; - // kbts_encoding_record Records[TableCount]; -} kbts_cmap; + // kbts__encoding_record Records[TableCount]; +} kbts__cmap; -typedef struct kbts_encoding_record +typedef struct kbts__encoding_record { kbts_u16 PlatformId; kbts_u16 EncodingId; kbts_u32 SubtableOffset; -} kbts_encoding_record; +} kbts__encoding_record; /* Precedence rules for cmap tables: - Tables are mutually exclusive, except for format 14 which completes the others. @@ -13464,33 +16142,33 @@ typedef struct kbts_encoding_record - There can be Unicode and Windows tables with the same formats. In that case, we prefer the Windows tables. */ -typedef struct kbts_cmap_0 +typedef struct kbts__cmap_0 { kbts_u16 Format; kbts_u16 Length; kbts_u16 Language; kbts_u8 GlyphIdArray[256]; -} kbts_cmap_0; +} kbts__cmap_0; -typedef struct kbts_cmap_2 +typedef struct kbts__cmap_2 { kbts_u16 Format; kbts_u16 Length; kbts_u16 Language; kbts_u16 SubHeaderKeys[256]; - // kbts_sub_header SubHeaders[]; + // kbts__sub_header SubHeaders[]; // kbts_u16 GlyphIdArray[]; -} kbts_cmap_2; +} kbts__cmap_2; -typedef struct kbts_sub_header +typedef struct kbts__sub_header { kbts_u16 FirstCode; kbts_u16 EntryCount; kbts_s16 IdDelta; kbts_u16 IdRangeOffset; -} kbts_sub_header; +} kbts__sub_header; -typedef struct kbts_cmap_4 +typedef struct kbts__cmap_4 { kbts_u16 Format; kbts_u16 Length; @@ -13505,9 +16183,9 @@ typedef struct kbts_cmap_4 // kbts_s16 IdDelta[SegmentCount]; // kbts_u16 IdRangeOffsets[SegmentCount]; // kbts_u16 GlyphIdArray[]; -} kbts_cmap_4; +} kbts__cmap_4; -typedef struct kbts_cmap_6 +typedef struct kbts__cmap_6 { kbts_u16 Format; kbts_u16 Length; @@ -13515,65 +16193,65 @@ typedef struct kbts_cmap_6 kbts_u16 FirstCode; kbts_u16 EntryCount; // kbts_u16 GlyphIdArray[EntryCount]; -} kbts_cmap_6; +} kbts__cmap_6; -typedef struct kbts_cmap_12_13 +typedef struct kbts__cmap_12_13 { kbts_u16 Format; kbts_u16 Reserved; kbts_u32 Length; kbts_u32 Language; kbts_u32 GroupCount; - // kbts_sequential_map_group Groups[GroupCount]; -} kbts_cmap_12_13; + // kbts__sequential_map_group Groups[GroupCount]; +} kbts__cmap_12_13; -typedef struct kbts_sequential_map_group +typedef struct kbts__sequential_map_group { kbts_u32 StartCharacterCode; kbts_u32 EndCharacterCode; kbts_u32 StartGlyphId; -} kbts_sequential_map_group; +} kbts__sequential_map_group; -typedef struct kbts_cmap_14 +typedef struct kbts__cmap_14 { kbts_u16 Format; kbts_u32 Length; kbts_u32 SelectorCount; - // kbts_variation_selector Selectors[SelectorCount]; -} kbts_cmap_14; + // kbts__variation_selector Selectors[SelectorCount]; +} kbts__cmap_14; -typedef struct kbts_variation_selector +typedef struct kbts__variation_selector { kbts_u8 Selector[3]; kbts_u32 DefaultUvsOffset; kbts_u32 NonDefaultUvsOffset; -} kbts_variation_selector; +} kbts__variation_selector; -typedef struct kbts_default_uvs +typedef struct kbts__default_uvs { kbts_u32 RangeCount; - // kbts_unicode_range Ranges[RangeCount]; -} kbts_default_uvs; + // kbts__unicode_range Ranges[RangeCount]; +} kbts__default_uvs; -typedef struct kbts_unicode_range +typedef struct kbts__unicode_range { kbts_u8 Start[3]; kbts_u8 Count; -} kbts_unicode_range; +} kbts__unicode_range; -typedef struct kbts_non_default_uvs +typedef struct kbts__non_default_uvs { kbts_u32 MappingCount; - // kbts_uvs_mapping Mappings[MappingCount]; -} kbts_non_default_uvs; + // kbts__uvs_mapping Mappings[MappingCount]; +} kbts__non_default_uvs; -typedef struct kbts_uvs_mapping +typedef struct kbts__uvs_mapping { kbts_u8 UnicodeValue[3]; kbts_u16 GlyphId; -} kbts_uvs_mapping; +} kbts__uvs_mapping; -typedef struct kbts_gsub_gpos +typedef struct kbts__gsub_gpos { kbts_u16 Major; kbts_u16 Minor; @@ -13581,9 +16259,9 @@ typedef struct kbts_gsub_gpos kbts_u16 FeatureListOffset; kbts_u16 LookupListOffset; kbts_u32 FeatureVariationsOffset; // Only present in v1.1 -} kbts_gsub_gpos; +} kbts__gsub_gpos; -typedef struct kbts_single_substitution +typedef struct kbts__single_substitution { kbts_u16 Format; kbts_u16 CoverageOffset; @@ -13594,65 +16272,65 @@ typedef struct kbts_single_substitution } DeltaOrCount; // If Format == 2: // kbts_u16 SubstituteGlyphIds[GlyphCount]; -} kbts_single_substitution; +} kbts__single_substitution; -typedef struct kbts_multiple_substitution +typedef struct kbts__multiple_substitution { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 SequenceCount; // kbts_u16 SequenceOffsets[SequenceCount]; -} kbts_multiple_substitution; +} kbts__multiple_substitution; -typedef struct kbts_sequence +typedef struct kbts__sequence { kbts_u16 GlyphCount; // kbts_u16 SubstituteGlyphIds[GlyphCount]; -} kbts_sequence; +} kbts__sequence; -typedef struct kbts_alternate_substitution +typedef struct kbts__alternate_substitution { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 AlternateSetCount; // kbts_u16 AlternateSetOffsets[AlternateSetCount]; -} kbts_alternate_substitution; +} kbts__alternate_substitution; -typedef struct kbts_alternate_set +typedef struct kbts__alternate_set { kbts_u16 GlyphCount; // kbts_u16 AlternateGlyphIds[GlyphCount]; -} kbts_alternate_set; +} kbts__alternate_set; -typedef struct kbts_ligature_substitution +typedef struct kbts__ligature_substitution { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 LigatureSetCount; // kbts_u16 LigatureSetOffsets[LigatureSetCount]; -} kbts_ligature_substitution; +} kbts__ligature_substitution; -typedef struct kbts_ligature_set +typedef struct kbts__ligature_set { kbts_u16 Count; // kbts_u16 Offsets[Count]; -} kbts_ligature_set; +} kbts__ligature_set; -typedef struct kbts_ligature +typedef struct kbts__ligature { kbts_u16 Glyph; kbts_u16 ComponentCount; // kbts_u16 ComponentGlyphIds[ComponentCount - 1]; -} kbts_ligature; +} kbts__ligature; -typedef struct kbts_extension +typedef struct kbts__extension { kbts_u16 Format; kbts_u16 LookupType; kbts_u32 Offset; -} kbts_extension; +} kbts__extension; -typedef struct kbts_reverse_chain_substitution +typedef struct kbts__reverse_chain_substitution { kbts_u16 Format; kbts_u16 CoverageOffset; @@ -13662,16 +16340,16 @@ typedef struct kbts_reverse_chain_substitution // kbts_u16 LookaheadCoverageOffsets[LookaheadGlyphCount]; // kbts_u16 GlyphCount; // kbts_u16 SubstituteGlyphIds[GlyphCount]; -} kbts_reverse_chain_substitution; +} kbts__reverse_chain_substitution; -typedef struct kbts_mark_glyph_sets +typedef struct kbts__mark_glyph_sets { kbts_u16 Format; kbts_u16 MarkGlyphSetCount; // kbts_u32 CoverageOffsets[MarkGlyphSetCount]; -} kbts_mark_glyph_sets; +} kbts__mark_glyph_sets; -typedef struct kbts_gdef +typedef struct kbts__gdef { kbts_u16 Major; kbts_u16 Minor; @@ -13681,9 +16359,9 @@ typedef struct kbts_gdef kbts_u16 MarkAttachmentClassDefinitionOffset; // May be 0 kbts_u16 MarkGlyphSetsDefinitionOffset; // v1.2 and up; may be 0 kbts_u32 ItemVariationStoreOffset; // v1.3 and up; may be 0 -} kbts_gdef; +} kbts__gdef; -typedef struct kbts_anchor +typedef struct kbts__anchor { kbts_u16 Format; kbts_s16 X; @@ -13694,51 +16372,51 @@ typedef struct kbts_anchor kbts_u16 XDeviceOffset; // If Format == 3 } U; kbts_u16 YDeviceOffset; // If Format == 3 -} kbts_anchor; +} kbts__anchor; -typedef struct kbts_mark_record +typedef struct kbts__mark_record { kbts_u16 Class; kbts_u16 AnchorOffset; -} kbts_mark_record; +} kbts__mark_record; -typedef struct kbts_mark_array +typedef struct kbts__mark_array { kbts_u16 Count; - // kbts_mark_record Records[Count]; -} kbts_mark_array; + // kbts__mark_record Records[Count]; +} kbts__mark_array; -typedef struct kbts_single_adjustment_1 +typedef struct kbts__single_adjustment_1 { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 ValueFormat; // ValueRecord; -} kbts_single_adjustment_1; +} kbts__single_adjustment_1; -typedef struct kbts_single_adjustment_2 +typedef struct kbts__single_adjustment_2 { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 ValueFormat; kbts_u16 RecordCount; // ValueRecord Records[RecordCount]; -} kbts_single_adjustment_2; +} kbts__single_adjustment_2; -typedef struct kbts_pair_value_record +typedef struct kbts__pair_value_record { kbts_u16 SecondGlyph; // ValueRecord Record1; // ValueRecord Record2; -} kbts_pair_value_record; +} kbts__pair_value_record; -typedef struct kbts_pair_set +typedef struct kbts__pair_set { kbts_u16 Count; - // kbts_pair_value_record PairValueRecords[Count]; -} kbts_pair_set; + // kbts__pair_value_record PairValueRecords[Count]; +} kbts__pair_set; -typedef struct kbts_pair_adjustment_1 +typedef struct kbts__pair_adjustment_1 { kbts_u16 Format; kbts_u16 CoverageOffset; @@ -13746,9 +16424,9 @@ typedef struct kbts_pair_adjustment_1 kbts_u16 ValueFormat2; // May be 0 kbts_u16 SetCount; // kbts_u16 SetOffsets[PairSetCount]; -} kbts_pair_adjustment_1; +} kbts__pair_adjustment_1; -typedef struct kbts_pair_adjustment_2 +typedef struct kbts__pair_adjustment_2 { kbts_u16 Format; kbts_u16 CoverageOffset; @@ -13759,29 +16437,29 @@ typedef struct kbts_pair_adjustment_2 kbts_u16 Class1Count; kbts_u16 Class2Count; // ValueRecord ValueRecords[Class1Count][Class2Count][2]; -} kbts_pair_adjustment_2; +} kbts__pair_adjustment_2; -typedef struct kbts_entry_exit +typedef struct kbts__entry_exit { kbts_u16 EntryAnchorOffset; kbts_u16 ExitAnchorOffset; -} kbts_entry_exit; +} kbts__entry_exit; -typedef struct kbts_cursive_attachment +typedef struct kbts__cursive_attachment { kbts_u16 Format; kbts_u16 CoverageOffset; kbts_u16 EntryExitCount; - // kbts_entry_exit Records[EntryExitCount]; -} kbts_cursive_attachment; + // kbts__entry_exit Records[EntryExitCount]; +} kbts__cursive_attachment; -typedef struct kbts_base_array +typedef struct kbts__base_array { kbts_u16 BaseCount; // kbts_u16 BaseAnchorOffsets[BaseCount][MarkClassCount]; // May be NULL -} kbts_base_array; +} kbts__base_array; -typedef struct kbts_mark_to_base_attachment +typedef struct kbts__mark_to_base_attachment { kbts_u16 Format; kbts_u16 MarkCoverageOffset; @@ -13789,21 +16467,21 @@ typedef struct kbts_mark_to_base_attachment kbts_u16 MarkClassCount; kbts_u16 MarkArrayOffset; kbts_u16 BaseArrayOffset; -} kbts_mark_to_base_attachment; +} kbts__mark_to_base_attachment; -typedef struct kbts_ligature_attach +typedef struct kbts__ligature_attach { kbts_u16 Count; // kbts_u16 LigatureAnchorOffsets[Count][MarkClassCount]; // May be NULL -} kbts_ligature_attach; +} kbts__ligature_attach; -typedef struct kbts_ligature_array +typedef struct kbts__ligature_array { kbts_u16 Count; // kbts_u16 LigatureAttachOffsets[Count]; -} kbts_ligature_array; +} kbts__ligature_array; -typedef struct kbts_mark_to_ligature_attachment +typedef struct kbts__mark_to_ligature_attachment { kbts_u16 Format; kbts_u16 MarkCoverageOffset; @@ -13811,15 +16489,15 @@ typedef struct kbts_mark_to_ligature_attachment kbts_u16 MarkClassCount; kbts_u16 MarkArrayOffset; kbts_u16 LigatureArrayOffset; -} kbts_mark_to_ligature_attachment; +} kbts__mark_to_ligature_attachment; -typedef struct kbts_long_mtx +typedef struct kbts__long_mtx { kbts_u16 Advance; kbts_s16 PreviousSideBearing; -} kbts_long_mtx; +} kbts__long_mtx; -struct kbts_head +struct kbts__head { kbts_u16 Major; kbts_u16 Minor; @@ -13841,7 +16519,7 @@ struct kbts_head kbts_s16 GlyphDataFormat; // Only 0 is defined. }; -typedef struct kbts_hea +typedef struct kbts__hea { kbts_u16 Major; kbts_u16 Minor; @@ -13865,9 +16543,84 @@ typedef struct kbts_hea kbts_s16 MetricDataFormat; kbts_u16 MetricCount; -} kbts_hea; +} kbts__hea; -typedef struct kbts_maxp +typedef struct kbts__os2 +{ + kbts_u16 Version; + kbts_s16 AverageCharacterWidth; + kbts_u16 WeightClass; + kbts_u16 WidthClass; + kbts_u16 Type; + kbts_s16 SubscriptXSize; + kbts_s16 SubscriptYSize; + kbts_s16 SubscriptXOffset; + kbts_s16 SubscriptYOffset; + kbts_s16 SuperscriptXSize; + kbts_s16 SuperscriptYSize; + kbts_s16 SuperscriptXOffset; + kbts_s16 SuperscriptYOffset; + kbts_s16 StrikeoutSize; + kbts_s16 StrikeoutPosition; + kbts_s16 FamilyClass; + kbts_u8 Panose[10]; + kbts_u32 UnicodeRange[4]; + kbts_u32 VendorId; + kbts_u16 Selection; + kbts_u16 FirstCharacterIndex; + kbts_u16 LastCharacterIndex; + + // Some version 0 fonts support this, others do not. + kbts_s16 TypoAscender; + kbts_s16 TypoDescender; + kbts_s16 TypoLineGap; + kbts_u16 WinAscent; + kbts_u16 WinDescent; + + // If Version >= 1: + kbts_u32 CodePageRange[2]; + + // If Version >= 2: + kbts_s16 Height; + kbts_s16 CapHeight; + kbts_u16 DefaultChar; + kbts_u16 BreakChar; + kbts_u16 MaxContext; + + // If Version >= 5: + kbts_u16 LowerOpticalPointSize; + kbts_u16 UpperOpticalPointSize; +} kbts__os2; + +typedef struct kbts__lang_tag_record +{ + kbts_u16 Length; + kbts_u16 LangTagOffset; +} kbts__lang_tag_record; + +typedef struct kbts__name_record +{ + kbts_u16 PlatformId; + kbts_u16 EncodingId; + kbts_u16 LanguageId; + kbts_u16 NameId; + kbts_u16 Length; + kbts_u16 StringOffset; +} kbts__name_record; + +typedef struct kbts__name +{ + kbts_u16 Version; + kbts_u16 Count; + kbts_u16 StringStorageOffset; + // kbts__name_record Records[Count]; + + // If Version == 1: + // kbts_u16 LangTagCount; + // kbts__lang_tag_record LangTags[LangTagCount]; +} kbts__name; + +typedef struct kbts__maxp { kbts_u16 Major; kbts_u16 Minor; @@ -13887,7 +16640,7 @@ typedef struct kbts_maxp kbts_u16 MaximumInstructionSize; kbts_u16 MaximumTopLevelComponentCount; kbts_u16 MaximumComponentDepth; -} kbts_maxp; +} kbts__maxp; # pragma pack(pop) @@ -13895,70 +16648,70 @@ typedef struct kbts_maxp // Unpack functions for TTF structs. // -typedef struct kbts_script_pointer +typedef struct kbts__script_pointer { kbts_u32 Tag; - kbts_ot_script *Script; -} kbts_script_pointer; + kbts__ot_script *Script; +} kbts__script_pointer; -typedef struct kbts_langsys_pointer +typedef struct kbts__langsys_pointer { kbts_u32 Tag; - kbts_langsys *Langsys; -} kbts_langsys_pointer; + kbts__langsys *Langsys; +} kbts__langsys_pointer; -typedef struct kbts_feature_pointer +typedef struct kbts__feature_pointer { kbts_u32 Tag; - kbts_feature *Feature; -} kbts_feature_pointer; + kbts__feature *Feature; +} kbts__feature_pointer; -typedef struct kbts_feature_variation_pointer +typedef struct kbts__feature_variation_pointer { - kbts_condition_set *ConditionSet; - kbts_feature_table_substitution *FeatureTableSubstitution; -} kbts_feature_variation_pointer; + kbts__condition_set *ConditionSet; + kbts__feature_table_substitution *FeatureTableSubstitution; +} kbts__feature_variation_pointer; -typedef struct kbts_feature_substitution_pointer +typedef struct kbts__feature_substitution_pointer { kbts_u16 SubstitutedFeatureIndex; - kbts_feature *AlternateFeature; -} kbts_feature_substitution_pointer; + kbts__feature *AlternateFeature; +} kbts__feature_substitution_pointer; -typedef struct kbts_cmap_subtable_pointer +typedef struct kbts__cmap_subtable_pointer { kbts_u16 PlatformId; kbts_u16 EncodingId; kbts_u16 *Subtable; -} kbts_cmap_subtable_pointer; +} kbts__cmap_subtable_pointer; -typedef struct kbts_unpacked_chained_sequence_rule +typedef struct kbts__unpacked_chained_sequence_rule { kbts_u16 *Input; kbts_u16 *Backtrack; kbts_u16 *Lookahead; - kbts_sequence_lookup_record *Records; + kbts__sequence_lookup_record *Records; kbts_u16 BacktrackCount; kbts_u16 InputCount; kbts_u16 LookaheadCount; kbts_u16 RecordCount; -} kbts_unpacked_chained_sequence_rule; +} kbts__unpacked_chained_sequence_rule; -typedef struct kbts_unpacked_chained_sequence_context_3 +typedef struct kbts__unpacked_chained_sequence_context_3 { kbts_u16 *BacktrackCoverageOffsets; kbts_u16 *InputCoverageOffsets; kbts_u16 *LookaheadCoverageOffsets; - kbts_sequence_lookup_record *Records; + kbts__sequence_lookup_record *Records; kbts_u16 BacktrackCount; kbts_u16 InputCount; kbts_u16 LookaheadCount; kbts_u16 RecordCount; -} kbts_unpacked_chained_sequence_context_3; +} kbts__unpacked_chained_sequence_context_3; -typedef struct kbts_unpacked_reverse_chain_substitution +typedef struct kbts__unpacked_reverse_chain_substitution { kbts_u16 *BacktrackCoverageOffsets; kbts_u16 *LookaheadCoverageOffsets; @@ -13967,9 +16720,9 @@ typedef struct kbts_unpacked_reverse_chain_substitution kbts_u16 BacktrackCount; kbts_u16 LookaheadCount; kbts_u16 GlyphCount; -} kbts_unpacked_reverse_chain_substitution; +} kbts__unpacked_reverse_chain_substitution; -typedef struct kbts_unpacked_value_record +typedef struct kbts__unpacked_value_record { kbts_u16 Size; @@ -13977,68 +16730,68 @@ typedef struct kbts_unpacked_value_record kbts_s16 PlacementY; kbts_s16 AdvanceX; kbts_s16 AdvanceY; - kbts_device *PlacementXDevice; - kbts_device *PlacementYDevice; - kbts_device *AdvanceXDevice; - kbts_device *AdvanceYDevice; -} kbts_unpacked_value_record; + kbts__device *PlacementXDevice; + kbts__device *PlacementYDevice; + kbts__device *AdvanceXDevice; + kbts__device *AdvanceYDevice; +} kbts__unpacked_value_record; -static kbts_unpacked_value_record kbts_UnpackValueRecord(void *Parent, kbts_u16 Format, kbts_u16 *Record) +static kbts__unpacked_value_record kbts__UnpackValueRecord(void *Parent, kbts_u16 Format, kbts_u16 *Record) { - kbts_unpacked_value_record Result = KBTS_ZERO; + kbts__unpacked_value_record Result = KBTS__ZERO; kbts_u16 *At = Record; - if(Format & KBTS_VALUE_FORMAT_X_PLACEMENT) + if(Format & KBTS__VALUE_FORMAT_X_PLACEMENT) { Result.PlacementX = (kbts_s16)*At++; } - if(Format & KBTS_VALUE_FORMAT_Y_PLACEMENT) + if(Format & KBTS__VALUE_FORMAT_Y_PLACEMENT) { Result.PlacementY = (kbts_s16)*At++; } - if(Format & KBTS_VALUE_FORMAT_X_ADVANCE) + if(Format & KBTS__VALUE_FORMAT_X_ADVANCE) { Result.AdvanceX = (kbts_s16)*At++; } - if(Format & KBTS_VALUE_FORMAT_Y_ADVANCE) + if(Format & KBTS__VALUE_FORMAT_Y_ADVANCE) { Result.AdvanceY = (kbts_s16)*At++; } - if(Format & KBTS_VALUE_FORMAT_X_PLACEMENT_DEVICE) + if(Format & KBTS__VALUE_FORMAT_X_PLACEMENT_DEVICE) { kbts_u16 Offset = *At++; if(Offset) { - Result.PlacementXDevice = KBTS_POINTER_OFFSET(kbts_device, Parent, Offset); + Result.PlacementXDevice = KBTS__POINTER_OFFSET(kbts__device, Parent, Offset); } } - if(Format & KBTS_VALUE_FORMAT_Y_PLACEMENT_DEVICE) + if(Format & KBTS__VALUE_FORMAT_Y_PLACEMENT_DEVICE) { kbts_u16 Offset = *At++; if(Offset) { - Result.PlacementYDevice = KBTS_POINTER_OFFSET(kbts_device, Parent, Offset); + Result.PlacementYDevice = KBTS__POINTER_OFFSET(kbts__device, Parent, Offset); } } - if(Format & KBTS_VALUE_FORMAT_X_ADVANCE_DEVICE) + if(Format & KBTS__VALUE_FORMAT_X_ADVANCE_DEVICE) { kbts_u16 Offset = *At++; if(Offset) { - Result.AdvanceXDevice = KBTS_POINTER_OFFSET(kbts_device, Parent, Offset); + Result.AdvanceXDevice = KBTS__POINTER_OFFSET(kbts__device, Parent, Offset); } } - if(Format & KBTS_VALUE_FORMAT_Y_ADVANCE_DEVICE) + if(Format & KBTS__VALUE_FORMAT_Y_ADVANCE_DEVICE) { kbts_u16 Offset = *At++; if(Offset) { - Result.AdvanceYDevice = KBTS_POINTER_OFFSET(kbts_device, Parent, Offset); + Result.AdvanceYDevice = KBTS__POINTER_OFFSET(kbts__device, Parent, Offset); } } @@ -14047,131 +16800,131 @@ static kbts_unpacked_value_record kbts_UnpackValueRecord(void *Parent, kbts_u16 return Result; } -static kbts_unpacked_reverse_chain_substitution kbts_UnpackReverseChainSubstitution(kbts_reverse_chain_substitution *Subst, int ByteSwap) +static kbts__unpacked_reverse_chain_substitution kbts__UnpackReverseChainSubstitution(kbts__reverse_chain_substitution *Subst, int ByteSwap) { - kbts_unpacked_reverse_chain_substitution Result; + kbts__unpacked_reverse_chain_substitution Result; - Result.BacktrackCoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); + Result.BacktrackCoverageOffsets = KBTS__POINTER_AFTER(kbts_u16, Subst); Result.BacktrackCount = Subst->BacktrackGlyphCount; if(ByteSwap) { - Result.BacktrackCount = kbts_ByteSwap16(Result.BacktrackCount); + Result.BacktrackCount = kbts__ByteSwap16(Result.BacktrackCount); } Result.LookaheadCoverageOffsets = Result.BacktrackCoverageOffsets + Result.BacktrackCount + 1; Result.LookaheadCount = Result.BacktrackCoverageOffsets[Result.BacktrackCount]; if(ByteSwap) { - Result.LookaheadCount = kbts_ByteSwap16(Result.LookaheadCount); + Result.LookaheadCount = kbts__ByteSwap16(Result.LookaheadCount); } Result.SubstituteGlyphIds = Result.LookaheadCoverageOffsets + Result.LookaheadCount + 1; Result.GlyphCount = Result.LookaheadCoverageOffsets[Result.LookaheadCount]; if(ByteSwap) { - Result.GlyphCount = kbts_ByteSwap16(Result.GlyphCount); + Result.GlyphCount = kbts__ByteSwap16(Result.GlyphCount); } return Result; } -static kbts_unpacked_chained_sequence_rule kbts_UnpackChainedSequenceRule(kbts_chained_sequence_rule *Rule, int ByteSwap) +static kbts__unpacked_chained_sequence_rule kbts__UnpackChainedSequenceRule(kbts__chained_sequence_rule *Rule, int ByteSwap) { - kbts_unpacked_chained_sequence_rule Result; + kbts__unpacked_chained_sequence_rule Result; - Result.Backtrack = KBTS_POINTER_AFTER(kbts_u16, Rule); + Result.Backtrack = KBTS__POINTER_AFTER(kbts_u16, Rule); Result.BacktrackCount = Rule->BacktrackGlyphCount; if(ByteSwap) { - Result.BacktrackCount = kbts_ByteSwap16(Result.BacktrackCount); + Result.BacktrackCount = kbts__ByteSwap16(Result.BacktrackCount); } Result.Input = Result.Backtrack + Result.BacktrackCount + 1; Result.InputCount = Result.Backtrack[Result.BacktrackCount]; if(ByteSwap) { - Result.InputCount = kbts_ByteSwap16(Result.InputCount); + Result.InputCount = kbts__ByteSwap16(Result.InputCount); } Result.Lookahead = Result.Input + Result.InputCount; Result.LookaheadCount = Result.Input[Result.InputCount - 1]; if(ByteSwap) { - Result.LookaheadCount = kbts_ByteSwap16(Result.LookaheadCount); + Result.LookaheadCount = kbts__ByteSwap16(Result.LookaheadCount); } - Result.Records = (kbts_sequence_lookup_record *)(Result.Lookahead + Result.LookaheadCount + 1); + Result.Records = (kbts__sequence_lookup_record *)(Result.Lookahead + Result.LookaheadCount + 1); Result.RecordCount = Result.Lookahead[Result.LookaheadCount]; if(ByteSwap) { - Result.RecordCount = kbts_ByteSwap16(Result.RecordCount); + Result.RecordCount = kbts__ByteSwap16(Result.RecordCount); } return Result; } -static kbts_unpacked_chained_sequence_context_3 kbts_UnpackChainedSequenceContext3(kbts_chained_sequence_context_3 *Subst, int ByteSwap) +static kbts__unpacked_chained_sequence_context_3 kbts__UnpackChainedSequenceContext3(kbts__chained_sequence_context_3 *Subst, int ByteSwap) { - kbts_unpacked_chained_sequence_context_3 Result; + kbts__unpacked_chained_sequence_context_3 Result; - Result.BacktrackCoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); + Result.BacktrackCoverageOffsets = KBTS__POINTER_AFTER(kbts_u16, Subst); Result.BacktrackCount = Subst->BacktrackGlyphCount; if(ByteSwap) { - Result.BacktrackCount = kbts_ByteSwap16(Result.BacktrackCount); + Result.BacktrackCount = kbts__ByteSwap16(Result.BacktrackCount); } Result.InputCoverageOffsets = Result.BacktrackCoverageOffsets + Result.BacktrackCount + 1; Result.InputCount = Result.BacktrackCoverageOffsets[Result.BacktrackCount]; if(ByteSwap) { - Result.InputCount = kbts_ByteSwap16(Result.InputCount); + Result.InputCount = kbts__ByteSwap16(Result.InputCount); } Result.LookaheadCoverageOffsets = Result.InputCoverageOffsets + Result.InputCount + 1; Result.LookaheadCount = Result.InputCoverageOffsets[Result.InputCount]; if(ByteSwap) { - Result.LookaheadCount = kbts_ByteSwap16(Result.LookaheadCount); + Result.LookaheadCount = kbts__ByteSwap16(Result.LookaheadCount); } - Result.Records = (kbts_sequence_lookup_record *)(Result.LookaheadCoverageOffsets + Result.LookaheadCount + 1); + Result.Records = (kbts__sequence_lookup_record *)(Result.LookaheadCoverageOffsets + Result.LookaheadCount + 1); Result.RecordCount = Result.LookaheadCoverageOffsets[Result.LookaheadCount]; if(ByteSwap) { - Result.RecordCount = kbts_ByteSwap16(Result.RecordCount); + Result.RecordCount = kbts__ByteSwap16(Result.RecordCount); } return Result; } -typedef struct kbts_unpacked_lookup +typedef struct kbts__unpacked_lookup { kbts_u16 *SubtableOffsets; - kbts_coverage *MarkFilteringSet; + kbts__coverage *MarkFilteringSet; kbts_u16 Type; kbts_u16 Flags; kbts_u16 SubtableCount; -} kbts_unpacked_lookup; +} kbts__unpacked_lookup; -static kbts_unpacked_lookup kbts_UnpackLookup(kbts_gdef *Gdef, kbts_lookup *Lookup) +static kbts__unpacked_lookup kbts__UnpackLookup(kbts__gdef *Gdef, kbts__lookup *Lookup) { - kbts_unpacked_lookup Result = KBTS_ZERO; + kbts__unpacked_lookup Result = KBTS__ZERO; Result.Type = Lookup->Type; Result.Flags = Lookup->Flag; Result.SubtableCount = Lookup->SubtableCount; - Result.SubtableOffsets = KBTS_POINTER_AFTER(kbts_u16, Lookup); - if(Result.Flags & KBTS_LOOKUP_FLAG_USE_MARK_FILTERING_SET) + Result.SubtableOffsets = KBTS__POINTER_AFTER(kbts_u16, Lookup); + if(Result.Flags & KBTS__LOOKUP_FLAG_USE_MARK_FILTERING_SET) { kbts_u16 MarkFilteringSetIndex = Result.SubtableOffsets[Result.SubtableCount]; if(Gdef && Gdef->MarkGlyphSetsDefinitionOffset) { - kbts_mark_glyph_sets *MarkGlyphSets = KBTS_POINTER_OFFSET(kbts_mark_glyph_sets, Gdef, Gdef->MarkGlyphSetsDefinitionOffset); + kbts__mark_glyph_sets *MarkGlyphSets = KBTS__POINTER_OFFSET(kbts__mark_glyph_sets, Gdef, Gdef->MarkGlyphSetsDefinitionOffset); if(MarkGlyphSets->MarkGlyphSetCount > MarkFilteringSetIndex) { - kbts_u32 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u32, MarkGlyphSets); - Result.MarkFilteringSet = KBTS_POINTER_OFFSET(kbts_coverage, MarkGlyphSets, CoverageOffsets[MarkFilteringSetIndex]); + kbts_u32 *CoverageOffsets = KBTS__POINTER_AFTER(kbts_u32, MarkGlyphSets); + Result.MarkFilteringSet = KBTS__POINTER_OFFSET(kbts__coverage, MarkGlyphSets, CoverageOffsets[MarkFilteringSetIndex]); } } } @@ -14179,248 +16932,248 @@ static kbts_unpacked_lookup kbts_UnpackLookup(kbts_gdef *Gdef, kbts_lookup *Look return Result; } -static kbts_script_pointer kbts_GetScript(kbts_script_list *List, kbts_un Index) +static kbts__script_pointer kbts__GetScript(kbts__script_list *List, kbts_un Index) { - kbts_script_record *Records = (kbts_script_record *)(List + 1); - kbts_script_record *Record = &Records[Index]; + kbts__script_record *Records = (kbts__script_record *)(List + 1); + kbts__script_record *Record = &Records[Index]; - kbts_script_pointer Result; + kbts__script_pointer Result; Result.Tag = Record->Tag; - Result.Script = (kbts_ot_script *)((char *)List + Record->Offset); + Result.Script = (kbts__ot_script *)((char *)List + Record->Offset); return Result; } -static kbts_langsys *kbts_GetDefaultLangsys(kbts_ot_script *Script) +static kbts__langsys *kbts__GetDefaultLangsys(kbts__ot_script *Script) { - kbts_langsys *Result = Script->DefaultLangsysOffset ? KBTS_POINTER_OFFSET(kbts_langsys, Script, Script->DefaultLangsysOffset) : 0; + kbts__langsys *Result = Script->DefaultLangsysOffset ? KBTS__POINTER_OFFSET(kbts__langsys, Script, Script->DefaultLangsysOffset) : 0; return Result; } -static kbts_langsys_pointer kbts_GetLangsys(kbts_ot_script *Script, kbts_un Index) +static kbts__langsys_pointer kbts__GetLangsys(kbts__ot_script *Script, kbts_un Index) { - kbts_langsys_record *Records = (kbts_langsys_record *)(Script + 1); - kbts_langsys_record *Record = &Records[Index]; + kbts__langsys_record *Records = (kbts__langsys_record *)(Script + 1); + kbts__langsys_record *Record = &Records[Index]; - kbts_langsys_pointer Result; + kbts__langsys_pointer Result; Result.Tag = Record->Tag; - Result.Langsys = KBTS_POINTER_OFFSET(kbts_langsys, Script, Record->Offset); + Result.Langsys = KBTS__POINTER_OFFSET(kbts__langsys, Script, Record->Offset); return Result; } -static kbts_feature_pointer kbts_GetFeature(kbts_feature_list *List, kbts_un Index) +static kbts__feature_pointer kbts__GetFeature(kbts__feature_list *List, kbts_un Index) { - kbts_feature_record *Records = (kbts_feature_record *)(List + 1); - kbts_feature_record *Record = &Records[Index]; + kbts__feature_record *Records = (kbts__feature_record *)(List + 1); + kbts__feature_record *Record = &Records[Index]; - kbts_feature_pointer Result; + kbts__feature_pointer Result; Result.Tag = Record->Tag; - Result.Feature = KBTS_POINTER_OFFSET(kbts_feature, List, Record->Offset); + Result.Feature = KBTS__POINTER_OFFSET(kbts__feature, List, Record->Offset); return Result; } -static kbts_lookup_list *kbts_GetLookupList(kbts_gsub_gpos *GsubGpos) +static kbts_lookup_list *kbts__GetLookupList(kbts__gsub_gpos *GsubGpos) { kbts_lookup_list *Result = 0; if(GsubGpos) { - Result = KBTS_POINTER_OFFSET(kbts_lookup_list, GsubGpos, GsubGpos->LookupListOffset); + Result = KBTS__POINTER_OFFSET(kbts_lookup_list, GsubGpos, GsubGpos->LookupListOffset); } return Result; } -static kbts_lookup *kbts_GetLookup(kbts_lookup_list *List, kbts_un Index) +static kbts__lookup *kbts__GetLookup(kbts_lookup_list *List, kbts_un Index) { KBTS_ASSERT(Index < List->Count); kbts_u16 *Offsets = (kbts_u16 *)(List + 1); - kbts_lookup *Result = KBTS_POINTER_OFFSET(kbts_lookup, List, Offsets[Index]); + kbts__lookup *Result = KBTS__POINTER_OFFSET(kbts__lookup, List, Offsets[Index]); return Result; } -static kbts_sequence_rule_set *kbts_GetSequenceRuleSet(kbts_sequence_context_1 *Context, kbts_un Index) +static kbts__sequence_rule_set *kbts__GetSequenceRuleSet(kbts__sequence_context_1 *Context, kbts_un Index) { KBTS_ASSERT(Index < Context->SeqRuleSetCount); kbts_u16 *Offsets = (kbts_u16 *)(Context + 1); kbts_u16 Offset = Offsets[Index]; - kbts_sequence_rule_set *Result = Offset ? KBTS_POINTER_OFFSET(kbts_sequence_rule_set, Context, Offsets[Index]) : 0; + kbts__sequence_rule_set *Result = Offset ? KBTS__POINTER_OFFSET(kbts__sequence_rule_set, Context, Offsets[Index]) : 0; return Result; } -static kbts_sequence_rule *kbts_GetSequenceRule(kbts_sequence_rule_set *Set, kbts_un Index) +static kbts__sequence_rule *kbts__GetSequenceRule(kbts__sequence_rule_set *Set, kbts_un Index) { KBTS_ASSERT(Index < Set->Count); kbts_u16 *Offsets = (kbts_u16 *)(Set + 1); - kbts_sequence_rule *Result = KBTS_POINTER_OFFSET(kbts_sequence_rule, Set, Offsets[Index]); + kbts__sequence_rule *Result = KBTS__POINTER_OFFSET(kbts__sequence_rule, Set, Offsets[Index]); return Result; } // A single class definition table can be used by multiple lookups, so it is possible // (and valid) to get an out-of-bounds glyph class. -static kbts_class_sequence_rule_set *kbts_GetClassSequenceRuleSet(kbts_sequence_context_2 *Context, kbts_un Index) +static kbts__class_sequence_rule_set *kbts__GetClassSequenceRuleSet(kbts__sequence_context_2 *Context, kbts_un Index) { - kbts_class_sequence_rule_set *Result = 0; + kbts__class_sequence_rule_set *Result = 0; if(Index < Context->ClassSequenceRuleSetCount) { kbts_u16 *Offsets = (kbts_u16 *)(Context + 1); - Result = Offsets[Index] ? KBTS_POINTER_OFFSET(kbts_class_sequence_rule_set, Context, Offsets[Index]) : 0; + Result = Offsets[Index] ? KBTS__POINTER_OFFSET(kbts__class_sequence_rule_set, Context, Offsets[Index]) : 0; } return Result; } -static kbts_class_sequence_rule *kbts_GetClassSequenceRule(kbts_class_sequence_rule_set *Set, kbts_un Index) +static kbts__class_sequence_rule *kbts__GetClassSequenceRule(kbts__class_sequence_rule_set *Set, kbts_un Index) { KBTS_ASSERT(Index < Set->Count); kbts_u16 *Offsets = (kbts_u16 *)(Set + 1); - kbts_class_sequence_rule *Result = KBTS_POINTER_OFFSET(kbts_class_sequence_rule, Set, Offsets[Index]); + kbts__class_sequence_rule *Result = KBTS__POINTER_OFFSET(kbts__class_sequence_rule, Set, Offsets[Index]); return Result; } -static kbts_chained_sequence_rule_set *kbts_GetChainedSequenceRuleSet(kbts_chained_sequence_context_1 *Context, kbts_un Index) +static kbts__chained_sequence_rule_set *kbts__GetChainedSequenceRuleSet(kbts__chained_sequence_context_1 *Context, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Context + 1); - kbts_chained_sequence_rule_set *Result = Offsets[Index] ? KBTS_POINTER_OFFSET(kbts_chained_sequence_rule_set, Context, Offsets[Index]) : 0; + kbts__chained_sequence_rule_set *Result = Offsets[Index] ? KBTS__POINTER_OFFSET(kbts__chained_sequence_rule_set, Context, Offsets[Index]) : 0; return Result; } -static kbts_chained_sequence_rule *kbts_GetChainedSequenceRule(kbts_chained_sequence_rule_set *Set, kbts_un Index) +static kbts__chained_sequence_rule *kbts__GetChainedSequenceRule(kbts__chained_sequence_rule_set *Set, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Set + 1); - kbts_chained_sequence_rule *Result = KBTS_POINTER_OFFSET(kbts_chained_sequence_rule, Set, Offsets[Index]); + kbts__chained_sequence_rule *Result = KBTS__POINTER_OFFSET(kbts__chained_sequence_rule, Set, Offsets[Index]); return Result; } -static kbts_chained_sequence_rule_set *kbts_GetChainedClassSequenceRuleSet(kbts_chained_sequence_context_2 *Context, kbts_un Index) +static kbts__chained_sequence_rule_set *kbts__GetChainedClassSequenceRuleSet(kbts__chained_sequence_context_2 *Context, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Context + 1); kbts_u16 Offset = Offsets[Index]; - kbts_chained_sequence_rule_set *Result = Offset ? KBTS_POINTER_OFFSET(kbts_chained_sequence_rule_set, Context, Offset) : NULL; + kbts__chained_sequence_rule_set *Result = Offset ? KBTS__POINTER_OFFSET(kbts__chained_sequence_rule_set, Context, Offset) : NULL; return Result; } -static kbts_chained_sequence_rule *kbts_GetChainedClassSequenceRule(kbts_chained_sequence_rule_set *Set, kbts_un Index) +static kbts__chained_sequence_rule *kbts__GetChainedClassSequenceRule(kbts__chained_sequence_rule_set *Set, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Set + 1); - kbts_chained_sequence_rule *Result = KBTS_POINTER_OFFSET(kbts_chained_sequence_rule, Set, Offsets[Index]); + kbts__chained_sequence_rule *Result = KBTS__POINTER_OFFSET(kbts__chained_sequence_rule, Set, Offsets[Index]); return Result; } #if 0 // @Incomplete -static kbts_feature_variation_pointer kbts_GetFeatureVariation(kbts_feature_variations *Variations, kbts_un Index) +static kbts__feature_variation_pointer kbts__GetFeatureVariation(kbts__feature_variations *Variations, kbts_un Index) { - kbts_feature_variation_record *Records = (kbts_feature_variation_record *)(Variations + 1); - kbts_feature_variation_record *Record = &Records[Index]; + kbts__feature_variation_record *Records = (kbts__feature_variation_record *)(Variations + 1); + kbts__feature_variation_record *Record = &Records[Index]; - kbts_feature_variation_pointer Result; - Result.ConditionSet = KBTS_POINTER_OFFSET(kbts_condition_set, Variations, Record->ConditionSetOffset); - Result.FeatureTableSubstitution = KBTS_POINTER_OFFSET(kbts_feature_table_substitution, Variations, Record->FeatureTableSubstitutionOffset); + kbts__feature_variation_pointer Result; + Result.ConditionSet = KBTS__POINTER_OFFSET(kbts__condition_set, Variations, Record->ConditionSetOffset); + Result.FeatureTableSubstitution = KBTS__POINTER_OFFSET(kbts__feature_table_substitution, Variations, Record->FeatureTableSubstitutionOffset); return Result; } -static kbts_condition_1 *kbts_GetCondition(kbts_condition_set *Set, kbts_un Index) +static kbts__condition_1 *kbts__GetCondition(kbts__condition_set *Set, kbts_un Index) { kbts_u32 *Offsets = (kbts_u32 *)(Set + 1); - kbts_condition_1 *Result = KBTS_POINTER_OFFSET(kbts_condition_1, Set, Offsets[Index]); + kbts__condition_1 *Result = KBTS__POINTER_OFFSET(kbts__condition_1, Set, Offsets[Index]); return Result; } -static kbts_feature_substitution_pointer kbts_GetFeatureSubstitution(kbts_feature_table_substitution *Table, kbts_un Index) +static kbts__feature_substitution_pointer kbts__GetFeatureSubstitution(kbts__feature_table_substitution *Table, kbts_un Index) { - kbts_feature_table_substitution_record *Records = (kbts_feature_table_substitution_record *)(Table + 1); - kbts_feature_table_substitution_record *Record = &Records[Index]; + kbts__feature_table_substitution_record *Records = (kbts__feature_table_substitution_record *)(Table + 1); + kbts__feature_table_substitution_record *Record = &Records[Index]; - kbts_feature_substitution_pointer Result; + kbts__feature_substitution_pointer Result; Result.SubstitutedFeatureIndex = Record->FeatureIndex; - Result.AlternateFeature = KBTS_POINTER_OFFSET(kbts_feature, Table, Record->AlternateFeatureOffset); + Result.AlternateFeature = KBTS__POINTER_OFFSET(kbts__feature, Table, Record->AlternateFeatureOffset); return Result; } #endif -static kbts_cmap_subtable_pointer kbts_GetCmapSubtable(kbts_cmap *Cmap, kbts_un Index) +static kbts__cmap_subtable_pointer kbts__GetCmapSubtable(kbts__cmap *Cmap, kbts_un Index) { - kbts_encoding_record *Records = (kbts_encoding_record *)(Cmap + 1); - kbts_encoding_record *Record = &Records[Index]; + kbts__encoding_record *Records = (kbts__encoding_record *)(Cmap + 1); + kbts__encoding_record *Record = &Records[Index]; - kbts_cmap_subtable_pointer Result; + kbts__cmap_subtable_pointer Result; Result.PlatformId = Record->PlatformId; Result.EncodingId = Record->EncodingId; - Result.Subtable = KBTS_POINTER_OFFSET(kbts_u16, Cmap, Record->SubtableOffset); + Result.Subtable = KBTS__POINTER_OFFSET(kbts_u16, Cmap, Record->SubtableOffset); return Result; } -static kbts_sequence *kbts_GetSequence(kbts_multiple_substitution *Subst, kbts_un Index) +static kbts__sequence *kbts__GetSequence(kbts__multiple_substitution *Subst, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Subst + 1); - kbts_sequence *Result = KBTS_POINTER_OFFSET(kbts_sequence, Subst, Offsets[Index]); + kbts__sequence *Result = KBTS__POINTER_OFFSET(kbts__sequence, Subst, Offsets[Index]); return Result; } -static kbts_alternate_set *kbts_GetAlternateSet(kbts_alternate_substitution *Subst, kbts_un Index) +static kbts__alternate_set *kbts__GetAlternateSet(kbts__alternate_substitution *Subst, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Subst + 1); - kbts_alternate_set *Result = KBTS_POINTER_OFFSET(kbts_alternate_set, Subst, Offsets[Index]); + kbts__alternate_set *Result = KBTS__POINTER_OFFSET(kbts__alternate_set, Subst, Offsets[Index]); return Result; } -static kbts_ligature_set *kbts_GetLigatureSet(kbts_ligature_substitution *Subst, kbts_un Index) +static kbts__ligature_set *kbts__GetLigatureSet(kbts__ligature_substitution *Subst, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Subst + 1); - kbts_ligature_set *Result = KBTS_POINTER_OFFSET(kbts_ligature_set, Subst, Offsets[Index]); + kbts__ligature_set *Result = KBTS__POINTER_OFFSET(kbts__ligature_set, Subst, Offsets[Index]); return Result; } -static kbts_ligature *kbts_GetLigature(kbts_ligature_set *Set, kbts_un Index) +static kbts__ligature *kbts__GetLigature(kbts__ligature_set *Set, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Set + 1); - kbts_ligature *Result = KBTS_POINTER_OFFSET(kbts_ligature, Set, Offsets[Index]); + kbts__ligature *Result = KBTS__POINTER_OFFSET(kbts__ligature, Set, Offsets[Index]); return Result; } -static kbts_mark_record *kbts_GetMarkRecord(kbts_mark_array *Array, kbts_un Index) +static kbts__mark_record *kbts__GetMarkRecord(kbts__mark_array *Array, kbts_un Index) { - kbts_mark_record *Records = KBTS_POINTER_AFTER(kbts_mark_record, Array); - kbts_mark_record *Result = &Records[Index]; + kbts__mark_record *Records = KBTS__POINTER_AFTER(kbts__mark_record, Array); + kbts__mark_record *Result = &Records[Index]; return Result; } -static kbts_ligature_attach *kbts_GetLigatureAttach(kbts_ligature_array *Array, kbts_un Index) +static kbts__ligature_attach *kbts__GetLigatureAttach(kbts__ligature_array *Array, kbts_un Index) { - kbts_u16 *Offsets = KBTS_POINTER_AFTER(kbts_u16, Array); - kbts_ligature_attach *Result = KBTS_POINTER_OFFSET(kbts_ligature_attach, Array, Offsets[Index]); + kbts_u16 *Offsets = KBTS__POINTER_AFTER(kbts_u16, Array); + kbts__ligature_attach *Result = KBTS__POINTER_OFFSET(kbts__ligature_attach, Array, Offsets[Index]); return Result; } -static kbts_anchor *kbts_GetLigatureAttachAnchor(kbts_mark_to_ligature_attachment *Adjust, kbts_ligature_attach *Attach, kbts_u16 MarkClass, kbts_un ComponentIndex) +static kbts__anchor *kbts__GetLigatureAttachAnchor(kbts__mark_to_ligature_attachment *Adjust, kbts__ligature_attach *Attach, kbts_u16 MarkClass, kbts_un ComponentIndex) { - kbts_u16 *Offsets = KBTS_POINTER_AFTER(kbts_u16, Attach); + kbts_u16 *Offsets = KBTS__POINTER_AFTER(kbts_u16, Attach); kbts_u16 Offset = Offsets[ComponentIndex * Adjust->MarkClassCount + MarkClass]; - kbts_anchor *Result = 0; + kbts__anchor *Result = 0; if(Offset) { - Result = KBTS_POINTER_OFFSET(kbts_anchor, Attach, Offset); + Result = KBTS__POINTER_OFFSET(kbts__anchor, Attach, Offset); } return Result; } -typedef struct kbts_mark_info +typedef struct kbts__mark_info { - kbts_mark_array *Array; - kbts_mark_record *Record; - kbts_anchor *Anchor; -} kbts_mark_info; + kbts__mark_array *Array; + kbts__mark_record *Record; + kbts__anchor *Anchor; +} kbts__mark_info; -static kbts_mark_info kbts_GetMarkInfo(void *Subtable, kbts_un SubtableOffsetToMarkArray, kbts_un CoverageIndex) +static kbts__mark_info kbts__GetMarkInfo(void *Subtable, kbts_un SubtableOffsetToMarkArray, kbts_un CoverageIndex) { - kbts_mark_info Result = KBTS_ZERO; + kbts__mark_info Result = KBTS__ZERO; - Result.Array = KBTS_POINTER_OFFSET(kbts_mark_array, Subtable, SubtableOffsetToMarkArray); - Result.Record = kbts_GetMarkRecord(Result.Array, CoverageIndex); - Result.Anchor = KBTS_POINTER_OFFSET(kbts_anchor, Result.Array, Result.Record->AnchorOffset); + Result.Array = KBTS__POINTER_OFFSET(kbts__mark_array, Subtable, SubtableOffsetToMarkArray); + Result.Record = kbts__GetMarkRecord(Result.Array, CoverageIndex); + Result.Anchor = KBTS__POINTER_OFFSET(kbts__anchor, Result.Array, Result.Record->AnchorOffset); return Result; } @@ -14429,7 +17182,7 @@ static kbts_mark_info kbts_GetMarkInfo(void *Subtable, kbts_un SubtableOffsetToM // // -typedef struct kbts_byteswap_context +typedef struct kbts__byteswap_context { char *FileBase; char *FileEnd; @@ -14438,29 +17191,29 @@ typedef struct kbts_byteswap_context kbts_un PointerCount; int Error; -} kbts_byteswap_context; +} kbts__byteswap_context; -static int kbts_ByteSwapArray16Context(kbts_u16 *Array, kbts_un Count, kbts_byteswap_context *Context) +static int kbts__ByteSwapArray16Context(kbts_u16 *Array, kbts_un Count, kbts__byteswap_context *Context) { - int Result = kbts_ByteSwapArray16(Array, Count, Context->FileEnd); + int Result = kbts__ByteSwapArray16(Array, Count, Context->FileEnd); Context->Error |= !Result; return Result; } -static int kbts_ByteSwapArray32Context(kbts_u32 *Array, kbts_un Count, kbts_byteswap_context *Context) +static int kbts__ByteSwapArray32Context(kbts_u32 *Array, kbts_un Count, kbts__byteswap_context *Context) { - int Result = kbts_ByteSwapArray32(Array, Count, Context->FileEnd); + int Result = kbts__ByteSwapArray32(Array, Count, Context->FileEnd); Context->Error |= !Result; return Result; } -typedef struct kbts_cover_glyph_result +typedef struct kbts__cover_glyph_result { - kbts_u32 Valid; + int Valid; kbts_u32 Index; -} kbts_cover_glyph_result; +} kbts__cover_glyph_result; -static int kbts_LookupBeginsWithCoverage(kbts_shaping_table ShapingTable, kbts_u16 LookupType, kbts_u16 Format) +static int kbts__LookupBeginsWithCoverage(kbts_shaping_table ShapingTable, kbts_u16 LookupType, kbts_u16 Format) { int Result = 0; if(ShapingTable == KBTS_SHAPING_TABLE_GSUB) @@ -14475,56 +17228,26 @@ static int kbts_LookupBeginsWithCoverage(kbts_shaping_table ShapingTable, kbts_u } // Except for lookup tables 5.3, 6.3 and 7, all tables begin with (kbts_u16 Format, kbts_u16 CoverageOffset). -KBTS_INLINE int kbts_GsubLookupBeginsWithCoverage(kbts_u16 LookupType, kbts_u16 Format) +KBTS_INLINE int kbts__GsubLookupBeginsWithCoverage(kbts_u16 LookupType, kbts_u16 Format) { - int Result = kbts_LookupBeginsWithCoverage(KBTS_SHAPING_TABLE_GSUB, LookupType, Format); + int Result = kbts__LookupBeginsWithCoverage(KBTS_SHAPING_TABLE_GSUB, LookupType, Format); return Result; } -KBTS_INLINE int kbts_GposLookupBeginsWithCoverage(kbts_u16 LookupType, kbts_u16 Format) +KBTS_INLINE int kbts__GposLookupBeginsWithCoverage(kbts_u16 LookupType, kbts_u16 Format) { - int Result = kbts_LookupBeginsWithCoverage(KBTS_SHAPING_TABLE_GPOS, LookupType, Format); + int Result = kbts__LookupBeginsWithCoverage(KBTS_SHAPING_TABLE_GPOS, LookupType, Format); return Result; } -typedef struct kbts_lookup_info_frame -{ - kbts_u16 *Base; - kbts_u16 LookupType; - kbts_u32 LookaheadOffset; - kbts_u32 StackSize; -} kbts_lookup_info_frame; - -static int kbts_PushLookup(kbts_gdef *Gdef, kbts_lookup_info_frame *Frames, kbts_un *FrameCount, kbts_un FrameCapacity, kbts_lookup *PackedLookup, kbts_u32 LookaheadOffset, kbts_un StackSize) -{ - int Result = 0; - kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Gdef, PackedLookup); - - if((*FrameCount + Lookup.SubtableCount) <= FrameCapacity) - { - KBTS_FOR(SubtableIndex, 0, Lookup.SubtableCount) - { - kbts_lookup_info_frame *Frame = &Frames[(*FrameCount)++]; - Frame->Base = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubtableIndex]); - Frame->LookupType = Lookup.Type; - Frame->LookaheadOffset = LookaheadOffset; - Frame->StackSize = (kbts_u32)StackSize; - } - - Result = 1; - } - - return Result; -} - -static int kbts_AlreadyVisited(kbts_byteswap_context *Context, void *Pointer) +static int kbts__AlreadyVisited(kbts__byteswap_context *Context, void *Pointer) { int Result = !Pointer || Context->Error; if(!Result) { kbts_u32 *Pointers = Context->Pointers; - kbts_u32 Pointer32 = KBTS_POINTER_DIFF32(Pointer, Context->FileBase); + kbts_u32 Pointer32 = KBTS__POINTER_DIFF32(Pointer, Context->FileBase); kbts_un Index = 0; if(Context->PointerCount) @@ -14558,20 +17281,20 @@ static int kbts_AlreadyVisited(kbts_byteswap_context *Context, void *Pointer) return Result; } -static void kbts_ByteSwapFeature(kbts_byteswap_context *Context, kbts_feature *Feature) +static void kbts__ByteSwapFeature(kbts__byteswap_context *Context, kbts__feature *Feature) { - if(!kbts_AlreadyVisited(Context, Feature)) + if(!kbts__AlreadyVisited(Context, Feature)) { - kbts_u16 *LookupIndices = KBTS_POINTER_AFTER(kbts_u16, Feature); - Context->Error |= !kbts_ByteSwapArray16(&Feature->FeatureParamsOffset, 2, Context->FileEnd); - Context->Error |= !kbts_ByteSwapArray16(LookupIndices, Feature->LookupIndexCount, Context->FileEnd); + kbts_u16 *LookupIndices = KBTS__POINTER_AFTER(kbts_u16, Feature); + Context->Error |= !kbts__ByteSwapArray16(&Feature->FeatureParamsOffset, 2, Context->FileEnd); + Context->Error |= !kbts__ByteSwapArray16(LookupIndices, Feature->LookupIndexCount, Context->FileEnd); // We require lookup indices to be sorted per feature for the lookup application order to match Harfbuzz. // Lookup indices are _typically_ sorted per feature, but we can't assume it is always the case. kbts_un LookupIndexCount = Feature->LookupIndexCount; - KBTS_FOR(Iter, 0, LookupIndexCount) + KBTS__FOR(Iter, 0, LookupIndexCount) { - KBTS_FOR(IndexIndex, 1, LookupIndexCount) + KBTS__FOR(IndexIndex, 1, LookupIndexCount) { kbts_u16 Left = LookupIndices[IndexIndex - 1]; kbts_u16 Right = LookupIndices[IndexIndex]; @@ -14586,63 +17309,63 @@ static void kbts_ByteSwapFeature(kbts_byteswap_context *Context, kbts_feature *F } # ifdef KBTS_DUMP - kbts_u16 *LookupIndices = KBTS_POINTER_AFTER(kbts_u16, Feature); - KBTS_FOR(LookupIndexIndex, 0, Feature->LookupIndexCount) + kbts_u16 *LookupIndices = KBTS__POINTER_AFTER(kbts_u16, Feature); + KBTS__FOR(LookupIndexIndex, 0, Feature->LookupIndexCount) { KBTS_DUMPF(" Lookup index %u\n", LookupIndices[LookupIndexIndex]); } # endif } -static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsub_gpos *Header) +static void kbts__ByteSwapGsubGposCommon(kbts__byteswap_context *Context, kbts__gsub_gpos *Header) { - kbts_ByteSwapArray16Context(&Header->Major, 5, Context); + kbts__ByteSwapArray16Context(&Header->Major, 5, Context); if(Header->Minor == 1) { - Header->FeatureVariationsOffset = kbts_ByteSwap32(Header->FeatureVariationsOffset); + Header->FeatureVariationsOffset = kbts__ByteSwap32(Header->FeatureVariationsOffset); } - kbts_script_list *ScriptList = KBTS_POINTER_OFFSET(kbts_script_list, Header, Header->ScriptListOffset); - if(!kbts_AlreadyVisited(Context, ScriptList)) + kbts__script_list *ScriptList = KBTS__POINTER_OFFSET(kbts__script_list, Header, Header->ScriptListOffset); + if(!kbts__AlreadyVisited(Context, ScriptList)) { - ScriptList->Count = kbts_ByteSwap16(ScriptList->Count); + ScriptList->Count = kbts__ByteSwap16(ScriptList->Count); - kbts_script_record *ScriptRecords = KBTS_POINTER_AFTER(kbts_script_record, ScriptList); - KBTS_FOR(It, 0, ScriptList->Count) + kbts__script_record *ScriptRecords = KBTS__POINTER_AFTER(kbts__script_record, ScriptList); + KBTS__FOR(It, 0, ScriptList->Count) { - kbts_script_record *Record = &ScriptRecords[It]; + kbts__script_record *Record = &ScriptRecords[It]; - Record->Offset = kbts_ByteSwap16(Record->Offset); + Record->Offset = kbts__ByteSwap16(Record->Offset); KBTS_DUMPF("Script %.4s\n", (char *)&Record->Tag); - kbts_ot_script *Script = KBTS_POINTER_OFFSET(kbts_ot_script, ScriptList, Record->Offset); - if(!kbts_AlreadyVisited(Context, Script)) + kbts__ot_script *Script = KBTS__POINTER_OFFSET(kbts__ot_script, ScriptList, Record->Offset); + if(!kbts__AlreadyVisited(Context, Script)) { - Script->DefaultLangsysOffset = kbts_ByteSwap16(Script->DefaultLangsysOffset); - Script->Count = kbts_ByteSwap16(Script->Count); + Script->DefaultLangsysOffset = kbts__ByteSwap16(Script->DefaultLangsysOffset); + Script->Count = kbts__ByteSwap16(Script->Count); - kbts_langsys *DefaultLangsys = kbts_GetDefaultLangsys(Script); + kbts__langsys *DefaultLangsys = kbts__GetDefaultLangsys(Script); - if(DefaultLangsys && !kbts_AlreadyVisited(Context, DefaultLangsys)) + if(DefaultLangsys && !kbts__AlreadyVisited(Context, DefaultLangsys)) { - kbts_ByteSwapArray16Context(&DefaultLangsys->LookupOrderOffset, 3, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, DefaultLangsys), DefaultLangsys->FeatureIndexCount, Context); + kbts__ByteSwapArray16Context(&DefaultLangsys->LookupOrderOffset, 3, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, DefaultLangsys), DefaultLangsys->FeatureIndexCount, Context); } - kbts_langsys_record *LangsysRecords = KBTS_POINTER_AFTER(kbts_langsys_record, Script); - KBTS_FOR(LangsysIndex, 0, Script->Count) + kbts__langsys_record *LangsysRecords = KBTS__POINTER_AFTER(kbts__langsys_record, Script); + KBTS__FOR(LangsysIndex, 0, Script->Count) { - kbts_langsys_record *LangsysRecord = &LangsysRecords[LangsysIndex]; + kbts__langsys_record *LangsysRecord = &LangsysRecords[LangsysIndex]; - LangsysRecord->Offset = kbts_ByteSwap16(LangsysRecord->Offset); + LangsysRecord->Offset = kbts__ByteSwap16(LangsysRecord->Offset); - kbts_langsys *Langsys = KBTS_POINTER_OFFSET(kbts_langsys, Script, LangsysRecord->Offset); - if(!kbts_AlreadyVisited(Context, Langsys)) + kbts__langsys *Langsys = KBTS__POINTER_OFFSET(kbts__langsys, Script, LangsysRecord->Offset); + if(!kbts__AlreadyVisited(Context, Langsys)) { - kbts_ByteSwapArray16Context(&Langsys->LookupOrderOffset, 3, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Langsys), Langsys->FeatureIndexCount, Context); + kbts__ByteSwapArray16Context(&Langsys->LookupOrderOffset, 3, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Langsys), Langsys->FeatureIndexCount, Context); } } @@ -14652,23 +17375,23 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu KBTS_DUMPF(" Default langsys @ %zu\n", (char *)DefaultLangsys - Context->FileBase); KBTS_DUMPF(" Lookup order offset %u\n", DefaultLangsys->LookupOrderOffset); KBTS_DUMPF(" Required feature %u\n", DefaultLangsys->RequiredFeatureIndex); - kbts_u16 *FeatureIndices = KBTS_POINTER_AFTER(kbts_u16, DefaultLangsys); - KBTS_FOR(FeatureIndexIndex, 0, DefaultLangsys->FeatureIndexCount) + kbts_u16 *FeatureIndices = KBTS__POINTER_AFTER(kbts_u16, DefaultLangsys); + KBTS__FOR(FeatureIndexIndex, 0, DefaultLangsys->FeatureIndexCount) { KBTS_DUMPF(" Feature %u\n", FeatureIndices[FeatureIndexIndex]); } } - KBTS_FOR(LangsysIndex, 0, Script->Count) + KBTS__FOR(LangsysIndex, 0, Script->Count) { - kbts_langsys_record *LangsysRecord = &LangsysRecords[LangsysIndex]; - kbts_langsys *Langsys = KBTS_POINTER_OFFSET(kbts_langsys, Script, LangsysRecord->Offset); + kbts__langsys_record *LangsysRecord = &LangsysRecords[LangsysIndex]; + kbts__langsys *Langsys = KBTS__POINTER_OFFSET(kbts__langsys, Script, LangsysRecord->Offset); KBTS_DUMPF(" Langsys %.4s @ %zu\n", (char *)&LangsysRecord->Tag, (char *)Script + LangsysRecord->Offset - Context->FileBase); KBTS_DUMPF(" Lookup order offset %u\n" " Required feature %u\n", Langsys->LookupOrderOffset, Langsys->RequiredFeatureIndex); - kbts_u16 *FeatureIndices = KBTS_POINTER_AFTER(kbts_u16, Langsys); - KBTS_FOR(FeatureIndexIndex, 0, Langsys->FeatureIndexCount) + kbts_u16 *FeatureIndices = KBTS__POINTER_AFTER(kbts_u16, Langsys); + KBTS__FOR(FeatureIndexIndex, 0, Langsys->FeatureIndexCount) { KBTS_DUMPF(" Feature %u\n", FeatureIndices[FeatureIndexIndex]); } @@ -14677,55 +17400,58 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu } } + // @Incomplete if((Header->Minor == 1) && Header->FeatureVariationsOffset) { - kbts_feature_variations *FeatureVariations = KBTS_POINTER_OFFSET(kbts_feature_variations, Header, Header->FeatureVariationsOffset); + kbts__feature_variations *FeatureVariations = KBTS__POINTER_OFFSET(kbts__feature_variations, Header, Header->FeatureVariationsOffset); - if(!kbts_AlreadyVisited(Context, FeatureVariations)) + if(!kbts__AlreadyVisited(Context, FeatureVariations)) { - kbts_ByteSwapArray16Context(&FeatureVariations->Major, 2, Context); - FeatureVariations->RecordCount = kbts_ByteSwap32(FeatureVariations->RecordCount); + kbts__ByteSwapArray16Context(&FeatureVariations->Major, 2, Context); + FeatureVariations->RecordCount = kbts__ByteSwap32(FeatureVariations->RecordCount); - kbts_feature_variation_record *Records = KBTS_POINTER_AFTER(kbts_feature_variation_record, FeatureVariations); - KBTS_FOR(VariationIndex, 0, FeatureVariations->RecordCount) + kbts__feature_variation_record *Records = KBTS__POINTER_AFTER(kbts__feature_variation_record, FeatureVariations); + KBTS__FOR(VariationIndex, 0, FeatureVariations->RecordCount) { - kbts_feature_variation_record *Record = &Records[VariationIndex]; + kbts__feature_variation_record *Record = &Records[VariationIndex]; - kbts_ByteSwapArray32Context(&Record->ConditionSetOffset, 2, Context); + kbts__ByteSwapArray32Context(&Record->ConditionSetOffset, 2, Context); - kbts_condition_set *Set = KBTS_POINTER_OFFSET(kbts_condition_set, FeatureVariations, Record->ConditionSetOffset); + kbts__condition_set *Set = KBTS__POINTER_OFFSET(kbts__condition_set, FeatureVariations, Record->ConditionSetOffset); - if(!kbts_AlreadyVisited(Context, Set)) + if(!kbts__AlreadyVisited(Context, Set)) { - Set->Count = kbts_ByteSwap16(Set->Count); + Set->Count = kbts__ByteSwap16(Set->Count); - kbts_u32 *ConditionOffsets = KBTS_POINTER_AFTER(kbts_u32, Set); - kbts_ByteSwapArray32Context(ConditionOffsets, Set->Count, Context); + kbts_u32 *ConditionOffsets = KBTS__POINTER_AFTER(kbts_u32, Set); + kbts__ByteSwapArray32Context(ConditionOffsets, Set->Count, Context); - KBTS_FOR(ConditionIndex, 0, Set->Count) + KBTS__FOR(ConditionIndex, 0, Set->Count) { - kbts_condition_1 *Condition = KBTS_POINTER_OFFSET(kbts_condition_1, Set, ConditionOffsets[ConditionIndex]); + kbts__condition_1 *Condition = KBTS__POINTER_OFFSET(kbts__condition_1, Set, ConditionOffsets[ConditionIndex]); - if(!kbts_AlreadyVisited(Context, Condition)) + if(!kbts__AlreadyVisited(Context, Condition)) { - kbts_ByteSwapArray16Context(&Condition->Format, 4, Context); + kbts__ByteSwapArray16Context(&Condition->Format, 4, Context); } } - kbts_feature_table_substitution *FeatureSubst = KBTS_POINTER_OFFSET(kbts_feature_table_substitution, FeatureVariations, Record->FeatureTableSubstitutionOffset); + // @Incomplete + kbts__feature_table_substitution *FeatureSubst = KBTS__POINTER_OFFSET(kbts__feature_table_substitution, FeatureVariations, Record->FeatureTableSubstitutionOffset); - if(!kbts_AlreadyVisited(Context, FeatureSubst)) + if(!kbts__AlreadyVisited(Context, FeatureSubst)) { - kbts_ByteSwapArray16Context(&FeatureSubst->Major, 3, Context); + kbts__ByteSwapArray16Context(&FeatureSubst->Major, 3, Context); - kbts_feature_table_substitution_record *SubstRecords = KBTS_POINTER_AFTER(kbts_feature_table_substitution_record, FeatureSubst); - KBTS_FOR(SubstRecordIndex, 0, FeatureSubst->Count) + kbts__feature_table_substitution_record *SubstRecords = KBTS__POINTER_AFTER(kbts__feature_table_substitution_record, FeatureSubst); + KBTS__FOR(SubstRecordIndex, 0, FeatureSubst->Count) { - kbts_feature_table_substitution_record *SubstRecord = &SubstRecords[SubstRecordIndex]; - SubstRecord->FeatureIndex = kbts_ByteSwap16(SubstRecord->FeatureIndex); + kbts__feature_table_substitution_record *SubstRecord = &SubstRecords[SubstRecordIndex]; + SubstRecord->FeatureIndex = kbts__ByteSwap16(SubstRecord->FeatureIndex); + SubstRecord->AlternateFeatureOffset = kbts__ByteSwap32(SubstRecord->AlternateFeatureOffset); - kbts_feature *Feature = KBTS_POINTER_OFFSET(kbts_feature, FeatureSubst, SubstRecord->AlternateFeatureOffset); - kbts_ByteSwapFeature(Context, Feature); + kbts__feature *Feature = KBTS__POINTER_OFFSET(kbts__feature, FeatureSubst, SubstRecord->AlternateFeatureOffset); + kbts__ByteSwapFeature(Context, Feature); } } } @@ -14734,55 +17460,55 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu } } - kbts_feature_list *FeatureList = KBTS_POINTER_OFFSET(kbts_feature_list, Header, Header->FeatureListOffset); - if(!kbts_AlreadyVisited(Context, FeatureList)) + kbts__feature_list *FeatureList = KBTS__POINTER_OFFSET(kbts__feature_list, Header, Header->FeatureListOffset); + if(!kbts__AlreadyVisited(Context, FeatureList)) { - FeatureList->Count = kbts_ByteSwap16(FeatureList->Count); - kbts_feature_record *FeatureRecords = KBTS_POINTER_AFTER(kbts_feature_record, FeatureList); - KBTS_FOR(FeatureRecordIndex, 0, FeatureList->Count) + FeatureList->Count = kbts__ByteSwap16(FeatureList->Count); + kbts__feature_record *FeatureRecords = KBTS__POINTER_AFTER(kbts__feature_record, FeatureList); + KBTS__FOR(FeatureRecordIndex, 0, FeatureList->Count) { - kbts_feature_record *FeatureRecord = &FeatureRecords[FeatureRecordIndex]; + kbts__feature_record *FeatureRecord = &FeatureRecords[FeatureRecordIndex]; KBTS_DUMPF("Feature %llu %.4s\n", FeatureRecordIndex, (char *)&FeatureRecord->Tag); - if(!kbts_AlreadyVisited(Context, FeatureRecord)) + if(!kbts__AlreadyVisited(Context, FeatureRecord)) { - FeatureRecord->Offset = kbts_ByteSwap16(FeatureRecord->Offset); + FeatureRecord->Offset = kbts__ByteSwap16(FeatureRecord->Offset); - kbts_feature *Feature = KBTS_POINTER_OFFSET(kbts_feature, FeatureList, FeatureRecord->Offset); - kbts_ByteSwapFeature(Context, Feature); + kbts__feature *Feature = KBTS__POINTER_OFFSET(kbts__feature, FeatureList, FeatureRecord->Offset); + kbts__ByteSwapFeature(Context, Feature); } } } } -static int kbts_ByteSwapLookup(kbts_byteswap_context *Context, kbts_lookup *Lookup) +static int kbts__ByteSwapLookup(kbts__byteswap_context *Context, kbts__lookup *Lookup) { int Result = 0; - if(!kbts_AlreadyVisited(Context, Lookup)) + if(!kbts__AlreadyVisited(Context, Lookup)) { Result = 1; - kbts_ByteSwapArray16Context(&Lookup->Type, 3, Context); - kbts_u16 *SubtableOffsets = KBTS_POINTER_AFTER(kbts_u16, Lookup); + kbts__ByteSwapArray16Context(&Lookup->Type, 3, Context); + kbts_u16 *SubtableOffsets = KBTS__POINTER_AFTER(kbts_u16, Lookup); kbts_un U16Count = Lookup->SubtableCount; - if(Lookup->Flag & KBTS_LOOKUP_FLAG_USE_MARK_FILTERING_SET) + if(Lookup->Flag & KBTS__LOOKUP_FLAG_USE_MARK_FILTERING_SET) { U16Count += 1; } - kbts_ByteSwapArray16Context(SubtableOffsets, U16Count, Context); + kbts__ByteSwapArray16Context(SubtableOffsets, U16Count, Context); } return Result; } -static void kbts_ByteSwapCoverage(kbts_byteswap_context *Context, kbts_coverage *Coverage) +static void kbts__ByteSwapCoverage(kbts__byteswap_context *Context, kbts__coverage *Coverage) { - if(!kbts_AlreadyVisited(Context, Coverage)) + if(!kbts__AlreadyVisited(Context, Coverage)) { - kbts_ByteSwapArray16Context(&Coverage->Format, 2, Context); + kbts__ByteSwapArray16Context(&Coverage->Format, 2, Context); kbts_un U16Count = 0; if(Coverage->Format == 1) @@ -14794,15 +17520,15 @@ static void kbts_ByteSwapCoverage(kbts_byteswap_context *Context, kbts_coverage U16Count = Coverage->Count * 3; } - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Coverage), U16Count, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Coverage), U16Count, Context); } } -static void kbts_ByteSwapAnchor(kbts_byteswap_context *Context, kbts_anchor *Anchor) +static void kbts__ByteSwapAnchor(kbts__byteswap_context *Context, kbts__anchor *Anchor) { - if(!kbts_AlreadyVisited(Context, Anchor)) + if(!kbts__AlreadyVisited(Context, Anchor)) { - Anchor->Format = kbts_ByteSwap16(Anchor->Format); + Anchor->Format = kbts__ByteSwap16(Anchor->Format); kbts_un U16Count = 2; if(Anchor->Format == 2) @@ -14814,36 +17540,36 @@ static void kbts_ByteSwapAnchor(kbts_byteswap_context *Context, kbts_anchor *Anc U16Count = 4; } - kbts_ByteSwapArray16Context((kbts_u16 *)&Anchor->X, U16Count, Context); + kbts__ByteSwapArray16Context((kbts_u16 *)&Anchor->X, U16Count, Context); } } -static void kbts_ByteSwapBaseArray(kbts_byteswap_context *Context, kbts_u16 MarkClassCount, kbts_base_array *Array) +static void kbts__ByteSwapBaseArray(kbts__byteswap_context *Context, kbts_u16 MarkClassCount, kbts__base_array *Array) { - if(!kbts_AlreadyVisited(Context, Array)) + if(!kbts__AlreadyVisited(Context, Array)) { - Array->BaseCount = kbts_ByteSwap16(Array->BaseCount); + Array->BaseCount = kbts__ByteSwap16(Array->BaseCount); - kbts_u16 *BaseAnchorOffsets = KBTS_POINTER_AFTER(kbts_u16, Array); - kbts_ByteSwapArray16Context(BaseAnchorOffsets, Array->BaseCount * MarkClassCount, Context); + kbts_u16 *BaseAnchorOffsets = KBTS__POINTER_AFTER(kbts_u16, Array); + kbts__ByteSwapArray16Context(BaseAnchorOffsets, Array->BaseCount * MarkClassCount, Context); - KBTS_FOR(OffsetIndex, 0, (kbts_un)Array->BaseCount * MarkClassCount) + KBTS__FOR(OffsetIndex, 0, (kbts_un)Array->BaseCount * MarkClassCount) { kbts_u16 Offset = BaseAnchorOffsets[OffsetIndex]; if(Offset) { - kbts_ByteSwapAnchor(Context, KBTS_POINTER_OFFSET(kbts_anchor, Array, Offset)); + kbts__ByteSwapAnchor(Context, KBTS__POINTER_OFFSET(kbts__anchor, Array, Offset)); } } } } -static void kbts_ByteSwapDevice(kbts_byteswap_context *Context, kbts_device *Device) +static void kbts__ByteSwapDevice(kbts__byteswap_context *Context, kbts__device *Device) { - if(!kbts_AlreadyVisited(Context, Device)) + if(!kbts__AlreadyVisited(Context, Device)) { - kbts_ByteSwapArray16Context(&Device->U.Device.StartSize, 3, Context); + kbts__ByteSwapArray16Context(&Device->U.Device.StartSize, 3, Context); if(Device->DeltaFormat <= 3) { @@ -14852,85 +17578,85 @@ static void kbts_ByteSwapDevice(kbts_byteswap_context *Context, kbts_device *Dev } } -static kbts_unpacked_value_record kbts_ByteSwapValueRecord(kbts_byteswap_context *Context, void *Parent, kbts_u16 ValueFormat, kbts_u16 *Record) +static kbts__unpacked_value_record kbts_ByteSwapValueRecord(kbts__byteswap_context *Context, void *Parent, kbts_u16 ValueFormat, kbts_u16 *Record) { - kbts_unpacked_value_record Result = KBTS_ZERO; + kbts__unpacked_value_record Result = KBTS__ZERO; if(ValueFormat) { - kbts_un U16Count = kbts_PopCount32(ValueFormat); + kbts_un U16Count = kbts__PopCount32(ValueFormat); - kbts_ByteSwapArray16Context(Record, U16Count, Context); + kbts__ByteSwapArray16Context(Record, U16Count, Context); - Result = kbts_UnpackValueRecord(Parent, ValueFormat, Record); + Result = kbts__UnpackValueRecord(Parent, ValueFormat, Record); - kbts_ByteSwapDevice(Context, Result.PlacementXDevice); - kbts_ByteSwapDevice(Context, Result.PlacementYDevice); - kbts_ByteSwapDevice(Context, Result.AdvanceXDevice); - kbts_ByteSwapDevice(Context, Result.AdvanceYDevice); + kbts__ByteSwapDevice(Context, Result.PlacementXDevice); + kbts__ByteSwapDevice(Context, Result.PlacementYDevice); + kbts__ByteSwapDevice(Context, Result.AdvanceXDevice); + kbts__ByteSwapDevice(Context, Result.AdvanceYDevice); } return Result; } -static void kbts_ByteSwapMarkArray(kbts_byteswap_context *Context, kbts_mark_array *MarkArray) +static void kbts__ByteSwapMarkArray(kbts__byteswap_context *Context, kbts__mark_array *MarkArray) { - if(!kbts_AlreadyVisited(Context, MarkArray)) + if(!kbts__AlreadyVisited(Context, MarkArray)) { - MarkArray->Count = kbts_ByteSwap16(MarkArray->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, MarkArray), MarkArray->Count * 2, Context); + MarkArray->Count = kbts__ByteSwap16(MarkArray->Count); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, MarkArray), MarkArray->Count * 2, Context); - kbts_mark_record *MarkRecords = KBTS_POINTER_AFTER(kbts_mark_record, MarkArray); - KBTS_FOR(MarkRecordIndex, 0, MarkArray->Count) + kbts__mark_record *MarkRecords = KBTS__POINTER_AFTER(kbts__mark_record, MarkArray); + KBTS__FOR(MarkRecordIndex, 0, MarkArray->Count) { - kbts_mark_record *MarkRecord = &MarkRecords[MarkRecordIndex]; + kbts__mark_record *MarkRecord = &MarkRecords[MarkRecordIndex]; - kbts_ByteSwapAnchor(Context, KBTS_POINTER_OFFSET(kbts_anchor, MarkArray, MarkRecord->AnchorOffset)); + kbts__ByteSwapAnchor(Context, KBTS__POINTER_OFFSET(kbts__anchor, MarkArray, MarkRecord->AnchorOffset)); } } } -static void kbts_ByteSwapChainedSequenceRuleSet(kbts_byteswap_context *Context, kbts_chained_sequence_rule_set *Set) +static void kbts__ByteSwapChainedSequenceRuleSet(kbts__byteswap_context *Context, kbts__chained_sequence_rule_set *Set) { - if(Set && !kbts_AlreadyVisited(Context, Set)) + if(Set && !kbts__AlreadyVisited(Context, Set)) { - Set->Count = kbts_ByteSwap16(Set->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count, Context); + Set->Count = kbts__ByteSwap16(Set->Count); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Set), Set->Count, Context); - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); + kbts__chained_sequence_rule *Rule = kbts__GetChainedSequenceRule(Set, RuleIndex); - if(!kbts_AlreadyVisited(Context, Rule)) + if(!kbts__AlreadyVisited(Context, Rule)) { - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 1); + kbts__unpacked_chained_sequence_rule Unpacked = kbts__UnpackChainedSequenceRule(Rule, 1); kbts_un U16Count = Unpacked.BacktrackCount + Unpacked.InputCount + Unpacked.LookaheadCount + Unpacked.RecordCount * 2 + 3; - kbts_ByteSwapArray16Context(&Rule->BacktrackGlyphCount, U16Count, Context); + kbts__ByteSwapArray16Context(&Rule->BacktrackGlyphCount, U16Count, Context); } } } } #ifdef KBTS_DUMP -static void kbts_DumpClassDefinition(kbts_u16 *Base) +static void kbts__DumpClassDefinition(kbts_u16 *Base) { if(*Base == 1) { - kbts_class_definition_1 *ClassDef = (kbts_class_definition_1 *)Base; - kbts_u16 *ClassValues = KBTS_POINTER_AFTER(kbts_u16, ClassDef); - KBTS_FOR(GlyphIndex, 0, ClassDef->GlyphCount) + kbts__class_definition_1 *ClassDef = (kbts__class_definition_1 *)Base; + kbts_u16 *ClassValues = KBTS__POINTER_AFTER(kbts_u16, ClassDef); + KBTS__FOR(GlyphIndex, 0, ClassDef->GlyphCount) { KBTS_DUMPF("%llx -> %u\n", ClassDef->StartGlyphId + GlyphIndex, ClassValues[GlyphIndex]); } } else if(*Base == 2) { - kbts_class_definition_2 *ClassDef = (kbts_class_definition_2 *)Base; - kbts_class_range_record *Ranges = KBTS_POINTER_AFTER(kbts_class_range_record, ClassDef); - KBTS_FOR(RangeIndex, 0, ClassDef->Count) + kbts__class_definition_2 *ClassDef = (kbts__class_definition_2 *)Base; + kbts__class_range_record *Ranges = KBTS__POINTER_AFTER(kbts__class_range_record, ClassDef); + KBTS__FOR(RangeIndex, 0, ClassDef->Count) { - kbts_class_range_record *Range = &Ranges[RangeIndex]; + kbts__class_range_record *Range = &Ranges[RangeIndex]; KBTS_DUMPF("[%x..%x] -> %u\n", Range->StartGlyphId, Range->EndGlyphId, Range->Class); } @@ -14938,37 +17664,37 @@ static void kbts_DumpClassDefinition(kbts_u16 *Base) } #endif -static void kbts_ByteSwapClassDefinition(kbts_byteswap_context *Context, kbts_u16 *Base) +static void kbts__ByteSwapClassDefinition(kbts__byteswap_context *Context, kbts_u16 *Base) { - if(!kbts_AlreadyVisited(Context, Base)) + if(!kbts__AlreadyVisited(Context, Base)) { - *Base = kbts_ByteSwap16(*Base); + *Base = kbts__ByteSwap16(*Base); if(*Base == 1) { - kbts_class_definition_1 *ClassDef = (kbts_class_definition_1 *)Base; - kbts_ByteSwapArray16Context(&ClassDef->StartGlyphId, 2, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, ClassDef), ClassDef->GlyphCount, Context); + kbts__class_definition_1 *ClassDef = (kbts__class_definition_1 *)Base; + kbts__ByteSwapArray16Context(&ClassDef->StartGlyphId, 2, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, ClassDef), ClassDef->GlyphCount, Context); } else if(*Base == 2) { - kbts_class_definition_2 *ClassDef = (kbts_class_definition_2 *)Base; - ClassDef->Count = kbts_ByteSwap16(ClassDef->Count); + kbts__class_definition_2 *ClassDef = (kbts__class_definition_2 *)Base; + ClassDef->Count = kbts__ByteSwap16(ClassDef->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, ClassDef), ClassDef->Count * 3, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, ClassDef), ClassDef->Count * 3, Context); } } } -typedef struct kbts_glyph_class_from_table_result +typedef struct kbts__glyph_class_from_table_result { int Found; kbts_u16 Class; -} kbts_glyph_class_from_table_result; +} kbts__glyph_class_from_table_result; -static kbts_glyph_class_from_table_result kbts_GlyphClassFromTable(kbts_u16 *ClassDefinitionBase, kbts_un Id) +static kbts__glyph_class_from_table_result kbts__GlyphClassFromTable(kbts_u16 *ClassDefinitionBase, kbts_un Id) { - kbts_glyph_class_from_table_result Result = KBTS_ZERO; + kbts__glyph_class_from_table_result Result = KBTS__ZERO; // From the Microsoft docs: // There is one offset to a ChainedClassSequenceRuleSet subtable for each class defined in the input sequence @@ -14982,8 +17708,8 @@ static kbts_glyph_class_from_table_result kbts_GlyphClassFromTable(kbts_u16 *Cla if(*ClassDefinitionBase == 1) { - kbts_class_definition_1 *ClassDef = (kbts_class_definition_1 *)ClassDefinitionBase; - kbts_u16 *GlyphClasses = KBTS_POINTER_AFTER(kbts_u16, ClassDef); + kbts__class_definition_1 *ClassDef = (kbts__class_definition_1 *)ClassDefinitionBase; + kbts_u16 *GlyphClasses = KBTS__POINTER_AFTER(kbts_u16, ClassDef); kbts_un Offset = Id - ClassDef->StartGlyphId; if(Offset < ClassDef->GlyphCount) @@ -14994,8 +17720,8 @@ static kbts_glyph_class_from_table_result kbts_GlyphClassFromTable(kbts_u16 *Cla } else if(*ClassDefinitionBase == 2) { - kbts_class_definition_2 *ClassDef = (kbts_class_definition_2 *)ClassDefinitionBase; - kbts_class_range_record *Ranges = KBTS_POINTER_AFTER(kbts_class_range_record, ClassDef); + kbts__class_definition_2 *ClassDef = (kbts__class_definition_2 *)ClassDefinitionBase; + kbts__class_range_record *Ranges = KBTS__POINTER_AFTER(kbts__class_range_record, ClassDef); kbts_un RangeCount = ClassDef->Count; if(RangeCount) @@ -15017,17 +17743,17 @@ static kbts_glyph_class_from_table_result kbts_GlyphClassFromTable(kbts_u16 *Cla return Result; } -static kbts_cover_glyph_result kbts_CoverGlyph(kbts_coverage *Coverage, kbts_u32 GlyphId) +static kbts__cover_glyph_result kbts__CoverGlyph(kbts__coverage *Coverage, kbts_u32 GlyphId) { - KBTS_INSTRUMENT_FUNCTION_BEGIN - kbts_cover_glyph_result Result = KBTS_ZERO; + KBTS_INSTRUMENT_FUNCTION_BEGIN; + kbts__cover_glyph_result Result = KBTS__ZERO; kbts_un Count = Coverage->Count; if(Count) { if(Coverage->Format == 1) { - kbts_u16 *GlyphIds = KBTS_POINTER_AFTER(kbts_u16, Coverage); + kbts_u16 *GlyphIds = KBTS__POINTER_AFTER(kbts_u16, Coverage); while(Count > 1) { @@ -15039,12 +17765,12 @@ static kbts_cover_glyph_result kbts_CoverGlyph(kbts_coverage *Coverage, kbts_u32 if(GlyphId == *GlyphIds) { Result.Valid = 1; - Result.Index = (kbts_u32)(GlyphIds - KBTS_POINTER_AFTER(kbts_u16, Coverage)); + Result.Index = (kbts_u32)(GlyphIds - KBTS__POINTER_AFTER(kbts_u16, Coverage)); } } else if(Coverage->Format == 2) { - kbts_range_record *Ranges = KBTS_POINTER_AFTER(kbts_range_record, Coverage); + kbts__range_record *Ranges = KBTS__POINTER_AFTER(kbts__range_record, Coverage); while(Count > 1) { @@ -15060,35 +17786,35 @@ static kbts_cover_glyph_result kbts_CoverGlyph(kbts_coverage *Coverage, kbts_u32 } } - KBTS_INSTRUMENT_END + KBTS_INSTRUMENT_FUNCTION_END; return Result; } -static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, kbts_u16 *Base) +static void kbts__ByteSwapSequenceContextSubtable(kbts__byteswap_context *Context, kbts_u16 *Base) { if(Base[0] == 1) { - kbts_sequence_context_1 *Subst = (kbts_sequence_context_1 *)Base; - Subst->SeqRuleSetCount = kbts_ByteSwap16(Subst->SeqRuleSetCount); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->SeqRuleSetCount, Context); + kbts__sequence_context_1 *Subst = (kbts__sequence_context_1 *)Base; + Subst->SeqRuleSetCount = kbts__ByteSwap16(Subst->SeqRuleSetCount); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Subst), Subst->SeqRuleSetCount, Context); - KBTS_FOR(SetIndex, 0, Subst->SeqRuleSetCount) + KBTS__FOR(SetIndex, 0, Subst->SeqRuleSetCount) { - kbts_sequence_rule_set *Set = kbts_GetSequenceRuleSet(Subst, SetIndex); + kbts__sequence_rule_set *Set = kbts__GetSequenceRuleSet(Subst, SetIndex); - if(Set && !kbts_AlreadyVisited(Context, Set)) + if(Set && !kbts__AlreadyVisited(Context, Set)) { - Set->Count = kbts_ByteSwap16(Set->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count, Context); + Set->Count = kbts__ByteSwap16(Set->Count); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Set), Set->Count, Context); - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_sequence_rule *Rule = kbts_GetSequenceRule(Set, RuleIndex); + kbts__sequence_rule *Rule = kbts__GetSequenceRule(Set, RuleIndex); - if(!kbts_AlreadyVisited(Context, Rule)) + if(!kbts__AlreadyVisited(Context, Rule)) { - kbts_ByteSwapArray16Context(&Rule->GlyphCount, 2, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Rule), (kbts_un)Rule->GlyphCount - 1 + (kbts_un)Rule->SequenceLookupCount * 2, Context); + kbts__ByteSwapArray16Context(&Rule->GlyphCount, 2, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Rule), (kbts_un)Rule->GlyphCount - 1 + (kbts_un)Rule->SequenceLookupCount * 2, Context); } } } @@ -15096,57 +17822,57 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, } else if(Base[0] == 2) { - kbts_sequence_context_2 *Subst = (kbts_sequence_context_2 *)Base; - kbts_ByteSwapArray16Context(&Subst->ClassDefOffset, 2, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ClassSequenceRuleSetCount, Context); + kbts__sequence_context_2 *Subst = (kbts__sequence_context_2 *)Base; + kbts__ByteSwapArray16Context(&Subst->ClassDefOffset, 2, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Subst), Subst->ClassSequenceRuleSetCount, Context); - kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->ClassDefOffset); - kbts_ByteSwapClassDefinition(Context, ClassDefBase); + kbts_u16 *ClassDefBase = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->ClassDefOffset); + kbts__ByteSwapClassDefinition(Context, ClassDefBase); - KBTS_FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) + KBTS__FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) { - kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, SetIndex); + kbts__class_sequence_rule_set *Set = kbts__GetClassSequenceRuleSet(Subst, SetIndex); - if(Set && !kbts_AlreadyVisited(Context, Set)) + if(Set && !kbts__AlreadyVisited(Context, Set)) { - Set->Count = kbts_ByteSwap16(Set->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count, Context); + Set->Count = kbts__ByteSwap16(Set->Count); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Set), Set->Count, Context); - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_class_sequence_rule *Rule = kbts_GetClassSequenceRule(Set, RuleIndex); + kbts__class_sequence_rule *Rule = kbts__GetClassSequenceRule(Set, RuleIndex); - if(!kbts_AlreadyVisited(Context, Rule)) + if(!kbts__AlreadyVisited(Context, Rule)) { - kbts_ByteSwapArray16Context(&Rule->GlyphCount, 2, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Rule), Rule->GlyphCount - 1 + 2 * Rule->SequenceLookupCount, Context); + kbts__ByteSwapArray16Context(&Rule->GlyphCount, 2, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Rule), Rule->GlyphCount - 1 + 2 * Rule->SequenceLookupCount, Context); } } } } #ifdef KBTS_DUMP - kbts_DumpClassDefinition(ClassDefBase); - KBTS_FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) + kbts__DumpClassDefinition(ClassDefBase); + KBTS__FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) { - kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, SetIndex); + kbts__class_sequence_rule_set *Set = kbts__GetClassSequenceRuleSet(Subst, SetIndex); if(Set) { - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_class_sequence_rule *Rule = kbts_GetClassSequenceRule(Set, RuleIndex); - kbts_u16 *InputSequence = KBTS_POINTER_AFTER(kbts_u16, Rule); - kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); + kbts__class_sequence_rule *Rule = kbts__GetClassSequenceRule(Set, RuleIndex); + kbts_u16 *InputSequence = KBTS__POINTER_AFTER(kbts_u16, Rule); + kbts__sequence_lookup_record *Records = (kbts__sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); KBTS_DUMPF("Input: ["); - KBTS_FOR(GlyphIndex, 1, Rule->GlyphCount) + KBTS__FOR(GlyphIndex, 1, Rule->GlyphCount) { KBTS_DUMPF(", %x", InputSequence[GlyphIndex - 1]); } KBTS_DUMPF("]\nRecords: ["); - KBTS_FOR(RecordIndex, 0, Rule->SequenceLookupCount) + KBTS__FOR(RecordIndex, 0, Rule->SequenceLookupCount) { - kbts_sequence_lookup_record *Record = &Records[RecordIndex]; + kbts__sequence_lookup_record *Record = &Records[RecordIndex]; if(RecordIndex) KBTS_DUMPF(", "); KBTS_DUMPF("%u@%u", Record->LookupListIndex, Record->SequenceIndex); } @@ -15158,111 +17884,127 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, } else if(Base[0] == 3) { - kbts_sequence_context_3 *Subst = (kbts_sequence_context_3 *)Base; - kbts_ByteSwapArray16Context(&Subst->GlyphCount, 2, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->GlyphCount + 2 * Subst->SequenceLookupCount, Context); + kbts__sequence_context_3 *Subst = (kbts__sequence_context_3 *)Base; + kbts__ByteSwapArray16Context(&Subst->GlyphCount, 2, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Subst), Subst->GlyphCount + 2 * Subst->SequenceLookupCount, Context); - kbts_u16 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); - KBTS_FOR(CoverageIndex, 0, Subst->GlyphCount) + kbts_u16 *CoverageOffsets = KBTS__POINTER_AFTER(kbts_u16, Subst); + KBTS__FOR(CoverageIndex, 0, Subst->GlyphCount) { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, CoverageOffsets[CoverageIndex]); + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, CoverageOffsets[CoverageIndex]); - kbts_ByteSwapCoverage(Context, Coverage); + kbts__ByteSwapCoverage(Context, Coverage); } } } -typedef kbts_u32 kbts_skip_flags; -enum kbts_skip_flags_enum +typedef kbts_u32 kbts__skip_flags; +enum kbts__skip_flags_enum { - KBTS_SKIP_FLAG_NONE, - KBTS_SKIP_FLAG_ZWNJ = (1 << 0), - KBTS_SKIP_FLAG_ZWJ = (1 << 1), + KBTS__SKIP_FLAG_NONE, + KBTS__SKIP_FLAG_ZWNJ = (1 << 0), + KBTS__SKIP_FLAG_ZWJ = (1 << 1), }; // The Harfbuzz behavior is: // - GPOS lookups always skip ZWNJ. // - Sequence lookups always skip ZWJ. // - GSUB sequence lookups skip ZWNJ when requested. // - Regular lookups skip ZWJ when requested. -#define KBTS_SKIP_FLAGS_GSUB_REGULAR(RequestedFlags) ((RequestedFlags) & KBTS_SKIP_FLAG_ZWJ) -#define KBTS_SKIP_FLAGS_GSUB_SEQUENCE(RequestedFlags) (KBTS_SKIP_FLAG_ZWJ | ((RequestedFlags) & KBTS_SKIP_FLAG_ZWNJ)) -#define KBTS_SKIP_FLAGS_GPOS_REGULAR(RequestedFlags) (((RequestedFlags) & KBTS_SKIP_FLAG_ZWJ) | KBTS_SKIP_FLAG_ZWNJ) -#define KBTS_SKIP_FLAGS_GPOS_SEQUENCE(RequestedFlags) (KBTS_SKIP_FLAG_ZWJ | KBTS_SKIP_FLAG_ZWNJ) +#define KBTS__SKIP_FLAGS_GSUB_REGULAR(RequestedFlags) ((RequestedFlags) & KBTS__SKIP_FLAG_ZWJ) +#define KBTS__SKIP_FLAGS_GSUB_SEQUENCE(RequestedFlags) (KBTS__SKIP_FLAG_ZWJ | ((RequestedFlags) & KBTS__SKIP_FLAG_ZWNJ)) +#define KBTS__SKIP_FLAGS_GPOS_REGULAR(RequestedFlags) (((RequestedFlags) & KBTS__SKIP_FLAG_ZWJ) | KBTS__SKIP_FLAG_ZWNJ) +#define KBTS__SKIP_FLAGS_GPOS_SEQUENCE(RequestedFlags) (KBTS__SKIP_FLAG_ZWJ | KBTS__SKIP_FLAG_ZWNJ) -static kbts_skip_flags kbts_SkipFlags(kbts_feature_id FeatureId, kbts_shaper Shaper) +static kbts__skip_flags kbts__SkipFlags(kbts__feature_id FeatureId, kbts_shaper Shaper) { - kbts_skip_flags Result = 0; + kbts__skip_flags Result = 0; switch(FeatureId) { - case KBTS_FEATURE_ID_nukt: - case KBTS_FEATURE_ID_akhn: - case KBTS_FEATURE_ID_rphf: - case KBTS_FEATURE_ID_rkrf: - case KBTS_FEATURE_ID_pref: - case KBTS_FEATURE_ID_blwf: - case KBTS_FEATURE_ID_abvf: - case KBTS_FEATURE_ID_half: - case KBTS_FEATURE_ID_pstf: - case KBTS_FEATURE_ID_vatu: - case KBTS_FEATURE_ID_cjct: - case KBTS_FEATURE_ID_pres: - case KBTS_FEATURE_ID_abvs: - case KBTS_FEATURE_ID_blws: - case KBTS_FEATURE_ID_psts: - case KBTS_FEATURE_ID_haln: - Result = (Shaper == KBTS_SHAPER_INDIC) ? 0 : KBTS_SKIP_FLAG_ZWNJ | KBTS_SKIP_FLAG_ZWJ; + case KBTS__FEATURE_ID_nukt: + case KBTS__FEATURE_ID_akhn: + case KBTS__FEATURE_ID_rphf: + case KBTS__FEATURE_ID_rkrf: + case KBTS__FEATURE_ID_pref: + case KBTS__FEATURE_ID_blwf: + case KBTS__FEATURE_ID_abvf: + case KBTS__FEATURE_ID_half: + case KBTS__FEATURE_ID_pstf: + case KBTS__FEATURE_ID_vatu: + case KBTS__FEATURE_ID_cjct: + case KBTS__FEATURE_ID_pres: + case KBTS__FEATURE_ID_abvs: + case KBTS__FEATURE_ID_blws: + case KBTS__FEATURE_ID_psts: + case KBTS__FEATURE_ID_haln: + case KBTS__FEATURE_ID_cfar: + if(Shaper == KBTS_SHAPER_USE) + { + Result = KBTS__SKIP_FLAG_ZWNJ; + } + else if((Shaper == KBTS_SHAPER_INDIC) || + (Shaper == KBTS_SHAPER_KHMER)) + { + Result = 0; + } + else + { + Result = KBTS__SKIP_FLAG_ZWNJ | KBTS__SKIP_FLAG_ZWJ; + } break; - case KBTS_FEATURE_ID_init: + case KBTS__FEATURE_ID_init: if (Shaper == KBTS_SHAPER_INDIC) {Result = 0;} - else if(Shaper == KBTS_SHAPER_ARABIC) {Result = KBTS_SKIP_FLAG_ZWNJ;} - else {Result = KBTS_SKIP_FLAG_ZWNJ | KBTS_SKIP_FLAG_ZWJ;} + else if(Shaper == KBTS_SHAPER_ARABIC) {Result = KBTS__SKIP_FLAG_ZWNJ;} + else {Result = KBTS__SKIP_FLAG_ZWNJ | KBTS__SKIP_FLAG_ZWJ;} break; - case KBTS_FEATURE_ID_ccmp: - case KBTS_FEATURE_ID_locl: - case KBTS_FEATURE_ID_isol: - case KBTS_FEATURE_ID_fina: - case KBTS_FEATURE_ID_fin2: - case KBTS_FEATURE_ID_fin3: - case KBTS_FEATURE_ID_medi: - case KBTS_FEATURE_ID_med2: - case KBTS_FEATURE_ID_calt: - case KBTS_FEATURE_ID_liga: - case KBTS_FEATURE_ID_clig: - case KBTS_FEATURE_ID_rlig: - case KBTS_FEATURE_ID_mset: - Result = (Shaper == KBTS_SHAPER_ARABIC) ? KBTS_SKIP_FLAG_ZWNJ : KBTS_SKIP_FLAG_ZWNJ | KBTS_SKIP_FLAG_ZWJ; + case KBTS__FEATURE_ID_ccmp: + case KBTS__FEATURE_ID_locl: + case KBTS__FEATURE_ID_isol: + case KBTS__FEATURE_ID_fina: + case KBTS__FEATURE_ID_fin2: + case KBTS__FEATURE_ID_fin3: + case KBTS__FEATURE_ID_medi: + case KBTS__FEATURE_ID_med2: + case KBTS__FEATURE_ID_calt: + case KBTS__FEATURE_ID_liga: + case KBTS__FEATURE_ID_clig: + case KBTS__FEATURE_ID_rlig: + case KBTS__FEATURE_ID_mset: + Result = (Shaper == KBTS_SHAPER_ARABIC) ? KBTS__SKIP_FLAG_ZWNJ : KBTS__SKIP_FLAG_ZWNJ | KBTS__SKIP_FLAG_ZWJ; break; - case KBTS_FEATURE_ID_mark: - case KBTS_FEATURE_ID_mkmk: + case KBTS__FEATURE_ID_mark: + case KBTS__FEATURE_ID_mkmk: Result = 0; break; default: - Result = KBTS_SKIP_FLAG_ZWNJ | KBTS_SKIP_FLAG_ZWJ; + Result = KBTS__SKIP_FLAG_ZWNJ | KBTS__SKIP_FLAG_ZWJ; break; } return Result; } -static kbts_u32 kbts_GlyphIncludedInLookup(kbts_font *Font, int Gpos, kbts_un LookupIndex, kbts_u32 Id) +static kbts_u32 kbts__GlyphIncludedInLookup(kbts_font *Font, int Gpos, kbts_un LookupIndex, kbts_u32 Id) { kbts_u32 Result = 1; - if(Font->GlyphLookupMatrix && (Id < Font->GlyphCount)) + kbts_u32 GlyphCount = Font->Blob->GlyphCount; + if(Font->Blob->GlyphLookupMatrixOffsetFromStartOfFile && (Id < GlyphCount)) { - kbts_un FlatLookupIndex = (Gpos ? Font->GposLookupIndexOffset : 0) + LookupIndex; - kbts_un FlatIndex = FlatLookupIndex * Font->Maxp->GlyphCount + Id; + kbts_u32 *GlyphLookupMatrix = KBTS__POINTER_OFFSET(kbts_u32, Font->Blob, Font->Blob->GlyphLookupMatrixOffsetFromStartOfFile); + + kbts_un FlatLookupIndex = (Gpos ? Font->Blob->GposLookupIndexOffset : 0) + LookupIndex; + kbts_un FlatIndex = FlatLookupIndex * Font->Blob->GlyphCount + Id; kbts_un WordIndex = FlatIndex / 32; kbts_un BitIndex = FlatIndex % 32; - Result = Font->GlyphLookupMatrix[WordIndex] & (1 << BitIndex); + Result = GlyphLookupMatrix[WordIndex] & (1 << BitIndex); } return Result; } -static int kbts_GlyphPassesLookupFilter(kbts_glyph *Glyph, kbts_unpacked_lookup *Lookup) +static int kbts__GlyphPassesLookupFilter(kbts_glyph *Glyph, kbts__unpacked_lookup *Lookup) { int Result = 1; kbts_u16 Class = Glyph->Classes.Class; @@ -15272,9 +18014,9 @@ static int kbts_GlyphPassesLookupFilter(kbts_glyph *Glyph, kbts_unpacked_lookup Result = 0; } - if(Result && (Class == KBTS_GLYPH_CLASS_MARK)) + if(Result && (Class == KBTS__GLYPH_CLASS_MARK)) { - if(Lookup->Flags & KBTS_LOOKUP_FLAG_MARK_ATTACHMENT_CLASS_FILTER) + if(Lookup->Flags & KBTS__LOOKUP_FLAG_MARK_ATTACHMENT_CLASS_FILTER) { kbts_u32 DesiredMarkAttachmentClass = Lookup->Flags >> 8; @@ -15287,7 +18029,7 @@ static int kbts_GlyphPassesLookupFilter(kbts_glyph *Glyph, kbts_unpacked_lookup if(Result && Lookup->MarkFilteringSet) { // @Speed: We may want to save the result of the last mark filtering test on the glyph itself. - kbts_cover_glyph_result Cover = kbts_CoverGlyph(Lookup->MarkFilteringSet, Glyph->Id); + kbts__cover_glyph_result Cover = kbts__CoverGlyph(Lookup->MarkFilteringSet, Glyph->Id); if(!Cover.Valid) { Result = 0; @@ -15298,108 +18040,131 @@ static int kbts_GlyphPassesLookupFilter(kbts_glyph *Glyph, kbts_unpacked_lookup return Result; } -static int kbts_SkipGlyph(kbts_glyph *Glyph, kbts_unpacked_lookup *Lookup, kbts_skip_flags SkipFlags, kbts_u32 SkipUnicodeFlags) +static int kbts__SkipGlyph(kbts_glyph *Glyph, kbts__unpacked_lookup *Lookup, kbts__skip_flags SkipFlags, kbts_u32 SkipUnicodeFlags) { int Result = (Glyph->UnicodeFlags & SkipUnicodeFlags) || - ((SkipFlags & KBTS_SKIP_FLAG_ZWNJ) && (Glyph->Codepoint == 0x200C)) || - ((SkipFlags & KBTS_SKIP_FLAG_ZWJ) && (Glyph->Codepoint == 0x200D)) || - !kbts_GlyphPassesLookupFilter(Glyph, Lookup); + ((SkipFlags & KBTS__SKIP_FLAG_ZWNJ) && (Glyph->Codepoint == 0x200C)) || + ((SkipFlags & KBTS__SKIP_FLAG_ZWJ) && (Glyph->Codepoint == 0x200D)) || + !kbts__GlyphPassesLookupFilter(Glyph, Lookup); return Result; } -static int kbts_GlyphsIncludedInLookupSubtable(kbts_font *Font, int Gpos, kbts_unpacked_lookup *Lookup, kbts_un LookupIndex, kbts_un SubtableIndex, kbts_glyph_array *Array, kbts_un CurrentGlyphIndex, kbts_skip_flags SkipFlags, kbts_unicode_flags SkipUnicodeFlags) +static int kbts__GlyphIsValid(kbts_glyph_storage *Storage, kbts_glyph *Glyph) { - if(Font->GlyphLookupSubtableMatrix) + int Result = Glyph != &Storage->GlyphSentinel; + return Result; +} + +typedef struct kbts__matrix_index +{ + kbts_un WordIndex; + kbts_un BitIndex; +} kbts__matrix_index; + +static kbts__matrix_index kbts__GlyphLookupMatrixIndex(kbts_un LookupIndex, kbts_un GlyphIndex, kbts_un GlyphCount) +{ + kbts_un FlatIndex = LookupIndex * GlyphCount + GlyphIndex; + + kbts__matrix_index Result = KBTS__ZERO; + Result.WordIndex = FlatIndex / 32; + Result.BitIndex = FlatIndex % 32; + + return Result; +} + +static kbts__matrix_index kbts__GlyphLookupSubtableMatrixIndex(kbts_un SubtableIndex, kbts_un SubtableCount, kbts_un GlyphIndex) +{ + // Since we try many subtables in a row against the same glyphs, the rows are glyph-wise and the columns subtable-wise. + kbts_un FlatIndex = GlyphIndex * SubtableCount + SubtableIndex; + + kbts__matrix_index Result = KBTS__ZERO; + Result.WordIndex = FlatIndex / 32; + Result.BitIndex = FlatIndex % 32; + return Result; +} + +static int kbts__GlyphsIncludedInLookupSubtable(kbts_glyph_storage *Storage, kbts_font *Font, int Gpos, kbts__unpacked_lookup *Lookup, kbts_un LookupIndex, kbts_un SubtableIndex, kbts_glyph *AtGlyph, kbts__skip_flags SkipFlags, kbts_unicode_flags SkipUnicodeFlags) +{ + if(Font->Blob->GlyphLookupSubtableMatrixOffsetFromStartOfFile) { - kbts_un FlatLookupIndex = (Gpos ? Font->GposLookupIndexOffset : 0) + LookupIndex; - kbts_un FlatSubtableIndex = Font->LookupSubtableIndexOffsets[FlatLookupIndex] + SubtableIndex; - kbts_un MatrixRowOffset = FlatSubtableIndex * Font->Maxp->GlyphCount; - kbts_lookup_subtable_info *Info = &Font->SubtableInfos[FlatSubtableIndex]; + kbts_u32 *GlyphLookupSubtableMatrix = KBTS__POINTER_OFFSET(kbts_u32, Font->Blob, Font->Blob->GlyphLookupSubtableMatrixOffsetFromStartOfFile); + kbts_u32 *LookupSubtableIndexOffsets = KBTS__POINTER_OFFSET(kbts_u32, Font->Blob, Font->Blob->LookupSubtableIndexOffsetsOffsetFromStartOfFile); + kbts_lookup_subtable_info *SubtableInfos = KBTS__POINTER_OFFSET(kbts_lookup_subtable_info, Font->Blob, Font->Blob->SubtableInfosOffsetFromStartOfFile); + kbts_un GlyphCount = Font->Blob->GlyphCount; + kbts_un SubtableCount = Font->Blob->LookupSubtableCount; + + kbts_un FlatLookupIndex = (Gpos ? Font->Blob->GposLookupIndexOffset : 0) + LookupIndex; + kbts_un FlatSubtableIndex = LookupSubtableIndexOffsets[FlatLookupIndex] + SubtableIndex; + kbts_lookup_subtable_info *Info = &SubtableInfos[FlatSubtableIndex]; kbts_un MinimumBacktrack = (Info->MinimumBacktrackPlusOne) ? Info->MinimumBacktrackPlusOne - 1 : 0; kbts_un MinimumFollowup = (Info->MinimumFollowupPlusOne) ? Info->MinimumFollowupPlusOne - 1 : 0; - kbts_un GlyphCount = Font->GlyphCount; - - if((MinimumBacktrack <= CurrentGlyphIndex) && (MinimumFollowup <= (Array->Count - CurrentGlyphIndex))) - { - { // Check the current glyph. - kbts_un Id = Array->Glyphs[CurrentGlyphIndex].Id; - kbts_un FlatIndex = MatrixRowOffset + Id; - kbts_un WordIndex = FlatIndex / 32; - kbts_un BitIndex = FlatIndex % 32; - if(Id >= GlyphCount) - { - return 1; - } - else if(!(Font->GlyphLookupSubtableMatrix[WordIndex] & (1 << BitIndex))) - { - return 0; - } - } + { // Check the current glyph. + kbts_un Id = AtGlyph->Id; + kbts__matrix_index MatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(FlatSubtableIndex, SubtableCount, Id); + if(Id >= GlyphCount) { - kbts_un BacktrackCounter = 0; - kbts_glyph *BacktrackGlyph = &Array->Glyphs[CurrentGlyphIndex - 1]; - while((BacktrackGlyph >= Array->Glyphs) && (BacktrackCounter < MinimumBacktrack)) - { - kbts_un FlatIndex = MatrixRowOffset + BacktrackGlyph->Id; - kbts_un WordIndex = FlatIndex / 32; - kbts_un BitIndex = FlatIndex % 32; - if(BacktrackGlyph->Id >= GlyphCount) - { - return 1; - } - else if(Font->GlyphLookupSubtableMatrix[WordIndex] & (1 << BitIndex)) - { - BacktrackCounter += 1; - } - else if(!kbts_SkipGlyph(BacktrackGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) - { - return 0; - } - - BacktrackGlyph -= 1; - } - - if(BacktrackCounter < MinimumBacktrack) - { - return 0; - } + return 1; } - + else if(!(GlyphLookupSubtableMatrix[MatrixIndex.WordIndex] & (1 << MatrixIndex.BitIndex))) { - kbts_un LookaheadCounter = 0; - kbts_glyph *LookaheadGlyph = &Array->Glyphs[CurrentGlyphIndex + 1]; - kbts_glyph *OnePastLastGlyph = Array->Glyphs + Array->Count; - while((LookaheadGlyph < OnePastLastGlyph) && (LookaheadCounter < MinimumFollowup)) - { - kbts_un FlatIndex = MatrixRowOffset + LookaheadGlyph->Id; - kbts_un WordIndex = FlatIndex / 32; - kbts_un BitIndex = FlatIndex % 32; - if(LookaheadGlyph->Id >= GlyphCount) - { - return 1; - } - else if(Font->GlyphLookupSubtableMatrix[WordIndex] & (1 << BitIndex)) - { - LookaheadCounter += 1; - } - else if(!kbts_SkipGlyph(LookaheadGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) - { - return 0; - } - - LookaheadGlyph += 1; - } - - if(LookaheadCounter < MinimumFollowup) - { - return 0; - } + return 0; } } - else + { - return 0; + kbts_un BacktrackCounter = 0; + kbts_glyph *BacktrackGlyph = AtGlyph->Prev; + while(kbts__GlyphIsValid(Storage, BacktrackGlyph) && + (BacktrackCounter < MinimumBacktrack)) + { + kbts__matrix_index MatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(FlatSubtableIndex, SubtableCount, BacktrackGlyph->Id); + + if((BacktrackGlyph->Id >= GlyphCount) || + kbts__SkipGlyph(BacktrackGlyph, Lookup, SkipFlags, SkipUnicodeFlags) || + GlyphLookupSubtableMatrix[MatrixIndex.WordIndex] & (1 << MatrixIndex.BitIndex)) + { + BacktrackCounter += 1; + } + else + { + return 0; + } + + BacktrackGlyph = BacktrackGlyph->Prev; + } + + if(BacktrackCounter < MinimumBacktrack) + { + return 0; + } + } + + { + kbts_un LookaheadCounter = 0; + kbts_glyph *LookaheadGlyph = AtGlyph->Next; + while(kbts__GlyphIsValid(Storage, LookaheadGlyph) && (LookaheadCounter < MinimumFollowup)) + { + kbts__matrix_index MatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(FlatSubtableIndex, SubtableCount, LookaheadGlyph->Id); + + if((LookaheadGlyph->Id >= GlyphCount) || + kbts__SkipGlyph(LookaheadGlyph, Lookup, SkipFlags, SkipUnicodeFlags) || + GlyphLookupSubtableMatrix[MatrixIndex.WordIndex] & (1 << MatrixIndex.BitIndex)) + { + LookaheadCounter += 1; + } + else + { + return 0; + } + + LookaheadGlyph = LookaheadGlyph->Next; + } + + if(LookaheadCounter < MinimumFollowup) + { + return 0; + } } } @@ -15407,23 +18172,23 @@ static int kbts_GlyphsIncludedInLookupSubtable(kbts_font *Font, int Gpos, kbts_u } # ifdef KBTS_DUMP -static void kbts_DumpCoverage(kbts_coverage *Coverage) +static void kbts__DumpCoverage(kbts__coverage *Coverage) { KBTS_DUMPF("["); if(Coverage->Format == 1) { - kbts_u16 *GlyphIds = KBTS_POINTER_AFTER(kbts_u16, Coverage); - KBTS_FOR(GlyphIndex, 0, Coverage->Count) + kbts_u16 *GlyphIds = KBTS__POINTER_AFTER(kbts_u16, Coverage); + KBTS__FOR(GlyphIndex, 0, Coverage->Count) { KBTS_DUMPF("%x,", GlyphIds[GlyphIndex]); } } else if(Coverage->Format == 2) { - kbts_range_record *Ranges = KBTS_POINTER_AFTER(kbts_range_record, Coverage); - KBTS_FOR(RangeIndex, 0, Coverage->Count) + kbts__range_record *Ranges = KBTS__POINTER_AFTER(kbts__range_record, Coverage); + KBTS__FOR(RangeIndex, 0, Coverage->Count) { - kbts_range_record *Range = &Ranges[RangeIndex]; + kbts__range_record *Range = &Ranges[RangeIndex]; KBTS_DUMPF("%x..%x @ %u,", Range->StartGlyphId, Range->EndGlyphId, Range->StartCoverageIndex); } } @@ -15431,81 +18196,81 @@ static void kbts_DumpCoverage(kbts_coverage *Coverage) } # endif -static void kbts_ByteSwapChainedSequenceContextSubtable(kbts_byteswap_context *Context, kbts_u16 *Base) +static void kbts__ByteSwapChainedSequenceContextSubtable(kbts__byteswap_context *Context, kbts_u16 *Base) { if(Base[0] == 1) { - kbts_chained_sequence_context_1 *Subst = (kbts_chained_sequence_context_1 *)Base; - Subst->ChainedSequenceRuleSetCount = kbts_ByteSwap16(Subst->ChainedSequenceRuleSetCount); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ChainedSequenceRuleSetCount, Context); + kbts__chained_sequence_context_1 *Subst = (kbts__chained_sequence_context_1 *)Base; + Subst->ChainedSequenceRuleSetCount = kbts__ByteSwap16(Subst->ChainedSequenceRuleSetCount); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Subst), Subst->ChainedSequenceRuleSetCount, Context); - KBTS_FOR(SetIndex, 0, Subst->ChainedSequenceRuleSetCount) + KBTS__FOR(SetIndex, 0, Subst->ChainedSequenceRuleSetCount) { - kbts_ByteSwapChainedSequenceRuleSet(Context, kbts_GetChainedSequenceRuleSet(Subst, SetIndex)); + kbts__ByteSwapChainedSequenceRuleSet(Context, kbts__GetChainedSequenceRuleSet(Subst, SetIndex)); } } else if(Base[0] == 2) { - kbts_chained_sequence_context_2 *Subst = (kbts_chained_sequence_context_2 *)Base; - kbts_ByteSwapArray16Context(&Subst->BacktrackClassDefOffset, 4, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ChainedClassSequenceRuleSetCount, Context); + kbts__chained_sequence_context_2 *Subst = (kbts__chained_sequence_context_2 *)Base; + kbts__ByteSwapArray16Context(&Subst->BacktrackClassDefOffset, 4, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Subst), Subst->ChainedClassSequenceRuleSetCount, Context); - kbts_u16 *BacktrackClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->BacktrackClassDefOffset); - kbts_ByteSwapClassDefinition(Context, BacktrackClassDefinition); + kbts_u16 *BacktrackClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->BacktrackClassDefOffset); + kbts__ByteSwapClassDefinition(Context, BacktrackClassDefinition); - kbts_u16 *InputClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->InputClassDefOffset); - kbts_ByteSwapClassDefinition(Context, InputClassDefinition); + kbts_u16 *InputClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->InputClassDefOffset); + kbts__ByteSwapClassDefinition(Context, InputClassDefinition); - kbts_u16 *LookaheadClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->LookaheadClassDefOffset); - kbts_ByteSwapClassDefinition(Context, LookaheadClassDefinition); + kbts_u16 *LookaheadClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->LookaheadClassDefOffset); + kbts__ByteSwapClassDefinition(Context, LookaheadClassDefinition); #ifdef KBTS_DUMP KBTS_DUMPF("Backtrack classes:\n"); - // kbts_DumpClassDefinition(BacktrackClassDefinition); + // kbts__DumpClassDefinition(BacktrackClassDefinition); KBTS_DUMPF("Input classes:\n"); - // kbts_DumpClassDefinition(InputClassDefinition); + // kbts__DumpClassDefinition(InputClassDefinition); KBTS_DUMPF("Lookahead classes:\n"); - // kbts_DumpClassDefinition(LookaheadClassDefinition); + // kbts__DumpClassDefinition(LookaheadClassDefinition); #endif - KBTS_FOR(SetIndex, 0, Subst->ChainedClassSequenceRuleSetCount) + KBTS__FOR(SetIndex, 0, Subst->ChainedClassSequenceRuleSetCount) { - kbts_chained_sequence_rule_set *Set = kbts_GetChainedClassSequenceRuleSet(Subst, SetIndex); - kbts_ByteSwapChainedSequenceRuleSet(Context, Set); + kbts__chained_sequence_rule_set *Set = kbts__GetChainedClassSequenceRuleSet(Subst, SetIndex); + kbts__ByteSwapChainedSequenceRuleSet(Context, Set); #ifdef KBTS_DUMP if(Set) { - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_chained_sequence_rule *Rule = kbts_GetChainedClassSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); + kbts__chained_sequence_rule *Rule = kbts__GetChainedClassSequenceRule(Set, RuleIndex); + kbts__unpacked_chained_sequence_rule Unpacked = kbts__UnpackChainedSequenceRule(Rule, 0); KBTS_DUMPF("Backtrack: ["); - KBTS_FOR(BacktrackIndex, 0, Unpacked.BacktrackCount) + KBTS__FOR(BacktrackIndex, 0, Unpacked.BacktrackCount) { if(BacktrackIndex) KBTS_DUMPF(", "); KBTS_DUMPF("%u", Unpacked.Backtrack[BacktrackIndex]); } KBTS_DUMPF("]\n" "Input: ["); - KBTS_FOR(InputIndex, 1, Unpacked.InputCount) + KBTS__FOR(InputIndex, 1, Unpacked.InputCount) { if(InputIndex) KBTS_DUMPF(", "); KBTS_DUMPF("%u", Unpacked.Input[InputIndex - 1]); } KBTS_DUMPF("]\n" "Lookahead: ["); - KBTS_FOR(LookaheadIndex, 0, Unpacked.LookaheadCount) + KBTS__FOR(LookaheadIndex, 0, Unpacked.LookaheadCount) { if(LookaheadIndex) KBTS_DUMPF(", "); KBTS_DUMPF("%u", Unpacked.Lookahead[LookaheadIndex]); } KBTS_DUMPF("]\n" "Records: ["); - KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) + KBTS__FOR(RecordIndex, 0, Unpacked.RecordCount) { - kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; + kbts__sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; if(RecordIndex) KBTS_DUMPF(", "); KBTS_DUMPF("%u@%u", Record->LookupListIndex, Record->SequenceIndex); } @@ -15517,53 +18282,53 @@ static void kbts_ByteSwapChainedSequenceContextSubtable(kbts_byteswap_context *C } else if(Base[0] == 3) { - kbts_chained_sequence_context_3 *Subst = (kbts_chained_sequence_context_3 *)Base; - kbts_unpacked_chained_sequence_context_3 Unpacked = kbts_UnpackChainedSequenceContext3(Subst, 1); + kbts__chained_sequence_context_3 *Subst = (kbts__chained_sequence_context_3 *)Base; + kbts__unpacked_chained_sequence_context_3 Unpacked = kbts__UnpackChainedSequenceContext3(Subst, 1); kbts_un U16Count = Unpacked.BacktrackCount + Unpacked.InputCount + Unpacked.LookaheadCount + Unpacked.RecordCount * 2 + 4; - kbts_ByteSwapArray16Context(&Subst->BacktrackGlyphCount, U16Count, Context); + kbts__ByteSwapArray16Context(&Subst->BacktrackGlyphCount, U16Count, Context); - KBTS_FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) + KBTS__FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackCoverageIndex]); + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackCoverageIndex]); - kbts_ByteSwapCoverage(Context, Coverage); + kbts__ByteSwapCoverage(Context, Coverage); } - KBTS_FOR(InputCoverageIndex, 0, Unpacked.InputCount) + KBTS__FOR(InputCoverageIndex, 0, Unpacked.InputCount) { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.InputCoverageOffsets[InputCoverageIndex]); + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.InputCoverageOffsets[InputCoverageIndex]); - kbts_ByteSwapCoverage(Context, Coverage); + kbts__ByteSwapCoverage(Context, Coverage); } - KBTS_FOR(LookaheadCoverageIndex, 0, Unpacked.LookaheadCount) + KBTS__FOR(LookaheadCoverageIndex, 0, Unpacked.LookaheadCount) { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadCoverageIndex]); + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadCoverageIndex]); - kbts_ByteSwapCoverage(Context, Coverage); + kbts__ByteSwapCoverage(Context, Coverage); } # ifdef KBTS_DUMP KBTS_DUMPF(" Backtrack: "); - KBTS_FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) + KBTS__FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) { - kbts_DumpCoverage(KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackCoverageIndex])); + kbts__DumpCoverage(KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackCoverageIndex])); } KBTS_DUMPF("\n Input: "); - KBTS_FOR(InputCoverageIndex, 0, Unpacked.InputCount) + KBTS__FOR(InputCoverageIndex, 0, Unpacked.InputCount) { - kbts_DumpCoverage(KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.InputCoverageOffsets[InputCoverageIndex])); + kbts__DumpCoverage(KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.InputCoverageOffsets[InputCoverageIndex])); } KBTS_DUMPF("\n Lookahead: "); - KBTS_FOR(LookaheadCoverageIndex, 0, Unpacked.LookaheadCount) + KBTS__FOR(LookaheadCoverageIndex, 0, Unpacked.LookaheadCount) { - kbts_DumpCoverage(KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadCoverageIndex])); + kbts__DumpCoverage(KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadCoverageIndex])); } KBTS_DUMPF("\n Lookups: ["); - KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) + KBTS__FOR(RecordIndex, 0, Unpacked.RecordCount) { - kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; + kbts__sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; KBTS_DUMPF("%u@%u,", Record->LookupListIndex, Record->SequenceIndex); } @@ -15572,58 +18337,58 @@ static void kbts_ByteSwapChainedSequenceContextSubtable(kbts_byteswap_context *C } } -static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts_u16 LookupType, kbts_u16 *Base) +static void kbts__ByteSwapGsubLookupSubtable(kbts__byteswap_context *Context, kbts_u16 LookupType, kbts_u16 *Base) { - int Swap = !kbts_AlreadyVisited(Context, Base); + int Swap = !kbts__AlreadyVisited(Context, Base); while(Swap && (LookupType == 7)) { - kbts_extension *Subst = (kbts_extension *)Base; - Subst->Format = kbts_ByteSwap16(Subst->Format); - Subst->LookupType = kbts_ByteSwap16(Subst->LookupType); - Subst->Offset = kbts_ByteSwap32(Subst->Offset); + kbts__extension *Subst = (kbts__extension *)Base; + Subst->Format = kbts__ByteSwap16(Subst->Format); + Subst->LookupType = kbts__ByteSwap16(Subst->LookupType); + Subst->Offset = kbts__ByteSwap32(Subst->Offset); KBTS_DUMPF(" Type 7.1\n" " Offset %zu\n" " -> %zu\n", (char *)Base - Context->FileBase, (char *)Subst + Subst->Offset - Context->FileBase); - Base = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->Offset); + Base = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->Offset); LookupType = Subst->LookupType; - Swap = !kbts_AlreadyVisited(Context, Base); + Swap = !kbts__AlreadyVisited(Context, Base); } if(Swap) { - *Base = kbts_ByteSwap16(*Base); + *Base = kbts__ByteSwap16(*Base); KBTS_DUMPF(" Type %u.%u\n" " Offset %zu\n", LookupType, *Base, (char *)Base - Context->FileBase); - if(kbts_GsubLookupBeginsWithCoverage(LookupType, Base[0])) + if(kbts__GsubLookupBeginsWithCoverage(LookupType, Base[0])) { - Base[1] = kbts_ByteSwap16(Base[1]); - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Base, Base[1]); + Base[1] = kbts__ByteSwap16(Base[1]); + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Base, Base[1]); - kbts_ByteSwapCoverage(Context, Coverage); + kbts__ByteSwapCoverage(Context, Coverage); # ifdef KBTS_DUMP KBTS_DUMPF(" Coverage %u\n", Coverage->Format); if(Coverage->Format == 1) { - kbts_u16 *GlyphIds = KBTS_POINTER_AFTER(kbts_u16, Coverage); - KBTS_FOR(GlyphIndex, 0, Coverage->Count) + kbts_u16 *GlyphIds = KBTS__POINTER_AFTER(kbts_u16, Coverage); + KBTS__FOR(GlyphIndex, 0, Coverage->Count) { KBTS_DUMPF(" %x\n", GlyphIds[GlyphIndex]); } } else if(Coverage->Format == 2) { - kbts_range_record *Ranges = KBTS_POINTER_AFTER(kbts_range_record, Coverage); - KBTS_FOR(RangeIndex, 0, Coverage->Count) + kbts__range_record *Ranges = KBTS__POINTER_AFTER(kbts__range_record, Coverage); + KBTS__FOR(RangeIndex, 0, Coverage->Count) { - kbts_range_record Range = Ranges[RangeIndex]; + kbts__range_record Range = Ranges[RangeIndex]; KBTS_DUMPF(" %x..%x@%u..%u\n", Range.StartGlyphId, Range.EndGlyphId, Range.StartCoverageIndex, Range.StartCoverageIndex + (Range.EndGlyphId - Range.StartGlyphId)); } } @@ -15634,8 +18399,8 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts { case 1: { - kbts_single_substitution *Subst = (kbts_single_substitution *)Base; - Subst->DeltaOrCount.GlyphCount = kbts_ByteSwap16(Subst->DeltaOrCount.GlyphCount); + kbts__single_substitution *Subst = (kbts__single_substitution *)Base; + Subst->DeltaOrCount.GlyphCount = kbts__ByteSwap16(Subst->DeltaOrCount.GlyphCount); # ifdef KBTS_DUMP if(Subst->Format == 1) @@ -15646,12 +18411,12 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts if(Subst->Format == 2) { - kbts_u16 *GlyphIds = KBTS_POINTER_AFTER(kbts_u16, Subst); - kbts_ByteSwapArray16Context(GlyphIds, Subst->DeltaOrCount.GlyphCount, Context); + kbts_u16 *GlyphIds = KBTS__POINTER_AFTER(kbts_u16, Subst); + kbts__ByteSwapArray16Context(GlyphIds, Subst->DeltaOrCount.GlyphCount, Context); #ifdef KBTS_DUMP KBTS_DUMPF(" ["); - KBTS_FOR(IdIndex, 0, Subst->DeltaOrCount.GlyphCount) + KBTS__FOR(IdIndex, 0, Subst->DeltaOrCount.GlyphCount) { if(IdIndex) KBTS_DUMPF(" "); KBTS_DUMPF("%x", GlyphIds[IdIndex]); @@ -15664,25 +18429,25 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts case 2: { - kbts_multiple_substitution *Subst = (kbts_multiple_substitution *)Base; - Subst->SequenceCount = kbts_ByteSwap16(Subst->SequenceCount); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->SequenceCount, Context); + kbts__multiple_substitution *Subst = (kbts__multiple_substitution *)Base; + Subst->SequenceCount = kbts__ByteSwap16(Subst->SequenceCount); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Subst), Subst->SequenceCount, Context); - KBTS_FOR(SequenceIndex, 0, Subst->SequenceCount) + KBTS__FOR(SequenceIndex, 0, Subst->SequenceCount) { - kbts_sequence *Sequence = kbts_GetSequence(Subst, SequenceIndex); + kbts__sequence *Sequence = kbts__GetSequence(Subst, SequenceIndex); - if(!kbts_AlreadyVisited(Context, Sequence)) + if(!kbts__AlreadyVisited(Context, Sequence)) { - Sequence->GlyphCount = kbts_ByteSwap16(Sequence->GlyphCount); + Sequence->GlyphCount = kbts__ByteSwap16(Sequence->GlyphCount); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Sequence), Sequence->GlyphCount, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Sequence), Sequence->GlyphCount, Context); } #ifdef KBTS_DUMP KBTS_DUMPF(" ["); - kbts_u16 *SequenceGlyphIds = KBTS_POINTER_AFTER(kbts_u16, Sequence); - KBTS_FOR(SequenceGlyphIndex, 0, Sequence->GlyphCount) + kbts_u16 *SequenceGlyphIds = KBTS__POINTER_AFTER(kbts_u16, Sequence); + KBTS__FOR(SequenceGlyphIndex, 0, Sequence->GlyphCount) { if(SequenceGlyphIndex) KBTS_DUMPF(" "); KBTS_DUMPF("%x", SequenceGlyphIds[SequenceGlyphIndex]); @@ -15695,25 +18460,25 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts case 3: { - kbts_alternate_substitution *Subst = (kbts_alternate_substitution *)Base; - Subst->AlternateSetCount = kbts_ByteSwap16(Subst->AlternateSetCount); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->AlternateSetCount, Context); + kbts__alternate_substitution *Subst = (kbts__alternate_substitution *)Base; + Subst->AlternateSetCount = kbts__ByteSwap16(Subst->AlternateSetCount); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Subst), Subst->AlternateSetCount, Context); - KBTS_FOR(SetIndex, 0, Subst->AlternateSetCount) + KBTS__FOR(SetIndex, 0, Subst->AlternateSetCount) { - kbts_alternate_set *Set = kbts_GetAlternateSet(Subst, SetIndex); + kbts__alternate_set *Set = kbts__GetAlternateSet(Subst, SetIndex); - if(!kbts_AlreadyVisited(Context, Set)) + if(!kbts__AlreadyVisited(Context, Set)) { - Set->GlyphCount = kbts_ByteSwap16(Set->GlyphCount); + Set->GlyphCount = kbts__ByteSwap16(Set->GlyphCount); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->GlyphCount, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Set), Set->GlyphCount, Context); } #ifdef KBTS_DUMP KBTS_DUMPF(" ["); - kbts_u16 *SetGlyphIds = KBTS_POINTER_AFTER(kbts_u16, Set); - KBTS_FOR(SetGlyphIndex, 0, Set->GlyphCount) + kbts_u16 *SetGlyphIds = KBTS__POINTER_AFTER(kbts_u16, Set); + KBTS__FOR(SetGlyphIndex, 0, Set->GlyphCount) { if(SetGlyphIndex) KBTS_DUMPF(" "); KBTS_DUMPF("%x", SetGlyphIds[SetGlyphIndex]); @@ -15726,32 +18491,32 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts case 4: { - kbts_ligature_substitution *Subst = (kbts_ligature_substitution *)Base; - Subst->LigatureSetCount = kbts_ByteSwap16(Subst->LigatureSetCount); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->LigatureSetCount, Context); + kbts__ligature_substitution *Subst = (kbts__ligature_substitution *)Base; + Subst->LigatureSetCount = kbts__ByteSwap16(Subst->LigatureSetCount); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Subst), Subst->LigatureSetCount, Context); - KBTS_FOR(SetIndex, 0, Subst->LigatureSetCount) + KBTS__FOR(SetIndex, 0, Subst->LigatureSetCount) { - kbts_ligature_set *Set = kbts_GetLigatureSet(Subst, SetIndex); + kbts__ligature_set *Set = kbts__GetLigatureSet(Subst, SetIndex); - if(!kbts_AlreadyVisited(Context, Set)) + if(!kbts__AlreadyVisited(Context, Set)) { - Set->Count = kbts_ByteSwap16(Set->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count, Context); + Set->Count = kbts__ByteSwap16(Set->Count); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Set), Set->Count, Context); - KBTS_FOR(LigatureIndex, 0, Set->Count) + KBTS__FOR(LigatureIndex, 0, Set->Count) { - kbts_ligature *Ligature = kbts_GetLigature(Set, LigatureIndex); + kbts__ligature *Ligature = kbts__GetLigature(Set, LigatureIndex); - if(!kbts_AlreadyVisited(Context, Ligature)) + if(!kbts__AlreadyVisited(Context, Ligature)) { - kbts_ByteSwapArray16Context(&Ligature->Glyph, 2, Context); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Ligature), Ligature->ComponentCount - 1, Context); + kbts__ByteSwapArray16Context(&Ligature->Glyph, 2, Context); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Ligature), Ligature->ComponentCount - 1, Context); # ifdef KBTS_DUMP KBTS_DUMPF("ligature: ["); - kbts_u16 *ComponentIds = KBTS_POINTER_AFTER(kbts_u16, Ligature); - KBTS_FOR(ComponentIndex, 1, Ligature->ComponentCount) + kbts_u16 *ComponentIds = KBTS__POINTER_AFTER(kbts_u16, Ligature); + KBTS__FOR(ComponentIndex, 1, Ligature->ComponentCount) { KBTS_DUMPF("%x,", ComponentIds[ComponentIndex - 1]); } @@ -15770,31 +18535,31 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts case 5: { - kbts_ByteSwapSequenceContextSubtable(Context, Base); + kbts__ByteSwapSequenceContextSubtable(Context, Base); } break; case 6: { - kbts_ByteSwapChainedSequenceContextSubtable(Context, Base); + kbts__ByteSwapChainedSequenceContextSubtable(Context, Base); } break; case 8: { - kbts_reverse_chain_substitution *Subst = (kbts_reverse_chain_substitution *)Base; - kbts_unpacked_reverse_chain_substitution Unpacked = kbts_UnpackReverseChainSubstitution(Subst, 1); + kbts__reverse_chain_substitution *Subst = (kbts__reverse_chain_substitution *)Base; + kbts__unpacked_reverse_chain_substitution Unpacked = kbts__UnpackReverseChainSubstitution(Subst, 1); kbts_un U16Count = Unpacked.BacktrackCount + Unpacked.GlyphCount + Unpacked.LookaheadCount + 3; - kbts_ByteSwapArray16Context(&Subst->BacktrackGlyphCount, U16Count, Context); + kbts__ByteSwapArray16Context(&Subst->BacktrackGlyphCount, U16Count, Context); - KBTS_FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) + KBTS__FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) { - kbts_ByteSwapCoverage(Context, KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackCoverageIndex])); + kbts__ByteSwapCoverage(Context, KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackCoverageIndex])); } - KBTS_FOR(LookaheadCoverageIndex, 0, Unpacked.LookaheadCount) + KBTS__FOR(LookaheadCoverageIndex, 0, Unpacked.LookaheadCount) { - kbts_ByteSwapCoverage(Context, KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadCoverageIndex])); + kbts__ByteSwapCoverage(Context, KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadCoverageIndex])); } } break; @@ -15802,43 +18567,43 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts } } -static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts_lookup_list *LookupList, kbts_u16 LookupType, kbts_u16 *Base) +static void kbts__ByteSwapGposLookupSubtable(kbts__byteswap_context *Context, kbts_lookup_list *LookupList, kbts_u16 LookupType, kbts_u16 *Base) { - if(!kbts_AlreadyVisited(Context, Base)) + if(!kbts__AlreadyVisited(Context, Base)) { - *Base = kbts_ByteSwap16(*Base); + *Base = kbts__ByteSwap16(*Base); KBTS_DUMPF(" GPOS subtable %u.%u\n", LookupType, *Base); - if(kbts_GposLookupBeginsWithCoverage(LookupType, *Base)) + if(kbts__GposLookupBeginsWithCoverage(LookupType, *Base)) { kbts_u16 *CoverageOffset = &Base[1]; - *CoverageOffset = kbts_ByteSwap16(*CoverageOffset); + *CoverageOffset = kbts__ByteSwap16(*CoverageOffset); - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Base, *CoverageOffset); - kbts_ByteSwapCoverage(Context, Coverage); + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Base, *CoverageOffset); + kbts__ByteSwapCoverage(Context, Coverage); } switch(LookupType) { case 1: { - kbts_single_adjustment_1 *Adjust = (kbts_single_adjustment_1 *)Base; - Adjust->ValueFormat = kbts_ByteSwap16(Adjust->ValueFormat); + kbts__single_adjustment_1 *Adjust = (kbts__single_adjustment_1 *)Base; + Adjust->ValueFormat = kbts__ByteSwap16(Adjust->ValueFormat); if(Adjust->Format == 1) { - kbts_ByteSwapValueRecord(Context, Adjust, Adjust->ValueFormat, KBTS_POINTER_AFTER(kbts_u16, Adjust)); + kbts_ByteSwapValueRecord(Context, Adjust, Adjust->ValueFormat, KBTS__POINTER_AFTER(kbts_u16, Adjust)); } else if(Adjust->Format == 2) { - kbts_single_adjustment_2 *Adjust2 = (kbts_single_adjustment_2 *)Base; - Adjust2->RecordCount = kbts_ByteSwap16(Adjust2->RecordCount); + kbts__single_adjustment_2 *Adjust2 = (kbts__single_adjustment_2 *)Base; + Adjust2->RecordCount = kbts__ByteSwap16(Adjust2->RecordCount); - kbts_u16 *At = KBTS_POINTER_AFTER(kbts_u16, Adjust2); - KBTS_FOR(RecordIndex, 0, Adjust2->RecordCount) + kbts_u16 *At = KBTS__POINTER_AFTER(kbts_u16, Adjust2); + KBTS__FOR(RecordIndex, 0, Adjust2->RecordCount) { - kbts_unpacked_value_record Unpacked = kbts_ByteSwapValueRecord(Context, Adjust2, Adjust2->ValueFormat, At); + kbts__unpacked_value_record Unpacked = kbts_ByteSwapValueRecord(Context, Adjust2, Adjust2->ValueFormat, At); At += Unpacked.Size; } @@ -15850,55 +18615,55 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts { if(*Base == 1) { - kbts_pair_adjustment_1 *Adjust = (kbts_pair_adjustment_1 *)Base; - kbts_ByteSwapArray16Context(&Adjust->ValueFormat1, 3, Context); + kbts__pair_adjustment_1 *Adjust = (kbts__pair_adjustment_1 *)Base; + kbts__ByteSwapArray16Context(&Adjust->ValueFormat1, 3, Context); - kbts_u16 *SetOffsets = KBTS_POINTER_AFTER(kbts_u16, Adjust); - kbts_ByteSwapArray16Context(SetOffsets, Adjust->SetCount, Context); + kbts_u16 *SetOffsets = KBTS__POINTER_AFTER(kbts_u16, Adjust); + kbts__ByteSwapArray16Context(SetOffsets, Adjust->SetCount, Context); - kbts_un Size1 = kbts_PopCount32(Adjust->ValueFormat1); - kbts_un Size2 = kbts_PopCount32(Adjust->ValueFormat2); + kbts_un Size1 = kbts__PopCount32(Adjust->ValueFormat1); + kbts_un Size2 = kbts__PopCount32(Adjust->ValueFormat2); kbts_un PairRecordSize = Size1 + Size2 + 1; - KBTS_FOR(SetIndex, 0, Adjust->SetCount) + KBTS__FOR(SetIndex, 0, Adjust->SetCount) { - kbts_pair_set *Set = KBTS_POINTER_OFFSET(kbts_pair_set, Adjust, SetOffsets[SetIndex]); + kbts__pair_set *Set = KBTS__POINTER_OFFSET(kbts__pair_set, Adjust, SetOffsets[SetIndex]); - if(!kbts_AlreadyVisited(Context, Set)) + if(!kbts__AlreadyVisited(Context, Set)) { - Set->Count = kbts_ByteSwap16(Set->Count); + Set->Count = kbts__ByteSwap16(Set->Count); - kbts_u16 *At = KBTS_POINTER_AFTER(kbts_u16, Set); + kbts_u16 *At = KBTS__POINTER_AFTER(kbts_u16, Set); - KBTS_FOR(RecordIndex, 0, Set->Count) + KBTS__FOR(RecordIndex, 0, Set->Count) { - kbts_pair_value_record *PairRecord = (kbts_pair_value_record *)(At + RecordIndex * PairRecordSize); + kbts__pair_value_record *PairRecord = (kbts__pair_value_record *)(At + RecordIndex * PairRecordSize); - PairRecord->SecondGlyph = kbts_ByteSwap16(PairRecord->SecondGlyph); - kbts_u16 *Record = KBTS_POINTER_AFTER(kbts_u16, PairRecord); + PairRecord->SecondGlyph = kbts__ByteSwap16(PairRecord->SecondGlyph); + kbts_u16 *Record = KBTS__POINTER_AFTER(kbts_u16, PairRecord); - kbts_unpacked_value_record Unpacked1 = kbts_ByteSwapValueRecord(Context, Record, Adjust->ValueFormat1, Record); + kbts__unpacked_value_record Unpacked1 = kbts_ByteSwapValueRecord(Context, Set, Adjust->ValueFormat1, Record); Record += Unpacked1.Size; - kbts_ByteSwapValueRecord(Context, Record, Adjust->ValueFormat2, Record); + kbts_ByteSwapValueRecord(Context, Set, Adjust->ValueFormat2, Record); } } } } else if(*Base == 2) { - kbts_pair_adjustment_2 *Adjust = (kbts_pair_adjustment_2 *)Base; - kbts_ByteSwapArray16Context(&Adjust->ValueFormat1, 6, Context); + kbts__pair_adjustment_2 *Adjust = (kbts__pair_adjustment_2 *)Base; + kbts__ByteSwapArray16Context(&Adjust->ValueFormat1, 6, Context); - kbts_ByteSwapClassDefinition(Context, KBTS_POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition1Offset)); - kbts_ByteSwapClassDefinition(Context, KBTS_POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition2Offset)); + kbts__ByteSwapClassDefinition(Context, KBTS__POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition1Offset)); + kbts__ByteSwapClassDefinition(Context, KBTS__POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition2Offset)); - kbts_u16 *Records = KBTS_POINTER_AFTER(kbts_u16, Adjust); + kbts_u16 *Records = KBTS__POINTER_AFTER(kbts_u16, Adjust); - kbts_un Size1 = kbts_PopCount32(Adjust->ValueFormat1); - kbts_un Size2 = kbts_PopCount32(Adjust->ValueFormat2); + kbts_un Size1 = kbts__PopCount32(Adjust->ValueFormat1); + kbts_un Size2 = kbts__PopCount32(Adjust->ValueFormat2); kbts_u16 *RecordPair = Records; - KBTS_FOR(RecordIndex, 0, (kbts_un)Adjust->Class1Count * (kbts_un)Adjust->Class2Count) + KBTS__FOR(RecordIndex, 0, (kbts_un)Adjust->Class1Count * (kbts_un)Adjust->Class2Count) { kbts_ByteSwapValueRecord(Context, Adjust, Adjust->ValueFormat1, RecordPair); RecordPair += Size1; @@ -15912,23 +18677,23 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts case 3: { - kbts_cursive_attachment *Adjust = (kbts_cursive_attachment *)Base; - Adjust->EntryExitCount = kbts_ByteSwap16(Adjust->EntryExitCount); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Adjust), Adjust->EntryExitCount * 2, Context); + kbts__cursive_attachment *Adjust = (kbts__cursive_attachment *)Base; + Adjust->EntryExitCount = kbts__ByteSwap16(Adjust->EntryExitCount); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, Adjust), Adjust->EntryExitCount * 2, Context); - kbts_entry_exit *EntryExits = KBTS_POINTER_AFTER(kbts_entry_exit, Adjust); - KBTS_FOR(EntryExitIndex, 0, Adjust->EntryExitCount) + kbts__entry_exit *EntryExits = KBTS__POINTER_AFTER(kbts__entry_exit, Adjust); + KBTS__FOR(EntryExitIndex, 0, Adjust->EntryExitCount) { - kbts_entry_exit *EntryExit = &EntryExits[EntryExitIndex]; + kbts__entry_exit *EntryExit = &EntryExits[EntryExitIndex]; if(EntryExit->EntryAnchorOffset) { - kbts_ByteSwapAnchor(Context, KBTS_POINTER_OFFSET(kbts_anchor, Adjust, EntryExit->EntryAnchorOffset)); + kbts__ByteSwapAnchor(Context, KBTS__POINTER_OFFSET(kbts__anchor, Adjust, EntryExit->EntryAnchorOffset)); } if(EntryExit->ExitAnchorOffset) { - kbts_ByteSwapAnchor(Context, KBTS_POINTER_OFFSET(kbts_anchor, Adjust, EntryExit->ExitAnchorOffset)); + kbts__ByteSwapAnchor(Context, KBTS__POINTER_OFFSET(kbts__anchor, Adjust, EntryExit->ExitAnchorOffset)); } } } @@ -15937,47 +18702,47 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts case 4: case 6: { - kbts_mark_to_base_attachment *Adjust = (kbts_mark_to_base_attachment *)Base; - kbts_ByteSwapArray16Context(&Adjust->BaseCoverageOffset, 4, Context); + kbts__mark_to_base_attachment *Adjust = (kbts__mark_to_base_attachment *)Base; + kbts__ByteSwapArray16Context(&Adjust->BaseCoverageOffset, 4, Context); - kbts_ByteSwapCoverage(Context, KBTS_POINTER_OFFSET(kbts_coverage, Adjust, Adjust->BaseCoverageOffset)); - kbts_ByteSwapMarkArray(Context, KBTS_POINTER_OFFSET(kbts_mark_array, Adjust, Adjust->MarkArrayOffset)); - kbts_ByteSwapBaseArray(Context, Adjust->MarkClassCount, KBTS_POINTER_OFFSET(kbts_base_array, Adjust, Adjust->BaseArrayOffset)); + kbts__ByteSwapCoverage(Context, KBTS__POINTER_OFFSET(kbts__coverage, Adjust, Adjust->BaseCoverageOffset)); + kbts__ByteSwapMarkArray(Context, KBTS__POINTER_OFFSET(kbts__mark_array, Adjust, Adjust->MarkArrayOffset)); + kbts__ByteSwapBaseArray(Context, Adjust->MarkClassCount, KBTS__POINTER_OFFSET(kbts__base_array, Adjust, Adjust->BaseArrayOffset)); } break; case 5: { - kbts_mark_to_ligature_attachment *Adjust = (kbts_mark_to_ligature_attachment *)Base; - kbts_ByteSwapArray16Context(&Adjust->LigatureCoverageOffset, 4, Context); + kbts__mark_to_ligature_attachment *Adjust = (kbts__mark_to_ligature_attachment *)Base; + kbts__ByteSwapArray16Context(&Adjust->LigatureCoverageOffset, 4, Context); - kbts_ByteSwapCoverage(Context, KBTS_POINTER_OFFSET(kbts_coverage, Adjust, Adjust->LigatureCoverageOffset)); - kbts_ByteSwapMarkArray(Context, KBTS_POINTER_OFFSET(kbts_mark_array, Adjust, Adjust->MarkArrayOffset)); + kbts__ByteSwapCoverage(Context, KBTS__POINTER_OFFSET(kbts__coverage, Adjust, Adjust->LigatureCoverageOffset)); + kbts__ByteSwapMarkArray(Context, KBTS__POINTER_OFFSET(kbts__mark_array, Adjust, Adjust->MarkArrayOffset)); - kbts_ligature_array *LigatureArray = KBTS_POINTER_OFFSET(kbts_ligature_array, Adjust, Adjust->LigatureArrayOffset); - if(!kbts_AlreadyVisited(Context, LigatureArray)) + kbts__ligature_array *LigatureArray = KBTS__POINTER_OFFSET(kbts__ligature_array, Adjust, Adjust->LigatureArrayOffset); + if(!kbts__AlreadyVisited(Context, LigatureArray)) { - LigatureArray->Count = kbts_ByteSwap16(LigatureArray->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, LigatureArray), LigatureArray->Count, Context); + LigatureArray->Count = kbts__ByteSwap16(LigatureArray->Count); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, LigatureArray), LigatureArray->Count, Context); - KBTS_FOR(AttachIndex, 0, LigatureArray->Count) + KBTS__FOR(AttachIndex, 0, LigatureArray->Count) { - kbts_ligature_attach *Attach = kbts_GetLigatureAttach(LigatureArray, AttachIndex); + kbts__ligature_attach *Attach = kbts__GetLigatureAttach(LigatureArray, AttachIndex); - if(!kbts_AlreadyVisited(Context, Attach)) + if(!kbts__AlreadyVisited(Context, Attach)) { - Attach->Count = kbts_ByteSwap16(Attach->Count); + Attach->Count = kbts__ByteSwap16(Attach->Count); - kbts_u16 *AttachAnchorOffsets = KBTS_POINTER_AFTER(kbts_u16, Attach); - kbts_ByteSwapArray16Context(AttachAnchorOffsets, Attach->Count * Adjust->MarkClassCount, Context); + kbts_u16 *AttachAnchorOffsets = KBTS__POINTER_AFTER(kbts_u16, Attach); + kbts__ByteSwapArray16Context(AttachAnchorOffsets, Attach->Count * Adjust->MarkClassCount, Context); - KBTS_FOR(ComponentIndex, 0, Attach->Count) + KBTS__FOR(ComponentIndex, 0, Attach->Count) { - KBTS_FOR(MarkClass, 0, Adjust->MarkClassCount) + KBTS__FOR(MarkClass, 0, Adjust->MarkClassCount) { - kbts_anchor *Anchor = kbts_GetLigatureAttachAnchor(Adjust, Attach, (kbts_u16)MarkClass, ComponentIndex); + kbts__anchor *Anchor = kbts__GetLigatureAttachAnchor(Adjust, Attach, (kbts_u16)MarkClass, ComponentIndex); - kbts_ByteSwapAnchor(Context, Anchor); + kbts__ByteSwapAnchor(Context, Anchor); } } } @@ -15988,101 +18753,85 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts case 7: { - kbts_ByteSwapSequenceContextSubtable(Context, Base); + kbts__ByteSwapSequenceContextSubtable(Context, Base); } break; case 8: { - kbts_ByteSwapChainedSequenceContextSubtable(Context, Base); + kbts__ByteSwapChainedSequenceContextSubtable(Context, Base); } break; case 9: { // @Cleanup: Replace recursion with a loop at the start of this function! - kbts_extension *Adjust = (kbts_extension *)Base; + kbts__extension *Adjust = (kbts__extension *)Base; - Adjust->LookupType = kbts_ByteSwap16(Adjust->LookupType); - Adjust->Offset = kbts_ByteSwap32(Adjust->Offset); + Adjust->LookupType = kbts__ByteSwap16(Adjust->LookupType); + Adjust->Offset = kbts__ByteSwap32(Adjust->Offset); - kbts_ByteSwapGposLookupSubtable(Context, LookupList, Adjust->LookupType, KBTS_POINTER_OFFSET(kbts_u16, Adjust, Adjust->Offset)); + kbts__ByteSwapGposLookupSubtable(Context, LookupList, Adjust->LookupType, KBTS__POINTER_OFFSET(kbts_u16, Adjust, Adjust->Offset)); } break; } } } -static kbts_glyph_classes kbts_GlyphClasses(kbts_font *Font, kbts_u32 Id) +static void *kbts__BlobTableData(kbts_blob_header *Header, kbts_blob_table_id TableId) { - kbts_glyph_classes Result = KBTS_ZERO; + void *Result = 0; + kbts_blob_table *Table = &Header->Tables[TableId]; + + if(Table->Length) + { + Result = KBTS__POINTER_OFFSET(void, Header, Table->OffsetFromStartOfFile); + } + + return Result; +} +#define kbts__BlobTableDataType(Header, TableId, Type) (Type *)kbts__BlobTableData((Header), (TableId)) + +static kbts_glyph_classes kbts__GlyphClasses(kbts_font *Font, kbts_u32 Id) +{ + kbts_glyph_classes Result = KBTS__ZERO; // Look up all glyph classes. - kbts_gdef *Gdef = Font->Gdef; + kbts__gdef *Gdef = kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_GDEF, kbts__gdef); if(Gdef) { if(Gdef->ClassDefinitionOffset) { - kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->ClassDefinitionOffset); - Result.Class = kbts_GlyphClassFromTable(ClassDefBase, Id).Class; + kbts_u16 *ClassDefBase = KBTS__POINTER_OFFSET(kbts_u16, Gdef, Gdef->ClassDefinitionOffset); + Result.Class = kbts__GlyphClassFromTable(ClassDefBase, Id).Class; } - if(Gdef->MarkAttachmentClassDefinitionOffset && (Result.Class == KBTS_GLYPH_CLASS_MARK)) + if(Gdef->MarkAttachmentClassDefinitionOffset && (Result.Class == KBTS__GLYPH_CLASS_MARK)) { - kbts_u16 *MarkAttachmentClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->MarkAttachmentClassDefinitionOffset); - Result.MarkAttachmentClass = kbts_GlyphClassFromTable(MarkAttachmentClassDefBase, Id).Class; + kbts_u16 *MarkAttachmentClassDefBase = KBTS__POINTER_OFFSET(kbts_u16, Gdef, Gdef->MarkAttachmentClassDefinitionOffset); + Result.MarkAttachmentClass = kbts__GlyphClassFromTable(MarkAttachmentClassDefBase, Id).Class; } } return Result; } -static int kbts_ScriptIsWeak(kbts_script Script) +static int kbts__ScriptIsWeak(kbts_script Script) { int Result = (Script == KBTS_SCRIPT_DONT_KNOW) || (Script == KBTS_SCRIPT_DEFAULT) || (Script == KBTS_SCRIPT_DEFAULT2); return Result; } -static int kbts_ShaperRtl(kbts_shaper Shaper) +static int kbts__ShaperRtl(kbts_shaper Shaper) { int Result = (Shaper == KBTS_SHAPER_ARABIC) || (Shaper == KBTS_SHAPER_HEBREW); return Result; } -// Easy shorthand for determining scripts in simple cases. -// Do not ship this! You should use script breaks instead. -KBTS_EXPORT void kbts_InferScript(kbts_direction *Direction, kbts_script *Script, kbts_script GlyphScript) +KBTS_EXPORT int kbts_CodepointToGlyphId(kbts_font *Font, int ICodepoint) { - if(Script) - { - if(kbts_ScriptIsWeak(*Script) && !kbts_ScriptIsWeak(GlyphScript)) - { - *Script = GlyphScript; - if(Direction && (!*Direction)) - { - kbts_script_properties *Properties = &kbts_ScriptProperties[GlyphScript]; - *Direction = kbts_ShaperRtl(Properties->Shaper) ? KBTS_DIRECTION_RTL : KBTS_DIRECTION_LTR; - } - } - } -} - -KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint) -{ - kbts_glyph Result = KBTS_ZERO; - Result.Codepoint = Codepoint; - - // Look up Unicode properties. - Result.Decomposition = kbts_GetUnicodeDecomposition(Codepoint); - Result.JoiningType = kbts_GetUnicodeJoiningType(Codepoint); - Result.UnicodeFlags = kbts_GetUnicodeFlags(Codepoint); - kbts_u16 SyllabicInfo = kbts_GetUnicodeSyllabicInfo(Codepoint); - Result.SyllabicClass = kbts_GetSyllabicClass(SyllabicInfo); - Result.SyllabicPosition = kbts_GetSyllabicPosition(SyllabicInfo); - Result.CombiningClass = kbts_GetUnicodeCombiningClass(Codepoint); - Result.UseClass = kbts_GetUnicodeUseClass(Codepoint); - Result.Script = kbts_GetUnicodeScript(Codepoint); - Result.ParentInfo = kbts_GetUnicodeParentInfo(Codepoint); + int Result = 0; + kbts_u32 Codepoint = (kbts_u32)ICodepoint; kbts_u16 *CmapBase = Font->Cmap; if(CmapBase) @@ -16091,18 +18840,18 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint { case 0: { - kbts_cmap_0 *Cmap0 = (kbts_cmap_0 *)CmapBase; + kbts__cmap_0 *Cmap0 = (kbts__cmap_0 *)CmapBase; - if((kbts_un)Codepoint < KBTS_ARRAY_LENGTH(Cmap0->GlyphIdArray)) + if((kbts_un)Codepoint < KBTS__ARRAY_LENGTH(Cmap0->GlyphIdArray)) { - Result.Id = Cmap0->GlyphIdArray[Codepoint]; + Result = Cmap0->GlyphIdArray[Codepoint]; } } break; case 2: { - kbts_cmap_2 *Cmap2 = (kbts_cmap_2 *)CmapBase; - kbts_sub_header *SubHeaders = KBTS_POINTER_AFTER(kbts_sub_header, Cmap2); + kbts__cmap_2 *Cmap2 = (kbts__cmap_2 *)CmapBase; + kbts__sub_header *SubHeaders = KBTS__POINTER_AFTER(kbts__sub_header, Cmap2); kbts_u32 High = (Codepoint >> 8) & 0xFF; @@ -16114,7 +18863,7 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint // The Microsoft documentation doesn't mention that the SubHeaderKeys are indices multiplied by 8, for some // reason..! The Apple documentation does. kbts_u16 SubHeaderIndex = Cmap2->SubHeaderKeys[High] / 8; - kbts_sub_header *SubHeader = &SubHeaders[SubHeaderIndex]; + kbts__sub_header *SubHeader = &SubHeaders[SubHeaderIndex]; if(!SubHeaderIndex) { @@ -16129,22 +18878,22 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint kbts_u32 Offset = Codepoint - SubHeader->FirstCode; if(Offset < SubHeader->EntryCount) { - kbts_u16 *GlyphIds = KBTS_POINTER_OFFSET(kbts_u16, &SubHeader->IdRangeOffset, SubHeader->IdRangeOffset); + kbts_u16 *GlyphIds = KBTS__POINTER_OFFSET(kbts_u16, &SubHeader->IdRangeOffset, SubHeader->IdRangeOffset); kbts_u16 GlyphId = GlyphIds[Offset]; if(GlyphId) { GlyphId += SubHeader->IdDelta; } - Result.Id = GlyphId; + Result = GlyphId; } } break; case 4: { - kbts_cmap_4 *Cmap4 = (kbts_cmap_4 *)CmapBase; + kbts__cmap_4 *Cmap4 = (kbts__cmap_4 *)CmapBase; kbts_un SegmentCount = Cmap4->SegmentCountTimesTwo / 2; - kbts_u16 *EndCodes = KBTS_POINTER_AFTER(kbts_u16, Cmap4); + kbts_u16 *EndCodes = KBTS__POINTER_AFTER(kbts_u16, Cmap4); kbts_u16 *StartCodes = EndCodes + SegmentCount + 1; kbts_s16 *IdDeltas = (kbts_s16 *)(StartCodes + SegmentCount); kbts_u16 *IdRangeOffsets = (kbts_u16 *)(IdDeltas + SegmentCount); @@ -16175,26 +18924,26 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint { GlyphId += (kbts_u16)(Codepoint); } - Result.Id = GlyphId; + Result = GlyphId; } } break; case 6: { - kbts_cmap_6 *Cmap6 = (kbts_cmap_6 *)CmapBase; - kbts_u16 *GlyphIds = KBTS_POINTER_AFTER(kbts_u16, Cmap6); + kbts__cmap_6 *Cmap6 = (kbts__cmap_6 *)CmapBase; + kbts_u16 *GlyphIds = KBTS__POINTER_AFTER(kbts_u16, Cmap6); kbts_un Offset = Codepoint - Cmap6->FirstCode; if(Offset < Cmap6->EntryCount) { - Result.Id = GlyphIds[Offset]; + Result = GlyphIds[Offset]; } } break; case 12: { - kbts_cmap_12_13 *Cmap12 = (kbts_cmap_12_13 *)CmapBase; - kbts_sequential_map_group *Groups = KBTS_POINTER_AFTER(kbts_sequential_map_group, Cmap12); + kbts__cmap_12_13 *Cmap12 = (kbts__cmap_12_13 *)CmapBase; + kbts__sequential_map_group *Groups = KBTS__POINTER_AFTER(kbts__sequential_map_group, Cmap12); kbts_un GlyphId = 0; kbts_un GroupCount = Cmap12->GroupCount; @@ -16214,62 +18963,89 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint GlyphId = Groups->StartGlyphId + Offset; } - Result.Id = (kbts_u16)GlyphId; + Result = (int)GlyphId; } break; } } - if(Font->Gdef) - { - Result.Classes = kbts_GlyphClasses(Font, Result.Id); - } - else - { - // @Cleanup: This is garbage compatibility-with-broken-fonts code. I would very much like to get rid of it. - if((Result.UnicodeFlags & KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE) || !(Result.UnicodeFlags & KBTS_UNICODE_FLAG_NON_SPACING_MARK)) - { - Result.Classes.Class = KBTS_GLYPH_CLASS_BASE; - } - else - { - Result.Classes.Class = KBTS_GLYPH_CLASS_MARK; - } - } - return Result; } -typedef struct kbts_iterate_features +KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, int ICodepoint, kbts_glyph_config *Config, int UserId) { - kbts_gsub_gpos *Header; - kbts_feature_list *FeatureList; - // @Incomplete - // kbts_feature_variations *FeatureVariations; - kbts_langsys *Langsys; + kbts_u32 Codepoint = (kbts_u32)ICodepoint; - kbts_feature_set EnabledFeatures; + kbts_glyph Result = KBTS__ZERO; + Result.Codepoint = Codepoint; + Result.Config = Config; + Result.UserIdOrCodepointIndex = UserId; + + // Look up Unicode properties. + Result.Decomposition = kbts__GetUnicodeDecomposition(Codepoint); + Result.JoiningType = kbts__GetUnicodeJoiningType(Codepoint); + Result.UnicodeFlags = kbts__GetUnicodeFlags(Codepoint); + kbts_u16 SyllabicInfo = kbts__GetUnicodeSyllabicInfo(Codepoint); + Result.SyllabicClass = kbts__GetSyllabicClass(SyllabicInfo); + Result.SyllabicPosition = kbts__GetSyllabicPosition(SyllabicInfo); + Result.CombiningClass = kbts__GetUnicodeCombiningClass(Codepoint); + Result.UseClass = kbts__GetUnicodeUseClass(Codepoint); + Result.ParentInfo = kbts__GetUnicodeParentInfo(Codepoint); + + Result.Id = (kbts_u16)kbts_CodepointToGlyphId(Font, ICodepoint); + + if(Font->Blob->Tables[KBTS_BLOB_TABLE_ID_GDEF].Length) + { + Result.Classes = kbts__GlyphClasses(Font, Result.Id); + } + else + { + // @Cleanup: This is garbage compatibility-with-broken-fonts code. I would very much like to get rid of it. + if((Result.UnicodeFlags & KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE) || !(Result.UnicodeFlags & KBTS_UNICODE_FLAG_NON_SPACING_MARK)) + { + Result.Classes.Class = KBTS__GLYPH_CLASS_BASE; + } + else + { + Result.Classes.Class = KBTS__GLYPH_CLASS_MARK; + } + } + + return Result; +} + +typedef struct kbts__iterate_features +{ + kbts__gsub_gpos *Header; + kbts__feature_list *FeatureList; + // @Incomplete + // kbts__feature_variations *FeatureVariations; + kbts__langsys *Langsys; + + kbts__feature_set EnabledFeatures; kbts_u32 CurrentFeatureTag; kbts_u32 CurrentFeatureFlag; kbts_u32 FeatureIndex; - kbts_feature *Feature; -} kbts_iterate_features; + kbts__feature *Feature; +} kbts__iterate_features; -static kbts_iterate_features kbts_IterateFeatures(kbts_shape_config *Config, kbts_shaping_table ShapingTable, kbts_feature_set EnabledFeatures) +static kbts__iterate_features kbts__IterateFeatures(kbts_shape_config *Config, kbts_shaping_table ShapingTable, kbts__feature_set EnabledFeatures) { - kbts_iterate_features Result = KBTS_ZERO; + kbts__iterate_features Result = KBTS__ZERO; - kbts_gsub_gpos *Header = Config->Font->ShapingTables[ShapingTable]; + kbts_blob_table_id TableId = (ShapingTable == KBTS_SHAPING_TABLE_GSUB) ? KBTS_BLOB_TABLE_ID_GSUB : KBTS_BLOB_TABLE_ID_GPOS; + + kbts__gsub_gpos *Header = kbts__BlobTableDataType(Config->Font->Blob, TableId, kbts__gsub_gpos); if(Header) { // @Incomplete // if(Header->Minor == 1) // { - // Result.FeatureVariations = KBTS_POINTER_OFFSET(kbts_feature_variations, Header, Header->FeatureVariationsOffset); + // Result.FeatureVariations = KBTS__POINTER_OFFSET(kbts__feature_variations, Header, Header->FeatureVariationsOffset); // } - Result.FeatureList = KBTS_POINTER_OFFSET(kbts_feature_list, Header, Header->FeatureListOffset); + Result.FeatureList = KBTS__POINTER_OFFSET(kbts__feature_list, Header, Header->FeatureListOffset); Result.Header = Header; Result.Langsys = Config->Langsys[ShapingTable]; Result.EnabledFeatures = EnabledFeatures; @@ -16278,37 +19054,37 @@ static kbts_iterate_features kbts_IterateFeatures(kbts_shape_config *Config, kbt return Result; } -static kbts_u32 kbts_IsValidFeatureIteration(kbts_iterate_features *It) +static int kbts__IsValidFeatureIteration(kbts__iterate_features *It) { - kbts_u32 Result = It->Langsys != 0; + int Result = It->Langsys != 0; return Result; } -static kbts_u32 kbts_NextFeature(kbts_iterate_features *It) +static int kbts__NextFeature(kbts__iterate_features *It) { - kbts_u32 Result = 0; + int Result = 0; It->Feature = 0; - if(kbts_IsValidFeatureIteration(It)) + if(kbts__IsValidFeatureIteration(It)) { - kbts_u16 *FeatureIndices = KBTS_POINTER_AFTER(kbts_u16, It->Langsys); + kbts_u16 *FeatureIndices = KBTS__POINTER_AFTER(kbts_u16, It->Langsys); // @Incomplete - // kbts_feature_variations *FeatureVariations = It->FeatureVariations; + // kbts__feature_variations *FeatureVariations = It->FeatureVariations; while(It->FeatureIndex < It->Langsys->FeatureIndexCount) { - kbts_feature_pointer Feature = kbts_GetFeature(It->FeatureList, FeatureIndices[It->FeatureIndex]); + kbts__feature_pointer Feature = kbts__GetFeature(It->FeatureList, FeatureIndices[It->FeatureIndex]); // We might need to swap out this feature with another. // Check for variations. // @Incomplete //if(FeatureVariations) //{ - // KBTS_FOR(VariationIndex, 0, FeatureVariations->RecordCount) + // KBTS__FOR(VariationIndex, 0, FeatureVariations->RecordCount) // { - // kbts_feature_variation_pointer Variation = kbts_GetFeatureVariation(FeatureVariations, VariationIndex); - // KBTS_FOR(ConditionIndex, 0, Variation.ConditionSet->Count) + // kbts__feature_variation_pointer Variation = kbts__GetFeatureVariation(FeatureVariations, VariationIndex); + // KBTS__FOR(ConditionIndex, 0, Variation.ConditionSet->Count) // { - // kbts_condition_1 *Condition = kbts_GetCondition(Variation.ConditionSet, ConditionIndex); + // kbts__condition_1 *Condition = kbts__GetCondition(Variation.ConditionSet, ConditionIndex); // KBTS_ASSERT(0); // } // } @@ -16316,14 +19092,14 @@ static kbts_u32 kbts_NextFeature(kbts_iterate_features *It) It->FeatureIndex += 1; - kbts_u32 FeatureId = kbts_FeatureTagToId(Feature.Tag); - if(kbts_ContainsFeature(&It->EnabledFeatures, FeatureId)) + kbts_u32 FeatureId = kbts__FeatureTagToId(Feature.Tag); + if(kbts__ContainsFeature(&It->EnabledFeatures, FeatureId)) { It->Feature = Feature.Feature; It->CurrentFeatureTag = Feature.Tag; if(FeatureId && (FeatureId <= 32)) { - It->CurrentFeatureFlag = (1 << (FeatureId - 1)) & KBTS_GLYPH_FEATURE_MASK; + It->CurrentFeatureFlag = (1 << (FeatureId - 1)) & KBTS__GLYPH_FEATURE_MASK; } Result = 1; @@ -16335,37 +19111,37 @@ static kbts_u32 kbts_NextFeature(kbts_iterate_features *It) return Result; } -typedef struct kbts_iterate_lookups +typedef struct kbts__iterate_lookups { kbts_lookup_list *LookupList; - kbts_feature *Feature; + kbts__feature *Feature; - kbts_lookup *Lookup; + kbts__lookup *Lookup; kbts_u16 LookupIndex; kbts_u32 LookupIndexIndex; -} kbts_iterate_lookups; +} kbts__iterate_lookups; -static kbts_iterate_lookups kbts_IterateLookups(kbts_lookup_list *List, kbts_feature *Feature) +static kbts__iterate_lookups kbts__IterateLookups(kbts_lookup_list *List, kbts__feature *Feature) { - kbts_iterate_lookups Result = KBTS_ZERO; + kbts__iterate_lookups Result = KBTS__ZERO; Result.LookupList = List; Result.Feature = Feature; return Result; } -static kbts_u32 kbts_NextLookup(kbts_iterate_lookups *It) +static int kbts__NextLookup(kbts__iterate_lookups *It) { - kbts_u32 Result = 0; - kbts_feature *Feature = It->Feature; + int Result = 0; + kbts__feature *Feature = It->Feature; if(It->LookupList && Feature && (It->LookupIndexIndex < Feature->LookupIndexCount)) { - kbts_u16 *LookupIndices = KBTS_POINTER_AFTER(kbts_u16, Feature); + kbts_u16 *LookupIndices = KBTS__POINTER_AFTER(kbts_u16, Feature); It->LookupIndex = LookupIndices[It->LookupIndexIndex]; - It->Lookup = kbts_GetLookup(It->LookupList, It->LookupIndex); + It->Lookup = kbts__GetLookup(It->LookupList, It->LookupIndex); Result = 1; It->LookupIndexIndex += 1; @@ -16374,522 +19150,581 @@ static kbts_u32 kbts_NextLookup(kbts_iterate_lookups *It) return Result; } -KBTS_INLINE void kbts_GsubMutate(kbts_font *Font, kbts_glyph *Glyph, kbts_u16 NewId, kbts_u32 Flags) +KBTS_INLINE void kbts__GsubMutate(kbts_font *Font, kbts_glyph *Glyph, kbts_u16 NewId, kbts_u32 Flags) { Glyph->Id = NewId; - Glyph->Classes = kbts_GlyphClasses(Font, NewId); + Glyph->Classes = kbts__GlyphClasses(Font, NewId); Glyph->Flags = (Glyph->Flags & ~KBTS_GLYPH_FLAG_FIRST_IN_MULTIPLE_SUBSTITUTION) | Flags; } -typedef struct kbts_do_single_substitution_result +static kbts__op_list *kbts__ShaperOpLists[KBTS_SHAPER_COUNT] = { + /* DEFAULT, */ &kbts__OpList_Default, + /* ARABIC, */ &kbts__OpList_ArabicRclt, + /* HANGUL, */ &kbts__OpList_Hangul, + /* HEBREW, */ &kbts__OpList_Default, + /* INDIC, */ &kbts__OpList_Indic, + /* KHMER, */ &kbts__OpList_Khmer, + /* MYANMAR, */ &kbts__OpList_Myanmar, + /* TIBETAN, */ &kbts__OpList_Tibetan, + /* USE, */ &kbts__OpList_Use, +}; + +typedef struct kbts__gsub_frame { - kbts_u32 ConsumedGlyphCount; - kbts_u32 WrittenGlyphCount; - kbts_u32 PerformedSubstitution; -} kbts_do_single_substitution_result; + kbts_glyph *InputGlyph; -static kbts_feature_set kbts_ShaperFeatures[] = { - /* KBTS_SHAPER_DEFAULT */ {{KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs) | - KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | - KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG0(calt), - 0, - KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(liga) | - KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | - KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk), - KBTS_FEATURE_FLAG3(rvrn)}}, - /* KBTS_SHAPER_ARABIC */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | - KBTS_FEATURE_FLAG0(isol) | KBTS_FEATURE_FLAG0(fina) | KBTS_FEATURE_FLAG0(fin2) | KBTS_FEATURE_FLAG0(fin3) | - KBTS_FEATURE_FLAG0(medi) | KBTS_FEATURE_FLAG0(med2) | KBTS_FEATURE_FLAG0(init) | KBTS_FEATURE_FLAG0(calt) | - KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG2(mset) | KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | - KBTS_FEATURE_FLAG0(curs), - 0, - KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(liga) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(mark) | - KBTS_FEATURE_FLAG2(mkmk) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | KBTS_FEATURE_FLAG2(locl), - KBTS_FEATURE_FLAG3(rvrn) | KBTS_FEATURE_FLAG3(rtlm) | KBTS_FEATURE_FLAG3(stch) | KBTS_FEATURE_FLAG3(rtla)}}, - /* KBTS_SHAPER_HANGUL */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ljmo) | - KBTS_FEATURE_FLAG0(vjmo) | KBTS_FEATURE_FLAG0(tjmo) | KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | - KBTS_FEATURE_FLAG0(ccmp) | KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG0(curs), - 0, - KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | - KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk) | - KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(liga), - KBTS_FEATURE_FLAG3(rvrn)}}, - /* KBTS_SHAPER_HEBREW */ {{KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs) | - KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | - KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG0(calt), - 0, - KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(liga) | - KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | - KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk), - KBTS_FEATURE_FLAG3(rvrn)}}, // (Same as DEFAULT) - /* KBTS_SHAPER_INDIC */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(akhn) | - KBTS_FEATURE_FLAG0(rphf) | KBTS_FEATURE_FLAG0(pref) | KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(abvf) | - KBTS_FEATURE_FLAG0(half) | KBTS_FEATURE_FLAG0(pstf) | KBTS_FEATURE_FLAG0(cjct) | KBTS_FEATURE_FLAG0(abvs) | - KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(init) | KBTS_FEATURE_FLAG0(calt) | KBTS_FEATURE_FLAG0(clig) | - KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs), - 0, - KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(nukt) | KBTS_FEATURE_FLAG2(rkrf) | - KBTS_FEATURE_FLAG2(pres) | KBTS_FEATURE_FLAG2(psts) | KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(rlig) | - KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(haln) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(mark) | - KBTS_FEATURE_FLAG2(mkmk) | KBTS_FEATURE_FLAG2(kern), - KBTS_FEATURE_FLAG3(rvrn) | KBTS_FEATURE_FLAG3(vatu),}}, - /* KBTS_SHAPER_KHMER */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | - KBTS_FEATURE_FLAG0(pref) | KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(abvf) | KBTS_FEATURE_FLAG0(pstf) | - KBTS_FEATURE_FLAG0(cfar) | KBTS_FEATURE_FLAG0(abvs) | KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(calt) | - KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs), - 0, - KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk) | - KBTS_FEATURE_FLAG2(pres) | KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(locl) | - KBTS_FEATURE_FLAG2(psts) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(rlig), - KBTS_FEATURE_FLAG3(rvrn)}}, - /* KBTS_SHAPER_MYANMAR */ {{KBTS_FEATURE_FLAG0(rphf) | KBTS_FEATURE_FLAG0(pref) | KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(pstf) | - KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | - KBTS_FEATURE_FLAG0(abvs) | KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(calt) | KBTS_FEATURE_FLAG0(clig) | - KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs), - 0, - KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk) | KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | - KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(psts) | KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(pres) | - KBTS_FEATURE_FLAG2(liga) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern), - KBTS_FEATURE_FLAG3(rvrn)}}, - /* KBTS_SHAPER_TIBETAN */ {{KBTS_FEATURE_FLAG0(ccmp) | KBTS_FEATURE_FLAG0(abvs) | KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(calt) | - KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm), - 0, - KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(liga) | KBTS_FEATURE_FLAG2(kern) | KBTS_FEATURE_FLAG2(mkmk), - 0}}, - /* KBTS_SHAPER_USE */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | - KBTS_FEATURE_FLAG0(akhn) | KBTS_FEATURE_FLAG0(rphf) | KBTS_FEATURE_FLAG0(pref) | KBTS_FEATURE_FLAG0(abvf) | - KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(cjct) | KBTS_FEATURE_FLAG0(half) | KBTS_FEATURE_FLAG0(pstf) | - KBTS_FEATURE_FLAG0(fina) | KBTS_FEATURE_FLAG0(init) | KBTS_FEATURE_FLAG0(isol) | KBTS_FEATURE_FLAG0(medi) | - KBTS_FEATURE_FLAG0(abvs) | KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(calt) | KBTS_FEATURE_FLAG0(clig) | - KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs), - 0, - KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(nukt) | - KBTS_FEATURE_FLAG2(rkrf) | KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | - KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk) | KBTS_FEATURE_FLAG2(haln) | KBTS_FEATURE_FLAG2(liga) | - KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(pres) | KBTS_FEATURE_FLAG2(psts), - KBTS_FEATURE_FLAG3(rvrn) | KBTS_FEATURE_FLAG3(vatu)}}, -}; + // This isn't really an index into anything, per se. + // It is just an offset that allows reordering for ShapeState->LookupOnePastLastGlyph. + kbts_u32 StartIndex; -// Make sure that these fit KBTS_MAX_SIMULTANEOUS_FEATURES! -static kbts_u8 kbts_Ops_Default[] = { - KBTS_OP_KIND_NORMALIZE, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 12, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, - KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_clig, - KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_rclt, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_curs, - KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; - -/* @Incomplete: Vertical text. -static kbts_u8 kbts_Ops_DefaultTtbBtt[] = { - KBTS_OP_KIND_NORMALIZE, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 3, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 10, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_mark, - KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_vert, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -*/ -static kbts_u8 kbts_Ops_Hangul[] = { - KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_NORMALIZE_HANGUL, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 8, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, - KBTS_FEATURE_ID_ljmo, KBTS_FEATURE_ID_vjmo, KBTS_FEATURE_ID_tjmo, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 13, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_mark, - KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_clig, KBTS_FEATURE_ID_curs, - KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, KBTS_FEATURE_ID_rclt, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -static kbts_u8 kbts_Ops_ArabicRclt[] = { - KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_FLAG_JOINING_LETTERS, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_rtla, KBTS_FEATURE_ID_rtlm, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_stch, - KBTS_OP_KIND_GSUB_FEATURES, 2, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_isol, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_fina, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_fin2, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_fin3, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_medi, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_med2, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_init, - // @Incomplete: In Arabic rlig, ZWJ should not be skipped in lookups (i.e. it should block ligatures). - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rlig, - // @Incomplete: In non-Arabic scripts, e.g. Syriac, Harfbuzz does not pause here. - KBTS_OP_KIND_GSUB_FEATURES, 5, KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_clig, KBTS_FEATURE_ID_mset, KBTS_FEATURE_ID_rclt, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_curs, - KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, - KBTS_OP_KIND_STCH_POSTPASS, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -static kbts_u8 kbts_Ops_ArabicNoRclt[] = { - KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_FLAG_JOINING_LETTERS, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_rtla, KBTS_FEATURE_ID_rtlm, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_stch, - KBTS_OP_KIND_GSUB_FEATURES, 2, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_isol, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_fina, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_fin2, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_fin3, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_medi, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_med2, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_init, - // @Incomplete: In Arabic rlig, ZWJ should not be skipped in lookups (i.e. it should block ligatures). - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rlig, - // @Incomplete: In non-Arabic scripts, e.g. Syriac, Harfbuzz does not pause here. - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_calt, - // When the font does not contain rclt, Uniscribe puts calt in its own pass, and so we pause here. - KBTS_OP_KIND_GSUB_FEATURES, 4, KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_clig, KBTS_FEATURE_ID_mset, KBTS_FEATURE_ID_rclt, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_curs, - KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, - KBTS_OP_KIND_STCH_POSTPASS, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -static kbts_u8 kbts_Ops_Indic0[] = { - KBTS_OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, KBTS_OP_KIND_NORMALIZE, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, -}; -// After BeginCluster. -static kbts_u8 kbts_Ops_Indic1[] = { - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_nukt, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_akhn, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rphf, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rkrf, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_pref, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_blwf, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_abvf, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_half, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_pstf, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_vatu, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_cjct, -}; -// After EndCluster. -static kbts_u8 kbts_Ops_Indic2[] = { - KBTS_OP_KIND_GSUB_FEATURES, 6, KBTS_FEATURE_ID_abvs, KBTS_FEATURE_ID_blws, KBTS_FEATURE_ID_haln, KBTS_FEATURE_ID_init, KBTS_FEATURE_ID_pres, - KBTS_FEATURE_ID_psts, -}; -// After syllable processing. -static kbts_u8 kbts_Ops_Indic3[] = { - KBTS_OP_KIND_GSUB_FEATURES, 5, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_clig, KBTS_FEATURE_ID_rclt, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_curs, - KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -static kbts_u8 kbts_Ops_Use0[] = { - KBTS_OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_FLAG_JOINING_LETTERS, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, -}; -static kbts_u8 kbts_Ops_Use1[] = { - KBTS_OP_KIND_GSUB_FEATURES, 4, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_nukt, KBTS_FEATURE_ID_akhn, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rphf, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_pref, - KBTS_OP_KIND_GSUB_FEATURES, 7, KBTS_FEATURE_ID_abvf, KBTS_FEATURE_ID_blwf, KBTS_FEATURE_ID_cjct, KBTS_FEATURE_ID_half, KBTS_FEATURE_ID_pstf, - KBTS_FEATURE_ID_rkrf, KBTS_FEATURE_ID_vatu, -}; -// There is no kbts_Ops_Use2, because there are no features that apply per syllable after reordering. -static kbts_u8 kbts_Ops_Use3[] = { - KBTS_OP_KIND_GSUB_FEATURES, 4, KBTS_FEATURE_ID_fina, KBTS_FEATURE_ID_init, KBTS_FEATURE_ID_isol, KBTS_FEATURE_ID_medi, - KBTS_OP_KIND_GSUB_FEATURES,10, KBTS_FEATURE_ID_abvs, KBTS_FEATURE_ID_blws, KBTS_FEATURE_ID_haln, KBTS_FEATURE_ID_pres, KBTS_FEATURE_ID_psts, - KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_clig, KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_rclt, KBTS_FEATURE_ID_rlig, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_curs, KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, - KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -static kbts_u8 kbts_Ops_Tibetan[] = { - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 1, KBTS_FEATURE_ID_locl, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_ccmp, - KBTS_OP_KIND_GSUB_FEATURES, 4, KBTS_FEATURE_ID_abvs, KBTS_FEATURE_ID_blws, KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_liga, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 4, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_kern, KBTS_FEATURE_ID_mkmk, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -static kbts_u8 kbts_Ops_Khmer0[] = { - KBTS_OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, KBTS_OP_KIND_NORMALIZE, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, -}; -static kbts_u8 kbts_Ops_Khmer1[] = { - KBTS_OP_KIND_GSUB_FEATURES, 7, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_pref, KBTS_FEATURE_ID_blwf, KBTS_FEATURE_ID_abvf, - KBTS_FEATURE_ID_pstf, KBTS_FEATURE_ID_cfar, -}; -static kbts_u8 kbts_Ops_Khmer3[] = { - KBTS_OP_KIND_GSUB_FEATURES, 8, KBTS_FEATURE_ID_pres, KBTS_FEATURE_ID_abvs, KBTS_FEATURE_ID_blws, KBTS_FEATURE_ID_psts, KBTS_FEATURE_ID_calt, - KBTS_FEATURE_ID_rclt, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_clig, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_curs, KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, - KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -#define kbts_Ops_Myanmar0 kbts_Ops_Khmer0 -static kbts_u8 kbts_Ops_Myanmar1[] = { - KBTS_OP_KIND_GSUB_FEATURES, 2, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_ccmp, -}; -static kbts_u8 kbts_Ops_Myanmar2[] = { - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rphf, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_pref, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_blwf, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_pstf, -}; -static kbts_u8 kbts_Ops_Myanmar3[] = { - KBTS_OP_KIND_GSUB_FEATURES, 9, KBTS_FEATURE_ID_pres, KBTS_FEATURE_ID_abvs, KBTS_FEATURE_ID_blws, KBTS_FEATURE_ID_psts, KBTS_FEATURE_ID_rlig, - KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_clig, KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_rclt, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_curs, KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, - KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; - -static kbts_op_list kbts_ShaperOpLists[KBTS_SHAPER_COUNT] = { - /* DEFAULT, */ {kbts_Ops_Default, KBTS_ARRAY_LENGTH(kbts_Ops_Default)}, - /* ARABIC, */ {kbts_Ops_ArabicRclt, KBTS_ARRAY_LENGTH(kbts_Ops_ArabicRclt)}, - /* HANGUL, */ {kbts_Ops_Hangul, KBTS_ARRAY_LENGTH(kbts_Ops_Hangul)}, - /* HEBREW, */ {kbts_Ops_Default, KBTS_ARRAY_LENGTH(kbts_Ops_Default)}, - /* INDIC, */ {kbts_Ops_Indic0, KBTS_ARRAY_LENGTH(kbts_Ops_Indic0)}, - /* KHMER, */ {kbts_Ops_Khmer0, KBTS_ARRAY_LENGTH(kbts_Ops_Khmer0)}, - /* MYANMAR, */ {kbts_Ops_Myanmar0, KBTS_ARRAY_LENGTH(kbts_Ops_Myanmar0)}, - /* TIBETAN, */ {kbts_Ops_Tibetan, KBTS_ARRAY_LENGTH(kbts_Ops_Tibetan)}, - /* USE, */ {kbts_Ops_Use0, KBTS_ARRAY_LENGTH(kbts_Ops_Use0)}, -}; - -typedef struct kbts_gsub_frame -{ kbts_u16 LookupIndex; kbts_u16 SubtableIndex; - kbts_u16 InputGlyphIndex; - kbts_u16 InputGlyphCount; - // Defined for nested lookups. - kbts_sequence_lookup_record *Records; + kbts__sequence_lookup_record *Records; kbts_u16 RecordCount; kbts_u16 RecordIndex; -} kbts_gsub_frame; +} kbts__gsub_frame; -KBTS_EXPORT kbts_un kbts_SizeOfShapeState(kbts_font *Font) +#define KBTS__DLLIST_REMOVE(Node) do {(Node)->Prev->Next = (Node)->Next; (Node)->Next->Prev = (Node)->Prev;} while(0) +#define KBTS__DLLIST_INSERT_BEFORE(A, B) do{(A)->Next = (B); (A)->Prev = (B)->Prev; (A)->Prev->Next = (A)->Next->Prev = (A);} while(0) +#define KBTS__DLLIST_INSERT_AFTER(A, B) do{(A)->Prev = (B); (A)->Next = (B)->Next; (A)->Prev->Next = (A)->Next->Prev = (A);} while(0) +#define KBTS__DLLIST_SENTINEL_INIT(Sentinel) do{(Sentinel)->Prev = (Sentinel)->Next = (Sentinel);} while(0) +#define KBTS__DLLIST_SORT(First, OnePastLast, Member) \ + do \ + { \ + kbts_glyph *DllistSort_OneBeforeFirst = (First)->Prev; \ + kbts_glyph *DllistSort_OnePastLast = (OnePastLast); \ + for(int DllistSort_Swapped = 1; \ + DllistSort_Swapped; \ + ) \ + { \ + DllistSort_Swapped = 0; \ + kbts_glyph *DllistSort_Glyph0 = DllistSort_OneBeforeFirst->Next; \ + kbts_glyph *DllistSort_Glyph1 = DllistSort_Glyph0->Next; \ + while(DllistSort_Glyph1 != DllistSort_OnePastLast) \ + { \ + kbts_glyph *DllistSort_Next = DllistSort_Glyph1->Next; \ + \ + if(DllistSort_Glyph0->Member > DllistSort_Glyph1->Member) \ + { \ + KBTS__DLLIST_SWAP(DllistSort_Glyph0, DllistSort_Glyph1); \ + DllistSort_Swapped = 1; \ + } \ + else \ + { \ + DllistSort_Glyph0 = DllistSort_Glyph1; \ + } \ + \ + DllistSort_Glyph1 = DllistSort_Next; \ + } \ + } \ + } while(0) + +#define KBTS__FOR_GLYPH(Storage, GlyphName) \ + for(kbts_glyph *GlyphName = (Storage)->GlyphSentinel.Next; \ + GlyphName != (kbts_glyph *)&(Storage)->GlyphSentinel; \ + GlyphName = GlyphName->Next) + +static void KBTS__DLLIST_SWAP(kbts_glyph *A, kbts_glyph *B) { - kbts_un GsubMemorySize = Font->LookupInfo.MaximumLookupStackSize * sizeof(kbts_gsub_frame); - kbts_un NormalizationMemorySize = KBTS_MAXIMUM_DECOMPOSITION_CODEPOINTS * sizeof(kbts_glyph); - kbts_un LeftoverMemorySize = KBTS_MAX(GsubMemorySize, NormalizationMemorySize); + kbts_glyph *APrev = A->Prev; + kbts_glyph *ANext = A->Next; + kbts_glyph *BPrev = B->Prev; + kbts_glyph *BNext = B->Next; - kbts_un Result = sizeof(kbts_shape_state) + LeftoverMemorySize; - return Result; + A->Prev = (BPrev == A) ? B : BPrev; + A->Next = (BNext == A) ? B : BNext; + B->Prev = (APrev == B) ? A : APrev; + B->Next = (ANext == B) ? A : ANext; + + A->Prev->Next = A->Next->Prev = A; + B->Prev->Next = B->Next->Prev = B; } -KBTS_EXPORT kbts_shape_state *kbts_PlaceShapeState(void *Address, kbts_un Size) +static void kbts__NullAllocator(void *Data, kbts_allocator_op *Op) { - kbts_shape_state *State = (kbts_shape_state *)Address; - KBTS_MEMSET(State, 0, Size); - - return State; + KBTS__UNUSED(Data); + KBTS__UNUSED(Op); } -#ifndef KB_TEXT_SHAPE_NO_CRT -KBTS_EXPORT kbts_shape_state *kbts_CreateShapeState(kbts_font *Font) +static void kbts__DefaultAllocator(void *Data, kbts_allocator_op *Op) { - kbts_un Size = kbts_SizeOfShapeState(Font); - void *Memory = malloc(Size); - kbts_shape_state *Result = kbts_PlaceShapeState(Memory, Size); + KBTS__UNUSED(Data); - return Result; -} - -KBTS_EXPORT void kbts_FreeShapeState(kbts_shape_state *State) -{ - if(State) + switch(Op->Kind) { - free(State); - } -} -#endif - -KBTS_EXPORT void kbts_ResetShapeState(kbts_shape_state *State) -{ - State->ResumePoint = 0; -} - -static kbts_glyph_array kbts_ClipGlyphArray(kbts_glyph_array *Source, kbts_un Count) -{ - kbts_glyph_array Result = KBTS_ZERO; - Result.Glyphs = Source->Glyphs; - Result.Count = (kbts_u32)Count; - Result.TotalCount = (kbts_u32)Count; - Result.Capacity = (kbts_u32)Count; - return Result; -} - -static void kbts_TransferGrowRequest(kbts_glyph_array *From, kbts_glyph_array *To) -{ - if(From->RequiredCapacity > From->Capacity) + case KBTS_ALLOCATOR_OP_KIND_ALLOCATE: { - To->RequiredCapacity = To->Capacity + From->RequiredCapacity - From->Capacity; + Op->Allocate.Pointer = KBTS_MALLOC(Data, Op->Allocate.Size); + } break; + + case KBTS_ALLOCATOR_OP_KIND_FREE: + { + KBTS_FREE(Data, Op->Free.Pointer); + } break; } } -static kbts_glyph_array kbts_GlyphSubArray(kbts_glyph_array *Source, kbts_un FromIndex) +static void *kbts__AllocatorAllocate(kbts_allocator_function *Allocator, void *AllocatorData, kbts_un Size) { - kbts_glyph_array Result = KBTS_ZERO; - Result.Glyphs = Source->Glyphs + FromIndex; - Result.Count = (kbts_u32)(Source->Count - FromIndex); - Result.TotalCount = (kbts_u32)(Source->TotalCount - FromIndex); - Result.Capacity = (kbts_u32)(Source->Capacity - FromIndex); - return Result; -} + kbts_allocator_op AllocatorOp = KBTS__ZERO; + AllocatorOp.Kind = KBTS_ALLOCATOR_OP_KIND_ALLOCATE; + AllocatorOp.Allocate.Size = (kbts_u32)Size; -static kbts_glyph_array kbts_GlyphArray(kbts_glyph *Glyphs, kbts_un Count, kbts_un TotalCount, kbts_un Capacity) -{ - kbts_glyph_array Result = KBTS_ZERO; - Result.Glyphs = Glyphs; - Result.Count = (kbts_u32)Count; - Result.TotalCount = (kbts_u32)TotalCount; - Result.Capacity = (kbts_u32)Capacity; - return Result; -} - -static int kbts_GrowGlyphArray(kbts_u32 *ResumePoint_, kbts_glyph_array *Array, kbts_un InsertIndex, kbts_un GrowCount, kbts_u32 ResumePoint, int DoNotModifyGlyphCounts) -{ - int Result = 0; - kbts_un TotalCount = Array->TotalCount; - kbts_un NewTotalCount = TotalCount + GrowCount; - - if(NewTotalCount <= Array->Capacity) + if(!Allocator) { - if(NewTotalCount > TotalCount) + Allocator = kbts__DefaultAllocator; + } + + Allocator(AllocatorData, &AllocatorOp); + + void *Result = AllocatorOp.Allocate.Pointer; + return Result; +} +#define kbts__AllocatorAllocateType(Allocator, AllocatorData, Type) (Type *)kbts_AllocatorAllocate((Allocator), (AllocatorData), sizeof(Type)) +#define kbts__AllocatorAllocateArray(Allocator, AllocatorData, Type, Count) (Type *)kbts_AllocatorAllocate((Allocator), (AllocatorData), sizeof(Type) * (Count)) + +static void kbts__AllocatorFree(kbts_allocator_function *Allocator, void *AllocatorData, void *Pointer) +{ + if(Pointer) + { + kbts_allocator_op AllocatorOp = KBTS__ZERO; + AllocatorOp.Kind = KBTS_ALLOCATOR_OP_KIND_FREE; + AllocatorOp.Free.Pointer = Pointer; + + if(!Allocator) { - for(kbts_un ToIndex = NewTotalCount; ToIndex > InsertIndex + GrowCount; --ToIndex) + Allocator = kbts__DefaultAllocator; + } + + Allocator(AllocatorData, &AllocatorOp); + } +} + +static void kbts__EnsureArenaInitialized(kbts_arena *Arena) +{ + if(!Arena->BlockSentinel.Next) + { + KBTS__DLLIST_SENTINEL_INIT(&Arena->BlockSentinel); + KBTS__DLLIST_SENTINEL_INIT(&Arena->FreeBlockSentinel); + + if(!Arena->Allocator) + { + Arena->Allocator = kbts__DefaultAllocator; + } + } +} + +#define KBTS_ARENA_MIN_BLOCK_SIZE 4096 +static kbts__arena_block *kbts__ArenaPushBlock(kbts_arena *Arena, kbts_un Size, kbts_un Align) +{ + kbts__arena_block *Result = (kbts__arena_block *)&Arena->BlockSentinel; + + if(!Arena->Error) + { + kbts_un BlockSize = sizeof(kbts__arena_block) + Size + KBTS_ALIGNOF(kbts__arena_block) + Align - 1; + if(BlockSize < KBTS_ARENA_MIN_BLOCK_SIZE) + { + BlockSize = KBTS_ARENA_MIN_BLOCK_SIZE; + } + + Result = (kbts__arena_block *)Arena->FreeBlockSentinel.Prev; + if((Result == (kbts__arena_block *)&Arena->FreeBlockSentinel) || + ((Result->Size + sizeof(kbts__arena_block)) < BlockSize)) + { + void *Base = kbts__AllocatorAllocate(Arena->Allocator, Arena->AllocatorData, BlockSize); + + if(Base) { - Array->Glyphs[ToIndex - 1] = Array->Glyphs[ToIndex - 1 - GrowCount]; + Result = KBTS__ALIGN_POINTER(kbts__arena_block, Base, KBTS_ALIGNOF(kbts__arena_block)); + KBTS_MEMSET(Result, 0, sizeof(*Result)); + Result->BaseAllocation = Base; + Result->Size = (kbts_un)(KBTS__POINTER_OFFSET(char, Base, BlockSize) - KBTS__POINTER_AFTER(char, Result)); + } + else + { + Result = (kbts__arena_block *)&Arena->BlockSentinel; + Arena->Error = 1; } } - else if(NewTotalCount < TotalCount) + else { - // Multiple substitutions that generate 0 glyphs exist. In that case, GrowCount will be negative. - KBTS_FOR(ToIndex, InsertIndex, NewTotalCount) + KBTS__DLLIST_REMOVE(&Result->Header); + } + + KBTS__DLLIST_INSERT_BEFORE(&Result->Header, &Arena->BlockSentinel); + + Result->Used = 0; + } + + return Result; +} + +static int kbts__ArenaBlockIsValid(kbts_arena *Arena, kbts__arena_block *Block) +{ + int Result = Block && (&Block->Header != &Arena->BlockSentinel); + return Result; +} + +static void kbts__ClearArena(kbts_arena *Arena) +{ + kbts__EnsureArenaInitialized(Arena); + + kbts_arena_block_header *First = Arena->BlockSentinel.Next; + kbts_arena_block_header *Last = Arena->BlockSentinel.Prev; + + if(kbts__ArenaBlockIsValid(Arena, (kbts__arena_block *)First)) + { + First->Prev = Arena->FreeBlockSentinel.Prev; + Last->Next = &Arena->FreeBlockSentinel; + + First->Prev->Next = First; + Last->Next->Prev = Last; + + KBTS__DLLIST_SENTINEL_INIT(&Arena->BlockSentinel); + } +} + +static void *kbts__PushSize(kbts_arena *Arena, kbts_un Size, kbts_un Align) +{ + kbts__EnsureArenaInitialized(Arena); + void *Result = 0; + + if(!Arena->Error) + { + kbts__arena_block *Block = (kbts__arena_block *)Arena->BlockSentinel.Prev; + char *BlockMemory = KBTS__POINTER_AFTER(char, Block); + + if(kbts__ArenaBlockIsValid(Arena, Block)) + { + char *Top = BlockMemory + Block->Used; + char *TopAligned = KBTS__ALIGN_POINTER(char, Top, Align); + + if((kbts_un)((TopAligned + Size) - BlockMemory) <= Block->Size) { - Array->Glyphs[ToIndex] = Array->Glyphs[ToIndex - GrowCount]; + Result = TopAligned; } } - if(!DoNotModifyGlyphCounts) + if(!Result) { - Array->Count += GrowCount; - Array->TotalCount = (kbts_u32)NewTotalCount; + Block = kbts__ArenaPushBlock(Arena, Size, Align); + + if(kbts__ArenaBlockIsValid(Arena, Block)) + { + BlockMemory = KBTS__POINTER_AFTER(char, Block); + char *Top = BlockMemory + Block->Used; + char *TopAligned = KBTS__ALIGN_POINTER(char, Top, Align); + + Result = TopAligned; + } } - Result = 1; + + if(Result) + { + Block->Used = (kbts_un)((char *)Result + Size - BlockMemory); + KBTS_ASSERT(Block->Used <= Block->Size); + } + } + + return Result; +} +#define kbts__PushType(Arena, Type) (Type *)kbts__PushSize((Arena), sizeof(Type), KBTS_ALIGNOF(Type)) +#define kbts__PushArray(Arena, Type, Count) (Type *)kbts__PushSize((Arena), sizeof(Type) * (Count), KBTS_ALIGNOF(Type)) + +static kbts__arena_lifetime kbts__BeginLifetime(kbts_arena *Arena) +{ + kbts__arena_block *Block = (kbts__arena_block *)Arena->BlockSentinel.Prev; + kbts_un Used = 0; + + if(!Block) + { + Block = (kbts__arena_block *)&Arena->BlockSentinel; + } + + if(kbts__ArenaBlockIsValid(Arena, Block)) + { + Used = Block->Used; + } + + kbts__arena_lifetime Result = KBTS__ZERO; + Result.Arena = Arena; + Result.BlockHeader = &Block->Header; + Result.Used = Used; + return Result; +} + +static void kbts__EndLifetime(kbts__arena_lifetime *Lifetime) +{ + kbts_arena *Arena = Lifetime->Arena; + if(Arena && !Arena->Error) + { + kbts__arena_block *Block = (kbts__arena_block *)Lifetime->BlockHeader; + + if(kbts__ArenaBlockIsValid(Lifetime->Arena, Block)) + { + Block->Used = Lifetime->Used; + } + + // This works even if Block is the sentinel. + for(kbts_arena_block_header *Header = Block->Header.Next; + kbts__ArenaBlockIsValid(Arena, (kbts__arena_block *)Header); + ) + { + kbts_arena_block_header *Next = Header->Next; + + KBTS__DLLIST_REMOVE(Header); + KBTS__DLLIST_INSERT_BEFORE(Header, &Arena->FreeBlockSentinel); + + Header = Next; + } + } +} + +static void kbts__MoveArena(kbts_arena *To, kbts_arena *From) +{ + *To = *From; + + if(To->BlockSentinel.Next != &From->BlockSentinel) + { + To->BlockSentinel.Next->Prev = To->BlockSentinel.Prev->Next = &To->BlockSentinel; } else { - Array->RequiredCapacity = (kbts_u32)(NewTotalCount + KBTS_GROW_BUFFER_MARGIN); - if(ResumePoint_) *ResumePoint_ = ResumePoint; + KBTS__DLLIST_SENTINEL_INIT(&To->BlockSentinel); + } + + if(To->FreeBlockSentinel.Next != &From->FreeBlockSentinel) + { + To->FreeBlockSentinel.Next->Prev = To->FreeBlockSentinel.Prev->Next = &To->FreeBlockSentinel; + } + else + { + KBTS__DLLIST_SENTINEL_INIT(&To->FreeBlockSentinel); + } +} + +static void kbts__FreeArena(kbts_arena *Arena) +{ + kbts__EnsureArenaInitialized(Arena); + + kbts_arena StackArena; + kbts__MoveArena(&StackArena, Arena); + + kbts_arena_block_header *Sentinels[2] = {&StackArena.BlockSentinel, &StackArena.FreeBlockSentinel}; + + KBTS__FOR(SentinelIndex, 0, KBTS__ARRAY_LENGTH(Sentinels)) + { + kbts_arena_block_header *Sentinel = Sentinels[SentinelIndex]; + + for(kbts_arena_block_header *Header = Sentinel->Next; + Header != Sentinel; + ) + { + kbts_arena_block_header *Next = Header->Next; + kbts__arena_block *Block = (kbts__arena_block *)Header; + + kbts__AllocatorFree(StackArena.Allocator, StackArena.AllocatorData, Block->BaseAllocation); + + Header = Next; + } + } +} + +static void kbts__ArenaAllocator(void *Data, kbts_allocator_op *Op) +{ + kbts_arena *Arena = (kbts_arena *)Data; + + if(Op->Kind == KBTS_ALLOCATOR_OP_KIND_ALLOCATE) + { + Op->Allocate.Pointer = kbts__PushSize(Arena, Op->Allocate.Size, 8); + } + // No free! +} + +static int kbts__InitializeFixedMemoryArena(kbts_arena *Arena, void *Memory, kbts_un MemorySize) +{ + int Result = 0; + KBTS_MEMSET(Arena, 0, sizeof(*Arena)); + Arena->Allocator = kbts__NullAllocator; + kbts__EnsureArenaInitialized(Arena); + + char *BaseAligned = KBTS__ALIGN_POINTER(char, Memory, KBTS_ALIGNOF(kbts__arena_block)); + char *End = (char *)Memory + MemorySize; + kbts_un TotalBlockSize = (kbts_un)(End - BaseAligned); + + if(TotalBlockSize >= sizeof(kbts__arena_block)) + { + kbts__arena_block *Block = (kbts__arena_block *)BaseAligned; + + Block->Header.Prev = Block->Header.Next = &Arena->BlockSentinel; + Block->Header.Prev->Next = Block->Header.Next->Prev = &Block->Header; + Block->BaseAllocation = 0; + Block->Size = TotalBlockSize - sizeof(kbts__arena_block); + Block->Used = 0; + + Result = 1; } return Result; } -static void kbts_BeginFeatures(kbts_op_state *State, kbts_shape_config *Config, kbts_shaping_table ShapingTable, kbts_feature_set EnabledFeatures) +static kbts_glyph *kbts__InsertGlyph(kbts_glyph_storage *Storage, kbts_glyph *Anchor, int InsertBeforeAnchor, kbts_glyph *BaseGlyph) { - kbts_un FeatureCount = 0; - kbts_gsub_gpos *Header = Config->Font->ShapingTables[ShapingTable]; - kbts_langsys *Langsys = Config->Langsys[ShapingTable]; - if(Header && Langsys) + kbts_glyph *Result = Storage->FreeGlyphSentinel.Next; + + if(Result != &Storage->FreeGlyphSentinel) { - kbts_feature_list *FeatureList = KBTS_POINTER_OFFSET(kbts_feature_list, Header, Header->FeatureListOffset); - kbts_u16 *FeatureIndices = KBTS_POINTER_AFTER(kbts_u16, Langsys); - // @Incomplete - // if(Header->Minor == 1) - // { - // kbts_feature_variations *FeatureVariations = KBTS_POINTER_OFFSET(kbts_feature_variations, Header, Header->FeatureVariationsOffset); - // } - - KBTS_FOR(FeatureIndexIndex, 0, Langsys->FeatureIndexCount) - { - kbts_un FeatureIndex = FeatureIndices[FeatureIndexIndex]; - kbts_feature_pointer Feature = kbts_GetFeature(FeatureList, FeatureIndex); - // @Incomplete - //if(FeatureVariations) - //{ - // KBTS_FOR(VariationIndex, 0, FeatureVariations->RecordCount) - // { - // kbts_feature_variation_pointer Variation = kbts_GetFeatureVariation(FeatureVariations, VariationIndex); - // KBTS_FOR(ConditionIndex, 0, Variation.ConditionSet->Count) - // { - // kbts_condition_1 *Condition = kbts_GetCondition(Variation.ConditionSet, ConditionIndex); - // KBTS_ASSERT(0); - // } - // } - //} - - kbts_u32 FeatureId = kbts_FeatureTagToId(Feature.Tag); - if(Feature.Feature->LookupIndexCount && kbts_ContainsFeature(&EnabledFeatures, FeatureId)) - { - kbts_lookup_indices LookupIndices = KBTS_ZERO; - LookupIndices.FeatureTag = Feature.Tag; - LookupIndices.FeatureId = FeatureId; - LookupIndices.SkipFlags = kbts_SkipFlags(FeatureId, Config->Shaper); - // For Myanmar, we could try and tag glyphs depending on their Indic properties in BeginCluster, just like we do for - // Indic scripts. - // However, Harfbuzz does _not_ do this, so it seems like a bunch of work that would, at best, make us diverge from - // Harfbuzz more often. - if((Config->Shaper != KBTS_SHAPER_MYANMAR) && (FeatureId >= 1) && (FeatureId <= 32)) - { - // These must properly map KBTS_FEATURE_ID to kbts_glyph_flags! - LookupIndices.GlyphFilter = (1 << (FeatureId - 1)) & KBTS_GLYPH_FEATURE_MASK; - } - LookupIndices.Count = Feature.Feature->LookupIndexCount; - LookupIndices.Indices = KBTS_POINTER_AFTER(kbts_u16, Feature.Feature); - State->FeatureLookupIndices[FeatureCount++] = LookupIndices; - } - } + KBTS__DLLIST_REMOVE(Result); } - State->FeatureCount = (kbts_u32)FeatureCount; + else + { + Result = kbts__PushType(&Storage->Arena, kbts_glyph); + } + + if(Result) + { + if(BaseGlyph) + { + *Result = *BaseGlyph; + } + + if(InsertBeforeAnchor) + { + Result->Next = Anchor; + Result->Prev = Anchor->Prev; + } + else + { + Result->Prev = Anchor; + Result->Next = Anchor->Next; + } + + Result->Prev->Next = Result->Next->Prev = Result; + } + else + { + Storage->Error = 1; + } + + return Result; +} +static kbts_glyph *kbts__InsertGlyphBefore(kbts_glyph_storage *Storage, kbts_glyph *Anchor, kbts_glyph *BaseGlyph) +{ + kbts_glyph *Result = kbts__InsertGlyph(Storage, Anchor, 1, BaseGlyph); + return Result; +} +static kbts_glyph *kbts__InsertGlyphAfter(kbts_glyph_storage *Storage, kbts_glyph *Anchor, kbts_glyph *BaseGlyph) +{ + kbts_glyph *Result = kbts__InsertGlyph(Storage, Anchor, 0, BaseGlyph); + return Result; } -typedef struct kbts_sequence_lookup_result +static kbts_glyph *kbts__FreeGlyph(kbts__shape_scratchpad *Scratchpad, kbts_glyph_storage *Storage, kbts_glyph *Glyph) { - kbts_sequence_lookup_record *Records; + if(Glyph == Scratchpad->LookupOnePastLastGlyph) + { + Scratchpad->LookupOnePastLastGlyph = Glyph->Next; + } + if(Glyph == Scratchpad->Cluster.SentinelPrev) + { + Scratchpad->Cluster.SentinelPrev = Glyph->Prev; + } + if(Glyph == Scratchpad->Cluster.SentinelNext) + { + Scratchpad->Cluster.SentinelNext = Glyph->Next; + } + + KBTS__DLLIST_REMOVE(Glyph); + KBTS__DLLIST_INSERT_BEFORE(Glyph, (kbts_glyph *)&Storage->FreeGlyphSentinel); + + return Glyph; +} + +static kbts_glyph *kbts__SetGlyphPreserveLinksAndUserId(kbts_glyph *Dest, kbts_glyph *Source) +{ + kbts_glyph *Prev = Dest->Prev; + kbts_glyph *Next = Dest->Next; + int UserId = Dest->UserIdOrCodepointIndex; + + *Dest = *Source; + + Dest->Prev = Prev; + Dest->Next = Next; + Dest->UserIdOrCodepointIndex = UserId; + + return Dest; +} + +static int kbts__BeginFeatures(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_shaping_table ShapingTable) +{ + int Result = 0; + kbts_blob_table_id TableId = (ShapingTable == KBTS_SHAPING_TABLE_GSUB) ? KBTS_BLOB_TABLE_ID_GSUB : KBTS_BLOB_TABLE_ID_GPOS; + kbts__gsub_gpos *GsubGpos = kbts__BlobTableDataType(Config->Font->Blob, TableId, kbts__gsub_gpos); + kbts__langsys *Langsys = Config->Langsys[ShapingTable]; + kbts_un FeatureStageIndex = Scratchpad->FeatureStagesRead - 1; + + Scratchpad->FeatureIndexIndex = 0; + + if(GsubGpos && Langsys && (FeatureStageIndex < Config->OpList.FeatureStageCount)) + { + kbts__baked_feature_stage *BakedFeatureStage = &Config->FeatureStages[FeatureStageIndex]; + + // @Incomplete + // if(GsubGpos->Minor == 1) + // { + // kbts__feature_variations *FeatureVariations = KBTS__POINTER_OFFSET(kbts__feature_variations, GsubGpos, GsubGpos->FeatureVariationsOffset); + // } + + + KBTS__FOR(FeatureIndex, 0, BakedFeatureStage->FeatureCount) + { + Scratchpad->BakedLookupSubtablesRead[FeatureIndex] = 0; + } + + Result = 1; + } + + return Result; +} + +typedef struct kbts__sequence_lookup_result +{ + kbts__sequence_lookup_record *Records; kbts_un RecordCount; kbts_un InputSequenceCountIncludingSkippedGlyphs; + kbts_glyph *OnePastLastGlyphMatched; + // This is specified _nowhere_ in the docs, BUT some sequence lookups have 0 records, and exist just to prevent the // next lookups from executing. // So, checking if we got any records is not enough to figure out whether we need to "apply" this lookup. // We need to have an explicit bool as well. // Sigh. int Matched; -} kbts_sequence_lookup_result; +} kbts__sequence_lookup_result; -typedef struct kbts_sequence_match +typedef struct kbts__sequence_match { kbts_un MatchCount; kbts_un MatchOrSkipCount; -} kbts_sequence_match; + kbts_glyph *OnePastMatchGlyph; +} kbts__sequence_match; -static kbts_sequence_match kbts_MatchCoverageSequence(kbts_unpacked_lookup *Lookup, kbts_u32 SkipFlags, kbts_u32 SkipUnicodeFlags, +static kbts__sequence_match kbts__MatchCoverageSequence(kbts_glyph_storage *Storage, kbts__unpacked_lookup *Lookup, kbts_u32 SkipFlags, kbts_u32 SkipUnicodeFlags, void *Base, kbts_u16 *CoverageOffsets, kbts_un CoverageCount, - kbts_glyph_array *GlyphArray, kbts_un GlyphStartIndex, kbts_un GlyphIndexIncrement) + kbts_glyph *AtGlyph, int Backward) { kbts_un CoverageIndex = 0; kbts_un GlyphCounter = 0; - kbts_un GlyphIndex = GlyphStartIndex; - while((GlyphCounter < GlyphArray->Count) && (CoverageIndex < CoverageCount)) + while(kbts__GlyphIsValid(Storage, AtGlyph) && (CoverageIndex < CoverageCount)) { - kbts_glyph *Glyph = &GlyphArray->Glyphs[GlyphIndex]; - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Base, CoverageOffsets[CoverageIndex]); + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Base, CoverageOffsets[CoverageIndex]); - if(!kbts_SkipGlyph(Glyph, Lookup, SkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(AtGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { - kbts_cover_glyph_result Cover = kbts_CoverGlyph(Coverage, Glyph->Id); + kbts__cover_glyph_result Cover = kbts__CoverGlyph(Coverage, AtGlyph->Id); if(Cover.Valid) { @@ -16901,41 +19736,38 @@ static kbts_sequence_match kbts_MatchCoverageSequence(kbts_unpacked_lookup *Look } } - GlyphIndex += GlyphIndexIncrement; + AtGlyph = Backward ? AtGlyph->Prev : AtGlyph->Next; GlyphCounter += 1; } - kbts_sequence_match Result = KBTS_ZERO; + kbts__sequence_match Result = KBTS__ZERO; Result.MatchCount = CoverageIndex; Result.MatchOrSkipCount = GlyphCounter; + Result.OnePastMatchGlyph = AtGlyph; return Result; } -static int kbts_BranchlessCompareArray16(kbts_u16 *A, kbts_u16 *B, kbts_un Count) +static int kbts__BranchlessCompareArray16(kbts_u16 *A, kbts_u16 *B, kbts_un Count) { kbts_u16 DifferenceMask = 0; - KBTS_FOR(Index, 0, Count) + KBTS__FOR(Index, 0, Count) { DifferenceMask |= A[Index] ^ B[Index]; } return (int)DifferenceMask; } -static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *Lookup, kbts_u16 *Base, kbts_cover_glyph_result Cover, - kbts_glyph_array *GlyphArray, kbts_un InputGlyphOffset, kbts_skip_flags SkipFlags, kbts_u32 SkipUnicodeFlags) +static kbts__sequence_lookup_result kbts__DoSequenceLookup(kbts_glyph_storage *Storage, kbts__unpacked_lookup *Lookup, kbts_u16 *Base, kbts__cover_glyph_result Cover, + kbts_glyph *AtGlyph, kbts__skip_flags SkipFlags, kbts_u32 SkipUnicodeFlags) { - KBTS_INSTRUMENT_FUNCTION_BEGIN - kbts_sequence_lookup_result Result = KBTS_ZERO; + KBTS_INSTRUMENT_FUNCTION_BEGIN; + kbts__sequence_lookup_result Result = KBTS__ZERO; + Result.OnePastLastGlyphMatched = AtGlyph->Next; - kbts_glyph_array BacktrackGlyphs = kbts_ClipGlyphArray(GlyphArray, InputGlyphOffset); - kbts_glyph_array InputGlyphs = kbts_GlyphSubArray(GlyphArray, InputGlyphOffset); - kbts_glyph_array FollowupGlyphs = kbts_GlyphSubArray(&InputGlyphs, 1); - - kbts_glyph *FirstGlyph = &GlyphArray->Glyphs[0]; - kbts_glyph *CurrentGlyph = &GlyphArray->Glyphs[InputGlyphOffset]; - kbts_glyph *InputGlyph = &GlyphArray->Glyphs[InputGlyphOffset + 1]; - kbts_glyph *OnePastLastGlyph = &GlyphArray->Glyphs[GlyphArray->Count]; - kbts_glyph *BacktrackGlyph = &GlyphArray->Glyphs[InputGlyphOffset - 1]; + kbts_glyph *CurrentGlyph = AtGlyph; + kbts_glyph *InputGlyph = AtGlyph->Next; + kbts_un InputGlyphsTraversed = 1; + kbts_glyph *BacktrackGlyph = AtGlyph->Prev; // Lookup types 7 and 8 are dispatch mechanisms. They do not substitute anything by themselves, they only point to // other rules. @@ -16949,43 +19781,47 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L case 0x50001: case 0x70001: { - kbts_sequence_context_1 *Subst = (kbts_sequence_context_1 *)Base; - kbts_sequence_rule_set *Set = kbts_GetSequenceRuleSet(Subst, Cover.Index); + kbts__sequence_context_1 *Subst = (kbts__sequence_context_1 *)Base; + kbts__sequence_rule_set *Set = kbts__GetSequenceRuleSet(Subst, Cover.Index); if(Set) { kbts_u16 Ids[64]; // @Hardcoded - kbts_u16 GlyphOffsets[64]; + kbts_u16 InputGlyphOffsets[64]; + kbts_glyph *InputGlyphs[64]; kbts_un IdCount = 0; - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_sequence_rule *Rule = kbts_GetSequenceRule(Set, RuleIndex); - kbts_u16 *InputSequence = KBTS_POINTER_AFTER(kbts_u16, Rule); + kbts__sequence_rule *Rule = kbts__GetSequenceRule(Set, RuleIndex); + kbts_u16 *InputSequence = KBTS__POINTER_AFTER(kbts_u16, Rule); // @Hardcoded! KBTS_ASSERT(Rule->GlyphCount <= 64); - while((InputGlyph < OnePastLastGlyph) && ((IdCount + 1) < Rule->GlyphCount)) + while(kbts__GlyphIsValid(Storage, InputGlyph) && ((IdCount + 1) < Rule->GlyphCount)) { - if(!kbts_SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { - GlyphOffsets[IdCount] = (kbts_u16)(InputGlyph - CurrentGlyph); + InputGlyphOffsets[IdCount] = (kbts_u16)InputGlyphsTraversed; + InputGlyphs[IdCount] = InputGlyph; Ids[IdCount++] = InputGlyph->Id; } - InputGlyph += 1; + InputGlyph = InputGlyph->Next; + InputGlyphsTraversed += 1; } if(((IdCount + 1) >= Rule->GlyphCount) && - !kbts_BranchlessCompareArray16(Ids, InputSequence, Rule->GlyphCount - 1)) + !kbts__BranchlessCompareArray16(Ids, InputSequence, Rule->GlyphCount - 1)) { - Result.Records = (kbts_sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); + Result.Records = (kbts__sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); Result.RecordCount = Rule->SequenceLookupCount; Result.InputSequenceCountIncludingSkippedGlyphs = 1; if(Rule->GlyphCount > 1) { - Result.InputSequenceCountIncludingSkippedGlyphs = GlyphOffsets[Rule->GlyphCount - 2] + 1; + Result.InputSequenceCountIncludingSkippedGlyphs = InputGlyphOffsets[Rule->GlyphCount - 2] + 1; + Result.OnePastLastGlyphMatched = InputGlyphs[Rule->GlyphCount - 2]->Next; } Result.Matched = 1; @@ -16999,48 +19835,52 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L case 0x50002: case 0x70002: { - kbts_sequence_context_2 *Subst = (kbts_sequence_context_2 *)Base; - kbts_u16 *ClassDefinitionBase = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->ClassDefOffset); + kbts__sequence_context_2 *Subst = (kbts__sequence_context_2 *)Base; + kbts_u16 *ClassDefinitionBase = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->ClassDefOffset); // @Hardcoded! kbts_u16 InputClasses[64]; kbts_u16 InputOffsets[64]; + kbts_glyph *InputGlyphs[64]; kbts_un InputCount = 0; // For class-based contexts, the coverage index is not used. // Instead, we know which set to use based on the current glyph's class. // From the Microsoft docs: // The class value is used as the index into an array of offsets to ClassSequenceRuleSet tables. - kbts_glyph_class_from_table_result CurrentGlyphClass = kbts_GlyphClassFromTable(ClassDefinitionBase, CurrentGlyph->Id); - kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, CurrentGlyphClass.Class); + kbts__glyph_class_from_table_result CurrentGlyphClass = kbts__GlyphClassFromTable(ClassDefinitionBase, CurrentGlyph->Id); + kbts__class_sequence_rule_set *Set = kbts__GetClassSequenceRuleSet(Subst, CurrentGlyphClass.Class); if((CurrentGlyphClass.Class < Subst->ClassSequenceRuleSetCount) && Set) { - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_class_sequence_rule *Rule = kbts_GetClassSequenceRule(Set, RuleIndex); - kbts_u16 *InputSequence = KBTS_POINTER_AFTER(kbts_u16, Rule); + kbts__class_sequence_rule *Rule = kbts__GetClassSequenceRule(Set, RuleIndex); + kbts_u16 *InputSequence = KBTS__POINTER_AFTER(kbts_u16, Rule); - while((InputGlyph < OnePastLastGlyph) && ((InputCount + 1) < Rule->GlyphCount)) + while(kbts__GlyphIsValid(Storage, InputGlyph) && ((InputCount + 1) < Rule->GlyphCount)) { - if(!kbts_SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { - InputOffsets[InputCount] = (kbts_u16)(InputGlyph - CurrentGlyph); - InputClasses[InputCount++] = kbts_GlyphClassFromTable(ClassDefinitionBase, InputGlyph->Id).Class; + InputOffsets[InputCount] = (kbts_u16)InputGlyphsTraversed; + InputGlyphs[InputCount] = InputGlyph; + InputClasses[InputCount++] = kbts__GlyphClassFromTable(ClassDefinitionBase, InputGlyph->Id).Class; } - InputGlyph += 1; + InputGlyph = InputGlyph->Next; + InputGlyphsTraversed += 1; } if(((InputCount + 1) >= Rule->GlyphCount) && - !kbts_BranchlessCompareArray16(InputClasses, InputSequence, Rule->GlyphCount - 1)) + !kbts__BranchlessCompareArray16(InputClasses, InputSequence, Rule->GlyphCount - 1)) { - Result.Records = (kbts_sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); + Result.Records = (kbts__sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); Result.RecordCount = Rule->SequenceLookupCount; Result.InputSequenceCountIncludingSkippedGlyphs = 1; if(Rule->GlyphCount > 1) { Result.InputSequenceCountIncludingSkippedGlyphs = InputOffsets[Rule->GlyphCount - 2] + 1; + Result.OnePastLastGlyphMatched = InputGlyphs[Rule->GlyphCount - 2]->Next; } Result.Matched = 1; @@ -17054,16 +19894,17 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L case 0x50003: case 0x70003: { - kbts_sequence_context_3 *Subst = (kbts_sequence_context_3 *)Base; - kbts_u16 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); + kbts__sequence_context_3 *Subst = (kbts__sequence_context_3 *)Base; + kbts_u16 *CoverageOffsets = KBTS__POINTER_AFTER(kbts_u16, Subst); - kbts_sequence_match InputMatch = kbts_MatchCoverageSequence(Lookup, SkipFlags, SkipUnicodeFlags, Subst, CoverageOffsets, Subst->GlyphCount, &InputGlyphs, 0, 1); + kbts__sequence_match InputMatch = kbts__MatchCoverageSequence(Storage, Lookup, SkipFlags, SkipUnicodeFlags, Subst, CoverageOffsets, Subst->GlyphCount, AtGlyph, 0); if(InputMatch.MatchCount == Subst->GlyphCount) { - Result.Records = (kbts_sequence_lookup_record *)(CoverageOffsets + Subst->GlyphCount); + Result.Records = (kbts__sequence_lookup_record *)(CoverageOffsets + Subst->GlyphCount); Result.RecordCount = Subst->SequenceLookupCount; Result.InputSequenceCountIncludingSkippedGlyphs = InputMatch.MatchOrSkipCount; + Result.OnePastLastGlyphMatched = InputMatch.OnePastMatchGlyph; Result.Matched = 1; } } @@ -17072,8 +19913,8 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L case 0x60001: case 0x80001: { - kbts_chained_sequence_context_1 *Subst = (kbts_chained_sequence_context_1 *)Base; - kbts_chained_sequence_rule_set *Set = kbts_GetChainedSequenceRuleSet(Subst, Cover.Index); + kbts__chained_sequence_context_1 *Subst = (kbts__chained_sequence_context_1 *)Base; + kbts__chained_sequence_rule_set *Set = kbts__GetChainedSequenceRuleSet(Subst, Cover.Index); if(Set) { @@ -17081,38 +19922,43 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L kbts_u16 BacktrackIds[64]; kbts_u16 InputIds[64]; kbts_u16 InputOffsets[64]; + kbts_glyph *InputGlyphs[64]; kbts_un BacktrackCount = 0; kbts_un InputCount = 0; - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); + kbts__chained_sequence_rule *Rule = kbts__GetChainedSequenceRule(Set, RuleIndex); + kbts__unpacked_chained_sequence_rule Unpacked = kbts__UnpackChainedSequenceRule(Rule, 0); - while((BacktrackGlyph >= FirstGlyph) && (BacktrackCount < Unpacked.BacktrackCount)) + while(kbts__GlyphIsValid(Storage, BacktrackGlyph) && (BacktrackCount < Unpacked.BacktrackCount)) { - if(!kbts_SkipGlyph(BacktrackGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(BacktrackGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { BacktrackIds[BacktrackCount++] = BacktrackGlyph->Id; } - BacktrackGlyph -= 1; + + BacktrackGlyph = BacktrackGlyph->Prev; } kbts_un TotalInputGlyphsRequired = Unpacked.InputCount - 1 + Unpacked.LookaheadCount; - while((InputGlyph < OnePastLastGlyph) && (InputCount < TotalInputGlyphsRequired)) + while(kbts__GlyphIsValid(Storage, InputGlyph) && (InputCount < TotalInputGlyphsRequired)) { - if(!kbts_SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { - InputOffsets[InputCount] = (kbts_u16)(InputGlyph - CurrentGlyph); + InputGlyphs[InputCount] = InputGlyph; + InputOffsets[InputCount] = (kbts_u16)InputGlyphsTraversed; InputIds[InputCount++] = InputGlyph->Id; } - InputGlyph += 1; + + InputGlyph = InputGlyph->Next; + InputGlyphsTraversed += 1; } if((BacktrackCount >= Unpacked.BacktrackCount) && (InputCount >= TotalInputGlyphsRequired) && - !kbts_BranchlessCompareArray16(BacktrackIds, Unpacked.Backtrack, Unpacked.BacktrackCount) && - !kbts_BranchlessCompareArray16(InputIds, Unpacked.Input, Unpacked.InputCount - 1) && - !kbts_BranchlessCompareArray16(InputIds + Unpacked.InputCount - 1, Unpacked.Lookahead, Unpacked.LookaheadCount)) + !kbts__BranchlessCompareArray16(BacktrackIds, Unpacked.Backtrack, Unpacked.BacktrackCount) && + !kbts__BranchlessCompareArray16(InputIds, Unpacked.Input, Unpacked.InputCount - 1) && + !kbts__BranchlessCompareArray16(InputIds + Unpacked.InputCount - 1, Unpacked.Lookahead, Unpacked.LookaheadCount)) { Result.Records = Unpacked.Records; Result.RecordCount = Unpacked.RecordCount; @@ -17120,6 +19966,7 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L if(Unpacked.InputCount > 1) { Result.InputSequenceCountIncludingSkippedGlyphs = InputOffsets[Unpacked.InputCount - 2] + 1; + Result.OnePastLastGlyphMatched = InputGlyphs[Unpacked.InputCount - 2]->Next; } Result.Matched = 1; @@ -17133,10 +19980,10 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L case 0x60002: case 0x80002: { - kbts_chained_sequence_context_2 *Subst = (kbts_chained_sequence_context_2 *)Base; - kbts_u16 *BacktrackClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->BacktrackClassDefOffset); - kbts_u16 *InputClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->InputClassDefOffset); - kbts_u16 *LookaheadClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->LookaheadClassDefOffset); + kbts__chained_sequence_context_2 *Subst = (kbts__chained_sequence_context_2 *)Base; + kbts_u16 *BacktrackClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->BacktrackClassDefOffset); + kbts_u16 *InputClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->InputClassDefOffset); + kbts_u16 *LookaheadClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->LookaheadClassDefOffset); // @Incomplete: Do this with all sequence types! @@ -17144,6 +19991,7 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L kbts_u16 BacktrackClasses[64]; kbts_u16 InputClasses[64]; kbts_u16 InputClassOffsets[64]; + kbts_glyph *InputGlyphs[64]; kbts_u16 LookaheadClasses[64]; kbts_un BacktrackClassCount = 0; kbts_un InputClassCount = 0; @@ -17154,8 +20002,8 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L // current glyph. The class value is used as the index into an array of offsets to ChainedClassSequenceRuleSet // tables. // - kbts_u16 CurrentGlyphClass = kbts_GlyphClassFromTable(InputClassDefinition, CurrentGlyph->Id).Class; - kbts_chained_sequence_rule_set *Set = kbts_GetChainedClassSequenceRuleSet(Subst, CurrentGlyphClass); + kbts_u16 CurrentGlyphClass = kbts__GlyphClassFromTable(InputClassDefinition, CurrentGlyph->Id).Class; + kbts__chained_sequence_rule_set *Set = kbts__GetChainedClassSequenceRuleSet(Subst, CurrentGlyphClass); // If the glyph was contained in the coverage table, then it should have a valid class. // Nevertheless, one Harfbuzz test font did not remove out-of-bounds glyph classes from the class definition // table here, so we double-check it here. @@ -17163,59 +20011,61 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L // in which case ignoring these classes becomes a feature.) if((CurrentGlyphClass < Subst->ChainedClassSequenceRuleSetCount) && Set) { - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS__FOR(RuleIndex, 0, Set->Count) { - kbts_chained_sequence_rule *Rule = kbts_GetChainedClassSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); + kbts__chained_sequence_rule *Rule = kbts__GetChainedClassSequenceRule(Set, RuleIndex); + kbts__unpacked_chained_sequence_rule Unpacked = kbts__UnpackChainedSequenceRule(Rule, 0); // @Hardcoded KBTS_ASSERT(Unpacked.BacktrackCount <= 64); KBTS_ASSERT(Unpacked.InputCount <= 64); KBTS_ASSERT(Unpacked.LookaheadCount <= 64); - while((BacktrackGlyph >= FirstGlyph) && (BacktrackClassCount < Unpacked.BacktrackCount)) + while(kbts__GlyphIsValid(Storage, BacktrackGlyph) && (BacktrackClassCount < Unpacked.BacktrackCount)) { - if(!kbts_SkipGlyph(BacktrackGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(BacktrackGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { // @Robustness: Do we want to break if we don't find a class? - kbts_glyph_class_from_table_result Class = kbts_GlyphClassFromTable(BacktrackClassDefinition, BacktrackGlyph->Id); + kbts__glyph_class_from_table_result Class = kbts__GlyphClassFromTable(BacktrackClassDefinition, BacktrackGlyph->Id); BacktrackClasses[BacktrackClassCount++] = Class.Class; } - BacktrackGlyph -= 1; + BacktrackGlyph = BacktrackGlyph->Prev; } kbts_un InputClassCountRequired = Unpacked.InputCount - 1 + Unpacked.LookaheadCount; - while((InputGlyph < OnePastLastGlyph) && (InputClassCount < InputClassCountRequired)) + while(kbts__GlyphIsValid(Storage, InputGlyph) && (InputClassCount < InputClassCountRequired)) { - if(!kbts_SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { - kbts_glyph_class_from_table_result InputClass = kbts_GlyphClassFromTable(InputClassDefinition, InputGlyph->Id); + kbts__glyph_class_from_table_result InputClass = kbts__GlyphClassFromTable(InputClassDefinition, InputGlyph->Id); // In many cases, the font designer just wants to match "a set of glyphs" forward, // and it doesn't matter whether those glyphs are in the input sequence or part of the lookahead. // This happens often enough that we care to special-case it. - kbts_glyph_class_from_table_result LookaheadClass = InputClass; + kbts__glyph_class_from_table_result LookaheadClass = InputClass; if(LookaheadClassDefinition != InputClassDefinition) { - LookaheadClass = kbts_GlyphClassFromTable(LookaheadClassDefinition, InputGlyph->Id); + LookaheadClass = kbts__GlyphClassFromTable(LookaheadClassDefinition, InputGlyph->Id); } // @Robustness: Do we want to break if we don't find a class? - InputClassOffsets[InputClassCount] = (kbts_u16)(InputGlyph - CurrentGlyph); + InputGlyphs[InputClassCount] = InputGlyph; + InputClassOffsets[InputClassCount] = (kbts_u16)InputGlyphsTraversed; InputClasses[InputClassCount] = InputClass.Class; LookaheadClasses[InputClassCount] = LookaheadClass.Class; InputClassCount += 1; } - InputGlyph += 1; + InputGlyph = InputGlyph->Next; + InputGlyphsTraversed += 1; } if((BacktrackClassCount >= Unpacked.BacktrackCount) && (InputClassCount >= InputClassCountRequired) && - !kbts_BranchlessCompareArray16(BacktrackClasses, Unpacked.Backtrack, Unpacked.BacktrackCount) && - !kbts_BranchlessCompareArray16(InputClasses, Unpacked.Input, Unpacked.InputCount - 1) && - !kbts_BranchlessCompareArray16(LookaheadClasses + Unpacked.InputCount - 1, Unpacked.Lookahead, Unpacked.LookaheadCount)) + !kbts__BranchlessCompareArray16(BacktrackClasses, Unpacked.Backtrack, Unpacked.BacktrackCount) && + !kbts__BranchlessCompareArray16(InputClasses, Unpacked.Input, Unpacked.InputCount - 1) && + !kbts__BranchlessCompareArray16(LookaheadClasses + Unpacked.InputCount - 1, Unpacked.Lookahead, Unpacked.LookaheadCount)) { Result.Records = Unpacked.Records; Result.RecordCount = Unpacked.RecordCount; @@ -17223,6 +20073,7 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L if(Unpacked.InputCount > 1) { Result.InputSequenceCountIncludingSkippedGlyphs = InputClassOffsets[Unpacked.InputCount - 2] + 1; + Result.OnePastLastGlyphMatched = InputGlyphs[Unpacked.InputCount - 2]->Next; } Result.Matched = 1; @@ -17236,36 +20087,36 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L case 0x60003: case 0x80003: { - kbts_chained_sequence_context_3 *Subst = (kbts_chained_sequence_context_3 *)Base; - kbts_unpacked_chained_sequence_context_3 Unpacked = kbts_UnpackChainedSequenceContext3(Subst, 0); + kbts__chained_sequence_context_3 *Subst = (kbts__chained_sequence_context_3 *)Base; + kbts__unpacked_chained_sequence_context_3 Unpacked = kbts__UnpackChainedSequenceContext3(Subst, 0); // Since chained sequence contexts roll the coverage for the first glyph into an array, you'd think that // the matching logic for the first glyph would be the same as for any other glyph in that array. // You'd be wrong! // The first glyph does _not_ have to pass glyph filtering logic. It just has to pass the coverage test. // Every other input glyph still does, though. - kbts_cover_glyph_result CurrentCover = KBTS_ZERO; - CurrentCover.Valid = !Unpacked.InputCount || kbts_GlyphPassesLookupFilter(InputGlyphs.Glyphs, Lookup); + kbts__cover_glyph_result CurrentCover = KBTS__ZERO; + CurrentCover.Valid = !Unpacked.InputCount || kbts__GlyphPassesLookupFilter(CurrentGlyph, Lookup); if(Unpacked.InputCount) { - kbts_coverage *CurrentCoverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.InputCoverageOffsets[0]); - CurrentCover = kbts_CoverGlyph(CurrentCoverage, InputGlyphs.Glyphs[0].Id); + kbts__coverage *CurrentCoverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.InputCoverageOffsets[0]); + CurrentCover = kbts__CoverGlyph(CurrentCoverage, CurrentGlyph->Id); } if(CurrentCover.Valid) { - kbts_sequence_match BacktrackMatch = kbts_MatchCoverageSequence(Lookup, SkipFlags, SkipUnicodeFlags, Subst, Unpacked.BacktrackCoverageOffsets, Unpacked.BacktrackCount, &BacktrackGlyphs, BacktrackGlyphs.Count - 1, -1); - kbts_sequence_match InputMatch = kbts_MatchCoverageSequence(Lookup, SkipFlags, SkipUnicodeFlags, Subst, Unpacked.InputCoverageOffsets + 1, Unpacked.InputCount - 1, &FollowupGlyphs, 0, 1); + kbts__sequence_match BacktrackMatch = kbts__MatchCoverageSequence(Storage, Lookup, SkipFlags, SkipUnicodeFlags, Subst, Unpacked.BacktrackCoverageOffsets, Unpacked.BacktrackCount, BacktrackGlyph, 1); + kbts__sequence_match InputMatch = kbts__MatchCoverageSequence(Storage, Lookup, SkipFlags, SkipUnicodeFlags, Subst, Unpacked.InputCoverageOffsets + 1, Unpacked.InputCount - 1, InputGlyph, 0); kbts_un MatchingInputGlyphCount = 1 + InputMatch.MatchCount; kbts_un MatchedOrSkippedInputGlyphCount = 1 + InputMatch.MatchOrSkipCount; - kbts_glyph_array LookaheadGlyphs = kbts_GlyphSubArray(&InputGlyphs, MatchedOrSkippedInputGlyphCount); - kbts_sequence_match LookaheadMatch = kbts_MatchCoverageSequence(Lookup, SkipFlags, SkipUnicodeFlags, Subst, Unpacked.LookaheadCoverageOffsets, Unpacked.LookaheadCount, &LookaheadGlyphs, 0, 1); + kbts__sequence_match LookaheadMatch = kbts__MatchCoverageSequence(Storage, Lookup, SkipFlags, SkipUnicodeFlags, Subst, Unpacked.LookaheadCoverageOffsets, Unpacked.LookaheadCount, InputMatch.OnePastMatchGlyph, 0); if((BacktrackMatch.MatchCount == Unpacked.BacktrackCount) && ((MatchingInputGlyphCount) == Unpacked.InputCount) && (LookaheadMatch.MatchCount == Unpacked.LookaheadCount)) { Result.Records = Unpacked.Records; Result.RecordCount = Unpacked.RecordCount; Result.InputSequenceCountIncludingSkippedGlyphs = MatchedOrSkippedInputGlyphCount; + Result.OnePastLastGlyphMatched = InputMatch.OnePastMatchGlyph; Result.Matched = 1; break; @@ -17275,11 +20126,11 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L break; } - KBTS_INSTRUMENT_END + KBTS_INSTRUMENT_FUNCTION_END; return Result; } -static void kbts_ApplyValueRecord(kbts_glyph *Glyph, kbts_unpacked_value_record *Unpacked) +static void kbts__ApplyValueRecord(kbts_glyph *Glyph, kbts__unpacked_value_record *Unpacked) { Glyph->OffsetX += Unpacked->PlacementX; Glyph->OffsetY += Unpacked->PlacementY; @@ -17288,38 +20139,28 @@ static void kbts_ApplyValueRecord(kbts_glyph *Glyph, kbts_unpacked_value_record Glyph->AdvanceY += Unpacked->AdvanceY; } -typedef struct kbts_indexed_glyph -{ - kbts_glyph *Glyph; - kbts_un Index; -} kbts_indexed_glyph; - -static int kbts_NextGlyph(kbts_unpacked_lookup *Lookup, kbts_glyph *InputGlyphs, kbts_un CurrentIndex, kbts_un InputGlyphCount, kbts_skip_flags SkipFlags, kbts_u32 SkipUnicodeFlags, kbts_indexed_glyph *IndexedGlyph) +static int kbts__NextGlyph(kbts_glyph_storage *Storage, kbts__unpacked_lookup *Lookup, kbts_glyph *AtGlyph, kbts__skip_flags SkipFlags, kbts_u32 SkipUnicodeFlags, kbts_glyph **Match, int Backward) { kbts_glyph *MatchingGlyph = 0; - kbts_un GlyphIndex = CurrentIndex; - while(GlyphIndex < InputGlyphCount) + while(kbts__GlyphIsValid(Storage, AtGlyph)) { - kbts_glyph *Glyph = &InputGlyphs[GlyphIndex]; - - if(!kbts_SkipGlyph(Glyph, Lookup, SkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(AtGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { - MatchingGlyph = Glyph; + MatchingGlyph = AtGlyph; break; } else { - GlyphIndex += 1; + AtGlyph = Backward ? AtGlyph->Prev : AtGlyph->Next; } } - IndexedGlyph->Glyph = MatchingGlyph; - IndexedGlyph->Index = GlyphIndex; + *Match = MatchingGlyph; return MatchingGlyph != 0; } -static void kbts_AttachGlyph(kbts_glyph_array *GlyphArray, kbts_glyph *Parent, kbts_glyph *Child, kbts_s32 X, kbts_s32 Y) +static void kbts__AttachGlyph(kbts_glyph_storage *Storage, kbts_glyph *Parent, kbts_glyph *Child, kbts_s32 X, kbts_s32 Y) { kbts_s32 DeltaOffsetX = X - Child->OffsetX; kbts_s32 DeltaOffsetY = Y - Child->OffsetY; @@ -17327,11 +20168,13 @@ static void kbts_AttachGlyph(kbts_glyph_array *GlyphArray, kbts_glyph *Parent, k Child->OffsetX = X; Child->OffsetY = Y; Child->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; - Child->AttachGlyphIndexPlusOne = (kbts_u16)(Parent - GlyphArray->Glyphs + 1); + Child->AttachGlyph = Parent; Parent->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; - for(kbts_glyph *MiddleGlyph = Parent + 1; MiddleGlyph < Child; ++MiddleGlyph) + for(kbts_glyph *MiddleGlyph = Parent->Next; + MiddleGlyph != Child; + MiddleGlyph = MiddleGlyph->Next) { MiddleGlyph->Flags |= KBTS_GLYPH_FLAG_NO_BREAK; } @@ -17339,144 +20182,167 @@ static void kbts_AttachGlyph(kbts_glyph_array *GlyphArray, kbts_glyph *Parent, k // @Speed: Fuck this. // Attachments can happen in anti-topological order, so we have to fix them up here. // We should store glyph parent/child indices instead of traversing all glyphs. - kbts_un ChildIndex = (kbts_un)(Child - GlyphArray->Glyphs); - KBTS_FOR(PostGlyphIndex, ChildIndex + 1, GlyphArray->Count) + for(kbts_glyph *PostGlyph = Child->Next; + kbts__GlyphIsValid(Storage, PostGlyph); + PostGlyph = PostGlyph->Next) { - kbts_glyph *Glyph = &GlyphArray->Glyphs[PostGlyphIndex]; - - if(Glyph->AttachGlyphIndexPlusOne == (ChildIndex + 1)) + if(PostGlyph->AttachGlyph == Child) { - Glyph->OffsetX += DeltaOffsetX; - Glyph->OffsetY += DeltaOffsetY; + PostGlyph->OffsetX += DeltaOffsetX; + PostGlyph->OffsetY += DeltaOffsetY; } } } -typedef struct kbts_do_single_adjustment_result +static void kbts__SetLookupOnePastLastGlyph(kbts__shape_scratchpad *Scratchpad, kbts_un Index, kbts_glyph *Glyph) { - kbts_un PositionedGlyphCount; - kbts_u32 PerformedAdjustment; -} kbts_do_single_adjustment_result; -static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_config *Config, kbts_lookup_list *LookupList, kbts_un LookupIndex, kbts_un SubtableIndex, kbts_unpacked_lookup *Lookup, kbts_u16 *Base, - kbts_glyph_array *GlyphArray, kbts_un CurrentGlyphIndex, kbts_skip_flags RequestedSkipFlags) -{ - KBTS_INSTRUMENT_FUNCTION_BEGIN - kbts_unicode_flags SkipUnicodeFlags = KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE; - kbts_skip_flags RegularSkipFlags = KBTS_SKIP_FLAGS_GPOS_REGULAR(RequestedSkipFlags); - kbts_skip_flags SequenceSkipFlags = KBTS_SKIP_FLAGS_GPOS_SEQUENCE(RequestedSkipFlags); - kbts_do_single_adjustment_result Result = KBTS_ZERO; - kbts_glyph *CurrentGlyph = &GlyphArray->Glyphs[CurrentGlyphIndex]; - - if(kbts_GlyphsIncludedInLookupSubtable(Config->Font, 1, Lookup, LookupIndex, SubtableIndex, GlyphArray, CurrentGlyphIndex, SequenceSkipFlags, SkipUnicodeFlags)) + if(Index > Scratchpad->LookupOnePastLastGlyphIndex) { - Result.PerformedAdjustment = 1; + Scratchpad->LookupOnePastLastGlyphIndex = (kbts_u32)Index; + Scratchpad->LookupOnePastLastGlyph = Glyph; + } +} - // CAREFUL: We want kbts_unpacked_lookup to be a useful bag-of-arguments type, but, for extension +typedef kbts_u32 kbts__cursive_flags; +enum kbts__cursive_flags_enum +{ + KBTS__CURSIVE_FLAG_NONE, + KBTS__CURSIVE_FLAG_START = (1 << 0), + KBTS__CURSIVE_FLAG_END = (1 << 1), +}; + +KBTS_INLINE kbts__cursive_flags kbts__GetCursiveFlags(kbts_glyph *Glyph) +{ + kbts__cursive_flags Result = (kbts__cursive_flags)Glyph->MarkOrdering; + return Result; +} + +KBTS_INLINE void kbts__SetCursiveFlags(kbts_glyph *Glyph, kbts__cursive_flags CursiveFlags) +{ + Glyph->MarkOrdering = (kbts_u8)CursiveFlags; +} + +static int kbts__DoSingleAdjustment(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_storage *Storage, + kbts_lookup_list *LookupList, kbts_un LookupIndex, kbts_un SubtableIndex, kbts__unpacked_lookup *Lookup, kbts_u16 *Base, + kbts_glyph *CurrentGlyph, kbts_un StartIndex, kbts__skip_flags RequestedSkipFlags) +{ + KBTS_INSTRUMENT_FUNCTION_BEGIN; + kbts_unicode_flags SkipUnicodeFlags = KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE; + kbts__skip_flags RegularSkipFlags = KBTS__SKIP_FLAGS_GPOS_REGULAR(RequestedSkipFlags); + kbts__skip_flags SequenceSkipFlags = KBTS__SKIP_FLAGS_GPOS_SEQUENCE(RequestedSkipFlags); + + int Result = 0; + + kbts_un OnePastLastGlyphIndex = StartIndex + 1; + kbts_glyph *OnePastLastGlyph = CurrentGlyph->Next; + + if(kbts__GlyphsIncludedInLookupSubtable(Storage, Config->Font, 1, Lookup, LookupIndex, SubtableIndex, CurrentGlyph, SequenceSkipFlags, SkipUnicodeFlags)) + { + // CAREFUL: We want kbts__unpacked_lookup to be a useful bag-of-arguments type, but, for extension // lookups, each subtable may specify its own lookup type, so we save it here and restore it at // the end of this function. kbts_u16 OriginalLookupType = Lookup->Type; while(Lookup->Type == 9) { - kbts_extension *Extension = (kbts_extension *)Base; + kbts__extension *Extension = (kbts__extension *)Base; Lookup->Type = Extension->LookupType; - Base = KBTS_POINTER_OFFSET(kbts_u16, Extension, Extension->Offset); + Base = KBTS__POINTER_OFFSET(kbts_u16, Extension, Extension->Offset); } - kbts_glyph *InputGlyphs = GlyphArray->Glyphs + CurrentGlyphIndex; - kbts_un InputGlyphCount = GlyphArray->Count - CurrentGlyphIndex; - - kbts_cover_glyph_result Cover = KBTS_ZERO; - Cover.Valid = kbts_GlyphPassesLookupFilter(CurrentGlyph, Lookup); - kbts_coverage *Coverage = 0; - if(Cover.Valid && kbts_GposLookupBeginsWithCoverage(Lookup->Type, Base[0])) + kbts__cover_glyph_result Cover = KBTS__ZERO; + Cover.Valid = kbts__GlyphPassesLookupFilter(CurrentGlyph, Lookup); + kbts__coverage *Coverage = 0; + if(Cover.Valid && kbts__GposLookupBeginsWithCoverage(Lookup->Type, Base[0])) { kbts_u16 *CoverageOffset = Base + 1; - Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Base, *CoverageOffset); - Cover = kbts_CoverGlyph(Coverage, CurrentGlyph->Id); + Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Base, *CoverageOffset); + Cover = kbts__CoverGlyph(Coverage, CurrentGlyph->Id); } if(Cover.Valid) { + kbts_un OnePastLastGlyphOffset = 0; + switch(Lookup->Type) { case 1: { - kbts_unpacked_value_record Unpacked = KBTS_ZERO; + kbts__unpacked_value_record Unpacked = KBTS__ZERO; if(Base[0] == 1) { - kbts_single_adjustment_1 *Adjust = (kbts_single_adjustment_1 *)Base; + kbts__single_adjustment_1 *Adjust = (kbts__single_adjustment_1 *)Base; - Unpacked = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat, KBTS_POINTER_AFTER(kbts_u16, Adjust)); + Unpacked = kbts__UnpackValueRecord(Adjust, Adjust->ValueFormat, KBTS__POINTER_AFTER(kbts_u16, Adjust)); } else if(Base[0] == 2) { - kbts_single_adjustment_2 *Adjust = (kbts_single_adjustment_2 *)Base; + kbts__single_adjustment_2 *Adjust = (kbts__single_adjustment_2 *)Base; - kbts_un RecordSize = kbts_PopCount32(Adjust->ValueFormat); - kbts_u16 *Records = KBTS_POINTER_AFTER(kbts_u16, Adjust); + kbts_un RecordSize = kbts__PopCount32(Adjust->ValueFormat); + kbts_u16 *Records = KBTS__POINTER_AFTER(kbts_u16, Adjust); kbts_u16 *Record = Records + RecordSize * Cover.Index; - Unpacked = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat, Record); + Unpacked = kbts__UnpackValueRecord(Adjust, Adjust->ValueFormat, Record); } - kbts_ApplyValueRecord(CurrentGlyph, &Unpacked); + kbts__ApplyValueRecord(CurrentGlyph, &Unpacked); CurrentGlyph->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; - Result.PositionedGlyphCount = 1; + Result = 1; } break; case 2: { - kbts_indexed_glyph NextGlyph; - if(kbts_NextGlyph(Lookup, InputGlyphs, 1, InputGlyphCount, RegularSkipFlags, SkipUnicodeFlags, &NextGlyph)) + kbts_glyph *NextGlyph; + if(kbts__NextGlyph(Storage, Lookup, CurrentGlyph->Next, RegularSkipFlags, SkipUnicodeFlags, &NextGlyph, 0)) { - kbts_u32 NextGlyphId = NextGlyph.Glyph->Id; + kbts_u32 NextGlyphId = NextGlyph->Id; - kbts_unpacked_value_record Unpacked1 = KBTS_ZERO; - kbts_unpacked_value_record Unpacked2 = KBTS_ZERO; + kbts__unpacked_value_record Unpacked1 = KBTS__ZERO; + kbts__unpacked_value_record Unpacked2 = KBTS__ZERO; int Valid = 0; if(Base[0] == 1) { - kbts_pair_adjustment_1 *Adjust = (kbts_pair_adjustment_1 *)Base; + kbts__pair_adjustment_1 *Adjust = (kbts__pair_adjustment_1 *)Base; - kbts_un Size1 = kbts_PopCount32(Adjust->ValueFormat1); - kbts_un Size2 = kbts_PopCount32(Adjust->ValueFormat2); + kbts_un Size1 = kbts__PopCount32(Adjust->ValueFormat1); + kbts_un Size2 = kbts__PopCount32(Adjust->ValueFormat2); - kbts_u16 *SetOffsets = KBTS_POINTER_AFTER(kbts_u16, Adjust); - kbts_pair_set *Set = KBTS_POINTER_OFFSET(kbts_pair_set, Adjust, SetOffsets[Cover.Index]); + kbts_u16 *SetOffsets = KBTS__POINTER_AFTER(kbts_u16, Adjust); + kbts__pair_set *Set = KBTS__POINTER_OFFSET(kbts__pair_set, Adjust, SetOffsets[Cover.Index]); kbts_un PairRecordSize = Size1 + Size2 + 1; // + 1 because each pair stores the next glyph ID. - kbts_pair_value_record *PairRecords = KBTS_POINTER_AFTER(kbts_pair_value_record, Set); - KBTS_FOR(PairIndex, 0, Set->Count) + kbts__pair_value_record *PairRecords = KBTS__POINTER_AFTER(kbts__pair_value_record, Set); + KBTS__FOR(PairIndex, 0, Set->Count) { - kbts_pair_value_record *PairRecord = PairRecords + PairRecordSize * PairIndex; + kbts__pair_value_record *PairRecord = PairRecords + PairRecordSize * PairIndex; if(PairRecord->SecondGlyph == NextGlyphId) { - kbts_u16 *Records = KBTS_POINTER_AFTER(kbts_u16, PairRecord); + kbts_u16 *Records = KBTS__POINTER_AFTER(kbts_u16, PairRecord); - Unpacked1 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat1, Records); + Unpacked1 = kbts__UnpackValueRecord(Adjust, Adjust->ValueFormat1, Records); Records += Unpacked1.Size; - Unpacked2 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat2, Records); + Unpacked2 = kbts__UnpackValueRecord(Adjust, Adjust->ValueFormat2, Records); Valid = 1; } } } else if(Base[0] == 2) { - kbts_pair_adjustment_2 *Adjust = (kbts_pair_adjustment_2 *)Base; - kbts_u16 *ClassDef1 = KBTS_POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition1Offset); - kbts_u16 *ClassDef2 = KBTS_POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition2Offset); + kbts__pair_adjustment_2 *Adjust = (kbts__pair_adjustment_2 *)Base; + kbts_u16 *ClassDef1 = KBTS__POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition1Offset); + kbts_u16 *ClassDef2 = KBTS__POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition2Offset); - kbts_un Size1 = kbts_PopCount32(Adjust->ValueFormat1); - kbts_un Size2 = kbts_PopCount32(Adjust->ValueFormat2); + kbts_un Size1 = kbts__PopCount32(Adjust->ValueFormat1); + kbts_un Size2 = kbts__PopCount32(Adjust->ValueFormat2); kbts_un PairRecordSize = Size1 + Size2; - kbts_u16 *PairRecords = KBTS_POINTER_AFTER(kbts_u16, Adjust); + kbts_u16 *PairRecords = KBTS__POINTER_AFTER(kbts_u16, Adjust); // From the Microsoft docs: // PairPosFormat2 requires that each glyph in all pairs be assigned to a class, which is @@ -17485,16 +20351,16 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi // class definition table, then we should skip the lookup. // However, this seems wrong in practice. Undefined classes seem to just default to 0, and then // the bounds check takes care of deciding whether the lookup is okay to apply or not. - kbts_glyph_class_from_table_result Class1 = kbts_GlyphClassFromTable(ClassDef1, CurrentGlyph->Id); - kbts_glyph_class_from_table_result Class2 = kbts_GlyphClassFromTable(ClassDef2, NextGlyphId); + kbts__glyph_class_from_table_result Class1 = kbts__GlyphClassFromTable(ClassDef1, CurrentGlyph->Id); + kbts__glyph_class_from_table_result Class2 = kbts__GlyphClassFromTable(ClassDef2, NextGlyphId); if((Class1.Class < Adjust->Class1Count) && (Class2.Class < Adjust->Class2Count)) { kbts_u16 *PairRecord = PairRecords + Class1.Class * PairRecordSize * Adjust->Class2Count + Class2.Class * PairRecordSize; - Unpacked1 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat1, PairRecord); + Unpacked1 = kbts__UnpackValueRecord(Adjust, Adjust->ValueFormat1, PairRecord); PairRecord += Size1; - Unpacked2 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat2, PairRecord); + Unpacked2 = kbts__UnpackValueRecord(Adjust, Adjust->ValueFormat2, PairRecord); PairRecord += Size2; Valid = 1; } @@ -17502,17 +20368,14 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi if(Valid) { - kbts_ApplyValueRecord(CurrentGlyph, &Unpacked1); + kbts__ApplyValueRecord(CurrentGlyph, &Unpacked1); CurrentGlyph->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; - kbts_ApplyValueRecord(&InputGlyphs[NextGlyph.Index], &Unpacked2); - NextGlyph.Glyph->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; + kbts__ApplyValueRecord(NextGlyph, &Unpacked2); + NextGlyph->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; - Result.PositionedGlyphCount = 2; - if(!Unpacked2.Size) - { - Result.PositionedGlyphCount = 1; - } + Result = 1; + OnePastLastGlyph = Unpacked2.Size ? NextGlyph->Next : CurrentGlyph->Next; } } } @@ -17521,35 +20384,25 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi // All three types of attachment (cursive, mark-to-base, mark-to-ligature) look backward instead of forward. case 3: { - kbts_cursive_attachment *Adjust = (kbts_cursive_attachment *)Base; - kbts_entry_exit *EntryExits = KBTS_POINTER_AFTER(kbts_entry_exit, Adjust); + kbts__cursive_attachment *Adjust = (kbts__cursive_attachment *)Base; + kbts__entry_exit *EntryExits = KBTS__POINTER_AFTER(kbts__entry_exit, Adjust); - kbts_glyph *Prev = 0; - for(kbts_un PrevIndex = CurrentGlyphIndex; PrevIndex > 0; --PrevIndex) - { - kbts_glyph *PrevGlyph = &GlyphArray->Glyphs[PrevIndex - 1]; - if(!kbts_SkipGlyph(PrevGlyph, Lookup, RegularSkipFlags, SkipUnicodeFlags)) - { - Prev = PrevGlyph; - break; - } - } - - if(Prev) + kbts_glyph *Prev; + if(kbts__NextGlyph(Storage, Lookup, CurrentGlyph->Prev, RegularSkipFlags, SkipUnicodeFlags, &Prev, 1)) { // For two cursive glyphs to be aligned, they both need to be defined by the same cursive attachment table. - kbts_cover_glyph_result PrevCover = kbts_CoverGlyph(Coverage, Prev->Id); + kbts__cover_glyph_result PrevCover = kbts__CoverGlyph(Coverage, Prev->Id); if(PrevCover.Valid) { // Get anchor points for both glyphs. - kbts_entry_exit *PrevEntryExit = &EntryExits[PrevCover.Index]; - kbts_entry_exit *EntryExit = &EntryExits[Cover.Index]; + kbts__entry_exit *PrevEntryExit = &EntryExits[PrevCover.Index]; + kbts__entry_exit *EntryExit = &EntryExits[Cover.Index]; if(PrevEntryExit->ExitAnchorOffset && EntryExit->EntryAnchorOffset) { - kbts_anchor *PrevExitAnchor = KBTS_POINTER_OFFSET(kbts_anchor, Adjust, PrevEntryExit->ExitAnchorOffset); - kbts_anchor *EntryAnchor = KBTS_POINTER_OFFSET(kbts_anchor, Adjust, EntryExit->EntryAnchorOffset); + kbts__anchor *PrevExitAnchor = KBTS__POINTER_OFFSET(kbts__anchor, Adjust, PrevEntryExit->ExitAnchorOffset); + kbts__anchor *EntryAnchor = KBTS__POINTER_OFFSET(kbts__anchor, Adjust, EntryExit->EntryAnchorOffset); kbts_s32 Anchor0X = PrevExitAnchor->X; kbts_s32 Anchor0Y = PrevExitAnchor->Y; kbts_s32 Anchor1X = EntryAnchor->X; @@ -17597,7 +20450,7 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi // invalid positions. The hope is that this does not matter in practice, because 'mark' is applied after // 'curs', and font designers are expected to know that and to know never to apply mark attachments // before cursive positioning, I guess. - if(!kbts_ShaperRtl(Config->Shaper)) + if(!kbts__ShaperRtl(Config->Shaper)) { Advance0X = Offset0X + Anchor0X; kbts_s32 Dx = -Anchor1X - Offset1X; @@ -17615,13 +20468,18 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi Prev->AdvanceX = Advance0X; Prev->OffsetX = Offset0X; Prev->Flags |= (KBTS_GLYPH_FLAG_CURSIVE | KBTS_GLYPH_FLAG_USED_IN_GPOS | KBTS_GLYPH_FLAG_NO_BREAK); + kbts__SetCursiveFlags(Prev, KBTS__CURSIVE_FLAG_START); CurrentGlyph->AdvanceX = Advance1X; CurrentGlyph->OffsetX = Offset1X; - CurrentGlyph->Flags |= (KBTS_GLYPH_FLAG_CURSIVE | KBTS_GLYPH_FLAG_USED_IN_GPOS); + CurrentGlyph->Flags |= KBTS_GLYPH_FLAG_CURSIVE | KBTS_GLYPH_FLAG_USED_IN_GPOS; + kbts__SetCursiveFlags(CurrentGlyph, KBTS__CURSIVE_FLAG_END); - for(kbts_glyph *CursiveGlyph = Prev + 1; CursiveGlyph < CurrentGlyph; ++CursiveGlyph) + for(kbts_glyph *CursiveGlyph = Prev->Next; + CursiveGlyph != CurrentGlyph; + CursiveGlyph = CursiveGlyph->Next) { CursiveGlyph->Flags |= KBTS_GLYPH_FLAG_NO_BREAK; + kbts__SetCursiveFlags(CursiveGlyph, 0); } // The second part is aligning the newly-formed cursive cluster to the "baseline". It is trickier than you'd expect. @@ -17639,10 +20497,11 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi // matters insofar as we need to align every other cursive glyph in its direction. kbts_s32 CursiveDy = 0; - kbts_un CursiveStartIndex = CurrentGlyphIndex; - kbts_un DIndex = 1; + kbts_glyph *CursiveGlyph = CurrentGlyph; + int Backward = 0; + kbts_u32 AdjustNearbyGlyphs = (CurrentGlyph->Flags & KBTS_GLYPH_FLAG_CURSIVE); - if(!(Lookup->Flags & KBTS_LOOKUP_FLAG_RIGHT_TO_LEFT)) + if(!(Lookup->Flags & KBTS__LOOKUP_FLAG_RIGHT_TO_LEFT)) { kbts_s32 NewOffset1Y = Offset0Y + Anchor0Y - Anchor1Y; CursiveDy = NewOffset1Y - Offset1Y; @@ -17651,25 +20510,42 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi { kbts_s32 NewOffset0Y = Offset1Y + Anchor1Y - Anchor0Y; CursiveDy = NewOffset0Y - Offset0Y; - DIndex = -1; - CursiveStartIndex = (kbts_un)(Prev - GlyphArray->Glyphs); + Backward = 1; + CursiveGlyph = Prev; + AdjustNearbyGlyphs = 1; } - for(kbts_un CursiveGlyphIndex = CursiveStartIndex; CursiveGlyphIndex < GlyphArray->Count; CursiveGlyphIndex += DIndex) + CursiveGlyph->OffsetY += CursiveDy; + + if(AdjustNearbyGlyphs) { - kbts_glyph *CursiveGlyph = &GlyphArray->Glyphs[CursiveGlyphIndex]; + kbts__SetCursiveFlags(CursiveGlyph, 0); + CursiveGlyph = Backward ? CursiveGlyph->Prev : CursiveGlyph->Next; - if(CursiveGlyph->Flags & KBTS_GLYPH_FLAG_CURSIVE) + kbts__cursive_flags StopFlag = Backward ? KBTS__CURSIVE_FLAG_END : KBTS__CURSIVE_FLAG_START; + + while(kbts__GlyphIsValid(Storage, CursiveGlyph)) { - CursiveGlyph->OffsetY += CursiveDy; - } - else if(CursiveGlyph->AdvanceX | CursiveGlyph->AdvanceY) // Ignore marks. - { - break; + kbts__cursive_flags CursiveFlags = kbts__GetCursiveFlags(CursiveGlyph); + + if(CursiveFlags & StopFlag) + { + break; + } + else if(CursiveGlyph->Flags & KBTS_GLYPH_FLAG_CURSIVE) + { + CursiveGlyph->OffsetY += CursiveDy; + } + else if(!(CursiveGlyph->Classes.Class & KBTS__GLYPH_CLASS_MARK)) // Ignore marks. + { + break; + } + + CursiveGlyph = Backward ? CursiveGlyph->Prev : CursiveGlyph->Next; } } - Result.PositionedGlyphCount = 1; + Result = 1; } } } @@ -17679,7 +20555,7 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi case 4: case 6: { - kbts_mark_to_base_attachment *Adjust = (kbts_mark_to_base_attachment *)Base; + kbts__mark_to_base_attachment *Adjust = (kbts__mark_to_base_attachment *)Base; // We want to know which glyph to attach to, and how far that glyph is from us. // To do that, we add up all advances between it and us. @@ -17688,27 +20564,51 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi // We have to take that into account here, and only accumulate mark advances for shapers that // don't do that zeroing at the end (either because they do it at the beginning of GPOS, or // because they don't do it at all). - int CountMarkAdvances = !kbts_ShaperClearsMarkAdvancesInPostGposFixup(Config->Shaper); + int CountMarkAdvances = !kbts__ShaperClearsMarkAdvancesInPostGposFixup(Config->Shaper); + kbts__coverage *BaseCoverage = KBTS__POINTER_OFFSET(kbts__coverage, Adjust, Adjust->BaseCoverageOffset); kbts_s32 AdvanceSinceBaseX = 0; kbts_s32 AdvanceSinceBaseY = 0; - kbts_u32 BaseClasses = Lookup->Type == 6 ? (1 << KBTS_GLYPH_CLASS_MARK) : (1 | (1 << KBTS_GLYPH_CLASS_BASE) | (1 << KBTS_GLYPH_CLASS_LIGATURE) | (1 << KBTS_GLYPH_CLASS_COMPONENT)); + kbts_u32 BaseClasses = Lookup->Type == 6 ? (1 << KBTS__GLYPH_CLASS_MARK) : (1 | (1 << KBTS__GLYPH_CLASS_BASE) | (1 << KBTS__GLYPH_CLASS_LIGATURE) | (1 << KBTS__GLYPH_CLASS_COMPONENT)); kbts_glyph *BaseGlyph = 0; - for(kbts_un PrevGlyphIndex = CurrentGlyphIndex; PrevGlyphIndex; --PrevGlyphIndex) + for(kbts_glyph *PrevGlyph = CurrentGlyph->Prev; kbts__GlyphIsValid(Storage, PrevGlyph); PrevGlyph = PrevGlyph->Prev) { - kbts_glyph *PrevGlyph = &GlyphArray->Glyphs[PrevGlyphIndex - 1]; - - if(CountMarkAdvances || (PrevGlyph->Classes.Class != KBTS_GLYPH_CLASS_MARK)) + if(CountMarkAdvances || (PrevGlyph->Classes.Class != KBTS__GLYPH_CLASS_MARK)) { AdvanceSinceBaseX += PrevGlyph->AdvanceX; AdvanceSinceBaseY += PrevGlyph->AdvanceY; } - if(!kbts_SkipGlyph(PrevGlyph, Lookup, RegularSkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(PrevGlyph, Lookup, RegularSkipFlags, SkipUnicodeFlags)) { if((1 << PrevGlyph->Classes.Class) & BaseClasses) { - BaseGlyph = PrevGlyph; - break; + // :MultipleSubstSadness + // There is some sadness when we have to look for bases here... + // In multiple substitutions, we allow skipping covered glyphs if they are: + // - Not the first in the multiple substitution + // - Not preceded by a mark + // + // More details on the sadness can be found here: + // https://github.com/harfbuzz/harfbuzz/issues/740 + // https://github.com/harfbuzz/harfbuzz/issues/1020 + // https://github.com/harfbuzz/harfbuzz/issues/4124 + if((Lookup->Type == 4) && + ((PrevGlyph->Flags & (KBTS_GLYPH_FLAG_FIRST_IN_MULTIPLE_SUBSTITUTION | KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION)) == KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION) && // Stop if we see the first of a multiple substitution. + (PrevGlyph->Prev->Classes.Class != KBTS__GLYPH_CLASS_MARK)) // Stop if we see any mark. + { + // Otherwise, we allow skipping uncovered glyphs. + kbts__cover_glyph_result BaseCover = kbts__CoverGlyph(BaseCoverage, PrevGlyph->Id); + if(BaseCover.Valid) + { + BaseGlyph = PrevGlyph; + break; + } + } + else + { + BaseGlyph = PrevGlyph; + break; + } } else if(Lookup->Type == 6) { @@ -17725,18 +20625,19 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi ((BaseGlyph->Flags | CurrentGlyph->Flags) & KBTS_GLYPH_FLAG_LIGATURE); // This is a mark-to-mark attachment, and either mark was created by a ligature substitution if(Ok) { - kbts_cover_glyph_result BaseCover = kbts_CoverGlyph(KBTS_POINTER_OFFSET(kbts_coverage, Adjust, Adjust->BaseCoverageOffset), BaseGlyph->Id); + // @Speed: This is duplicating work in the :MultipleSubstSadness case. + kbts__cover_glyph_result BaseCover = kbts__CoverGlyph(BaseCoverage, BaseGlyph->Id); if(BaseCover.Valid) { - kbts_base_array *BaseArray = KBTS_POINTER_OFFSET(kbts_base_array, Adjust, Adjust->BaseArrayOffset); - kbts_u16 *BaseAnchorOffsets = KBTS_POINTER_AFTER(kbts_u16, BaseArray) + BaseCover.Index * Adjust->MarkClassCount; - kbts_mark_info MarkInfo = kbts_GetMarkInfo(Adjust, Adjust->MarkArrayOffset, Cover.Index); + kbts__base_array *BaseArray = KBTS__POINTER_OFFSET(kbts__base_array, Adjust, Adjust->BaseArrayOffset); + kbts_u16 *BaseAnchorOffsets = KBTS__POINTER_AFTER(kbts_u16, BaseArray) + BaseCover.Index * Adjust->MarkClassCount; + kbts__mark_info MarkInfo = kbts__GetMarkInfo(Adjust, Adjust->MarkArrayOffset, Cover.Index); kbts_u16 BaseAnchorOffset = BaseAnchorOffsets[MarkInfo.Record->Class]; if(BaseAnchorOffset) { - kbts_anchor *BaseAnchor = KBTS_POINTER_OFFSET(kbts_anchor, BaseArray, BaseAnchorOffset); + kbts__anchor *BaseAnchor = KBTS__POINTER_OFFSET(kbts__anchor, BaseArray, BaseAnchorOffset); /* From the Microsoft docs: When a mark is combined with a given base, the mark placement is adjusted so that the mark anchor is @@ -17747,9 +20648,9 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi kbts_s32 NewOffsetX = BaseGlyph->OffsetX - AdvanceSinceBaseX + (BaseAnchor->X - MarkInfo.Anchor->X); kbts_s32 NewOffsetY = BaseGlyph->OffsetY - AdvanceSinceBaseY + (BaseAnchor->Y - MarkInfo.Anchor->Y); - kbts_AttachGlyph(GlyphArray, BaseGlyph, CurrentGlyph, NewOffsetX, NewOffsetY); + kbts__AttachGlyph(Storage, BaseGlyph, CurrentGlyph, NewOffsetX, NewOffsetY); - Result.PositionedGlyphCount = 1; + Result = 1; } } } @@ -17759,18 +20660,19 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi case 5: { - kbts_mark_to_ligature_attachment *Adjust = (kbts_mark_to_ligature_attachment *)Base; + kbts__mark_to_ligature_attachment *Adjust = (kbts__mark_to_ligature_attachment *)Base; kbts_s32 AdvanceSinceBaseX = 0; kbts_s32 AdvanceSinceBaseY = 0; - kbts_u32 BaseClasses = (1 | (1 << KBTS_GLYPH_CLASS_BASE) | (1 << KBTS_GLYPH_CLASS_LIGATURE) | (1 << KBTS_GLYPH_CLASS_COMPONENT)); + kbts_u32 BaseClasses = (1 | (1 << KBTS__GLYPH_CLASS_BASE) | (1 << KBTS__GLYPH_CLASS_LIGATURE) | (1 << KBTS__GLYPH_CLASS_COMPONENT)); kbts_glyph *LigatureGlyph = 0; - for(kbts_un PrevGlyphIndex = CurrentGlyphIndex; PrevGlyphIndex; --PrevGlyphIndex) + for(kbts_glyph *PrevGlyph = CurrentGlyph->Prev; + kbts__GlyphIsValid(Storage, PrevGlyph); + PrevGlyph = PrevGlyph->Prev) { - kbts_glyph *PrevGlyph = &GlyphArray->Glyphs[PrevGlyphIndex - 1]; AdvanceSinceBaseX += PrevGlyph->AdvanceX; AdvanceSinceBaseY += PrevGlyph->AdvanceY; - if(!kbts_SkipGlyph(PrevGlyph, Lookup, RegularSkipFlags, SkipUnicodeFlags) && ((1 << PrevGlyph->Classes.Class) & BaseClasses)) + if(!kbts__SkipGlyph(PrevGlyph, Lookup, RegularSkipFlags, SkipUnicodeFlags) && ((1 << PrevGlyph->Classes.Class) & BaseClasses)) { LigatureGlyph = PrevGlyph; break; @@ -17779,25 +20681,25 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi if(LigatureGlyph) { - kbts_cover_glyph_result LigatureCover = kbts_CoverGlyph(KBTS_POINTER_OFFSET(kbts_coverage, Adjust, Adjust->LigatureCoverageOffset), + kbts__cover_glyph_result LigatureCover = kbts__CoverGlyph(KBTS__POINTER_OFFSET(kbts__coverage, Adjust, Adjust->LigatureCoverageOffset), LigatureGlyph->Id); if(LigatureCover.Valid) { - kbts_ligature_array *LigatureArray = KBTS_POINTER_OFFSET(kbts_ligature_array, Adjust, Adjust->LigatureArrayOffset); - kbts_ligature_attach *LigatureAttach = kbts_GetLigatureAttach(LigatureArray, LigatureCover.Index); - kbts_mark_info MarkInfo = kbts_GetMarkInfo(Adjust, Adjust->MarkArrayOffset, Cover.Index); - kbts_un LigatureComponentIndex = CurrentGlyph->LigatureComponentIndexPlusOne; + kbts__ligature_array *LigatureArray = KBTS__POINTER_OFFSET(kbts__ligature_array, Adjust, Adjust->LigatureArrayOffset); + kbts__ligature_attach *LigatureAttach = kbts__GetLigatureAttach(LigatureArray, LigatureCover.Index); + kbts__mark_info MarkInfo = kbts__GetMarkInfo(Adjust, Adjust->MarkArrayOffset, Cover.Index); + kbts_un LigatureComponentIndexPlusOne = CurrentGlyph->LigatureComponentIndexPlusOne; if(CurrentGlyph->LigatureUid != LigatureGlyph->Uid) { // If the mark is not yet attached to the ligature, attach it now. // Apparently, in this case, the mark should be considered part of the _last_ component of the ligature. - LigatureComponentIndex = LigatureAttach->Count; + LigatureComponentIndexPlusOne = LigatureAttach->Count; } - if(CurrentGlyph->LigatureComponentIndexPlusOne <= LigatureAttach->Count) + if(LigatureComponentIndexPlusOne <= LigatureAttach->Count) { - kbts_un AnchorIndex = LigatureComponentIndex; + kbts_un AnchorIndex = LigatureComponentIndexPlusOne; if(AnchorIndex) { AnchorIndex -= 1; @@ -17807,26 +20709,28 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi AnchorIndex = LigatureAttach->Count - 1; } - kbts_anchor *LigatureAnchor = kbts_GetLigatureAttachAnchor(Adjust, LigatureAttach, MarkInfo.Record->Class, AnchorIndex); + kbts__anchor *LigatureAnchor = kbts__GetLigatureAttachAnchor(Adjust, LigatureAttach, MarkInfo.Record->Class, AnchorIndex); kbts_s32 NewOffsetX = LigatureGlyph->OffsetX - AdvanceSinceBaseX + (LigatureAnchor->X - MarkInfo.Anchor->X); kbts_s32 NewOffsetY = LigatureGlyph->OffsetY - AdvanceSinceBaseY + (LigatureAnchor->Y - MarkInfo.Anchor->Y); - kbts_AttachGlyph(GlyphArray, LigatureGlyph, CurrentGlyph, NewOffsetX, NewOffsetY); + kbts__AttachGlyph(Storage, LigatureGlyph, CurrentGlyph, NewOffsetX, NewOffsetY); - CurrentGlyph->LigatureComponentIndexPlusOne = (kbts_u16)LigatureComponentIndex; + CurrentGlyph->LigatureComponentIndexPlusOne = (kbts_u16)LigatureComponentIndexPlusOne; CurrentGlyph->LigatureUid = LigatureGlyph->Uid; // I'm not sure why, but Harfbuzz only clears this in mark-to-ligature substitutions, and not in mark-to-base or // mark-to-mark. CurrentGlyph->AdvanceX = 0; CurrentGlyph->AdvanceY = 0; - for(kbts_glyph *MiddleGlyph = LigatureGlyph + 1; MiddleGlyph < CurrentGlyph; ++MiddleGlyph) + for(kbts_glyph *MiddleGlyph = LigatureGlyph->Next; + MiddleGlyph != CurrentGlyph; + MiddleGlyph = MiddleGlyph->Next) { MiddleGlyph->Flags |= KBTS_GLYPH_FLAG_NO_BREAK; } - Result.PositionedGlyphCount = 1; + Result = 1; } } } @@ -17836,265 +20740,257 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi case 7: case 8: { - kbts_sequence_lookup_result SequenceLookup = kbts_DoSequenceLookup(Lookup, Base, Cover, GlyphArray, CurrentGlyphIndex, SequenceSkipFlags, SkipUnicodeFlags); + kbts__sequence_lookup_result SequenceLookup = kbts__DoSequenceLookup(Storage, Lookup, Base, Cover, CurrentGlyph, SequenceSkipFlags, SkipUnicodeFlags); if(SequenceLookup.RecordCount) { - KBTS_FOR(RecordIndex, 0, SequenceLookup.RecordCount) + KBTS__FOR(RecordIndex, 0, SequenceLookup.RecordCount) { - kbts_sequence_lookup_record *Record = &SequenceLookup.Records[RecordIndex]; - kbts_lookup *PackedRecordLookup = kbts_GetLookup(LookupList, Record->LookupListIndex); - kbts_unpacked_lookup RecordLookup = kbts_UnpackLookup(Config->Font->Gdef, PackedRecordLookup); + kbts__sequence_lookup_record *Record = &SequenceLookup.Records[RecordIndex]; + kbts__lookup *PackedRecordLookup = kbts__GetLookup(LookupList, Record->LookupListIndex); + kbts__unpacked_lookup RecordLookup = kbts__UnpackLookup(kbts__BlobTableDataType(Config->Font->Blob, KBTS_BLOB_TABLE_ID_GDEF, kbts__gdef), PackedRecordLookup); - kbts_un NestedCurrentGlyphIndex = CurrentGlyphIndex; + kbts_glyph *NestedCurrentGlyph = CurrentGlyph; + kbts_un NestedCurrentGlyphIndex = 0; { // Figure out where in the input sequence we need to apply the lookup. kbts_un SequenceIndex = 0; - KBTS_FOR(InputGlyphIndex, 0, InputGlyphCount) + for(kbts_glyph *Glyph = CurrentGlyph; + kbts__GlyphIsValid(Storage, Glyph); + Glyph = Glyph->Next) { - kbts_un NestedIndex = CurrentGlyphIndex + InputGlyphIndex; - kbts_glyph *Glyph = &GlyphArray->Glyphs[NestedIndex]; - - if(!kbts_SkipGlyph(Glyph, Lookup, SequenceSkipFlags, SkipUnicodeFlags)) + if(!kbts__SkipGlyph(Glyph, Lookup, SequenceSkipFlags, SkipUnicodeFlags)) { if(SequenceIndex == Record->SequenceIndex) { - NestedCurrentGlyphIndex = NestedIndex; + NestedCurrentGlyph = Glyph; break; } SequenceIndex += 1; } + + NestedCurrentGlyphIndex += 1; } } - KBTS_FOR(NestedSubtableIndex, 0, RecordLookup.SubtableCount) + KBTS__FOR(NestedSubtableIndex, 0, RecordLookup.SubtableCount) { - kbts_u16 *NestedBase = KBTS_POINTER_OFFSET(kbts_u16, PackedRecordLookup, RecordLookup.SubtableOffsets[NestedSubtableIndex]); + kbts_u16 *NestedBase = KBTS__POINTER_OFFSET(kbts_u16, PackedRecordLookup, RecordLookup.SubtableOffsets[NestedSubtableIndex]); - kbts_DoSingleAdjustment(Config, LookupList, Record->LookupListIndex, NestedSubtableIndex, &RecordLookup, NestedBase, GlyphArray, NestedCurrentGlyphIndex, RequestedSkipFlags); + kbts__DoSingleAdjustment(Scratchpad, Config, Storage, LookupList, + Record->LookupListIndex, NestedSubtableIndex, &RecordLookup, NestedBase, + NestedCurrentGlyph, StartIndex + NestedCurrentGlyphIndex, RequestedSkipFlags); } } - Result.PositionedGlyphCount = SequenceLookup.InputSequenceCountIncludingSkippedGlyphs; + OnePastLastGlyphIndex = StartIndex + SequenceLookup.InputSequenceCountIncludingSkippedGlyphs; + OnePastLastGlyph = SequenceLookup.OnePastLastGlyphMatched; } } break; } + + kbts__SetLookupOnePastLastGlyph(Scratchpad, OnePastLastGlyphIndex + OnePastLastGlyphOffset, OnePastLastGlyph); } Lookup->Type = OriginalLookupType; } - if(!Result.PositionedGlyphCount) - { - Result.PositionedGlyphCount = 1; - Result.PerformedAdjustment = 0; - } - - KBTS_INSTRUMENT_END + KBTS_INSTRUMENT_FUNCTION_END; return Result; } -typedef kbts_u32 kbts_mcm_sequence_state; -enum kbts_mcm_sequence_state_enum +typedef kbts_u32 kbts__mcm_sequence_state; +enum kbts__mcm_sequence_state_enum { - KBTS_MCM_SEQUENCE_STATE_NONE, - KBTS_MCM_SEQUENCE_STATE_OUT, - KBTS_MCM_SEQUENCE_STATE_IN, + KBTS__MCM_SEQUENCE_STATE_NONE, + KBTS__MCM_SEQUENCE_STATE_OUT, + KBTS__MCM_SEQUENCE_STATE_IN, }; -typedef kbts_u32 kbts_hangul_syllable_type; -enum kbts_hangul_syllable_type_enum +typedef kbts_u32 kbts__hangul_syllable_type; +enum kbts__hangul_syllable_type_enum { - KBTS_HANGUL_SYLLABLE_TYPE_NONE, + KBTS__HANGUL_SYLLABLE_TYPE_NONE, - KBTS_HANGUL_SYLLABLE_TYPE_L, - KBTS_HANGUL_SYLLABLE_TYPE_V, - KBTS_HANGUL_SYLLABLE_TYPE_T, - KBTS_HANGUL_SYLLABLE_TYPE_LV, - KBTS_HANGUL_SYLLABLE_TYPE_LVT, + KBTS__HANGUL_SYLLABLE_TYPE_L, + KBTS__HANGUL_SYLLABLE_TYPE_V, + KBTS__HANGUL_SYLLABLE_TYPE_T, + KBTS__HANGUL_SYLLABLE_TYPE_LV, + KBTS__HANGUL_SYLLABLE_TYPE_LVT, - KBTS_HANGUL_SYLLABLE_TYPE_COUNT, + KBTS__HANGUL_SYLLABLE_TYPE_COUNT, }; -typedef struct kbts_hangul_syllable_info +typedef struct kbts__hangul_syllable_info { - kbts_hangul_syllable_type Type; + kbts__hangul_syllable_type Type; kbts_u32 Composable; -} kbts_hangul_syllable_info; +} kbts__hangul_syllable_info; -static kbts_hangul_syllable_info kbts_HangulSyllableInfo(kbts_u32 Codepoint) +static kbts__hangul_syllable_info kbts__HangulSyllableInfo(kbts_u32 Codepoint) { - kbts_hangul_syllable_info Result = KBTS_ZERO; + kbts__hangul_syllable_info Result = KBTS__ZERO; if((Codepoint >= 0x1100) && (Codepoint <= 0x115F)) { - Result.Type = KBTS_HANGUL_SYLLABLE_TYPE_L; + Result.Type = KBTS__HANGUL_SYLLABLE_TYPE_L; Result.Composable = (Codepoint < 0x1113); } else if((Codepoint >= 0x1160) && (Codepoint <= 0x11A7)) { - Result.Type = KBTS_HANGUL_SYLLABLE_TYPE_V; + Result.Type = KBTS__HANGUL_SYLLABLE_TYPE_V; Result.Composable = ((Codepoint >= 0x1161) & (Codepoint <= 0x1175)); } else if((Codepoint >= 0x11A8) && (Codepoint <= 0x11FF)) { - Result.Type = KBTS_HANGUL_SYLLABLE_TYPE_T; + Result.Type = KBTS__HANGUL_SYLLABLE_TYPE_T; Result.Composable = (Codepoint < 0x11C3); } else if((Codepoint >= 0xA960) && (Codepoint <= 0xA97C)) { - Result.Type = KBTS_HANGUL_SYLLABLE_TYPE_L; + Result.Type = KBTS__HANGUL_SYLLABLE_TYPE_L; } else if((Codepoint == 0xAC00) && (Codepoint <= 0xD7A3)) { - Result.Type = KBTS_HANGUL_SYLLABLE_TYPE_LVT; + Result.Type = KBTS__HANGUL_SYLLABLE_TYPE_LVT; if(!((Codepoint - 0xAC00) % 28)) { - Result.Type = KBTS_HANGUL_SYLLABLE_TYPE_LV; + Result.Type = KBTS__HANGUL_SYLLABLE_TYPE_LV; } } else if((Codepoint >= 0xD7B0) && (Codepoint <= 0xD7C6)) { - Result.Type = KBTS_HANGUL_SYLLABLE_TYPE_V; + Result.Type = KBTS__HANGUL_SYLLABLE_TYPE_V; } else if((Codepoint >= 0xD7CB) && (Codepoint <= 0xD7FB)) { - Result.Type = KBTS_HANGUL_SYLLABLE_TYPE_T; + Result.Type = KBTS__HANGUL_SYLLABLE_TYPE_T; } return Result; } -KBTS_INLINE kbts_u32 kbts_SyllabicClassIsConsonant(kbts_indic_syllabic_class Class) +KBTS_INLINE kbts_u32 kbts__SyllabicClassIsConsonant(kbts_indic_syllabic_class Class) { kbts_u32 Result = - KBTS_IN_SET(Class, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_CONSONANT)(KBTS_INDIC_SYLLABIC_CLASS_RA)(KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_WITH_STACKER)(KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_MEDIAL))); + KBTS__IN_SET(Class, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_CONSONANT)(KBTS_INDIC_SYLLABIC_CLASS_RA)(KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_WITH_STACKER)(KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_MEDIAL))); return Result; } enum { - KBTS_FEATURE_MATCH_FLAG_KA_JA = (1 << 0), - KBTS_FEATURE_MATCH_FLAG_SSA_NYA = (1 << 1), - KBTS_FEATURE_MATCH_FLAG_RA_HA_VA = (1 << 2), - KBTS_FEATURE_MATCH_FLAG_YA = (1 << 3), - KBTS_FEATURE_MATCH_FLAG_CONSONANT = (1 << 4), - KBTS_FEATURE_MATCH_FLAG_NUKTA = (1 << 5), - KBTS_FEATURE_MATCH_FLAG_HALANT = (1 << 6), - KBTS_FEATURE_MATCH_FLAG_ZWJ = (1 << 7), - KBTS_FEATURE_MATCH_FLAG_ZWNJ = (1 << 8), - KBTS_FEATURE_MATCH_FLAG_INITIAL = (1 << 10), - KBTS_FEATURE_MATCH_FLAG_POST_BASE = (1 << 11), + KBTS__FEATURE_MATCH_FLAG_KA_JA = (1 << 0), + KBTS__FEATURE_MATCH_FLAG_SSA_NYA = (1 << 1), + KBTS__FEATURE_MATCH_FLAG_RA_HA_VA = (1 << 2), + KBTS__FEATURE_MATCH_FLAG_YA = (1 << 3), + KBTS__FEATURE_MATCH_FLAG_CONSONANT = (1 << 4), + KBTS__FEATURE_MATCH_FLAG_NUKTA = (1 << 5), + KBTS__FEATURE_MATCH_FLAG_HALANT = (1 << 6), + KBTS__FEATURE_MATCH_FLAG_ZWJ = (1 << 7), + KBTS__FEATURE_MATCH_FLAG_ZWNJ = (1 << 8), + KBTS__FEATURE_MATCH_FLAG_INITIAL = (1 << 10), + KBTS__FEATURE_MATCH_FLAG_POST_BASE = (1 << 11), - KBTS_FEATURE_MATCH_FLAG_RPHF = KBTS_FEATURE_FLAG0(rphf), + KBTS__FEATURE_MATCH_FLAG_RPHF = KBTS__FEATURE_FLAG0(rphf), }; -# define KBTS_FEATURE_MATCH_MASK(W0, W1, W2, W3) (((kbts_u64)(W0) << 48) | ((kbts_u64)(W1) << 32) | ((kbts_u64)(W0) << 16) | (kbts_u64)(W0)) -# define KBTS_FEATURE_MATCH_MASK_AKHN KBTS_FEATURE_MATCH_MASK(0, KBTS_FEATURE_MATCH_FLAG_KA_JA, KBTS_FEATURE_MATCH_FLAG_HALANT, KBTS_FEATURE_MATCH_FLAG_SSA_NYA) -# define KBTS_FEATURE_MATCH_MASK_BLWF_PRE KBTS_FEATURE_MATCH_MASK(0, 0, KBTS_FEATURE_MATCH_FLAG_RA_HA_VA | KBTS_FEATURE_MATCH_FLAG_INITIAL, KBTS_FEATURE_MATCH_FLAG_HALANT) -# define KBTS_FEATURE_MATCH_MASK_BLWF_PRE_OK KBTS_FEATURE_MATCH_MASK(0, 0, KBTS_FEATURE_MATCH_FLAG_RA_HA_VA, KBTS_FEATURE_MATCH_FLAG_HALANT) -# define KBTS_FEATURE_MATCH_MASK_BLWF_POST KBTS_FEATURE_MATCH_MASK(0, 0, KBTS_FEATURE_MATCH_FLAG_HALANT, KBTS_FEATURE_MATCH_FLAG_RA_HA_VA) -# define KBTS_FEATURE_MATCH_MASK_CJCT KBTS_FEATURE_MATCH_MASK(0, KBTS_FEATURE_MATCH_FLAG_CONSONANT, KBTS_FEATURE_MATCH_FLAG_HALANT, KBTS_FEATURE_MATCH_FLAG_CONSONANT) -# define KBTS_FEATURE_MATCH_MASK_HALF KBTS_FEATURE_MATCH_MASK(KBTS_FEATURE_MATCH_FLAG_CONSONANT | KBTS_FEATURE_MATCH_FLAG_RPHF | KBTS_FEATURE_MATCH_FLAG_POST_BASE, KBTS_FEATURE_MATCH_FLAG_HALANT, KBTS_FEATURE_MATCH_FLAG_ZWNJ, KBTS_FEATURE_MATCH_FLAG_CONSONANT) -# define KBTS_FEATURE_MATCH_MASK_HALF_OK KBTS_FEATURE_MATCH_MASK(KBTS_FEATURE_MATCH_FLAG_CONSONANT, KBTS_FEATURE_MATCH_FLAG_HALANT, 0, 0) -# define KBTS_FEATURE_MATCH_MASK_NUKT KBTS_FEATURE_MATCH_MASK(0, 0, KBTS_FEATURE_MATCH_FLAG_CONSONANT, KBTS_FEATURE_MATCH_FLAG_HALANT) -# define KBTS_FEATURE_MATCH_MASK_PSTF KBTS_FEATURE_MATCH_MASK(0, 0, KBTS_FEATURE_MATCH_FLAG_HALANT | KBTS_FEATURE_MATCH_FLAG_INITIAL, KBTS_FEATURE_MATCH_FLAG_YA) -# define KBTS_FEATURE_MATCH_MASK_VATU KBTS_FEATURE_MATCH_MASK(0, KBTS_FEATURE_MATCH_FLAG_CONSONANT, KBTS_FEATURE_MATCH_FLAG_HALANT, KBTS_FEATURE_MATCH_FLAG_RA_HA_VA) +# define KBTS__FEATURE_MATCH_MASK(W0, W1, W2, W3) (((kbts_u64)(W0) << 48) | ((kbts_u64)(W1) << 32) | ((kbts_u64)(W0) << 16) | (kbts_u64)(W0)) +# define KBTS__FEATURE_MATCH_MASK_AKHN KBTS__FEATURE_MATCH_MASK(0, KBTS__FEATURE_MATCH_FLAG_KA_JA, KBTS__FEATURE_MATCH_FLAG_HALANT, KBTS__FEATURE_MATCH_FLAG_SSA_NYA) +# define KBTS__FEATURE_MATCH_MASK_BLWF_PRE KBTS__FEATURE_MATCH_MASK(0, 0, KBTS__FEATURE_MATCH_FLAG_RA_HA_VA | KBTS__FEATURE_MATCH_FLAG_INITIAL, KBTS__FEATURE_MATCH_FLAG_HALANT) +# define KBTS__FEATURE_MATCH_MASK_BLWF_PRE_OK KBTS__FEATURE_MATCH_MASK(0, 0, KBTS__FEATURE_MATCH_FLAG_RA_HA_VA, KBTS__FEATURE_MATCH_FLAG_HALANT) +# define KBTS__FEATURE_MATCH_MASK_BLWF_POST KBTS__FEATURE_MATCH_MASK(0, 0, KBTS__FEATURE_MATCH_FLAG_HALANT, KBTS__FEATURE_MATCH_FLAG_RA_HA_VA) +# define KBTS__FEATURE_MATCH_MASK_CJCT KBTS__FEATURE_MATCH_MASK(0, KBTS__FEATURE_MATCH_FLAG_CONSONANT, KBTS__FEATURE_MATCH_FLAG_HALANT, KBTS__FEATURE_MATCH_FLAG_CONSONANT) +# define KBTS__FEATURE_MATCH_MASK_HALF KBTS__FEATURE_MATCH_MASK(KBTS__FEATURE_MATCH_FLAG_CONSONANT | KBTS__FEATURE_MATCH_FLAG_RPHF | KBTS__FEATURE_MATCH_FLAG_POST_BASE, KBTS__FEATURE_MATCH_FLAG_HALANT, KBTS__FEATURE_MATCH_FLAG_ZWNJ, KBTS__FEATURE_MATCH_FLAG_CONSONANT) +# define KBTS__FEATURE_MATCH_MASK_HALF_OK KBTS__FEATURE_MATCH_MASK(KBTS__FEATURE_MATCH_FLAG_CONSONANT, KBTS__FEATURE_MATCH_FLAG_HALANT, 0, 0) +# define KBTS__FEATURE_MATCH_MASK_NUKT KBTS__FEATURE_MATCH_MASK(0, 0, KBTS__FEATURE_MATCH_FLAG_CONSONANT, KBTS__FEATURE_MATCH_FLAG_HALANT) +# define KBTS__FEATURE_MATCH_MASK_PSTF KBTS__FEATURE_MATCH_MASK(0, 0, KBTS__FEATURE_MATCH_FLAG_HALANT | KBTS__FEATURE_MATCH_FLAG_INITIAL, KBTS__FEATURE_MATCH_FLAG_YA) +# define KBTS__FEATURE_MATCH_MASK_VATU KBTS__FEATURE_MATCH_MASK(0, KBTS__FEATURE_MATCH_FLAG_CONSONANT, KBTS__FEATURE_MATCH_FLAG_HALANT, KBTS__FEATURE_MATCH_FLAG_RA_HA_VA) -typedef kbts_u32 kbts_substitution_result_flags; -enum kbts_substitution_result_flags_enum +typedef kbts_u32 kbts__substitution_result_flags; +enum kbts__substitution_result_flags_enum { - KBTS_SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION = (1 << 0), - KBTS_SUBSTITUTION_RESULT_FLAG_GROW_BUFFER = (1 << 1), + KBTS__SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION = (1 << 0), + KBTS__SUBSTITUTION_RESULT_FLAG_TRIED_TO_INSERT_WHILE_CHECK_ONLY = (1 << 1), }; -static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *ShapeState, kbts_lookup_list *LookupList, kbts_gsub_frame *Frames, kbts_u32 *FrameCount_, - kbts_glyph_array *GlyphArray, kbts_u32 CheckOnly, kbts_skip_flags RequestedSkipFlags, kbts_u32 GeneratedGlyphFlags) +static void kbts__BeginLookupApplication(kbts__shape_scratchpad *Scratchpad, kbts_glyph *Glyph) { - kbts_substitution_result_flags Result = 0; - kbts_font *Font = ShapeState->Config->Font; - kbts_glyph *Glyphs = GlyphArray->Glyphs; + Scratchpad->LookupOnePastLastGlyph = Glyph->Next; + Scratchpad->LookupOnePastLastGlyphIndex = 0; +} + +static kbts_glyph *kbts__EndLookupApplication(kbts__shape_scratchpad *Scratchpad) +{ + kbts_glyph *Result = Scratchpad->LookupOnePastLastGlyph; + return Result; +} + +static kbts__substitution_result_flags kbts__DoSubstitution(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_storage *Storage, + kbts_lookup_list *LookupList, kbts__gsub_frame *Frames, kbts_un *FrameCount_, + int CheckOnly, kbts__skip_flags RequestedSkipFlags, kbts_u32 GeneratedGlyphFlags) +{ + kbts__substitution_result_flags Result = 0; + kbts_font *Font = Config->Font; kbts_unicode_flags SkipUnicodeFlags = 0; // @Incomplete - kbts_skip_flags RegularSkipFlags = KBTS_SKIP_FLAGS_GSUB_REGULAR(RequestedSkipFlags); - kbts_skip_flags SequenceSkipFlags = KBTS_SKIP_FLAGS_GSUB_SEQUENCE(RequestedSkipFlags); + kbts__skip_flags RegularSkipFlags = KBTS__SKIP_FLAGS_GSUB_REGULAR(RequestedSkipFlags); + kbts__skip_flags SequenceSkipFlags = KBTS__SKIP_FLAGS_GSUB_SEQUENCE(RequestedSkipFlags); GeneratedGlyphFlags |= KBTS_GLYPH_FLAG_GENERATED_BY_GSUB; kbts_un FrameCount = *FrameCount_; - kbts_gsub_frame *Frame = &Frames[FrameCount - 1]; + kbts__gsub_frame *Frame = &Frames[FrameCount - 1]; - kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, Frame->LookupIndex); - kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); + kbts__lookup *PackedLookup = kbts__GetLookup(LookupList, Frame->LookupIndex); + kbts__unpacked_lookup Lookup = kbts__UnpackLookup(kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_GDEF, kbts__gdef), PackedLookup); kbts_u16 BaseLookupType = Lookup.Type; while(Frame->SubtableIndex < Lookup.SubtableCount) { - kbts_u16 *Subtable = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[Frame->SubtableIndex]); + kbts_u16 *Subtable = KBTS__POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[Frame->SubtableIndex]); // In a type-7 lookup, each subtable can specify a different lookup type. - // We still want to pass kbts_unpacked_lookup around as a useful bag of arguments, though. + // We still want to pass kbts__unpacked_lookup around as a useful bag of arguments, though. // So, we restore the original lookup's type here before resolving each subtable. Lookup.Type = BaseLookupType; while(Lookup.Type == 7) { - kbts_extension *Extension = (kbts_extension *)Subtable; + kbts__extension *Extension = (kbts__extension *)Subtable; Lookup.Type = Extension->LookupType; - Subtable = KBTS_POINTER_OFFSET(kbts_u16, Extension, Extension->Offset); + Subtable = KBTS__POINTER_OFFSET(kbts_u16, Extension, Extension->Offset); } - // :ReverseChaining - // From the Microsoft docs: - // In processing a reverse chaining substitution, i begins at the logical end of the string and moves to the beginning. - // This comment only makes sense when the reverse chaining substitution happens at the top level. - // When it _is_ at the top level, we know we are just doing a linear scan through the string, and since this is a single - // substitution (i.e. one glyph -> one glyph), we know the size of the buffer won't change throughout the scan. - // Given all of this, handling the reverse-scanning nature of this lookup actually becomes very easy: - if((Lookup.Type == 8) && (FrameCount == 1)) - { - Frame->InputGlyphIndex = (kbts_u16)(GlyphArray->Count - 1 - Frame->InputGlyphIndex); - } + kbts_glyph *CurrentGlyph = Frame->InputGlyph; - kbts_glyph *CurrentGlyph = &Glyphs[Frame->InputGlyphIndex]; - kbts_glyph_array InputGlyphs = kbts_GlyphSubArray(GlyphArray, Frame->InputGlyphIndex); - - kbts_skip_flags SkipFlags = (Lookup.Type >= 5) ? SequenceSkipFlags : RegularSkipFlags; - if(kbts_GlyphsIncludedInLookupSubtable(Font, 0, &Lookup, Frame->LookupIndex, Frame->SubtableIndex, GlyphArray, Frame->InputGlyphIndex, SkipFlags, SkipUnicodeFlags)) + kbts__skip_flags SkipFlags = (Lookup.Type >= 5) ? SequenceSkipFlags : RegularSkipFlags; + if(kbts__GlyphsIncludedInLookupSubtable(Storage, Font, 0, &Lookup, Frame->LookupIndex, Frame->SubtableIndex, CurrentGlyph, SkipFlags, SkipUnicodeFlags)) { - kbts_cover_glyph_result Cover = KBTS_ZERO; - Cover.Valid = kbts_GlyphPassesLookupFilter(CurrentGlyph, &Lookup); - if(Cover.Valid && kbts_GsubLookupBeginsWithCoverage(Lookup.Type, Subtable[0])) + kbts__cover_glyph_result Cover = KBTS__ZERO; + Cover.Valid = kbts__GlyphPassesLookupFilter(CurrentGlyph, &Lookup); + if(Cover.Valid && kbts__GsubLookupBeginsWithCoverage(Lookup.Type, Subtable[0])) { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subtable, Subtable[1]); - Cover = kbts_CoverGlyph(Coverage, CurrentGlyph->Id); + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Subtable, Subtable[1]); + Cover = kbts__CoverGlyph(Coverage, CurrentGlyph->Id); } if(Cover.Valid) { - kbts_un DeltaGlyphCount = 0; + kbts_un OnePastLastGlyphOffset = 1; + kbts_glyph *OnePastLastGlyph = CurrentGlyph->Next; if((Lookup.Type == 5) || (Lookup.Type == 6)) { - kbts_sequence_lookup_result SequenceLookup = kbts_DoSequenceLookup(&Lookup, Subtable, Cover, GlyphArray, Frame->InputGlyphIndex, SequenceSkipFlags, SkipUnicodeFlags); + kbts__sequence_lookup_result SequenceLookup = kbts__DoSequenceLookup(Storage, &Lookup, Subtable, Cover, CurrentGlyph, SequenceSkipFlags, SkipUnicodeFlags); if(SequenceLookup.Matched) { - KBTS_FOR(FrameIndex, 0, FrameCount - 1) - { - kbts_gsub_frame *ParentFrame = &Frames[FrameIndex]; - - if((ParentFrame->InputGlyphIndex < (Frame->InputGlyphIndex + SequenceLookup.InputSequenceCountIncludingSkippedGlyphs)) && - ((ParentFrame->InputGlyphIndex + ParentFrame->InputGlyphCount) > Frame->InputGlyphIndex)) - { - ParentFrame->InputGlyphCount += (kbts_u16)(SequenceLookup.InputSequenceCountIncludingSkippedGlyphs - Frame->InputGlyphCount); - } - } - - Frame->InputGlyphCount = (kbts_u16)SequenceLookup.InputSequenceCountIncludingSkippedGlyphs; Frame->Records = SequenceLookup.Records; Frame->RecordCount = (kbts_u16)SequenceLookup.RecordCount; Frame->RecordIndex = 0; Frame->SubtableIndex = 0xFFFE; + + OnePastLastGlyphOffset = SequenceLookup.InputSequenceCountIncludingSkippedGlyphs; + OnePastLastGlyph = SequenceLookup.OnePastLastGlyphMatched; } } else @@ -18105,11 +21001,11 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap { case 1: { - Result |= KBTS_SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; + Result |= KBTS__SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; if(!CheckOnly) { - kbts_single_substitution *Subst = (kbts_single_substitution *)Subtable; + kbts__single_substitution *Subst = (kbts__single_substitution *)Subtable; kbts_u16 NewId = 0; if(Subst->Format == 1) @@ -18120,88 +21016,101 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap } else if(Subst->Format == 2) { - kbts_u16 *SubstituteGlyphIds = KBTS_POINTER_AFTER(kbts_u16, Subst); + kbts_u16 *SubstituteGlyphIds = KBTS__POINTER_AFTER(kbts_u16, Subst); NewId = SubstituteGlyphIds[Cover.Index]; } - kbts_GsubMutate(Font, CurrentGlyph, NewId, GeneratedGlyphFlags); + kbts__GsubMutate(Font, CurrentGlyph, NewId, GeneratedGlyphFlags); } } break; case 2: { - Result |= KBTS_SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; + kbts__multiple_substitution *Subst = (kbts__multiple_substitution *)Subtable; + kbts__sequence *Sequence = kbts__GetSequence(Subst, Cover.Index); + Result |= KBTS__SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; if(!CheckOnly) { - kbts_multiple_substitution *Subst = (kbts_multiple_substitution *)Subtable; - kbts_sequence *Sequence = kbts_GetSequence(Subst, Cover.Index); - kbts_u16 *SubstGlyphIds = KBTS_POINTER_AFTER(kbts_u16, Sequence); + kbts_u16 *SubstGlyphIds = KBTS__POINTER_AFTER(kbts_u16, Sequence); kbts_un GrowCount = Sequence->GlyphCount - 1; - if(!kbts_GrowGlyphArray(0, &InputGlyphs, 0, GrowCount, 0, 0)) - { - Result |= KBTS_SUBSTITUTION_RESULT_FLAG_GROW_BUFFER; - kbts_TransferGrowRequest(&InputGlyphs, GlyphArray); - goto Cleanup; + if(Sequence->GlyphCount) + { + kbts_glyph OriginalGlyph = *CurrentGlyph; + kbts_glyph *LastInsert = CurrentGlyph; + KBTS__FOR(SubstGlyphIndex, 0, Sequence->GlyphCount) + { + kbts_u32 NewGlyphFlags = GeneratedGlyphFlags | KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION; + + kbts_glyph *NewGlyph = CurrentGlyph; + if(SubstGlyphIndex) + { + NewGlyph = kbts__InsertGlyphAfter(Storage, LastInsert, &OriginalGlyph); + + if(!NewGlyph) + { + Scratchpad->Error = KBTS_SHAPE_ERROR_OUT_OF_MEMORY; + return Result; + } + + NewGlyph->Uid = (kbts_u16)++Scratchpad->NextGlyphUid; + } + else + { + NewGlyphFlags |= KBTS_GLYPH_FLAG_FIRST_IN_MULTIPLE_SUBSTITUTION; + } + + kbts__GsubMutate(Font, NewGlyph, SubstGlyphIds[SubstGlyphIndex], GeneratedGlyphFlags | NewGlyphFlags); + LastInsert = NewGlyph; + } + + OnePastLastGlyph = LastInsert->Next; } - DeltaGlyphCount += GrowCount; - - kbts_glyph OriginalGlyph = *CurrentGlyph; - KBTS_FOR(SubstGlyphIndex, 0, Sequence->GlyphCount) + else { - kbts_glyph NewGlyph = OriginalGlyph; - kbts_GsubMutate(Font, &NewGlyph, SubstGlyphIds[SubstGlyphIndex], GeneratedGlyphFlags | KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION); - if(SubstGlyphIndex) - { - NewGlyph.Uid = (kbts_u16)++ShapeState->NextGlyphUid; - } - else - { - NewGlyph.Flags |= KBTS_GLYPH_FLAG_FIRST_IN_MULTIPLE_SUBSTITUTION; - } - Glyphs[Frame->InputGlyphIndex + SubstGlyphIndex] = NewGlyph; + kbts__FreeGlyph(Scratchpad, Storage, CurrentGlyph); } - Frame->InputGlyphCount = Sequence->GlyphCount; + OnePastLastGlyphOffset = 1 + GrowCount; // Shift other frames' input cursors. - KBTS_FOR(FrameIndex, 0, FrameCount - 1) + KBTS__FOR(FrameIndex, 0, FrameCount - 1) { - kbts_gsub_frame *OtherFrame = &Frames[FrameIndex]; + kbts__gsub_frame *OtherFrame = &Frames[FrameIndex]; - if(OtherFrame->InputGlyphIndex > Frame->InputGlyphIndex) + if(OtherFrame->StartIndex > Frame->StartIndex) { - OtherFrame->InputGlyphIndex = (kbts_u16)(OtherFrame->InputGlyphIndex + GrowCount); - } - else if((OtherFrame->InputGlyphIndex + OtherFrame->InputGlyphCount) > Frame->InputGlyphIndex) - { - OtherFrame->InputGlyphCount = (kbts_u16)(OtherFrame->InputGlyphCount + GrowCount); + OtherFrame->StartIndex = (kbts_u16)(OtherFrame->StartIndex + GrowCount); } } } + else if(Sequence->GlyphCount > 1) + { + Result |= KBTS__SUBSTITUTION_RESULT_FLAG_TRIED_TO_INSERT_WHILE_CHECK_ONLY; + } } break; case 3: { - Result |= KBTS_SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; + Result |= KBTS__SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; if(!CheckOnly) { - kbts_alternate_substitution *Subst = (kbts_alternate_substitution *)Subtable; - kbts_alternate_set *Set = kbts_GetAlternateSet(Subst, Cover.Index); - kbts_u16 *AltGlyphIds = KBTS_POINTER_AFTER(kbts_u16, Set); + kbts__alternate_substitution *Subst = (kbts__alternate_substitution *)Subtable; + kbts__alternate_set *Set = kbts__GetAlternateSet(Subst, Cover.Index); + kbts_u16 *AltGlyphIds = KBTS__POINTER_AFTER(kbts_u16, Set); kbts_un AlternateIndex = 0; { - kbts_feature_set *ShaperFeatures = ShapeState->Config->Features; + kbts__feature_set *ShaperFeatures = &Config->Features; kbts_glyph_config *GlyphConfig = CurrentGlyph->Config; if(GlyphConfig) { int HasOverride = 0; - KBTS_FOR(WordIndex, 0, KBTS_ARRAY_LENGTH(GlyphConfig->EnabledFeatures.Flags)) + KBTS__FOR(WordIndex, 0, KBTS__ARRAY_LENGTH(GlyphConfig->EnabledFeatures.Flags)) { kbts_u64 Flags = GlyphConfig->EnabledFeatures.Flags[WordIndex] & ShaperFeatures->Flags[WordIndex]; if(Flags) @@ -18213,22 +21122,25 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap if(HasOverride) { - kbts_op_state_gsub *Gsub = &ShapeState->OpState.OpSpecific.Gsub; - KBTS_FOR(OverrideIndex, 0, GlyphConfig->FeatureOverrideCount) + for(kbts__feature_override_header *OverrideHeader = GlyphConfig->FeatureOverrideSentinel.Next; + OverrideHeader != &GlyphConfig->FeatureOverrideSentinel; + OverrideHeader = OverrideHeader->Next) { - kbts_feature_override *Override = &GlyphConfig->FeatureOverrides[OverrideIndex]; - kbts_u32 EnabledOrAlternatePlusOne = Override->EnabledOrAlternatePlusOne; + kbts__feature_override *Override = (kbts__feature_override *)OverrideHeader; + int Value = Override->Value; + kbts__feature_id Id = kbts__FeatureTagToId(Override->Tag); - if(EnabledOrAlternatePlusOne && kbts_ContainsFeature(&Gsub->LookupFeatures, Override->Id)) + if(Value && + kbts__ContainsFeature(&Scratchpad->LookupFeatures, Id)) { int Match = 1; - if(!Override->Id) + if(!Id) { // Slow path for unregistered features. Match = 0; - KBTS_FOR(UnregisteredFeatureIndex, 0, ShapeState->OpState.UnregisteredFeatureCount) + KBTS__FOR(UnregisteredFeatureIndex, 0, Scratchpad->UnregisteredFeatureCount) { - if(Override->Tag == ShapeState->OpState.UnregisteredFeatureTags[UnregisteredFeatureIndex]) + if(Override->Tag == Scratchpad->UnregisteredFeatureTags[UnregisteredFeatureIndex]) { Match = 1; break; @@ -18238,7 +21150,8 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap if(Match) { - AlternateIndex = EnabledOrAlternatePlusOne - 1; + AlternateIndex = (kbts_un)(Value - 1); + break; } } @@ -18253,157 +21166,204 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap } kbts_u16 NewId = AltGlyphIds[AlternateIndex]; - kbts_GsubMutate(Font, CurrentGlyph, NewId, GeneratedGlyphFlags); + kbts__GsubMutate(Font, CurrentGlyph, NewId, GeneratedGlyphFlags); } } break; case 4: { - kbts_ligature_substitution *Subst = (kbts_ligature_substitution *)Subtable; - kbts_ligature_set *Set = kbts_GetLigatureSet(Subst, Cover.Index); + kbts__ligature_substitution *Subst = (kbts__ligature_substitution *)Subtable; + kbts__ligature_set *Set = kbts__GetLigatureSet(Subst, Cover.Index); - KBTS_FOR(LigatureIndex, 0, Set->Count) + KBTS__FOR(LigatureIndex, 0, Set->Count) { - kbts_ligature *Ligature = kbts_GetLigature(Set, LigatureIndex); - kbts_u16 *ComponentIds = KBTS_POINTER_AFTER(kbts_u16, Ligature); + kbts__ligature *Ligature = kbts__GetLigature(Set, LigatureIndex); + kbts_u16 *ComponentIds = KBTS__POINTER_AFTER(kbts_u16, Ligature); + + kbts_un MatchingGlyphCount = 1; - if(Ligature->ComponentCount <= InputGlyphs.Count) { - kbts_un MatchingGlyphCount = 1; - + kbts_glyph *LigatureGlyphCursor = CurrentGlyph->Next; + while(kbts__GlyphIsValid(Storage, LigatureGlyphCursor) && (MatchingGlyphCount < Ligature->ComponentCount)) { - kbts_un InputIndex = 1; - while((InputIndex < InputGlyphs.Count) && (MatchingGlyphCount < Ligature->ComponentCount)) + // A ligature may contain an explicit ZWJ, which SkipGlyph() would probably skip. + // The expected behavior in that case is to assume the font designer knows what they are doing + // and match the ZWJ. + if(LigatureGlyphCursor->Id == ComponentIds[MatchingGlyphCount - 1]) { - kbts_glyph *Glyph = &InputGlyphs.Glyphs[InputIndex]; - // A ligature may contain an explicit ZWJ, which SkipGlyph() would probably skip. - // The expected behavior in that case is to assume the font designer knows what they are doing - // and match the ZWJ. - if(Glyph->Id == ComponentIds[MatchingGlyphCount - 1]) - { - MatchingGlyphCount += 1; - } - else if(!kbts_SkipGlyph(Glyph, &Lookup, RegularSkipFlags, SkipUnicodeFlags)) - { - break; - } - InputIndex += 1; + MatchingGlyphCount += 1; } - } - - if(MatchingGlyphCount == Ligature->ComponentCount) - { - Result |= KBTS_SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; - - if(!CheckOnly) + else if(!kbts__SkipGlyph(LigatureGlyphCursor, &Lookup, RegularSkipFlags, SkipUnicodeFlags)) { - kbts_u32 LigatureUid = ++ShapeState->NextGlyphUid; + break; + } - { // For glyphs that aren't part of the ligature, store which component it is attached to. - // For glyphs that _are_, eat them. - kbts_un LigatureGlyphCount = 1; - kbts_un ReadCursor = 1; - kbts_un WriteCursor = 1; + LigatureGlyphCursor = LigatureGlyphCursor->Next; + } + } - while((LigatureGlyphCount < InputGlyphs.Count) && (LigatureGlyphCount < Ligature->ComponentCount) && (ReadCursor < InputGlyphs.Count)) + if(MatchingGlyphCount == Ligature->ComponentCount) + { + Result |= KBTS__SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; + + if(!CheckOnly) + { + kbts_u32 LigatureUid = ++Scratchpad->NextGlyphUid; + kbts_un ComponentCount = Ligature->ComponentCount; + + { // For glyphs that aren't part of the ligature, store which component it is attached to. + // For glyphs that _are_, eat them. + kbts_un LigatureGlyphCount = 1; + kbts_un GrowCount = 0; + + // Consider this example: + // ABCD -> DB (A and C form a ligature) + // DB -> DEB (E is inserted in whatever way) + // DEB -> FB (D and E form a ligature) + // In that case, 'B' was supposed to be attached to D, but D does not exist anymore. + // To handle this case, we go through the ligature, counting the total flattened components + // and the ligature info of the last glyph that is part of the ligature. + // Then, all trailing marks get are re-attached to the new ligature, and their component indices + // are adjusted to take into account the new (flattened) component count. + // (I say 'flattened' here because a previous ligature glyph counts as multiple components.) + kbts_un PreviousLigatureUid = CurrentGlyph->LigatureUid; + kbts_un PreviousComponentCount = CurrentGlyph->LigatureComponentCount; + if(!PreviousComponentCount) + { + PreviousComponentCount = 1; + } + kbts_un RunningComponentCount = PreviousComponentCount; + + kbts_glyph *LigatureGlyphCursor = CurrentGlyph->Next; + while(kbts__GlyphIsValid(Storage, LigatureGlyphCursor) && (LigatureGlyphCount < ComponentCount)) + { + kbts_glyph *Next = LigatureGlyphCursor->Next; + + if(LigatureGlyphCursor->Id == ComponentIds[LigatureGlyphCount - 1]) { - kbts_glyph *Glyph = &InputGlyphs.Glyphs[ReadCursor]; - - if(Glyph->Id == ComponentIds[LigatureGlyphCount - 1]) + PreviousLigatureUid = LigatureGlyphCursor->LigatureUid; + PreviousComponentCount = LigatureGlyphCursor->LigatureComponentCount; + if(!PreviousComponentCount) { - LigatureGlyphCount += 1; - ReadCursor += 1; + PreviousComponentCount = 1; } - else if(kbts_SkipGlyph(Glyph, &Lookup, RegularSkipFlags, SkipUnicodeFlags)) - { - Glyph->LigatureComponentIndexPlusOne = (kbts_u16)LigatureGlyphCount; - Glyph->LigatureUid = (kbts_u16)LigatureUid; + RunningComponentCount += PreviousComponentCount; - InputGlyphs.Glyphs[WriteCursor++] = InputGlyphs.Glyphs[ReadCursor++]; + LigatureGlyphCount += 1; + + // Eat! + kbts__FreeGlyph(Scratchpad, Storage, LigatureGlyphCursor); + GrowCount -= 1; + } + else if(kbts__SkipGlyph(LigatureGlyphCursor, &Lookup, RegularSkipFlags, SkipUnicodeFlags)) + { + LigatureGlyphCursor->LigatureComponentIndexPlusOne = (kbts_u16)LigatureGlyphCount; + LigatureGlyphCursor->LigatureUid = (kbts_u16)LigatureUid; + LigatureGlyphCursor->LigatureComponentCount = (kbts_u16)ComponentCount; + } + else + { + break; + } + + LigatureGlyphCursor = Next; + } + + OnePastLastGlyph = CurrentGlyph->Next; + + // Re-attach trailing marks that were part of a component ligature to the new ligature. + if(PreviousLigatureUid) + { + kbts_un DeltaComponentCount = RunningComponentCount - PreviousComponentCount; + + while(kbts__GlyphIsValid(Storage, LigatureGlyphCursor)) + { + kbts_un PreviousComponentIndexPlusOne = LigatureGlyphCursor->LigatureComponentIndexPlusOne; + + if((LigatureGlyphCursor->Classes.Class == KBTS__GLYPH_CLASS_MARK) && + (LigatureGlyphCursor->LigatureUid == PreviousLigatureUid) && + PreviousComponentIndexPlusOne) + { + if(PreviousComponentIndexPlusOne > PreviousComponentCount) + { + PreviousComponentIndexPlusOne = PreviousComponentCount; + } + + kbts_un NewComponentIndexPlusOne = PreviousComponentIndexPlusOne + DeltaComponentCount; + + LigatureGlyphCursor->LigatureUid = (kbts_u16)LigatureUid; + LigatureGlyphCursor->LigatureComponentIndexPlusOne = (kbts_u16)NewComponentIndexPlusOne; } else { break; } - } - // Finish moving all non-ligature glyphs in their place. - // We are effectively shrinking the whole buffer here, so we have to touch - // glyphs outside of our own cluster. - kbts_un GrowCount = WriteCursor - ReadCursor; // Negative - kbts_GrowGlyphArray(0, &InputGlyphs, WriteCursor, GrowCount, 0, 0); - DeltaGlyphCount += GrowCount; - - // Update frame input cursors. - KBTS_FOR(FrameIndex, 0, FrameCount - 1) - { - kbts_gsub_frame *OtherFrame = &Frames[FrameIndex]; - - if(OtherFrame->InputGlyphIndex >= Frame->InputGlyphIndex) - { - kbts_un OtherInputIndex = OtherFrame->InputGlyphIndex + GrowCount; - OtherInputIndex = KBTS_MAX(OtherInputIndex, Frame->InputGlyphIndex); - OtherFrame->InputGlyphIndex = (kbts_u16)OtherInputIndex; - } - else - { - kbts_un End = OtherFrame->InputGlyphIndex + OtherFrame->InputGlyphCount; - End = KBTS_MIN(End, GlyphArray->TotalCount); - OtherFrame->InputGlyphCount = (kbts_u16)(End - OtherFrame->InputGlyphIndex); - } + LigatureGlyphCursor = LigatureGlyphCursor->Next; } } - // Currently, we only take the main glyph's config into account while making the ligature's config. - // Maybe we should merge all of the components' configs into one instead? - kbts_GsubMutate(Font, CurrentGlyph, Ligature->Glyph, GeneratedGlyphFlags | KBTS_GLYPH_FLAG_LIGATURE); - CurrentGlyph->Uid = (kbts_u16)LigatureUid; - // Harfbuzz does this, because Uniscribe does this, and so we do the same. Sigh. - CurrentGlyph->Flags &= ~KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION; + // Update frame input cursors. + KBTS__FOR(FrameIndex, 0, FrameCount - 1) + { + kbts__gsub_frame *OtherFrame = &Frames[FrameIndex]; + + if(OtherFrame->StartIndex >= Frame->StartIndex) + { + OtherFrame->StartIndex += (kbts_u16)GrowCount; + } + } } - break; + // Currently, we only take the main glyph's config into account while making the ligature's config. + // Maybe we should merge all of the components' configs into one instead? + kbts__GsubMutate(Font, CurrentGlyph, Ligature->Glyph, GeneratedGlyphFlags | KBTS_GLYPH_FLAG_LIGATURE); + CurrentGlyph->Uid = (kbts_u16)LigatureUid; + CurrentGlyph->LigatureUid = (kbts_u16)LigatureUid; + CurrentGlyph->LigatureComponentCount = (kbts_u16)ComponentCount; + + // Harfbuzz does this, because Uniscribe does this, and so we do the same. Sigh. + CurrentGlyph->Flags &= ~KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION; } + + break; } } } break; case 8: { - kbts_reverse_chain_substitution *Subst = (kbts_reverse_chain_substitution *)Subtable; - kbts_unpacked_reverse_chain_substitution Unpacked = kbts_UnpackReverseChainSubstitution(Subst, 0); + kbts__reverse_chain_substitution *Subst = (kbts__reverse_chain_substitution *)Subtable; + kbts__unpacked_reverse_chain_substitution Unpacked = kbts__UnpackReverseChainSubstitution(Subst, 0); // :BoundsChecking if(Cover.Index < Unpacked.GlyphCount) { // Should we use regular or sequence skip flags here? - kbts_glyph_array BacktrackGlyphs = kbts_ClipGlyphArray(GlyphArray, Frame->InputGlyphIndex); - kbts_sequence_match BacktrackMatch = kbts_MatchCoverageSequence(&Lookup, RegularSkipFlags, SkipUnicodeFlags, Subst, Unpacked.BacktrackCoverageOffsets, Unpacked.BacktrackCount, - &BacktrackGlyphs, (kbts_un)Frame->InputGlyphIndex - 1, -1); - kbts_glyph_array FollowupGlyphs = kbts_GlyphSubArray(&InputGlyphs, 1); - kbts_sequence_match LookaheadMatch = kbts_MatchCoverageSequence(&Lookup, RegularSkipFlags, SkipUnicodeFlags, Subst, Unpacked.LookaheadCoverageOffsets, Unpacked.LookaheadCount, - &FollowupGlyphs, 0, 1); + kbts__sequence_match BacktrackMatch = kbts__MatchCoverageSequence(Storage, &Lookup, RegularSkipFlags, SkipUnicodeFlags, Subst, Unpacked.BacktrackCoverageOffsets, Unpacked.BacktrackCount, + CurrentGlyph->Prev, 1); + kbts__sequence_match LookaheadMatch = kbts__MatchCoverageSequence(Storage, &Lookup, RegularSkipFlags, SkipUnicodeFlags, Subst, Unpacked.LookaheadCoverageOffsets, Unpacked.LookaheadCount, + CurrentGlyph->Next, 0); if((BacktrackMatch.MatchCount == Unpacked.BacktrackCount) && (LookaheadMatch.MatchCount == Unpacked.LookaheadCount)) { - Result |= KBTS_SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; - kbts_GsubMutate(Font, CurrentGlyph, Unpacked.SubstituteGlyphIds[Cover.Index], GeneratedGlyphFlags); + Result |= KBTS__SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION; + kbts__GsubMutate(Font, CurrentGlyph, Unpacked.SubstituteGlyphIds[Cover.Index], GeneratedGlyphFlags); } } } break; } - if(Result & KBTS_SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION) + if(Result & KBTS__SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION) { Frame->SubtableIndex = 0xFFFE; // From the Microsoft docs: // To move to the “next” glyph, the client skips all the glyphs that participated in the lookup operation: // glyphs that were substituted/positioned as well as any other glyphs in the matched input sequence. - - GlyphArray->Count += DeltaGlyphCount; - GlyphArray->TotalCount += DeltaGlyphCount; } } + + kbts__SetLookupOnePastLastGlyph(Scratchpad, Frame->StartIndex + OnePastLastGlyphOffset, OnePastLastGlyph); } } @@ -18412,39 +21372,43 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap if(Frame->RecordIndex < Frame->RecordCount) { - kbts_sequence_lookup_record *SequenceRecord = &Frame->Records[Frame->RecordIndex++]; + kbts__sequence_lookup_record *SequenceRecord = &Frame->Records[Frame->RecordIndex++]; - kbts_gsub_frame NewFrame = KBTS_ZERO; - NewFrame.LookupIndex = SequenceRecord->LookupListIndex; - NewFrame.SubtableIndex = 0; - NewFrame.InputGlyphIndex = Frame->InputGlyphIndex; - - if(SequenceRecord->SequenceIndex) + if(FrameCount < KBTS_LOOKUP_STACK_SIZE) { - // @Speed: Re-scan the sequence to find where we are in the _filtered_ sequence. - kbts_un SequenceInputIndex = 0; - KBTS_FOR(FilterAt, Frame->InputGlyphIndex + 1, GlyphArray->Count) + kbts__gsub_frame NewFrame = KBTS__ZERO; + NewFrame.LookupIndex = SequenceRecord->LookupListIndex; + NewFrame.StartIndex = Frame->StartIndex; + NewFrame.InputGlyph = Frame->InputGlyph; + + if(SequenceRecord->SequenceIndex) { - kbts_glyph *FilterGlyph = &Glyphs[FilterAt]; - - // @Incomplete: SequenceSkipFlags? 0? - // What do we use here? - if(!kbts_SkipGlyph(FilterGlyph, &Lookup, 0, SkipUnicodeFlags)) + // @Speed: Re-scan the sequence to find where we are in the _filtered_ sequence. + kbts_un SequenceInputIndex = 0; + for(kbts_glyph *FilterGlyph = Frame->InputGlyph->Next; + kbts__GlyphIsValid(Storage, FilterGlyph); + FilterGlyph = FilterGlyph->Next) { - SequenceInputIndex += 1; - - if(SequenceInputIndex == SequenceRecord->SequenceIndex) + // @Incomplete: SequenceSkipFlags? 0? + // What do we use here? + if(!kbts__SkipGlyph(FilterGlyph, &Lookup, 0, SkipUnicodeFlags)) { - NewFrame.InputGlyphIndex = (kbts_u16)FilterAt; + SequenceInputIndex += 1; - break; + if(SequenceInputIndex == SequenceRecord->SequenceIndex) + { + NewFrame.InputGlyph = FilterGlyph; + + break; + } } + + NewFrame.StartIndex += 1; } } - } - NewFrame.InputGlyphCount = 1; - Frames[FrameCount++] = NewFrame; + Frames[FrameCount++] = NewFrame; + } } // Only actually pop the frame if we haven't pushed anything else in the meantime. @@ -18453,101 +21417,175 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap FrameCount -= 1; } -Cleanup:; - *FrameCount_ = (kbts_u32)FrameCount; + *FrameCount_ = FrameCount; return Result; } -static kbts_u32 kbts_WouldSubstitute(kbts_shape_state *ShapeState, kbts_lookup_list *LookupList, kbts_gsub_frame *Frames, kbts_feature *Feature, kbts_skip_flags SkipFlags, kbts_glyph *Glyphs, kbts_un GlyphCount) +static kbts__glyph_list kbts__PushGlyphList(kbts_glyph_storage *Storage, kbts_glyph *First, kbts_glyph *Last) { - kbts_u32 Result = 0; - kbts_glyph_array GlyphArray = kbts_GlyphArray(Glyphs, GlyphCount, GlyphCount, GlyphCount); + kbts__glyph_list Result = KBTS__ZERO; - kbts_iterate_lookups IterateLookups = kbts_IterateLookups(LookupList, Feature); - while(kbts_NextLookup(&IterateLookups)) + Result.SentinelPrev = Storage->GlyphSentinel.Prev; + Result.SentinelNext = Storage->GlyphSentinel.Next; + Result.OneBeforeFirst = First->Prev; + Result.OnePastLast = Last->Next; + + First->Prev = Last->Next = (kbts_glyph *)&Storage->GlyphSentinel; + First->Prev->Next = First; + Last->Next->Prev = Last; + + return Result; +} + +static void kbts__PopGlyphList(kbts_glyph_storage *Storage, kbts__glyph_list *List) +{ + List->OneBeforeFirst->Next = Storage->GlyphSentinel.Next; + List->OnePastLast->Prev = Storage->GlyphSentinel.Prev; + + // Storage->GlyphSentinel.Prev->Next = Storage->GlyphSentinel.Next->Prev = &Storage->GlyphSentinel; + + if((kbts_glyph *)&Storage->GlyphSentinel != List->OneBeforeFirst) { - kbts_un GlyphIndex = 0; + Storage->GlyphSentinel.Next = List->SentinelNext; + } + if((kbts_glyph *)&Storage->GlyphSentinel != List->OnePastLast) + { + Storage->GlyphSentinel.Prev = List->SentinelPrev; + } - while(GlyphIndex < GlyphCount) + Storage->GlyphSentinel.Prev->Next = Storage->GlyphSentinel.Next->Prev = (kbts_glyph *)&Storage->GlyphSentinel; + + *List = KBTS__ZERO_TYPE(kbts__glyph_list); +} + +static int kbts__WouldSubstitute(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_storage *Storage, + kbts_lookup_list *LookupList, + kbts__gsub_frame *Frames, kbts__feature *Feature, kbts__skip_flags SkipFlags, + kbts_glyph *Glyphs, kbts_un GlyphCount) +{ + int Result = 0; + + KBTS_ASSERT(GlyphCount <= 4); + kbts_glyph TempSentinel; + kbts_glyph Scratch[4]; + + KBTS__FOR(GlyphIndex, 0, GlyphCount) + { + kbts_glyph *ScratchGlyph = &Scratch[GlyphIndex]; + + // Glyphs is assumed to be in-order on the stack or something. + // This is one of the rare places where we manipulate arrays. + *ScratchGlyph = Glyphs[GlyphIndex]; + ScratchGlyph->Prev = ScratchGlyph - 1; + ScratchGlyph->Next = ScratchGlyph + 1; + } + Scratch[0].Prev = &TempSentinel; + Scratch[GlyphCount - 1].Next = &TempSentinel; + TempSentinel.Next = &Scratch[0]; + TempSentinel.Prev = &Scratch[GlyphCount - 1]; + + kbts__glyph_list OldList = kbts__PushGlyphList(Storage, &Scratch[0], &Scratch[GlyphCount - 1]); + + kbts__iterate_lookups IterateLookups = kbts__IterateLookups(LookupList, Feature); + while(kbts__NextLookup(&IterateLookups)) + { + kbts_glyph *CurrentGlyph = &Scratch[0]; + while(kbts__GlyphIsValid(Storage, CurrentGlyph)) { - kbts_gsub_frame *Frame = &Frames[0]; + kbts__gsub_frame *Frame = &Frames[0]; + *Frame = KBTS__ZERO_TYPE(kbts__gsub_frame); Frame->LookupIndex = IterateLookups.LookupIndex; Frame->SubtableIndex = 0; - Frame->InputGlyphIndex = 0; - Frame->InputGlyphCount = 1; - kbts_u32 FrameCount = 1; + Frame->StartIndex = 0; + Frame->InputGlyph = CurrentGlyph; + kbts_un FrameCount = 1; + + kbts__BeginLookupApplication(Scratchpad, CurrentGlyph); while(FrameCount) { - kbts_substitution_result_flags SubstitutionResult = kbts_DoSubstitution(ShapeState, LookupList, Frames, &FrameCount, &GlyphArray, 1, SkipFlags, 0); + kbts__substitution_result_flags SubstitutionResult = kbts__DoSubstitution(Scratchpad, Config, Storage, LookupList, Frames, &FrameCount, 1, SkipFlags, 0); - if(SubstitutionResult & KBTS_SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION) + if(SubstitutionResult & KBTS__SUBSTITUTION_RESULT_FLAG_MATCHED_SUBSTITUTION) { Result = 1; goto Done; } } - GlyphIndex += Frames[0].InputGlyphCount; + CurrentGlyph = kbts__EndLookupApplication(Scratchpad); } } Done:; + kbts__PopGlyphList(Storage, &OldList); + return Result; } -static int kbts_NextLookupIndex(kbts_op_state *S, kbts_un *LookupIndex, kbts_u32 *SkipFlags_, kbts_u32 *GlyphFilter_, kbts_feature_set *FeatureSet_) +static int kbts__NextLookupIndex(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_un *LookupIndex_, kbts_u32 *SkipFlags_, kbts_u32 *GlyphFilter_, kbts__feature_set *FeatureSet_) { - kbts_un LowestIndex = 0xFFFFFFFF; - KBTS_FOR(FeatureIndex, 0, S->FeatureCount) - { - kbts_lookup_indices *Indices = &S->FeatureLookupIndices[FeatureIndex]; - kbts_un Index = Indices->Indices[0]; - if(Index < LowestIndex) - { - LowestIndex = Index; - } - } + int Result = 0; - kbts_feature_set FeatureSet = KBTS_ZERO; - kbts_skip_flags SkipFlags = 0; + kbts_un FeatureStageIndex = Scratchpad->FeatureStagesRead - 1; + kbts_u32 SkipFlags = 0; kbts_u32 GlyphFilter = 0; - kbts_un UnregisteredFeatureCount = 0; - KBTS_FOR(FeatureIndex, 0, S->FeatureCount) + Scratchpad->UnregisteredFeatureCount = 0; + kbts_un ResultLookupIndex = 0; + kbts__baked_feature_stage *FeatureStage = &Config->FeatureStages[FeatureStageIndex]; + + *FeatureSet_ = KBTS__ZERO_TYPE(kbts__feature_set); + + while(Scratchpad->FeatureIndexIndex < FeatureStage->FeatureIndexCount) { - kbts_lookup_indices *Indices = &S->FeatureLookupIndices[FeatureIndex]; - kbts_un Index = Indices->Indices[0]; - if(Index == LowestIndex) + KBTS_ASSERT(Scratchpad->FeatureIndexIndex < FeatureStage->FeatureIndexCount); + + kbts_u16 FeatureIndex = FeatureStage->FeatureIndices[Scratchpad->FeatureIndexIndex]; + KBTS_ASSERT(FeatureIndex < FeatureStage->FeatureIndexCount); + + kbts__baked_feature *Feature = &FeatureStage->Features[FeatureIndex]; + kbts_un Offset = Scratchpad->BakedLookupSubtablesRead[FeatureIndex]; + + KBTS_ASSERT(Offset < Feature->Count); + kbts_un LookupIndex = Feature->Indices[Offset]; + + if(!Result) { - SkipFlags |= Indices->SkipFlags; - GlyphFilter |= Indices->GlyphFilter; - kbts_AddFeature(&FeatureSet, Indices->FeatureId); + ResultLookupIndex = LookupIndex; + Result = 1; + } - if(!Indices->FeatureId && (UnregisteredFeatureCount < KBTS_MAX_SIMULTANEOUS_FEATURES)) + if(LookupIndex == ResultLookupIndex) + { + kbts__AddFeature(FeatureSet_, Feature->FeatureId); + SkipFlags |= Feature->SkipFlags; + GlyphFilter |= Feature->GlyphFilter; + + if(!Feature->FeatureId && (Scratchpad->UnregisteredFeatureCount < KBTS_MAX_SIMULTANEOUS_FEATURES)) { - S->UnregisteredFeatureTags[UnregisteredFeatureCount++] = Indices->FeatureTag; + Scratchpad->UnregisteredFeatureTags[Scratchpad->UnregisteredFeatureCount++] = Feature->FeatureTag; } - ++Indices->Indices; --Indices->Count; - if(!Indices->Count) - { - S->FeatureLookupIndices[FeatureIndex--] = S->FeatureLookupIndices[--S->FeatureCount]; - } + Scratchpad->FeatureIndexIndex += 1; + Scratchpad->BakedLookupSubtablesRead[FeatureIndex] += 1; + } + else + { + break; } } - S->UnregisteredFeatureCount = (kbts_u32)UnregisteredFeatureCount; - *LookupIndex = LowestIndex; + *LookupIndex_ = ResultLookupIndex; *SkipFlags_ = SkipFlags; *GlyphFilter_ = GlyphFilter; - *FeatureSet_ = FeatureSet; - return LowestIndex != 0xFFFFFFFF; + + return Result; } -static int kbts_ConfigAllowsFeatures(kbts_op_state *S, kbts_shape_config *Config, kbts_glyph_config *GlyphConfig, kbts_feature_set *Features) +static int kbts__ConfigAllowsFeatures(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_config *GlyphConfig, kbts__feature_set *Features) { - kbts_glyph_config DummyGlyphConfig = KBTS_ZERO; + kbts_glyph_config DummyGlyphConfig = KBTS__ZERO; kbts_u64 UserEnabled = 0; // Whether the user enabled _any_ feature corresponding to this lookup. kbts_u64 UserDisabled = 1; // Whether the user disabled _all_ features corresponding to this lookup. kbts_u64 DefaultEnabled = 0; // Whether any feature is non-user. @@ -18558,686 +21596,793 @@ static int kbts_ConfigAllowsFeatures(kbts_op_state *S, kbts_shape_config *Config } kbts_u64 Mask = 1; // Ignore unregistered features in the broad pass. - KBTS_FOR(WordIndex, 0, KBTS_ARRAY_LENGTH(GlyphConfig->EnabledFeatures.Flags)) + KBTS__FOR(WordIndex, 0, KBTS__ARRAY_LENGTH(GlyphConfig->EnabledFeatures.Flags)) { kbts_u64 LookupFeatureFlags = Features->Flags[WordIndex] & ~Mask; Mask = 0; UserEnabled |= GlyphConfig->EnabledFeatures.Flags[WordIndex] & LookupFeatureFlags; UserDisabled &= (GlyphConfig->DisabledFeatures.Flags[WordIndex] & LookupFeatureFlags) == LookupFeatureFlags; - DefaultEnabled |= Features->Flags[WordIndex] & Config->Features->Flags[WordIndex]; + DefaultEnabled |= Features->Flags[WordIndex] & Config->Features.Flags[WordIndex]; } - if(Features->Flags[0] & (GlyphConfig->EnabledFeatures.Flags[0] | GlyphConfig->DisabledFeatures.Flags[0]) & KBTS_FEATURE_FLAG0(UNREGISTERED)) + if(Features->Flags[0] & (GlyphConfig->EnabledFeatures.Flags[0] | GlyphConfig->DisabledFeatures.Flags[0]) & KBTS__FEATURE_FLAG0(UNREGISTERED)) { // Slow path for unregistered features. - KBTS_FOR(FeatureOverrideIndex, 0, GlyphConfig->FeatureOverrideCount) + for(kbts__feature_override_header *Header = GlyphConfig->FeatureOverrideSentinel.Next; + Header != &GlyphConfig->FeatureOverrideSentinel; + Header = Header->Next) { - kbts_feature_override *Override = &GlyphConfig->FeatureOverrides[FeatureOverrideIndex]; - if(Override->Id == KBTS_FEATURE_ID_UNREGISTERED) + kbts__feature_override *Override = (kbts__feature_override *)Header; + kbts__feature_id Id = kbts__FeatureTagToId(Override->Tag); + + if(Id == KBTS__FEATURE_ID_UNREGISTERED) { kbts_feature_tag OverrideTag = Override->Tag; - KBTS_FOR(UnregisteredFeatureIndex, 0, S->UnregisteredFeatureCount) + + KBTS__FOR(UnregisteredFeatureIndex, 0, Scratchpad->UnregisteredFeatureCount) { - if(OverrideTag == S->UnregisteredFeatureTags[UnregisteredFeatureIndex]) + if(OverrideTag == Scratchpad->UnregisteredFeatureTags[UnregisteredFeatureIndex]) { - UserEnabled |= Override->EnabledOrAlternatePlusOne; - UserDisabled &= !Override->EnabledOrAlternatePlusOne; + UserEnabled |= (kbts_u32)Override->Value; + UserDisabled &= !Override->Value; break; } } } } } + int Result = (!UserDisabled && (DefaultEnabled || UserEnabled)); + return Result; } -static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *GlyphArray) +static void kbts__DllistReverseSublist(kbts_glyph *First, kbts_glyph *OnePastLast) { - KBTS_INSTRUMENT_FUNCTION_BEGIN - kbts_shape_config *Config = ShapeState->Config; - kbts_font *Font = Config->Font; - kbts_op *Op = &ShapeState->Op; - kbts_op_state *S = &ShapeState->OpState; - kbts_glyph *Glyphs = GlyphArray->Glyphs; + kbts_glyph *OneBeforeFirst = First->Prev; + kbts_glyph *Last = First; - kbts_u32 ResumePoint = S->ResumePoint; - S->ResumePoint = 0; - switch(ResumePoint) + for(kbts_glyph *Glyph = First; + ; + ) { - case 1: goto ResumePoint1; break; - case 2: goto ResumePoint2; break; - case 3: goto ResumePoint3; break; - case 4: goto ResumePoint4; break; - case 5: goto ResumePoint5; break; + kbts_glyph *Next = Glyph->Next; + + Glyph->Next = Glyph->Prev; + Glyph->Prev = Next; + + Last = Glyph; + Glyph = Next; + if(Glyph == OnePastLast) + { + break; + } } - S->WrittenCount = 0; + if(First != OnePastLast) + { + Last->Prev = OneBeforeFirst; + Last->Prev->Next = Last; + First->Next = OnePastLast; + First->Next->Prev = First; + } +} - switch(Op->Kind) +static void kbts__ExecuteOp(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_storage *Storage) +{ + KBTS_INSTRUMENT_FUNCTION_BEGIN; + + if(Config) { - case KBTS_OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES: - { - KBTS_INSTRUMENT_BLOCK_BEGIN("PRE_NORMALIZE_DOTTED_CIRCLES") - // Before even trying to normalize anything, there are some _exceptions_ we have to take care of. - // The USE spec gives us a list of codepoint sequences which necessitate insertion of a dotted circle. - // These sequences are from IndicShapingInvalidClusters.txt. - S->GlyphIndex = 0; - kbts_u64 Codepoints; Codepoints = 0; // 0xABBBBBCCCCCDDDDD - for(; S->GlyphIndex < GlyphArray->Count; ++S->GlyphIndex) + kbts_font *Font = Config->Font; + kbts__op_kind OpKind = Scratchpad->OpKind; + + switch(OpKind) { - kbts_u32 NewCodepoint; NewCodepoint = Glyphs[S->GlyphIndex].Codepoint & 0x1FFFFF; - Codepoints = (Codepoints << 21) | NewCodepoint; + case KBTS__OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES: + { + KBTS_INSTRUMENT_BLOCK_BEGIN(PRE_NORMALIZE_DOTTED_CIRCLES); - // This switch is faster than any table lookup I could come up with in my tests. - #define KBTS_C2(A, B) case (((kbts_u64)(A) << 21) | (kbts_u64)(B)): - switch(Codepoints & (((kbts_u64)1 << 42) - 1)) + // Before even trying to normalize anything, there are some _exceptions_ we have to take care of. + // The USE spec gives us a list of codepoint sequences which necessitate insertion of a dotted circle. + // These sequences are from IndicShapingInvalidClusters.txt. + kbts_u64 Codepoints; Codepoints = 0; // 0xABBBBBCCCCCDDDDD + KBTS__FOR_GLYPH(Storage, Glyph) { - default: - if((Codepoints & (((kbts_u64)1 << 63) - 1)) == (((kbts_u64)0x930 << 42) | ((kbts_u64)0x94D << 21) | ((kbts_u64)0x907))) + kbts_u32 NewCodepoint; NewCodepoint = Glyph->Codepoint & 0x1FFFFF; + Codepoints = (Codepoints << 21) | NewCodepoint; + + // This switch is faster than any table lookup I could come up with in my tests. + #define KBTS_C2(A, B) case (((kbts_u64)(A) << 21) | (kbts_u64)(B)): + switch(Codepoints & (((kbts_u64)1 << 42) - 1)) { - if(0) + default: + if((Codepoints & (((kbts_u64)1 << 63) - 1)) == (((kbts_u64)0x930 << 42) | ((kbts_u64)0x94D << 21) | ((kbts_u64)0x907))) { - ResumePoint4:; - Codepoints = 0; - } - KBTS_C2(0x905, 0x93A) - KBTS_C2(0x905, 0x93B) - KBTS_C2(0x905, 0x93E) - KBTS_C2(0x905, 0x945) - KBTS_C2(0x905, 0x946) - KBTS_C2(0x905, 0x949) - KBTS_C2(0x905, 0x94A) - KBTS_C2(0x905, 0x94B) - KBTS_C2(0x905, 0x94C) - KBTS_C2(0x905, 0x94F) - KBTS_C2(0x905, 0x956) - KBTS_C2(0x905, 0x957) - KBTS_C2(0x906, 0x93A) - KBTS_C2(0x906, 0x945) - KBTS_C2(0x906, 0x946) - KBTS_C2(0x906, 0x947) - KBTS_C2(0x906, 0x948) - KBTS_C2(0x909, 0x941) - KBTS_C2(0x90F, 0x945) - KBTS_C2(0x90F, 0x946) - KBTS_C2(0x90F, 0x947) - KBTS_C2(0x985, 0x9BE) - KBTS_C2(0x98B, 0x9C3) - KBTS_C2(0x98C, 0x9E2) - KBTS_C2(0xA05, 0xA3E) - KBTS_C2(0xA05, 0xA48) - KBTS_C2(0xA05, 0xA4C) - KBTS_C2(0xA72, 0xA3F) - KBTS_C2(0xA72, 0xA40) - KBTS_C2(0xA72, 0xA47) - KBTS_C2(0xA73, 0xA41) - KBTS_C2(0xA73, 0xA42) - KBTS_C2(0xA73, 0xA4B) - KBTS_C2(0xA85, 0xABE) - KBTS_C2(0xA85, 0xAC5) - KBTS_C2(0xA85, 0xAC7) - KBTS_C2(0xA85, 0xAC8) - KBTS_C2(0xA85, 0xAC9) - KBTS_C2(0xA85, 0xACB) - KBTS_C2(0xA85, 0xACC) - KBTS_C2(0xAC5, 0xABE) - KBTS_C2(0xB05, 0xB3E) - KBTS_C2(0xB0F, 0xB57) - KBTS_C2(0xB13, 0xB57) - KBTS_C2(0xB85, 0xBC2) - KBTS_C2(0xC12, 0xC4C) - KBTS_C2(0xC12, 0xC55) - KBTS_C2(0xC3F, 0xC55) - KBTS_C2(0xC46, 0xC55) - KBTS_C2(0xC4A, 0xC55) - KBTS_C2(0xC89, 0xCBE) - KBTS_C2(0xC8B, 0xCBE) - KBTS_C2(0xC92, 0xCCC) - KBTS_C2(0xD07, 0xD57) - KBTS_C2(0xD09, 0xD57) - KBTS_C2(0xD0E, 0xD46) - KBTS_C2(0xD12, 0xD3E) - KBTS_C2(0xD12, 0xD57) - KBTS_C2(0xD85, 0xDCF) - KBTS_C2(0xD85, 0xDD0) - KBTS_C2(0xD85, 0xDD1) - KBTS_C2(0xD8B, 0xDDF) - KBTS_C2(0xD8D, 0xDD8) - KBTS_C2(0xD8F, 0xDDF) - KBTS_C2(0xD91, 0xDCA) - KBTS_C2(0xD91, 0xDD9) - KBTS_C2(0xD91, 0xDDA) - KBTS_C2(0xD91, 0xDDC) - KBTS_C2(0xD91, 0xDDD) - KBTS_C2(0xD91, 0xDDE) - KBTS_C2(0xD94, 0xDDF) - KBTS_C2(0x11005, 0x11038) - KBTS_C2(0x1100B, 0x1103E) - KBTS_C2(0x1100F, 0x11042) - KBTS_C2(0x11200, 0x1122C) - KBTS_C2(0x11200, 0x11231) - KBTS_C2(0x11200, 0x11233) - KBTS_C2(0x11206, 0x1122C) - KBTS_C2(0x1122C, 0x11230) - KBTS_C2(0x1122C, 0x11231) - KBTS_C2(0x11240, 0x1122E) - KBTS_C2(0x112B0, 0x112E0) - KBTS_C2(0x112B0, 0x112E5) - KBTS_C2(0x112B0, 0x112E6) - KBTS_C2(0x112B0, 0x112E7) - KBTS_C2(0x112B0, 0x112E8) - KBTS_C2(0x11481, 0x114B0) - KBTS_C2(0x1148B, 0x114BA) - KBTS_C2(0x1148D, 0x114BA) - KBTS_C2(0x114AA, 0x114B5) - KBTS_C2(0x114AA, 0x114B6) - KBTS_C2(0x11600, 0x11639) - KBTS_C2(0x11600, 0x1163A) - KBTS_C2(0x11601, 0x11639) - KBTS_C2(0x11601, 0x1163A) - KBTS_C2(0x11680, 0x116AD) - KBTS_C2(0x11680, 0x116B4) - KBTS_C2(0x11680, 0x116B5) - KBTS_C2(0x11686, 0x116B2) - if(!kbts_GrowGlyphArray(&S->ResumePoint, GlyphArray, S->GlyphIndex, 1, 4, 0)) + KBTS_C2(0x905, 0x93A) + KBTS_C2(0x905, 0x93B) + KBTS_C2(0x905, 0x93E) + KBTS_C2(0x905, 0x945) + KBTS_C2(0x905, 0x946) + KBTS_C2(0x905, 0x949) + KBTS_C2(0x905, 0x94A) + KBTS_C2(0x905, 0x94B) + KBTS_C2(0x905, 0x94C) + KBTS_C2(0x905, 0x94F) + KBTS_C2(0x905, 0x956) + KBTS_C2(0x905, 0x957) + KBTS_C2(0x906, 0x93A) + KBTS_C2(0x906, 0x945) + KBTS_C2(0x906, 0x946) + KBTS_C2(0x906, 0x947) + KBTS_C2(0x906, 0x948) + KBTS_C2(0x909, 0x941) + KBTS_C2(0x90F, 0x945) + KBTS_C2(0x90F, 0x946) + KBTS_C2(0x90F, 0x947) + KBTS_C2(0x985, 0x9BE) + KBTS_C2(0x98B, 0x9C3) + KBTS_C2(0x98C, 0x9E2) + KBTS_C2(0xA05, 0xA3E) + KBTS_C2(0xA05, 0xA48) + KBTS_C2(0xA05, 0xA4C) + KBTS_C2(0xA72, 0xA3F) + KBTS_C2(0xA72, 0xA40) + KBTS_C2(0xA72, 0xA47) + KBTS_C2(0xA73, 0xA41) + KBTS_C2(0xA73, 0xA42) + KBTS_C2(0xA73, 0xA4B) + KBTS_C2(0xA85, 0xABE) + KBTS_C2(0xA85, 0xAC5) + KBTS_C2(0xA85, 0xAC7) + KBTS_C2(0xA85, 0xAC8) + KBTS_C2(0xA85, 0xAC9) + KBTS_C2(0xA85, 0xACB) + KBTS_C2(0xA85, 0xACC) + KBTS_C2(0xAC5, 0xABE) + KBTS_C2(0xB05, 0xB3E) + KBTS_C2(0xB0F, 0xB57) + KBTS_C2(0xB13, 0xB57) + KBTS_C2(0xB85, 0xBC2) + KBTS_C2(0xC12, 0xC4C) + KBTS_C2(0xC12, 0xC55) + KBTS_C2(0xC3F, 0xC55) + KBTS_C2(0xC46, 0xC55) + KBTS_C2(0xC4A, 0xC55) + KBTS_C2(0xC89, 0xCBE) + KBTS_C2(0xC8B, 0xCBE) + KBTS_C2(0xC92, 0xCCC) + KBTS_C2(0xD07, 0xD57) + KBTS_C2(0xD09, 0xD57) + KBTS_C2(0xD0E, 0xD46) + KBTS_C2(0xD12, 0xD3E) + KBTS_C2(0xD12, 0xD57) + KBTS_C2(0xD85, 0xDCF) + KBTS_C2(0xD85, 0xDD0) + KBTS_C2(0xD85, 0xDD1) + KBTS_C2(0xD8B, 0xDDF) + KBTS_C2(0xD8D, 0xDD8) + KBTS_C2(0xD8F, 0xDDF) + KBTS_C2(0xD91, 0xDCA) + KBTS_C2(0xD91, 0xDD9) + KBTS_C2(0xD91, 0xDDA) + KBTS_C2(0xD91, 0xDDC) + KBTS_C2(0xD91, 0xDDD) + KBTS_C2(0xD91, 0xDDE) + KBTS_C2(0xD94, 0xDDF) + KBTS_C2(0x11005, 0x11038) + KBTS_C2(0x1100B, 0x1103E) + KBTS_C2(0x1100F, 0x11042) + KBTS_C2(0x11200, 0x1122C) + KBTS_C2(0x11200, 0x11231) + KBTS_C2(0x11200, 0x11233) + KBTS_C2(0x11206, 0x1122C) + KBTS_C2(0x1122C, 0x11230) + KBTS_C2(0x1122C, 0x11231) + KBTS_C2(0x11240, 0x1122E) + KBTS_C2(0x112B0, 0x112E0) + KBTS_C2(0x112B0, 0x112E5) + KBTS_C2(0x112B0, 0x112E6) + KBTS_C2(0x112B0, 0x112E7) + KBTS_C2(0x112B0, 0x112E8) + KBTS_C2(0x11481, 0x114B0) + KBTS_C2(0x1148B, 0x114BA) + KBTS_C2(0x1148D, 0x114BA) + KBTS_C2(0x114AA, 0x114B5) + KBTS_C2(0x114AA, 0x114B6) + KBTS_C2(0x11600, 0x11639) + KBTS_C2(0x11600, 0x1163A) + KBTS_C2(0x11601, 0x11639) + KBTS_C2(0x11601, 0x1163A) + KBTS_C2(0x11680, 0x116AD) + KBTS_C2(0x11680, 0x116B4) + KBTS_C2(0x11680, 0x116B5) + KBTS_C2(0x11686, 0x116B2) { - KBTS_INSTRUMENT_END - return 1; - } - GlyphArray->Glyphs[S->GlyphIndex] = Config->DottedCircle; - } - } - #undef KBTS_C2 - } - - S->WrittenCount = GlyphArray->TotalCount; - KBTS_INSTRUMENT_END - } break; - - case KBTS_OP_KIND_NORMALIZE: - { - KBTS_INSTRUMENT_BLOCK_BEGIN("NORMALIZE") - // @Incomplete: We need to honor this. - // HB_OT_SHAPE_NORMALIZATION_MODE_NONE, - // HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, - // HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* Always fully decomposes and then recompose back */ - // - // hangul: HB_OT_SHAPE_NORMALIZATION_MODE_NONE, - // arabic: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, - // default: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, - // hebrew: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, - // thai: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, - // indic: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, - // khmer: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, - // myanmar: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, - // use: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, - - { // Full NFD decomposition - kbts_glyph *DecompositionGlyphs; DecompositionGlyphs = KBTS_POINTER_AFTER(kbts_glyph, S); - for(S->GlyphIndex = 0; S->GlyphIndex < GlyphArray->Count; ++S->GlyphIndex) - { - DecompositionGlyphs[0] = Glyphs[S->GlyphIndex]; - S->OpSpecific.Normalize.CodepointsToDecomposeCount = 1; - - while(S->OpSpecific.Normalize.CodepointsToDecomposeCount) - { - if(0) - { - ResumePoint1:; - DecompositionGlyphs = KBTS_POINTER_AFTER(kbts_glyph, S); - } - - kbts_glyph GlyphToDecompose = DecompositionGlyphs[--S->OpSpecific.Normalize.CodepointsToDecomposeCount]; - kbts_u64 Decomposition = 0; - kbts_u32 DecompositionSize = 0; - kbts_u32 AnyUnsupported = 0; - kbts_glyph Decomposed[2]; - - if(!(GlyphToDecompose.Flags & KBTS_GLYPH_FLAG_DO_NOT_DECOMPOSE)) - { - Decomposition = GlyphToDecompose.Decomposition; - DecompositionSize = kbts_GetDecompositionSize(Decomposition); - - // Only decompose when the font supports the decomposed form. - KBTS_FOR(DecompositionIndex, 0, DecompositionSize) + kbts_glyph *NewGlyph = kbts__InsertGlyphBefore(Storage, Glyph, &Config->DottedCircle); + if(!NewGlyph) { - kbts_glyph DecompositionGlyph = kbts_CodepointToGlyph(Font, kbts_GetDecompositionCodepoint(Decomposition, DecompositionIndex)); - DecompositionGlyph.Config = GlyphToDecompose.Config; - - AnyUnsupported |= !DecompositionGlyph.Id; - Decomposed[DecompositionIndex] = DecompositionGlyph; + goto OutOfMemory; } + } break; } + } + #undef KBTS_C2 + } - if(AnyUnsupported | !DecompositionSize) + KBTS_INSTRUMENT_BLOCK_END(PRE_NORMALIZE_DOTTED_CIRCLES); + } break; + + case KBTS__OP_KIND_NORMALIZE: + { + KBTS_INSTRUMENT_BLOCK_BEGIN(NORMALIZE); + // @Incomplete: We need to honor this. + // HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + // HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, + // HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* Always fully decomposes and then recompose back */ + // + // hangul: HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + // arabic: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, + // default: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, + // hebrew: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, + // thai: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, + // indic: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + // khmer: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + // myanmar: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + // use: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + + KBTS_INSTRUMENT_BLOCK_BEGIN(Decompose); + { // Full NFD decomposition + kbts__arena_lifetime Lifetime = kbts__BeginLifetime(Scratchpad->Arena); + kbts_glyph *DecompositionGlyphs = kbts__PushArray(Scratchpad->Arena, kbts_glyph, KBTS__MAXIMUM_DECOMPOSITION_CODEPOINTS); + kbts_un CodepointsToDecomposeCount = 0; + + KBTS__FOR_GLYPH(Storage, Glyph) + { + if(kbts__GetDecompositionSize(Glyph->Decomposition)) { - if(S->WrittenCount > S->GlyphIndex) - { - if(!kbts_GrowGlyphArray(&S->ResumePoint, GlyphArray, S->WrittenCount, 1, 1, 0)) - { - S->OpSpecific.Normalize.CodepointsToDecomposeCount += 1; // Push the codepoint back on the stack. + kbts_glyph *PrevAnchor = Glyph->Prev; - KBTS_INSTRUMENT_END - return 1; + DecompositionGlyphs[0] = *Glyph; + CodepointsToDecomposeCount = 1; + + kbts__FreeGlyph(Scratchpad, Storage, Glyph); + + while(CodepointsToDecomposeCount) + { + kbts_glyph GlyphToDecompose = DecompositionGlyphs[--CodepointsToDecomposeCount]; + kbts_u64 Decomposition = 0; + kbts_u32 DecompositionSize = 0; + int AnyUnsupported = 0; + kbts_glyph Decomposed[2]; + + if(!(GlyphToDecompose.Flags & KBTS_GLYPH_FLAG_DO_NOT_DECOMPOSE)) + { + Decomposition = GlyphToDecompose.Decomposition; + DecompositionSize = kbts__GetDecompositionSize(Decomposition); + + // Only decompose when the font supports the decomposed form. + KBTS__FOR(DecompositionIndex, 0, DecompositionSize) + { + kbts_glyph DecompositionGlyph = kbts_CodepointToGlyph(Font, (int)kbts__GetDecompositionCodepoint(Decomposition, DecompositionIndex), 0, 0); + DecompositionGlyph.Config = GlyphToDecompose.Config; + DecompositionGlyph.UserIdOrCodepointIndex = GlyphToDecompose.UserIdOrCodepointIndex; + + AnyUnsupported |= !DecompositionGlyph.Id; + Decomposed[DecompositionIndex] = DecompositionGlyph; + } } - S->GlyphIndex += 1; - } - - Glyphs[S->WrittenCount++] = GlyphToDecompose; - } - else - { - KBTS_ASSERT((S->OpSpecific.Normalize.CodepointsToDecomposeCount + DecompositionSize) <= KBTS_MAXIMUM_DECOMPOSITION_CODEPOINTS); - - if(Decomposition & KBTS_UNICODE_DECOMPOSITION_DO_NOT_RECURSE0) - { - Decomposed[0].Flags |= KBTS_GLYPH_FLAG_DO_NOT_DECOMPOSE; - } - if(Decomposition & KBTS_UNICODE_DECOMPOSITION_DO_NOT_RECURSE1) - { - Decomposed[1].Flags |= KBTS_GLYPH_FLAG_DO_NOT_DECOMPOSE; - } - - KBTS_FOR(DecompositionIndex, 0, DecompositionSize) - { - // We reverse the glyphs here because we use a stack. - DecompositionGlyphs[S->OpSpecific.Normalize.CodepointsToDecomposeCount++] = Decomposed[DecompositionSize - 1 - DecompositionIndex]; - } - } - } - } - } - - { // Selective recomposition. - // The OpenType shaping documents say that Hebrew Alphabetic Presentation Form compositions aren't canonical, - // but looking at UnicodeData.txt, it seems like they totally are, so they are handled here. - kbts_glyph *LastBase = 0; - kbts_un LastBaseParentCount = 0; - kbts_s32 *LastBaseParentDeltas = 0; - kbts_un PreSlashDecimalDigitCount = 0; - kbts_un DecimalDigitCount = 0; - int InFraction = 0; - - kbts_u32 BeforeFractionSlashGlyphFlags = KBTS_GLYPH_FLAG_NUMR | KBTS_GLYPH_FLAG_FRAC; - kbts_u32 AfterFractionSlashGlyphFlags = KBTS_GLYPH_FLAG_DNOM | KBTS_GLYPH_FLAG_FRAC; - if(kbts_ShaperRtl(Config->Shaper)) - { - // RTL needs to invert NUMR and DNOM. - kbts_u32 Swap = BeforeFractionSlashGlyphFlags; - BeforeFractionSlashGlyphFlags = AfterFractionSlashGlyphFlags; - AfterFractionSlashGlyphFlags = Swap; - } - - // We also collate user features here. - kbts_feature_set UserFeatures = KBTS_ZERO; - kbts_feature_set *DefaultFeatures = Config->Features; - - KBTS_FOR(GlyphIndex, 0, S->WrittenCount) - { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - Glyph->Uid = (kbts_u16)++ShapeState->NextGlyphUid; - - if(Glyph->Config) - { - kbts_glyph_config *GlyphConfig = Glyph->Config; - KBTS_FOR(WordIndex, 0, KBTS_ARRAY_LENGTH(UserFeatures.Flags)) - { - UserFeatures.Flags[WordIndex] |= GlyphConfig->EnabledFeatures.Flags[WordIndex] & ~DefaultFeatures->Flags[WordIndex]; - } - } - - kbts_un AvailableGlyphCount = 0; - if(!Glyph->CombiningClass) - { - LastBase = Glyph; - // From the Microsoft docs: - // USE decomposes split vowel characters belonging to UISC = Vowel_Dependent according to character - // decomposition mappings defined in UnicodeData.txt - // Cluster validation, is done based on the decomposed state of a split vowel. - // - // (Note: our Matra corresponds to Vowel_Dependent + Pure_Killer.) - if((Config->Shaper != KBTS_SHAPER_USE) || (Glyph->SyllabicClass != KBTS_INDIC_SYLLABIC_CLASS_MATRA)) - { - LastBaseParentDeltas = kbts_GetParentInfoDeltas(Glyph->ParentInfo); - LastBaseParentCount = kbts_GetParentInfoCount(Glyph->ParentInfo); - AvailableGlyphCount = 1; - } - else - { - LastBaseParentDeltas = 0; - LastBaseParentCount = 0; - } - } - else - { - if(LastBase) - { - AvailableGlyphCount = 2; - } - } - - KBTS_FOR(ParentIndex, 0, LastBaseParentCount) - { - kbts_u32 ParentCodepoint = LastBase->Codepoint + (kbts_u32)LastBaseParentDeltas[ParentIndex]; - kbts_glyph ParentGlyph = kbts_CodepointToGlyph(Font, ParentCodepoint); - kbts_u32 DecompositionSize = kbts_GetDecompositionSize(ParentGlyph.Decomposition); - - ParentGlyph.Uid = LastBase->Uid; - ParentGlyph.Config = LastBase->Config; - if((DecompositionSize == AvailableGlyphCount) && ParentGlyph.Id) - { - if(DecompositionSize == 2) - { - if(kbts_GetDecompositionCodepoint(ParentGlyph.Decomposition, 1) == Glyph->Codepoint) + if(AnyUnsupported | !DecompositionSize) { - // Both match. Reclaim space. - // @Speed: We should use separate read and write cursors instead. - KBTS_FOR(ShrinkIndex, GlyphIndex + 1, S->WrittenCount) + kbts_glyph *NewGlyph = kbts__InsertGlyphAfter(Storage, PrevAnchor, &GlyphToDecompose); + if(!NewGlyph) { - Glyphs[ShrinkIndex - 1] = Glyphs[ShrinkIndex]; + goto OutOfMemory; } - S->WrittenCount -= 1; + + PrevAnchor = NewGlyph; } else { - continue; + KBTS_ASSERT((CodepointsToDecomposeCount + DecompositionSize) <= KBTS__MAXIMUM_DECOMPOSITION_CODEPOINTS); + + if(Decomposition & KBTS_UNICODE_DECOMPOSITION_DO_NOT_RECURSE0) + { + Decomposed[0].Flags |= KBTS_GLYPH_FLAG_DO_NOT_DECOMPOSE; + } + if(Decomposition & KBTS_UNICODE_DECOMPOSITION_DO_NOT_RECURSE1) + { + Decomposed[1].Flags |= KBTS_GLYPH_FLAG_DO_NOT_DECOMPOSE; + } + + KBTS__FOR(DecompositionIndex, 0, DecompositionSize) + { + // We reverse the glyphs here because we use a stack. + DecompositionGlyphs[CodepointsToDecomposeCount++] = Decomposed[DecompositionSize - 1 - DecompositionIndex]; + } } } - *LastBase = ParentGlyph; - GlyphIndex = (kbts_un)((LastBase - Glyphs) - 1); // Handle recursive recomposition. + Glyph = PrevAnchor; + } + } + + kbts__EndLifetime(&Lifetime); + } + KBTS_INSTRUMENT_BLOCK_END(Decompose); + + KBTS_INSTRUMENT_BLOCK_BEGIN(Recompose); + { // Selective recomposition. + // The OpenType shaping documents say that Hebrew Alphabetic Presentation Form compositions aren't canonical, + // but looking at UnicodeData.txt, it seems like they totally are, so they are handled here. + kbts__arena_lifetime Lifetime = kbts__BeginLifetime(Scratchpad->Arena); + kbts_glyph *LastBase = 0; + kbts_un LastBaseParentCount = 0; + kbts_un PreSlashDecimalDigitCount = 0; + kbts_glyph *PreSlashGlyph = 0; + kbts_glyph *DigitGlyph = 0; + kbts_un DecimalDigitCount = 0; + int InFraction = 0; + kbts_u32 LastBaseParentsLoaded = 0; + kbts_glyph_parent LastBaseParents[KBTS_MAXIMUM_RECOMPOSITION_PARENTS]; + kbts_glyph *Parents = kbts__PushArray(Scratchpad->Arena, kbts_glyph, KBTS_MAXIMUM_RECOMPOSITION_PARENTS); + + kbts_u32 BeforeFractionSlashGlyphFlags = KBTS_GLYPH_FLAG_NUMR | KBTS_GLYPH_FLAG_FRAC; + kbts_u32 AfterFractionSlashGlyphFlags = KBTS_GLYPH_FLAG_DNOM | KBTS_GLYPH_FLAG_FRAC; + if(kbts__ShaperRtl(Config->Shaper)) + { + // RTL needs to invert NUMR and DNOM. + kbts_u32 Swap = BeforeFractionSlashGlyphFlags; + BeforeFractionSlashGlyphFlags = AfterFractionSlashGlyphFlags; + AfterFractionSlashGlyphFlags = Swap; + } + + // We also collate user features here. + kbts__feature_set UserFeatures = KBTS__ZERO; + kbts__feature_set *DefaultFeatures = &Config->Features; + + int ShouldFlip = (Scratchpad->RunDirection == KBTS_DIRECTION_RTL); + + KBTS__FOR_GLYPH(Storage, Glyph) + { + Glyph->Uid = (kbts_u16)++Scratchpad->NextGlyphUid; + + if(Glyph->Config) + { + kbts_glyph_config *GlyphConfig = Glyph->Config; + KBTS__FOR(WordIndex, 0, KBTS__ARRAY_LENGTH(UserFeatures.Flags)) + { + UserFeatures.Flags[WordIndex] |= GlyphConfig->EnabledFeatures.Flags[WordIndex] & ~DefaultFeatures->Flags[WordIndex]; + } + } + + // In RTL, mirror all glyphs when their mirror is covered. + if(ShouldFlip && + (Glyph->UnicodeFlags & KBTS_UNICODE_FLAG_MIRRORED)) + { + kbts_u32 MatchingBracketCodepoint = kbts__GetUnicodeMirrorCodepoint(Glyph->Codepoint); + kbts_glyph MatchingBracket = kbts_CodepointToGlyph(Font, (int)MatchingBracketCodepoint, 0, 0); + if(MatchingBracket.Id) + { + kbts__SetGlyphPreserveLinksAndUserId(Glyph, &MatchingBracket); + } + } + + kbts_u32 SingleRecompositionCodepoints[KBTS_MAXIMUM_RECOMPOSITION_PARENTS]; + kbts_un SingleRecompositionCodepointCount = 0; + kbts_un DoubleRecompositionCount = LastBaseParentCount; + + if(!Glyph->CombiningClass) + { + LastBase = Glyph; + // From the Microsoft docs: + // USE decomposes split vowel characters belonging to UISC = Vowel_Dependent according to character + // decomposition mappings defined in UnicodeData.txt + // Cluster validation, is done based on the decomposed state of a split vowel. + // + // (Note: our Matra corresponds to Vowel_Dependent + Pure_Killer.) + if((Config->Shaper != KBTS_SHAPER_USE) || (Glyph->SyllabicClass != KBTS_INDIC_SYLLABIC_CLASS_MATRA)) + { + kbts_s32 *LastBaseParentDeltas = kbts__GetParentInfoDeltas(Glyph->ParentInfo); + kbts_un ParentCount = kbts__GetParentInfoCount(Glyph->ParentInfo); + + kbts_un DoubleDecompositionCount = 0; + KBTS__FOR(ParentIndex, 0, ParentCount) + { + kbts_glyph_parent Parent = KBTS__ZERO; + Parent.Codepoint = Glyph->Codepoint + (kbts_u32)LastBaseParentDeltas[ParentIndex]; + Parent.Decomposition = kbts__GetUnicodeDecomposition(Parent.Codepoint); + + kbts_un DecompositionSize = kbts__GetDecompositionSize(Parent.Decomposition); + if(DecompositionSize == 1) + { + SingleRecompositionCodepoints[SingleRecompositionCodepointCount++] = Parent.Codepoint; + } + else + { + LastBaseParents[DoubleDecompositionCount++] = Parent; + } + } + + LastBaseParentCount = DoubleDecompositionCount; + LastBaseParentsLoaded = 0; + } + else + { + LastBaseParentCount = 0; + } + + DoubleRecompositionCount = 0; + } + + int Recomposed = 0; + + if(!Recomposed) + { + KBTS_INSTRUMENT_BLOCK_BEGIN(ParentNSquaredStupidity); + KBTS__FOR(ParentIndex, 0, DoubleRecompositionCount) + { + kbts_glyph_parent *Parent = &LastBaseParents[ParentIndex]; + kbts_u32 Codepoint1 = kbts__GetDecompositionCodepoint(Parent->Decomposition, 1); + + if(Glyph->Codepoint == Codepoint1) + { + kbts_glyph *ParentGlyph = &Parents[ParentIndex]; + if(!(LastBaseParentsLoaded & (1 << ParentIndex))) + { + *ParentGlyph = kbts_CodepointToGlyph(Font, (int)Parent->Codepoint, 0, 0); + ParentGlyph->Uid = LastBase->Uid; + ParentGlyph->UserIdOrCodepointIndex = LastBase->UserIdOrCodepointIndex; + ParentGlyph->Config = LastBase->Config; + } + + if(ParentGlyph->Id) + { + // Both match. Reclaim space. + kbts_glyph *Next = Glyph->Next; + KBTS__DLLIST_REMOVE(Glyph); + Glyph = Next; + + Recomposed = 1; + kbts__SetGlyphPreserveLinksAndUserId(LastBase, ParentGlyph); + + break; + } + else + { + // This glyph is never good. Forget it. + LastBaseParents[ParentIndex] = LastBaseParents[LastBaseParentCount - 1]; + Parents[ParentIndex] = Parents[LastBaseParentCount - 1]; + LastBaseParentsLoaded &= ~(1 << ParentIndex); + LastBaseParentsLoaded |= (LastBaseParentsLoaded & (1 << (LastBaseParentCount - 1))) >> (LastBaseParentCount - 1 - ParentIndex); + + LastBaseParentCount -= 1; + DoubleRecompositionCount -= 1; + ParentIndex -= 1; + } + } + } + KBTS_INSTRUMENT_BLOCK_END(ParentNSquaredStupidity); + } + + if(!Recomposed) + { + KBTS__FOR(SingleRecompositionIndex, 0, SingleRecompositionCodepointCount) + { + kbts_glyph ParentGlyph = kbts_CodepointToGlyph(Font, (int)SingleRecompositionCodepoints[SingleRecompositionIndex], 0, 0); + if(ParentGlyph.Id) + { + ParentGlyph.Config = Glyph->Config; + + kbts__SetGlyphPreserveLinksAndUserId(Glyph, &ParentGlyph); + Recomposed = 1; + break; + } + } + } + + // It is safe to look for fractions here, because decimal digits/the fraction slash are not marks or + // jamos, so they should not get reordered after this pass. + if(Glyph->UnicodeFlags & KBTS_UNICODE_FLAG_DECIMAL_DIGIT) + { + if(InFraction) + { + // We are in the post-slash part of the fraction. + Glyph->Flags |= AfterFractionSlashGlyphFlags; + // Only flag the pre-slash part of the fraction if there is a post-slash part. + KBTS__FOR(DecimalDigitIndex, 0, PreSlashDecimalDigitCount) + { + PreSlashGlyph->Flags |= BeforeFractionSlashGlyphFlags; + PreSlashGlyph = PreSlashGlyph->Next; + } + PreSlashDecimalDigitCount = 0; + } + if(!DecimalDigitCount) + { + DigitGlyph = Glyph; + } + DecimalDigitCount += 1; + } + else if((Glyph->Codepoint == 0x2044) && (!InFraction || DecimalDigitCount)) + { + // Fraction slash. + Glyph->Flags |= KBTS_GLYPH_FLAG_FRAC; + PreSlashDecimalDigitCount = DecimalDigitCount; + PreSlashGlyph = DigitGlyph; + InFraction = DecimalDigitCount != 0; + DecimalDigitCount = 0; + } + else + { + InFraction = 0; + } + + if(Recomposed) + { + // @Robustness: This doesn't work when LastBase != Glyph->Prev, does it? + Glyph = Glyph->Prev; // Handle recursive recomposition. + } + } + + // Ignore added features that are already part of the shaper. + kbts__feature_set *ShaperFeatures = &Config->Features; + KBTS__FOR(WordIndex, 0, KBTS__ARRAY_LENGTH(UserFeatures.Flags)) + { + UserFeatures.Flags[WordIndex] &= ~ShaperFeatures->Flags[WordIndex]; + } + + Scratchpad->UserFeatures = UserFeatures; + kbts__EndLifetime(&Lifetime); + } + KBTS_INSTRUMENT_BLOCK_END(Recompose); + + KBTS_INSTRUMENT_BLOCK_BEGIN(MarkReordering); + { // Unicode mark reordering. + for(kbts_glyph *Glyph = Storage->GlyphSentinel.Next; + kbts__GlyphIsValid(Storage, Glyph); + ) + { + kbts_u8 CombiningClass = Glyph->CombiningClass; + + if(CombiningClass) + { + Glyph->MarkOrdering = CombiningClass; + + kbts_glyph *SequenceGlyph = Glyph->Next; + for(; + kbts__GlyphIsValid(Storage, SequenceGlyph); + SequenceGlyph = SequenceGlyph->Next) + { + kbts_u8 SequenceGlyphCombiningClass = SequenceGlyph->CombiningClass; + if(SequenceGlyphCombiningClass) + { + SequenceGlyph->MarkOrdering = SequenceGlyphCombiningClass; + } + else + { + break; + } + } + + kbts_glyph *AfterSequence = SequenceGlyph; + if(kbts__GlyphIsValid(Storage, AfterSequence)) + { + AfterSequence = AfterSequence->Next; + } + + KBTS__DLLIST_SORT(Glyph, SequenceGlyph, MarkOrdering); + + #ifdef KBTS_SANITY_CHECK + { + kbts_glyph *Glyph0 = OneBeforeSequence->Next; + kbts_glyph *Glyph1 = Glyph0->Next; + KBTS__FOR(SequenceIndex, 1, MarkSequenceLength) + { + KBTS_ASSERT(Glyph0->MarkOrdering <= Glyph1->MarkOrdering); + + Glyph0 = Glyph0->Next; + Glyph1 = Glyph1->Next; + } + } + #endif + + Glyph = AfterSequence; + } + else + { + Glyph = Glyph->Next; + } + } + } + KBTS_INSTRUMENT_BLOCK_END(MarkReordering); + + if(Config->Script == KBTS_SCRIPT_ARABIC) + { + for(kbts_glyph *Glyph = Storage->GlyphSentinel.Next; + kbts__GlyphIsValid(Storage, Glyph); + ) + { + kbts_glyph *Next = Glyph->Next; + + // Find a mark sequence. + kbts_u8 CombiningClass = Glyph->CombiningClass; + + if(CombiningClass) + { + // Arabic: Reorder sequences of mark glyphs. + // + // From the Unicode standard: + // - Move any shadda characters (ccc=33) to the beginning of S. + // - If a sequence of ccc=230 characters begins with any MCM characters, move the sequence of such MCM + // characters + // to the beginning of S (before any characters with ccc=33). + // - If a sequence of ccc=220 characters begins with any MCM characters, move the sequence of such MCM + // characters + // to the beginning of S (before any MCM with ccc=230 or ccc=33). + // + // Final ordering: 220 230 shadda other + + kbts__mcm_sequence_state Mcm220SequenceState = 0; + kbts__mcm_sequence_state Mcm230SequenceState = 0; + + # define KBTS_REMAPPED_CCC_33 27 + + kbts_glyph *SequenceGlyph = Glyph; + for(; + kbts__GlyphIsValid(Storage, SequenceGlyph); + SequenceGlyph = SequenceGlyph->Next) + { + kbts_u16 SequenceGlyphCombiningClass = SequenceGlyph->CombiningClass; + kbts_u16 SequenceGlyphFlags = SequenceGlyph->UnicodeFlags; + + kbts_u8 MarkOrdering = 3; + + if(SequenceGlyphCombiningClass == KBTS_REMAPPED_CCC_33) + { + MarkOrdering = 2; + } + else if(SequenceGlyphCombiningClass == 220) + { + if(SequenceGlyphFlags & KBTS_UNICODE_FLAG_MODIFIER_COMBINING_MARK) + { + if(Mcm220SequenceState != KBTS__MCM_SEQUENCE_STATE_OUT) + { + Mcm220SequenceState = KBTS__MCM_SEQUENCE_STATE_IN; + } + } + else + { + Mcm220SequenceState = KBTS__MCM_SEQUENCE_STATE_OUT; + } + + if(Mcm220SequenceState == KBTS__MCM_SEQUENCE_STATE_IN) + { + MarkOrdering = 0; + } + + Mcm230SequenceState = KBTS__MCM_SEQUENCE_STATE_NONE; + } + else if(SequenceGlyphCombiningClass == 230) + { + if(SequenceGlyphFlags & KBTS_UNICODE_FLAG_MODIFIER_COMBINING_MARK) + { + if(Mcm230SequenceState != KBTS__MCM_SEQUENCE_STATE_OUT) + { + Mcm230SequenceState = KBTS__MCM_SEQUENCE_STATE_IN; + } + } + else + { + Mcm230SequenceState = KBTS__MCM_SEQUENCE_STATE_OUT; + } + + if(Mcm230SequenceState == KBTS__MCM_SEQUENCE_STATE_IN) + { + MarkOrdering = 1; + } + } + else if(SequenceGlyphCombiningClass) + { + Mcm220SequenceState = KBTS__MCM_SEQUENCE_STATE_NONE; + Mcm230SequenceState = KBTS__MCM_SEQUENCE_STATE_NONE; + } + else + { + break; + } + + SequenceGlyph->MarkOrdering = MarkOrdering; + } + + KBTS__DLLIST_SORT(Glyph, SequenceGlyph, MarkOrdering); + + #ifdef KBTS_SANITY_CHECK + { + kbts_glyph *Glyph0 = OneBeforeGlyph->Next; + for(kbts_glyph *Glyph1 = Glyph0->Next; + Glyph1 != SequenceGlyph; + Glyph1 = Glyph1->Next) + { + KBTS_ASSERT(Glyph0->MarkOrdering <= Glyph1->MarkOrdering); + Glyph0 = Glyph1; + } + } + #endif + + Next = SequenceGlyph; + } + + Glyph = Next; + } + } + else if((Config->Script == KBTS_SCRIPT_THAI) || (Config->Script == KBTS_SCRIPT_LAO)) + { + // Decompose sara/sala ams. + kbts_glyph *AboveBaseGlyph; AboveBaseGlyph = 0; + + KBTS__FOR_GLYPH(Storage, Glyph) + { + kbts_u32 Codepoint = Glyph->Codepoint; + + switch(Codepoint) + { + // Sara am/sala am. + // We match both because storing the sara am codepoint that corresponds to the current script + // doesn't seem that worthwhile, given that this is already a pretty big switch case. + // If we choose to use a unicode flag or indic syllabic category to notate above-base marks, + // then this loops gets a lot tighter and it would probably become the right call to pre-determine + // the sara am codepoint. + case 0xE33: case 0xEB3: // Sara am + { + kbts_glyph *NewGlyph = kbts__InsertGlyphBefore(Storage, AboveBaseGlyph, &Config->Nikhahit); + if(!NewGlyph) + { + goto OutOfMemory; + } + + kbts__SetGlyphPreserveLinksAndUserId(Glyph, &Config->SaraAa); + } break; + + case 0xE31: case 0xE34: case 0xE35: case 0xE36: case 0xE37: case 0xE3B: + case 0xE47: case 0xE48: case 0xE49: case 0xE4A: case 0xE4B: case 0xE4C: case 0xE4D: case 0xE4E: + case 0xEB1: case 0xEB4: case 0xEB5: case 0xEB6: case 0xEB7: case 0xEBB: + case 0xEC7: case 0xEC8: case 0xEC9: case 0xECA: case 0xECB: case 0xECC: case 0xECD: case 0xECE: + if(!AboveBaseGlyph) + { + AboveBaseGlyph = Glyph; + } + break; + + default: + AboveBaseGlyph = 0; break; } } - - // It is safe to look for fractions here, because decimal digits/the fraction slash are not marks or - // jamos, so they should not get reordered after this pass. - if(Glyph->UnicodeFlags & KBTS_UNICODE_FLAG_DECIMAL_DIGIT) - { - if(InFraction) - { - // We are in the post-slash part of the fraction. - Glyph->Flags |= AfterFractionSlashGlyphFlags; - // Only flag the pre-slash part of the fraction if there is a post-slash part. - KBTS_FOR(DecimalDigitIndex, 0, PreSlashDecimalDigitCount) - { - Glyphs[GlyphIndex - PreSlashDecimalDigitCount - 1 + DecimalDigitIndex].Flags |= BeforeFractionSlashGlyphFlags; - } - PreSlashDecimalDigitCount = 0; - } - DecimalDigitCount += 1; - } - else if((Glyph->Codepoint == 0x2044) && (!InFraction || DecimalDigitCount)) - { - // Fraction slash. - Glyph->Flags |= KBTS_GLYPH_FLAG_FRAC; - PreSlashDecimalDigitCount = DecimalDigitCount; - InFraction = DecimalDigitCount != 0; - DecimalDigitCount = 0; - } - else - { - InFraction = 0; - } } - // Ignore added features that are already part of the shaper. - kbts_feature_set *ShaperFeatures = Config->Features; - KBTS_FOR(WordIndex, 0, KBTS_ARRAY_LENGTH(UserFeatures.Flags)) - { - UserFeatures.Flags[WordIndex] &= ~ShaperFeatures->Flags[WordIndex]; - } + KBTS_INSTRUMENT_BLOCK_END(NORMALIZE); + } break; - ShapeState->UserFeatures = UserFeatures; - } - - { // Unicode mark reordering. - S->GlyphIndex = 0; - while(S->GlyphIndex < S->WrittenCount) - { - kbts_glyph *Glyph = &Glyphs[S->GlyphIndex]; - kbts_u8 CombiningClass = Glyph->CombiningClass; - - kbts_un IndexIncrement = 1; - - if(CombiningClass) - { - Glyph->MarkOrdering = CombiningClass; - kbts_un MarkSequenceLength = 1; - while((S->GlyphIndex + MarkSequenceLength) < S->WrittenCount) - { - kbts_glyph *SequenceGlyph = &Glyphs[S->GlyphIndex + MarkSequenceLength]; - kbts_u8 SequenceGlyphCombiningClass = SequenceGlyph->CombiningClass; - if(SequenceGlyphCombiningClass) - { - SequenceGlyph->MarkOrdering = SequenceGlyphCombiningClass; - MarkSequenceLength += 1; - } - else - { - break; - } - } - - KBTS_FOR(Iter, 0, MarkSequenceLength) - { - KBTS_FOR(SequenceIndex, 1, MarkSequenceLength) - { - kbts_glyph *Glyph0 = &Glyphs[S->GlyphIndex + SequenceIndex - 1]; - kbts_glyph *Glyph1 = &Glyphs[S->GlyphIndex + SequenceIndex]; - - if(Glyph0->MarkOrdering > Glyph1->MarkOrdering) - { - kbts_glyph Swap = *Glyph0; - *Glyph0 = *Glyph1; - *Glyph1 = Swap; - } - } - } - - IndexIncrement = MarkSequenceLength; - } - - S->GlyphIndex += IndexIncrement; - } - } - - if(Config->Script == KBTS_SCRIPT_ARABIC) + case KBTS__OP_KIND_NORMALIZE_HANGUL: { - S->GlyphIndex = 0; - while(S->GlyphIndex < S->WrittenCount) + KBTS_INSTRUMENT_BLOCK_BEGIN(NORMALIZE_HANGUL); + + for(kbts_glyph *Glyph = Storage->GlyphSentinel.Next; + kbts__GlyphIsValid(Storage, Glyph); + ) { - // Find a mark sequence. - kbts_glyph *Glyph = &Glyphs[S->GlyphIndex]; - kbts_u8 CombiningClass = Glyph->CombiningClass; + kbts_glyph *Next = Glyph->Next; - if(CombiningClass) - { - // Arabic: Reorder sequences of mark glyphs. - // - // From the Unicode standard: - // - Move any shadda characters (ccc=33) to the beginning of S. - // - If a sequence of ccc=230 characters begins with any MCM characters, move the sequence of such MCM - // characters - // to the beginning of S (before any characters with ccc=33). - // - If a sequence of ccc=220 characters begins with any MCM characters, move the sequence of such MCM - // characters - // to the beginning of S (before any MCM with ccc=230 or ccc=33). - // - // Final ordering: 220 230 shadda other - - kbts_mcm_sequence_state Mcm220SequenceState = 0; - kbts_mcm_sequence_state Mcm230SequenceState = 0; - - kbts_un SequenceStart = S->GlyphIndex; - -# define KBTS_REMAPPED_CCC_33 27 - - while(S->GlyphIndex < S->WrittenCount) - { - kbts_glyph *SequenceGlyph = &Glyphs[S->GlyphIndex]; - kbts_u16 SequenceGlyphCombiningClass = SequenceGlyph->CombiningClass; - kbts_u16 SequenceGlyphFlags = SequenceGlyph->UnicodeFlags; - - kbts_u8 MarkOrdering = 3; - - if(SequenceGlyphCombiningClass == KBTS_REMAPPED_CCC_33) - { - MarkOrdering = 2; - } - else if(SequenceGlyphCombiningClass == 220) - { - if(SequenceGlyphFlags & KBTS_UNICODE_FLAG_MODIFIER_COMBINING_MARK) - { - if(Mcm220SequenceState != KBTS_MCM_SEQUENCE_STATE_OUT) - { - Mcm220SequenceState = KBTS_MCM_SEQUENCE_STATE_IN; - } - } - else - { - Mcm220SequenceState = KBTS_MCM_SEQUENCE_STATE_OUT; - } - - if(Mcm220SequenceState == KBTS_MCM_SEQUENCE_STATE_IN) - { - MarkOrdering = 0; - } - - Mcm230SequenceState = KBTS_MCM_SEQUENCE_STATE_NONE; - } - else if(SequenceGlyphCombiningClass == 230) - { - if(SequenceGlyphFlags & KBTS_UNICODE_FLAG_MODIFIER_COMBINING_MARK) - { - if(Mcm230SequenceState != KBTS_MCM_SEQUENCE_STATE_OUT) - { - Mcm230SequenceState = KBTS_MCM_SEQUENCE_STATE_IN; - } - } - else - { - Mcm230SequenceState = KBTS_MCM_SEQUENCE_STATE_OUT; - } - - if(Mcm230SequenceState == KBTS_MCM_SEQUENCE_STATE_IN) - { - MarkOrdering = 1; - } - } - else if(SequenceGlyphCombiningClass) - { - Mcm220SequenceState = KBTS_MCM_SEQUENCE_STATE_NONE; - Mcm230SequenceState = KBTS_MCM_SEQUENCE_STATE_NONE; - } - else - { - break; - } - - SequenceGlyph->MarkOrdering = MarkOrdering; - S->GlyphIndex += 1; - } - - KBTS_FOR(Iter, SequenceStart, S->GlyphIndex) - { - KBTS_FOR(SortIndex, SequenceStart + 1, S->GlyphIndex) - { - kbts_glyph *Left = &Glyphs[SortIndex - 1]; - kbts_glyph *Right = &Glyphs[SortIndex]; - - if(Left->MarkOrdering > Right->MarkOrdering) - { - kbts_glyph Swap = *Left; - *Left = *Right; - *Right = Swap; - } - } - } - } - else - { - S->GlyphIndex += 1; - } - } - } - else if((Config->Script == KBTS_SCRIPT_THAI) || (Config->Script == KBTS_SCRIPT_LAO)) - { - // Decompose sara/sala ams. - kbts_un AboveBaseGlyphCount; AboveBaseGlyphCount = 0; - for(S->GlyphIndex = 0; S->GlyphIndex < S->WrittenCount; ++S->GlyphIndex) - { - if(0) - { - ResumePoint5:; - AboveBaseGlyphCount = S->OpSpecific.Normalize.AboveBaseGlyphCount; - } - kbts_glyph *Glyph = &Glyphs[S->GlyphIndex]; - kbts_u32 Codepoint = Glyph->Codepoint; - - switch(Codepoint) - { - // Sara am/sala am. - // We match both because storing the sara am codepoint that corresponds to the current script - // doesn't seem that worthwhile, given that this is already a pretty big switch case. - // If we choose to use a unicode flag or indic syllabic category to notate above-base marks, - // then this loops gets a lot tighter and it would probably become the right call to pre-determine - // the sara am codepoint. - case 0xE33: case 0xEB3: // Sara am - { - if(!kbts_GrowGlyphArray(&S->ResumePoint, GlyphArray, S->GlyphIndex, 1, 5, 0)) - { - S->OpSpecific.Normalize.AboveBaseGlyphCount = AboveBaseGlyphCount; - KBTS_INSTRUMENT_END - return 1; - } - - AboveBaseGlyphCount = S->OpSpecific.Normalize.AboveBaseGlyphCount; - for(kbts_un AboveBaseIndex = 0; AboveBaseIndex < AboveBaseGlyphCount; ++AboveBaseIndex) - { - Glyphs[S->GlyphIndex - AboveBaseIndex] = Glyphs[S->GlyphIndex - AboveBaseIndex - 1]; - } - Glyphs[S->GlyphIndex - AboveBaseGlyphCount] = Config->Nikhahit; - Glyphs[S->GlyphIndex + 1] = Config->SaraAa; - S->WrittenCount += 1; - } break; - - case 0xE31: case 0xE34: case 0xE35: case 0xE36: case 0xE37: case 0xE3B: - case 0xE47: case 0xE48: case 0xE49: case 0xE4A: case 0xE4B: case 0xE4C: case 0xE4D: case 0xE4E: - case 0xEB1: case 0xEB4: case 0xEB5: case 0xEB6: case 0xEB7: case 0xEBB: - case 0xEC7: case 0xEC8: case 0xEC9: case 0xECA: case 0xECB: case 0xECC: case 0xECD: case 0xECE: - AboveBaseGlyphCount += 1; - break; - - default: - AboveBaseGlyphCount = 0; - break; - } - } - } - - KBTS_INSTRUMENT_END - } - break; - - case KBTS_OP_KIND_NORMALIZE_HANGUL: - { - KBTS_INSTRUMENT_BLOCK_BEGIN("NORMALIZE_HANGUL") - kbts_op_state_normalize_hangul *NormalizeHangul; NormalizeHangul = &S->OpSpecific.NormalizeHangul; - S->GlyphIndex = 0; - while(S->GlyphIndex < GlyphArray->Count) - { - { - kbts_glyph *Glyph = &Glyphs[S->GlyphIndex]; kbts_un L = 0; kbts_un V = 0; kbts_un T = 0; + kbts_un LvtGlyphCount = 0; + kbts_glyph LvtGlyphs[4]; - kbts_hangul_syllable_info LInfo = kbts_HangulSyllableInfo(Glyph->Codepoint); - if(LInfo.Type >= KBTS_HANGUL_SYLLABLE_TYPE_LV) + kbts__hangul_syllable_info LInfo = kbts__HangulSyllableInfo(Glyph->Codepoint); + if(LInfo.Type >= KBTS__HANGUL_SYLLABLE_TYPE_LV) { kbts_un SIndex = (Glyph->Codepoint - 0xAC00); @@ -19250,64 +22395,60 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G T = 0x11A7 + TIndex; } } - else if(LInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_L) + else if(LInfo.Type == KBTS__HANGUL_SYLLABLE_TYPE_L) { L = Glyph->Codepoint; } - S->GlyphIndex += 1; - if(L) { - kbts_hangul_syllable_info VInfo = KBTS_ZERO; + kbts__hangul_syllable_info VInfo = KBTS__ZERO; - if(!V && (S->GlyphIndex < GlyphArray->Count)) + if(!V && kbts__GlyphIsValid(Storage, Next)) { - kbts_u32 VCodepoint = Glyphs[S->GlyphIndex].Codepoint; + kbts_u32 VCodepoint = Next->Codepoint; - VInfo = kbts_HangulSyllableInfo(VCodepoint); + VInfo = kbts__HangulSyllableInfo(VCodepoint); - if(VInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_V) + if(VInfo.Type == KBTS__HANGUL_SYLLABLE_TYPE_V) { V = VCodepoint; - S->GlyphIndex += 1; + Next = Next->Next; } } if(V) { - kbts_hangul_syllable_info TInfo = KBTS_ZERO; + kbts__hangul_syllable_info TInfo = KBTS__ZERO; - if(!T && (S->GlyphIndex < GlyphArray->Count)) + if(!T && kbts__GlyphIsValid(Storage, Next)) { - kbts_u32 TCodepoint = Glyphs[S->GlyphIndex].Codepoint; + kbts_u32 TCodepoint = Next->Codepoint; - TInfo = kbts_HangulSyllableInfo(TCodepoint); + TInfo = kbts__HangulSyllableInfo(TCodepoint); - if(TInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_T) + if(TInfo.Type == KBTS__HANGUL_SYLLABLE_TYPE_T) { T = TCodepoint; - S->GlyphIndex += 1; + Next = Next->Next; } } - NormalizeHangul->LvtGlyphCount = 0; - // Check for any tone marks that we need to swap to the front of the syllable. // The OpenType shaping documents say that we need to do this after applying GSUB features, but // harfbuzz does it before, so it's probably fine to do it here? // It's also basically free to do here, which is nice. - if(S->GlyphIndex < GlyphArray->Count) + if(kbts__GlyphIsValid(Storage, Next)) { - kbts_u32 ToneMarkCodepoint = Glyphs[S->GlyphIndex].Codepoint; + kbts_u32 ToneMarkCodepoint = Next->Codepoint; if((ToneMarkCodepoint >= 0x302E) && (ToneMarkCodepoint <= 0x302F)) { - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = kbts_CodepointToGlyph(Font, ToneMarkCodepoint); + LvtGlyphs[LvtGlyphCount++] = kbts_CodepointToGlyph(Font, (int)ToneMarkCodepoint, 0, 0); - S->GlyphIndex += 1; + Next = Next->Next; } } @@ -19316,570 +22457,570 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G // Try LVT. kbts_un LvtCodepoint = 0xAC00 + (L - 0x1100) * 588 + (V - 0x1161) * 28 + (T - 0x11A7); - kbts_glyph LvtGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)LvtCodepoint); + kbts_glyph LvtGlyph = kbts_CodepointToGlyph(Font, (int)LvtCodepoint, 0, 0); if(LvtGlyph.Id) { - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LvtGlyph; + LvtGlyphs[LvtGlyphCount++] = LvtGlyph; } } - if(!NormalizeHangul->LvtGlyphCount) + if(!LvtGlyphCount) { - kbts_glyph LvGlyph = KBTS_ZERO; + kbts_glyph LvGlyph = KBTS__ZERO; if(LInfo.Composable & VInfo.Composable) { // Try LV. kbts_un LvCodepoint = 0xAC00 + (L - 0x1100) * 588 + (V - 0x1161) * 28; - LvGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)LvCodepoint); + LvGlyph = kbts_CodepointToGlyph(Font, (int)LvCodepoint, 0, 0); } if(LvGlyph.Id) { - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LvGlyph; + LvtGlyphs[LvtGlyphCount++] = LvGlyph; } else { // Do L-V. - kbts_glyph LGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)L); + kbts_glyph LGlyph = kbts_CodepointToGlyph(Font, (int)L, 0, 0); LGlyph.Flags |= KBTS_GLYPH_FLAG_LJMO; - kbts_glyph VGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)V); + kbts_glyph VGlyph = kbts_CodepointToGlyph(Font, (int)V, 0, 0); VGlyph.Flags |= KBTS_GLYPH_FLAG_VJMO; - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LGlyph; - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = VGlyph; + LvtGlyphs[LvtGlyphCount++] = LGlyph; + LvtGlyphs[LvtGlyphCount++] = VGlyph; } if(T) { - kbts_glyph TGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)T); + kbts_glyph TGlyph = kbts_CodepointToGlyph(Font, (int)T, 0, 0); TGlyph.Flags |= KBTS_GLYPH_FLAG_TJMO; - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = TGlyph; + LvtGlyphs[LvtGlyphCount++] = TGlyph; } } } } - if(!NormalizeHangul->LvtGlyphCount) + if(!LvtGlyphCount) { - kbts_glyph NewGlyph = kbts_CodepointToGlyph(Font, Glyph->Codepoint); + kbts_glyph NewGlyph = kbts_CodepointToGlyph(Font, (int)Glyph->Codepoint, 0, 0); - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = NewGlyph; + LvtGlyphs[LvtGlyphCount++] = NewGlyph; } - } - { // Insert the LVT glyphs. - ResumePoint3:; - NormalizeHangul = &S->OpSpecific.NormalizeHangul; + { // Insert the LVT glyphs. + kbts_glyph *LastConsumed = Next->Prev; - kbts_un NewWrittenCount = S->WrittenCount + NormalizeHangul->LvtGlyphCount; - if(NewWrittenCount > S->GlyphIndex) - { - if(!kbts_GrowGlyphArray(&S->ResumePoint, GlyphArray, S->WrittenCount, NormalizeHangul->LvtGlyphCount, 3, 0)) + // Remove the sub-list from the main list. + Glyph->Prev->Next = LastConsumed->Next; + LastConsumed->Next->Prev = Glyph->Prev; + + // Send it to the free list. + Glyph->Prev = (kbts_glyph *)&Storage->FreeGlyphSentinel; + LastConsumed->Next = Storage->FreeGlyphSentinel.Next; + Glyph->Prev->Next = Glyph; + LastConsumed->Next->Prev = LastConsumed; + + for(kbts_un LvtGlyphIndex = 0; + LvtGlyphIndex < LvtGlyphCount; + ++LvtGlyphIndex) { - KBTS_INSTRUMENT_END - return 1; - } - - S->GlyphIndex += NormalizeHangul->LvtGlyphCount; - } - - KBTS_FOR(LvtGlyphIndex, 0, NormalizeHangul->LvtGlyphCount) - { - Glyphs[S->WrittenCount++] = NormalizeHangul->LvtGlyphs[LvtGlyphIndex]; - } - } - } - KBTS_INSTRUMENT_END - } - break; - - case KBTS_OP_KIND_GSUB_FEATURES: - { - KBTS_INSTRUMENT_BLOCK_BEGIN("GSUB_FEATURES") - - kbts_gsub_gpos *FontGsub; FontGsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; - kbts_lookup_list *LookupList; LookupList = kbts_GetLookupList(FontGsub); - kbts_gsub_frame *Frames; Frames = KBTS_POINTER_AFTER(kbts_gsub_frame, S); - kbts_op_state_gsub *Gsub; Gsub = &S->OpSpecific.Gsub; - kbts_u32 GlyphFilter; - kbts_skip_flags SkipFlags; - kbts_feature_set LookupFeatures; - - kbts_BeginFeatures(S, Config, KBTS_SHAPING_TABLE_GSUB, Op->Features); - while(kbts_NextLookupIndex(S, &Gsub->LookupIndex, &SkipFlags, &GlyphFilter, &LookupFeatures)) - { - Gsub->LookupFeatures = LookupFeatures; - S->GlyphIndex = 0; - - // From the Microsoft docs: - // If a Lookup table has multiple subtables, the subtables are processed in order, testing the glyph sequence - // at the current glyph position for a match with the input sequence patterns specified by each subtable in - // turn. - // - // This means the subtable loop is _inside_ of the loop over our glyphs. - while(S->GlyphIndex < GlyphArray->Count) - { - Frames[0].InputGlyphCount = 1; - - { - kbts_u32 FilterMask = Config->Shaper == KBTS_SHAPER_USE ? KBTS_USE_GLYPH_FEATURE_MASK : KBTS_GLYPH_FEATURE_MASK; - kbts_u32 EffectiveGlyphFilter = GlyphFilter & FilterMask; - - // Reverse chaining substitutions are tricky. - // See the comment at :ReverseChaining. - // @Duplication: We just copy the top-level mirroring logic from DoSubstitution here for now. - kbts_lookup *Lookup = kbts_GetLookup(LookupList, Gsub->LookupIndex); - kbts_un CurrentGlyphIndex = (Lookup->Type == 8) ? GlyphArray->Count - 1 - S->GlyphIndex : S->GlyphIndex; - kbts_glyph *CurrentGlyph = &Glyphs[CurrentGlyphIndex]; - - if(kbts_GlyphIncludedInLookup(Config->Font, 0, Gsub->LookupIndex, CurrentGlyph->Id) && - ((CurrentGlyph->Flags & EffectiveGlyphFilter) == EffectiveGlyphFilter) && - kbts_ConfigAllowsFeatures(S, Config, CurrentGlyph->Config, &LookupFeatures)) - { - kbts_gsub_frame *Frame = &Frames[0]; - - Frame->LookupIndex = (kbts_u16)Gsub->LookupIndex; - Frame->SubtableIndex = 0; - Frame->InputGlyphIndex = (kbts_u16)S->GlyphIndex; - S->FrameCount = 1; - } - } - - while(S->FrameCount) - { - if(0) - { - ResumePoint2:; - FontGsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; - Gsub = &S->OpSpecific.Gsub; - LookupList = kbts_GetLookupList(FontGsub); - Frames = KBTS_POINTER_AFTER(kbts_gsub_frame, S); - LookupFeatures = Gsub->LookupFeatures; - GlyphFilter = Gsub->GlyphFilter; - SkipFlags = Gsub->SkipFlags; - } - - // These flags are used by USE. - kbts_u32 GeneratedGlyphFlags = GlyphFilter & (KBTS_GLYPH_FLAG_RPHF | KBTS_GLYPH_FLAG_PREF); - kbts_substitution_result_flags SubstitutionFlags = kbts_DoSubstitution(ShapeState, LookupList, Frames, &S->FrameCount, GlyphArray, 0, SkipFlags, GeneratedGlyphFlags); - if(SubstitutionFlags & KBTS_SUBSTITUTION_RESULT_FLAG_GROW_BUFFER) - { - Gsub->GlyphFilter = GlyphFilter; - Gsub->SkipFlags = SkipFlags; - S->ResumePoint = 2; - - KBTS_INSTRUMENT_END - return 1; - } - } - - S->GlyphIndex += Frames[0].InputGlyphCount; - } - } - - S->WrittenCount = GlyphArray->Count; - KBTS_INSTRUMENT_END - } - break; - - case KBTS_OP_KIND_FLAG_JOINING_LETTERS: - { - KBTS_INSTRUMENT_BLOCK_BEGIN("FLAG_JOINING_LETTERS") - kbts_u64 JoiningTypesMatchLookup = - (((1ull << KBTS_UNICODE_JOINING_TYPE_RIGHT) | (1ull << KBTS_UNICODE_JOINING_TYPE_DUAL) | (1ull << KBTS_UNICODE_JOINING_TYPE_FORCE)) << (8 * KBTS_UNICODE_JOINING_TYPE_LEFT)) | - (((1ull << KBTS_UNICODE_JOINING_TYPE_RIGHT) | (1ull << KBTS_UNICODE_JOINING_TYPE_DUAL) | (1ull << KBTS_UNICODE_JOINING_TYPE_FORCE)) << (8 * KBTS_UNICODE_JOINING_TYPE_DUAL)) | - (((1ull << KBTS_UNICODE_JOINING_TYPE_RIGHT) | (1ull << KBTS_UNICODE_JOINING_TYPE_DUAL) | (1ull << KBTS_UNICODE_JOINING_TYPE_FORCE)) << (8 * KBTS_UNICODE_JOINING_TYPE_FORCE)); - - kbts_u64 JoiningFeatureTransition = ((kbts_u64)KBTS_JOINING_FEATURE_INIT << (8 * KBTS_JOINING_FEATURE_ISOL)) | ((kbts_u64)KBTS_JOINING_FEATURE_MEDI << (8 * KBTS_JOINING_FEATURE_FINA)) | - ((kbts_u64)KBTS_JOINING_FEATURE_MEDI << (8 * KBTS_JOINING_FEATURE_MEDI)) | ((kbts_u64)KBTS_JOINING_FEATURE_MED2 << (8 * KBTS_JOINING_FEATURE_MED2)); - - // Tag letters for joining features. - kbts_glyph PreviousGlyph_ = KBTS_ZERO; - kbts_glyph *PreviousGlyph = &PreviousGlyph_; - KBTS_FOR(GlyphIndex, 0, GlyphArray->Count) - { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - - if(Glyph->JoiningType != KBTS_UNICODE_JOINING_TYPE_TRANSPARENT) - { - Glyph->JoiningFeature = !PreviousGlyph->JoiningType ? KBTS_JOINING_FEATURE_INIT : KBTS_JOINING_FEATURE_FINA; - - if(JoiningTypesMatchLookup & (1ull << (Glyph->JoiningType + 8 * PreviousGlyph->JoiningType))) - { - PreviousGlyph->JoiningFeature = (JoiningFeatureTransition >> (8 * PreviousGlyph->JoiningFeature)) & 0xFF; - PreviousGlyph->Flags = (PreviousGlyph->Flags & ~KBTS_JOINING_FEATURE_MASK) | KBTS_JOINING_FEATURE_TO_GLYPH_FLAG(PreviousGlyph->JoiningFeature); - - Glyph->JoiningFeature = KBTS_JOINING_FEATURE_FINA; - } - else - { - Glyph->JoiningFeature = KBTS_JOINING_FEATURE_ISOL; - } - - if(Glyph->JoiningFeature) - { - // Be careful that this properly maps kbts_joining_feature to KBTS_GLYPH_FLAG! - Glyph->Flags = (Glyph->Flags & ~KBTS_JOINING_FEATURE_MASK) | KBTS_JOINING_FEATURE_TO_GLYPH_FLAG(Glyph->JoiningFeature); - } - - PreviousGlyph = Glyph; - } - } - - S->WrittenCount = GlyphArray->Count; - KBTS_INSTRUMENT_END - } - break; - - case KBTS_OP_KIND_GPOS_METRICS: - { - KBTS_INSTRUMENT_BLOCK_BEGIN("GPOS_METRICS") - // hmtx/vmtx pass. - int ClearMarkAdvances = (Config->Shaper == KBTS_SHAPER_MYANMAR) || (Config->Shaper == KBTS_SHAPER_USE); - kbts_u32 Orientation = KBTS_ORIENTATION_HORIZONTAL; // @Hardcoded - kbts_hea *Hea = Font->Hea[Orientation]; - kbts_u16 *Mtx = Font->Mtx[Orientation]; - - kbts_long_mtx *LongMetrics = 0; - kbts_s16 *ShortMetrics = 0; - if(Hea && Mtx) - { - LongMetrics = (kbts_long_mtx *)Mtx; - ShortMetrics = (kbts_s16 *)(LongMetrics + Hea->MetricCount); - } - - kbts_long_mtx DefaultMetric = {1024, 0}; - if(Font->Head) - { - DefaultMetric.Advance = Font->Head->UnitsPerEm; - if(Orientation == KBTS_ORIENTATION_HORIZONTAL) - { - DefaultMetric.Advance /= 2; - } - } - - KBTS_FOR(GlyphIndex, 0, GlyphArray->Count) - { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - - kbts_long_mtx Metric = DefaultMetric; - if(LongMetrics) - { - kbts_u32 Id = Glyph->Id; - - // At the end of shaping, default ignorable glyphs that are not generated by GSUB are replaced with zero-width - // whitespace glyphs (or a zero-width empty glyph if no whitespace glyph is present). - // (By the way, we do this because Harfbuzz does it, and Harfbuzz does it probably because Uniscribe does it.) - // We handle this in two steps: - // - The first is here. We want cursive attachments, and mark-to-base attachments, and other relative placements - // to take the zero width into account, and zeroing the width right at the beginning of GPOS is the most - // straighforward way to accomplish this. - // (@Incomplete: It is likely that we also need to take the default ignorable case into account when accumulating - // cursive offsets.) - // - In POST_GPOS_FIXUP, we perform the glyph ID substitution. We have to wait until all GPOS features have been - // executed to do this because they might possibly match the original ID. - if(!(Glyph->Flags & KBTS_GLYPH_FLAG_GENERATED_BY_GSUB) && (Glyph->UnicodeFlags & KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE)) - { - Metric.Advance = 0; - Metric.PreviousSideBearing = 0; - } - else if(Id < Hea->MetricCount) - { - Metric = LongMetrics[Id]; - } - else - { - Metric.Advance = LongMetrics[Hea->MetricCount - 1].Advance; - Metric.PreviousSideBearing = ShortMetrics[Id - Hea->MetricCount]; - } - } - - if(!ClearMarkAdvances | (Glyph->Classes.Class != KBTS_GLYPH_CLASS_MARK)) - { - if(Orientation == KBTS_ORIENTATION_HORIZONTAL) - { - // @Cleanup: Why does harfbuzz not take bearings into account in these tests? - // P.X += Metric.PreviousSideBearing; - Glyph->AdvanceX = Metric.Advance; - } - else - { - // @Cleanup: Why does harfbuzz not take bearings into account in these tests? - // P.Y += Metric.PreviousSideBearing; - Glyph->AdvanceY = Metric.Advance; - } - } - } - KBTS_INSTRUMENT_END - } - break; - - case KBTS_OP_KIND_GPOS_FEATURES: - { - KBTS_INSTRUMENT_BLOCK_BEGIN("GPOS_FEATURES") - kbts_gsub_gpos *Gpos = Font->ShapingTables[KBTS_SHAPING_TABLE_GPOS]; - kbts_BeginFeatures(S, Config, KBTS_SHAPING_TABLE_GPOS, Op->Features); - kbts_lookup_list *LookupList = kbts_GetLookupList(Gpos); - kbts_un LookupIndex; - kbts_skip_flags SkipFlags; - kbts_u32 GlyphFilter; - kbts_feature_set LookupFeatures; - while(kbts_NextLookupIndex(S, &LookupIndex, &SkipFlags, &GlyphFilter, &LookupFeatures)) - { - kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); - kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); - - kbts_un PositionedGlyphCount = 0; - while(PositionedGlyphCount < GlyphArray->Count) - { - kbts_un DeltaGlyphIndex = 1; - - if(kbts_GlyphIncludedInLookup(Config->Font, 1, LookupIndex, GlyphArray->Glyphs[PositionedGlyphCount].Id) && - kbts_ConfigAllowsFeatures(S, Config, Glyphs[PositionedGlyphCount].Config, &LookupFeatures)) - { - KBTS_FOR(SubtableIndex, 0, Lookup.SubtableCount) - { - kbts_u16 *Subtable = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubtableIndex]); - kbts_do_single_adjustment_result Adjustment = kbts_DoSingleAdjustment(Config, LookupList, LookupIndex, SubtableIndex, &Lookup, Subtable, GlyphArray, PositionedGlyphCount, SkipFlags); - if(Adjustment.PerformedAdjustment) + kbts_glyph *LvtGlyph = &LvtGlyphs[LvtGlyphIndex]; + kbts_glyph *NewGlyph = kbts__InsertGlyphBefore(Storage, Next, LvtGlyph); + if(!NewGlyph) { - DeltaGlyphIndex = Adjustment.PositionedGlyphCount; - break; + goto OutOfMemory; } } } - PositionedGlyphCount += DeltaGlyphIndex; + Glyph = Next; } + // KBTS_INSTRUMENT_BLOCK_END(NORMALIZE_HANGUL); } - KBTS_INSTRUMENT_END - } - break; + break; - case KBTS_OP_KIND_POST_GPOS_FIXUP: - { - KBTS_INSTRUMENT_BLOCK_BEGIN("POST_GPOS_FIXUP") - kbts_un WriteAt = 0; - kbts_glyph WhitespaceGlyph = Config->Whitespace; - int ClearMarkAdvances = kbts_ShaperClearsMarkAdvancesInPostGposFixup(Config->Shaper); - - if(ShapeState->MainDirection != ShapeState->RunDirection) + case KBTS__OP_KIND_GSUB_FEATURES: { - // Flip direction. - // This might seem like a totally superfluous thing to do, because we have to do a bunch - // of work to reverse glyph order while still preserving their relative positions. - // However, for mainly LTR documents, the anchor position will naturally be left-aligned, - // while, in RTL documents, it will naturally right-align. - // As such, going through the glyph sequence and reversing it is needed _anyway_ at some point, - // and we might as well do it here, because this is where we can take resolve attachments - // for relatively cheap, since they are always back-looking. (The next paragraph explains this - // in more detail.) + KBTS_INSTRUMENT_BLOCK_BEGIN(GSUB_FEATURES); - // The unintuitive part about this pass is the glyph advances. - // Normally, when iterating over a sequence of glyphs, we see base glyphs before marks. Obviously. - // However, when flipping the sequence, we see the marks before seeing the bases, which means we - // won't have accumulated the base glyph's advance yet! So we have to go through the sequence here - // and compensate for missing advances by precomputing them and baking them into the marks. - // Another way to reach the same result would be to keep marks on the same side as their bases when - // flipping, but that is not what Harfbuzz does, and, the day we decide we want to diverge from Harfbuzz, - // we will very likely be better off not flipping the sequence at all and deleting all of this garbage code. - kbts_s32 MarkAttachAdvanceX = 0; - kbts_s32 MarkAttachAdvanceY = 0; - KBTS_FOR(GlyphIndex, 0, GlyphArray->Count) + kbts__gsub_gpos *FontGsub = kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_GSUB, kbts__gsub_gpos); + kbts_lookup_list *LookupList; LookupList = kbts__GetLookupList(FontGsub); + kbts__arena_lifetime Lifetime = kbts__BeginLifetime(Scratchpad->Arena); + kbts__gsub_frame *Frames = kbts__PushArray(Scratchpad->Arena, kbts__gsub_frame, KBTS_LOOKUP_STACK_SIZE); + kbts_u32 GlyphFilter; + kbts__skip_flags SkipFlags; + kbts_un LookupIndex; + + if(kbts__BeginFeatures(Scratchpad, Config, KBTS_SHAPING_TABLE_GSUB)) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - - if(Glyph->AttachGlyphIndexPlusOne) + while(kbts__NextLookupIndex(Scratchpad, Config, &LookupIndex, &SkipFlags, &GlyphFilter, &Scratchpad->LookupFeatures)) { - kbts_glyph *Attach = &Glyphs[Glyph->AttachGlyphIndexPlusOne - 1]; - kbts_s32 AttachAdvanceX = MarkAttachAdvanceX; - kbts_s32 AttachAdvanceY = MarkAttachAdvanceY; - if(Attach->Classes.Class != KBTS_GLYPH_CLASS_MARK) + kbts__lookup *Lookup = kbts__GetLookup(LookupList, LookupIndex); + + // From the Microsoft docs: + // If a Lookup table has multiple subtables, the subtables are processed in order, testing the glyph sequence + // at the current glyph position for a match with the input sequence patterns specified by each subtable in + // turn. + // + // This means the subtable loop is _inside_ of the loop over our glyphs. + + // Reverse chaining substitutions are tricky. + // See the comment at :ReverseChaining. + for(kbts_glyph *Glyph = (Lookup->Type == 8) ? Storage->GlyphSentinel.Prev : Storage->GlyphSentinel.Next; + kbts__GlyphIsValid(Storage, Glyph); + ) { - AttachAdvanceX = Attach->AdvanceX; - AttachAdvanceY = Attach->AdvanceY; + kbts__BeginLookupApplication(Scratchpad, Glyph); + kbts_un FrameCount = 0; + kbts_u32 FilterMask = Config->Shaper == KBTS_SHAPER_USE ? KBTS__USE_GLYPH_FEATURE_MASK : KBTS__GLYPH_FEATURE_MASK; + kbts_u32 EffectiveGlyphFilter = GlyphFilter & FilterMask; + + if(kbts__GlyphIncludedInLookup(Font, 0, LookupIndex, Glyph->Id) && + ((Glyph->Flags & EffectiveGlyphFilter) == EffectiveGlyphFilter) && + kbts__ConfigAllowsFeatures(Scratchpad, Config, Glyph->Config, &Scratchpad->LookupFeatures)) + { + kbts__gsub_frame FirstFrame = KBTS__ZERO; + FirstFrame.LookupIndex = (kbts_u16)LookupIndex; + FirstFrame.InputGlyph = Glyph; + + Frames[0] = FirstFrame; + FrameCount = 1; + + while(FrameCount) + { + // These flags are used by USE. + kbts_u32 GeneratedGlyphFlags = GlyphFilter & (KBTS_GLYPH_FLAG_RPHF | KBTS_GLYPH_FLAG_PREF); + kbts__DoSubstitution(Scratchpad, Config, Storage, LookupList, Frames, &FrameCount, 0, SkipFlags, GeneratedGlyphFlags); + } + } + + kbts_glyph *OnePastLast = kbts__EndLookupApplication(Scratchpad); + Glyph = (Lookup->Type == 8) ? Glyph->Prev : OnePastLast; } - Glyph->OffsetX += AttachAdvanceX; - Glyph->OffsetY += AttachAdvanceY; - MarkAttachAdvanceX = AttachAdvanceX; - MarkAttachAdvanceY = AttachAdvanceY; } } - kbts_un SwapCount = GlyphArray->Count / 2; - KBTS_FOR(LeftIndex, 0, SwapCount) - { - kbts_un RightIndex = GlyphArray->Count - 1 - LeftIndex; - - kbts_glyph SwapGlyph = Glyphs[LeftIndex]; - Glyphs[LeftIndex] = Glyphs[RightIndex]; - Glyphs[RightIndex] = SwapGlyph; - } + kbts__EndLifetime(&Lifetime); + KBTS_INSTRUMENT_BLOCK_END(GSUB_FEATURES); } + break; - // Make default ignorables that weren't explicitly created by the font invisible. - // - // It is tempting to put this loop inside of an if(WhitespaceGlyph.Id), just like we do - // for dotted circle insertion. However, Harfbuzz does not do this! - // When the font does not contain a dotted circle, nothing is inserted, but when the font - // does not contain a whitespace glyph, _we still insert an empty glyph_. - KBTS_FOR(GlyphIndex, 0, GlyphArray->Count) + case KBTS__OP_KIND_FLAG_JOINING_LETTERS: { - kbts_glyph Glyph = Glyphs[GlyphIndex]; - int Write = 0; + KBTS_INSTRUMENT_BLOCK_BEGIN(FLAG_JOINING_LETTERS); + kbts_u64 JoiningTypesMatchLookup = + (((1ull << KBTS_UNICODE_JOINING_TYPE_RIGHT) | (1ull << KBTS_UNICODE_JOINING_TYPE_DUAL) | (1ull << KBTS_UNICODE_JOINING_TYPE_FORCE)) << (8 * KBTS_UNICODE_JOINING_TYPE_LEFT)) | + (((1ull << KBTS_UNICODE_JOINING_TYPE_RIGHT) | (1ull << KBTS_UNICODE_JOINING_TYPE_DUAL) | (1ull << KBTS_UNICODE_JOINING_TYPE_FORCE)) << (8 * KBTS_UNICODE_JOINING_TYPE_DUAL)) | + (((1ull << KBTS_UNICODE_JOINING_TYPE_RIGHT) | (1ull << KBTS_UNICODE_JOINING_TYPE_DUAL) | (1ull << KBTS_UNICODE_JOINING_TYPE_FORCE)) << (8 * KBTS_UNICODE_JOINING_TYPE_FORCE)); - // It might be tempting to keep glyphs that were used in GPOS, but Harfbuzz doesn't care. - if(!(Glyph.Flags & KBTS_GLYPH_FLAG_GENERATED_BY_GSUB) && (Glyph.UnicodeFlags & KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE)) + kbts_u64 JoiningFeatureTransition = ((kbts_u64)KBTS_JOINING_FEATURE_INIT << (8 * KBTS_JOINING_FEATURE_ISOL)) | ((kbts_u64)KBTS_JOINING_FEATURE_MEDI << (8 * KBTS_JOINING_FEATURE_FINA)) | + ((kbts_u64)KBTS_JOINING_FEATURE_MEDI << (8 * KBTS_JOINING_FEATURE_MEDI)) | ((kbts_u64)KBTS_JOINING_FEATURE_MED2 << (8 * KBTS_JOINING_FEATURE_MED2)); + + // Tag letters for joining features. + kbts_glyph PreviousGlyph_ = KBTS__ZERO; + kbts_glyph *PreviousGlyph = &PreviousGlyph_; + KBTS__FOR_GLYPH(Storage, Glyph) { - // This glyph is default ignorable and should be ignored. - if(WhitespaceGlyph.Id) + if(Glyph->JoiningType != KBTS_UNICODE_JOINING_TYPE_TRANSPARENT) { - Write = 1; - Glyph = WhitespaceGlyph; - } - } - else - { - if(ClearMarkAdvances && (Glyph.Classes.Class == KBTS_GLYPH_CLASS_MARK)) - { - Glyph.AdvanceX = Glyph.AdvanceY = 0; - } - Write = 1; - } + Glyph->JoiningFeature = (kbts_joining_feature)(!PreviousGlyph->JoiningType ? KBTS_JOINING_FEATURE_INIT : KBTS_JOINING_FEATURE_FINA); - if(Write) - { - Glyphs[WriteAt++] = Glyph; - } - } - - GlyphArray->Count = (kbts_u32)WriteAt; - GlyphArray->TotalCount = GlyphArray->Count; - KBTS_INSTRUMENT_END - } - break; - - case KBTS_OP_KIND_STCH_POSTPASS: - { - KBTS_FOR(GlyphIndex, 0, GlyphArray->Count) - { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - - if(Glyph->Flags & (KBTS_GLYPH_FLAG_STCH_ENDPOINT | KBTS_GLYPH_FLAG_STCH_EXTENSION)) - { - kbts_un At = GlyphIndex; - - kbts_un ExtensionIndex = At; - kbts_u32 SawExtension = 0; - - kbts_s32 EndpointWidth = 0; - kbts_s32 ExtensionWidth = 0; - - while(At < GlyphArray->Count) - { - kbts_glyph *AtGlyph = &Glyphs[At]; - - if(AtGlyph->Flags & KBTS_GLYPH_FLAG_STCH_ENDPOINT) + if(JoiningTypesMatchLookup & (1ull << (Glyph->JoiningType + 8 * PreviousGlyph->JoiningType))) { - EndpointWidth += AtGlyph->AdvanceX; + PreviousGlyph->JoiningFeature = (JoiningFeatureTransition >> (8 * PreviousGlyph->JoiningFeature)) & 0xFF; + PreviousGlyph->Flags = (PreviousGlyph->Flags & ~KBTS__JOINING_FEATURE_MASK) | KBTS__JOINING_FEATURE_TO_GLYPH_FLAG(PreviousGlyph->JoiningFeature); - At += 1; - } - else if(AtGlyph->Flags & KBTS_GLYPH_FLAG_STCH_EXTENSION) - { - ExtensionIndex = At; - ExtensionWidth += AtGlyph->AdvanceX; - SawExtension = 1; - - At += 1; + Glyph->JoiningFeature = KBTS_JOINING_FEATURE_FINA; } else { - break; + Glyph->JoiningFeature = KBTS_JOINING_FEATURE_ISOL; } + + if(Glyph->JoiningFeature) + { + // Be careful that this properly maps kbts_joining_feature to KBTS_GLYPH_FLAG! + Glyph->Flags = (Glyph->Flags & ~KBTS__JOINING_FEATURE_MASK) | KBTS__JOINING_FEATURE_TO_GLYPH_FLAG(Glyph->JoiningFeature); + } + + PreviousGlyph = Glyph; } + } - kbts_sn WordWidth = 0; + KBTS_INSTRUMENT_BLOCK_END(FLAG_JOINING_LETTERS); + } + break; - while(At < GlyphArray->Count) + case KBTS__OP_KIND_GPOS_METRICS: + { + KBTS_INSTRUMENT_BLOCK_BEGIN(GPOS_METRICS); + // hmtx/vmtx pass. + int ClearMarkAdvances = (Config->Shaper == KBTS_SHAPER_MYANMAR) || (Config->Shaper == KBTS_SHAPER_USE); + + kbts_u32 Orientation = KBTS_ORIENTATION_HORIZONTAL; // @Hardcoded + kbts_blob_table_id HeaTableId = KBTS_BLOB_TABLE_ID_HHEA; + kbts_blob_table_id MtxTableId = KBTS_BLOB_TABLE_ID_HMTX; + if(Orientation == KBTS_ORIENTATION_VERTICAL) + { + HeaTableId = KBTS_BLOB_TABLE_ID_VHEA; + MtxTableId = KBTS_BLOB_TABLE_ID_VMTX; + } + + kbts__hea *Hea = kbts__BlobTableDataType(Font->Blob, HeaTableId, kbts__hea); + kbts_u16 *Mtx = kbts__BlobTableDataType(Font->Blob, MtxTableId, kbts_u16); + + kbts__long_mtx *LongMetrics = 0; + // :LeftSideBearing + // kbts_s16 *ShortMetrics = 0; + if(Hea && Mtx) + { + LongMetrics = (kbts__long_mtx *)Mtx; + // :LeftSideBearing + // ShortMetrics = (kbts_s16 *)(LongMetrics + Hea->MetricCount); + } + + kbts__long_mtx DefaultMetric = {1024, 0}; + kbts__head *Head = kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_HEAD, kbts__head); + if(Head) + { + DefaultMetric.Advance = Head->UnitsPerEm; + if(Orientation == KBTS_ORIENTATION_HORIZONTAL) { - kbts_glyph *AtGlyph = &Glyphs[At]; + DefaultMetric.Advance /= 2; + } + } - int Ok = 0; + KBTS__FOR_GLYPH(Storage, Glyph) + { + kbts__SetCursiveFlags(Glyph, 0); - if(!(AtGlyph->Flags & (KBTS_GLYPH_FLAG_STCH_ENDPOINT | KBTS_GLYPH_FLAG_STCH_EXTENSION)) && (AtGlyph->UnicodeFlags & KBTS_UNICODE_FLAG_PART_OF_WORD)) + kbts__long_mtx Metric = DefaultMetric; + if(LongMetrics) + { + kbts_u32 Id = Glyph->Id; + + // At the end of shaping, default ignorable glyphs that are not generated by GSUB are replaced with zero-width + // whitespace glyphs (or a zero-width empty glyph if no whitespace glyph is present). + // (By the way, we do this because Harfbuzz does it, and Harfbuzz does it probably because Uniscribe does it.) + // We handle this in two steps: + // - The first is here. We want cursive attachments, and mark-to-base attachments, and other relative placements + // to take the zero width into account, and zeroing the width right at the beginning of GPOS is the most + // straighforward way to accomplish this. + // (@Incomplete: It is likely that we also need to take the default ignorable case into account when accumulating + // cursive offsets.) + // - In POST_GPOS_FIXUP, we perform the glyph ID substitution. We have to wait until all GPOS features have been + // executed to do this because they might possibly match the original ID. + if(!(Glyph->Flags & KBTS_GLYPH_FLAG_GENERATED_BY_GSUB) && (Glyph->UnicodeFlags & KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE)) { - Ok = 1; + Metric.Advance = 0; + // :LeftSideBearing + // Metric.PreviousSideBearing = 0; } - - if(Ok) + else if(Id < Hea->MetricCount) { - WordWidth += AtGlyph->AdvanceX; - - At += 1; + Metric = LongMetrics[Id]; } else { - break; + Metric.Advance = LongMetrics[Hea->MetricCount - 1].Advance; + // :LeftSideBearing + // Metric.PreviousSideBearing = ShortMetrics[Id - Hea->MetricCount]; } } - if(WordWidth > EndpointWidth) + if(!ClearMarkAdvances | (Glyph->Classes.Class != KBTS__GLYPH_CLASS_MARK)) { - kbts_sn ExtensionCount = (WordWidth - EndpointWidth) / ExtensionWidth; - if((ExtensionWidth * ExtensionCount) < (WordWidth - EndpointWidth)) + if(Orientation == KBTS_ORIENTATION_HORIZONTAL) { - ExtensionCount += 1; + // :LeftSideBearing + // Why does harfbuzz not take bearings into account in these tests? + // P.X += Metric.PreviousSideBearing; + Glyph->AdvanceX = Metric.Advance; } + else + { + // :LeftSideBearing + // Why does harfbuzz not take bearings into account in these tests? + // P.Y += Metric.PreviousSideBearing; + Glyph->AdvanceY = Metric.Advance; + } + } + } + KBTS_INSTRUMENT_BLOCK_END(GPOS_METRICS); + } + break; - (void)ExtensionCount; - (void)ExtensionIndex; - (void)SawExtension; - /* @Incomplete - kbts_glyph_adjustment *ExtensionAdjustment = &Positions[ExtensionIndex]; - ExtensionAdjustment->LastRepeatIndex = ExtensionCount - 1; - ExtensionAdjustment->RepeatOffsetX = (WordWidth - EndpointWidth - ExtensionWidth) / ExtensionCount; - */ + case KBTS__OP_KIND_GPOS_FEATURES: + { + KBTS_INSTRUMENT_BLOCK_BEGIN(GPOS_FEATURES); + kbts__gsub_gpos *Gpos = kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_GPOS, kbts__gsub_gpos); + + if(kbts__BeginFeatures(Scratchpad, Config, KBTS_SHAPING_TABLE_GPOS)) + { + kbts_lookup_list *LookupList = kbts__GetLookupList(Gpos); + kbts_un LookupIndex; + kbts__skip_flags SkipFlags; + kbts_u32 GlyphFilter; + kbts__feature_set LookupFeatures; + while(kbts__NextLookupIndex(Scratchpad, Config, &LookupIndex, &SkipFlags, &GlyphFilter, &LookupFeatures)) + { + kbts__lookup *PackedLookup = kbts__GetLookup(LookupList, LookupIndex); + kbts__unpacked_lookup Lookup = kbts__UnpackLookup(kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_GDEF, kbts__gdef), PackedLookup); + + kbts_glyph *Glyph = Storage->GlyphSentinel.Next; + while(kbts__GlyphIsValid(Storage, Glyph)) + { + kbts__BeginLookupApplication(Scratchpad, Glyph); + + if(kbts__GlyphIncludedInLookup(Config->Font, 1, LookupIndex, Glyph->Id) && + kbts__ConfigAllowsFeatures(Scratchpad, Config, Glyph->Config, &LookupFeatures)) + { + KBTS__FOR(SubtableIndex, 0, Lookup.SubtableCount) + { + kbts_u16 *Subtable = KBTS__POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubtableIndex]); + + if(kbts__DoSingleAdjustment(Scratchpad, Config, Storage, LookupList, + LookupIndex, SubtableIndex, &Lookup, Subtable, + Glyph, 0, SkipFlags)) + { + break; + } + } + } + + Glyph = kbts__EndLookupApplication(Scratchpad); + } + } + } + + KBTS_INSTRUMENT_BLOCK_END(GPOS_FEATURES); + } + break; + + case KBTS__OP_KIND_POST_GPOS_FIXUP: + { + KBTS_INSTRUMENT_BLOCK_BEGIN(POST_GPOS_FIXUP); + kbts_glyph WhitespaceGlyph = Config->Whitespace; + int ClearMarkAdvances = kbts__ShaperClearsMarkAdvancesInPostGposFixup(Config->Shaper); + + if(Scratchpad->RunDirection == KBTS_DIRECTION_RTL) + { + // Flip direction. + // This might seem like a totally superfluous thing to do, because we have to do a bunch + // of work to reverse glyph order while still preserving their relative positions. + // However, for mainly LTR documents, the anchor position will naturally be left-aligned, + // while, in RTL documents, it will naturally right-align. + // As such, going through the glyph sequence and reversing it is needed _anyway_ at some point, + // and we might as well do it here, because this is where we can take resolve attachments + // for relatively cheap, since they are always back-looking. (The next paragraph explains this + // in more detail.) + + // The unintuitive part about this pass is the glyph advances. + // Normally, when iterating over a sequence of glyphs, we see base glyphs before marks. Obviously. + // However, when flipping the sequence, we see the marks before seeing the bases, which means we + // won't have accumulated the base glyph's advance yet! So we have to go through the sequence here + // and compensate for missing advances by precomputing them and baking them into the marks. + // Another way to reach the same result would be to keep marks on the same side as their bases when + // flipping, but that is not what Harfbuzz does, and, the day we decide we want to diverge from Harfbuzz, + // we will very likely be better off not flipping the sequence at all and deleting all of this garbage code. + kbts_s32 MarkAttachAdvanceX = 0; + kbts_s32 MarkAttachAdvanceY = 0; + KBTS__FOR_GLYPH(Storage, Glyph) + { + kbts_glyph *Attach = Glyph->AttachGlyph; + if(Attach) + { + kbts_s32 AttachAdvanceX = MarkAttachAdvanceX; + kbts_s32 AttachAdvanceY = MarkAttachAdvanceY; + if(Attach->Classes.Class != KBTS__GLYPH_CLASS_MARK) + { + AttachAdvanceX = Attach->AdvanceX; + AttachAdvanceY = Attach->AdvanceY; + } + Glyph->OffsetX += AttachAdvanceX; + Glyph->OffsetY += AttachAdvanceY; + MarkAttachAdvanceX = AttachAdvanceX; + MarkAttachAdvanceY = AttachAdvanceY; + } } - GlyphIndex = At; + // Swap. + // @Speed: We could just choose the correct links when traversing instead. + kbts__DllistReverseSublist((kbts_glyph *)&Storage->GlyphSentinel, (kbts_glyph *)&Storage->GlyphSentinel); } + + // Make default ignorables that weren't explicitly created by the font invisible. + // + // It is tempting to put this loop inside of an if(WhitespaceGlyph.Id), just like we do + // for dotted circle insertion. However, Harfbuzz does not do this! + // When the font does not contain a dotted circle, nothing is inserted, but when the font + // does not contain a whitespace glyph, _we still insert an empty glyph_. + for(kbts_glyph *Glyph = Storage->GlyphSentinel.Next; + kbts__GlyphIsValid(Storage, Glyph); + ) + { + kbts_glyph *Next = Glyph->Next; + int Keep = 0; + + // It might be tempting to keep glyphs that were used in GPOS, but Harfbuzz doesn't care. + if(!(Glyph->Flags & KBTS_GLYPH_FLAG_GENERATED_BY_GSUB) && (Glyph->UnicodeFlags & KBTS_UNICODE_FLAG_DEFAULT_IGNORABLE)) + { + // This glyph is default ignorable and should be ignored. + if(WhitespaceGlyph.Id) + { + Keep = 1; + kbts__SetGlyphPreserveLinksAndUserId(Glyph, &WhitespaceGlyph); + } + } + else + { + if(ClearMarkAdvances && (Glyph->Classes.Class == KBTS__GLYPH_CLASS_MARK)) + { + Glyph->AdvanceX = Glyph->AdvanceY = 0; + } + Keep = 1; + } + + if(!Keep) + { + kbts__FreeGlyph(Scratchpad, Storage, Glyph); + } + + Glyph = Next; + } + + KBTS_INSTRUMENT_BLOCK_END(POST_GPOS_FIXUP); + } + break; + + case KBTS__OP_KIND_STCH_POSTPASS: + { + #if 0 + KBTS__FOR_GLYPH(ShapeState, Glyph) + { + if(Glyph->Flags & (KBTS_GLYPH_FLAG_STCH_ENDPOINT | KBTS_GLYPH_FLAG_STCH_EXTENSION)) + { + kbts_glyph *Extension = 0; + + kbts_s32 EndpointWidth = 0; + kbts_s32 ExtensionWidth = 0; + + while(kbts__GlyphIsValid(ShapeState, Glyph)) + { + kbts_glyph *AtGlyph = &Glyphs[At]; + + if(AtGlyph->Flags & KBTS_GLYPH_FLAG_STCH_ENDPOINT) + { + EndpointWidth += AtGlyph->AdvanceX; + + At += 1; + } + else if(AtGlyph->Flags & KBTS_GLYPH_FLAG_STCH_EXTENSION) + { + ExtensionIndex = At; + ExtensionWidth += AtGlyph->AdvanceX; + SawExtension = 1; + + At += 1; + } + else + { + break; + } + } + + kbts_sn WordWidth = 0; + + while(At < GlyphArray->Count) + { + kbts_glyph *AtGlyph = &Glyphs[At]; + + int Ok = 0; + + if(!(AtGlyph->Flags & (KBTS_GLYPH_FLAG_STCH_ENDPOINT | KBTS_GLYPH_FLAG_STCH_EXTENSION)) && (AtGlyph->UnicodeFlags & KBTS_UNICODE_FLAG_PART_OF_WORD)) + { + Ok = 1; + } + + if(Ok) + { + WordWidth += AtGlyph->AdvanceX; + + At += 1; + } + else + { + break; + } + } + + if(WordWidth > EndpointWidth) + { + kbts_sn ExtensionCount = (WordWidth - EndpointWidth) / ExtensionWidth; + if((ExtensionWidth * ExtensionCount) < (WordWidth - EndpointWidth)) + { + ExtensionCount += 1; + } + + (void)ExtensionCount; + (void)ExtensionIndex; + (void)SawExtension; + /* @Incomplete + kbts_glyph_adjustment *ExtensionAdjustment = &Positions[ExtensionIndex]; + ExtensionAdjustment->LastRepeatIndex = ExtensionCount - 1; + ExtensionAdjustment->RepeatOffsetX = (WordWidth - EndpointWidth - ExtensionWidth) / ExtensionCount; + */ + } + + GlyphIndex = At; + } + } + #endif + } + break; + } + + if(0) + { + OutOfMemory:; + Scratchpad->Error = KBTS_SHAPE_ERROR_OUT_OF_MEMORY; } } - break; - } - if(Op->Kind < KBTS_OP_KIND_GPOS_METRICS) - { - kbts_un DeltaCount = S->WrittenCount - GlyphArray->Count; - GlyphArray->Count += (kbts_u32)DeltaCount; - GlyphArray->TotalCount += (kbts_u32)DeltaCount; - } - - KBTS_INSTRUMENT_END - return 0; + KBTS_INSTRUMENT_FUNCTION_END; } -static kbts_glyph kbts_Substitute1(kbts_shape_state *ShapeState, kbts_lookup_list *LookupList, kbts_feature *Feature, kbts_skip_flags SkipFlags, kbts_glyph *Glyph) +static kbts_glyph kbts__Substitute1(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_storage *Storage, + kbts_lookup_list *LookupList, kbts__feature *Feature, kbts__skip_flags SkipFlags, kbts_glyph *Glyph) { kbts_glyph Result = *Glyph; + kbts_glyph TempSentinel; + TempSentinel.Prev = TempSentinel.Next = &Result; + Result.Prev = Result.Next = &TempSentinel; + kbts__glyph_list List = kbts__PushGlyphList(Storage, &Result, &Result); - kbts_glyph_array GlyphArray = kbts_GlyphArray(Glyph, 1, 1, 1); - - kbts_iterate_lookups IterateLookups = kbts_IterateLookups(LookupList, Feature); - while(kbts_NextLookup(&IterateLookups)) + kbts__iterate_lookups IterateLookups = kbts__IterateLookups(LookupList, Feature); + while(kbts__NextLookup(&IterateLookups)) { - kbts_gsub_frame Frames[8]; - Frames[0].InputGlyphCount = 1; - kbts_u32 FrameCount = 1; + kbts__gsub_frame Frames[8]; + { + kbts__gsub_frame *Frame = &Frames[0]; + *Frame = KBTS__ZERO_TYPE(kbts__gsub_frame); + Frame->LookupIndex = IterateLookups.LookupIndex; + Frame->SubtableIndex = 0; + Frame->StartIndex = 0; + Frame->InputGlyph = &Result; + } + kbts_un FrameCount = 1; while(FrameCount) { - kbts_substitution_result_flags SubstitutionResult = kbts_DoSubstitution(ShapeState, LookupList, Frames, &FrameCount, &GlyphArray, 0, SkipFlags, 0); - if(SubstitutionResult & KBTS_SUBSTITUTION_RESULT_FLAG_GROW_BUFFER) + kbts__substitution_result_flags SubstitutionResult = kbts__DoSubstitution(Scratchpad, Config, Storage, + LookupList, Frames, &FrameCount, 0, SkipFlags, 0); + if(SubstitutionResult & KBTS__SUBSTITUTION_RESULT_FLAG_TRIED_TO_INSERT_WHILE_CHECK_ONLY) { goto Done; } @@ -19887,73 +23028,78 @@ static kbts_glyph kbts_Substitute1(kbts_shape_state *ShapeState, kbts_lookup_lis } Done:; + kbts__PopGlyphList(Storage, &List); return Result; } -typedef struct kbts_attach_state +typedef struct kbts__attach_state { - kbts_syllabic_position CurrentPosition; - kbts_syllabic_position LastPositionThatWasNotPreBaseMatra; + kbts__syllabic_position CurrentPosition; + kbts__syllabic_position LastPositionThatWasNotPreBaseMatra; kbts_indic_syllabic_class LastClass; - kbts_un Start; - kbts_un OnePastLast; -} kbts_attach_state; + kbts_glyph *Start; + kbts_glyph *OnePastLast; + kbts_glyph *At; +} kbts__attach_state; -typedef struct kbts_begin_cluster_result +static kbts_glyph *kbts__BeginCluster(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_storage *Storage, + kbts_glyph *Glyph) { - kbts_un ClusterGlyphCount; - kbts_un InsertedGlyphCount; -} kbts_begin_cluster_result; - -static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, kbts_glyph *Glyphs, kbts_un GlyphCount) -{ - kbts_begin_cluster_result Result = KBTS_ZERO; - Result.ClusterGlyphCount = GlyphCount; - kbts_op_state *OpState = &ShapeState->OpState; - kbts_shape_config *Config = ShapeState->Config; kbts_font *Font = Config->Font; - ShapeState->RealCluster = 0; + kbts_glyph *Result = Glyph->Next; + kbts__arena_lifetime Lifetime = kbts__BeginLifetime(Scratchpad->Arena); + + Scratchpad->RealCluster = 0; switch(Config->Shaper) { case KBTS_SHAPER_INDIC: { - kbts_lookup_list *LookupList = kbts_GetLookupList(Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]); + kbts_lookup_list *LookupList = kbts__GetLookupList(kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_GSUB, kbts__gsub_gpos)); - kbts_un OtherCount = 0; - while((OtherCount < GlyphCount) && ((Glyphs[OtherCount].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_OTHER) || (Glyphs[OtherCount].SyllabicClass >= KBTS_INDIC_SYLLABIC_CLASS_COUNT))) + int NonCluster = 0; + while(kbts__GlyphIsValid(Storage, Glyph) && + ((Glyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_OTHER) || + (Glyph->SyllabicClass >= KBTS_INDIC_SYLLABIC_CLASS_COUNT))) { - ++OtherCount; + NonCluster = 1; + Glyph = Glyph->Next; } - if(OtherCount) - { - // Not an Indic syllable. - // Just pass it along. - Result.ClusterGlyphCount = OtherCount; - } - else + Result = Glyph; + + if(!NonCluster) { kbts_un ScanGlyphIndex = 0; kbts_un State = 0; kbts_un Broken = 1; kbts_un BrokenState = 0; + kbts_glyph *FirstGlyphs[3]; - while((ScanGlyphIndex < GlyphCount) && (State < KBTS_INDIC_SYLLABIC_STATE_COUNT)) + while(kbts__GlyphIsValid(Storage, Glyph) && + (State < KBTS_INDIC_SYLLABIC_STATE_COUNT)) { - kbts_glyph *Glyph = &Glyphs[ScanGlyphIndex]; kbts_un Class = Glyph->SyllabicClass; - if(KBTS_IN_SET(Class, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_RA)(KBTS_INDIC_SYLLABIC_CLASS_CONSONANT) - (KBTS_INDIC_SYLLABIC_CLASS_VOWEL)(KBTS_INDIC_SYLLABIC_CLASS_DOTTED_CIRCLE) - (KBTS_INDIC_SYLLABIC_CLASS_PLACEHOLDER)(KBTS_INDIC_SYLLABIC_CLASS_SYMBOL)))) + if(KBTS__IN_SET(Class, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_RA) + (KBTS_INDIC_SYLLABIC_CLASS_CONSONANT) + (KBTS_INDIC_SYLLABIC_CLASS_VOWEL) + (KBTS_INDIC_SYLLABIC_CLASS_DOTTED_CIRCLE) + (KBTS_INDIC_SYLLABIC_CLASS_PLACEHOLDER) + (KBTS_INDIC_SYLLABIC_CLASS_SYMBOL)))) { Broken = 0; } BrokenState = (BrokenState << 1) | Broken; + if(ScanGlyphIndex < KBTS__ARRAY_LENGTH(FirstGlyphs)) + { + FirstGlyphs[ScanGlyphIndex] = Glyph; + } + State = kbts_IndicSyllabicTransition[Class][State]; State -= 1; // @Incomplete @Cleanup: Decrement every state by 1 in the transition table. + Glyph = Glyph->Next; ScanGlyphIndex += 1; } @@ -19962,6 +23108,12 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, ScanGlyphIndex -= State - KBTS_INDIC_SYLLABIC_STATE_COUNT; BrokenState >>= State - KBTS_INDIC_SYLLABIC_STATE_COUNT; + while(State > KBTS_INDIC_SYLLABIC_STATE_COUNT) + { + Glyph = Glyph->Prev; + State -= 1; + } + if(!ScanGlyphIndex) { // If we backtrack all the way to the beginning, still eat a character. @@ -19974,11 +23126,15 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, // _strongly_ recommends that shapers use the font's tables instead, ie. doing tentative lookups // with specific features. - kbts_gsub_frame *Frames = KBTS_POINTER_AFTER(kbts_gsub_frame, OpState); - kbts_glyph *OnePastLastSyllableGlyph = Glyphs + ScanGlyphIndex; + kbts__gsub_frame *Frames = kbts__PushArray(Scratchpad->Arena, kbts__gsub_frame, KBTS_LOOKUP_STACK_SIZE); + kbts_glyph *OnePastLastSyllableGlyph = Glyph; + kbts_glyph *FirstGlyph = FirstGlyphs[0]; + kbts_glyph *OneBeforeSyllableGlyph = FirstGlyph->Prev; - if((Config->Script == KBTS_SCRIPT_KANNADA) && (ScanGlyphIndex >= 3) && (Glyphs[0].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_RA) && - (Glyphs[1].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && (Glyphs[2].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ)) + if((Config->Script == KBTS_SCRIPT_KANNADA) && (ScanGlyphIndex >= 3) && + (FirstGlyphs[0]->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_RA) && + (FirstGlyphs[1]->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && + (FirstGlyphs[2]->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ)) { // In older versions of Unicode (~4.x, pre-2004), Ra-Virama-Zwj was recommended in Kannada // to communicate the intent of displaying (Ra + (next consonant as below-base form)). @@ -19986,40 +23142,48 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, // (next consonant as below-base form)) is Ra-Zwj-Virama. // Since Ra-Virama-Zwj is not a useful sequence that happens organically in Kannada, // we are free to transform it into Ra-Zwj-Virama here without losing information. - kbts_glyph Swap = Glyphs[1]; - Glyphs[1] = Glyphs[2]; - Glyphs[2] = Swap; + KBTS__DLLIST_SWAP(FirstGlyphs[1], FirstGlyphs[2]); + + // Also swap this, because this is used below for OnePastReph. + kbts_glyph *Swap = FirstGlyphs[1]; + FirstGlyphs[1] = FirstGlyphs[2]; + FirstGlyphs[2] = Swap; } kbts_glyph_flags RephFlags = KBTS_GLYPH_FLAG_RPHF; + kbts_glyph *OnePastReph = FirstGlyphs[0]; kbts_un OnePastRephIndex = 0; { // Reph tagging. // This first pass only figures out where the reph ends. // We delay updating glyph properties until we know whether the reph is the base or not. - kbts_glyph *First = &Glyphs[0]; - kbts_glyph *Second = &Glyphs[1]; - kbts_feature *Rphf = Config->Rphf; + kbts_glyph *Second = FirstGlyphs[1]; + kbts_glyph *Third = FirstGlyphs[2]; + kbts__feature *Rphf = Config->Rphf; switch(Config->IndicScriptProperties.RephEncoding) { - case KBTS_REPH_ENCODING_LOGICAL_REPHA: - if(First->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_REPHA) + case KBTS__REPH_ENCODING_LOGICAL_REPHA: + if(FirstGlyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_REPHA) { RephFlags = 0; OnePastRephIndex = 1; } break; - case KBTS_REPH_ENCODING_IMPLICIT: - if((ScanGlyphIndex >= 2) && (Second->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && kbts_WouldSubstitute(ShapeState, LookupList, Frames, Rphf, 0, Glyphs, 2)) + case KBTS__REPH_ENCODING_IMPLICIT: + if((ScanGlyphIndex >= 2) && + (Second->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && + kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, Rphf, 0, FirstGlyphs[0], 2)) { OnePastRephIndex = 2; } break; - case KBTS_REPH_ENCODING_EXPLICIT: - if((ScanGlyphIndex >= 3) && (Second->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && (Glyphs[2].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ) && - kbts_WouldSubstitute(ShapeState, LookupList, Frames, Rphf, 0, Glyphs, 3)) + case KBTS__REPH_ENCODING_EXPLICIT: + if((ScanGlyphIndex >= 3) && + (Second->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && + (Third->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ) && + kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, Rphf, 0, FirstGlyphs[0], 3)) { OnePastRephIndex = 3; } @@ -20029,29 +23193,29 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, // Extend the reph with suffixed joiners. if(OnePastRephIndex) { - while((OnePastRephIndex < ScanGlyphIndex) && KBTS_IN_SET(Glyphs[OnePastRephIndex].SyllabicClass, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_ZWJ)(KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)))) + OnePastReph = FirstGlyphs[OnePastRephIndex - 1]->Next; + while(kbts__GlyphIsValid(Storage, OnePastReph) && + KBTS__IN_SET(OnePastReph->SyllabicClass, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_ZWJ) + (KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)))) { - OnePastRephIndex += 1; + OnePastReph = OnePastReph->Next; } } } - kbts_feature *Pref = Config->Pref; - kbts_un BaseIndex = ScanGlyphIndex; - kbts_un LastConsonantIndex = 0; - kbts_syllabic_position LastConsonantPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; + kbts__feature *Pref = Config->Pref; + kbts_glyph *BaseGlyph = OnePastLastSyllableGlyph; + kbts_glyph *LastConsonant = 0; + kbts__syllabic_position LastConsonantPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; if(Config->DottedCircle.Id && (BrokenState & 1)) { - // Insert a dotted circle after Reph. - for(kbts_un GlyphIndex = GlyphCount; GlyphIndex > OnePastRephIndex; --GlyphIndex) + BaseGlyph = kbts__InsertGlyphBefore(Storage, OnePastReph, &Config->DottedCircle); + if(!BaseGlyph) { - Glyphs[GlyphIndex] = Glyphs[GlyphIndex - 1]; + goto OutOfMemory; } - ScanGlyphIndex += 1; - Result.InsertedGlyphCount = 1; - Glyphs[OnePastRephIndex] = Config->DottedCircle; - BaseIndex = OnePastRephIndex; + BaseGlyph->UserIdOrCodepointIndex = OnePastReph->UserIdOrCodepointIndex; } else { @@ -20060,19 +23224,20 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, Scratch[0] = Config->Virama; Scratch[2] = Config->Virama; - kbts_feature *Locl = Config->Locl; - kbts_feature *Vatu = Config->Vatu; + kbts__feature *Locl = Config->Locl; + kbts__feature *Vatu = Config->Vatu; // pstf forms come last, then blwf. - kbts_feature *SectionFeature = Config->Pstf; - kbts_syllabic_position SectionPosition = KBTS_SYLLABIC_POSITION_POSTBASE_CONSONANT; + kbts__feature *SectionFeature = Config->Pstf; + kbts__syllabic_position SectionPosition = KBTS__SYLLABIC_POSITION_POSTBASE_CONSONANT; // If we see no consonant/matra, then surely we want to attach to the base. - kbts_syllabic_position SyllabicPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; + kbts__syllabic_position SyllabicPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; kbts_indic_syllabic_class LastClass = KBTS_INDIC_SYLLABIC_CLASS_COUNT; - for(kbts_un GlyphIndex = ScanGlyphIndex; GlyphIndex > OnePastRephIndex; --GlyphIndex) + for(Glyph = OnePastLastSyllableGlyph->Prev; + ; + Glyph = Glyph->Prev) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex - 1]; kbts_indic_syllabic_class Class = Glyph->SyllabicClass; switch(Class) @@ -20082,7 +23247,7 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, case KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_WITH_STACKER: case KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_MEDIAL: // Apply locl first. - Scratch[1] = kbts_Substitute1(ShapeState, LookupList, Locl, KBTS_SKIP_FLAG_ZWNJ | KBTS_SKIP_FLAG_ZWJ, Glyph); + Scratch[1] = kbts__Substitute1(Scratchpad, Config, Storage, LookupList, Locl, KBTS__SKIP_FLAG_ZWNJ | KBTS__SKIP_FLAG_ZWJ, Glyph); // Microsoft says to "move backwards until a consonant is found that does not have a below-base // or post-base form". @@ -20091,26 +23256,29 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, // syllable base. // So, as we sweep backward, set the base index on consonants unconditionally. // @Robustness: Do we want to update Base only if we do not match Pref/Vatu? - BaseIndex = GlyphIndex - 1; + BaseGlyph = Glyph; - if(kbts_WouldSubstitute(ShapeState, LookupList, Frames, Pref, 0, Scratch, 2) || kbts_WouldSubstitute(ShapeState, LookupList, Frames, Pref, 0, Scratch + 1, 2) || - kbts_WouldSubstitute(ShapeState, LookupList, Frames, Vatu, 0, Scratch, 2) || kbts_WouldSubstitute(ShapeState, LookupList, Frames, Vatu, 0, Scratch + 1, 2)) + if(kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, Pref, 0, Scratch, 2) || + kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, Pref, 0, Scratch + 1, 2) || + kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, Vatu, 0, Scratch, 2) || + kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, Vatu, 0, Scratch + 1, 2)) { - SyllabicPosition = KBTS_SYLLABIC_POSITION_POSTBASE_CONSONANT; + SyllabicPosition = KBTS__SYLLABIC_POSITION_POSTBASE_CONSONANT; } else { for(;;) { - if(kbts_WouldSubstitute(ShapeState, LookupList, Frames, SectionFeature, 0, Scratch, 2) || kbts_WouldSubstitute(ShapeState, LookupList, Frames, SectionFeature, 0, Scratch + 1, 2)) + if(kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, SectionFeature, 0, Scratch, 2) || + kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, SectionFeature, 0, Scratch + 1, 2)) { SyllabicPosition = SectionPosition; break; } - else if(SectionPosition == KBTS_SYLLABIC_POSITION_POSTBASE_CONSONANT) + else if(SectionPosition == KBTS__SYLLABIC_POSITION_POSTBASE_CONSONANT) { SectionFeature = Config->Blwf; - SectionPosition = KBTS_SYLLABIC_POSITION_BELOWBASE_CONSONANT; + SectionPosition = KBTS__SYLLABIC_POSITION_BELOWBASE_CONSONANT; // Retry with Blwf. } else @@ -20120,9 +23288,9 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, } } - if(!LastConsonantIndex) + if(!LastConsonant) { - LastConsonantIndex = GlyphIndex - 1; + LastConsonant = Glyph; LastConsonantPosition = SyllabicPosition; } @@ -20132,7 +23300,7 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, case KBTS_INDIC_SYLLABIC_CLASS_VOWEL: case KBTS_INDIC_SYLLABIC_CLASS_PLACEHOLDER: case KBTS_INDIC_SYLLABIC_CLASS_DOTTED_CIRCLE: - BaseIndex = GlyphIndex - 1; + BaseGlyph = Glyph; goto DoneScanningForBase; break; @@ -20152,67 +23320,72 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, } LastClass = Class; + if(Glyph == OnePastReph) + { + break; + } } DoneScanningForBase:; } // Fix reph position now. - if(OnePastRephIndex) + if(OnePastReph != FirstGlyph) { - kbts_syllabic_position RephPosition = KBTS_SYLLABIC_POSITION_RA_TO_BECOME_REPH; + kbts__syllabic_position RephPosition = KBTS__SYLLABIC_POSITION_RA_TO_BECOME_REPH; kbts_glyph_flags Flags = RephFlags; - kbts_un OnePastLast = OnePastRephIndex; - if(BaseIndex == ScanGlyphIndex) + kbts_un RephGlyphCount = OnePastRephIndex; + if(BaseGlyph == OnePastLastSyllableGlyph) { - RephPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; + RephPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; Flags = 0; - BaseIndex = 0; - OnePastRephIndex = 0; + BaseGlyph = OnePastReph = FirstGlyph; } - KBTS_FOR(GlyphIndex, 0, OnePastLast) + KBTS__FOR(GlyphIndex, 0, RephGlyphCount) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - Glyph->SyllabicPosition = RephPosition; - Glyph->Flags |= Flags; + kbts_glyph *RephGlyph = FirstGlyphs[GlyphIndex]; + RephGlyph->SyllabicPosition = RephPosition; + RephGlyph->Flags |= Flags; } } - if(BaseIndex < ScanGlyphIndex) + if(BaseGlyph != OnePastLastSyllableGlyph) { - Glyphs[BaseIndex].SyllabicPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; + BaseGlyph->SyllabicPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; - if(!LastConsonantIndex) + if(!LastConsonant) { - LastConsonantIndex = BaseIndex; + LastConsonant = BaseGlyph; // LastConsonantPosition is already set to SYLLABLE_BASE. } } { // Reorder marks. // @Cleanup @Speed: Supposedly, NORMALIZE already does this... - kbts_glyph *ReorderGlyph = Glyphs + ScanGlyphIndex - 1; - while(ReorderGlyph >= Glyphs) + kbts_glyph *ReorderGlyph = OnePastLastSyllableGlyph->Prev; + while(ReorderGlyph != OneBeforeSyllableGlyph) { if(ReorderGlyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_NUKTA) { - kbts_glyph *Prev = ReorderGlyph - 1; + kbts_glyph *Prev = ReorderGlyph->Prev; - while((Prev >= Glyphs) && KBTS_IN_SET(Prev->SyllabicClass, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_HALANT)(KBTS_INDIC_SYLLABIC_CLASS_VEDIC_SIGN)))) + while((Prev != OneBeforeSyllableGlyph) && + KBTS__IN_SET(Prev->SyllabicClass, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_HALANT) + (KBTS_INDIC_SYLLABIC_CLASS_VEDIC_SIGN)))) { - kbts_glyph Swap = *Prev; - *Prev = Prev[1]; - Prev[1] = Swap; + kbts_glyph *NextPrev = Prev->Prev; - Prev -= 1; + KBTS__DLLIST_SWAP(Prev, Prev->Next); + + Prev = NextPrev; } ReorderGlyph = Prev; } else { - ReorderGlyph -= 1; + ReorderGlyph = ReorderGlyph->Prev; } } } @@ -20222,12 +23395,13 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, // We want to bake this ordering into our sort, so we increase our syllabic position bits // and sub-order matras with the low nibble. (There should only be 4 matras tops, anyway.) kbts_un LeftMatraCount = 0; - KBTS_FOR(GlyphIndex, 0, ScanGlyphIndex) + for(Glyph = OneBeforeSyllableGlyph->Next; + Glyph != OnePastLastSyllableGlyph; + Glyph = Glyph->Next) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; Glyph->SyllabicPosition <<= 4; - if(Glyph->SyllabicPosition == (KBTS_SYLLABIC_POSITION_PREBASE_MATRA << 4)) + if(Glyph->SyllabicPosition == (KBTS__SYLLABIC_POSITION_PREBASE_MATRA << 4)) { Glyph->SyllabicPosition += (15 - LeftMatraCount++) & 0xF; } @@ -20241,20 +23415,36 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, // a pre-base+base part (right attachments), a post-base consonant part (left attachments), and // a post-consonant part (right attachments again). // This part handles right attachments. Left attachments were handled in the base syllable search. - kbts_attach_state States[2] = { - {KBTS_SYLLABIC_POSITION_RA_TO_BECOME_REPH << 4, KBTS_SYLLABIC_POSITION_RA_TO_BECOME_REPH << 4, KBTS_INDIC_SYLLABIC_CLASS_COUNT, OnePastRephIndex, BaseIndex}, - {(kbts_u8)(LastConsonantPosition << 4), (kbts_u8)(LastConsonantPosition << 4), KBTS_INDIC_SYLLABIC_CLASS_COUNT, LastConsonantIndex + 1, ScanGlyphIndex}, - }; - kbts_un Iterations = KBTS_MAX(States[0].OnePastLast - States[0].Start, States[1].OnePastLast - States[1].Start); - KBTS_FOR(GlyphIndex, 0, Iterations) - { - KBTS_FOR(StateIndex, 0, KBTS_ARRAY_LENGTH(States)) + kbts__attach_state States[2] = { { - kbts_attach_state *Attach = &States[StateIndex]; + KBTS__SYLLABIC_POSITION_RA_TO_BECOME_REPH << 4, + KBTS__SYLLABIC_POSITION_RA_TO_BECOME_REPH << 4, + KBTS_INDIC_SYLLABIC_CLASS_COUNT, + OnePastReph, + BaseGlyph, + OnePastReph, + }, + { + (kbts_u8)(LastConsonantPosition << 4), + (kbts_u8)(LastConsonantPosition << 4), + KBTS_INDIC_SYLLABIC_CLASS_COUNT, + LastConsonant ? LastConsonant->Next : OnePastLastSyllableGlyph, + OnePastLastSyllableGlyph, + LastConsonant ? LastConsonant->Next : OnePastLastSyllableGlyph, + }, + }; + for(;;) + { + int Done = 1; - if((Attach->Start + GlyphIndex) < Attach->OnePastLast) + KBTS__FOR(StateIndex, 0, KBTS__ARRAY_LENGTH(States)) + { + kbts__attach_state *Attach = &States[StateIndex]; + + if(Attach->At != Attach->OnePastLast) { - kbts_glyph *Glyph = &Glyphs[Attach->Start + GlyphIndex]; + Glyph = Attach->At; + kbts_indic_syllabic_class Class = Glyph->SyllabicClass; switch(Class) { @@ -20263,8 +23453,8 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, case KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_WITH_STACKER: case KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_MEDIAL: // This only happens in the pre-base state. - Glyph->SyllabicPosition = (KBTS_SYLLABIC_POSITION_PREBASE_CONSONANT << 4); - Attach->CurrentPosition = (KBTS_SYLLABIC_POSITION_PREBASE_CONSONANT << 4); + Glyph->SyllabicPosition = (KBTS__SYLLABIC_POSITION_PREBASE_CONSONANT << 4); + Attach->CurrentPosition = (KBTS__SYLLABIC_POSITION_PREBASE_CONSONANT << 4); break; case KBTS_INDIC_SYLLABIC_CLASS_MATRA_POST: @@ -20274,18 +23464,18 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, // and not to the consonant. if(Attach->LastClass == KBTS_INDIC_SYLLABIC_CLASS_SYLLABLE_MODIFIER) { - Glyph[-1].SyllabicPosition = Glyph->SyllabicPosition; + Glyph->Prev->SyllabicPosition = Glyph->SyllabicPosition; } case KBTS_INDIC_SYLLABIC_CLASS_MATRA: Attach->CurrentPosition = Glyph->SyllabicPosition; - if((Attach->CurrentPosition >> 4) != KBTS_SYLLABIC_POSITION_PREBASE_MATRA) + if((Attach->CurrentPosition >> 4) != KBTS__SYLLABIC_POSITION_PREBASE_MATRA) { Attach->LastPositionThatWasNotPreBaseMatra = Attach->CurrentPosition; } break; case KBTS_INDIC_SYLLABIC_CLASS_HALANT: - if((Attach->CurrentPosition >> 4) == KBTS_SYLLABIC_POSITION_PREBASE_MATRA) + if((Attach->CurrentPosition >> 4) == KBTS__SYLLABIC_POSITION_PREBASE_MATRA) { // Uniscribe does not reorder halants with pre-base matras. Not sure why. Glyph->SyllabicPosition = Attach->LastPositionThatWasNotPreBaseMatra; @@ -20298,48 +23488,55 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, Glyph->SyllabicPosition = Attach->CurrentPosition; break; } + Attach->LastClass = Class; + Attach->At = Glyph->Next; + Done = 0; } } + + if(Done) + { + break; + } } } { // Do the sort! // NOTE: It is important that we sort _now_, because some characters will be reordered across the base from // where they started, which can change which features will apply to them. - KBTS_FOR(Iteration, 0, ScanGlyphIndex) + KBTS__DLLIST_SORT(OneBeforeSyllableGlyph->Next, OnePastLastSyllableGlyph, SyllabicPosition); + +#ifdef KBTS_SANITY_CHECK { - KBTS_FOR(GlyphIndex, 1, ScanGlyphIndex) + kbts_glyph *Glyph0 = OneBeforeSyllableGlyph->Next; + if(Glyph0 != OnePastLastSyllableGlyph) { - kbts_glyph *Left = &Glyphs[GlyphIndex - 1]; - kbts_glyph *Right = &Glyphs[GlyphIndex]; - - if(Left->SyllabicPosition > Right->SyllabicPosition) + for(kbts_glyph *Glyph1 = Glyph0->Next; + kbts__GlyphIsValid(C, Glyph1); + Glyph1 = Glyph1->Next) { - kbts_glyph Swap = *Left; - *Left = *Right; - *Right = Swap; - - // Make sure we don't lose track of the base... - if(GlyphIndex == BaseIndex) + if(Glyph1 != OnePastLastSyllableGlyph) { - BaseIndex = GlyphIndex - 1; + KBTS_ASSERT(Glyph0->SyllabicPosition <= Glyph1->SyllabicPosition); } - else if((GlyphIndex - 1) == BaseIndex) + else { - BaseIndex = GlyphIndex; + break; } } } } + #endif } - { // @Temporary @Cleanup - // Revert our syllabic position shift for now. - KBTS_FOR(GlyphIndex, 0, ScanGlyphIndex) - { - Glyphs[GlyphIndex].SyllabicPosition >>= 4; - } + // @Temporary @Cleanup + // Revert our syllabic position shift for now. + for(Glyph = OneBeforeSyllableGlyph->Next; + Glyph != OnePastLastSyllableGlyph; + Glyph = Glyph->Next) + { + Glyph->SyllabicPosition >>= 4; } // This is what Harfbuzz does: @@ -20359,9 +23556,10 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, } kbts_indic_syllabic_class LastClass = 0; - for(kbts_un GlyphIndex = BaseIndex; GlyphIndex > 0; --GlyphIndex) + for(Glyph = BaseGlyph->Prev; + Glyph != OneBeforeSyllableGlyph; + Glyph = Glyph->Prev) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex - 1]; kbts_glyph_flags Flags = BaseFlags; if(LastClass != KBTS_INDIC_SYLLABIC_CLASS_ZWNJ) @@ -20375,17 +23573,17 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, } // All post-base glyphs get BLWF, ABVF, PSTF. Some get PREF. - kbts_glyph Scratch[2]; Scratch[0] = Scratch[1] = KBTS_ZERO_TYPE(kbts_glyph); + kbts_glyph Scratch[2]; Scratch[0] = Scratch[1] = KBTS__ZERO_TYPE(kbts_glyph); kbts_glyph *LastGlyph; LastGlyph = 0; - KBTS_FOR(GlyphIndex, BaseIndex + 1, ScanGlyphIndex) + for(Glyph = BaseGlyph->Next; + Glyph != OnePastLastSyllableGlyph; + Glyph = Glyph->Next) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - // It is tempting to apply PREF to every glyph after the base. // However, the PREF flag is also used to find the syllable base again in EndCluster, // so we have to be precise with it. Scratch[1] = *Glyph; - if(kbts_WouldSubstitute(ShapeState, LookupList, Frames, Pref, 0, Scratch, 2)) + if(kbts__WouldSubstitute(Scratchpad, Config, Storage, LookupList, Frames, Pref, 0, Scratch, 2)) { LastGlyph->Flags |= KBTS_GLYPH_FLAG_PREF; Glyph->Flags |= KBTS_GLYPH_FLAG_PREF; @@ -20398,238 +23596,270 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, // The base only inherits stuff after it, up to whatever and not including what was grabbed by // post-base consonants. - if(BaseIndex < ScanGlyphIndex) + if(BaseGlyph != OnePastLastSyllableGlyph) { - Glyphs[BaseIndex].SyllabicPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; + BaseGlyph->SyllabicPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; - kbts_u64 Classes = (1ull << KBTS_INDIC_SYLLABIC_CLASS_HALANT) | (1ull << KBTS_INDIC_SYLLABIC_CLASS_VEDIC_SIGN) | (1ull << KBTS_INDIC_SYLLABIC_CLASS_NUKTA) | - (1ull << KBTS_INDIC_SYLLABIC_CLASS_ZWJ) | (1ull << KBTS_INDIC_SYLLABIC_CLASS_ZWNJ); - kbts_glyph *Next = &Glyphs[BaseIndex + 1]; - while((Next < OnePastLastSyllableGlyph) && (Next->SyllabicPosition < KBTS_SYLLABIC_POSITION_SYLLABLE_BASE) && ((1ull << Next->SyllabicClass) & Classes)) + kbts_u64 Classes = (1ull << KBTS_INDIC_SYLLABIC_CLASS_HALANT) | + (1ull << KBTS_INDIC_SYLLABIC_CLASS_VEDIC_SIGN) | + (1ull << KBTS_INDIC_SYLLABIC_CLASS_NUKTA) | + (1ull << KBTS_INDIC_SYLLABIC_CLASS_ZWJ) | + (1ull << KBTS_INDIC_SYLLABIC_CLASS_ZWNJ); + + kbts_glyph *Next = BaseGlyph->Next; + while((Next != OnePastLastSyllableGlyph) && + (Next->SyllabicPosition < KBTS__SYLLABIC_POSITION_SYLLABLE_BASE) && + ((1ull << Next->SyllabicClass) & Classes)) { - Next->SyllabicPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; + Next->SyllabicPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; - Next += 1; + Next = Next->Next; } } - Result.ClusterGlyphCount = ScanGlyphIndex; - ShapeState->RealCluster = 1; + Result = OnePastLastSyllableGlyph; + Scratchpad->RealCluster = 1; } - } - break; + } break; case KBTS_SHAPER_USE: { - kbts_un OtherCount = 0; - while((OtherCount < GlyphCount) && ((Glyphs[OtherCount].UseClass == KBTS_USE_SYLLABIC_CLASS_OTHER) || - (Glyphs[OtherCount].UseClass == KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_MOD_POST))) + int NonCluster = 0; + while(kbts__GlyphIsValid(Storage, Glyph) && + ((Glyph->UseClass == KBTS_USE_SYLLABIC_CLASS_OTHER) || + (Glyph->UseClass == KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_MOD_POST))) { - OtherCount += 1; + NonCluster = 1; + + Glyph = Glyph->Next; } - if(OtherCount) - { - Result.ClusterGlyphCount = OtherCount; - } - else + if(!NonCluster) { // Parse a real cluster. kbts_u8 State = 0; - kbts_un ScanGlyphIndex = 0; - while((ScanGlyphIndex < GlyphCount) && (State < KBTS_USE_STATE_s0)) + kbts_glyph *StartGlyph = Glyph; + while(kbts__GlyphIsValid(Storage, Glyph) && + (State < KBTS_USE_STATE_s0)) { - kbts_glyph *Glyph = &Glyphs[ScanGlyphIndex++]; State = kbts_UseTransition[Glyph->UseClass][State]; // @Incomplete: Remove this!!! State -= 1; + Glyph = Glyph->Next; } if(State >= KBTS_USE_STATE_s0) { - ScanGlyphIndex -= 1; + Glyph = Glyph->Prev; } - if((ScanGlyphIndex < GlyphCount) && (Glyphs[ScanGlyphIndex].UseClass == KBTS_USE_SYLLABIC_CLASS_ZWNJ)) + if(kbts__GlyphIsValid(Storage, Glyph) && + (Glyph->UseClass == KBTS_USE_SYLLABIC_CLASS_ZWNJ)) { - ScanGlyphIndex += 1; + Glyph = Glyph->Next; + } + if(Glyph == StartGlyph) + { + Glyph = Glyph->Next; } - if(!ScanGlyphIndex) ScanGlyphIndex = 1; - Result.ClusterGlyphCount = ScanGlyphIndex; - ShapeState->RealCluster = 1; + Scratchpad->RealCluster = 1; } + + Result = Glyph; } break; case KBTS_SHAPER_KHMER: { - kbts_un OtherCount = 0; - while((OtherCount < GlyphCount) && ((Glyphs[OtherCount].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_OTHER) || - (Glyphs[OtherCount].SyllabicClass >= KBTS_KHMER_SYLLABIC_CLASS_COUNT))) + int NonCluster = 0; + while(kbts__GlyphIsValid(Storage, Glyph) && + ((Glyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_OTHER) || + (Glyph->SyllabicClass >= KBTS_KHMER_SYLLABIC_CLASS_COUNT))) { - OtherCount += 1; + Glyph = Glyph->Next; + NonCluster = 1; } - if(OtherCount) - { - Result.ClusterGlyphCount = OtherCount; - } - else + Result = Glyph; + + if(!NonCluster) { kbts_u8 State = 0; - kbts_un ScanGlyphIndex = 0; - while((ScanGlyphIndex < GlyphCount) && (State < KBTS_KHMER_SYLLABIC_STATE_COUNT)) + kbts_glyph *FirstGlyph = Glyph; + kbts_glyph *OneBeforeSyllableGlyph = Glyph->Prev; + + while(kbts__GlyphIsValid(Storage, Glyph) && + (State < KBTS_KHMER_SYLLABIC_STATE_COUNT)) { - kbts_glyph *Glyph = &Glyphs[ScanGlyphIndex++]; kbts_indic_syllabic_class Class = Glyph->SyllabicClass; State = kbts_KhmerSyllabicTransition[Class][State]; // @Incomplete: Remove this! State -= 1; + Glyph = Glyph->Next; } if(State >= KBTS_KHMER_SYLLABIC_STATE_COUNT) { - ScanGlyphIndex -= 1; + Glyph = Glyph->Prev; } - if(!ScanGlyphIndex) ScanGlyphIndex = 1; + if(Glyph == FirstGlyph) + { + Glyph = Glyph->Next; + } + + kbts_glyph *OnePastLastSyllableGlyph = Glyph; if(Config->DottedCircle.Id && - !KBTS_IN_SET64(Glyphs->SyllabicClass, KBTS_SET64((KBTS_INDIC_SYLLABIC_CLASS_CONSONANT)(KBTS_INDIC_SYLLABIC_CLASS_RA) - (KBTS_INDIC_SYLLABIC_CLASS_VOWEL)(KBTS_INDIC_SYLLABIC_CLASS_DOTTED_CIRCLE) - (KBTS_INDIC_SYLLABIC_CLASS_PLACEHOLDER)))) + !KBTS__IN_SET64(FirstGlyph->SyllabicClass, KBTS__SET64((KBTS_INDIC_SYLLABIC_CLASS_CONSONANT) + (KBTS_INDIC_SYLLABIC_CLASS_RA) + (KBTS_INDIC_SYLLABIC_CLASS_VOWEL) + (KBTS_INDIC_SYLLABIC_CLASS_DOTTED_CIRCLE) + (KBTS_INDIC_SYLLABIC_CLASS_PLACEHOLDER)))) { // Broken cluster. // Insert a dotted circle. - for(kbts_un GlyphIndex = GlyphCount; GlyphIndex; --GlyphIndex) + kbts_glyph *NewFirstGlyph = kbts__InsertGlyphBefore(Storage, FirstGlyph, &Config->DottedCircle); + if(!NewFirstGlyph) { - Glyphs[GlyphIndex] = Glyphs[GlyphIndex - 1]; + goto OutOfMemory; } - ScanGlyphIndex += 1; - Result.InsertedGlyphCount = 1; - Glyphs[0] = Config->DottedCircle; + + NewFirstGlyph->UserIdOrCodepointIndex = FirstGlyph->UserIdOrCodepointIndex; + FirstGlyph = NewFirstGlyph; } // In Khmer, the first glyph is always the base. - kbts_glyph PreBaseGlyphs[3]; + kbts_glyph *PreBaseGlyphs[3]; int SawHalantRa = 0; kbts_un PreBaseGlyphCount = 0; kbts_un Classes = 0; - kbts_un WriteCursor = ScanGlyphIndex - 1; - for(kbts_un GlyphIndex = ScanGlyphIndex; GlyphIndex; --GlyphIndex) + + kbts_u32 AddFlags = KBTS_GLYPH_FLAG_ABVF | KBTS_GLYPH_FLAG_BLWF | KBTS_GLYPH_FLAG_PSTF; + + kbts_glyph *LastGlyph = OnePastLastSyllableGlyph; + for(Glyph = OnePastLastSyllableGlyph->Prev; + Glyph != OneBeforeSyllableGlyph; + Glyph = Glyph->Prev) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex - 1]; kbts_indic_syllabic_class Class = Glyph->SyllabicClass; - if(GlyphIndex > 1) {Glyph->Flags |= KBTS_GLYPH_FLAG_ABVF | KBTS_GLYPH_FLAG_BLWF | KBTS_GLYPH_FLAG_PSTF;} + if(Glyph == FirstGlyph) + { + AddFlags = 0; + } + + Glyph->Flags |= AddFlags; Classes = ((Classes << 8) | Class) & 0xFFFF; if(Class == KBTS_INDIC_SYLLABIC_CLASS_VOWEL_PRE) { - PreBaseGlyphs[PreBaseGlyphCount++] = *Glyph; + PreBaseGlyphs[PreBaseGlyphCount++] = Glyph; } else if(!SawHalantRa && (Classes == (KBTS_INDIC_SYLLABIC_CLASS_HALANT | (KBTS_INDIC_SYLLABIC_CLASS_RA << 8)))) { - kbts_glyph *Ra = &Glyphs[GlyphIndex]; Glyph->Flags |= KBTS_GLYPH_FLAG_PREF; - Ra->Flags |= KBTS_GLYPH_FLAG_PREF; + LastGlyph->Flags |= KBTS_GLYPH_FLAG_PREF; + + PreBaseGlyphs[PreBaseGlyphCount++] = Glyph; + PreBaseGlyphs[PreBaseGlyphCount++] = LastGlyph; - PreBaseGlyphs[PreBaseGlyphCount++] = *Glyph; - PreBaseGlyphs[PreBaseGlyphCount++] = *Ra; - WriteCursor += 1; // Cancel the Ra write. // All of the glyphs written so far need to be flagged with cfar. - KBTS_FOR(CfarGlyphIndex, WriteCursor, ScanGlyphIndex) + // @Speed: Detect the Halant-Ra sequence in the FSM loop. + for(kbts_glyph *PostRaGlyph = LastGlyph->Next; + PostRaGlyph != OnePastLastSyllableGlyph; + PostRaGlyph = PostRaGlyph->Next) { - Glyphs[CfarGlyphIndex].Flags |= KBTS_GLYPH_FLAG_CFAR; + PostRaGlyph->Flags |= KBTS_GLYPH_FLAG_CFAR; } + SawHalantRa = 1; } - else - { - Glyphs[WriteCursor--] = *Glyph; - } + + LastGlyph = Glyph; } - KBTS_FOR(PreBaseGlyphIndex, 0, PreBaseGlyphCount) + while(PreBaseGlyphCount) { - Glyphs[PreBaseGlyphIndex] = PreBaseGlyphs[PreBaseGlyphIndex]; + kbts_glyph *PreBaseGlyph = PreBaseGlyphs[--PreBaseGlyphCount]; + KBTS__DLLIST_REMOVE(PreBaseGlyph); + PreBaseGlyph->Prev = OneBeforeSyllableGlyph; + PreBaseGlyph->Next = OneBeforeSyllableGlyph->Next; + PreBaseGlyph->Prev->Next = PreBaseGlyph->Next->Prev = PreBaseGlyph; } - Result.ClusterGlyphCount = ScanGlyphIndex; + Result = OnePastLastSyllableGlyph; } } break; case KBTS_SHAPER_MYANMAR: { - kbts_un OtherCount = 0; - while((OtherCount < GlyphCount) && ((Glyphs[OtherCount].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_OTHER) || - (Glyphs[OtherCount].SyllabicClass >= KBTS_MYANMAR_SYLLABIC_CLASS_COUNT))) + int NonCluster = 0; + while(kbts__GlyphIsValid(Storage, Glyph) && + ((Glyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_OTHER) || + (Glyph->SyllabicClass >= KBTS_MYANMAR_SYLLABIC_CLASS_COUNT))) { - OtherCount += 1; + NonCluster = 1; } - if(OtherCount) - { - Result.ClusterGlyphCount = OtherCount; - } - else + if(!NonCluster) { kbts_u8 State = 0; - kbts_un ScanGlyphIndex = 0; - while((ScanGlyphIndex < GlyphCount) && (State < KBTS_MYANMAR_SYLLABIC_STATE_COUNT)) + kbts_glyph *FirstGlyph = Glyph; + while(kbts__GlyphIsValid(Storage, Glyph) && + (State < KBTS_MYANMAR_SYLLABIC_STATE_COUNT)) { - kbts_glyph *Glyph = &Glyphs[ScanGlyphIndex++]; kbts_indic_syllabic_class Class = Glyph->SyllabicClass; State = kbts_MyanmarSyllabicTransition[Class][State]; // @Incomplete: Remove this! State -= 1; + + Glyph = Glyph->Next; } if(State >= KBTS_MYANMAR_SYLLABIC_STATE_COUNT) { - ScanGlyphIndex -= 1; + Glyph = Glyph->Prev; + } + if(Glyph == FirstGlyph) + { + Glyph = Glyph->Next; } - if(!ScanGlyphIndex) ScanGlyphIndex = 1; - ShapeState->RealCluster = 1; - Result.ClusterGlyphCount = ScanGlyphIndex; + Scratchpad->RealCluster = 1; } + + Result = Glyph; } break; } + if(0) + { + OutOfMemory:; + Scratchpad->Error = KBTS_SHAPE_ERROR_OUT_OF_MEMORY; + } + + kbts__EndLifetime(&Lifetime); return Result; } -typedef struct kbts_end_cluster_result +static void kbts__EndCluster(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_storage *Storage) { - int InsertDottedCircle; - kbts_un DottedCircleIndex; -} kbts_end_cluster_result; - -static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbts_glyph_array *Cluster) -{ - kbts_end_cluster_result Result = KBTS_ZERO; - kbts_shape_config *Config = ShapeState->Config; - kbts_glyph *Glyphs = Cluster->Glyphs; - kbts_un GlyphCount = Cluster->Count; - switch(Config->Shaper) { case KBTS_SHAPER_INDIC: - if(GlyphCount > 1) + if(kbts__GlyphIsValid(Storage, Storage->GlyphSentinel.Next)) { // Final syllable reordering. // We are just copying Harfbuzz here, because I've read all the documentation I can and, frankly, I have no idea. - kbts_feature *Pref = Config->Pref; + kbts__feature *Pref = Config->Pref; kbts_u32 ViramaId = Config->Virama.Id; - KBTS_FOR(GlyphIndex, 0, GlyphCount) + KBTS__FOR_GLYPH(Storage, Glyph) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - // Harfbuzz tests for (ligated | multiplied) here. // I don't really get why, because, supposedly, all viramas should be // classified as halants regardless. @@ -20646,44 +23876,48 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt } // Locate the syllable base. - kbts_un BaseIndex = GlyphCount; + kbts_glyph *BaseGlyph = (kbts_glyph *)&Storage->GlyphSentinel; { - kbts_un AfterPreBaseIndex = 0; - while((AfterPreBaseIndex < GlyphCount) && (Glyphs[AfterPreBaseIndex].SyllabicPosition < KBTS_SYLLABIC_POSITION_SYLLABLE_BASE)) + kbts_glyph *AfterPreBase = Storage->GlyphSentinel.Next; + while(kbts__GlyphIsValid(Storage, AfterPreBase) && + (AfterPreBase->SyllabicPosition < KBTS__SYLLABIC_POSITION_SYLLABLE_BASE)) { - ++AfterPreBaseIndex; + AfterPreBase = AfterPreBase->Next; } - if(AfterPreBaseIndex < GlyphCount) + if(kbts__GlyphIsValid(Storage, AfterPreBase)) { - BaseIndex = AfterPreBaseIndex; + BaseGlyph = AfterPreBase; - if(Pref && ((AfterPreBaseIndex + 1) < GlyphCount)) + if(Pref && kbts__GlyphIsValid(Storage, AfterPreBase->Next)) { // If we find a pre-base-reordering ra, then the base is probably right after it! - kbts_un At = AfterPreBaseIndex + 1; - while((At < GlyphCount) && !(Glyphs[At].Flags & KBTS_GLYPH_FLAG_PREF)) + kbts_glyph *AtGlyph = AfterPreBase->Next; + while(kbts__GlyphIsValid(Storage, AtGlyph) && + !(AtGlyph->Flags & KBTS_GLYPH_FLAG_PREF)) { - ++At; + AtGlyph = AtGlyph->Next; } // If we are not a consonant, then we are attached to a consonant. // If we are a pref-able consonant, but we did not form a pref, then we should be right after the base. // Either way, we are very close to the base. - kbts_glyph *Prefable = &Glyphs[At]; - if((At < GlyphCount) && - (!(Prefable->Flags & KBTS_GLYPH_FLAG_GENERATED_BY_GSUB) || !(Prefable->Flags & KBTS_GLYPH_FLAG_LIGATURE) || (Prefable->Flags & KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION))) + kbts_glyph *Prefable = AtGlyph; // Read like "pref-able". :) + if(kbts__GlyphIsValid(Storage, Prefable) && + (!(Prefable->Flags & KBTS_GLYPH_FLAG_GENERATED_BY_GSUB) || + !(Prefable->Flags & KBTS_GLYPH_FLAG_LIGATURE) || + (Prefable->Flags & KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION))) { - while((At < GlyphCount) && (Prefable->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT)) + while(kbts__GlyphIsValid(Storage, Prefable) && + (Prefable->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT)) { - ++At; - ++Prefable; + Prefable = Prefable->Next; } - if(At < GlyphCount) + if(kbts__GlyphIsValid(Storage, Prefable)) { - Prefable->SyllabicPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; - BaseIndex = At; + Prefable->SyllabicPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; + BaseGlyph = Prefable; // Fall through to the Malayalam test. } } @@ -20694,35 +23928,40 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt // In Malayalam, the base is after below-base forms. // (We actually just give all below-base forms the base pos.) // @Speed: This would be way simpler with a null sentinel at the end of Glyphs! - kbts_un At = BaseIndex + 1; - while(At < GlyphCount) + kbts_glyph *AtGlyph = BaseGlyph->Next; + while(kbts__GlyphIsValid(Storage, AtGlyph)) { - while((At < GlyphCount) && KBTS_IN_SET(Glyphs[At].SyllabicClass, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_ZWJ)(KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)))) + while(kbts__GlyphIsValid(Storage, AtGlyph) && + KBTS__IN_SET(AtGlyph->SyllabicClass, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_ZWJ) + (KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)))) { - ++At; + AtGlyph = AtGlyph->Next; } - if((At < GlyphCount) && (Glyphs[At].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT)) + if(kbts__GlyphIsValid(Storage, AtGlyph) && + (AtGlyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT)) { - ++At; + AtGlyph = AtGlyph->Next; } else { break; } - while((At < GlyphCount) && KBTS_IN_SET(Glyphs[At].SyllabicClass, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_ZWJ)(KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)))) + while(kbts__GlyphIsValid(Storage, AtGlyph) && + KBTS__IN_SET(AtGlyph->SyllabicClass, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_ZWJ) + (KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)))) { - ++At; + AtGlyph = AtGlyph->Next; } - if(At < GlyphCount) + if(kbts__GlyphIsValid(Storage, AtGlyph)) { - kbts_glyph *Glyph = &Glyphs[At]; - if(kbts_SyllabicClassIsConsonant(Glyph->SyllabicClass) && (Glyph->SyllabicPosition == KBTS_SYLLABIC_POSITION_BELOWBASE_CONSONANT)) + if(kbts__SyllabicClassIsConsonant(AtGlyph->SyllabicClass) && + (AtGlyph->SyllabicPosition == KBTS__SYLLABIC_POSITION_BELOWBASE_CONSONANT)) { - Glyph->SyllabicPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; - BaseIndex = At; + AtGlyph->SyllabicPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; + BaseGlyph = AtGlyph; // Do not break; keep matching below-base forms. } } @@ -20730,55 +23969,70 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt } } - if(BaseIndex && (BaseIndex < GlyphCount) && (Glyphs[BaseIndex].SyllabicPosition > KBTS_SYLLABIC_POSITION_SYLLABLE_BASE)) + if(kbts__GlyphIsValid(Storage, BaseGlyph) && + kbts__GlyphIsValid(Storage, BaseGlyph->Prev) && + (BaseGlyph->SyllabicPosition > KBTS__SYLLABIC_POSITION_SYLLABLE_BASE)) { - BaseIndex -= 1; + BaseGlyph = BaseGlyph->Prev; } - if((BaseIndex == GlyphCount) && (Glyphs[GlyphCount - 1].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ)) + if(!kbts__GlyphIsValid(Storage, BaseGlyph) && + (BaseGlyph->Prev->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ)) { - BaseIndex = GlyphCount - 1; + BaseGlyph = BaseGlyph->Prev; } - if(BaseIndex < GlyphCount) + if(kbts__GlyphIsValid(Storage, BaseGlyph)) { - while(BaseIndex && KBTS_IN_SET(Glyphs[BaseIndex].SyllabicClass, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_NUKTA)(KBTS_INDIC_SYLLABIC_CLASS_HALANT)))) + while(kbts__GlyphIsValid(Storage, BaseGlyph->Prev) && + KBTS__IN_SET(BaseGlyph->SyllabicClass, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_NUKTA) + (KBTS_INDIC_SYLLABIC_CLASS_HALANT)))) { - BaseIndex -= 1; + BaseGlyph = BaseGlyph->Prev; } } } // Reorder matras. - if(BaseIndex && (GlyphCount > 1)) + if(kbts__GlyphIsValid(Storage, BaseGlyph->Prev) && + (Storage->GlyphSentinel.Prev != Storage->GlyphSentinel.Next) /* GlyphCount > 1 */) { - kbts_un ToIndex = BaseIndex - 1; - if(BaseIndex == GlyphCount) + kbts_glyph *FirstGlyph = Storage->GlyphSentinel.Next; + kbts_glyph *To = BaseGlyph->Prev; + if(!kbts__GlyphIsValid(Storage, BaseGlyph)) { - ToIndex = GlyphCount - 2; + To = To->Prev; } if((Config->Script != KBTS_SCRIPT_MALAYALAM) && (Config->Script != KBTS_SCRIPT_TAMIL)) { + kbts_glyph *LastGlyph = To->Next; + for(;;) { - while(ToIndex && !KBTS_IN_SET(Glyphs[ToIndex].SyllabicClass, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_MATRA)(KBTS_INDIC_SYLLABIC_CLASS_MATRA_POST)(KBTS_INDIC_SYLLABIC_CLASS_HALANT)))) + while((To != FirstGlyph) && + !KBTS__IN_SET(To->SyllabicClass, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_MATRA) + (KBTS_INDIC_SYLLABIC_CLASS_MATRA_POST) + (KBTS_INDIC_SYLLABIC_CLASS_HALANT)))) { - --ToIndex; + LastGlyph = To; + To = To->Prev; } - kbts_glyph *Glyph = &Glyphs[ToIndex]; - if((Glyph->SyllabicClass != KBTS_INDIC_SYLLABIC_CLASS_HALANT) || (Glyph->SyllabicPosition == KBTS_SYLLABIC_POSITION_PREBASE_MATRA)) + if((To->SyllabicClass != KBTS_INDIC_SYLLABIC_CLASS_HALANT) || + (To->SyllabicPosition == KBTS__SYLLABIC_POSITION_PREBASE_MATRA)) { // We have nothing to move. - ToIndex = 0; + To = FirstGlyph; break; } - else if(ToIndex && ((ToIndex + 1) < GlyphCount) && (Glyphs[ToIndex + 1].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ)) + else if((To != FirstGlyph) && + kbts__GlyphIsValid(Storage, LastGlyph) && + (LastGlyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ)) { // Zwj+Halant cancels matra movement when the Halant is not attached to the matra. - --ToIndex; + To = To->Prev; } else { @@ -20787,84 +24041,95 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt } } - if(ToIndex && (Glyphs[ToIndex].SyllabicPosition != KBTS_SYLLABIC_POSITION_PREBASE_MATRA)) + if((To != FirstGlyph) && + (To->SyllabicPosition != KBTS__SYLLABIC_POSITION_PREBASE_MATRA)) { - for(kbts_un MatraIndex = ToIndex; MatraIndex; --MatraIndex) + for(kbts_glyph *Matra = To->Prev; + kbts__GlyphIsValid(Storage, Matra); + ) { - kbts_glyph *Matra = &Glyphs[MatraIndex - 1]; + kbts_glyph *MatraPrev = Matra->Prev; - if(Matra->SyllabicPosition == KBTS_SYLLABIC_POSITION_PREBASE_MATRA) + if(Matra->SyllabicPosition == KBTS__SYLLABIC_POSITION_PREBASE_MATRA) { - kbts_glyph MatraToReorder = *Matra; + KBTS__DLLIST_REMOVE(Matra); + KBTS__DLLIST_INSERT_AFTER(Matra, To); - KBTS_FOR(GlyphIndex, MatraIndex, ToIndex + 1) - { - Glyphs[GlyphIndex - 1] = Glyphs[GlyphIndex]; - } - - Glyphs[ToIndex] = MatraToReorder; - ToIndex -= 1; + To = Matra->Prev; } + + Matra = MatraPrev; } } } // Reorder reph. // We should only reorder reph when it is a single Repha glyph, and not a Ra sequence. - if((Glyphs[0].SyllabicPosition == KBTS_SYLLABIC_POSITION_RA_TO_BECOME_REPH) && (Glyphs[1].SyllabicPosition != KBTS_SYLLABIC_POSITION_RA_TO_BECOME_REPH)) + kbts_glyph *First = Storage->GlyphSentinel.Next; + kbts_glyph *Second = First->Next; + if((First->SyllabicPosition == KBTS__SYLLABIC_POSITION_RA_TO_BECOME_REPH) && + (Second->SyllabicPosition != KBTS__SYLLABIC_POSITION_RA_TO_BECOME_REPH)) { - kbts_reph_position RephPosition = Config->IndicScriptProperties.RephPosition; + kbts__reph_position RephPosition = Config->IndicScriptProperties.RephPosition; + kbts_glyph *To = 0; - kbts_un ToIndex = 0; - - KBTS_FOR(GlyphIndex, 0, BaseIndex) + for(kbts_glyph *Glyph = First; + Glyph != BaseGlyph; + ) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; + kbts_glyph *Next = Glyph->Next; if(Glyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) { - ToIndex = GlyphIndex; + To = Glyph; - if((GlyphIndex + 1) < BaseIndex) + if(Next != BaseGlyph) { - kbts_u8 NextClass = Glyphs[GlyphIndex + 1].SyllabicClass; + kbts_u8 NextClass = Next->SyllabicClass; - if((NextClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ) || (NextClass == KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)) + if((NextClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ) || + (NextClass == KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)) { - ToIndex = GlyphIndex + 1; + To = Next; } } break; } + + Glyph = Next; } - if(!ToIndex) + if(!To) { - if(RephPosition == KBTS_REPH_POSITION_AFTER_SUBJOINED) + // @Cleanup: We can merge these loops. + if(RephPosition == KBTS__REPH_POSITION_AFTER_SUBJOINED) { - KBTS_FOR(GlyphIndex, BaseIndex, GlyphCount) + for(kbts_glyph *Glyph = BaseGlyph; + kbts__GlyphIsValid(Storage, Glyph); + Glyph = Glyph->Next) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; kbts_u8 SyllabicPosition = Glyph->SyllabicPosition; - if(KBTS_IN_SET(SyllabicPosition, KBTS_SET32((KBTS_SYLLABIC_POSITION_POSTBASE_CONSONANT)(KBTS_SYLLABIC_POSITION_AFTER_POST)(KBTS_SYLLABIC_POSITION_SMVD)))) + if(KBTS__IN_SET(SyllabicPosition, KBTS__SET32((KBTS__SYLLABIC_POSITION_POSTBASE_CONSONANT) + (KBTS__SYLLABIC_POSITION_AFTER_POST) + (KBTS__SYLLABIC_POSITION_SMVD)))) { - ToIndex = GlyphIndex - 1; + To = Glyph->Prev; break; } } } - else if(RephPosition == KBTS_REPH_POSITION_AFTER_MAIN) + else if(RephPosition == KBTS__REPH_POSITION_AFTER_MAIN) { - KBTS_FOR(GlyphIndex, BaseIndex, GlyphCount) + for(kbts_glyph *Glyph = BaseGlyph; + kbts__GlyphIsValid(Storage, Glyph); + Glyph = Glyph->Next) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - - if(Glyph->SyllabicPosition > KBTS_SYLLABIC_POSITION_AFTER_MAIN) + if(Glyph->SyllabicPosition > KBTS__SYLLABIC_POSITION_AFTER_MAIN) { - ToIndex = GlyphIndex - 1; + To = Glyph->Prev; break; } @@ -20872,121 +24137,112 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt } } - if(!ToIndex) + if(!To) { // As a fallback, move reph to the end of the syllable. - for(kbts_un GlyphIndex = GlyphCount; GlyphIndex; --GlyphIndex) + for(kbts_glyph *Glyph = Storage->GlyphSentinel.Prev; + kbts__GlyphIsValid(Storage, Glyph); + Glyph = Glyph->Prev) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex - 1]; - - if(Glyph->SyllabicPosition != KBTS_SYLLABIC_POSITION_SMVD) + if(Glyph->SyllabicPosition != KBTS__SYLLABIC_POSITION_SMVD) { - ToIndex = GlyphIndex - 1; + To = Glyph; break; } } // Matras will still come after reph. - kbts_glyph *Glyph = &Glyphs[ToIndex]; + kbts_glyph *Glyph = To; if(Glyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) { - KBTS_FOR(MatraIndex, BaseIndex + 1, ToIndex) + for(kbts_glyph *Matra = BaseGlyph->Next; + Matra != To; + Matra = Matra->Next) { - kbts_glyph *Matra = &Glyphs[MatraIndex]; kbts_u8 Class = Matra->SyllabicClass; - if(KBTS_IN_SET(Class, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_MATRA)(KBTS_INDIC_SYLLABIC_CLASS_MATRA_POST)(KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_MEDIAL)))) + if(KBTS__IN_SET(Class, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_MATRA) + (KBTS_INDIC_SYLLABIC_CLASS_MATRA_POST) + (KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_MEDIAL)))) { - ToIndex -= 1; + To = To->Prev; } } } } - if(ToIndex) + if(To) { // Do the reorder! - kbts_glyph Reph = Glyphs[0]; - - KBTS_FOR(GlyphIndex, 0, ToIndex) - { - Glyphs[GlyphIndex] = Glyphs[GlyphIndex + 1]; - } - - Glyphs[ToIndex] = Reph; - - if(BaseIndex && (BaseIndex <= ToIndex)) - { - BaseIndex -= 1; - } + KBTS__DLLIST_REMOVE(First); + KBTS__DLLIST_INSERT_AFTER(First, To); } } // Reorder pre-base-reordering consonants. if(Pref) { - kbts_un PrefIndex = 0; + First = Storage->GlyphSentinel.Next; + kbts_glyph *PrefGlyph = 0; - KBTS_FOR(GlyphIndex, BaseIndex + 1, GlyphCount) + for(kbts_glyph *Glyph = BaseGlyph->Next; + kbts__GlyphIsValid(Storage, Glyph); + Glyph = Glyph->Next) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - if(Glyph->Flags & KBTS_GLYPH_FLAG_PREF) { - PrefIndex = GlyphIndex; + PrefGlyph = Glyph; break; } } - if(PrefIndex) + if(PrefGlyph) { - kbts_glyph PrefGlyph = Glyphs[PrefIndex]; - // Harfbuzz says the ideal is to check that this glyph was generated by the pref feature specifically, // but then just uses a generic flag like this. // The ideal solution would not be very difficult to implement! - if(PrefGlyph.Flags & KBTS_GLYPH_FLAG_GENERATED_BY_GSUB) + if(PrefGlyph->Flags & KBTS_GLYPH_FLAG_GENERATED_BY_GSUB) { - kbts_un ToIndex = 0; + kbts_glyph *To = First; - for(kbts_un GlyphIndex = BaseIndex; GlyphIndex; --GlyphIndex) + kbts_glyph *Last = BaseGlyph; + for(kbts_glyph *Glyph = BaseGlyph->Prev; + kbts__GlyphIsValid(Storage, Glyph); + Glyph = Glyph->Prev) { - kbts_u8 Class = Glyphs[GlyphIndex - 1].SyllabicClass; + kbts_u8 Class = Glyph->SyllabicClass; - if(KBTS_IN_SET(Class, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_MATRA)(KBTS_INDIC_SYLLABIC_CLASS_MATRA_POST)(KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_MEDIAL)(KBTS_INDIC_SYLLABIC_CLASS_HALANT)))) + if(KBTS__IN_SET(Class, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_MATRA) + (KBTS_INDIC_SYLLABIC_CLASS_MATRA_POST) + (KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_MEDIAL) + (KBTS_INDIC_SYLLABIC_CLASS_HALANT)))) { - ToIndex = GlyphIndex; + To = Last; break; } + + Last = Glyph; } - if(ToIndex && (ToIndex < GlyphCount)) + if(To != First) { - kbts_glyph *Prev = &Glyphs[ToIndex - 1]; - kbts_u8 CurrentClass = Glyphs[ToIndex].SyllabicClass; + kbts_glyph *Prev = To->Prev; + kbts_u8 CurrentClass = To->SyllabicClass; - if((Prev->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && KBTS_IN_SET(CurrentClass, KBTS_SET32((KBTS_INDIC_SYLLABIC_CLASS_ZWJ)(KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)))) + if((Prev->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && + KBTS__IN_SET(CurrentClass, KBTS__SET32((KBTS_INDIC_SYLLABIC_CLASS_ZWJ) + (KBTS_INDIC_SYLLABIC_CLASS_ZWNJ)))) { - ToIndex += 1; + To = To->Next; } } // Do the reorder! - for(kbts_un GlyphIndex = PrefIndex; GlyphIndex > ToIndex; --GlyphIndex) - { - Glyphs[GlyphIndex] = Glyphs[GlyphIndex - 1]; - } - - Glyphs[ToIndex] = PrefGlyph; - - // @Cleanup: This is probably always true? - if((BaseIndex >= ToIndex) && (BaseIndex < PrefIndex)) - { - BaseIndex += 1; - } + KBTS__DLLIST_REMOVE(PrefGlyph); + KBTS__DLLIST_INSERT_BEFORE(PrefGlyph, To); } } } @@ -20994,9 +24250,9 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt { // Mark matras at the beginning of a word for init application. // It is tempting to check for this at the same time as the reph reordering, but glyphs could have been // reordered since then and the reordering procedure is clearly defined to happen in sequential steps. - kbts_glyph *First = &Glyphs[0]; + First = Storage->GlyphSentinel.Next; // I don't think this needs to be reloaded, but you never know. - if((First->SyllabicPosition == KBTS_SYLLABIC_POSITION_PREBASE_MATRA) && ShapeState->ClusterAtStartOfWord) + if((First->SyllabicPosition == KBTS__SYLLABIC_POSITION_PREBASE_MATRA) && Scratchpad->ClusterAtStartOfWord) { First->Flags |= KBTS_GLYPH_FLAG_INIT; } @@ -21006,38 +24262,42 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt case KBTS_SHAPER_USE: { - kbts_u64 PostBaseSet = KBTS_SET64((KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_ABOVE)(KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_BELOW) - (KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_MOD_ABOVE)(KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_MOD_BELOW) - (KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_MOD_POST)(KBTS_USE_SYLLABIC_CLASS_CONS_MED_ABOVE) - (KBTS_USE_SYLLABIC_CLASS_CONS_MED_BELOW)(KBTS_USE_SYLLABIC_CLASS_CONS_MED_POST) - (KBTS_USE_SYLLABIC_CLASS_CONS_MED_PRE)(KBTS_USE_SYLLABIC_CLASS_VOWEL_ABOVE) - (KBTS_USE_SYLLABIC_CLASS_VOWEL_BELOW)(KBTS_USE_SYLLABIC_CLASS_VOWEL_POST) - (KBTS_USE_SYLLABIC_CLASS_VOWEL_PRE)(KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_ABOVE) - (KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_BELOW)(KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_POST) + kbts_u64 PostBaseSet = KBTS__SET64((KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_ABOVE) (KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_BELOW) + (KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_MOD_ABOVE) (KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_MOD_BELOW) + (KBTS_USE_SYLLABIC_CLASS_CONS_FINAL_MOD_POST) (KBTS_USE_SYLLABIC_CLASS_CONS_MED_ABOVE) + (KBTS_USE_SYLLABIC_CLASS_CONS_MED_BELOW) (KBTS_USE_SYLLABIC_CLASS_CONS_MED_POST) + (KBTS_USE_SYLLABIC_CLASS_CONS_MED_PRE) (KBTS_USE_SYLLABIC_CLASS_VOWEL_ABOVE) + (KBTS_USE_SYLLABIC_CLASS_VOWEL_BELOW) (KBTS_USE_SYLLABIC_CLASS_VOWEL_POST) + (KBTS_USE_SYLLABIC_CLASS_VOWEL_PRE) (KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_ABOVE) + (KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_BELOW) (KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_POST) (KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_PRE)); - kbts_u64 HalantSet = KBTS_SET64((KBTS_USE_SYLLABIC_CLASS_HALANT)(KBTS_USE_SYLLABIC_CLASS_HALANT_OR_VOWEL_MODIFIER)(KBTS_USE_SYLLABIC_CLASS_INVISIBLE_STACKER)); + kbts_u64 HalantSet = KBTS__SET64((KBTS_USE_SYLLABIC_CLASS_HALANT) + (KBTS_USE_SYLLABIC_CLASS_HALANT_OR_VOWEL_MODIFIER) + (KBTS_USE_SYLLABIC_CLASS_INVISIBLE_STACKER)); // In the reorderings below, we check for halants because that's what Harfbuzz does. // Nowhere does Microsoft mention that halants should block reorderings! - kbts_glyph Repha = Glyphs[0]; - if((Repha.UseClass == KBTS_USE_SYLLABIC_CLASS_REPHA) || (Repha.Flags & KBTS_GLYPH_FLAG_RPHF)) + kbts_glyph *Repha = Storage->GlyphSentinel.Next; + if((Repha->UseClass == KBTS_USE_SYLLABIC_CLASS_REPHA) || + (Repha->Flags & KBTS_GLYPH_FLAG_RPHF)) { - kbts_un GlyphIndex = 1; - for(; GlyphIndex < GlyphCount; ++GlyphIndex) + kbts_glyph *To = Repha->Next; + for(; + kbts__GlyphIsValid(Storage, To); + To = To->Next) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - // Microsoft says that rephas are reordered "after a following full base", which is needlessly vague. // Harfbuzz reorders them in front of the first post-base glyph. - if(KBTS_IN_SET64(Glyph->UseClass, PostBaseSet) || (!(Glyph->Flags & KBTS_GLYPH_FLAG_LIGATURE) && KBTS_IN_SET64(Glyph->UseClass, HalantSet))) + if(KBTS__IN_SET64(To->UseClass, PostBaseSet) || + (!(To->Flags & KBTS_GLYPH_FLAG_LIGATURE) && + KBTS__IN_SET64(To->UseClass, HalantSet))) { break; } - - Glyphs[GlyphIndex - 1] = *Glyph; } - Glyphs[GlyphIndex - 1] = Repha; + KBTS__DLLIST_REMOVE(Repha); + KBTS__DLLIST_INSERT_BEFORE(Repha, To); } { @@ -21045,1770 +24305,2339 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt // and, if present, before a pre-base glyph reordered via the 'pref' feature". // In practice, we tag 'pref'-generated glyphs with the VOWEL_PRE class so that they get reordered here. // Note that the final reordered glyphs end up backwards from their original order. - kbts_glyph PreBaseVowels[16]; // @Robustness: How many vowels can there realistically be in a single cluster? + kbts_glyph *PreBaseVowels[16]; // @Robustness: How many vowels can there realistically be in a single cluster? kbts_un PreBaseVowelCount = 0; kbts_u64 SawBase = 0; - for(kbts_un GlyphIndex = GlyphCount; GlyphIndex; --GlyphIndex) + for(kbts_glyph *Glyph = Storage->GlyphSentinel.Prev; + kbts__GlyphIsValid(Storage, Glyph); + Glyph = Glyph->Prev) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex - 1]; - SawBase |= KBTS_IN_SET64(Glyph->UseClass, KBTS_SET64((KBTS_USE_SYLLABIC_CLASS_BASE)(KBTS_USE_SYLLABIC_CLASS_BASE_OTHER)(KBTS_USE_SYLLABIC_CLASS_BASE_NUM))); + SawBase |= KBTS__IN_SET64(Glyph->UseClass, KBTS__SET64((KBTS_USE_SYLLABIC_CLASS_BASE) + (KBTS_USE_SYLLABIC_CLASS_BASE_OTHER) + (KBTS_USE_SYLLABIC_CLASS_BASE_NUM))); kbts_u32 Flags = Glyph->Flags; // Only accept the first glyphs produced by multiple substitutions. - if(((Flags & KBTS_GLYPH_FLAG_FIRST_IN_MULTIPLE_SUBSTITUTION) || !(Flags & KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION)) && - (KBTS_IN_SET64(Glyph->UseClass, KBTS_SET64((KBTS_USE_SYLLABIC_CLASS_VOWEL_PRE)(KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_PRE))) || - (Flags & KBTS_GLYPH_FLAG_PREF))) + if(((Flags & KBTS_GLYPH_FLAG_FIRST_IN_MULTIPLE_SUBSTITUTION) || + !(Flags & KBTS_GLYPH_FLAG_MULTIPLE_SUBSTITUTION)) && + (KBTS__IN_SET64(Glyph->UseClass, KBTS__SET64((KBTS_USE_SYLLABIC_CLASS_VOWEL_PRE) + (KBTS_USE_SYLLABIC_CLASS_VOWEL_MOD_PRE))) || + (Flags & KBTS_GLYPH_FLAG_PREF))) { - if(PreBaseVowelCount < KBTS_ARRAY_LENGTH(PreBaseVowels)) + if(PreBaseVowelCount < KBTS__ARRAY_LENGTH(PreBaseVowels)) { - PreBaseVowels[PreBaseVowelCount++] = *Glyph; + PreBaseVowels[PreBaseVowelCount++] = Glyph; } } - else if(!(Glyph->Flags & KBTS_GLYPH_FLAG_LIGATURE) && KBTS_IN_SET64(Glyph->UseClass, HalantSet)) + else if(!(Glyph->Flags & KBTS_GLYPH_FLAG_LIGATURE) && + KBTS__IN_SET64(Glyph->UseClass, HalantSet)) { // Reordering stops at halants, apparently. // We want to write the vowels in the reverse order they appear in the glyph sequence. // Since we are iterating backwards, they already _are_ backwards in the PreBaseVowels array, // so there's no need to do anything. - KBTS_FOR(PreBaseVowelIndex, 0, PreBaseVowelCount) + kbts_glyph *InsertAfter = Glyph; + KBTS__FOR(PreBaseVowelIndex, 0, PreBaseVowelCount) { - Glyphs[GlyphIndex + PreBaseVowelIndex] = PreBaseVowels[PreBaseVowelIndex]; + kbts_glyph *PreBaseVowel = PreBaseVowels[PreBaseVowelIndex]; + + KBTS__DLLIST_REMOVE(PreBaseVowel); + KBTS__DLLIST_INSERT_AFTER(PreBaseVowel, InsertAfter); + InsertAfter = PreBaseVowel; } PreBaseVowelCount = 0; // Valid bases only show up before halants. SawBase = 0; } - else + } + + kbts_glyph *DottedCircleInsertBefore = Storage->GlyphSentinel.Next; + + { // Flush the rest of the vowels. + kbts_glyph *InsertAfter = (kbts_glyph *)&Storage->GlyphSentinel; + KBTS__FOR(PreBaseVowelIndex, 0, PreBaseVowelCount) { - Glyphs[GlyphIndex - 1 + PreBaseVowelCount] = *Glyph; + kbts_glyph *PreBaseVowel = PreBaseVowels[PreBaseVowelIndex]; + + KBTS__DLLIST_REMOVE(PreBaseVowel); + KBTS__DLLIST_INSERT_AFTER(PreBaseVowel, InsertAfter); + + InsertAfter = PreBaseVowel; } } - // Flush the rest of the vowels. - KBTS_FOR(PreBaseVowelIndex, 0, PreBaseVowelCount) + if(Scratchpad->RealCluster && !SawBase && Config->DottedCircle.Id) { - Glyphs[PreBaseVowelIndex] = PreBaseVowels[PreBaseVowelIndex]; - } + kbts_glyph *DottedCircle = kbts__InsertGlyphBefore(Storage, DottedCircleInsertBefore, &Config->DottedCircle); + if(!DottedCircle) + { + goto OutOfMemory; + } - // We are supposed to insert a dotted circle after repha when a base is missing. - // The issue is that we have no idea whether the first glyph is a repha or not before we apply - // RPHF. - // So, this is really sucky, but I haven't found a way to merge this logic into any already- - // existing loop, so we have to eat the extra traversal for now. - if(ShapeState->RealCluster && !SawBase) - { - Result.InsertDottedCircle = 1; - Result.DottedCircleIndex = PreBaseVowelCount; + DottedCircle->UserIdOrCodepointIndex = DottedCircleInsertBefore->UserIdOrCodepointIndex; } } } break; case KBTS_SHAPER_MYANMAR: - if(ShapeState->RealCluster) + if(Scratchpad->RealCluster) { - kbts_un OnePastRephIndex = 0; - kbts_un BaseIndex = GlyphCount; - if(GlyphCount >= 3) + kbts_glyph *BaseGlyph = (kbts_glyph *)&Storage->GlyphSentinel; + kbts_glyph *First = BaseGlyph->Next; + kbts_glyph *Second = First->Next; + kbts_glyph *Third = Second->Next; + kbts_glyph *OnePastReph = First; + + kbts_un MinimumGlyphCount = 0; + + if(kbts__GlyphIsValid(Storage, First)) { - kbts_glyph *First = &Glyphs[0]; - kbts_glyph *Second = &Glyphs[1]; - kbts_glyph *Third = &Glyphs[2]; + MinimumGlyphCount += 1; - if((First->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_RA) && - (Second->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ASAT) && - (Third->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT)) + if(kbts__GlyphIsValid(Storage, Second)) { - First->SyllabicPosition = KBTS_SYLLABIC_POSITION_AFTER_MAIN; - Second->SyllabicPosition = KBTS_SYLLABIC_POSITION_AFTER_MAIN; - Third->SyllabicPosition = KBTS_SYLLABIC_POSITION_AFTER_MAIN; + MinimumGlyphCount += 1; - OnePastRephIndex = 3; - BaseIndex = 0; + if(kbts__GlyphIsValid(Storage, Third)) + { + MinimumGlyphCount += 1; + } } } - kbts_u64 BaseSet = KBTS_SET64((KBTS_INDIC_SYLLABIC_CLASS_CONSONANT) + if((MinimumGlyphCount == 3) && + (First->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_RA) && + (Second->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ASAT) && + (Third->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT)) + { + First->SyllabicPosition = KBTS__SYLLABIC_POSITION_AFTER_MAIN; + Second->SyllabicPosition = KBTS__SYLLABIC_POSITION_AFTER_MAIN; + Third->SyllabicPosition = KBTS__SYLLABIC_POSITION_AFTER_MAIN; + + OnePastReph = Third->Next; + BaseGlyph = First; + } + + kbts_u64 BaseSet = KBTS__SET64((KBTS_INDIC_SYLLABIC_CLASS_CONSONANT) (KBTS_INDIC_SYLLABIC_CLASS_CONSONANT_WITH_STACKER) (KBTS_INDIC_SYLLABIC_CLASS_RA) (KBTS_INDIC_SYLLABIC_CLASS_VOWEL) (KBTS_INDIC_SYLLABIC_CLASS_PLACEHOLDER) (KBTS_INDIC_SYLLABIC_CLASS_DOTTED_CIRCLE)); - KBTS_FOR(GlyphIndex, OnePastRephIndex, GlyphCount) + + // Scan for a non-reph base glyph. + for(kbts_glyph *Glyph = OnePastReph; + kbts__GlyphIsValid(Storage, Glyph); + Glyph = Glyph->Next) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; kbts_indic_syllabic_class Class = Glyph->SyllabicClass; - if((BaseIndex == GlyphCount) && KBTS_IN_SET64(Class, BaseSet)) + if(!kbts__GlyphIsValid(Storage, BaseGlyph) && + KBTS__IN_SET64(Class, BaseSet)) { - BaseIndex = GlyphIndex; + BaseGlyph = Glyph; + break; } } - if((BaseIndex == GlyphCount) && OnePastRephIndex) + // Otherwise, reph can be the base. + // @Cleanup: I'm pretty sure this is @Duplication with the reph check above. + if(!kbts__GlyphIsValid(Storage, BaseGlyph) && + (OnePastReph != First)) { - BaseIndex = 0; + BaseGlyph = First; } - kbts_syllabic_position LastPosition = KBTS_SYLLABIC_POSITION_SYLLABLE_BASE; - kbts_syllabic_position SectionPosition = KBTS_SYLLABIC_POSITION_AFTER_MAIN; - kbts_un LastPreBaseMatraIndex = GlyphCount; - KBTS_FOR(GlyphIndex, BaseIndex + 1, GlyphCount) + if(kbts__GlyphIsValid(Storage, BaseGlyph)) { - kbts_glyph *Glyph = &Glyphs[GlyphIndex]; - kbts_syllabic_position Position = SectionPosition; - - switch(Glyph->SyllabicClass) + kbts__syllabic_position LastPosition = KBTS__SYLLABIC_POSITION_SYLLABLE_BASE; + kbts__syllabic_position SectionPosition = KBTS__SYLLABIC_POSITION_AFTER_MAIN; + kbts_glyph *LastPreBaseMatra = (kbts_glyph *)&Storage->GlyphSentinel; + for(kbts_glyph *Glyph = BaseGlyph->Next; + kbts__GlyphIsValid(Storage, Glyph); + ) { - case KBTS_INDIC_SYLLABIC_CLASS_MEDIAL_RA: - Position = KBTS_SYLLABIC_POSITION_PREBASE_CONSONANT; - break; + kbts_glyph *Next = Glyph->Next; // Make sure we preserve the link when reversing. + kbts__syllabic_position Position = SectionPosition; - case KBTS_INDIC_SYLLABIC_CLASS_VOWEL_PRE: - { - Position = KBTS_SYLLABIC_POSITION_PREBASE_MATRA; - - // According to Unicode 15.0: - // For combining characters placed to the left of a base character, the combining characters start from the base - // character and are arranged leftward. - // Since these guys will get reordered into being pre-base, we need to flip them. - if(GlyphIndex > LastPreBaseMatraIndex) + switch(Glyph->SyllabicClass) { - kbts_un FlipCount = GlyphIndex - LastPreBaseMatraIndex + 1; - KBTS_FOR(FlipIndex, 0, FlipCount / 2) + case KBTS_INDIC_SYLLABIC_CLASS_MEDIAL_RA: + Position = KBTS__SYLLABIC_POSITION_PREBASE_CONSONANT; + break; + + case KBTS_INDIC_SYLLABIC_CLASS_VOWEL_PRE: + { + Position = KBTS__SYLLABIC_POSITION_PREBASE_MATRA; + + // According to Unicode 15.0: + // For combining characters placed to the left of a base character, the combining characters start from the base + // character and are arranged leftward. + // Since these guys will get reordered into being pre-base, we need to flip them. + if(kbts__GlyphIsValid(Storage, LastPreBaseMatra)) { - kbts_un LeftIndex = LastPreBaseMatraIndex + FlipIndex; - kbts_un RightIndex = LastPreBaseMatraIndex + FlipCount - 1 - FlipIndex; - kbts_glyph Swap = Glyphs[LeftIndex]; - Glyphs[LeftIndex] = Glyphs[RightIndex]; - Glyphs[RightIndex] = Swap; + kbts__DllistReverseSublist(LastPreBaseMatra, Glyph->Next); } + else + { + // When we reverse, this will become the "last" pre-base matra again, so we only need to set it once. + LastPreBaseMatra = Glyph; + } + } break; - // Readjust Glyph to compensate for the flip. - Glyph = &Glyphs[LastPreBaseMatraIndex]; + case KBTS_INDIC_SYLLABIC_CLASS_VARIATION_SELECTOR: + Position = LastPosition; + break; + + case KBTS_INDIC_SYLLABIC_CLASS_VOWEL_BELOW: + if(Position == KBTS__SYLLABIC_POSITION_AFTER_MAIN) + { + Position = KBTS__SYLLABIC_POSITION_BELOWBASE_CONSONANT; + } + else if(Position == KBTS__SYLLABIC_POSITION_BELOWBASE_CONSONANT) + { + Position = SectionPosition; + } + break; + + case KBTS_INDIC_SYLLABIC_CLASS_VEDIC_SIGN: + if(Position == KBTS__SYLLABIC_POSITION_BELOWBASE_CONSONANT) + { + Position = KBTS__SYLLABIC_POSITION_BEFORE_SUBJOINED; + } + break; } - LastPreBaseMatraIndex = GlyphIndex; - } break; - - case KBTS_INDIC_SYLLABIC_CLASS_VARIATION_SELECTOR: - Position = LastPosition; - break; - - case KBTS_INDIC_SYLLABIC_CLASS_VOWEL_BELOW: - if(Position == KBTS_SYLLABIC_POSITION_AFTER_MAIN) - { - Position = KBTS_SYLLABIC_POSITION_BELOWBASE_CONSONANT; - } - else if(Position == KBTS_SYLLABIC_POSITION_BELOWBASE_CONSONANT) + if((SectionPosition == KBTS__SYLLABIC_POSITION_BELOWBASE_CONSONANT) && (Glyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_VEDIC_SIGN)) { + SectionPosition = KBTS__SYLLABIC_POSITION_AFTER_SUBJOINED; Position = SectionPosition; } - break; - case KBTS_INDIC_SYLLABIC_CLASS_VEDIC_SIGN: - if(Position == KBTS_SYLLABIC_POSITION_BELOWBASE_CONSONANT) - { - Position = KBTS_SYLLABIC_POSITION_BEFORE_SUBJOINED; - } - break; + Glyph->SyllabicPosition = Position; + LastPosition = Position; + Glyph = Next; } - - if((SectionPosition == KBTS_SYLLABIC_POSITION_BELOWBASE_CONSONANT) && (Glyph->SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_VEDIC_SIGN)) - { - SectionPosition = KBTS_SYLLABIC_POSITION_AFTER_SUBJOINED; - Position = SectionPosition; - } - - Glyph->SyllabicPosition = Position; - LastPosition = Position; } - KBTS_FOR(Iter, 1, GlyphCount) + if(MinimumGlyphCount > 1) { - KBTS_FOR(GlyphIndex, 1, GlyphCount) - { - kbts_glyph *A = &Glyphs[GlyphIndex - 1]; - kbts_glyph *B = &Glyphs[GlyphIndex]; + KBTS__DLLIST_SORT(Storage->GlyphSentinel.Next, (kbts_glyph *)&Storage->GlyphSentinel, SyllabicPosition); - if(A->SyllabicPosition > B->SyllabicPosition) + #ifdef KBTS_SANITY_CHECK + { + kbts_glyph *Left = C->GlyphSentinel.Next; + kbts_glyph *Right = Left->Next; + while(kbts__GlyphIsValid(C, Right)) { - kbts_glyph Swap = *A; - *A = *B; - *B = Swap; + kbts_glyph *Next = Right->Next; + + KBTS_ASSERT(Left->SyllabicPosition <= Right->SyllabicPosition); + + Left = Right; + Right = Next; } } + #endif } } break; } - return Result; -} - -static kbts_op_list kbts_OpList(kbts_u8 *Ops, kbts_un Size) -{ - kbts_op_list Result = KBTS_ZERO; - Result.Ops = Ops; - Result.Length = Size; - return Result; -} -#define KBTS_OP_LIST(OpList) kbts_OpList((kbts_Ops_##OpList), KBTS_ARRAY_LENGTH(kbts_Ops_##OpList)) - -KBTS_EXPORT kbts_shape_config kbts_ShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language) -{ - kbts_shape_config Result = KBTS_ZERO; - - Result.Font = Font; - Result.Script = Script; - Result.Language = Language; - - kbts_gsub_gpos *Gsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; - - // Find the appropriate language system in GSUB/GPOS. - kbts_script_properties *ScriptProperties = &kbts_ScriptProperties[Script]; - int FoundScriptIsIndic3 = 0; - - KBTS_FOR(ShapingTableIndex, 0, KBTS_SHAPING_TABLE_COUNT) + if(0) { - kbts_gsub_gpos *ShapingTable = Font->ShapingTables[ShapingTableIndex]; - if(ShapingTable) + OutOfMemory:; + Scratchpad->Error = KBTS_SHAPE_ERROR_OUT_OF_MEMORY; + } +} + +static void *kbts__PointerPush(char **Pointer, kbts_un Size, kbts_un Align) +{ + char *At = *Pointer; + char *Aligned = KBTS__ALIGN_POINTER(char, At, Align); + *Pointer = Aligned + Size; + + void *Result = Aligned; + return Result; +} +#define kbts__PointerPushType(Pointer, Type) (Type *)kbts__PointerPush((Pointer), sizeof(Type), KBTS_ALIGNOF(Type)) +#define kbts__PointerPushArray(Pointer, Type, Count) (Type *)kbts__PointerPush((Pointer), sizeof(Type) * (Count), KBTS_ALIGNOF(Type)) + +static kbts_shape_config *kbts__PlaceShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language, void *Memory, kbts_un *Size) +{ + kbts_shape_config *Result = 0; + kbts_shape_config DummyConfig; + char *MemoryAt = (char *)Memory; + + if(Font) + { + Result = kbts__PointerPushType(&MemoryAt, kbts_shape_config); + if(!Memory) { - kbts_script_list *ScriptList = KBTS_POINTER_OFFSET(kbts_script_list, ShapingTable, ShapingTable->ScriptListOffset); + Result = &DummyConfig; + } - kbts_langsys *ChosenLangsys = 0; - kbts_u32 DesiredTag = ScriptProperties->Tag; + KBTS_MEMSET(Result, 0, sizeof(*Result)); - KBTS_FOR(ScriptIndex, 0, ScriptList->Count) + Result->Font = Font; + Result->Script = Script; + Result->Language = Language; + + kbts__gsub_gpos *ShapingTables[2] = { + kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_GSUB, kbts__gsub_gpos), + kbts__BlobTableDataType(Font->Blob, KBTS_BLOB_TABLE_ID_GPOS, kbts__gsub_gpos), + }; + + kbts__gsub_gpos *Gsub = ShapingTables[0]; + + // Find the appropriate language system in GSUB/GPOS. + kbts__script_properties *ScriptProperties = &kbts__ScriptProperties[Script]; + int FoundScriptIsIndic3 = 0; + + KBTS__FOR(ShapingTableIndex, 0, KBTS_SHAPING_TABLE_COUNT) + { + kbts__gsub_gpos *ShapingTable = ShapingTables[ShapingTableIndex]; + if(ShapingTable) { - kbts_script_pointer ThisScript = kbts_GetScript(ScriptList, ScriptIndex); - kbts_u32 Tag = ThisScript.Tag; + kbts__script_list *ScriptList = KBTS__POINTER_OFFSET(kbts__script_list, ShapingTable, ShapingTable->ScriptListOffset); - // The OpenType spec does not define or even mention "Indic3" scripts at all. - // Nevertheless, Harfbuzz at least checks for '3' at the end of script tags, and, if one exists, they choose USE. - int Indic3 = (ThisScript.Tag >> 24) == '3'; - kbts_u32 MatchMask = Indic3 ? 0xFFFFFF : 0xFFFFFFFF; - int PerfectMatch = !((Tag ^ DesiredTag) & MatchMask); - if(!ScriptIndex || PerfectMatch || (Tag == KBTS_FOURCC('D', 'F', 'L', 'T'))) + kbts__langsys *ChosenLangsys = 0; + kbts_u32 DesiredTag = ScriptProperties->Tag; + + KBTS__FOR(ScriptIndex, 0, ScriptList->Count) { - kbts_langsys *Langsys = kbts_GetDefaultLangsys(ThisScript.Script); - KBTS_FOR(LangsysIndex, 0, ThisScript.Script->Count) - { - kbts_langsys_pointer LangsysPointer = kbts_GetLangsys(ThisScript.Script, LangsysIndex); + kbts__script_pointer ThisScript = kbts__GetScript(ScriptList, ScriptIndex); + kbts_u32 Tag = ThisScript.Tag; - if(LangsysPointer.Tag == Language) + // The OpenType spec does not define or even mention "Indic3" scripts at all. + // Nevertheless, Harfbuzz at least checks for '3' at the end of script tags, and, if one exists, they choose USE. + int Indic3 = (ThisScript.Tag >> 24) == '3'; + kbts_u32 MatchMask = Indic3 ? 0xFFFFFF : 0xFFFFFFFF; + int PerfectMatch = !((Tag ^ DesiredTag) & MatchMask); + if(!ScriptIndex || PerfectMatch || (Tag == KBTS_FOURCC('D', 'F', 'L', 'T'))) + { + kbts__langsys *Langsys = kbts__GetDefaultLangsys(ThisScript.Script); + KBTS__FOR(LangsysIndex, 0, ThisScript.Script->Count) + { + kbts__langsys_pointer LangsysPointer = kbts__GetLangsys(ThisScript.Script, LangsysIndex); + + if(LangsysPointer.Tag == Language) + { + Langsys = LangsysPointer.Langsys; + break; + } + } + + // It is tempting to try to look for another script if the one we want has no langsys. + // However, it is possible for a script to purposefully have no langsys at all. In that case, + // the shaper should not apply any GSUB features. + // In that case, the shaper should not apply any GSUB features. + // So, store the result _regardless_ of whether Langsys is null or not. + ChosenLangsys = Langsys; + if(ShapingTableIndex == KBTS_SHAPING_TABLE_GSUB) + { + // Apparently, it is allowed to have script-specific GSUB tables, and a huge DFLT GPOS table. + FoundScriptIsIndic3 = Indic3; + } + // And then get out if we found the appropriate script, even if the langsys is null! + if(PerfectMatch) { - Langsys = LangsysPointer.Langsys; break; } } + } - // It is tempting to try to look for another script if the one we want has no langsys. - // However, it is possible for a script to purposefully have no langsys at all. In that case, - // the shaper should not apply any GSUB features. - // In that case, the shaper should not apply any GSUB features. - // So, store the result _regardless_ of whether Langsys is null or not. - ChosenLangsys = Langsys; - if(ShapingTableIndex == KBTS_SHAPING_TABLE_GSUB) + Result->Langsys[ShapingTableIndex] = ChosenLangsys; + } + } + + Result->IndicScriptProperties = kbts__IndicScriptProperties(Script); + Result->Shaper = FoundScriptIsIndic3 ? KBTS_SHAPER_USE : ScriptProperties->Shaper; + Result->OpList = *kbts__ShaperOpLists[Result->Shaper]; + + Result->Features = KBTS__ZERO_TYPE(kbts__feature_set); + KBTS__FOR(StageIndex, 0, Result->OpList.FeatureStageCount) + { + kbts__feature_stage *Stage = &Result->OpList.FeatureStages[StageIndex]; + + KBTS__FOR(WordIndex, 0, KBTS__ARRAY_LENGTH(Result->Features.Flags)) + { + Result->Features.Flags[WordIndex] |= Stage->Features.Flags[WordIndex]; + } + } + + kbts__feature *Rclt = 0; + kbts__feature_set SyllableFeatureSet = {{KBTS__FEATURE_FLAG0(rphf) | KBTS__FEATURE_FLAG0(blwf) | KBTS__FEATURE_FLAG0(half) | KBTS__FEATURE_FLAG0(pstf) | KBTS__FEATURE_FLAG0(pref), + 0, KBTS__FEATURE_FLAG2(rclt) | KBTS__FEATURE_FLAG2(locl), KBTS__FEATURE_FLAG3(vatu)}}; + kbts__iterate_features IterateFeatures = kbts__IterateFeatures(Result, KBTS_SHAPING_TABLE_GSUB, SyllableFeatureSet); + while(kbts__NextFeature(&IterateFeatures)) + { + switch(IterateFeatures.CurrentFeatureTag) + { + case KBTS_FEATURE_TAG_blwf: Result->Blwf = IterateFeatures.Feature; break; + case KBTS_FEATURE_TAG_pref: Result->Pref = IterateFeatures.Feature; break; + case KBTS_FEATURE_TAG_pstf: Result->Pstf = IterateFeatures.Feature; break; + case KBTS_FEATURE_TAG_locl: Result->Locl = IterateFeatures.Feature; break; + case KBTS_FEATURE_TAG_rphf: Result->Rphf = IterateFeatures.Feature; break; + case KBTS_FEATURE_TAG_half: Result->Half = IterateFeatures.Feature; break; + case KBTS_FEATURE_TAG_vatu: Result->Vatu = IterateFeatures.Feature; break; + case KBTS_FEATURE_TAG_rclt: Rclt = IterateFeatures.Feature; break; + } + } + + if((Result->Shaper == KBTS_SHAPER_ARABIC) && !Rclt) + { + Result->OpList = kbts__OpList_ArabicNoRclt; + } + + if(Result->IndicScriptProperties.ViramaCodepoint) + { + kbts__shape_scratchpad DummyScratchpad = KBTS__ZERO; + kbts_glyph_storage DummyStorage = KBTS__ZERO; + KBTS__DLLIST_SENTINEL_INIT(&DummyStorage.GlyphSentinel); + KBTS__DLLIST_SENTINEL_INIT(&DummyStorage.FreeGlyphSentinel); + + // Bake the locl-ized virama. + kbts_glyph Virama = kbts_CodepointToGlyph(Font, (int)Result->IndicScriptProperties.ViramaCodepoint, 0, 0); + Result->Virama = kbts__Substitute1(&DummyScratchpad, Result, &DummyStorage, kbts__GetLookupList(Gsub), Result->Locl, KBTS__SKIP_FLAG_ZWNJ | KBTS__SKIP_FLAG_ZWJ, &Virama); + } + + if((Result->Script == KBTS_SCRIPT_THAI) || (Result->Script == KBTS_SCRIPT_LAO)) + { + kbts_u32 NikhahitCodepoint = (Result->Script == KBTS_SCRIPT_THAI) ? 0xE4D : 0xECD; + kbts_u32 SaraAaCodepoint = (Result->Script == KBTS_SCRIPT_THAI) ? 0xE32 : 0xEB2; + Result->Nikhahit = kbts_CodepointToGlyph(Font, (int)NikhahitCodepoint, 0, 0); + Result->SaraAa = kbts_CodepointToGlyph(Font, (int)SaraAaCodepoint, 0, 0); + } + + Result->DottedCircle = kbts_CodepointToGlyph(Font, 0x25CC, 0, 0); + Result->Whitespace = kbts_CodepointToGlyph(Font, ' ', 0, 0); + + if(ShapingTables[KBTS_SHAPING_TABLE_GSUB] || ShapingTables[KBTS_SHAPING_TABLE_GPOS]) + { // Initialize the per-feature lookup lists. + kbts__baked_feature_stage *BakedStages = kbts__PointerPushArray(&MemoryAt, kbts__baked_feature_stage, Result->OpList.FeatureStageCount); + + kbts_un FeatureStageIndex = 0; + KBTS__FOR(OpIndex, 0, Result->OpList.OpCount) + { + kbts__op_kind Op = Result->OpList.Ops[OpIndex]; + int UserFeaturesAllowed = KBTS__IN_SET(Op, KBTS__SET32((KBTS__OP_KIND_GSUB_FEATURES_WITH_USER) + (KBTS__OP_KIND_GPOS_FEATURES))); + + if(KBTS__IN_SET(Op, KBTS__SET32((KBTS__OP_KIND_GSUB_FEATURES) + (KBTS__OP_KIND_GSUB_FEATURES_WITH_USER) + (KBTS__OP_KIND_GPOS_FEATURES)))) + { + kbts__feature_stage *FeatureStage = &Result->OpList.FeatureStages[FeatureStageIndex]; + kbts_shaping_table ShapingTable = (kbts_shaping_table)((Op == KBTS__OP_KIND_GPOS_FEATURES) ? KBTS_SHAPING_TABLE_GPOS : KBTS_SHAPING_TABLE_GSUB); + kbts__gsub_gpos *GsubGpos = ShapingTables[ShapingTable]; + kbts__langsys *Langsys = Result->Langsys[ShapingTable]; + kbts_un BakedFeatureCount = 0; + kbts_un BakedFeatureLookupIndexCount = 0; + + kbts__baked_feature_stage *BakedStage = &BakedStages[FeatureStageIndex]; + + if(GsubGpos && Langsys) { - // Apparently, it is allowed to have script-specific GSUB tables, and a huge DFLT GPOS table. - FoundScriptIsIndic3 = Indic3; + kbts__feature_list *FeatureList = KBTS__POINTER_OFFSET(kbts__feature_list, GsubGpos, GsubGpos->FeatureListOffset); + kbts_u16 *FeatureIndices = KBTS__POINTER_AFTER(kbts_u16, Langsys); + kbts__baked_feature *BakedFeatures; + + // @Speed: Maybe we don't care about fragmentation and we just allocate Langsys->FeatureIndexCount in advance? + KBTS__FOR(FeatureIndexIndex, 0, Langsys->FeatureIndexCount) + { + kbts_un FeatureIndex = FeatureIndices[FeatureIndexIndex]; + kbts__feature_pointer Feature = kbts__GetFeature(FeatureList, FeatureIndex); + + kbts_u32 FeatureId = kbts__FeatureTagToId(Feature.Tag); + + if(Feature.Feature->LookupIndexCount && + ((UserFeaturesAllowed && + !kbts__ContainsFeature(&Result->Features, FeatureId)) || + kbts__ContainsFeature(&FeatureStage->Features, FeatureId))) + { + BakedFeatureCount += 1; + } + } + + kbts_un BakedFeatureCapacity = KBTS__MIN(BakedFeatureCount, KBTS_MAX_SIMULTANEOUS_FEATURES); + + BakedFeatures = kbts__PointerPushArray(&MemoryAt, kbts__baked_feature, BakedFeatureCapacity); + + BakedFeatureCount = 0; + + KBTS__FOR(FeatureIndexIndex, 0, Langsys->FeatureIndexCount) + { + kbts_un FeatureIndex = FeatureIndices[FeatureIndexIndex]; + kbts__feature_pointer Feature = kbts__GetFeature(FeatureList, FeatureIndex); + + kbts_u32 FeatureId = kbts__FeatureTagToId(Feature.Tag); + // We add all features indiscriminately for ops that might incorporate user features. + if(Feature.Feature->LookupIndexCount && + // We add "all" features to user feature stages, except for features that are a default part of the shaper. + // If a user asks for a default feature explicitly, it is unclear whether it should be applied now or in its + // default stage. + // Leaving the feature in its default stage seems like the better thing to do, because, supposedly, these features + // will have been designed to apply at a specific point in the pipeline, and moving them makes little sense. + ((UserFeaturesAllowed && + !kbts__ContainsFeature(&Result->Features, FeatureId)) || + kbts__ContainsFeature(&FeatureStage->Features, FeatureId))) + { + kbts__baked_feature BakedFeature = KBTS__ZERO; + BakedFeature.FeatureTag = Feature.Tag; + BakedFeature.FeatureId = FeatureId; + // CAREFUL: We use SkipFlags as a temporary index until the end of the stage. + BakedFeature.SkipFlags = kbts__SkipFlags(BakedFeature.FeatureId, Result->Shaper); + BakedFeature.Count = Feature.Feature->LookupIndexCount; + // These point directly into the file. + BakedFeature.Indices = KBTS__POINTER_AFTER(kbts_u16, Feature.Feature); + + // @Incomplete + //if(FeatureVariations) + //{ + // KBTS__FOR(VariationIndex, 0, FeatureVariations->RecordCount) + // { + // kbts__feature_variation_pointer Variation = kbts__GetFeatureVariation(FeatureVariations, VariationIndex); + // KBTS__FOR(ConditionIndex, 0, Variation.ConditionSet->Count) + // { + // kbts__condition_1 *Condition = kbts__GetCondition(Variation.ConditionSet, ConditionIndex); + // KBTS_ASSERT(0); + // } + // } + //} + + // For Myanmar, we could try and tag glyphs depending on their Indic properties in BeginCluster, just like we do for + // Indic scripts. + // However, Harfbuzz does _not_ do this, so it seems like a bunch of work that would, at best, make us diverge from + // Harfbuzz more often. + if((Result->Shaper != KBTS_SHAPER_MYANMAR) && (FeatureId >= 1) && (FeatureId <= 32)) + { + // These must properly map KBTS__FEATURE_ID to kbts_glyph_flags! + BakedFeature.GlyphFilter = (1 << (FeatureId - 1)) & KBTS__GLYPH_FEATURE_MASK; + } + + if(Memory) + { + BakedFeatures[BakedFeatureCount] = BakedFeature; + } + + BakedFeatureCount += 1; + BakedFeatureLookupIndexCount += BakedFeature.Count; + + if(BakedFeatureCount >= BakedFeatureCapacity) + { + break; + } + } + } + + kbts_u16 *OrderedFeatureIndices = kbts__PointerPushArray(&MemoryAt, kbts_u16, BakedFeatureLookupIndexCount); + kbts_u16 *BakedFeatureLookupIndicesRead = kbts__PointerPushArray(&MemoryAt, kbts_u16, BakedFeatureCount); + + if(Memory) + { + kbts_un OrderedFeatureIndicesWritten = 0; + + KBTS__FOR(BakedFeatureIndex, 0, BakedFeatureCount) + { + BakedFeatureLookupIndicesRead[BakedFeatureIndex] = 0; + } + + KBTS__FOR(OrderedLookupIndex, 0, BakedFeatureLookupIndexCount) + { + kbts_u16 LowestLookupIndex = 0xFFFF; + kbts_un BestBakedFeatureIndex = 0; + + KBTS__FOR(BakedFeatureIndex, 0, BakedFeatureCount) + { + kbts__baked_feature *BakedFeature = &BakedFeatures[BakedFeatureIndex]; + kbts_un LookupIndicesRead = BakedFeatureLookupIndicesRead[BakedFeatureIndex]; + + if(LookupIndicesRead < BakedFeature->Count) + { + kbts_u16 NextIndex = BakedFeature->Indices[LookupIndicesRead]; + + if(NextIndex < LowestLookupIndex) + { + LowestLookupIndex = NextIndex; + BestBakedFeatureIndex = BakedFeatureIndex; + } + } + } + + BakedFeatureLookupIndicesRead[BestBakedFeatureIndex] += 1; + OrderedFeatureIndices[OrderedFeatureIndicesWritten++] = (kbts_u16)BestBakedFeatureIndex; + } + + KBTS_ASSERT(OrderedFeatureIndicesWritten == BakedFeatureLookupIndexCount); + + BakedStage->FeatureCount = (kbts_u16)BakedFeatureCount; + BakedStage->FeatureIndexCount = (kbts_u16)BakedFeatureLookupIndexCount; + BakedStage->Features = BakedFeatures; + BakedStage->FeatureIndices = OrderedFeatureIndices; + } } - // And then get out if we found the appropriate script, even if the langsys is null! - if(PerfectMatch) + + KBTS_ASSERT(FeatureStageIndex < Result->OpList.FeatureStageCount); + FeatureStageIndex += 1; + } + } + + Result->FeatureStages = BakedStages; + } + } + + if(!Memory) + { + *Size = (kbts_un)(MemoryAt - (char *)Memory) + KBTS_ALIGNOF(kbts_shape_config); + Result = 0; + } + + return Result; +} + +KBTS_EXPORT int kbts_SizeOfShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language) +{ + kbts_un Size; + kbts__PlaceShapeConfig(Font, Script, Language, 0, &Size); + + return (int)Size; +} + +KBTS_EXPORT kbts_shape_config *kbts_PlaceShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language, void *Memory) +{ + kbts_un Size; + kbts_shape_config *Result = kbts__PlaceShapeConfig(Font, Script, Language, Memory, &Size); + return Result; +} + +KBTS_EXPORT kbts_shape_config *kbts_CreateShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language, kbts_allocator_function *Allocator, void *AllocatorData) +{ + if(!Allocator) + { + Allocator = kbts__DefaultAllocator; + } + + kbts_un Size; + kbts__PlaceShapeConfig(Font, Script, Language, 0, &Size); + kbts_shape_config *Result = kbts__PlaceShapeConfig(Font, Script, Language, kbts__AllocatorAllocate(Allocator, AllocatorData, Size), &Size); + if(Result) + { + Result->Allocator = Allocator; + Result->AllocatorData = AllocatorData; + } + + return Result; +} + +KBTS_EXPORT void kbts_DestroyShapeConfig(kbts_shape_config *Config) +{ + if(Config->Allocator) + { + kbts__AllocatorFree(Config->Allocator, Config->AllocatorData, Config); + } +} + +static int kbts__ReadOp(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts__op_kind End) +{ + KBTS_INSTRUMENT_FUNCTION_BEGIN; + int Result = 0; + + if(Config) + { + kbts__op_list *OpList = &Config->OpList; + + if(Scratchpad->Ip < OpList->OpCount) + { + kbts__op_kind Kind = OpList->Ops[Scratchpad->Ip++]; + + if((Kind == KBTS__OP_KIND_GSUB_FEATURES_WITH_USER) || + (Kind == KBTS__OP_KIND_GSUB_FEATURES) || + (Kind == KBTS__OP_KIND_GPOS_FEATURES)) + { + kbts__feature_stage *FeatureStage = &OpList->FeatureStages[Scratchpad->FeatureStagesRead++]; + Scratchpad->OpFeatures = &FeatureStage->Features; + + // We only have one GPOS_FEATURES op per op list, so it's fine to add user features like this. + if((Kind == KBTS__OP_KIND_GSUB_FEATURES_WITH_USER) || + (Kind == KBTS__OP_KIND_GPOS_FEATURES)) + { + if(Kind == KBTS__OP_KIND_GSUB_FEATURES_WITH_USER) { + Kind = KBTS__OP_KIND_GSUB_FEATURES; + } + + KBTS__FOR(WordIndex, 0, KBTS__ARRAY_LENGTH(Scratchpad->ScratchFeatures.Flags)) + { + Scratchpad->ScratchFeatures.Flags[WordIndex] = Scratchpad->UserFeatures.Flags[WordIndex] | FeatureStage->Features.Flags[WordIndex]; + } + + Scratchpad->OpFeatures = &Scratchpad->ScratchFeatures; + } + } + + Scratchpad->OpKind = Kind; + Result = (Kind != End); + } + } + + KBTS_INSTRUMENT_FUNCTION_END; + return Result; +} + +KBTS_EXPORT int kbts_SizeOfShapeContext(void) +{ + int Result = sizeof(kbts_shape_context); + return Result; +} + +KBTS_EXPORT kbts_shape_context *kbts_PlaceShapeContext(kbts_allocator_function *Allocator, void *AllocatorData, void *Memory) +{ + kbts_shape_context *Result = (kbts_shape_context *)Memory; + + if(Memory) + { + if(!Allocator) + { + Allocator = kbts__DefaultAllocator; + } + + KBTS_MEMSET(Result, 0, sizeof(*Result)); + + Result->FontArena.Allocator = Allocator; + Result->FontArena.AllocatorData = AllocatorData; + + Result->ConfigArena.Allocator = Allocator; + Result->ConfigArena.AllocatorData = AllocatorData; + + Result->ScratchArena.Allocator = Allocator; + Result->ScratchArena.AllocatorData = AllocatorData; + + KBTS__DLLIST_SENTINEL_INIT(&Result->FeatureOverrideSentinel); + KBTS__DLLIST_SENTINEL_INIT(&Result->FreeFeatureOverrideSentinel); + } + + return Result; +} + +KBTS_EXPORT kbts_shape_context *kbts_PlaceShapeContextFixedMemory(void *Memory, int Size) +{ + kbts_shape_context *Result = 0; + kbts_un SizeOfShapeContext = kbts_SizeOfShapeContext(); + kbts_un ContextAndArenaSize = sizeof(kbts_arena) + SizeOfShapeContext; + + if((Size >= 0) && ((kbts_un)Size >= ContextAndArenaSize)) + { + kbts_arena *Arena = (kbts_arena *)KBTS__POINTER_OFFSET(kbts_arena, Memory, SizeOfShapeContext); + kbts__InitializeFixedMemoryArena(Arena, KBTS__POINTER_AFTER(void, Arena), (kbts_un)Size - ContextAndArenaSize); + + Result = kbts_PlaceShapeContext(kbts__ArenaAllocator, Arena, Memory); + } + + return Result; +} + +KBTS_EXPORT kbts_shape_context *kbts_CreateShapeContext(kbts_allocator_function *Allocator, void *AllocatorData) +{ + if(!Allocator) + { + Allocator = kbts__DefaultAllocator; + } + + kbts_shape_context *Result = kbts_PlaceShapeContext(Allocator, AllocatorData, kbts__AllocatorAllocate(Allocator, AllocatorData, (kbts_un)kbts_SizeOfShapeContext())); + Result->SelfAllocator = Allocator; + Result->SelfAllocatorData = AllocatorData; + return Result; +} + +KBTS_EXPORT void kbts_DestroyShapeContext(kbts_shape_context *Context) +{ + if(Context) + { + kbts__FreeArena(&Context->ConfigArena); + kbts__FreeArena(&Context->ScratchArena); + kbts__FreeArena(&Context->FontArena); + kbts__FreeArena(&Context->GlyphStorage.Arena); + + if(Context->SelfAllocator) + { + kbts__AllocatorFree(Context->SelfAllocator, Context->SelfAllocatorData, Context); + } + } +} + +KBTS_EXPORT kbts_direction kbts_ScriptDirection(kbts_script Script) +{ + kbts_direction Result = ((Script == KBTS_SCRIPT_ARABIC) || (Script == KBTS_SCRIPT_HEBREW)) ? KBTS_DIRECTION_RTL : KBTS_DIRECTION_LTR; + return Result; +} + +static kbts__context_font *kbts__ShapePushFont(kbts_shape_context *Context) +{ + kbts__context_font *Result = 0; + + if(!Context->Error && (Context->FontCount < KBTS_CONTEXT_MAX_FONT_COUNT)) + { + Result = &Context->Fonts[Context->FontCount++]; + Result->Font = 0; + Result->Lifetime = kbts__BeginLifetime(&Context->FontArena); + } + + return Result; +} + +KBTS_EXPORT kbts_font *kbts_ShapePushFont(kbts_shape_context *Context, kbts_font *Font) +{ + if(!Context->Error) + { + kbts__context_font *ContextFont = kbts__ShapePushFont(Context); + + if(ContextFont) + { + ContextFont->Font = Font; + } + } + + return Font; +} + +KBTS_EXPORT kbts_font *kbts_ShapePopFont(kbts_shape_context *Context) +{ + kbts_font *Result = 0; + + if(!Context->Error && Context->FontCount) + { + kbts__context_font *ContextFont = &Context->Fonts[Context->FontCount - 1]; + Result = ContextFont->Font; + + kbts__EndLifetime(&ContextFont->Lifetime); + + Context->FontCount -= 1; + } + + return Result; +} + +#ifndef KB_TEXT_SHAPE_NO_CRT +KBTS_EXPORT kbts_font *kbts_ShapePushFontFromFile(kbts_shape_context *Context, const char *FileName, int FontIndex) +{ + kbts__context_font *ContextFont = kbts__ShapePushFont(Context); + kbts_font *Result = 0; + + if(!Context->Error) + { + Result = kbts__PushType(&Context->FontArena, kbts_font); + if(Result) + { + *Result = kbts_FontFromFile(FileName, FontIndex, kbts__ArenaAllocator, &Context->FontArena, 0, 0); + + if(!Result->Error) + { + ContextFont->Font = Result; + } + else + { + kbts_ShapePopFont(Context); + Result = 0; + } + } + else + { + Context->Error = KBTS_SHAPE_ERROR_OUT_OF_MEMORY; + } + } + + return Result; +} +#endif + +KBTS_EXPORT kbts_font *kbts_ShapePushFontFromMemory(kbts_shape_context *Context, void *Memory, int Length, int FontIndex) +{ + kbts_font *Result = 0; + + if(!Context->Error && (Length > 0)) + { + kbts__context_font *ContextFont = kbts__ShapePushFont(Context); + + Result = kbts__PushType(&Context->FontArena, kbts_font); + + if(Result) + { + int ScratchSize, OutputSize; + kbts_load_font_state State = KBTS__ZERO; + kbts_load_font_error Error = kbts_LoadFont(Result, &State, Memory, Length, FontIndex, &ScratchSize, &OutputSize); + if(Error == KBTS_LOAD_FONT_ERROR_NEED_TO_CREATE_BLOB) + { + void *Scratch = kbts__PushSize(&Context->ScratchArena, (kbts_un)ScratchSize, 8); + void *Output = kbts__PushSize(&Context->FontArena, (kbts_un)OutputSize, 8); + + Error = kbts_PlaceBlob(Result, &State, Scratch, Output); + } + + if(!Error) + { + ContextFont->Font = Result; + } + else + { + kbts_ShapePopFont(Context); + Result = 0; + } + } + } + + return Result; +} + +static void kbts__EnsureGlyphStorageInitialized(kbts_glyph_storage *Storage) +{ + if(!Storage->GlyphSentinel.Next) + { + KBTS__DLLIST_SENTINEL_INIT(&Storage->GlyphSentinel); + KBTS__DLLIST_SENTINEL_INIT(&Storage->FreeGlyphSentinel); + } +} + +KBTS_EXPORT int kbts_InitializeGlyphStorage(kbts_glyph_storage *Storage, kbts_allocator_function *Allocator, void *AllocatorData) +{ + int Result = 0; + + if(Storage) + { + Result = 1; + KBTS_MEMSET(Storage, 0, sizeof(*Storage)); + + Storage->Arena.Allocator = Allocator; + Storage->Arena.AllocatorData = AllocatorData; + } + + return Result; +} + +KBTS_EXPORT int kbts_InitializeGlyphStorageFixedMemory(kbts_glyph_storage *Storage, void *Memory, int MemorySize) +{ + int Result = 0; + + if(Storage && + (MemorySize > 0)) + { + KBTS_MEMSET(Storage, 0, sizeof(*Storage)); + + Result = kbts__InitializeFixedMemoryArena(&Storage->Arena, Memory, (kbts_un)MemorySize); + } + + return Result; +} + +KBTS_EXPORT kbts_glyph_iterator kbts_ActiveGlyphIterator(kbts_glyph_storage *Storage) +{ + kbts_glyph_iterator Result = KBTS__ZERO; + + if(Storage && !Storage->Error) + { + kbts__EnsureGlyphStorageInitialized(Storage); + + Result.GlyphStorage = Storage; + Result.CurrentGlyph = Storage->GlyphSentinel.Next; + } + + return Result; +} + +KBTS_EXPORT void kbts_ClearActiveGlyphs(kbts_glyph_storage *Storage) +{ + if(!Storage->Error) + { + kbts__EnsureGlyphStorageInitialized(Storage); + + kbts_glyph *First = Storage->GlyphSentinel.Next; + kbts_glyph *Last = Storage->GlyphSentinel.Prev; + + if(kbts__GlyphIsValid(Storage, First)) + { + // Free all previous glyphs. + First->Prev = Storage->FreeGlyphSentinel.Prev; + Last->Next = &Storage->FreeGlyphSentinel; + First->Prev->Next = First; + Last->Next->Prev = Last; + } + + KBTS__DLLIST_SENTINEL_INIT(&Storage->GlyphSentinel); + } +} + +KBTS_EXPORT void kbts_FreeAllGlyphs(kbts_glyph_storage *Storage) +{ + kbts__FreeArena(&Storage->Arena); + + KBTS__DLLIST_SENTINEL_INIT(&Storage->GlyphSentinel); + KBTS__DLLIST_SENTINEL_INIT(&Storage->FreeGlyphSentinel); +} + +KBTS_EXPORT kbts_glyph *kbts_PushGlyph(kbts_glyph_storage *Storage, kbts_font *Font, int Codepoint, kbts_glyph_config *Config, int UserId) +{ + kbts_glyph *Result = 0; + + if(!Storage->Error) + { + kbts__EnsureGlyphStorageInitialized(Storage); + + kbts_glyph Template = KBTS__ZERO; + if(Font) + { + Template = kbts_CodepointToGlyph(Font, Codepoint, Config, UserId); + } + else + { + Template.Codepoint = (kbts_u32)Codepoint; + Template.Config = Config; + // We do not want to use the template's UserId here, because it is just a bag of properties! + // We want to keep the original UserId. + } + + Result = kbts__InsertGlyphBefore(Storage, &Storage->GlyphSentinel, &Template); + } + + return Result; +} + +static int kbts__FeatureOverrideIsExtra(kbts_u32 Tag, int Value) +{ + int Result = 0; + kbts__feature_id Id = kbts__FeatureTagToId(Tag); + + if(!Id || (kbts_u32)(Value > 1)) + { + Result = 1; + } + + return Result; +} + +static void kbts__AddExtraFeatureOverride(kbts_glyph_config *Config, kbts__feature_override *Extra, kbts_u32 Tag, int Value) +{ + Extra->Tag = Tag; + Extra->Value = Value; + + KBTS__DLLIST_INSERT_BEFORE(&Extra->Header, &Config->FeatureOverrideSentinel); +} + +KBTS_EXPORT int kbts_SizeOfGlyphConfig(kbts_feature_override *Overrides, int OverrideCount) +{ + kbts_un StoredOverrideCount = 0; + + if(OverrideCount > 0) + { + KBTS__FOR(OverrideIndex, 0, (kbts_un)OverrideCount) + { + kbts_feature_override *Override = &Overrides[OverrideIndex]; + + if(kbts__FeatureOverrideIsExtra(Override->Tag, Override->Value)) + { + StoredOverrideCount += 1; + } + } + } + + kbts_un Result = sizeof(kbts_glyph_config) + StoredOverrideCount * sizeof(kbts__feature_override); + return (int)Result; +} + +KBTS_EXPORT kbts_glyph_config *kbts_PlaceGlyphConfig(kbts_feature_override *Overrides, int OverrideCount, void *Memory) +{ + kbts_glyph_config *Result = (kbts_glyph_config *)Memory; + if(Memory) + { + KBTS_MEMSET(Result, 0, sizeof(*Result)); + KBTS__DLLIST_SENTINEL_INIT(&Result->FeatureOverrideSentinel); + + kbts__feature_override *ExtraOverrides = KBTS__POINTER_AFTER(kbts__feature_override, Result); + + if(OverrideCount > 0) + { + KBTS__FOR(OverrideIndex, 0, (kbts_un)OverrideCount) + { + kbts_feature_override *Override = &Overrides[OverrideIndex]; + kbts__feature_id Id = kbts__FeatureTagToId(Override->Tag); + + if(kbts__FeatureOverrideIsExtra(Override->Tag, Override->Value)) + { + kbts__feature_override *ExtraOverride = &ExtraOverrides[Result->ExtraOverrideCount++]; + + kbts__AddExtraFeatureOverride(Result, ExtraOverride, Override->Tag, Override->Value); + } + + kbts__AddFeature((Override->Value) ? &Result->EnabledFeatures : &Result->DisabledFeatures, Id); + } + + if(Result->ExtraOverrideCount) + { + kbts__feature_override *Last = &ExtraOverrides[Result->ExtraOverrideCount - 1]; + Last->Header.Next = &Result->FeatureOverrideSentinel; + Result->FeatureOverrideSentinel.Prev = &Last->Header; + } + } + } + + return Result; +} + +KBTS_EXPORT kbts_glyph_config *kbts_CreateGlyphConfig(kbts_feature_override *Overrides, int OverrideCount, kbts_allocator_function *Allocator, void *AllocatorData) +{ + if(!Allocator) + { + Allocator = kbts__DefaultAllocator; + } + + kbts_glyph_config *Result = kbts_PlaceGlyphConfig(Overrides, OverrideCount, kbts__AllocatorAllocate(Allocator, AllocatorData, (kbts_un)kbts_SizeOfGlyphConfig(Overrides, OverrideCount))); + + if(Result) + { + Result->Allocator = Allocator; + Result->AllocatorData = AllocatorData; + } + + return Result; +} + +KBTS_EXPORT void kbts_DestroyGlyphConfig(kbts_glyph_config *Config) +{ + if(Config && Config->Allocator) + { + kbts__AllocatorFree(Config->Allocator, Config->AllocatorData, Config); + } +} + +KBTS_EXPORT kbts_shape_error kbts_ShapeError(kbts_shape_context *Context) +{ + kbts_shape_error Result = Context->Error; + return Result; +} + +KBTS_EXPORT void kbts_ShapeBegin(kbts_shape_context *Context, kbts_direction ParagraphDirection, kbts_language Language) +{ + if(!Context->Error) + { + kbts__ClearArena(&Context->ScratchArena); + kbts_ClearActiveGlyphs(&Context->GlyphStorage); + + Context->BreakStartIndex = 0; + Context->CurrentGlyphConfig = 0; + Context->NeedNewGlyphConfig = 1; + + Context->ParagraphDirection = ParagraphDirection; + Context->Language = Language; + Context->UserFeatures = KBTS__ZERO_TYPE(kbts__feature_set); + + Context->InputCodepointCount = 0; + Context->NextUserId = 0; + Context->LastGraphemeBreak = 0; + Context->LastLineBreakIndex = 0; + Context->RunFont = 0; + Context->RunScript = 0; + Context->RunDirection = 0; + Context->ExistingShapeConfigCount = 0; + + kbts_BreakBegin(&Context->BreakState, ParagraphDirection, KBTS_JAPANESE_LINE_BREAK_STYLE_NORMAL, 0); + } +} + +typedef struct kbts__input_codepoint_index +{ + kbts_u32 BlockIndex; + kbts_u32 CodepointIndex; + kbts_u32 BlockCodepointCount; +} kbts__input_codepoint_index; + +static kbts__input_codepoint_index kbts__InputCodepointIndex(kbts_un FlatCodepointIndex) +{ + kbts__input_codepoint_index Result = KBTS__ZERO; + + kbts_un MsbPosition = kbts__MsbPositionOrZero32((kbts_u32)FlatCodepointIndex); + if(MsbPosition <= KBTS__INPUT_CODEPOINT_FIRST_BLOCK_MSB) + { + Result.BlockIndex = 0; + Result.CodepointIndex = (kbts_u32)FlatCodepointIndex; + Result.BlockCodepointCount = 1 << (KBTS__INPUT_CODEPOINT_FIRST_BLOCK_MSB + 1); + } + else + { + Result.BlockIndex = (kbts_u32)(MsbPosition - KBTS__INPUT_CODEPOINT_FIRST_BLOCK_MSB); + Result.CodepointIndex = (kbts_u32)(FlatCodepointIndex & ~(1 << MsbPosition)); + Result.BlockCodepointCount = (kbts_u32)(1 << MsbPosition); + } + + return Result; +} + +static kbts_shape_codepoint *kbts__InputCodepoint(kbts_shape_context *Context, kbts_un Index) +{ + kbts_shape_codepoint *Result = 0; + + if(!Context->Error) + { + kbts__input_codepoint_index InputIndex = kbts__InputCodepointIndex(Index); + + kbts_shape_codepoint *Block = Context->InputBlocks[InputIndex.BlockIndex]; + if(!Block) + { + Block = kbts__PushArray(&Context->PermanentArena, kbts_shape_codepoint, InputIndex.BlockCodepointCount); + if(!Block) + { + Context->Error = KBTS_SHAPE_ERROR_OUT_OF_MEMORY; + return Result; + } + + Context->InputBlocks[InputIndex.BlockIndex] = Block; + } + + Result = &Context->InputBlocks[InputIndex.BlockIndex][InputIndex.CodepointIndex]; + } + + return Result; +} + +static kbts_shape_codepoint_iterator kbts__InputCodepointIterator(kbts_shape_context *Context, kbts_un StartCodepointIndex, kbts_un OnePastLastCodepointIndex) +{ + kbts_shape_codepoint_iterator Result = KBTS__ZERO; + + if(Context && + !Context->Error && + (OnePastLastCodepointIndex > StartCodepointIndex)) + { + kbts__input_codepoint_index StartInputIndex = kbts__InputCodepointIndex(StartCodepointIndex); + kbts__input_codepoint_index OnePastLastInputIndex = kbts__InputCodepointIndex(OnePastLastCodepointIndex); + + Result.Context = Context; + Result.BlockIndex = StartInputIndex.BlockIndex; + Result.CodepointIndex = StartInputIndex.CodepointIndex; + Result.EndBlockIndex = OnePastLastInputIndex.BlockIndex; + Result.OnePastLastCodepointIndex = OnePastLastInputIndex.CodepointIndex; + Result.CurrentBlockCodepointCount = (Result.BlockIndex == Result.EndBlockIndex) ? Result.OnePastLastCodepointIndex : StartInputIndex.BlockCodepointCount; + Result.FlatCodepointIndex = (kbts_u32)StartCodepointIndex; + } + + return Result; +} + +KBTS_EXPORT kbts_shape_codepoint_iterator kbts_ShapeCurrentCodepointsIterator(kbts_shape_context *Context) +{ + kbts_shape_codepoint_iterator Result = kbts__InputCodepointIterator(Context, 0, Context->InputCodepointCount); + + return Result; +} + +static int kbts__NextInputCodepoint(kbts_shape_codepoint_iterator *It, int *CodepointIndex) +{ + int Result = 0; + + if(It->CodepointIndex >= It->CurrentBlockCodepointCount) + { + It->BlockIndex += 1; + It->CodepointIndex = 0; + It->CurrentBlockCodepointCount = (It->BlockIndex == It->EndBlockIndex) ? It->OnePastLastCodepointIndex : (1 << (It->BlockIndex + KBTS__INPUT_CODEPOINT_FIRST_BLOCK_MSB)); + } + + if(It->BlockIndex <= It->EndBlockIndex) + { + if(CodepointIndex) + { + *CodepointIndex = (int)It->FlatCodepointIndex; + } + + It->Codepoint = &It->Context->InputBlocks[It->BlockIndex][It->CodepointIndex++]; + It->FlatCodepointIndex += 1; + + Result = 1; + } + + return Result; +} + +KBTS_EXPORT int kbts_ShapeCodepointIteratorIsValid(kbts_shape_codepoint_iterator *It) +{ + int Result = It->Context && (It->BlockIndex <= It->EndBlockIndex); + return Result; +} + +KBTS_EXPORT int kbts_ShapeCodepointIteratorNext(kbts_shape_codepoint_iterator *It, kbts_shape_codepoint *Codepoint, int *CodepointIndex) +{ + int Result = kbts__NextInputCodepoint(It, CodepointIndex); + + if(Result) + { + *Codepoint = *It->Codepoint; + } + + return Result; +} + +KBTS_EXPORT int kbts_ShapeGetShapeCodepoint(kbts_shape_context *Context, int CodepointIndex, kbts_shape_codepoint *Codepoint) +{ + int Result = 0; + + if((CodepointIndex >= 0) && + ((kbts_un)CodepointIndex < Context->InputCodepointCount)) + { + kbts_shape_codepoint *Source = kbts__InputCodepoint(Context, (kbts_un)CodepointIndex); + *Codepoint = *Source; + + Result = 1; + } + + return Result; +} + +static void kbts__UpdateBreaks(kbts_shape_context *Context) +{ + if(!(Context->Flags & KBTS__CONTEXT_FLAG_MANUAL_SEGMENTATION)) + { + kbts_break Break; + while(kbts_Break(&Context->BreakState, &Break)) + { + // Strictly speaking, we do not need all of the flags, but we record them all anyway so we can expose them to the user. + kbts_un BreakPosition = (kbts_u32)Break.Position + Context->BreakStartIndex; + kbts_shape_codepoint *InputCodepoint = kbts__InputCodepoint(Context, BreakPosition); + + if(Break.Flags & KBTS_BREAK_FLAG_LINE_HARD) + { + Context->LastLineBreakIndex = (kbts_u32)BreakPosition; + } + + if(Break.Flags & KBTS_BREAK_FLAG_GRAPHEME) + { + // Try fonts, potentially break run. + kbts_font *MatchFont = 0; + + for(kbts_un FontIndex = Context->FontCount; + FontIndex; + --FontIndex) + { + kbts_font *Font = Context->Fonts[FontIndex - 1].Font; + kbts_font_coverage_test CoverageTest; + kbts_FontCoverageTestBegin(&CoverageTest, Font); + + kbts_shape_codepoint_iterator It = kbts__InputCodepointIterator(Context, Context->LastGraphemeBreakIndex, BreakPosition); + + while(kbts__NextInputCodepoint(&It, 0)) + { + kbts_shape_codepoint *GraphemeCodepoint = It.Codepoint; + + kbts_FontCoverageTestCodepoint(&CoverageTest, GraphemeCodepoint->Codepoint); + } + + kbts_FontCoverageTestEnd(&CoverageTest); + + if(!CoverageTest.Error) + { + MatchFont = Font; + break; } } + + Context->LastGraphemeBreak->Font = MatchFont; + Context->LastGraphemeBreak = InputCodepoint; + Context->LastGraphemeBreakIndex = (kbts_u32)BreakPosition; } - Result.Langsys[ShapingTableIndex] = ChosenLangsys; + InputCodepoint->BreakFlags |= Break.Flags; + if(Break.Flags & KBTS_BREAK_FLAG_SCRIPT) + { + InputCodepoint->Script = Break.Script; + } + if(Break.Flags & KBTS_BREAK_FLAG_DIRECTION) + { + InputCodepoint->Direction = Break.Direction; + } + if(Break.Flags & KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION) + { + InputCodepoint->ParagraphDirection = Break.ParagraphDirection; + } } } +} - Result.IndicScriptProperties = kbts_IndicScriptProperties(Script); - Result.Shaper = FoundScriptIsIndic3 ? KBTS_SHAPER_USE : ScriptProperties->Shaper; - Result.OpLists[0] = kbts_ShaperOpLists[Result.Shaper]; - - switch(Result.Shaper) +KBTS_EXPORT void kbts_ShapeBeginManualRuns(kbts_shape_context *Context) +{ + if(!Context->Error) { - case KBTS_SHAPER_INDIC: - Result.OpLists[1] = KBTS_OP_LIST(Indic1); - Result.OpLists[2] = KBTS_OP_LIST(Indic2); - Result.OpLists[3] = KBTS_OP_LIST(Indic3); - break; - case KBTS_SHAPER_USE: - Result.OpLists[1] = KBTS_OP_LIST(Use1); - Result.OpLists[3] = KBTS_OP_LIST(Use3); - break; - case KBTS_SHAPER_KHMER: - Result.OpLists[1] = KBTS_OP_LIST(Khmer1); - Result.OpLists[3] = KBTS_OP_LIST(Khmer3); - break; - case KBTS_SHAPER_MYANMAR: - Result.OpLists[1] = KBTS_OP_LIST(Myanmar1); - Result.OpLists[2] = KBTS_OP_LIST(Myanmar2); - Result.OpLists[3] = KBTS_OP_LIST(Myanmar3); - break; - } - - Result.Features = &kbts_ShaperFeatures[Result.Shaper]; - - kbts_feature *Rclt = 0; - kbts_feature_set SyllableFeatureSet = {{KBTS_FEATURE_FLAG0(rphf) | KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(half) | KBTS_FEATURE_FLAG0(pstf) | KBTS_FEATURE_FLAG0(pref), - 0, KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(locl), KBTS_FEATURE_FLAG3(vatu)}}; - kbts_iterate_features IterateFeatures = kbts_IterateFeatures(&Result, KBTS_SHAPING_TABLE_GSUB, SyllableFeatureSet); - while(kbts_NextFeature(&IterateFeatures)) - { - switch(IterateFeatures.CurrentFeatureTag) + if(Context->InputCodepointCount > 0) { - case KBTS_FEATURE_TAG_blwf: Result.Blwf = IterateFeatures.Feature; break; - case KBTS_FEATURE_TAG_pref: Result.Pref = IterateFeatures.Feature; break; - case KBTS_FEATURE_TAG_pstf: Result.Pstf = IterateFeatures.Feature; break; - case KBTS_FEATURE_TAG_locl: Result.Locl = IterateFeatures.Feature; break; - case KBTS_FEATURE_TAG_rphf: Result.Rphf = IterateFeatures.Feature; break; - case KBTS_FEATURE_TAG_half: Result.Half = IterateFeatures.Feature; break; - case KBTS_FEATURE_TAG_vatu: Result.Vatu = IterateFeatures.Feature; break; - case KBTS_FEATURE_TAG_rclt: Rclt = IterateFeatures.Feature; break; + kbts_BreakEnd(&Context->BreakState); + kbts__UpdateBreaks(Context); + } + + Context->Flags |= KBTS__CONTEXT_FLAG_MANUAL_SEGMENTATION; + } +} + +KBTS_EXPORT void kbts_ShapeNextManualRun(kbts_shape_context *Context, kbts_direction Direction, kbts_script Script) +{ + if(!Context->Error && + (Context->Flags & KBTS__CONTEXT_FLAG_MANUAL_SEGMENTATION)) + { + Context->Flags |= KBTS__CONTEXT_FLAG_START_OF_MANUAL_RUN | KBTS__CONTEXT_FLAG_USE_MANUAL_BREAK_INFO; + + Context->ManualRunDirection = Direction; + Context->ManualRunScript = Script; + } +} + +KBTS_EXPORT void kbts_ShapeEndManualRuns(kbts_shape_context *Context) +{ + if(!Context->Error && + (Context->Flags & KBTS__CONTEXT_FLAG_MANUAL_SEGMENTATION)) + { + Context->Flags &= ~KBTS__CONTEXT_FLAG_MANUAL_SEGMENTATION; + + if(Context->InputCodepointCount > 0) + { + kbts_BreakBegin(&Context->BreakState, Context->ParagraphDirection, KBTS_JAPANESE_LINE_BREAK_STYLE_NORMAL, 0); + Context->BreakStartIndex = (kbts_u32)Context->InputCodepointCount; } } +} - if((Result.Shaper == KBTS_SHAPER_ARABIC) && !Rclt) +KBTS_EXPORT void kbts_ShapeManualBreak(kbts_shape_context *Context) +{ + kbts_ShapeBeginManualRuns(Context); + Context->Flags |= KBTS__CONTEXT_FLAG_START_OF_MANUAL_RUN; + kbts_ShapeEndManualRuns(Context); +} + +KBTS_EXPORT void kbts_ShapeCodepointWithUserId(kbts_shape_context *Context, int Codepoint, int UserId) +{ + if(!Context->Error) { - Result.OpLists[0] = KBTS_OP_LIST(ArabicNoRclt); + if(Context->NeedNewGlyphConfig) + { + kbts_glyph_config *Config = kbts__PushType(&Context->ScratchArena, kbts_glyph_config); + if(!Config) + { + Context->Error = KBTS_SHAPE_ERROR_OUT_OF_MEMORY; + return; + } + + *Config = KBTS__ZERO_TYPE(kbts_glyph_config); + KBTS__DLLIST_SENTINEL_INIT(&Config->FeatureOverrideSentinel); + + kbts__feature_set Features = KBTS__ZERO; + for(kbts__feature_override_header *OverrideHeader = Context->FeatureOverrideSentinel.Prev; + OverrideHeader != &Context->FeatureOverrideSentinel; + OverrideHeader = OverrideHeader->Prev) + { + kbts__feature_override *Override = (kbts__feature_override *)OverrideHeader; + kbts__feature_id Id = kbts__FeatureTagToId(Override->Tag); + + if(!Id || !kbts__ContainsFeature(&Features, Id)) + { + if(kbts__FeatureOverrideIsExtra(Override->Tag, Override->Value)) + { + kbts__feature_override *Insert = kbts__PushType(&Context->ScratchArena, kbts__feature_override); + if(!Insert) + { + Context->Error = KBTS_SHAPE_ERROR_OUT_OF_MEMORY; + return; + } + + kbts__AddExtraFeatureOverride(Config, Insert, Override->Tag, Override->Value); + } + + kbts__AddFeature(&Features, Id); + kbts__AddFeature((Override->Value) ? &Config->EnabledFeatures : &Config->DisabledFeatures, Id); + } + } + + Context->CurrentGlyphConfig = Config; + Context->NeedNewGlyphConfig = 0; + } + + { // Add the codepoint. + kbts_un FlatCodepointIndex = Context->InputCodepointCount; + kbts_shape_codepoint InputCodepoint = KBTS__ZERO; + InputCodepoint.Codepoint = Codepoint; + InputCodepoint.UserId = UserId; + InputCodepoint.Config = Context->CurrentGlyphConfig; + + // @Robustness: There is probably a saner way of doing this. + // When we do a manual break, we may have line breaks go out-of-bounds, and we + // do not want to lose that information. + if(FlatCodepointIndex && + (FlatCodepointIndex == Context->LastLineBreakIndex)) + { + InputCodepoint.BreakFlags |= KBTS_BREAK_FLAG_LINE; + } + + if(Context->Flags & KBTS__CONTEXT_FLAG_START_OF_MANUAL_RUN) + { + InputCodepoint.BreakFlags |= KBTS_BREAK_FLAG_MANUAL; + + if(Context->Flags & KBTS__CONTEXT_FLAG_USE_MANUAL_BREAK_INFO) + { + InputCodepoint.Direction = Context->ManualRunDirection; + InputCodepoint.Script = Context->ManualRunScript; + } + + Context->Flags &= ~KBTS__CONTEXT_FLAG_START_OF_MANUAL_RUN; + } + + kbts_shape_codepoint *To = kbts__InputCodepoint(Context, FlatCodepointIndex); + if(To) + { + *To = InputCodepoint; + } + + if(!Context->LastGraphemeBreak) + { + Context->LastGraphemeBreak = To; + } + + Context->InputCodepointCount += 1; + } + + if(!(Context->Flags & KBTS__CONTEXT_FLAG_MANUAL_SEGMENTATION)) + { + // Check for breaks. + kbts_BreakAddCodepoint(&Context->BreakState, Codepoint, 1, 0); + + kbts__UpdateBreaks(Context); + } } - - kbts_shape_state DummyState = KBTS_ZERO; - DummyState.Config = &Result; - - if(Result.IndicScriptProperties.ViramaCodepoint) +} +static void kbts__ShapeCodepoint(kbts_shape_context *Context, int Codepoint, int UserIdIncrement) +{ + int UserId = Context->NextUserId; + Context->NextUserId += UserIdIncrement; + kbts_ShapeCodepointWithUserId(Context, Codepoint, UserId); +} +KBTS_EXPORT void kbts_ShapeCodepoint(kbts_shape_context *Context, int Codepoint) +{ + if(!Context->Error) { - // Bake the locl-ized virama. - kbts_glyph Virama = kbts_CodepointToGlyph(Font, Result.IndicScriptProperties.ViramaCodepoint); - Result.Virama = kbts_Substitute1(&DummyState, kbts_GetLookupList(Gsub), Result.Locl, KBTS_SKIP_FLAG_ZWNJ | KBTS_SKIP_FLAG_ZWJ, &Virama); + kbts__ShapeCodepoint(Context, Codepoint, 1); } +} - if((Result.Script == KBTS_SCRIPT_THAI) || (Result.Script == KBTS_SCRIPT_LAO)) +KBTS_EXPORT void kbts_ShapeUtf32WithUserId(kbts_shape_context *Context, int *Utf32, int Length, int BaseUserId, int UserIdIncrement) +{ + if(!Context->Error && (Length > 0)) { - kbts_u32 NikhahitCodepoint = (Result.Script == KBTS_SCRIPT_THAI) ? 0xE4D : 0xECD; - kbts_u32 SaraAaCodepoint = (Result.Script == KBTS_SCRIPT_THAI) ? 0xE32 : 0xEB2; - Result.Nikhahit = kbts_CodepointToGlyph(Font, NikhahitCodepoint); - Result.SaraAa = kbts_CodepointToGlyph(Font, SaraAaCodepoint); - } + int UserId = BaseUserId; - Result.DottedCircle = kbts_CodepointToGlyph(Font, 0x25CC); - Result.Whitespace = kbts_CodepointToGlyph(Font, ' '); + KBTS__FOR(Utf32Index, 0, (kbts_un)Length) + { + int Codepoint = Utf32[Utf32Index]; + kbts_ShapeCodepointWithUserId(Context, Codepoint, UserId); + + UserId += UserIdIncrement; + } + } +} +KBTS_EXPORT void kbts_ShapeUtf32(kbts_shape_context *Context, int *Utf32, int Length) +{ + if(!Context->Error && (Length > 0)) + { + KBTS__FOR(Utf32Index, 0, (kbts_un)Length) + { + int Codepoint = Utf32[Utf32Index]; + kbts_ShapeCodepoint(Context, Codepoint); + } + } +} + +KBTS_EXPORT void kbts_ShapeUtf8WithUserId(kbts_shape_context *Context, const char *Utf8, int Length, int BaseUserId, kbts_user_id_generation_mode UserIdGenerationMode) +{ + if(!Context->Error && (Length > 0)) + { + const char *At = Utf8; + const char *End = Utf8 + Length; + int UserId = BaseUserId; + + int CodepointIncrement = (UserIdGenerationMode == KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX) ? 1 : 0; + int SourceCharacterMask = (UserIdGenerationMode == KBTS_USER_ID_GENERATION_MODE_SOURCE_INDEX) ? ~0 : 0; + + while(At < End) + { + kbts_decode Decode = kbts_DecodeUtf8(At, (kbts_un)(End - At)); + + if(Decode.Valid) + { + kbts_ShapeCodepointWithUserId(Context, Decode.Codepoint, UserId); + + UserId += CodepointIncrement; + } + + At += Decode.SourceCharactersConsumed; + UserId += Decode.SourceCharactersConsumed & SourceCharacterMask; + } + } +} +KBTS_EXPORT void kbts_ShapeUtf8(kbts_shape_context *Context, const char *Utf8, int Length, kbts_user_id_generation_mode UserIdGenerationMode) +{ + if(!Context->Error && (Length > 0)) + { + const char *At = Utf8; + const char *End = Utf8 + Length; + while(At < End) + { + kbts_decode Decode = kbts_DecodeUtf8(At, (kbts_un)(End - At)); + + if(Decode.Valid) + { + int Increment = 0; + if(UserIdGenerationMode == KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX) + { + Increment = 1; + } + else if(UserIdGenerationMode == KBTS_USER_ID_GENERATION_MODE_SOURCE_INDEX) + { + Increment = Decode.SourceCharactersConsumed; + } + + kbts__ShapeCodepoint(Context, Decode.Codepoint, Increment); + } + + At += Decode.SourceCharactersConsumed; + } + } +} + +KBTS_EXPORT void kbts_ShapePushFeature(kbts_shape_context *Context, kbts_u32 Tag, int Value) +{ + if(!Context->Error) + { + kbts__feature_override *Override; + kbts__feature_override_header *Header = Context->FreeFeatureOverrideSentinel.Prev; + if(Header != &Context->FreeFeatureOverrideSentinel) + { + Override = (kbts__feature_override *)Header; + } + else + { + Override = kbts__PushType(&Context->PermanentArena, kbts__feature_override); + } + + KBTS__DLLIST_INSERT_BEFORE(&Override->Header, &Context->FeatureOverrideSentinel); + Override->Tag = Tag; + Override->Value = Value; + + kbts__feature_id Id = kbts__FeatureTagToId(Tag); + kbts__AddFeature(&Context->UserFeaturesEnabled, Id); + + Context->NeedNewGlyphConfig = 1; + } +} + +KBTS_EXPORT int kbts_ShapePopFeature(kbts_shape_context *Context, kbts_u32 Tag) +{ + int Result = 0; + + if(!Context->Error) + { + for(kbts__feature_override_header *Header = Context->FeatureOverrideSentinel.Prev; + Header != &Context->FeatureOverrideSentinel; + Header = Header->Prev) + { + kbts__feature_override *Override = (kbts__feature_override *)Header; + + if(Override->Tag == Tag) + { + Result = 1; + + KBTS__DLLIST_REMOVE(Header); + KBTS__DLLIST_INSERT_BEFORE(Header, &Context->FreeFeatureOverrideSentinel); + + break; + } + } + + if(Result) + { + Context->NeedNewGlyphConfig = 1; + } + } return Result; } -static kbts_op kbts_ReadOp(kbts_shape_state *State, kbts_u8 *Ops) +static void kbts__ShapeDirect(kbts__shape_scratchpad *Scratchpad, kbts_shape_config *Config, kbts_glyph_storage *Storage) { - kbts_op Result = KBTS_ZERO; - Result.Kind = Ops[State->Ip++]; - - if(Result.Kind == KBTS_OP_KIND_GSUB_FEATURES_WITH_USER) - { - Result.Kind = KBTS_OP_KIND_GSUB_FEATURES; - Result.Features = State->UserFeatures; - } - - if((Result.Kind == KBTS_OP_KIND_GSUB_FEATURES) || (Result.Kind == KBTS_OP_KIND_GPOS_FEATURES)) - { - kbts_un FeatureCount = Ops[State->Ip++]; - int Rtl = (State->RunDirection == KBTS_DIRECTION_RTL); - KBTS_FOR(FeatureIndex, 0, FeatureCount) - { - kbts_u32 FeatureId = Ops[State->Ip++]; - - if((FeatureId == KBTS_FEATURE_ID_ltra) && Rtl) - { - FeatureId = KBTS_FEATURE_ID_rtla; - } - else if((FeatureId == KBTS_FEATURE_ID_ltrm) && Rtl) - { - FeatureId = KBTS_FEATURE_ID_rtlm; - } - - kbts_AddFeature(&Result.Features, FeatureId); - } - } - return Result; -} - -KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, kbts_direction MainDirection, kbts_direction RunDirection, kbts_glyph *Glyphs, kbts_u32 *GlyphCount, kbts_u32 GlyphCapacity) -{ - KBTS_INSTRUMENT_FUNCTION_BEGIN - State->Config = Config; - State->MainDirection = MainDirection; - State->RunDirection = RunDirection; - - kbts_glyph_array *GlyphArray = &State->GlyphArray; - // The Glyphs array might move after a grow, so update the pointers here. - // We preserve Count information, though, because it makes it simpler not to touch anything - // when we are dealing with sub-arrays like Cluster. - GlyphArray->Glyphs = Glyphs; - GlyphArray->Capacity = GlyphCapacity; - kbts_glyph_array *Cluster = &State->ClusterGlyphArray; - Cluster->Glyphs = Glyphs + State->At; - Cluster->Capacity = GlyphCapacity - State->At; - - kbts_u32 ResumePoint = State->ResumePoint; - State->ResumePoint = 0; - switch(ResumePoint) - { - case 1: goto ResumePoint1; break; - case 2: goto ResumePoint2; break; - case 3: goto ResumePoint3; break; - case 4: goto ResumePoint4; break; - case 5: goto ResumePoint5; break; - case 6: goto ResumePoint6; break; - } - - *GlyphArray = kbts_GlyphArray(Glyphs, *GlyphCount, *GlyphCount, GlyphCapacity); + KBTS_INSTRUMENT_BLOCK_BEGIN(ReadOpLoop0); // For simple shapers, all of the shaping happens in this single loop. // For complex shapers, this loop is preparing the text for clustering logic, which happens below. - for(State->Ip = 0; State->Ip < Config->OpLists[0].Length;) + while(kbts__ReadOp(Scratchpad, Config, KBTS__OP_KIND_BEGIN_CLUSTER)) { - State->Op = kbts_ReadOp(State, Config->OpLists[0].Ops); - ResumePoint1:; - if(kbts_ExecuteOp(State, GlyphArray)) + kbts__ExecuteOp(Scratchpad, Config, Storage); + + if(Scratchpad->Error) { - State->ResumePoint = 1; - goto GrowRequest; + return; } } - if(KBTS_IN_SET(Config->Shaper, KBTS_SET32((KBTS_SHAPER_INDIC)(KBTS_SHAPER_USE)(KBTS_SHAPER_KHMER)(KBTS_SHAPER_MYANMAR)))) + KBTS_INSTRUMENT_BLOCK_END(ReadOpLoop0); + + if(Scratchpad->OpKind == KBTS__OP_KIND_BEGIN_CLUSTER) { - State->ClusterAtStartOfWord = 1; - State->At = 0; - while(State->At < GlyphArray->Count) + kbts_u32 BeginClusterIp = Scratchpad->Ip; + kbts_u32 BeginClusterFeatureStagesRead = Scratchpad->FeatureStagesRead; + kbts_glyph *TopLevelGlyph = Storage->GlyphSentinel.Next; + + Scratchpad->ClusterAtStartOfWord = 1; + + while(kbts__GlyphIsValid(Storage, TopLevelGlyph)) { - if(KBTS_IN_SET(Config->Shaper, KBTS_SET32((KBTS_SHAPER_INDIC)(KBTS_SHAPER_KHMER)))) + int WordBreak = 0; + + Scratchpad->Ip = BeginClusterIp; + Scratchpad->FeatureStagesRead = BeginClusterFeatureStagesRead; + { - // Reserve a slot in case we need to insert a dotted circle into a broken syllable. - ResumePoint2:; - if(!kbts_GrowGlyphArray(&State->ResumePoint, GlyphArray, GlyphArray->Count, 1, 2, 1)) + // We need to store this in case TopLevelGlyph is reordered. + kbts_glyph *OneBeforeFirstClusterGlyph = TopLevelGlyph->Prev; + kbts_glyph *OnePastLastClusterGlyph = kbts__BeginCluster(Scratchpad, Config, Storage, TopLevelGlyph); + + if(Scratchpad->Error) { - goto GrowRequest; + return; + } + + WordBreak = !(OnePastLastClusterGlyph->Prev->UnicodeFlags & KBTS_UNICODE_FLAG_PART_OF_WORD); + Scratchpad->Cluster = kbts__PushGlyphList(Storage, OneBeforeFirstClusterGlyph->Next, OnePastLastClusterGlyph->Prev); + } + + while(kbts__ReadOp(Scratchpad, Config, KBTS__OP_KIND_END_CLUSTER)) + { + kbts__ExecuteOp(Scratchpad, Config, Storage); + + if(Scratchpad->Error) + { + return; } } - { - kbts_begin_cluster_result BeginClusterResult = kbts_BeginCluster(State, Glyphs + State->At, GlyphArray->Count - State->At); - GlyphArray->Count += BeginClusterResult.InsertedGlyphCount; - GlyphArray->TotalCount += BeginClusterResult.InsertedGlyphCount; - State->ClusterGlyphCount = (kbts_u32)BeginClusterResult.ClusterGlyphCount; - *Cluster = kbts_GlyphArray(Glyphs + State->At, BeginClusterResult.ClusterGlyphCount, GlyphArray->Count - State->At, GlyphCapacity - State->At); - } + kbts__EndCluster(Scratchpad, Config, Storage); - kbts_glyph *LastGlyphInCluster; LastGlyphInCluster = &Glyphs[State->At + Cluster->Count - 1]; - State->WordBreak = !(LastGlyphInCluster->UnicodeFlags & KBTS_UNICODE_FLAG_PART_OF_WORD); - - for(State->Ip = 0; State->Ip < Config->OpLists[1].Length;) + while(kbts__ReadOp(Scratchpad, Config, KBTS__OP_KIND_END_SYLLABLE)) { - State->Op = kbts_ReadOp(State, Config->OpLists[1].Ops); - ResumePoint3:; - if(kbts_ExecuteOp(State, Cluster)) + kbts__ExecuteOp(Scratchpad, Config, Storage); + + if(Scratchpad->Error) { - State->ResumePoint = 3; - goto GrowRequest; + return; } } - kbts_end_cluster_result EndClusterResult; EndClusterResult = kbts_EndCluster(State, Cluster); - if(EndClusterResult.InsertDottedCircle) - { - State->DottedCircleInsertIndex = (kbts_u32)EndClusterResult.DottedCircleIndex; - ResumePoint6:; - if(!kbts_GrowGlyphArray(&State->ResumePoint, Cluster, State->DottedCircleInsertIndex, 1, 6, 0)) - { - State->RequiredGlyphCapacity = Cluster->RequiredCapacity + State->At; - goto GrowRequest; - } - Glyphs[State->At + State->DottedCircleInsertIndex] = Config->DottedCircle; - } + // Reattach the cluster to the main list. + Storage->GlyphSentinel.Next->Prev = Scratchpad->Cluster.OneBeforeFirst; + Storage->GlyphSentinel.Prev->Next = Scratchpad->Cluster.OnePastLast; + TopLevelGlyph = Scratchpad->Cluster.OnePastLast; - for(State->Ip = 0; State->Ip < Config->OpLists[2].Length;) - { - State->Op = kbts_ReadOp(State, Config->OpLists[2].Ops); - ResumePoint4:; - if(kbts_ExecuteOp(State, Cluster)) - { - State->ResumePoint = 4; - goto GrowRequest; - } - } + kbts__PopGlyphList(Storage, &Scratchpad->Cluster); - State->At += Cluster->Count; - kbts_un DeltaClusterGlyphCount = Cluster->Count - State->ClusterGlyphCount; - GlyphArray->Count += DeltaClusterGlyphCount; - GlyphArray->TotalCount += DeltaClusterGlyphCount; - State->ClusterAtStartOfWord = State->WordBreak; + Scratchpad->ClusterAtStartOfWord = WordBreak; } // Post-clustering ops work across clusters. // This is where Indic GPOS + post-passes happen. - for(State->Ip = 0; State->Ip < Config->OpLists[3].Length;) + while(kbts__ReadOp(Scratchpad, Config, 0)) { - State->Op = kbts_ReadOp(State, Config->OpLists[3].Ops); - ResumePoint5:; - if(kbts_ExecuteOp(State, GlyphArray)) + kbts__ExecuteOp(Scratchpad, Config, Storage); + + if(Scratchpad->Error) { - State->ResumePoint = 5; - goto GrowRequest; + return; } } } - if(0) + KBTS_INSTRUMENT_FUNCTION_END; +} + +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) +{ + if(!Allocator) { - GrowRequest:; - kbts_TransferGrowRequest(Cluster, GlyphArray); - State->RequiredGlyphCapacity = GlyphArray->RequiredCapacity; - KBTS_INSTRUMENT_END - return 1; + Allocator = kbts__DefaultAllocator; } - *GlyphCount = GlyphArray->Count; - KBTS_INSTRUMENT_END - return 0; -} + kbts_arena Arena = KBTS__ZERO; + Arena.Allocator = Allocator; + Arena.AllocatorData = AllocatorData; -KBTS_EXPORT kbts_cursor kbts_Cursor(kbts_direction Direction) -{ - kbts_cursor Result = KBTS_ZERO; - Result.Direction = Direction; - return Result; -} + kbts__shape_scratchpad *Scratchpad = kbts__PushType(&Arena, kbts__shape_scratchpad); + KBTS_MEMSET(Scratchpad, 0, sizeof(*Scratchpad)); + Scratchpad->Arena = &Arena; + Scratchpad->RunDirection = RunDirection; -KBTS_EXPORT void kbts_PositionGlyph(kbts_cursor *Cursor, kbts_glyph *Glyph, kbts_s32 *X, kbts_s32 *Y) -{ - kbts_s32 AdvanceX = Glyph->AdvanceX; - kbts_s32 AdvanceY = Glyph->AdvanceY; - kbts_s32 OffsetX = Glyph->OffsetX; - kbts_s32 OffsetY = Glyph->OffsetY; - if(Cursor->Direction != KBTS_DIRECTION_RTL) + kbts__ShapeDirect(Scratchpad, Config, Storage); + kbts_shape_error Result = Scratchpad->Error; + + if(!Result) { - *X = Cursor->X + OffsetX; - *Y = Cursor->Y + OffsetY; - - Cursor->X += AdvanceX; - Cursor->Y += AdvanceY; + *Output = kbts_ActiveGlyphIterator(Storage); } else { - // Right-to-left should, in theory, work almost identically to left-to-right: just right-align everything. - // However, the convention for glyph bounding boxes, etc., is that they are left-aligned. - // To compensate for this, we add base glyphs' advances to their positions, which should left-align them. - // We can't accumulate the advances immediately into the cursor, though, because then mark offsets will be wrong. - // - // By the way, Harfbuzz just reverses glyphs to always be left-to-right for the sake of layout. - // The reason we try to handle right-to-left somewhat properly here is that it allows the user to position one - // glyph at a time by just going through the glyph sequence, no matter the text direction. - // This should hopefully save the user from having to allocate a buffer of glyph positions and/or doing a pre-pass - // just to get the total string width, in the case of cross-direction text. - // - // The classic Harfbuzz behavior can be obtained by passing MainDirection = LTR to Shape, even for RTL text. - // (Cursor.Direction also needs to be NONE or LTR, of course, which is the default.) - if(AdvanceX) + *Output = KBTS__ZERO_TYPE(kbts_glyph_iterator); + } + + kbts__FreeArena(&Arena); + + return Result; +} + +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) +{ + kbts_shape_error Result = 0; + + if(Config && Storage && !Storage->Error && Memory && (MemorySize > 0)) + { + kbts_arena Arena; + + if(kbts__InitializeFixedMemoryArena(&Arena, Memory, (kbts_un)MemorySize)) { - Cursor->X -= Cursor->LastAdvanceX; - Cursor->LastAdvanceX = AdvanceX; + Result = kbts_ShapeDirect(Config, Storage, RunDirection, kbts__ArenaAllocator, &Arena, Output); } + } - *X = Cursor->X - AdvanceX + OffsetX; - *Y = Cursor->Y + OffsetY; + return Result; +} - Cursor->Y += AdvanceY; +static void kbts__BeginParagraph(kbts_shape_context *Context) +{ + Context->RunFont = 0; + if(Context->FontCount) + { + Context->RunFont = Context->Fonts[Context->FontCount - 1].Font; + } + + Context->RunScript = 0; + Context->RunParagraphDirection = 0; + Context->RunDirection = 0; +} + +KBTS_EXPORT void kbts_ShapeEnd(kbts_shape_context *Context) +{ + if(!Context->Error) + { + // We check the break flags of the one-past-last codepoint, so reset it here. + kbts_shape_codepoint *OnePastLastCodepoint = kbts__InputCodepoint(Context, Context->InputCodepointCount); + *OnePastLastCodepoint = KBTS__ZERO_TYPE(kbts_shape_codepoint); + + kbts_BreakEnd(&Context->BreakState); + kbts__UpdateBreaks(Context); + + Context->RunCodepointIterator.Context = 0; + Context->DoneShapingRuns = 0; + + kbts__BeginParagraph(Context); } } -KBTS_EXPORT kbts_un kbts_ReadFontHeader(kbts_font *Font, void *Data, kbts_un Size) +KBTS_EXPORT int kbts_ShapeRun(kbts_shape_context *Context, kbts_run *Run) { - KBTS_UNUSED(Size); - kbts_un Result = 0; - if(Size >= sizeof(kbts_table_directory)) + int Result = 0; + + if(!Context->Error && + !Context->DoneShapingRuns) { - char *FileEnd = (char *)Data + Size; - Font->FileBase = (char *)Data; - Font->FileSize = Size; - kbts_table_directory *Directory = (kbts_table_directory *)Font->FileBase; + kbts_font *RunFont = Context->RunFont; + kbts_script RunScript = Context->RunScript; + kbts_direction RunParagraphDirection = Context->RunParagraphDirection; + kbts_direction RunDirection = Context->RunDirection; + kbts_language Language = Context->Language; - Directory->TableCount = kbts_ByteSwap16(Directory->TableCount); - Directory->SearchRange = kbts_ByteSwap16(Directory->SearchRange); - Directory->EntrySelector = kbts_ByteSwap16(Directory->EntrySelector); - Directory->RangeShift = kbts_ByteSwap16(Directory->RangeShift); + Run->Flags = 0; - kbts_table_record *Tables = KBTS_POINTER_AFTER(kbts_table_record, Directory); + kbts_ClearActiveGlyphs(&Context->GlyphStorage); - kbts_un ShapingTableSizes[KBTS_SHAPING_TABLE_COUNT] = {0}; - kbts_u32 GdefSize = 0; - kbts_un DirectoryTableCapacity = (FileEnd - (char *)Tables) / sizeof(kbts_table_record); - if(Directory->TableCount <= DirectoryTableCapacity) + int Initialized = 1; + + if(!Context->RunCodepointIterator.Context) { - for(kbts_un TableIndex = 0; TableIndex < Directory->TableCount; ++TableIndex) + Context->RunCodepointIterator = kbts__InputCodepointIterator(Context, 0, Context->InputCodepointCount); + Initialized = 0; + } + + kbts_shape_codepoint_iterator *It = &Context->RunCodepointIterator; + + if(kbts_ShapeCodepointIteratorIsValid(It)) + { + int InputCodepointIndex = 0; + while(kbts__NextInputCodepoint(It, &InputCodepointIndex)) { - kbts_table_record *Table = &Tables[TableIndex]; - Table->Checksum = kbts_ByteSwap32(Table->Checksum); - Table->Offset = kbts_ByteSwap32(Table->Offset); - Table->Length = kbts_ByteSwap32(Table->Length); - int TableValid = 0; + kbts_shape_codepoint *InputCodepoint = It->Codepoint; - void *TableBase = KBTS_POINTER_OFFSET(void, Font->FileBase, Table->Offset); - char *TableEnd = (char *)TableBase + Table->Length; - if(((char *)TableBase >= (char *)(Tables + Directory->TableCount)) && (TableEnd <= FileEnd)) + // Resolve neutral directions. + if((InputCodepoint->BreakFlags & KBTS_BREAK_FLAG_DIRECTION) && + (InputCodepoint->Direction == KBTS_DIRECTION_DONT_KNOW)) { - switch(Table->Tag) + InputCodepoint->Direction = RunParagraphDirection; + } + + int First = !Result; + Result = 1; + + kbts_font *CodepointFont = InputCodepoint->Font; + kbts_script CodepointScript = InputCodepoint->Script; + kbts_direction CodepointDirection = InputCodepoint->Direction; + kbts_direction CodepointParagraphDirection = InputCodepoint->ParagraphDirection; + kbts_break_flags CodepointBreakFlags = InputCodepoint->BreakFlags; + + int NewLine = !First && (CodepointBreakFlags & (KBTS_BREAK_FLAG_LINE_HARD | KBTS_BREAK_FLAG_MANUAL)); + + if(First) + { + Run->Flags = CodepointBreakFlags; + + if(CodepointBreakFlags & KBTS_BREAK_FLAG_LINE_HARD) { - case KBTS_FOURCC('h', 'e', 'a', 'd'): - { - if(Table->Length >= sizeof(kbts_head)) - { - kbts_head *Head = (kbts_head *)TableBase; - kbts_ByteSwapArray16Unchecked(&Head->Major, 2); - kbts_ByteSwapArray32Unchecked(&Head->Revision, 2); - // We do not swap the magic number. - kbts_ByteSwapArray16Unchecked(&Head->Flags, 2); - // We do not swap file times. - kbts_ByteSwapArray16Unchecked((kbts_u16 *)&Head->XMin, 9); + kbts__BeginParagraph(Context); + RunFont = Context->RunFont; + RunScript = 0; + RunDirection = 0; - Font->Head = Head; - TableValid = 1; - } - } break; - - case KBTS_FOURCC('c', 'm', 'a', 'p'): - { - if(Table->Length >= sizeof(kbts_cmap)) - { - kbts_cmap *Cmap = (kbts_cmap *)TableBase; - Cmap->Version = kbts_ByteSwap16(Cmap->Version); - Cmap->TableCount = kbts_ByteSwap16(Cmap->TableCount); - - kbts_encoding_record *Records = KBTS_POINTER_AFTER(kbts_encoding_record, Cmap); - - if((char *)(Records + Cmap->TableCount) <= TableEnd) - { - KBTS_FOR(It, 0, Cmap->TableCount) - { - kbts_encoding_record *Record = &Records[It]; - Record->EncodingId = kbts_ByteSwap16(Record->EncodingId); - Record->PlatformId = kbts_ByteSwap16(Record->PlatformId); - Record->SubtableOffset = kbts_ByteSwap32(Record->SubtableOffset); - } - - kbts_cmap_subtable_pointer PreferredSubtable = KBTS_ZERO; - kbts_u16 PreferredFormat = 1; - KBTS_FOR(It, 0, Cmap->TableCount) - { - kbts_cmap_subtable_pointer Subtable = kbts_GetCmapSubtable(Cmap, It); - if((char *)(Subtable.Subtable + 1) <= TableEnd) - { - kbts_u16 Format = kbts_ByteSwap16(*Subtable.Subtable); - - if(Format == 14) - { - if((char *)(Subtable.Subtable + sizeof(kbts_cmap_14)) <= TableEnd) - { - Font->Cmap14 = (kbts_cmap_14 *)Subtable.Subtable; - } - } - else if(!PreferredSubtable.Subtable) - { - PreferredSubtable = Subtable; - } - else if(Format < KBTS_ARRAY_LENGTH(kbts_CmapFormatPrecedence)) - { - kbts_u16 Precedence = kbts_CmapFormatPrecedence[Format]; - kbts_u16 PreferredPrecedence = kbts_CmapFormatPrecedence[PreferredFormat]; - - if((Precedence > PreferredPrecedence) || ((Precedence == PreferredPrecedence) && (Subtable.PlatformId == 3))) - { - PreferredSubtable = Subtable; - PreferredFormat = Format; - } - } - } - } - - if(PreferredSubtable.Subtable) - { - *PreferredSubtable.Subtable = kbts_ByteSwap16(*PreferredSubtable.Subtable); - switch(*PreferredSubtable.Subtable) - { - case 0: - { - kbts_cmap_0 *Cmap0 = (kbts_cmap_0 *)PreferredSubtable.Subtable; - if((char *)(Cmap0 + 1) <= TableEnd) - { - Cmap0->Length = kbts_ByteSwap16(Cmap0->Length); - Cmap0->Language = kbts_ByteSwap16(Cmap0->Language); - TableValid = 1; - } - } - break; - - case 2: - { - kbts_cmap_2 *Cmap2 = (kbts_cmap_2 *)PreferredSubtable.Subtable; - if(kbts_ByteSwapArray16(&Cmap2->Length, 258, TableEnd)) - { - kbts_un SubHeaderCount = 0; - KBTS_FOR(It, 0, 256) - { - kbts_un SubHeaderIndex = Cmap2->SubHeaderKeys[It]; - SubHeaderCount = KBTS_MAX(SubHeaderCount, SubHeaderIndex + 1); - } - - kbts_sub_header *SubHeaders = KBTS_POINTER_AFTER(kbts_sub_header, Cmap2); - if(kbts_ByteSwapArray16(&SubHeaders->FirstCode, 4 * SubHeaderCount, TableEnd)) - { - kbts_u16 *GlyphIds = (kbts_u16 *)(SubHeaders + SubHeaderCount); - - kbts_sn GlyphIdCount = 0; - KBTS_FOR(It, 0, SubHeaderCount) - { - kbts_sub_header *SubHeader = &SubHeaders[It]; - - kbts_u16 *OnePastLastGlyphId = &SubHeader->IdRangeOffset + SubHeader->IdRangeOffset / 2 + SubHeader->EntryCount; - GlyphIdCount = KBTS_MAX(GlyphIdCount, OnePastLastGlyphId - GlyphIds); - } - - if(kbts_ByteSwapArray16(GlyphIds, (kbts_un)GlyphIdCount, TableEnd)) - { - TableValid = 1; - } - } - } - } - break; - - case 4: - { - kbts_cmap_4 *Cmap4 = (kbts_cmap_4 *)PreferredSubtable.Subtable; - if(kbts_ByteSwapArray16(&Cmap4->Length, 5, TableEnd) && - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Cmap4), Cmap4->SegmentCountTimesTwo * 2 + 1, TableEnd)) - { - kbts_un SegmentCount = Cmap4->SegmentCountTimesTwo / 2; - kbts_u16 *EndCodes = KBTS_POINTER_AFTER(kbts_u16, Cmap4); - kbts_u16 *StartCodes = EndCodes + SegmentCount + 1; - kbts_s16 *IdDeltas = (kbts_s16 *)(StartCodes + SegmentCount); - kbts_u16 *IdRangeOffsets = (kbts_u16 *)(IdDeltas + SegmentCount); - kbts_u16 *GlyphIds = IdRangeOffsets + SegmentCount; - - kbts_sn GlyphIdCount = 0; - - KBTS_FOR(SegmentIndex, 0, SegmentCount) - { - kbts_u16 Offset = IdRangeOffsets[SegmentIndex]; - - if(Offset) - { - kbts_u16 *IdLookup = &IdRangeOffsets[SegmentIndex] + (EndCodes[SegmentIndex] - StartCodes[SegmentIndex] + 1) + Offset / 2; - - GlyphIdCount = KBTS_MAX(GlyphIdCount, (IdLookup - GlyphIds)); - } - } - - if(kbts_ByteSwapArray16(GlyphIds, (kbts_un)GlyphIdCount, TableEnd)) - { - TableValid = 1; - } - } - } - break; - - case 6: - { - kbts_cmap_6 *Cmap6 = (kbts_cmap_6 *)PreferredSubtable.Subtable; - if(kbts_ByteSwapArray16(&Cmap6->Length, 4, TableEnd) && - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Cmap6), Cmap6->EntryCount, TableEnd)) - { - TableValid = 1; - } - } - break; - - case 12: - { - kbts_cmap_12_13 *Cmap12 = (kbts_cmap_12_13 *)PreferredSubtable.Subtable; - if(kbts_ByteSwapArray32(&Cmap12->Length, 3, TableEnd) && - kbts_ByteSwapArray32(KBTS_POINTER_AFTER(kbts_u32, Cmap12), Cmap12->GroupCount * 3, TableEnd)) - { - TableValid = 1; - } - } - break; - } - - Font->Cmap = PreferredSubtable.Subtable; - } - } - } - } - break; - - case KBTS_FOURCC('G', 'D', 'E', 'F'): - { - kbts_gdef *Gdef = (kbts_gdef *)TableBase; - GdefSize = Table->Length; - - if(kbts_ByteSwapArray16(&Gdef->Major, 6, TableEnd)) - { - if(Gdef->Minor >= 2) - { - if(Table->Length >= 14) - { - Gdef->MarkGlyphSetsDefinitionOffset = kbts_ByteSwap16(Gdef->MarkGlyphSetsDefinitionOffset); - - if(Gdef->Minor == 3) - { - if(Table->Length >= sizeof(kbts_gdef)) - { - // @Incomplete - Gdef->ItemVariationStoreOffset = kbts_ByteSwap32(Gdef->ItemVariationStoreOffset); - TableValid = 1; - } - } - else - { - TableValid = 1; - } - } - } - else - { - TableValid = 1; - } - } - - Font->Gdef = Gdef; - } - break; - - case KBTS_FOURCC('G', 'S', 'U', 'B'): - case KBTS_FOURCC('G', 'P', 'O', 'S'): - { - // We do not do any bounds checking here because Gsub/Gpos tables get byteswapped later on, - // in ByteSwapGsubGposCommon. - kbts_un Index = (Table->Tag == KBTS_FOURCC('G', 'S', 'U', 'B')) ? KBTS_SHAPING_TABLE_GSUB : KBTS_SHAPING_TABLE_GPOS; - Font->ShapingTables[Index] = (kbts_gsub_gpos *)TableBase; - ShapingTableSizes[Index] = Table->Length; - TableValid = 1; - } break; - - case KBTS_FOURCC('h', 'h', 'e', 'a'): - case KBTS_FOURCC('v', 'h', 'e', 'a'): - { - kbts_un Orientation = Table->Tag == KBTS_FOURCC('h', 'h', 'e', 'a') ? KBTS_ORIENTATION_HORIZONTAL : KBTS_ORIENTATION_VERTICAL; - kbts_hea *Hea = (kbts_hea *)TableBase; - if(kbts_ByteSwapArray16((kbts_u16 *)Hea, sizeof(kbts_hea) / sizeof(kbts_u16), TableEnd)) - { - Font->Hea[Orientation] = Hea; - TableValid = 1; - } - } break; - - case KBTS_FOURCC('h', 'm', 't', 'x'): - case KBTS_FOURCC('v', 'm', 't', 'x'): - { - kbts_un Orientation = Table->Tag == KBTS_FOURCC('h', 'm', 't', 'x') ? KBTS_ORIENTATION_HORIZONTAL : KBTS_ORIENTATION_VERTICAL; - kbts_u16 *Mtx = (kbts_u16 *)TableBase; - kbts_ByteSwapArray16Unchecked(Mtx, Table->Length / sizeof(kbts_u16)); - Font->Mtx[Orientation] = Mtx; - TableValid = 1; - } break; - - case KBTS_FOURCC('m', 'a', 'x', 'p'): - { - if(Table->Length >= 6) - { - Font->Maxp = (kbts_maxp *)TableBase; - Font->Maxp->Major = kbts_ByteSwap16(Font->Maxp->Major); - Font->Maxp->Minor = kbts_ByteSwap16(Font->Maxp->Minor); - - kbts_un U16Count = 0; - if(!Font->Maxp->Major && (Font->Maxp->Minor == 0x5000)) - { - U16Count = 1; - } - else if((Font->Maxp->Major == 1) && !Font->Maxp->Minor) - { - U16Count = 14; - } - - if(kbts_ByteSwapArray16(&Font->Maxp->GlyphCount, U16Count, TableEnd)) - { - Font->GlyphCount = Font->Maxp->GlyphCount; - TableValid = 1; - } - } - } break; - - default: - { - TableValid = 1; - } break; + // If we see a bunch of whitespace at the beginning of a paragraph, + // we want to merge with the first actual text that shows up. + Initialized = 0; } } - if(!TableValid) + if((CodepointFont && (CodepointFont != RunFont)) || + (CodepointScript && (CodepointScript != RunScript)) || + (CodepointDirection && (CodepointDirection != RunDirection)) || + (CodepointParagraphDirection && (CodepointParagraphDirection != RunParagraphDirection)) || + NewLine) { - goto Error; + if(CodepointFont) + { + Context->RunFont = CodepointFont; + } + if(CodepointScript) + { + Context->RunScript = CodepointScript; + } + if(CodepointDirection) + { + Context->RunDirection = CodepointDirection; + } + if(CodepointParagraphDirection) + { + Context->RunParagraphDirection = CodepointParagraphDirection; + } + + if(Initialized || NewLine) + { + // Rewind the current codepoint. + // We could also try to peek the codepoint before advancing, but this seems fine. + if(!It->CodepointIndex) + { + It->BlockIndex -= 1; + It->CodepointIndex = (1 << (It->BlockIndex + KBTS__INPUT_CODEPOINT_FIRST_BLOCK_MSB)) - 1; + } + else + { + It->CodepointIndex -= 1; + } + + It->FlatCodepointIndex -= 1; + + goto FoundBreak; + } + else + { + // Initialize and keep matching. + RunFont = Context->RunFont; + RunScript = Context->RunScript; + RunDirection = Context->RunDirection; + RunParagraphDirection = Context->RunParagraphDirection; + + kbts_PushGlyph(&Context->GlyphStorage, RunFont, InputCodepoint->Codepoint, InputCodepoint->Config, InputCodepointIndex); + + Initialized = 1; + } + } + else + { + kbts_PushGlyph(&Context->GlyphStorage, RunFont, InputCodepoint->Codepoint, InputCodepoint->Config, InputCodepointIndex); + } + } + + FoundBreak:; + + if(Result) + { + kbts_shape_config *Config = 0; + KBTS__FOR(ExistingConfigIndex, 0, Context->ExistingShapeConfigCount) + { + kbts__existing_shape_config *Existing = &Context->ExistingShapeConfigs[ExistingConfigIndex]; + + if((Existing->Font == RunFont) && + (Existing->Script == RunScript)) + { + Config = Existing->Config; + + break; + } + } + + if(!Config) + { + Config = kbts_CreateShapeConfig(RunFont, RunScript, Language, kbts__ArenaAllocator, &Context->ScratchArena); + + if(Context->ExistingShapeConfigCount < KBTS__ARRAY_LENGTH(Context->ExistingShapeConfigs)) + { + kbts__existing_shape_config *NewExisting = &Context->ExistingShapeConfigs[Context->ExistingShapeConfigCount++]; + + NewExisting->Config = Config; + NewExisting->Font = RunFont; + NewExisting->Script = RunScript; + } + } + + if(!RunDirection) + { + RunDirection = KBTS_DIRECTION_LTR; + + if((Config->Shaper == KBTS_SHAPER_ARABIC) || + (Config->Shaper == KBTS_SHAPER_HEBREW)) + { + RunDirection = KBTS_DIRECTION_RTL; + } + } + + kbts__shape_scratchpad *Scratchpad = kbts__PushType(&Context->ScratchArena, kbts__shape_scratchpad); + KBTS_MEMSET(Scratchpad, 0, sizeof(*Scratchpad)); + Scratchpad->Arena = &Context->ScratchArena; + Scratchpad->RunDirection = RunDirection; + + kbts__ShapeDirect(Scratchpad, Config, &Context->GlyphStorage); + + if(Scratchpad->Error) + { + Context->Error = Scratchpad->Error; } } } else { - goto Error; + kbts_shape_codepoint *OnePastLast = kbts__InputCodepoint(Context, Context->InputCodepointCount); + + if(OnePastLast->BreakFlags & KBTS_BREAK_FLAG_LINE_HARD) + { + // Signal the terminating line break with a 0-sized run. + Run->Flags = OnePastLast->BreakFlags; + + Result = 1; + } + + Context->DoneShapingRuns = 1; } - if(kbts_FontIsValid(Font)) + if(Result) { - Result = sizeof(kbts_u32) * ((ShapingTableSizes[KBTS_SHAPING_TABLE_GSUB] + ShapingTableSizes[KBTS_SHAPING_TABLE_GPOS] + GdefSize) / 2); + Run->Font = RunFont; + Run->Script = RunScript; + Run->ParagraphDirection = RunParagraphDirection; + Run->Direction = RunDirection; + Run->Glyphs = kbts_ActiveGlyphIterator(&Context->GlyphStorage); } } - else - { - goto Error; - } - if(0) - { - Error:; - Font->Error = 1; - } - - return (kbts_u32)Result; -} - -KBTS_EXPORT kbts_un kbts_ReadFontData(kbts_font *Font, void *Scratch, kbts_un ScratchSize) -{ - kbts_un Result = 0; - if(kbts_FontIsValid(Font)) - { - kbts_byteswap_context ByteSwapContext = KBTS_ZERO; - ByteSwapContext.FileBase = Font->FileBase; - ByteSwapContext.FileEnd = Font->FileBase + Font->FileSize; - ByteSwapContext.PointerCapacity = ScratchSize / sizeof(kbts_u32); - ByteSwapContext.Pointers = (kbts_u32 *)Scratch; - - kbts_un TotalLookupCount = 0; - kbts_un TotalSubtableCount = 0; - - kbts_gdef *Gdef = Font->Gdef; - if(Gdef) - { - if(Gdef->ClassDefinitionOffset) - { - kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->ClassDefinitionOffset); - kbts_ByteSwapClassDefinition(&ByteSwapContext, ClassDefBase); - } - - if(Gdef->MarkAttachmentClassDefinitionOffset) - { - kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->MarkAttachmentClassDefinitionOffset); - kbts_ByteSwapClassDefinition(&ByteSwapContext, ClassDefBase); - } - - if((Gdef->Minor >= 2) && Gdef->MarkGlyphSetsDefinitionOffset) - { - kbts_mark_glyph_sets *MarkGlyphSets = KBTS_POINTER_OFFSET(kbts_mark_glyph_sets, Gdef, Gdef->MarkGlyphSetsDefinitionOffset); - kbts_ByteSwapArray16Context(&MarkGlyphSets->Format, 2, &ByteSwapContext); - if(MarkGlyphSets->Format == 1) - { - kbts_u32 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u32, MarkGlyphSets); - kbts_ByteSwapArray32Context(CoverageOffsets, MarkGlyphSets->MarkGlyphSetCount, &ByteSwapContext); - - KBTS_FOR(MarkGlyphSetIndex, 0, MarkGlyphSets->MarkGlyphSetCount) - { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, MarkGlyphSets, CoverageOffsets[MarkGlyphSetIndex]); - kbts_ByteSwapCoverage(&ByteSwapContext, Coverage); - } - } - } - } - - kbts_gsub_gpos *Gsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; - if(Gsub) - { - kbts_ByteSwapGsubGposCommon(&ByteSwapContext, Gsub); - - kbts_lookup_list *LookupList = kbts_GetLookupList(Gsub); - LookupList->Count = kbts_ByteSwap16(LookupList->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, LookupList), LookupList->Count, &ByteSwapContext); - - TotalLookupCount += LookupList->Count; - - KBTS_FOR(LookupIndex, 0, LookupList->Count) - { - kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); - - KBTS_DUMPF("GSUB Lookup %llu:\n", LookupIndex); - - if(kbts_ByteSwapLookup(&ByteSwapContext, PackedLookup)) - { - kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); - KBTS_DUMPF(" Flags %u\n", Lookup.Flags); - - KBTS_FOR(SubstitutionIndex, 0, Lookup.SubtableCount) - { - kbts_u16 *Base = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubstitutionIndex]); - - KBTS_DUMPF(" Subtable %llu:\n", SubstitutionIndex); - - kbts_ByteSwapGsubLookupSubtable(&ByteSwapContext, Lookup.Type, Base); - } - } - - TotalSubtableCount += PackedLookup->SubtableCount; - } - } - - kbts_gsub_gpos *Gpos = Font->ShapingTables[KBTS_SHAPING_TABLE_GPOS]; - if(Gpos) - { - kbts_ByteSwapGsubGposCommon(&ByteSwapContext, Gpos); - - kbts_lookup_list *LookupList = kbts_GetLookupList(Gpos); - LookupList->Count = kbts_ByteSwap16(LookupList->Count); - kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, LookupList), LookupList->Count, &ByteSwapContext); - - TotalLookupCount += LookupList->Count; - - KBTS_FOR(LookupIndex, 0, LookupList->Count) - { - kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); - - KBTS_DUMPF("GPOS Lookup %llu:\n", LookupIndex); - - if(kbts_ByteSwapLookup(&ByteSwapContext, PackedLookup)) - { - kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); - - KBTS_DUMPF(" Flags %x\n", Lookup.Flags); - - KBTS_FOR(SubstitutionIndex, 0, Lookup.SubtableCount) - { - kbts_u16 *Base = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubstitutionIndex]); - - KBTS_DUMPF(" Subtable %llu:\n", (kbts_un)SubstitutionIndex); - - kbts_ByteSwapGposLookupSubtable(&ByteSwapContext, LookupList, Lookup.Type, Base); - } - } - - TotalSubtableCount += PackedLookup->SubtableCount; - } - } - - // At this point, we are done byteswapping the file, so we can start reusing the scratch memory. - - if(Gsub) - { - kbts_lookup_list *LookupList = kbts_GetLookupList(Gsub); - - // Figure out lookup recursion depth and other useful metrics. - // Most of these are not used yet, but would be useful for a streaming shaper and/or to inform GLYPH_BUFFER_GROW_MARGIN. - kbts_un MaximumBacktrackWithoutSkippingGlyphs = 0; - kbts_un MaximumLookaheadWithoutSkippingGlyphs = 0; - kbts_un MaximumLookupStackSize = 1; - kbts_un MaximumSubstitutionOutputSize = 1; - kbts_un MaximumInputSequenceLength = 1; - - // We are done byteswapping the file, so we can reclaim the scratch memory. - kbts_lookup_info_frame *Frames = (kbts_lookup_info_frame *)Scratch; - kbts_un FrameCapacity = ScratchSize / sizeof(kbts_lookup_info_frame); - - KBTS_FOR(RootLookupIndex, 0, LookupList->Count) - { - kbts_lookup *PackedRootLookup = kbts_GetLookup(LookupList, RootLookupIndex); - kbts_unpacked_lookup RootLookup = kbts_UnpackLookup(Gdef, PackedRootLookup); - - KBTS_FOR(RootSubtableIndex, 0, RootLookup.SubtableCount) - { - kbts_un FrameCount = 0; - { - kbts_lookup_info_frame First = KBTS_ZERO; - First.LookupType = RootLookup.Type; - First.Base = KBTS_POINTER_OFFSET(kbts_u16, PackedRootLookup, RootLookup.SubtableOffsets[RootSubtableIndex]); - First.StackSize = 1; - if(FrameCount < FrameCapacity) - { - Frames[FrameCount++] = First; - } - else - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - - while(FrameCount) - { - kbts_lookup_info_frame Frame = Frames[--FrameCount]; - kbts_u16 LookupType = Frame.LookupType; - kbts_u16 *Base = Frame.Base; - kbts_u32 LookaheadOffset = Frame.LookaheadOffset; - kbts_u32 StackSize = Frame.StackSize; - - MaximumLookupStackSize = KBTS_MAX(MaximumLookupStackSize, StackSize); - - while(LookupType == 7) - { - kbts_extension *Subst = (kbts_extension *)Base; - - Base = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->Offset); - LookupType = Subst->LookupType; - } - - switch(LookupType) - { - case 2: - { - kbts_multiple_substitution *Subst = (kbts_multiple_substitution *)Base; - KBTS_FOR(SequenceIndex, 0, Subst->SequenceCount) - { - kbts_sequence *Sequence = kbts_GetSequence(Subst, SequenceIndex); - - MaximumSubstitutionOutputSize = KBTS_MAX(MaximumSubstitutionOutputSize, Sequence->GlyphCount); - } - } break; - - case 4: - { - kbts_ligature_substitution *Subst = (kbts_ligature_substitution *)Base; - - KBTS_FOR(SetIndex, 0, Subst->LigatureSetCount) - { - kbts_ligature_set *Set = kbts_GetLigatureSet(Subst, SetIndex); - - KBTS_FOR(LigatureIndex, 0, Set->Count) - { - kbts_ligature *Ligature = kbts_GetLigature(Set, LigatureIndex); - - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Ligature->ComponentCount - 1); - } - } - } break; - - case 5: - { - if(Base[0] == 1) - { - kbts_sequence_context_1 *Subst = (kbts_sequence_context_1 *)Base; - - KBTS_FOR(SetIndex, 0, Subst->SeqRuleSetCount) - { - kbts_sequence_rule_set *Set = kbts_GetSequenceRuleSet(Subst, SetIndex); - - KBTS_FOR(RuleIndex, 0, Set->Count) - { - kbts_sequence_rule *Rule = kbts_GetSequenceRule(Set, RuleIndex); - - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Rule->GlyphCount - 1); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Rule->GlyphCount); - - kbts_u16 *InputSequence = KBTS_POINTER_AFTER(kbts_u16, Rule); - kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); - KBTS_FOR(RecordIndex, 0, Rule->SequenceLookupCount) - { - kbts_sequence_lookup_record *Record = &Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - } - } - } - else if(Base[0] == 2) - { - kbts_sequence_context_2 *Subst = (kbts_sequence_context_2 *)Base; - - KBTS_FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) - { - kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, SetIndex); - - if(Set) - { - KBTS_FOR(RuleIndex, 0, Set->Count) - { - kbts_class_sequence_rule *Rule = kbts_GetClassSequenceRule(Set, RuleIndex); - - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Rule->GlyphCount - 1); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Rule->GlyphCount); - - kbts_u16 *InputSequence = KBTS_POINTER_AFTER(kbts_u16, Rule); - kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); - - KBTS_FOR(RecordIndex, 0, Rule->SequenceLookupCount) - { - kbts_sequence_lookup_record *Record = &Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - } - } - } - } - else if(Base[0] == 3) - { - kbts_sequence_context_3 *Subst = (kbts_sequence_context_3 *)Base; - - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Subst->GlyphCount - 1); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Subst->GlyphCount); - - kbts_u16 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); - kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(CoverageOffsets + Subst->GlyphCount); - - KBTS_FOR(RecordIndex, 0, Subst->SequenceLookupCount) - { - kbts_sequence_lookup_record *Record = &Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - } - } break; - - case 6: - { - if(Base[0] == 1) - { - kbts_chained_sequence_context_1 *Subst = (kbts_chained_sequence_context_1 *)Base; - - KBTS_FOR(SetIndex, 0, Subst->ChainedSequenceRuleSetCount) - { - kbts_chained_sequence_rule_set *Set = kbts_GetChainedSequenceRuleSet(Subst, SetIndex); - - if(Set) - { - KBTS_FOR(RuleIndex, 0, Set->Count) - { - kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); - - MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); - - KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) - { - kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - } - } - } - } - else if(Base[0] == 2) - { - kbts_chained_sequence_context_2 *Subst = (kbts_chained_sequence_context_2 *)Base; - - KBTS_FOR(SetIndex, 0, Subst->ChainedClassSequenceRuleSetCount) - { - // @Duplication with 6.1. - kbts_chained_sequence_rule_set *Set = kbts_GetChainedClassSequenceRuleSet(Subst, SetIndex); - - if(Set) - { - KBTS_FOR(RuleIndex, 0, Set->Count) - { - kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); - - MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); - - KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) - { - kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - } - } - } - } - else if(Base[0] == 3) - { - kbts_chained_sequence_context_3 *Subst = (kbts_chained_sequence_context_3 *)Base; - kbts_unpacked_chained_sequence_context_3 Unpacked = kbts_UnpackChainedSequenceContext3(Subst, 0); - - MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); - - KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) - { - kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - } - } break; - - case 8: - { - kbts_reverse_chain_substitution *Subst = (kbts_reverse_chain_substitution *)Base; - kbts_unpacked_reverse_chain_substitution Unpacked = kbts_UnpackReverseChainSubstitution(Subst, 0); - - MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); - } break; - } - } - } - } - - DoneGatheringLookupInfo:; - Font->LookupInfo.MaximumBacktrackWithoutSkippingGlyphs = (kbts_u32)MaximumBacktrackWithoutSkippingGlyphs; - Font->LookupInfo.MaximumLookaheadWithoutSkippingGlyphs = (kbts_u32)MaximumLookaheadWithoutSkippingGlyphs; - Font->LookupInfo.MaximumSubstitutionOutputSize = (kbts_u32)MaximumSubstitutionOutputSize; - Font->LookupInfo.MaximumInputSequenceLength = (kbts_u32)MaximumInputSequenceLength; - Font->LookupInfo.MaximumLookupStackSize = (kbts_u32)MaximumLookupStackSize; - } - - Font->LookupCount = (kbts_u32)TotalLookupCount; - Font->SubtableCount = (kbts_u32)TotalSubtableCount; - - kbts_un GlyphLookupMatrixSizeInBytes = ((((TotalLookupCount * Font->GlyphCount) + 7) / 8) + 3) & ~3; - kbts_un GlyphLookupSubtableMatrixSizeInBytes = ((((TotalSubtableCount * Font->GlyphCount) + 7) / 8) + 3) & ~3; - Result = GlyphLookupMatrixSizeInBytes + GlyphLookupSubtableMatrixSizeInBytes + sizeof(kbts_u32) * TotalLookupCount + sizeof(kbts_lookup_subtable_info) * TotalSubtableCount; - - Font->Error |= ByteSwapContext.Error; - } return Result; } -KBTS_EXPORT int kbts_PostReadFontInitialize(kbts_font *Font, void *Memory, kbts_un MemorySize) +KBTS_EXPORT int kbts_GlyphIteratorIsValid(kbts_glyph_iterator *It) { - if(kbts_FontIsValid(Font) && Font->Maxp) + int Result = It->CurrentGlyph && kbts__GlyphIsValid(It->GlyphStorage, It->CurrentGlyph); + return Result; +} + +KBTS_EXPORT int kbts_GlyphIteratorNext(kbts_glyph_iterator *It, kbts_glyph **Glyph) +{ + int Result = 0; + kbts_glyph *CurrentGlyph = It->CurrentGlyph; + + if(kbts_GlyphIteratorIsValid(It)) { - // Bake table filters. - kbts_un GlyphCount = Font->GlyphCount; + *Glyph = CurrentGlyph; - kbts_un GlyphLookupMatrixSizeInBits = Font->LookupCount * GlyphCount; - kbts_un GlyphLookupMatrixSizeInBytes = (GlyphLookupMatrixSizeInBits + 7) / 8; - GlyphLookupMatrixSizeInBytes = (GlyphLookupMatrixSizeInBytes + 3) & ~3; // Align to u32 + Result = 1; + It->CurrentGlyph = CurrentGlyph->Next; + } - kbts_un GlyphLookupSubtableMatrixSizeInBits = Font->SubtableCount * GlyphCount; - kbts_un GlyphLookupSubtableMatrixSizeInBytes = (GlyphLookupSubtableMatrixSizeInBits + 7) / 8; - GlyphLookupSubtableMatrixSizeInBytes = (GlyphLookupSubtableMatrixSizeInBytes + 3) & ~3; // Align to u32 + return Result; +} - KBTS_MEMSET(Memory, 0, MemorySize); +KBTS_EXPORT int kbts_FontCount(void *Data, int Size) +{ + int Result = 0; - kbts_u32 *GlyphLookupMatrix = (kbts_u32 *)Memory; - kbts_u32 *GlyphLookupSubtableMatrix = KBTS_POINTER_OFFSET(kbts_u32, GlyphLookupMatrix, GlyphLookupMatrixSizeInBytes); - kbts_u32 *LookupSubtableIndexOffsets = KBTS_POINTER_OFFSET(kbts_u32, GlyphLookupSubtableMatrix, GlyphLookupSubtableMatrixSizeInBytes); - kbts_lookup_subtable_info *SubtableInfos = KBTS_POINTER_OFFSET(kbts_lookup_subtable_info, LookupSubtableIndexOffsets, sizeof(kbts_u32) * Font->LookupCount); - kbts_un GposLookupIndexOffset = 0; + if(Data && (Size >= 4)) + { + kbts_u32 Magic = *(kbts_u32 *)Data; - kbts_un RunningLookupIndex = 0; - kbts_un RunningSubtableIndex = 0; - - KBTS_FOR(ShapingTableIndex, 0, KBTS_SHAPING_TABLE_COUNT) + if(Magic == KBTS_FOURCC('t', 't', 'c', 'f')) { - kbts_gsub_gpos *ShapingTable = Font->ShapingTables[ShapingTableIndex]; + kbts__ttc_header *Header = (kbts__ttc_header *)Data; - if(ShapingTableIndex == KBTS_SHAPING_TABLE_GPOS) + if(Header->Magic == KBTS_FOURCC('t', 't', 'c', 'f')) { - GposLookupIndexOffset = RunningLookupIndex; + Result = (int)kbts__ByteSwap32(Header->FontCount); } + } + else if((Magic == KBTS_FOURCC('O', 'T', 'T', 'O')) || + (Magic == KBTS__U32BE(0x10000)) || + (Magic == KBTS_FOURCC('k', 'b', 't', 's'))) + { + Result = 1; + } + } - if(ShapingTable) + return Result; +} + +static kbts__cmap_subtable_pointer kbts__SelectCmapSubtable(kbts_blob_header *Header, kbts_blob_table *CmapTable, kbts__cmap_14 **Cmap14, kbts_u16 *ResultFormat) +{ + kbts__cmap_subtable_pointer Result = KBTS__ZERO; + + if(CmapTable->Length >= sizeof(kbts__cmap)) + { + char *TableEnd = KBTS__POINTER_OFFSET(char, Header, CmapTable->OffsetFromStartOfFile + CmapTable->Length); + kbts__cmap *Cmap = KBTS__POINTER_OFFSET(kbts__cmap, Header, CmapTable->OffsetFromStartOfFile); + + kbts_u16 PreferredFormat = 1; + KBTS__FOR(It, 0, Cmap->TableCount) + { + kbts__cmap_subtable_pointer Subtable = kbts__GetCmapSubtable(Cmap, It); + if((char *)(Subtable.Subtable + 1) <= TableEnd) { - kbts_lookup_list *LookupList = kbts_GetLookupList(ShapingTable); - KBTS_FOR(LookupIndex, 0, LookupList->Count) + kbts_u16 Format = *Subtable.Subtable; + + // This is kind of iffy, but the statelessness is useful for selecting + // the cmap from an already-prepared blob without having to deal with + // the byteswap context. + if((Format > 0xFF) && + ((Format >> 8) <= 14)) { - kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); - kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); + Format = kbts__ByteSwap16(Format); + *Subtable.Subtable = Format; + } - LookupSubtableIndexOffsets[RunningLookupIndex] = (kbts_u32)RunningSubtableIndex; - - KBTS_FOR(SubtableIndex, 0, Lookup.SubtableCount) + if(Format == 14) + { + if((char *)(Subtable.Subtable + sizeof(kbts__cmap_14)) <= TableEnd) { - kbts_lookup_subtable_info SubtableInfo = KBTS_ZERO; - kbts_u32 LookupType = Lookup.Type; - kbts_u16 *Base = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubtableIndex]); - - while(((ShapingTableIndex == KBTS_SHAPING_TABLE_GSUB) && (LookupType == 7)) || - ((ShapingTableIndex == KBTS_SHAPING_TABLE_GPOS) && (LookupType == 9))) + if(Cmap14) { - kbts_extension *Extension = (kbts_extension *)Base; - LookupType = Extension->LookupType; - Base = KBTS_POINTER_OFFSET(kbts_u16, Extension, Extension->Offset); + *Cmap14 = (kbts__cmap_14 *)Subtable.Subtable; } - - KBTS_FOR(GlyphIndex, 0, GlyphCount) - { - kbts_glyph Glyph = KBTS_ZERO; - Glyph.Id = (kbts_u16)GlyphIndex; - Glyph.Classes = kbts_GlyphClasses(Font, Glyph.Id); - - // @Duplication - kbts_cover_glyph_result Cover = KBTS_ZERO; - Cover.Valid = kbts_GlyphPassesLookupFilter(&Glyph, &Lookup); - if(Cover.Valid && kbts_LookupBeginsWithCoverage((kbts_shaping_table)ShapingTableIndex, Lookup.Type, Base[0])) - { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Base, Base[1]); - Cover = kbts_CoverGlyph(Coverage, Glyph.Id); - } - - // The "primary" slot here refers to the "current glyph" when matching. - // It is the first glyph in the input sequence, not counting backtrack. - // We use the primary slot for per-feature filtering. - int InPrimary = Cover.Valid; - // The secondary slot is anything else: input sequence, lookahead, backtrack, ligature components. - // They are used for subtable filtering. - int InSecondary = 0; - - if((ShapingTableIndex == KBTS_SHAPING_TABLE_GSUB) && (LookupType == 4)) - { - kbts_ligature_substitution *Subst = (kbts_ligature_substitution *)Base; - KBTS_FOR(SetIndex, 0, Subst->LigatureSetCount) - { - kbts_ligature_set *Set = kbts_GetLigatureSet(Subst, SetIndex); - KBTS_FOR(LigatureIndex, 0, Set->Count) - { - kbts_ligature *Ligature = kbts_GetLigature(Set, LigatureIndex); - kbts_u16 *Ids = KBTS_POINTER_AFTER(kbts_u16, Ligature); - - SubtableInfo.MinimumFollowupPlusOne = KBTS_MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Ligature->ComponentCount - 1) + 1; - - KBTS_FOR(IdIndex, 1, Ligature->ComponentCount) - { - kbts_u16 LigatureGlyphId = Ids[IdIndex - 1]; - if(LigatureGlyphId == Glyph.Id) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - } - } - } - else if(((ShapingTableIndex == KBTS_SHAPING_TABLE_GSUB) && (LookupType == 5)) || - ((ShapingTableIndex == KBTS_SHAPING_TABLE_GPOS) && (LookupType == 7))) - { - if(Base[0] == 1) - { - kbts_sequence_context_1 *Subst = (kbts_sequence_context_1 *)Base; - - KBTS_FOR(SetIndex, 0, Subst->SeqRuleSetCount) - { - kbts_sequence_rule_set *Set = kbts_GetSequenceRuleSet(Subst, SetIndex); - - if(Set) - { - KBTS_FOR(RuleIndex, 0, Set->Count) - { - kbts_sequence_rule *Rule = kbts_GetSequenceRule(Set, RuleIndex); - SubtableInfo.MinimumFollowupPlusOne = KBTS_MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Rule->GlyphCount - 1) + 1; - - kbts_u16 *SequenceIds = KBTS_POINTER_AFTER(kbts_u16, Rule); - KBTS_FOR(InputIndex, 1, Rule->GlyphCount) - { - kbts_u16 SequenceId = SequenceIds[InputIndex - 1]; - if(SequenceId == Glyph.Id) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - } - } - } - } - else if(Base[0] == 2) - { - kbts_sequence_context_2 *Subst = (kbts_sequence_context_2 *)Base; - kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->ClassDefOffset); - kbts_glyph_class_from_table_result Class = kbts_GlyphClassFromTable(ClassDefBase, Glyph.Id); - - KBTS_FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) - { - kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, SetIndex); - if(Set) - { - KBTS_FOR(RuleIndex, 0, Set->Count) - { - kbts_class_sequence_rule *Rule = kbts_GetClassSequenceRule(Set, RuleIndex); - kbts_u16 *SequenceClasses = KBTS_POINTER_AFTER(kbts_u16, Rule); - - SubtableInfo.MinimumFollowupPlusOne = KBTS_MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Rule->GlyphCount - 1) + 1; - - KBTS_FOR(SequenceIndex, 1, Rule->GlyphCount) - { - if(SequenceClasses[SequenceIndex - 1] == Class.Class) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - } - } - } - } - else if(Base[0] == 3) - { - kbts_sequence_context_3 *Subst = (kbts_sequence_context_3 *)Base; - kbts_u16 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); - - SubtableInfo.MinimumFollowupPlusOne = KBTS_MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Subst->GlyphCount - 1) + 1; - - InPrimary = 0; - KBTS_FOR(CoverageIndex, 0, Subst->GlyphCount) - { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, CoverageOffsets[CoverageIndex]); - kbts_cover_glyph_result SequenceCover = kbts_CoverGlyph(Coverage, Glyph.Id); - if(SequenceCover.Valid) - { - if(!CoverageIndex) - { - InPrimary = 1; - } - else - { - InSecondary = 1; - } - goto DoneCheckingForInclusion; - } - } - } - } - else if(((ShapingTableIndex == KBTS_SHAPING_TABLE_GSUB) && (LookupType == 6)) || - ((ShapingTableIndex == KBTS_SHAPING_TABLE_GPOS) && (LookupType == 8))) - { - if(Base[0] == 1) - { - kbts_chained_sequence_context_1 *Subst = (kbts_chained_sequence_context_1 *)Base; - kbts_u16 *ChainedSequenceRuleSetOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); - - KBTS_FOR(SetIndex, 0, Subst->ChainedSequenceRuleSetCount) - { - kbts_chained_sequence_rule_set *Set = KBTS_POINTER_OFFSET(kbts_chained_sequence_rule_set, Subst, ChainedSequenceRuleSetOffsets[SetIndex]); - KBTS_FOR(RuleIndex, 0, Set->Count) - { - kbts_chained_sequence_rule *Rule = kbts_GetChainedClassSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); - - SubtableInfo.MinimumBacktrackPlusOne = KBTS_MIN(SubtableInfo.MinimumBacktrackPlusOne - 1, Unpacked.BacktrackCount) + 1; - SubtableInfo.MinimumFollowupPlusOne = KBTS_MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Unpacked.InputCount - 1 + Unpacked.LookaheadCount) + 1; - - KBTS_FOR(BacktrackIndex, 0, Unpacked.BacktrackCount) - { - if(Unpacked.Backtrack[BacktrackIndex] == Glyph.Id) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - KBTS_FOR(InputIndex, 1, Unpacked.InputCount) - { - if(Unpacked.Input[InputIndex - 1] == Glyph.Id) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - KBTS_FOR(LookaheadIndex, 0, Unpacked.LookaheadCount) - { - if(Unpacked.Lookahead[LookaheadIndex] == Glyph.Id) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - } - } - } - else if(Base[0] == 2) - { - kbts_chained_sequence_context_2 *Subst = (kbts_chained_sequence_context_2 *)Base; - kbts_u16 *BacktrackClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->BacktrackClassDefOffset); - kbts_u16 *InputClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->InputClassDefOffset); - kbts_u16 *LookaheadClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->LookaheadClassDefOffset); - - kbts_u16 BacktrackClass = kbts_GlyphClassFromTable(BacktrackClassDefinition, Glyph.Id).Class; - kbts_u16 InputClass = kbts_GlyphClassFromTable(InputClassDefinition, Glyph.Id).Class; - kbts_u16 LookaheadClass = kbts_GlyphClassFromTable(LookaheadClassDefinition, Glyph.Id).Class; - - KBTS_FOR(SetIndex, 0, Subst->ChainedClassSequenceRuleSetCount) - { - kbts_chained_sequence_rule_set *Set = kbts_GetChainedClassSequenceRuleSet(Subst, SetIndex); - if(Set) - { - KBTS_FOR(RuleIndex, 0, Set->Count) - { - kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); - - SubtableInfo.MinimumBacktrackPlusOne = KBTS_MIN(SubtableInfo.MinimumBacktrackPlusOne - 1, Unpacked.BacktrackCount) + 1; - SubtableInfo.MinimumFollowupPlusOne = KBTS_MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Unpacked.InputCount - 1 + Unpacked.LookaheadCount) + 1; - - KBTS_FOR(BacktrackIndex, 0, Unpacked.BacktrackCount) - { - if(Unpacked.Backtrack[BacktrackIndex] == BacktrackClass) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - KBTS_FOR(InputIndex, 1, Unpacked.InputCount) - { - if(Unpacked.Input[InputIndex - 1] == InputClass) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - KBTS_FOR(LookaheadIndex, 0, Unpacked.LookaheadCount) - { - if(Unpacked.Lookahead[LookaheadIndex] == LookaheadClass) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - } - } - } - } - else if(Base[0] == 3) - { - kbts_chained_sequence_context_3 *Subst = (kbts_chained_sequence_context_3 *)Base; - kbts_unpacked_chained_sequence_context_3 Unpacked = kbts_UnpackChainedSequenceContext3(Subst, 0); - - SubtableInfo.MinimumBacktrackPlusOne = KBTS_MIN(SubtableInfo.MinimumBacktrackPlusOne - 1, Unpacked.BacktrackCount) + 1; - SubtableInfo.MinimumFollowupPlusOne = KBTS_MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Unpacked.InputCount - 1 + Unpacked.LookaheadCount) + 1; - - KBTS_FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) - { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackCoverageIndex]); - kbts_cover_glyph_result SequenceCover = kbts_CoverGlyph(Coverage, Glyph.Id); - if(SequenceCover.Valid) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - - InPrimary = 0; - KBTS_FOR(InputCoverageIndex, 0, Unpacked.InputCount) - { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.InputCoverageOffsets[InputCoverageIndex]); - kbts_cover_glyph_result SequenceCover = kbts_CoverGlyph(Coverage, Glyph.Id); - if(SequenceCover.Valid) - { - if(!InputCoverageIndex) - { - InPrimary = 1; - } - else - { - InSecondary = 1; - } - goto DoneCheckingForInclusion; - } - } - - KBTS_FOR(LookaheadCoverageIndex, 0, Unpacked.LookaheadCount) - { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadCoverageIndex]); - kbts_cover_glyph_result SequenceCover = kbts_CoverGlyph(Coverage, Glyph.Id); - if(SequenceCover.Valid) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - } - } - else if((ShapingTableIndex == KBTS_SHAPING_TABLE_GSUB) && (LookupType == 8)) - { - kbts_reverse_chain_substitution *Subst = (kbts_reverse_chain_substitution *)Base; - kbts_unpacked_reverse_chain_substitution Unpacked = kbts_UnpackReverseChainSubstitution(Subst, 0); - - SubtableInfo.MinimumBacktrackPlusOne = KBTS_MIN(SubtableInfo.MinimumBacktrackPlusOne - 1, Unpacked.BacktrackCount) + 1; - SubtableInfo.MinimumFollowupPlusOne = KBTS_MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Unpacked.LookaheadCount) + 1; - - KBTS_FOR(BacktrackIndex, 0, Unpacked.BacktrackCount) - { - kbts_cover_glyph_result BacktrackCover = kbts_CoverGlyph(KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackIndex]), Glyph.Id); - if(BacktrackCover.Valid) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - KBTS_FOR(LookaheadIndex, 0, Unpacked.LookaheadCount) - { - kbts_cover_glyph_result LookaheadCover = kbts_CoverGlyph(KBTS_POINTER_OFFSET(kbts_coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadIndex]), Glyph.Id); - if(LookaheadCover.Valid) - { - InSecondary = 1; - goto DoneCheckingForInclusion; - } - } - } - DoneCheckingForInclusion:; - - if(InPrimary) - { - kbts_un FlatIndex = RunningLookupIndex * GlyphCount + GlyphIndex; - kbts_un WordIndex = FlatIndex / 32; - kbts_un BitIndex = FlatIndex % 32; - GlyphLookupMatrix[WordIndex] |= (1 << BitIndex); - } - if(InPrimary || InSecondary) - { - kbts_un FlatIndex = RunningSubtableIndex * GlyphCount + GlyphIndex; - kbts_un WordIndex = FlatIndex / 32; - kbts_un BitIndex = FlatIndex % 32; - GlyphLookupSubtableMatrix[WordIndex] |= (1 << BitIndex); - } - } - - SubtableInfos[RunningSubtableIndex] = SubtableInfo; - RunningSubtableIndex += 1; } + } + else if(!Result.Subtable) + { + Result = Subtable; + } + else if(Format < KBTS__ARRAY_LENGTH(kbts__CmapFormatPrecedence)) + { + kbts_u16 Precedence = kbts__CmapFormatPrecedence[Format]; + kbts_u16 PreferredPrecedence = kbts__CmapFormatPrecedence[PreferredFormat]; - RunningLookupIndex += 1; + if((Precedence > PreferredPrecedence) || ((Precedence == PreferredPrecedence) && (Subtable.PlatformId == 3))) + { + Result = Subtable; + if(ResultFormat) + { + *ResultFormat = Format; + } + } + } + } + } + } + + return Result; +} + +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_load_font_error Result = 0; + kbts_un ScratchSize = 0; + kbts_un OutputSize = 0; + + if(FontDataSize >= 4) + { + char *FileEnd = (char *)FontData + FontDataSize; + kbts_u32 Magic = *(kbts_u32 *)FontData; + kbts_un DirectoryOffset = 0; + + if(Magic == KBTS_FOURCC('t', 't', 'c', 'f')) + { + Magic = 0; + + if(FontDataSize >= (int)sizeof(kbts__ttc_header)) + { + kbts__ttc_header *Header = (kbts__ttc_header *)FontData; + kbts_un FontCount = kbts__ByteSwap32(Header->FontCount); + + if(((kbts_u32)FontIndex < FontCount) && + ((kbts_un)FontDataSize >= (sizeof(kbts__ttc_header) + sizeof(kbts_u32) * FontCount))) + { + kbts_u32 *TableDirectoryOffsets = KBTS__POINTER_AFTER(kbts_u32, Header); + DirectoryOffset = kbts__ByteSwap32(TableDirectoryOffsets[FontIndex]); + + Magic = KBTS__U32BE(0x10000); } } } - Font->SubtableInfos = SubtableInfos; - Font->GlyphLookupMatrix = GlyphLookupMatrix; - Font->GlyphLookupSubtableMatrix = GlyphLookupSubtableMatrix; - Font->LookupSubtableIndexOffsets = LookupSubtableIndexOffsets; - Font->GposLookupIndexOffset = (kbts_u32)GposLookupIndexOffset; - Font->GlyphCount = (kbts_u32)GlyphCount; + if((Magic == KBTS_FOURCC('O', 'T', 'T', 'O')) || + (Magic == KBTS__U32BE(0x10000))) + { + Result = KBTS_LOAD_FONT_ERROR_NEED_TO_CREATE_BLOB; + + State->FontData = FontData; + State->FontDataSize = (kbts_u32)FontDataSize; + + kbts__table_directory *Directory = KBTS__POINTER_OFFSET(kbts__table_directory, FontData, DirectoryOffset); + kbts_un DirectoryTableCount = kbts__ByteSwap16(Directory->TableCount); + + kbts__table_record *Tables = KBTS__POINTER_AFTER(kbts__table_record, Directory); + + kbts_un DirectoryTableCapacity = (kbts_un)(FileEnd - (char *)Tables) / sizeof(kbts__table_record); + if(DirectoryTableCount <= DirectoryTableCapacity) + { + for(kbts_un TableIndex = 0; TableIndex < DirectoryTableCount; ++TableIndex) + { + kbts__table_record *Table = &Tables[TableIndex]; + kbts_u32 TableOffset = kbts__ByteSwap32(Table->Offset); + kbts_u32 TableLength = kbts__ByteSwap32(Table->Length); + + void *TableBase = KBTS__POINTER_OFFSET(void, FontData, TableOffset); + char *TableEnd = (char *)TableBase + TableLength; + if(((char *)TableBase >= (char *)(Tables + DirectoryTableCount)) && (TableEnd <= FileEnd)) + { + kbts_blob_table_id TableId = 0; + + switch(Table->Tag) + { + case KBTS_FOURCC('h', 'e', 'a', 'd'): TableId = KBTS_BLOB_TABLE_ID_HEAD; break; + case KBTS_FOURCC('c', 'm', 'a', 'p'): TableId = KBTS_BLOB_TABLE_ID_CMAP; break; + case KBTS_FOURCC('G', 'D', 'E', 'F'): TableId = KBTS_BLOB_TABLE_ID_GDEF; break; + case KBTS_FOURCC('G', 'S', 'U', 'B'): TableId = KBTS_BLOB_TABLE_ID_GSUB; break; + case KBTS_FOURCC('G', 'P', 'O', 'S'): TableId = KBTS_BLOB_TABLE_ID_GPOS; break; + case KBTS_FOURCC('h', 'h', 'e', 'a'): TableId = KBTS_BLOB_TABLE_ID_HHEA; break; + case KBTS_FOURCC('v', 'h', 'e', 'a'): TableId = KBTS_BLOB_TABLE_ID_VHEA; break; + case KBTS_FOURCC('h', 'm', 't', 'x'): TableId = KBTS_BLOB_TABLE_ID_HMTX; break; + case KBTS_FOURCC('v', 'm', 't', 'x'): TableId = KBTS_BLOB_TABLE_ID_VMTX; break; + case KBTS_FOURCC('m', 'a', 'x', 'p'): TableId = KBTS_BLOB_TABLE_ID_MAXP; break; + case KBTS_FOURCC('O', 'S', '/', '2'): TableId = KBTS_BLOB_TABLE_ID_OS2; break; + case KBTS_FOURCC('n', 'a', 'm', 'e'): TableId = KBTS_BLOB_TABLE_ID_NAME; break; + } + + if(TableId) + { + kbts_blob_table *ReadTable = &State->Tables[TableId]; + ReadTable->OffsetFromStartOfFile = TableOffset; + ReadTable->Length = TableLength; + } + } + } + } + + { + kbts_un TotalLookupCount = 0; + kbts_un TotalSubtableCount = 0; + + kbts_blob_table *GsubGposTables[] = {&State->Tables[KBTS_BLOB_TABLE_ID_GSUB], &State->Tables[KBTS_BLOB_TABLE_ID_GPOS]}; + KBTS__FOR(GsubGposTableIndex, 0, KBTS__ARRAY_LENGTH(GsubGposTables)) + { + kbts_blob_table *Table = GsubGposTables[GsubGposTableIndex]; + + if(Table->Length) + { + kbts__gsub_gpos *GsubGpos = KBTS__POINTER_OFFSET(kbts__gsub_gpos, FontData, Table->OffsetFromStartOfFile); + kbts_lookup_list *LookupList = KBTS__POINTER_OFFSET(kbts_lookup_list, GsubGpos, kbts__ByteSwap16(GsubGpos->LookupListOffset)); + kbts_u16 *LookupOffsets = KBTS__POINTER_AFTER(kbts_u16, LookupList); + kbts_un LookupCount = kbts__ByteSwap16(LookupList->Count); + + KBTS__FOR(LookupIndex, 0, LookupCount) + { + kbts__lookup *Lookup = KBTS__POINTER_OFFSET(kbts__lookup, LookupList, kbts__ByteSwap16(LookupOffsets[LookupIndex])); + + TotalSubtableCount += kbts__ByteSwap16(Lookup->SubtableCount); + } + + TotalLookupCount += LookupCount; + } + } + + State->LookupCount = (kbts_u32)TotalLookupCount; + State->LookupSubtableCount = (kbts_u32)TotalSubtableCount; + } + + { + kbts_un GlyphCount = 0; + + kbts_blob_table *MaxpTable = &State->Tables[KBTS_BLOB_TABLE_ID_MAXP]; + if(MaxpTable->Length) + { + kbts__maxp *Maxp = KBTS__POINTER_OFFSET(kbts__maxp, FontData, MaxpTable->OffsetFromStartOfFile); + + GlyphCount = kbts__ByteSwap16(Maxp->GlyphCount); + } + + State->GlyphCount = (kbts_u32)GlyphCount; + } + + ScratchSize = (State->Tables[KBTS_BLOB_TABLE_ID_GSUB].Length + + State->Tables[KBTS_BLOB_TABLE_ID_GPOS].Length + + State->Tables[KBTS_BLOB_TABLE_ID_GDEF].Length) * sizeof(kbts_u32) / 2; + + kbts_un GlyphLookupMatrixSizeInBytes = ((((State->LookupCount * State->GlyphCount) + 7) / 8) + 3) & ~3u; + kbts_un GlyphLookupSubtableMatrixSizeInBytes = ((((State->LookupSubtableCount * State->GlyphCount) + 7) / 8) + 3) & ~3u; + OutputSize = sizeof(kbts_blob_header) + + sizeof(kbts_blob_table) * KBTS_BLOB_TABLE_ID_COUNT + + GlyphLookupMatrixSizeInBytes + + GlyphLookupSubtableMatrixSizeInBytes + + sizeof(kbts_u32) * State->LookupCount + + sizeof(kbts_lookup_subtable_info) * State->LookupSubtableCount; + + KBTS__FOR(TableId, 0, KBTS_BLOB_TABLE_ID_COUNT) + { + OutputSize += State->Tables[TableId].Length; + } + + *ScratchSize_ = (int)ScratchSize; + *OutputSize_ = (int)OutputSize; + + State->ScratchSize = (kbts_u32)ScratchSize; + State->GlyphLookupMatrixSizeInBytes = (kbts_u32)GlyphLookupMatrixSizeInBytes; + State->GlyphLookupSubtableMatrixSizeInBytes = (kbts_u32)GlyphLookupSubtableMatrixSizeInBytes; + State->TotalSize = (kbts_u32)OutputSize; + } + else if(Magic == KBTS_FOURCC('k', 'b', 't', 's')) + { + kbts_blob_header *Header = (kbts_blob_header *)FontData; + + if(Header->Version != KBTS_BLOB_VERSION_CURRENT) + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + + // @Incomplete: Bounds check or something. + + Font->Blob = Header; + kbts__cmap_subtable_pointer PreferredSubtable = kbts__SelectCmapSubtable(Header, &Header->Tables[KBTS_BLOB_TABLE_ID_CMAP], &Font->Cmap14, 0); + Font->Cmap = PreferredSubtable.Subtable; + } } - return kbts_FontIsValid(Font); + return Result; } KBTS_EXPORT int kbts_FontIsValid(kbts_font *Font) @@ -22817,10 +26646,1172 @@ KBTS_EXPORT int kbts_FontIsValid(kbts_font *Font) return Result; } -#ifndef KB_TEXT_SHAPE_NO_CRT -KBTS_EXPORT kbts_font kbts_FontFromFile(const char *FileName) +static void kbts__MarkMatrixCoverage(kbts_u32 *Matrix, kbts_un TableIndex, kbts_un TableCount, kbts_un GlyphCount, kbts__coverage *Coverage, int SubtableMatrix) { - kbts_font Result = KBTS_ZERO; + if(Coverage) + { + if(Coverage->Format == 1) + { + kbts_u16 *GlyphIds = KBTS__POINTER_AFTER(kbts_u16, Coverage); + + KBTS__FOR(GlyphIndex, 0, Coverage->Count) + { + kbts_un GlyphId = GlyphIds[GlyphIndex]; + kbts__matrix_index MatrixIndex = SubtableMatrix ? + kbts__GlyphLookupSubtableMatrixIndex(TableIndex, TableCount, GlyphId) : + kbts__GlyphLookupMatrixIndex(TableIndex, GlyphId, GlyphCount); + + if(GlyphId < GlyphCount) + { + Matrix[MatrixIndex.WordIndex] |= 1 << MatrixIndex.BitIndex; + } + } + } + else if(Coverage->Format == 2) + { + kbts__range_record *Ranges = KBTS__POINTER_AFTER(kbts__range_record, Coverage); + + KBTS__FOR(RangeIndex, 0, Coverage->Count) + { + kbts__range_record *Range = &Ranges[RangeIndex]; + KBTS__FOR(GlyphId, Range->StartGlyphId, (kbts_un)Range->EndGlyphId + 1) + { + kbts__matrix_index MatrixIndex = SubtableMatrix ? + kbts__GlyphLookupSubtableMatrixIndex(TableIndex, TableCount, GlyphId) : + kbts__GlyphLookupMatrixIndex(TableIndex, GlyphId, GlyphCount); + + if(GlyphId < GlyphCount) + { + Matrix[MatrixIndex.WordIndex] |= 1 << MatrixIndex.BitIndex; + } + } + } + } + } +} + +static void kbts__MarkMatrixClassDef(kbts_u32 *Matrix, kbts_un SubtableIndex, kbts_un SubtableCount, kbts_un GlyphCount, kbts_u16 *ClassDefBase, kbts_u64 *ClassesIncluded, kbts_un ClassesIncludedLength) +{ + if(ClassDefBase) + { + if(*ClassDefBase == 1) + { + kbts__class_definition_1 *ClassDef = (kbts__class_definition_1 *)ClassDefBase; + kbts_u16 *GlyphClasses = KBTS__POINTER_AFTER(kbts_u16, ClassDef); + + KBTS__FOR(GlyphIndex, 0, ClassDef->GlyphCount) + { + kbts_un GlyphId = ClassDef->StartGlyphId + GlyphIndex; + kbts_un GlyphClass = GlyphClasses[GlyphIndex]; + + if((GlyphId >= (ClassesIncludedLength * 64)) || + (ClassesIncluded[GlyphClass / 64] & (1ull << (GlyphClass % 64)))) + { + kbts__matrix_index SubtableMatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(SubtableIndex, SubtableCount, GlyphId); + Matrix[SubtableMatrixIndex.WordIndex] |= 1 << SubtableMatrixIndex.BitIndex; + } + } + } + else if(*ClassDefBase == 2) + { + kbts__class_definition_2 *ClassDef = (kbts__class_definition_2 *)ClassDefBase; + kbts__class_range_record *Ranges = KBTS__POINTER_AFTER(kbts__class_range_record, ClassDef); + + KBTS__FOR(RangeIndex, 0, ClassDef->Count) + { + kbts__class_range_record *Range = &Ranges[RangeIndex]; + kbts_un RangeClass = Range->Class; + + if((RangeClass >= (ClassesIncludedLength * 64)) || + (ClassesIncluded[RangeClass / 64] & (1ull << (RangeClass % 64)))) + { + kbts_un OnePastLastGlyphId = Range->EndGlyphId + 1; + if(OnePastLastGlyphId > GlyphCount) + { + OnePastLastGlyphId = GlyphCount; + } + + KBTS__FOR(GlyphId, Range->StartGlyphId, OnePastLastGlyphId) + { + kbts__matrix_index SubtableMatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(SubtableIndex, SubtableCount, GlyphId); + Matrix[SubtableMatrixIndex.WordIndex] |= 1 << SubtableMatrixIndex.BitIndex; + } + } + } + } + } +} + +KBTS_EXPORT kbts_load_font_error kbts_PlaceBlob(kbts_font *Font, kbts_load_font_state *State, void *ScratchMemory, void *OutputMemory) +{ + kbts_load_font_error Result = 0; + + if(!Font) + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + + if(!ScratchMemory || !OutputMemory) + { + Result = KBTS_LOAD_FONT_ERROR_OUT_OF_MEMORY; + } + + if(Result == KBTS_LOAD_FONT_ERROR_NONE) + { + kbts_blob_header *Header = (kbts_blob_header *)OutputMemory; + *Header = KBTS__ZERO_TYPE(kbts_blob_header); + Header->Magic = KBTS_FOURCC('k', 'b', 't', 's'); + Header->Version = 1; + Header->LookupCount = State->LookupCount; + Header->LookupSubtableCount = State->LookupSubtableCount; + + // Stamp packed font data. + char *OutData = KBTS__POINTER_AFTER(char, Header); + KBTS__FOR(TableId, 0, KBTS_BLOB_TABLE_ID_COUNT) + { + kbts_blob_table InTable = State->Tables[TableId]; + kbts_blob_table *OutTable = &Header->Tables[TableId]; + + OutTable->OffsetFromStartOfFile = KBTS__POINTER_DIFF32(OutData, Header); + OutTable->Length = InTable.Length; + + void *InData = KBTS__POINTER_OFFSET(void, State->FontData, InTable.OffsetFromStartOfFile); + KBTS_MEMCPY(OutData, InData, InTable.Length); + + OutData += InTable.Length; + } + + // Byteswap it. + + { + kbts_blob_table *HeadTable = &Header->Tables[KBTS_BLOB_TABLE_ID_HEAD]; + + if(HeadTable->Length) + { + if(HeadTable->Length >= sizeof(kbts__head)) + { + kbts__head *Head = KBTS__POINTER_OFFSET(kbts__head, Header, HeadTable->OffsetFromStartOfFile); + + kbts__ByteSwapArray16Unchecked(&Head->Major, 2); + kbts__ByteSwapArray32Unchecked(&Head->Revision, 2); + // We do not swap the magic number. + kbts__ByteSwapArray16Unchecked(&Head->Flags, 2); + // We do not swap file times. + kbts__ByteSwapArray16Unchecked((kbts_u16 *)&Head->XMin, 9); + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + + Font->Blob = Header; + + { + kbts_blob_table *CmapTable = &Header->Tables[KBTS_BLOB_TABLE_ID_CMAP]; + + if(CmapTable->Length) + { + if(CmapTable->Length >= sizeof(kbts__cmap)) + { + int TableValid = 0; + char *TableEnd = KBTS__POINTER_OFFSET(char, Header, CmapTable->OffsetFromStartOfFile + CmapTable->Length); + kbts__cmap *Cmap = KBTS__POINTER_OFFSET(kbts__cmap, Header, CmapTable->OffsetFromStartOfFile); + Cmap->Version = kbts__ByteSwap16(Cmap->Version); + Cmap->TableCount = kbts__ByteSwap16(Cmap->TableCount); + + kbts__encoding_record *Records = KBTS__POINTER_AFTER(kbts__encoding_record, Cmap); + + if((char *)(Records + Cmap->TableCount) <= TableEnd) + { + KBTS__FOR(It, 0, Cmap->TableCount) + { + kbts__encoding_record *Record = &Records[It]; + Record->EncodingId = kbts__ByteSwap16(Record->EncodingId); + Record->PlatformId = kbts__ByteSwap16(Record->PlatformId); + Record->SubtableOffset = kbts__ByteSwap32(Record->SubtableOffset); + } + + kbts_u16 PreferredFormat = 1; + + kbts__cmap_subtable_pointer PreferredSubtable = kbts__SelectCmapSubtable(Header, CmapTable, &Font->Cmap14, &PreferredFormat); + if(PreferredSubtable.Subtable) + { + switch(*PreferredSubtable.Subtable) + { + case 0: + { + kbts__cmap_0 *Cmap0 = (kbts__cmap_0 *)PreferredSubtable.Subtable; + if((char *)(Cmap0 + 1) <= TableEnd) + { + Cmap0->Length = kbts__ByteSwap16(Cmap0->Length); + Cmap0->Language = kbts__ByteSwap16(Cmap0->Language); + TableValid = 1; + } + } + break; + + case 2: + { + kbts__cmap_2 *Cmap2 = (kbts__cmap_2 *)PreferredSubtable.Subtable; + if(kbts__ByteSwapArray16(&Cmap2->Length, 258, TableEnd)) + { + kbts_un SubHeaderCount = 0; + KBTS__FOR(It, 0, 256) + { + kbts_un SubHeaderIndex = Cmap2->SubHeaderKeys[It]; + SubHeaderCount = KBTS__MAX(SubHeaderCount, SubHeaderIndex + 1); + } + + kbts__sub_header *SubHeaders = KBTS__POINTER_AFTER(kbts__sub_header, Cmap2); + if(kbts__ByteSwapArray16(&SubHeaders->FirstCode, 4 * SubHeaderCount, TableEnd)) + { + kbts_u16 *GlyphIds = (kbts_u16 *)(SubHeaders + SubHeaderCount); + + kbts_sn GlyphIdCount = 0; + KBTS__FOR(It, 0, SubHeaderCount) + { + kbts__sub_header *SubHeader = &SubHeaders[It]; + + kbts_u16 *OnePastLastGlyphId = &SubHeader->IdRangeOffset + SubHeader->IdRangeOffset / 2 + SubHeader->EntryCount; + GlyphIdCount = KBTS__MAX(GlyphIdCount, OnePastLastGlyphId - GlyphIds); + } + + if(kbts__ByteSwapArray16(GlyphIds, (kbts_un)GlyphIdCount, TableEnd)) + { + TableValid = 1; + } + } + } + } + break; + + case 4: + { + kbts__cmap_4 *Cmap4 = (kbts__cmap_4 *)PreferredSubtable.Subtable; + if(kbts__ByteSwapArray16(&Cmap4->Length, 5, TableEnd) && + kbts__ByteSwapArray16(KBTS__POINTER_AFTER(kbts_u16, Cmap4), Cmap4->SegmentCountTimesTwo * 2 + 1, TableEnd)) + { + kbts_un SegmentCount = Cmap4->SegmentCountTimesTwo / 2; + kbts_u16 *EndCodes = KBTS__POINTER_AFTER(kbts_u16, Cmap4); + kbts_u16 *StartCodes = EndCodes + SegmentCount + 1; + kbts_s16 *IdDeltas = (kbts_s16 *)(StartCodes + SegmentCount); + kbts_u16 *IdRangeOffsets = (kbts_u16 *)(IdDeltas + SegmentCount); + kbts_u16 *GlyphIds = IdRangeOffsets + SegmentCount; + + kbts_sn GlyphIdCount = 0; + + KBTS__FOR(SegmentIndex, 0, SegmentCount) + { + kbts_u16 Offset = IdRangeOffsets[SegmentIndex]; + + if(Offset) + { + kbts_u16 *IdLookup = &IdRangeOffsets[SegmentIndex] + (EndCodes[SegmentIndex] - StartCodes[SegmentIndex] + 1) + Offset / 2; + + GlyphIdCount = KBTS__MAX(GlyphIdCount, (IdLookup - GlyphIds)); + } + } + + if(kbts__ByteSwapArray16(GlyphIds, (kbts_un)GlyphIdCount, TableEnd)) + { + TableValid = 1; + } + } + } + break; + + case 6: + { + kbts__cmap_6 *Cmap6 = (kbts__cmap_6 *)PreferredSubtable.Subtable; + if(kbts__ByteSwapArray16(&Cmap6->Length, 4, TableEnd) && + kbts__ByteSwapArray16(KBTS__POINTER_AFTER(kbts_u16, Cmap6), Cmap6->EntryCount, TableEnd)) + { + TableValid = 1; + } + } + break; + + case 12: + { + kbts__cmap_12_13 *Cmap12 = (kbts__cmap_12_13 *)PreferredSubtable.Subtable; + if(kbts__ByteSwapArray32(&Cmap12->Length, 3, TableEnd) && + kbts__ByteSwapArray32(KBTS__POINTER_AFTER(kbts_u32, Cmap12), Cmap12->GroupCount * 3, TableEnd)) + { + TableValid = 1; + } + } + break; + } + + Font->Cmap = PreferredSubtable.Subtable; + } + } + + if(!TableValid) + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + + { + kbts_blob_table *GdefTable = &Header->Tables[KBTS_BLOB_TABLE_ID_GDEF]; + + if(GdefTable->Length) + { + kbts__gdef *Gdef = KBTS__POINTER_OFFSET(kbts__gdef, Header, GdefTable->OffsetFromStartOfFile); + char *TableEnd = KBTS__POINTER_OFFSET(char, Header, GdefTable->OffsetFromStartOfFile + GdefTable->Length); + + if(kbts__ByteSwapArray16(&Gdef->Major, 6, TableEnd)) + { + if(Gdef->Minor >= 2) + { + if(GdefTable->Length >= 14) + { + Gdef->MarkGlyphSetsDefinitionOffset = kbts__ByteSwap16(Gdef->MarkGlyphSetsDefinitionOffset); + + if(Gdef->Minor == 3) + { + if(GdefTable->Length >= sizeof(kbts__gdef)) + { + // @Incomplete + Gdef->ItemVariationStoreOffset = kbts__ByteSwap32(Gdef->ItemVariationStoreOffset); + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + + { + kbts_blob_table_id HeaTableIds[2] = {KBTS_BLOB_TABLE_ID_HHEA, KBTS_BLOB_TABLE_ID_VHEA}; + KBTS__FOR(HeaTableIndex, 0, KBTS__ARRAY_LENGTH(HeaTableIds)) + { + kbts_blob_table *HeaTable = &Header->Tables[HeaTableIds[HeaTableIndex]]; + + if(HeaTable->Length) + { + kbts__hea *Hea = KBTS__POINTER_OFFSET(kbts__hea, Header, HeaTable->OffsetFromStartOfFile); + char *TableEnd = KBTS__POINTER_OFFSET(char, Header, HeaTable->OffsetFromStartOfFile + HeaTable->Length); + + if(!kbts__ByteSwapArray16((kbts_u16 *)Hea, sizeof(kbts__hea) / sizeof(kbts_u16), TableEnd)) + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + } + + { + kbts_blob_table_id MtxTableIds[2] = {KBTS_BLOB_TABLE_ID_HMTX, KBTS_BLOB_TABLE_ID_VMTX}; + + KBTS__FOR(MtxTableIndex, 0, KBTS__ARRAY_LENGTH(MtxTableIds)) + { + kbts_blob_table *MtxTable = &Header->Tables[MtxTableIds[MtxTableIndex]]; + + if(MtxTable->Length) + { + kbts_u16 *Mtx = KBTS__POINTER_OFFSET(kbts_u16, Header, MtxTable->OffsetFromStartOfFile); + kbts__ByteSwapArray16Unchecked(Mtx, MtxTable->Length / sizeof(kbts_u16)); + } + } + } + + { + kbts_blob_table *MaxpTable = &Header->Tables[KBTS_BLOB_TABLE_ID_MAXP]; + + if(MaxpTable->Length) + { + if(MaxpTable->Length >= 6) + { + kbts__maxp *Maxp = KBTS__POINTER_OFFSET(kbts__maxp, Header, MaxpTable->OffsetFromStartOfFile); + char *TableEnd = KBTS__POINTER_OFFSET(char, Header, MaxpTable->OffsetFromStartOfFile + MaxpTable->Length); + + Maxp->Major = kbts__ByteSwap16(Maxp->Major); + Maxp->Minor = kbts__ByteSwap16(Maxp->Minor); + + kbts_un U16Count = 0; + if(!Maxp->Major && (Maxp->Minor == 0x5000)) + { + U16Count = 1; + } + else if((Maxp->Major == 1) && !Maxp->Minor) + { + U16Count = 14; + } + + if(kbts__ByteSwapArray16(&Maxp->GlyphCount, U16Count, TableEnd)) + { + Header->GlyphCount = Maxp->GlyphCount; + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + + { + kbts_blob_table *Os2Table = &Header->Tables[KBTS_BLOB_TABLE_ID_OS2]; + + if(Os2Table->Length) + { + kbts__os2 *Os2 = KBTS__POINTER_OFFSET(kbts__os2, Header, Os2Table->OffsetFromStartOfFile); + kbts_un Length = Os2Table->Length; + + if(Length >= 68) + { + kbts__ByteSwapArray16Unchecked(&Os2->Version, 16); + kbts__ByteSwapArray32Unchecked(Os2->UnicodeRange, 4); + kbts__ByteSwapArray16Unchecked(&Os2->Selection, 3); + + kbts_un Version = Os2->Version; + + if(Length >= 78) + { + kbts__ByteSwapArray16Unchecked((kbts_u16 *)&Os2->TypoAscender, 5); + + if(Version >= 1) + { + if(Length >= 86) + { + kbts__ByteSwapArray32Unchecked(Os2->CodePageRange, 2); + + if(Version >= 2) + { + if(Length >= 96) + { + kbts__ByteSwapArray16Unchecked((kbts_u16 *)&Os2->Height, 5); + + if(Version >= 5) + { + if(Length >= 100) + { + kbts__ByteSwapArray16Unchecked(&Os2->LowerOpticalPointSize, 2); + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + } + } + else + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + // We should normally set Result to INVALID_FONT if there is no OS/2 table. + // However, one Harfbuzz test has a font with no OS/2 table. + } + + { + kbts_blob_table *NameTable = &Header->Tables[KBTS_BLOB_TABLE_ID_NAME]; + + if(NameTable->Length >= sizeof(kbts__name)) + { + kbts__name *Name = KBTS__POINTER_OFFSET(kbts__name, Header, NameTable->OffsetFromStartOfFile); + char *TableEnd = KBTS__POINTER_OFFSET(char, Name, NameTable->Length); + + kbts__ByteSwapArray16Unchecked(&Name->Version, 3); + + kbts__name_record *NameRecords = KBTS__POINTER_AFTER(kbts__name_record, Name); + + kbts_un U16Count = Name->Count * 6; + if(!kbts__ByteSwapArray16(&NameRecords->PlatformId, U16Count, TableEnd)) + { + Result = KBTS_LOAD_FONT_ERROR_INVALID_FONT; + } + } + // We should normally set Result to INVALID_FONT if there is no name table. + // However, one Harfbuzz test has a font with no name table. + } + + kbts__byteswap_context ByteSwapContext = KBTS__ZERO; + ByteSwapContext.FileBase = (char *)Header; + ByteSwapContext.FileEnd = (char *)Header + State->TotalSize; + ByteSwapContext.PointerCapacity = State->ScratchSize / sizeof(kbts_u32); + ByteSwapContext.Pointers = (kbts_u32 *)ScratchMemory; + + kbts_blob_table *GdefTable = &Header->Tables[KBTS_BLOB_TABLE_ID_GDEF]; + if(GdefTable->Length) + { + kbts__gdef *Gdef = KBTS__POINTER_OFFSET(kbts__gdef, Header, GdefTable->OffsetFromStartOfFile); + + if(Gdef->ClassDefinitionOffset) + { + kbts_u16 *ClassDefBase = KBTS__POINTER_OFFSET(kbts_u16, Gdef, Gdef->ClassDefinitionOffset); + kbts__ByteSwapClassDefinition(&ByteSwapContext, ClassDefBase); + } + + if(Gdef->MarkAttachmentClassDefinitionOffset) + { + kbts_u16 *ClassDefBase = KBTS__POINTER_OFFSET(kbts_u16, Gdef, Gdef->MarkAttachmentClassDefinitionOffset); + kbts__ByteSwapClassDefinition(&ByteSwapContext, ClassDefBase); + } + + if((Gdef->Minor >= 2) && Gdef->MarkGlyphSetsDefinitionOffset) + { + kbts__mark_glyph_sets *MarkGlyphSets = KBTS__POINTER_OFFSET(kbts__mark_glyph_sets, Gdef, Gdef->MarkGlyphSetsDefinitionOffset); + kbts__ByteSwapArray16Context(&MarkGlyphSets->Format, 2, &ByteSwapContext); + if(MarkGlyphSets->Format == 1) + { + kbts_u32 *CoverageOffsets = KBTS__POINTER_AFTER(kbts_u32, MarkGlyphSets); + kbts__ByteSwapArray32Context(CoverageOffsets, MarkGlyphSets->MarkGlyphSetCount, &ByteSwapContext); + + KBTS__FOR(MarkGlyphSetIndex, 0, MarkGlyphSets->MarkGlyphSetCount) + { + kbts__coverage *Coverage = KBTS__POINTER_OFFSET(kbts__coverage, MarkGlyphSets, CoverageOffsets[MarkGlyphSetIndex]); + kbts__ByteSwapCoverage(&ByteSwapContext, Coverage); + } + } + } + } + + kbts__gsub_gpos *Gsub = kbts__BlobTableDataType(Header, KBTS_BLOB_TABLE_ID_GSUB, kbts__gsub_gpos); + kbts__gsub_gpos *Gpos = kbts__BlobTableDataType(Header, KBTS_BLOB_TABLE_ID_GPOS, kbts__gsub_gpos); + kbts__gdef *Gdef = kbts__BlobTableDataType(Header, KBTS_BLOB_TABLE_ID_GDEF, kbts__gdef); + + if(Gsub) + { + kbts__ByteSwapGsubGposCommon(&ByteSwapContext, Gsub); + + kbts_lookup_list *LookupList = kbts__GetLookupList(Gsub); + LookupList->Count = kbts__ByteSwap16(LookupList->Count); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, LookupList), LookupList->Count, &ByteSwapContext); + + KBTS__FOR(LookupIndex, 0, LookupList->Count) + { + kbts__lookup *PackedLookup = kbts__GetLookup(LookupList, LookupIndex); + + KBTS_DUMPF("GSUB Lookup %llu:\n", LookupIndex); + + if(kbts__ByteSwapLookup(&ByteSwapContext, PackedLookup)) + { + kbts__unpacked_lookup Lookup = kbts__UnpackLookup(Gdef, PackedLookup); + KBTS_DUMPF(" Flags %u\n", Lookup.Flags); + + KBTS__FOR(SubstitutionIndex, 0, Lookup.SubtableCount) + { + kbts_u16 *Base = KBTS__POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubstitutionIndex]); + + KBTS_DUMPF(" Subtable %llu:\n", SubstitutionIndex); + + kbts__ByteSwapGsubLookupSubtable(&ByteSwapContext, Lookup.Type, Base); + } + } + } + } + + if(Gpos) + { + kbts__ByteSwapGsubGposCommon(&ByteSwapContext, Gpos); + + kbts_lookup_list *LookupList = kbts__GetLookupList(Gpos); + LookupList->Count = kbts__ByteSwap16(LookupList->Count); + kbts__ByteSwapArray16Context(KBTS__POINTER_AFTER(kbts_u16, LookupList), LookupList->Count, &ByteSwapContext); + + KBTS__FOR(LookupIndex, 0, LookupList->Count) + { + kbts__lookup *PackedLookup = kbts__GetLookup(LookupList, LookupIndex); + + KBTS_DUMPF("GPOS Lookup %llu:\n", LookupIndex); + + if(kbts__ByteSwapLookup(&ByteSwapContext, PackedLookup)) + { + kbts__unpacked_lookup Lookup = kbts__UnpackLookup(Gdef, PackedLookup); + + KBTS_DUMPF(" Flags %x\n", Lookup.Flags); + + KBTS__FOR(SubstitutionIndex, 0, Lookup.SubtableCount) + { + kbts_u16 *Base = KBTS__POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubstitutionIndex]); + + KBTS_DUMPF(" Subtable %llu:\n", (kbts_un)SubstitutionIndex); + + kbts__ByteSwapGposLookupSubtable(&ByteSwapContext, LookupList, Lookup.Type, Base); + } + } + } + } + + // At this point, we are done byteswapping the file, so we can start reusing scratch memory. + + // Bake our own data. + + if(!Result && Header->Tables[KBTS_BLOB_TABLE_ID_MAXP].Length) + { + kbts_un GlyphCount = Header->GlyphCount; + kbts_un LookupCount = Header->LookupCount; + kbts_un SubtableCount = Header->LookupSubtableCount; + + kbts_u32 *GlyphLookupMatrix = (kbts_u32 *)OutData; + kbts_u32 *GlyphLookupSubtableMatrix = KBTS__POINTER_OFFSET(kbts_u32, GlyphLookupMatrix, State->GlyphLookupMatrixSizeInBytes); + kbts_u32 *LookupSubtableIndexOffsets = KBTS__POINTER_OFFSET(kbts_u32, GlyphLookupSubtableMatrix, State->GlyphLookupSubtableMatrixSizeInBytes); + kbts_lookup_subtable_info *SubtableInfos = KBTS__POINTER_OFFSET(kbts_lookup_subtable_info, LookupSubtableIndexOffsets, sizeof(kbts_u32) * Header->LookupCount); + + KBTS_MEMSET(GlyphLookupMatrix, 0, State->GlyphLookupMatrixSizeInBytes + + State->GlyphLookupSubtableMatrixSizeInBytes + + sizeof(kbts_u32) * Header->LookupCount + + sizeof(kbts_lookup_subtable_info) * Header->LookupSubtableCount); + + kbts_un GposLookupIndexOffset = 0; + kbts_un RunningLookupIndex = 0; + kbts_un RunningSubtableIndex = 0; + + kbts_blob_table_id TableIds[2] = {KBTS_BLOB_TABLE_ID_GSUB, KBTS_BLOB_TABLE_ID_GPOS}; + KBTS__FOR(TableIdIndex, 0, KBTS__ARRAY_LENGTH(TableIds)) + { + kbts_blob_table_id TableId = TableIds[TableIdIndex]; + kbts_blob_table *Table = &Header->Tables[TableId]; + + if(Table->Length) + { + kbts__gsub_gpos *ShapingTable = KBTS__POINTER_OFFSET(kbts__gsub_gpos, Header, Table->OffsetFromStartOfFile); + int InGpos = (TableId == KBTS_BLOB_TABLE_ID_GPOS); + kbts_shaping_table ShapingTableId = (kbts_shaping_table)(InGpos ? KBTS_SHAPING_TABLE_GPOS : KBTS_SHAPING_TABLE_GSUB); + + if(InGpos) + { + GposLookupIndexOffset = RunningLookupIndex; + } + + if(ShapingTable) + { + kbts_lookup_list *LookupList = kbts__GetLookupList(ShapingTable); + KBTS__FOR(LookupIndex, 0, LookupList->Count) + { + kbts__lookup *PackedLookup = kbts__GetLookup(LookupList, LookupIndex); + kbts__unpacked_lookup Lookup = kbts__UnpackLookup(Gdef, PackedLookup); + + LookupSubtableIndexOffsets[RunningLookupIndex] = (kbts_u32)RunningSubtableIndex; + + KBTS__FOR(SubtableIndex, 0, Lookup.SubtableCount) + { + kbts_lookup_subtable_info SubtableInfo = KBTS__ZERO; + kbts_u16 LookupType = Lookup.Type; + kbts_u16 *Base = KBTS__POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubtableIndex]); + + while((!InGpos && (LookupType == 7)) || + (InGpos && (LookupType == 9))) + { + kbts__extension *Extension = (kbts__extension *)Base; + + // CAREFUL: Here, we remap LookupType only. + // Do not use Lookup.Type beyond this point! + LookupType = Extension->LookupType; + Base = KBTS__POINTER_OFFSET(kbts_u16, Extension, Extension->Offset); + } + + kbts__coverage *Coverage = 0; + + if(kbts__LookupBeginsWithCoverage(ShapingTableId, LookupType, Base[0])) + { + Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Base, Base[1]); + } + + if(!InGpos && (LookupType == 4)) + { + kbts__ligature_substitution *Subst = (kbts__ligature_substitution *)Base; + KBTS__FOR(SetIndex, 0, Subst->LigatureSetCount) + { + kbts__ligature_set *Set = kbts__GetLigatureSet(Subst, SetIndex); + KBTS__FOR(LigatureIndex, 0, Set->Count) + { + kbts__ligature *Ligature = kbts__GetLigature(Set, LigatureIndex); + kbts_u16 *Ids = KBTS__POINTER_AFTER(kbts_u16, Ligature); + + KBTS__FOR(IdIndex, 1, Ligature->ComponentCount) + { + kbts_un GlyphId = Ids[IdIndex - 1]; + + kbts__matrix_index SubtableMatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(RunningSubtableIndex, SubtableCount, GlyphId); + GlyphLookupSubtableMatrix[SubtableMatrixIndex.WordIndex] |= 1 << SubtableMatrixIndex.BitIndex; + } + + SubtableInfo.MinimumFollowupPlusOne = KBTS__MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Ligature->ComponentCount - 1) + 1; + } + } + } + else if((!InGpos && (LookupType == 5)) || + (InGpos && (LookupType == 7))) + { + switch(Base[0]) + { + case 1: + { + kbts__sequence_context_1 *Subst = (kbts__sequence_context_1 *)Base; + KBTS__FOR(SetIndex, 0, Subst->SeqRuleSetCount) + { + kbts__sequence_rule_set *Set = kbts__GetSequenceRuleSet(Subst, SetIndex); + + if(Set) + { + KBTS__FOR(RuleIndex, 0, Set->Count) + { + kbts__sequence_rule *Rule = kbts__GetSequenceRule(Set, RuleIndex); + SubtableInfo.MinimumFollowupPlusOne = KBTS__MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Rule->GlyphCount - 1) + 1; + + kbts_u16 *SequenceGlyphIds = KBTS__POINTER_AFTER(kbts_u16, Rule); + KBTS__FOR(InputIndex, 1, Rule->GlyphCount) + { + kbts_un GlyphId = SequenceGlyphIds[InputIndex - 1]; + + kbts__matrix_index SubtableMatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(RunningSubtableIndex, SubtableCount, GlyphId); + GlyphLookupSubtableMatrix[SubtableMatrixIndex.WordIndex] |= 1 << SubtableMatrixIndex.BitIndex; + } + } + } + } + } break; + + case 2: + { + kbts__sequence_context_2 *Subst = (kbts__sequence_context_2 *)Base; + kbts_u16 *ClassDefBase = KBTS__POINTER_OFFSET(kbts_u16, Subst, Subst->ClassDefOffset); + + kbts_u64 ClassesIncluded[16] = KBTS__ZERO; + + KBTS__FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) + { + kbts__class_sequence_rule_set *Set = kbts__GetClassSequenceRuleSet(Subst, SetIndex); + if(Set) + { + KBTS__FOR(RuleIndex, 0, Set->Count) + { + kbts__class_sequence_rule *Rule = kbts__GetClassSequenceRule(Set, RuleIndex); + kbts_u16 *SequenceClasses = KBTS__POINTER_AFTER(kbts_u16, Rule); + + SubtableInfo.MinimumFollowupPlusOne = KBTS__MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Rule->GlyphCount - 1) + 1; + + KBTS__FOR(SequenceIndex, 1, Rule->GlyphCount) + { + kbts_un Class = SequenceClasses[SequenceIndex - 1]; + + if(Class < (KBTS__ARRAY_LENGTH(ClassesIncluded) * 64)) + { + ClassesIncluded[Class / 64] |= 1ull << (Class % 64); + } + } + } + } + } + + kbts__MarkMatrixClassDef(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, ClassDefBase, ClassesIncluded, KBTS__ARRAY_LENGTH(ClassesIncluded)); + } break; + + case 3: + { + kbts__sequence_context_3 *Subst = (kbts__sequence_context_3 *)Base; + kbts_u16 *CoverageOffsets = KBTS__POINTER_AFTER(kbts_u16, Subst); + + SubtableInfo.MinimumFollowupPlusOne = KBTS__MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Subst->GlyphCount - 1) + 1; + + KBTS__FOR(CoverageIndex, 1, Subst->GlyphCount) + { + kbts__coverage *SubstCoverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, CoverageOffsets[CoverageIndex]); + + kbts__MarkMatrixCoverage(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, SubstCoverage, 1); + } + + Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, CoverageOffsets[0]); + } break; + } + } + else if((!InGpos && (LookupType == 6)) || + (InGpos && (LookupType == 8))) + { + switch(Base[0]) + { + case 1: + { + kbts__chained_sequence_context_1 *Subst = (kbts__chained_sequence_context_1 *)Base; + kbts_u16 *ChainedSequenceRuleSetOffsets = KBTS__POINTER_AFTER(kbts_u16, Subst); + + KBTS__FOR(SetIndex, 0, Subst->ChainedSequenceRuleSetCount) + { + kbts__chained_sequence_rule_set *Set = KBTS__POINTER_OFFSET(kbts__chained_sequence_rule_set, Subst, ChainedSequenceRuleSetOffsets[SetIndex]); + KBTS__FOR(RuleIndex, 0, Set->Count) + { + kbts__chained_sequence_rule *Rule = kbts__GetChainedClassSequenceRule(Set, RuleIndex); + kbts__unpacked_chained_sequence_rule Unpacked = kbts__UnpackChainedSequenceRule(Rule, 0); + + SubtableInfo.MinimumBacktrackPlusOne = KBTS__MIN(SubtableInfo.MinimumBacktrackPlusOne - 1, Unpacked.BacktrackCount) + 1; + SubtableInfo.MinimumFollowupPlusOne = KBTS__MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Unpacked.InputCount - 1 + Unpacked.LookaheadCount) + 1; + + KBTS__FOR(BacktrackIndex, 0, Unpacked.BacktrackCount) + { + kbts_un GlyphId = Unpacked.Backtrack[BacktrackIndex]; + kbts__matrix_index SubtableMatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(RunningSubtableIndex, SubtableCount, GlyphId); + GlyphLookupSubtableMatrix[SubtableMatrixIndex.WordIndex] |= 1 << SubtableMatrixIndex.BitIndex; + } + + KBTS__FOR(InputIndex, 1, Unpacked.InputCount) + { + kbts_un GlyphId = Unpacked.Input[InputIndex - 1]; + kbts__matrix_index SubtableMatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(RunningSubtableIndex, SubtableCount, GlyphId); + GlyphLookupSubtableMatrix[SubtableMatrixIndex.WordIndex] |= 1 << SubtableMatrixIndex.BitIndex; + } + + KBTS__FOR(LookaheadIndex, 0, Unpacked.LookaheadCount) + { + kbts_un GlyphId = Unpacked.Lookahead[LookaheadIndex]; + kbts__matrix_index SubtableMatrixIndex = kbts__GlyphLookupSubtableMatrixIndex(RunningSubtableIndex, SubtableCount, GlyphId); + GlyphLookupSubtableMatrix[SubtableMatrixIndex.WordIndex] |= 1 << SubtableMatrixIndex.BitIndex; + } + } + } + } break; + + case 2: + { + kbts__chained_sequence_context_2 *Subst = (kbts__chained_sequence_context_2 *)Base; + kbts_un BacktrackClassDefOffset = Subst->BacktrackClassDefOffset; + kbts_un InputClassDefOffset = Subst->InputClassDefOffset; + kbts_un LookaheadClassDefOffset = Subst->LookaheadClassDefOffset; + kbts_u16 *BacktrackClassDefinition = 0; + if(BacktrackClassDefOffset) + { + BacktrackClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, BacktrackClassDefOffset); + } + kbts_u16 *InputClassDefinition = 0; + if(InputClassDefOffset) + { + InputClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, InputClassDefOffset); + } + kbts_u16 *LookaheadClassDefinition = 0; + if(LookaheadClassDefOffset) + { + LookaheadClassDefinition = KBTS__POINTER_OFFSET(kbts_u16, Subst, LookaheadClassDefOffset); + } + + kbts_u64 BacktrackClassesIncluded[16] = KBTS__ZERO; + kbts_u64 InputClassesIncluded[16] = KBTS__ZERO; + kbts_u64 LookaheadClassesIncluded[16] = KBTS__ZERO; + + KBTS__FOR(SetIndex, 0, Subst->ChainedClassSequenceRuleSetCount) + { + kbts__chained_sequence_rule_set *Set = kbts__GetChainedClassSequenceRuleSet(Subst, SetIndex); + if(Set) + { + KBTS__FOR(RuleIndex, 0, Set->Count) + { + kbts__chained_sequence_rule *Rule = kbts__GetChainedSequenceRule(Set, RuleIndex); + kbts__unpacked_chained_sequence_rule Unpacked = kbts__UnpackChainedSequenceRule(Rule, 0); + + SubtableInfo.MinimumBacktrackPlusOne = KBTS__MIN(SubtableInfo.MinimumBacktrackPlusOne - 1, Unpacked.BacktrackCount) + 1; + SubtableInfo.MinimumFollowupPlusOne = KBTS__MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Unpacked.InputCount - 1 + Unpacked.LookaheadCount) + 1; + + KBTS__FOR(BacktrackIndex, 0, Unpacked.BacktrackCount) + { + kbts_un Class = Unpacked.Backtrack[BacktrackIndex]; + if(Class < (KBTS__ARRAY_LENGTH(BacktrackClassesIncluded) * 64)) + { + BacktrackClassesIncluded[Class / 64] |= 1ull << (Class % 64); + } + } + + KBTS__FOR(InputIndex, 1, Unpacked.InputCount) + { + kbts_un Class = Unpacked.Input[InputIndex - 1]; + if(Class < (KBTS__ARRAY_LENGTH(InputClassesIncluded) * 64)) + { + InputClassesIncluded[Class / 64] |= 1ull << (Class % 64); + } + } + + KBTS__FOR(LookaheadIndex, 0, Unpacked.LookaheadCount) + { + kbts_un Class = Unpacked.Lookahead[LookaheadIndex]; + if(Class < (KBTS__ARRAY_LENGTH(LookaheadClassesIncluded) * 64)) + { + LookaheadClassesIncluded[Class / 64] |= 1ull << (Class % 64); + } + } + } + } + } + + kbts__MarkMatrixClassDef(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, BacktrackClassDefinition, BacktrackClassesIncluded, KBTS__ARRAY_LENGTH(BacktrackClassesIncluded)); + kbts__MarkMatrixClassDef(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, InputClassDefinition, InputClassesIncluded, KBTS__ARRAY_LENGTH(InputClassesIncluded)); + kbts__MarkMatrixClassDef(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, LookaheadClassDefinition, LookaheadClassesIncluded, KBTS__ARRAY_LENGTH(LookaheadClassesIncluded)); + } break; + + case 3: + { + kbts__chained_sequence_context_3 *Subst = (kbts__chained_sequence_context_3 *)Base; + kbts__unpacked_chained_sequence_context_3 Unpacked = kbts__UnpackChainedSequenceContext3(Subst, 0); + + Coverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.InputCoverageOffsets[0]); + + SubtableInfo.MinimumBacktrackPlusOne = KBTS__MIN(SubtableInfo.MinimumBacktrackPlusOne - 1, Unpacked.BacktrackCount) + 1; + SubtableInfo.MinimumFollowupPlusOne = KBTS__MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Unpacked.InputCount - 1 + Unpacked.LookaheadCount) + 1; + + KBTS__FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) + { + kbts__coverage *SubCoverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackCoverageIndex]); + kbts__MarkMatrixCoverage(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, SubCoverage, 1); + } + + KBTS__FOR(InputCoverageIndex, 1, Unpacked.InputCount) + { + kbts__coverage *SubCoverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.InputCoverageOffsets[InputCoverageIndex]); + kbts__MarkMatrixCoverage(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, SubCoverage, 1); + } + + + KBTS__FOR(LookaheadCoverageIndex, 0, Unpacked.LookaheadCount) + { + kbts__coverage *SubCoverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadCoverageIndex]); + kbts__MarkMatrixCoverage(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, SubCoverage, 1); + } + } break; + } + } + else if(!InGpos && (LookupType == 8)) + { + kbts__reverse_chain_substitution *Subst = (kbts__reverse_chain_substitution *)Base; + kbts__unpacked_reverse_chain_substitution Unpacked = kbts__UnpackReverseChainSubstitution(Subst, 0); + SubtableInfo.MinimumBacktrackPlusOne = KBTS__MIN(SubtableInfo.MinimumBacktrackPlusOne - 1, Unpacked.BacktrackCount) + 1; + SubtableInfo.MinimumFollowupPlusOne = KBTS__MIN(SubtableInfo.MinimumFollowupPlusOne - 1, Unpacked.LookaheadCount) + 1; + + KBTS__FOR(BacktrackIndex, 0, Unpacked.BacktrackCount) + { + kbts__coverage *SubCoverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.BacktrackCoverageOffsets[BacktrackIndex]); + kbts__MarkMatrixCoverage(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, SubCoverage, 1); + } + + KBTS__FOR(LookaheadIndex, 0, Unpacked.LookaheadCount) + { + kbts__coverage *SubCoverage = KBTS__POINTER_OFFSET(kbts__coverage, Subst, Unpacked.LookaheadCoverageOffsets[LookaheadIndex]); + kbts__MarkMatrixCoverage(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, SubCoverage, 1); + } + } + + kbts__MarkMatrixCoverage(GlyphLookupMatrix, RunningLookupIndex, LookupCount, GlyphCount, Coverage, 0); + kbts__MarkMatrixCoverage(GlyphLookupSubtableMatrix, RunningSubtableIndex, SubtableCount, GlyphCount, Coverage, 1); + + SubtableInfos[RunningSubtableIndex] = SubtableInfo; + RunningSubtableIndex += 1; + } + + RunningLookupIndex += 1; + } + } + } + } + + Header->GposLookupIndexOffset = (kbts_u32)GposLookupIndexOffset; + Header->GlyphLookupMatrixOffsetFromStartOfFile = KBTS__POINTER_DIFF32(GlyphLookupMatrix, Header); + Header->GlyphLookupSubtableMatrixOffsetFromStartOfFile = KBTS__POINTER_DIFF32(GlyphLookupSubtableMatrix, Header); + Header->LookupSubtableIndexOffsetsOffsetFromStartOfFile = KBTS__POINTER_DIFF32(LookupSubtableIndexOffsets, Header); + Header->SubtableInfosOffsetFromStartOfFile = KBTS__POINTER_DIFF32(SubtableInfos, Header); + } + } + + if(Result != KBTS_LOAD_FONT_ERROR_NONE) + { + Font->Blob = 0; + } + Font->Error = Result; + + return Result; +} + +KBTS_EXPORT void kbts_GetFontInfo(kbts_font *Font, kbts_font_info *Info) +{ + KBTS_MEMSET(Info, 0, sizeof(*Info)); + kbts_blob_header *Blob = Font->Blob; + + if(kbts_FontIsValid(Font) && Blob) + { + kbts_blob_table *NameTable = &Blob->Tables[KBTS_BLOB_TABLE_ID_NAME]; + + if(NameTable->Length) + { + kbts__name *Name = KBTS__POINTER_OFFSET(kbts__name, Blob, NameTable->OffsetFromStartOfFile); + kbts__name_record *Records = KBTS__POINTER_AFTER(kbts__name_record, Name); + char *StringBase = KBTS__POINTER_OFFSET(char, Name, Name->StringStorageOffset); + + KBTS__FOR(RecordIndex, 0, Name->Count) + { + kbts__name_record *Record = &Records[RecordIndex]; + + if(!Record->LanguageId) + { + kbts_font_info_string_id Id = KBTS_FONT_INFO_STRING_ID_NONE; + + switch(Record->NameId) + { + case 0: Id = KBTS_FONT_INFO_STRING_ID_COPYRIGHT; break; + case 1: Id = KBTS_FONT_INFO_STRING_ID_FAMILY; break; + case 2: Id = KBTS_FONT_INFO_STRING_ID_SUBFAMILY; break; + case 3: Id = KBTS_FONT_INFO_STRING_ID_UID; break; + case 4: Id = KBTS_FONT_INFO_STRING_ID_FULL_NAME; break; + case 5: Id = KBTS_FONT_INFO_STRING_ID_VERSION; break; + case 6: Id = KBTS_FONT_INFO_STRING_ID_POSTSCRIPT_NAME; break; + case 7: Id = KBTS_FONT_INFO_STRING_ID_TRADEMARK; break; + case 8: Id = KBTS_FONT_INFO_STRING_ID_MANUFACTURER; break; + case 9: Id = KBTS_FONT_INFO_STRING_ID_DESIGNER; break; + case 10: Id = KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_FAMILY; break; + case 11: Id = KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_SUBFAMILY; break; + } + + if(Id) + { + Info->Strings[Id] = KBTS__POINTER_OFFSET(char, StringBase, Record->StringOffset); + Info->StringLengths[Id] = Record->Length; + } + } + } + + if(!Info->Strings[KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_FAMILY]) + { + Info->Strings[KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_FAMILY] = Info->Strings[KBTS_FONT_INFO_STRING_ID_FAMILY]; + Info->StringLengths[KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_FAMILY] = Info->StringLengths[KBTS_FONT_INFO_STRING_ID_FAMILY]; + } + + if(!Info->Strings[KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_SUBFAMILY]) + { + Info->Strings[KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_SUBFAMILY] = Info->Strings[KBTS_FONT_INFO_STRING_ID_SUBFAMILY]; + Info->StringLengths[KBTS_FONT_INFO_STRING_ID_TYPOGRAPHIC_SUBFAMILY] = Info->StringLengths[KBTS_FONT_INFO_STRING_ID_SUBFAMILY]; + } + } + + kbts_blob_table *Os2Table = &Blob->Tables[KBTS_BLOB_TABLE_ID_OS2]; + + if(Os2Table->Length) + { + kbts__os2 *Os2 = KBTS__POINTER_OFFSET(kbts__os2, Blob, Os2Table->OffsetFromStartOfFile); + kbts_font_weight Weight = KBTS_FONT_WEIGHT_UNKNOWN; + kbts_font_width Width = KBTS_FONT_WIDTH_UNKNOWN; + kbts_font_style_flags StyleFlags = KBTS_FONT_STYLE_FLAG_NONE; + + switch(Os2->WeightClass) + { + case 100: Weight = KBTS_FONT_WEIGHT_THIN; break; + case 200: Weight = KBTS_FONT_WEIGHT_EXTRA_LIGHT; break; + case 300: Weight = KBTS_FONT_WEIGHT_LIGHT; break; + case 400: Weight = KBTS_FONT_WEIGHT_NORMAL; break; + case 500: Weight = KBTS_FONT_WEIGHT_MEDIUM; break; + case 600: Weight = KBTS_FONT_WEIGHT_SEMI_BOLD; break; + case 700: Weight = KBTS_FONT_WEIGHT_BOLD; break; + case 800: Weight = KBTS_FONT_WEIGHT_EXTRA_BOLD; break; + case 900: Weight = KBTS_FONT_WEIGHT_BLACK; break; + } + + switch(Os2->WidthClass) + { + case 1: Width = KBTS_FONT_WIDTH_ULTRA_CONDENSED; break; + case 2: Width = KBTS_FONT_WIDTH_EXTRA_CONDENSED; break; + case 3: Width = KBTS_FONT_WIDTH_CONDENSED; break; + case 4: Width = KBTS_FONT_WIDTH_SEMI_CONDENSED; break; + case 5: Width = KBTS_FONT_WIDTH_NORMAL; break; + case 6: Width = KBTS_FONT_WIDTH_SEMI_EXPANDED; break; + case 7: Width = KBTS_FONT_WIDTH_EXPANDED; break; + case 8: Width = KBTS_FONT_WIDTH_EXTRA_EXPANDED; break; + case 9: Width = KBTS_FONT_WIDTH_ULTRA_EXPANDED; break; + } + + if(Os2->Selection & (KBTS__OS2_SELECTION_FLAG_ITALIC | KBTS__OS2_SELECTION_FLAG_OBLIQUE)) + { + StyleFlags |= KBTS_FONT_STYLE_FLAG_ITALIC; + } + if(Os2->Selection & KBTS__OS2_SELECTION_FLAG_BOLD) + { + StyleFlags |= KBTS_FONT_STYLE_FLAG_BOLD; + } + if(Os2->Selection & KBTS__OS2_SELECTION_FLAG_REGULAR) + { + StyleFlags |= KBTS_FONT_STYLE_FLAG_REGULAR; + } + + Info->Weight = Weight; + Info->Width = Width; + Info->StyleFlags = StyleFlags; + } + } +} + +KBTS_EXPORT kbts_font kbts_FontFromMemory(void *FileData, int FileSize, int FontIndex, kbts_allocator_function *Allocator, void *AllocatorData) +{ + kbts_font Result = KBTS__ZERO; + + if(!Allocator) + { + Allocator = kbts__DefaultAllocator; + } + + if(FileData && (FileSize > 0)) + { + kbts_load_font_state LoadFontState = KBTS__ZERO; + int ScratchSize, OutputSize; + kbts_load_font_error Error = kbts_LoadFont(&Result, &LoadFontState, FileData, (int)FileSize, FontIndex, &ScratchSize, &OutputSize); + + if(Error == KBTS_LOAD_FONT_ERROR_NEED_TO_CREATE_BLOB) + { + void *ScratchMemory = kbts__AllocatorAllocate(Allocator, AllocatorData, (kbts_un)ScratchSize); + void *OutputMemory = kbts__AllocatorAllocate(Allocator, AllocatorData, (kbts_un)OutputSize); + + kbts_PlaceBlob(&Result, &LoadFontState, ScratchMemory, OutputMemory); + + kbts__AllocatorFree(Allocator, AllocatorData, ScratchMemory); + + Result.Allocator = Allocator; + Result.AllocatorData = AllocatorData; + } + } + + return Result; +} + +#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_) +{ + kbts_font Result = KBTS__ZERO; + + if(!Allocator) + { + Allocator = kbts__DefaultAllocator; + } FILE *File; # ifndef _MSC_VER @@ -22835,90 +27826,189 @@ KBTS_EXPORT kbts_font kbts_FontFromFile(const char *FileName) long FileSize = ftell(File); fseek(File, 0, SEEK_SET); - void *Data = malloc((kbts_un)FileSize); - kbts_un Ok = fread(Data, (kbts_un)FileSize, 1, File); - fclose(File); - - if(Ok) + if(FileSize > 0) { - kbts_un ScratchSize = kbts_ReadFontHeader(&Result, Data, (kbts_un)FileSize); + void *Data = kbts__AllocatorAllocate(Allocator, AllocatorData, (kbts_un)FileSize); - void *Scratch = malloc(ScratchSize); - kbts_un MemorySize = kbts_ReadFontData(&Result, Scratch, ScratchSize); - - void *Memory = Scratch; - if(MemorySize > ScratchSize) + if(Data) { - free(Scratch); - Memory = malloc(MemorySize); - } + kbts_un Ok = fread(Data, (kbts_un)FileSize, 1, File); - kbts_PostReadFontInitialize(&Result, Memory, MemorySize); + if(Ok) + { + Result = kbts_FontFromMemory(Data, (int)FileSize, FontIndex, Allocator, AllocatorData); + + if(!Result.Error) + { + + if(FileData) + { + *FileData = Data; + } + else + { + kbts__AllocatorFree(Allocator, AllocatorData, Data); + } + + if(FileSize_) + { + *FileSize_ = (int)FileSize; + } + } + } + else + { + Result.Error = KBTS_LOAD_FONT_ERROR_READ_ERROR; + } + } + else + { + Result.Error = KBTS_LOAD_FONT_ERROR_OUT_OF_MEMORY; + } } + + fclose(File); + } + else + { + Result.Error = KBTS_LOAD_FONT_ERROR_COULD_NOT_OPEN_FILE; } return Result; } +#endif + KBTS_EXPORT void kbts_FreeFont(kbts_font *Font) { - if(Font->FileBase) + if(Font->Blob && Font->Allocator) { - free(Font->FileBase); - if(Font->GlyphLookupMatrix) - { - free(Font->GlyphLookupMatrix); // This frees everything else. - } + kbts__AllocatorFree(Font->Allocator, Font->AllocatorData, Font->Blob); } } -#endif -KBTS_EXPORT int kbts_BreakStateIsValid(kbts_break_state *State) +static void kbts__DoBreak(kbts_break_state *State, kbts_s32 Position, kbts_u8 Flags, kbts_direction Direction, kbts_direction ParagraphDirection, kbts_script Script) { - int Result = !(State->Flags & KBTS_BREAK_STATE_FLAG_RAN_OUT_OF_REORDER_BUFFER_SPACE); - return Result; -} - -static void kbts_DoBreak(kbts_break_state *State, kbts_s32 Position, kbts_u8 Flags, kbts_direction Direction, kbts_script Script) -{ - if(kbts_BreakStateIsValid(State) && Flags && (State->BreakCount < KBTS_BREAK_REORDER_BUFFER_SIZE)) + kbts_u32 BreakPosition = State->CurrentPosition + (kbts_u32)Position; + if(Flags && + (BreakPosition <= State->CurrentPosition)) { - if(State->BreakCount < KBTS_BREAK_REORDER_BUFFER_SIZE) + kbts_break Break = KBTS__ZERO; + Break.Position = (int)BreakPosition; + Break.Flags = Flags; + Break.Direction = Direction; + Break.ParagraphDirection = ParagraphDirection; + Break.Script = Script; + + if((Flags & KBTS_BREAK_FLAG_SCRIPT) && + (State->LastScriptBreakScript)) { - kbts_break Break = KBTS_ZERO; - Break.Position = State->CurrentPosition + (kbts_u32)Position; - Break.Flags = Flags; - Break.Direction = Direction; - Break.Script = Script; + kbts_un LastScriptBreakPosition = State->LastScriptBreakPosition; + kbts_u8 LastScriptBreakScript = State->LastScriptBreakScript; - int Matched = 0; - KBTS_FOR(BreakIndex, 0, State->BreakCount) + // Resolve bracket scripts here. + // The only span we can fully resolve is the _previous_ script, but we don't + // know when it starts, so we might as well tag all of the brackets we have stored so far. + for(kbts_un BracketIndex = State->BracketCount; + BracketIndex; + --BracketIndex) { - kbts_break *Existing = &State->Breaks[BreakIndex]; + kbts_bracket *Bracket = &State->Brackets[BracketIndex - 1]; - if(Existing->Position == Break.Position) + if(Bracket->Position >= BreakPosition) + { + Bracket->Script = (kbts_u8)Script; + } + else if(Bracket->Position >= LastScriptBreakPosition) + { + Bracket->Script = LastScriptBreakScript; + } + else { - Existing->Flags |= Break.Flags; - if(Break.Flags & KBTS_BREAK_FLAG_DIRECTION) Existing->Direction = Break.Direction; - if(Break.Flags & KBTS_BREAK_FLAG_SCRIPT) Existing->Script = Break.Script; - Matched = 1; break; } - else if(Existing->Position < Break.Position) + } + + State->LastScriptBreakPosition = BreakPosition; + State->LastScriptBreakScript = (kbts_u8)Script; + } + + if((Flags & KBTS_BREAK_FLAG_DIRECTION) && + (State->LastDirectionBreakDirection)) + { + kbts_un LastDirectionBreakPosition = State->LastDirectionBreakPosition; + kbts_u8 LastDirectionBreakDirection = State->LastDirectionBreakDirection; + + for(kbts_un BracketIndex = State->BracketCount; + BracketIndex; + --BracketIndex) + { + kbts_bracket *Bracket = &State->Brackets[BracketIndex - 1]; + + if(Bracket->Position >= BreakPosition) { - kbts_break Swap = *Existing; - *Existing = Break; - Break = Swap; + Bracket->Direction = (kbts_u8)Direction; + } + else if(Bracket->Position >= LastDirectionBreakPosition) + { + Bracket->Direction = LastDirectionBreakDirection; + } + else + { + break; } } - State->Breaks[State->BreakCount] = Break; - State->BreakCount += !Matched; + + State->LastDirectionBreakPosition = BreakPosition; + State->LastDirectionBreakDirection = (kbts_u8)Direction; } - else + + int Matched = 0; + KBTS__FOR(BreakIndex, 0, State->BreakCount) { - // This should never happen as long as we call Break() after every BreakAddCodepoint(). - State->Flags |= KBTS_BREAK_STATE_FLAG_RAN_OUT_OF_REORDER_BUFFER_SPACE; + kbts_break *Existing = &State->Breaks[BreakIndex]; + + if(Existing->Position == Break.Position) + { + Existing->Flags |= Break.Flags; + + if(Break.Flags & KBTS_BREAK_FLAG_DIRECTION) + { + Existing->Direction = Break.Direction; + } + + if(Break.Flags & KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION) + { + Existing->ParagraphDirection = Break.ParagraphDirection; + } + + if(Break.Flags & KBTS_BREAK_FLAG_SCRIPT) + { + Existing->Script = Break.Script; + } + + Matched = 1; + + break; + } + else if(Existing->Position < Break.Position) + { + // We order breaks in backwards order here, because we want to simply pop them off the top in Break(). + kbts_break Swap = *Existing; + *Existing = Break; + Break = Swap; + } + else if((Break.Flags & KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION) && + (Existing->Flags & KBTS_BREAK_FLAG_DIRECTION) && + !Existing->Direction) + { + // Short-circuit coerce neutral blocks to the paragraph direction. + Existing->Direction = Break.ParagraphDirection; + } } + + State->Breaks[State->BreakCount] = Break; + State->BreakCount += !Matched; } } @@ -22927,52 +28017,144 @@ static void kbts_DoBreak(kbts_break_state *State, kbts_s32 Position, kbts_u8 Fla // A required break implies an allowed break. // A priority N break implies priority 0..N-1 breaks. enum { - KBTS_LINE_BREAK_ALLOWED0 = 1, - KBTS_LINE_BREAK_ALLOWED1 = 3, - KBTS_LINE_BREAK_ALLOWED2 = 7, - KBTS_LINE_BREAK_ALLOWED3 = 0xF, - KBTS_LINE_BREAK_ALLOWED4 = 0x1F, - KBTS_LINE_BREAK_ALLOWED5 = 0x3F, - KBTS_LINE_BREAK_REQUIRED0 = (1 << 6) | KBTS_LINE_BREAK_ALLOWED0, - KBTS_LINE_BREAK_REQUIRED1 = (3 << 6) | KBTS_LINE_BREAK_ALLOWED1, - KBTS_LINE_BREAK_REQUIRED2 = (7 << 6) | KBTS_LINE_BREAK_ALLOWED2, - KBTS_LINE_BREAK_REQUIRED3 = (0xF << 6) | KBTS_LINE_BREAK_ALLOWED3, - KBTS_LINE_BREAK_REQUIRED4 = (0x1F << 6) | KBTS_LINE_BREAK_ALLOWED4, - KBTS_LINE_BREAK_REQUIRED5 = (0x3F << 6) | KBTS_LINE_BREAK_ALLOWED5, - KBTS_LINE_BREAK_REQUIRED_MASK = 0x3F << 6, - KBTS_LINE_BREAK_ALLOWED_MASK = 0x3F, - KBTS_LINE_BREAK_MASK = KBTS_LINE_BREAK_REQUIRED_MASK | KBTS_LINE_BREAK_ALLOWED_MASK, + KBTS__LINE_BREAK_ALLOWED0 = 1, + KBTS__LINE_BREAK_ALLOWED1 = 3, + KBTS__LINE_BREAK_ALLOWED2 = 7, + KBTS__LINE_BREAK_ALLOWED3 = 0xF, + KBTS__LINE_BREAK_ALLOWED4 = 0x1F, + KBTS__LINE_BREAK_ALLOWED5 = 0x3F, + KBTS__LINE_BREAK_REQUIRED0 = (1 << 6) | KBTS__LINE_BREAK_ALLOWED0, + KBTS__LINE_BREAK_REQUIRED1 = (3 << 6) | KBTS__LINE_BREAK_ALLOWED1, + KBTS__LINE_BREAK_REQUIRED2 = (7 << 6) | KBTS__LINE_BREAK_ALLOWED2, + KBTS__LINE_BREAK_REQUIRED3 = (0xF << 6) | KBTS__LINE_BREAK_ALLOWED3, + KBTS__LINE_BREAK_REQUIRED4 = (0x1F << 6) | KBTS__LINE_BREAK_ALLOWED4, + KBTS__LINE_BREAK_REQUIRED5 = (0x3F << 6) | KBTS__LINE_BREAK_ALLOWED5, + KBTS__LINE_BREAK_REQUIRED_MASK = 0x3F << 6, + KBTS__LINE_BREAK_ALLOWED_MASK = 0x3F, + KBTS__LINE_BREAK_MASK = KBTS__LINE_BREAK_REQUIRED_MASK | KBTS__LINE_BREAK_ALLOWED_MASK, }; -static void kbts_DoLineBreak(kbts_break_state *State, int Position, kbts_u64 EffectiveLineBreaks, int Rtl, kbts_script Script) +static void kbts__DoLineBreak(kbts_break_state *State, int Position, kbts_u64 EffectiveLineBreaks) { - if(EffectiveLineBreaks & KBTS_LINE_BREAK_MASK) + if(EffectiveLineBreaks & KBTS__LINE_BREAK_MASK) { kbts_u8 Flags = 0; - if(EffectiveLineBreaks & KBTS_LINE_BREAK_ALLOWED_MASK) Flags |= KBTS_BREAK_FLAG_LINE_SOFT; - if(EffectiveLineBreaks & KBTS_LINE_BREAK_REQUIRED_MASK) Flags |= KBTS_BREAK_FLAG_LINE_HARD; - kbts_DoBreak(State, Position, Flags, Rtl, Script); + if(EffectiveLineBreaks & KBTS__LINE_BREAK_ALLOWED_MASK) + { + Flags |= KBTS_BREAK_FLAG_LINE_SOFT; + } + + if(EffectiveLineBreaks & KBTS__LINE_BREAK_REQUIRED_MASK) + { + Flags |= KBTS_BREAK_FLAG_LINE_HARD; + } + + kbts__DoBreak(State, Position, Flags, 0, 0, 0); } } -static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, kbts_u32 PositionIncrement, int MaybeEndOfText) +static void kbts__BreakStateStartParagraph(kbts_break_state *State) +{ + kbts_direction ParagraphDirection = State->UserParagraphDirection; + // At the beginning of a paragraph, we want to pretend like start-of-text is an actual character + // with bidirectional data that depends on its direction. + kbts_u32 StartOfTextBidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_NI; + kbts_u32 Flags = 0; + + if(ParagraphDirection == KBTS_DIRECTION_LTR) + { + StartOfTextBidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_L; + } + else if(ParagraphDirection == KBTS_DIRECTION_RTL) + { + Flags = KBTS_BREAK_STATE_FLAG_SAW_R_AFTER_L; + StartOfTextBidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_R; + } + + State->ParagraphDirection = (kbts_u8)ParagraphDirection; + State->BidirectionalClass1 = (kbts_u8)StartOfTextBidirectionalClass; + State->Flags = Flags; +} + +typedef kbts_u32 kbts__break_flush_flags; +enum kbts__break_flush_flags_enum +{ + KBTS__BREAK_FLUSH_FLAG_NONE, + KBTS__BREAK_FLUSH_FLAG_SCRIPT = (1 << 0), + KBTS__BREAK_FLUSH_FLAG_DIRECTION_2 = (1 << 1), + KBTS__BREAK_FLUSH_FLAG_DIRECTION_1 = (1 << 2), + KBTS__BREAK_FLUSH_FLAG_DIRECTION_PARAGRAPH = (1 << 3), +}; + +static void kbts__FlushDirection(kbts_break_state *State, kbts_direction *LastDirection, kbts_unicode_bidirectional_class BidirectionalClass, kbts_s16 PositionOffset) +{ + // @Incomplete: ET+ EN -> EN+ EN + kbts_break_flags BreakFlags = 0; + kbts_direction Direction = KBTS_DIRECTION_DONT_KNOW; + + switch(BidirectionalClass) + { + // @Incomplete: Surely, there are other edge cases we could handle here. + case KBTS_UNICODE_BIDIRECTIONAL_CLASS_L: + BreakFlags = KBTS_BREAK_FLAG_DIRECTION | KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION; + Direction = KBTS_DIRECTION_LTR; + break; + + case KBTS_UNICODE_BIDIRECTIONAL_CLASS_R: + BreakFlags = KBTS_BREAK_FLAG_DIRECTION | KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION; + Direction = KBTS_DIRECTION_RTL; + break; + + case KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN: + case KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN: + // Digits are weak LTR, i.e. they do not influence the paragraph direction. + BreakFlags = KBTS_BREAK_FLAG_DIRECTION; + Direction = KBTS_DIRECTION_LTR; + break; + + case KBTS_UNICODE_BIDIRECTIONAL_CLASS_NI: + // We've already checked the ParagraphDirection earlier, when coercing neutrals to L or R. + // If a neutral shows up here, it means that we did not find any way to resolve it to either direction. + // In this case, neutrals get the unresolved embedding direction. + BreakFlags = KBTS_BREAK_FLAG_DIRECTION; + break; + } + + if((BreakFlags & KBTS_BREAK_FLAG_DIRECTION) && + (Direction != *LastDirection)) + { + *LastDirection = Direction; + kbts__DoBreak(State, PositionOffset, KBTS_BREAK_FLAG_DIRECTION, Direction, 0, 0); + } + + if((BreakFlags & KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION) && + !State->ParagraphDirection) + { + kbts_s32 StartOfParagraphOffset = (kbts_s16)(State->ParagraphStartPosition - State->CurrentPosition); + kbts__DoBreak(State, StartOfParagraphOffset, KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION, 0, Direction, 0); + State->ParagraphDirection = (kbts_u8)Direction; + } +} + +static void kbts__BreakAddCodepoint(kbts_break_state *State, kbts_u32 Codepoint, kbts_u32 PositionIncrement, int MaybeEndOfText) { // In these macros, and in FlagState, and in the way we buffer our state in general, // index 0 means _after_ the codepoint currently being added, // index 1 means _before_ the codepoint currently being added. #define KBTS_BREAK(Flags, Position) do {FlagState |= ((Flags) << (8 * (Position)));} while(0) #define KBTS_BREAK2(Flags, Position0, Position1) do {FlagState |= ((Flags) << (8 * (Position0))) | ((Flags) << (8 * (Position1)));} while(0) - if(!kbts_BreakStateIsValid(State)) return; - kbts_u8 Script = kbts_GetUnicodeScript(Codepoint); - kbts_unicode_bidirectional_class BidirectionalClass = kbts_GetUnicodeBidirectionalClass(Codepoint); - kbts_u8 UnicodeFlags = kbts_GetUnicodeFlags(Codepoint); - kbts_u32 MatchingBracket = kbts_GetUnicodeMatchingBracket(Codepoint); - kbts_u8 GraphemeBreakClass = kbts_GetUnicodeGraphemeBreakClass(Codepoint); - kbts_u8 LineBreakClass = kbts_GetUnicodeLineBreakClass(Codepoint); - kbts_u8 WordBreakClass = kbts_GetUnicodeWordBreakClass(Codepoint); - kbts_u8 LastScript = State->LastScripts[0]; + kbts_unicode_bidirectional_class BidirectionalClass = kbts__GetUnicodeBidirectionalClass(Codepoint); + kbts_u8 UnicodeFlags = kbts__GetUnicodeFlags(Codepoint); + kbts_u32 MatchingBracket = kbts__GetUnicodeMirrorCodepoint(Codepoint); + kbts_u8 GraphemeBreakClass = kbts__GetUnicodeGraphemeBreakClass(Codepoint); + kbts_u8 LineBreakClass = kbts__GetUnicodeLineBreakClass(Codepoint); + kbts_u8 WordBreakClass = kbts__GetUnicodeWordBreakClass(Codepoint); + kbts_u16 CodepointScriptExtension = kbts__GetUnicodeScriptExtension(Codepoint); + kbts_u32 CodepointScriptCount = (kbts_u32)kbts__ScriptExtensionCount(CodepointScriptExtension); + kbts_u32 CodepointScriptOffset = (kbts_u32)kbts__ScriptExtensionOffset(CodepointScriptExtension); + kbts_u8 *CodepointScripts = &kbts__ScriptExtensions[CodepointScriptOffset]; kbts_u32 FlagState = State->FlagState << 8; kbts_u8 LastLineBreakClass = State->LastLineBreakClass; // Super secret cheat code for signaling end-of-text @@ -22987,6 +28169,16 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, kbts_s16 PositionOffset3 = State->PositionOffset3; kbts_u32 Flags = State->Flags; kbts_direction LastDirection = State->LastDirection; + kbts_u8 *ScriptSet = State->ScriptSet; + kbts_s16 ScriptPositionOffset = State->ScriptPositionOffset; + kbts_u32 ScriptCount = State->ScriptCount; + kbts_u32 ScriptCountAtBeginningOfUpdate = ScriptCount; + kbts_u8 BreakScript = ScriptSet[0]; + kbts__break_flush_flags FlushFlags = 0; + kbts_s16 Bidirectional1PositionOffset = State->Bidirectional1PositionOffset; + kbts_s16 Bidirectional2PositionOffset = State->Bidirectional2PositionOffset; + kbts_u8 Bidirectional2 = State->BidirectionalClass2; + kbts_u8 Bidirectional1 = State->BidirectionalClass1; if(StartOfText) { @@ -22994,28 +28186,31 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, WordBreakHistory = LastWordBreakClass = KBTS_WORD_BREAK_CLASS_SOT; } - - if((Script == KBTS_SCRIPT_DEFAULT) || (Script == KBTS_SCRIPT_DEFAULT2)) - { - Script = LastScript; - } - // Bracket pairing overrides default directions/scripts. - if(UnicodeFlags & KBTS_UNICODE_FLAG_OPEN_BRACKET) + if((UnicodeFlags & KBTS_UNICODE_FLAG_MIRRORED) == KBTS_UNICODE_FLAG_OPEN_BRACKET) { - if(State->BracketCount < KBTS_ARRAY_LENGTH(State->Brackets)) + if(State->BracketCount < KBTS__ARRAY_LENGTH(State->Brackets)) { kbts_bracket *Bracket = &State->Brackets[State->BracketCount++]; // @Incomplete: Canonicalize the bracket. Bracket->Codepoint = Codepoint; - Bracket->Direction = KBTS_DIRECTION_NONE; - Bracket->Script = Script; + Bracket->Position = State->CurrentPosition; + + // Unfortunately, because our script/direction breaks are arbitrary lookback now, + // we have to wait until DoBreak() to resolve these. + Bracket->Direction = KBTS_DIRECTION_DONT_KNOW; + Bracket->Script = KBTS_SCRIPT_DONT_KNOW; + + if(ScriptCount) + { + Bracket->Script = ScriptSet[0]; + } State->Flags |= KBTS_BREAK_STATE_FLAG_LAST_WAS_BRACKET; } } - else if(UnicodeFlags & KBTS_UNICODE_FLAG_CLOSE_BRACKET) + else if((UnicodeFlags & KBTS_UNICODE_FLAG_MIRRORED) == KBTS_UNICODE_FLAG_CLOSE_BRACKET) { if(State->BracketCount) { @@ -23023,7 +28218,7 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, kbts_bracket *FoundBracket = 0; // @Incomplete: Canonicalize the bracket. - KBTS_FOR(BracketIndex, 0, State->BracketCount) + KBTS__FOR(BracketIndex, 0, State->BracketCount) { kbts_bracket *Bracket = &State->Brackets[State->BracketCount - 1 - BracketIndex]; @@ -23038,18 +28233,129 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, if(FoundBracket) { + // In case the bracket hasn't been resolved yet, take the current values. + kbts_u8 BracketScript = FoundBracket->Script; + kbts_u8 BracketDirection = FoundBracket->Direction; + + if(!BracketScript && ScriptCount) + { + BracketScript = ScriptSet[0]; + } + + if(!BracketDirection) + { + BracketDirection = (kbts_u8)LastDirection; + } + + if(BracketDirection == KBTS_DIRECTION_LTR) + { + BidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_L; + } + else if(BracketDirection == KBTS_DIRECTION_RTL) + { + BidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_R; + } + BidirectionalClass = FoundBracket->Direction; - Script = FoundBracket->Script; + CodepointScriptCount = 1; + CodepointScriptOffset = BracketScript; State->BracketCount = (kbts_u32)FoundBracketIndex; } } } - { // Direction breaking. - kbts_u8 Bidirectional2 = State->BidirectionalClass2; - kbts_u8 Bidirectional1 = State->BidirectionalClass1; + // Script breaking. + if(EndOfText) + { + FlushFlags |= KBTS__BREAK_FLUSH_FLAG_SCRIPT; + } + if(CodepointScriptCount < 2) + { + // We special case this entire path because, supposedly, this is the common case. + kbts_u8 CodepointScript = (kbts_u8)CodepointScriptOffset; + + if((CodepointScript == KBTS_SCRIPT_DONT_KNOW) || + (CodepointScript == KBTS_SCRIPT_DEFAULT) || + (CodepointScript == KBTS_SCRIPT_DEFAULT2)) + { + // Nothing to do. + } + else + { + kbts_u32 ScriptSetMatch = 0; + KBTS__FOR(ScriptIndex, 0, ScriptCount) + { + ScriptSetMatch |= (ScriptSet[ScriptIndex] == CodepointScript); + } + + if(!ScriptSetMatch) + { + FlushFlags |= KBTS__BREAK_FLUSH_FLAG_SCRIPT; + } + + ScriptCount = 1; + ScriptSet[0] = CodepointScript; + } + } + else + { + // Refine the script set. + kbts_un NewScriptCount = 0; + + { + kbts_un CodepointScriptIndex = 0; + kbts_un ScriptIndex = 0; + + while((ScriptIndex < ScriptCount) && + (CodepointScriptIndex < CodepointScriptCount)) + { + kbts_u8 CodepointScript = CodepointScripts[CodepointScriptIndex]; + kbts_u8 Script = ScriptSet[ScriptIndex]; + + if(CodepointScript < Script) + { + CodepointScriptIndex += 1; + } + else if(Script < CodepointScript) + { + ScriptIndex += 1; + } + else + { + ScriptSet[NewScriptCount++] = Script; + + CodepointScriptIndex += 1; + ScriptIndex += 1; + } + } + } + + if(!NewScriptCount) + { + FlushFlags |= KBTS__BREAK_FLUSH_FLAG_SCRIPT; + + KBTS__FOR(CodepointScriptIndex, 0, CodepointScriptCount) + { + ScriptSet[CodepointScriptIndex] = CodepointScripts[CodepointScriptIndex]; + } + ScriptCount = (kbts_u32)CodepointScriptCount; + } + else + { + ScriptCount = (kbts_u32)NewScriptCount; + } + } + + // Direction breaking. + if(EndOfText) + { + BidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_NI; + } + + if(BidirectionalClass != KBTS_UNICODE_BIDIRECTIONAL_CLASS_BN) // Formatting characters should be ignored. + { switch(BidirectionalClass) { case KBTS_UNICODE_BIDIRECTIONAL_CLASS_NSM: BidirectionalClass = Bidirectional1; break; @@ -23075,18 +28381,28 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, BidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN; goto CaseAn; } - if((Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN) && ((Bidirectional1 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_ES) || (Bidirectional1 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_CS))) + if((Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN) && + ((Bidirectional1 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_ES) || + (Bidirectional1 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_CS))) { Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN; } - if(!(Flags & KBTS_BREAK_STATE_FLAG_SAW_R_AFTER_L)) + + // We test State->ParagraphDirection here because we do not want + // digits to coerce to L when the paragrpah direction is unknown. + // It might be cleaner to explicitly store the last strong direction seen, + // with DONT_KNOW as an option. + // @Cleanup + if(State->ParagraphDirection && + !(Flags & KBTS_BREAK_STATE_FLAG_SAW_R_AFTER_L)) { BidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_L; } break; case KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN: CaseAn:; - if((Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN) && (Bidirectional1 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_CS)) + if((Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN) && + (Bidirectional1 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_CS)) { Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN; } @@ -23099,57 +28415,77 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, break; } - if(KBTS_IN_SET(BidirectionalClass, KBTS_SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_ET)(KBTS_UNICODE_BIDIRECTIONAL_CLASS_ES)(KBTS_UNICODE_BIDIRECTIONAL_CLASS_CS)))) + // This rule has a lower priority than AN CS AN -> AN AN AN, so we have to wait until slot 1 to apply it. + if(KBTS__IN_SET(Bidirectional1, KBTS__SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_ET) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_ES) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_CS)))) { - BidirectionalClass = KBTS_UNICODE_BIDIRECTIONAL_CLASS_NI; + Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_NI; } if(Bidirectional1 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_NI) { - if(KBTS_IN_SET(Bidirectional2, KBTS_SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_R)(KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN)(KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN))) && - KBTS_IN_SET(BidirectionalClass, KBTS_SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_R)(KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN)(KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN)))) + if(KBTS__IN_SET(BidirectionalClass, KBTS__SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_NI) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_ET) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_ES) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_CS)))) { + // All of these input classes end up resolving to NI later on anyway if they are preceded by NI. + // We are in a situation where: + // - We have an NI in slot 1 + // - The direction in slot 0 will eventually resolve to NI due to the NI in slot 1 + // - Storing multiple NIs in our shift buffer is redundant, because no rule necessitates multiple NIs + // - NIs don't interact with anything, except that they resolve when surrounded by strong characters + // - NIs are resolved in groups. As per the Unicode specification: + // N1. A sequence of NIs takes the direction of the surrounding strong text if the text on both + // sides has the same direction. + // This means we can merge the current bidirectional class with the preceding NI, bump the offset, + // and it just works. + goto SkipDirectionBreak; + } + else if(((Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_R) || + (BidirectionalClass == KBTS_UNICODE_BIDIRECTIONAL_CLASS_R)) && + KBTS__IN_SET(Bidirectional2, KBTS__SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_R) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN))) && + KBTS__IN_SET(BidirectionalClass, KBTS__SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_R) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN)))) + { + // Note that the way we resolve digits is different from the way the Unicode standard specifies it. + // This is because the standard assumes the paragraph direction is always known, whereas in our case it isn't. + // We want neutral surrounded by uncoerced digits to resolve to the paragraph direction, which may be DONT_KNOW. Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_R; } - else if((Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_L) && (BidirectionalClass == KBTS_UNICODE_BIDIRECTIONAL_CLASS_L)) + else if(((Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_L) || + (BidirectionalClass == KBTS_UNICODE_BIDIRECTIONAL_CLASS_L)) && + KBTS__IN_SET(Bidirectional2, KBTS__SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_L) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN))) && + KBTS__IN_SET(BidirectionalClass, KBTS__SET32((KBTS_UNICODE_BIDIRECTIONAL_CLASS_L) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN) + (KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN)))) { Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_L; } else { - if (State->MainDirection == KBTS_DIRECTION_LTR) Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_L; - else if(State->MainDirection == KBTS_DIRECTION_RTL) Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_R; + if (State->ParagraphDirection == KBTS_DIRECTION_LTR) Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_L; + else if(State->ParagraphDirection == KBTS_DIRECTION_RTL) Bidirectional1 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_R; } } - // These rules happen at the very end, so we wait until the last slot to apply them. - if((Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_AN) || (Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_EN)) + FlushFlags |= KBTS__BREAK_FLUSH_FLAG_DIRECTION_2; + if(EndOfText) { - Bidirectional2 = KBTS_UNICODE_BIDIRECTIONAL_CLASS_L; + FlushFlags |= KBTS__BREAK_FLUSH_FLAG_DIRECTION_1; } - - // @Incomplete: ET+ EN -> EN+ EN - kbts_direction Direction = KBTS_DIRECTION_NONE; - if(Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_L) Direction = KBTS_DIRECTION_LTR; - else if(Bidirectional2 == KBTS_UNICODE_BIDIRECTIONAL_CLASS_R) Direction = KBTS_DIRECTION_RTL; - if(Direction && (Direction != LastDirection)) - { - LastDirection = Direction; - KBTS_BREAK(KBTS_BREAK_FLAG_DIRECTION, 3); - - if(!State->MainDirection) - { - State->MainDirection = (kbts_u8)Direction; - } - } - - State->BidirectionalClass2 = Bidirectional1; - State->BidirectionalClass1 = BidirectionalClass; } - - if(Script != LastScript) + else { - KBTS_BREAK(KBTS_BREAK_FLAG_SCRIPT, 1); + SkipDirectionBreak:; + State->Bidirectional2PositionOffset -= (kbts_s16)PositionIncrement; + State->Bidirectional1PositionOffset -= (kbts_s16)PositionIncrement; } { // Grapheme breaking. @@ -23196,10 +28532,10 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, // Ignore [EX FO ZWJ] after ^[_sot_ CR LF NL]. // @Cleanup: This is the only time we explicitly use EX and FO. They can be merged. - if(KBTS_IN_SET(WordBreakClass, KBTS_SET32((KBTS_WORD_BREAK_CLASS_EX)(KBTS_WORD_BREAK_CLASS_FO)(KBTS_WORD_BREAK_CLASS_ZWJ))) && - !KBTS_IN_SET(LastWordBreakClass, KBTS_SET32((KBTS_WORD_BREAK_CLASS_SOT)(KBTS_WORD_BREAK_CLASS_CR)(KBTS_WORD_BREAK_CLASS_LF)(KBTS_WORD_BREAK_CLASS_NL)))) + if(KBTS__IN_SET(WordBreakClass, KBTS__SET32((KBTS_WORD_BREAK_CLASS_EX)(KBTS_WORD_BREAK_CLASS_FO)(KBTS_WORD_BREAK_CLASS_ZWJ))) && + !KBTS__IN_SET(LastWordBreakClass, KBTS__SET32((KBTS_WORD_BREAK_CLASS_SOT)(KBTS_WORD_BREAK_CLASS_CR)(KBTS_WORD_BREAK_CLASS_LF)(KBTS_WORD_BREAK_CLASS_NL)))) { - WordBreak2PositionOffset -= PositionIncrement; + WordBreak2PositionOffset -= (kbts_s16)PositionIncrement; State->WordBreak2PositionOffset = WordBreak2PositionOffset; } else @@ -23214,11 +28550,11 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, WordBreaks |= KBTS_WORD_BREAK_BITS(2, 1); } - if(KBTS_IN_SET(WordBreakClass, KBTS_SET32((KBTS_WORD_BREAK_CLASS_CR)(KBTS_WORD_BREAK_CLASS_LF)(KBTS_WORD_BREAK_CLASS_NL)))) + if(KBTS__IN_SET(WordBreakClass, KBTS__SET32((KBTS_WORD_BREAK_CLASS_CR)(KBTS_WORD_BREAK_CLASS_LF)(KBTS_WORD_BREAK_CLASS_NL)))) { WordBreaks |= KBTS_WORD_BREAK_BITS(1, 1) | KBTS_WORD_BREAK_BITS(1, 0); } - else if(KBTS_IN_SET(WordBreakClass, KBTS_SET32((KBTS_WORD_BREAK_CLASS_Oep)(KBTS_WORD_BREAK_CLASS_ALep)))) + else if(KBTS__IN_SET(WordBreakClass, KBTS__SET32((KBTS_WORD_BREAK_CLASS_Oep)(KBTS_WORD_BREAK_CLASS_ALep)))) { // ZWJ x {Extended_Pictographic} if(LastWordBreakClassIncludingIgnored == KBTS_WORD_BREAK_CLASS_ZWJ) @@ -23269,7 +28605,7 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, kbts_u32 EffectiveWordBreaks = WordBreaks & ~WordUnbreaks; if(EffectiveWordBreaks & KBTS_WORD_BREAK_BITS(2, 2)) { - kbts_DoBreak(State, PositionOffset2 + WordBreak2PositionOffset, KBTS_BREAK_FLAG_WORD, 0, 0); + kbts__DoBreak(State, PositionOffset2 + WordBreak2PositionOffset, KBTS_BREAK_FLAG_WORD, 0, 0, 0); } if(EndOfText) { @@ -23291,6 +28627,7 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, kbts_s16 LineBreak3PositionOffset = State->LineBreak3PositionOffset; kbts_s16 LineBreak2PositionOffset = State->LineBreak2PositionOffset; + int HardLineBreak = 0; { // Line breaking. kbts_u64 LineBreaks = State->LineBreaks << 16; kbts_u64 LineUnbreaks = State->LineUnbreaks << 16; @@ -23300,10 +28637,10 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, #define KBTS_C2(A, B) case (KBTS_LINE_BREAK_CLASS_##A << 8) | (KBTS_LINE_BREAK_CLASS_##B) #define KBTS_C3(A, B, C) case (KBTS_LINE_BREAK_CLASS_##A << 16) | (KBTS_LINE_BREAK_CLASS_##B << 8) | KBTS_LINE_BREAK_CLASS_##C #define KBTS_C4(A, B, C, D) case (KBTS_LINE_BREAK_CLASS_##A << 24) | (KBTS_LINE_BREAK_CLASS_##B << 16) | (KBTS_LINE_BREAK_CLASS_##C << 8) | KBTS_LINE_BREAK_CLASS_##D - #define KBTS_REQUIRED_LINE_BREAK(Priority, Position) do {LineBreaks |= (kbts_u64)KBTS_LINE_BREAK_REQUIRED##Priority << ((Position) * 16);} while(0) - #define KBTS_LINE_BREAK(Priority, Position) do {LineBreaks |= (kbts_u64)KBTS_LINE_BREAK_ALLOWED##Priority << ((Position) * 16);} while(0) - #define KBTS_LINE_UNBREAK(Priority, Position) do {LineUnbreaks |= (kbts_u64)KBTS_LINE_BREAK_REQUIRED##Priority << ((Position) * 16);} while(0) - #define KBTS_LINE_UNBREAK_ASYNC(Priority, Position) do {LineUnbreaksAsync |= (kbts_u64)KBTS_LINE_BREAK_REQUIRED##Priority << ((Position) * 16);} while(0) + #define KBTS_REQUIRED_LINE_BREAK(Priority, Position) do {LineBreaks |= (kbts_u64)KBTS__LINE_BREAK_REQUIRED##Priority << ((Position) * 16);} while(0) + #define KBTS_LINE_BREAK(Priority, Position) do {LineBreaks |= (kbts_u64)KBTS__LINE_BREAK_ALLOWED##Priority << ((Position) * 16);} while(0) + #define KBTS_LINE_UNBREAK(Priority, Position) do {LineUnbreaks |= (kbts_u64)KBTS__LINE_BREAK_REQUIRED##Priority << ((Position) * 16);} while(0) + #define KBTS_LINE_UNBREAK_ASYNC(Priority, Position) do {LineUnbreaksAsync |= (kbts_u64)KBTS__LINE_BREAK_REQUIRED##Priority << ((Position) * 16);} while(0) if(EndOfText) { @@ -23344,7 +28681,7 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, } else if(LineBreakClass == KBTS_LINE_BREAK_CLASS_CJ) { - LineBreakClass = State->JapaneseLineBreakStyle == KBTS_JAPANESE_LINE_BREAK_STYLE_STRICT ? KBTS_LINE_BREAK_CLASS_NSea : KBTS_LINE_BREAK_CLASS_IDea; + LineBreakClass = (kbts_u8)((State->JapaneseLineBreakStyle == KBTS_JAPANESE_LINE_BREAK_STYLE_STRICT) ? KBTS_LINE_BREAK_CLASS_NSea : KBTS_LINE_BREAK_CLASS_IDea); } } @@ -23359,7 +28696,7 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, LineBreakHistory = (LineBreakHistory << 8) | LineBreakClass; - if(EndOfText) + if(EndOfText && (State->ConfigFlags & KBTS_BREAK_CONFIG_FLAG_END_OF_TEXT_GENERATES_HARD_LINE_BREAK)) { // Always break at the end of text. KBTS_REQUIRED_LINE_BREAK(5, 1); @@ -23379,7 +28716,23 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, KBTS_C1(BK): KBTS_C1(CR): KBTS_C1(LF): - KBTS_C1(NL): KBTS_LINE_UNBREAK(4, 1); KBTS_REQUIRED_LINE_BREAK(5, 0); break; + KBTS_C1(NL): + { + KBTS_LINE_UNBREAK(4, 1); + + // This is the only place that's not the end of text where we generate hard line breaks. + // When we see a hard line break, we want to reset the direction/script state, so that + // we generate new direction/script breaks for the new paragraph. + FlushFlags |= (KBTS__BREAK_FLUSH_FLAG_SCRIPT | + KBTS__BREAK_FLUSH_FLAG_DIRECTION_2 | + KBTS__BREAK_FLUSH_FLAG_DIRECTION_1 | + KBTS__BREAK_FLUSH_FLAG_DIRECTION_PARAGRAPH); + ScriptCount = 0; + HardLineBreak = 1; + + KBTS_REQUIRED_LINE_BREAK(5, 0); + } break; + KBTS_C1(ZW): KBTS_LINE_BREAK(4, 0); KBTS_LINE_UNBREAK(4, 1); break; KBTS_C1(BB): KBTS_LINE_UNBREAK(0, 0); break; @@ -23782,16 +29135,17 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, { kbts_u64 EffectiveLineBreaks = LineBreaks & ~(LineUnbreaks | LineUnbreaksAsync); - kbts_DoLineBreak(State, PositionOffset3 + LineBreak3PositionOffset, EffectiveLineBreaks >> 48, 0, 0); + kbts__DoLineBreak(State, PositionOffset3 + LineBreak3PositionOffset, EffectiveLineBreaks >> 48); if(EndOfText) { - kbts_DoLineBreak(State, PositionOffset2 + LineBreak2PositionOffset, EffectiveLineBreaks >> 32, 0, 0); + kbts__DoLineBreak(State, PositionOffset2 + LineBreak2PositionOffset, EffectiveLineBreaks >> 32); { // @Cleanup: This is the same flag code as DoLineBreak, but we want to use FlagState buffering for this. // The only places where we want to manually call DoBreak is for asynchronous/buffered guys. - kbts_u8 FlushFlags = 0; - if((EffectiveLineBreaks >> 16) & KBTS_LINE_BREAK_ALLOWED_MASK) FlushFlags |= KBTS_BREAK_FLAG_LINE_SOFT; - if((EffectiveLineBreaks >> 16) & KBTS_LINE_BREAK_REQUIRED_MASK) FlushFlags |= KBTS_BREAK_FLAG_LINE_HARD; - KBTS_BREAK(FlushFlags, 1); + kbts_u8 FlushedLineFlags = 0; + if((EffectiveLineBreaks >> 16) & KBTS__LINE_BREAK_ALLOWED_MASK) FlushedLineFlags |= KBTS_BREAK_FLAG_LINE_SOFT; + if((EffectiveLineBreaks >> 16) & KBTS__LINE_BREAK_REQUIRED_MASK) FlushedLineFlags |= KBTS_BREAK_FLAG_LINE_HARD; + + KBTS_BREAK(FlushedLineFlags, 1); } // Lines are never broken after the end of text. } @@ -23803,91 +29157,380 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, State->LineBreak3PositionOffset = LineBreak2PositionOffset; State->LastLineBreakClass = LineBreakClass; State->LineBreakHistory = LineBreakHistory; - State->LastDirection = (kbts_u8)LastDirection; if(0) { LineBreakAbsorbCharacter:; - State->LineBreak2PositionOffset -= PositionIncrement; - State->LineBreak3PositionOffset -= PositionIncrement; + State->LineBreak2PositionOffset -= (kbts_s16)PositionIncrement; + State->LineBreak3PositionOffset -= (kbts_s16)PositionIncrement; } // This always gets updated. State->LineUnbreaksAsync = LineUnbreaksAsync; } + // We flush scripts late because hard line breaks also want to flush scripts. + // + // If we have no active script at all, then either we are at the very beginning of the text, + // or we have only seen common/inherited scripts so far. + // Either way, we want everything before us to coerce to our type, so don't actually break, + // and do not update the script break position. + if((FlushFlags & KBTS__BREAK_FLUSH_FLAG_SCRIPT) && + ScriptCountAtBeginningOfUpdate) + { + kbts__DoBreak(State, ScriptPositionOffset, KBTS_BREAK_FLAG_SCRIPT, 0, 0, BreakScript); + ScriptPositionOffset = 0; + + if(HardLineBreak) + { + // Put the next script break after the newline. + ScriptPositionOffset = (kbts_s16)PositionIncrement; + } + } + + if(FlushFlags & KBTS__BREAK_FLUSH_FLAG_DIRECTION_2) + { + kbts__FlushDirection(State, &LastDirection, Bidirectional2, Bidirectional2PositionOffset); + } + if(FlushFlags & KBTS__BREAK_FLUSH_FLAG_DIRECTION_1) + { + kbts__FlushDirection(State, &LastDirection, Bidirectional1, Bidirectional1PositionOffset); + } + + if(FlushFlags & KBTS__BREAK_FLUSH_FLAG_DIRECTION_PARAGRAPH) + { + // Reset direction state for the new paragraph. + Bidirectional1 = 0; + + // Clear direction history. + // This is necessary to uphold the invariant that the first direction break is always + // on the first character of a paragraph. + LastDirection = 0; + + // CAUTION: This needs to be updated _after_ calling kbts__FlushDirection! + // Alternatively, we could load ParagraphStartPosition into a variable at the start of this function. + State->ParagraphStartPosition = State->CurrentPosition + 1; + + kbts__BreakStateStartParagraph(State); + + if(State->UserParagraphDirection == KBTS_DIRECTION_DONT_KNOW) + { + kbts__DoBreak(State, 1, KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION, 0, KBTS_DIRECTION_DONT_KNOW, 0); + } + } + + ScriptPositionOffset -= (kbts_s16)PositionIncrement; + // This is where we flush the normal breaks that don't need any special position adjustment. - kbts_DoBreak(State, PositionOffset3, (FlagState >> 24) & 0xFF, LastDirection, State->LastScripts[1]); + kbts__DoBreak(State, PositionOffset3, (FlagState >> 24) & 0xFF, 0, 0, 0); if(EndOfText) { - kbts_DoBreak(State, PositionOffset2, (FlagState >> 16) & 0xFF, 0, LastScript); - kbts_DoBreak(State, 0, (FlagState >> 8) & 0xFF, 0, Script); - kbts_DoBreak(State, PositionIncrement, FlagState & 0xFF, 0, Script); + kbts__DoBreak(State, PositionOffset2, (FlagState >> 16) & 0xFF, 0, 0, 0); + kbts__DoBreak(State, 0, (FlagState >> 8) & 0xFF, 0, 0, 0); + kbts__DoBreak(State, (int)PositionIncrement, FlagState & 0xFF, 0, 0, 0); } State->FlagState = FlagState; - State->Flags |= KBTS_BREAK_STATE_FLAG_STARTED; - if(EndOfText) State->Flags |= KBTS_BREAK_STATE_FLAG_END; + Flags |= KBTS_BREAK_STATE_FLAG_STARTED; + if(EndOfText) Flags |= KBTS_BREAK_STATE_FLAG_END; + State->Flags = Flags; State->PositionOffset2 = (kbts_s16)-(int)PositionIncrement; State->PositionOffset3 = (kbts_s16)(PositionOffset2 - (int)PositionIncrement); State->CurrentPosition += PositionIncrement; - State->LastScripts[1] = State->LastScripts[0]; - State->LastScripts[0] = Script; + if(FlushFlags & KBTS__BREAK_FLUSH_FLAG_DIRECTION_2) + { + // Only update the buffer if we've actually flushed something. + State->BidirectionalClass2 = Bidirectional1; + State->BidirectionalClass1 = BidirectionalClass; + State->Bidirectional2PositionOffset = State->Bidirectional1PositionOffset - (kbts_s16)PositionIncrement; + State->Bidirectional1PositionOffset = (kbts_s16)-(int)PositionIncrement; + } + State->LastDirection = (kbts_u8)LastDirection; + + State->ScriptPositionOffset = ScriptPositionOffset; + State->ScriptCount = ScriptCount; #undef KBTS_BREAK #undef KBTS_BREAK2 } -KBTS_EXPORT void kbts_BreakFlush(kbts_break_state *State) +KBTS_EXPORT void kbts_BreakEnd(kbts_break_state *State) { // We pass 3, aka. ASCII end-of-text, at the end of text. - kbts_BreakAddCodepoint_(State, 3, 0, 1); + kbts__BreakAddCodepoint(State, 3, 0, 1); } -KBTS_EXPORT void kbts_BreakAddCodepoint(kbts_break_state *State, kbts_u32 Codepoint, kbts_u32 PositionIncrement, int EndOfText) +KBTS_EXPORT void kbts_BreakAddCodepoint(kbts_break_state *State, int Codepoint, int PositionIncrement, int EndOfText) { - kbts_BreakAddCodepoint_(State, Codepoint, PositionIncrement, 0); - if(EndOfText) kbts_BreakFlush(State); + kbts__BreakAddCodepoint(State, Codepoint, (kbts_u32)PositionIncrement, 0); + + if(EndOfText) + { + kbts_BreakEnd(State); + } } KBTS_EXPORT int kbts_Break(kbts_break_state *State, kbts_break *Break) { int Result = 0; - kbts_un Threshold = State->Flags & KBTS_BREAK_STATE_FLAG_END ? 0 : KBTS_BREAK_REORDER_BUFFER_FLUSH_THRESHOLD; - if(kbts_BreakStateIsValid(State) && (State->BreakCount > Threshold)) + if(State->BreakCount) { - kbts_break *ToFlush = &State->Breaks[State->BreakCount - 1]; - // @Incomplete: Handle wrapping. - if(ToFlush->Position >= State->LastFlushedBreakPosition) - { - *Break = *ToFlush; - State->LastFlushedBreakPosition = ToFlush->Position; - State->BreakCount -= 1; - Result = 1; - } - else - { - State->Flags |= KBTS_BREAK_STATE_FLAG_RAN_OUT_OF_REORDER_BUFFER_SPACE; - } + kbts_break *ToFlush = &State->Breaks[--State->BreakCount]; + + *Break = *ToFlush; + Result = 1; } return Result; } -KBTS_EXPORT void kbts_BeginBreak(kbts_break_state *State, kbts_direction MainDirection, kbts_japanese_line_break_style JapaneseLineBreakStyle) +KBTS_EXPORT void kbts_GuessTextProperties(void *Text, int TextSizeInBytes, kbts_text_format Format, kbts_direction *Direction_, kbts_script *Script_) +{ + kbts_script Script = KBTS_SCRIPT_DONT_KNOW; + kbts_direction Direction = KBTS_DIRECTION_DONT_KNOW; + + if(Text && (TextSizeInBytes > 0) && Format) + { + const char *At = (const char *)Text; + const char *End = (const char *)Text + TextSizeInBytes; + + kbts_break_state BreakState; + kbts_BreakBegin(&BreakState, KBTS_DIRECTION_DONT_KNOW, KBTS_JAPANESE_LINE_BREAK_STYLE_NORMAL, 0); + + while(((Script == KBTS_SCRIPT_DONT_KNOW) || + (Direction == KBTS_DIRECTION_DONT_KNOW)) && + (At < End)) + { + kbts_u32 Codepoint = 0; + kbts_u32 PositionIncrement = 1; + + switch(Format) + { + case KBTS_TEXT_FORMAT_UTF32: + { + Codepoint = *(kbts_u32 *)At; + PositionIncrement = 4; + + } break; + + case KBTS_TEXT_FORMAT_UTF8: + { + kbts_decode Decode = kbts_DecodeUtf8(At, End - At); + + PositionIncrement = Decode.SourceCharactersConsumed; + + if(Decode.Valid) + { + Codepoint = Decode.Codepoint; + } + } break; + } + + kbts_BreakAddCodepoint(&BreakState, Codepoint, PositionIncrement, (At + PositionIncrement) == End); + kbts_break Break; + while(kbts_Break(&BreakState, &Break)) + { + if((Script == KBTS_SCRIPT_DONT_KNOW) && (Break.Flags & KBTS_BREAK_FLAG_SCRIPT)) + { + Script = Break.Script; + } + + if((Direction == KBTS_DIRECTION_DONT_KNOW) && (Break.Flags & KBTS_BREAK_FLAG_DIRECTION)) + { + Direction = Break.Direction; + } + } + + At += PositionIncrement; + } + } + + if(Script_) + { + *Script_ = Script; + } + + if(Direction_) + { + *Direction_ = Direction; + } +} + +KBTS_EXPORT void kbts_GuessTextPropertiesUtf32(const int *Utf32, int Utf32Count, kbts_direction *Direction, kbts_script *Script) +{ + kbts_GuessTextProperties((void *)Utf32, sizeof(int) * Utf32Count, KBTS_TEXT_FORMAT_UTF32, Direction, Script); +} + +KBTS_EXPORT void kbts_GuessTextPropertiesUtf8(const char *Utf8, int Utf8Length, kbts_direction *Direction, kbts_script *Script) +{ + kbts_GuessTextProperties((void *)Utf8, Utf8Length, KBTS_TEXT_FORMAT_UTF8, Direction, Script); +} + +KBTS_EXPORT void kbts_BreakBegin(kbts_break_state *State, kbts_direction ParagraphDirection, kbts_japanese_line_break_style JapaneseLineBreakStyle, kbts_break_config_flags ConfigFlags) { if(State) { + // @Speed: Clearing all the brackets isn't great. KBTS_MEMSET(State, 0, sizeof(*State)); - State->MainDirection = (kbts_u8)MainDirection; + State->UserParagraphDirection = (kbts_u8)ParagraphDirection; + State->ParagraphDirection = (kbts_u8)ParagraphDirection; State->JapaneseLineBreakStyle = JapaneseLineBreakStyle; + State->ConfigFlags = ConfigFlags; + + // Force a direction break at the start of the text. + State->LastDirection = KBTS_DIRECTION_COUNT; + + // These should be out-of-bounds while the buffers haven't filled up. + State->PositionOffset2 = -100; + State->PositionOffset3 = -100; + State->WordBreak2PositionOffset = -100; + State->LineBreak2PositionOffset = -100; + State->LineBreak3PositionOffset = -100; + State->Bidirectional1PositionOffset = -100; + State->Bidirectional2PositionOffset = -100; + + kbts__BreakStateStartParagraph(State); + + if(ParagraphDirection) + { + kbts__DoBreak(State, 0, KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION, 0, ParagraphDirection, 0); + } } } +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 IBreakCapacity, int *BreakCount, + kbts_break_flags *BreakFlags, int IBreakFlagCapacity, int *BreakFlagCount) +{ + kbts_break_state BreakState = KBTS__ZERO; + kbts_BreakBegin(&BreakState, Direction, JapaneseLineBreakStyle, ConfigFlags); + + char *InputElement = (char *)Input; + char *End = (char *)Input + InputSizeInBytes; + kbts_un BreaksWritten = 0; + kbts_un MaxBreakPosition = 0; + kbts_un BreakCapacity = (kbts_un)IBreakCapacity; + kbts_un BreakFlagCapacity = (kbts_un)IBreakFlagCapacity; + + if(BreakFlags && BreakFlagCapacity) + { + KBTS_MEMSET(BreakFlags, 0, sizeof(kbts_break_flags) * BreakFlagCapacity); + } + + while(InputElement < End) + { + kbts_u32 Codepoint = 0; + kbts_un InputElementSize = 1; + kbts_un SourceCharacterCount = 1; + + switch(InputFormat) + { + case KBTS_TEXT_FORMAT_UTF32: + { + Codepoint = *(kbts_u32 *)InputElement; + + InputElementSize = sizeof(kbts_u32); + } break; + + case KBTS_TEXT_FORMAT_UTF8: + { + kbts_decode Decode = kbts_DecodeUtf8(InputElement, End - InputElement); + + if(Decode.Valid) + { + Codepoint = Decode.Codepoint; + + InputElementSize = Decode.SourceCharactersConsumed; + SourceCharacterCount = Decode.SourceCharactersConsumed; + } + } break; + } + + InputElement += InputElementSize; + + kbts_BreakAddCodepoint(&BreakState, Codepoint, (kbts_u32)SourceCharacterCount, InputElement >= End); + kbts_break Break; + while(kbts_Break(&BreakState, &Break)) + { + if(Breaks && (BreaksWritten < BreakCapacity)) + { + int Found = 0; + kbts_un ExistingBreakCount = KBTS__MIN(BreaksWritten, BreakCapacity); + + // @Speed: Binary search! + for(kbts_un ExistingBreakIndex = ExistingBreakCount; + ExistingBreakIndex; + --ExistingBreakIndex) + { + kbts_break *Existing = &Breaks[ExistingBreakIndex - 1]; + + if(Existing->Position == Break.Position) + { + Existing->Flags |= Break.Flags; + + if(Break.Flags & KBTS_BREAK_FLAG_SCRIPT) + { + Existing->Script = Break.Script; + } + + if(Break.Flags & KBTS_BREAK_FLAG_DIRECTION) + { + Existing->Direction = Break.Direction; + } + + Found = 1; + + break; + } + } + + if(!Found) + { + Breaks[BreaksWritten] = Break; + } + } + + if(BreakFlags && ((kbts_u32)Break.Position < BreakFlagCapacity)) + { + BreakFlags[Break.Position] |= Break.Flags; + } + + MaxBreakPosition = KBTS__MAX(MaxBreakPosition, (kbts_u32)Break.Position); + + BreaksWritten += 1; + } + } + + if(!Breaks && BreakCount) + { + *BreakCount = (int)BreaksWritten; + } + if(!BreakFlags && BreakFlagCount) + { + *BreakFlagCount = (int)(MaxBreakPosition + 1); + } +} + +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_BreakEntireString(Direction, JapaneseLineBreakStyle, ConfigFlags, Utf32, sizeof(int) * Utf32Count, KBTS_TEXT_FORMAT_UTF32, Breaks, BreakCapacity, BreakCount, BreakFlags, BreakFlagCapacity, 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) +{ + kbts_BreakEntireString(Direction, JapaneseLineBreakStyle, ConfigFlags, Utf8, Utf8Length, KBTS_TEXT_FORMAT_UTF8, Breaks, BreakCapacity, BreakCount, BreakFlags, BreakFlagCapacity, BreakFlagCount); +} + KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, kbts_un Length) { - kbts_decode Result = KBTS_ZERO; + kbts_decode Result = KBTS__ZERO; const char *Utf8Start = Utf8; if(Length) @@ -23939,7 +29582,7 @@ KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, kbts_un Length) if(Length > FollowupCharacterCount) { - KBTS_FOR(FollowupCharacterIndex, 0, FollowupCharacterCount) + KBTS__FOR(FollowupCharacterIndex, 0, FollowupCharacterCount) { kbts_u8 C = (kbts_u8)*Utf8++; @@ -23966,7 +29609,42 @@ KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, kbts_un Length) return Result; } -KBTS_EXPORT int kbts_ShaperIsComplex(kbts_shaper Shaper) +KBTS_EXPORT kbts_encode_utf8 kbts_EncodeUtf8(int Codepoint) +{ + kbts_encode_utf8 Result = KBTS__ZERO; + + if(Codepoint <= 0x7F) + { + Result.Encoded[0] = (char)Codepoint; + Result.EncodedLength = 1; + } + else if(Codepoint <= 0x7FF) + { + Result.Encoded[1] = (Codepoint & 0x3F) | 0x80; + Result.Encoded[0] = ((Codepoint >> 6) & 0x1F) | 0xC0; + Result.EncodedLength = 2; + } + else if(Codepoint <= 0xFFFF) + { + Result.Encoded[2] = (Codepoint & 0x3F) | 0x80; + Result.Encoded[1] = ((Codepoint >> 6) & 0x3F) | 0x80; + Result.Encoded[0] = ((Codepoint >> 12) & 0xF) | 0xE0; + Result.EncodedLength = 3; + } + else if(Codepoint <= 0x10FFFF) + { + Result.Encoded[3] = (Codepoint & 0x3F) | 0x80; + Result.Encoded[2] = ((Codepoint >> 6) & 0x3F) | 0x80; + Result.Encoded[1] = ((Codepoint >> 12) & 0x3F) | 0x80; + Result.Encoded[0] = ((Codepoint >> 18) & 0x7) | 0xF0; + Result.EncodedLength = 4; + } + + Result.Valid = Result.EncodedLength > 0; + return Result; +} + +static int kbts__ShaperIsComplex(kbts_shaper Shaper) { int Result = Shaper != KBTS_SHAPER_DEFAULT; // @Incomplete? @@ -23975,8 +29653,13 @@ KBTS_EXPORT int kbts_ShaperIsComplex(kbts_shaper Shaper) KBTS_EXPORT int kbts_ScriptIsComplex(kbts_script Script) { - kbts_script_properties *Properties = &kbts_ScriptProperties[Script]; - int Result = kbts_ShaperIsComplex(Properties->Shaper); + if(Script >= KBTS_SCRIPT_COUNT) + { + Script = KBTS_SCRIPT_DONT_KNOW; + } + + kbts__script_properties *Properties = &kbts__ScriptProperties[Script]; + int Result = kbts__ShaperIsComplex(Properties->Shaper); return Result; } From 6002a29c515e81f98becb9398ec040adc243bf72 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Dec 2025 11:41:35 +0000 Subject: [PATCH 2/8] Fix indentation --- vendor/kb_text_shape/kb_text_shape_types.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/kb_text_shape/kb_text_shape_types.odin b/vendor/kb_text_shape/kb_text_shape_types.odin index 6c4465319..b391ef0e4 100644 --- a/vendor/kb_text_shape/kb_text_shape_types.odin +++ b/vendor/kb_text_shape/kb_text_shape_types.odin @@ -1933,7 +1933,7 @@ break_state :: struct { } decode :: struct { - Codepoint: rune, + Codepoint: rune, SourceCharactersConsumed: c.int, Valid: b32, From c284fcb3a319029176445589fca486645da4a565 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Dec 2025 12:10:50 +0000 Subject: [PATCH 3/8] Add some examples to the docs; Improve numerous procedures --- vendor/kb_text_shape/doc.odin | 59 +++++++++++++++ vendor/kb_text_shape/kb_text_shape_procs.odin | 74 +++++++++++++++++-- 2 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 vendor/kb_text_shape/doc.odin diff --git a/vendor/kb_text_shape/doc.odin b/vendor/kb_text_shape/doc.odin new file mode 100644 index 000000000..487d4a8db --- /dev/null +++ b/vendor/kb_text_shape/doc.odin @@ -0,0 +1,59 @@ +/* + Bindings for [[ Jimmy Lefevre's Text Shape ; https://github.com/JimmyLefevre/kb ]] Unicode text segmentation and OpenType shaping. + + Example: + // Basic + OdinAllocator := context.allocator + + FontData, _ := os.read_entire_file("myfonts.ttf", OdinAllocator) + + Context := kbts.CreateShapeContext(kbts.AllocatorFromOdinAllocator(&OdinAllocator)) + kbts.ShapePushFontFromMemory(Context, FontData, 0) + + kbts.ShapeBegin(Context, .DONT_KNOW, .DONT_KNOW) + kbts.ShapeUtf8(Context, "Let's shape something!", .CODEPOINT_INDEX) + kbts.ShapeEnd(Context) + + CursorX, CursorY: c.int = 0, 0 + for Run in kbts.ShapeRun(Context) { + Run := Run + for Glyph in kbts.GlyphIteratorNext(&Run.Glyphs) { + GlyphX := CursorX + Glyph.OffsetX + GlyphY := CursorY + Glyph.OffsetY + + _ = GlyphX + _ = GlyphY + // DisplayGlyph(Glyph.Id, GlyphX, GlyphY) + + CursorX += Glyph.AdvanceX + CursorY += Glyph.AdvanceY + } + } + + Example: + // Font collections + OdinAllocator := context.allocator + + FontData, _ := os.read_entire_file("myfonts.ttf", OdinAllocator) + Font := kbts.FontFromMemory(FontData, 0, kbts.AllocatorFromOdinAllocator(&OdinAllocator)) + + _ = kbts.ShapePushFont(Context, &Font) + + FontCount := kbts.FontCount(FontData) + for FontIndex in 1.. c.int --- PlaceShapeContext :: proc(Allocator: allocator_function, AllocatorData: rawptr, Memory: rawptr) -> ^shape_context --- - PlaceShapeContextFixedMemory :: proc(Memory: rawptr, Size: c.int) -> ^shape_context --- CreateShapeContext :: proc(Allocator: allocator_function, AllocatorData: rawptr) -> ^shape_context --- DestroyShapeContext :: proc(Context: ^shape_context) --- - ShapePushFontFromMemory :: proc(Context: ^shape_context, Memory: rawptr, Size: c.int, FontIndex: c.int) -> ^font --- ShapePushFont :: proc(Context: ^shape_context, Font: ^font) -> ^font --- ShapePopFont :: proc(Context: ^shape_context) -> ^font --- ShapeBegin :: proc(Context: ^shape_context, ParagraphDirection: direction, Language: language) --- ShapeEnd :: proc(Context: ^shape_context) --- - ShapePushFeature :: proc(Context: ^shape_context, FeatureTag: u32, Value: c.int) --- - ShapePopFeature :: proc(Context: ^shape_context, FeatureTag: u32) -> b32 --- + ShapePushFeature :: proc(Context: ^shape_context, FeatureTag: feature_tag, Value: c.int) --- + ShapePopFeature :: proc(Context: ^shape_context, FeatureTag: feature_tag) -> b32 --- ShapeCodepoint :: proc(Context: ^shape_context, Codepoint: rune) --- ShapeCodepointWithUserId :: proc(Context: ^shape_context, Codepoint: rune, UserId: c.int) --- ShapeError :: proc(Context: ^shape_context) -> shape_error --- @@ -40,6 +38,23 @@ foreign lib { ShapeManualBreak :: proc(Context: ^shape_context) --- } +@(require_results) +PlaceShapeContextFixedMemory :: proc "c" (Memory: []byte) -> ^shape_context { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + PlaceShapeContextFixedMemory :: proc(Memory: rawptr, Size: c.int) -> ^shape_context --- + } + return PlaceShapeContextFixedMemory(raw_data(Memory), c.int(len(Memory))) +} + +ShapePushFontFromMemory :: proc "c" (Context: ^shape_context, Memory: []byte, FontIndex: c.int) -> ^font { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + ShapePushFontFromMemory :: proc(Context: ^shape_context, Memory: rawptr, Size: c.int, FontIndex: c.int) -> ^font --- + } + return ShapePushFontFromMemory(Context, raw_data(Memory), c.int(len(Memory)), FontIndex) +} + @(require_results) ShapeRun :: proc "contextless" (Context: ^shape_context) -> (Run: run, ok: b32) { @(default_calling_convention="c", link_prefix="kbts_", require_results) @@ -105,8 +120,6 @@ ShapeCodepointIteratorNext :: proc "contextless" (It: ^shape_codepoint_iterator) // @(default_calling_convention="c", link_prefix="kbts_", require_results) foreign lib { - FontCount :: proc(FileData: rawptr, FileSize: c.int) -> b32 --- - FontFromMemory :: proc(FileData: rawptr, FileSize: c.int, FontIndex: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> font --- FreeFont :: proc(Font: ^font) --- FontIsValid :: proc(Font: ^font) -> b32 --- LoadFont :: proc(Font: ^font, State: ^load_font_state, FontData: rawptr, FontDataSize: c.int, FontIndex: c.int, ScratchSize_: ^c.int, OutputSize_: ^c.int) -> load_font_error --- @@ -135,6 +148,24 @@ foreign lib { DestroyGlyphConfig :: proc(Config: ^glyph_config) --- } +@(require_results) +FontCount :: proc "c" (FileData: []byte) -> c.int { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + FontCount :: proc(FileData: rawptr, FileSize: c.int) -> c.int --- + } + return FontCount(raw_data(FileData), c.int(len(FileData))) +} + +@(require_results) +FontFromMemory :: proc "c" (FileData: []byte, FontIndex: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> font { + @(default_calling_convention="c", link_prefix="kbts_", require_results) + foreign lib { + FontFromMemory :: proc(FileData: rawptr, FileSize: c.int, FontIndex: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> font --- + } + return FontFromMemory(raw_data(FileData), c.int(len(FileData)), FontIndex, Allocator, AllocatorData) +} + @(require_results) ShapeDirect :: proc "contextless" (Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Allocator: allocator_function, AllocatorData: rawptr) -> (Output: glyph_iterator, err: shape_error) { @@ -321,4 +352,31 @@ GuessTextPropertiesUtf8 :: proc "contextless" (Utf8: string) -> (Direction: dire } GuessTextPropertiesUtf8(cstring(raw_data(Utf8)), c.int(len(Utf8)), &Direction, &Script) return -} \ No newline at end of file +} + + + + +@(require_results) +AllocatorFromOdinAllocator :: proc "contextless" (allocator: ^runtime.Allocator) -> (Allocator: allocator_function, AllocatorData: rawptr) { + allocator_function :: proc "c" (Data: rawptr, Op: ^allocator_op) { + if Data == nil { + return + } + context = runtime.default_context() + context.allocator = (^runtime.Allocator)(Data)^ + + switch Op.Kind { + case .NONE: + return + case .ALLOCATE: + data, _ := runtime.mem_alloc(int(Op.Allocate.Size), runtime.DEFAULT_ALIGNMENT) + Op.Allocate.Pointer = raw_data(data) + Op.Allocate.Size = u32(len(data)) + case .FREE: + _ = runtime.mem_free(Op.Free.Pointer) + } + } + + return allocator_function, rawptr(allocator) +} From b2f49e49793d7bc56c365c0844be84302f700c0a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Dec 2025 12:19:42 +0000 Subject: [PATCH 4/8] Use `bit_field` where appropriate and improve formatting --- vendor/kb_text_shape/kb_text_shape_procs.odin | 62 +++++++++--------- vendor/kb_text_shape/kb_text_shape_types.odin | 65 +++++++++++++++---- 2 files changed, 84 insertions(+), 43 deletions(-) diff --git a/vendor/kb_text_shape/kb_text_shape_procs.odin b/vendor/kb_text_shape/kb_text_shape_procs.odin index e86123fd1..c4e75fbd7 100644 --- a/vendor/kb_text_shape/kb_text_shape_procs.odin +++ b/vendor/kb_text_shape/kb_text_shape_procs.odin @@ -19,23 +19,23 @@ import "core:c" // @(default_calling_convention="c", link_prefix="kbts_", require_results) foreign lib { - SizeOfShapeContext :: proc() -> c.int --- - PlaceShapeContext :: proc(Allocator: allocator_function, AllocatorData: rawptr, Memory: rawptr) -> ^shape_context --- - CreateShapeContext :: proc(Allocator: allocator_function, AllocatorData: rawptr) -> ^shape_context --- - DestroyShapeContext :: proc(Context: ^shape_context) --- - ShapePushFont :: proc(Context: ^shape_context, Font: ^font) -> ^font --- - ShapePopFont :: proc(Context: ^shape_context) -> ^font --- - ShapeBegin :: proc(Context: ^shape_context, ParagraphDirection: direction, Language: language) --- - ShapeEnd :: proc(Context: ^shape_context) --- - ShapePushFeature :: proc(Context: ^shape_context, FeatureTag: feature_tag, Value: c.int) --- - ShapePopFeature :: proc(Context: ^shape_context, FeatureTag: feature_tag) -> b32 --- - ShapeCodepoint :: proc(Context: ^shape_context, Codepoint: rune) --- - ShapeCodepointWithUserId :: proc(Context: ^shape_context, Codepoint: rune, UserId: c.int) --- - ShapeError :: proc(Context: ^shape_context) -> shape_error --- - ShapeBeginManualRuns :: proc(Context: ^shape_context) --- - ShapeNextManualRun :: proc(Context: ^shape_context, Direction: direction, Script: script) --- - ShapeEndManualRuns :: proc(Context: ^shape_context) --- - ShapeManualBreak :: proc(Context: ^shape_context) --- + SizeOfShapeContext :: proc() -> c.int --- + PlaceShapeContext :: proc(Allocator: allocator_function, AllocatorData: rawptr, Memory: rawptr) -> ^shape_context --- + CreateShapeContext :: proc(Allocator: allocator_function, AllocatorData: rawptr) -> ^shape_context --- + DestroyShapeContext :: proc(Context: ^shape_context) --- + ShapePushFont :: proc(Context: ^shape_context, Font: ^font) -> ^font --- + ShapePopFont :: proc(Context: ^shape_context) -> ^font --- + ShapeBegin :: proc(Context: ^shape_context, ParagraphDirection: direction, Language: language) --- + ShapeEnd :: proc(Context: ^shape_context) --- + ShapePushFeature :: proc(Context: ^shape_context, FeatureTag: feature_tag, Value: c.int) --- + ShapePopFeature :: proc(Context: ^shape_context, FeatureTag: feature_tag) -> b32 --- + ShapeCodepoint :: proc(Context: ^shape_context, Codepoint: rune) --- + ShapeCodepointWithUserId :: proc(Context: ^shape_context, Codepoint: rune, UserId: c.int) --- + ShapeError :: proc(Context: ^shape_context) -> shape_error --- + ShapeBeginManualRuns :: proc(Context: ^shape_context) --- + ShapeNextManualRun :: proc(Context: ^shape_context, Direction: direction, Script: script) --- + ShapeEndManualRuns :: proc(Context: ^shape_context) --- + ShapeManualBreak :: proc(Context: ^shape_context) --- } @(require_results) @@ -120,17 +120,17 @@ ShapeCodepointIteratorNext :: proc "contextless" (It: ^shape_codepoint_iterator) // @(default_calling_convention="c", link_prefix="kbts_", require_results) foreign lib { - FreeFont :: proc(Font: ^font) --- - FontIsValid :: proc(Font: ^font) -> b32 --- - LoadFont :: proc(Font: ^font, State: ^load_font_state, FontData: rawptr, FontDataSize: c.int, FontIndex: c.int, ScratchSize_: ^c.int, OutputSize_: ^c.int) -> load_font_error --- - PlaceBlob :: proc(Font: ^font, State: ^load_font_state, ScratchMemory: rawptr, OutputMemory: rawptr) -> load_font_error --- - GetFontInfo :: proc(Font: ^font, Info: ^font_info) --- + FreeFont :: proc(Font: ^font) --- + FontIsValid :: proc(Font: ^font) -> b32 --- + LoadFont :: proc(Font: ^font, State: ^load_font_state, FontData: rawptr, FontDataSize: c.int, FontIndex: c.int, ScratchSize_: ^c.int, OutputSize_: ^c.int) -> load_font_error --- + PlaceBlob :: proc(Font: ^font, State: ^load_font_state, ScratchMemory: rawptr, OutputMemory: rawptr) -> load_font_error --- + GetFontInfo :: proc(Font: ^font, Info: ^font_info) --- // A shape_config is a bag of pre-computed data for a specific shaping setup. - SizeOfShapeConfig :: proc(Font: ^font, Script: script, Language: language) -> b32 --- - PlaceShapeConfig :: proc(Font: ^font, Script: script, Language: language, Memory: rawptr) -> ^shape_config --- - CreateShapeConfig :: proc(Font: ^font, Script: script, Language: language, Allocator: allocator_function, AllocatorData: rawptr) -> ^shape_config --- - DestroyShapeConfig :: proc(Config: ^shape_config) --- + SizeOfShapeConfig :: proc(Font: ^font, Script: script, Language: language) -> b32 --- + PlaceShapeConfig :: proc(Font: ^font, Script: script, Language: language, Memory: rawptr) -> ^shape_config --- + CreateShapeConfig :: proc(Font: ^font, Script: script, Language: language, Allocator: allocator_function, AllocatorData: rawptr) -> ^shape_config --- + DestroyShapeConfig :: proc(Config: ^shape_config) --- // A glyph_storage holds and recycles glyph data. InitializeGlyphStorage :: proc(Storage: ^glyph_storage, Allocator: allocator_function, AllocatorData: rawptr) -> b32 --- @@ -145,7 +145,7 @@ foreign lib { // A glyph_config specifies glyph-specific shaping parameters. // A single glyph_config can be shared by multiple glyphs. - DestroyGlyphConfig :: proc(Config: ^glyph_config) --- + DestroyGlyphConfig :: proc(Config: ^glyph_config) --- } @(require_results) @@ -303,10 +303,10 @@ foreign lib { FontCoverageTestCodepoint :: proc(Test: ^font_coverage_test, Codepoint: rune) --- FontCoverageTestEnd :: proc(Test: ^font_coverage_test) -> b32 --- - EncodeUtf8 :: proc(Codepoint: rune) -> encode_utf8 --- - ScriptDirection :: proc(Script: script) -> direction --- - ScriptIsComplex :: proc(Script: script) -> b32 --- - ScriptTagToScript :: proc(Tag: script_tag) -> script --- + EncodeUtf8 :: proc(Codepoint: rune) -> encode_utf8 --- + ScriptDirection :: proc(Script: script) -> direction --- + ScriptIsComplex :: proc(Script: script) -> b32 --- + ScriptTagToScript :: proc(Tag: script_tag) -> script --- } @(require_results) diff --git a/vendor/kb_text_shape/kb_text_shape_types.odin b/vendor/kb_text_shape/kb_text_shape_types.odin index b391ef0e4..f56d5436d 100644 --- a/vendor/kb_text_shape/kb_text_shape_types.odin +++ b/vendor/kb_text_shape/kb_text_shape_types.odin @@ -1868,8 +1868,12 @@ break_type :: struct { bracket :: struct { Codepoint: rune, Position: u32, - Direction: u8, - Script: u8, + using DirectionBitField: bit_field u8 { + Direction: direction | 8, + }, + using ScriptBitField: bit_field u8 { + Script: script | 8, + }, } // In the worst case, a single call to BreakAddCodepoint would generate 4 breaks. @@ -1899,26 +1903,63 @@ break_state :: struct { BracketCount: u32, Flags: break_state_flags, - FlagState: u32, // u8(break_flags)x4 + FlagState: bit_field u32 { + _0: u32 | 8, // break_flags + _1: u32 | 8, // break_flags + _2: u32 | 8, // break_flags + _3: u32 | 8, // break_flags + }, PositionOffset2: i16, PositionOffset3: i16, - WordBreakHistory: u32, // u8x4 - WordBreaks: u16, // u4x4 - WordUnbreaks: u16, // u4x4 + WordBreakHistory: bit_field u32 { + _0: u32 | 8, + _1: u32 | 8, + _2: u32 | 8, + _3: u32 | 8, + }, + WordBreaks, + WordUnbreaks: bit_field u16 { + _0: u16 | 4, + _1: u16 | 4, + _2: u16 | 4, + _3: u16 | 4, + }, WordBreak2PositionOffset: i16, - LineBreaks: u64, // u16x4 + LineBreaks: bit_field u64 { + _0: u64 | 16, + _1: u64 | 16, + _2: u64 | 16, + _3: u64 | 16, + }, // 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.) - LineUnbreaksAsync: u64, // u16x4 - LineUnbreaks: u64, // u16x4 - LineBreakHistory: u32, // u8(line_break_class)x4 + LineUnbreaksAsync: bit_field u64 { + _0: u64 | 16, + _1: u64 | 16, + _2: u64 | 16, + _3: u64 | 16, + }, + LineUnbreaks: bit_field u64 { + _0: u64 | 16, + _1: u64 | 16, + _2: u64 | 16, + _3: u64 | 16, + }, + LineBreakHistory: bit_field u32 { + _0: u32 | 8, + _1: u32 | 8, + _2: u32 | 8, + _3: u32 | 8, + }, LineBreak2PositionOffset: i16, LineBreak3PositionOffset: i16, - LastDirection: u8, + using LastDirectionBitField: bit_field u8 { + LastDirection: direction | 8, + }, BidirectionalClass2: u8, BidirectionalClass1: u8, Bidirectional1PositionOffset: i16, @@ -2012,7 +2053,7 @@ glyph :: struct { // Unicode properties filled in by CodepointToGlyph. JoiningType: unicode_joining_type, - UnicodeFlags: u8, + UnicodeFlags: unicode_flags, SyllabicClass: u8, SyllabicPosition: u8, UseClass: u8, From 0bbb65614745e5a9ed0347f17d4c5aa3cbabe9d8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Dec 2025 12:23:17 +0000 Subject: [PATCH 5/8] Use `b32` for `Error` fields instead of `c.int` --- vendor/kb_text_shape/kb_text_shape_types.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor/kb_text_shape/kb_text_shape_types.odin b/vendor/kb_text_shape/kb_text_shape_types.odin index f56d5436d..2d29267ae 100644 --- a/vendor/kb_text_shape/kb_text_shape_types.odin +++ b/vendor/kb_text_shape/kb_text_shape_types.odin @@ -2108,7 +2108,7 @@ arena :: struct { BlockSentinel: arena_block_header, FreeBlockSentinel: arena_block_header, - Error: c.int, + Error: b32, } glyph_storage :: struct { @@ -2117,7 +2117,7 @@ glyph_storage :: struct { GlyphSentinel: glyph, FreeGlyphSentinel: glyph, - Error: c.int, + Error: b32, } glyph_parent :: struct { @@ -2129,8 +2129,8 @@ font_coverage_test :: struct { Font: ^font, BaseCodepoint: rune, - CurrentBaseError: c.int, - Error: c.int, + CurrentBaseError: b32, + Error: b32, BaseParents: [MAXIMUM_RECOMPOSITION_PARENTS]glyph_parent `fmt:"v,BaseParentCount"`, BaseParentCount: u32, From 9a2a240293637e9205f4bc58bdf911c726f9211b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Dec 2025 12:59:14 +0000 Subject: [PATCH 6/8] Wrap `LoadFont`; More styling changes --- vendor/kb_text_shape/doc.odin | 4 +- vendor/kb_text_shape/kb_text_shape_procs.odin | 207 +++++++++--------- 2 files changed, 109 insertions(+), 102 deletions(-) diff --git a/vendor/kb_text_shape/doc.odin b/vendor/kb_text_shape/doc.odin index 487d4a8db..685b0d5a0 100644 --- a/vendor/kb_text_shape/doc.odin +++ b/vendor/kb_text_shape/doc.odin @@ -21,9 +21,7 @@ GlyphX := CursorX + Glyph.OffsetX GlyphY := CursorY + Glyph.OffsetY - _ = GlyphX - _ = GlyphY - // DisplayGlyph(Glyph.Id, GlyphX, GlyphY) + DisplayGlyph(Glyph.Id, GlyphX, GlyphY) CursorX += Glyph.AdvanceX CursorY += Glyph.AdvanceY diff --git a/vendor/kb_text_shape/kb_text_shape_procs.odin b/vendor/kb_text_shape/kb_text_shape_procs.odin index c4e75fbd7..686328b28 100644 --- a/vendor/kb_text_shape/kb_text_shape_procs.odin +++ b/vendor/kb_text_shape/kb_text_shape_procs.odin @@ -40,59 +40,59 @@ foreign lib { @(require_results) PlaceShapeContextFixedMemory :: proc "c" (Memory: []byte) -> ^shape_context { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - PlaceShapeContextFixedMemory :: proc(Memory: rawptr, Size: c.int) -> ^shape_context --- + kbts_PlaceShapeContextFixedMemory :: proc(Memory: rawptr, Size: c.int) -> ^shape_context --- } - return PlaceShapeContextFixedMemory(raw_data(Memory), c.int(len(Memory))) + return kbts_PlaceShapeContextFixedMemory(raw_data(Memory), c.int(len(Memory))) } ShapePushFontFromMemory :: proc "c" (Context: ^shape_context, Memory: []byte, FontIndex: c.int) -> ^font { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - ShapePushFontFromMemory :: proc(Context: ^shape_context, Memory: rawptr, Size: c.int, FontIndex: c.int) -> ^font --- + kbts_ShapePushFontFromMemory :: proc(Context: ^shape_context, Memory: rawptr, Size: c.int, FontIndex: c.int) -> ^font --- } - return ShapePushFontFromMemory(Context, raw_data(Memory), c.int(len(Memory)), FontIndex) + return kbts_ShapePushFontFromMemory(Context, raw_data(Memory), c.int(len(Memory)), FontIndex) } @(require_results) -ShapeRun :: proc "contextless" (Context: ^shape_context) -> (Run: run, ok: b32) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) +ShapeRun :: proc "contextless" (Context: ^shape_context) -> (Run: run, Ok: b32) { + @(default_calling_convention="c", require_results) foreign lib { - ShapeRun :: proc(Context: ^shape_context, Run: ^run) -> b32 --- + kbts_ShapeRun :: proc(Context: ^shape_context, Run: ^run) -> b32 --- } - ok = ShapeRun(Context, &Run) + Ok = kbts_ShapeRun(Context, &Run) return } ShapeUtf32 :: proc "c" (Context: ^shape_context, Utf32: []rune) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - ShapeUtf32 :: proc(Context: ^shape_context, Utf32: [^]rune, Length: c.int) --- + kbts_ShapeUtf32 :: proc(Context: ^shape_context, Utf32: [^]rune, Length: c.int) --- } - ShapeUtf32(Context, raw_data(Utf32), c.int(len(Utf32))) + kbts_ShapeUtf32(Context, raw_data(Utf32), c.int(len(Utf32))) } ShapeUtf32WithUserId :: proc "c" (Context: ^shape_context, Utf32: []rune, UserId: c.int, UserIdIncrement: c.int) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - ShapeUtf32WithUserId :: proc(Context: ^shape_context, Utf32: [^]rune, Length: c.int, UserId: c.int, UserIdIncrement: c.int) --- + kbts_ShapeUtf32WithUserId :: proc(Context: ^shape_context, Utf32: [^]rune, Length: c.int, UserId: c.int, UserIdIncrement: c.int) --- } - ShapeUtf32WithUserId(Context, raw_data(Utf32), c.int(len(Utf32)), UserId, UserIdIncrement) + kbts_ShapeUtf32WithUserId(Context, raw_data(Utf32), c.int(len(Utf32)), UserId, UserIdIncrement) } ShapeUtf8 :: proc(Context: ^shape_context, Utf8: string, UserIdGenerationMode: user_id_generation_mode) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - ShapeUtf8 :: proc(Context: ^shape_context, Utf8: [^]byte, Length: c.int, UserIdGenerationMode: user_id_generation_mode) --- + kbts_ShapeUtf8 :: proc(Context: ^shape_context, Utf8: [^]byte, Length: c.int, UserIdGenerationMode: user_id_generation_mode) --- } - ShapeUtf8(Context, raw_data(Utf8), c.int(len(Utf8)), UserIdGenerationMode) + kbts_ShapeUtf8(Context, raw_data(Utf8), c.int(len(Utf8)), UserIdGenerationMode) } ShapeUtf8WithUserId :: proc(Context: ^shape_context, Utf8: string, UserId: c.int, UserIdGenerationMode: user_id_generation_mode) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - ShapeUtf8WithUserId :: proc(Context: ^shape_context, Utf8: [^]byte, Length: c.int, UserId: c.int, UserIdGenerationMode: user_id_generation_mode) --- + kbts_ShapeUtf8WithUserId :: proc(Context: ^shape_context, Utf8: [^]byte, Length: c.int, UserId: c.int, UserIdGenerationMode: user_id_generation_mode) --- } - ShapeUtf8WithUserId(Context, raw_data(Utf8), c.int(len(Utf8)), UserId, UserIdGenerationMode) + kbts_ShapeUtf8WithUserId(Context, raw_data(Utf8), c.int(len(Utf8)), UserId, UserIdGenerationMode) } @@ -104,12 +104,12 @@ foreign lib { } @(require_results) -ShapeCodepointIteratorNext :: proc "contextless" (It: ^shape_codepoint_iterator) -> (Codepoint: shape_codepoint, CodepointIndex: c.int, ok: b32) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) +ShapeCodepointIteratorNext :: proc "contextless" (It: ^shape_codepoint_iterator) -> (Codepoint: shape_codepoint, CodepointIndex: c.int, Ok: b32) { + @(default_calling_convention="c", require_results) foreign lib { - ShapeCodepointIteratorNext :: proc(It: ^shape_codepoint_iterator, Codepoint: ^shape_codepoint, CodepointIndex: ^c.int) -> b32 --- + kbts_ShapeCodepointIteratorNext :: proc(It: ^shape_codepoint_iterator, Codepoint: ^shape_codepoint, CodepointIndex: ^c.int) -> b32 --- } - ok = ShapeCodepointIteratorNext(It, &Codepoint, &CodepointIndex) + Ok = kbts_ShapeCodepointIteratorNext(It, &Codepoint, &CodepointIndex) return } @@ -122,7 +122,6 @@ ShapeCodepointIteratorNext :: proc "contextless" (It: ^shape_codepoint_iterator) foreign lib { FreeFont :: proc(Font: ^font) --- FontIsValid :: proc(Font: ^font) -> b32 --- - LoadFont :: proc(Font: ^font, State: ^load_font_state, FontData: rawptr, FontDataSize: c.int, FontIndex: c.int, ScratchSize_: ^c.int, OutputSize_: ^c.int) -> load_font_error --- PlaceBlob :: proc(Font: ^font, State: ^load_font_state, ScratchMemory: rawptr, OutputMemory: rawptr) -> load_font_error --- GetFontInfo :: proc(Font: ^font, Info: ^font_info) --- @@ -149,70 +148,80 @@ foreign lib { } @(require_results) -FontCount :: proc "c" (FileData: []byte) -> c.int { - @(default_calling_convention="c", link_prefix="kbts_", require_results) +LoadFont :: proc "contextless" (Font: ^font, State: ^load_font_state, FontData: []byte, FontIndex: c.int) -> (ScratchSize, OutputSize: c.int, Err: load_font_error) { + @(default_calling_convention="c", require_results) foreign lib { - FontCount :: proc(FileData: rawptr, FileSize: c.int) -> c.int --- + kbts_LoadFont :: proc(Font: ^font, State: ^load_font_state, FontData: rawptr, FontDataSize: c.int, FontIndex: c.int, ScratchSize_: ^c.int, OutputSize_: ^c.int) -> load_font_error --- } - return FontCount(raw_data(FileData), c.int(len(FileData))) -} - -@(require_results) -FontFromMemory :: proc "c" (FileData: []byte, FontIndex: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> font { - @(default_calling_convention="c", link_prefix="kbts_", require_results) - foreign lib { - FontFromMemory :: proc(FileData: rawptr, FileSize: c.int, FontIndex: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> font --- - } - return FontFromMemory(raw_data(FileData), c.int(len(FileData)), FontIndex, Allocator, AllocatorData) -} - - -@(require_results) -ShapeDirect :: proc "contextless" (Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Allocator: allocator_function, AllocatorData: rawptr) -> (Output: glyph_iterator, err: shape_error) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) - foreign lib { - ShapeDirect :: proc(Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Allocator: allocator_function, AllocatorData: rawptr, Output: ^glyph_iterator) -> shape_error --- - } - err = ShapeDirect(Config, Storage, RunDirection, Allocator, AllocatorData, &Output) + Err = kbts_LoadFont(Font, State, raw_data(FontData), c.int(len(FontData)), FontIndex, &ScratchSize, &OutputSize) return } @(require_results) -ShapeDirectFixedMemory :: proc "contextless" (Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Memory: rawptr, MemorySize: c.int) -> (Output: glyph_iterator, err: shape_error) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) +FontCount :: proc "c" (FileData: []byte) -> c.int { + @(default_calling_convention="c", require_results) foreign lib { - ShapeDirectFixedMemory :: proc(Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Memory: rawptr, MemorySize: c.int, Output: ^glyph_iterator) -> shape_error --- + kbts_FontCount :: proc(FileData: rawptr, FileSize: c.int) -> c.int --- } - err = ShapeDirectFixedMemory(Config, Storage, RunDirection, Memory, MemorySize, &Output) + return kbts_FontCount(raw_data(FileData), c.int(len(FileData))) +} + +@(require_results) +FontFromMemory :: proc "c" (FileData: []byte, FontIndex: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> font { + @(default_calling_convention="c", require_results) + foreign lib { + kbts_FontFromMemory :: proc(FileData: rawptr, FileSize: c.int, FontIndex: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> font --- + } + return kbts_FontFromMemory(raw_data(FileData), c.int(len(FileData)), FontIndex, Allocator, AllocatorData) +} + + +@(require_results) +ShapeDirect :: proc "contextless" (Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Allocator: allocator_function, AllocatorData: rawptr) -> (Output: glyph_iterator, Err: shape_error) { + @(default_calling_convention="c", require_results) + foreign lib { + kbts_ShapeDirect :: proc(Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Allocator: allocator_function, AllocatorData: rawptr, Output: ^glyph_iterator) -> shape_error --- + } + Err = kbts_ShapeDirect(Config, Storage, RunDirection, Allocator, AllocatorData, &Output) + return +} + +@(require_results) +ShapeDirectFixedMemory :: proc "contextless" (Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Memory: rawptr, MemorySize: c.int) -> (Output: glyph_iterator, Err: shape_error) { + @(default_calling_convention="c", require_results) + foreign lib { + kbts_ShapeDirectFixedMemory :: proc(Config: ^shape_config, Storage: ^glyph_storage, RunDirection: direction, Memory: rawptr, MemorySize: c.int, Output: ^glyph_iterator) -> shape_error --- + } + Err = kbts_ShapeDirectFixedMemory(Config, Storage, RunDirection, Memory, MemorySize, &Output) return } @(require_results) SizeOfGlyphConfig :: proc "c" (Overrides: []feature_override) -> c.int { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - SizeOfGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int) -> c.int --- + kbts_SizeOfGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int) -> c.int --- } - return SizeOfGlyphConfig(raw_data(Overrides), c.int(len(Overrides))) + return kbts_SizeOfGlyphConfig(raw_data(Overrides), c.int(len(Overrides))) } @(require_results) PlaceGlyphConfig :: proc "c" (Overrides: []feature_override, Memory: rawptr) -> ^glyph_config { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - PlaceGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int, Memory: rawptr) -> ^glyph_config --- + kbts_PlaceGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int, Memory: rawptr) -> ^glyph_config --- } - return PlaceGlyphConfig(raw_data(Overrides), c.int(len(Overrides)), Memory) + return kbts_PlaceGlyphConfig(raw_data(Overrides), c.int(len(Overrides)), Memory) } @(require_results) CreateGlyphConfig :: proc(Overrides: []feature_override, Allocator: allocator_function, AllocatorData: rawptr) -> ^glyph_config { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - CreateGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> ^glyph_config --- + kbts_CreateGlyphConfig :: proc(Overrides: [^]feature_override, OverrideCount: c.int, Allocator: allocator_function, AllocatorData: rawptr) -> ^glyph_config --- } - return CreateGlyphConfig(raw_data(Overrides), c.int(len(Overrides)), Allocator, AllocatorData) + return kbts_CreateGlyphConfig(raw_data(Overrides), c.int(len(Overrides)), Allocator, AllocatorData) } @(default_calling_convention="c", link_prefix="kbts_", require_results) @@ -221,12 +230,12 @@ foreign lib { } @(require_results) -GlyphIteratorNext :: proc "contextless" (It: ^glyph_iterator) -> (Glyph: ^glyph, ok: b32) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) +GlyphIteratorNext :: proc "contextless" (It: ^glyph_iterator) -> (Glyph: ^glyph, Ok: b32) { + @(default_calling_convention="c", require_results) foreign lib { - GlyphIteratorNext :: proc(It: ^glyph_iterator, Glyph: ^^glyph) -> b32 --- + kbts_GlyphIteratorNext :: proc(It: ^glyph_iterator, Glyph: ^^glyph) -> b32 --- } - ok = GlyphIteratorNext(It, &Glyph) + Ok = kbts_GlyphIteratorNext(It, &Glyph) return } @@ -242,12 +251,12 @@ foreign lib { } @(require_results) -Break :: proc "contextless" (State: ^break_state) -> (Break: break_type, ok: b32) { +Break :: proc "contextless" (State: ^break_state) -> (Break: break_type, Ok: b32) { @(default_calling_convention="c", require_results) foreign lib { kbts_Break :: proc(State: ^break_state, Break: ^break_type) -> b32 --- } - ok = kbts_Break(State, &Break) + Ok = kbts_Break(State, &Break) return } @@ -256,42 +265,42 @@ BreakEntireString :: proc "c" (Direction: direction, JapaneseLineBreakStyle: jap Input: []byte, InputFormat: text_format, Breaks: []break_type, BreakCount: ^c.int, BreakFlags: []break_flags, BreakFlagCount: ^c.int) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - BreakEntireString :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, - Input: rawptr, InputSizeInBytes: c.int, InputFormat: text_format, - Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, - BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- + kbts_BreakEntireString :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Input: rawptr, InputSizeInBytes: c.int, InputFormat: text_format, + Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, + BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- } - BreakEntireString(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Input), c.int(len(Input)), InputFormat, raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) + kbts_BreakEntireString(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Input), c.int(len(Input)), InputFormat, raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) } BreakEntireStringUtf32 :: proc "c" (Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, Utf32: []rune, Breaks: []break_type, BreakCount: ^c.int, BreakFlags: []break_flags, BreakFlagCount: ^c.int) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - BreakEntireStringUtf32 :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, - Utf32: [^]rune, Utf32Count: c.int, - Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, - BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- + kbts_BreakEntireStringUtf32 :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Utf32: [^]rune, Utf32Count: c.int, + Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, + BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- } - BreakEntireStringUtf32(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Utf32), c.int(len(Utf32)), raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) + kbts_BreakEntireStringUtf32(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Utf32), c.int(len(Utf32)), raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) } BreakEntireStringUtf8 :: proc "c" (Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, Utf8: string, Breaks: []break_type, BreakCount: ^c.int, BreakFlags: []break_flags, BreakFlagCount: ^c.int) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - BreakEntireStringUtf8 :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, - Utf8: [^]byte, Utf8Length: c.int, - Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, - BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- + kbts_BreakEntireStringUtf8 :: proc(Direction: direction, JapaneseLineBreakStyle: japanese_line_break_style, ConfigFlags: break_config_flags, + Utf8: [^]byte, Utf8Length: c.int, + Breaks: [^]break_type, BreakCapacity: c.int, BreakCount: ^c.int, + BreakFlags: [^]break_flags, BreakFlagCapacity: c.int, BreakFlagCount: ^c.int) --- } - BreakEntireStringUtf8(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Utf8), c.int(len(Utf8)), raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) + kbts_BreakEntireStringUtf8(Direction, JapaneseLineBreakStyle, ConfigFlags, raw_data(Utf8), c.int(len(Utf8)), raw_data(Breaks), c.int(len(Breaks)), BreakCount, raw_data(BreakFlags), c.int(len(BreakFlags)), BreakFlagCount) } @@ -311,22 +320,22 @@ foreign lib { @(require_results) DecodeUtf8 :: proc "c" (Utf8: string) -> decode { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - DecodeUtf8 :: proc(Utf8: [^]byte, Length: un) -> decode --- + kbts_DecodeUtf8 :: proc(Utf8: [^]byte, Length: un) -> decode --- } - return DecodeUtf8(raw_data(Utf8), un(len(Utf8))) + return kbts_DecodeUtf8(raw_data(Utf8), un(len(Utf8))) } // 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. @(require_results) GuessTextProperties :: proc "contextless" (Text: []byte, Format: text_format) -> (Direction: direction, Script: script) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - GuessTextProperties :: proc(Text: rawptr, TextSizeInBytes: c.int, Format: text_format, Direction: ^direction, Script: ^script) --- + kbts_GuessTextProperties :: proc(Text: rawptr, TextSizeInBytes: c.int, Format: text_format, Direction: ^direction, Script: ^script) --- } - GuessTextProperties(raw_data(Text), c.int(len(Text)), Format, &Direction, &Script) + kbts_GuessTextProperties(raw_data(Text), c.int(len(Text)), Format, &Direction, &Script) return } @@ -334,11 +343,11 @@ GuessTextProperties :: proc "contextless" (Text: []byte, Format: text_format) -> // It is convenient, but only works if you are sure your input text is mono-script and mono-direction. @(require_results) GuessTextPropertiesUtf32 :: proc "contextless" (Utf32: []rune) -> (Direction: direction, Script: script) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - GuessTextPropertiesUtf32 :: proc(Utf32: [^]rune, Utf32Count: c.int, Direction: ^direction, Script: ^script) --- + kbts_GuessTextPropertiesUtf32 :: proc(Utf32: [^]rune, Utf32Count: c.int, Direction: ^direction, Script: ^script) --- } - GuessTextPropertiesUtf32(raw_data(Utf32), c.int(len(Utf32)), &Direction, &Script) + kbts_GuessTextPropertiesUtf32(raw_data(Utf32), c.int(len(Utf32)), &Direction, &Script) return } @@ -346,11 +355,11 @@ GuessTextPropertiesUtf32 :: proc "contextless" (Utf32: []rune) -> (Direction: di // It is convenient, but only works if you are sure your input text is mono-script and mono-direction._results) @(require_results) GuessTextPropertiesUtf8 :: proc "contextless" (Utf8: string) -> (Direction: direction, Script: script) { - @(default_calling_convention="c", link_prefix="kbts_", require_results) + @(default_calling_convention="c", require_results) foreign lib { - GuessTextPropertiesUtf8 :: proc(Utf8: cstring, Utf8Length: c.int, Direction: ^direction, Script: ^script) --- + kbts_GuessTextPropertiesUtf8 :: proc(Utf8: cstring, Utf8Length: c.int, Direction: ^direction, Script: ^script) --- } - GuessTextPropertiesUtf8(cstring(raw_data(Utf8)), c.int(len(Utf8)), &Direction, &Script) + kbts_GuessTextPropertiesUtf8(cstring(raw_data(Utf8)), c.int(len(Utf8)), &Direction, &Script) return } From ccbe5845601a5a4d7c14f5744e9ea42a2e50be90 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Dec 2025 13:01:24 +0000 Subject: [PATCH 7/8] `data` -> `res` to minimize confusion --- vendor/kb_text_shape/kb_text_shape_procs.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/kb_text_shape/kb_text_shape_procs.odin b/vendor/kb_text_shape/kb_text_shape_procs.odin index 686328b28..a9cee184c 100644 --- a/vendor/kb_text_shape/kb_text_shape_procs.odin +++ b/vendor/kb_text_shape/kb_text_shape_procs.odin @@ -379,9 +379,9 @@ AllocatorFromOdinAllocator :: proc "contextless" (allocator: ^runtime.Allocator) case .NONE: return case .ALLOCATE: - data, _ := runtime.mem_alloc(int(Op.Allocate.Size), runtime.DEFAULT_ALIGNMENT) - Op.Allocate.Pointer = raw_data(data) - Op.Allocate.Size = u32(len(data)) + res, _ := runtime.mem_alloc(int(Op.Allocate.Size), runtime.DEFAULT_ALIGNMENT) + Op.Allocate.Pointer = raw_data(res) + Op.Allocate.Size = u32(len(res)) case .FREE: _ = runtime.mem_free(Op.Free.Pointer) } From f8bb5c742744db447ca062de6ffadb579920ab90 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Dec 2025 13:12:29 +0000 Subject: [PATCH 8/8] Wrap `EncodeUtf8` --- vendor/kb_text_shape/kb_text_shape_procs.odin | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/vendor/kb_text_shape/kb_text_shape_procs.odin b/vendor/kb_text_shape/kb_text_shape_procs.odin index a9cee184c..08dd4c300 100644 --- a/vendor/kb_text_shape/kb_text_shape_procs.odin +++ b/vendor/kb_text_shape/kb_text_shape_procs.odin @@ -312,12 +312,21 @@ foreign lib { FontCoverageTestCodepoint :: proc(Test: ^font_coverage_test, Codepoint: rune) --- FontCoverageTestEnd :: proc(Test: ^font_coverage_test) -> b32 --- - EncodeUtf8 :: proc(Codepoint: rune) -> encode_utf8 --- ScriptDirection :: proc(Script: script) -> direction --- ScriptIsComplex :: proc(Script: script) -> b32 --- ScriptTagToScript :: proc(Tag: script_tag) -> script --- } +@(require_results) +EncodeUtf8 :: proc "c" (Codepoint: rune) -> (Encoded: [4]u8, EncodedLength: c.int, Valid: b32) { + return expand_values(kbts_EncodeUtf8(Codepoint)) +} + +@(default_calling_convention="c", require_results) +foreign lib { + kbts_EncodeUtf8 :: proc(Codepoint: rune) -> encode_utf8 --- +} + @(require_results) DecodeUtf8 :: proc "c" (Utf8: string) -> decode { @(default_calling_convention="c", require_results)