diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 7d4b53a7cb9..7bb65dced4d 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -174,6 +174,33 @@ Copyright: 2015-2020 Google, Inc. 2002, NVIDIA Corporation. License: glslang +Files: ./thirdparty/graphite/ +Comment: Graphite engine +Copyright: 2010, SIL International +License: MPL-2.0 + +Files: ./thirdparty/harfbuzz/ +Comment: HarfBuzz text shaping library +Copyright: 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc. + 2018,2019,2020 Ebrahim Byagowi + 2019,2020 Facebook, Inc. + 2012 Mozilla Foundation + 2011 Codethink Limited + 2008,2010 Nokia Corporation and/or its subsidiary(-ies) + 2009 Keith Stribley + 2009 Martin Hosken and SIL International + 2007 Chris Wilson + 2006 Behdad Esfahbod + 2005 David Turner + 2004,2007,2008,2009,2010 Red Hat, Inc. + 1998-2004 David Turner and Werner Lemberg +License: HarfBuzz + +Files: ./thirdparty/icu4c/ +Comment: International Components for Unicode +Copyright: 1991-2020, Unicode +License: Unicode + Files: ./thirdparty/jpeg_compressor/ Comment: jpeg-compressor Copyright: 2012, Rich Geldreich @@ -1180,6 +1207,46 @@ License: FTL Robert Wilhelm Werner Lemberg +License: HarfBuzz + HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. + For parts of HarfBuzz that are licensed under different licenses see individual + files names COPYING in subdirectories where applicable. + . + Copyright (C) 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc. + Copyright (C) 2018,2019,2020 Ebrahim Byagowi + Copyright (C) 2019,2020 Facebook, Inc. + Copyright (C) 2012 Mozilla Foundation + Copyright (C) 2011 Codethink Limited + Copyright (C) 2008,2010 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Keith Stribley + Copyright (C) 2009 Martin Hosken and SIL International + Copyright (C) 2007 Chris Wilson + Copyright (C) 2006 Behdad Esfahbod + Copyright (C) 2005 David Turner + Copyright (C) 2004,2007,2008,2009,2010 Red Hat, Inc. + Copyright (C) 1998-2004 David Turner and Werner Lemberg + . + For full copyright notices consult the individual files in the package. + . + . + Permission is hereby granted, without written agreement and without + license or royalty fees, to use, copy, modify, and distribute this + software and its documentation for any purpose, provided that the + above copyright notice and the following two paragraphs appear in + all copies of this software. + . + IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + . + THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + License: MPL-2.0 Mozilla Public License Version 2.0 ================================== @@ -1650,6 +1717,41 @@ License: Tamsyn In no event will the author be held liable for damages arising from the use of this font. +License: Unicode + COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later) + . + Copyright (C) 1991-2020 Unicode, Inc. All rights reserved. + Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + . + Permission is hereby granted, free of charge, to any person obtaining + a copy of the Unicode data files and any associated documentation + (the "Data Files") or Unicode software and any associated documentation + (the "Software") to deal in the Data Files or Software + without restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, and/or sell copies of + the Data Files or Software, and to permit persons to whom the Data Files + or Software are furnished to do so, provided that either + (a) this copyright and permission notice appear with all copies + of the Data Files or Software, or + (b) this copyright and permission notice appear in associated + Documentation. + . + THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS + NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL + DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THE DATA FILES OR SOFTWARE. + . + Except as contained in this notice, the name of a copyright holder + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in these Data Files or Software without prior + written authorization of the copyright holder. + License: Zlib This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/SConstruct b/SConstruct index d82ceb514b2..10f961cff8f 100644 --- a/SConstruct +++ b/SConstruct @@ -144,6 +144,9 @@ opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundle opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True)) opts.Add(BoolVariable("builtin_freetype", "Use the built-in FreeType library", True)) opts.Add(BoolVariable("builtin_glslang", "Use the built-in glslang library", True)) +opts.Add(BoolVariable("builtin_graphite", "Use the built-in Graphite library", True)) +opts.Add(BoolVariable("builtin_harfbuzz", "Use the built-in HarfBuzz library", True)) +opts.Add(BoolVariable("builtin_icu", "Use the built-in ICU library", True)) opts.Add(BoolVariable("builtin_libogg", "Use the built-in libogg library", True)) opts.Add(BoolVariable("builtin_libpng", "Use the built-in libpng library", True)) opts.Add(BoolVariable("builtin_libtheora", "Use the built-in libtheora library", True)) diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub new file mode 100644 index 00000000000..819ae4f4da0 --- /dev/null +++ b/modules/text_server_adv/SCsub @@ -0,0 +1,419 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +if env["builtin_harfbuzz"]: + env_harfbuzz = env_modules.Clone() + + # Thirdparty source files + thirdparty_dir = "#thirdparty/harfbuzz/" + thirdparty_sources = [ + "src/hb-aat-layout.cc", + "src/hb-aat-map.cc", + "src/hb-blob.cc", + "src/hb-buffer-serialize.cc", + "src/hb-buffer.cc", + "src/hb-common.cc", + #'src/hb-coretext.cc', + #'src/hb-directwrite.cc', + "src/hb-draw.cc", + "src/hb-face.cc", + "src/hb-fallback-shape.cc", + "src/hb-font.cc", + "src/hb-ft.cc", + #'src/hb-gdi.cc', + #'src/hb-glib.cc', + #'src/hb-gobject-structs.cc', + "src/hb-graphite2.cc", + "src/hb-icu.cc", + "src/hb-map.cc", + "src/hb-number.cc", + "src/hb-ot-cff1-table.cc", + "src/hb-ot-cff2-table.cc", + "src/hb-ot-color.cc", + "src/hb-ot-face.cc", + "src/hb-ot-font.cc", + "src/hb-ot-layout.cc", + "src/hb-ot-map.cc", + "src/hb-ot-math.cc", + "src/hb-ot-meta.cc", + "src/hb-ot-metrics.cc", + "src/hb-ot-name.cc", + "src/hb-ot-shape-complex-arabic.cc", + "src/hb-ot-shape-complex-default.cc", + "src/hb-ot-shape-complex-hangul.cc", + "src/hb-ot-shape-complex-hebrew.cc", + "src/hb-ot-shape-complex-indic-table.cc", + "src/hb-ot-shape-complex-indic.cc", + "src/hb-ot-shape-complex-khmer.cc", + "src/hb-ot-shape-complex-myanmar.cc", + "src/hb-ot-shape-complex-thai.cc", + "src/hb-ot-shape-complex-use-table.cc", + "src/hb-ot-shape-complex-use.cc", + "src/hb-ot-shape-complex-vowel-constraints.cc", + "src/hb-ot-shape-fallback.cc", + "src/hb-ot-shape-normalize.cc", + "src/hb-ot-shape.cc", + "src/hb-ot-tag.cc", + "src/hb-ot-var.cc", + "src/hb-set.cc", + "src/hb-shape-plan.cc", + "src/hb-shape.cc", + "src/hb-shaper.cc", + "src/hb-static.cc", + "src/hb-style.cc", + "src/hb-subset-cff-common.cc", + "src/hb-subset-cff1.cc", + "src/hb-subset-cff2.cc", + "src/hb-subset-input.cc", + "src/hb-subset-plan.cc", + "src/hb-subset.cc", + "src/hb-ucd.cc", + "src/hb-unicode.cc", + #'src/hb-uniscribe.cc' + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + if env["platform"] == "android" or env["platform"] == "linuxbsd" or env["platform"] == "server": + env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"]) + + if env["platform"] == "javascript": + if env["threads_enabled"]: + env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"]) + else: + env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"]) + + env_harfbuzz.Append( + CPPPATH=[ + "#thirdparty/harfbuzz/src", + "#thirdparty/freetype/include", + "#thirdparty/graphite/include", + "#thirdparty/icu4c/common/", + ] + ) + env_harfbuzz.Append( + CCFLAGS=["-DHAVE_ICU_BUILTIN", "-DHAVE_ICU", "-DHAVE_FREETYPE", "-DHAVE_GRAPHITE2", "-DGRAPHITE2_STATIC",] + ) + env_harfbuzz.disable_warnings() + env_thirdparty = env_harfbuzz.Clone() + env_thirdparty.disable_warnings() + lib = env_thirdparty.add_library("harfbuzz_builtin", thirdparty_sources) + + # Needs to be appended to arrive after libscene in the linker call, + # but we don't want it to arrive *after* system libs, so manual hack + # LIBS contains first SCons Library objects ("SCons.Node.FS.File object") + # and then plain strings for system library. We insert between the two. + inserted = False + for idx, linklib in enumerate(env["LIBS"]): + if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object + env["LIBS"].insert(idx, lib) + inserted = True + break + if not inserted: + env.Append(LIBS=[lib]) + +if env["builtin_graphite"]: + env_graphite = env_modules.Clone() + + # Thirdparty source files + thirdparty_dir = "#thirdparty/graphite/" + thirdparty_sources = [ + "src/gr_char_info.cpp", + "src/gr_face.cpp", + "src/gr_features.cpp", + "src/gr_font.cpp", + "src/gr_logging.cpp", + "src/gr_segment.cpp", + "src/gr_slot.cpp", + "src/CmapCache.cpp", + "src/Code.cpp", + "src/Collider.cpp", + "src/Decompressor.cpp", + "src/Face.cpp", + #'src/FileFace.cpp', + "src/FeatureMap.cpp", + "src/Font.cpp", + "src/GlyphCache.cpp", + "src/GlyphFace.cpp", + "src/Intervals.cpp", + "src/Justifier.cpp", + "src/NameTable.cpp", + "src/Pass.cpp", + "src/Position.cpp", + "src/Segment.cpp", + "src/Silf.cpp", + "src/Slot.cpp", + "src/Sparse.cpp", + "src/TtfUtil.cpp", + "src/UtfCodec.cpp", + "src/FileFace.cpp", + "src/json.cpp", + ] + if not env_graphite.msvc: + thirdparty_sources += ["src/direct_machine.cpp"] + else: + thirdparty_sources += ["src/call_machine.cpp"] + + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + env_graphite.Append(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"]) + env_graphite.Append(CCFLAGS=["-DGRAPHITE2_STATIC", "-DGRAPHITE2_NTRACING", "-DGRAPHITE2_NFILEFACE"]) + env_graphite.disable_warnings() + env_thirdparty = env_graphite.Clone() + env_thirdparty.disable_warnings() + lib = env_thirdparty.add_library("graphite_builtin", thirdparty_sources) + + # Needs to be appended to arrive after libscene in the linker call, + # but we don't want it to arrive *after* system libs, so manual hack + # LIBS contains first SCons Library objects ("SCons.Node.FS.File object") + # and then plain strings for system library. We insert between the two. + inserted = False + for idx, linklib in enumerate(env["LIBS"]): + if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object + env["LIBS"].insert(idx, lib) + inserted = True + break + if not inserted: + env.Append(LIBS=[lib]) + +if env["builtin_icu"]: + env_icu = env_modules.Clone() + + # Thirdparty source files + thirdparty_dir = "#thirdparty/icu4c/" + # Thirdparty source files + thirdparty_sources = [ + "common/appendable.cpp", + "common/bmpset.cpp", + "common/brkeng.cpp", + "common/brkiter.cpp", + "common/bytesinkutil.cpp", + "common/bytestream.cpp", + "common/bytestrie.cpp", + "common/bytestriebuilder.cpp", + "common/bytestrieiterator.cpp", + "common/caniter.cpp", + "common/characterproperties.cpp", + "common/chariter.cpp", + "common/charstr.cpp", + "common/cmemory.cpp", + "common/cstr.cpp", + "common/cstring.cpp", + "common/cwchar.cpp", + "common/dictbe.cpp", + "common/dictionarydata.cpp", + "common/dtintrv.cpp", + "common/edits.cpp", + "common/errorcode.cpp", + "common/filteredbrk.cpp", + "common/filterednormalizer2.cpp", + "common/icudataver.cpp", + "common/icuplug.cpp", + "common/loadednormalizer2impl.cpp", + "common/localebuilder.cpp", + "common/localematcher.cpp", + "common/localeprioritylist.cpp", + "common/locavailable.cpp", + "common/locbased.cpp", + "common/locdispnames.cpp", + "common/locdistance.cpp", + "common/locdspnm.cpp", + "common/locid.cpp", + "common/loclikely.cpp", + "common/loclikelysubtags.cpp", + "common/locmap.cpp", + "common/locresdata.cpp", + "common/locutil.cpp", + "common/lsr.cpp", + "common/messagepattern.cpp", + "common/normalizer2.cpp", + "common/normalizer2impl.cpp", + "common/normlzr.cpp", + "common/parsepos.cpp", + "common/patternprops.cpp", + "common/pluralmap.cpp", + "common/propname.cpp", + "common/propsvec.cpp", + "common/punycode.cpp", + "common/putil.cpp", + "common/rbbi.cpp", + "common/rbbi_cache.cpp", + "common/rbbidata.cpp", + "common/rbbinode.cpp", + "common/rbbirb.cpp", + "common/rbbiscan.cpp", + "common/rbbisetb.cpp", + "common/rbbistbl.cpp", + "common/rbbitblb.cpp", + "common/resbund.cpp", + "common/resbund_cnv.cpp", + "common/resource.cpp", + "common/restrace.cpp", + "common/ruleiter.cpp", + "common/schriter.cpp", + "common/serv.cpp", + "common/servlk.cpp", + "common/servlkf.cpp", + "common/servls.cpp", + "common/servnotf.cpp", + "common/servrbf.cpp", + "common/servslkf.cpp", + "common/sharedobject.cpp", + "common/simpleformatter.cpp", + "common/static_unicode_sets.cpp", + "common/stringpiece.cpp", + "common/stringtriebuilder.cpp", + "common/uarrsort.cpp", + "common/ubidi.cpp", + "common/ubidi_props.cpp", + "common/ubidiln.cpp", + "common/ubiditransform.cpp", + "common/ubidiwrt.cpp", + "common/ubrk.cpp", + "common/ucase.cpp", + "common/ucasemap.cpp", + "common/ucasemap_titlecase_brkiter.cpp", + "common/ucat.cpp", + "common/uchar.cpp", + "common/ucharstrie.cpp", + "common/ucharstriebuilder.cpp", + "common/ucharstrieiterator.cpp", + "common/uchriter.cpp", + "common/ucln_cmn.cpp", + "common/ucmndata.cpp", + "common/ucnv.cpp", + "common/ucnv2022.cpp", + "common/ucnv_bld.cpp", + "common/ucnv_cb.cpp", + "common/ucnv_cnv.cpp", + "common/ucnv_ct.cpp", + "common/ucnv_err.cpp", + "common/ucnv_ext.cpp", + "common/ucnv_io.cpp", + "common/ucnv_lmb.cpp", + "common/ucnv_set.cpp", + "common/ucnv_u16.cpp", + "common/ucnv_u32.cpp", + "common/ucnv_u7.cpp", + "common/ucnv_u8.cpp", + "common/ucnvbocu.cpp", + "common/ucnvdisp.cpp", + "common/ucnvhz.cpp", + "common/ucnvisci.cpp", + "common/ucnvlat1.cpp", + "common/ucnvmbcs.cpp", + "common/ucnvscsu.cpp", + "common/ucnvsel.cpp", + "common/ucol_swp.cpp", + "common/ucptrie.cpp", + "common/ucurr.cpp", + "common/udata.cpp", + "common/udatamem.cpp", + "common/udataswp.cpp", + "common/uenum.cpp", + "common/uhash.cpp", + "common/uhash_us.cpp", + "common/uidna.cpp", + "common/uinit.cpp", + "common/uinvchar.cpp", + "common/uiter.cpp", + "common/ulist.cpp", + "common/uloc.cpp", + "common/uloc_keytype.cpp", + "common/uloc_tag.cpp", + "common/umapfile.cpp", + "common/umath.cpp", + "common/umutablecptrie.cpp", + "common/umutex.cpp", + "common/unames.cpp", + "common/unifiedcache.cpp", + "common/unifilt.cpp", + "common/unifunct.cpp", + "common/uniset.cpp", + "common/uniset_closure.cpp", + "common/uniset_props.cpp", + "common/unisetspan.cpp", + "common/unistr.cpp", + "common/unistr_case.cpp", + "common/unistr_case_locale.cpp", + "common/unistr_cnv.cpp", + "common/unistr_props.cpp", + "common/unistr_titlecase_brkiter.cpp", + "common/unorm.cpp", + "common/unormcmp.cpp", + "common/uobject.cpp", + "common/uprops.cpp", + "common/ures_cnv.cpp", + "common/uresbund.cpp", + "common/uresdata.cpp", + "common/usc_impl.cpp", + "common/uscript.cpp", + "common/uscript_props.cpp", + "common/uset.cpp", + "common/uset_props.cpp", + "common/usetiter.cpp", + "common/ushape.cpp", + "common/usprep.cpp", + "common/ustack.cpp", + "common/ustr_cnv.cpp", + "common/ustr_titlecase_brkiter.cpp", + "common/ustr_wcs.cpp", + "common/ustrcase.cpp", + "common/ustrcase_locale.cpp", + "common/ustrenum.cpp", + "common/ustrfmt.cpp", + "common/ustring.cpp", + "common/ustrtrns.cpp", + "common/utext.cpp", + "common/utf_impl.cpp", + "common/util.cpp", + "common/util_props.cpp", + "common/utrace.cpp", + "common/utrie.cpp", + "common/utrie2.cpp", + "common/utrie2_builder.cpp", + "common/utrie_swap.cpp", + "common/uts46.cpp", + "common/utypes.cpp", + "common/uvector.cpp", + "common/uvectr32.cpp", + "common/uvectr64.cpp", + "common/wintz.cpp", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + thirdparty_sources += ["icu_data/icudata_stub.cpp"] + + env_icu.Append(CPPPATH=["#thirdparty/icu4c/common/"]) + env_icu.Append( + CXXFLAGS=["-DU_STATIC_IMPLEMENTATION", "-DU_COMMON_IMPLEMENTATION", "-DPKGDATA_MODE=static",] + ) + + env_icu.disable_warnings() + env_thirdparty = env_icu.Clone() + env_thirdparty.disable_warnings() + lib = env_thirdparty.add_library("icu_builtin", thirdparty_sources) + + # Needs to be appended to arrive after libscene in the linker call, + # but we don't want it to arrive *after* system libs, so manual hack + # LIBS contains first SCons Library objects ("SCons.Node.FS.File object") + # and then plain strings for system library. We insert between the two. + inserted = False + for idx, linklib in enumerate(env["LIBS"]): + if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object + env["LIBS"].insert(idx, lib) + inserted = True + break + if not inserted: + env.Append(LIBS=[lib]) + +env_text_server_adv = env_modules.Clone() +env_text_server_adv.Append( + CPPPATH=[ + "#thirdparty/harfbuzz/src", + "#thirdparty/freetype/include", + "#thirdparty/graphite/include", + "#thirdparty/icu4c/common/", + ] +) +env_text_server_adv.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/text_server_adv/config.py b/modules/text_server_adv/config.py new file mode 100644 index 00000000000..1c8cd12a2dc --- /dev/null +++ b/modules/text_server_adv/config.py @@ -0,0 +1,5 @@ +def can_build(env, platform): + return True + +def configure(env): + pass diff --git a/modules/text_server_adv/icu_data/icudata_stub.cpp b/modules/text_server_adv/icu_data/icudata_stub.cpp new file mode 100644 index 00000000000..13f0ac0c50c --- /dev/null +++ b/modules/text_server_adv/icu_data/icudata_stub.cpp @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* icudata_stub.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "unicode/udata.h" +#include "unicode/utypes.h" +#include "unicode/uversion.h" + +typedef struct { + uint16_t header_size; + uint8_t magic_1, magic_2; + UDataInfo info; + char padding[8]; + uint32_t count, reserved; + int fake_name_and_data[4]; +} ICU_data_header; + +extern "C" U_EXPORT const ICU_data_header U_ICUDATA_ENTRY_POINT = { + 32, + 0xDA, 0x27, + { sizeof(UDataInfo), + 0, +#if U_IS_BIG_ENDIAN + 1, +#else + 0, +#endif + U_CHARSET_FAMILY, + sizeof(UChar), + 0, + { 0x54, 0x6F, 0x43, 0x50 }, + { 1, 0, 0, 0 }, + { 0, 0, 0, 0 } }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + 0, 0, + { 0, 0, 0, 0 } +}; diff --git a/platform/android/detect.py b/platform/android/detect.py index 0accacb679d..60d41467124 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -215,7 +215,7 @@ def configure(env): env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++abi/include"]) # Disable exceptions and rtti on non-tools (template) builds - if env["tools"]: + if env["tools"] or env["builtin_icu"]: env.Append(CXXFLAGS=["-frtti"]) else: env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"]) diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 71189cf697a..9f584d08995 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -71,6 +71,8 @@ def configure(env): ) # Tools need more memory. Initial stack memory in bytes. See `src/settings.js` in emscripten repository (will be renamed to INITIAL_MEMORY). env.Append(LINKFLAGS=["-s", "TOTAL_MEMORY=33554432"]) + elif env["builtin_icu"]: + env.Append(CCFLAGS=["-frtti"]) else: # Disable exceptions and rtti on non-tools (template) builds # These flags help keep the file size down. diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index a8bc3a09f6f..277aafc107b 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -205,14 +205,31 @@ def configure(env): # freetype depends on libpng and zlib, so bundling one of them while keeping others # as shared libraries leads to weird issues - if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]: + if ( + env["builtin_freetype"] + or env["builtin_libpng"] + or env["builtin_zlib"] + or env["builtin_graphite"] + or env["builtin_harfbuzz"] + ): env["builtin_freetype"] = True env["builtin_libpng"] = True env["builtin_zlib"] = True + env["builtin_graphite"] = True + env["builtin_harfbuzz"] = True if not env["builtin_freetype"]: env.ParseConfig("pkg-config freetype2 --cflags --libs") + if not env["builtin_graphite"]: + env.ParseConfig("pkg-config graphite2 --cflags --libs") + + if not env["builtin_icu"]: + env.ParseConfig("pkg-config icu-uc --cflags --libs") + + if not env["builtin_harfbuzz"]: + env.ParseConfig("pkg-config harfbuzz harfbuzz-icu --cflags --libs") + if not env["builtin_libpng"]: env.ParseConfig("pkg-config libpng16 --cflags --libs") diff --git a/platform/server/detect.py b/platform/server/detect.py index d9ac3576791..bf4744a234f 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -138,14 +138,31 @@ def configure(env): # freetype depends on libpng and zlib, so bundling one of them while keeping others # as shared libraries leads to weird issues - if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]: + if ( + env["builtin_freetype"] + or env["builtin_libpng"] + or env["builtin_zlib"] + or env["builtin_graphite"] + or env["builtin_harfbuzz"] + ): env["builtin_freetype"] = True env["builtin_libpng"] = True env["builtin_zlib"] = True + env["builtin_graphite"] = True + env["builtin_harfbuzz"] = True if not env["builtin_freetype"]: env.ParseConfig("pkg-config freetype2 --cflags --libs") + if not env["builtin_graphite"]: + env.ParseConfig("pkg-config graphite2 --cflags --libs") + + if not env["builtin_icu"]: + env.ParseConfig("pkg-config icu-uc --cflags --libs") + + if not env["builtin_harfbuzz"]: + env.ParseConfig("pkg-config harfbuzz harfbuzz-icu --cflags --libs") + if not env["builtin_libpng"]: env.ParseConfig("pkg-config libpng16 --cflags --libs") @@ -233,7 +250,17 @@ def configure(env): env.Append(CPPDEFINES=["SERVER_ENABLED", "UNIX_ENABLED"]) if platform.system() == "Darwin": - env.Append(LINKFLAGS=["-framework", "Cocoa", "-framework", "Carbon", "-lz", "-framework", "IOKit"]) + env.Append( + LINKFLAGS=[ + "-framework", + "Cocoa", + "-framework", + "Carbon", + "-lz", + "-framework", + "IOKit", + ] + ) env.Append(LIBS=["pthread"]) diff --git a/thirdparty/README.md b/thirdparty/README.md index 37518857c7c..d0118322102 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -184,6 +184,39 @@ Files extracted from upstream source: Patches in the `patches` directory should be re-applied after updates. +## Graphite engine + +- Upstream: https://github.com/silnrsi/graphite +- Version: 1.3.14 +- License: MPL-2.0 + +Files extracted from upstream source: +- the `include` folder +- the `src` folder +- `COPYING`, `ChangeLog` + +## HarfBuzz + +- Upstream: https://github.com/harfbuzz/harfbuzz +- Version: 2.7.2 +- License: HarfBuzz + +Files extracted from upstream source: +- the `src` folder +- `AUTHORS`, `COPYING`, `NEWS`, `THANKS` + +## International Components for Unicode + +- Upstream: https://github.com/unicode-org/icu +- Version: 68.1 +- License: Unicode + +Files extracted from upstream source: +- the `common` folder +- `APIChangeReport.md`, `LICENSE` + +Files generated from upstream source: +- the `icudt68l.dat` built with the provided `godot_data.json` config file (see https://github.com/unicode-org/icu/blob/master/docs/userguide/icu_data/buildtool.md for instructions) ## jpeg-compressor diff --git a/thirdparty/graphite/COPYING b/thirdparty/graphite/COPYING new file mode 100644 index 00000000000..f6630af5336 --- /dev/null +++ b/thirdparty/graphite/COPYING @@ -0,0 +1,26 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + + Alternatively, you may use this library under the terms of the Mozilla + Public License (http://mozilla.org/MPL) or under the GNU General Public + License, as published by the Free Sofware Foundation; either version + 2 of the license or (at your option) any later version. +*/ diff --git a/thirdparty/graphite/ChangeLog b/thirdparty/graphite/ChangeLog new file mode 100644 index 00000000000..e36110e1c18 --- /dev/null +++ b/thirdparty/graphite/ChangeLog @@ -0,0 +1,238 @@ +1.3.14 + . Bug fixes + . Allow features to be hidden (for aliases) + . Move to python3 + . Rename doc files from .txt to .asc + +1.3.13 + . Resolve minor spacing issue in rtl non-overlap kerning + . python3 for graphite.py + . Better fuzzing + . Better building on windows + +1.3.12 + . Graphite no longer does dumb rendering for fonts with no smarts + . Segment caching code removed. Anything attempting to use the segment cache gets given a regular face instead + . Add libfuzzer support + . Builds now require C++11 + . Improvements to Windows 64 bit builds + . Support different versions of python including 32 bit and python 3 + . Various minor bug fixes + +1.3.11 + . Fixes due to security review + . Minor collision avoidance fixes + . Fix LZ4 decompressor against high compression + +1.3.10 + . Address floating point build parameters to give consistent positioning results across platforms + . Various bug fixes + +1.3.9 + . Add Collision COLL_ISSPACE to allow for visible spaces in collision avoidance + . Add segment and pass direction information to tracing output + . Bug fix rule length testing in 32-bit + . Increase slanted margin distances for collision avoidance + . Change kerning algorithm to simple outline expansion. Seems to make no visible difference. + . Add trace2svg to test tools + +1.3.8 + . Various bug fixes arising from fuzzing + . Fix regression that stopped piglatin from working + . Make collision avoidance kerning give more regular results + . Minor modification to clustering algorithm to handle variable width chars + +1.3.7 + . Bug fixes + . Start to deprecate SegCache. This will be going away in a later release. + +1.3.6 + . Bug fixes + +1.3.5 + . Bug fixes + . Security bug fix + . Fix ARM misalignment problem + . Track latest cmake + +1.3.4 + . Transition from Mercurial to Git + . Bug fixes + . Fix Collision Kerning ignoring some diacritics + . Handle pass bits 16-31 to speed up fonts with > 16 passes + . Various minor fuzz bug fixes + . Make Coverity happy + . Add GR_FALLTHROUGH macro for clang c++11 + +1.3.3 + . Slight speed up in Collision Avoidance + . Remove dead bidi code + . Bug fixes + . Between pass bidi reorderings and at the end + . Decompressor fuzz bugs + . Other fuzz bugs + +1.3.2 + . Remove full bidi. All segments are assumed to be single directioned. + . Bug fixes: + . Decompressor corner cases + . Various fuzz bugs + +1.3.1 + . Deprecation warning: Full bidi support is about to be deprecated. Make contact + if this impacts you. + . Change compression block format slightly to conform to LZ4 + . Bug fixes: + . Handle mono direction text with diacritics consistently. Fonts + now see the direction they expect consistently and bidi now + gives expected results. + . Fixed lots of fuzz bugs + . Coverity cleanups + . Build now works for clang and/or asan and/or afl etc. + +1.3.0 + . Add collision avoidance + . Shift Collider + . Kern Collider + . Octabox outlines and subboxes + . Add compressed Silf and Glat table support + . Bug fixes: + . Stop loops forming in the child, sibling tree + . Handle bidi mirroring correctly if no bidi occurring + +1.2.4 + . Face failure now has error code reporting via debug logging + . can now call gr_start_logging(NULL, fname) + . gr2fonttest --alltrace added + . Format 14 table support + . Not done. To be handled entirely in the compiler + . Bidi support for Unicode 6.3 Isolating direction controls + . Fonts no longer require a glyf/loca table. In such cases the bounding box is always 0. + . Clang ASAN build support added for testing. + . Handle out of memory sanely. + . Documentation improvements + . Bug fixes: + . Enforce fonts having to store glyph attributes by monotonically increasing attribute number + . zeropadding was not getting called on feature tags + . automatic associations for unassociated characters + . use direct engine on Mac + . various extreme case reading 1 past the end errors fixed + . remove tabs from sources so that it becomes readable again + +1.2.3 + . Bug fixes only: + . fix byte swapping when testing cmap subtable lengths + . work around armel compilation problems with conditional operators + . fix pseudoglyph support for advance and bbox + +1.2.2 + . Add support for passKeySlot (makes Charis 2x faster) up to 32 passes + . Add telemetry output to json if enabled in build GRAPHITE2_TELEMETRY + . Shrink font memory footprint particularly in the fsm + . Add -S to comparerenderer + . Bug fixes: + . Fix shift.x being reversed for rtl text + . Fix faulty fallback justification + . Fix bad cmap handling + . Support compiling on old Solaris where bidi attributes clash with register names + . Follow the crowd in using Windows.h + +1.2.1 + . Bug fixes: + . Allow glyph reattachment + . Allow signed glyph attributes + . Various build problems with MacOS, old gcc versions, etc. + . Various overrun read errors fixed + +1.2.0 + . API Changes: + . Added Windows friendly gr_start_logging and gr_stop_logging, now per face + . Added gr_make_face_with_ops, gr_make_face_with_seg_cache_and_ops + . Added gr_make_font_with_ops + . Added gr_face_is_char_supported + . Added gr_face_info to give info to apps about face capabilities + . Deprecated gr_make_face, gr_make_face_with_seg_cache, gr_make_font_with_advance_fn + . Deprecated graphite_start_logging and graphite_stop_logging + . These functions are stubbed now and do nothing, but do compile and link. + . Bump API version to 3 + . Add C# wrapper to contrib + . Handle justification information in a font and do something useful with it + . Builds clang clean (has done for a while) + . Bug fixes + . Windows build and bug fixes + . Add extra information to json debug output + . Added windows build documentation + . Added freetype sample code and test + +1.1.3 + . Default build has GRAPHITE2_COMPARE_RENDERER to OFF to reduce dependencies + . Builds on Mac with clang + . Debug output improvements + . Tidy up perl wrappers + . Fuzz tester improvements + . Various bug fixes for bad font handling + +1.1.2 + . Support feature ids < 4 chars when space padded for inclusion in FF 14. + . More fuzztesting and removal of causes of valgrind bad reads and sigabrts + . Remove contrib/android into its own repo (http://hg.palaso.org/grandroid) + . Update comparerenderer to latest harfbuzzng api + +1.1.1 + . Missing Log.h included + . perl wrappers updated + +1.1.0 + . Refactored debug output to use json + . Renamed VM_MACHINE_TYPE to GRAPHITE2_VM_TYPE + . Renamed DISABLE_SEGCACHE to GRAPHITE2_NSEGCACE + . Renamed DISBALE_FILE_FACE to GRAPHITE2_NFILEFACE + . Renamed ENABLE_COMPARE_RENDERER to GRAPHTIE2_COMPARE_RENDERER + . Renamed DOXYGEN_CONFIG to GRAPHITE2_DOXYGEN_CONFIG + . Renamed GR2_CUSTOM_HEADER to GRAPHITE2_CUSTOM_HEADER + . Renamed GR2_EXPORTING to GRAPHITE2_EXPORTING + . Added GRAPHITE2_STATIC for static only builds + . Added GRAPHITE2_NTRACING to compile out tracing code + . Documented GRAPHITE2_{EXPORTING,STATIC,NTRACING} in hacking.txt + . Bump libtool version to 2.1.0 + . dumb font rendering works + . slot user attributes are now signed rather than unsigned + . add support for long class maps + . Rename perl library to avoid nameclash on Windows + . Various robustness fixes + . Moved internal .h files into src/inc + . Parallelise fuzztest + . General build improvements, particularly on Windows + +1.0.3 + . Fix UTF16 surrogate support + . script and lang tags may be space padded or null padded + . Remove need for WORDS_BIGENDIAN, do it all automatically + . Remove all #include . Use CLASS_NEW_DELETE instead. + . Fix comparerenderer to work with current hbng + . Add valgrind to fuzztest to ensure good memory use at all times + . Fix new fuzztest exposed bugs. + . Fix bugs exposed by Mozilla security review + . Add continuous integration build on Windows support + +1.0.2 + . Fix Windows build + . Comparerenderer uses hbng enforcing ot rendering + . Add Bidi .hasChar support and refactor mirroring code + . Make cmake default Release rather than debug + . Don't compile in a boat load of TtfUtil that isn't used, saving 15% of binary + . Chase the FSF around its latest office moves + . WORDS_BIGENDIAN is set at the top so tests now pass on ppc, etc. + . More words in the manual + +1.0.1 + . Release is the default build in cmake now. + . Refactor cmake build to not rebuild things so much. + . Include a missing file + . Remove -nostdlibs, making gcc happy everywhere + . Update comparerenderer to latest hbng interface + . Add changelog + +1.0.0 + . First major release of perfect code! + diff --git a/thirdparty/graphite/include/graphite2/Font.h b/thirdparty/graphite/include/graphite2/Font.h new file mode 100644 index 00000000000..fe569295a53 --- /dev/null +++ b/thirdparty/graphite/include/graphite2/Font.h @@ -0,0 +1,389 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + + Alternatively, the contents of this file may be used under the terms + of the Mozilla Public License (http://mozilla.org/MPL) or the GNU + General Public License, as published by the Free Software Foundation, + either version 2 of the License or (at your option) any later version. +*/ +#pragma once + +#include "graphite2/Types.h" + +#define GR2_VERSION_MAJOR 1 +#define GR2_VERSION_MINOR 3 +#define GR2_VERSION_BUGFIX 14 + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct gr_face gr_face; +typedef struct gr_font gr_font; +typedef struct gr_feature_ref gr_feature_ref; +typedef struct gr_feature_val gr_feature_val; + +/** +* Returns version information on this engine +*/ +GR2_API void gr_engine_version(int *nMajor, int *nMinor, int *nBugFix); + +/** +* The Face Options allow the application to require that certain tables are +* read during face construction. This may be of concern if the appFaceHandle +* used in the gr_get_table_fn may change. +* The values can be combined +*/ +enum gr_face_options { + /** No preload, no cmap caching, fail if the graphite tables are invalid */ + gr_face_default = 0, + /** Dumb rendering will be enabled if the graphite tables are invalid. @deprecated Since 1.311 */ + gr_face_dumbRendering = 1, + /** preload glyphs at construction time */ + gr_face_preloadGlyphs = 2, + /** Cache the lookup from code point to glyph ID at construction time */ + gr_face_cacheCmap = 4, + /** Preload everything */ + gr_face_preloadAll = gr_face_preloadGlyphs | gr_face_cacheCmap +}; + +/** Holds information about a particular Graphite silf table that has been loaded */ +struct gr_faceinfo { + gr_uint16 extra_ascent; /**< The extra_ascent in the GDL, in design units */ + gr_uint16 extra_descent; /**< The extra_descent in the GDL, in design units */ + gr_uint16 upem; /**< The design units for the font */ + enum gr_space_contextuals { + gr_space_unknown = 0, /**< no information is known. */ + gr_space_none = 1, /**< the space character never occurs in any rules. */ + gr_space_left_only = 2, /**< the space character only occurs as the first element in a rule. */ + gr_space_right_only = 3, /**< the space character only occurs as the last element in a rule. */ + gr_space_either_only = 4, /**< the space character only occurs as the only element in a rule. */ + gr_space_both = 5, /**< the space character may occur as the first or last element of a rule. */ + gr_space_cross = 6 /**< the space character occurs in a rule not as a first or last element. */ + } space_contextuals; + unsigned int has_bidi_pass : 1; /**< the table specifies that a bidirectional pass should run */ + unsigned int line_ends : 1; /**< there are line end contextuals somewhere */ + unsigned int justifies : 1; /**< there are .justify properties set somewhere on some glyphs */ +}; + +typedef struct gr_faceinfo gr_faceinfo; + +/** type describing function to retrieve font table information + * + * @return a pointer to the table in memory. The pointed to memory must exist as + * long as the gr_face which makes the call. + * @param appFaceHandle is the unique information passed to gr_make_face() + * @param name is a 32bit tag to the table name. + * @param len returned by this function to say how long the table is in memory. + */ +typedef const void *(*gr_get_table_fn)(const void* appFaceHandle, unsigned int name, size_t *len); + +/** type describing function to release any resources allocated by the above get_table table function + * + * @param appFaceHandle is the unique information passed to gr_make_face() + * @param pointer to table memory returned by get_table. + */ +typedef void (*gr_release_table_fn)(const void* appFaceHandle, const void *table_buffer); + +/** struct housing function pointers to manage font table buffers for the graphite engine. */ +struct gr_face_ops +{ + /** size in bytes of this structure */ + size_t size; + /** a pointer to a function to request a table from the client. */ + gr_get_table_fn get_table; + /** is a pointer to a function to notify the client the a table can be released. + * This can be NULL to signify that the client does not wish to do any release handling. */ + gr_release_table_fn release_table; +}; +typedef struct gr_face_ops gr_face_ops; + +/** Create a gr_face object given application information and a table functions. + * + * @return gr_face or NULL if the font fails to load for some reason. + * @param appFaceHandle This is application specific information that is passed + * to the getTable function. The appFaceHandle must stay + * alive as long as the gr_face is alive. + * @param face_ops Pointer to face specific callback structure for table + * management. Must stay alive for the duration of the + * call only. + * @param faceOptions Bitfield describing various options. See enum gr_face_options for details. + */ +GR2_API gr_face* gr_make_face_with_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *face_ops, unsigned int faceOptions); + +/** @deprecated Since v1.2.0 in favour of gr_make_face_with_ops. + * Create a gr_face object given application information and a getTable function. + * + * @return gr_face or NULL if the font fails to load for some reason. + * @param appFaceHandle This is application specific information that is passed + * to the getTable function. The appFaceHandle must stay + * alive as long as the gr_face is alive. + * @param getTable Callback function to get table data. + * @param faceOptions Bitfield describing various options. See enum gr_face_options for details. + */ +GR2_DEPRECATED_API gr_face* gr_make_face(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn getTable, unsigned int faceOptions); + +/** @deprecated Since 1.3.7 this function is now an alias for gr_make_face_with_ops(). + * + * Create a gr_face object given application information, with subsegmental caching support + * + * @return gr_face or NULL if the font fails to load. + * @param appFaceHandle is a pointer to application specific information that is passed to getTable. + * This may not be NULL and must stay alive as long as the gr_face is alive. + * @param face_ops Pointer to face specific callback structure for table management. Must stay + * alive for the duration of the call only. + * @param segCacheMaxSize Unused. + * @param faceOptions Bitfield of values from enum gr_face_options + */ +GR2_DEPRECATED_API gr_face* gr_make_face_with_seg_cache_and_ops(const void* appFaceHandle, const gr_face_ops *face_ops, unsigned int segCacheMaxSize, unsigned int faceOptions); + +/** @deprecated Since 1.3.7 this function is now an alias for gr_make_face(). + * + * Create a gr_face object given application information, with subsegmental caching support. + * This function is deprecated as of v1.2.0 in favour of gr_make_face_with_seg_cache_and_ops. + * + * @return gr_face or NULL if the font fails to load. + * @param appFaceHandle is a pointer to application specific information that is passed to getTable. + * This may not be NULL and must stay alive as long as the gr_face is alive. + * @param getTable The function graphite calls to access font table data + * @param segCacheMaxSize How large the segment cache is. + * @param faceOptions Bitfield of values from enum gr_face_options + */ +GR2_DEPRECATED_API gr_face* gr_make_face_with_seg_cache(const void* appFaceHandle, gr_get_table_fn getTable, unsigned int segCacheMaxSize, unsigned int faceOptions); + +/** Convert a tag in a string into a gr_uint32 + * + * @return gr_uint32 tag, zero padded + * @param str a nul terminated string of which at most the first 4 characters are read + */ +GR2_API gr_uint32 gr_str_to_tag(const char *str); + +/** Convert a gr_uint32 tag into a string + * + * @param tag contains the tag to convert + * @param str is a pointer to a char array of at least size 4 bytes. The first 4 bytes of this array + * will be overwritten by this function. No nul is appended. + */ +GR2_API void gr_tag_to_str(gr_uint32 tag, char *str); + +/** Get feature values for a given language or default + * + * @return a copy of the default feature values for a given language. The application must call + * gr_featureval_destroy() to free this object when done. + * @param pFace The font face to get feature values from + * @param langname The language tag to get feature values for. If there is no such language or + * langname is 0, the default feature values for the font are returned. + * langname is right 0 padded and assumes lowercase. Thus the en langauge + * would be 0x656E0000. Langname may also be space padded, thus 0x656E2020. + */ +GR2_API gr_feature_val* gr_face_featureval_for_lang(const gr_face* pFace, gr_uint32 langname); + +/** Get feature reference for a given feature id from a face + * + * @return a feature reference corresponding to the given id. This data is part of the gr_face and + * will be freed when the face is destroyed. + * @param pFace Font face to get information on. + * @param featId Feature id tag to get reference to. + */ +GR2_API const gr_feature_ref* gr_face_find_fref(const gr_face* pFace, gr_uint32 featId); + +/** Returns number of feature references in a face **/ +GR2_API gr_uint16 gr_face_n_fref(const gr_face* pFace); + +/** Returns feature reference at given index in face **/ +GR2_API const gr_feature_ref* gr_face_fref(const gr_face* pFace, gr_uint16 i); + +/** Return number of languages the face knows about **/ +GR2_API unsigned short gr_face_n_languages(const gr_face* pFace); + +/** Returns a language id corresponding to a language of given index in the face **/ +GR2_API gr_uint32 gr_face_lang_by_index(const gr_face* pFace, gr_uint16 i); + +/** Destroy the given face and free its memory **/ +GR2_API void gr_face_destroy(gr_face *face); + +/** Returns the number of glyphs in the face **/ +GR2_API unsigned short gr_face_n_glyphs(const gr_face* pFace); + +/** Returns a faceinfo for the face and script **/ +GR2_API const gr_faceinfo *gr_face_info(const gr_face *pFace, gr_uint32 script); + +/** Returns whether the font supports a given Unicode character + * + * @return true if the character is supported. + * @param pFace face to test within + * @param usv Unicode Scalar Value of character to test + * @param script Tag of script for selecting which set of pseudo glyphs to test. May be NULL. + */ +GR2_API int gr_face_is_char_supported(const gr_face *pFace, gr_uint32 usv, gr_uint32 script); + +#ifndef GRAPHITE2_NFILEFACE +/** Create gr_face from a font file + * + * @return gr_face that accesses a font file directly. Returns NULL on failure. + * @param filename Full path and filename to font file + * @param faceOptions Bitfile from enum gr_face_options to control face options. + */ +GR2_API gr_face* gr_make_file_face(const char *filename, unsigned int faceOptions); + +/** @deprecated Since 1.3.7. This function is now an alias for gr_make_file_face(). + * + * Create gr_face from a font file, with subsegment caching support. + * + * @return gr_face that accesses a font file directly. Returns NULL on failure. + * @param filename Full path and filename to font file + * @param segCacheMaxSize Specifies how big to make the cache in segments. + * @param faceOptions Bitfield from enum gr_face_options to control face options. + */ +GR2_DEPRECATED_API gr_face* gr_make_file_face_with_seg_cache(const char *filename, unsigned int segCacheMaxSize, unsigned int faceOptions); +#endif // !GRAPHITE2_NFILEFACE + +/** Create a font from a face + * + * @return gr_font Call font_destroy to free this font + * @param ppm Resolution of the font in pixels per em + * @param face Face this font corresponds to. This must stay alive as long as the font is alive. + */ +GR2_API gr_font* gr_make_font(float ppm, const gr_face *face); + +/** query function to find the hinted advance of a glyph + * + * @param appFontHandle is the unique information passed to gr_make_font_with_advance() + * @param glyphid is the glyph to retireve the hinted advance for. + */ +typedef float (*gr_advance_fn)(const void* appFontHandle, gr_uint16 glyphid); + +/** struct housing function pointers to manage font hinted metrics for the + * graphite engine. */ +struct gr_font_ops +{ + /** size of the structure in bytes to allow for future extensibility */ + size_t size; + /** a pointer to a function to retrieve the hinted + * advance width of a glyph which the font cannot + * provide without client assistance. This can be + * NULL to signify no horizontal hinted metrics are necessary. */ + gr_advance_fn glyph_advance_x; + /** a pointer to a function to retrieve the hinted + * advance height of a glyph which the font cannot + * provide without client assistance. This can be + * NULL to signify no horizontal hinted metrics are necessary. */ + gr_advance_fn glyph_advance_y; +}; +typedef struct gr_font_ops gr_font_ops; + +/** Creates a font with hinted advance width query functions + * + * @return gr_font to be destroyed via font_destroy + * @param ppm size of font in pixels per em + * @param appFontHandle font specific information that must stay alive as long + * as the font does + * @param font_ops pointer font specific callback structure for hinted metrics. + * Need only stay alive for the duration of the call. + * @param face the face this font corresponds to. Must stay alive as long as + * the font does. + */ +GR2_API gr_font* gr_make_font_with_ops(float ppm, const void* appFontHandle, const gr_font_ops * font_ops, const gr_face *face); + +/** Creates a font with hinted advance width query function. + * This function is deprecated. Use gr_make_font_with_ops instead. + * + * @return gr_font to be destroyed via font_destroy + * @param ppm size of font in pixels per em + * @param appFontHandle font specific information that must stay alive as long + * as the font does + * @param getAdvance callback function reference that returns horizontal advance in pixels for a glyph. + * @param face the face this font corresponds to. Must stay alive as long as + * the font does. + */ +GR2_API gr_font* gr_make_font_with_advance_fn(float ppm, const void* appFontHandle, gr_advance_fn getAdvance, const gr_face *face); + +/** Free a font **/ +GR2_API void gr_font_destroy(gr_font *font); + +/** get a feature value + * + * @return value of specific feature or 0 if any problems. + * @param pfeatureref gr_feature_ref to the feature + * @param feats gr_feature_val containing all the values + */ +GR2_API gr_uint16 gr_fref_feature_value(const gr_feature_ref* pfeatureref, const gr_feature_val* feats); + +/** set a feature value + * + * @return false if there were any problems (value out of range, etc.) + * @param pfeatureref gr_feature_ref to the feature + * @param val value to set the feature to + * @param pDest the gr_feature_val containing all the values for all the features + */ +GR2_API int gr_fref_set_feature_value(const gr_feature_ref* pfeatureref, gr_uint16 val, gr_feature_val* pDest); + +/** Returns the id tag for a gr_feature_ref **/ +GR2_API gr_uint32 gr_fref_id(const gr_feature_ref* pfeatureref); + +/** Returns number of values a feature may take, given a gr_feature_ref **/ +GR2_API gr_uint16 gr_fref_n_values(const gr_feature_ref* pfeatureref); + +/** Returns the value associated with a particular value in a feature + * + * @return value + * @param pfeatureref gr_feature_ref of the feature of interest + * @param settingno Index up to the return value of gr_fref_n_values() of the value + */ +GR2_API gr_int16 gr_fref_value(const gr_feature_ref* pfeatureref, gr_uint16 settingno); + +/** Returns a string of the UI name of a feature + * + * @return string of the UI name, in the encoding form requested. Call gr_label_destroy() after use. + * @param pfeatureref gr_feature_ref of the feature + * @param langId This is a pointer since the face may not support a string in the requested + * language. The actual language of the string is returned in langId + * @param utf Encoding form for the string + * @param length Used to return the length of the string returned in bytes. + */ +GR2_API void* gr_fref_label(const gr_feature_ref* pfeatureref, gr_uint16 *langId, enum gr_encform utf, gr_uint32 *length); + +/** Return a UI string for a possible value of a feature + * + * @return string of the UI name, in the encoding form requested. nul terminated. Call gr_label_destroy() + * after use. + * @param pfeatureref gr_feature_ref of the feature + * @param settingno Value setting index + * @param langId This is a pointer to the requested language. The requested language id is + * replaced by the actual language id of the string returned. + * @param utf Encoding form for the string + * @param length Returns the length of the string returned in bytes. + */ +GR2_API void* gr_fref_value_label(const gr_feature_ref* pfeatureref, gr_uint16 settingno/*rather than a value*/, gr_uint16 *langId, enum gr_encform utf, gr_uint32 *length); + +/** Destroy a previously returned label string **/ +GR2_API void gr_label_destroy(void * label); + +/** Copies a gr_feature_val **/ +GR2_API gr_feature_val* gr_featureval_clone(const gr_feature_val* pfeatures); + +/** Destroys a gr_feature_val **/ +GR2_API void gr_featureval_destroy(gr_feature_val *pfeatures); + +#ifdef __cplusplus +} +#endif diff --git a/thirdparty/graphite/include/graphite2/Log.h b/thirdparty/graphite/include/graphite2/Log.h new file mode 100644 index 00000000000..a5a6947fabe --- /dev/null +++ b/thirdparty/graphite/include/graphite2/Log.h @@ -0,0 +1,85 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + + Alternatively, the contents of this file may be used under the terms + of the Mozilla Public License (http://mozilla.org/MPL) or the GNU + General Public License, as published by the Free Software Foundation, + either version 2 of the License or (at your option) any later version. +*/ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** deprecated mechanism that doesn't do anything now. */ +typedef enum { + GRLOG_NONE = 0x0, + GRLOG_FACE = 0x01, + GRLOG_SEGMENT = 0x02, + GRLOG_PASS = 0x04, + GRLOG_CACHE = 0x08, + + GRLOG_OPCODE = 0x80, + GRLOG_ALL = 0xFF +} GrLogMask; + +/** Start logging all segment creation and updates on the provided face. This + * is logged to a JSON file, see "Segment JSON Schema.txt" for a precise + * definition of the file + * + * @return true if the file was successfully created and logging is correctly + * initialised. + * @param face the gr_face whose segments you want to log to the given file + * @param log_path a utf8 encoded file name and path to log to. + */ +GR2_API bool gr_start_logging(gr_face * face, const char *log_path); + + +/** Stop logging on the given face. This will close the log file created by + * gr_start_logging. + * + * @param face the gr_face whose segments you want to stop logging + */ +GR2_API void gr_stop_logging(gr_face * face); + +/** Start logging to a FILE object. + * This function is deprecated as of 1.2.0, use the _face versions instead. + * + * @return True on success + * @param logfile FILE reference to output logging to + * @param mask What aspects of logging to report (ignored) + */ +GR2_API bool graphite_start_logging(FILE * logFile, GrLogMask mask); //may not do anthing if disabled in the implementation of the engine. + +/** Stop logging to a FILE object. + * This function is deprecated as of 1.2.0, use the _face versions instead. + */ +GR2_API void graphite_stop_logging(); + +#ifdef __cplusplus +} +#endif diff --git a/thirdparty/graphite/include/graphite2/Segment.h b/thirdparty/graphite/include/graphite2/Segment.h new file mode 100644 index 00000000000..0e24f5d7955 --- /dev/null +++ b/thirdparty/graphite/include/graphite2/Segment.h @@ -0,0 +1,461 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + + Alternatively, the contents of this file may be used under the terms + of the Mozilla Public License (http://mozilla.org/MPL) or the GNU + General Public License, as published by the Free Software Foundation, + either version 2 of the License or (at your option) any later version. +*/ +#pragma once + +#include "graphite2/Types.h" +#include "graphite2/Font.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum gr_break_weight { + gr_breakNone = 0, + /* after break weights */ + gr_breakWhitespace = 10, + gr_breakWord = 15, + gr_breakIntra = 20, + gr_breakLetter = 30, + gr_breakClip = 40, + /* before break weights */ + gr_breakBeforeWhitespace = -10, + gr_breakBeforeWord = -15, + gr_breakBeforeIntra = -20, + gr_breakBeforeLetter = -30, + gr_breakBeforeClip = -40 +}; + +enum gr_justFlags { + /// Indicates that this segment is a complete line + gr_justCompleteLine = 0, + /// Indicates that the start of the slot list is not at the start of a line + gr_justStartInline = 1, + /// Indicates that the end of the slot list is not at the end of a line + gr_justEndInline = 2 +}; + +/** Used for looking up slot attributes. Most are already available in other functions **/ +enum gr_attrCode { + /// adjusted glyph advance in x direction in design units + gr_slatAdvX = 0, + /// adjusted glyph advance in y direction (usually 0) in design units + gr_slatAdvY, + /// returns 0. Deprecated. + gr_slatAttTo, + /// This slot attaches to its parent at the given design units in the x direction + gr_slatAttX, + /// This slot attaches to its parent at the given design units in the y direction + gr_slatAttY, + /// This slot attaches to its parent at the given glyph point (not implemented) + gr_slatAttGpt, + /// x-direction adjustment from the given glyph point (not implemented) + gr_slatAttXOff, + /// y-direction adjustment from the given glyph point (not implemented) + gr_slatAttYOff, + /// Where on this glyph should align with the attachment point on the parent glyph in the x-direction. + gr_slatAttWithX, + /// Where on this glyph should align with the attachment point on the parent glyph in the y-direction + gr_slatAttWithY, + /// Which glyph point on this glyph should align with the attachment point on the parent glyph (not implemented). + gr_slatWithGpt, + /// Adjustment to gr_slatWithGpt in x-direction (not implemented) + gr_slatAttWithXOff, + /// Adjustment to gr_slatWithGpt in y-direction (not implemented) + gr_slatAttWithYOff, + /// Attach at given nesting level (not implemented) + gr_slatAttLevel, + /// Line break breakweight for this glyph + gr_slatBreak, + /// Ligature component reference (not implemented) + gr_slatCompRef, + /// bidi directionality of this glyph (not implemented) + gr_slatDir, + /// Whether insertion is allowed before this glyph + gr_slatInsert, + /// Final positioned position of this glyph relative to its parent in x-direction in pixels + gr_slatPosX, + /// Final positioned position of this glyph relative to its parent in y-direction in pixels + gr_slatPosY, + /// Amount to shift glyph by in x-direction design units + gr_slatShiftX, + /// Amount to shift glyph by in y-direction design units + gr_slatShiftY, + /// attribute user1 + gr_slatUserDefnV1, + /// not implemented + gr_slatMeasureSol, + /// not implemented + gr_slatMeasureEol, + /// Amount this slot can stretch (not implemented) + gr_slatJStretch, + /// Amount this slot can shrink (not implemented) + gr_slatJShrink, + /// Granularity by which this slot can stretch or shrink (not implemented) + gr_slatJStep, + /// Justification weight for this glyph (not implemented) + gr_slatJWeight, + /// Amount this slot mush shrink or stretch in design units + gr_slatJWidth = 29, + /// SubSegment split point + gr_slatSegSplit = gr_slatJStretch + 29, + /// User defined attribute, see subattr for user attr number + gr_slatUserDefn, + /// Bidi level + gr_slatBidiLevel = 56, + /// Collision flags + gr_slatColFlags, + /// Collision constraint rectangle left (bl.x) + gr_slatColLimitblx, + /// Collision constraint rectangle lower (bl.y) + gr_slatColLimitbly, + /// Collision constraint rectangle right (tr.x) + gr_slatColLimittrx, + /// Collision constraint rectangle upper (tr.y) + gr_slatColLimittry, + /// Collision shift x + gr_slatColShiftx, + /// Collision shift y + gr_slatColShifty, + /// Collision margin + gr_slatColMargin, + /// Margin cost weight + gr_slatColMarginWt, + // Additional glyph that excludes movement near this one: + gr_slatColExclGlyph, + gr_slatColExclOffx, + gr_slatColExclOffy, + // Collision sequence enforcing attributes: + gr_slatSeqClass, + gr_slatSeqProxClass, + gr_slatSeqOrder, + gr_slatSeqAboveXoff, + gr_slatSeqAboveWt, + gr_slatSeqBelowXlim, + gr_slatSeqBelowWt, + gr_slatSeqValignHt, + gr_slatSeqValignWt, + + /// not implemented + gr_slatMax, + /// not implemented + gr_slatNoEffect = gr_slatMax + 1 +}; + +enum gr_bidirtl { + /// Underlying paragraph direction is RTL + gr_rtl = 1, + /// Set this to not run the bidi pass internally, even if the font asks for it. + /// This presumes that the segment is in a single direction. Most of the time + /// this bit should be set unless you know you are passing full paragraphs of text. + gr_nobidi = 2, + /// Disable auto mirroring for rtl text + gr_nomirror = 4 +}; + +typedef struct gr_char_info gr_char_info; +typedef struct gr_segment gr_segment; +typedef struct gr_slot gr_slot; + +/** Returns Unicode character for a charinfo. + * + * @param p Pointer to charinfo to return information on. + */ +GR2_API unsigned int gr_cinfo_unicode_char(const gr_char_info* p/*not NULL*/); + +/** Returns breakweight for a charinfo. + * + * @return Breakweight is a number between -50 and 50 indicating the cost of a + * break before or after this character. If the value < 0, the absolute value + * is this character's contribution to the overall breakweight before it. If the value + * > 0, then the value is this character's contribution to the overall breakweight after it. + * The overall breakweight between two characters is the maximum of the breakweight + * contributions from the characters either side of it. If a character makes no + * contribution to the breakweight on one side of it, the contribution is considered + * to be 0. + * @param p Pointer to charinfo to return information on. + */ +GR2_API int gr_cinfo_break_weight(const gr_char_info* p/*not NULL*/); + +/** Returns the slot index that after this character is after in the slot stream + * + * In effect each character is associated with a set of slots and this returns + * the index of the last slot in the segment this character is associated with. + * + * @return after slot index between 0 and gr_seg_n_slots() + * @param p Pointer to charinfo to return information on. + */ +GR2_API int gr_cinfo_after(const gr_char_info* p/*not NULL*/); + +/** Returns the slot index that before this character is before in the slot stream + * + * In effect each character is associated with a set of slots and this returns + * the index of the first slot in the segment this character is associated with. + * + * @return before slot index between 0 and gr_seg_n_slots() + * @param p Pointer to charinfo to return information on. + */ +GR2_API int gr_cinfo_before(const gr_char_info* p/*not NULL*/); + +/** Returns the code unit index of this character in the input string + * + * @return code unit index between 0 and the end of the string + * @param p Pointer to charinfo to return information on. + */ +GR2_API size_t gr_cinfo_base(const gr_char_info* p/*not NULL*/); + +/** Returns the number of unicode characters in a string. + * + * @return number of characters in the string + * @param enc Specifies the type of data in the string: utf8, utf16, utf32 + * @param buffer_begin The start of the string + * @param buffer_end Measure up to the first nul or when end is reached, whichever is earliest. + * This parameter may be NULL. + * @param pError If there is a structural fault in the string, the location is returned + * in this variable. If no error occurs, pError will contain NULL. NULL + * may be passed for pError if no such information is required. + */ +GR2_API size_t gr_count_unicode_characters(enum gr_encform enc, const void* buffer_begin, const void* buffer_end, const void** pError); + +/** Creates and returns a segment. + * + * @return a segment that needs seg_destroy called on it. May return NULL if bad problems + * in segment processing. + * @param font Gives the size of the font in pixels per em for final positioning. If + * NULL, positions are returned in design units, i.e. at a ppm of the upem + * of the face. + * @param face The face containing all the non-size dependent information. + * @param script This is a tag containing a script identifier that is used to choose + * which graphite table within the font to use. Maybe 0. Tag may be 4 chars + * NULL padded in LSBs or space padded in LSBs. + * @param pFeats Pointer to a feature values to be used for the segment. Only one + * feature values may be used for a segment. If NULL the default features + * for the font will be used. + * @param enc Specifies what encoding form the string is in (utf8, utf16, utf32) + * @param pStart Start of the string + * @param nChars Number of unicode characters to process in the string. The string will + * be processed either up to the first NULL or until nChars have been + * processed. nChars is also used to initialise the internal memory + * allocations of the segment. So it is wise not to make nChars too much + * greater than the actual number of characters being processed. + * @param dir Specifies whether the segment is processed right to left (1) or left to + * right (0) and whether to run the internal bidi pass, if a font requests it. + * See enum gr_bidirtl for details. + */ +GR2_API gr_segment* gr_make_seg(const gr_font* font, const gr_face* face, gr_uint32 script, const gr_feature_val* pFeats, enum gr_encform enc, const void* pStart, size_t nChars, int dir); + +/** Destroys a segment, freeing the memory. + * + * @param p The segment to destroy + */ +GR2_API void gr_seg_destroy(gr_segment* p); + +/** Returns the advance for the whole segment. + * + * Returns the width of the segment up to the next glyph origin after the segment + */ +GR2_API float gr_seg_advance_X(const gr_segment* pSeg/*not NULL*/); + +/** Returns the height advance for the segment. **/ +GR2_API float gr_seg_advance_Y(const gr_segment* pSeg/*not NULL*/); + +/** Returns the number of gr_char_infos in the segment. **/ +GR2_API unsigned int gr_seg_n_cinfo(const gr_segment* pSeg/*not NULL*/); + +/** Returns a gr_char_info at a given index in the segment. **/ +GR2_API const gr_char_info* gr_seg_cinfo(const gr_segment* pSeg/*not NULL*/, unsigned int index/*must be + +typedef unsigned char gr_uint8; +typedef gr_uint8 gr_byte; +typedef signed char gr_int8; +typedef unsigned short gr_uint16; +typedef short gr_int16; +typedef unsigned int gr_uint32; +typedef int gr_int32; + +enum gr_encform { + gr_utf8 = 1/*sizeof(uint8)*/, gr_utf16 = 2/*sizeof(uint16)*/, gr_utf32 = 4/*sizeof(uint32)*/ +}; + + +// Define API function declspec/attributes and how each supported compiler or OS +// allows us to specify them. +#if defined __GNUC__ + #define _gr2_and , + #define _gr2_tag_fn(a) __attribute__((a)) + #define _gr2_deprecated_flag deprecated + #define _gr2_export_flag visibility("default") + #define _gr2_import_flag visibility("default") + #define _gr2_static_flag visibility("hidden") +#endif + +#if defined _WIN32 || defined __CYGWIN__ + #if defined __GNUC__ // These three will be redefined for Windows + #undef _gr2_export_flag + #undef _gr2_import_flag + #undef _gr2_static_flag + #else // How MSVC sepcifies function level attributes adn deprecation + #define _gr2_and + #define _gr2_tag_fn(a) __declspec(a) + #define _gr2_deprecated_flag deprecated + #endif + #define _gr2_export_flag dllexport + #define _gr2_import_flag dllimport + #define _gr2_static_flag +#endif + +#if defined GRAPHITE2_STATIC + #define GR2_API _gr2_tag_fn(_gr2_static_flag) + #define GR2_DEPRECATED_API _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_static_flag) +#elif defined GRAPHITE2_EXPORTING + #define GR2_API _gr2_tag_fn(_gr2_export_flag) + #define GR2_DEPRECATED_API _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_export_flag) +#else + #define GR2_API _gr2_tag_fn(_gr2_import_flag) + #define GR2_DEPRECATED_API _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_import_flag) +#endif diff --git a/thirdparty/graphite/src/CmapCache.cpp b/thirdparty/graphite/src/CmapCache.cpp new file mode 100644 index 00000000000..d070019a340 --- /dev/null +++ b/thirdparty/graphite/src/CmapCache.cpp @@ -0,0 +1,155 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ + +#include "inc/Main.h" +#include "inc/CmapCache.h" +#include "inc/Face.h" +#include "inc/TtfTypes.h" +#include "inc/TtfUtil.h" + + +using namespace graphite2; + +const void * bmp_subtable(const Face::Table & cmap) +{ + const void * stbl; + if (!cmap.size()) return 0; + if (TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 1, cmap.size()), cmap + cmap.size()) + || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 3, cmap.size()), cmap + cmap.size()) + || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 2, cmap.size()), cmap + cmap.size()) + || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 1, cmap.size()), cmap + cmap.size()) + || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 0, cmap.size()), cmap + cmap.size())) + return stbl; + return 0; +} + +const void * smp_subtable(const Face::Table & cmap) +{ + const void * stbl; + if (!cmap.size()) return 0; + if (TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 10, cmap.size()), cmap + cmap.size()) + || TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 4, cmap.size()), cmap + cmap.size())) + return stbl; + return 0; +} + +template +bool cache_subtable(uint16 * blocks[], const void * cst, const unsigned int limit) +{ + int rangeKey = 0; + uint32 codePoint = NextCodePoint(cst, 0, &rangeKey), + prevCodePoint = 0; + while (codePoint < limit) + { + unsigned int block = codePoint >> 8; + if (!blocks[block]) + { + blocks[block] = grzeroalloc(0x100); + if (!blocks[block]) + return false; + } + blocks[block][codePoint & 0xFF] = LookupCodePoint(cst, codePoint, rangeKey); + // prevent infinite loop + if (codePoint <= prevCodePoint) + codePoint = prevCodePoint + 1; + prevCodePoint = codePoint; + codePoint = NextCodePoint(cst, codePoint, &rangeKey); + } + return true; +} + + +CachedCmap::CachedCmap(const Face & face) +: m_isBmpOnly(true), + m_blocks(0) +{ + const Face::Table cmap(face, Tag::cmap); + if (!cmap) return; + + const void * bmp_cmap = bmp_subtable(cmap); + const void * smp_cmap = smp_subtable(cmap); + m_isBmpOnly = !smp_cmap; + + m_blocks = grzeroalloc(m_isBmpOnly ? 0x100 : 0x1100); + if (m_blocks && smp_cmap) + { + if (!cache_subtable(m_blocks, smp_cmap, 0x10FFFF)) + return; + } + + if (m_blocks && bmp_cmap) + { + if (!cache_subtable(m_blocks, bmp_cmap, 0xFFFF)) + return; + } +} + +CachedCmap::~CachedCmap() throw() +{ + if (!m_blocks) return; + unsigned int numBlocks = (m_isBmpOnly)? 0x100 : 0x1100; + for (unsigned int i = 0; i < numBlocks; i++) + free(m_blocks[i]); + free(m_blocks); +} + +uint16 CachedCmap::operator [] (const uint32 usv) const throw() +{ + if ((m_isBmpOnly && usv > 0xFFFF) || (usv > 0x10FFFF)) + return 0; + const uint32 block = 0xFFFF & (usv >> 8); + if (m_blocks[block]) + return m_blocks[block][usv & 0xFF]; + return 0; +}; + +CachedCmap::operator bool() const throw() +{ + return m_blocks != 0; +} + + +DirectCmap::DirectCmap(const Face & face) +: _cmap(face, Tag::cmap), + _smp(smp_subtable(_cmap)), + _bmp(bmp_subtable(_cmap)) +{ +} + +uint16 DirectCmap::operator [] (const uint32 usv) const throw() +{ + return usv > 0xFFFF + ? (_smp ? TtfUtil::CmapSubtable12Lookup(_smp, usv, 0) : 0) + : TtfUtil::CmapSubtable4Lookup(_bmp, usv, 0); +} + +DirectCmap::operator bool () const throw() +{ + return _cmap && _bmp; +} + diff --git a/thirdparty/graphite/src/Code.cpp b/thirdparty/graphite/src/Code.cpp new file mode 100644 index 00000000000..ec5ab298ca4 --- /dev/null +++ b/thirdparty/graphite/src/Code.cpp @@ -0,0 +1,782 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// This class represents loaded graphite stack machine code. It performs +// basic sanity checks, on the incoming code to prevent more obvious problems +// from crashing graphite. +// Author: Tim Eves + +#include +#include +#include +#include +#include "graphite2/Segment.h" +#include "inc/Code.h" +#include "inc/Face.h" +#include "inc/GlyphFace.h" +#include "inc/GlyphCache.h" +#include "inc/Machine.h" +#include "inc/Rule.h" +#include "inc/Silf.h" + +#include + +#ifdef NDEBUG +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif +#endif + + +using namespace graphite2; +using namespace vm; + +namespace { + +inline bool is_return(const instr i) { + const opcode_t * opmap = Machine::getOpcodeTable(); + const instr pop_ret = *opmap[POP_RET].impl, + ret_zero = *opmap[RET_ZERO].impl, + ret_true = *opmap[RET_TRUE].impl; + return i == pop_ret || i == ret_zero || i == ret_true; +} + +struct context +{ + context(uint8 ref=0) : codeRef(ref) {flags.changed=false; flags.referenced=false;} + struct { + uint8 changed:1, + referenced:1; + } flags; + uint8 codeRef; +}; + +} // end namespace + + +class Machine::Code::decoder +{ +public: + struct limits; + static const int NUMCONTEXTS = 256; + + decoder(limits & lims, Code &code, enum passtype pt) throw(); + + bool load(const byte * bc_begin, const byte * bc_end); + void apply_analysis(instr * const code, instr * code_end); + byte max_ref() { return _max_ref; } + int out_index() const { return _out_index; } + +private: + void set_ref(int index) throw(); + void set_noref(int index) throw(); + void set_changed(int index) throw(); + opcode fetch_opcode(const byte * bc); + void analyse_opcode(const opcode, const int8 * const dp) throw(); + bool emit_opcode(opcode opc, const byte * & bc); + bool validate_opcode(const byte opc, const byte * const bc); + bool valid_upto(const uint16 limit, const uint16 x) const throw(); + bool test_context() const throw(); + bool test_ref(int8 index) const throw(); + bool test_attr(attrCode attr) const throw(); + void failure(const status_t s) const throw() { _code.failure(s); } + + Code & _code; + int _out_index; + uint16 _out_length; + instr * _instr; + byte * _data; + limits & _max; + enum passtype _passtype; + int _stack_depth; + bool _in_ctxt_item; + int16 _slotref; + context _contexts[NUMCONTEXTS]; + byte _max_ref; +}; + + +struct Machine::Code::decoder::limits +{ + const byte * bytecode; + const uint8 pre_context; + const uint16 rule_length, + classes, + glyf_attrs, + features; + const byte attrid[gr_slatMax]; +}; + +inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype pt) throw() +: _code(code), + _out_index(code._constraint ? 0 : lims.pre_context), + _out_length(code._constraint ? 1 : lims.rule_length), + _instr(code._code), _data(code._data), _max(lims), _passtype(pt), + _stack_depth(0), + _in_ctxt_item(false), + _slotref(0), + _max_ref(0) +{ } + + + +Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end, + uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face, + enum passtype pt, byte * * const _out) + : _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded), + _constraint(is_constraint), _modify(false), _delete(false), _own(_out==0) +{ +#ifdef GRAPHITE2_TELEMETRY + telemetry::category _code_cat(face.tele.code); +#endif + assert(bytecode_begin != 0); + if (bytecode_begin == bytecode_end) + { + // ::new (this) Code(); + return; + } + assert(bytecode_end > bytecode_begin); + const opcode_t * op_to_fn = Machine::getOpcodeTable(); + + // Allocate code and data target buffers, these sizes are a worst case + // estimate. Once we know their real sizes the we'll shrink them. + if (_out) _code = reinterpret_cast(*_out); + else _code = static_cast(malloc(estimateCodeDataOut(bytecode_end-bytecode_begin, 1, is_constraint ? 0 : rule_length))); + _data = reinterpret_cast(_code + (bytecode_end - bytecode_begin)); + + if (!_code || !_data) { + failure(alloc_failed); + return; + } + + decoder::limits lims = { + bytecode_end, + pre_context, + rule_length, + silf.numClasses(), + face.glyphs().numAttrs(), + face.numFeatures(), + {1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,255, + 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, silf.numUser()} + }; + + decoder dec(lims, *this, pt); + if(!dec.load(bytecode_begin, bytecode_end)) + return; + + // Is this an empty program? + if (_instr_count == 0) + { + release_buffers(); + ::new (this) Code(); + return; + } + + // When we reach the end check we've terminated it correctly + if (!is_return(_code[_instr_count-1])) { + failure(missing_return); + return; + } + + assert((_constraint && immutable()) || !_constraint); + dec.apply_analysis(_code, _code + _instr_count); + _max_ref = dec.max_ref(); + + // Now we know exactly how much code and data the program really needs + // realloc the buffers to exactly the right size so we don't waste any + // memory. + assert((bytecode_end - bytecode_begin) >= ptrdiff_t(_instr_count)); + assert((bytecode_end - bytecode_begin) >= ptrdiff_t(_data_size)); + memmove(_code + (_instr_count+1), _data, _data_size*sizeof(byte)); + size_t const total_sz = ((_instr_count+1) + (_data_size + sizeof(instr)-1)/sizeof(instr))*sizeof(instr); + if (_out) + *_out += total_sz; + else + { + instr * const old_code = _code; + _code = static_cast(realloc(_code, total_sz)); + if (!_code) free(old_code); + } + _data = reinterpret_cast(_code + (_instr_count+1)); + + if (!_code) + { + failure(alloc_failed); + return; + } + + // Make this RET_ZERO, we should never reach this but just in case ... + _code[_instr_count] = op_to_fn[RET_ZERO].impl[_constraint]; + +#ifdef GRAPHITE2_TELEMETRY + telemetry::count_bytes(_data_size + (_instr_count+1)*sizeof(instr)); +#endif +} + +Machine::Code::~Code() throw () +{ + if (_own) + release_buffers(); +} + + +bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end) +{ + _max.bytecode = bc_end; + while (bc < bc_end) + { + const opcode opc = fetch_opcode(bc++); + if (opc == vm::MAX_OPCODE) + return false; + + analyse_opcode(opc, reinterpret_cast(bc)); + + if (!emit_opcode(opc, bc)) + return false; + } + + return bool(_code); +} + +// Validation check and fixups. +// + +opcode Machine::Code::decoder::fetch_opcode(const byte * bc) +{ + const byte opc = *bc++; + + // Do some basic sanity checks based on what we know about the opcode + if (!validate_opcode(opc, bc)) return MAX_OPCODE; + + // And check its arguments as far as possible + switch (opcode(opc)) + { + case NOP : + break; + case PUSH_BYTE : + case PUSH_BYTEU : + case PUSH_SHORT : + case PUSH_SHORTU : + case PUSH_LONG : + ++_stack_depth; + break; + case ADD : + case SUB : + case MUL : + case DIV : + case MIN_ : + case MAX_ : + case AND : + case OR : + case EQUAL : + case NOT_EQ : + case LESS : + case GTR : + case LESS_EQ : + case GTR_EQ : + case BITOR : + case BITAND : + if (--_stack_depth <= 0) + failure(underfull_stack); + break; + case NEG : + case TRUNC8 : + case TRUNC16 : + case NOT : + case BITNOT : + case BITSET : + if (_stack_depth <= 0) + failure(underfull_stack); + break; + case COND : + _stack_depth -= 2; + if (_stack_depth <= 0) + failure(underfull_stack); + break; + case NEXT_N : // runtime checked + break; + case NEXT : + case COPY_NEXT : + ++_out_index; + if (_out_index < -1 || _out_index > _out_length || _slotref > _max.rule_length) + failure(out_of_range_data); + break; + case PUT_GLYPH_8BIT_OBS : + valid_upto(_max.classes, bc[0]); + test_context(); + break; + case PUT_SUBS_8BIT_OBS : + test_ref(int8(bc[0])); + valid_upto(_max.classes, bc[1]); + valid_upto(_max.classes, bc[2]); + test_context(); + break; + case PUT_COPY : + test_ref(int8(bc[0])); + test_context(); + break; + case INSERT : + if (_passtype >= PASS_TYPE_POSITIONING) + failure(invalid_opcode); + ++_out_length; + if (_out_index < 0) ++_out_index; + if (_out_index < -1 || _out_index >= _out_length) + failure(out_of_range_data); + break; + case DELETE : + if (_passtype >= PASS_TYPE_POSITIONING) + failure(invalid_opcode); + if (_out_index < _max.pre_context) + failure(out_of_range_data); + --_out_index; + --_out_length; + if (_out_index < -1 || _out_index > _out_length) + failure(out_of_range_data); + break; + case ASSOC : + if (bc[0] == 0) + failure(out_of_range_data); + for (uint8 num = bc[0]; num; --num) + test_ref(int8(bc[num])); + test_context(); + break; + case CNTXT_ITEM : + valid_upto(_max.rule_length, _max.pre_context + int8(bc[0])); + if (bc + 2 + bc[1] >= _max.bytecode) failure(jump_past_end); + if (_in_ctxt_item) failure(nested_context_item); + break; + case ATTR_SET : + case ATTR_ADD : + case ATTR_SUB : + case ATTR_SET_SLOT : + if (--_stack_depth < 0) + failure(underfull_stack); + valid_upto(gr_slatMax, bc[0]); + if (attrCode(bc[0]) == gr_slatUserDefn) // use IATTR for user attributes + failure(out_of_range_data); + test_attr(attrCode(bc[0])); + test_context(); + break; + case IATTR_SET_SLOT : + if (--_stack_depth < 0) + failure(underfull_stack); + if (valid_upto(gr_slatMax, bc[0])) + valid_upto(_max.attrid[bc[0]], bc[1]); + test_attr(attrCode(bc[0])); + test_context(); + break; + case PUSH_SLOT_ATTR : + ++_stack_depth; + valid_upto(gr_slatMax, bc[0]); + test_ref(int8(bc[1])); + if (attrCode(bc[0]) == gr_slatUserDefn) // use IATTR for user attributes + failure(out_of_range_data); + test_attr(attrCode(bc[0])); + break; + case PUSH_GLYPH_ATTR_OBS : + case PUSH_ATT_TO_GATTR_OBS : + ++_stack_depth; + valid_upto(_max.glyf_attrs, bc[0]); + test_ref(int8(bc[1])); + break; + case PUSH_ATT_TO_GLYPH_METRIC : + case PUSH_GLYPH_METRIC : + ++_stack_depth; + valid_upto(kgmetDescent, bc[0]); + test_ref(int8(bc[1])); + // level: dp[2] no check necessary + break; + case PUSH_FEAT : + ++_stack_depth; + valid_upto(_max.features, bc[0]); + test_ref(int8(bc[1])); + break; + case PUSH_ISLOT_ATTR : + ++_stack_depth; + if (valid_upto(gr_slatMax, bc[0])) + { + test_ref(int8(bc[1])); + valid_upto(_max.attrid[bc[0]], bc[2]); + } + test_attr(attrCode(bc[0])); + break; + case PUSH_IGLYPH_ATTR :// not implemented + ++_stack_depth; + break; + case POP_RET : + if (--_stack_depth < 0) + failure(underfull_stack); + GR_FALLTHROUGH; + // no break + case RET_ZERO : + case RET_TRUE : + break; + case IATTR_SET : + case IATTR_ADD : + case IATTR_SUB : + if (--_stack_depth < 0) + failure(underfull_stack); + if (valid_upto(gr_slatMax, bc[0])) + valid_upto(_max.attrid[bc[0]], bc[1]); + test_attr(attrCode(bc[0])); + test_context(); + break; + case PUSH_PROC_STATE : // dummy: dp[0] no check necessary + case PUSH_VERSION : + ++_stack_depth; + break; + case PUT_SUBS : + test_ref(int8(bc[0])); + valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]); + valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]); + test_context(); + break; + case PUT_SUBS2 : // not implemented + case PUT_SUBS3 : // not implemented + break; + case PUT_GLYPH : + valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]); + test_context(); + break; + case PUSH_GLYPH_ATTR : + case PUSH_ATT_TO_GLYPH_ATTR : + ++_stack_depth; + valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]); + test_ref(int8(bc[2])); + break; + case SET_FEAT : + valid_upto(_max.features, bc[0]); + test_ref(int8(bc[1])); + break; + default: + failure(invalid_opcode); + break; + } + + return bool(_code) ? opcode(opc) : MAX_OPCODE; +} + + +void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8 * arg) throw() +{ + switch (opc) + { + case DELETE : + _code._delete = true; + break; + case ASSOC : + set_changed(0); +// for (uint8 num = arg[0]; num; --num) +// _analysis.set_noref(num); + break; + case PUT_GLYPH_8BIT_OBS : + case PUT_GLYPH : + _code._modify = true; + set_changed(0); + break; + case ATTR_SET : + case ATTR_ADD : + case ATTR_SUB : + case ATTR_SET_SLOT : + case IATTR_SET_SLOT : + case IATTR_SET : + case IATTR_ADD : + case IATTR_SUB : + set_noref(0); + break; + case NEXT : + case COPY_NEXT : + ++_slotref; + _contexts[_slotref] = context(uint8(_code._instr_count+1)); + // if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref; + break; + case INSERT : + if (_slotref >= 0) --_slotref; + _code._modify = true; + break; + case PUT_SUBS_8BIT_OBS : // slotref on 1st parameter + case PUT_SUBS : + _code._modify = true; + set_changed(0); + GR_FALLTHROUGH; + // no break + case PUT_COPY : + if (arg[0] != 0) { set_changed(0); _code._modify = true; } + set_ref(arg[0]); + break; + case PUSH_GLYPH_ATTR_OBS : + case PUSH_SLOT_ATTR : + case PUSH_GLYPH_METRIC : + case PUSH_ATT_TO_GATTR_OBS : + case PUSH_ATT_TO_GLYPH_METRIC : + case PUSH_ISLOT_ATTR : + case PUSH_FEAT : + case SET_FEAT : + set_ref(arg[1]); + break; + case PUSH_ATT_TO_GLYPH_ATTR : + case PUSH_GLYPH_ATTR : + set_ref(arg[2]); + break; + default: + break; + } +} + + +bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc) +{ + const opcode_t * op_to_fn = Machine::getOpcodeTable(); + const opcode_t & op = op_to_fn[opc]; + if (op.impl[_code._constraint] == 0) + { + failure(unimplemented_opcode_used); + return false; + } + + const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz; + + // Add this instruction + *_instr++ = op.impl[_code._constraint]; + ++_code._instr_count; + + // Grab the parameters + if (param_sz) { + memcpy(_data, bc, param_sz * sizeof(byte)); + bc += param_sz; + _data += param_sz; + _code._data_size += param_sz; + } + + // recursively decode a context item so we can split the skip into + // instruction and data portions. + if (opc == CNTXT_ITEM) + { + assert(_out_index == 0); + _in_ctxt_item = true; + _out_index = _max.pre_context + int8(_data[-2]); + _slotref = int8(_data[-2]); + _out_length = _max.rule_length; + + const size_t ctxt_start = _code._instr_count; + byte & instr_skip = _data[-1]; + byte & data_skip = *_data++; + ++_code._data_size; + const byte *curr_end = _max.bytecode; + + if (load(bc, bc + instr_skip)) + { + bc += instr_skip; + data_skip = instr_skip - byte(_code._instr_count - ctxt_start); + instr_skip = byte(_code._instr_count - ctxt_start); + _max.bytecode = curr_end; + + _out_length = 1; + _out_index = 0; + _slotref = 0; + _in_ctxt_item = false; + } + else + { + _out_index = 0; + _slotref = 0; + return false; + } + } + + return bool(_code); +} + + +void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end) +{ + // insert TEMP_COPY commands for slots that need them (that change and are referenced later) + int tempcount = 0; + if (_code._constraint) return; + + const instr temp_copy = Machine::getOpcodeTable()[TEMP_COPY].impl[0]; + for (const context * c = _contexts, * const ce = c + _slotref; c < ce; ++c) + { + if (!c->flags.referenced || !c->flags.changed) continue; + + instr * const tip = code + c->codeRef + tempcount; + memmove(tip+1, tip, (code_end - tip) * sizeof(instr)); + *tip = temp_copy; + ++code_end; + ++tempcount; + _code._delete = true; + } + + _code._instr_count = code_end - code; +} + + +inline +bool Machine::Code::decoder::validate_opcode(const byte opc, const byte * const bc) +{ + if (opc >= MAX_OPCODE) + { + failure(invalid_opcode); + return false; + } + const opcode_t & op = Machine::getOpcodeTable()[opc]; + if (op.impl[_code._constraint] == 0) + { + failure(unimplemented_opcode_used); + return false; + } + if (op.param_sz == VARARGS && bc >= _max.bytecode) + { + failure(arguments_exhausted); + return false; + } + const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz; + if (bc - 1 + param_sz >= _max.bytecode) + { + failure(arguments_exhausted); + return false; + } + return true; +} + + +bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) const throw() +{ + const bool t = (limit != 0) && (x < limit); + if (!t) failure(out_of_range_data); + return t; +} + +inline +bool Machine::Code::decoder::test_ref(int8 index) const throw() +{ + if (_code._constraint && !_in_ctxt_item) + { + if (index > 0 || -index > _max.pre_context) + { + failure(out_of_range_data); + return false; + } + } + else + { + if (_max.rule_length == 0 + || (_slotref + _max.pre_context + index >= _max.rule_length) + || (_slotref + _max.pre_context + index < 0)) + { + failure(out_of_range_data); + return false; + } + } + return true; +} + +bool Machine::Code::decoder::test_context() const throw() +{ + if (_out_index >= _out_length || _out_index < 0 || _slotref >= NUMCONTEXTS - 1) + { + failure(out_of_range_data); + return false; + } + return true; +} + +bool Machine::Code::decoder::test_attr(attrCode) const throw() +{ +#if 0 // This code is coming but causes backward compatibility problems. + if (_passtype < PASS_TYPE_POSITIONING) + { + if (attr != gr_slatBreak && attr != gr_slatDir && attr != gr_slatUserDefn + && attr != gr_slatCompRef) + { + failure(out_of_range_data); + return false; + } + } +#endif + return true; +} + +inline +void Machine::Code::failure(const status_t s) throw() { + release_buffers(); + _status = s; +} + + +inline +void Machine::Code::decoder::set_ref(int index) throw() { + if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return; + _contexts[index + _slotref].flags.referenced = true; + if (index + _slotref > _max_ref) _max_ref = index + _slotref; +} + + +inline +void Machine::Code::decoder::set_noref(int index) throw() { + if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return; + if (index + _slotref > _max_ref) _max_ref = index + _slotref; +} + + +inline +void Machine::Code::decoder::set_changed(int index) throw() { + if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return; + _contexts[index + _slotref].flags.changed= true; + if (index + _slotref > _max_ref) _max_ref = index + _slotref; +} + + +void Machine::Code::release_buffers() throw() +{ + if (_own) + free(_code); + _code = 0; + _data = 0; + _own = false; +} + + +int32 Machine::Code::run(Machine & m, slotref * & map) const +{ +// assert(_own); + assert(*this); // Check we are actually runnable + + if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context()) + || m.slotMap()[_max_ref + m.slotMap().context()] == 0) + { + m._status = Machine::slot_offset_out_bounds; + return 1; +// return m.run(_code, _data, map); + } + + return m.run(_code, _data, map); +} diff --git a/thirdparty/graphite/src/Collider.cpp b/thirdparty/graphite/src/Collider.cpp new file mode 100644 index 00000000000..1929b39a58e --- /dev/null +++ b/thirdparty/graphite/src/Collider.cpp @@ -0,0 +1,1115 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include +#include +#include +#include +#include +#include "inc/Collider.h" +#include "inc/Segment.h" +#include "inc/Slot.h" +#include "inc/GlyphCache.h" +#include "inc/Sparse.h" + +#define ISQRT2 0.707106781f + +// Possible rounding error for subbox boundaries: 0.016 = 1/64 = 1/256 * 4 +// (values in font range from 0..256) +// #define SUBBOX_RND_ERR 0.016 + +using namespace graphite2; + +//// SHIFT-COLLIDER //// + +// Initialize the Collider to hold the basic movement limits for the +// target slot, the one we are focusing on fixing. +bool ShiftCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin, float marginWeight, + const Position &currShift, const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout) +{ + int i; + float mx, mn; + float a, shift; + const GlyphCache &gc = seg->getFace()->glyphs(); + unsigned short gid = aSlot->gid(); + if (!gc.check(gid)) + return false; + const BBox &bb = gc.getBoundingBBox(gid); + const SlantBox &sb = gc.getBoundingSlantBox(gid); + //float sx = aSlot->origin().x + currShift.x; + //float sy = aSlot->origin().y + currShift.y; + if (currOffset.x != 0.f || currOffset.y != 0.f) + _limit = Rect(limit.bl - currOffset, limit.tr - currOffset); + else + _limit = limit; + // For a ShiftCollider, these indices indicate which vector we are moving by: + // each _ranges represents absolute space with respect to the origin of the slot. Thus take into account true origins but subtract the vmin for the slot + for (i = 0; i < 4; ++i) + { + switch (i) { + case 0 : // x direction + mn = _limit.bl.x + currOffset.x; + mx = _limit.tr.x + currOffset.x; + _len[i] = bb.xa - bb.xi; + a = currOffset.y + currShift.y; + _ranges[i].initialise(mn, mx, margin, marginWeight, a); + break; + case 1 : // y direction + mn = _limit.bl.y + currOffset.y; + mx = _limit.tr.y + currOffset.y; + _len[i] = bb.ya - bb.yi; + a = currOffset.x + currShift.x; + _ranges[i].initialise(mn, mx, margin, marginWeight, a); + break; + case 2 : // sum (negatively sloped diagonal boundaries) + // pick closest x,y limit boundaries in s direction + shift = currOffset.x + currOffset.y + currShift.x + currShift.y; + mn = -2 * min(currShift.x - _limit.bl.x, currShift.y - _limit.bl.y) + shift; + mx = 2 * min(_limit.tr.x - currShift.x, _limit.tr.y - currShift.y) + shift; + _len[i] = sb.sa - sb.si; + a = currOffset.x - currOffset.y + currShift.x - currShift.y; + _ranges[i].initialise(mn, mx, margin / ISQRT2, marginWeight, a); + break; + case 3 : // diff (positively sloped diagonal boundaries) + // pick closest x,y limit boundaries in d direction + shift = currOffset.x - currOffset.y + currShift.x - currShift.y; + mn = -2 * min(currShift.x - _limit.bl.x, _limit.tr.y - currShift.y) + shift; + mx = 2 * min(_limit.tr.x - currShift.x, currShift.y - _limit.bl.y) + shift; + _len[i] = sb.da - sb.di; + a = currOffset.x + currOffset.y + currShift.x + currShift.y; + _ranges[i].initialise(mn, mx, margin / ISQRT2, marginWeight, a); + break; + } + } + + _target = aSlot; + if ((dir & 1) == 0) + { + // For LTR, switch and negate x limits. + _limit.bl.x = -1 * limit.tr.x; + //_limit.tr.x = -1 * limit.bl.x; + } + _currOffset = currOffset; + _currShift = currShift; + _origin = aSlot->origin() - currOffset; // the original anchor position of the glyph + + _margin = margin; + _marginWt = marginWeight; + + SlotCollision *c = seg->collisionInfo(aSlot); + _seqClass = c->seqClass(); + _seqProxClass = c->seqProxClass(); + _seqOrder = c->seqOrder(); + return true; +} + +template +float sdm(float vi, float va, float mx, float my, O op) +{ + float res = 2 * mx - vi; + if (op(res, vi + 2 * my)) + { + res = va + 2 * my; + if (op(res, 2 * mx - va)) + res = mx + my; + } + return res; +} + +// Mark an area with a cost that can vary along the x or y axis. The region is expressed in terms of the centre of the target glyph in each axis +void ShiftCollider::addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int axis) +{ + float a, c; + switch (axis) { + case 0 : + if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0) + { + a = org.y + 0.5f * (bb.yi + bb.ya); + c = 0.5f * (bb.xi + bb.xa); + if (isx) + _ranges[axis].weighted(box.bl.x - c, box.tr.x - c, weight, a, m, + (minright ? box.tr.x : box.bl.x) - c, a, 0, false); + else + _ranges[axis].weighted(box.bl.x - c, box.tr.x - c, weight, a, 0, 0, org.y, + m * (a * a + sqr((minright ? box.tr.y : box.bl.y) - 0.5f * (bb.yi + bb.ya))), false); + } + break; + case 1 : + if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0) + { + a = org.x + 0.5f * (bb.xi + bb.xa); + c = 0.5f * (bb.yi + bb.ya); + if (isx) + _ranges[axis].weighted(box.bl.y - c, box.tr.y - c, weight, a, 0, 0, org.x, + m * (a * a + sqr((minright ? box.tr.x : box.bl.x) - 0.5f * (bb.xi + bb.xa))), false); + else + _ranges[axis].weighted(box.bl.y - c, box.tr.y - c, weight, a, m, + (minright ? box.tr.y : box.bl.y) - c, a, 0, false); + } + break; + case 2 : + if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di) + { + float d = org.x - org.y + 0.5f * (sb.di + sb.da); + c = 0.5f * (sb.si + sb.sa); + float smax = min(2 * box.tr.x - d, 2 * box.tr.y + d); + float smin = max(2 * box.bl.x - d, 2 * box.bl.y + d); + if (smin > smax) return; + float si; + a = d; + if (isx) + si = 2 * (minright ? box.tr.x : box.bl.x) - a; + else + si = 2 * (minright ? box.tr.y : box.bl.y) + a; + _ranges[axis].weighted(smin - c, smax - c, weight / 2, a, m / 2, si, 0, 0, isx); + } + break; + case 3 : + if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si) + { + float s = org.x + org.y + 0.5f * (sb.si + sb.sa); + c = 0.5f * (sb.di + sb.da); + float dmax = min(2 * box.tr.x - s, s - 2 * box.bl.y); + float dmin = max(2 * box.bl.x - s, s - 2 * box.tr.y); + if (dmin > dmax) return; + float di; + a = s; + if (isx) + di = 2 * (minright ? box.tr.x : box.bl.x) - a; + else + di = 2 * (minright ? box.tr.y : box.bl.y) + a; + _ranges[axis].weighted(dmin - c, dmax - c, weight / 2, a, m / 2, di, 0, 0, !isx); + } + break; + default : + break; + } + return; +} + +// Mark an area with an absolute cost, making it completely inaccessible. +inline void ShiftCollider::removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int axis) +{ + float c; + switch (axis) { + case 0 : + if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0) + { + c = 0.5f * (bb.xi + bb.xa); + _ranges[axis].exclude(box.bl.x - c, box.tr.x - c); + } + break; + case 1 : + if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0) + { + c = 0.5f * (bb.yi + bb.ya); + _ranges[axis].exclude(box.bl.y - c, box.tr.y - c); + } + break; + case 2 : + if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di + && box.width() > 0 && box.height() > 0) + { + float di = org.x - org.y + sb.di; + float da = org.x - org.y + sb.da; + float smax = sdm(di, da, box.tr.x, box.tr.y, std::greater()); + float smin = sdm(da, di, box.bl.x, box.bl.y, std::less()); + c = 0.5f * (sb.si + sb.sa); + _ranges[axis].exclude(smin - c, smax - c); + } + break; + case 3 : + if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si + && box.width() > 0 && box.height() > 0) + { + float si = org.x + org.y + sb.si; + float sa = org.x + org.y + sb.sa; + float dmax = sdm(si, sa, box.tr.x, -box.bl.y, std::greater()); + float dmin = sdm(sa, si, box.bl.x, -box.tr.y, std::less()); + c = 0.5f * (sb.di + sb.da); + _ranges[axis].exclude(dmin - c, dmax - c); + } + break; + default : + break; + } + return; +} + +// Adjust the movement limits for the target to avoid having it collide +// with the given neighbor slot. Also determine if there is in fact a collision +// between the target and the given slot. +bool ShiftCollider::mergeSlot(Segment *seg, Slot *slot, const SlotCollision *cslot, const Position &currShift, + bool isAfter, // slot is logically after _target + bool sameCluster, bool &hasCol, bool isExclusion, + GR_MAYBE_UNUSED json * const dbgout ) +{ + bool isCol = false; + const float sx = slot->origin().x - _origin.x + currShift.x; + const float sy = slot->origin().y - _origin.y + currShift.y; + const float sd = sx - sy; + const float ss = sx + sy; + float vmin, vmax; + float omin, omax, otmin, otmax; + float cmin, cmax; // target limits + float torg; + const GlyphCache &gc = seg->getFace()->glyphs(); + const unsigned short gid = slot->gid(); + if (!gc.check(gid)) + return false; + const BBox &bb = gc.getBoundingBBox(gid); + + // SlotCollision * cslot = seg->collisionInfo(slot); + int orderFlags = 0; + bool sameClass = _seqProxClass == 0 && cslot->seqClass() == _seqClass; + if (sameCluster && _seqClass + && (sameClass || (_seqProxClass != 0 && cslot->seqClass() == _seqProxClass))) + // Force the target glyph to be in the specified direction from the slot we're testing. + orderFlags = _seqOrder; + + // short circuit if only interested in direct collision and we are out of range + if (orderFlags || (sx + bb.xa + _margin >= _limit.bl.x && sx + bb.xi - _margin <= _limit.tr.x) + || (sy + bb.ya + _margin >= _limit.bl.y && sy + bb.yi - _margin <= _limit.tr.y)) + + { + const float tx = _currOffset.x + _currShift.x; + const float ty = _currOffset.y + _currShift.y; + const float td = tx - ty; + const float ts = tx + ty; + const SlantBox &sb = gc.getBoundingSlantBox(gid); + const unsigned short tgid = _target->gid(); + const BBox &tbb = gc.getBoundingBBox(tgid); + const SlantBox &tsb = gc.getBoundingSlantBox(tgid); + float seq_above_wt = cslot->seqAboveWt(); + float seq_below_wt = cslot->seqBelowWt(); + float seq_valign_wt = cslot->seqValignWt(); + float lmargin; + // if isAfter, invert orderFlags for diagonal orders. + if (isAfter) + { + // invert appropriate bits + orderFlags ^= (sameClass ? 0x3F : 0x3); + // consider 2 bits at a time, non overlapping. If both bits set, clear them + orderFlags = orderFlags ^ ((((orderFlags >> 1) & orderFlags) & 0x15) * 3); + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + dbgout->setenv(0, slot); +#endif + + // Process main bounding octabox. + for (int i = 0; i < 4; ++i) + { + switch (i) { + case 0 : // x direction + vmin = max(max(bb.xi - tbb.xa + sx, sb.di - tsb.da + ty + sd), sb.si - tsb.sa - ty + ss); + vmax = min(min(bb.xa - tbb.xi + sx, sb.da - tsb.di + ty + sd), sb.sa - tsb.si - ty + ss); + otmin = tbb.yi + ty; + otmax = tbb.ya + ty; + omin = bb.yi + sy; + omax = bb.ya + sy; + torg = _currOffset.x; + cmin = _limit.bl.x + torg; + cmax = _limit.tr.x - tbb.xi + tbb.xa + torg; + lmargin = _margin; + break; + case 1 : // y direction + vmin = max(max(bb.yi - tbb.ya + sy, tsb.di - sb.da + tx - sd), sb.si - tsb.sa - tx + ss); + vmax = min(min(bb.ya - tbb.yi + sy, tsb.da - sb.di + tx - sd), sb.sa - tsb.si - tx + ss); + otmin = tbb.xi + tx; + otmax = tbb.xa + tx; + omin = bb.xi + sx; + omax = bb.xa + sx; + torg = _currOffset.y; + cmin = _limit.bl.y + torg; + cmax = _limit.tr.y - tbb.yi + tbb.ya + torg; + lmargin = _margin; + break; + case 2 : // sum - moving along the positively-sloped vector, so the boundaries are the + // negatively-sloped boundaries. + vmin = max(max(sb.si - tsb.sa + ss, 2 * (bb.yi - tbb.ya + sy) + td), 2 * (bb.xi - tbb.xa + sx) - td); + vmax = min(min(sb.sa - tsb.si + ss, 2 * (bb.ya - tbb.yi + sy) + td), 2 * (bb.xa - tbb.xi + sx) - td); + otmin = tsb.di + td; + otmax = tsb.da + td; + omin = sb.di + sd; + omax = sb.da + sd; + torg = _currOffset.x + _currOffset.y; + cmin = _limit.bl.x + _limit.bl.y + torg; + cmax = _limit.tr.x + _limit.tr.y - tsb.si + tsb.sa + torg; + lmargin = _margin / ISQRT2; + break; + case 3 : // diff - moving along the negatively-sloped vector, so the boundaries are the + // positively-sloped boundaries. + vmin = max(max(sb.di - tsb.da + sd, 2 * (bb.xi - tbb.xa + sx) - ts), -2 * (bb.ya - tbb.yi + sy) + ts); + vmax = min(min(sb.da - tsb.di + sd, 2 * (bb.xa - tbb.xi + sx) - ts), -2 * (bb.yi - tbb.ya + sy) + ts); + otmin = tsb.si + ts; + otmax = tsb.sa + ts; + omin = sb.si + ss; + omax = sb.sa + ss; + torg = _currOffset.x - _currOffset.y; + cmin = _limit.bl.x - _limit.tr.y + torg; + cmax = _limit.tr.x - _limit.bl.y - tsb.di + tsb.da + torg; + lmargin = _margin / ISQRT2; + break; + default : + continue; + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + dbgout->setenv(1, reinterpret_cast(-1)); +#define DBGTAG(x) if (dbgout) dbgout->setenv(1, reinterpret_cast(-x)); +#else +#define DBGTAG(x) +#endif + + if (orderFlags) + { + Position org(tx, ty); + float xminf = _limit.bl.x + _currOffset.x + tbb.xi; + float xpinf = _limit.tr.x + _currOffset.x + tbb.xa; + float ypinf = _limit.tr.y + _currOffset.y + tbb.ya; + float yminf = _limit.bl.y + _currOffset.y + tbb.yi; + switch (orderFlags) { + case SlotCollision::SEQ_ORDER_RIGHTUP : + { + float r1Xedge = cslot->seqAboveXoff() + 0.5f * (bb.xi + bb.xa) + sx; + float r3Xedge = cslot->seqBelowXlim() + bb.xa + sx + 0.5f * (tbb.xa - tbb.xi); + float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy; + + // DBGTAG(1x) means the regions are up and right + // region 1 + DBGTAG(11) + addBox_slope(true, Rect(Position(xminf, r2Yedge), Position(r1Xedge, ypinf)), + tbb, tsb, org, 0, seq_above_wt, true, i); + // region 2 + DBGTAG(12) + removeBox(Rect(Position(xminf, yminf), Position(r3Xedge, r2Yedge)), tbb, tsb, org, i); + // region 3, which end is zero is irrelevant since m weight is 0 + DBGTAG(13) + addBox_slope(true, Rect(Position(r3Xedge, yminf), Position(xpinf, r2Yedge - cslot->seqValignHt())), + tbb, tsb, org, seq_below_wt, 0, true, i); + // region 4 + DBGTAG(14) + addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge), Position(xpinf, r2Yedge + cslot->seqValignHt())), + tbb, tsb, org, 0, seq_valign_wt, true, i); + // region 5 + DBGTAG(15) + addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge - cslot->seqValignHt()), Position(xpinf, r2Yedge)), + tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i); + break; + } + case SlotCollision::SEQ_ORDER_LEFTDOWN : + { + float r1Xedge = 0.5f * (bb.xi + bb.xa) + cslot->seqAboveXoff() + sx; + float r3Xedge = bb.xi - cslot->seqBelowXlim() + sx - 0.5f * (tbb.xa - tbb.xi); + float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy; + // DBGTAG(2x) means the regions are up and right + // region 1 + DBGTAG(21) + addBox_slope(true, Rect(Position(r1Xedge, yminf), Position(xpinf, r2Yedge)), + tbb, tsb, org, 0, seq_above_wt, false, i); + // region 2 + DBGTAG(22) + removeBox(Rect(Position(r3Xedge, r2Yedge), Position(xpinf, ypinf)), tbb, tsb, org, i); + // region 3 + DBGTAG(23) + addBox_slope(true, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), Position(r3Xedge, ypinf)), + tbb, tsb, org, seq_below_wt, 0, false, i); + // region 4 + DBGTAG(24) + addBox_slope(false, Rect(Position(xminf, r2Yedge), Position(sx + bb.xa, r2Yedge + cslot->seqValignHt())), + tbb, tsb, org, 0, seq_valign_wt, true, i); + // region 5 + DBGTAG(25) + addBox_slope(false, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), + Position(sx + bb.xa, r2Yedge)), tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i); + break; + } + case SlotCollision::SEQ_ORDER_NOABOVE : // enforce neighboring glyph being above + DBGTAG(31); + removeBox(Rect(Position(bb.xi - tbb.xa + sx, sy + bb.ya), + Position(bb.xa - tbb.xi + sx, ypinf)), tbb, tsb, org, i); + break; + case SlotCollision::SEQ_ORDER_NOBELOW : // enforce neighboring glyph being below + DBGTAG(32); + removeBox(Rect(Position(bb.xi - tbb.xa + sx, yminf), + Position(bb.xa - tbb.xi + sx, sy + bb.yi)), tbb, tsb, org, i); + break; + case SlotCollision::SEQ_ORDER_NOLEFT : // enforce neighboring glyph being to the left + DBGTAG(33) + removeBox(Rect(Position(xminf, bb.yi - tbb.ya + sy), + Position(bb.xi - tbb.xa + sx, bb.ya - tbb.yi + sy)), tbb, tsb, org, i); + break; + case SlotCollision::SEQ_ORDER_NORIGHT : // enforce neighboring glyph being to the right + DBGTAG(34) + removeBox(Rect(Position(bb.xa - tbb.xi + sx, bb.yi - tbb.ya + sy), + Position(xpinf, bb.ya - tbb.yi + sy)), tbb, tsb, org, i); + break; + default : + break; + } + } + + if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin) + continue; + + // Process sub-boxes that are defined for this glyph. + // We only need to do this if there was in fact a collision with the main octabox. + uint8 numsub = gc.numSubBounds(gid); + if (numsub > 0) + { + bool anyhits = false; + for (int j = 0; j < numsub; ++j) + { + const BBox &sbb = gc.getSubBoundingBBox(gid, j); + const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, j); + switch (i) { + case 0 : // x + vmin = max(max(sbb.xi-tbb.xa+sx, ssb.di-tsb.da+sd+ty), ssb.si-tsb.sa+ss-ty); + vmax = min(min(sbb.xa-tbb.xi+sx, ssb.da-tsb.di+sd+ty), ssb.sa-tsb.si+ss-ty); + omin = sbb.yi + sy; + omax = sbb.ya + sy; + break; + case 1 : // y + vmin = max(max(sbb.yi-tbb.ya+sy, tsb.di-ssb.da-sd+tx), ssb.si-tsb.sa+ss-tx); + vmax = min(min(sbb.ya-tbb.yi+sy, tsb.da-ssb.di-sd+tx), ssb.sa-tsb.si+ss-tx); + omin = sbb.xi + sx; + omax = sbb.xa + sx; + break; + case 2 : // sum + vmin = max(max(ssb.si-tsb.sa+ss, 2*(sbb.yi-tbb.ya+sy)+td), 2*(sbb.xi-tbb.xa+sx)-td); + vmax = min(min(ssb.sa-tsb.si+ss, 2*(sbb.ya-tbb.yi+sy)+td), 2*(sbb.xa-tbb.xi+sx)-td); + omin = ssb.di + sd; + omax = ssb.da + sd; + break; + case 3 : // diff + vmin = max(max(ssb.di-tsb.da+sd, 2*(sbb.xi-tbb.xa+sx)-ts), -2*(sbb.ya-tbb.yi+sy)+ts); + vmax = min(min(ssb.da-tsb.di+sd, 2*(sbb.xa-tbb.xi+sx)-ts), -2*(sbb.yi-tbb.ya+sy)+ts); + omin = ssb.si + ss; + omax = ssb.sa + ss; + break; + } + if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin) + continue; + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + dbgout->setenv(1, reinterpret_cast(j)); +#endif + if (omin > otmax) + _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0, + sqr(lmargin - omin + otmax) * _marginWt, false); + else if (omax < otmin) + _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0, + sqr(lmargin - otmin + omax) * _marginWt, false); + else + _ranges[i].exclude_with_margins(vmin, vmax, i); + anyhits = true; + } + if (anyhits) + isCol = true; + } + else // no sub-boxes + { +#if !defined GRAPHITE2_NTRACING + if (dbgout) + dbgout->setenv(1, reinterpret_cast(-1)); +#endif + isCol = true; + if (omin > otmax) + _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0, + sqr(lmargin - omin + otmax) * _marginWt, false); + else if (omax < otmin) + _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0, + sqr(lmargin - otmin + omax) * _marginWt, false); + else + _ranges[i].exclude_with_margins(vmin, vmax, i); + + } + } + } + bool res = true; + if (cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion) + { + // Set up the bogus slot representing the exclusion glyph. + Slot *exclSlot = seg->newSlot(); + if (!exclSlot) + return res; + exclSlot->setGlyph(seg, cslot->exclGlyph()); + Position exclOrigin(slot->origin() + cslot->exclOffset()); + exclSlot->origin(exclOrigin); + SlotCollision exclInfo(seg, exclSlot); + res &= mergeSlot(seg, exclSlot, &exclInfo, currShift, isAfter, sameCluster, isCol, true, dbgout ); + seg->freeSlot(exclSlot); + } + hasCol |= isCol; + return res; + +} // end of ShiftCollider::mergeSlot + + +// Figure out where to move the target glyph to, and return the amount to shift by. +Position ShiftCollider::resolve(GR_MAYBE_UNUSED Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout) +{ + float tbase; + float totalCost = (float)(std::numeric_limits::max() / 2); + Position resultPos = Position(0, 0); +#if !defined GRAPHITE2_NTRACING + int bestAxis = -1; + if (dbgout) + { + outputJsonDbgStartSlot(dbgout, seg); + *dbgout << "vectors" << json::array; + } +#endif + isCol = true; + for (int i = 0; i < 4; ++i) + { + float bestCost = -1; + float bestPos; + // Calculate the margin depending on whether we are moving diagonally or not: + switch (i) { + case 0 : // x direction + tbase = _currOffset.x; + break; + case 1 : // y direction + tbase = _currOffset.y; + break; + case 2 : // sum (negatively-sloped diagonals) + tbase = _currOffset.x + _currOffset.y; + break; + case 3 : // diff (positively-sloped diagonals) + tbase = _currOffset.x - _currOffset.y; + break; + } + Position testp; + bestPos = _ranges[i].closest(0, bestCost) - tbase; // Get the best relative position +#if !defined GRAPHITE2_NTRACING + if (dbgout) + outputJsonDbgOneVector(dbgout, seg, i, tbase, bestCost, bestPos) ; +#endif + if (bestCost >= 0.0f) + { + isCol = false; + switch (i) { + case 0 : testp = Position(bestPos, _currShift.y); break; + case 1 : testp = Position(_currShift.x, bestPos); break; + case 2 : testp = Position(0.5f * (_currShift.x - _currShift.y + bestPos), 0.5f * (_currShift.y - _currShift.x + bestPos)); break; + case 3 : testp = Position(0.5f * (_currShift.x + _currShift.y + bestPos), 0.5f * (_currShift.x + _currShift.y - bestPos)); break; + } + if (bestCost < totalCost - 0.01f) + { + totalCost = bestCost; + resultPos = testp; +#if !defined GRAPHITE2_NTRACING + bestAxis = i; +#endif + } + } + } // end of loop over 4 directions + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + outputJsonDbgEndSlot(dbgout, resultPos, bestAxis, isCol); +#endif + + return resultPos; + +} // end of ShiftCollider::resolve + + +#if !defined GRAPHITE2_NTRACING + +void ShiftCollider::outputJsonDbg(json * const dbgout, Segment *seg, int axis) +{ + int axisMax = axis; + if (axis < 0) // output all axes + { + *dbgout << "gid" << _target->gid() + << "limit" << _limit + << "target" << json::object + << "origin" << _target->origin() + << "margin" << _margin + << "bbox" << seg->theGlyphBBoxTemporary(_target->gid()) + << "slantbox" << seg->getFace()->glyphs().slant(_target->gid()) + << json::close; // target object + *dbgout << "ranges" << json::array; + axis = 0; + axisMax = 3; + } + for (int iAxis = axis; iAxis <= axisMax; ++iAxis) + { + *dbgout << json::flat << json::array << _ranges[iAxis].position(); + for (Zones::const_iterator s = _ranges[iAxis].begin(), e = _ranges[iAxis].end(); s != e; ++s) + *dbgout << json::flat << json::array + << Position(s->x, s->xm) << s->sm << s->smx << s->c + << json::close; + *dbgout << json::close; + } + if (axis < axisMax) // looped through the _ranges array for all axes + *dbgout << json::close; // ranges array +} + +void ShiftCollider::outputJsonDbgStartSlot(json * const dbgout, Segment *seg) +{ + *dbgout << json::object // slot - not closed till the end of the caller method + << "slot" << objectid(dslot(seg, _target)) + << "gid" << _target->gid() + << "limit" << _limit + << "target" << json::object + << "origin" << _origin + << "currShift" << _currShift + << "currOffset" << seg->collisionInfo(_target)->offset() + << "bbox" << seg->theGlyphBBoxTemporary(_target->gid()) + << "slantBox" << seg->getFace()->glyphs().slant(_target->gid()) + << "fix" << "shift"; + *dbgout << json::close; // target object +} + +void ShiftCollider::outputJsonDbgEndSlot(GR_MAYBE_UNUSED json * const dbgout, + Position resultPos, int bestAxis, bool isCol) +{ + *dbgout << json::close // vectors array + << "result" << resultPos + //<< "scraping" << _scraping[bestAxis] + << "bestAxis" << bestAxis + << "stillBad" << isCol + << json::close; // slot object +} + +void ShiftCollider::outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis, + float tleft, float bestCost, float bestVal) +{ + const char * label; + switch (axis) + { + case 0: label = "x"; break; + case 1: label = "y"; break; + case 2: label = "sum (NE-SW)"; break; + case 3: label = "diff (NW-SE)"; break; + default: label = "???"; break; + } + + *dbgout << json::object // vector + << "direction" << label + << "targetMin" << tleft; + + outputJsonDbgRemovals(dbgout, axis, seg); + + *dbgout << "ranges"; + outputJsonDbg(dbgout, seg, axis); + + *dbgout << "bestCost" << bestCost + << "bestVal" << bestVal + tleft + << json::close; // vectors object +} + +void ShiftCollider::outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg) +{ + *dbgout << "removals" << json::array; + _ranges[axis].jsonDbgOut(seg); + *dbgout << json::close; // removals array +} + +#endif // !defined GRAPHITE2_NTRACING + + +//// KERN-COLLIDER //// + +inline +static float localmax (float al, float au, float bl, float bu, float x) +{ + if (al < bl) + { if (au < bu) return au < x ? au : x; } + else if (au > bu) return bl < x ? bl : x; + return x; +} + +inline +static float localmin(float al, float au, float bl, float bu, float x) +{ + if (bl > al) + { if (bu > au) return bl > x ? bl : x; } + else if (au > bu) return al > x ? al : x; + return x; +} + +// Return the given edge of the glyph at height y, taking any slant box into account. +static float get_edge(Segment *seg, const Slot *s, const Position &shift, float y, float width, float margin, bool isRight) +{ + const GlyphCache &gc = seg->getFace()->glyphs(); + unsigned short gid = s->gid(); + float sx = s->origin().x + shift.x; + float sy = s->origin().y + shift.y; + uint8 numsub = gc.numSubBounds(gid); + float res = isRight ? (float)-1e38 : (float)1e38; + + if (numsub > 0) + { + for (int i = 0; i < numsub; ++i) + { + const BBox &sbb = gc.getSubBoundingBBox(gid, i); + const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, i); + if (sy + sbb.yi - margin > y + width / 2 || sy + sbb.ya + margin < y - width / 2) + continue; + if (isRight) + { + float x = sx + sbb.xa + margin; + if (x > res) + { + float td = sx - sy + ssb.da + margin + y; + float ts = sx + sy + ssb.sa + margin - y; + x = localmax(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x); + if (x > res) + res = x; + } + } + else + { + float x = sx + sbb.xi - margin; + if (x < res) + { + float td = sx - sy + ssb.di - margin + y; + float ts = sx + sy + ssb.si - margin - y; + x = localmin(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x); + if (x < res) + res = x; + } + } + } + } + else + { + const BBox &bb = gc.getBoundingBBox(gid); + const SlantBox &sb = gc.getBoundingSlantBox(gid); + if (sy + bb.yi - margin > y + width / 2 || sy + bb.ya + margin < y - width / 2) + return res; + float td = sx - sy + y; + float ts = sx + sy - y; + if (isRight) + res = localmax(td + sb.da - width / 2, td + sb.da + width / 2, ts + sb.sa - width / 2, ts + sb.sa + width / 2, sx + bb.xa) + margin; + else + res = localmin(td + sb.di - width / 2, td + sb.di + width / 2, ts + sb.si - width / 2, ts + sb.si + width / 2, sx + bb.xi) - margin; + } + return res; +} + + +bool KernCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin, + const Position &currShift, const Position &offsetPrev, int dir, + float ymin, float ymax, GR_MAYBE_UNUSED json * const dbgout) +{ + const GlyphCache &gc = seg->getFace()->glyphs(); + const Slot *base = aSlot; + // const Slot *last = aSlot; + const Slot *s; + int numSlices; + while (base->attachedTo()) + base = base->attachedTo(); + if (margin < 10) margin = 10; + + _limit = limit; + _offsetPrev = offsetPrev; // kern from a previous pass + + // Calculate the height of the glyph and how many horizontal slices to use. + if (_maxy >= 1e37f) + { + _sliceWidth = margin / 1.5f; + _maxy = ymax + margin; + _miny = ymin - margin; + numSlices = int((_maxy - _miny + 2) / (_sliceWidth / 1.5f) + 1.f); // +2 helps with rounding errors + _edges.clear(); + _edges.insert(_edges.begin(), numSlices, (dir & 1) ? 1e38f : -1e38f); + _xbound = (dir & 1) ? (float)1e38f : (float)-1e38f; + } + else if (_maxy != ymax || _miny != ymin) + { + if (_miny != ymin) + { + numSlices = int((ymin - margin - _miny) / _sliceWidth - 1); + _miny += numSlices * _sliceWidth; + if (numSlices < 0) + _edges.insert(_edges.begin(), -numSlices, (dir & 1) ? 1e38f : -1e38f); + else if ((unsigned)numSlices < _edges.size()) // this shouldn't fire since we always grow the range + { + Vector::iterator e = _edges.begin(); + while (numSlices--) + ++e; + _edges.erase(_edges.begin(), e); + } + } + if (_maxy != ymax) + { + numSlices = int((ymax + margin - _miny) / _sliceWidth + 1); + _maxy = numSlices * _sliceWidth + _miny; + if (numSlices > (int)_edges.size()) + _edges.insert(_edges.end(), numSlices - _edges.size(), (dir & 1) ? 1e38f : -1e38f); + else if (numSlices < (int)_edges.size()) // this shouldn't fire since we always grow the range + { + while ((int)_edges.size() > numSlices) + _edges.pop_back(); + } + } + goto done; + } + numSlices = int(_edges.size()); + +#if !defined GRAPHITE2_NTRACING + // Debugging + _seg = seg; + _slotNear.clear(); + _slotNear.insert(_slotNear.begin(), numSlices, NULL); + _nearEdges.clear(); + _nearEdges.insert(_nearEdges.begin(), numSlices, (dir & 1) ? -1e38f : +1e38f); +#endif + + // Determine the trailing edge of each slice (ie, left edge for a RTL glyph). + for (s = base; s; s = s->nextInCluster(s)) + { + SlotCollision *c = seg->collisionInfo(s); + if (!gc.check(s->gid())) + return false; + const BBox &bs = gc.getBoundingBBox(s->gid()); + float x = s->origin().x + c->shift().x + ((dir & 1) ? bs.xi : bs.xa); + // Loop over slices. + // Note smin might not be zero if glyph s is not at the bottom of the cluster; similarly for smax. + float toffset = c->shift().y - _miny + 1 + s->origin().y; + int smin = max(0, int((bs.yi + toffset) / _sliceWidth)); + int smax = min(numSlices - 1, int((bs.ya + toffset) / _sliceWidth + 1)); + for (int i = smin; i <= smax; ++i) + { + float t; + float y = _miny - 1 + (i + .5f) * _sliceWidth; // vertical center of slice + if ((dir & 1) && x < _edges[i]) + { + t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, false); + if (t < _edges[i]) + { + _edges[i] = t; + if (t < _xbound) + _xbound = t; + } + } + else if (!(dir & 1) && x > _edges[i]) + { + t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, true); + if (t > _edges[i]) + { + _edges[i] = t; + if (t > _xbound) + _xbound = t; + } + } + } + } + done: + _mingap = (float)1e37; // less than 1e38 s.t. 1e38-_mingap is really big + _target = aSlot; + _margin = margin; + _currShift = currShift; + return true; +} // end of KernCollider::initSlot + + +// Determine how much the target slot needs to kern away from the given slot. +// In other words, merge information from given slot's position with what the target slot knows +// about how it can kern. +// Return false if we know there is no collision, true if we think there might be one. +bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, GR_MAYBE_UNUSED json * const dbgout) +{ + int rtl = (dir & 1) * 2 - 1; + if (!seg->getFace()->glyphs().check(slot->gid())) + return false; + const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid()); + const float sx = slot->origin().x + currShift.x; + float x = (sx + (rtl > 0 ? bb.tr.x : bb.bl.x)) * rtl; + // this isn't going to reduce _mingap so skip + if (_hit && x < rtl * (_xbound - _mingap - currSpace)) + return false; + + const float sy = slot->origin().y + currShift.y; + int smin = max(1, int((bb.bl.y + (1 - _miny + sy)) / _sliceWidth + 1)) - 1; + int smax = min((int)_edges.size() - 2, int((bb.tr.y + (1 - _miny + sy)) / _sliceWidth + 1)) + 1; + if (smin > smax) + return false; + bool collides = false; + bool nooverlap = true; + + for (int i = smin; i <= smax; ++i) + { + float here = _edges[i] * rtl; + if (here > (float)9e37) + continue; + if (!_hit || x > here - _mingap - currSpace) + { + float y = (float)(_miny - 1 + (i + .5f) * _sliceWidth); // vertical center of slice + // 2 * currSpace to account for the space that is already separating them and the space we want to add + float m = get_edge(seg, slot, currShift, y, _sliceWidth, 0., rtl > 0) * rtl + 2 * currSpace; + if (m < (float)-8e37) // only true if the glyph has a gap in it + continue; + nooverlap = false; + float t = here - m; + // _mingap is positive to shrink + if (t < _mingap || (!_hit && !collides)) + { + _mingap = t; + collides = true; + } +#if !defined GRAPHITE2_NTRACING + // Debugging - remember the closest neighboring edge for this slice. + if (m > rtl * _nearEdges[i]) + { + _slotNear[i] = slot; + _nearEdges[i] = m * rtl; + } +#endif + } + else + nooverlap = false; + } + if (nooverlap) + _mingap = max(_mingap, _xbound - rtl * (currSpace + _margin + x)); + if (collides && !nooverlap) + _hit = true; + return collides | nooverlap; // note that true is not a necessarily reliable value + +} // end of KernCollider::mergeSlot + + +// Return the amount to kern by. +Position KernCollider::resolve(GR_MAYBE_UNUSED Segment *seg, GR_MAYBE_UNUSED Slot *slot, + int dir, GR_MAYBE_UNUSED json * const dbgout) +{ + float resultNeeded = (1 - 2 * (dir & 1)) * _mingap; + // float resultNeeded = (1 - 2 * (dir & 1)) * (_mingap - margin); + float result = min(_limit.tr.x - _offsetPrev.x, max(resultNeeded, _limit.bl.x - _offsetPrev.x)); + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + { + *dbgout << json::object // slot + << "slot" << objectid(dslot(seg, _target)) + << "gid" << _target->gid() + << "limit" << _limit + << "miny" << _miny + << "maxy" << _maxy + << "slicewidth" << _sliceWidth + << "target" << json::object + << "origin" << _target->origin() + //<< "currShift" << _currShift + << "offsetPrev" << _offsetPrev + << "bbox" << seg->theGlyphBBoxTemporary(_target->gid()) + << "slantBox" << seg->getFace()->glyphs().slant(_target->gid()) + << "fix" << "kern" + << json::close; // target object + + *dbgout << "slices" << json::array; + for (int is = 0; is < (int)_edges.size(); is++) + { + *dbgout << json::flat << json::object + << "i" << is + << "targetEdge" << _edges[is] + << "neighbor" << objectid(dslot(seg, _slotNear[is])) + << "nearEdge" << _nearEdges[is] + << json::close; + } + *dbgout << json::close; // slices array + + *dbgout + << "xbound" << _xbound + << "minGap" << _mingap + << "needed" << resultNeeded + << "result" << result + << "stillBad" << (result != resultNeeded) + << json::close; // slot object + } +#endif + + return Position(result, 0.); + +} // end of KernCollider::resolve + +void KernCollider::shift(const Position &mv, int dir) +{ + for (Vector::iterator e = _edges.begin(); e != _edges.end(); ++e) + *e += mv.x; + _xbound += (1 - 2 * (dir & 1)) * mv.x; +} + +//// SLOT-COLLISION //// + +// Initialize the collision attributes for the given slot. +SlotCollision::SlotCollision(Segment *seg, Slot *slot) +{ + initFromSlot(seg, slot); +} + +void SlotCollision::initFromSlot(Segment *seg, Slot *slot) +{ + // Initialize slot attributes from glyph attributes. + // The order here must match the order in the grcompiler code, + // GrcSymbolTable::AssignInternalGlyphAttrIDs. + uint16 gid = slot->gid(); + uint16 aCol = seg->silf()->aCollision(); // flags attr ID + const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(gid); + if (!glyphFace) + return; + const sparse &p = glyphFace->attrs(); + _flags = p[aCol]; + _limit = Rect(Position(int16(p[aCol+1]), int16(p[aCol+2])), + Position(int16(p[aCol+3]), int16(p[aCol+4]))); + _margin = p[aCol+5]; + _marginWt = p[aCol+6]; + + _seqClass = p[aCol+7]; + _seqProxClass = p[aCol+8]; + _seqOrder = p[aCol+9]; + _seqAboveXoff = p[aCol+10]; + _seqAboveWt = p[aCol+11]; + _seqBelowXlim = p[aCol+12]; + _seqBelowWt = p[aCol+13]; + _seqValignHt = p[aCol+14]; + _seqValignWt = p[aCol+15]; + + // These attributes do not have corresponding glyph attribute: + _exclGlyph = 0; + _exclOffset = Position(0, 0); +} + +float SlotCollision::getKern(int dir) const +{ + if ((_flags & SlotCollision::COLL_KERN) != 0) + return float(_shift.x * ((dir & 1) ? -1 : 1)); + else + return 0; +} + +bool SlotCollision::ignore() const +{ + return ((flags() & SlotCollision::COLL_IGNORE) || (flags() & SlotCollision::COLL_ISSPACE)); +} diff --git a/thirdparty/graphite/src/Decompressor.cpp b/thirdparty/graphite/src/Decompressor.cpp new file mode 100644 index 00000000000..42dc9113e54 --- /dev/null +++ b/thirdparty/graphite/src/Decompressor.cpp @@ -0,0 +1,125 @@ +/* GRAPHITE2 LICENSING + + Copyright 2015, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include + +#include "inc/Decompressor.h" +#include "inc/Compression.h" + +using namespace lz4; + +namespace { + +inline +u32 read_literal(u8 const * &s, u8 const * const e, u32 l) { + if (l == 15 && s != e) + { + u8 b = 0; + do { l += b = *s++; } while(b==0xff && s != e); + } + return l; +} + +bool read_sequence(u8 const * &src, u8 const * const end, u8 const * &literal, + u32 & literal_len, u32 & match_len, u32 & match_dist) +{ + u8 const token = *src++; + + literal_len = read_literal(src, end, token >> 4); + literal = src; + src += literal_len; + + // Normal exit for end of stream, wrap arround check and parital match check. + if (src > end - sizeof(u16) || src < literal) + return false; + + match_dist = *src++; + match_dist |= *src++ << 8; + match_len = read_literal(src, end, token & 0xf) + MINMATCH; + + // Malformed stream check. + return src <= end-MINCODA; +} + +} + +int lz4::decompress(void const *in, size_t in_size, void *out, size_t out_size) +{ + if (out_size <= in_size || in_size < MINSRCSIZE) + return -1; + + u8 const * src = static_cast(in), + * literal = 0, + * const src_end = src + in_size; + + u8 * dst = static_cast(out), + * const dst_end = dst + out_size; + + // Check the in and out size hasn't wrapped around. + if (src >= src_end || dst >= dst_end) + return -1; + + u32 literal_len = 0, + match_len = 0, + match_dist = 0; + + while (read_sequence(src, src_end, literal, literal_len, match_len, + match_dist)) + { + if (literal_len != 0) + { + // Copy in literal. At this point the a minimal literal + minminal + // match plus the coda (1 + 2 + 5) must be 8 bytes or more allowing + // us to remain within the src buffer for an overrun_copy on + // machines upto 64 bits. + if (align(literal_len) > out_size) + return -1; + dst = overrun_copy(dst, literal, literal_len); + out_size -= literal_len; + } + + // Copy, possibly repeating, match from earlier in the + // decoded output. + u8 const * const pcpy = dst - match_dist; + if (pcpy < static_cast(out) + || match_len > unsigned(out_size - LASTLITERALS) + // Wrap around checks: + || out_size < LASTLITERALS || pcpy >= dst) + return -1; + if (dst > pcpy+sizeof(unsigned long) + && align(match_len) <= out_size) + dst = overrun_copy(dst, pcpy, match_len); + else + dst = safe_copy(dst, pcpy, match_len); + out_size -= match_len; + } + + if (literal > src_end - literal_len || literal_len > out_size) + return -1; + dst = fast_copy(dst, literal, literal_len); + + return int(dst - (u8*)out); +} diff --git a/thirdparty/graphite/src/Face.cpp b/thirdparty/graphite/src/Face.cpp new file mode 100644 index 00000000000..3e106050d7d --- /dev/null +++ b/thirdparty/graphite/src/Face.cpp @@ -0,0 +1,366 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include +#include "graphite2/Segment.h" +#include "inc/CmapCache.h" +#include "inc/debug.h" +#include "inc/Decompressor.h" +#include "inc/Endian.h" +#include "inc/Face.h" +#include "inc/FileFace.h" +#include "inc/GlyphFace.h" +#include "inc/json.h" +#include "inc/Segment.h" +#include "inc/NameTable.h" +#include "inc/Error.h" + +using namespace graphite2; + +namespace +{ +enum compression +{ + NONE, + LZ4 +}; + +} + +Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops) +: m_appFaceHandle(appFaceHandle), + m_pFileFace(NULL), + m_pGlyphFaceCache(NULL), + m_cmap(NULL), + m_pNames(NULL), + m_logger(NULL), + m_error(0), m_errcntxt(0), + m_silfs(NULL), + m_numSilf(0), + m_ascent(0), + m_descent(0) +{ + memset(&m_ops, 0, sizeof m_ops); + memcpy(&m_ops, &ops, min(sizeof m_ops, ops.size)); +} + + +Face::~Face() +{ + setLogger(0); + delete m_pGlyphFaceCache; + delete m_cmap; + delete[] m_silfs; +#ifndef GRAPHITE2_NFILEFACE + delete m_pFileFace; +#endif + delete m_pNames; +} + +float Face::default_glyph_advance(const void* font_ptr, gr_uint16 glyphid) +{ + const Font & font = *reinterpret_cast(font_ptr); + + return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale(); +} + +bool Face::readGlyphs(uint32 faceOptions) +{ + Error e; +#ifdef GRAPHITE2_TELEMETRY + telemetry::category _glyph_cat(tele.glyph); +#endif + error_context(EC_READGLYPHS); + m_pGlyphFaceCache = new GlyphCache(*this, faceOptions); + + if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM) + || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS) + || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM)) + { + return error(e); + } + + if (faceOptions & gr_face_cacheCmap) + m_cmap = new CachedCmap(*this); + else + m_cmap = new DirectCmap(*this); + if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP)) + return error(e); + + if (faceOptions & gr_face_preloadGlyphs) + nameTable(); // preload the name table along with the glyphs. + + return true; +} + +bool Face::readGraphite(const Table & silf) +{ +#ifdef GRAPHITE2_TELEMETRY + telemetry::category _silf_cat(tele.silf); +#endif + Error e; + error_context(EC_READSILF); + const byte * p = silf; + if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20, E_BADSIZE)) return error(e); + + const uint32 version = be::read(p); + if (e.test(version < 0x00020000, E_TOOOLD)) return error(e); + if (version >= 0x00030000) + be::skip(p); // compilerVersion + m_numSilf = be::read(p); + + be::skip(p); // reserved + + bool havePasses = false; + m_silfs = new Silf[m_numSilf]; + if (e.test(!m_silfs, E_OUTOFMEM)) return error(e); + for (int i = 0; i < m_numSilf; i++) + { + error_context(EC_ASILF + (i << 8)); + const uint32 offset = be::read(p), + next = i == m_numSilf - 1 ? uint32(silf.size()) : be::peek(p); + if (e.test(next > silf.size() || offset >= next, E_BADSIZE)) + return error(e); + + if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version)) + return false; + + if (m_silfs[i].numPasses()) + havePasses = true; + } + + return havePasses; +} + +bool Face::readFeatures() +{ + return m_Sill.readFace(*this); +} + +bool Face::runGraphite(Segment *seg, const Silf *aSilf) const +{ +#if !defined GRAPHITE2_NTRACING + json * dbgout = logger(); + if (dbgout) + { + *dbgout << json::object + << "id" << objectid(seg) + << "passes" << json::array; + } +#endif + +// if ((seg->dir() & 1) != aSilf->dir()) +// seg->reverseSlots(); + if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF) + seg->doMirror(aSilf->aMirror()); + bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true); + if (res) + { + seg->associateChars(0, seg->charInfoCount()); + if (aSilf->flags() & 0x20) + res &= seg->initCollisions(); + if (res) + res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false); + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) +{ + seg->positionSlots(0, 0, 0, seg->currdir()); + *dbgout << json::item + << json::close // Close up the passes array + << "outputdir" << (seg->currdir() ? "rtl" : "ltr") + << "output" << json::array; + for(Slot * s = seg->first(); s; s = s->next()) + *dbgout << dslot(seg, s); + *dbgout << json::close + << "advance" << seg->advance() + << "chars" << json::array; + for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i) + *dbgout << json::flat << *seg->charinfo(int(i)); + *dbgout << json::close // Close up the chars array + << json::close; // Close up the segment object + } +#endif + + return res; +} + +void Face::setLogger(FILE * log_file GR_MAYBE_UNUSED) +{ +#if !defined GRAPHITE2_NTRACING + delete m_logger; + m_logger = log_file ? new json(log_file) : 0; +#endif +} + +const Silf *Face::chooseSilf(uint32 script) const +{ + if (m_numSilf == 0) + return NULL; + else if (m_numSilf == 1 || script == 0) + return m_silfs; + else // do more work here + return m_silfs; +} + +uint16 Face::findPseudo(uint32 uid) const +{ + return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0; +} + +int32 Face::getGlyphMetric(uint16 gid, uint8 metric) const +{ + switch (metrics(metric)) + { + case kgmetAscent : return m_ascent; + case kgmetDescent : return m_descent; + default: + if (gid >= glyphs().numGlyphs()) return 0; + return glyphs().glyph(gid)->getMetric(metric); + } +} + +void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/) +{ +#ifndef GRAPHITE2_NFILEFACE + if (m_pFileFace==pFileFace) + return; + + delete m_pFileFace; + m_pFileFace = pFileFace; +#endif +} + +NameTable * Face::nameTable() const +{ + if (m_pNames) return m_pNames; + const Table name(*this, Tag::name); + if (name) + m_pNames = new NameTable(name, name.size()); + return m_pNames; +} + +uint16 Face::languageForLocale(const char * locale) const +{ + nameTable(); + if (m_pNames) + return m_pNames->getLanguageId(locale); + return 0; +} + + + +Face::Table::Table(const Face & face, const Tag n, uint32 version) throw() +: _f(&face), _sz(0), _compressed(false) +{ + _p = static_cast((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &_sz)); + + if (!TtfUtil::CheckTable(n, _p, _sz)) + { + release(); // Make sure we release the table buffer even if the table failed its checks + return; + } + + if (be::peek(_p) >= version) + decompress(); +} + +void Face::Table::release() +{ + if (_compressed) + free(const_cast(_p)); + else if (_p && _f->m_ops.release_table) + (*_f->m_ops.release_table)(_f->m_appFaceHandle, _p); + _p = 0; _sz = 0; +} + +Face::Table & Face::Table::operator = (const Table && rhs) throw() +{ + if (this == &rhs) return *this; + release(); + new (this) Table(std::move(rhs)); + return *this; +} + +Error Face::Table::decompress() +{ + Error e; + if (e.test(_sz < 5 * sizeof(uint32), E_BADSIZE)) + return e; + byte * uncompressed_table = 0; + size_t uncompressed_size = 0; + + const byte * p = _p; + const uint32 version = be::read(p); // Table version number. + + // The scheme is in the top 5 bits of the 1st uint32. + const uint32 hdr = be::read(p); + switch(compression(hdr >> 27)) + { + case NONE: return e; + + case LZ4: + { + uncompressed_size = hdr & 0x07ffffff; + uncompressed_table = gralloc(uncompressed_size); + if (!e.test(!uncompressed_table || uncompressed_size < 4, E_OUTOFMEM)) + { + memset(uncompressed_table, 0, 4); // make sure version number is initialised + // coverity[forward_null : FALSE] - uncompressed_table has been checked so can't be null + // coverity[checked_return : FALSE] - we test e later + e.test(lz4::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED); + } + break; + } + + default: + e.error(E_BADSCHEME); + }; + + // Check the uncompressed version number against the original. + if (!e) + // coverity[forward_null : FALSE] - uncompressed_table has already been tested so can't be null + // coverity[checked_return : FALSE] - we test e later + e.test(be::peek(uncompressed_table) != version, E_SHRINKERFAILED); + + // Tell the provider to release the compressed form since were replacing + // it anyway. + release(); + + if (e) + { + free(uncompressed_table); + uncompressed_table = 0; + uncompressed_size = 0; + } + + _p = uncompressed_table; + _sz = uncompressed_size; + _compressed = true; + + return e; +} diff --git a/thirdparty/graphite/src/FeatureMap.cpp b/thirdparty/graphite/src/FeatureMap.cpp new file mode 100644 index 00000000000..014a88fd08e --- /dev/null +++ b/thirdparty/graphite/src/FeatureMap.cpp @@ -0,0 +1,293 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include + +#include "inc/Main.h" +#include "inc/bits.h" +#include "inc/Endian.h" +#include "inc/FeatureMap.h" +#include "inc/FeatureVal.h" +#include "graphite2/Font.h" +#include "inc/TtfUtil.h" +#include +#include "inc/Face.h" + + +using namespace graphite2; + +namespace +{ + static int cmpNameAndFeatures(const void *ap, const void *bp) + { + const NameAndFeatureRef & a = *static_cast(ap), + & b = *static_cast(bp); + return (a < b ? -1 : (b < a ? 1 : 0)); + } + + const size_t FEAT_HEADER = sizeof(uint32) + 2*sizeof(uint16) + sizeof(uint32), + FEATURE_SIZE = sizeof(uint32) + + 2*sizeof(uint16) + + sizeof(uint32) + + 2*sizeof(uint16), + FEATURE_SETTING_SIZE = sizeof(int16) + sizeof(uint16); + + uint16 readFeatureSettings(const byte * p, FeatureSetting * s, size_t num_settings) + { + uint16 max_val = 0; + for (FeatureSetting * const end = s + num_settings; s != end; ++s) + { + const int16 value = be::read(p); + ::new (s) FeatureSetting(value, be::read(p)); + if (uint16(value) > max_val) max_val = value; + } + + return max_val; + } +} + +FeatureRef::FeatureRef(const Face & face, + unsigned short & bits_offset, uint32 max_val, + uint32 name, uint16 uiName, flags_t flags, + FeatureSetting *settings, uint16 num_set) throw() +: m_face(&face), + m_nameValues(settings), + m_mask(mask_over_val(max_val)), + m_max(max_val), + m_id(name), + m_nameid(uiName), + m_numSet(num_set), + m_flags(flags) +{ + const uint8 need_bits = bit_set_count(m_mask); + m_index = (bits_offset + need_bits) / SIZEOF_CHUNK; + if (m_index > bits_offset / SIZEOF_CHUNK) + bits_offset = m_index*SIZEOF_CHUNK; + m_bits = bits_offset % SIZEOF_CHUNK; + bits_offset += need_bits; + m_mask <<= m_bits; +} + +FeatureRef::~FeatureRef() throw() +{ + free(m_nameValues); +} + +bool FeatureMap::readFeats(const Face & face) +{ + const Face::Table feat(face, TtfUtil::Tag::Feat); + const byte * p = feat; + if (!p) return true; + if (feat.size() < FEAT_HEADER) return false; + + const byte *const feat_start = p, + *const feat_end = p + feat.size(); + + const uint32 version = be::read(p); + m_numFeats = be::read(p); + be::skip(p); + be::skip(p); + + // Sanity checks + if (m_numFeats == 0) return true; + if (version < 0x00010000 || + p + m_numFeats*FEATURE_SIZE > feat_end) + { //defensive + m_numFeats = 0; + return false; + } + + m_feats = new FeatureRef [m_numFeats]; + uint16 * const defVals = gralloc(m_numFeats); + if (!defVals || !m_feats) return false; + unsigned short bits = 0; //to cause overflow on first Feature + + for (int i = 0, ie = m_numFeats; i != ie; i++) + { + const uint32 label = version < 0x00020000 ? be::read(p) : be::read(p); + const uint16 num_settings = be::read(p); + if (version >= 0x00020000) + be::skip(p); + const uint32 settings_offset = be::read(p); + const uint16 flags = be::read(p), + uiName = be::read(p); + + if (settings_offset > size_t(feat_end - feat_start) + || settings_offset + num_settings * FEATURE_SETTING_SIZE > size_t(feat_end - feat_start)) + { + free(defVals); + return false; + } + + FeatureSetting *uiSet; + uint32 maxVal; + if (num_settings != 0) + { + uiSet = gralloc(num_settings); + if (!uiSet) + { + free(defVals); + return false; + } + maxVal = readFeatureSettings(feat_start + settings_offset, uiSet, num_settings); + defVals[i] = uiSet[0].value(); + } + else + { + uiSet = 0; + maxVal = 0xffffffff; + defVals[i] = 0; + } + + ::new (m_feats + i) FeatureRef (face, bits, maxVal, + label, uiName, + FeatureRef::flags_t(flags), + uiSet, num_settings); + } + new (&m_defaultFeatures) Features(bits/(sizeof(uint32)*8) + 1, *this); + m_pNamedFeats = new NameAndFeatureRef[m_numFeats]; + if (!m_pNamedFeats) + { + free(defVals); + return false; + } + for (int i = 0; i < m_numFeats; ++i) + { + m_feats[i].applyValToFeature(defVals[i], m_defaultFeatures); + m_pNamedFeats[i] = m_feats[i]; + } + + free(defVals); + + qsort(m_pNamedFeats, m_numFeats, sizeof(NameAndFeatureRef), &cmpNameAndFeatures); + + return true; +} + +bool SillMap::readFace(const Face & face) +{ + if (!m_FeatureMap.readFeats(face)) return false; + if (!readSill(face)) return false; + return true; +} + + +bool SillMap::readSill(const Face & face) +{ + const Face::Table sill(face, TtfUtil::Tag::Sill); + const byte *p = sill; + + if (!p) return true; + if (sill.size() < 12) return false; + if (be::read(p) != 0x00010000UL) return false; + m_numLanguages = be::read(p); + m_langFeats = new LangFeaturePair[m_numLanguages]; + if (!m_langFeats || !m_FeatureMap.m_numFeats) { m_numLanguages = 0; return true; } //defensive + + p += 6; // skip the fast search + if (sill.size() < m_numLanguages * 8U + 12) return false; + + for (int i = 0; i < m_numLanguages; i++) + { + uint32 langid = be::read(p); + uint16 numSettings = be::read(p); + uint16 offset = be::read(p); + if (offset + 8U * numSettings > sill.size() && numSettings > 0) return false; + Features* feats = new Features(m_FeatureMap.m_defaultFeatures); + if (!feats) return false; + const byte *pLSet = sill + offset; + + // Apply langauge specific settings + for (int j = 0; j < numSettings; j++) + { + uint32 name = be::read(pLSet); + uint16 val = be::read(pLSet); + pLSet += 2; + const FeatureRef* pRef = m_FeatureMap.findFeatureRef(name); + if (pRef) pRef->applyValToFeature(val, *feats); + } + // Add the language id feature which is always feature id 1 + const FeatureRef* pRef = m_FeatureMap.findFeatureRef(1); + if (pRef) pRef->applyValToFeature(langid, *feats); + + m_langFeats[i].m_lang = langid; + m_langFeats[i].m_pFeatures = feats; + } + return true; +} + + +Features* SillMap::cloneFeatures(uint32 langname/*0 means default*/) const +{ + if (langname) + { + // the number of languages in a font is usually small e.g. 8 in Doulos + // so this loop is not very expensive + for (uint16 i = 0; i < m_numLanguages; i++) + { + if (m_langFeats[i].m_lang == langname) + return new Features(*m_langFeats[i].m_pFeatures); + } + } + return new Features (m_FeatureMap.m_defaultFeatures); +} + + + +const FeatureRef *FeatureMap::findFeatureRef(uint32 name) const +{ + NameAndFeatureRef *it; + + for (it = m_pNamedFeats; it < m_pNamedFeats + m_numFeats; ++it) + if (it->m_name == name) + return it->m_pFRef; + return NULL; +} + +bool FeatureRef::applyValToFeature(uint32 val, Features & pDest) const +{ + if (val>maxVal() || !m_face) + return false; + if (pDest.m_pMap==NULL) + pDest.m_pMap = &m_face->theSill().theFeatureMap(); + else + if (pDest.m_pMap!=&m_face->theSill().theFeatureMap()) + return false; //incompatible + if (m_index >= pDest.size()) + pDest.resize(m_index+1); + pDest[m_index] &= ~m_mask; + pDest[m_index] |= (uint32(val) << m_bits); + return true; +} + +uint32 FeatureRef::getFeatureVal(const Features& feats) const +{ + if (m_index < feats.size() && m_face + && &m_face->theSill().theFeatureMap()==feats.m_pMap) + return (feats[m_index] & m_mask) >> m_bits; + else + return 0; +} diff --git a/thirdparty/graphite/src/FileFace.cpp b/thirdparty/graphite/src/FileFace.cpp new file mode 100644 index 00000000000..7e663876a74 --- /dev/null +++ b/thirdparty/graphite/src/FileFace.cpp @@ -0,0 +1,115 @@ +/* GRAPHITE2 LICENSING + + Copyright 2012, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include +#include "inc/FileFace.h" + + +#ifndef GRAPHITE2_NFILEFACE + +using namespace graphite2; + +FileFace::FileFace(const char *filename) +: _file(fopen(filename, "rb")), + _file_len(0), + _header_tbl(NULL), + _table_dir(NULL) +{ + if (!_file) return; + + if (fseek(_file, 0, SEEK_END)) return; + _file_len = ftell(_file); + if (fseek(_file, 0, SEEK_SET)) return; + + size_t tbl_offset, tbl_len; + + // Get the header. + if (!TtfUtil::GetHeaderInfo(tbl_offset, tbl_len)) return; + if (fseek(_file, long(tbl_offset), SEEK_SET)) return; + _header_tbl = (TtfUtil::Sfnt::OffsetSubTable*)gralloc(tbl_len); + if (_header_tbl) + { + if (fread(_header_tbl, 1, tbl_len, _file) != tbl_len) return; + if (!TtfUtil::CheckHeader(_header_tbl)) return; + } + + // Get the table directory + if (!TtfUtil::GetTableDirInfo(_header_tbl, tbl_offset, tbl_len)) return; + _table_dir = (TtfUtil::Sfnt::OffsetSubTable::Entry*)gralloc(tbl_len); + if (fseek(_file, long(tbl_offset), SEEK_SET)) return; + if (_table_dir && fread(_table_dir, 1, tbl_len, _file) != tbl_len) + { + free(_table_dir); + _table_dir = NULL; + } + return; +} + +FileFace::~FileFace() +{ + free(_table_dir); + free(_header_tbl); + if (_file) + fclose(_file); +} + + +const void *FileFace::get_table_fn(const void* appFaceHandle, unsigned int name, size_t *len) +{ + if (appFaceHandle == 0) return 0; + const FileFace & file_face = *static_cast(appFaceHandle); + + void *tbl; + size_t tbl_offset, tbl_len; + if (!TtfUtil::GetTableInfo(name, file_face._header_tbl, file_face._table_dir, tbl_offset, tbl_len)) + return 0; + + if (tbl_offset > file_face._file_len || tbl_len > file_face._file_len - tbl_offset + || fseek(file_face._file, long(tbl_offset), SEEK_SET) != 0) + return 0; + + tbl = malloc(tbl_len); + if (!tbl || fread(tbl, 1, tbl_len, file_face._file) != tbl_len) + { + free(tbl); + return 0; + } + + if (len) *len = tbl_len; + return tbl; +} + +void FileFace::rel_table_fn(const void* appFaceHandle, const void *table_buffer) +{ + if (appFaceHandle == 0) return; + + free(const_cast(table_buffer)); +} + +const gr_face_ops FileFace::ops = { sizeof FileFace::ops, &FileFace::get_table_fn, &FileFace::rel_table_fn }; + + +#endif //!GRAPHITE2_NFILEFACE diff --git a/thirdparty/graphite/src/Font.cpp b/thirdparty/graphite/src/Font.cpp new file mode 100644 index 00000000000..faf3715f9d8 --- /dev/null +++ b/thirdparty/graphite/src/Font.cpp @@ -0,0 +1,58 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/Face.h" +#include "inc/Font.h" +#include "inc/GlyphCache.h" + +using namespace graphite2; + +Font::Font(float ppm, const Face & f, const void * appFontHandle, const gr_font_ops * ops) +: m_appFontHandle(appFontHandle ? appFontHandle : this), + m_face(f), + m_scale(ppm / f.glyphs().unitsPerEm()), + m_hinted(appFontHandle && ops && (ops->glyph_advance_x || ops->glyph_advance_y)) +{ + memset(&m_ops, 0, sizeof m_ops); + if (m_hinted && ops) + memcpy(&m_ops, ops, min(sizeof m_ops, ops->size)); + else + m_ops.glyph_advance_x = &Face::default_glyph_advance; + + size_t nGlyphs = f.glyphs().numGlyphs(); + m_advances = gralloc(nGlyphs); + if (m_advances) + { + for (float *advp = m_advances; nGlyphs; --nGlyphs, ++advp) + *advp = INVALID_ADVANCE; + } +} + + +/*virtual*/ Font::~Font() +{ + free(m_advances); +} diff --git a/thirdparty/graphite/src/GlyphCache.cpp b/thirdparty/graphite/src/GlyphCache.cpp new file mode 100644 index 00000000000..282bdc18fd6 --- /dev/null +++ b/thirdparty/graphite/src/GlyphCache.cpp @@ -0,0 +1,492 @@ +/* GRAPHITE2 LICENSING + + Copyright 2012, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "graphite2/Font.h" + +#include "inc/Main.h" +#include "inc/Face.h" //for the tags +#include "inc/GlyphCache.h" +#include "inc/GlyphFace.h" +#include "inc/Endian.h" +#include "inc/bits.h" + +using namespace graphite2; + +namespace +{ + // Iterator over version 1 or 2 glat entries which consist of a series of + // +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+ + // v1 |k|n|v1 |v2 |...|vN | or v2 | k | n |v1 |v2 |...|vN | + // +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+ + // variable length structures. + + template + class _glat_iterator : public std::iterator > + { + unsigned short key() const { return uint16(be::peek(_e) + _n); } + unsigned int run() const { return be::peek(_e+sizeof(W)); } + void advance_entry() { _n = 0; _e = _v; be::skip(_v,2); } + public: + _glat_iterator(const void * glat=0) : _e(reinterpret_cast(glat)), _v(_e+2*sizeof(W)), _n(0) {} + + _glat_iterator & operator ++ () { + ++_n; be::skip(_v); + if (_n == run()) advance_entry(); + return *this; + } + _glat_iterator operator ++ (int) { _glat_iterator tmp(*this); operator++(); return tmp; } + + // This is strictly a >= operator. A true == operator could be + // implemented that test for overlap but it would be more expensive a + // test. + bool operator == (const _glat_iterator & rhs) { return _v >= rhs._e - 1; } + bool operator != (const _glat_iterator & rhs) { return !operator==(rhs); } + + value_type operator * () const { + return value_type(key(), be::peek(_v)); + } + + protected: + const byte * _e, * _v; + size_t _n; + }; + + typedef _glat_iterator glat_iterator; + typedef _glat_iterator glat2_iterator; +} + +const SlantBox SlantBox::empty = {0,0,0,0}; + + +class GlyphCache::Loader +{ +public: + Loader(const Face & face); //return result indicates success. Do not use if failed. + + operator bool () const throw(); + unsigned short int units_per_em() const throw(); + unsigned short int num_glyphs() const throw(); + unsigned short int num_attrs() const throw(); + bool has_boxes() const throw(); + + const GlyphFace * read_glyph(unsigned short gid, GlyphFace &, int *numsubs) const throw(); + GlyphBox * read_box(uint16 gid, GlyphBox *curr, const GlyphFace & face) const throw(); + + CLASS_NEW_DELETE; +private: + Face::Table _head, + _hhea, + _hmtx, + _glyf, + _loca, + m_pGlat, + m_pGloc; + + bool _long_fmt; + bool _has_boxes; + unsigned short _num_glyphs_graphics, //i.e. boundary box and advance + _num_glyphs_attributes, + _num_attrs; // number of glyph attributes per glyph +}; + + + +GlyphCache::GlyphCache(const Face & face, const uint32 face_options) +: _glyph_loader(new Loader(face)), + _glyphs(_glyph_loader && *_glyph_loader && _glyph_loader->num_glyphs() + ? grzeroalloc(_glyph_loader->num_glyphs()) : 0), + _boxes(_glyph_loader && _glyph_loader->has_boxes() && _glyph_loader->num_glyphs() + ? grzeroalloc(_glyph_loader->num_glyphs()) : 0), + _num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0), + _num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0), + _upem(_glyphs ? _glyph_loader->units_per_em() : 0) +{ + if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs) + { + int numsubs = 0; + GlyphFace * const glyphs = new GlyphFace [_num_glyphs]; + if (!glyphs) + return; + + // The 0 glyph is definately required. + _glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0], &numsubs); + + // glyphs[0] has the same address as the glyphs array just allocated, + // thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points + // to the entire array. + const GlyphFace * loaded = _glyphs[0]; + for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid) + _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid], &numsubs); + + if (!loaded) + { + _glyphs[0] = 0; + delete [] glyphs; + } + else if (numsubs > 0 && _boxes) + { + GlyphBox * boxes = (GlyphBox *)gralloc(_num_glyphs * sizeof(GlyphBox) + numsubs * 8 * sizeof(float)); + GlyphBox * currbox = boxes; + + for (uint16 gid = 0; currbox && gid != _num_glyphs; ++gid) + { + _boxes[gid] = currbox; + currbox = _glyph_loader->read_box(gid, currbox, *_glyphs[gid]); + } + if (!currbox) + { + free(boxes); + _boxes[0] = 0; + } + } + delete _glyph_loader; + _glyph_loader = 0; + // coverity[leaked_storage : FALSE] - calling read_glyph on index 0 saved + // glyphs as _glyphs[0]. Setting _glyph_loader to nullptr here flags that + // the dtor needs to call delete[] on _glyphs[0] to release what was allocated + // as glyphs + } + + if (_glyphs && glyph(0) == 0) + { + free(_glyphs); + _glyphs = 0; + if (_boxes) + { + free(_boxes); + _boxes = 0; + } + _num_glyphs = _num_attrs = _upem = 0; + } +} + + +GlyphCache::~GlyphCache() +{ + if (_glyphs) + { + if (_glyph_loader) + { + const GlyphFace * * g = _glyphs; + for(unsigned short n = _num_glyphs; n; --n, ++g) + delete *g; + } + else + delete [] _glyphs[0]; + free(_glyphs); + } + if (_boxes) + { + if (_glyph_loader) + { + GlyphBox * * g = _boxes; + for (uint16 n = _num_glyphs; n; --n, ++g) + free(*g); + } + else + free(_boxes[0]); + free(_boxes); + } + delete _glyph_loader; +} + +const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const //result may be changed by subsequent call with a different glyphid +{ + if (glyphid >= numGlyphs()) + return _glyphs[0]; + const GlyphFace * & p = _glyphs[glyphid]; + if (p == 0 && _glyph_loader) + { + int numsubs = 0; + GlyphFace * g = new GlyphFace(); + if (g) p = _glyph_loader->read_glyph(glyphid, *g, &numsubs); + if (!p) + { + delete g; + return *_glyphs; + } + if (_boxes) + { + _boxes[glyphid] = (GlyphBox *)gralloc(sizeof(GlyphBox) + 8 * numsubs * sizeof(float)); + if (!_glyph_loader->read_box(glyphid, _boxes[glyphid], *_glyphs[glyphid])) + { + free(_boxes[glyphid]); + _boxes[glyphid] = 0; + } + } + } + return p; +} + + + +GlyphCache::Loader::Loader(const Face & face) +: _head(face, Tag::head), + _hhea(face, Tag::hhea), + _hmtx(face, Tag::hmtx), + _glyf(face, Tag::glyf), + _loca(face, Tag::loca), + _long_fmt(false), + _has_boxes(false), + _num_glyphs_graphics(0), + _num_glyphs_attributes(0), + _num_attrs(0) +{ + if (!operator bool()) + return; + + const Face::Table maxp = Face::Table(face, Tag::maxp); + if (!maxp) { _head = Face::Table(); return; } + + _num_glyphs_graphics = static_cast(TtfUtil::GlyphCount(maxp)); + // This will fail if the number of glyphs is wildly out of range. + if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-2)) + { + _head = Face::Table(); + return; + } + + if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL + || (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL + || m_pGloc.size() < 8) + { + _head = Face::Table(); + return; + } + const byte * p = m_pGloc; + int version = be::read(p); + const uint16 flags = be::read(p); + _num_attrs = be::read(p); + // We can accurately calculate the number of attributed glyphs by + // subtracting the length of the attribids array (numAttribs long if present) + // and dividing by either 2 or 4 depending on shor or lonf format + _long_fmt = flags & 1; + ptrdiff_t tmpnumgattrs = (m_pGloc.size() + - (p - m_pGloc) + - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0)) + / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1; + + if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535 + || _num_attrs == 0 || _num_attrs > 0x3000 // is this hard limit appropriate? + || _num_glyphs_graphics > tmpnumgattrs + || m_pGlat.size() < 4) + { + _head = Face::Table(); + return; + } + + _num_glyphs_attributes = static_cast(tmpnumgattrs); + p = m_pGlat; + version = be::read(p); + if (version >= 0x00040000 || (version >= 0x00030000 && m_pGlat.size() < 8)) // reject Glat tables that are too new + { + _head = Face::Table(); + return; + } + else if (version >= 0x00030000) + { + unsigned int glatflags = be::read(p); + _has_boxes = glatflags & 1; + // delete this once the compiler is fixed + _has_boxes = true; + } +} + +inline +GlyphCache::Loader::operator bool () const throw() +{ + return _head && _hhea && _hmtx && !(bool(_glyf) != bool(_loca)); +} + +inline +unsigned short int GlyphCache::Loader::units_per_em() const throw() +{ + return _head ? TtfUtil::DesignUnits(_head) : 0; +} + +inline +unsigned short int GlyphCache::Loader::num_glyphs() const throw() +{ + return max(_num_glyphs_graphics, _num_glyphs_attributes); +} + +inline +unsigned short int GlyphCache::Loader::num_attrs() const throw() +{ + return _num_attrs; +} + +inline +bool GlyphCache::Loader::has_boxes () const throw() +{ + return _has_boxes; +} + +const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph, int *numsubs) const throw() +{ + Rect bbox; + Position advance; + + if (glyphid < _num_glyphs_graphics) + { + int nLsb; + unsigned int nAdvWid; + if (_glyf) + { + int xMin, yMin, xMax, yMax; + size_t locidx = TtfUtil::LocaLookup(glyphid, _loca, _loca.size(), _head); + void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size()); + + if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax)) + { + if ((xMin > xMax) || (yMin > yMax)) + return 0; + bbox = Rect(Position(static_cast(xMin), static_cast(yMin)), + Position(static_cast(xMax), static_cast(yMax))); + } + } + if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid)) + advance = Position(static_cast(nAdvWid), 0); + } + + if (glyphid < _num_glyphs_attributes) + { + const byte * gloc = m_pGloc; + size_t glocs = 0, gloce = 0; + + be::skip(gloc); + be::skip(gloc,2); + if (_long_fmt) + { + if (8 + glyphid * sizeof(uint32) > m_pGloc.size()) + return 0; + be::skip(gloc, glyphid); + glocs = be::read(gloc); + gloce = be::peek(gloc); + } + else + { + if (8 + glyphid * sizeof(uint16) > m_pGloc.size()) + return 0; + be::skip(gloc, glyphid); + glocs = be::read(gloc); + gloce = be::peek(gloc); + } + + if (glocs >= m_pGlat.size() - 1 || gloce > m_pGlat.size()) + return 0; + + const uint32 glat_version = be::peek(m_pGlat); + if (glat_version >= 0x00030000) + { + if (glocs >= gloce) + return 0; + const byte * p = m_pGlat + glocs; + uint16 bmap = be::read(p); + int num = bit_set_count((uint32)bmap); + if (numsubs) *numsubs += num; + glocs += 6 + 8 * num; + if (glocs > gloce) + return 0; + } + if (glat_version < 0x00020000) + { + if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16) + || gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16))) + return 0; + new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce)); + } + else + { + if (gloce - glocs < 3*sizeof(uint16) // can a glyph have no attributes? why not? + || gloce - glocs > _num_attrs*3*sizeof(uint16) + || glocs > m_pGlat.size() - 2*sizeof(uint16)) + return 0; + new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce)); + } + if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs) + return 0; + } + return &glyph; +} + +inline float scale_to(uint8 t, float zmin, float zmax) +{ + return (zmin + t * (zmax - zmin) / 255); +} + +Rect readbox(Rect &b, uint8 zxmin, uint8 zymin, uint8 zxmax, uint8 zymax) +{ + return Rect(Position(scale_to(zxmin, b.bl.x, b.tr.x), scale_to(zymin, b.bl.y, b.tr.y)), + Position(scale_to(zxmax, b.bl.x, b.tr.x), scale_to(zymax, b.bl.y, b.tr.y))); +} + +GlyphBox * GlyphCache::Loader::read_box(uint16 gid, GlyphBox *curr, const GlyphFace & glyph) const throw() +{ + if (gid >= _num_glyphs_attributes) return 0; + + const byte * gloc = m_pGloc; + size_t glocs = 0, gloce = 0; + + be::skip(gloc); + be::skip(gloc,2); + if (_long_fmt) + { + be::skip(gloc, gid); + glocs = be::read(gloc); + gloce = be::peek(gloc); + } + else + { + be::skip(gloc, gid); + glocs = be::read(gloc); + gloce = be::peek(gloc); + } + + if (gloce > m_pGlat.size() || glocs + 6 >= gloce) + return 0; + + const byte * p = m_pGlat + glocs; + uint16 bmap = be::read(p); + int num = bit_set_count((uint32)bmap); + + Rect bbox = glyph.theBBox(); + Rect diamax(Position(bbox.bl.x + bbox.bl.y, bbox.bl.x - bbox.tr.y), + Position(bbox.tr.x + bbox.tr.y, bbox.tr.x - bbox.bl.y)); + Rect diabound = readbox(diamax, p[0], p[2], p[1], p[3]); + ::new (curr) GlyphBox(num, bmap, &diabound); + be::skip(p, 4); + if (glocs + 6 + num * 8 >= gloce) + return 0; + + for (int i = 0; i < num * 2; ++i) + { + Rect box = readbox((i & 1) ? diamax : bbox, p[0], p[2], p[1], p[3]); + curr->addSubBox(i >> 1, i & 1, &box); + be::skip(p, 4); + } + return (GlyphBox *)((char *)(curr) + sizeof(GlyphBox) + 2 * num * sizeof(Rect)); +} diff --git a/thirdparty/graphite/src/GlyphFace.cpp b/thirdparty/graphite/src/GlyphFace.cpp new file mode 100644 index 00000000000..bc5e63a9f0c --- /dev/null +++ b/thirdparty/graphite/src/GlyphFace.cpp @@ -0,0 +1,48 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/GlyphFace.h" + + +using namespace graphite2; + +int32 GlyphFace::getMetric(uint8 metric) const +{ + switch (metrics(metric)) + { + case kgmetLsb : return int32(m_bbox.bl.x); + case kgmetRsb : return int32(m_advance.x - m_bbox.tr.x); + case kgmetBbTop : return int32(m_bbox.tr.y); + case kgmetBbBottom : return int32(m_bbox.bl.y); + case kgmetBbLeft : return int32(m_bbox.bl.x); + case kgmetBbRight : return int32(m_bbox.tr.x); + case kgmetBbHeight : return int32(m_bbox.tr.y - m_bbox.bl.y); + case kgmetBbWidth : return int32(m_bbox.tr.x - m_bbox.bl.x); + case kgmetAdvWidth : return int32(m_advance.x); + case kgmetAdvHeight : return int32(m_advance.y); + default : return 0; + } +} diff --git a/thirdparty/graphite/src/Intervals.cpp b/thirdparty/graphite/src/Intervals.cpp new file mode 100644 index 00000000000..0fe99a127a8 --- /dev/null +++ b/thirdparty/graphite/src/Intervals.cpp @@ -0,0 +1,298 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include +#include +#include + +#include "inc/Intervals.h" +#include "inc/Segment.h" +#include "inc/Slot.h" +#include "inc/debug.h" +#include "inc/bits.h" + +using namespace graphite2; + +#include + +inline +Zones::Exclusion Zones::Exclusion::split_at(float p) { + Exclusion r(*this); + r.xm = x = p; + return r; +} + +inline +void Zones::Exclusion::left_trim(float p) { + x = p; +} + +inline +Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) { + c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false; + return *this; +} + +inline +uint8 Zones::Exclusion::outcode(float val) const { + float p = val; + //float d = std::numeric_limits::epsilon(); + float d = 0.; + return ((p - xm >= d) << 1) | (x - p > d); +} + +void Zones::exclude_with_margins(float xmin, float xmax, int axis) { + remove(xmin, xmax); + weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false); + weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false); +} + +namespace +{ + +inline +bool separated(float a, float b) { + return a != b; + //int exp; + //float res = frexpf(fabs(a - b), &exp); + //return (*(unsigned int *)(&res) > 4); + //return std::fabs(a-b) > std::numeric_limits::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests + //return std::fabs(a-b) > 0.5f; +} + +} + +void Zones::insert(Exclusion e) +{ +#if !defined GRAPHITE2_NTRACING + addDebug(&e); +#endif + e.x = max(e.x, _pos); + e.xm = min(e.xm, _posm); + if (e.x >= e.xm) return; + + for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i) + { + const uint8 oca = e.outcode(i->x), + ocb = e.outcode(i->xm); + if ((oca & ocb) != 0) continue; + + switch (oca ^ ocb) // What kind of overlap? + { + case 0: // e completely covers i + // split e at i.x into e1,e2 + // split e2 at i.mx into e2,e3 + // drop e1 ,i+e2, e=e3 + *i += e; + e.left_trim(i->xm); + break; + case 1: // e overlaps on the rhs of i + // split i at e->x into i1,i2 + // split e at i.mx into e1,e2 + // trim i1, insert i2+e1, e=e2 + if (!separated(i->xm, e.x)) break; + if (separated(i->x,e.x)) { i = _exclusions.insert(i,i->split_at(e.x)); ++i; } + *i += e; + e.left_trim(i->xm); + break; + case 2: // e overlaps on the lhs of i + // split e at i->x into e1,e2 + // split i at e.mx into i1,i2 + // drop e1, insert e2+i1, trim i2 + if (!separated(e.xm, i->x)) return; + if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm)); + *i += e; + return; + case 3: // i completely covers e + // split i at e.x into i1,i2 + // split i2 at e.mx into i2,i3 + // insert i1, insert e+i2 + if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm)); + i = _exclusions.insert(i, i->split_at(e.x)); + *++i += e; + return; + } + + ie = _exclusions.end(); + } +} + + +void Zones::remove(float x, float xm) +{ +#if !defined GRAPHITE2_NTRACING + removeDebug(x, xm); +#endif + x = max(x, _pos); + xm = min(xm, _posm); + if (x >= xm) return; + + for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i) + { + const uint8 oca = i->outcode(x), + ocb = i->outcode(xm); + if ((oca & ocb) != 0) continue; + + switch (oca ^ ocb) // What kind of overlap? + { + case 0: // i completely covers e + if (separated(i->x, x)) { i = _exclusions.insert(i,i->split_at(x)); ++i; } + GR_FALLTHROUGH; + // no break + case 1: // i overlaps on the rhs of e + i->left_trim(xm); + return; + case 2: // i overlaps on the lhs of e + i->xm = x; + if (separated(i->x, i->xm)) break; + GR_FALLTHROUGH; + // no break + case 3: // e completely covers i + i = _exclusions.erase(i); + --i; + break; + } + + ie = _exclusions.end(); + } +} + + +Zones::const_iterator Zones::find_exclusion_under(float x) const +{ + size_t l = 0, h = _exclusions.size(); + + while (l < h) + { + size_t const p = (l+h) >> 1; + switch (_exclusions[p].outcode(x)) + { + case 0 : return _exclusions.begin()+p; + case 1 : h = p; break; + case 2 : + case 3 : l = p+1; break; + } + } + + return _exclusions.begin()+l; +} + + +float Zones::closest(float origin, float & cost) const +{ + float best_c = std::numeric_limits::max(), + best_x = 0; + + const const_iterator start = find_exclusion_under(origin); + + // Forward scan looking for lowest cost + for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i) + if (i->track_cost(best_c, best_x, origin)) break; + + // Backward scan looking for lowest cost + // We start from the exclusion to the immediate left of start since we've + // already tested start with the right most scan above. + for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i) + if (i->track_cost(best_c, best_x, origin)) break; + + cost = (best_c == std::numeric_limits::max() ? -1 : best_c); + return best_x; +} + + +// Cost and test position functions + +bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const { + const float p = test_position(origin), + localc = cost(p - origin); + if (open && localc > best_cost) return true; + + if (localc < best_cost) + { + best_cost = localc; + best_pos = p; + } + return false; +} + +inline +float Zones::Exclusion::cost(float p) const { + return (sm * p - 2 * smx) * p + c; +} + + +float Zones::Exclusion::test_position(float origin) const { + if (sm < 0) + { + // sigh, test both ends and perhaps the middle too! + float res = x; + float cl = cost(x); + if (x < origin && xm > origin) + { + float co = cost(origin); + if (co < cl) + { + cl = co; + res = origin; + } + } + float cr = cost(xm); + return cl > cr ? xm : res; + } + else + { + float zerox = smx / sm + origin; + if (zerox < x) return x; + else if (zerox > xm) return xm; + else return zerox; + } +} + + +#if !defined GRAPHITE2_NTRACING + +void Zones::jsonDbgOut(Segment *seg) const { + + if (_dbg) + { + for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s) + { + *_dbg << json::flat << json::array + << objectid(dslot(seg, (Slot *)(s->_env[0]))) + << reinterpret_cast(s->_env[1]); + if (s->_isdel) + *_dbg << "remove" << Position(s->_excl.x, s->_excl.xm); + else + *_dbg << "exclude" << json::flat << json::array + << s->_excl.x << s->_excl.xm + << s->_excl.sm << s->_excl.smx << s->_excl.c + << json::close; + *_dbg << json::close; + } + } +} + +#endif diff --git a/thirdparty/graphite/src/Justifier.cpp b/thirdparty/graphite/src/Justifier.cpp new file mode 100644 index 00000000000..78c11e6a516 --- /dev/null +++ b/thirdparty/graphite/src/Justifier.cpp @@ -0,0 +1,282 @@ +/* GRAPHITE2 LICENSING + + Copyright 2012, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ + +#include "inc/Segment.h" +#include "graphite2/Font.h" +#include "inc/debug.h" +#include "inc/CharInfo.h" +#include "inc/Slot.h" +#include "inc/Main.h" +#include + +using namespace graphite2; + +class JustifyTotal { +public: + JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {} + void accumulate(Slot *s, Segment *seg, int level); + int weight() const { return m_tWeight; } + + CLASS_NEW_DELETE + +private: + int m_numGlyphs; + int m_tStretch; + int m_tShrink; + int m_tStep; + int m_tWeight; +}; + +void JustifyTotal::accumulate(Slot *s, Segment *seg, int level) +{ + ++m_numGlyphs; + m_tStretch += s->getJustify(seg, level, 0); + m_tShrink += s->getJustify(seg, level, 1); + m_tStep += s->getJustify(seg, level, 2); + m_tWeight += s->getJustify(seg, level, 3); +} + +float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast) +{ + Slot *end = last(); + float currWidth = 0.0; + const float scale = font ? font->scale() : 1.0f; + Position res; + + if (width < 0 && !(silf()->flags())) + return width; + + if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses()) + { + reverseSlots(); + std::swap(pFirst, pLast); + } + if (!pFirst) pFirst = pSlot; + while (!pFirst->isBase()) pFirst = pFirst->attachedTo(); + if (!pLast) pLast = last(); + while (!pLast->isBase()) pLast = pLast->attachedTo(); + const float base = pFirst->origin().x / scale; + width = width / scale; + if ((jflags & gr_justEndInline) == 0) + { + while (pLast != pFirst && pLast) + { + Rect bbox = theGlyphBBoxTemporary(pLast->glyph()); + if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f) + break; + pLast = pLast->prev(); + } + } + + if (pLast) + end = pLast->nextSibling(); + if (pFirst) + pFirst = pFirst->nextSibling(); + + int icount = 0; + int numLevels = silf()->numJustLevels(); + if (!numLevels) + { + for (Slot *s = pSlot; s && s != end; s = s->nextSibling()) + { + CharInfo *c = charinfo(s->before()); + if (isWhitespace(c->unicodeChar())) + { + s->setJustify(this, 0, 3, 1); + s->setJustify(this, 0, 2, 1); + s->setJustify(this, 0, 0, -1); + ++icount; + } + } + if (!icount) + { + for (Slot *s = pSlot; s && s != end; s = s->nextSibling()) + { + s->setJustify(this, 0, 3, 1); + s->setJustify(this, 0, 2, 1); + s->setJustify(this, 0, 0, -1); + } + } + ++numLevels; + } + + Vector stats(numLevels); + for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) + { + float w = s->origin().x / scale + s->advance() - base; + if (w > currWidth) currWidth = w; + for (int j = 0; j < numLevels; ++j) + stats[j].accumulate(s, this, j); + s->just(0); + } + + for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i) + { + float diff; + float error = 0.; + float diffpw; + int tWeight = stats[i].weight(); + if (tWeight == 0) continue; + + do { + error = 0.; + diff = width - currWidth; + diffpw = diff / tWeight; + tWeight = 0; + for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) // don't include final glyph + { + int w = s->getJustify(this, i, 3); + float pref = diffpw * w + error; + int step = s->getJustify(this, i, 2); + if (!step) step = 1; // handle lazy font developers + if (pref > 0) + { + float max = uint16(s->getJustify(this, i, 0)); + if (i == 0) max -= s->just(); + if (pref > max) pref = max; + else tWeight += w; + } + else + { + float max = uint16(s->getJustify(this, i, 1)); + if (i == 0) max += s->just(); + if (-pref > max) pref = -max; + else tWeight += w; + } + int actual = int(pref / step) * step; + + if (actual) + { + error += diffpw * w - actual; + if (i == 0) + s->just(s->just() + actual); + else + s->setJustify(this, i, 4, actual); + } + } + currWidth += diff - error; + } while (i == 0 && int(std::abs(error)) > 0 && tWeight); + } + + Slot *oldFirst = m_first; + Slot *oldLast = m_last; + if (silf()->flags() & 1) + { + m_first = pSlot = addLineEnd(pSlot); + m_last = pLast = addLineEnd(end); + if (!m_first || !m_last) return -1.0; + } + else + { + m_first = pSlot; + m_last = pLast; + } + + // run justification passes here +#if !defined GRAPHITE2_NTRACING + json * const dbgout = m_face->logger(); + if (dbgout) + *dbgout << json::object + << "justifies" << objectid(this) + << "passes" << json::array; +#endif + + if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1))) + m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass()); + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + { + *dbgout << json::item << json::close; // Close up the passes array + positionSlots(NULL, pSlot, pLast, m_dir); + Slot *lEnd = pLast->nextSibling(); + *dbgout << "output" << json::array; + for(Slot * t = pSlot; t != lEnd; t = t->next()) + *dbgout << dslot(this, t); + *dbgout << json::close << json::close; + } +#endif + + res = positionSlots(font, pSlot, pLast, m_dir); + + if (silf()->flags() & 1) + { + if (m_first) + delLineEnd(m_first); + if (m_last) + delLineEnd(m_last); + } + m_first = oldFirst; + m_last = oldLast; + + if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses()) + reverseSlots(); + return res.x; +} + +Slot *Segment::addLineEnd(Slot *nSlot) +{ + Slot *eSlot = newSlot(); + if (!eSlot) return NULL; + const uint16 gid = silf()->endLineGlyphid(); + const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid); + eSlot->setGlyph(this, gid, theGlyph); + if (nSlot) + { + eSlot->next(nSlot); + eSlot->prev(nSlot->prev()); + nSlot->prev(eSlot); + eSlot->before(nSlot->before()); + if (eSlot->prev()) + eSlot->after(eSlot->prev()->after()); + else + eSlot->after(nSlot->before()); + } + else + { + nSlot = m_last; + eSlot->prev(nSlot); + nSlot->next(eSlot); + eSlot->after(eSlot->prev()->after()); + eSlot->before(nSlot->after()); + } + return eSlot; +} + +void Segment::delLineEnd(Slot *s) +{ + Slot *nSlot = s->next(); + if (nSlot) + { + nSlot->prev(s->prev()); + if (s->prev()) + s->prev()->next(nSlot); + } + else + s->prev()->next(NULL); + freeSlot(s); +} diff --git a/thirdparty/graphite/src/NameTable.cpp b/thirdparty/graphite/src/NameTable.cpp new file mode 100644 index 00000000000..d42b7f95bdc --- /dev/null +++ b/thirdparty/graphite/src/NameTable.cpp @@ -0,0 +1,254 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/Main.h" +#include "inc/Endian.h" + +#include "inc/NameTable.h" +#include "inc/UtfCodec.h" + +using namespace graphite2; + +NameTable::NameTable(const void* data, size_t length, uint16 platformId, uint16 encodingID) + : m_platformId(0), m_encodingId(0), m_languageCount(0), + m_platformOffset(0), m_platformLastRecord(0), m_nameDataLength(0), + m_table(0), m_nameData(NULL) +{ + void *pdata = gralloc(length); + if (!pdata) return; + memcpy(pdata, data, length); + m_table = reinterpret_cast(pdata); + + if ((length > sizeof(TtfUtil::Sfnt::FontNames)) && + (length > sizeof(TtfUtil::Sfnt::FontNames) + + sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap(m_table->count) - 1))) + { + uint16 offset = be::swap(m_table->string_offset); + if (offset < length) + { + m_nameData = reinterpret_cast(pdata) + offset; + setPlatformEncoding(platformId, encodingID); + m_nameDataLength = uint16(length - offset); + return; + } + } + free(const_cast(m_table)); + m_table = NULL; +} + +uint16 NameTable::setPlatformEncoding(uint16 platformId, uint16 encodingID) +{ + if (!m_nameData) return 0; + uint16 i = 0; + uint16 count = be::swap(m_table->count); + for (; i < count; i++) + { + if (be::swap(m_table->name_record[i].platform_id) == platformId && + be::swap(m_table->name_record[i].platform_specific_id) == encodingID) + { + m_platformOffset = i; + break; + } + } + while ((++i < count) && + (be::swap(m_table->name_record[i].platform_id) == platformId) && + (be::swap(m_table->name_record[i].platform_specific_id) == encodingID)) + { + m_platformLastRecord = i; + } + m_encodingId = encodingID; + m_platformId = platformId; + return 0; +} + +void* NameTable::getName(uint16& languageId, uint16 nameId, gr_encform enc, uint32& length) +{ + uint16 anyLang = 0; + uint16 enUSLang = 0; + uint16 bestLang = 0; + if (!m_table) + { + languageId = 0; + length = 0; + return NULL; + } + for (uint16 i = m_platformOffset; i <= m_platformLastRecord; i++) + { + if (be::swap(m_table->name_record[i].name_id) == nameId) + { + uint16 langId = be::swap(m_table->name_record[i].language_id); + if (langId == languageId) + { + bestLang = i; + break; + } + // MS language tags have the language in the lower byte, region in the higher + else if ((langId & 0xFF) == (languageId & 0xFF)) + { + bestLang = i; + } + else if (langId == 0x409) + { + enUSLang = i; + } + else + { + anyLang = i; + } + } + } + if (!bestLang) + { + if (enUSLang) bestLang = enUSLang; + else + { + bestLang = anyLang; + if (!anyLang) + { + languageId = 0; + length = 0; + return NULL; + } + } + } + const TtfUtil::Sfnt::NameRecord & nameRecord = m_table->name_record[bestLang]; + languageId = be::swap(nameRecord.language_id); + uint16 utf16Length = be::swap(nameRecord.length); + uint16 offset = be::swap(nameRecord.offset); + if(offset + utf16Length > m_nameDataLength) + { + languageId = 0; + length = 0; + return NULL; + } + utf16Length >>= 1; // in utf16 units + utf16::codeunit_t * utf16Name = gralloc(utf16Length + 1); + if (!utf16Name) + { + languageId = 0; + length = 0; + return NULL; + } + const uint8* pName = m_nameData + offset; + for (size_t i = 0; i < utf16Length; i++) + { + utf16Name[i] = be::read(pName); + } + utf16Name[utf16Length] = 0; + if (!utf16::validate(utf16Name, utf16Name + utf16Length)) + { + free(utf16Name); + languageId = 0; + length = 0; + return NULL; + } + switch (enc) + { + case gr_utf8: + { + utf8::codeunit_t* uniBuffer = gralloc(3 * utf16Length + 1); + if (!uniBuffer) + { + free(utf16Name); + languageId = 0; + length = 0; + return NULL; + } + utf8::iterator d = uniBuffer; + for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d) + *d = *s; + length = uint32(d - uniBuffer); + uniBuffer[length] = 0; + free(utf16Name); + return uniBuffer; + } + case gr_utf16: + length = utf16Length; + return utf16Name; + case gr_utf32: + { + utf32::codeunit_t * uniBuffer = gralloc(utf16Length + 1); + if (!uniBuffer) + { + free(utf16Name); + languageId = 0; + length = 0; + return NULL; + } + utf32::iterator d = uniBuffer; + for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d) + *d = *s; + length = uint32(d - uniBuffer); + uniBuffer[length] = 0; + free(utf16Name); + return uniBuffer; + } + } + free(utf16Name); + languageId = 0; + length = 0; + return NULL; +} + +uint16 NameTable::getLanguageId(const char * bcp47Locale) +{ + size_t localeLength = strlen(bcp47Locale); + uint16 localeId = m_locale2Lang.getMsId(bcp47Locale); + if (m_table && (be::swap(m_table->format) == 1)) + { + const uint8 * pLangEntries = reinterpret_cast(m_table) + + sizeof(TtfUtil::Sfnt::FontNames) + + sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap(m_table->count) - 1); + uint16 numLangEntries = be::read(pLangEntries); + const TtfUtil::Sfnt::LangTagRecord * langTag = + reinterpret_cast(pLangEntries); + if (pLangEntries + numLangEntries * sizeof(TtfUtil::Sfnt::LangTagRecord) <= m_nameData) + { + for (uint16 i = 0; i < numLangEntries; i++) + { + uint16 offset = be::swap(langTag[i].offset); + uint16 length = be::swap(langTag[i].length); + if ((offset + length <= m_nameDataLength) && (length == 2 * localeLength)) + { + const uint8* pName = m_nameData + offset; + bool match = true; + for (size_t j = 0; j < localeLength; j++) + { + uint16 code = be::read(pName); + if ((code > 0x7F) || (code != bcp47Locale[j])) + { + match = false; + break; + } + } + if (match) + return 0x8000 + i; + } + } + } + } + return localeId; +} diff --git a/thirdparty/graphite/src/Pass.cpp b/thirdparty/graphite/src/Pass.cpp new file mode 100644 index 00000000000..db31c22d46b --- /dev/null +++ b/thirdparty/graphite/src/Pass.cpp @@ -0,0 +1,1107 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/Main.h" +#include "inc/debug.h" +#include "inc/Endian.h" +#include "inc/Pass.h" +#include +#include +#include +#include +#include "inc/Segment.h" +#include "inc/Code.h" +#include "inc/Rule.h" +#include "inc/Error.h" +#include "inc/Collider.h" + +using namespace graphite2; +using vm::Machine; +typedef Machine::Code Code; + +enum KernCollison +{ + None = 0, + CrossSpace = 1, + InWord = 2, + reserved = 3 +}; + +Pass::Pass() +: m_silf(0), + m_cols(0), + m_rules(0), + m_ruleMap(0), + m_startStates(0), + m_transitions(0), + m_states(0), + m_codes(0), + m_progs(0), + m_numCollRuns(0), + m_kernColls(0), + m_iMaxLoop(0), + m_numGlyphs(0), + m_numRules(0), + m_numStates(0), + m_numTransition(0), + m_numSuccess(0), + m_successStart(0), + m_numColumns(0), + m_minPreCtxt(0), + m_maxPreCtxt(0), + m_colThreshold(0), + m_isReverseDir(false) +{ +} + +Pass::~Pass() +{ + free(m_cols); + free(m_startStates); + free(m_transitions); + free(m_states); + free(m_ruleMap); + + if (m_rules) delete [] m_rules; + if (m_codes) delete [] m_codes; + free(m_progs); +} + +bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, + GR_MAYBE_UNUSED Face & face, passtype pt, GR_MAYBE_UNUSED uint32 version, Error &e) +{ + const byte * p = pass_start, + * const pass_end = p + pass_length; + size_t numRanges; + + if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e); + // Read in basic values + const byte flags = be::read(p); + if (e.test((flags & 0x1f) && + (pt < PASS_TYPE_POSITIONING || !m_silf->aCollision() || !face.glyphs().hasBoxes() || !(m_silf->flags() & 0x20)), + E_BADCOLLISIONPASS)) + return face.error(e); + m_numCollRuns = flags & 0x7; + m_kernColls = (flags >> 3) & 0x3; + m_isReverseDir = (flags >> 5) & 0x1; + m_iMaxLoop = be::read(p); + if (m_iMaxLoop < 1) m_iMaxLoop = 1; + be::skip(p,2); // skip maxContext & maxBackup + m_numRules = be::read(p); + if (e.test(!m_numRules && m_numCollRuns == 0, E_BADEMPTYPASS)) return face.error(e); + be::skip(p); // fsmOffset - not sure why we would want this + const byte * const pcCode = pass_start + be::read(p) - subtable_base, + * const rcCode = pass_start + be::read(p) - subtable_base, + * const aCode = pass_start + be::read(p) - subtable_base; + be::skip(p); + m_numStates = be::read(p); + m_numTransition = be::read(p); + m_numSuccess = be::read(p); + m_numColumns = be::read(p); + numRanges = be::read(p); + be::skip(p, 3); // skip searchRange, entrySelector & rangeShift. + assert(p - pass_start == 40); + // Perform some sanity checks. + if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS) + || e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS) + || e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES) + || e.test(m_numRules && numRanges == 0, E_NORANGES) + || e.test(m_numColumns > 0x7FFF, E_BADNUMCOLUMNS)) + return face.error(e); + + m_successStart = m_numStates - m_numSuccess; + // test for beyond end - 1 to account for reading uint16 + if (e.test(p + numRanges * 6 - 2 > pass_end, E_BADPASSLENGTH)) return face.error(e); + m_numGlyphs = be::peek(p + numRanges * 6 - 4) + 1; + // Calculate the start of various arrays. + const byte * const ranges = p; + be::skip(p, numRanges*3); + const byte * const o_rule_map = p; + be::skip(p, m_numSuccess + 1); + + // More sanity checks + if (e.test(reinterpret_cast(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end + || p > pass_end, E_BADRULEMAPLEN)) + return face.error(e); + const size_t numEntries = be::peek(o_rule_map + m_numSuccess*sizeof(uint16)); + const byte * const rule_map = p; + be::skip(p, numEntries); + + if (e.test(p + 2*sizeof(uint8) > pass_end, E_BADPASSLENGTH)) return face.error(e); + m_minPreCtxt = be::read(p); + m_maxPreCtxt = be::read(p); + if (e.test(m_minPreCtxt > m_maxPreCtxt, E_BADCTXTLENBOUNDS)) return face.error(e); + const byte * const start_states = p; + be::skip(p, m_maxPreCtxt - m_minPreCtxt + 1); + const uint16 * const sort_keys = reinterpret_cast(p); + be::skip(p, m_numRules); + const byte * const precontext = p; + be::skip(p, m_numRules); + + if (e.test(p + sizeof(uint16) + sizeof(uint8) > pass_end, E_BADCTXTLENS)) return face.error(e); + m_colThreshold = be::read(p); + if (m_colThreshold == 0) m_colThreshold = 10; // A default + const size_t pass_constraint_len = be::read(p); + + const uint16 * const o_constraint = reinterpret_cast(p); + be::skip(p, m_numRules + 1); + const uint16 * const o_actions = reinterpret_cast(p); + be::skip(p, m_numRules + 1); + const byte * const states = p; + if (e.test(2u*m_numTransition*m_numColumns >= (unsigned)(pass_end - p), E_BADPASSLENGTH) + || e.test(p >= pass_end, E_BADPASSLENGTH)) + return face.error(e); + be::skip(p, m_numTransition*m_numColumns); + be::skip(p); + if (e.test(p != pcCode, E_BADPASSCCODEPTR)) return face.error(e); + be::skip(p, pass_constraint_len); + if (e.test(p != rcCode, E_BADRULECCODEPTR) + || e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e); + be::skip(p, be::peek(o_constraint + m_numRules)); + if (e.test(p != aCode, E_BADACTIONCODEPTR)) return face.error(e); + be::skip(p, be::peek(o_actions + m_numRules)); + + // We should be at the end or within the pass + if (e.test(p > pass_end, E_BADPASSLENGTH)) return face.error(e); + + // Load the pass constraint if there is one. + if (pass_constraint_len) + { + face.error_context(face.error_context() + 1); + m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len, + precontext[0], be::peek(sort_keys), *m_silf, face, PASS_TYPE_UNKNOWN); + if (e.test(!m_cPConstraint, E_OUTOFMEM) + || e.test(m_cPConstraint.status() != Code::loaded, m_cPConstraint.status() + E_CODEFAILURE)) + return face.error(e); + face.error_context(face.error_context() - 1); + } + if (m_numRules) + { + if (!readRanges(ranges, numRanges, e)) return face.error(e); + if (!readRules(rule_map, numEntries, precontext, sort_keys, + o_constraint, rcCode, o_actions, aCode, face, pt, e)) return false; + } +#ifdef GRAPHITE2_TELEMETRY + telemetry::category _states_cat(face.tele.states); +#endif + return m_numRules ? readStates(start_states, states, o_rule_map, face, e) : true; +} + + +bool Pass::readRules(const byte * rule_map, const size_t num_entries, + const byte *precontext, const uint16 * sort_key, + const uint16 * o_constraint, const byte *rc_data, + const uint16 * o_action, const byte * ac_data, + Face & face, passtype pt, Error &e) +{ + const byte * const ac_data_end = ac_data + be::peek(o_action + m_numRules); + const byte * const rc_data_end = rc_data + be::peek(o_constraint + m_numRules); + + precontext += m_numRules; + sort_key += m_numRules; + o_constraint += m_numRules; + o_action += m_numRules; + + // Load rules. + const byte * ac_begin = 0, * rc_begin = 0, + * ac_end = ac_data + be::peek(o_action), + * rc_end = rc_data + be::peek(o_constraint); + + // Allocate pools + m_rules = new Rule [m_numRules]; + m_codes = new Code [m_numRules*2]; + int totalSlots = 0; + const uint16 *tsort = sort_key; + for (int i = 0; i < m_numRules; ++i) + totalSlots += be::peek(--tsort); + const size_t prog_pool_sz = vm::Machine::Code::estimateCodeDataOut(ac_end - ac_data + rc_end - rc_data, 2 * m_numRules, totalSlots); + m_progs = gralloc(prog_pool_sz); + byte * prog_pool_free = m_progs, + * prog_pool_end = m_progs + prog_pool_sz; + if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e); + + Rule * r = m_rules + m_numRules - 1; + for (size_t n = m_numRules; r >= m_rules; --n, --r, ac_end = ac_begin, rc_end = rc_begin) + { + face.error_context((face.error_context() & 0xFFFF00) + EC_ARULE + int((n - 1) << 24)); + r->preContext = *--precontext; + r->sort = be::peek(--sort_key); +#ifndef NDEBUG + r->rule_idx = uint16(n - 1); +#endif + if (r->sort > 63 || r->preContext >= r->sort || r->preContext > m_maxPreCtxt || r->preContext < m_minPreCtxt) + return false; + ac_begin = ac_data + be::peek(--o_action); + --o_constraint; + rc_begin = be::peek(o_constraint) ? rc_data + be::peek(o_constraint) : rc_end; + + if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end + || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end + || vm::Machine::Code::estimateCodeDataOut(ac_end - ac_begin + rc_end - rc_begin, 2, r->sort) > size_t(prog_pool_end - prog_pool_free)) + return false; + r->action = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free); + r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free); + + if (e.test(!r->action || !r->constraint, E_OUTOFMEM) + || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE) + || e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE) + || e.test(!r->constraint->immutable(), E_MUTABLECCODE)) + return face.error(e); + } + + byte * const moved_progs = prog_pool_free > m_progs ? static_cast(realloc(m_progs, prog_pool_free - m_progs)) : 0; + if (e.test(!moved_progs, E_OUTOFMEM)) + { + free(m_progs); + m_progs = 0; + return face.error(e); + } + + if (moved_progs != m_progs) + { + for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c) + { + c->externalProgramMoved(moved_progs - m_progs); + } + m_progs = moved_progs; + } + + // Load the rule entries map + face.error_context((face.error_context() & 0xFFFF00) + EC_APASS); + //TODO: Coverity: 1315804: FORWARD_NULL + RuleEntry * re = m_ruleMap = gralloc(num_entries); + if (e.test(!re, E_OUTOFMEM)) return face.error(e); + for (size_t n = num_entries; n; --n, ++re) + { + const ptrdiff_t rn = be::read(rule_map); + if (e.test(rn >= m_numRules, E_BADRULENUM)) return face.error(e); + re->rule = m_rules + rn; + } + + return true; +} + +static int cmpRuleEntry(const void *a, const void *b) { return (*(RuleEntry *)a < *(RuleEntry *)b ? -1 : + (*(RuleEntry *)b < *(RuleEntry *)a ? 1 : 0)); } + +bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED Face & face, Error &e) +{ +#ifdef GRAPHITE2_TELEMETRY + telemetry::category _states_cat(face.tele.starts); +#endif + m_startStates = gralloc(m_maxPreCtxt - m_minPreCtxt + 1); +#ifdef GRAPHITE2_TELEMETRY + telemetry::set_category(face.tele.states); +#endif + m_states = gralloc(m_numStates); +#ifdef GRAPHITE2_TELEMETRY + telemetry::set_category(face.tele.transitions); +#endif + m_transitions = gralloc(m_numTransition * m_numColumns); + + if (e.test(!m_startStates || !m_states || !m_transitions, E_OUTOFMEM)) return face.error(e); + // load start states + for (uint16 * s = m_startStates, + * const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s) + { + *s = be::read(starts); + if (e.test(*s >= m_numStates, E_BADSTATE)) + { + face.error_context((face.error_context() & 0xFFFF00) + EC_ASTARTS + int((s - m_startStates) << 24)); + return face.error(e); // true; + } + } + + // load state transition table. + for (uint16 * t = m_transitions, + * const t_end = t + m_numTransition*m_numColumns; t != t_end; ++t) + { + *t = be::read(states); + if (e.test(*t >= m_numStates, E_BADSTATE)) + { + face.error_context((face.error_context() & 0xFFFF00) + EC_ATRANS + int(((t - m_transitions) / m_numColumns) << 8)); + return face.error(e); + } + } + + State * s = m_states, + * const success_begin = m_states + m_numStates - m_numSuccess; + const RuleEntry * rule_map_end = m_ruleMap + be::peek(o_rule_map + m_numSuccess*sizeof(uint16)); + for (size_t n = m_numStates; n; --n, ++s) + { + RuleEntry * const begin = s < success_begin ? 0 : m_ruleMap + be::read(o_rule_map), + * const end = s < success_begin ? 0 : m_ruleMap + be::peek(o_rule_map); + + if (e.test(begin >= rule_map_end || end > rule_map_end || begin > end, E_BADRULEMAPPING)) + { + face.error_context((face.error_context() & 0xFFFF00) + EC_ARULEMAP + int(n << 24)); + return face.error(e); + } + s->rules = begin; + s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end : + begin + FiniteStateMachine::MAX_RULES; + if (begin) // keep UBSan happy can't call qsort with null begin + qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry); + } + + return true; +} + +bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e) +{ + m_cols = gralloc(m_numGlyphs); + if (e.test(!m_cols, E_OUTOFMEM)) return false; + memset(m_cols, 0xFF, m_numGlyphs * sizeof(uint16)); + for (size_t n = num_ranges; n; --n) + { + uint16 * ci = m_cols + be::read(ranges), + * ci_end = m_cols + be::read(ranges) + 1, + col = be::read(ranges); + + if (e.test(ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns, E_BADRANGE)) + return false; + + // A glyph must only belong to one column at a time + while (ci != ci_end && *ci == 0xffff) + *ci++ = col; + + if (e.test(ci != ci_end, E_BADRANGE)) + return false; + } + return true; +} + + +bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const +{ + Slot *s = m.slotMap().segment.first(); + if (!s || !testPassConstraint(m)) return true; + if (reverse) + { + m.slotMap().segment.reverseSlots(); + s = m.slotMap().segment.first(); + } + if (m_numRules) + { + Slot *currHigh = s->next(); + +#if !defined GRAPHITE2_NTRACING + if (fsm.dbgout) *fsm.dbgout << "rules" << json::array; + json::closer rules_array_closer(fsm.dbgout); +#endif + + m.slotMap().highwater(currHigh); + int lc = m_iMaxLoop; + do + { + findNDoRule(s, m, fsm); + if (m.status() != Machine::finished) return false; + if (s && (s == m.slotMap().highwater() || m.slotMap().highpassed() || --lc == 0)) { + if (!lc) + s = m.slotMap().highwater(); + lc = m_iMaxLoop; + if (s) + m.slotMap().highwater(s->next()); + } + } while (s); + } + //TODO: Use enums for flags + const bool collisions = m_numCollRuns || m_kernColls; + + if (!collisions || !m.slotMap().segment.hasCollisionInfo()) + return true; + + if (m_numCollRuns) + { + if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS)) + { + m.slotMap().segment.positionSlots(0, 0, 0, m.slotMap().dir(), true); +// m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS); + } + if (!collisionShift(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout)) + return false; + } + if ((m_kernColls) && !collisionKern(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout)) + return false; + if (collisions && !collisionFinish(&m.slotMap().segment, fsm.dbgout)) + return false; + return true; +} + +bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const +{ + fsm.reset(slot, m_maxPreCtxt); + if (fsm.slots.context() < m_minPreCtxt) + return false; + + uint16 state = m_startStates[m_maxPreCtxt - fsm.slots.context()]; + uint8 free_slots = SlotMap::MAX_SLOTS; + do + { + fsm.slots.pushSlot(slot); + if (slot->gid() >= m_numGlyphs + || m_cols[slot->gid()] == 0xffffU + || --free_slots == 0 + || state >= m_numTransition) + return free_slots != 0; + + const uint16 * transitions = m_transitions + state*m_numColumns; + state = transitions[m_cols[slot->gid()]]; + if (state >= m_successStart) + fsm.rules.accumulate_rules(m_states[state]); + + slot = slot->next(); + } while (state != 0 && slot); + + fsm.slots.pushSlot(slot); + return true; +} + +#if !defined GRAPHITE2_NTRACING + +inline +Slot * input_slot(const SlotMap & slots, const int n) +{ + Slot * s = slots[slots.context() + n]; + if (!s->isCopied()) return s; + + return s->prev() ? s->prev()->next() : (s->next() ? s->next()->prev() : slots.segment.last()); +} + +inline +Slot * output_slot(const SlotMap & slots, const int n) +{ + Slot * s = slots[slots.context() + n - 1]; + return s ? s->next() : slots.segment.first(); +} + +#endif //!defined GRAPHITE2_NTRACING + +void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) const +{ + assert(slot); + + if (runFSM(fsm, slot)) + { + // Search for the first rule which passes the constraint + const RuleEntry * r = fsm.rules.begin(), + * const re = fsm.rules.end(); + while (r != re && !testConstraint(*r->rule, m)) + { + ++r; + if (m.status() != Machine::finished) + return; + } + +#if !defined GRAPHITE2_NTRACING + if (fsm.dbgout) + { + if (fsm.rules.size() != 0) + { + *fsm.dbgout << json::item << json::object; + dumpRuleEventConsidered(fsm, *r); + if (r != re) + { + const int adv = doAction(r->rule->action, slot, m); + dumpRuleEventOutput(fsm, *r->rule, slot); + if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot); + adjustSlot(adv, slot, fsm.slots); + *fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot)) + << json::close; // Close RuelEvent object + + return; + } + else + { + *fsm.dbgout << json::close // close "considered" array + << "output" << json::null + << "cursor" << objectid(dslot(&fsm.slots.segment, slot->next())) + << json::close; + } + } + } + else +#endif + { + if (r != re) + { + const int adv = doAction(r->rule->action, slot, m); + if (m.status() != Machine::finished) return; + if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot); + adjustSlot(adv, slot, fsm.slots); + return; + } + } + } + + slot = slot->next(); + return; +} + +#if !defined GRAPHITE2_NTRACING + +void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const +{ + *fsm.dbgout << "considered" << json::array; + for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r) + { + if (r->rule->preContext > fsm.slots.context()) + continue; + *fsm.dbgout << json::flat << json::object + << "id" << r->rule - m_rules + << "failed" << true + << "input" << json::flat << json::object + << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext))) + << "length" << r->rule->sort + << json::close // close "input" + << json::close; // close Rule object + } +} + + +void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const +{ + *fsm.dbgout << json::item << json::flat << json::object + << "id" << &r - m_rules + << "failed" << false + << "input" << json::flat << json::object + << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0))) + << "length" << r.sort - r.preContext + << json::close // close "input" + << json::close // close Rule object + << json::close // close considered array + << "output" << json::object + << "range" << json::flat << json::object + << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0))) + << "end" << objectid(dslot(&fsm.slots.segment, last_slot)) + << json::close // close "input" + << "slots" << json::array; + const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance(); + fsm.slots.segment.positionSlots(0, 0, 0, fsm.slots.segment.currdir()); + + for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next()) + *fsm.dbgout << dslot(&fsm.slots.segment, slot); + *fsm.dbgout << json::close // close "slots" + << "postshift" << (last_slot ? last_slot->origin() : fsm.slots.segment.advance()) - rsb_prepos + << json::close; // close "output" object + +} + +#endif + + +inline +bool Pass::testPassConstraint(Machine & m) const +{ + if (!m_cPConstraint) return true; + + assert(m_cPConstraint.constraint()); + + m.slotMap().reset(*m.slotMap().segment.first(), 0); + m.slotMap().pushSlot(m.slotMap().segment.first()); + vm::slotref * map = m.slotMap().begin(); + const uint32 ret = m_cPConstraint.run(m, map); + +#if !defined GRAPHITE2_NTRACING + json * const dbgout = m.slotMap().segment.getFace()->logger(); + if (dbgout) + *dbgout << "constraint" << (ret && m.status() == Machine::finished); +#endif + + return ret && m.status() == Machine::finished; +} + + +bool Pass::testConstraint(const Rule & r, Machine & m) const +{ + const uint16 curr_context = m.slotMap().context(); + if (unsigned(r.sort + curr_context - r.preContext) > m.slotMap().size() + || curr_context - r.preContext < 0) return false; + + vm::slotref * map = m.slotMap().begin() + curr_context - r.preContext; + if (map[r.sort - 1] == 0) + return false; + + if (!*r.constraint) return true; + assert(r.constraint->constraint()); + for (int n = r.sort; n && map; --n, ++map) + { + if (!*map) continue; + const int32 ret = r.constraint->run(m, map); + if (!ret || m.status() != Machine::finished) + return false; + } + + return true; +} + + +void SlotMap::collectGarbage(Slot * &aSlot) +{ + for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) { + Slot *& slot = *s; + if(slot && (slot->isDeleted() || slot->isCopied())) + { + if (slot == aSlot) + aSlot = slot->prev() ? slot->prev() : slot->next(); + segment.freeSlot(slot); + } + } +} + + + +int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) const +{ + assert(codeptr); + if (!*codeptr) return 0; + SlotMap & smap = m.slotMap(); + vm::slotref * map = &smap[smap.context()]; + smap.highpassed(false); + + int32 ret = codeptr->run(m, map); + + if (m.status() != Machine::finished) + { + slot_out = NULL; + smap.highwater(0); + return 0; + } + + slot_out = *map; + return ret; +} + + +void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const +{ + if (!slot_out) + { + if (smap.highpassed() || slot_out == smap.highwater()) + { + slot_out = smap.segment.last(); + ++delta; + if (!smap.highwater() || smap.highwater() == slot_out) + smap.highpassed(false); + } + else + { + slot_out = smap.segment.first(); + --delta; + } + } + if (delta < 0) + { + while (++delta <= 0 && slot_out) + { + slot_out = slot_out->prev(); + if (smap.highpassed() && smap.highwater() == slot_out) + smap.highpassed(false); + } + } + else if (delta > 0) + { + while (--delta >= 0 && slot_out) + { + if (slot_out == smap.highwater() && slot_out) + smap.highpassed(true); + slot_out = slot_out->next(); + } + } +} + +bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const +{ + ShiftCollider shiftcoll(dbgout); + // bool isfirst = true; + bool hasCollisions = false; + Slot *start = seg->first(); // turn on collision fixing for the first slot + Slot *end = NULL; + bool moved = false; + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << "collisions" << json::array + << json::flat << json::object << "num-loops" << m_numCollRuns << json::close; +#endif + + while (start) + { +#if !defined GRAPHITE2_NTRACING + if (dbgout) *dbgout << json::object << "phase" << "1" << "moves" << json::array; +#endif + hasCollisions = false; + end = NULL; + // phase 1 : position shiftable glyphs, ignoring kernable glyphs + for (Slot *s = start; s; s = s->next()) + { + const SlotCollision * c = seg->collisionInfo(s); + if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX + && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout)) + return false; + if (s != start && (c->flags() & SlotCollision::COLL_END)) + { + end = s->next(); + break; + } + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close << json::close; // phase-1 +#endif + + // phase 2 : loop until happy. + for (int i = 0; i < m_numCollRuns - 1; ++i) + { + if (hasCollisions || moved) + { + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array; +#endif + // phase 2a : if any shiftable glyphs are in collision, iterate backwards, + // fixing them and ignoring other non-collided glyphs. Note that this handles ONLY + // glyphs that are actually in collision from phases 1 or 2b, and working backwards + // has the intended effect of breaking logjams. + if (hasCollisions) + { + hasCollisions = false; + #if 0 + moved = true; + for (Slot *s = start; s != end; s = s->next()) + { + SlotCollision * c = seg->collisionInfo(s); + c->setShift(Position(0, 0)); + } + #endif + Slot *lend = end ? end->prev() : seg->last(); + Slot *lstart = start->prev(); + for (Slot *s = lend; s != lstart; s = s->prev()) + { + SlotCollision * c = seg->collisionInfo(s); + if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN | SlotCollision::COLL_ISCOL)) + == (SlotCollision::COLL_FIX | SlotCollision::COLL_ISCOL)) // ONLY if this glyph is still colliding + { + if (!resolveCollisions(seg, s, lend, shiftcoll, true, dir, moved, hasCollisions, dbgout)) + return false; + c->setFlags(c->flags() | SlotCollision::COLL_TEMPLOCK); + } + } + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close << json::close // phase 2a + << json::object << "phase" << "2b" << "loop" << i << "moves" << json::array; +#endif + + // phase 2b : redo basic diacritic positioning pass for ALL glyphs. Each successive loop adjusts + // glyphs from their current adjusted position, which has the effect of gradually minimizing the + // resulting adjustment; ie, the final result will be gradually closer to the original location. + // Also it allows more flexibility in the final adjustment, since it is moving along the + // possible 8 vectors from successively different starting locations. + if (moved) + { + moved = false; + for (Slot *s = start; s != end; s = s->next()) + { + SlotCollision * c = seg->collisionInfo(s); + if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_TEMPLOCK + | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX + && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout)) + return false; + else if (c->flags() & SlotCollision::COLL_TEMPLOCK) + c->setFlags(c->flags() & ~SlotCollision::COLL_TEMPLOCK); + } + } + // if (!hasCollisions) // no, don't leave yet because phase 2b will continue to improve things + // break; +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close << json::close; // phase 2 +#endif + } + } + if (!end) + break; + start = NULL; + for (Slot *s = end->prev(); s; s = s->next()) + { + if (seg->collisionInfo(s)->flags() & SlotCollision::COLL_START) + { + start = s; + break; + } + } + } + return true; +} + +bool Pass::collisionKern(Segment *seg, int dir, json * const dbgout) const +{ + Slot *start = seg->first(); + float ymin = 1e38f; + float ymax = -1e38f; + const GlyphCache &gc = seg->getFace()->glyphs(); + + // phase 3 : handle kerning of clusters +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::object << "phase" << "3" << "moves" << json::array; +#endif + + for (Slot *s = seg->first(); s; s = s->next()) + { + if (!gc.check(s->gid())) + return false; + const SlotCollision * c = seg->collisionInfo(s); + const Rect &bbox = seg->theGlyphBBoxTemporary(s->gid()); + float y = s->origin().y + c->shift().y; + if (!(c->flags() & SlotCollision::COLL_ISSPACE)) + { + ymax = max(y + bbox.tr.y, ymax); + ymin = min(y + bbox.bl.y, ymin); + } + if (start && (c->flags() & (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX)) + == (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX)) + resolveKern(seg, s, start, dir, ymin, ymax, dbgout); + if (c->flags() & SlotCollision::COLL_END) + start = NULL; + if (c->flags() & SlotCollision::COLL_START) + start = s; + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close << json::close; // phase 3 +#endif + return true; +} + +bool Pass::collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const +{ + for (Slot *s = seg->first(); s; s = s->next()) + { + SlotCollision *c = seg->collisionInfo(s); + if (c->shift().x != 0 || c->shift().y != 0) + { + const Position newOffset = c->shift(); + const Position nullPosition(0, 0); + c->setOffset(newOffset + c->offset()); + c->setShift(nullPosition); + } + } +// seg->positionSlots(); + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + *dbgout << json::close; +#endif + return true; +} + +// Can slot s be kerned, or is it attached to something that can be kerned? +static bool inKernCluster(Segment *seg, Slot *s) +{ + SlotCollision *c = seg->collisionInfo(s); + if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ ) + return true; + while (s->attachedTo()) + { + s = s->attachedTo(); + c = seg->collisionInfo(s); + if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ ) + return true; + } + return false; +} + +// Fix collisions for the given slot. +// Return true if everything was fixed, false if there are still collisions remaining. +// isRev means be we are processing backwards. +bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start, + ShiftCollider &coll, GR_MAYBE_UNUSED bool isRev, int dir, bool &moved, bool &hasCol, + json * const dbgout) const +{ + Slot * nbor; // neighboring slot + SlotCollision *cFix = seg->collisionInfo(slotFix); + if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), cFix->marginWt(), + cFix->shift(), cFix->offset(), dir, dbgout)) + return false; + bool collides = false; + // When we're processing forward, ignore kernable glyphs that preceed the target glyph. + // When processing backward, don't ignore these until we pass slotFix. + bool ignoreForKern = !isRev; + bool rtl = dir & 1; + Slot *base = slotFix; + while (base->attachedTo()) + base = base->attachedTo(); + Position zero(0., 0.); + + // Look for collisions with the neighboring glyphs. + for (nbor = start; nbor; nbor = isRev ? nbor->prev() : nbor->next()) + { + SlotCollision *cNbor = seg->collisionInfo(nbor); + bool sameCluster = nbor->isChildOf(base); + if (nbor != slotFix // don't process if this is the slot of interest + && !(cNbor->ignore()) // don't process if ignoring + && (nbor == base || sameCluster // process if in the same cluster as slotFix + || !inKernCluster(seg, nbor)) // or this cluster is not to be kerned +// || (rtl ^ ignoreForKern)) // or it comes before(ltr) or after(rtl) + && (!isRev // if processing forwards then good to merge otherwise only: + || !(cNbor->flags() & SlotCollision::COLL_FIX) // merge in immovable stuff + || ((cNbor->flags() & SlotCollision::COLL_KERN) && !sameCluster) // ignore other kernable clusters + || (cNbor->flags() & SlotCollision::COLL_ISCOL)) // test against other collided glyphs + && !coll.mergeSlot(seg, nbor, cNbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout)) + return false; + else if (nbor == slotFix) + // Switching sides of this glyph - if we were ignoring kernable stuff before, don't anymore. + ignoreForKern = !ignoreForKern; + + if (nbor != start && (cNbor->flags() & (isRev ? SlotCollision::COLL_START : SlotCollision::COLL_END))) + break; + } + bool isCol = false; + if (collides || cFix->shift().x != 0.f || cFix->shift().y != 0.f) + { + Position shift = coll.resolve(seg, isCol, dbgout); + // isCol has been set to true if a collision remains. + if (std::fabs(shift.x) < 1e38f && std::fabs(shift.y) < 1e38f) + { + if (sqr(shift.x-cFix->shift().x) + sqr(shift.y-cFix->shift().y) >= m_colThreshold * m_colThreshold) + moved = true; + cFix->setShift(shift); + if (slotFix->firstChild()) + { + Rect bbox; + Position here = slotFix->origin() + shift; + float clusterMin = here.x; + slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, rtl, false); + } + } + } + else + { + // This glyph is not colliding with anything. +#if !defined GRAPHITE2_NTRACING + if (dbgout) + { + *dbgout << json::object + << "missed" << objectid(dslot(seg, slotFix)); + coll.outputJsonDbg(dbgout, seg, -1); + *dbgout << json::close; + } +#endif + } + + // Set the is-collision flag bit. + if (isCol) + { cFix->setFlags(cFix->flags() | SlotCollision::COLL_ISCOL | SlotCollision::COLL_KNOWN); } + else + { cFix->setFlags((cFix->flags() & ~SlotCollision::COLL_ISCOL) | SlotCollision::COLL_KNOWN); } + hasCol |= isCol; + return true; +} + +float Pass::resolveKern(Segment *seg, Slot *slotFix, GR_MAYBE_UNUSED Slot *start, int dir, + float &ymin, float &ymax, json *const dbgout) const +{ + Slot *nbor; // neighboring slot + float currSpace = 0.; + bool collides = false; + unsigned int space_count = 0; + Slot *base = slotFix; + while (base->attachedTo()) + base = base->attachedTo(); + SlotCollision *cFix = seg->collisionInfo(base); + const GlyphCache &gc = seg->getFace()->glyphs(); + const Rect &bbb = seg->theGlyphBBoxTemporary(slotFix->gid()); + const float by = slotFix->origin().y + cFix->shift().y; + + if (base != slotFix) + { + cFix->setFlags(cFix->flags() | SlotCollision::COLL_KERN | SlotCollision::COLL_FIX); + return 0; + } + bool seenEnd = (cFix->flags() & SlotCollision::COLL_END) != 0; + bool isInit = false; + KernCollider coll(dbgout); + + ymax = max(by + bbb.tr.y, ymax); + ymin = min(by + bbb.bl.y, ymin); + for (nbor = slotFix->next(); nbor; nbor = nbor->next()) + { + if (nbor->isChildOf(base)) + continue; + if (!gc.check(nbor->gid())) + return 0.; + const Rect &bb = seg->theGlyphBBoxTemporary(nbor->gid()); + SlotCollision *cNbor = seg->collisionInfo(nbor); + if ((bb.bl.y == 0.f && bb.tr.y == 0.f) || (cNbor->flags() & SlotCollision::COLL_ISSPACE)) + { + if (m_kernColls == InWord) + break; + // Add space for a space glyph. + currSpace += nbor->advance(); + ++space_count; + } + else + { + space_count = 0; + if (nbor != slotFix && !cNbor->ignore()) + { + seenEnd = true; + if (!isInit) + { + if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), + cFix->shift(), cFix->offset(), dir, ymin, ymax, dbgout)) + return 0.; + isInit = true; + } + collides |= coll.mergeSlot(seg, nbor, cNbor->shift(), currSpace, dir, dbgout); + } + } + if (cNbor->flags() & SlotCollision::COLL_END) + { + if (seenEnd && space_count < 2) + break; + else + seenEnd = true; + } + } + if (collides) + { + Position mv = coll.resolve(seg, slotFix, dir, dbgout); + coll.shift(mv, dir); + Position delta = slotFix->advancePos() + mv - cFix->shift(); + slotFix->advance(delta); + cFix->setShift(mv); + return mv.x; + } + return 0.; +} diff --git a/thirdparty/graphite/src/Position.cpp b/thirdparty/graphite/src/Position.cpp new file mode 100644 index 00000000000..d2fdbd4e7c1 --- /dev/null +++ b/thirdparty/graphite/src/Position.cpp @@ -0,0 +1,97 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/Position.h" +#include + +using namespace graphite2; + +bool Rect::hitTest(Rect &other) +{ + if (bl.x > other.tr.x) return false; + if (tr.x < other.bl.x) return false; + if (bl.y > other.tr.y) return false; + if (tr.y < other.bl.y) return false; + return true; +} + +Position Rect::overlap(Position &offset, Rect &other, Position &othero) +{ + float ax = (bl.x + offset.x) - (other.tr.x + othero.x); + float ay = (bl.y + offset.y) - (other.tr.y + othero.y); + float bx = (other.bl.x + othero.x) - (tr.x + offset.x); + float by = (other.bl.y + othero.y) - (tr.y + offset.y); + return Position((ax > bx ? ax : bx), (ay > by ? ay : by)); +} + +float boundmin(float move, float lim1, float lim2, float &error) +{ + // error is always positive for easy comparison + if (move < lim1 && move < lim2) + { error = 0.; return move; } + else if (lim1 < lim2) + { error = std::fabs(move - lim1); return lim1; } + else + { error = std::fabs(move - lim2); return lim2; } +} + +#if 0 +Position Rect::constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox) +{ + // a = max, i = min, s = sum, d = diff + float eax, eay, eix, eiy, eas, eis, ead, eid; + float beste = INF; + Position res; + // calculate the movements in each direction and the error (amount of remaining overlap) + // first param is movement, second and third are movement over the constraining box + float ax = boundmin(obox.tr.x + other.x - box.bl.x - offset.x + 1, tr.x - offset.x, INF, &eax); + float ay = boundmin(obox.tr.y + other.y - box.bl.y - offset.y + 1, tr.y - offset.y, INF, &eay); + float ix = boundmin(obox.bl.x + other.x - box.tr.x - offset.x + 1, bl.x - offset.x, INF, &eix); + float iy = boundmin(obox.bl.y + other.y - box.tr.y - offset.y + 1, bl.y - offset.y, INF, &eiy); + float as = boundmin(ISQRT2 * (osdbox.tr.x + other.x + other.y - sdbox.bl.x - offset.x - offset.y) + 1, tr.x - offset.x, tr.y - offset.y, &eas); + float is = boundmin(ISQRT2 * (osdbox.bl.x + other.x + other.y - sdbox.tr.x - offset.x - offset.y) + 1, bl.x - offset.x, bl.y - offset.y, &eis); + float ad = boundmin(ISQRT2 * (osdbox.tr.y + other.x - other.y - sdbox.bl.y - offset.x + offset.y) + 1, tr.y - offset.y, tr.x - offset.x, &ead); + float id = boundmin(ISQRT2 * (osdbox.bl.y + other.x - other.y - sdbox.tr.y - offset.x + offset.y) + 1, bl.y - offset.y, bl.x - offset.x, &eid); + + if (eax < beste) + { res = Position(ax, 0); beste = eax; } + if (eay < beste) + { res = Position(0, ay); beste = eay; } + if (eix < beste) + { res = Position(ix, 0); beste = eix; } + if (eiy < beste) + { res = Position(0, iy); beste = eiy; } + if (SQRT2 * (eas) < beste) + { res = Position(as, ad); beste = SQRT2 * (eas); } + if (SQRT2 * (eis) < beste) + { res = Position(is, is); beste = SQRT2 * (eis); } + if (SQRT2 * (ead) < beste) + { res = Position(ad, ad); beste = SQRT2 * (ead); } + if (SQRT2 * (eid) < beste) + { res = Position(id, id); beste = SQRT2 * (eid); } + return res; +} +#endif diff --git a/thirdparty/graphite/src/Segment.cpp b/thirdparty/graphite/src/Segment.cpp new file mode 100644 index 00000000000..62edd4250ff --- /dev/null +++ b/thirdparty/graphite/src/Segment.cpp @@ -0,0 +1,423 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/UtfCodec.h" +#include +#include + +#include "inc/bits.h" +#include "inc/Segment.h" +#include "graphite2/Font.h" +#include "inc/CharInfo.h" +#include "inc/debug.h" +#include "inc/Slot.h" +#include "inc/Main.h" +#include "inc/CmapCache.h" +#include "inc/Collider.h" +#include "graphite2/Segment.h" + + +using namespace graphite2; + +Segment::Segment(size_t numchars, const Face* face, uint32 script, int textDir) +: m_freeSlots(NULL), + m_freeJustifies(NULL), + m_charinfo(new CharInfo[numchars]), + m_collisions(NULL), + m_face(face), + m_silf(face->chooseSilf(script)), + m_first(NULL), + m_last(NULL), + m_bufSize(numchars + 10), + m_numGlyphs(numchars), + m_numCharinfo(numchars), + m_defaultOriginal(0), + m_dir(textDir), + m_flags(((m_silf->flags() & 0x20) != 0) << 1), + m_passBits(m_silf->aPassBits() ? -1 : 0) +{ + freeSlot(newSlot()); + m_bufSize = log_binary(numchars)+1; +} + +Segment::~Segment() +{ + for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i) + free(*i); + for (AttributeRope::iterator i = m_userAttrs.begin(); i != m_userAttrs.end(); ++i) + free(*i); + for (JustifyRope::iterator i = m_justifies.begin(); i != m_justifies.end(); ++i) + free(*i); + delete[] m_charinfo; + free(m_collisions); +} + +void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset) +{ + Slot *aSlot = newSlot(); + + if (!aSlot) return; + m_charinfo[id].init(cid); + m_charinfo[id].feats(iFeats); + m_charinfo[id].base(coffset); + const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid); + m_charinfo[id].breakWeight(theGlyph ? theGlyph->attrs()[m_silf->aBreak()] : 0); + + aSlot->child(NULL); + aSlot->setGlyph(this, gid, theGlyph); + aSlot->originate(id); + aSlot->before(id); + aSlot->after(id); + if (m_last) m_last->next(aSlot); + aSlot->prev(m_last); + m_last = aSlot; + if (!m_first) m_first = aSlot; + if (theGlyph && m_silf->aPassBits()) + m_passBits &= theGlyph->attrs()[m_silf->aPassBits()] + | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0); +} + +Slot *Segment::newSlot() +{ + if (!m_freeSlots) + { + // check that the segment doesn't grow indefinintely + if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR) + return NULL; + int numUser = m_silf->numUser(); +#if !defined GRAPHITE2_NTRACING + if (m_face->logger()) ++numUser; +#endif + Slot *newSlots = grzeroalloc(m_bufSize); + int16 *newAttrs = grzeroalloc(m_bufSize * numUser); + if (!newSlots || !newAttrs) + { + free(newSlots); + free(newAttrs); + return NULL; + } + for (size_t i = 0; i < m_bufSize; i++) + { + ::new (newSlots + i) Slot(newAttrs + i * numUser); + newSlots[i].next(newSlots + i + 1); + } + newSlots[m_bufSize - 1].next(NULL); + newSlots[0].next(NULL); + m_slots.push_back(newSlots); + m_userAttrs.push_back(newAttrs); + m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL; + return newSlots; + } + Slot *res = m_freeSlots; + m_freeSlots = m_freeSlots->next(); + res->next(NULL); + return res; +} + +void Segment::freeSlot(Slot *aSlot) +{ + if (aSlot == nullptr) return; + if (m_last == aSlot) m_last = aSlot->prev(); + if (m_first == aSlot) m_first = aSlot->next(); + if (aSlot->attachedTo()) + aSlot->attachedTo()->removeChild(aSlot); + while (aSlot->firstChild()) + { + if (aSlot->firstChild()->attachedTo() == aSlot) + { + aSlot->firstChild()->attachTo(nullptr); + aSlot->removeChild(aSlot->firstChild()); + } + else + aSlot->firstChild(nullptr); + } + // reset the slot incase it is reused + ::new (aSlot) Slot(aSlot->userAttrs()); + memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16)); + // Update generation counter for debug +#if !defined GRAPHITE2_NTRACING + if (m_face->logger()) + ++aSlot->userAttrs()[m_silf->numUser()]; +#endif + // update next pointer + if (!m_freeSlots) + aSlot->next(nullptr); + else + aSlot->next(m_freeSlots); + m_freeSlots = aSlot; +} + +SlotJustify *Segment::newJustify() +{ + if (!m_freeJustifies) + { + const size_t justSize = SlotJustify::size_of(m_silf->numJustLevels()); + byte *justs = grzeroalloc(justSize * m_bufSize); + if (!justs) return NULL; + for (ptrdiff_t i = m_bufSize - 2; i >= 0; --i) + { + SlotJustify *p = reinterpret_cast(justs + justSize * i); + SlotJustify *next = reinterpret_cast(justs + justSize * (i + 1)); + p->next = next; + } + m_freeJustifies = (SlotJustify *)justs; + m_justifies.push_back(m_freeJustifies); + } + SlotJustify *res = m_freeJustifies; + m_freeJustifies = m_freeJustifies->next; + res->next = NULL; + return res; +} + +void Segment::freeJustify(SlotJustify *aJustify) +{ + int numJust = m_silf->numJustLevels(); + if (m_silf->numJustLevels() <= 0) numJust = 1; + aJustify->next = m_freeJustifies; + memset(aJustify->values, 0, numJust*SlotJustify::NUMJUSTPARAMS*sizeof(int16)); + m_freeJustifies = aJustify; +} + +// reverse the slots but keep diacritics in their same position after their bases +void Segment::reverseSlots() +{ + m_dir = m_dir ^ 64; // invert the reverse flag + if (m_first == m_last) return; // skip 0 or 1 glyph runs + + Slot *t = 0; + Slot *curr = m_first; + Slot *tlast; + Slot *tfirst; + Slot *out = 0; + + while (curr && getSlotBidiClass(curr) == 16) + curr = curr->next(); + if (!curr) return; + tfirst = curr->prev(); + tlast = curr; + + while (curr) + { + if (getSlotBidiClass(curr) == 16) + { + Slot *d = curr->next(); + while (d && getSlotBidiClass(d) == 16) + d = d->next(); + + d = d ? d->prev() : m_last; + Slot *p = out->next(); // one after the diacritics. out can't be null + if (p) + p->prev(d); + else + tlast = d; + t = d->next(); + d->next(p); + curr->prev(out); + out->next(curr); + } + else // will always fire first time round the loop + { + if (out) + out->prev(curr); + t = curr->next(); + curr->next(out); + out = curr; + } + curr = t; + } + out->prev(tfirst); + if (tfirst) + tfirst->next(out); + else + m_first = out; + m_last = tlast; +} + +void Segment::linkClusters(Slot *s, Slot * end) +{ + end = end->next(); + + for (; s != end && !s->isBase(); s = s->next()); + Slot * ls = s; + + if (m_dir & 1) + { + for (; s != end; s = s->next()) + { + if (!s->isBase()) continue; + + s->sibling(ls); + ls = s; + } + } + else + { + for (; s != end; s = s->next()) + { + if (!s->isBase()) continue; + + ls->sibling(s); + ls = s; + } + } +} + +Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isRtl, bool isFinal) +{ + Position currpos(0., 0.); + float clusterMin = 0.; + Rect bbox; + bool reorder = (currdir() != isRtl); + + if (reorder) + { + Slot *temp; + reverseSlots(); + temp = iStart; + iStart = iEnd; + iEnd = temp; + } + if (!iStart) iStart = m_first; + if (!iEnd) iEnd = m_last; + + if (!iStart || !iEnd) // only true for empty segments + return currpos; + + if (isRtl) + { + for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev()) + { + if (s->isBase()) + currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal); + } + } + else + { + for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next()) + { + if (s->isBase()) + currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal); + } + } + if (reorder) + reverseSlots(); + return currpos; +} + + +void Segment::associateChars(int offset, size_t numChars) +{ + int i = 0, j = 0; + CharInfo *c, *cend; + for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c) + { + c->before(-1); + c->after(-1); + } + for (Slot * s = m_first; s; s->index(i++), s = s->next()) + { + j = s->before(); + if (j < 0) continue; + + for (const int after = s->after(); j <= after; ++j) + { + c = charinfo(j); + if (c->before() == -1 || i < c->before()) c->before(i); + if (c->after() < i) c->after(i); + } + } + for (Slot *s = m_first; s; s = s->next()) + { + int a; + for (a = s->after() + 1; a < offset + int(numChars) && charinfo(a)->after() < 0; ++a) + { charinfo(a)->after(s->index()); } + --a; + s->after(a); + + for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a) + { charinfo(a)->before(s->index()); } + ++a; + s->before(a); + } +} + + +template +inline void process_utf_data(Segment & seg, const Face & face, const int fid, utf_iter c, size_t n_chars) +{ + const Cmap & cmap = face.cmap(); + int slotid = 0; + + const typename utf_iter::codeunit_type * const base = c; + for (; n_chars; --n_chars, ++c, ++slotid) + { + const uint32 usv = *c; + uint16 gid = cmap[usv]; + if (!gid) gid = face.findPseudo(usv); + seg.appendSlot(slotid, usv, gid, fid, c - base); + } +} + + +bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars) +{ + assert(face); + assert(pFeats); + if (!m_charinfo) return false; + + // utf iterator is self recovering so we don't care about the error state of the iterator. + switch (enc) + { + case gr_utf8: process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break; + case gr_utf16: process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break; + case gr_utf32: process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break; + } + return true; +} + +void Segment::doMirror(uint16 aMirror) +{ + Slot * s; + for (s = m_first; s; s = s->next()) + { + unsigned short g = glyphAttr(s->gid(), aMirror); + if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1))) + s->setGlyph(this, g); + } +} + +bool Segment::initCollisions() +{ + m_collisions = grzeroalloc(slotCount()); + if (!m_collisions) return false; + + for (Slot *p = m_first; p; p = p->next()) + if (p->index() < slotCount()) + ::new (collisionInfo(p)) SlotCollision(this, p); + else + return false; + return true; +} diff --git a/thirdparty/graphite/src/Silf.cpp b/thirdparty/graphite/src/Silf.cpp new file mode 100644 index 00000000000..44d3c96171d --- /dev/null +++ b/thirdparty/graphite/src/Silf.cpp @@ -0,0 +1,439 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include +#include "graphite2/Segment.h" +#include "inc/debug.h" +#include "inc/Endian.h" +#include "inc/Silf.h" +#include "inc/Segment.h" +#include "inc/Rule.h" +#include "inc/Error.h" + + +using namespace graphite2; + +namespace { static const uint32 ERROROFFSET = 0xFFFFFFFF; } + +Silf::Silf() throw() +: m_passes(0), + m_pseudos(0), + m_classOffsets(0), + m_classData(0), + m_justs(0), + m_numPasses(0), + m_numJusts(0), + m_sPass(0), + m_pPass(0), + m_jPass(0), + m_bPass(0), + m_flags(0), + m_dir(0), + m_aPseudo(0), + m_aBreak(0), + m_aUser(0), + m_aBidi(0), + m_aMirror(0), + m_aPassBits(0), + m_iMaxComp(0), + m_aCollision(0), + m_aLig(0), + m_numPseudo(0), + m_nClass(0), + m_nLinear(0), + m_gEndLine(0) +{ + memset(&m_silfinfo, 0, sizeof m_silfinfo); +} + +Silf::~Silf() throw() +{ + releaseBuffers(); +} + +void Silf::releaseBuffers() throw() +{ + delete [] m_passes; + delete [] m_pseudos; + free(m_classOffsets); + free(m_classData); + free(m_justs); + m_passes= 0; + m_pseudos = 0; + m_classOffsets = 0; + m_classData = 0; + m_justs = 0; +} + + +bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, uint32 version) +{ + const byte * p = silf_start, + * const silf_end = p + lSilf; + Error e; + + if (e.test(version >= 0x00060000, E_BADSILFVERSION)) + { + releaseBuffers(); return face.error(e); + } + if (version >= 0x00030000) + { + if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); } + be::skip(p); // ruleVersion + be::skip(p,2); // passOffset & pseudosOffset + } + else if (e.test(lSilf < 20, E_BADSIZE)) { releaseBuffers(); return face.error(e); } + const uint16 maxGlyph = be::read(p); + m_silfinfo.extra_ascent = be::read(p); + m_silfinfo.extra_descent = be::read(p); + m_numPasses = be::read(p); + m_sPass = be::read(p); + m_pPass = be::read(p); + m_jPass = be::read(p); + m_bPass = be::read(p); + m_flags = be::read(p); + be::skip(p,2); // max{Pre,Post}Context. + m_aPseudo = be::read(p); + m_aBreak = be::read(p); + m_aBidi = be::read(p); + m_aMirror = be::read(p); + m_aPassBits = be::read(p); + + // Read Justification levels. + m_numJusts = be::read(p); + if (e.test(maxGlyph >= face.glyphs().numGlyphs(), E_BADMAXGLYPH) + || e.test(p + m_numJusts * 8 >= silf_end, E_BADNUMJUSTS)) + { + releaseBuffers(); return face.error(e); + } + + if (m_numJusts) + { + m_justs = gralloc(m_numJusts); + if (e.test(!m_justs, E_OUTOFMEM)) return face.error(e); + + for (uint8 i = 0; i < m_numJusts; i++) + { + ::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]); + be::skip(p,8); + } + } + + if (e.test(p + sizeof(uint16) + sizeof(uint8)*8 >= silf_end, E_BADENDJUSTS)) { releaseBuffers(); return face.error(e); } + m_aLig = be::read(p); + m_aUser = be::read(p); + m_iMaxComp = be::read(p); + m_dir = be::read(p) - 1; + m_aCollision = be::read(p); + be::skip(p,3); + be::skip(p, be::read(p)); // don't need critical features yet + be::skip(p); // reserved + if (e.test(p >= silf_end, E_BADCRITFEATURES)) { releaseBuffers(); return face.error(e); } + be::skip(p, be::read(p)); // don't use scriptTag array. + if (e.test(p + sizeof(uint16) + sizeof(uint32) >= silf_end, E_BADSCRIPTTAGS)) { releaseBuffers(); return face.error(e); } + m_gEndLine = be::read(p); // lbGID + const byte * o_passes = p; + uint32 passes_start = be::read(p); + + const size_t num_attrs = face.glyphs().numAttrs(); + if (e.test(m_aPseudo >= num_attrs, E_BADAPSEUDO) + || e.test(m_aBreak >= num_attrs, E_BADABREAK) + || e.test(m_aBidi >= num_attrs, E_BADABIDI) + || e.test(m_aMirror>= num_attrs, E_BADAMIRROR) + || e.test(m_aCollision && m_aCollision >= num_attrs - 5, E_BADACOLLISION) + || e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= lSilf, E_BADPASSESSTART) + || e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS) + || e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS) + || e.test((m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)), E_BADBPASS) + || e.test(m_aLig > 127, E_BADALIG)) + { + releaseBuffers(); + return face.error(e); + } + be::skip(p, m_numPasses); + if (e.test(unsigned(p - silf_start) + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); } + m_numPseudo = be::read(p); + be::skip(p, 3); // searchPseudo, pseudoSelector, pseudoShift + m_pseudos = new Pseudo[m_numPseudo]; + if (e.test(unsigned(p - silf_start) + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO) + || e.test(!m_pseudos, E_OUTOFMEM)) + { + releaseBuffers(); return face.error(e); + } + for (int i = 0; i < m_numPseudo; i++) + { + m_pseudos[i].uid = be::read(p); + m_pseudos[i].gid = be::read(p); + } + + const size_t clen = readClassMap(p, passes_start + silf_start - p, version, e); + m_passes = new Pass[m_numPasses]; + if (e || e.test(clen > unsigned(passes_start + silf_start - p), E_BADPASSESSTART) + || e.test(!m_passes, E_OUTOFMEM)) + { releaseBuffers(); return face.error(e); } + + for (size_t i = 0; i < m_numPasses; ++i) + { + uint32 pass_start = be::read(o_passes); + uint32 pass_end = be::peek(o_passes); + face.error_context((face.error_context() & 0xFF00) + EC_ASILF + unsigned(i << 16)); + if (e.test(pass_start > pass_end, E_BADPASSSTART) + || e.test(pass_start < passes_start, E_BADPASSSTART) + || e.test(pass_end > lSilf, E_BADPASSEND)) { + releaseBuffers(); return face.error(e); + } + + enum passtype pt = PASS_TYPE_UNKNOWN; + if (i >= m_jPass) pt = PASS_TYPE_JUSTIFICATION; + else if (i >= m_pPass) pt = PASS_TYPE_POSITIONING; + else if (i >= m_sPass) pt = PASS_TYPE_SUBSTITUTE; + else pt = PASS_TYPE_LINEBREAK; + + m_passes[i].init(this); + if (!m_passes[i].readPass(silf_start + pass_start, pass_end - pass_start, pass_start, face, pt, + version, e)) + { + releaseBuffers(); + return false; + } + } + + // fill in gr_faceinfo + m_silfinfo.upem = face.glyphs().unitsPerEm(); + m_silfinfo.has_bidi_pass = (m_bPass != 0xFF); + m_silfinfo.justifies = (m_numJusts != 0) || (m_jPass < m_pPass); + m_silfinfo.line_ends = (m_flags & 1); + m_silfinfo.space_contextuals = gr_faceinfo::gr_space_contextuals((m_flags >> 2) & 0x7); + return true; +} + +template inline uint32 Silf::readClassOffsets(const byte *&p, size_t data_len, Error &e) +{ + const T cls_off = 2*sizeof(uint16) + sizeof(T)*(m_nClass+1); + const uint32 max_off = (be::peek(p + sizeof(T)*m_nClass) - cls_off)/sizeof(uint16); + // Check that the last+1 offset is less than or equal to the class map length. + if (e.test(be::peek(p) != cls_off, E_MISALIGNEDCLASSES) + || e.test(max_off > (data_len - cls_off)/sizeof(uint16), E_HIGHCLASSOFFSET)) + return ERROROFFSET; + + // Read in all the offsets. + m_classOffsets = gralloc(m_nClass+1); + if (e.test(!m_classOffsets, E_OUTOFMEM)) return ERROROFFSET; + for (uint32 * o = m_classOffsets, * const o_end = o + m_nClass + 1; o != o_end; ++o) + { + *o = (be::read(p) - cls_off)/sizeof(uint16); + if (e.test(*o > max_off, E_HIGHCLASSOFFSET)) + return ERROROFFSET; + } + return max_off; +} + +size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version, Error &e) +{ + if (e.test(data_len < sizeof(uint16)*2, E_BADCLASSSIZE)) return ERROROFFSET; + + m_nClass = be::read(p); + m_nLinear = be::read(p); + + // Check that numLinear < numClass, + // that there is at least enough data for numClasses offsets. + if (e.test(m_nLinear > m_nClass, E_TOOMANYLINEAR) + || e.test((m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32) : sizeof(uint16)) > (data_len - 4), E_CLASSESTOOBIG)) + return ERROROFFSET; + + uint32 max_off; + if (version >= 0x00040000) + max_off = readClassOffsets(p, data_len, e); + else + max_off = readClassOffsets(p, data_len, e); + + if (max_off == ERROROFFSET) return ERROROFFSET; + + if (e.test((int)max_off < m_nLinear + (m_nClass - m_nLinear) * 6, E_CLASSESTOOBIG)) + return ERROROFFSET; + + // Check the linear offsets are sane, these must be monotonically increasing. + assert(m_nClass >= m_nLinear); + for (const uint32 *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o) + if (e.test(o[0] > o[1], E_BADCLASSOFFSET)) + return ERROROFFSET; + + // Fortunately the class data is all uint16s so we can decode these now + m_classData = gralloc(max_off); + if (e.test(!m_classData, E_OUTOFMEM)) return ERROROFFSET; + for (uint16 *d = m_classData, * const d_end = d + max_off; d != d_end; ++d) + *d = be::read(p); + + // Check the lookup class invariants for each non-linear class + for (const uint32 *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o) + { + const uint16 * lookup = m_classData + *o; + if (e.test(*o + 4 > max_off, E_HIGHCLASSOFFSET) // LookupClass doesn't stretch over max_off + || e.test(lookup[0] == 0 // A LookupClass with no looks is a suspicious thing ... + || lookup[0] * 2 + *o + 4 > max_off // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off] + || lookup[3] + lookup[1] != lookup[0], E_BADCLASSLOOKUPINFO) // rangeShift: numIDs - searchRange + || e.test(((o[1] - *o) & 1) != 0, ERROROFFSET)) // glyphs are in pairs so difference must be even. + return ERROROFFSET; + } + + return max_off; +} + +uint16 Silf::findPseudo(uint32 uid) const +{ + for (int i = 0; i < m_numPseudo; i++) + if (m_pseudos[i].uid == uid) return m_pseudos[i].gid; + return 0; +} + +uint16 Silf::findClassIndex(uint16 cid, uint16 gid) const +{ + if (cid > m_nClass) return -1; + + const uint16 * cls = m_classData + m_classOffsets[cid]; + if (cid < m_nLinear) // output class being used for input, shouldn't happen + { + for (unsigned int i = 0, n = m_classOffsets[cid + 1] - m_classOffsets[cid]; i < n; ++i, ++cls) + if (*cls == gid) return i; + return -1; + } + else + { + const uint16 * min = cls + 4, // lookups array + * max = min + cls[0]*2; // lookups aray is numIDs (cls[0]) uint16 pairs long + do + { + const uint16 * p = min + (-2 & ((max-min)/2)); + if (p[0] > gid) max = p; + else min = p; + } + while (max - min > 2); + return min[0] == gid ? min[1] : -1; + } +} + +uint16 Silf::getClassGlyph(uint16 cid, unsigned int index) const +{ + if (cid > m_nClass) return 0; + + uint32 loc = m_classOffsets[cid]; + if (cid < m_nLinear) + { + if (index < m_classOffsets[cid + 1] - loc) + return m_classData[index + loc]; + } + else // input class being used for output. Shouldn't happen + { + for (unsigned int i = loc + 4; i < m_classOffsets[cid + 1]; i += 2) + if (m_classData[i + 1] == index) return m_classData[i]; + } + return 0; +} + + +bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const +{ + assert(seg != 0); + size_t maxSize = seg->slotCount() * MAX_SEG_GROWTH_FACTOR; + SlotMap map(*seg, m_dir, maxSize); + FiniteStateMachine fsm(map, seg->getFace()->logger()); + vm::Machine m(map); + uint8 lbidi = m_bPass; +#if !defined GRAPHITE2_NTRACING + json * const dbgout = seg->getFace()->logger(); +#endif + + if (lastPass == 0) + { + if (firstPass == lastPass && lbidi == 0xFF) + return true; + lastPass = m_numPasses; + } + if ((firstPass < lbidi || (dobidi && firstPass == lbidi)) && (lastPass >= lbidi || (dobidi && lastPass + 1 == lbidi))) + lastPass++; + else + lbidi = 0xFF; + + for (size_t i = firstPass; i < lastPass; ++i) + { + // bidi and mirroring + if (i == lbidi) + { +#if !defined GRAPHITE2_NTRACING + if (dbgout) + { + *dbgout << json::item << json::object +// << "pindex" << i // for debugging + << "id" << -1 + << "slotsdir" << (seg->currdir() ? "rtl" : "ltr") + << "passdir" << (m_dir & 1 ? "rtl" : "ltr") + << "slots" << json::array; + seg->positionSlots(0, 0, 0, seg->currdir()); + for(Slot * s = seg->first(); s; s = s->next()) + *dbgout << dslot(seg, s); + *dbgout << json::close + << "rules" << json::array << json::close + << json::close; + } +#endif + if (seg->currdir() != (m_dir & 1)) + seg->reverseSlots(); + if (m_aMirror && (seg->dir() & 3) == 3) + seg->doMirror(m_aMirror); + --i; + lbidi = lastPass; + --lastPass; + continue; + } + +#if !defined GRAPHITE2_NTRACING + if (dbgout) + { + *dbgout << json::item << json::object +// << "pindex" << i // for debugging + << "id" << i+1 + << "slotsdir" << (seg->currdir() ? "rtl" : "ltr") + << "passdir" << ((m_dir & 1) ^ m_passes[i].reverseDir() ? "rtl" : "ltr") + << "slots" << json::array; + seg->positionSlots(0, 0, 0, seg->currdir()); + for(Slot * s = seg->first(); s; s = s->next()) + *dbgout << dslot(seg, s); + *dbgout << json::close; + } +#endif + + // test whether to reorder, prepare for positioning + bool reverse = (lbidi == 0xFF) && (seg->currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir())); + if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops()) + && !m_passes[i].runGraphite(m, fsm, reverse)) + return false; + // only subsitution passes can change segment length, cached subsegments are short for their text + if (m.status() != vm::Machine::finished + || (seg->slotCount() && seg->slotCount() > maxSize)) + return false; + } + return true; +} diff --git a/thirdparty/graphite/src/Slot.cpp b/thirdparty/graphite/src/Slot.cpp new file mode 100644 index 00000000000..0fdb0989526 --- /dev/null +++ b/thirdparty/graphite/src/Slot.cpp @@ -0,0 +1,529 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/Segment.h" +#include "inc/Slot.h" +#include "inc/Silf.h" +#include "inc/CharInfo.h" +#include "inc/Rule.h" +#include "inc/Collider.h" + + +using namespace graphite2; + +Slot::Slot(int16 *user_attrs) : + m_next(NULL), m_prev(NULL), + m_glyphid(0), m_realglyphid(0), m_original(0), m_before(0), m_after(0), + m_index(0), m_parent(NULL), m_child(NULL), m_sibling(NULL), + m_position(0, 0), m_shift(0, 0), m_advance(0, 0), + m_attach(0, 0), m_with(0, 0), m_just(0.), + m_flags(0), m_attLevel(0), m_bidiCls(-1), m_bidiLevel(0), + m_userAttr(user_attrs), m_justs(NULL) +{ +} + +// take care, this does not copy any of the GrSlot pointer fields +void Slot::set(const Slot & orig, int charOffset, size_t sizeAttr, size_t justLevels, size_t numChars) +{ + // leave m_next and m_prev unchanged + m_glyphid = orig.m_glyphid; + m_realglyphid = orig.m_realglyphid; + m_original = orig.m_original + charOffset; + if (charOffset + int(orig.m_before) < 0) + m_before = 0; + else + m_before = orig.m_before + charOffset; + if (charOffset <= 0 && orig.m_after + charOffset >= numChars) + m_after = int(numChars) - 1; + else + m_after = orig.m_after + charOffset; + m_parent = NULL; + m_child = NULL; + m_sibling = NULL; + m_position = orig.m_position; + m_shift = orig.m_shift; + m_advance = orig.m_advance; + m_attach = orig.m_attach; + m_with = orig.m_with; + m_flags = orig.m_flags; + m_attLevel = orig.m_attLevel; + m_bidiCls = orig.m_bidiCls; + m_bidiLevel = orig.m_bidiLevel; + if (m_userAttr && orig.m_userAttr) + memcpy(m_userAttr, orig.m_userAttr, sizeAttr * sizeof(*m_userAttr)); + if (m_justs && orig.m_justs) + memcpy(m_justs, orig.m_justs, SlotJustify::size_of(justLevels)); +} + +void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos) +{ + m_before += numCharInfo; + m_after += numCharInfo; + m_position = m_position + relpos; +} + +Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth) +{ + SlotCollision *coll = NULL; + if (depth > 100 || (attrLevel && m_attLevel > attrLevel)) return Position(0, 0); + float scale = font ? font->scale() : 1.0f; + Position shift(m_shift.x * (rtl * -2 + 1) + m_just, m_shift.y); + float tAdvance = m_advance.x + m_just; + if (isFinal && (coll = seg->collisionInfo(this))) + { + const Position &collshift = coll->offset(); + if (!(coll->flags() & SlotCollision::COLL_KERN) || rtl) + shift = shift + collshift; + } + const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(glyph()); + if (font) + { + scale = font->scale(); + shift *= scale; + if (font->isHinted() && glyphFace) + tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(glyph()); + else + tAdvance *= scale; + } + Position res; + + m_position = base + shift; + if (!m_parent) + { + res = base + Position(tAdvance, m_advance.y * scale); + clusterMin = m_position.x; + } + else + { + float tAdv; + m_position += (m_attach - m_with) * scale; + tAdv = m_advance.x >= 0.5f ? m_position.x + tAdvance - shift.x : 0.f; + res = Position(tAdv, 0); + if ((m_advance.x >= 0.5f || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x; + } + + if (glyphFace) + { + Rect ourBbox = glyphFace->theBBox() * scale + m_position; + bbox = bbox.widen(ourBbox); + } + + if (m_child && m_child != this && m_child->attachedTo() == this) + { + Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1); + if ((!m_parent || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes; + } + + if (m_parent && m_sibling && m_sibling != this && m_sibling->attachedTo() == m_parent) + { + Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1); + if (tRes.x > res.x) res = tRes; + } + + if (!m_parent && clusterMin < base.x) + { + Position adj = Position(m_position.x - clusterMin, 0.); + res += adj; + m_position += adj; + if (m_child) m_child->floodShift(adj); + } + return res; +} + +int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel, bool rtl) +{ + Position base; + if (glyph() >= seg->getFace()->glyphs().numGlyphs()) + return 0; + Rect bbox = seg->theGlyphBBoxTemporary(glyph()); + float clusterMin = 0.; + Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, rtl, false); + + switch (metrics(metric)) + { + case kgmetLsb : + return int32(bbox.bl.x); + case kgmetRsb : + return int32(res.x - bbox.tr.x); + case kgmetBbTop : + return int32(bbox.tr.y); + case kgmetBbBottom : + return int32(bbox.bl.y); + case kgmetBbLeft : + return int32(bbox.bl.x); + case kgmetBbRight : + return int32(bbox.tr.x); + case kgmetBbWidth : + return int32(bbox.tr.x - bbox.bl.x); + case kgmetBbHeight : + return int32(bbox.tr.y - bbox.bl.y); + case kgmetAdvWidth : + return int32(res.x); + case kgmetAdvHeight : + return int32(res.y); + default : + return 0; + } +} + +#define SLOTGETCOLATTR(x) { SlotCollision *c = seg->collisionInfo(this); return c ? int(c-> x) : 0; } + +int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const +{ + if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth) + { + int indx = ind - gr_slatJStretch; + return getJustify(seg, indx / 5, indx % 5); + } + + switch (ind) + { + case gr_slatAdvX : return int(m_advance.x); + case gr_slatAdvY : return int(m_advance.y); + case gr_slatAttTo : return m_parent ? 1 : 0; + case gr_slatAttX : return int(m_attach.x); + case gr_slatAttY : return int(m_attach.y); + case gr_slatAttXOff : + case gr_slatAttYOff : return 0; + case gr_slatAttWithX : return int(m_with.x); + case gr_slatAttWithY : return int(m_with.y); + case gr_slatAttWithXOff: + case gr_slatAttWithYOff:return 0; + case gr_slatAttLevel : return m_attLevel; + case gr_slatBreak : return seg->charinfo(m_original)->breakWeight(); + case gr_slatCompRef : return 0; + case gr_slatDir : return seg->dir() & 1; + case gr_slatInsert : return isInsertBefore(); + case gr_slatPosX : return int(m_position.x); // but need to calculate it + case gr_slatPosY : return int(m_position.y); + case gr_slatShiftX : return int(m_shift.x); + case gr_slatShiftY : return int(m_shift.y); + case gr_slatMeasureSol: return -1; // err what's this? + case gr_slatMeasureEol: return -1; + case gr_slatJWidth: return int(m_just); + case gr_slatUserDefnV1: subindex = 0; GR_FALLTHROUGH; + // no break + case gr_slatUserDefn : return subindex < seg->numAttrs() ? m_userAttr[subindex] : 0; + case gr_slatSegSplit : return seg->charinfo(m_original)->flags() & 3; + case gr_slatBidiLevel: return m_bidiLevel; + case gr_slatColFlags : { SlotCollision *c = seg->collisionInfo(this); return c ? c->flags() : 0; } + case gr_slatColLimitblx:SLOTGETCOLATTR(limit().bl.x) + case gr_slatColLimitbly:SLOTGETCOLATTR(limit().bl.y) + case gr_slatColLimittrx:SLOTGETCOLATTR(limit().tr.x) + case gr_slatColLimittry:SLOTGETCOLATTR(limit().tr.y) + case gr_slatColShiftx : SLOTGETCOLATTR(offset().x) + case gr_slatColShifty : SLOTGETCOLATTR(offset().y) + case gr_slatColMargin : SLOTGETCOLATTR(margin()) + case gr_slatColMarginWt:SLOTGETCOLATTR(marginWt()) + case gr_slatColExclGlyph:SLOTGETCOLATTR(exclGlyph()) + case gr_slatColExclOffx:SLOTGETCOLATTR(exclOffset().x) + case gr_slatColExclOffy:SLOTGETCOLATTR(exclOffset().y) + case gr_slatSeqClass : SLOTGETCOLATTR(seqClass()) + case gr_slatSeqProxClass:SLOTGETCOLATTR(seqProxClass()) + case gr_slatSeqOrder : SLOTGETCOLATTR(seqOrder()) + case gr_slatSeqAboveXoff:SLOTGETCOLATTR(seqAboveXoff()) + case gr_slatSeqAboveWt: SLOTGETCOLATTR(seqAboveWt()) + case gr_slatSeqBelowXlim:SLOTGETCOLATTR(seqBelowXlim()) + case gr_slatSeqBelowWt: SLOTGETCOLATTR(seqBelowWt()) + case gr_slatSeqValignHt:SLOTGETCOLATTR(seqValignHt()) + case gr_slatSeqValignWt:SLOTGETCOLATTR(seqValignWt()) + default : return 0; + } +} + +#define SLOTCOLSETATTR(x) { \ + SlotCollision *c = seg->collisionInfo(this); \ + if (c) { c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \ + break; } +#define SLOTCOLSETCOMPLEXATTR(t, y, x) { \ + SlotCollision *c = seg->collisionInfo(this); \ + if (c) { \ + const t &s = c-> y; \ + c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \ + break; } + +void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, const SlotMap & map) +{ + if (ind == gr_slatUserDefnV1) + { + ind = gr_slatUserDefn; + subindex = 0; + if (seg->numAttrs() == 0) + return; + } + else if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth) + { + int indx = ind - gr_slatJStretch; + return setJustify(seg, indx / 5, indx % 5, value); + } + + switch (ind) + { + case gr_slatAdvX : m_advance.x = value; break; + case gr_slatAdvY : m_advance.y = value; break; + case gr_slatAttTo : + { + const uint16 idx = uint16(value); + if (idx < map.size() && map[idx]) + { + Slot *other = map[idx]; + if (other == this || other == m_parent || other->isCopied()) break; + if (m_parent) { m_parent->removeChild(this); attachTo(NULL); } + Slot *pOther = other; + int count = 0; + bool foundOther = false; + while (pOther) + { + ++count; + if (pOther == this) foundOther = true; + pOther = pOther->attachedTo(); + } + for (pOther = m_child; pOther; pOther = pOther->m_child) + ++count; + for (pOther = m_sibling; pOther; pOther = pOther->m_sibling) + ++count; + if (count < 100 && !foundOther && other->child(this)) + { + attachTo(other); + if ((map.dir() != 0) ^ (idx > subindex)) + m_with = Position(advance(), 0); + else // normal match to previous root + m_attach = Position(other->advance(), 0); + } + } + break; + } + case gr_slatAttX : m_attach.x = value; break; + case gr_slatAttY : m_attach.y = value; break; + case gr_slatAttXOff : + case gr_slatAttYOff : break; + case gr_slatAttWithX : m_with.x = value; break; + case gr_slatAttWithY : m_with.y = value; break; + case gr_slatAttWithXOff : + case gr_slatAttWithYOff : break; + case gr_slatAttLevel : + m_attLevel = byte(value); + break; + case gr_slatBreak : + seg->charinfo(m_original)->breakWeight(value); + break; + case gr_slatCompRef : break; // not sure what to do here + case gr_slatDir : break; + case gr_slatInsert : + markInsertBefore(value? true : false); + break; + case gr_slatPosX : break; // can't set these here + case gr_slatPosY : break; + case gr_slatShiftX : m_shift.x = value; break; + case gr_slatShiftY : m_shift.y = value; break; + case gr_slatMeasureSol : break; + case gr_slatMeasureEol : break; + case gr_slatJWidth : just(value); break; + case gr_slatSegSplit : seg->charinfo(m_original)->addflags(value & 3); break; + case gr_slatUserDefn : m_userAttr[subindex] = value; break; + case gr_slatColFlags : { + SlotCollision *c = seg->collisionInfo(this); + if (c) + c->setFlags(value); + break; } + case gr_slatColLimitblx : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(value, s.bl.y), s.tr))) + case gr_slatColLimitbly : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(s.bl.x, value), s.tr))) + case gr_slatColLimittrx : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(value, s.tr.y)))) + case gr_slatColLimittry : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(s.tr.x, value)))) + case gr_slatColMargin : SLOTCOLSETATTR(setMargin(value)) + case gr_slatColMarginWt : SLOTCOLSETATTR(setMarginWt(value)) + case gr_slatColExclGlyph : SLOTCOLSETATTR(setExclGlyph(value)) + case gr_slatColExclOffx : SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(value, s.y))) + case gr_slatColExclOffy : SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(s.x, value))) + case gr_slatSeqClass : SLOTCOLSETATTR(setSeqClass(value)) + case gr_slatSeqProxClass : SLOTCOLSETATTR(setSeqProxClass(value)) + case gr_slatSeqOrder : SLOTCOLSETATTR(setSeqOrder(value)) + case gr_slatSeqAboveXoff : SLOTCOLSETATTR(setSeqAboveXoff(value)) + case gr_slatSeqAboveWt : SLOTCOLSETATTR(setSeqAboveWt(value)) + case gr_slatSeqBelowXlim : SLOTCOLSETATTR(setSeqBelowXlim(value)) + case gr_slatSeqBelowWt : SLOTCOLSETATTR(setSeqBelowWt(value)) + case gr_slatSeqValignHt : SLOTCOLSETATTR(setSeqValignHt(value)) + case gr_slatSeqValignWt : SLOTCOLSETATTR(setSeqValignWt(value)) + default : + break; + } +} + +int Slot::getJustify(const Segment *seg, uint8 level, uint8 subindex) const +{ + if (level && level >= seg->silf()->numJustLevels()) return 0; + + if (m_justs) + return m_justs->values[level * SlotJustify::NUMJUSTPARAMS + subindex]; + + if (level >= seg->silf()->numJustLevels()) return 0; + Justinfo *jAttrs = seg->silf()->justAttrs() + level; + + switch (subindex) { + case 0 : return seg->glyphAttr(gid(), jAttrs->attrStretch()); + case 1 : return seg->glyphAttr(gid(), jAttrs->attrShrink()); + case 2 : return seg->glyphAttr(gid(), jAttrs->attrStep()); + case 3 : return seg->glyphAttr(gid(), jAttrs->attrWeight()); + case 4 : return 0; // not been set yet, so clearly 0 + default: return 0; + } +} + +void Slot::setJustify(Segment *seg, uint8 level, uint8 subindex, int16 value) +{ + if (level && level >= seg->silf()->numJustLevels()) return; + if (!m_justs) + { + SlotJustify *j = seg->newJustify(); + if (!j) return; + j->LoadSlot(this, seg); + m_justs = j; + } + m_justs->values[level * SlotJustify::NUMJUSTPARAMS + subindex] = value; +} + +bool Slot::child(Slot *ap) +{ + if (this == ap) return false; + else if (ap == m_child) return true; + else if (!m_child) + m_child = ap; + else + return m_child->sibling(ap); + return true; +} + +bool Slot::sibling(Slot *ap) +{ + if (this == ap) return false; + else if (ap == m_sibling) return true; + else if (!m_sibling || !ap) + m_sibling = ap; + else + return m_sibling->sibling(ap); + return true; +} + +bool Slot::removeChild(Slot *ap) +{ + if (this == ap || !m_child || !ap) return false; + else if (ap == m_child) + { + Slot *nSibling = m_child->nextSibling(); + m_child->nextSibling(NULL); + m_child = nSibling; + return true; + } + for (Slot *p = m_child; p; p = p->m_sibling) + { + if (p->m_sibling && p->m_sibling == ap) + { + p->m_sibling = p->m_sibling->m_sibling; + ap->nextSibling(NULL); + return true; + } + } + return false; +} + +void Slot::setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph) +{ + m_glyphid = glyphid; + m_bidiCls = -1; + if (!theGlyph) + { + theGlyph = seg->getFace()->glyphs().glyphSafe(glyphid); + if (!theGlyph) + { + m_realglyphid = 0; + m_advance = Position(0.,0.); + return; + } + } + m_realglyphid = theGlyph->attrs()[seg->silf()->aPseudo()]; + if (m_realglyphid > seg->getFace()->glyphs().numGlyphs()) + m_realglyphid = 0; + const GlyphFace *aGlyph = theGlyph; + if (m_realglyphid) + { + aGlyph = seg->getFace()->glyphs().glyphSafe(m_realglyphid); + if (!aGlyph) aGlyph = theGlyph; + } + m_advance = Position(aGlyph->theAdvance().x, 0.); + if (seg->silf()->aPassBits()) + { + seg->mergePassBits(uint8(theGlyph->attrs()[seg->silf()->aPassBits()])); + if (seg->silf()->numPasses() > 16) + seg->mergePassBits(theGlyph->attrs()[seg->silf()->aPassBits()+1] << 16); + } +} + +void Slot::floodShift(Position adj, int depth) +{ + if (depth > 100) + return; + m_position += adj; + if (m_child) m_child->floodShift(adj, depth + 1); + if (m_sibling) m_sibling->floodShift(adj, depth + 1); +} + +void SlotJustify::LoadSlot(const Slot *s, const Segment *seg) +{ + for (int i = seg->silf()->numJustLevels() - 1; i >= 0; --i) + { + Justinfo *justs = seg->silf()->justAttrs() + i; + int16 *v = values + i * NUMJUSTPARAMS; + v[0] = seg->glyphAttr(s->gid(), justs->attrStretch()); + v[1] = seg->glyphAttr(s->gid(), justs->attrShrink()); + v[2] = seg->glyphAttr(s->gid(), justs->attrStep()); + v[3] = seg->glyphAttr(s->gid(), justs->attrWeight()); + } +} + +Slot * Slot::nextInCluster(const Slot *s) const +{ + Slot *base; + if (s->firstChild()) + return s->firstChild(); + else if (s->nextSibling()) + return s->nextSibling(); + while ((base = s->attachedTo())) + { + // if (base->firstChild() == s && base->nextSibling()) + if (base->nextSibling()) + return base->nextSibling(); + s = base; + } + return NULL; +} + +bool Slot::isChildOf(const Slot *base) const +{ + for (Slot *p = m_parent; p; p = p->m_parent) + if (p == base) + return true; + return false; +} diff --git a/thirdparty/graphite/src/Sparse.cpp b/thirdparty/graphite/src/Sparse.cpp new file mode 100644 index 00000000000..aa43113669b --- /dev/null +++ b/thirdparty/graphite/src/Sparse.cpp @@ -0,0 +1,62 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include +#include "inc/Sparse.h" +#include "inc/bits.h" + +using namespace graphite2; + +const sparse::chunk sparse::empty_chunk = {0,0}; + +sparse::~sparse() throw() +{ + if (m_array.map == &empty_chunk) return; + free(m_array.values); +} + + +sparse::mapped_type sparse::operator [] (const key_type k) const throw() +{ + mapped_type g = key_type(k/SIZEOF_CHUNK - m_nchunks) >> (sizeof k*8 - 1); + const chunk & c = m_array.map[g*k/SIZEOF_CHUNK]; + const mask_t m = c.mask >> (SIZEOF_CHUNK - 1 - (k%SIZEOF_CHUNK)); + g *= m & 1; + + return g*m_array.values[g*(c.offset + bit_set_count(m >> 1))]; +} + + +size_t sparse::capacity() const throw() +{ + size_t n = m_nchunks, + s = 0; + + for (const chunk *ci=m_array.map; n; --n, ++ci) + s += bit_set_count(ci->mask); + + return s; +} diff --git a/thirdparty/graphite/src/TtfUtil.cpp b/thirdparty/graphite/src/TtfUtil.cpp new file mode 100644 index 00000000000..2eb46a11fb3 --- /dev/null +++ b/thirdparty/graphite/src/TtfUtil.cpp @@ -0,0 +1,2053 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +/*--------------------------------------------------------------------*//*:Ignore this sentence. + +File: TtfUtil.cpp +Responsibility: Alan Ward +Last reviewed: Not yet. + +Description + Implements the methods for TtfUtil class. This file should remain portable to any C++ + environment by only using standard C++ and the TTF structurs defined in Tt.h. +-------------------------------------------------------------------------------*//*:End Ignore*/ + + +/*********************************************************************************************** + Include files +***********************************************************************************************/ +// Language headers +//#include +#include +#include +#include +#include +#include +//#include +// Platform headers +// Module headers +#include "inc/TtfUtil.h" +#include "inc/TtfTypes.h" +#include "inc/Endian.h" + +/*********************************************************************************************** + Forward declarations +***********************************************************************************************/ + +/*********************************************************************************************** + Local Constants and static variables +***********************************************************************************************/ +namespace +{ +#ifdef ALL_TTFUTILS + // max number of components allowed in composite glyphs + const int kMaxGlyphComponents = 8; +#endif + + template + inline float fixed_to_float(const T f) { + return float(f)/float(2^R); + } + +/*---------------------------------------------------------------------------------------------- + Table of standard Postscript glyph names. From Martin Hosken. Disagress with ttfdump.exe +---------------------------------------------------------------------------------------------*/ +#ifdef ALL_TTFUTILS + const int kcPostNames = 258; + + const char * rgPostName[kcPostNames] = { + ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quotesingle", "parenleft", + "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", + "nine", "colon", "semicolon", "less", "equal", "greater", "question", + "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "bracketleft", "backslash", "bracketright", "asciicircum", + "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", + "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", + "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", + "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", + "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", + "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", + "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", + "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", + "section", "bullet", "paragraph", "germandbls", "registered", + "copyright", "trademark", "acute", "dieresis", "notequal", "AE", + "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", + "mu", "partialdiff", "summation", "product", "pi", "integral", + "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", + "exclamdown", "logicalnot", "radical", "florin", "approxequal", + "Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace", + "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", + "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", + "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", + "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", + "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", + "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", + "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", + "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", + "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", + "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", + "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", + "Yacute", "yacute", "Thorn", "thorn", "minus", "multiply", + "onesuperior", "twosuperior", "threesuperior", "onehalf", "onequarter", + "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla", + "scedilla", "Cacute", "cacute", "Ccaron", "ccaron", + "dcroat" }; +#endif + +} // end of namespace + +/*********************************************************************************************** + Methods +***********************************************************************************************/ + +/* Note on error processing: The code guards against bad glyph ids being used to look up data +in open ended tables (loca, hmtx). If the glyph id comes from a cmap this shouldn't happen +but it seems prudent to check for user errors here. The code does assume that data obtained +from the TTF file is valid otherwise (though the CheckTable method seeks to check for +obvious problems that might accompany a change in table versions). For example an invalid +offset in the loca table which could exceed the size of the glyf table is NOT trapped. +Likewise if numberOf_LongHorMetrics in the hhea table is wrong, this will NOT be trapped, +which could cause a lookup in the hmtx table to exceed the table length. Of course, TTF tables +that are completely corrupt will cause unpredictable results. */ + +/* Note on composite glyphs: Glyphs that have components that are themselves composites +are not supported. IsDeepComposite can be used to test for this. False is returned from many +of the methods in this cases. It is unclear how to build composite glyphs in some cases, +so this code represents my best guess until test cases can be found. See notes on the high- +level GlyfPoints method. */ +namespace graphite2 +{ +namespace TtfUtil +{ + + +/*---------------------------------------------------------------------------------------------- + Get offset and size of the offset table needed to find table directory. + Return true if success, false otherwise. + lSize excludes any table directory entries. +----------------------------------------------------------------------------------------------*/ +bool GetHeaderInfo(size_t & lOffset, size_t & lSize) +{ + lOffset = 0; + lSize = offsetof(Sfnt::OffsetSubTable, table_directory); + assert(sizeof(uint32) + 4*sizeof (uint16) == lSize); + return true; +} + +/*---------------------------------------------------------------------------------------------- + Check the offset table for expected data. + Return true if success, false otherwise. +----------------------------------------------------------------------------------------------*/ +bool CheckHeader(const void * pHdr) +{ + const Sfnt::OffsetSubTable * pOffsetTable + = reinterpret_cast(pHdr); + + return pHdr && be::swap(pOffsetTable->scaler_type) == Sfnt::OffsetSubTable::TrueTypeWin; +} + +/*---------------------------------------------------------------------------------------------- + Get offset and size of the table directory. + Return true if successful, false otherwise. +----------------------------------------------------------------------------------------------*/ +bool GetTableDirInfo(const void * pHdr, size_t & lOffset, size_t & lSize) +{ + const Sfnt::OffsetSubTable * pOffsetTable + = reinterpret_cast(pHdr); + + lOffset = offsetof(Sfnt::OffsetSubTable, table_directory); + lSize = be::swap(pOffsetTable->num_tables) + * sizeof(Sfnt::OffsetSubTable::Entry); + + return true; +} + + +/*---------------------------------------------------------------------------------------------- + Get offset and size of the specified table. + Return true if successful, false otherwise. On false, offset and size will be 0. +----------------------------------------------------------------------------------------------*/ +bool GetTableInfo(const Tag TableTag, const void * pHdr, const void * pTableDir, + size_t & lOffset, size_t & lSize) +{ + const Sfnt::OffsetSubTable * pOffsetTable + = reinterpret_cast(pHdr); + const size_t num_tables = be::swap(pOffsetTable->num_tables); + const Sfnt::OffsetSubTable::Entry + * entry_itr = reinterpret_cast( + pTableDir), + * const dir_end = entry_itr + num_tables; + + if (num_tables > 40) + return false; + + for (;entry_itr != dir_end; ++entry_itr) // 40 - safe guard + { + if (be::swap(entry_itr->tag) == TableTag) + { + lOffset = be::swap(entry_itr->offset); + lSize = be::swap(entry_itr->length); + return true; + } + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Check the specified table. Tests depend on the table type. + Return true if successful, false otherwise. +----------------------------------------------------------------------------------------------*/ +bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize) +{ + using namespace Sfnt; + + if (pTable == 0 || lTableSize < 4) return false; + + switch(TableId) + { + case Tag::cmap: // cmap + { + const Sfnt::CharacterCodeMap * const pCmap + = reinterpret_cast(pTable); + if (lTableSize < sizeof(Sfnt::CharacterCodeMap)) + return false; + return be::swap(pCmap->version) == 0; + } + + case Tag::head: // head + { + const Sfnt::FontHeader * const pHead + = reinterpret_cast(pTable); + if (lTableSize < sizeof(Sfnt::FontHeader)) + return false; + bool r = be::swap(pHead->version) == OneFix + && be::swap(pHead->magic_number) == FontHeader::MagicNumber + && be::swap(pHead->glyph_data_format) + == FontHeader::GlypDataFormat + && (be::swap(pHead->index_to_loc_format) + == FontHeader::ShortIndexLocFormat + || be::swap(pHead->index_to_loc_format) + == FontHeader::LongIndexLocFormat) + && sizeof(FontHeader) <= lTableSize; + return r; + } + + case Tag::post: // post + { + const Sfnt::PostScriptGlyphName * const pPost + = reinterpret_cast(pTable); + if (lTableSize < sizeof(Sfnt::PostScriptGlyphName)) + return false; + const fixed format = be::swap(pPost->format); + bool r = format == PostScriptGlyphName::Format1 + || format == PostScriptGlyphName::Format2 + || format == PostScriptGlyphName::Format3 + || format == PostScriptGlyphName::Format25; + return r; + } + + case Tag::hhea: // hhea + { + const Sfnt::HorizontalHeader * pHhea = + reinterpret_cast(pTable); + if (lTableSize < sizeof(Sfnt::HorizontalHeader)) + return false; + bool r = be::swap(pHhea->version) == OneFix + && be::swap(pHhea->metric_data_format) == 0 + && sizeof (Sfnt::HorizontalHeader) <= lTableSize; + return r; + } + + case Tag::maxp: // maxp + { + const Sfnt::MaximumProfile * pMaxp = + reinterpret_cast(pTable); + if (lTableSize < sizeof(Sfnt::MaximumProfile)) + return false; + bool r = be::swap(pMaxp->version) == OneFix + && sizeof(Sfnt::MaximumProfile) <= lTableSize; + return r; + } + + case Tag::OS_2: // OS/2 + { + const Sfnt::Compatibility * pOs2 + = reinterpret_cast(pTable); + if (be::swap(pOs2->version) == 0) + { // OS/2 table version 1 size +// if (sizeof(Sfnt::Compatibility) +// - sizeof(uint32)*2 - sizeof(int16)*2 +// - sizeof(uint16)*3 <= lTableSize) + if (sizeof(Sfnt::Compatibility0) <= lTableSize) + return true; + } + else if (be::swap(pOs2->version) == 1) + { // OS/2 table version 2 size +// if (sizeof(Sfnt::Compatibility) +// - sizeof(int16) *2 +// - sizeof(uint16)*3 <= lTableSize) + if (sizeof(Sfnt::Compatibility1) <= lTableSize) + return true; + } + else if (be::swap(pOs2->version) == 2) + { // OS/2 table version 3 size + if (sizeof(Sfnt::Compatibility2) <= lTableSize) + return true; + } + else if (be::swap(pOs2->version) == 3 || be::swap(pOs2->version) == 4) + { // OS/2 table version 4 size - version 4 changed the meaning of some fields which we don't use + if (sizeof(Sfnt::Compatibility3) <= lTableSize) + return true; + } + else + return false; + break; + } + + case Tag::name: + { + const Sfnt::FontNames * pName + = reinterpret_cast(pTable); + if (lTableSize < sizeof(Sfnt::FontNames)) + return false; + return be::swap(pName->format) == 0; + } + + case Tag::glyf: + { + return (lTableSize >= sizeof(Sfnt::Glyph)); + } + + default: + break; + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return the number of glyphs in the font. Should never be less than zero. + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +size_t GlyphCount(const void * pMaxp) +{ + const Sfnt::MaximumProfile * pTable = + reinterpret_cast(pMaxp); + return be::swap(pTable->num_glyphs); +} + +#ifdef ALL_TTFUTILS +/*---------------------------------------------------------------------------------------------- + Return the maximum number of components for any composite glyph in the font. + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +size_t MaxCompositeComponentCount(const void * pMaxp) +{ + const Sfnt::MaximumProfile * pTable = + reinterpret_cast(pMaxp); + return be::swap(pTable->max_component_elements); +} + +/*---------------------------------------------------------------------------------------------- + Composite glyphs can be composed of glyphs that are themselves composites. + This method returns the maximum number of levels like this for any glyph in the font. + A non-composite glyph has a level of 1. + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +size_t MaxCompositeLevelCount(const void * pMaxp) +{ + const Sfnt::MaximumProfile * pTable = + reinterpret_cast(pMaxp); + return be::swap(pTable->max_component_depth); +} + +/*---------------------------------------------------------------------------------------------- + Return the number of glyphs in the font according to a differt source. + Should never be less than zero. Return -1 on failure. + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +size_t LocaGlyphCount(size_t lLocaSize, const void * pHead) //throw(std::domain_error) +{ + + const Sfnt::FontHeader * pTable + = reinterpret_cast(pHead); + + if (be::swap(pTable->index_to_loc_format) + == Sfnt::FontHeader::ShortIndexLocFormat) + // loca entries are two bytes and have been divided by two + return (lLocaSize >> 1) - 1; + + if (be::swap(pTable->index_to_loc_format) + == Sfnt::FontHeader::LongIndexLocFormat) + // loca entries are four bytes + return (lLocaSize >> 2) - 1; + + return -1; + //throw std::domain_error("head table in inconsistent state. The font may be corrupted"); +} +#endif + +/*---------------------------------------------------------------------------------------------- + Return the design units the font is designed with +----------------------------------------------------------------------------------------------*/ +int DesignUnits(const void * pHead) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast(pHead); + + return be::swap(pTable->units_per_em); +} + +#ifdef ALL_TTFUTILS +/*---------------------------------------------------------------------------------------------- + Return the checksum from the head table, which serves as a unique identifer for the font. +----------------------------------------------------------------------------------------------*/ +int HeadTableCheckSum(const void * pHead) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast(pHead); + + return be::swap(pTable->check_sum_adjustment); +} + +/*---------------------------------------------------------------------------------------------- + Return the create time from the head table. This consists of a 64-bit integer, which + we return here as two 32-bit integers. + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +void HeadTableCreateTime(const void * pHead, + unsigned int * pnDateBC, unsigned int * pnDateAD) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast(pHead); + + *pnDateBC = be::swap(pTable->created[0]); + *pnDateAD = be::swap(pTable->created[1]); +} + +/*---------------------------------------------------------------------------------------------- + Return the modify time from the head table.This consists of a 64-bit integer, which + we return here as two 32-bit integers. + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +void HeadTableModifyTime(const void * pHead, + unsigned int * pnDateBC, unsigned int *pnDateAD) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast(pHead); + ; + *pnDateBC = be::swap(pTable->modified[0]); + *pnDateAD = be::swap(pTable->modified[1]); +} + +/*---------------------------------------------------------------------------------------------- + Return true if the font is italic. +----------------------------------------------------------------------------------------------*/ +bool IsItalic(const void * pHead) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast(pHead); + + return ((be::swap(pTable->mac_style) & 0x00000002) != 0); +} + +/*---------------------------------------------------------------------------------------------- + Return the ascent for the font +----------------------------------------------------------------------------------------------*/ +int FontAscent(const void * pOs2) +{ + const Sfnt::Compatibility * pTable = reinterpret_cast(pOs2); + + return be::swap(pTable->win_ascent); +} + +/*---------------------------------------------------------------------------------------------- + Return the descent for the font +----------------------------------------------------------------------------------------------*/ +int FontDescent(const void * pOs2) +{ + const Sfnt::Compatibility * pTable = reinterpret_cast(pOs2); + + return be::swap(pTable->win_descent); +} + +/*---------------------------------------------------------------------------------------------- + Get the bold and italic style bits. + Return true if successful. false otherwise. + In addition to checking the OS/2 table, one could also check + the head table's macStyle field (overridden by the OS/2 table on Win) + the sub-family name in the name table (though this can contain oblique, dark, etc too) +----------------------------------------------------------------------------------------------*/ +bool FontOs2Style(const void *pOs2, bool & fBold, bool & fItalic) +{ + const Sfnt::Compatibility * pTable = reinterpret_cast(pOs2); + + fBold = (be::swap(pTable->fs_selection) & Sfnt::Compatibility::Bold) != 0; + fItalic = (be::swap(pTable->fs_selection) & Sfnt::Compatibility::Italic) != 0; + + return true; +} +#endif + +/*---------------------------------------------------------------------------------------------- + Method for searching name table. +----------------------------------------------------------------------------------------------*/ +bool GetNameInfo(const void * pName, int nPlatformId, int nEncodingId, + int nLangId, int nNameId, size_t & lOffset, size_t & lSize) +{ + lOffset = 0; + lSize = 0; + + const Sfnt::FontNames * pTable = reinterpret_cast(pName); + uint16 cRecord = be::swap(pTable->count); + uint16 nRecordOffset = be::swap(pTable->string_offset); + const Sfnt::NameRecord * pRecord = reinterpret_cast(pTable + 1); + + for (int i = 0; i < cRecord; ++i) + { + if (be::swap(pRecord->platform_id) == nPlatformId && + be::swap(pRecord->platform_specific_id) == nEncodingId && + be::swap(pRecord->language_id) == nLangId && + be::swap(pRecord->name_id) == nNameId) + { + lOffset = be::swap(pRecord->offset) + nRecordOffset; + lSize = be::swap(pRecord->length); + return true; + } + pRecord++; + } + + return false; +} + +#ifdef ALL_TTFUTILS +/*---------------------------------------------------------------------------------------------- + Return all the lang-IDs that have data for the given name-IDs. Assume that there is room + in the return array (langIdList) for 128 items. The purpose of this method is to return + a list of all possible lang-IDs. +----------------------------------------------------------------------------------------------*/ +int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId, + int * nameIdList, int cNameIds, short * langIdList) +{ + const Sfnt::FontNames * pTable = reinterpret_cast(pName); + int cLangIds = 0; + uint16 cRecord = be::swap(pTable->count); + if (cRecord > 127) return cLangIds; + //uint16 nRecordOffset = swapw(pTable->stringOffset); + const Sfnt::NameRecord * pRecord = reinterpret_cast(pTable + 1); + + for (int i = 0; i < cRecord; ++i) + { + if (be::swap(pRecord->platform_id) == nPlatformId && + be::swap(pRecord->platform_specific_id) == nEncodingId) + { + bool fNameFound = false; + int nLangId = be::swap(pRecord->language_id); + int nNameId = be::swap(pRecord->name_id); + for (int j = 0; j < cNameIds; j++) + { + if (nNameId == nameIdList[j]) + { + fNameFound = true; + break; + } + } + if (fNameFound) + { + // Add it if it's not there. + int ilang; + for (ilang = 0; ilang < cLangIds; ilang++) + if (langIdList[ilang] == nLangId) + break; + if (ilang >= cLangIds) + { + langIdList[cLangIds] = short(nLangId); + cLangIds++; + } + if (cLangIds == 128) + return cLangIds; + } + } + pRecord++; + } + + return cLangIds; +} + +/*---------------------------------------------------------------------------------------------- + Get the offset and size of the font family name in English for the MS Platform with Unicode + writing system. The offset is within the pName data. The string is double byte with MSB + first. +----------------------------------------------------------------------------------------------*/ +bool Get31EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize) +{ + return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 1, 1033, + Sfnt::NameRecord::Family, lOffset, lSize); +} + +/*---------------------------------------------------------------------------------------------- + Get the offset and size of the full font name in English for the MS Platform with Unicode + writing system. The offset is within the pName data. The string is double byte with MSB + first. + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +bool Get31EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize) +{ + return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 1, 1033, + Sfnt::NameRecord::Fullname, lOffset, lSize); +} + +/*---------------------------------------------------------------------------------------------- + Get the offset and size of the font family name in English for the MS Platform with Symbol + writing system. The offset is within the pName data. The string is double byte with MSB + first. +----------------------------------------------------------------------------------------------*/ +bool Get30EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize) +{ + return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 0, 1033, + Sfnt::NameRecord::Family, lOffset, lSize); +} + +/*---------------------------------------------------------------------------------------------- + Get the offset and size of the full font name in English for the MS Platform with Symbol + writing system. The offset is within the pName data. The string is double byte with MSB + first. + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +bool Get30EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize) +{ + return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 0, 1033, + Sfnt::NameRecord::Fullname, lOffset, lSize); +} + +/*---------------------------------------------------------------------------------------------- + Return the Glyph ID for a given Postscript name. This method finds the first glyph which + matches the requested Postscript name. Ideally every glyph should have a unique Postscript + name (except for special names such as .notdef), but this is not always true. + On failure return value less than zero. + -1 - table search failed + -2 - format 3 table (no Postscript glyph info) + -3 - other failures + + Note: this method is not currently used by the Graphite engine. +----------------------------------------------------------------------------------------------*/ +int PostLookup(const void * pPost, size_t lPostSize, const void * pMaxp, + const char * pPostName) +{ + using namespace Sfnt; + + const Sfnt::PostScriptGlyphName * pTable + = reinterpret_cast(pPost); + fixed format = be::swap(pTable->format); + + if (format == PostScriptGlyphName::Format3) + { // format 3 - no Postscript glyph info in font + return -2; + } + + // search for given Postscript name among the standard names + int iPostName = -1; // index in standard names + for (int i = 0; i < kcPostNames; i++) + { + if (!strcmp(pPostName, rgPostName[i])) + { + iPostName = i; + break; + } + } + + if (format == PostScriptGlyphName::Format1) + { // format 1 - use standard Postscript names + return iPostName; + } + + if (format == PostScriptGlyphName::Format25) + { + if (iPostName == -1) + return -1; + + const PostScriptGlyphName25 * pTable25 + = static_cast(pTable); + int cnGlyphs = GlyphCount(pMaxp); + for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs && nGlyphId < kcPostNames; + nGlyphId++) + { // glyph_name_index25 contains bytes so no byte swapping needed + // search for first glyph id that uses the standard name + if (nGlyphId + pTable25->offset[nGlyphId] == iPostName) + return nGlyphId; + } + } + + if (format == PostScriptGlyphName::Format2) + { // format 2 + const PostScriptGlyphName2 * pTable2 + = static_cast(pTable); + + int cnGlyphs = be::swap(pTable2->number_of_glyphs); + + if (iPostName != -1) + { // did match a standard name, look for first glyph id mapped to that name + for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs; nGlyphId++) + { + if (be::swap(pTable2->glyph_name_index[nGlyphId]) == iPostName) + return nGlyphId; + } + } + + { // did not match a standard name, search font specific names + size_t nStrSizeGoal = strlen(pPostName); + const char * pFirstGlyphName = reinterpret_cast( + &pTable2->glyph_name_index[0] + cnGlyphs); + const char * pGlyphName = pFirstGlyphName; + int iInNames = 0; // index in font specific names + bool fFound = false; + const char * const endOfTable + = reinterpret_cast(pTable2) + lPostSize; + while (pGlyphName < endOfTable && !fFound) + { // search Pascal strings for first matching name + size_t nStringSize = size_t(*pGlyphName); + if (nStrSizeGoal != nStringSize || + strncmp(pGlyphName + 1, pPostName, nStringSize)) + { // did not match + ++iInNames; + pGlyphName += nStringSize + 1; + } + else + { // did match + fFound = true; + } + } + if (!fFound) + return -1; // no font specific name matches request + + iInNames += kcPostNames; + for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs; nGlyphId++) + { // search for first glyph id that maps to the found string index + if (be::swap(pTable2->glyph_name_index[nGlyphId]) == iInNames) + return nGlyphId; + } + return -1; // no glyph mapped to this index (very strange) + } + } + + return -3; +} + +/*---------------------------------------------------------------------------------------------- + Convert a Unicode character string from big endian (MSB first, Motorola) format to little + endian (LSB first, Intel) format. + nSize is the number of Unicode characters in the string. It should not include any + terminating null. If nSize is 0, it is assumed the string is null terminated. nSize + defaults to 0. + Return true if successful, false otherwise. +----------------------------------------------------------------------------------------------*/ +void SwapWString(void * pWStr, size_t nSize /* = 0 */) //throw (std::invalid_argument) +{ + if (pWStr == 0) + { +// throw std::invalid_argument("null pointer given"); + return; + } + + uint16 * pStr = reinterpret_cast(pWStr); + uint16 * const pStrEnd = pStr + (nSize == 0 ? wcslen((const wchar_t*)pStr) : nSize); + + for (; pStr != pStrEnd; ++pStr) + *pStr = be::swap(*pStr); +// std::transform(pStr, pStrEnd, pStr, read); + +// for (int i = 0; i < nSize; i++) +// { // swap the wide characters in the string +// pStr[i] = utf16(be::swap(uint16(pStr[i]))); +// } +} +#endif + +/*---------------------------------------------------------------------------------------------- + Get the left-side bearing and and advance width based on the given tables and Glyph ID + Return true if successful, false otherwise. On false, one or both value could be INT_MIN +----------------------------------------------------------------------------------------------*/ +bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, const void * pHhea, + int & nLsb, unsigned int & nAdvWid) +{ + const Sfnt::HorizontalMetric * phmtx = + reinterpret_cast(pHmtx); + + const Sfnt::HorizontalHeader * phhea = + reinterpret_cast(pHhea); + + size_t cLongHorMetrics = be::swap(phhea->num_long_hor_metrics); + if (nGlyphId < cLongHorMetrics) + { // glyph id is acceptable + if ((nGlyphId + 1) * sizeof(Sfnt::HorizontalMetric) > lHmtxSize) return false; + nAdvWid = be::swap(phmtx[nGlyphId].advance_width); + nLsb = be::swap(phmtx[nGlyphId].left_side_bearing); + } + else + { + // guard against bad glyph id + size_t lLsbOffset = sizeof(Sfnt::HorizontalMetric) * cLongHorMetrics + + sizeof(int16) * (nGlyphId - cLongHorMetrics); // offset in bytes + // We test like this as LsbOffset is an offset not a length. + if (lLsbOffset >= lHmtxSize - sizeof(int16) || cLongHorMetrics == 0) + { + nLsb = 0; + return false; + } + nAdvWid = be::swap(phmtx[cLongHorMetrics - 1].advance_width); + nLsb = be::peek(reinterpret_cast(phmtx) + lLsbOffset); + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return a pointer to the requested cmap subtable. By default find the Microsoft Unicode + subtable. Pass nEncoding as -1 to find first table that matches only nPlatformId. + Return NULL if the subtable cannot be found. +----------------------------------------------------------------------------------------------*/ +const void * FindCmapSubtable(const void * pCmap, int nPlatformId, /* =3 */ int nEncodingId, /* = 1 */ size_t length) +{ + const Sfnt::CharacterCodeMap * pTable = reinterpret_cast(pCmap); + uint16 csuPlatforms = be::swap(pTable->num_subtables); + if (length && (sizeof(Sfnt::CharacterCodeMap) + 8 * (csuPlatforms - 1) > length)) + return NULL; + for (int i = 0; i < csuPlatforms; i++) + { + if (be::swap(pTable->encoding[i].platform_id) == nPlatformId && + (nEncodingId == -1 || be::swap(pTable->encoding[i].platform_specific_id) == nEncodingId)) + { + uint32 offset = be::swap(pTable->encoding[i].offset); + const uint8 * pRtn = reinterpret_cast(pCmap) + offset; + if (length) + { + if (offset > length - 2) return NULL; + uint16 format = be::read(pRtn); + if (format == 4) + { + if (offset > length - 4) return NULL; + uint16 subTableLength = be::peek(pRtn); + if (i + 1 == csuPlatforms) + { + if (subTableLength > length - offset) + return NULL; + } + else if (subTableLength > be::swap(pTable->encoding[i+1].offset)) + return NULL; + } + if (format == 12) + { + if (offset > length - 6) return NULL; + uint32 subTableLength = be::peek(pRtn); + if (i + 1 == csuPlatforms) + { + if (subTableLength > length - offset) + return NULL; + } + else if (subTableLength > be::swap(pTable->encoding[i+1].offset)) + return NULL; + } + } + return reinterpret_cast(pCmap) + offset; + } + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Check the Microsoft Unicode subtable for expected values +----------------------------------------------------------------------------------------------*/ +bool CheckCmapSubtable4(const void * pCmapSubtable4, const void * pCmapEnd /*, unsigned int maxgid*/) +{ + size_t table_len = (const byte *)pCmapEnd - (const byte *)pCmapSubtable4; + if (!pCmapSubtable4) return false; + const Sfnt::CmapSubTable * pTable = reinterpret_cast(pCmapSubtable4); + // Bob H say some freeware TT fonts have version 1 (eg, CALIGULA.TTF) + // so don't check subtable version. 21 Mar 2002 spec changes version to language. + if (table_len < sizeof(*pTable) || be::swap(pTable->format) != 4) return false; + const Sfnt::CmapSubTableFormat4 * pTable4 = reinterpret_cast(pCmapSubtable4); + if (table_len < sizeof(*pTable4)) + return false; + uint16 length = be::swap(pTable4->length); + if (length > table_len) + return false; + if (length < sizeof(Sfnt::CmapSubTableFormat4)) + return false; + uint16 nRanges = be::swap(pTable4->seg_count_x2) >> 1; + if (!nRanges || length < sizeof(Sfnt::CmapSubTableFormat4) + 4 * nRanges * sizeof(uint16)) + return false; + // check last range is properly terminated + uint16 chEnd = be::peek(pTable4->end_code + nRanges - 1); + if (chEnd != 0xFFFF) + return false; +#if 0 + int lastend = -1; + for (int i = 0; i < nRanges; ++i) + { + uint16 end = be::peek(pTable4->end_code + i); + uint16 start = be::peek(pTable4->end_code + nRanges + 1 + i); + int16 delta = be::peek(pTable4->end_code + 2*nRanges + 1 + i); + uint16 offset = be::peek(pTable4->end_code + 3*nRanges + 1 + i); + if (lastend >= end || lastend >= start) + return false; + if (offset) + { + const uint16 *gstart = pTable4->end_code + 3*nRanges + 1 + i + (offset >> 1); + const uint16 *gend = gstart + end - start; + if ((char *)gend >= (char *)pCmapSubtable4 + length) + return false; + while (gstart <= gend) + { + uint16 g = be::peek(gstart++); + if (g && ((g + delta) & 0xFFFF) > maxgid) + return false; + } + } + else if (((delta + end) & 0xFFFF) > maxgid) + return false; + lastend = end; + } +#endif + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return the Glyph ID for the given Unicode ID in the Microsoft Unicode subtable. + (Actually this code only depends on subtable being format 4.) + Return 0 if the Unicode ID is not in the subtable. +----------------------------------------------------------------------------------------------*/ +gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey) +{ + const Sfnt::CmapSubTableFormat4 * pTable = reinterpret_cast(pCmapSubtabel4); + + uint16 nSeg = be::swap(pTable->seg_count_x2) >> 1; + + uint16 n; + const uint16 * pLeft, * pMid; + uint16 cMid, chStart, chEnd; + + if (rangeKey) + { + pMid = &(pTable->end_code[rangeKey]); + chEnd = be::peek(pMid); + } + else + { + // Binary search of the endCode[] array + pLeft = &(pTable->end_code[0]); + n = nSeg; + while (n > 0) + { + cMid = n >> 1; // Pick an element in the middle + pMid = pLeft + cMid; + chEnd = be::peek(pMid); + if (nUnicodeId <= chEnd) + { + if (cMid == 0 || nUnicodeId > be::peek(pMid -1)) + break; // Must be this seg or none! + n = cMid; // Continue on left side, omitting mid point + } + else + { + pLeft = pMid + 1; // Continue on right side, omitting mid point + n -= (cMid + 1); + } + } + + if (!n) + return 0; + } + + // Ok, we're down to one segment and pMid points to the endCode element + // Either this is it or none is. + + chStart = be::peek(pMid += nSeg + 1); + if (chEnd >= nUnicodeId && nUnicodeId >= chStart) + { + // Found correct segment. Find Glyph Id + int16 idDelta = be::peek(pMid += nSeg); + uint16 idRangeOffset = be::peek(pMid += nSeg); + + if (idRangeOffset == 0) + return (uint16)(idDelta + nUnicodeId); // must use modulus 2^16 + + // Look up value in glyphIdArray + const ptrdiff_t offset = (nUnicodeId - chStart) + (idRangeOffset >> 1) + + (pMid - reinterpret_cast(pTable)); + if (offset * 2 + 1 >= be::swap(pTable->length)) + return 0; + gid16 nGlyphId = be::peek(reinterpret_cast(pTable)+offset); + // If this value is 0, return 0. Else add the idDelta + return nGlyphId ? nGlyphId + idDelta : 0; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the next Unicode value in the cmap. Pass 0 to obtain the first item. + Returns 0xFFFF as the last item. + pRangeKey is an optional key that is used to optimize the search; its value is the range + in which the character is found. +----------------------------------------------------------------------------------------------*/ +unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId, int * pRangeKey) +{ + const Sfnt::CmapSubTableFormat4 * pTable = reinterpret_cast(pCmap31); + + uint16 nRange = be::swap(pTable->seg_count_x2) >> 1; + + uint32 nUnicodePrev = (uint32)nUnicodeId; + + const uint16 * pStartCode = &(pTable->end_code[0]) + + nRange // length of end code array + + 1; // reserved word + + if (nUnicodePrev == 0) + { + // return the first codepoint. + if (pRangeKey) + *pRangeKey = 0; + return be::peek(pStartCode); + } + else if (nUnicodePrev >= 0xFFFF) + { + if (pRangeKey) + *pRangeKey = nRange - 1; + return 0xFFFF; + } + + int iRange = (pRangeKey) ? *pRangeKey : 0; + // Just in case we have a bad key: + while (iRange > 0 && be::peek(pStartCode + iRange) > nUnicodePrev) + iRange--; + while (iRange < nRange - 1 && be::peek(pTable->end_code + iRange) < nUnicodePrev) + iRange++; + + // Now iRange is the range containing nUnicodePrev. + unsigned int nStartCode = be::peek(pStartCode + iRange); + unsigned int nEndCode = be::peek(pTable->end_code + iRange); + + if (nStartCode > nUnicodePrev) + // Oops, nUnicodePrev is not in the cmap! Adjust so we get a reasonable + // answer this time around. + nUnicodePrev = nStartCode - 1; + + if (nEndCode > nUnicodePrev) + { + // Next is in the same range; it is the next successive codepoint. + if (pRangeKey) + *pRangeKey = iRange; + return nUnicodePrev + 1; + } + + // Otherwise the next codepoint is the first one in the next range. + // There is guaranteed to be a next range because there must be one that + // ends with 0xFFFF. + if (pRangeKey) + *pRangeKey = iRange + 1; + return (iRange + 1 >= nRange) ? 0xFFFF : be::peek(pStartCode + iRange + 1); +} + +/*---------------------------------------------------------------------------------------------- + Check the Microsoft UCS-4 subtable for expected values. +----------------------------------------------------------------------------------------------*/ +bool CheckCmapSubtable12(const void *pCmapSubtable12, const void *pCmapEnd /*, unsigned int maxgid*/) +{ + size_t table_len = (const byte *)pCmapEnd - (const byte *)pCmapSubtable12; + if (!pCmapSubtable12) return false; + const Sfnt::CmapSubTable * pTable = reinterpret_cast(pCmapSubtable12); + if (table_len < sizeof(*pTable) || be::swap(pTable->format) != 12) + return false; + const Sfnt::CmapSubTableFormat12 * pTable12 = reinterpret_cast(pCmapSubtable12); + if (table_len < sizeof(*pTable12)) + return false; + uint32 length = be::swap(pTable12->length); + if (length > table_len) + return false; + if (length < sizeof(Sfnt::CmapSubTableFormat12)) + return false; + uint32 num_groups = be::swap(pTable12->num_groups); + if (num_groups > 0x10000000 || length != (sizeof(Sfnt::CmapSubTableFormat12) + (num_groups - 1) * sizeof(uint32) * 3)) + return false; +#if 0 + for (unsigned int i = 0; i < num_groups; ++i) + { + if (be::swap(pTable12->group[i].end_char_code) - be::swap(pTable12->group[i].start_char_code) + be::swap(pTable12->group[i].start_glyph_id) > maxgid) + return false; + if (i > 0 && be::swap(pTable12->group[i].start_char_code) <= be::swap(pTable12->group[i-1].end_char_code)) + return false; + } +#endif + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return the Glyph ID for the given Unicode ID in the Microsoft UCS-4 subtable. + (Actually this code only depends on subtable being format 12.) + Return 0 if the Unicode ID is not in the subtable. +----------------------------------------------------------------------------------------------*/ +gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey) +{ + const Sfnt::CmapSubTableFormat12 * pTable = reinterpret_cast(pCmap310); + + //uint32 uLength = be::swap(pTable->length); //could use to test for premature end of table + uint32 ucGroups = be::swap(pTable->num_groups); + + for (unsigned int i = rangeKey; i < ucGroups; i++) + { + uint32 uStartCode = be::swap(pTable->group[i].start_char_code); + uint32 uEndCode = be::swap(pTable->group[i].end_char_code); + if (uUnicodeId >= uStartCode && uUnicodeId <= uEndCode) + { + uint32 uDiff = uUnicodeId - uStartCode; + uint32 uStartGid = be::swap(pTable->group[i].start_glyph_id); + return static_cast(uStartGid + uDiff); + } + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the next Unicode value in the cmap. Pass 0 to obtain the first item. + Returns 0x10FFFF as the last item. + pRangeKey is an optional key that is used to optimize the search; its value is the range + in which the character is found. +----------------------------------------------------------------------------------------------*/ +unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId, int * pRangeKey) +{ + const Sfnt::CmapSubTableFormat12 * pTable = reinterpret_cast(pCmap310); + + int nRange = be::swap(pTable->num_groups); + + uint32 nUnicodePrev = (uint32)nUnicodeId; + + if (nUnicodePrev == 0) + { + // return the first codepoint. + if (pRangeKey) + *pRangeKey = 0; + return be::swap(pTable->group[0].start_char_code); + } + else if (nUnicodePrev >= 0x10FFFF) + { + if (pRangeKey) + *pRangeKey = nRange; + return 0x10FFFF; + } + + int iRange = (pRangeKey) ? *pRangeKey : 0; + // Just in case we have a bad key: + while (iRange > 0 && be::swap(pTable->group[iRange].start_char_code) > nUnicodePrev) + iRange--; + while (iRange < nRange - 1 && be::swap(pTable->group[iRange].end_char_code) < nUnicodePrev) + iRange++; + + // Now iRange is the range containing nUnicodePrev. + + unsigned int nStartCode = be::swap(pTable->group[iRange].start_char_code); + unsigned int nEndCode = be::swap(pTable->group[iRange].end_char_code); + + if (nStartCode > nUnicodePrev) + // Oops, nUnicodePrev is not in the cmap! Adjust so we get a reasonable + // answer this time around. + nUnicodePrev = nStartCode - 1; + + if (nEndCode > nUnicodePrev) + { + // Next is in the same range; it is the next successive codepoint. + if (pRangeKey) + *pRangeKey = iRange; + return nUnicodePrev + 1; + } + + // Otherwise the next codepoint is the first one in the next range, or 10FFFF if we're done. + if (pRangeKey) + *pRangeKey = iRange + 1; + return (iRange + 1 >= nRange) ? 0x10FFFF : be::swap(pTable->group[iRange + 1].start_char_code); +} + +/*---------------------------------------------------------------------------------------------- + Return the offset stored in the loca table for the given Glyph ID. + (This offset is into the glyf table.) + Return -1 if the lookup failed. + Technically this method should return an unsigned long but it is unlikely the offset will + exceed 2^31. +----------------------------------------------------------------------------------------------*/ +size_t LocaLookup(gid16 nGlyphId, + const void * pLoca, size_t lLocaSize, + const void * pHead) // throw (std::out_of_range) +{ + const Sfnt::FontHeader * pTable = reinterpret_cast(pHead); + size_t res = -2; + + // CheckTable verifies the index_to_loc_format is valid + if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat) + { // loca entries are two bytes and have been divided by two + if (lLocaSize > 1 && nGlyphId + 1u < lLocaSize >> 1) // allow sentinel value to be accessed + { + const uint16 * pShortTable = reinterpret_cast(pLoca); + res = be::peek(pShortTable + nGlyphId) << 1; + if (res == static_cast(be::peek(pShortTable + nGlyphId + 1) << 1)) + return -1; + } + } + else if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat) + { // loca entries are four bytes + if (lLocaSize > 3 && nGlyphId + 1u < lLocaSize >> 2) + { + const uint32 * pLongTable = reinterpret_cast(pLoca); + res = be::peek(pLongTable + nGlyphId); + if (res == static_cast(be::peek(pLongTable + nGlyphId + 1))) + return -1; + } + } + + // only get here if glyph id was bad + return res; + //throw std::out_of_range("glyph id out of range for font"); +} + +/*---------------------------------------------------------------------------------------------- + Return a pointer into the glyf table based on the given offset (from LocaLookup). + Return NULL on error. +----------------------------------------------------------------------------------------------*/ +void * GlyfLookup(const void * pGlyf, size_t nGlyfOffset, size_t nTableLen) +{ + const uint8 * pByte = reinterpret_cast(pGlyf); + if (OVERFLOW_OFFSET_CHECK(pByte, nGlyfOffset) || nGlyfOffset >= nTableLen - sizeof(Sfnt::Glyph)) + return NULL; + return const_cast(pByte + nGlyfOffset); +} + +/*---------------------------------------------------------------------------------------------- + Get the bounding box coordinates for a simple glyf entry (non-composite). + Return true if successful, false otherwise. +----------------------------------------------------------------------------------------------*/ +bool GlyfBox(const void * pSimpleGlyf, int & xMin, int & yMin, + int & xMax, int & yMax) +{ + const Sfnt::Glyph * pGlyph = reinterpret_cast(pSimpleGlyf); + + xMin = be::swap(pGlyph->x_min); + yMin = be::swap(pGlyph->y_min); + xMax = be::swap(pGlyph->x_max); + yMax = be::swap(pGlyph->y_max); + + return true; +} + +#ifdef ALL_TTFUTILS +/*---------------------------------------------------------------------------------------------- + Return the number of contours for a simple glyf entry (non-composite) + Returning -1 means this is a composite glyph +----------------------------------------------------------------------------------------------*/ +int GlyfContourCount(const void * pSimpleGlyf) +{ + const Sfnt::Glyph * pGlyph = reinterpret_cast(pSimpleGlyf); + return be::swap(pGlyph->number_of_contours); // -1 means composite glyph +} + +/*---------------------------------------------------------------------------------------------- + Get the point numbers for the end points of the glyph contours for a simple + glyf entry (non-composite). + cnPointsTotal - count of contours from GlyfContourCount(); (same as number of end points) + prgnContourEndPoints - should point to a buffer large enough to hold cnPoints integers + cnPoints - count of points placed in above range + Return true if successful, false otherwise. + False could indicate a multi-level composite glyphs. +----------------------------------------------------------------------------------------------*/ +bool GlyfContourEndPoints(const void * pSimpleGlyf, int * prgnContourEndPoint, + int cnPointsTotal, int & cnPoints) +{ + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast(pSimpleGlyf); + + int cContours = be::swap(pGlyph->number_of_contours); + if (cContours < 0) + return false; // this method isn't supposed handle composite glyphs + + for (int i = 0; i < cContours && i < cnPointsTotal; i++) + { + prgnContourEndPoint[i] = be::swap(pGlyph->end_pts_of_contours[i]); + } + + cnPoints = cContours; + return true; +} + +/*---------------------------------------------------------------------------------------------- + Get the points for a simple glyf entry (non-composite) + cnPointsTotal - count of points from largest end point obtained from GlyfContourEndPoints + prgnX & prgnY - should point to buffers large enough to hold cnPointsTotal integers + The ranges are parallel so that coordinates for point(n) are found at offset n in both + ranges. This is raw point data with relative coordinates. + prgbFlag - should point to a buffer a large enough to hold cnPointsTotal bytes + This range is parallel to the prgnX & prgnY + cnPoints - count of points placed in above ranges + Return true if successful, false otherwise. + False could indicate a composite glyph +----------------------------------------------------------------------------------------------*/ +bool GlyfPoints(const void * pSimpleGlyf, int * prgnX, int * prgnY, + char * prgbFlag, int cnPointsTotal, int & cnPoints) +{ + using namespace Sfnt; + + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast(pSimpleGlyf); + int cContours = be::swap(pGlyph->number_of_contours); + // return false for composite glyph + if (cContours <= 0) + return false; + int cPts = be::swap(pGlyph->end_pts_of_contours[cContours - 1]) + 1; + if (cPts > cnPointsTotal) + return false; + + // skip over bounding box data & point to byte count of instructions (hints) + const uint8 * pbGlyph = reinterpret_cast + (&pGlyph->end_pts_of_contours[cContours]); + + // skip over hints & point to first flag + int cbHints = be::swap(*(uint16 *)pbGlyph); + pbGlyph += sizeof(uint16); + pbGlyph += cbHints; + + // load flags & point to first x coordinate + int iFlag = 0; + while (iFlag < cPts) + { + if (!(*pbGlyph & SimpleGlyph::Repeat)) + { // flag isn't repeated + prgbFlag[iFlag] = (char)*pbGlyph; + pbGlyph++; + iFlag++; + } + else + { // flag is repeated; count specified by next byte + char chFlag = (char)*pbGlyph; + pbGlyph++; + int cFlags = (int)*pbGlyph; + pbGlyph++; + prgbFlag[iFlag] = chFlag; + iFlag++; + for (int i = 0; i < cFlags; i++) + { + prgbFlag[iFlag + i] = chFlag; + } + iFlag += cFlags; + } + } + if (iFlag != cPts) + return false; + + // load x coordinates + iFlag = 0; + while (iFlag < cPts) + { + if (prgbFlag[iFlag] & SimpleGlyph::XShort) + { + prgnX[iFlag] = *pbGlyph; + if (!(prgbFlag[iFlag] & SimpleGlyph::XIsPos)) + { + prgnX[iFlag] = -prgnX[iFlag]; + } + pbGlyph++; + } + else + { + if (prgbFlag[iFlag] & SimpleGlyph::XIsSame) + { + prgnX[iFlag] = 0; + // do NOT increment pbGlyph + } + else + { + prgnX[iFlag] = be::swap(*(int16 *)pbGlyph); + pbGlyph += sizeof(int16); + } + } + iFlag++; + } + + // load y coordinates + iFlag = 0; + while (iFlag < cPts) + { + if (prgbFlag[iFlag] & SimpleGlyph::YShort) + { + prgnY[iFlag] = *pbGlyph; + if (!(prgbFlag[iFlag] & SimpleGlyph::YIsPos)) + { + prgnY[iFlag] = -prgnY[iFlag]; + } + pbGlyph++; + } + else + { + if (prgbFlag[iFlag] & SimpleGlyph::YIsSame) + { + prgnY[iFlag] = 0; + // do NOT increment pbGlyph + } + else + { + prgnY[iFlag] = be::swap(*(int16 *)pbGlyph); + pbGlyph += sizeof(int16); + } + } + iFlag++; + } + + cnPoints = cPts; + return true; +} + +/*---------------------------------------------------------------------------------------------- + Fill prgnCompId with the component Glyph IDs from pSimpleGlyf. + Client must allocate space before calling. + pSimpleGlyf - assumed to point to a composite glyph + cCompIdTotal - the number of elements in prgnCompId + cCompId - the total number of Glyph IDs stored in prgnCompId + Return true if successful, false otherwise + False could indicate a non-composite glyph or the input array was not big enough +----------------------------------------------------------------------------------------------*/ +bool GetComponentGlyphIds(const void * pSimpleGlyf, int * prgnCompId, + size_t cnCompIdTotal, size_t & cnCompId) +{ + using namespace Sfnt; + + if (GlyfContourCount(pSimpleGlyf) >= 0) + return false; + + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast(pSimpleGlyf); + // for a composite glyph, the special data begins here + const uint8 * pbGlyph = reinterpret_cast(&pGlyph->end_pts_of_contours[0]); + + uint16 GlyphFlags; + size_t iCurrentComp = 0; + do + { + GlyphFlags = be::swap(*((uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + prgnCompId[iCurrentComp++] = be::swap(*((uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + if (iCurrentComp >= cnCompIdTotal) + return false; + int nOffset = 0; + nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; + nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0; + pbGlyph += nOffset; + } while (GlyphFlags & CompoundGlyph::MoreComponents); + + cnCompId = iCurrentComp; + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return info on how a component glyph is to be placed + pSimpleGlyph - assumed to point to a composite glyph + nCompId - glyph id for component of interest + bOffset - if true, a & b are the x & y offsets for this component + if false, b is the point on this component that is attaching to point a on the + preceding glyph + Return true if successful, false otherwise + False could indicate a non-composite glyph or that component wasn't found +----------------------------------------------------------------------------------------------*/ +bool GetComponentPlacement(const void * pSimpleGlyf, int nCompId, + bool fOffset, int & a, int & b) +{ + using namespace Sfnt; + + if (GlyfContourCount(pSimpleGlyf) >= 0) + return false; + + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast(pSimpleGlyf); + // for a composite glyph, the special data begins here + const uint8 * pbGlyph = reinterpret_cast(&pGlyph->end_pts_of_contours[0]); + + uint16 GlyphFlags; + do + { + GlyphFlags = be::swap(*((uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + if (be::swap(*((uint16 *)pbGlyph)) == nCompId) + { + pbGlyph += sizeof(uint16); // skip over glyph id of component + fOffset = (GlyphFlags & CompoundGlyph::ArgsAreXYValues) == CompoundGlyph::ArgsAreXYValues; + + if (GlyphFlags & CompoundGlyph::Arg1Arg2Words ) + { + a = be::swap(*(int16 *)pbGlyph); + pbGlyph += sizeof(int16); + b = be::swap(*(int16 *)pbGlyph); + pbGlyph += sizeof(int16); + } + else + { // args are signed bytes + a = *pbGlyph++; + b = *pbGlyph++; + } + return true; + } + pbGlyph += sizeof(uint16); // skip over glyph id of component + int nOffset = 0; + nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; + nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0; + pbGlyph += nOffset; + } while (GlyphFlags & CompoundGlyph::MoreComponents); + + // didn't find requested component + fOffset = true; + a = 0; + b = 0; + return false; +} + +/*---------------------------------------------------------------------------------------------- + Return info on how a component glyph is to be transformed + pSimpleGlyph - assumed to point to a composite glyph + nCompId - glyph id for component of interest + flt11, flt11, flt11, flt11 - a 2x2 matrix giving the transform + bTransOffset - whether to transform the offset from above method + The spec is unclear about the meaning of this flag + Currently - initialize to true for MS rasterizer and false for Mac rasterizer, then + on return it will indicate whether transform should apply to offset (MSDN CD 10/99) + Return true if successful, false otherwise + False could indicate a non-composite glyph or that component wasn't found +----------------------------------------------------------------------------------------------*/ +bool GetComponentTransform(const void * pSimpleGlyf, int nCompId, + float & flt11, float & flt12, float & flt21, float & flt22, + bool & fTransOffset) +{ + using namespace Sfnt; + + if (GlyfContourCount(pSimpleGlyf) >= 0) + return false; + + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast(pSimpleGlyf); + // for a composite glyph, the special data begins here + const uint8 * pbGlyph = reinterpret_cast(&pGlyph->end_pts_of_contours[0]); + + uint16 GlyphFlags; + do + { + GlyphFlags = be::swap(*((uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + if (be::swap(*((uint16 *)pbGlyph)) == nCompId) + { + pbGlyph += sizeof(uint16); // skip over glyph id of component + pbGlyph += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; // skip over placement data + + if (fTransOffset) // MS rasterizer + fTransOffset = !(GlyphFlags & CompoundGlyph::UnscaledOffset); + else // Apple rasterizer + fTransOffset = (GlyphFlags & CompoundGlyph::ScaledOffset) != 0; + + if (GlyphFlags & CompoundGlyph::HaveScale) + { + flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt12 = 0; + flt21 = 0; + flt22 = flt11; + } + else if (GlyphFlags & CompoundGlyph::HaveXAndYScale) + { + flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt12 = 0; + flt21 = 0; + flt22 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + } + else if (GlyphFlags & CompoundGlyph::HaveTwoByTwo) + { + flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt12 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt21 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt22 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + } + else + { // identity transform + flt11 = 1.0; + flt12 = 0.0; + flt21 = 0.0; + flt22 = 1.0; + } + return true; + } + pbGlyph += sizeof(uint16); // skip over glyph id of component + int nOffset = 0; + nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; + nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0; + pbGlyph += nOffset; + } while (GlyphFlags & CompoundGlyph::MoreComponents); + + // didn't find requested component + fTransOffset = false; + flt11 = 1; + flt12 = 0; + flt21 = 0; + flt22 = 1; + return false; +} +#endif + +/*---------------------------------------------------------------------------------------------- + Return a pointer into the glyf table based on the given tables and Glyph ID + Since this method doesn't check for spaces, it is good to call IsSpace before using it. + Return NULL on error. +----------------------------------------------------------------------------------------------*/ +void * GlyfLookup(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void * pHead) +{ + // test for valid glyph id + // CheckTable verifies the index_to_loc_format is valid + + const Sfnt::FontHeader * pTable + = reinterpret_cast(pHead); + + if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat) + { // loca entries are two bytes (and have been divided by two) + if (nGlyphId >= (lLocaSize >> 1) - 1) // don't allow nGlyphId to access sentinel + { +// throw std::out_of_range("glyph id out of range for font"); + return NULL; + } + } + if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat) + { // loca entries are four bytes + if (nGlyphId >= (lLocaSize >> 2) - 1) + { +// throw std::out_of_range("glyph id out of range for font"); + return NULL; + } + } + + size_t lGlyfOffset = LocaLookup(nGlyphId, pLoca, lLocaSize, pHead); + void * pSimpleGlyf = GlyfLookup(pGlyf, lGlyfOffset, lGlyfSize); // invalid loca offset returns null + return pSimpleGlyf; +} + +#ifdef ALL_TTFUTILS +/*---------------------------------------------------------------------------------------------- + Determine if a particular Glyph ID has any data in the glyf table. If it is white space, + there will be no glyf data, though there will be metric data in hmtx, etc. +----------------------------------------------------------------------------------------------*/ +bool IsSpace(gid16 nGlyphId, const void * pLoca, size_t lLocaSize, const void * pHead) +{ + size_t lGlyfOffset = LocaLookup(nGlyphId, pLoca, lLocaSize, pHead); + + // the +1 should always work because there is a sentinel value at the end of the loca table + size_t lNextGlyfOffset = LocaLookup(nGlyphId + 1, pLoca, lLocaSize, pHead); + + return (lNextGlyfOffset - lGlyfOffset) == 0; +} + +/*---------------------------------------------------------------------------------------------- + Determine if a particular Glyph ID is a multi-level composite. +----------------------------------------------------------------------------------------------*/ +bool IsDeepComposite(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, long lLocaSize, const void * pHead) +{ + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;} + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pSimpleGlyf == NULL) + return false; // no way to really indicate an error occured here + + if (GlyfContourCount(pSimpleGlyf) >= 0) + return false; + + int rgnCompId[kMaxGlyphComponents]; // assumes only a limited number of glyph components + size_t cCompIdTotal = kMaxGlyphComponents; + size_t cCompId = 0; + + if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId)) + return false; + + for (size_t i = 0; i < cCompId; i++) + { + pSimpleGlyf = GlyfLookup(static_cast(rgnCompId[i]), + pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + + if (GlyfContourCount(pSimpleGlyf) < 0) + return true; + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Get the bounding box coordinates based on the given tables and Glyph ID + Handles both simple and composite glyphs. + Return true if successful, false otherwise. On false, all point values will be INT_MIN + False may indicate a white space glyph +----------------------------------------------------------------------------------------------*/ +bool GlyfBox(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void * pHead, int & xMin, int & yMin, int & xMax, int & yMax) +{ + xMin = yMin = xMax = yMax = INT_MIN; + + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;} + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + + return GlyfBox(pSimpleGlyf, xMin, yMin, xMax, yMax); +} + +/*---------------------------------------------------------------------------------------------- + Get the number of contours based on the given tables and Glyph ID + Handles both simple and composite glyphs. + Return true if successful, false otherwise. On false, cnContours will be INT_MIN + False may indicate a white space glyph or a multi-level composite glyph. +----------------------------------------------------------------------------------------------*/ +bool GlyfContourCount(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void * pHead, size_t & cnContours) +{ + cnContours = static_cast(INT_MIN); + + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;} + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + + int cRtnContours = GlyfContourCount(pSimpleGlyf); + if (cRtnContours >= 0) + { + cnContours = size_t(cRtnContours); + return true; + } + + //handle composite glyphs + + int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components + size_t cCompIdTotal = kMaxGlyphComponents; + size_t cCompId = 0; + + if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId)) + return false; + + cRtnContours = 0; + int cTmp = 0; + for (size_t i = 0; i < cCompId; i++) + { + if (IsSpace(static_cast(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;} + pSimpleGlyf = GlyfLookup(static_cast(rgnCompId[i]), + pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pSimpleGlyf == 0) {return false;} + // return false on multi-level composite + if ((cTmp = GlyfContourCount(pSimpleGlyf)) < 0) + return false; + cRtnContours += cTmp; + } + + cnContours = size_t(cRtnContours); + return true; +} + +/*---------------------------------------------------------------------------------------------- + Get the point numbers for the end points of the glyph contours based on the given tables + and Glyph ID + Handles both simple and composite glyphs. + cnPoints - count of contours from GlyfContourCount (same as number of end points) + prgnContourEndPoints - should point to a buffer large enough to hold cnPoints integers + Return true if successful, false otherwise. On false, all end points are INT_MIN + False may indicate a white space glyph or a multi-level composite glyph. +----------------------------------------------------------------------------------------------*/ +bool GlyfContourEndPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void * pHead, + int * prgnContourEndPoint, size_t cnPoints) +{ + memset(prgnContourEndPoint, 0xFF, cnPoints * sizeof(int)); + // std::fill_n(prgnContourEndPoint, cnPoints, INT_MIN); + + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;} + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + + int cContours = GlyfContourCount(pSimpleGlyf); + int cActualPts = 0; + if (cContours > 0) + return GlyfContourEndPoints(pSimpleGlyf, prgnContourEndPoint, cnPoints, cActualPts); + + // handle composite glyphs + + int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components + size_t cCompIdTotal = kMaxGlyphComponents; + size_t cCompId = 0; + + if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId)) + return false; + + int * prgnCurrentEndPoint = prgnContourEndPoint; + int cCurrentPoints = cnPoints; + int nPrevPt = 0; + for (size_t i = 0; i < cCompId; i++) + { + if (IsSpace(static_cast(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;} + pSimpleGlyf = GlyfLookup(static_cast(rgnCompId[i]), pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + // returns false on multi-level composite + if (!GlyfContourEndPoints(pSimpleGlyf, prgnCurrentEndPoint, cCurrentPoints, cActualPts)) + return false; + // points in composite are numbered sequentially as components are added + // must adjust end point numbers for new point numbers + for (int j = 0; j < cActualPts; j++) + prgnCurrentEndPoint[j] += nPrevPt; + nPrevPt = prgnCurrentEndPoint[cActualPts - 1] + 1; + + prgnCurrentEndPoint += cActualPts; + cCurrentPoints -= cActualPts; + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Get the points for a glyph based on the given tables and Glyph ID + Handles both simple and composite glyphs. + cnPoints - count of points from largest end point obtained from GlyfContourEndPoints + prgnX & prgnY - should point to buffers large enough to hold cnPoints integers + The ranges are parallel so that coordinates for point(n) are found at offset n in + both ranges. These points are in absolute coordinates. + prgfOnCurve - should point to a buffer a large enough to hold cnPoints bytes (bool) + This range is parallel to the prgnX & prgnY + Return true if successful, false otherwise. On false, all points may be INT_MIN + False may indicate a white space glyph, a multi-level composite, or a corrupt font + It's not clear from the TTF spec when the transforms should be applied. Should the + transform be done before or after attachment point calcs? (current code - before) + Should the transform be applied to other offsets? (currently - no; however commented + out code is in place so that if CompoundGlyph::UnscaledOffset on the MS rasterizer is + clear (typical) then yes, and if CompoundGlyph::ScaledOffset on the Apple rasterizer is + clear (typical?) then no). See GetComponentTransform. + It's also unclear where point numbering with attachment poinst starts + (currently - first point number is relative to whole glyph, second point number is + relative to current glyph). +----------------------------------------------------------------------------------------------*/ +bool GlyfPoints(gid16 nGlyphId, const void * pGlyf, + const void * pLoca, size_t lGlyfSize, size_t lLocaSize, const void * pHead, + const int * /*prgnContourEndPoint*/, size_t /*cnEndPoints*/, + int * prgnX, int * prgnY, bool * prgfOnCurve, size_t cnPoints) +{ + memset(prgnX, 0x7F, cnPoints * sizeof(int)); + memset(prgnY, 0x7F, cnPoints * sizeof(int)); + + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) + return false; + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pSimpleGlyf == NULL) + return false; + + int cContours = GlyfContourCount(pSimpleGlyf); + int cActualPts; + if (cContours > 0) + { + if (!GlyfPoints(pSimpleGlyf, prgnX, prgnY, (char *)prgfOnCurve, cnPoints, cActualPts)) + return false; + CalcAbsolutePoints(prgnX, prgnY, cnPoints); + SimplifyFlags((char *)prgfOnCurve, cnPoints); + return true; + } + + // handle composite glyphs + int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components + size_t cCompIdTotal = kMaxGlyphComponents; + size_t cCompId = 0; + + // this will fail if there are more components than there is room for + if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId)) + return false; + + int * prgnCurrentX = prgnX; + int * prgnCurrentY = prgnY; + char * prgbCurrentFlag = (char *)prgfOnCurve; // converting bool to char should be safe + int cCurrentPoints = cnPoints; + bool fOffset = true, fTransOff = true; + int a, b; + float flt11, flt12, flt21, flt22; + // int * prgnPrevX = prgnX; // in case first att pt number relative to preceding glyph + // int * prgnPrevY = prgnY; + for (size_t i = 0; i < cCompId; i++) + { + if (IsSpace(static_cast(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;} + void * pCompGlyf = GlyfLookup(static_cast(rgnCompId[i]), pGlyf, pLoca, lGlyfSize, lLocaSize, pHead); + if (pCompGlyf == NULL) {return false;} + // returns false on multi-level composite + if (!GlyfPoints(pCompGlyf, prgnCurrentX, prgnCurrentY, prgbCurrentFlag, + cCurrentPoints, cActualPts)) + return false; + if (!GetComponentPlacement(pSimpleGlyf, rgnCompId[i], fOffset, a, b)) + return false; + if (!GetComponentTransform(pSimpleGlyf, rgnCompId[i], + flt11, flt12, flt21, flt22, fTransOff)) + return false; + bool fIdTrans = flt11 == 1.0 && flt12 == 0.0 && flt21 == 0.0 && flt22 == 1.0; + + // convert points to absolute coordinates + // do before transform and attachment point placement are applied + CalcAbsolutePoints(prgnCurrentX, prgnCurrentY, cActualPts); + + // apply transform - see main method note above + // do before attachment point calcs + if (!fIdTrans) + for (int j = 0; j < cActualPts; j++) + { + int x = prgnCurrentX[j]; // store before transform applied + int y = prgnCurrentY[j]; + prgnCurrentX[j] = (int)(x * flt11 + y * flt12); + prgnCurrentY[j] = (int)(x * flt21 + y * flt22); + } + + // apply placement - see main method note above + int nXOff, nYOff; + if (fOffset) // explicit x & y offsets + { + /* ignore fTransOff for now + if (fTransOff && !fIdTrans) + { // transform x & y offsets + nXOff = (int)(a * flt11 + b * flt12); + nYOff = (int)(a * flt21 + b * flt22); + } + else */ + { // don't transform offset + nXOff = a; + nYOff = b; + } + } + else // attachment points + { // in case first point is relative to preceding glyph and second relative to current + // nXOff = prgnPrevX[a] - prgnCurrentX[b]; + // nYOff = prgnPrevY[a] - prgnCurrentY[b]; + // first point number relative to whole composite, second relative to current glyph + nXOff = prgnX[a] - prgnCurrentX[b]; + nYOff = prgnY[a] - prgnCurrentY[b]; + } + for (int j = 0; j < cActualPts; j++) + { + prgnCurrentX[j] += nXOff; + prgnCurrentY[j] += nYOff; + } + + // prgnPrevX = prgnCurrentX; + // prgnPrevY = prgnCurrentY; + prgnCurrentX += cActualPts; + prgnCurrentY += cActualPts; + prgbCurrentFlag += cActualPts; + cCurrentPoints -= cActualPts; + } + + SimplifyFlags((char *)prgfOnCurve, cnPoints); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Simplify the meaning of flags to just indicate whether point is on-curve or off-curve. +---------------------------------------------------------------------------------------------*/ +bool SimplifyFlags(char * prgbFlags, int cnPoints) +{ + for (int i = 0; i < cnPoints; i++) + prgbFlags[i] = static_cast(prgbFlags[i] & Sfnt::SimpleGlyph::OnCurve); + return true; +} + +/*---------------------------------------------------------------------------------------------- + Convert relative point coordinates to absolute coordinates + Points are stored in the font such that they are offsets from one another except for the + first point of a glyph. +---------------------------------------------------------------------------------------------*/ +bool CalcAbsolutePoints(int * prgnX, int * prgnY, int cnPoints) +{ + int nX = prgnX[0]; + int nY = prgnY[0]; + for (int i = 1; i < cnPoints; i++) + { + prgnX[i] += nX; + nX = prgnX[i]; + prgnY[i] += nY; + nY = prgnY[i]; + } + + return true; +} +#endif + +/*---------------------------------------------------------------------------------------------- + Return the length of the 'name' table in bytes. + Currently used. +---------------------------------------------------------------------------------------------*/ +#if 0 +size_t NameTableLength(const byte * pTable) +{ + byte * pb = (const_cast(pTable)) + 2; // skip format + size_t cRecords = *pb++ << 8; cRecords += *pb++; + int dbStringOffset0 = (*pb++) << 8; dbStringOffset0 += *pb++; + int dbMaxStringOffset = 0; + for (size_t irec = 0; irec < cRecords; irec++) + { + int nPlatform = (*pb++) << 8; nPlatform += *pb++; + int nEncoding = (*pb++) << 8; nEncoding += *pb++; + int nLanguage = (*pb++) << 8; nLanguage += *pb++; + int nName = (*pb++) << 8; nName += *pb++; + int cbStringLen = (*pb++) << 8; cbStringLen += *pb++; + int dbStringOffset = (*pb++) << 8; dbStringOffset += *pb++; + if (dbMaxStringOffset < dbStringOffset + cbStringLen) + dbMaxStringOffset = dbStringOffset + cbStringLen; + } + return dbStringOffset0 + dbMaxStringOffset; +} +#endif + +} // end of namespace TtfUtil +} // end of namespace graphite diff --git a/thirdparty/graphite/src/UtfCodec.cpp b/thirdparty/graphite/src/UtfCodec.cpp new file mode 100644 index 00000000000..a944bbf9d08 --- /dev/null +++ b/thirdparty/graphite/src/UtfCodec.cpp @@ -0,0 +1,45 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/UtfCodec.h" +//using namespace graphite2; + +namespace graphite2 { + +} + +using namespace graphite2; + +const int8 _utf_codec<8>::sz_lut[16] = +{ + 1,1,1,1,1,1,1,1, // 1 byte + 0,0,0,0, // trailing byte + 2,2, // 2 bytes + 3, // 3 bytes + 4 // 4 bytes +}; + +const byte _utf_codec<8>::mask_lut[5] = {0x7f, 0xff, 0x3f, 0x1f, 0x0f}; diff --git a/thirdparty/graphite/src/call_machine.cpp b/thirdparty/graphite/src/call_machine.cpp new file mode 100644 index 00000000000..fcd8a0c2c17 --- /dev/null +++ b/thirdparty/graphite/src/call_machine.cpp @@ -0,0 +1,138 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// This call threaded interpreter implmentation for machine.h +// Author: Tim Eves + +// Build either this interpreter or the direct_machine implementation. +// The call threaded interpreter is portable across compilers and +// architectures as well as being useful to debug (you can set breakpoints on +// opcodes) but is slower that the direct threaded interpreter by a factor of 2 + +#include +#include +#include +#include "inc/Machine.h" +#include "inc/Segment.h" +#include "inc/Slot.h" +#include "inc/Rule.h" + +// Disable the unused parameter warning as th compiler is mistaken since dp +// is always updated (even if by 0) on every opcode. +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#define registers const byte * & dp, vm::Machine::stack_t * & sp, \ + vm::Machine::stack_t * const sb, regbank & reg + +// These are required by opcodes.h and should not be changed +#define STARTOP(name) bool name(registers) REGPARM(4);\ + bool name(registers) { +#define ENDOP return (sp - sb)/Machine::STACK_MAX==0; \ + } + +#define EXIT(status) { push(status); return false; } + +// This is required by opcode_table.h +#define do_(name) instr(name) + + +using namespace graphite2; +using namespace vm; + +struct regbank { + slotref is; + slotref * map; + SlotMap & smap; + slotref * const map_base; + const instr * & ip; + uint8 direction; + int8 flags; + Machine::status_t & status; +}; + +typedef bool (* ip_t)(registers); + +// Pull in the opcode definitions +// We pull these into a private namespace so these otherwise common names dont +// pollute the toplevel namespace. +namespace { +#define smap reg.smap +#define seg smap.segment +#define is reg.is +#define ip reg.ip +#define map reg.map +#define mapb reg.map_base +#define flags reg.flags +#define dir reg.direction +#define status reg.status + +#include "inc/opcodes.h" + +#undef smap +#undef seg +#undef is +#undef ip +#undef map +#undef mapb +#undef flags +#undef dir +} + +Machine::stack_t Machine::run(const instr * program, + const byte * data, + slotref * & map) + +{ + assert(program != 0); + + // Declare virtual machine registers + const instr * ip = program-1; + const byte * dp = data; + stack_t * sp = _stack + Machine::STACK_GUARD, + * const sb = sp; + regbank reg = {*map, map, _map, _map.begin()+_map.context(), ip, _map.dir(), 0, _status}; + + // Run the program + while ((reinterpret_cast(*++ip))(dp, sp, sb, reg)) {} + const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0; + + check_final_stack(sp); + map = reg.map; + *map = reg.is; + return ret; +} + +// Pull in the opcode table +namespace { +#include "inc/opcode_table.h" +} + +const opcode_t * Machine::getOpcodeTable() throw() +{ + return opcode_table; +} diff --git a/thirdparty/graphite/src/direct_machine.cpp b/thirdparty/graphite/src/direct_machine.cpp new file mode 100644 index 00000000000..86206cfe37b --- /dev/null +++ b/thirdparty/graphite/src/direct_machine.cpp @@ -0,0 +1,140 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// This direct threaded interpreter implmentation for machine.h +// Author: Tim Eves + +// Build either this interpreter or the call_machine implementation. +// The direct threaded interpreter is relies upon a gcc feature called +// labels-as-values so is only portable to compilers that support the +// extension (gcc only as far as I know) however it should build on any +// architecture gcc supports. +// This is twice as fast as the call threaded model and is likely faster on +// inorder processors with short pipelines and little branch prediction such +// as the ARM and possibly Atom chips. + + +#include +#include +#include "inc/Machine.h" +#include "inc/Segment.h" +#include "inc/Slot.h" +#include "inc/Rule.h" + +#define STARTOP(name) name: { +#define ENDOP }; goto *((sp - sb)/Machine::STACK_MAX ? &&end : *++ip); +#define EXIT(status) { push(status); goto end; } + +#define do_(name) &&name + + +using namespace graphite2; +using namespace vm; + +namespace { + +// The GCC manual has this to say about labels as values: +// The &&foo expressions for the same label might have different values +// if the containing function is inlined or cloned. If a program relies +// on them being always the same, __attribute__((__noinline__,__noclone__)) +// should be used to prevent inlining and cloning. +// +// is_return in Code.cpp relies on being able to do comparisons, so it needs +// them to be always the same. +// +// The GCC manual further adds: +// If &&foo is used in a static variable initializer, inlining and +// cloning is forbidden. +// +// In this file, &&foo *is* used in a static variable initializer, and it's not +// entirely clear whether this should prevent inlining of the function or not. +// In practice, though, clang 7 can end up inlining the function with ThinLTO, +// which breaks at least is_return. https://bugs.llvm.org/show_bug.cgi?id=39241 +// So all in all, we need at least the __noinline__ attribute. __noclone__ +// is not supported by clang. +__attribute__((__noinline__)) +const void * direct_run(const bool get_table_mode, + const instr * program, + const byte * data, + Machine::stack_t * stack, + slotref * & __map, + uint8 _dir, + Machine::status_t & status, + SlotMap * __smap=0) +{ + // We need to define and return to opcode table from within this function + // other inorder to take the addresses of the instruction bodies. + #include "inc/opcode_table.h" + if (get_table_mode) + return opcode_table; + + // Declare virtual machine registers + const instr * ip = program; + const byte * dp = data; + Machine::stack_t * sp = stack + Machine::STACK_GUARD, + * const sb = sp; + SlotMap & smap = *__smap; + Segment & seg = smap.segment; + slotref is = *__map, + * map = __map, + * const mapb = smap.begin()+smap.context(); + uint8 dir = _dir; + int8 flags = 0; + + // start the program + goto **ip; + + // Pull in the opcode definitions + #include "inc/opcodes.h" + + end: + __map = map; + *__map = is; + return sp; +} + +} + +const opcode_t * Machine::getOpcodeTable() throw() +{ + slotref * dummy; + Machine::status_t dumstat = Machine::finished; + return static_cast(direct_run(true, 0, 0, 0, dummy, 0, dumstat)); +} + + +Machine::stack_t Machine::run(const instr * program, + const byte * data, + slotref * & is) +{ + assert(program != 0); + + const stack_t *sp = static_cast( + direct_run(false, program, data, _stack, is, _map.dir(), _status, &_map)); + const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0; + check_final_stack(sp); + return ret; +} diff --git a/thirdparty/graphite/src/gr_char_info.cpp b/thirdparty/graphite/src/gr_char_info.cpp new file mode 100644 index 00000000000..612f9ba694f --- /dev/null +++ b/thirdparty/graphite/src/gr_char_info.cpp @@ -0,0 +1,65 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include +#include "graphite2/Segment.h" +#include "inc/CharInfo.h" + +extern "C" +{ + +unsigned int gr_cinfo_unicode_char(const gr_char_info* p/*not NULL*/) +{ + assert(p); + return p->unicodeChar(); +} + + +int gr_cinfo_break_weight(const gr_char_info* p/*not NULL*/) +{ + assert(p); + return p->breakWeight(); +} + +int gr_cinfo_after(const gr_char_info *p/*not NULL*/) +{ + assert(p); + return p->after(); +} + +int gr_cinfo_before(const gr_char_info *p/*not NULL*/) +{ + assert(p); + return p->before(); +} + +size_t gr_cinfo_base(const gr_char_info *p/*not NULL*/) +{ + assert(p); + return p->base(); +} + +} // extern "C" diff --git a/thirdparty/graphite/src/gr_face.cpp b/thirdparty/graphite/src/gr_face.cpp new file mode 100644 index 00000000000..baa469727bb --- /dev/null +++ b/thirdparty/graphite/src/gr_face.cpp @@ -0,0 +1,267 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "graphite2/Font.h" +#include "inc/Face.h" +#include "inc/FileFace.h" +#include "inc/GlyphCache.h" +#include "inc/CmapCache.h" +#include "inc/Silf.h" +#include "inc/json.h" + +using namespace graphite2; + +#if !defined GRAPHITE2_NTRACING +extern json *global_log; +#endif + +namespace +{ + bool load_face(Face & face, unsigned int options) + { +#ifdef GRAPHITE2_TELEMETRY + telemetry::category _misc_cat(face.tele.misc); +#endif + Face::Table silf(face, Tag::Silf, 0x00050000); + if (!silf) + return false; + + if (!face.readGlyphs(options)) + return false; + + if (silf) + { + if (!face.readFeatures() || !face.readGraphite(silf)) + { +#if !defined GRAPHITE2_NTRACING + if (global_log) + { + *global_log << json::object + << "type" << "fontload" + << "failure" << face.error() + << "context" << face.error_context() + << json::close; + } +#endif + return false; + } + else + return true; + } + else + return false; + } + + inline + uint32 zeropad(const uint32 x) + { + if (x == 0x20202020) return 0; + if ((x & 0x00FFFFFF) == 0x00202020) return x & 0xFF000000; + if ((x & 0x0000FFFF) == 0x00002020) return x & 0xFFFF0000; + if ((x & 0x000000FF) == 0x00000020) return x & 0xFFFFFF00; + return x; + } +} + +extern "C" { + +gr_face* gr_make_face_with_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *ops, unsigned int faceOptions) + //the appFaceHandle must stay alive all the time when the gr_face is alive. When finished with the gr_face, call destroy_face +{ + if (ops == 0) return 0; + + Face *res = new Face(appFaceHandle, *ops); + if (res && load_face(*res, faceOptions)) + return static_cast(res); + + delete res; + return 0; +} + +gr_face* gr_make_face(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn tablefn, unsigned int faceOptions) +{ + const gr_face_ops ops = {sizeof(gr_face_ops), tablefn, NULL}; + return gr_make_face_with_ops(appFaceHandle, &ops, faceOptions); +} + + +gr_face* gr_make_face_with_seg_cache_and_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *ops, unsigned int , unsigned int faceOptions) +{ + return gr_make_face_with_ops(appFaceHandle, ops, faceOptions); +} + +gr_face* gr_make_face_with_seg_cache(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn tablefn, unsigned int, unsigned int faceOptions) +{ + const gr_face_ops ops = {sizeof(gr_face_ops), tablefn, NULL}; + return gr_make_face_with_ops(appFaceHandle, &ops, faceOptions); +} + +gr_uint32 gr_str_to_tag(const char *str) +{ + uint32 res = 0; + switch(max(strlen(str),size_t(4))) + { + case 4: res |= str[3]; GR_FALLTHROUGH; + case 3: res |= str[2] << 8; GR_FALLTHROUGH; + case 2: res |= str[1] << 16; GR_FALLTHROUGH; + case 1: res |= str[0] << 24; GR_FALLTHROUGH; + default: break; + } + return res; +} + +void gr_tag_to_str(gr_uint32 tag, char *str) +{ + if (!str) return; + + *str++ = char(tag >> 24); + *str++ = char(tag >> 16); + *str++ = char(tag >> 8); + *str++ = char(tag); + *str = '\0'; +} + +gr_feature_val* gr_face_featureval_for_lang(const gr_face* pFace, gr_uint32 langname/*0 means clone default*/) //clones the features. if none for language, clones the default +{ + assert(pFace); + langname = zeropad(langname); + return static_cast(pFace->theSill().cloneFeatures(langname)); +} + + +const gr_feature_ref* gr_face_find_fref(const gr_face* pFace, gr_uint32 featId) //When finished with the FeatureRef, call destroy_FeatureRef +{ + assert(pFace); + featId = zeropad(featId); + const FeatureRef* pRef = pFace->featureById(featId); + return static_cast(pRef); +} + +unsigned short gr_face_n_fref(const gr_face* pFace) +{ + assert(pFace); + int res = 0; + for (int i = 0; i < pFace->numFeatures(); ++i) + if (!(pFace->feature(i)->getFlags() & FeatureRef::HIDDEN)) + ++res; + return res; +} + +const gr_feature_ref* gr_face_fref(const gr_face* pFace, gr_uint16 i) //When finished with the FeatureRef, call destroy_FeatureRef +{ + assert(pFace); + int count = 0; + for (int j = 0; j < pFace->numFeatures(); ++j) + { + const FeatureRef* pRef = pFace->feature(j); + if (!(pRef->getFlags() & FeatureRef::HIDDEN)) + if (count++ == i) + return static_cast(pRef); + } + return 0; +} + +unsigned short gr_face_n_languages(const gr_face* pFace) +{ + assert(pFace); + return pFace->theSill().numLanguages(); +} + +gr_uint32 gr_face_lang_by_index(const gr_face* pFace, gr_uint16 i) +{ + assert(pFace); + return pFace->theSill().getLangName(i); +} + + +void gr_face_destroy(gr_face *face) +{ + delete static_cast(face); +} + + +gr_uint16 gr_face_name_lang_for_locale(gr_face *face, const char * locale) +{ + if (face) + { + return face->languageForLocale(locale); + } + return 0; +} + +unsigned short gr_face_n_glyphs(const gr_face* pFace) +{ + return pFace->glyphs().numGlyphs(); +} + +const gr_faceinfo *gr_face_info(const gr_face *pFace, gr_uint32 script) +{ + if (!pFace) return 0; + const Silf *silf = pFace->chooseSilf(script); + if (silf) return silf->silfInfo(); + return 0; +} + +int gr_face_is_char_supported(const gr_face* pFace, gr_uint32 usv, gr_uint32 script) +{ + const Cmap & cmap = pFace->cmap(); + gr_uint16 gid = cmap[usv]; + if (!gid) + { + const Silf * silf = pFace->chooseSilf(script); + gid = silf->findPseudo(usv); + } + return (gid != 0); +} + +#ifndef GRAPHITE2_NFILEFACE +gr_face* gr_make_file_face(const char *filename, unsigned int faceOptions) +{ + FileFace* pFileFace = new FileFace(filename); + if (*pFileFace) + { + gr_face* pRes = gr_make_face_with_ops(pFileFace, &FileFace::ops, faceOptions); + if (pRes) + { + pRes->takeFileFace(pFileFace); //takes ownership + return pRes; + } + } + + //error when loading + + delete pFileFace; + return NULL; +} + +gr_face* gr_make_file_face_with_seg_cache(const char* filename, unsigned int, unsigned int faceOptions) //returns NULL on failure. //TBD better error handling + //when finished with, call destroy_face +{ + return gr_make_file_face(filename, faceOptions); +} +#endif //!GRAPHITE2_NFILEFACE + +} // extern "C" diff --git a/thirdparty/graphite/src/gr_features.cpp b/thirdparty/graphite/src/gr_features.cpp new file mode 100644 index 00000000000..a560e053f2e --- /dev/null +++ b/thirdparty/graphite/src/gr_features.cpp @@ -0,0 +1,138 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "graphite2/Font.h" +#include "inc/Face.h" +#include "inc/FeatureMap.h" +#include "inc/FeatureVal.h" +#include "inc/NameTable.h" + +using namespace graphite2; + +extern "C" { + + +gr_uint16 gr_fref_feature_value(const gr_feature_ref* pfeatureref, const gr_feature_val* feats) //returns 0 if either pointer is NULL +{ + if (!pfeatureref || !feats) return 0; + + return pfeatureref->getFeatureVal(*feats); +} + + +int gr_fref_set_feature_value(const gr_feature_ref* pfeatureref, gr_uint16 val, gr_feature_val* pDest) +{ + if (!pfeatureref || !pDest) return 0; + + return pfeatureref->applyValToFeature(val, *pDest); +} + + +gr_uint32 gr_fref_id(const gr_feature_ref* pfeatureref) //returns 0 if pointer is NULL +{ + if (!pfeatureref) + return 0; + + return pfeatureref->getId(); +} + + +gr_uint16 gr_fref_n_values(const gr_feature_ref* pfeatureref) +{ + if(!pfeatureref) + return 0; + return pfeatureref->getNumSettings(); +} + + +gr_int16 gr_fref_value(const gr_feature_ref* pfeatureref, gr_uint16 settingno) +{ + if(!pfeatureref || (settingno >= pfeatureref->getNumSettings())) + { + return 0; + } + return pfeatureref->getSettingValue(settingno); +} + + +void* gr_fref_label(const gr_feature_ref* pfeatureref, gr_uint16 *langId, gr_encform utf, gr_uint32 *length) +{ + if(!pfeatureref) + { + langId = 0; + length = 0; + return NULL; + } + uint16 label = pfeatureref->getNameId(); + NameTable * names = pfeatureref->getFace().nameTable(); + if (!names) + { + langId = 0; + length = 0; + return NULL; + } + return names->getName(*langId, label, utf, *length); +} + + +void* gr_fref_value_label(const gr_feature_ref*pfeatureref, gr_uint16 setting, + gr_uint16 *langId, gr_encform utf, gr_uint32 *length) +{ + if(!pfeatureref || (setting >= pfeatureref->getNumSettings())) + { + langId = 0; + length = 0; + return NULL; + } + uint16 label = pfeatureref->getSettingName(setting); + NameTable * names = pfeatureref->getFace().nameTable(); + if (!names) + { + langId = 0; + length = 0; + return NULL; + } + return names->getName(*langId, label, utf, *length); +} + + +void gr_label_destroy(void * label) +{ + free(label); +} + +gr_feature_val* gr_featureval_clone(const gr_feature_val* pfeatures/*may be NULL*/) +{ //When finished with the Features, call features_destroy + return static_cast(pfeatures ? new Features(*pfeatures) : new Features); +} + +void gr_featureval_destroy(gr_feature_val *p) +{ + delete static_cast(p); +} + + +} // extern "C" diff --git a/thirdparty/graphite/src/gr_font.cpp b/thirdparty/graphite/src/gr_font.cpp new file mode 100644 index 00000000000..724cc83c138 --- /dev/null +++ b/thirdparty/graphite/src/gr_font.cpp @@ -0,0 +1,74 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "graphite2/Font.h" +#include "inc/Font.h" + + +using namespace graphite2; + +extern "C" { + +void gr_engine_version(int *nMajor, int *nMinor, int *nBugFix) +{ + if (nMajor) *nMajor = GR2_VERSION_MAJOR; + if (nMinor) *nMinor = GR2_VERSION_MINOR; + if (nBugFix) *nBugFix = GR2_VERSION_BUGFIX; +} + +gr_font* gr_make_font(float ppm/*pixels per em*/, const gr_face *face) +{ + return gr_make_font_with_advance_fn(ppm, 0, 0, face); +} + + +gr_font* gr_make_font_with_ops(float ppm/*pixels per em*/, const void* appFontHandle/*non-NULL*/, const gr_font_ops * font_ops, const gr_face * face/*needed for scaling*/) +{ //the appFontHandle must stay alive all the time when the gr_font is alive. When finished with the gr_font, call destroy_gr_font + if (face == 0 || ppm <= 0) return 0; + + Font * const res = new Font(ppm, *face, appFontHandle, font_ops); + if (*res) + return static_cast(res); + else + { + delete res; + return 0; + } +} + +gr_font* gr_make_font_with_advance_fn(float ppm/*pixels per em*/, const void* appFontHandle/*non-NULL*/, gr_advance_fn getAdvance, const gr_face * face/*needed for scaling*/) +{ + const gr_font_ops ops = {sizeof(gr_font_ops), getAdvance, NULL}; + return gr_make_font_with_ops(ppm, appFontHandle, &ops, face); +} + +void gr_font_destroy(gr_font *font) +{ + delete static_cast(font); +} + + +} // extern "C" diff --git a/thirdparty/graphite/src/gr_logging.cpp b/thirdparty/graphite/src/gr_logging.cpp new file mode 100644 index 00000000000..8f1e675e628 --- /dev/null +++ b/thirdparty/graphite/src/gr_logging.cpp @@ -0,0 +1,267 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include + +#include "graphite2/Log.h" +#include "inc/debug.h" +#include "inc/CharInfo.h" +#include "inc/Slot.h" +#include "inc/Segment.h" +#include "inc/json.h" +#include "inc/Collider.h" + +#if defined _WIN32 +#include "windows.h" +#endif + +using namespace graphite2; + +#if !defined GRAPHITE2_NTRACING +json *global_log = 0; +#endif + +extern "C" { + +bool gr_start_logging(GR_MAYBE_UNUSED gr_face * face, const char *log_path) +{ + if (!log_path) return false; + +#if !defined GRAPHITE2_NTRACING + gr_stop_logging(face); +#if defined _WIN32 + int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, 0, 0); + if (n == 0 || n > MAX_PATH - 12) return false; + + LPWSTR wlog_path = gralloc(n); + if (!wlog_path) return false; + FILE *log = 0; + if (wlog_path && MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, wlog_path, n)) + log = _wfopen(wlog_path, L"wt"); + + free(wlog_path); +#else // _WIN32 + FILE *log = fopen(log_path, "wt"); +#endif // _WIN32 + if (!log) return false; + + if (face) + { + face->setLogger(log); + if (!face->logger()) return false; + + *face->logger() << json::array; +#ifdef GRAPHITE2_TELEMETRY + *face->logger() << face->tele; +#endif + } + else + { + global_log = new json(log); + *global_log << json::array; + } + + return true; +#else // GRAPHITE2_NTRACING + return false; +#endif // GRAPHITE2_NTRACING +} + +bool graphite_start_logging(FILE * /* log */, GrLogMask /* mask */) +{ +//#if !defined GRAPHITE2_NTRACING +// graphite_stop_logging(); +// +// if (!log) return false; +// +// dbgout = new json(log); +// if (!dbgout) return false; +// +// *dbgout << json::array; +// return true; +//#else + return false; +//#endif +} + +void gr_stop_logging(GR_MAYBE_UNUSED gr_face * face) +{ +#if !defined GRAPHITE2_NTRACING + if (face && face->logger()) + { + FILE * log = face->logger()->stream(); + face->setLogger(0); + fclose(log); + } + else if (!face && global_log) + { + FILE * log = global_log->stream(); + delete global_log; + global_log = 0; + fclose(log); + } +#endif +} + +void graphite_stop_logging() +{ +// if (dbgout) delete dbgout; +// dbgout = 0; +} + +} // extern "C" + +#ifdef GRAPHITE2_TELEMETRY +size_t * graphite2::telemetry::_category = 0UL; +#endif + +#if !defined GRAPHITE2_NTRACING + +#ifdef GRAPHITE2_TELEMETRY + +json & graphite2::operator << (json & j, const telemetry & t) throw() +{ + j << json::object + << "type" << "telemetry" + << "silf" << t.silf + << "states" << t.states + << "starts" << t.starts + << "transitions" << t.transitions + << "glyphs" << t.glyph + << "code" << t.code + << "misc" << t.misc + << "total" << (t.silf + t.states + t.starts + t.transitions + t.glyph + t.code + t.misc) + << json::close; + return j; +} +#else +json & graphite2::operator << (json & j, const telemetry &) throw() +{ + return j; +} +#endif + + +json & graphite2::operator << (json & j, const CharInfo & ci) throw() +{ + return j << json::object + << "offset" << ci.base() + << "unicode" << ci.unicodeChar() + << "break" << ci.breakWeight() + << "flags" << ci.flags() + << "slot" << json::flat << json::object + << "before" << ci.before() + << "after" << ci.after() + << json::close + << json::close; +} + + +json & graphite2::operator << (json & j, const dslot & ds) throw() +{ + assert(ds.first); + assert(ds.second); + const Segment & seg = *ds.first; + const Slot & s = *ds.second; + const SlotCollision *cslot = seg.collisionInfo(ds.second); + + j << json::object + << "id" << objectid(ds) + << "gid" << s.gid() + << "charinfo" << json::flat << json::object + << "original" << s.original() + << "before" << s.before() + << "after" << s.after() + << json::close + << "origin" << s.origin() + << "shift" << Position(float(s.getAttr(0, gr_slatShiftX, 0)), + float(s.getAttr(0, gr_slatShiftY, 0))) + << "advance" << s.advancePos() + << "insert" << s.isInsertBefore() + << "break" << s.getAttr(&seg, gr_slatBreak, 0); + if (s.just() > 0) + j << "justification" << s.just(); + if (s.getBidiLevel() > 0) + j << "bidi" << s.getBidiLevel(); + if (!s.isBase()) + j << "parent" << json::flat << json::object + << "id" << objectid(dslot(&seg, s.attachedTo())) + << "level" << s.getAttr(0, gr_slatAttLevel, 0) + << "offset" << s.attachOffset() + << json::close; + j << "user" << json::flat << json::array; + for (int n = 0; n!= seg.numAttrs(); ++n) + j << s.userAttrs()[n]; + j << json::close; + if (s.firstChild()) + { + j << "children" << json::flat << json::array; + for (const Slot *c = s.firstChild(); c; c = c->nextSibling()) + j << objectid(dslot(&seg, c)); + j << json::close; + } + if (cslot) + { + // Note: the reason for using Positions to lump together related attributes is to make the + // JSON output slightly more compact. + j << "collision" << json::flat << json::object +// << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself + << "offset" << cslot->offset() + << "limit" << cslot->limit() + << "flags" << cslot->flags() + << "margin" << Position(cslot->margin(), cslot->marginWt()) + << "exclude" << cslot->exclGlyph() + << "excludeoffset" << cslot->exclOffset(); + if (cslot->seqOrder() != 0) + { + j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass()) + << "seqorder" << cslot->seqOrder() + << "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt()) + << "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt()) + << "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt()); + } + j << json::close; + } + return j << json::close; +} + + +graphite2::objectid::objectid(const dslot & ds) throw() +{ + const Slot * const p = ds.second; + uint32 s = uint32(reinterpret_cast(p)); + sprintf(name, "%.4x-%.2x-%.4hx", uint16(s >> 16), uint16(p ? p->userAttrs()[ds.first->silf()->numUser()] : 0), uint16(s)); + name[sizeof name-1] = 0; +} + +graphite2::objectid::objectid(const Segment * const p) throw() +{ + uint32 s = uint32(reinterpret_cast(p)); + sprintf(name, "%.4x-%.2x-%.4hx", uint16(s >> 16), 0, uint16(s)); + name[sizeof name-1] = 0; +} + +#endif diff --git a/thirdparty/graphite/src/gr_segment.cpp b/thirdparty/graphite/src/gr_segment.cpp new file mode 100644 index 00000000000..7a27e9c562e --- /dev/null +++ b/thirdparty/graphite/src/gr_segment.cpp @@ -0,0 +1,175 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "graphite2/Segment.h" +#include "inc/UtfCodec.h" +#include "inc/Segment.h" + +using namespace graphite2; + +namespace +{ + + gr_segment* makeAndInitialize(const Font *font, const Face *face, uint32 script, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars, int dir) + { + if (script == 0x20202020) script = 0; + else if ((script & 0x00FFFFFF) == 0x00202020) script = script & 0xFF000000; + else if ((script & 0x0000FFFF) == 0x00002020) script = script & 0xFFFF0000; + else if ((script & 0x000000FF) == 0x00000020) script = script & 0xFFFFFF00; + // if (!font) return NULL; + Segment* pRes=new Segment(nChars, face, script, dir); + + + if (!pRes->read_text(face, pFeats, enc, pStart, nChars) || !pRes->runGraphite()) + { + delete pRes; + return NULL; + } + pRes->finalise(font, true); + + return static_cast(pRes); + } + + template + inline size_t count_unicode_chars(utf_iter first, const utf_iter last, const void **error) + { + size_t n_chars = 0; + uint32 usv = 0; + + if (last) + { + if (!first.validate(last)) + { + if (error) *error = last - 1; + return 0; + } + for (;first != last; ++first, ++n_chars) + if ((usv = *first) == 0 || first.error()) break; + } + else + { + while ((usv = *first) != 0 && !first.error()) + { + ++first; + ++n_chars; + } + } + + if (error) *error = first.error() ? first : 0; + return n_chars; + } +} + + +extern "C" { + +size_t gr_count_unicode_characters(gr_encform enc, const void* buffer_begin, const void* buffer_end/*don't go on or past end, If NULL then ignored*/, const void** pError) //Also stops on nul. Any nul is not in the count +{ + assert(buffer_begin); + + switch (enc) + { + case gr_utf8: return count_unicode_chars(buffer_begin, buffer_end, pError); break; + case gr_utf16: return count_unicode_chars(buffer_begin, buffer_end, pError); break; + case gr_utf32: return count_unicode_chars(buffer_begin, buffer_end, pError); break; + default: return 0; + } +} + + +gr_segment* gr_make_seg(const gr_font *font, const gr_face *face, gr_uint32 script, const gr_feature_val* pFeats, gr_encform enc, const void* pStart, size_t nChars, int dir) +{ + if (!face) return nullptr; + + const gr_feature_val * tmp_feats = 0; + if (pFeats == 0) + pFeats = tmp_feats = static_cast(face->theSill().cloneFeatures(0)); + gr_segment * seg = makeAndInitialize(font, face, script, pFeats, enc, pStart, nChars, dir); + delete static_cast(tmp_feats); + + return seg; +} + + +void gr_seg_destroy(gr_segment* p) +{ + delete static_cast(p); +} + + +float gr_seg_advance_X(const gr_segment* pSeg/*not NULL*/) +{ + assert(pSeg); + return pSeg->advance().x; +} + + +float gr_seg_advance_Y(const gr_segment* pSeg/*not NULL*/) +{ + assert(pSeg); + return pSeg->advance().y; +} + + +unsigned int gr_seg_n_cinfo(const gr_segment* pSeg/*not NULL*/) +{ + assert(pSeg); + return static_cast(pSeg->charInfoCount()); +} + + +const gr_char_info* gr_seg_cinfo(const gr_segment* pSeg/*not NULL*/, unsigned int index/*must be (pSeg->charinfo(index)); +} + +unsigned int gr_seg_n_slots(const gr_segment* pSeg/*not NULL*/) +{ + assert(pSeg); + return static_cast(pSeg->slotCount()); +} + +const gr_slot* gr_seg_first_slot(gr_segment* pSeg/*not NULL*/) +{ + assert(pSeg); + return static_cast(pSeg->first()); +} + +const gr_slot* gr_seg_last_slot(gr_segment* pSeg/*not NULL*/) +{ + assert(pSeg); + return static_cast(pSeg->last()); +} + +float gr_seg_justify(gr_segment* pSeg/*not NULL*/, const gr_slot* pSlot/*not NULL*/, const gr_font *pFont, double width, enum gr_justFlags flags, const gr_slot *pFirst, const gr_slot *pLast) +{ + assert(pSeg); + assert(pSlot); + return pSeg->justify(const_cast(pSlot), pFont, float(width), justFlags(flags), const_cast(pFirst), const_cast(pLast)); +} + +} // extern "C" diff --git a/thirdparty/graphite/src/gr_slot.cpp b/thirdparty/graphite/src/gr_slot.cpp new file mode 100644 index 00000000000..a3c6b46a7f4 --- /dev/null +++ b/thirdparty/graphite/src/gr_slot.cpp @@ -0,0 +1,173 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "graphite2/Segment.h" +#include "inc/Segment.h" +#include "inc/Slot.h" +#include "inc/Font.h" + + +extern "C" { + + +const gr_slot* gr_slot_next_in_segment(const gr_slot* p/*not NULL*/) +{ + assert(p); + return static_cast(p->next()); +} + +const gr_slot* gr_slot_prev_in_segment(const gr_slot* p/*not NULL*/) +{ + assert(p); + return static_cast(p->prev()); +} + +const gr_slot* gr_slot_attached_to(const gr_slot* p/*not NULL*/) //returns NULL iff base. If called repeatedly on result, will get to a base +{ + assert(p); + return static_cast(p->attachedTo()); +} + + +const gr_slot* gr_slot_first_attachment(const gr_slot* p/*not NULL*/) //returns NULL iff no attachments. +{ //if slot_first_attachment(p) is not NULL, then slot_attached_to(slot_first_attachment(p))==p. + assert(p); + return static_cast(p->firstChild()); +} + + +const gr_slot* gr_slot_next_sibling_attachment(const gr_slot* p/*not NULL*/) //returns NULL iff no more attachments. +{ //if slot_next_sibling_attachment(p) is not NULL, then slot_attached_to(slot_next_sibling_attachment(p))==slot_attached_to(p). + assert(p); + return static_cast(p->nextSibling()); +} + + +unsigned short gr_slot_gid(const gr_slot* p/*not NULL*/) +{ + assert(p); + return p->glyph(); +} + + +float gr_slot_origin_X(const gr_slot* p/*not NULL*/) +{ + assert(p); + return p->origin().x; +} + + +float gr_slot_origin_Y(const gr_slot* p/*not NULL*/) +{ + assert(p); + return p->origin().y; +} + + +float gr_slot_advance_X(const gr_slot* p/*not NULL*/, const gr_face *face, const gr_font *font) +{ + assert(p); + float scale = 1.0; + float res = p->advance(); + if (font) + { + scale = font->scale(); + int gid = p->glyph(); + if (face && font->isHinted() && gid < face->glyphs().numGlyphs()) + res = (res - face->glyphs().glyph(gid)->theAdvance().x) * scale + font->advance(gid); + else + res = res * scale; + } + return res; +} + +float gr_slot_advance_Y(const gr_slot *p/*not NULL*/, GR_MAYBE_UNUSED const gr_face *face, const gr_font *font) +{ + assert(p); + float res = p->advancePos().y; + if (font) + return res * font->scale(); + else + return res; +} + +int gr_slot_before(const gr_slot* p/*not NULL*/) +{ + assert(p); + return p->before(); +} + + +int gr_slot_after(const gr_slot* p/*not NULL*/) +{ + assert(p); + return p->after(); +} + +unsigned int gr_slot_index(const gr_slot *p/*not NULL*/) +{ + assert(p); + return p->index(); +} + +int gr_slot_attr(const gr_slot* p/*not NULL*/, const gr_segment* pSeg/*not NULL*/, gr_attrCode index, gr_uint8 subindex) +{ + assert(p); + return p->getAttr(pSeg, index, subindex); +} + + +int gr_slot_can_insert_before(const gr_slot* p/*not NULL*/) +{ + assert(p); + return (p->isInsertBefore())? 1 : 0; +} + + +int gr_slot_original(const gr_slot* p/*not NULL*/) +{ + assert(p); + return p->original(); +} + +void gr_slot_linebreak_before(gr_slot* p/*not NULL*/) +{ + assert(p); + gr_slot *prev = (gr_slot *)p->prev(); + prev->sibling(NULL); + prev->next(NULL); + p->prev(NULL); +} + +#if 0 //what should this be +size_t id(const gr_slot* p/*not NULL*/) +{ + return (size_t)p->id(); +} +#endif + + +} // extern "C" diff --git a/thirdparty/graphite/src/inc/CharInfo.h b/thirdparty/graphite/src/inc/CharInfo.h new file mode 100644 index 00000000000..01e7e31ac93 --- /dev/null +++ b/thirdparty/graphite/src/inc/CharInfo.h @@ -0,0 +1,66 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once +#include "inc/Main.h" + + +namespace graphite2 { + +class CharInfo +{ + +public: + CharInfo() : m_char(0), m_before(-1), m_after(-1), m_base(0), m_featureid(0), m_break(0), m_flags(0) {} + void init(int cid) { m_char = cid; } + unsigned int unicodeChar() const { return m_char; } + void feats(int offset) { m_featureid = offset; } + int fid() const { return m_featureid; } + int breakWeight() const { return m_break; } + void breakWeight(int val) { m_break = val; } + int after() const { return m_after; } + void after(int val) { m_after = val; } + int before() const { return m_before; } + void before(int val) { m_before = val; } + size_t base() const { return m_base; } + void base(size_t offset) { m_base = offset; } + void addflags(uint8 val) { m_flags |= val; } + uint8 flags() const { return m_flags; } + + CLASS_NEW_DELETE +private: + int m_char; // Unicode character from character stream + int m_before; // slot index before us, comes before + int m_after; // slot index after us, comes after + size_t m_base; // offset into input string corresponding to this charinfo + uint8 m_featureid; // index into features list in the segment + int8 m_break; // breakweight coming from lb table + uint8 m_flags; // 0,1 segment split. +}; + +} // namespace graphite2 + +struct gr_char_info : public graphite2::CharInfo {}; diff --git a/thirdparty/graphite/src/inc/CmapCache.h b/thirdparty/graphite/src/inc/CmapCache.h new file mode 100644 index 00000000000..7820c958b0e --- /dev/null +++ b/thirdparty/graphite/src/inc/CmapCache.h @@ -0,0 +1,82 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include "inc/Main.h" +#include "inc/Face.h" + +namespace graphite2 { + +class Face; + +class Cmap +{ +public: + + virtual ~Cmap() throw() {} + + virtual uint16 operator [] (const uint32) const throw() { return 0; } + + virtual operator bool () const throw() { return false; } + + CLASS_NEW_DELETE; +}; + +class DirectCmap : public Cmap +{ + DirectCmap(const DirectCmap &); + DirectCmap & operator = (const DirectCmap &); + +public: + DirectCmap(const Face &); + virtual uint16 operator [] (const uint32 usv) const throw(); + virtual operator bool () const throw(); + + CLASS_NEW_DELETE; +private: + const Face::Table _cmap; + const void * _smp, + * _bmp; +}; + +class CachedCmap : public Cmap +{ + CachedCmap(const CachedCmap &); + CachedCmap & operator = (const CachedCmap &); + +public: + CachedCmap(const Face &); + virtual ~CachedCmap() throw(); + virtual uint16 operator [] (const uint32 usv) const throw(); + virtual operator bool () const throw(); + CLASS_NEW_DELETE; +private: + bool m_isBmpOnly; + uint16 ** m_blocks; +}; + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Code.h b/thirdparty/graphite/src/inc/Code.h new file mode 100644 index 00000000000..3cee67c81d3 --- /dev/null +++ b/thirdparty/graphite/src/inc/Code.h @@ -0,0 +1,171 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// This class represents loaded graphite stack machine code. It performs +// basic sanity checks, on the incoming code to prevent more obvious problems +// from crashing graphite. +// Author: Tim Eves + +#pragma once + +#include +#include +#include "inc/Main.h" +#include "inc/Machine.h" + +namespace graphite2 { + +class Silf; +class Face; + +enum passtype { + PASS_TYPE_UNKNOWN = 0, + PASS_TYPE_LINEBREAK, + PASS_TYPE_SUBSTITUTE, + PASS_TYPE_POSITIONING, + PASS_TYPE_JUSTIFICATION +}; + +namespace vm { + +class Machine::Code +{ +public: + enum status_t + { + loaded, + alloc_failed, + invalid_opcode, + unimplemented_opcode_used, + out_of_range_data, + jump_past_end, + arguments_exhausted, + missing_return, + nested_context_item, + underfull_stack + }; + +private: + class decoder; + + instr * _code; + byte * _data; + size_t _data_size, + _instr_count; + byte _max_ref; + mutable status_t _status; + bool _constraint, + _modify, + _delete; + mutable bool _own; + + void release_buffers() throw (); + void failure(const status_t) throw(); + +public: + static size_t estimateCodeDataOut(size_t num_bytecodes, int nRules, int nSlots); + + Code() throw(); + Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end, + uint8 pre_context, uint16 rule_length, const Silf &, const Face &, + enum passtype pt, byte * * const _out = 0); + Code(const Machine::Code &) throw(); + ~Code() throw(); + + Code & operator=(const Code &rhs) throw(); + operator bool () const throw() { return _code && status() == loaded; } + status_t status() const throw() { return _status; } + bool constraint() const throw() { return _constraint; } + size_t dataSize() const throw() { return _data_size; } + size_t instructionCount() const throw() { return _instr_count; } + bool immutable() const throw() { return !(_delete || _modify); } + bool deletes() const throw() { return _delete; } + size_t maxRef() const throw() { return _max_ref; } + void externalProgramMoved(ptrdiff_t) throw(); + + int32 run(Machine &m, slotref * & map) const; + + CLASS_NEW_DELETE; +}; + +inline +size_t Machine::Code::estimateCodeDataOut(size_t n_bc, int nRules, int nSlots) +{ + // max is: all codes are instructions + 1 for each rule + max tempcopies + // allocate space for separate maximal code and data then merge them later + return (n_bc + nRules + nSlots) * sizeof(instr) + n_bc * sizeof(byte); +} + + +inline Machine::Code::Code() throw() +: _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), + _status(loaded), _constraint(false), _modify(false), _delete(false), + _own(false) +{ +} + +inline Machine::Code::Code(const Machine::Code &obj) throw () + : _code(obj._code), + _data(obj._data), + _data_size(obj._data_size), + _instr_count(obj._instr_count), + _max_ref(obj._max_ref), + _status(obj._status), + _constraint(obj._constraint), + _modify(obj._modify), + _delete(obj._delete), + _own(obj._own) +{ + obj._own = false; +} + +inline Machine::Code & Machine::Code::operator=(const Machine::Code &rhs) throw() { + if (_instr_count > 0) + release_buffers(); + _code = rhs._code; + _data = rhs._data; + _data_size = rhs._data_size; + _instr_count = rhs._instr_count; + _status = rhs._status; + _constraint = rhs._constraint; + _modify = rhs._modify; + _delete = rhs._delete; + _own = rhs._own; + rhs._own = false; + return *this; +} + +inline void Machine::Code::externalProgramMoved(ptrdiff_t dist) throw() +{ + if (_code && !_own) + { + _code += dist / signed(sizeof(instr)); + _data += dist; + } +} + +} // namespace vm +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Collider.h b/thirdparty/graphite/src/inc/Collider.h new file mode 100644 index 00000000000..71e8400501f --- /dev/null +++ b/thirdparty/graphite/src/inc/Collider.h @@ -0,0 +1,245 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include "inc/List.h" +#include "inc/Position.h" +#include "inc/Intervals.h" +#include "inc/debug.h" + +namespace graphite2 { + +class json; +class Slot; +class Segment; + +#define SLOTCOLSETUINTPROP(x, y) uint16 x() const { return _ ##x; } void y (uint16 v) { _ ##x = v; } +#define SLOTCOLSETINTPROP(x, y) int16 x() const { return _ ##x; } void y (int16 v) { _ ##x = v; } +#define SLOTCOLSETPOSITIONPROP(x, y) const Position &x() const { return _ ##x; } void y (const Position &v) { _ ##x = v; } + +// Slot attributes related to collision-fixing +class SlotCollision +{ +public: + enum { + // COLL_TESTONLY = 0, // default - test other glyphs for collision with this one, but don't move this one + COLL_FIX = 1, // fix collisions involving this glyph + COLL_IGNORE = 2, // ignore this glyph altogether + COLL_START = 4, // start of range of possible collisions + COLL_END = 8, // end of range of possible collisions + COLL_KERN = 16, // collisions with this glyph are fixed by adding kerning space after it + COLL_ISCOL = 32, // this glyph has a collision + COLL_KNOWN = 64, // we've figured out what's happening with this glyph + COLL_ISSPACE = 128, // treat this glyph as a space with regard to kerning + COLL_TEMPLOCK = 256, // Lock glyphs that have been given priority positioning + ////COLL_JUMPABLE = 128, // moving glyphs may jump this stationary glyph in any direction - DELETE + ////COLL_OVERLAP = 256, // use maxoverlap to restrict - DELETE + }; + + // Behavior for the collision.order attribute. To GDL this is an enum, to us it's a bitfield, with only 1 bit set + // Allows for easier inversion. + enum { + SEQ_ORDER_LEFTDOWN = 1, + SEQ_ORDER_RIGHTUP = 2, + SEQ_ORDER_NOABOVE = 4, + SEQ_ORDER_NOBELOW = 8, + SEQ_ORDER_NOLEFT = 16, + SEQ_ORDER_NORIGHT = 32 + }; + + SlotCollision(Segment *seg, Slot *slot); + void initFromSlot(Segment *seg, Slot *slot); + + const Rect &limit() const { return _limit; } + void setLimit(const Rect &r) { _limit = r; } + SLOTCOLSETPOSITIONPROP(shift, setShift) + SLOTCOLSETPOSITIONPROP(offset, setOffset) + SLOTCOLSETPOSITIONPROP(exclOffset, setExclOffset) + SLOTCOLSETUINTPROP(margin, setMargin) + SLOTCOLSETUINTPROP(marginWt, setMarginWt) + SLOTCOLSETUINTPROP(flags, setFlags) + SLOTCOLSETUINTPROP(exclGlyph, setExclGlyph) + SLOTCOLSETUINTPROP(seqClass, setSeqClass) + SLOTCOLSETUINTPROP(seqProxClass, setSeqProxClass) + SLOTCOLSETUINTPROP(seqOrder, setSeqOrder) + SLOTCOLSETINTPROP(seqAboveXoff, setSeqAboveXoff) + SLOTCOLSETUINTPROP(seqAboveWt, setSeqAboveWt) + SLOTCOLSETINTPROP(seqBelowXlim, setSeqBelowXlim) + SLOTCOLSETUINTPROP(seqBelowWt, setSeqBelowWt) + SLOTCOLSETUINTPROP(seqValignHt, setSeqValignHt) + SLOTCOLSETUINTPROP(seqValignWt, setSeqValignWt) + + float getKern(int dir) const; + bool ignore() const; + +private: + Rect _limit; + Position _shift; // adjustment within the given pass + Position _offset; // total adjustment for collisions + Position _exclOffset; + uint16 _margin; + uint16 _marginWt; + uint16 _flags; + uint16 _exclGlyph; + uint16 _seqClass; + uint16 _seqProxClass; + uint16 _seqOrder; + int16 _seqAboveXoff; + uint16 _seqAboveWt; + int16 _seqBelowXlim; + uint16 _seqBelowWt; + uint16 _seqValignHt; + uint16 _seqValignWt; + +}; // end of class SlotColllision + +struct BBox; +struct SlantBox; + +class ShiftCollider +{ +public: + typedef std::pair fpair; + typedef Vector vfpairs; + typedef vfpairs::iterator ivfpairs; + + ShiftCollider(json *dbgout); + ~ShiftCollider() throw() { }; + + bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint, + float margin, float marginMin, const Position &currShift, + const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout); + bool mergeSlot(Segment *seg, Slot *slot, const SlotCollision *cinfo, const Position &currShift, bool isAfter, + bool sameCluster, bool &hasCol, bool isExclusion, GR_MAYBE_UNUSED json * const dbgout); + Position resolve(Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout); + void addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int mode); + void removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int mode); + const Position &origin() const { return _origin; } + +#if !defined GRAPHITE2_NTRACING + void outputJsonDbg(json * const dbgout, Segment *seg, int axis); + void outputJsonDbgStartSlot(json * const dbgout, Segment *seg); + void outputJsonDbgEndSlot(json * const dbgout, Position resultPos, int bestAxis, bool isCol); + void outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis, float tleft, float bestCost, float bestVal); + void outputJsonDbgRawRanges(json * const dbgout, int axis); + void outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg); +#endif + + CLASS_NEW_DELETE; + +protected: + Zones _ranges[4]; // possible movements in 4 directions (horizontally, vertically, diagonally); + Slot * _target; // the glyph to fix + Rect _limit; + Position _currShift; + Position _currOffset; + Position _origin; // Base for all relative calculations + float _margin; + float _marginWt; + float _len[4]; + uint16 _seqClass; + uint16 _seqProxClass; + uint16 _seqOrder; + + //bool _scraping[4]; + +}; // end of class ShiftCollider + +inline +ShiftCollider::ShiftCollider(GR_MAYBE_UNUSED json *dbgout) +: _target(0), + _margin(0.0), + _marginWt(0.0), + _seqClass(0), + _seqProxClass(0), + _seqOrder(0) +{ +#if !defined GRAPHITE2_NTRACING + for (int i = 0; i < 4; ++i) + _ranges[i].setdebug(dbgout); +#endif +} + +class KernCollider +{ +public: + KernCollider(json *dbg); + ~KernCollider() throw() { }; + bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint, float margin, + const Position &currShift, const Position &offsetPrev, int dir, + float ymin, float ymax, json * const dbgout); + bool mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, json * const dbgout); + Position resolve(Segment *seg, Slot *slot, int dir, json * const dbgout); + void shift(const Position &mv, int dir); + + CLASS_NEW_DELETE; + +private: + Slot * _target; // the glyph to fix + Rect _limit; + float _margin; + Position _offsetPrev; // kern from a previous pass + Position _currShift; // NOT USED?? + float _miny; // y-coordinates offset by global slot position + float _maxy; + Vector _edges; // edges of horizontal slices + float _sliceWidth; // width of each slice + float _mingap; + float _xbound; // max or min edge + bool _hit; + +#if !defined GRAPHITE2_NTRACING + // Debugging + Segment * _seg; + Vector _nearEdges; // closest potential collision in each slice + Vector _slotNear; +#endif +}; // end of class KernCollider + + +inline +float sqr(float x) { + return x * x; +} + +inline +KernCollider::KernCollider(GR_MAYBE_UNUSED json *dbg) +: _target(0), + _margin(0.0f), + _miny(-1e38f), + _maxy(1e38f), + _sliceWidth(0.0f), + _mingap(0.0f), + _xbound(0.0), + _hit(false) +{ +#if !defined GRAPHITE2_NTRACING + _seg = 0; +#endif +}; + +}; // end of namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Compression.h b/thirdparty/graphite/src/inc/Compression.h new file mode 100644 index 00000000000..9fe10e025d6 --- /dev/null +++ b/thirdparty/graphite/src/inc/Compression.h @@ -0,0 +1,104 @@ +/* GRAPHITE2 LICENSING + + Copyright 2015, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ + +#pragma once + +#include +#include +#include + +namespace +{ + +#if defined(_MSC_VER) +typedef unsigned __int8 u8; +typedef unsigned __int16 u16; +typedef unsigned __int32 u32; +typedef unsigned __int64 u64; +#else +#include +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +#endif + +ptrdiff_t const MINMATCH = 4, + LASTLITERALS = 5, + MINCODA = LASTLITERALS+1, + MINSRCSIZE = 13; + +template +inline +void unaligned_copy(void * d, void const * s) { + ::memcpy(d, s, S); +} + +inline +size_t align(size_t p) { + return (p + sizeof(unsigned long)-1) & ~(sizeof(unsigned long)-1); +} + +inline +u8 * safe_copy(u8 * d, u8 const * s, size_t n) { + while (n--) *d++ = *s++; + return d; +} + +inline +u8 * overrun_copy(u8 * d, u8 const * s, size_t n) { + size_t const WS = sizeof(unsigned long); + u8 const * e = s + n; + do + { + unaligned_copy(d, s); + d += WS; + s += WS; + } + while (s < e); + d-=(s-e); + + return d; +} + + +inline +u8 * fast_copy(u8 * d, u8 const * s, size_t n) { + size_t const WS = sizeof(unsigned long); + size_t wn = n/WS; + while (wn--) + { + unaligned_copy(d, s); + d += WS; + s += WS; + } + n &= WS-1; + return safe_copy(d, s, n); +} + + +} // end of anonymous namespace diff --git a/thirdparty/graphite/src/inc/Decompressor.h b/thirdparty/graphite/src/inc/Decompressor.h new file mode 100644 index 00000000000..10f21b7af18 --- /dev/null +++ b/thirdparty/graphite/src/inc/Decompressor.h @@ -0,0 +1,54 @@ +/* GRAPHITE2 LICENSING + + Copyright 2015, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ + +#pragma once + +#include + +namespace lz4 +{ + +// decompress an LZ4 block +// Parameters: +// @in - Input buffer containing an LZ4 block. +// @in_size - Size of the input LZ4 block in bytes. +// @out - Output buffer to hold decompressed results. +// @out_size - The size of the buffer pointed to by @out. +// Invariants: +// @in - This buffer must be at least 1 machine word in length, +// regardless of the actual LZ4 block size. +// @in_size - This must be at least 4 and must also be <= to the +// allocated buffer @in. +// @out - This must be bigger than the input buffer and at least +// 13 bytes. +// @out_size - Must always be big enough to hold the expected size. +// Return: +// -1 - Decompression failed. +// size - Actual number of bytes decompressed. +int decompress(void const *in, size_t in_size, void *out, size_t out_size); + +} // end of namespace shrinker diff --git a/thirdparty/graphite/src/inc/Endian.h b/thirdparty/graphite/src/inc/Endian.h new file mode 100644 index 00000000000..56ecfd86671 --- /dev/null +++ b/thirdparty/graphite/src/inc/Endian.h @@ -0,0 +1,111 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ + +/* +Description: + A set of fast template based decoders for decoding values of any C integer + type up to long int size laid out with most significant byte first or least + significant byte first (aka big endian or little endian). These are CPU + byte order agnostic and will function the same regardless of the CPUs native + byte order. + + Being template based means if the either le or be class is not used then + template code of unused functions will not be instantiated by the compiler + and thus shouldn't cause any overhead. +*/ + +#include + +#pragma once + + +class be +{ + template + inline static unsigned long int _peek(const unsigned char * p) { + return _peek(p) << (S/2)*8 | _peek(p+S/2); + } +public: + template + inline static T peek(const void * p) { + return T(_peek(static_cast(p))); + } + + template + inline static T read(const unsigned char * &p) { + const T r = T(_peek(p)); + p += sizeof r; + return r; + } + + template + inline static T swap(const T x) { + return T(_peek(reinterpret_cast(&x))); + } + + template + inline static void skip(const unsigned char * &p, size_t n=1) { + p += sizeof(T)*n; + } +}; + +template<> +inline unsigned long int be::_peek<1>(const unsigned char * p) { return *p; } + + +class le +{ + template + inline static unsigned long int _peek(const unsigned char * p) { + return _peek(p) | _peek(p+S/2) << (S/2)*8; + } +public: + template + inline static T peek(const void * p) { + return T(_peek(static_cast(p))); + } + + template + inline static T read(const unsigned char * &p) { + const T r = T(_peek(p)); + p += sizeof r; + return r; + } + + template + inline static T swap(const T x) { + return T(_peek(reinterpret_cast(&x))); + } + + template + inline static void skip(const unsigned char * &p, size_t n=1) { + p += sizeof(T)*n; + } +}; + +template<> +inline unsigned long int le::_peek<1>(const unsigned char * p) { return *p; } diff --git a/thirdparty/graphite/src/inc/Error.h b/thirdparty/graphite/src/inc/Error.h new file mode 100644 index 00000000000..2b7ab763a24 --- /dev/null +++ b/thirdparty/graphite/src/inc/Error.h @@ -0,0 +1,134 @@ +/* GRAPHITE2 LICENSING + + Copyright 2013, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +// numbers are explicitly assigned for future proofing + +namespace graphite2 +{ + +class Error +{ +public: + Error() : _e(0) {}; + operator bool() { return (_e != 0); } + int error() { return _e; } + void error(int e) { _e = e; } + bool test(bool pr, int err) { return (_e = int(pr) * err); } + +private: + int _e; +}; + +enum errcontext { + EC_READGLYPHS = 1, // while reading glyphs + EC_READSILF = 2, // in Silf table + EC_ASILF = 3, // in Silf %d + EC_APASS = 4, // in Silf %d, pass %d + EC_PASSCCODE = 5, // in pass constraint code for Silf %d, pass %d + EC_ARULE = 6, // in Silf %d, pass %d, rule %d + EC_ASTARTS = 7, // in Silf %d, pass %d, start state %d + EC_ATRANS = 8, // in Silf %d, pass %d, fsm state %d + EC_ARULEMAP = 9 // in Silf %d, pass %d, state %d +}; + +enum errors { + E_OUTOFMEM = 1, // Out of memory + E_NOGLYPHS = 2, // There are no glyphs in the font + E_BADUPEM = 3, // The units per em for the font is bad (0) + E_BADCMAP = 4, // The font does not contain any useful cmaps + E_NOSILF = 5, // Missing Silf table + E_TOOOLD = 6, // Silf table version is too old + E_BADSIZE = 7, // context object has the wrong structural size +// Silf Subtable Errors take a Silf subtable number * 256 in the context + E_BADMAXGLYPH = 8, // Silf max glyph id is too high + E_BADNUMJUSTS = 9, // Number of Silf justification blocks is too high + E_BADENDJUSTS = 10, // Silf justification blocks take too much of the Silf table space + E_BADCRITFEATURES = 11, // Critical features section in a Silf table is too big + E_BADSCRIPTTAGS = 12, // Silf script tags area is too big + E_BADAPSEUDO = 13, // The pseudo glyph attribute number is too high + E_BADABREAK = 14, // The linebreak glyph attribute number is too high + E_BADABIDI = 15, // The bidi glyph attribute number is too high + E_BADAMIRROR = 16, // The mirrored glyph attribute number is too high + E_BADNUMPASSES = 17, // The number of passes is > 128 + E_BADPASSESSTART = 18, // The Silf table is too small to hold any passes + E_BADPASSBOUND = 19, // The positioning pass number is too low or the substitution pass number is too high + E_BADPPASS = 20, // The positioning pass number is too high + E_BADSPASS = 21, // the substitution pass number is too high + E_BADJPASSBOUND = 22, // the justification pass must be higher than the positioning pass + E_BADJPASS = 23, // the justification pass is too high + E_BADALIG = 24, // the number of initial ligature component glyph attributes is too high + E_BADBPASS = 25, // the bidi pass number is specified and is either too high or too low + E_BADNUMPSEUDO = 26, // The number of pseudo glyphs is too high + E_BADCLASSSIZE = 27, // The size of the classes block is bad + E_TOOMANYLINEAR = 28, // The number of linear classes in the silf table is too high + E_CLASSESTOOBIG = 29, // There are too many classes for the space allocated in the Silf subtable + E_MISALIGNEDCLASSES = 30, // The class offsets in the class table don't line up with the number of classes + E_HIGHCLASSOFFSET = 31, // The class offsets point out of the class table + E_BADCLASSOFFSET = 32, // A class offset is less than one following it + E_BADCLASSLOOKUPINFO = 33, // The search header info for a non-linear class has wrong values in it +// Pass subtable errors. Context has pass number * 65536 + E_BADPASSSTART = 34, // The start offset for a particular pass is bad + E_BADPASSEND = 35, // The end offset for a particular pass is bad + E_BADPASSLENGTH = 36, // The length of the pass is too small + E_BADNUMTRANS = 37, // The number of transition states in the fsm is bad + E_BADNUMSUCCESS = 38, // The number of success states in the fsm is bad + E_BADNUMSTATES = 39, // The number of states in the fsm is bad + E_NORANGES = 40, // There are no columns in the fsm + E_BADRULEMAPLEN = 41, // The size of the success state to rule mapping is bad + E_BADCTXTLENBOUNDS = 42, // The precontext maximum is greater than its minimum + E_BADCTXTLENS = 43, // The lists of rule lengths or pre context lengths is bad + E_BADPASSCCODEPTR = 44, // The pass constraint code position does not align with where the forward reference says it should be + E_BADRULECCODEPTR = 45, // The rule constraint code position does not align with where the forward reference says it should be + E_BADCCODELEN = 46, // Bad rule/pass constraint code length + E_BADACTIONCODEPTR = 47, // The action code position does not align with where the forward reference says it should be + E_MUTABLECCODE = 48, // Constraint code edits slots. It shouldn't. + E_BADSTATE = 49, // Bad state transition referencing an illegal state + E_BADRULEMAPPING = 50, // The structure of the rule mapping is bad + E_BADRANGE = 51, // Bad column range structure including a glyph in more than one column + E_BADRULENUM = 52, // A reference to a rule is out of range (too high) + E_BADACOLLISION = 53, // Bad Silf table collision attribute number (too high) + E_BADEMPTYPASS = 54, // Can't have empty passes (no rules) except for collision passes + E_BADSILFVERSION = 55, // The Silf table has a bad version (probably too high) + E_BADCOLLISIONPASS = 56, // Collision flags set on a non positioning pass + E_BADNUMCOLUMNS = 57, // Arbitrarily limit number of columns in fsm +// Code errors + E_CODEFAILURE = 60, // Base code error. The following subcodes must align with Machine::Code::status_t in Code.h + E_CODEALLOC = 61, // Out of memory + E_INVALIDOPCODE = 62, // Invalid op code + E_UNIMPOPCODE = 63, // Unimplemented op code encountered + E_OUTOFRANGECODE = 64, // Code argument out of range + E_BADJUMPCODE = 65, // Code jumps past end of op codes + E_CODEBADARGS = 66, // Code arguments exhausted + E_CODENORETURN = 67, // Missing return type op code at end of code + E_CODENESTEDCTXT = 68, // Nested context encountered in code +// Compression errors + E_BADSCHEME = 69, + E_SHRINKERFAILED = 70, +}; + +} diff --git a/thirdparty/graphite/src/inc/Face.h b/thirdparty/graphite/src/inc/Face.h new file mode 100644 index 00000000000..355c5aa0d33 --- /dev/null +++ b/thirdparty/graphite/src/inc/Face.h @@ -0,0 +1,225 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include + +#include "graphite2/Font.h" + +#include "inc/Main.h" +#include "inc/FeatureMap.h" +#include "inc/TtfUtil.h" +#include "inc/Silf.h" +#include "inc/Error.h" + +namespace graphite2 { + +class Cmap; +class FileFace; +class GlyphCache; +class NameTable; +class json; +class Font; + + +using TtfUtil::Tag; + +// These are the actual tags, as distinct from the consecutive IDs in TtfUtil.h + +class Face +{ + // Prevent any kind of copying + Face(const Face&); + Face& operator=(const Face&); + +public: + class Table; + static float default_glyph_advance(const void* face_ptr, gr_uint16 glyphid); + + Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops); + virtual ~Face(); + + virtual bool runGraphite(Segment *seg, const Silf *silf) const; + +public: + bool readGlyphs(uint32 faceOptions); + bool readGraphite(const Table & silf); + bool readFeatures(); + void takeFileFace(FileFace* pFileFace/*takes ownership*/); + + const SillMap & theSill() const; + const GlyphCache & glyphs() const; + Cmap & cmap() const; + NameTable * nameTable() const; + void setLogger(FILE *log_file); + json * logger() const throw(); + + const Silf * chooseSilf(uint32 script) const; + uint16 languageForLocale(const char * locale) const; + + // Features + uint16 numFeatures() const; + const FeatureRef * featureById(uint32 id) const; + const FeatureRef * feature(uint16 index) const; + + // Glyph related + int32 getGlyphMetric(uint16 gid, uint8 metric) const; + uint16 findPseudo(uint32 uid) const; + + // Errors + unsigned int error() const { return m_error; } + bool error(Error e) { m_error = e.error(); return false; } + unsigned int error_context() const { return m_error; } + void error_context(unsigned int errcntxt) { m_errcntxt = errcntxt; } + + CLASS_NEW_DELETE; +private: + SillMap m_Sill; + gr_face_ops m_ops; + const void * m_appFaceHandle; // non-NULL + FileFace * m_pFileFace; //owned + mutable GlyphCache * m_pGlyphFaceCache; // owned - never NULL + mutable Cmap * m_cmap; // cmap cache if available + mutable NameTable * m_pNames; + mutable json * m_logger; + unsigned int m_error; + unsigned int m_errcntxt; +protected: + Silf * m_silfs; // silf subtables. + uint16 m_numSilf; // num silf subtables in the silf table +private: + uint16 m_ascent, + m_descent; +#ifdef GRAPHITE2_TELEMETRY +public: + mutable telemetry tele; +#endif +}; + + + +inline +const SillMap & Face::theSill() const +{ + return m_Sill; +} + +inline +uint16 Face::numFeatures() const +{ + return m_Sill.theFeatureMap().numFeats(); +} + +inline +const FeatureRef * Face::featureById(uint32 id) const +{ + return m_Sill.theFeatureMap().findFeatureRef(id); +} + +inline +const FeatureRef *Face::feature(uint16 index) const +{ + return m_Sill.theFeatureMap().feature(index); +} + +inline +const GlyphCache & Face::glyphs() const +{ + return *m_pGlyphFaceCache; +} + +inline +Cmap & Face::cmap() const +{ + return *m_cmap; +}; + +inline +json * Face::logger() const throw() +{ + return m_logger; +} + + + +class Face::Table +{ + const Face * _f; + mutable const byte * _p; + size_t _sz; + bool _compressed; + + Error decompress(); + + void release(); + +public: + Table() throw(); + Table(const Face & face, const Tag n, uint32 version=0xffffffff) throw(); + ~Table() throw(); + Table(const Table && rhs) throw(); + + operator const byte * () const throw(); + + size_t size() const throw(); + Table & operator = (const Table && rhs) throw(); +}; + +inline +Face::Table::Table() throw() +: _f(0), _p(0), _sz(0), _compressed(false) +{ +} + +inline +Face::Table::Table(const Table && rhs) throw() +: _f(rhs._f), _p(rhs._p), _sz(rhs._sz), _compressed(rhs._compressed) +{ + rhs._p = 0; +} + +inline +Face::Table::~Table() throw() +{ + release(); +} + +inline +Face::Table::operator const byte * () const throw() +{ + return _p; +} + +inline +size_t Face::Table::size() const throw() +{ + return _sz; +} + +} // namespace graphite2 + +struct gr_face : public graphite2::Face {}; diff --git a/thirdparty/graphite/src/inc/FeatureMap.h b/thirdparty/graphite/src/inc/FeatureMap.h new file mode 100644 index 00000000000..0f05e941a27 --- /dev/null +++ b/thirdparty/graphite/src/inc/FeatureMap.h @@ -0,0 +1,198 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once +#include "inc/Main.h" +#include "inc/FeatureVal.h" + +namespace graphite2 { + +// Forward declarations for implmentation types +class FeatureMap; +class Face; + + +class FeatureSetting +{ +public: + FeatureSetting(int16 theValue, uint16 labelId) : m_label(labelId), m_value(theValue) {}; + uint16 label() const { return m_label; } + int16 value() const { return m_value; } + + CLASS_NEW_DELETE; +private: + FeatureSetting(const FeatureSetting & fs) : m_label(fs.m_label), m_value(fs.m_value) {}; + + uint16 m_label; + int16 m_value; +}; + +class FeatureRef +{ + typedef uint32 chunk_t; + static const uint8 SIZEOF_CHUNK = sizeof(chunk_t)*8; + +public: + enum flags_t : uint16 { + HIDDEN = 0x0800 + }; + FeatureRef() throw(); + FeatureRef(const Face & face, unsigned short & bits_offset, uint32 max_val, + uint32 name, uint16 uiName, flags_t flags, + FeatureSetting *settings, uint16 num_set) throw(); + ~FeatureRef() throw(); + + bool applyValToFeature(uint32 val, Features& pDest) const; //defined in GrFaceImp.h + void maskFeature(Features & pDest) const { + if (m_index < pDest.size()) //defensive + pDest[m_index] |= m_mask; + } + + uint32 getFeatureVal(const Features& feats) const; //defined in GrFaceImp.h + + uint32 getId() const { return m_id; } + uint16 getNameId() const { return m_nameid; } + uint16 getNumSettings() const { return m_numSet; } + uint16 getSettingName(uint16 index) const { return m_nameValues[index].label(); } + int16 getSettingValue(uint16 index) const { return m_nameValues[index].value(); } + flags_t getFlags() const { return m_flags; } + uint32 maxVal() const { return m_max; } + const Face & getFace() const { assert(m_face); return *m_face;} + const FeatureMap* getFeatureMap() const;// { return m_pFace;} + + CLASS_NEW_DELETE; +private: + FeatureRef(const FeatureRef & rhs); + + const Face * m_face; + FeatureSetting * m_nameValues; // array of name table ids for feature values + chunk_t m_mask, // bit mask to get the value from the vector + m_max; // max value the value can take + uint32 m_id; // feature identifier/name + uint16 m_nameid, // Name table id for feature name + m_numSet; // number of values (number of entries in m_nameValues) + flags_t m_flags; // feature flags see FeatureRef::flags_t. + byte m_bits, // how many bits to shift the value into place + m_index; // index into the array to find the ulong to mask + +private: //unimplemented + FeatureRef& operator=(const FeatureRef&); +}; + +inline +FeatureRef::FeatureRef() throw() +: m_face(0), + m_nameValues(0), + m_mask(0), m_max(0), + m_id(0), m_nameid(0), m_numSet(0), + m_flags(flags_t(0)), + m_bits(0), m_index(0) +{ +} + + +class NameAndFeatureRef +{ + public: + NameAndFeatureRef(uint32 name = 0) : m_name(name) , m_pFRef(NULL){} + NameAndFeatureRef(FeatureRef const & p) : m_name(p.getId()), m_pFRef(&p) {} + + bool operator<(const NameAndFeatureRef& rhs) const //orders by m_name + { return m_name +#include +#include "inc/Main.h" +#include "inc/List.h" + +namespace graphite2 { + +class FeatureRef; +class FeatureMap; + +class FeatureVal : public Vector +{ +public: + FeatureVal() : m_pMap(0) { } + FeatureVal(int num, const FeatureMap & pMap) : Vector(num), m_pMap(&pMap) {} + FeatureVal(const FeatureVal & rhs) : Vector(rhs), m_pMap(rhs.m_pMap) {} + + FeatureVal & operator = (const FeatureVal & rhs) { Vector::operator = (rhs); m_pMap = rhs.m_pMap; return *this; } + + bool operator ==(const FeatureVal & b) const + { + size_t n = size(); + if (n != b.size()) return false; + + for(const_iterator l = begin(), r = b.begin(); n && *l == *r; --n, ++l, ++r); + + return n == 0; + } + + CLASS_NEW_DELETE +private: + friend class FeatureRef; //so that FeatureRefs can manipulate m_vec directly + const FeatureMap* m_pMap; +}; + +typedef FeatureVal Features; + +} // namespace graphite2 + + +struct gr_feature_val : public graphite2::FeatureVal {}; diff --git a/thirdparty/graphite/src/inc/FileFace.h b/thirdparty/graphite/src/inc/FileFace.h new file mode 100644 index 00000000000..35927847f8f --- /dev/null +++ b/thirdparty/graphite/src/inc/FileFace.h @@ -0,0 +1,80 @@ +/* GRAPHITE2 LICENSING + + Copyright 2012, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +//#include "inc/FeatureMap.h" +//#include "inc/GlyphsCache.h" +//#include "inc/Silf.h" + +#ifndef GRAPHITE2_NFILEFACE + +#include +#include + +#include "graphite2/Font.h" + +#include "inc/Main.h" +#include "inc/TtfTypes.h" +#include "inc/TtfUtil.h" + +namespace graphite2 { + + +class FileFace +{ + static const void * get_table_fn(const void* appFaceHandle, unsigned int name, size_t *len); + static void rel_table_fn(const void* appFaceHandle, const void *table_buffer); + +public: + static const gr_face_ops ops; + + FileFace(const char *filename); + ~FileFace(); + + operator bool () const throw(); + CLASS_NEW_DELETE; + +private: //defensive + FILE * _file; + size_t _file_len; + + TtfUtil::Sfnt::OffsetSubTable * _header_tbl; + TtfUtil::Sfnt::OffsetSubTable::Entry * _table_dir; + + FileFace(const FileFace&); + FileFace& operator=(const FileFace&); +}; + +inline +FileFace::operator bool() const throw() +{ + return _file && _header_tbl && _table_dir; +} + +} // namespace graphite2 + +#endif //!GRAPHITE2_NFILEFACE diff --git a/thirdparty/graphite/src/inc/Font.h b/thirdparty/graphite/src/inc/Font.h new file mode 100644 index 00000000000..9bc9ffb510c --- /dev/null +++ b/thirdparty/graphite/src/inc/Font.h @@ -0,0 +1,90 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once +#include +#include "graphite2/Font.h" +#include "inc/Main.h" +#include "inc/Face.h" + +namespace graphite2 { + +#define INVALID_ADVANCE -1e38f // can't be a static const because non-integral + +class Font +{ +public: + Font(float ppm, const Face & face, const void * appFontHandle=0, const gr_font_ops * ops=0); + virtual ~Font(); + + float advance(unsigned short glyphid) const; + float scale() const; + bool isHinted() const; + const Face & face() const; + operator bool () const throw() { return m_advances; } + + CLASS_NEW_DELETE; +private: + gr_font_ops m_ops; + const void * const m_appFontHandle; + float * m_advances; // One advance per glyph in pixels. Nan if not defined + const Face & m_face; + float m_scale; // scales from design units to ppm + bool m_hinted; + + Font(const Font&); + Font& operator=(const Font&); +}; + +inline +float Font::advance(unsigned short glyphid) const +{ + if (m_advances[glyphid] == INVALID_ADVANCE) + m_advances[glyphid] = (*m_ops.glyph_advance_x)(m_appFontHandle, glyphid); + return m_advances[glyphid]; +} + +inline +float Font::scale() const +{ + return m_scale; +} + +inline +bool Font::isHinted() const +{ + return m_hinted; +} + +inline +const Face & Font::face() const +{ + return m_face; +} + +} // namespace graphite2 + +struct gr_font : public graphite2::Font {}; diff --git a/thirdparty/graphite/src/inc/GlyphCache.h b/thirdparty/graphite/src/inc/GlyphCache.h new file mode 100644 index 00000000000..7d5324e5224 --- /dev/null +++ b/thirdparty/graphite/src/inc/GlyphCache.h @@ -0,0 +1,223 @@ +/* GRAPHITE2 LICENSING + + Copyright 2012, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + + +#include "graphite2/Font.h" +#include "inc/Main.h" +#include "inc/Position.h" +#include "inc/GlyphFace.h" + +namespace graphite2 { + +class Face; +class FeatureVal; +class Segment; + + +struct SlantBox +{ + static const SlantBox empty; + +// SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {}; + float width() const { return sa - si; } + float height() const { return da - di; } + float si; // min + float di; // min + float sa; // max + float da; // max +}; + + +struct BBox +{ + BBox(float pxi = 0, float pyi = 0., float pxa = 0., float pya = 0.) : xi(pxi), yi(pyi), xa(pxa), ya(pya) {}; + float width() const { return xa - xi; } + float height() const { return ya - yi; } + float xi; // min + float yi; // min + float xa; // max + float ya; // max +}; + + +class GlyphBox +{ + GlyphBox(const GlyphBox &); + GlyphBox & operator = (const GlyphBox &); + +public: + GlyphBox(uint8 numsubs, unsigned short bitmap, Rect *slanted) : _num(numsubs), _bitmap(bitmap), _slant(*slanted) {}; + + void addSubBox(int subindex, int boundary, Rect *val) { _subs[subindex * 2 + boundary] = *val; } + Rect &subVal(int subindex, int boundary) { return _subs[subindex * 2 + boundary]; } + const Rect &slant() const { return _slant; } + uint8 num() const { return _num; } + const Rect *subs() const { return _subs; } + +private: + uint8 _num; + unsigned short _bitmap; + Rect _slant; + Rect _subs[1]; +}; + +class GlyphCache +{ + class Loader; + + GlyphCache(const GlyphCache&); + GlyphCache& operator=(const GlyphCache&); + +public: + GlyphCache(const Face & face, const uint32 face_options); + ~GlyphCache(); + + unsigned short numGlyphs() const throw(); + unsigned short numAttrs() const throw(); + unsigned short unitsPerEm() const throw(); + + const GlyphFace *glyph(unsigned short glyphid) const; //result may be changed by subsequent call with a different glyphid + const GlyphFace *glyphSafe(unsigned short glyphid) const; + float getBoundingMetric(unsigned short glyphid, uint8 metric) const; + uint8 numSubBounds(unsigned short glyphid) const; + float getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const; + const Rect & slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : _empty_slant_box; } + const SlantBox & getBoundingSlantBox(unsigned short glyphid) const; + const BBox & getBoundingBBox(unsigned short glyphid) const; + const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const; + const BBox & getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const; + bool check(unsigned short glyphid) const; + bool hasBoxes() const { return _boxes != 0; } + + CLASS_NEW_DELETE; + +private: + const Rect _empty_slant_box; + const Loader * _glyph_loader; + const GlyphFace * * _glyphs; + GlyphBox * * _boxes; + unsigned short _num_glyphs, + _num_attrs, + _upem; +}; + +inline +unsigned short GlyphCache::numGlyphs() const throw() +{ + return _num_glyphs; +} + +inline +unsigned short GlyphCache::numAttrs() const throw() +{ + return _num_attrs; +} + +inline +unsigned short GlyphCache::unitsPerEm() const throw() +{ + return _upem; +} + +inline +bool GlyphCache::check(unsigned short glyphid) const +{ + return _boxes && glyphid < _num_glyphs; +} + +inline +const GlyphFace *GlyphCache::glyphSafe(unsigned short glyphid) const +{ + return glyphid < _num_glyphs ? glyph(glyphid) : NULL; +} + +inline +float GlyphCache::getBoundingMetric(unsigned short glyphid, uint8 metric) const +{ + if (glyphid >= _num_glyphs) return 0.; + switch (metric) { + case 0: return (float)(glyph(glyphid)->theBBox().bl.x); // x_min + case 1: return (float)(glyph(glyphid)->theBBox().bl.y); // y_min + case 2: return (float)(glyph(glyphid)->theBBox().tr.x); // x_max + case 3: return (float)(glyph(glyphid)->theBBox().tr.y); // y_max + case 4: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.x : 0.f); // sum_min + case 5: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.y : 0.f); // diff_min + case 6: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.x : 0.f); // sum_max + case 7: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.y : 0.f); // diff_max + default: return 0.; + } +} + +inline const SlantBox &GlyphCache::getBoundingSlantBox(unsigned short glyphid) const +{ + return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : SlantBox::empty; +} + +inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const +{ + return *(BBox *)(&(glyph(glyphid)->theBBox())); +} + +inline +float GlyphCache::getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const +{ + GlyphBox *b = _boxes[glyphid]; + if (b == NULL || subindex >= b->num()) return 0; + + switch (metric) { + case 0: return b->subVal(subindex, 0).bl.x; + case 1: return b->subVal(subindex, 0).bl.y; + case 2: return b->subVal(subindex, 0).tr.x; + case 3: return b->subVal(subindex, 0).tr.y; + case 4: return b->subVal(subindex, 1).bl.x; + case 5: return b->subVal(subindex, 1).bl.y; + case 6: return b->subVal(subindex, 1).tr.x; + case 7: return b->subVal(subindex, 1).tr.y; + default: return 0.; + } +} + +inline const SlantBox &GlyphCache::getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const +{ + GlyphBox *b = _boxes[glyphid]; + return *(SlantBox *)(b->subs() + 2 * subindex + 1); +} + +inline const BBox &GlyphCache::getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const +{ + GlyphBox *b = _boxes[glyphid]; + return *(BBox *)(b->subs() + 2 * subindex); +} + +inline +uint8 GlyphCache::numSubBounds(unsigned short glyphid) const +{ + return _boxes[glyphid] ? _boxes[glyphid]->num() : 0; +} + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/GlyphFace.h b/thirdparty/graphite/src/inc/GlyphFace.h new file mode 100644 index 00000000000..fc29056146c --- /dev/null +++ b/thirdparty/graphite/src/inc/GlyphFace.h @@ -0,0 +1,83 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include "inc/Main.h" +#include "inc/Position.h" +#include "inc/Sparse.h" + +namespace graphite2 { + +enum metrics { + kgmetLsb = 0, kgmetRsb, + kgmetBbTop, kgmetBbBottom, kgmetBbLeft, kgmetBbRight, + kgmetBbHeight, kgmetBbWidth, + kgmetAdvWidth, kgmetAdvHeight, + kgmetAscent, kgmetDescent +}; + + +class GlyphFace +{ +public: + GlyphFace(); + template + GlyphFace(const Rect & bbox, const Position & adv, I first, const I last); + + const Position & theAdvance() const; + const Rect & theBBox() const { return m_bbox; } + const sparse & attrs() const { return m_attrs; } + int32 getMetric(uint8 metric) const; + + CLASS_NEW_DELETE; +private: + Rect m_bbox; // bounding box metrics in design units + Position m_advance; // Advance width and height in design units + sparse m_attrs; +}; + + +// Inlines: class GlyphFace +// +inline +GlyphFace::GlyphFace() +{} + +template +GlyphFace::GlyphFace(const Rect & bbox, const Position & adv, I first, const I last) +: m_bbox(bbox), + m_advance(adv), + m_attrs(first, last) +{ +} + +inline +const Position & GlyphFace::theAdvance() const { + return m_advance; +} + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Intervals.h b/thirdparty/graphite/src/inc/Intervals.h new file mode 100644 index 00000000000..81d23187b60 --- /dev/null +++ b/thirdparty/graphite/src/inc/Intervals.h @@ -0,0 +1,234 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include + +#include "inc/Main.h" +#include "inc/List.h" +#include "inc/json.h" +#include "inc/Position.h" + +// An IntervalSet represents the possible movement of a given glyph in a given direction +// (horizontally, vertically, or diagonally). +// A vector is needed to represent disjoint ranges, eg, -300..-150, 20..200, 500..750. +// Each pair represents the min/max of a sub-range. + +namespace graphite2 { + +class Segment; + +enum zones_t {SD, XY}; + +class Zones +{ + struct Exclusion + { + template + static Exclusion weighted(float xmin, float xmax, float f, float a0, + float m, float xi, float ai, float c, bool nega); + + float x, // x position + xm, // xmax position + c, // constant + sum(MiXi^2) + sm, // sum(Mi) + smx; // sum(MiXi) + bool open; + + Exclusion(float x, float w, float smi, float smxi, float c); + Exclusion & operator += (Exclusion const & rhs); + uint8 outcode(float p) const; + + Exclusion split_at(float p); + void left_trim(float p); + + bool track_cost(float & cost, float & x, float origin) const; + + private: + float test_position(float origin) const; + float cost(float x) const; + }; + + typedef Vector exclusions; + + typedef exclusions::iterator iterator; + typedef Exclusion * pointer; + typedef Exclusion & reference; + typedef std::reverse_iterator reverse_iterator; + +public: + typedef exclusions::const_iterator const_iterator; + typedef Exclusion const * const_pointer; + typedef Exclusion const & const_reference; + typedef std::reverse_iterator const_reverse_iterator; + +#if !defined GRAPHITE2_NTRACING + struct Debug + { + Exclusion _excl; + bool _isdel; + Vector _env; + + Debug(Exclusion *e, bool isdel, json *dbg) : _excl(*e), _isdel(isdel), _env(dbg->getenvs()) { }; + }; + + typedef Vector debugs; + typedef debugs::const_iterator idebugs; + void addDebug(Exclusion *e); + void removeDebug(float pos, float posm); + void setdebug(json *dbgout) { _dbg = dbgout; } + idebugs dbgs_begin() const { return _dbgs.begin(); } + idebugs dbgs_end() const { return _dbgs.end(); } + void jsonDbgOut(Segment *seg) const; + Position position() const { return Position(_pos, _posm); } +#endif + + Zones(); + template + void initialise(float xmin, float xmax, float margin_len, float margin_weight, float ao); + + void exclude(float xmin, float xmax); + void exclude_with_margins(float xmin, float xmax, int axis); + + template + void weighted(float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega); + void weightedAxis(int axis, float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega); + + float closest( float origin, float &cost) const; + + const_iterator begin() const { return _exclusions.begin(); } + const_iterator end() const { return _exclusions.end(); } + +private: + exclusions _exclusions; +#if !defined GRAPHITE2_NTRACING + json * _dbg; + debugs _dbgs; +#endif + float _margin_len, + _margin_weight, + _pos, + _posm; + + void insert(Exclusion e); + void remove(float x, float xm); + const_iterator find_exclusion_under(float x) const; +}; + + +inline +Zones::Zones() +: _margin_len(0), _margin_weight(0), _pos(0), _posm(0) +{ +#if !defined GRAPHITE2_NTRACING + _dbg = 0; +#endif + _exclusions.reserve(8); +} + +inline +Zones::Exclusion::Exclusion(float x_, float xm_, float smi, float smxi, float c_) +: x(x_), xm(xm_), c(c_), sm(smi), smx(smxi), open(false) +{ } + +template +inline +void Zones::initialise(float xmin, float xmax, float margin_len, + float margin_weight, float a0) +{ + _margin_len = margin_len; + _margin_weight = margin_weight; + _pos = xmin; + _posm = xmax; + _exclusions.clear(); + _exclusions.push_back(Exclusion::weighted(xmin, xmax, 1, a0, 0, 0, 0, 0, false)); + _exclusions.front().open = true; +#if !defined GRAPHITE2_NTRACING + _dbgs.clear(); +#endif +} + +inline +void Zones::exclude(float xmin, float xmax) { + remove(xmin, xmax); +} + +template +inline +void Zones::weighted(float xmin, float xmax, float f, float a0, + float m, float xi, float ai, float c, bool nega) { + insert(Exclusion::weighted(xmin, xmax, f, a0, m, xi, ai, c, nega)); +} + +inline +void Zones::weightedAxis(int axis, float xmin, float xmax, float f, float a0, + float m, float xi, float ai, float c, bool nega) { + if (axis < 2) + weighted(xmin, xmax, f, a0, m, xi, ai, c, nega); + else + weighted(xmin, xmax, f, a0, m, xi, ai, c, nega); +} + +#if !defined GRAPHITE2_NTRACING +inline +void Zones::addDebug(Exclusion *e) { + if (_dbg) + _dbgs.push_back(Debug(e, false, _dbg)); +} + +inline +void Zones::removeDebug(float pos, float posm) { + if (_dbg) + { + Exclusion e(pos, posm, 0, 0, 0); + _dbgs.push_back(Debug(&e, true, _dbg)); + } +} +#endif + +template<> +inline +Zones::Exclusion Zones::Exclusion::weighted(float xmin, float xmax, float f, float a0, + float m, float xi, GR_MAYBE_UNUSED float ai, float c, GR_MAYBE_UNUSED bool nega) { + return Exclusion(xmin, xmax, + m + f, + m * xi, + m * xi * xi + f * a0 * a0 + c); +} + +template<> +inline +Zones::Exclusion Zones::Exclusion::weighted(float xmin, float xmax, float f, float a0, + float m, float xi, float ai,float c, bool nega) { + float xia = nega ? xi - ai : xi + ai; + return Exclusion(xmin, xmax, + 0.25f * (m + 2.f * f), + 0.25f * m * xia, + 0.25f * (m * xia * xia + 2.f * f * a0 * a0) + c); +} + +} // end of namespace graphite2 diff --git a/thirdparty/graphite/src/inc/List.h b/thirdparty/graphite/src/inc/List.h new file mode 100644 index 00000000000..a3b7a779617 --- /dev/null +++ b/thirdparty/graphite/src/inc/List.h @@ -0,0 +1,168 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ + +// designed to have a limited subset of the std::vector api +#pragma once + +#include +#include +#include +#include +#include + +#include "Main.h" + +namespace graphite2 { + +template +inline +ptrdiff_t distance(T* first, T* last) { return last-first; } + + +template +class Vector +{ + T * m_first, *m_last, *m_end; +public: + typedef T & reference; + typedef const T & const_reference; + typedef T * iterator; + typedef const T * const_iterator; + + Vector() : m_first(0), m_last(0), m_end(0) {} + Vector(size_t n, const T& value = T()) : m_first(0), m_last(0), m_end(0) { insert(begin(), n, value); } + Vector(const Vector &rhs) : m_first(0), m_last(0), m_end(0) { insert(begin(), rhs.begin(), rhs.end()); } + template + Vector(I first, const I last) : m_first(0), m_last(0), m_end(0) { insert(begin(), first, last); } + ~Vector() { clear(); free(m_first); } + + iterator begin() { return m_first; } + const_iterator begin() const { return m_first; } + + iterator end() { return m_last; } + const_iterator end() const { return m_last; } + + bool empty() const { return m_first == m_last; } + size_t size() const { return m_last - m_first; } + size_t capacity() const{ return m_end - m_first; } + + void reserve(size_t n); + void resize(size_t n, const T & v = T()); + + reference front() { assert(size() > 0); return *begin(); } + const_reference front() const { assert(size() > 0); return *begin(); } + reference back() { assert(size() > 0); return *(end()-1); } + const_reference back() const { assert(size() > 0); return *(end()-1); } + + Vector & operator = (const Vector & rhs) { assign(rhs.begin(), rhs.end()); return *this; } + reference operator [] (size_t n) { assert(size() > n); return m_first[n]; } + const_reference operator [] (size_t n) const { assert(size() > n); return m_first[n]; } + + void assign(size_t n, const T& u) { clear(); insert(begin(), n, u); } + void assign(const_iterator first, const_iterator last) { clear(); insert(begin(), first, last); } + iterator insert(iterator p, const T & x) { p = _insert_default(p, 1); new (p) T(x); return p; } + void insert(iterator p, size_t n, const T & x); + void insert(iterator p, const_iterator first, const_iterator last); + void pop_back() { assert(size() > 0); --m_last; } + void push_back(const T &v) { if (m_last == m_end) reserve(size()+1); new (m_last++) T(v); } + + void clear() { erase(begin(), end()); } + iterator erase(iterator p) { return erase(p, p+1); } + iterator erase(iterator first, iterator last); + +private: + iterator _insert_default(iterator p, size_t n); +}; + +template +inline +void Vector::reserve(size_t n) +{ + if (n > capacity()) + { + const ptrdiff_t sz = size(); + size_t requested; + if (checked_mul(n,sizeof(T), requested)) std::abort(); + m_first = static_cast(realloc(m_first, requested)); + if (!m_first) std::abort(); + m_last = m_first + sz; + m_end = m_first + n; + } +} + +template +inline +void Vector::resize(size_t n, const T & v) { + const ptrdiff_t d = n-size(); + if (d < 0) erase(end()+d, end()); + else if (d > 0) insert(end(), d, v); +} + +template +inline +typename Vector::iterator Vector::_insert_default(iterator p, size_t n) +{ + assert(begin() <= p && p <= end()); + const ptrdiff_t i = p - begin(); + reserve(((size() + n + 7) >> 3) << 3); + p = begin() + i; + // Move tail if there is one + if (p != end()) memmove(p + n, p, distance(p,end())*sizeof(T)); + m_last += n; + return p; +} + +template +inline +void Vector::insert(iterator p, size_t n, const T & x) +{ + p = _insert_default(p, n); + // Copy in elements + for (; n; --n, ++p) { new (p) T(x); } +} + +template +inline +void Vector::insert(iterator p, const_iterator first, const_iterator last) +{ + p = _insert_default(p, distance(first, last)); + // Copy in elements + for (;first != last; ++first, ++p) { new (p) T(*first); } +} + +template +inline +typename Vector::iterator Vector::erase(iterator first, iterator last) +{ + for (iterator e = first; e != last; ++e) e->~T(); + const size_t sz = distance(first, last); + if (m_last != last) memmove(first, last, distance(last,end())*sizeof(T)); + m_last -= sz; + return first; +} + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Machine.h b/thirdparty/graphite/src/inc/Machine.h new file mode 100644 index 00000000000..b23819fb981 --- /dev/null +++ b/thirdparty/graphite/src/inc/Machine.h @@ -0,0 +1,207 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// This general interpreter interface. +// Author: Tim Eves + +// Build one of direct_machine.cpp or call_machine.cpp to implement this +// interface. + +#pragma once +#include +#include +#include +#include "inc/Main.h" + +#if defined(__GNUC__) +#if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ * 10) < 430 +#define HOT +#if defined(__x86_64) +#define REGPARM(n) __attribute__((regparm(n))) +#else +#define REGPARM(n) +#endif +#else +#define HOT __attribute__((hot)) +#if defined(__x86_64) +#define REGPARM(n) __attribute__((hot, regparm(n))) +#else +#define REGPARM(n) +#endif +#endif +#else +#define HOT +#define REGPARM(n) +#endif + +#if defined(__MINGW32__) +// MinGW's at some point includes winnt.h which #define's a +// DELETE macro, which conflicts with enum opcode below, so we undefine +// it here. +#undef DELETE +#endif + +namespace graphite2 { + +// Forward declarations +class Segment; +class Slot; +class SlotMap; + + +namespace vm +{ + + +typedef void * instr; +typedef Slot * slotref; + +enum {VARARGS = 0xff, MAX_NAME_LEN=32}; + +enum opcode { + NOP = 0, + + PUSH_BYTE, PUSH_BYTEU, PUSH_SHORT, PUSH_SHORTU, PUSH_LONG, + + ADD, SUB, MUL, DIV, + MIN_, MAX_, + NEG, + TRUNC8, TRUNC16, + + COND, + + AND, OR, NOT, + EQUAL, NOT_EQ, + LESS, GTR, LESS_EQ, GTR_EQ, + + NEXT, NEXT_N, COPY_NEXT, + PUT_GLYPH_8BIT_OBS, PUT_SUBS_8BIT_OBS, PUT_COPY, + INSERT, DELETE, + ASSOC, + CNTXT_ITEM, + + ATTR_SET, ATTR_ADD, ATTR_SUB, + ATTR_SET_SLOT, + IATTR_SET_SLOT, + PUSH_SLOT_ATTR, PUSH_GLYPH_ATTR_OBS, + PUSH_GLYPH_METRIC, PUSH_FEAT, + PUSH_ATT_TO_GATTR_OBS, PUSH_ATT_TO_GLYPH_METRIC, + PUSH_ISLOT_ATTR, + + PUSH_IGLYPH_ATTR, // not implemented + + POP_RET, RET_ZERO, RET_TRUE, + IATTR_SET, IATTR_ADD, IATTR_SUB, + PUSH_PROC_STATE, PUSH_VERSION, + PUT_SUBS, PUT_SUBS2, PUT_SUBS3, + PUT_GLYPH, PUSH_GLYPH_ATTR, PUSH_ATT_TO_GLYPH_ATTR, + BITOR, BITAND, BITNOT, + BITSET, SET_FEAT, + MAX_OPCODE, + // private opcodes for internal use only, comes after all other on disk opcodes + TEMP_COPY = MAX_OPCODE +}; + +struct opcode_t +{ + instr impl[2]; + uint8 param_sz; + char name[MAX_NAME_LEN]; +}; + + +class Machine +{ +public: + typedef int32 stack_t; + static size_t const STACK_ORDER = 10, + STACK_MAX = 1 << STACK_ORDER, + STACK_GUARD = 2; + + class Code; + + enum status_t { + finished = 0, + stack_underflow, + stack_not_empty, + stack_overflow, + slot_offset_out_bounds, + died_early + }; + + Machine(SlotMap &) throw(); + static const opcode_t * getOpcodeTable() throw(); + + CLASS_NEW_DELETE; + + SlotMap & slotMap() const throw(); + status_t status() const throw(); +// operator bool () const throw(); + +private: + void check_final_stack(const stack_t * const sp); + stack_t run(const instr * program, const byte * data, + slotref * & map) HOT; + + SlotMap & _map; + stack_t _stack[STACK_MAX + 2*STACK_GUARD]; + status_t _status; +}; + +inline Machine::Machine(SlotMap & map) throw() +: _map(map), _status(finished) +{ + // Initialise stack guard +1 entries as the stack pointer points to the + // current top of stack, hence the first push will never write entry 0. + // Initialising the guard space like this is unnecessary and is only + // done to keep valgrind happy during fuzz testing. Hopefully loop + // unrolling will flatten this. + for (size_t n = STACK_GUARD + 1; n; --n) _stack[n-1] = 0; +} + +inline SlotMap& Machine::slotMap() const throw() +{ + return _map; +} + +inline Machine::status_t Machine::status() const throw() +{ + return _status; +} + +inline void Machine::check_final_stack(const stack_t * const sp) +{ + if (_status != finished) return; + + stack_t const * const base = _stack + STACK_GUARD, + * const limit = base + STACK_MAX; + if (sp < base) _status = stack_underflow; // This should be impossible now. + else if (sp >= limit) _status = stack_overflow; // So should this. + else if (sp != base) _status = stack_not_empty; +} + +} // namespace vm +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Main.h b/thirdparty/graphite/src/inc/Main.h new file mode 100644 index 00000000000..ebf02dd5533 --- /dev/null +++ b/thirdparty/graphite/src/inc/Main.h @@ -0,0 +1,199 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include +#include "graphite2/Types.h" + +#ifdef GRAPHITE2_CUSTOM_HEADER +#include GRAPHITE2_CUSTOM_HEADER +#endif + +namespace graphite2 { + +typedef gr_uint8 uint8; +typedef gr_uint8 byte; +typedef gr_uint16 uint16; +typedef gr_uint32 uint32; +typedef gr_int8 int8; +typedef gr_int16 int16; +typedef gr_int32 int32; +typedef size_t uintptr; + +#ifdef GRAPHITE2_TELEMETRY +struct telemetry +{ + class category; + + static size_t * _category; + static void set_category(size_t & t) throw() { _category = &t; } + static void stop() throw() { _category = 0; } + static void count_bytes(size_t n) throw() { if (_category) *_category += n; } + + size_t misc, + silf, + glyph, + code, + states, + starts, + transitions; + + telemetry() : misc(0), silf(0), glyph(0), code(0), states(0), starts(0), transitions(0) {} +}; + +class telemetry::category +{ + size_t * _prev; +public: + category(size_t & t) : _prev(_category) { _category = &t; } + ~category() { _category = _prev; } +}; + +#else +struct telemetry {}; +#endif + +// Checked multiplaction to catch overflow or underflow when allocating memory +#if defined(__has_builtin) + #if __has_builtin(__builtin_mul_overflow) + #define HAVE_BUILTIN_OVERFLOW + #endif +#elif defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__INTEL_COMPILER) + #define HAVE_BUILTIN_OVERFLOW +#endif +#if defined(__has_include) + #if __has_include() && !defined(__CYGWIN__) + #define HAVE_INTSAFE_H + #endif +#elif defined(_WIN32) + #define HAVE_INTSAFE_H +#endif + +// Need to import intsafe into the top level namespace +#if defined(HAVE_INTSAFE_H) +} // namespace graphite2 + +#include + +namespace graphite2 { +#endif + +#if defined(HAVE_BUILTIN_OVERFLOW) +inline +bool checked_mul(const size_t a, const size_t b, size_t & t) { + return __builtin_mul_overflow(a, b, &t); +} +#elif defined(HAVE_INTSAFE_H) +inline +bool checked_mul(const size_t a, const size_t b, size_t & t) { + return SizeTMult(a, b, &t) == INTSAFE_E_ARITHMETIC_OVERFLOW; +} +#else +inline +bool checked_mul(const size_t a, const size_t b, size_t & t) { + t = a*b; + return (((a | b) & (~size_t(0) << (sizeof(size_t) << 2))) && (t / a != b)); +} +#endif + +// typesafe wrapper around malloc for simple types +// use free(pointer) to deallocate + +template T * gralloc(size_t n) +{ + size_t total; + if (checked_mul(n, sizeof(T), total)) + return 0; +#ifdef GRAPHITE2_TELEMETRY + telemetry::count_bytes(total); +#endif + return static_cast(malloc(total)); +} + +template T * grzeroalloc(size_t n) +{ +#ifdef GRAPHITE2_TELEMETRY + telemetry::count_bytes(sizeof(T) * n); +#endif + return static_cast(calloc(n, sizeof(T))); +} + +template +inline T min(const T a, const T b) +{ + return a < b ? a : b; +} + +template +inline T max(const T a, const T b) +{ + return a > b ? a : b; +} + +} // namespace graphite2 + +#define CLASS_NEW_DELETE \ + void * operator new (size_t size){ return gralloc(size);} \ + void * operator new (size_t, void * p) throw() { return p; } \ + void * operator new[] (size_t size) {return gralloc(size);} \ + void * operator new[] (size_t, void * p) throw() { return p; } \ + void operator delete (void * p) throw() { free(p);} \ + void operator delete (void *, void *) throw() {} \ + void operator delete[] (void * p)throw() { free(p); } \ + void operator delete[] (void *, void *) throw() {} + +#if defined(__GNUC__) || defined(__clang__) +#define GR_MAYBE_UNUSED __attribute__((unused)) +#else +#define GR_MAYBE_UNUSED +#endif + +#ifndef __has_cpp_attribute +# define __has_cpp_attribute(x) 0 +#endif + +#if __has_cpp_attribute(clang::fallthrough) +# define GR_FALLTHROUGH [[clang::fallthrough]] +#elif __has_cpp_attribute(gnu::fallthrough) +# define GR_FALLTHROUGH [[gnu::fallthrough]] +#elif defined(_MSC_VER) + /* + * MSVC's __fallthrough annotations are checked by /analyze (Code Analysis): + * https://msdn.microsoft.com/en-us/library/ms235402%28VS.80%29.aspx + */ + #include + #define GR_FALLTHROUGH __fallthrough +#elif __GNUC__ >= 7 + #define GR_FALLTHROUGH __attribute__ ((fallthrough)) +#else + #define GR_FALLTHROUGH /* fallthrough */ +#endif + +#ifdef _MSC_VER +#pragma warning(disable: 4800) +#pragma warning(disable: 4355) +#endif diff --git a/thirdparty/graphite/src/inc/NameTable.h b/thirdparty/graphite/src/inc/NameTable.h new file mode 100644 index 00000000000..0fdbeb4d85f --- /dev/null +++ b/thirdparty/graphite/src/inc/NameTable.h @@ -0,0 +1,65 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include +#include "inc/TtfTypes.h" +#include "inc/locale2lcid.h" + +namespace graphite2 { + +class NameTable +{ + NameTable(const NameTable &); + NameTable & operator = (const NameTable &); + +public: + NameTable(const void * data, size_t length, uint16 platfromId=3, uint16 encodingID = 1); + ~NameTable() { free(const_cast(m_table)); } + enum eNameFallback { + eNoFallback = 0, + eEnUSFallbackOnly = 1, + eEnOrAnyFallback = 2 + }; + uint16 setPlatformEncoding(uint16 platfromId=3, uint16 encodingID = 1); + void * getName(uint16 & languageId, uint16 nameId, gr_encform enc, uint32 & length); + uint16 getLanguageId(const char * bcp47Locale); + + CLASS_NEW_DELETE +private: + uint16 m_platformId; + uint16 m_encodingId; + uint16 m_languageCount; + uint16 m_platformOffset; // offset of first NameRecord with for platform 3, encoding 1 + uint16 m_platformLastRecord; + uint16 m_nameDataLength; + const TtfUtil::Sfnt::FontNames * m_table; + const uint8 * m_nameData; + Locale2Lang m_locale2Lang; +}; + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Pass.h b/thirdparty/graphite/src/inc/Pass.h new file mode 100644 index 00000000000..e687a87d8c2 --- /dev/null +++ b/thirdparty/graphite/src/inc/Pass.h @@ -0,0 +1,118 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include +#include "inc/Code.h" + +namespace graphite2 { + +class Segment; +class Face; +class Silf; +struct Rule; +struct RuleEntry; +struct State; +class FiniteStateMachine; +class Error; +class ShiftCollider; +class KernCollider; +class json; + +enum passtype; + +class Pass +{ +public: + Pass(); + ~Pass(); + + bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face, + enum passtype pt, uint32 version, Error &e); + bool runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const; + void init(Silf *silf) { m_silf = silf; } + byte collisionLoops() const { return m_numCollRuns; } + bool reverseDir() const { return m_isReverseDir; } + + CLASS_NEW_DELETE +private: + void findNDoRule(Slot* & iSlot, vm::Machine &, FiniteStateMachine& fsm) const; + int doAction(const vm::Machine::Code* codeptr, Slot * & slot_out, vm::Machine &) const; + bool testPassConstraint(vm::Machine & m) const; + bool testConstraint(const Rule & r, vm::Machine &) const; + bool readRules(const byte * rule_map, const size_t num_entries, + const byte *precontext, const uint16 * sort_key, + const uint16 * o_constraint, const byte *constraint_data, + const uint16 * o_action, const byte * action_data, + Face &, enum passtype pt, Error &e); + bool readStates(const byte * starts, const byte * states, const byte * o_rule_map, Face &, Error &e); + bool readRanges(const byte * ranges, size_t num_ranges, Error &e); + uint16 glyphToCol(const uint16 gid) const; + bool runFSM(FiniteStateMachine & fsm, Slot * slot) const; + void dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const; + void dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * os) const; + void adjustSlot(int delta, Slot * & slot_out, SlotMap &) const; + bool collisionShift(Segment *seg, int dir, json * const dbgout) const; + bool collisionKern(Segment *seg, int dir, json * const dbgout) const; + bool collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const; + bool resolveCollisions(Segment *seg, Slot *slot, Slot *start, ShiftCollider &coll, bool isRev, + int dir, bool &moved, bool &hasCol, json * const dbgout) const; + float resolveKern(Segment *seg, Slot *slot, Slot *start, int dir, + float &ymin, float &ymax, json *const dbgout) const; + + const Silf * m_silf; + uint16 * m_cols; + Rule * m_rules; // rules + RuleEntry * m_ruleMap; + uint16 * m_startStates; // prectxt length + uint16 * m_transitions; + State * m_states; + vm::Machine::Code * m_codes; + byte * m_progs; + + byte m_numCollRuns; + byte m_kernColls; + byte m_iMaxLoop; + uint16 m_numGlyphs; + uint16 m_numRules; + uint16 m_numStates; + uint16 m_numTransition; + uint16 m_numSuccess; + uint16 m_successStart; + uint16 m_numColumns; + byte m_minPreCtxt; + byte m_maxPreCtxt; + byte m_colThreshold; + bool m_isReverseDir; + vm::Machine::Code m_cPConstraint; + +private: //defensive + Pass(const Pass&); + Pass& operator=(const Pass&); +}; + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Position.h b/thirdparty/graphite/src/inc/Position.h new file mode 100644 index 00000000000..510e4f4c41d --- /dev/null +++ b/thirdparty/graphite/src/inc/Position.h @@ -0,0 +1,68 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +namespace graphite2 { + +class Position +{ +public: + Position() : x(0), y(0) { } + Position(const float inx, const float iny) : x(inx), y(iny) {} + Position operator + (const Position& a) const { return Position(x + a.x, y + a.y); } + Position operator - (const Position& a) const { return Position(x - a.x, y - a.y); } + Position operator * (const float m) const { return Position(x * m, y * m); } + Position &operator += (const Position &a) { x += a.x; y += a.y; return *this; } + Position &operator *= (const float m) { x *= m; y *= m; return *this; } + + float x; + float y; +}; + +class Rect +{ +public : + Rect() {} + Rect(const Position& botLeft, const Position& topRight): bl(botLeft), tr(topRight) {} + Rect widen(const Rect& other) { return Rect(Position(bl.x > other.bl.x ? other.bl.x : bl.x, bl.y > other.bl.y ? other.bl.y : bl.y), Position(tr.x > other.tr.x ? tr.x : other.tr.x, tr.y > other.tr.y ? tr.y : other.tr.y)); } + Rect operator + (const Position &a) const { return Rect(Position(bl.x + a.x, bl.y + a.y), Position(tr.x + a.x, tr.y + a.y)); } + Rect operator - (const Position &a) const { return Rect(Position(bl.x - a.x, bl.y - a.y), Position(tr.x - a.x, tr.y - a.y)); } + Rect operator * (float m) const { return Rect(Position(bl.x, bl.y) * m, Position(tr.x, tr.y) * m); } + float width() const { return tr.x - bl.x; } + float height() const { return tr.y - bl.y; } + + bool hitTest(Rect &other); + + // returns Position(overlapx, overlapy) where overlap<0 if overlapping else positive) + Position overlap(Position &offset, Rect &other, Position &otherOffset); + //Position constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox); + + Position bl; + Position tr; +}; + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Rule.h b/thirdparty/graphite/src/inc/Rule.h new file mode 100644 index 00000000000..5964e003a6a --- /dev/null +++ b/thirdparty/graphite/src/inc/Rule.h @@ -0,0 +1,305 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ + +#pragma once + +#include "inc/Code.h" +#include "inc/Slot.h" + +namespace graphite2 { + +struct Rule { + const vm::Machine::Code * constraint, + * action; + unsigned short sort; + byte preContext; +#ifndef NDEBUG + uint16 rule_idx; +#endif + + Rule(); + ~Rule() {} + + CLASS_NEW_DELETE; + +private: + Rule(const Rule &); + Rule & operator = (const Rule &); +}; + +inline +Rule::Rule() +: constraint(0), + action(0), + sort(0), + preContext(0) +{ +#ifndef NDEBUG + rule_idx = 0; +#endif +} + + +struct RuleEntry +{ + const Rule * rule; + + inline + bool operator < (const RuleEntry &r) const + { + const unsigned short lsort = rule->sort, rsort = r.rule->sort; + return lsort > rsort || (lsort == rsort && rule < r.rule); + } + + inline + bool operator == (const RuleEntry &r) const + { + return rule == r.rule; + } +}; + + +struct State +{ + const RuleEntry * rules, + * rules_end; + + bool empty() const; +}; + +inline +bool State::empty() const +{ + return rules_end == rules; +} + + +class SlotMap +{ +public: + enum {MAX_SLOTS=64}; + SlotMap(Segment & seg, uint8 direction, size_t maxSize); + + Slot * * begin(); + Slot * * end(); + size_t size() const; + unsigned short context() const; + void reset(Slot &, unsigned short); + + Slot * const & operator[](int n) const; + Slot * & operator [] (int); + void pushSlot(Slot * const slot); + void collectGarbage(Slot *& aSlot); + + Slot * highwater() { return m_highwater; } + void highwater(Slot *s) { m_highwater = s; m_highpassed = false; } + bool highpassed() const { return m_highpassed; } + void highpassed(bool v) { m_highpassed = v; } + + uint8 dir() const { return m_dir; } + int decMax() { return --m_maxSize; } + + Segment & segment; +private: + Slot * m_slot_map[MAX_SLOTS+1]; + unsigned short m_size; + unsigned short m_precontext; + Slot * m_highwater; + int m_maxSize; + uint8 m_dir; + bool m_highpassed; +}; + + +class FiniteStateMachine +{ +public: + enum {MAX_RULES=128}; + +private: + class Rules + { + public: + Rules(); + void clear(); + const RuleEntry * begin() const; + const RuleEntry * end() const; + size_t size() const; + + void accumulate_rules(const State &state); + + private: + RuleEntry * m_begin, + * m_end, + m_rules[MAX_RULES*2]; + }; + +public: + FiniteStateMachine(SlotMap & map, json * logger); + void reset(Slot * & slot, const short unsigned int max_pre_ctxt); + + Rules rules; + SlotMap & slots; + json * const dbgout; +}; + + +inline +FiniteStateMachine::FiniteStateMachine(SlotMap& map, json * logger) +: slots(map), + dbgout(logger) +{ +} + +inline +void FiniteStateMachine::reset(Slot * & slot, const short unsigned int max_pre_ctxt) +{ + rules.clear(); + int ctxt = 0; + for (; ctxt != max_pre_ctxt && slot->prev(); ++ctxt, slot = slot->prev()); + slots.reset(*slot, ctxt); +} + +inline +FiniteStateMachine::Rules::Rules() + : m_begin(m_rules), m_end(m_rules) +{ +} + +inline +void FiniteStateMachine::Rules::clear() +{ + m_end = m_begin; +} + +inline +const RuleEntry * FiniteStateMachine::Rules::begin() const +{ + return m_begin; +} + +inline +const RuleEntry * FiniteStateMachine::Rules::end() const +{ + return m_end; +} + +inline +size_t FiniteStateMachine::Rules::size() const +{ + return m_end - m_begin; +} + +inline +void FiniteStateMachine::Rules::accumulate_rules(const State &state) +{ + // Only bother if there are rules in the State object. + if (state.empty()) return; + + // Merge the new sorted rules list into the current sorted result set. + const RuleEntry * lre = begin(), * rre = state.rules; + RuleEntry * out = m_rules + (m_begin == m_rules)*MAX_RULES; + const RuleEntry * const lrend = out + MAX_RULES, + * const rrend = state.rules_end; + m_begin = out; + while (lre != end() && out != lrend) + { + if (*lre < *rre) *out++ = *lre++; + else if (*rre < *lre) { *out++ = *rre++; } + else { *out++ = *lre++; ++rre; } + + if (rre == rrend) + { + while (lre != end() && out != lrend) { *out++ = *lre++; } + m_end = out; + return; + } + } + while (rre != rrend && out != lrend) { *out++ = *rre++; } + m_end = out; +} + +inline +SlotMap::SlotMap(Segment & seg, uint8 direction, size_t maxSize) +: segment(seg), m_size(0), m_precontext(0), m_highwater(0), + m_maxSize(int(maxSize)), m_dir(direction), m_highpassed(false) +{ + m_slot_map[0] = 0; +} + +inline +Slot * * SlotMap::begin() +{ + return &m_slot_map[1]; // allow map to go 1 before slot_map when inserting + // at start of segment. +} + +inline +Slot * * SlotMap::end() +{ + return m_slot_map + m_size + 1; +} + +inline +size_t SlotMap::size() const +{ + return m_size; +} + +inline +short unsigned int SlotMap::context() const +{ + return m_precontext; +} + +inline +void SlotMap::reset(Slot & slot, short unsigned int ctxt) +{ + m_size = 0; + m_precontext = ctxt; + *m_slot_map = slot.prev(); +} + +inline +void SlotMap::pushSlot(Slot*const slot) +{ + m_slot_map[++m_size] = slot; +} + +inline +Slot * const & SlotMap::operator[](int n) const +{ + return m_slot_map[n + 1]; +} + +inline +Slot * & SlotMap::operator[](int n) +{ + return m_slot_map[n + 1]; +} + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Segment.h b/thirdparty/graphite/src/inc/Segment.h new file mode 100644 index 00000000000..6cf83408d4d --- /dev/null +++ b/thirdparty/graphite/src/inc/Segment.h @@ -0,0 +1,236 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include "inc/Main.h" + +#include + +#include "inc/CharInfo.h" +#include "inc/Face.h" +#include "inc/FeatureVal.h" +#include "inc/GlyphCache.h" +#include "inc/GlyphFace.h" +#include "inc/Slot.h" +#include "inc/Position.h" +#include "inc/List.h" +#include "inc/Collider.h" + +#define MAX_SEG_GROWTH_FACTOR 64 + +namespace graphite2 { + +typedef Vector FeatureList; +typedef Vector SlotRope; +typedef Vector AttributeRope; +typedef Vector JustifyRope; + +class Font; +class Segment; +class Silf; + +enum SpliceParam { +/** sub-Segments longer than this are not cached + * (in Unicode code points) */ + eMaxSpliceSize = 96 +}; + +enum justFlags { + gr_justStartInline = 1, + gr_justEndInline = 2 +}; + +class SegmentScopeState +{ +private: + friend class Segment; + Slot * realFirstSlot; + Slot * slotBeforeScope; + Slot * slotAfterScope; + Slot * realLastSlot; + size_t numGlyphsOutsideScope; +}; + +class Segment +{ + // Prevent copying of any kind. + Segment(const Segment&); + Segment& operator=(const Segment&); + +public: + + enum { + SEG_INITCOLLISIONS = 1, + SEG_HASCOLLISIONS = 2 + }; + + size_t slotCount() const { return m_numGlyphs; } //one slot per glyph + void extendLength(ptrdiff_t num) { m_numGlyphs += num; } + Position advance() const { return m_advance; } + bool runGraphite() { if (m_silf) return m_face->runGraphite(this, m_silf); else return true;}; + void chooseSilf(uint32 script) { m_silf = m_face->chooseSilf(script); } + const Silf *silf() const { return m_silf; } + size_t charInfoCount() const { return m_numCharinfo; } + const CharInfo *charinfo(unsigned int index) const { return index < m_numCharinfo ? m_charinfo + index : NULL; } + CharInfo *charinfo(unsigned int index) { return index < m_numCharinfo ? m_charinfo + index : NULL; } + + Segment(size_t numchars, const Face* face, uint32 script, int dir); + ~Segment(); + uint8 flags() const { return m_flags; } + void flags(uint8 f) { m_flags = f; } + Slot *first() { return m_first; } + void first(Slot *p) { m_first = p; } + Slot *last() { return m_last; } + void last(Slot *p) { m_last = p; } + void appendSlot(int i, int cid, int gid, int fid, size_t coffset); + Slot *newSlot(); + void freeSlot(Slot *); + SlotJustify *newJustify(); + void freeJustify(SlotJustify *aJustify); + Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, bool isRtl = false, bool isFinal = true); + void associateChars(int offset, size_t num); + void linkClusters(Slot *first, Slot *last); + uint16 getClassGlyph(uint16 cid, uint16 offset) const { return m_silf->getClassGlyph(cid, offset); } + uint16 findClassIndex(uint16 cid, uint16 gid) const { return m_silf->findClassIndex(cid, gid); } + int addFeatures(const Features& feats) { m_feats.push_back(feats); return int(m_feats.size()) - 1; } + uint32 getFeature(int index, uint8 findex) const { const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); if (!pFR) return 0; else return pFR->getFeatureVal(m_feats[index]); } + void setFeature(int index, uint8 findex, uint32 val) { + const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); + if (pFR) + { + if (val > pFR->maxVal()) val = pFR->maxVal(); + pFR->applyValToFeature(val, m_feats[index]); + } } + int8 dir() const { return m_dir; } + void dir(int8 val) { m_dir = val; } + bool currdir() const { return ((m_dir >> 6) ^ m_dir) & 1; } + uint8 passBits() const { return m_passBits; } + void mergePassBits(const uint8 val) { m_passBits &= val; } + int16 glyphAttr(uint16 gid, uint16 gattr) const { const GlyphFace * p = m_face->glyphs().glyphSafe(gid); return p ? p->attrs()[gattr] : 0; } + int32 getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel, bool rtl) const; + float glyphAdvance(uint16 gid) const { return m_face->glyphs().glyph(gid)->theAdvance().x; } + const Rect &theGlyphBBoxTemporary(uint16 gid) const { return m_face->glyphs().glyph(gid)->theBBox(); } //warning value may become invalid when another glyph is accessed + Slot *findRoot(Slot *is) const { return is->attachedTo() ? findRoot(is->attachedTo()) : is; } + int numAttrs() const { return m_silf->numUser(); } + int defaultOriginal() const { return m_defaultOriginal; } + const Face * getFace() const { return m_face; } + const Features & getFeatures(unsigned int /*charIndex*/) { assert(m_feats.size() == 1); return m_feats[0]; } + void bidiPass(int paradir, uint8 aMirror); + int8 getSlotBidiClass(Slot *s) const; + void doMirror(uint16 aMirror); + Slot *addLineEnd(Slot *nSlot); + void delLineEnd(Slot *s); + bool hasJustification() const { return m_justifies.size() != 0; } + void reverseSlots(); + + bool isWhitespace(const int cid) const; + bool hasCollisionInfo() const { return (m_flags & SEG_HASCOLLISIONS) && m_collisions; } + SlotCollision *collisionInfo(const Slot *s) const { return m_collisions ? m_collisions + s->index() : 0; } + CLASS_NEW_DELETE + +public: //only used by: GrSegment* makeAndInitialize(const GrFont *font, const GrFace *face, uint32 script, const FeaturesHandle& pFeats/*must not be IsNull*/, encform enc, const void* pStart, size_t nChars, int dir); + bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars); + void finalise(const Font *font, bool reverse=false); + float justify(Slot *pSlot, const Font *font, float width, enum justFlags flags, Slot *pFirst, Slot *pLast); + bool initCollisions(); + +private: + Position m_advance; // whole segment advance + SlotRope m_slots; // Vector of slot buffers + AttributeRope m_userAttrs; // Vector of userAttrs buffers + JustifyRope m_justifies; // Slot justification info buffers + FeatureList m_feats; // feature settings referenced by charinfos in this segment + Slot * m_freeSlots; // linked list of free slots + SlotJustify * m_freeJustifies; // Slot justification blocks free list + CharInfo * m_charinfo; // character info, one per input character + SlotCollision * m_collisions; + const Face * m_face; // GrFace + const Silf * m_silf; + Slot * m_first; // first slot in segment + Slot * m_last; // last slot in segment + size_t m_bufSize, // how big a buffer to create when need more slots + m_numGlyphs, + m_numCharinfo; // size of the array and number of input characters + int m_defaultOriginal; // number of whitespace chars in the string + int8 m_dir; + uint8 m_flags, // General purpose flags + m_passBits; // if bit set then skip pass +}; + +inline +int8 Segment::getSlotBidiClass(Slot *s) const +{ + int8 res = s->getBidiClass(); + if (res != -1) return res; + res = int8(glyphAttr(s->gid(), m_silf->aBidi())); + s->setBidiClass(res); + return res; +} + +inline +void Segment::finalise(const Font *font, bool reverse) +{ + if (!m_first || !m_last) return; + + m_advance = positionSlots(font, m_first, m_last, m_silf->dir(), true); + //associateChars(0, m_numCharinfo); + if (reverse && currdir() != (m_dir & 1)) + reverseSlots(); + linkClusters(m_first, m_last); +} + +inline +int32 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel, bool rtl) const { + if (attrLevel > 0) + { + Slot *is = findRoot(iSlot); + return is->clusterMetric(this, metric, attrLevel, rtl); + } + else + return m_face->getGlyphMetric(iSlot->gid(), metric); +} + +inline +bool Segment::isWhitespace(const int cid) const +{ + return ((cid >= 0x0009) * (cid <= 0x000D) + + (cid == 0x0020) + + (cid == 0x0085) + + (cid == 0x00A0) + + (cid == 0x1680) + + (cid == 0x180E) + + (cid >= 0x2000) * (cid <= 0x200A) + + (cid == 0x2028) + + (cid == 0x2029) + + (cid == 0x202F) + + (cid == 0x205F) + + (cid == 0x3000)) != 0; +} + +} // namespace graphite2 + +struct gr_segment : public graphite2::Segment {}; diff --git a/thirdparty/graphite/src/inc/Silf.h b/thirdparty/graphite/src/inc/Silf.h new file mode 100644 index 00000000000..edc0c3a16d8 --- /dev/null +++ b/thirdparty/graphite/src/inc/Silf.h @@ -0,0 +1,128 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include "graphite2/Font.h" +#include "inc/Main.h" +#include "inc/Pass.h" + +namespace graphite2 { + +class Face; +class Segment; +class FeatureVal; +class VMScratch; +class Error; + +class Pseudo +{ +public: + uint32 uid; + uint32 gid; + CLASS_NEW_DELETE; +}; + +class Justinfo +{ +public: + Justinfo(uint8 stretch, uint8 shrink, uint8 step, uint8 weight) : + m_astretch(stretch), m_ashrink(shrink), m_astep(step), + m_aweight(weight) {}; + uint8 attrStretch() const { return m_astretch; } + uint8 attrShrink() const { return m_ashrink; } + uint8 attrStep() const { return m_astep; } + uint8 attrWeight() const { return m_aweight; } + +private: + uint8 m_astretch; + uint8 m_ashrink; + uint8 m_astep; + uint8 m_aweight; +}; + +class Silf +{ + // Prevent copying + Silf(const Silf&); + Silf& operator=(const Silf&); + +public: + Silf() throw(); + ~Silf() throw(); + + bool readGraphite(const byte * const pSilf, size_t lSilf, Face &face, uint32 version); + bool runGraphite(Segment *seg, uint8 firstPass=0, uint8 lastPass=0, int dobidi = 0) const; + uint16 findClassIndex(uint16 cid, uint16 gid) const; + uint16 getClassGlyph(uint16 cid, unsigned int index) const; + uint16 findPseudo(uint32 uid) const; + uint8 numUser() const { return m_aUser; } + uint8 aPseudo() const { return m_aPseudo; } + uint8 aBreak() const { return m_aBreak; } + uint8 aMirror() const {return m_aMirror; } + uint8 aPassBits() const { return m_aPassBits; } + uint8 aBidi() const { return m_aBidi; } + uint8 aCollision() const { return m_aCollision; } + uint8 substitutionPass() const { return m_sPass; } + uint8 positionPass() const { return m_pPass; } + uint8 justificationPass() const { return m_jPass; } + uint8 bidiPass() const { return m_bPass; } + uint8 numPasses() const { return m_numPasses; } + uint8 maxCompPerLig() const { return m_iMaxComp; } + uint16 numClasses() const { return m_nClass; } + byte flags() const { return m_flags; } + byte dir() const { return m_dir; } + uint8 numJustLevels() const { return m_numJusts; } + Justinfo *justAttrs() const { return m_justs; } + uint16 endLineGlyphid() const { return m_gEndLine; } + const gr_faceinfo *silfInfo() const { return &m_silfinfo; } + + CLASS_NEW_DELETE; + +private: + size_t readClassMap(const byte *p, size_t data_len, uint32 version, Error &e); + template inline uint32 readClassOffsets(const byte *&p, size_t data_len, Error &e); + + Pass * m_passes; + Pseudo * m_pseudos; + uint32 * m_classOffsets; + uint16 * m_classData; + Justinfo * m_justs; + uint8 m_numPasses; + uint8 m_numJusts; + uint8 m_sPass, m_pPass, m_jPass, m_bPass, + m_flags, m_dir; + + uint8 m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits, + m_iMaxComp, m_aCollision; + uint16 m_aLig, m_numPseudo, m_nClass, m_nLinear, + m_gEndLine; + gr_faceinfo m_silfinfo; + + void releaseBuffers() throw(); +}; + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/Slot.h b/thirdparty/graphite/src/inc/Slot.h new file mode 100644 index 00000000000..df39d9a3bbe --- /dev/null +++ b/thirdparty/graphite/src/inc/Slot.h @@ -0,0 +1,170 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include "graphite2/Types.h" +#include "graphite2/Segment.h" +#include "inc/Main.h" +#include "inc/Font.h" +#include "inc/Position.h" + +namespace graphite2 { + +typedef gr_attrCode attrCode; + +class GlyphFace; +class Segment; + +struct SlotJustify +{ + static const int NUMJUSTPARAMS = 5; + + SlotJustify(const SlotJustify &); + SlotJustify & operator = (const SlotJustify &); + +public: + static size_t size_of(size_t levels) { return sizeof(SlotJustify) + ((levels > 1 ? levels : 1)*NUMJUSTPARAMS - 1)*sizeof(int16); } + + void LoadSlot(const Slot *s, const Segment *seg); + + SlotJustify *next; + int16 values[1]; +}; + +class Slot +{ + enum Flag + { + DELETED = 1, + INSERTED = 2, + COPIED = 4, + POSITIONED = 8, + ATTACHED = 16 + }; + +public: + struct iterator; + + unsigned short gid() const { return m_glyphid; } + Position origin() const { return m_position; } + float advance() const { return m_advance.x; } + void advance(Position &val) { m_advance = val; } + Position advancePos() const { return m_advance; } + int before() const { return m_before; } + int after() const { return m_after; } + uint32 index() const { return m_index; } + void index(uint32 val) { m_index = val; } + + Slot(int16 *m_userAttr = NULL); + void set(const Slot & slot, int charOffset, size_t numUserAttr, size_t justLevels, size_t numChars); + Slot *next() const { return m_next; } + void next(Slot *s) { m_next = s; } + Slot *prev() const { return m_prev; } + void prev(Slot *s) { m_prev = s; } + uint16 glyph() const { return m_realglyphid ? m_realglyphid : m_glyphid; } + void setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph = NULL); + void setRealGid(uint16 realGid) { m_realglyphid = realGid; } + void adjKern(const Position &pos) { m_shift = m_shift + pos; m_advance = m_advance + pos; } + void origin(const Position &pos) { m_position = pos + m_shift; } + void originate(int ind) { m_original = ind; } + int original() const { return m_original; } + void before(int ind) { m_before = ind; } + void after(int ind) { m_after = ind; } + bool isBase() const { return (!m_parent); } + void update(int numSlots, int numCharInfo, Position &relpos); + Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth = 0); + bool isDeleted() const { return (m_flags & DELETED) ? true : false; } + void markDeleted(bool state) { if (state) m_flags |= DELETED; else m_flags &= ~DELETED; } + bool isCopied() const { return (m_flags & COPIED) ? true : false; } + void markCopied(bool state) { if (state) m_flags |= COPIED; else m_flags &= ~COPIED; } + bool isPositioned() const { return (m_flags & POSITIONED) ? true : false; } + void markPositioned(bool state) { if (state) m_flags |= POSITIONED; else m_flags &= ~POSITIONED; } + bool isInsertBefore() const { return !(m_flags & INSERTED); } + uint8 getBidiLevel() const { return m_bidiLevel; } + void setBidiLevel(uint8 level) { m_bidiLevel = level; } + int8 getBidiClass(const Segment *seg); + int8 getBidiClass() const { return m_bidiCls; } + void setBidiClass(int8 cls) { m_bidiCls = cls; } + int16 *userAttrs() const { return m_userAttr; } + void userAttrs(int16 *p) { m_userAttr = p; } + void markInsertBefore(bool state) { if (!state) m_flags |= INSERTED; else m_flags &= ~INSERTED; } + void setAttr(Segment* seg, attrCode ind, uint8 subindex, int16 val, const SlotMap & map); + int getAttr(const Segment *seg, attrCode ind, uint8 subindex) const; + int getJustify(const Segment *seg, uint8 level, uint8 subindex) const; + void setJustify(Segment *seg, uint8 level, uint8 subindex, int16 value); + bool isLocalJustify() const { return m_justs != NULL; }; + void attachTo(Slot *ap) { m_parent = ap; } + Slot *attachedTo() const { return m_parent; } + Position attachOffset() const { return m_attach - m_with; } + Slot* firstChild() const { return m_child; } + void firstChild(Slot *ap) { m_child = ap; } + bool child(Slot *ap); + Slot* nextSibling() const { return m_sibling; } + void nextSibling(Slot *ap) { m_sibling = ap; } + bool sibling(Slot *ap); + bool removeChild(Slot *ap); + int32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel, bool rtl); + void positionShift(Position a) { m_position += a; } + void floodShift(Position adj, int depth = 0); + float just() const { return m_just; } + void just(float j) { m_just = j; } + Slot *nextInCluster(const Slot *s) const; + bool isChildOf(const Slot *base) const; + + CLASS_NEW_DELETE + +private: + Slot *m_next; // linked list of slots + Slot *m_prev; + unsigned short m_glyphid; // glyph id + uint16 m_realglyphid; + uint32 m_original; // charinfo that originated this slot (e.g. for feature values) + uint32 m_before; // charinfo index of before association + uint32 m_after; // charinfo index of after association + uint32 m_index; // slot index given to this slot during finalising + Slot *m_parent; // index to parent we are attached to + Slot *m_child; // index to first child slot that attaches to us + Slot *m_sibling; // index to next child that attaches to our parent + Position m_position; // absolute position of glyph + Position m_shift; // .shift slot attribute + Position m_advance; // .advance slot attribute + Position m_attach; // attachment point on us + Position m_with; // attachment point position on parent + float m_just; // Justification inserted space + uint8 m_flags; // holds bit flags + byte m_attLevel; // attachment level + int8 m_bidiCls; // bidirectional class + byte m_bidiLevel; // bidirectional level + int16 *m_userAttr; // pointer to user attributes + SlotJustify *m_justs; // pointer to justification parameters + + friend class Segment; +}; + +} // namespace graphite2 + +struct gr_slot : public graphite2::Slot {}; diff --git a/thirdparty/graphite/src/inc/Sparse.h b/thirdparty/graphite/src/inc/Sparse.h new file mode 100644 index 00000000000..fcda890171b --- /dev/null +++ b/thirdparty/graphite/src/inc/Sparse.h @@ -0,0 +1,168 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once +#include +#include + +#include "inc/Main.h" + +namespace graphite2 { + + +// A read-only packed fast sparse array of uint16 with uint16 keys. +// Like most container classes this has capacity and size properties and these +// refer to the number of stored entries and the number of addressable entries +// as normal. However due the sparse nature the capacity is always <= than the +// size. +class sparse +{ +public: + typedef uint16 key_type; + typedef uint16 mapped_type; + typedef std::pair value_type; + +private: + typedef unsigned long mask_t; + + static const unsigned char SIZEOF_CHUNK = (sizeof(mask_t) - sizeof(key_type))*8; + + struct chunk + { + mask_t mask:SIZEOF_CHUNK; + key_type offset; + }; + + static const chunk empty_chunk; + sparse(const sparse &); + sparse & operator = (const sparse &); + +public: + template + sparse(I first, const I last); + sparse() throw(); + ~sparse() throw(); + + operator bool () const throw(); + mapped_type operator [] (const key_type k) const throw(); + + size_t capacity() const throw(); + size_t size() const throw(); + + size_t _sizeof() const throw(); + + CLASS_NEW_DELETE; + +private: + union { + chunk * map; + mapped_type * values; + } m_array; + key_type m_nchunks; +}; + + +inline +sparse::sparse() throw() : m_nchunks(0) +{ + m_array.map = const_cast(&empty_chunk); +} + + +template +sparse::sparse(I attr, const I last) +: m_nchunks(0) +{ + m_array.map = 0; + + // Find the maximum extent of the key space. + size_t n_values=0; + long lastkey = -1; + for (I i = attr; i != last; ++i, ++n_values) + { + const typename std::iterator_traits::value_type v = *i; + if (v.second == 0) { --n_values; continue; } + if (v.first <= lastkey) { m_nchunks = 0; return; } + + lastkey = v.first; + const key_type k = v.first / SIZEOF_CHUNK; + if (k >= m_nchunks) m_nchunks = k+1; + } + if (m_nchunks == 0) + { + m_array.map=const_cast(&empty_chunk); + return; + } + + m_array.values = grzeroalloc((m_nchunks*sizeof(chunk) + sizeof(mapped_type)-1) + / sizeof(mapped_type) + + n_values); + + if (m_array.values == 0) + return; + + // coverity[forward_null : FALSE] Since m_array is union and m_array.values is not NULL + chunk * ci = m_array.map; + ci->offset = (m_nchunks*sizeof(chunk) + sizeof(mapped_type)-1)/sizeof(mapped_type); + mapped_type * vi = m_array.values + ci->offset; + for (; attr != last; ++attr, ++vi) + { + const typename std::iterator_traits::value_type v = *attr; + if (v.second == 0) { --vi; continue; } + + chunk * const ci_ = m_array.map + v.first/SIZEOF_CHUNK; + + if (ci != ci_) + { + ci = ci_; + ci->offset = key_type(vi - m_array.values); + } + + ci->mask |= 1UL << (SIZEOF_CHUNK - 1 - (v.first % SIZEOF_CHUNK)); + *vi = v.second; + } +} + + +inline +sparse::operator bool () const throw() +{ + return m_array.map != 0; +} + +inline +size_t sparse::size() const throw() +{ + return m_nchunks*SIZEOF_CHUNK; +} + +inline +size_t sparse::_sizeof() const throw() +{ + return sizeof(sparse) + capacity()*sizeof(mapped_type) + m_nchunks*sizeof(chunk); +} + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/TtfTypes.h b/thirdparty/graphite/src/inc/TtfTypes.h new file mode 100644 index 00000000000..ae67915304e --- /dev/null +++ b/thirdparty/graphite/src/inc/TtfTypes.h @@ -0,0 +1,419 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once +/*--------------------------------------------------------------------*//*:Ignore this sentence. + +File: TtfTypes.h +Responsibility: Tim Eves +Last reviewed: Not yet. + +Description: +Provides types required to represent the TTF basic types. +-------------------------------------------------------------------------------*//*:End Ignore*/ + + +//********************************************************************************************** +// Include files +//********************************************************************************************** +namespace graphite2 +{ +namespace TtfUtil +{ +//********************************************************************************************** +// Forward declarations +//********************************************************************************************** + + +//********************************************************************************************** +// Type declarations +//********************************************************************************************** +typedef unsigned char uint8; +typedef uint8 byte; +typedef signed char int8; +typedef unsigned short uint16; +typedef short int16; +typedef unsigned int uint32; +typedef int int32; + +typedef int16 short_frac; +typedef int32 fixed; +typedef int16 fword; +typedef uint16 ufword; +typedef int16 f2dot14; +typedef uint32 long_date_time[2]; + +//********************************************************************************************** +// Constants and enum types +//**********************************************************************************************/ +enum +{ + OneFix = 1<<16 +}; + +//********************************************************************************************** +// Table declarations +//********************************************************************************************** +namespace Sfnt +{ +#pragma pack(push,1) // We need this or the structure members aren't aligned + // correctly. Fortunately this form of pragma is supposed + // to be recognised by VS C++ too (at least according to + // MSDN). + + struct OffsetSubTable + { + uint32 scaler_type; + uint16 num_tables, + search_range, + entry_selector, + range_shift; + struct Entry + { + uint32 tag, + checksum, + offset, + length; + } table_directory[1]; + + enum ScalerType + { + TrueTypeMac = 0x74727565U, + TrueTypeWin = 0x00010000U, + Type1 = 0x74797031U + }; + }; + + + + + struct CharacterCodeMap + { + uint16 version, + num_subtables; + struct + { + uint16 platform_id, + platform_specific_id; + uint32 offset; + } encoding[1]; + }; + + struct CmapSubTable + { + uint16 format, + length, + language; + }; + + struct CmapSubTableFormat4 : CmapSubTable + { + uint16 seg_count_x2, + search_range, + entry_selector, + range_shift, + end_code[1]; + // There are arrarys after this which need their + // start positions calculated since end_code is + // seg_count uint16s long. + }; + + struct CmapSubTableFormat12 + { + fixed format; + uint32 length, + language, + num_groups; + struct + { + uint32 start_char_code, + end_char_code, + start_glyph_id; + } group[1]; + }; + + + + struct FontHeader + { + fixed version, + font_revision; + uint32 check_sum_adjustment, + magic_number; + uint16 flags, + units_per_em; + long_date_time created, + modified; + fword x_min, + y_min, + x_max, + y_max; + uint16 mac_style, + lowest_rec_ppem; + int16 font_direction_hint, + index_to_loc_format, + glyph_data_format; + enum + { + MagicNumber = 0x5F0F3CF5, + GlypDataFormat = 0 + }; + enum {ShortIndexLocFormat, LongIndexLocFormat}; + }; + + + + + struct PostScriptGlyphName + { + fixed format, + italic_angle; + fword underline_position, + underline_thickness; + uint32 is_fixed_pitch, + min_mem_type42, + max_mem_type42, + min_mem_type1, + max_mem_type1; + enum + { + Format1 = 0x10000, + Format2 = 0x20000, + Format25 = 0x28000, + Format3 = 0x30000, + Format4 = 0x40000 + }; + }; + + struct PostScriptGlyphName2 : PostScriptGlyphName + { + uint16 number_of_glyphs, + glyph_name_index[1]; + }; + + struct PostScriptGlyphName25 : PostScriptGlyphName + { + uint16 number_of_glyphs; + int8 offset[1]; + }; + + struct PostScriptGlyphName3 : PostScriptGlyphName {}; + + struct PostScriptGlyphName4 : PostScriptGlyphName + { + uint16 glyph_to_char_map[1]; + }; + + + struct HorizontalHeader + { + fixed version; + fword ascent, + descent, + line_gap; + ufword advance_width_max; + fword min_left_side_bearing, + max_left_side_bearing, + x_max_element; + int16 caret_slope_rise, + caret_slope_run; + fword caret_offset; + int16 reserved[4], + metric_data_format; + uint16 num_long_hor_metrics; + }; + + struct MaximumProfile + { + fixed version; + uint16 num_glyphs, + max_points, + max_contours, + max_component_points, + max_component_contours, + max_zones, + max_twilight_points, + max_storage, + max_function_defs, + max_instruction_defs, + max_stack_elements, + max_size_of_instructions, + max_component_elements, + max_component_depth; + }; + + + typedef byte Panose[10]; + + struct Compatibility0 + { + uint16 version; + int16 x_avg_char_width; + uint16 weight_class, + width_class; + int16 fs_type, + y_subscript_x_size, + y_subscript_y_size, + y_subscript_x_offset, + y_subscript_y_offset, + y_superscript_x_size, + y_superscript_y_size, + y_superscript_x_offset, + y_superscript_y_offset, + y_strikeout_size, + y_strikeout_position, + family_class; + Panose panose; + uint32 unicode_range[4]; + int8 ach_vend_id[4]; + uint16 fs_selection, + fs_first_char_index, + fs_last_char_index, // Acording to Apple's spec this is where v0 should end + typo_ascender, + typo_descender, + type_linegap, + win_ascent, + win_descent; + + enum + { + Italic =0x01, + Underscore=0x02, + Negative =0x04, + Outlined =0x08, + StrikeOut =0x10, + Bold =0x20 + }; + }; + + struct Compatibility1 : Compatibility0 + { + uint32 codepage_range[2]; + }; + + struct Compatibility2 : Compatibility1 + { + int16 x_height, + cap_height; + uint16 default_char, + break_char, + max_context; + }; + + struct Compatibility3 : Compatibility2 {}; + + typedef Compatibility3 Compatibility; + + + struct NameRecord + { + uint16 platform_id, + platform_specific_id, + language_id, + name_id, + length, + offset; + enum {Unicode, Mactintosh, Reserved, Microsoft}; + enum + { + Copyright, Family, Subfamily, UniqueSubfamily, + Fullname, Version, PostScript + }; + }; + + struct LangTagRecord + { + uint16 length, + offset; + }; + + struct FontNames + { + uint16 format, + count, + string_offset; + NameRecord name_record[1]; + }; + + + struct HorizontalMetric + { + uint16 advance_width; + int16 left_side_bearing; + }; + + + struct Glyph + { + int16 number_of_contours; + fword x_min, + y_min, + x_max, + y_max; + }; + + struct SimpleGlyph : Glyph + { + uint16 end_pts_of_contours[1]; + enum + { + OnCurve = 0x01, + XShort = 0x02, + YShort = 0x04, + Repeat = 0x08, + XIsSame = 0x10, + XIsPos = 0x10, + YIsSame = 0x20, + YIsPos = 0x20 + }; + }; + + struct CompoundGlyph : Glyph + { + uint16 flags, + glyph_index; + enum + { + Arg1Arg2Words = 0x01, + ArgsAreXYValues = 0x02, + RoundXYToGrid = 0x04, + HaveScale = 0x08, + MoreComponents = 0x20, + HaveXAndYScale = 0x40, + HaveTwoByTwo = 0x80, + HaveInstructions = 0x100, + UseMyMetrics = 0x200, + OverlapCompund = 0x400, + ScaledOffset = 0x800, + UnscaledOffset = 0x1000 + }; + }; + +#pragma pack(pop) +} // end of namespace Sfnt + +} // end of namespace TtfUtil +} // end of namespace graphite2 diff --git a/thirdparty/graphite/src/inc/TtfUtil.h b/thirdparty/graphite/src/inc/TtfUtil.h new file mode 100644 index 00000000000..3952bc06fb1 --- /dev/null +++ b/thirdparty/graphite/src/inc/TtfUtil.h @@ -0,0 +1,208 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once +/*--------------------------------------------------------------------*//*:Ignore this sentence. + +File: TtfUtil.h +Responsibility: Alan Ward +Last reviewed: Not yet. + +Description: + Utility class for handling TrueType font files. +----------------------------------------------------------------------------------------------*/ + + +#include + +namespace graphite2 +{ +namespace TtfUtil +{ + +#define OVERFLOW_OFFSET_CHECK(p, o) (o + reinterpret_cast(p) < reinterpret_cast(p)) + +typedef long fontTableId32; +typedef unsigned short gid16; + +#define TTF_TAG(a,b,c,d) ((a << 24UL) + (b << 16UL) + (c << 8UL) + (d)) + +// Enumeration used to specify a table in a TTF file +class Tag +{ + unsigned int _v; +public: + Tag(const char n[5]) throw() : _v(TTF_TAG(n[0],n[1],n[2],n[3])) {} + Tag(const unsigned int tag) throw() : _v(tag) {} + + operator unsigned int () const throw () { return _v; } + + enum + { + Feat = TTF_TAG('F','e','a','t'), + Glat = TTF_TAG('G','l','a','t'), + Gloc = TTF_TAG('G','l','o','c'), + Sile = TTF_TAG('S','i','l','e'), + Silf = TTF_TAG('S','i','l','f'), + Sill = TTF_TAG('S','i','l','l'), + cmap = TTF_TAG('c','m','a','p'), + cvt = TTF_TAG('c','v','t',' '), + cryp = TTF_TAG('c','r','y','p'), + head = TTF_TAG('h','e','a','d'), + fpgm = TTF_TAG('f','p','g','m'), + gdir = TTF_TAG('g','d','i','r'), + glyf = TTF_TAG('g','l','y','f'), + hdmx = TTF_TAG('h','d','m','x'), + hhea = TTF_TAG('h','h','e','a'), + hmtx = TTF_TAG('h','m','t','x'), + loca = TTF_TAG('l','o','c','a'), + kern = TTF_TAG('k','e','r','n'), + LTSH = TTF_TAG('L','T','S','H'), + maxp = TTF_TAG('m','a','x','p'), + name = TTF_TAG('n','a','m','e'), + OS_2 = TTF_TAG('O','S','/','2'), + post = TTF_TAG('p','o','s','t'), + prep = TTF_TAG('p','r','e','p') + }; +}; + +/*---------------------------------------------------------------------------------------------- + Class providing utility methods to parse a TrueType font file (TTF). + Callling application handles all file input and memory allocation. + Assumes minimal knowledge of TTF file format. +----------------------------------------------------------------------------------------------*/ + ////////////////////////////////// tools to find & check TTF tables + bool GetHeaderInfo(size_t & lOffset, size_t & lSize); + bool CheckHeader(const void * pHdr); + bool GetTableDirInfo(const void * pHdr, size_t & lOffset, size_t & lSize); + bool GetTableInfo(const Tag TableTag, const void * pHdr, const void * pTableDir, + size_t & lOffset, size_t & lSize); + bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize); + + ////////////////////////////////// simple font wide info + size_t GlyphCount(const void * pMaxp); +#ifdef ALL_TTFUTILS + size_t MaxCompositeComponentCount(const void * pMaxp); + size_t MaxCompositeLevelCount(const void * pMaxp); + size_t LocaGlyphCount(size_t lLocaSize, const void * pHead); // throw (std::domain_error); +#endif + int DesignUnits(const void * pHead); +#ifdef ALL_TTFUTILS + int HeadTableCheckSum(const void * pHead); + void HeadTableCreateTime(const void * pHead, unsigned int * pnDateBC, unsigned int * pnDateAD); + void HeadTableModifyTime(const void * pHead, unsigned int * pnDateBC, unsigned int * pnDateAD); + bool IsItalic(const void * pHead); + int FontAscent(const void * pOs2); + int FontDescent(const void * pOs2); + bool FontOs2Style(const void *pOs2, bool & fBold, bool & fItalic); + bool Get31EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize); + bool Get31EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize); + bool Get30EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize); + bool Get30EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize); + int PostLookup(const void * pPost, size_t lPostSize, const void * pMaxp, + const char * pPostName); +#endif + + ////////////////////////////////// utility methods helpful for name table + bool GetNameInfo(const void * pName, int nPlatformId, int nEncodingId, + int nLangId, int nNameId, size_t & lOffset, size_t & lSize); + //size_t NameTableLength(const byte * pTable); +#ifdef ALL_TTFUTILS + int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId, + int *nameIdList, int cNameIds, short *langIdList); + void SwapWString(void * pWStr, size_t nSize = 0); // throw (std::invalid_argument); +#endif + + ////////////////////////////////// cmap lookup tools + const void * FindCmapSubtable(const void * pCmap, int nPlatformId = 3, + int nEncodingId = 1, size_t length = 0); + bool CheckCmapSubtable4(const void * pCmap31, const void * pCmapEnd /*, unsigned int maxgid*/); + gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey = 0); + unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId, + int * pRangeKey = 0); + bool CheckCmapSubtable12(const void *pCmap310, const void * pCmapEnd /*, unsigned int maxgid*/); + gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey = 0); + unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId, + int * pRangeKey = 0); + + ///////////////////////////////// horizontal metric data for a glyph + bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, + const void * pHhea, int & nLsb, unsigned int & nAdvWid); + + ////////////////////////////////// primitives for loca and glyf lookup + size_t LocaLookup(gid16 nGlyphId, const void * pLoca, size_t lLocaSize, + const void * pHead); // throw (std::out_of_range); + void * GlyfLookup(const void * pGlyf, size_t lGlyfOffset, size_t lTableLen); + + ////////////////////////////////// primitves for simple glyph data + bool GlyfBox(const void * pSimpleGlyf, int & xMin, int & yMin, + int & xMax, int & yMax); + +#ifdef ALL_TTFUTILS + int GlyfContourCount(const void * pSimpleGlyf); + bool GlyfContourEndPoints(const void * pSimpleGlyf, int * prgnContourEndPoint, + int cnPointsTotal, size_t & cnPoints); + bool GlyfPoints(const void * pSimpleGlyf, int * prgnX, int * prgnY, + char * prgbFlag, int cnPointsTotal, int & cnPoints); + + // primitive to find the glyph ids in a composite glyph + bool GetComponentGlyphIds(const void * pSimpleGlyf, int * prgnCompId, + size_t cnCompIdTotal, size_t & cnCompId); + // primitive to find the placement data for a component in a composite glyph + bool GetComponentPlacement(const void * pSimpleGlyf, int nCompId, + bool fOffset, int & a, int & b); + // primitive to find the transform data for a component in a composite glyph + bool GetComponentTransform(const void * pSimpleGlyf, int nCompId, + float & flt11, float & flt12, float & flt21, float & flt22, bool & fTransOffset); +#endif + + ////////////////////////////////// operate on composite or simple glyph (auto glyf lookup) + void * GlyfLookup(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void * pHead); // primitive used by below methods + +#ifdef ALL_TTFUTILS + // below are primary user methods for handling glyf data + bool IsSpace(gid16 nGlyphId, const void * pLoca, size_t lLocaSize, const void * pHead); + bool IsDeepComposite(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void * pHead); + + bool GlyfBox(gid16 nGlyphId, const void * pGlyf, const void * pLoca, size_t lGlyfSize, size_t lLocaSize, + const void * pHead, int & xMin, int & yMin, int & xMax, int & yMax); + bool GlyfContourCount(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void *pHead, size_t & cnContours); + bool GlyfContourEndPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void * pHead, int * prgnContourEndPoint, size_t cnPoints); + bool GlyfPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lGlyfSize, size_t lLocaSize, const void * pHead, const int * prgnContourEndPoint, size_t cnEndPoints, + int * prgnX, int * prgnY, bool * prgfOnCurve, size_t cnPoints); + + // utitily method used by high-level GlyfPoints + bool SimplifyFlags(char * prgbFlags, int cnPoints); + bool CalcAbsolutePoints(int * prgnX, int * prgnY, int cnPoints); +#endif + +} // end of namespace TtfUtil +} // end of namespace graphite2 diff --git a/thirdparty/graphite/src/inc/UtfCodec.h b/thirdparty/graphite/src/inc/UtfCodec.h new file mode 100644 index 00000000000..24a343d8d99 --- /dev/null +++ b/thirdparty/graphite/src/inc/UtfCodec.h @@ -0,0 +1,251 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +#include +#include "inc/Main.h" + +namespace graphite2 { + +typedef uint32 uchar_t; + +template +struct _utf_codec +{ + typedef uchar_t codeunit_t; + + static void put(codeunit_t * cp, const uchar_t , int8 & len) throw(); + static uchar_t get(const codeunit_t * cp, int8 & len) throw(); + static bool validate(const codeunit_t * s, const codeunit_t * const e) throw(); +}; + + +template <> +struct _utf_codec<32> +{ +private: + static const uchar_t limit = 0x110000; +public: + typedef uint32 codeunit_t; + + inline + static void put(codeunit_t * cp, const uchar_t usv, int8 & l) throw() + { + *cp = usv; l = 1; + } + + inline + static uchar_t get(const codeunit_t * cp, int8 & l) throw() + { + if (cp[0] < limit) { l = 1; return cp[0]; } + else { l = -1; return 0xFFFD; } + } + + inline + static bool validate(const codeunit_t * s, const codeunit_t * const e) throw() + { + return s <= e; + } +}; + + +template <> +struct _utf_codec<16> +{ +private: + static const int32 lead_offset = 0xD800 - (0x10000 >> 10); + static const int32 surrogate_offset = 0x10000 - (0xD800 << 10) - 0xDC00; +public: + typedef uint16 codeunit_t; + + inline + static void put(codeunit_t * cp, const uchar_t usv, int8 & l) throw() + { + if (usv < 0x10000) { l = 1; cp[0] = codeunit_t(usv); } + else + { + cp[0] = codeunit_t(lead_offset + (usv >> 10)); + cp[1] = codeunit_t(0xDC00 + (usv & 0x3FF)); + l = 2; + } + } + + inline + static uchar_t get(const codeunit_t * cp, int8 & l) throw() + { + const uint32 uh = cp[0]; + l = 1; + + if (uh < 0xD800|| uh > 0xDFFF) { return uh; } + if (uh > 0xDBFF) { l = -1; return 0xFFFD; } + const uint32 ul = cp[1]; + if (ul < 0xDC00 || ul > 0xDFFF) { l = -1; return 0xFFFD; } + ++l; + return (uh<<10) + ul + surrogate_offset; + } + + inline + static bool validate(const codeunit_t * s, const codeunit_t * const e) throw() + { + const ptrdiff_t n = e-s; + if (n <= 0) return n == 0; + const uint32 u = *(e-1); // Get the last codepoint + return (u < 0xD800 || u > 0xDBFF); + } +}; + + +template <> +struct _utf_codec<8> +{ +private: + static const int8 sz_lut[16]; + static const byte mask_lut[5]; + static const uchar_t limit = 0x110000; + +public: + typedef uint8 codeunit_t; + + inline + static void put(codeunit_t * cp, const uchar_t usv, int8 & l) throw() + { + if (usv < 0x80) {l = 1; cp[0] = usv; return; } + if (usv < 0x0800) {l = 2; cp[0] = 0xC0 + (usv >> 6); cp[1] = 0x80 + (usv & 0x3F); return; } + if (usv < 0x10000) {l = 3; cp[0] = 0xE0 + (usv >> 12); cp[1] = 0x80 + ((usv >> 6) & 0x3F); cp[2] = 0x80 + (usv & 0x3F); return; } + else {l = 4; cp[0] = 0xF0 + (usv >> 18); cp[1] = 0x80 + ((usv >> 12) & 0x3F); cp[2] = 0x80 + ((usv >> 6) & 0x3F); cp[3] = 0x80 + (usv & 0x3F); return; } + } + + inline + static uchar_t get(const codeunit_t * cp, int8 & l) throw() + { + const int8 seq_sz = sz_lut[*cp >> 4]; + uchar_t u = *cp & mask_lut[seq_sz]; + l = 1; + bool toolong = false; + + switch(seq_sz) { + case 4: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong = (u < 0x10); GR_FALLTHROUGH; + // no break + case 3: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x20); GR_FALLTHROUGH; + // no break + case 2: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x80); GR_FALLTHROUGH; + // no break + case 1: break; + case 0: l = -1; return 0xFFFD; + } + + if (l != seq_sz || toolong || u >= limit) + { + l = -l; + return 0xFFFD; + } + return u; + } + + inline + static bool validate(const codeunit_t * s, const codeunit_t * const e) throw() + { + const ptrdiff_t n = e-s; + if (n <= 0) return n == 0; + s += (n-1); + if (*s < 0x80) return true; + if (*s >= 0xC0) return false; + if (n == 1) return true; + if (*--s < 0x80) return true; + if (*s >= 0xE0) return false; + if (n == 2 || *s >= 0xC0) return true; + if (*--s < 0x80) return true; + if (*s >= 0xF0) return false; + return true; + } + +}; + + +template +class _utf_iterator +{ + typedef _utf_codec codec; + + C * cp; + mutable int8 sl; + +public: + typedef C codeunit_type; + typedef uchar_t value_type; + typedef uchar_t * pointer; + + class reference + { + const _utf_iterator & _i; + + reference(const _utf_iterator & i): _i(i) {} + public: + operator value_type () const throw () { return codec::get(_i.cp, _i.sl); } + reference & operator = (const value_type usv) throw() { codec::put(_i.cp, usv, _i.sl); return *this; } + + friend class _utf_iterator; + }; + + + _utf_iterator(const void * us=0) : cp(reinterpret_cast(const_cast(us))), sl(1) { } + + _utf_iterator & operator ++ () { cp += abs(sl); return *this; } + _utf_iterator operator ++ (int) { _utf_iterator tmp(*this); operator++(); return tmp; } + + bool operator == (const _utf_iterator & rhs) const throw() { return cp >= rhs.cp; } + bool operator != (const _utf_iterator & rhs) const throw() { return !operator==(rhs); } + + reference operator * () const throw() { return *this; } + pointer operator ->() const throw() { return &operator *(); } + + operator codeunit_type * () const throw() { return cp; } + + bool error() const throw() { return sl < 1; } + bool validate(const _utf_iterator & e) { return codec::validate(cp, e.cp); } +}; + +template +struct utf +{ + typedef typename _utf_codec::codeunit_t codeunit_t; + + typedef _utf_iterator iterator; + typedef _utf_iterator const_iterator; + + inline + static bool validate(codeunit_t * s, codeunit_t * e) throw() { + return _utf_codec::validate(s,e); + } +}; + + +typedef utf utf32; +typedef utf utf16; +typedef utf utf8; + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/bits.h b/thirdparty/graphite/src/inc/bits.h new file mode 100644 index 00000000000..9365986a10c --- /dev/null +++ b/thirdparty/graphite/src/inc/bits.h @@ -0,0 +1,150 @@ +/* GRAPHITE2 LICENSING + + Copyright 2012, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once + +namespace graphite2 +{ + + +#if defined GRAPHITE2_BUILTINS && (defined __GNUC__ || defined __clang__) + +template +inline unsigned int bit_set_count(T v) +{ + return __builtin_popcount(v); +} + +template<> +inline unsigned int bit_set_count(int16 v) +{ + return __builtin_popcount(static_cast(v)); +} + +template<> +inline unsigned int bit_set_count(int8 v) +{ + return __builtin_popcount(static_cast(v)); +} + +template<> +inline unsigned int bit_set_count(unsigned long v) +{ + return __builtin_popcountl(v); +} + +template<> +inline unsigned int bit_set_count(signed long v) +{ + return __builtin_popcountl(v); +} + +template<> +inline unsigned int bit_set_count(unsigned long long v) +{ + return __builtin_popcountll(v); +} + +template<> +inline unsigned int bit_set_count(signed long long v) +{ + return __builtin_popcountll(v); +} + +#else + +template +inline unsigned int bit_set_count(T v) +{ + static size_t const ONES = ~0; + + v = v - ((v >> 1) & T(ONES/3)); // temp + v = (v & T(ONES/15*3)) + ((v >> 2) & T(ONES/15*3)); // temp + v = (v + (v >> 4)) & T(ONES/255*15); // temp + return (T)(v * T(ONES/255)) >> (sizeof(T)-1)*8; // count +} + +#endif + +//TODO: Changed these to uintmax_t when we go to C++11 +template +inline size_t _mask_over_val(size_t v) +{ + v = _mask_over_val(v); + v |= v >> S*4; + return v; +} + +//TODO: Changed these to uintmax_t when we go to C++11 +template<> +inline size_t _mask_over_val<1>(size_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + return v; +} + +template +inline T mask_over_val(T v) +{ + return T(_mask_over_val(v)); +} + +template +inline unsigned long next_highest_power2(T v) +{ + return _mask_over_val(v-1)+1; +} + +template +inline unsigned int log_binary(T v) +{ + return bit_set_count(mask_over_val(v))-1; +} + +template +inline T has_zero(const T x) +{ + return (x - T(~T(0)/255)) & ~x & T(~T(0)/255*128); +} + +template +inline T zero_bytes(const T x, unsigned char n) +{ + const T t = T(~T(0)/255*n); + return T((has_zero(x^t) >> 7)*n); +} + +#if 0 +inline float float_round(float x, uint32 m) +{ + *reinterpret_cast(&x) &= m; + return *reinterpret_cast(&x); +} +#endif + +} diff --git a/thirdparty/graphite/src/inc/debug.h b/thirdparty/graphite/src/inc/debug.h new file mode 100644 index 00000000000..97175eb2cc4 --- /dev/null +++ b/thirdparty/graphite/src/inc/debug.h @@ -0,0 +1,89 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// debug.h +// +// Created on: 22 Dec 2011 +// Author: tim + +#pragma once + +#if !defined GRAPHITE2_NTRACING + +#include +#include "inc/json.h" +#include "inc/Position.h" + +namespace graphite2 +{ + +class CharInfo; +class Segment; +class Slot; + +typedef std::pair dslot; +struct objectid +{ + char name[16]; + objectid(const dslot &) throw(); + objectid(const Segment * const p) throw(); +}; + + +json & operator << (json & j, const Position &) throw(); +json & operator << (json & j, const Rect &) throw(); +json & operator << (json & j, const CharInfo &) throw(); +json & operator << (json & j, const dslot &) throw(); +json & operator << (json & j, const objectid &) throw(); +json & operator << (json & j, const telemetry &) throw(); + + + +inline +json & operator << (json & j, const Position & p) throw() +{ + return j << json::flat << json::array << p.x << p.y << json::close; +} + + +inline +json & operator << (json & j, const Rect & p) throw() +{ + return j << json::flat << json::array << p.bl.x << p.bl.y << p.tr.x << p.tr.y << json::close; +} + + +inline +json & operator << (json & j, const objectid & sid) throw() +{ + return j << sid.name; +} + + +} // namespace graphite2 + +#endif //!defined GRAPHITE2_NTRACING + diff --git a/thirdparty/graphite/src/inc/json.h b/thirdparty/graphite/src/inc/json.h new file mode 100644 index 00000000000..554cd9a3d19 --- /dev/null +++ b/thirdparty/graphite/src/inc/json.h @@ -0,0 +1,178 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// JSON pretty printer for graphite font debug output logging. +// Created on: 15 Dec 2011 +// Author: Tim Eves + +#pragma once + +#include "inc/Main.h" +#include +#include +#include +#include "inc/List.h" + +namespace graphite2 { + +class json +{ + // Prevent copying + json(const json &); + json & operator = (const json &); + + typedef void (*_context_t)(json &); + + FILE * const _stream; + char _contexts[128], // context stack + * _context, // current context (top of stack) + * _flatten; // if !0 points to context above which + // pretty printed output should occur. + Vector _env; + + void context(const char current) throw(); + void indent(const int d=0) throw(); + void push_context(const char, const char) throw(); + void pop_context() throw(); + +public: + class closer; + + using string = const char *; + using number = double; + enum class integer : std::intmax_t {}; + enum class integer_u : std::uintmax_t {}; + using boolean = bool; + static const std::nullptr_t null; + + void setenv(unsigned int index, void *val) { _env.reserve(index + 1); if (index >= _env.size()) _env.insert(_env.end(), _env.size() - index + 1, 0); _env[index] = val; } + void *getenv(unsigned int index) const { return _env[index]; } + const Vector &getenvs() const { return _env; } + + static void flat(json &) throw(); + static void close(json &) throw(); + static void object(json &) throw(); + static void array(json &) throw(); + static void item(json &) throw(); + + json(FILE * stream) throw(); + ~json() throw (); + + FILE * stream() const throw(); + + json & operator << (string) throw(); + json & operator << (number) throw(); + json & operator << (integer) throw(); + json & operator << (integer_u) throw(); + json & operator << (boolean) throw(); + json & operator << (std::nullptr_t) throw(); + json & operator << (_context_t) throw(); + + operator bool() const throw(); + bool good() const throw(); + bool eof() const throw(); + + CLASS_NEW_DELETE; +}; + +class json::closer +{ + // Prevent copying. + closer(const closer &); + closer & operator = (const closer &); + + json * const _j; +public: + closer(json * const j) : _j(j) {} + ~closer() throw() { if (_j) *_j << close; } +}; + +inline +json::json(FILE * s) throw() +: _stream(s), _context(_contexts), _flatten(0) +{ + if (good()) + fflush(s); +} + + +inline +json::~json() throw () +{ + while (_context > _contexts) pop_context(); +} + +inline +FILE * json::stream() const throw() { return _stream; } + + +inline +json & json::operator << (json::_context_t ctxt) throw() +{ + ctxt(*this); + return *this; +} + +inline +json & operator << (json & j, signed char d) throw() { return j << json::integer(d); } + +inline +json & operator << (json & j, unsigned char d) throw() { return j << json::integer_u(d); } + +inline +json & operator << (json & j, short int d) throw() { return j << json::integer(d); } + +inline +json & operator << (json & j, unsigned short int d) throw() { return j << json::integer_u(d); } + +inline +json & operator << (json & j, int d) throw() { return j << json::integer(d); } + +inline +json & operator << (json & j, unsigned int d) throw() { return j << json::integer_u(d); } + +inline +json & operator << (json & j, long int d) throw() { return j << json::integer(d); } + +inline +json & operator << (json & j, unsigned long int d) throw() { return j << json::integer_u(d); } + +inline +json & operator << (json & j, long long int d) throw() { return j << json::integer(d); } + +inline +json & operator << (json & j, unsigned long long int d) throw() { return j << json::integer_u(d); } + +inline +json::operator bool() const throw() { return good(); } + +inline +bool json::good() const throw() { return _stream && ferror(_stream) == 0; } + +inline +bool json::eof() const throw() { return feof(_stream) != 0; } + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/locale2lcid.h b/thirdparty/graphite/src/inc/locale2lcid.h new file mode 100644 index 00000000000..25d5c0a3c8f --- /dev/null +++ b/thirdparty/graphite/src/inc/locale2lcid.h @@ -0,0 +1,450 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once +#include +#include + +#include "inc/Main.h" + + +namespace graphite2 { + +struct IsoLangEntry +{ + unsigned short mnLang; + char maLangStr[4]; + char maCountry[3]; +}; + +// Windows Language ID, Locale ISO-639 language, country code as used in +// naming table of OpenType fonts +const IsoLangEntry LANG_ENTRIES[] = { + { 0x0401, "ar","SA" }, // Arabic Saudi Arabia + { 0x0402, "bg","BG" }, // Bulgarian Bulgaria + { 0x0403, "ca","ES" }, // Catalan Catalan + { 0x0404, "zh","TW" }, // Chinese Taiwan + { 0x0405, "cs","CZ" }, // Czech Czech Republic + { 0x0406, "da","DK" }, // Danish Denmark + { 0x0407, "de","DE" }, // German Germany + { 0x0408, "el","GR" }, // Greek Greece + { 0x0409, "en","US" }, // English United States + { 0x040A, "es","ES" }, // Spanish (Traditional Sort) Spain + { 0x040B, "fi","FI" }, // Finnish Finland + { 0x040C, "fr","FR" }, // French France + { 0x040D, "he","IL" }, // Hebrew Israel + { 0x040E, "hu","HU" }, // Hungarian Hungary + { 0x040F, "is","IS" }, // Icelandic Iceland + { 0x0410, "it","IT" }, // Italian Italy + { 0x0411, "jp","JP" }, // Japanese Japan + { 0x0412, "ko","KR" }, // Korean Korea + { 0x0413, "nl","NL" }, // Dutch Netherlands + { 0x0414, "no","NO" }, // Norwegian (Bokmal) Norway + { 0x0415, "pl","PL" }, // Polish Poland + { 0x0416, "pt","BR" }, // Portuguese Brazil + { 0x0417, "rm","CH" }, // Romansh Switzerland + { 0x0418, "ro","RO" }, // Romanian Romania + { 0x0419, "ru","RU" }, // Russian Russia + { 0x041A, "hr","HR" }, // Croatian Croatia + { 0x041B, "sk","SK" }, // Slovak Slovakia + { 0x041C, "sq","AL" }, // Albanian Albania + { 0x041D, "sv","SE" }, // Swedish Sweden + { 0x041E, "th","TH" }, // Thai Thailand + { 0x041F, "tr","TR" }, // Turkish Turkey + { 0x0420, "ur","PK" }, // Urdu Islamic Republic of Pakistan + { 0x0421, "id","ID" }, // Indonesian Indonesia + { 0x0422, "uk","UA" }, // Ukrainian Ukraine + { 0x0423, "be","BY" }, // Belarusian Belarus + { 0x0424, "sl","SI" }, // Slovenian Slovenia + { 0x0425, "et","EE" }, // Estonian Estonia + { 0x0426, "lv","LV" }, // Latvian Latvia + { 0x0427, "lt","LT" }, // Lithuanian Lithuania + { 0x0428, "tg","TJ" }, // Tajik (Cyrillic) Tajikistan + { 0x042A, "vi","VN" }, // Vietnamese Vietnam + { 0x042B, "hy","AM" }, // Armenian Armenia + { 0x042C, "az","AZ" }, // Azeri (Latin) Azerbaijan + { 0x042D, "eu","" }, // Basque Basque + { 0x042E, "hsb","DE" }, // Upper Sorbian Germany + { 0x042F, "mk","MK" }, // Macedonian (FYROM) Former Yugoslav Republic of Macedonia + { 0x0432, "tn","ZA" }, // Setswana South Africa + { 0x0434, "xh","ZA" }, // isiXhosa South Africa + { 0x0435, "zu","ZA" }, // isiZulu South Africa + { 0x0436, "af","ZA" }, // Afrikaans South Africa + { 0x0437, "ka","GE" }, // Georgian Georgia + { 0x0438, "fo","FO" }, // Faroese Faroe Islands + { 0x0439, "hi","IN" }, // Hindi India + { 0x043A, "mt","MT" }, // Maltese Malta + { 0x043B, "se","NO" }, // Sami (Northern) Norway + { 0x043E, "ms","MY" }, // Malay Malaysia + { 0x043F, "kk","KZ" }, // Kazakh Kazakhstan + { 0x0440, "ky","KG" }, // Kyrgyz Kyrgyzstan + { 0x0441, "sw","KE" }, // Kiswahili Kenya + { 0x0442, "tk","TM" }, // Turkmen Turkmenistan + { 0x0443, "uz","UZ" }, // Uzbek (Latin) Uzbekistan + { 0x0444, "tt","RU" }, // Tatar Russia + { 0x0445, "bn","IN" }, // Bengali India + { 0x0446, "pa","IN" }, // Punjabi India + { 0x0447, "gu","IN" }, // Gujarati India + { 0x0448, "or","IN" }, // Oriya India + { 0x0448, "wo","SN" }, // Wolof Senegal + { 0x0449, "ta","IN" }, // Tamil India + { 0x044A, "te","IN" }, // Telugu India + { 0x044B, "kn","IN" }, // Kannada India + { 0x044C, "ml","IN" }, // Malayalam India + { 0x044D, "as","IN" }, // Assamese India + { 0x044E, "mr","IN" }, // Marathi India + { 0x044F, "sa","IN" }, // Sanskrit India + { 0x0450, "mn","MN" }, // Mongolian (Cyrillic) Mongolia + { 0x0451, "bo","CN" }, // Tibetan PRC + { 0x0452, "cy","GB" }, // Welsh United Kingdom + { 0x0453, "km","KH" }, // Khmer Cambodia + { 0x0454, "lo","LA" }, // Lao Lao P.D.R. + { 0x0455, "my","MM" }, // Burmese Myanmar - not listed in Microsoft docs anymore + { 0x0456, "gl","ES" }, // Galician Galician + { 0x0457, "kok","IN" }, // Konkani India + { 0x045A, "syr","TR" }, // Syriac Syria + { 0x045B, "si","LK" }, // Sinhala Sri Lanka + { 0x045D, "iu","CA" }, // Inuktitut Canada + { 0x045E, "am","ET" }, // Amharic Ethiopia + { 0x0461, "ne","NP" }, // Nepali Nepal + { 0x0462, "fy","NL" }, // Frisian Netherlands + { 0x0463, "ps","AF" }, // Pashto Afghanistan + { 0x0464, "fil","PH" }, // Filipino Philippines + { 0x0465, "dv","MV" }, // Divehi Maldives + { 0x0468, "ha","NG" }, // Hausa (Latin) Nigeria + { 0x046A, "yo","NG" }, // Yoruba Nigeria + { 0x046B, "qu","BO" }, // Quechua Bolivia + { 0x046C, "st","ZA" }, // Sesotho sa Leboa South Africa + { 0x046D, "ba","RU" }, // Bashkir Russia + { 0x046E, "lb","LU" }, // Luxembourgish Luxembourg + { 0x046F, "kl","GL" }, // Greenlandic Greenland + { 0x0470, "ig","NG" }, // Igbo Nigeria + { 0x0478, "ii","CN" }, // Yi PRC + { 0x047A, "arn","CL" }, // Mapudungun Chile + { 0x047C, "moh","CA" }, // Mohawk Mohawk + { 0x047E, "br","FR" }, // Breton France + { 0x0480, "ug","CN" }, // Uighur PRC + { 0x0481, "mi","NZ" }, // Maori New Zealand + { 0x0482, "oc","FR" }, // Occitan France + { 0x0483, "co","FR" }, // Corsican France + { 0x0484, "gsw","FR" }, // Alsatian France + { 0x0485, "sah","RU" }, // Yakut Russia + { 0x0486, "qut","GT" }, // K'iche Guatemala + { 0x0487, "rw","RW" }, // Kinyarwanda Rwanda + { 0x048C, "gbz","AF" }, // Dari Afghanistan + { 0x0801, "ar","IQ" }, // Arabic Iraq + { 0x0804, "zn","CH" }, // Chinese People's Republic of China + { 0x0807, "de","CH" }, // German Switzerland + { 0x0809, "en","GB" }, // English United Kingdom + { 0x080A, "es","MX" }, // Spanish Mexico + { 0x080C, "fr","BE" }, // French Belgium + { 0x0810, "it","CH" }, // Italian Switzerland + { 0x0813, "nl","BE" }, // Dutch Belgium + { 0x0814, "nn","NO" }, // Norwegian (Nynorsk) Norway + { 0x0816, "pt","PT" }, // Portuguese Portugal + { 0x081A, "sh","RS" }, // Serbian (Latin) Serbia + { 0x081D, "sv","FI" }, // Sweden Finland + { 0x082C, "az","AZ" }, // Azeri (Cyrillic) Azerbaijan + { 0x082E, "dsb","DE" }, // Lower Sorbian Germany + { 0x083B, "se","SE" }, // Sami (Northern) Sweden + { 0x083C, "ga","IE" }, // Irish Ireland + { 0x083E, "ms","BN" }, // Malay Brunei Darussalam + { 0x0843, "uz","UZ" }, // Uzbek (Cyrillic) Uzbekistan + { 0x0845, "bn","BD" }, // Bengali Bangladesh + { 0x0850, "mn","MN" }, // Mongolian (Traditional) People's Republic of China + { 0x085D, "iu","CA" }, // Inuktitut (Latin) Canada + { 0x085F, "ber","DZ" }, // Tamazight (Latin) Algeria + { 0x086B, "es","EC" }, // Quechua Ecuador + { 0x0C01, "ar","EG" }, // Arabic Egypt + { 0x0C04, "zh","HK" }, // Chinese Hong Kong S.A.R. + { 0x0C07, "de","AT" }, // German Austria + { 0x0C09, "en","AU" }, // English Australia + { 0x0C0A, "es","ES" }, // Spanish (Modern Sort) Spain + { 0x0C0C, "fr","CA" }, // French Canada + { 0x0C1A, "sr","CS" }, // Serbian (Cyrillic) Serbia + { 0x0C3B, "se","FI" }, // Sami (Northern) Finland + { 0x0C6B, "qu","PE" }, // Quechua Peru + { 0x1001, "ar","LY" }, // Arabic Libya + { 0x1004, "zh","SG" }, // Chinese Singapore + { 0x1007, "de","LU" }, // German Luxembourg + { 0x1009, "en","CA" }, // English Canada + { 0x100A, "es","GT" }, // Spanish Guatemala + { 0x100C, "fr","CH" }, // French Switzerland + { 0x101A, "hr","BA" }, // Croatian (Latin) Bosnia and Herzegovina + { 0x103B, "smj","NO" }, // Sami (Lule) Norway + { 0x1401, "ar","DZ" }, // Arabic Algeria + { 0x1404, "zh","MO" }, // Chinese Macao S.A.R. + { 0x1407, "de","LI" }, // German Liechtenstein + { 0x1409, "en","NZ" }, // English New Zealand + { 0x140A, "es","CR" }, // Spanish Costa Rica + { 0x140C, "fr","LU" }, // French Luxembourg + { 0x141A, "bs","BA" }, // Bosnian (Latin) Bosnia and Herzegovina + { 0x143B, "smj","SE" }, // Sami (Lule) Sweden + { 0x1801, "ar","MA" }, // Arabic Morocco + { 0x1809, "en","IE" }, // English Ireland + { 0x180A, "es","PA" }, // Spanish Panama + { 0x180C, "fr","MC" }, // French Principality of Monoco + { 0x181A, "sh","BA" }, // Serbian (Latin) Bosnia and Herzegovina + { 0x183B, "sma","NO" }, // Sami (Southern) Norway + { 0x1C01, "ar","TN" }, // Arabic Tunisia + { 0x1C09, "en","ZA" }, // English South Africa + { 0x1C0A, "es","DO" }, // Spanish Dominican Republic + { 0x1C1A, "sr","BA" }, // Serbian (Cyrillic) Bosnia and Herzegovina + { 0x1C3B, "sma","SE" }, // Sami (Southern) Sweden + { 0x2001, "ar","OM" }, // Arabic Oman + { 0x2009, "en","JM" }, // English Jamaica + { 0x200A, "es","VE" }, // Spanish Venezuela + { 0x201A, "bs","BA" }, // Bosnian (Cyrillic) Bosnia and Herzegovina + { 0x203B, "sms","FI" }, // Sami (Skolt) Finland + { 0x2401, "ar","YE" }, // Arabic Yemen + { 0x2409, "en","BS" }, // English Caribbean + { 0x240A, "es","CO" }, // Spanish Colombia + { 0x243B, "smn","FI" }, // Sami (Inari) Finland + { 0x2801, "ar","SY" }, // Arabic Syria + { 0x2809, "en","BZ" }, // English Belize + { 0x280A, "es","PE" }, // Spanish Peru + { 0x2C01, "ar","JO" }, // Arabic Jordan + { 0x2C09, "en","TT" }, // English Trinidad and Tobago + { 0x2C0A, "es","AR" }, // Spanish Argentina + { 0x3001, "ar","LB" }, // Arabic Lebanon + { 0x3009, "en","ZW" }, // English Zimbabwe + { 0x300A, "es","EC" }, // Spanish Ecuador + { 0x3401, "ar","KW" }, // Arabic Kuwait + { 0x3409, "en","PH" }, // English Republic of the Philippines + { 0x340A, "es","CL" }, // Spanish Chile + { 0x3801, "ar","AE" }, // Arabic U.A.E. + { 0x380A, "es","UY" }, // Spanish Uruguay + { 0x3C01, "ar","BH" }, // Arabic Bahrain + { 0x3C0A, "es","PY" }, // Spanish Paraguay + { 0x4001, "ar","QA" }, // Arabic Qatar + { 0x4009, "en","IN" }, // English India + { 0x400A, "es","BO" }, // Spanish Bolivia + { 0x4409, "en","MY" }, // English Malaysia + { 0x440A, "es","SV" }, // Spanish El Salvador + { 0x4809, "en","SG" }, // English Singapore + { 0x480A, "es","HN" }, // Spanish Honduras + { 0x4C0A, "es","NI" }, // Spanish Nicaragua + { 0x500A, "es","PR" }, // Spanish Puerto Rico + { 0x540A, "es","US" } // Spanish United States +}; + +class Locale2Lang +{ + Locale2Lang(const Locale2Lang &); + Locale2Lang & operator = (const Locale2Lang &); + +public: + Locale2Lang() : mSeedPosition(128) + { + memset((void*)mLangLookup, 0, sizeof(mLangLookup)); + // create a tri lookup on first 2 letters of language code + static const int maxIndex = sizeof(LANG_ENTRIES)/sizeof(IsoLangEntry); + for (int i = 0; i < maxIndex; i++) + { + size_t a = LANG_ENTRIES[i].maLangStr[0] - 'a'; + size_t b = LANG_ENTRIES[i].maLangStr[1] - 'a'; + if (mLangLookup[a][b]) + { + const IsoLangEntry ** old = mLangLookup[a][b]; + int len = 1; + while (old[len]) len++; + len += 2; + mLangLookup[a][b] = gralloc(len); + if (!mLangLookup[a][b]) + { + mLangLookup[a][b] = old; + continue; + } + mLangLookup[a][b][--len] = NULL; + mLangLookup[a][b][--len] = &LANG_ENTRIES[i]; + while (--len >= 0) + { + assert(len >= 0); + mLangLookup[a][b][len] = old[len]; + } + free(old); + } + else + { + mLangLookup[a][b] = gralloc(2); + if (!mLangLookup[a][b]) continue; + mLangLookup[a][b][1] = NULL; + mLangLookup[a][b][0] = &LANG_ENTRIES[i]; + } + } + while (2 * mSeedPosition < maxIndex) + mSeedPosition *= 2; + }; + ~Locale2Lang() + { + for (int i = 0; i != 26; ++i) + for (int j = 0; j != 26; ++j) + free(mLangLookup[i][j]); + } + unsigned short getMsId(const char * locale) const + { + size_t length = strlen(locale); + size_t langLength = length; + const char * language = locale; + const char * script = NULL; + const char * region = NULL; + size_t regionLength = 0; + const char * dash = strchr(locale, '-'); + if (dash && (dash != locale)) + { + langLength = (dash - locale); + size_t nextPartLength = length - langLength - 1; + if (nextPartLength >= 2) + { + script = ++dash; + dash = strchr(dash, '-'); + if (dash) + { + nextPartLength = (dash - script); + region = ++dash; + } + if (nextPartLength == 2 && + (locale[langLength+1] > 0x40) && (locale[langLength+1] < 0x5B) && + (locale[langLength+2] > 0x40) && (locale[langLength+2] < 0x5B)) + { + region = script; + regionLength = nextPartLength; + script = NULL; + } + else if (nextPartLength == 4) + { + if (dash) + { + dash = strchr(dash, '-'); + if (dash) + { + nextPartLength = (dash - region); + } + else + { + nextPartLength = langLength - (region - locale); + } + regionLength = nextPartLength; + } + } + } + } + size_t a = 'e' - 'a'; + size_t b = 'n' - 'a'; + unsigned short langId = 0; + int i = 0; + switch (langLength) + { + case 2: + { + a = language[0] - 'a'; + b = language[1] - 'a'; + if ((a < 26) && (b < 26) && mLangLookup[a][b]) + { + while (mLangLookup[a][b][i]) + { + if (mLangLookup[a][b][i]->maLangStr[2] != '\0') + { + ++i; + continue; + } + if (region && (strncmp(mLangLookup[a][b][i]->maCountry, region, regionLength) == 0)) + { + langId = mLangLookup[a][b][i]->mnLang; + break; + } + else if (langId == 0) + { + // possible fallback code + langId = mLangLookup[a][b][i]->mnLang; + } + ++i; + } + } + } + break; + case 3: + { + a = language[0] - 'a'; + b = language[1] - 'a'; + if (mLangLookup[a][b]) + { + while (mLangLookup[a][b][i]) + { + if (mLangLookup[a][b][i]->maLangStr[2] != language[2]) + { + ++i; + continue; + } + if (region && (strncmp(mLangLookup[a][b][i]->maCountry, region, regionLength) == 0)) + { + langId = mLangLookup[a][b][i]->mnLang; + break; + } + else if (langId == 0) + { + // possible fallback code + langId = mLangLookup[a][b][i]->mnLang; + } + ++i; + } + } + } + break; + default: + break; + } + if (langId == 0) langId = 0x409; + return langId; + } + const IsoLangEntry * findEntryById(unsigned short langId) const + { + static const int maxIndex = sizeof(LANG_ENTRIES)/sizeof(IsoLangEntry); + int window = mSeedPosition; + int guess = mSeedPosition - 1; + while (LANG_ENTRIES[guess].mnLang != langId) + { + window /= 2; + if (window == 0) return NULL; + guess += (LANG_ENTRIES[guess].mnLang > langId)? -window : window; + while (guess >= maxIndex) + { + window /= 2; + guess -= window; + assert(window); + } + } + return &LANG_ENTRIES[guess]; + } + + CLASS_NEW_DELETE; + +private: + const IsoLangEntry ** mLangLookup[26][26]; + int mSeedPosition; +}; + +} // namespace graphite2 diff --git a/thirdparty/graphite/src/inc/opcode_table.h b/thirdparty/graphite/src/inc/opcode_table.h new file mode 100644 index 00000000000..cb5acde9a47 --- /dev/null +++ b/thirdparty/graphite/src/inc/opcode_table.h @@ -0,0 +1,124 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// This file will be pulled into and integrated into a machine implmentation +// DO NOT build directly +#pragma once + +#define do2(n) do_(n) ,do_(n) +#define NILOP 0U + +// types or parameters are: (.. is inclusive) +// number - any byte +// output_class - 0 .. silf.m_nClass +// input_class - 0 .. silf.m_nClass +// sattrnum - 0 .. 29 (gr_slatJWidth) , 55 (gr_slatUserDefn) +// attrid - 0 .. silf.numUser() where sattrnum == 55; 0..silf.m_iMaxComp where sattrnum == 15 otherwise 0 +// gattrnum - 0 .. face->getGlyphFaceCache->numAttrs() +// gmetric - 0 .. 11 (kgmetDescent) +// featidx - 0 .. face.numFeatures() +// level - any byte +static const opcode_t opcode_table[] = +{ + {{do2(nop)}, 0, "NOP"}, + + {{do2(push_byte)}, 1, "PUSH_BYTE"}, // number + {{do2(push_byte_u)}, 1, "PUSH_BYTE_U"}, // number + {{do2(push_short)}, 2, "PUSH_SHORT"}, // number number + {{do2(push_short_u)}, 2, "PUSH_SHORT_U"}, // number number + {{do2(push_long)}, 4, "PUSH_LONG"}, // number number number number + + {{do2(add)}, 0, "ADD"}, + {{do2(sub)}, 0, "SUB"}, + {{do2(mul)}, 0, "MUL"}, + {{do2(div_)}, 0, "DIV"}, + {{do2(min_)}, 0, "MIN"}, + {{do2(max_)}, 0, "MAX"}, + {{do2(neg)}, 0, "NEG"}, + {{do2(trunc8)}, 0, "TRUNC8"}, + {{do2(trunc16)}, 0, "TRUNC16"}, + + {{do2(cond)}, 0, "COND"}, + {{do2(and_)}, 0, "AND"}, // 0x10 + {{do2(or_)}, 0, "OR"}, + {{do2(not_)}, 0, "NOT"}, + {{do2(equal)}, 0, "EQUAL"}, + {{do2(not_eq_)}, 0, "NOT_EQ"}, + {{do2(less)}, 0, "LESS"}, + {{do2(gtr)}, 0, "GTR"}, + {{do2(less_eq)}, 0, "LESS_EQ"}, + {{do2(gtr_eq)}, 0, "GTR_EQ"}, // 0x18 + + {{do_(next), NILOP}, 0, "NEXT"}, + {{NILOP, NILOP}, 1, "NEXT_N"}, // number <= smap.end - map + {{do_(next), NILOP}, 0, "COPY_NEXT"}, + {{do_(put_glyph_8bit_obs), NILOP}, 1, "PUT_GLYPH_8BIT_OBS"}, // output_class + {{do_(put_subs_8bit_obs), NILOP}, 3, "PUT_SUBS_8BIT_OBS"}, // slot input_class output_class + {{do_(put_copy), NILOP}, 1, "PUT_COPY"}, // slot + {{do_(insert), NILOP}, 0, "INSERT"}, + {{do_(delete_), NILOP}, 0, "DELETE"}, // 0x20 + {{do_(assoc), NILOP}, VARARGS, "ASSOC"}, + {{NILOP ,do_(cntxt_item)}, 2, "CNTXT_ITEM"}, // slot offset + + {{do_(attr_set), NILOP}, 1, "ATTR_SET"}, // sattrnum + {{do_(attr_add), NILOP}, 1, "ATTR_ADD"}, // sattrnum + {{do_(attr_sub), NILOP}, 1, "ATTR_SUB"}, // sattrnum + {{do_(attr_set_slot), NILOP}, 1, "ATTR_SET_SLOT"}, // sattrnum + {{do_(iattr_set_slot), NILOP}, 2, "IATTR_SET_SLOT"}, // sattrnum attrid + {{do2(push_slot_attr)}, 2, "PUSH_SLOT_ATTR"}, // sattrnum slot + {{do2(push_glyph_attr_obs)}, 2, "PUSH_GLYPH_ATTR_OBS"}, // gattrnum slot + {{do2(push_glyph_metric)}, 3, "PUSH_GLYPH_METRIC"}, // gmetric slot level + {{do2(push_feat)}, 2, "PUSH_FEAT"}, // featidx slot + + {{do2(push_att_to_gattr_obs)}, 2, "PUSH_ATT_TO_GATTR_OBS"}, // gattrnum slot + {{do2(push_att_to_glyph_metric)}, 3, "PUSH_ATT_TO_GLYPH_METRIC"}, // gmetric slot level + {{do2(push_islot_attr)}, 3, "PUSH_ISLOT_ATTR"}, // sattrnum slot attrid + + {{NILOP,NILOP}, 3, "PUSH_IGLYPH_ATTR"}, + + {{do2(pop_ret)}, 0, "POP_RET"}, // 0x30 + {{do2(ret_zero)}, 0, "RET_ZERO"}, + {{do2(ret_true)}, 0, "RET_TRUE"}, + + {{do_(iattr_set), NILOP}, 2, "IATTR_SET"}, // sattrnum attrid + {{do_(iattr_add), NILOP}, 2, "IATTR_ADD"}, // sattrnum attrid + {{do_(iattr_sub), NILOP}, 2, "IATTR_SUB"}, // sattrnum attrid + {{do2(push_proc_state)}, 1, "PUSH_PROC_STATE"}, // dummy + {{do2(push_version)}, 0, "PUSH_VERSION"}, + {{do_(put_subs), NILOP}, 5, "PUT_SUBS"}, // slot input_class input_class output_class output_class + {{NILOP,NILOP}, 0, "PUT_SUBS2"}, + {{NILOP,NILOP}, 0, "PUT_SUBS3"}, + {{do_(put_glyph), NILOP}, 2, "PUT_GLYPH"}, // output_class output_class + {{do2(push_glyph_attr)}, 3, "PUSH_GLYPH_ATTR"}, // gattrnum gattrnum slot + {{do2(push_att_to_glyph_attr)}, 3, "PUSH_ATT_TO_GLYPH_ATTR"}, // gattrnum gattrnum slot + {{do2(bor)}, 0, "BITOR"}, + {{do2(band)}, 0, "BITAND"}, + {{do2(bnot)}, 0, "BITNOT"}, // 0x40 + {{do2(setbits)}, 4, "BITSET"}, + {{do_(set_feat), NILOP}, 2, "SET_FEAT"}, // featidx slot + // private opcodes for internal use only, comes after all other on disk opcodes. + {{do_(temp_copy), NILOP}, 0, "TEMP_COPY"} +}; diff --git a/thirdparty/graphite/src/inc/opcodes.h b/thirdparty/graphite/src/inc/opcodes.h new file mode 100644 index 00000000000..ff2f1741e2f --- /dev/null +++ b/thirdparty/graphite/src/inc/opcodes.h @@ -0,0 +1,691 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#pragma once +// This file will be pulled into and integrated into a machine implmentation +// DO NOT build directly and under no circumstances ever #include headers in +// here or you will break the direct_machine. +// +// Implementers' notes +// ================== +// You have access to a few primitives and the full C++ code: +// declare_params(n) Tells the interpreter how many bytes of parameter +// space to claim for this instruction uses and +// initialises the param pointer. You *must* before the +// first use of param. +// use_params(n) Claim n extra bytes of param space beyond what was +// claimed using delcare_param. +// param A const byte pointer for the parameter space claimed by +// this instruction. +// binop(op) Implement a binary operation on the stack using the +// specified C++ operator. +// NOT_IMPLEMENTED Any instruction body containing this will exit the +// program with an assertion error. Instructions that are +// not implemented should also be marked NILOP in the +// opcodes tables this will cause the code class to spot +// them in a live code stream and throw a runtime_error +// instead. +// push(n) Push the value n onto the stack. +// pop() Pop the top most value and return it. +// +// You have access to the following named fast 'registers': +// sp = The pointer to the current top of stack, the last value +// pushed. +// seg = A reference to the Segment this code is running over. +// is = The current slot index +// isb = The original base slot index at the start of this rule +// isf = The first positioned slot +// isl = The last positioned slot +// ip = The current instruction pointer +// endPos = Position of advance of last cluster +// dir = writing system directionality of the font + + +// #define NOT_IMPLEMENTED assert(false) +// #define NOT_IMPLEMENTED + +#define binop(op) const uint32 a = pop(); *sp = uint32(*sp) op a +#define sbinop(op) const int32 a = pop(); *sp = int32(*sp) op a +#define use_params(n) dp += n + +#define declare_params(n) const byte * param = dp; \ + use_params(n); + +#define push(n) { *++sp = n; } +#define pop() (*sp--) +#define slotat(x) (map[(x)]) +#define DIE { is=seg.last(); status = Machine::died_early; EXIT(1); } +#define POSITIONED 1 + +STARTOP(nop) + do {} while (0); +ENDOP + +STARTOP(push_byte) + declare_params(1); + push(int8(*param)); +ENDOP + +STARTOP(push_byte_u) + declare_params(1); + push(uint8(*param)); +ENDOP + +STARTOP(push_short) + declare_params(2); + const int16 r = int16(param[0]) << 8 + | uint8(param[1]); + push(r); +ENDOP + +STARTOP(push_short_u) + declare_params(2); + const uint16 r = uint16(param[0]) << 8 + | uint8(param[1]); + push(r); +ENDOP + +STARTOP(push_long) + declare_params(4); + const int32 r = int32(param[0]) << 24 + | uint32(param[1]) << 16 + | uint32(param[2]) << 8 + | uint8(param[3]); + push(r); +ENDOP + +STARTOP(add) + binop(+); +ENDOP + +STARTOP(sub) + binop(-); +ENDOP + +STARTOP(mul) + binop(*); +ENDOP + +STARTOP(div_) + const int32 b = pop(); + const int32 a = int32(*sp); + if (b == 0 || (a == std::numeric_limits::min() && b == -1)) DIE; + *sp = int32(*sp) / b; +ENDOP + +STARTOP(min_) + const int32 a = pop(), b = *sp; + if (a < b) *sp = a; +ENDOP + +STARTOP(max_) + const int32 a = pop(), b = *sp; + if (a > b) *sp = a; +ENDOP + +STARTOP(neg) + *sp = uint32(-int32(*sp)); +ENDOP + +STARTOP(trunc8) + *sp = uint8(*sp); +ENDOP + +STARTOP(trunc16) + *sp = uint16(*sp); +ENDOP + +STARTOP(cond) + const uint32 f = pop(), t = pop(), c = pop(); + push(c ? t : f); +ENDOP + +STARTOP(and_) + binop(&&); +ENDOP + +STARTOP(or_) + binop(||); +ENDOP + +STARTOP(not_) + *sp = !*sp; +ENDOP + +STARTOP(equal) + binop(==); +ENDOP + +STARTOP(not_eq_) + binop(!=); +ENDOP + +STARTOP(less) + sbinop(<); +ENDOP + +STARTOP(gtr) + sbinop(>); +ENDOP + +STARTOP(less_eq) + sbinop(<=); +ENDOP + +STARTOP(gtr_eq) + sbinop(>=); +ENDOP + +STARTOP(next) + if (map - &smap[0] >= int(smap.size())) DIE + if (is) + { + if (is == smap.highwater()) + smap.highpassed(true); + is = is->next(); + } + ++map; +ENDOP + +//STARTOP(next_n) +// use_params(1); +// NOT_IMPLEMENTED; + //declare_params(1); + //const size_t num = uint8(*param); +//ENDOP + +//STARTOP(copy_next) +// if (is) is = is->next(); +// ++map; +// ENDOP + +STARTOP(put_glyph_8bit_obs) + declare_params(1); + const unsigned int output_class = uint8(*param); + is->setGlyph(&seg, seg.getClassGlyph(output_class, 0)); +ENDOP + +STARTOP(put_subs_8bit_obs) + declare_params(3); + const int slot_ref = int8(param[0]); + const unsigned int input_class = uint8(param[1]), + output_class = uint8(param[2]); + uint16 index; + slotref slot = slotat(slot_ref); + if (slot) + { + index = seg.findClassIndex(input_class, slot->gid()); + is->setGlyph(&seg, seg.getClassGlyph(output_class, index)); + } +ENDOP + +STARTOP(put_copy) + declare_params(1); + const int slot_ref = int8(*param); + if (is && !is->isDeleted()) + { + slotref ref = slotat(slot_ref); + if (ref && ref != is) + { + int16 *tempUserAttrs = is->userAttrs(); + if (is->attachedTo() || is->firstChild()) DIE + Slot *prev = is->prev(); + Slot *next = is->next(); + memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16)); + memcpy(is, ref, sizeof(Slot)); + is->firstChild(NULL); + is->nextSibling(NULL); + is->userAttrs(tempUserAttrs); + is->next(next); + is->prev(prev); + if (is->attachedTo()) + is->attachedTo()->child(is); + } + is->markCopied(false); + is->markDeleted(false); + } +ENDOP + +STARTOP(insert) + if (smap.decMax() <= 0) DIE; + Slot *newSlot = seg.newSlot(); + if (!newSlot) DIE; + Slot *iss = is; + while (iss && iss->isDeleted()) iss = iss->next(); + if (!iss) + { + if (seg.last()) + { + seg.last()->next(newSlot); + newSlot->prev(seg.last()); + newSlot->before(seg.last()->before()); + seg.last(newSlot); + } + else + { + seg.first(newSlot); + seg.last(newSlot); + } + } + else if (iss->prev()) + { + iss->prev()->next(newSlot); + newSlot->prev(iss->prev()); + newSlot->before(iss->prev()->after()); + } + else + { + newSlot->prev(NULL); + newSlot->before(iss->before()); + seg.first(newSlot); + } + newSlot->next(iss); + if (iss) + { + iss->prev(newSlot); + newSlot->originate(iss->original()); + newSlot->after(iss->before()); + } + else if (newSlot->prev()) + { + newSlot->originate(newSlot->prev()->original()); + newSlot->after(newSlot->prev()->after()); + } + else + { + newSlot->originate(seg.defaultOriginal()); + } + if (is == smap.highwater()) + smap.highpassed(false); + is = newSlot; + seg.extendLength(1); + if (map != &smap[-1]) + --map; +ENDOP + +STARTOP(delete_) + if (!is || is->isDeleted()) DIE + is->markDeleted(true); + if (is->prev()) + is->prev()->next(is->next()); + else + seg.first(is->next()); + + if (is->next()) + is->next()->prev(is->prev()); + else + seg.last(is->prev()); + + + if (is == smap.highwater()) + smap.highwater(is->next()); + if (is->prev()) + is = is->prev(); + seg.extendLength(-1); +ENDOP + +STARTOP(assoc) + declare_params(1); + unsigned int num = uint8(*param); + const int8 * assocs = reinterpret_cast(param+1); + use_params(num); + int max = -1; + int min = -1; + + while (num-- > 0) + { + int sr = *assocs++; + slotref ts = slotat(sr); + if (ts && (min == -1 || ts->before() < min)) min = ts->before(); + if (ts && ts->after() > max) max = ts->after(); + } + if (min > -1) // implies max > -1 + { + is->before(min); + is->after(max); + } +ENDOP + +STARTOP(cntxt_item) + // It turns out this is a cunningly disguised condition forward jump. + declare_params(3); + const int is_arg = int8(param[0]); + const size_t iskip = uint8(param[1]), + dskip = uint8(param[2]); + + if (mapb + is_arg != map) + { + ip += iskip; + dp += dskip; + push(true); + } +ENDOP + +STARTOP(attr_set) + declare_params(1); + const attrCode slat = attrCode(uint8(*param)); + const int val = pop(); + is->setAttr(&seg, slat, 0, val, smap); +ENDOP + +STARTOP(attr_add) + declare_params(1); + const attrCode slat = attrCode(uint8(*param)); + const uint32_t val = pop(); + if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) + { + seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); + flags |= POSITIONED; + } + uint32_t res = uint32_t(is->getAttr(&seg, slat, 0)); + is->setAttr(&seg, slat, 0, int32_t(val + res), smap); +ENDOP + +STARTOP(attr_sub) + declare_params(1); + const attrCode slat = attrCode(uint8(*param)); + const uint32_t val = pop(); + if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) + { + seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); + flags |= POSITIONED; + } + uint32_t res = uint32_t(is->getAttr(&seg, slat, 0)); + is->setAttr(&seg, slat, 0, int32_t(res - val), smap); +ENDOP + +STARTOP(attr_set_slot) + declare_params(1); + const attrCode slat = attrCode(uint8(*param)); + const int offset = int(map - smap.begin())*int(slat == gr_slatAttTo); + const int val = pop() + offset; + is->setAttr(&seg, slat, offset, val, smap); +ENDOP + +STARTOP(iattr_set_slot) + declare_params(2); + const attrCode slat = attrCode(uint8(param[0])); + const uint8 idx = uint8(param[1]); + const int val = int(pop() + (map - smap.begin())*int(slat == gr_slatAttTo)); + is->setAttr(&seg, slat, idx, val, smap); +ENDOP + +STARTOP(push_slot_attr) + declare_params(2); + const attrCode slat = attrCode(uint8(param[0])); + const int slot_ref = int8(param[1]); + if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) + { + seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); + flags |= POSITIONED; + } + slotref slot = slotat(slot_ref); + if (slot) + { + int res = slot->getAttr(&seg, slat, 0); + push(res); + } +ENDOP + +STARTOP(push_glyph_attr_obs) + declare_params(2); + const unsigned int glyph_attr = uint8(param[0]); + const int slot_ref = int8(param[1]); + slotref slot = slotat(slot_ref); + if (slot) + push(int32(seg.glyphAttr(slot->gid(), glyph_attr))); +ENDOP + +STARTOP(push_glyph_metric) + declare_params(3); + const unsigned int glyph_attr = uint8(param[0]); + const int slot_ref = int8(param[1]); + const signed int attr_level = uint8(param[2]); + slotref slot = slotat(slot_ref); + if (slot) + push(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir)); +ENDOP + +STARTOP(push_feat) + declare_params(2); + const unsigned int feat = uint8(param[0]); + const int slot_ref = int8(param[1]); + slotref slot = slotat(slot_ref); + if (slot) + { + uint8 fid = seg.charinfo(slot->original())->fid(); + push(seg.getFeature(fid, feat)); + } +ENDOP + +STARTOP(push_att_to_gattr_obs) + declare_params(2); + const unsigned int glyph_attr = uint8(param[0]); + const int slot_ref = int8(param[1]); + slotref slot = slotat(slot_ref); + if (slot) + { + slotref att = slot->attachedTo(); + if (att) slot = att; + push(int32(seg.glyphAttr(slot->gid(), glyph_attr))); + } +ENDOP + +STARTOP(push_att_to_glyph_metric) + declare_params(3); + const unsigned int glyph_attr = uint8(param[0]); + const int slot_ref = int8(param[1]); + const signed int attr_level = uint8(param[2]); + slotref slot = slotat(slot_ref); + if (slot) + { + slotref att = slot->attachedTo(); + if (att) slot = att; + push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir))); + } +ENDOP + +STARTOP(push_islot_attr) + declare_params(3); + const attrCode slat = attrCode(uint8(param[0])); + const int slot_ref = int8(param[1]), + idx = uint8(param[2]); + if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) + { + seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); + flags |= POSITIONED; + } + slotref slot = slotat(slot_ref); + if (slot) + { + int res = slot->getAttr(&seg, slat, idx); + push(res); + } +ENDOP + +#if 0 +STARTOP(push_iglyph_attr) // not implemented + NOT_IMPLEMENTED; +ENDOP +#endif + +STARTOP(pop_ret) + const uint32 ret = pop(); + EXIT(ret); +ENDOP + +STARTOP(ret_zero) + EXIT(0); +ENDOP + +STARTOP(ret_true) + EXIT(1); +ENDOP + +STARTOP(iattr_set) + declare_params(2); + const attrCode slat = attrCode(uint8(param[0])); + const uint8 idx = uint8(param[1]); + const int val = pop(); + is->setAttr(&seg, slat, idx, val, smap); +ENDOP + +STARTOP(iattr_add) + declare_params(2); + const attrCode slat = attrCode(uint8(param[0])); + const uint8 idx = uint8(param[1]); + const uint32_t val = pop(); + if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) + { + seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); + flags |= POSITIONED; + } + uint32_t res = uint32_t(is->getAttr(&seg, slat, idx)); + is->setAttr(&seg, slat, idx, int32_t(val + res), smap); +ENDOP + +STARTOP(iattr_sub) + declare_params(2); + const attrCode slat = attrCode(uint8(param[0])); + const uint8 idx = uint8(param[1]); + const uint32_t val = pop(); + if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0) + { + seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir()); + flags |= POSITIONED; + } + uint32_t res = uint32_t(is->getAttr(&seg, slat, idx)); + is->setAttr(&seg, slat, idx, int32_t(res - val), smap); +ENDOP + +STARTOP(push_proc_state) + use_params(1); + push(1); +ENDOP + +STARTOP(push_version) + push(0x00030000); +ENDOP + +STARTOP(put_subs) + declare_params(5); + const int slot_ref = int8(param[0]); + const unsigned int input_class = uint8(param[1]) << 8 + | uint8(param[2]); + const unsigned int output_class = uint8(param[3]) << 8 + | uint8(param[4]); + slotref slot = slotat(slot_ref); + if (slot) + { + int index = seg.findClassIndex(input_class, slot->gid()); + is->setGlyph(&seg, seg.getClassGlyph(output_class, index)); + } +ENDOP + +#if 0 +STARTOP(put_subs2) // not implemented + NOT_IMPLEMENTED; +ENDOP + +STARTOP(put_subs3) // not implemented + NOT_IMPLEMENTED; +ENDOP +#endif + +STARTOP(put_glyph) + declare_params(2); + const unsigned int output_class = uint8(param[0]) << 8 + | uint8(param[1]); + is->setGlyph(&seg, seg.getClassGlyph(output_class, 0)); +ENDOP + +STARTOP(push_glyph_attr) + declare_params(3); + const unsigned int glyph_attr = uint8(param[0]) << 8 + | uint8(param[1]); + const int slot_ref = int8(param[2]); + slotref slot = slotat(slot_ref); + if (slot) + push(int32(seg.glyphAttr(slot->gid(), glyph_attr))); +ENDOP + +STARTOP(push_att_to_glyph_attr) + declare_params(3); + const unsigned int glyph_attr = uint8(param[0]) << 8 + | uint8(param[1]); + const int slot_ref = int8(param[2]); + slotref slot = slotat(slot_ref); + if (slot) + { + slotref att = slot->attachedTo(); + if (att) slot = att; + push(int32(seg.glyphAttr(slot->gid(), glyph_attr))); + } +ENDOP + +STARTOP(temp_copy) + slotref newSlot = seg.newSlot(); + if (!newSlot || !is) DIE; + int16 *tempUserAttrs = newSlot->userAttrs(); + memcpy(newSlot, is, sizeof(Slot)); + memcpy(tempUserAttrs, is->userAttrs(), seg.numAttrs() * sizeof(uint16)); + newSlot->userAttrs(tempUserAttrs); + newSlot->markCopied(true); + *map = newSlot; +ENDOP + +STARTOP(band) + binop(&); +ENDOP + +STARTOP(bor) + binop(|); +ENDOP + +STARTOP(bnot) + *sp = ~*sp; +ENDOP + +STARTOP(setbits) + declare_params(4); + const uint16 m = uint16(param[0]) << 8 + | uint8(param[1]); + const uint16 v = uint16(param[2]) << 8 + | uint8(param[3]); + *sp = ((*sp) & ~m) | v; +ENDOP + +STARTOP(set_feat) + declare_params(2); + const unsigned int feat = uint8(param[0]); + const int slot_ref = int8(param[1]); + slotref slot = slotat(slot_ref); + if (slot) + { + uint8 fid = seg.charinfo(slot->original())->fid(); + seg.setFeature(fid, feat, pop()); + } +ENDOP diff --git a/thirdparty/graphite/src/json.cpp b/thirdparty/graphite/src/json.cpp new file mode 100644 index 00000000000..25f2190f71a --- /dev/null +++ b/thirdparty/graphite/src/json.cpp @@ -0,0 +1,147 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +// JSON debug logging +// Author: Tim Eves + +#if !defined GRAPHITE2_NTRACING + +#include +#include +#include "inc/json.h" + +#if defined(_MSC_VER) +#define FORMAT_INTMAX "%lli" +#define FORMAT_UINTMAX "%llu" +#else +#define FORMAT_INTMAX "%ji" +#define FORMAT_UINTMAX "%ju" +#endif + +using namespace graphite2; + +namespace +{ + enum + { + seq = ',', + obj='}', member=':', empty_obj='{', + arr=']', empty_arr='[' + }; +} + +const std::nullptr_t json::null = nullptr; + +inline +void json::context(const char current) throw() +{ + fprintf(_stream, "%c", *_context); + indent(); + *_context = current; +} + + +void json::indent(const int d) throw() +{ + if (*_context == member || (_flatten && _flatten < _context)) + fputc(' ', _stream); + else + fprintf(_stream, "\n%*s", 4*int(_context - _contexts + d), ""); +} + + +inline +void json::push_context(const char prefix, const char suffix) throw() +{ + assert(_context - _contexts < ptrdiff_t(sizeof _contexts)); + + if (_context == _contexts) + *_context = suffix; + else + context(suffix); + *++_context = prefix; +} + + +void json::pop_context() throw() +{ + assert(_context > _contexts); + + if (*_context == seq) indent(-1); + else fputc(*_context, _stream); + + fputc(*--_context, _stream); + if (_context == _contexts) fputc('\n', _stream); + fflush(_stream); + + if (_flatten >= _context) _flatten = 0; + *_context = seq; +} + + +// These four functions cannot be inlined as pointers to these +// functions are needed for operator << (_context_t) to work. +void json::flat(json & j) throw() { if (!j._flatten) j._flatten = j._context; } +void json::close(json & j) throw() { j.pop_context(); } +void json::object(json & j) throw() { j.push_context('{', '}'); } +void json::array(json & j) throw() { j.push_context('[', ']'); } +void json::item(json & j) throw() +{ + while (j._context > j._contexts+1 && j._context[-1] != arr) + j.pop_context(); +} + + +json & json::operator << (json::string s) throw() +{ + const char ctxt = _context[-1] == obj ? *_context == member ? seq : member : seq; + context(ctxt); + fprintf(_stream, "\"%s\"", s); + if (ctxt == member) fputc(' ', _stream); + + return *this; +} + +json & json::operator << (json::number f) throw() +{ + context(seq); + if (std::numeric_limits::infinity() == f) + fputs("Infinity", _stream); + else if (-std::numeric_limits::infinity() == f) + fputs("-Infinity", _stream); + else if (std::numeric_limits::quiet_NaN() == f || + std::numeric_limits::signaling_NaN() == f) + fputs("NaN", _stream); + else + fprintf(_stream, "%g", f); + return *this; +} +json & json::operator << (json::integer d) throw() { context(seq); fprintf(_stream, FORMAT_INTMAX, intmax_t(d)); return *this; } +json & json::operator << (json::integer_u d) throw() { context(seq); fprintf(_stream, FORMAT_UINTMAX, uintmax_t(d)); return *this; } +json & json::operator << (json::boolean b) throw() { context(seq); fputs(b ? "true" : "false", _stream); return *this; } +json & json::operator << (std::nullptr_t) throw() { context(seq); fputs("null",_stream); return *this; } + +#endif diff --git a/thirdparty/harfbuzz/AUTHORS b/thirdparty/harfbuzz/AUTHORS new file mode 100644 index 00000000000..83c0c66f99e --- /dev/null +++ b/thirdparty/harfbuzz/AUTHORS @@ -0,0 +1,14 @@ +Behdad Esfahbod +David Corbett +David Turner +Ebrahim Byagowi +Garret Rieger +Jonathan Kew +Khaled Hosny +Lars Knoll +Martin Hosken +Owen Taylor +Roderick Sheeter +Roozbeh Pournader +Simon Hausmann +Werner Lemberg diff --git a/thirdparty/harfbuzz/COPYING b/thirdparty/harfbuzz/COPYING new file mode 100644 index 00000000000..57343164f21 --- /dev/null +++ b/thirdparty/harfbuzz/COPYING @@ -0,0 +1,38 @@ +HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. +For parts of HarfBuzz that are licensed under different licenses see individual +files names COPYING in subdirectories where applicable. + +Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc. +Copyright © 2018,2019,2020 Ebrahim Byagowi +Copyright © 2019,2020 Facebook, Inc. +Copyright © 2012 Mozilla Foundation +Copyright © 2011 Codethink Limited +Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) +Copyright © 2009 Keith Stribley +Copyright © 2009 Martin Hosken and SIL International +Copyright © 2007 Chris Wilson +Copyright © 2006 Behdad Esfahbod +Copyright © 2005 David Turner +Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. +Copyright © 1998-2004 David Turner and Werner Lemberg + +For full copyright notices consult the individual files in the package. + + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/thirdparty/harfbuzz/NEWS b/thirdparty/harfbuzz/NEWS new file mode 100644 index 00000000000..f211a3781c9 --- /dev/null +++ b/thirdparty/harfbuzz/NEWS @@ -0,0 +1,2412 @@ +Overview of changes leading to 2.7.2 +Saturday, August 29, 2020 +==================================== +- Fix a regression in the previous release that caused a crash with Kaithi. +- More OOM fixes. + + +Overview of changes leading to 2.7.1 +Thursday, August 13, 2020 +==================================== +- ot-funcs now handles variable empty glyphs better when hvar/vvar isn't present. +- Reverted a GDEF processing regression. +- A couple of fixes to handle OOM better. + + +Overview of changes leading to 2.7.0 +Saturday, July 25, 2020 +==================================== +- Use an implementation for round that always rounds up, some minor fluctuations + are expected on var font specially when hb-ot callback is used. +- Fix an AAT's `kerx` issue on broken rendering of Devanagari Sangam MN. +- Remove AAT's `lcar` table support from _get_ligature_carets API, not even much + use on macOS installed fonts (only two files). GDEF support is the recommended + one and expected to work properly after issues fixed two releases ago. +- Minor memory fixes to handle OOM better specially in hb-ft. +- Minor .so files versioning scheme change and remove stable/unstable scheme + differences, was never used in practice (always default to stable scheme). +- We are now suggesting careful packaging of the library using meson, + https://github.com/harfbuzz/harfbuzz/wiki/Notes-on-migration-to-meson + for more information. +- Distribution package URL is changed, either use GitHub generated tarballs, + `https://github.com/harfbuzz/harfbuzz/archive/$pkgver.tar.gz` + or, even more preferably use commit hash of the release and git checkouts like, + `git+https://github.com/harfbuzz/harfbuzz#commit=$commit` + + +Overview of changes leading to 2.6.8 +Monday, June 22, 2020 +==================================== +- New API to fetch glyph alternates from GSUB table. +- hb-coretext build fix for macOS < 10.10. +- Meson build fixes, cmake port removal is postponed but please prepare for + it and give us feedback. + Autotools is still our main build system however please consider + experimenting with meson also for packaging the library. +- New API: ++hb_ot_layout_lookup_get_glyph_alternates() + + +Overview of changes leading to 2.6.7 +Wednesday, June 3, 2020 +==================================== +- Update to Unicode 13.0.0. +- Fix hb_ot_layout_get_ligature_carets for fonts without lcar table, it was + completely broken for all the other fonts since 2.1.2. +- As a part of our migration to meson, this release will be the last one + to provide cmake port files but autotools still is our main build system. + There is a possibility that the next version or the after be released + using meson. + + +Overview of changes leading to 2.6.6 +Tuesday, May 12, 2020 +==================================== +- A fix in AAT kerning for Geeza Pro. +- Better support for resource fork fonts on macOS. + + +Overview of changes leading to 2.6.5 +Friday, April 17, 2020 +==================================== +- Add experimental meson build system. Autotools is still the primary + and supported build system. +- AAT is now always preferred for horizontal scripts when both AAT and OT + layout tables exist at the same time. +- Subsetter improvements. +- New API: ++hb_ft_font_lock_face() ++hb_ft_font_unlock_face() + + +Overview of changes leading to 2.6.4 +Monday, October 29, 2019 +==================================== +- Small bug fix. +- Build fixes. + + +Overview of changes leading to 2.6.3 +Monday, October 28, 2019 +==================================== +- Misc small fixes, mostly to build-related issues. +- New API: ++hb_font_get_nominal_glyphs() + + +Overview of changes leading to 2.6.2 +Monday, September 30, 2019 +==================================== +- Misc small fixes, mostly to build-related issues. + + +Overview of changes leading to 2.6.1 +Thursday, August 22, 2019 +==================================== +- Fix regression with hb_font_create_sub_font scaling introduced in 2.6.0. +- Change interpretation of font PTEM size / CoreText font size handling. + See https://github.com/harfbuzz/harfbuzz/pull/1484 +- hb-ot-font: Prefer symbol cmap subtable if present. +- Apply 'dist'/'abvm'/'blwm' features to all scripts. +- Drop experimental DirectWrite API. + + +Overview of changes leading to 2.6.0 +Tuesday, August 13, 2019 +==================================== +- New OpenType metrics, baseline, and metadata table access APIs. +- New API to set font variations to a named-instance. +- New hb-gdi.h header and API for creating hb_face_t from HFONT. +- Amalgam: Provide a single-file harfbuzz.cc file for easier alternate building. +- More size-reduction configurable options, enabled by HB_TINY. +- New API: ++hb_font_set_var_named_instance() ++hb_gdi_face_create() ++hb_ot_layout_baseline_tag_t ++hb_ot_layout_get_baseline() ++hb_ot_meta_tag_t ++hb_ot_meta_get_entry_tags() ++hb_ot_meta_reference_entry() ++hb_ot_metrics_tag_t ++hb_ot_metrics_get_position() ++hb_ot_metrics_get_variation() ++hb_ot_metrics_get_x_variation() ++hb_ot_metrics_get_y_variation() + + +Overview of changes leading to 2.5.3 +Wednesday, June 26, 2019 +==================================== +- Fix UCD script data for Unicode 10+ scripts. This was broken since 2.5.0. +- More optimizations for HB_TINY. + + +Overview of changes leading to 2.5.2 +Thursday, June 20, 2019 +==================================== +- More hb-config.hh facilities to shrink library size, namely when built as + HB_TINY. +- New documentation of custom configurations in CONFIG.md. +- Fix build on gcc 4.8. That's supported again. +- Universal Shaping Engine improvements thanks to David Corbett. +- API Changes: Undeprecate some horizontal-kerning API and re-enable in hb-ft, + such that Type1 fonts will continue kerning. + + +Overview of changes leading to 2.5.1 +Friday, May 31, 2019 +==================================== +- Fix build with various versions of Visual Studio. +- Improved documentation, thanks to Nathan Willis. +- Bugfix in subsetting glyf table. +- Improved scripts for cross-compiling for Windows using mingw. +- Rename HB_MATH_GLYPH_PART_FLAG_EXTENDER to HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER. + A deprecated macro is added for backwards-compatibility. + + +Overview of changes leading to 2.5.0 +Friday, May 24, 2019 +==================================== +- This release does not include much functional changes, but includes major internal + code-base changes. We now require C++11. Support for gcc 4.8 and earlier has been + dropped. +- New hb-config.hh facility for compiling smaller library for embedded and web usecases. +- New Unicode Character Databse implementation that is half the size of previously-used + UCDN. +- Subsetter improvements. +- Improved documentation, thanks to Nathan Willis. +- Misc shaping fixes. + + +Overview of changes leading to 2.4.0 +Monday, March 25, 2019 +==================================== +- Unicode 12. +- Misc fixes. +- Subsetter improvements. +- New API: +HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE +hb_directwrite_face_create() + + +Overview of changes leading to 2.3.1 +Wednesday, January 30, 2019 +==================================== +- AAT bug fixes. +- Misc internal housekeeping cleanup. + + +Overview of changes leading to 2.3.0 +Thursday, December 20, 2018 +==================================== +- Fix regression on big-endian architectures. Ouch! +- Misc bug and build fixes. +- Fix subsetting of simple GSUB/GDEF. +- Merge CFF / CFF2 support contributed by Adobe. This mostly involves + the subsetter, but also get_glyph_extents on CFF fonts. + +New API in hb-aat.h: ++hb_aat_layout_has_substitution() ++hb_aat_layout_has_positioning() ++hb_aat_layout_has_tracking() + + +Overview of changes leading to 2.2.0 +Thursday, November 29, 2018 +==================================== +- Misc shaping bug fixes. +- Add font variations named-instance API. +- Deprecate font variations axis enumeration API and add replacement. +- AAT shaping improvements: + o Fixed 'kern' table Format 2 implementation. + o Implement 'feat' table API for feature detection. + o Blacklist 'GSUB' table of fonts from 'MUTF' foundry that also have 'morx'. + +New API: ++hb_aat_layout_feature_type_t ++hb_aat_layout_feature_selector_t ++hb_aat_layout_get_feature_types() ++hb_aat_layout_feature_type_get_name_id ++hb_aat_layout_feature_selector_info_t ++HB_AAT_LAYOUT_NO_SELECTOR_INDEX ++hb_aat_layout_feature_type_get_selector_infos() ++hb_ot_var_axis_flags_t ++hb_ot_var_axis_info_t ++hb_ot_var_get_axis_infos() ++hb_ot_var_find_axis_info() ++hb_ot_var_get_named_instance_count() ++hb_ot_var_named_instance_get_subfamily_name_id() ++hb_ot_var_named_instance_get_postscript_name_id() ++hb_ot_var_named_instance_get_design_coords() + +Deprecated API: ++HB_OT_VAR_NO_AXIS_INDEX ++hb_ot_var_axis_t ++hb_ot_var_get_axes() ++hb_ot_var_find_axis() + + +Overview of changes leading to 2.1.3 +Friday, November 16, 2018 +==================================== +- Fix AAT 'mort' shaping, which was broken in 2.1.2 + + +Overview of changes leading to 2.1.2 +Friday, November 16, 2018 +==================================== +- Various internal changes. +- AAT shaping improvements: + o Implement kern table Format 1 state-machine-based kerning. + o Implement cross-stream kerning (cursive positioning, etc). + o Ignore emptyish GSUB tables (zero scripts) if morx present. + o Don't apply GPOS if morx is being applied. Matches Apple. + + +-Overview of changes leading to 2.1.1 +Monday, November 5, 2018 +==================================== +- AAT improvements: + o Implement 'mort' table. + o Implement 'kern' subtables Format 1 and Format 3. + + +Overview of changes leading to 2.1.0 +Tuesday, October 30, 2018 +==================================== +- AAT shaping improvements: + o Allow user controlling AAT features, for whole buffer only currently. + o Several 'morx' fixes. + o Implement tuple-kerns in 'kerx'; Fixes kerning with Apple default + San Francisco fonts. +- Support for color fonts: + o COLR/CPAL API to fetch color layers. + o SVG table to fetch SVG documents. + o CBDT/sbix API to fetch PNG images. +- New 'name' table API. +- hb-ot-font now uses 'VORG' table to correctly position CFF glyphs + in vertical layout. +- Various fuzzer-found bug fixes. + +Changed API: + +A type and a macro added in 2.0.0 were renamed: + +hb_name_id_t -> hb_ot_name_id_t +HB_NAME_ID_INVALID -> HB_OT_NAME_ID_INVALID + +New API: + ++hb_color_t ++HB_COLOR ++hb_color_get_alpha() ++hb_color_get_red() ++hb_color_get_green() ++hb_color_get_blue() ++hb_ot_color_has_palettes() ++hb_ot_color_palette_get_count() ++hb_ot_color_palette_get_name_id() ++hb_ot_color_palette_color_get_name_id() ++hb_ot_color_palette_flags_t ++hb_ot_color_palette_get_flags() ++hb_ot_color_palette_get_colors() ++hb_ot_color_has_layers() ++hb_ot_color_layer_t ++hb_ot_color_glyph_get_layers() ++hb_ot_color_has_svg() ++hb_ot_color_glyph_reference_svg() ++hb_ot_color_has_png() ++hb_ot_color_glyph_reference_png() + ++hb_ot_name_id_t ++HB_OT_NAME_ID_INVALID ++HB_OT_NAME_ID_COPYRIGHT ++HB_OT_NAME_ID_FONT_FAMILY ++HB_OT_NAME_ID_FONT_SUBFAMILY ++HB_OT_NAME_ID_UNIQUE_ID ++HB_OT_NAME_ID_FULL_NAME ++HB_OT_NAME_ID_VERSION_STRING ++HB_OT_NAME_ID_POSTSCRIPT_NAME ++HB_OT_NAME_ID_TRADEMARK ++HB_OT_NAME_ID_MANUFACTURER ++HB_OT_NAME_ID_DESIGNER ++HB_OT_NAME_ID_DESCRIPTION ++HB_OT_NAME_ID_VENDOR_URL ++HB_OT_NAME_ID_DESIGNER_URL ++HB_OT_NAME_ID_LICENSE ++HB_OT_NAME_ID_LICENSE_URL ++HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY ++HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY ++HB_OT_NAME_ID_MAC_FULL_NAME ++HB_OT_NAME_ID_SAMPLE_TEXT ++HB_OT_NAME_ID_CID_FINDFONT_NAME ++HB_OT_NAME_ID_WWS_FAMILY ++HB_OT_NAME_ID_WWS_SUBFAMILY ++HB_OT_NAME_ID_LIGHT_BACKGROUND ++HB_OT_NAME_ID_DARK_BACKGROUND ++HB_OT_NAME_ID_VARIATIONS_PS_PREFIX ++hb_ot_name_entry_t ++hb_ot_name_list_names() ++hb_ot_name_get_utf8() ++hb_ot_name_get_utf16() ++hb_ot_name_get_utf32() + + +Overview of changes leading to 2.0.2 +Saturday, October 20, 2018 +==================================== +- Fix two minor memory access issues in AAT tables. + + +Overview of changes leading to 2.0.1 +Friday, October 19, 2018 +==================================== +- Fix hb-version.h reported release version that went wrong (1.8.0) + with previous release. +- Fix extrapolation in 'trak' table. +- Fix hb-font infinite-recursion issue with some font funcs and + subclassed fonts. +- Implement variation-kerning format in kerx table, although without + variation. +- Fix return value of hb_map_is_empty(). + + +Overview of changes leading to 2.0.0 +Thursday, October 18, 2018 +==================================== +- Added AAT shaping support (morx/kerx/trak). + Automatically used if GSUB/GPOS are not available respectively. + Set HB_OPTIONS=aat env var to have morx/kerx preferred over + GSUB/GPOS. +- Apply TrueType kern table internally, instead of relying on + hb_font_t callbacks. +- Khmer shaper significantly rewritten to better match Uniscribe. +- Indic3 tags ('dev3', etc) are passed to USE shaper. +- .dfont Mac font containers implemented. +- Script- and language-mapping revamped to better use BCP 47. +- Misc USE and Indic fixes. +- Misc everything fixes. +- Too many things to list. Biggest release since 0.9.1, with + over 500 commits in just over 5 weeks! Didn't intend it to + be a big release. Just happened to become. +- hb-ft now locks underlying FT_Face during use. + +API changes: + +- Newly-created hb_font_t's now have our internal "hb-ot-font" + callbacks set on them, so they should work out of the box + without any callbacks set. If callbacks are set, everything + is back to what it was before, the fallback callbacks are + null. If you to get the internal implementation modified, + sub_font it. + +- New hb_font_funcs_set_nominal_glyphs_func() allows speeding + up character to glyph mapping. + +New API: ++HB_FEATURE_GLOBAL_START ++HB_FEATURE_GLOBAL_END ++hb_buffer_set_invisible_glyph() ++hb_buffer_get_invisible_glyph() ++hb_font_funcs_set_nominal_glyphs_func() ++hb_ot_layout_table_select_script() ++hb_ot_layout_script_select_language() ++hb_ot_layout_feature_get_name_ids() ++hb_ot_layout_feature_get_characters() ++hb_name_id_t ++HB_NAME_ID_INVALID ++HB_OT_MAX_TAGS_PER_SCRIPT ++hb_ot_tags_from_script_and_language() ++hb_ot_tags_to_script_and_language() + +Deprecated API: +-hb_font_funcs_set_glyph_func() +-hb_unicode_eastasian_width_func_t +-hb_unicode_funcs_set_eastasian_width_func() +-hb_unicode_eastasian_width() +-hb_unicode_decompose_compatibility_func_t +-HB_UNICODE_MAX_DECOMPOSITION_LEN +-hb_unicode_funcs_set_decompose_compatibility_func() +-hb_unicode_decompose_compatibility() +-hb_font_funcs_set_glyph_h_kerning_func() +-hb_font_funcs_set_glyph_v_kerning_func() +-hb_font_get_glyph_h_kerning() +-hb_font_get_glyph_v_kerning() +-hb_font_get_glyph_kerning_for_direction() +-hb_ot_layout_table_choose_script() +-hb_ot_layout_script_find_language() +-hb_ot_tags_from_script() +-hb_ot_tag_from_language() + + +Overview of changes leading to 1.9.0 +Monday, September 10, 2018 +==================================== +- Added 'cmap' API to hb_face_t. +- Face-builder API. +- hb-ot-font re-creation should be much leaner now, as the + font tables it uses are cached on hb_face_t now. +- Internal source header file name changes: + hb-*-private.hh is renamed to hb-*.hh. + +New API: ++HB_UNICODE_MAX ++hb_face_collect_unicodes() ++hb_face_collect_variation_selectors() ++hb_face_collect_variation_unicodes() ++hb_face_builder_create() ++hb_face_builder_add_table() + + +Overview of changes leading to 1.8.8 +Tuesday, August 14, 2018 +==================================== +- Fix hb-icu crash on architectures where compare_exchange_weak() can + fail falsely. This bug was introduced in 1.8.4. + https://bugs.chromium.org/p/chromium/issues/detail?id=873568 +- More internal refactoring of atomic operations and singletons. +- API changes: + The following functions do NOT reference their return value before + returning: + * hb_unicode_funcs_get_default() + * hb_glib_get_unicode_funcs() + * hb_icu_get_unicode_funcs() + This is consistent with their naming ("get", instead of "reference") + as well as how they are used in the wild (ie. no one calls destroy() + on their return value.) + + +Overview of changes leading to 1.8.7 +Wednesday, August 8, 2018 +==================================== +- Fix assertion failure with GDEF-blacklisted fonts. + + +Overview of changes leading to 1.8.6 +Tuesday, August 7, 2018 +==================================== +- Internal code shuffling. +- New API to speed up getting advance widths for implementations + that have heavy overhead in get_h_advance callback: ++hb_font_funcs_set_glyph_h_advances_func ++hb_font_funcs_set_glyph_v_advances_func ++hb_font_get_glyph_advances_for_direction ++hb_font_get_glyph_h_advances ++hb_font_get_glyph_h_advances_func_t ++hb_font_get_glyph_v_advances ++hb_font_get_glyph_v_advances_func_t + + +Overview of changes leading to 1.8.5 +Wednesday, August 1, 2018 +==================================== +- Major Khmer shaper improvements to better match Microsoft. +- Indic bug fixes. +- Internal improvements to atomic operations. + + +Overview of changes leading to 1.8.4 +Tuesday, July 17, 2018 +==================================== +- Fix build on non-C++11. +- Use C++-style GCC atomics and C++11 atomics. + + +Overview of changes leading to 1.8.3 +Wednesday, July 11, 2018 +==================================== +- A couple of Indic / USE bug fixes. +- Disable vectorization, as it was causing unaligned access bus error on + certain 32bit architectures. + + +Overview of changes leading to 1.8.2 +Tuesday, July 3, 2018 +==================================== +- Fix infinite loop in Khmer shaper. +- Improve hb_blob_create_from_file() for streams. + + +Overview of changes leading to 1.8.1 +Tuesday, June 12, 2018 +==================================== +- Fix hb-version.h file generation; last two releases went out with wrong ones. +- Add correctness bug in hb_set_t operations, introduced in 1.7.7. +- Remove HB_SUBSET_BUILTIN build option. Not necessary. + + +Overview of changes leading to 1.8.0 +Tuesday, June 5, 2018 +==================================== +- Update to Unicode 11.0.0. + + +Overview of changes leading to 1.7.7 +Tuesday, June 5, 2018 +==================================== +- Lots of internal changes, but not yet exposed externally. +- All HarfBuzz objects are significantly smaller in size now. +- Sinhala: Position repha on top of post-consonant, not base. + This better matches Windows 10 behavior, which was changed + from previous Windows versions. +- New build options: + o New cpp macro HB_NO_ATEXIT + o New cpp macro HB_SUBSET_BUILTIN +- Significant libharfbuzz-subset changes. API subject to change. +- New API in libharfbuzz: + ++hb_blob_create_from_file() ++hb_face_count() + +A hashmap implementation: ++hb-map.h ++HB_MAP_VALUE_INVALID ++hb_map_t ++hb_map_create() ++hb_map_get_empty() ++hb_map_reference() ++hb_map_destroy() ++hb_map_set_user_data() ++hb_map_get_user_data() ++hb_map_allocation_successful() ++hb_map_clear() ++hb_map_is_empty() ++hb_map_get_population() ++hb_map_set() ++hb_map_get() ++hb_map_del() ++hb_map_has() + + +Overview of changes leading to 1.7.6 +Wednesday, March 7, 2018 +==================================== + +- Fix to hb_set_t binary operations. Ouch. +- New experimental harfbuzz-subset library. All of hb-subset.h + is experimental right now and API WILL change. + +- New API: +hb_blob_copy_writable_or_fail() +HB_OT_TAG_BASE +hb_set_previous() +hb_set_previous_range() + + +Overview of changes leading to 1.7.5 +Tuesday, January 30, 2018 +==================================== + +- Separate Khmer shaper from Indic. +- First stab at AAT morx. Not hooked up. +- Misc bug fixes. + + +Overview of changes leading to 1.7.4 +Wednesday, December 20, 2017 +==================================== + +- Fix collect_glyphs() regression caused by hb_set_t changes. + + +Overview of changes leading to 1.7.3 +Monday, December 18, 2017 +==================================== + +- hb_set_t performance tuning and optimizations. +- Speed up collect_glyphs() and reject garbage data. +- In hb_coretext_font_create() set font point-size (ptem). +- Misc fixes. + + +Overview of changes leading to 1.7.2 +Monday, December 4, 2017 +==================================== + +- Optimize hb_set_add_range(). +- Misc fixes. +- New API: +hb_coretext_font_create() + + +Overview of changes leading to 1.7.1 +Tuesday, November 14, 2017 +==================================== + +- Fix atexit object destruction regression. +- Fix minor integer-overflow. + + +Overview of changes leading to 1.7.0 +Monday, November 13, 2017 +==================================== + +- Minor Indic fixes. +- Implement kerning and glyph names in hb-ot-font. +- Various DSO optimization re .data and .bss sizes. +- Make C++11 optional; build fixes. +- Mark all other backends "unsafe-to-break". +- Graphite fix. + + +Overview of changes leading to 1.6.3 +Thursday, October 26th, 2017 +==================================== + +- Fix hb_set_t some more. Should be solid now. +- Implement get_glyph_name() for hb-ot-font. +- Misc fixes. + + +Overview of changes leading to 1.6.2 +Monday, October 23nd, 2017 +==================================== + +- Yesterday's release had a bad crasher; don't use it. That's what + happens when one works on Sunday... + https://github.com/harfbuzz/harfbuzz/issues/578 +- Build fixes for FreeBSD and Chrome Android. + + +Overview of changes leading to 1.6.1 +Sunday, October 22nd, 2017 +==================================== + +- Don't skip over COMBINING GRAPHEME JOINER when ligating, etc. + To be refined: https://github.com/harfbuzz/harfbuzz/issues/554 +- Faster hb_set_t implementation. +- Don't use deprecated ICU API. +- Fix undefined-behavior in Myanmar shaper, introduced in 1.6.0 +- Deprecated API: + hb_set_invert() + + +Overview of changes leading to 1.6.0 +Friday, October the 13th, 2017 +==================================== + +- Update to Unicode 10. + +- Various Indic and Universal Shaping Engine fixes as a result of + HarfBuzz Hackfest with Jonathan Kew at Web Engines Hackfest at + the Igalia offices in A Coruña, Spain. Thanks Igalia for having + us! + +- Implement Unicode Arabic Mark Ordering Algorithm UTR#53. + +- Implement optical sizing / tracking in CoreText backend, using + new API hb_font_set_ptem(). + +- Allow notifying hb_font_t that underlying FT_Face changed sizing, + using new API hb_ft_font_changed(). + +- More Graphite backend RTL fixes. + +- Fix caching of variable font shaping plans. + +- hb-view / hb-shape now accept following new arguments: + + o --unicodes: takes a list of hex numbers that represent Unicode + codepoints. + +New API: ++hb_face_get_table_tags() ++hb_font_set_ptem() ++hb_font_get_ptem() ++hb_ft_font_changed() + + +Overview of changes leading to 1.5.1 +Tuesday, September 5, 2017 +==================================== + +- Fix "unsafe-to-break" in fallback shaping and other corner cases. + All our tests pass with --verify now, meaning unsafe-to-break API + works as expected. +- Add --unicodes to hb-view / hb-shape. +- [indic] Treat Consonant_With_Stacker as consonant. This will need + further tweaking. +- hb_buffer_diff() tweaks. + + +Overview of changes leading to 1.5.0 +Wednesday, August 23, 2017 +==================================== + +- Misc new API, for appending a buffer to another, and for comparing + contents of two buffers for types of differences. + +- New "unsafe-to-break" API. Can be used to speed up reshaping + in line-breaking situations. Essentially, after shaping, it returns + positions in the input string (some of the cluster boundaries) that + are "safe to break" in that if the text is segmented at that position + and two sides reshaped and concatenated, the shaping result is + exactly the same as shaping the text in one piece. + + hb-view and hb-shape and hb-shape now take --verify, which verifies + the above property. + + Some corner cases of the implementation are still not quite working. + Those will be fixed in subsequent releases. + +- New API: + +hb_buffer_append() + +hb_glyph_flags_t +HB_GLYPH_FLAG_UNSAFE_TO_BREAK +HB_GLYPH_FLAG_DEFINED +hb_glyph_info_get_glyph_flags() + +HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS + +hb_buffer_diff_flags_t +HB_BUFFER_DIFF_FLAG_EQUAL +HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH +HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH +HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT +HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT +HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH +HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH +HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH +HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH +hb_buffer_diff + + +Overview of changes leading to 1.4.8 +Tuesday, August 8, 2017 +==================================== + +- Major fix to avar table handling. +- Rename hb-shape --show-message to --trace. +- Build fixes. + + +Overview of changes leading to 1.4.7 +Tuesday, July 18, 2017 +==================================== + +- Multiple Indic, Tibetan, and Cham fixes. +- CoreText: Allow disabling kerning. +- Adjust Arabic feature order again. +- Misc build fixes. + + +Overview of changes leading to 1.4.6 +Sunday, April 23, 2017 +==================================== + +- Graphite2: Fix RTL positioning issue. +- Backlist GDEF of more versions of Padauk and Tahoma. +- New, experimental, cmake alternative build system. + + +Overview of changes leading to 1.4.5 +Friday, March 10, 2017 +==================================== + +- Revert "Fix Context lookup application when moving back after a glyph..." + This introduced memory access problems. To be fixed properly soon. + + +Overview of changes leading to 1.4.4 +Sunday, March 5, 2017 +==================================== + +- Fix Context lookup application when moving back after a glyph deletion. +- Fix buffer-overrun in Bengali. + + +Overview of changes leading to 1.4.3 +Saturday, February 25, 2017 +==================================== + +- Route Adlam script to Arabic shaper. +- Misc fixes. +- New API: + hb_font_set_face() +- Deprecate API: + hb_graphite2_font_get_gr_font() + + +Overview of changes leading to 1.4.2 +Monday, January 23, 2017 +==================================== + +- Implement OpenType Font Variation tables avar/fvar/HVAR/VVAR. +- hb-shape and hb-view now accept --variations. +- New API: + +hb_variation_t +hb_variation_from_string() +hb_variation_to_string() + +hb_font_set_variations() +hb_font_set_var_coords_design() +hb_font_get_var_coords_normalized() + +hb-ot-var.h: +hb_ot_var_axis_t +hb_ot_var_has_data() +hb_ot_var_get_axis_count() +hb_ot_var_get_axes() +hb_ot_var_find_axis() +hb_ot_var_normalize_variations() +hb_ot_var_normalize_coords() + +- MVAR to be implemented later. Access to named instances to be + implemented later as well. + +- Misc fixes. + + +Overview of changes leading to 1.4.1 +Thursday, January 5, 2017 +==================================== + +- Always build and use UCDN for Unicode data by default. + Reduces dependence on version of Unicode data in glib, + specially in the Windows bundles we are shipping, which + have very old glib. + + +Overview of changes leading to 1.4.0 +Thursday, January 5, 2017 +==================================== + +- Merged "OpenType GX" branch which adds core of support for + OpenType 1.8 Font Variations. To that extent, the relevant + new API is: + +New API: +hb_font_set_var_coords_normalized() + + with supporting API: + +New API: +HB_OT_LAYOUT_NO_VARIATIONS_INDEX +hb_ot_layout_table_find_feature_variations() +hb_ot_layout_feature_with_variations_get_lookups() +hb_shape_plan_create2() +hb_shape_plan_create_cached2() + + Currently variations in GSUB/GPOS/GDEF are fully supported, + and no other tables are supported. In particular, fvar/avar + are NOT supported, hence the hb_font_set_var_coords_normalized() + taking normalized coordinates. API to take design coordinates + will be added in the future. + + HVAR/VVAR/MVAR support will also be added to hb-ot-font in the + future. + +- Fix regression in GDEF glyph class processing. +- Add decompositions for Chakma, Limbu, and Balinese in USE shaper. +- Misc fixes. + + +Overview of changes leading to 1.3.4 +Monday, December 5, 2016 +==================================== + +- Fix vertical glyph origin in hb-ot-font. +- Implement CBDT/CBLC color font glyph extents in hb-ot-font. + + +Overview of changes leading to 1.3.3 +Wednesday, September 28, 2016 +==================================== + +- Implement parsing of OpenType MATH table. +New API: +HB_OT_TAG_MATH +HB_OT_MATH_SCRIPT +hb_ot_math_constant_t +hb_ot_math_kern_t +hb_ot_math_glyph_variant_t +hb_ot_math_glyph_part_flags_t +hb_ot_math_glyph_part_t +hb_ot_math_has_data +hb_ot_math_get_constant +hb_ot_math_get_glyph_italics_correction +hb_ot_math_get_glyph_top_accent_attachment +hb_ot_math_get_glyph_kerning +hb_ot_math_is_glyph_extended_shape +hb_ot_math_get_glyph_variants +hb_ot_math_get_min_connector_overlap +hb_ot_math_get_glyph_assembly + + +Overview of changes leading to 1.3.2 +Wednesday, September 27, 2016 +==================================== + +- Fix build of hb-coretext on older OS X versions. + + +Overview of changes leading to 1.3.1 +Wednesday, September 7, 2016 +==================================== + +- Blacklist bad GDEF of more fonts (Padauk). +- More CoreText backend crash fixes with OS X 10.9.5. +- Misc fixes. + + +Overview of changes leading to 1.3.0 +Thursday, July 21, 2016 +==================================== + +- Update to Unicode 9.0.0 +- Move Javanese from Indic shaper to Universal Shaping Engine. +- Allow MultipleSubst to delete a glyph (matching Windows engine). +- Update Universal Shaping Engine to latest draft from Microsoft. +- DirectWrite backend improvements. Note: this backend is for testing ONLY. +- CoreText backend improvements with unreachable fonts. +- Implement symbol fonts (cmap 3.0.0) in hb-ft and hb-ot-font. +- Blacklist bad GDEF of more fonts (Tahoma & others). +- Misc fixes. + + +Overview of changes leading to 1.2.7 +Monday, May 2, 2016 +==================================== + +- Blacklist another version of Times New Roman (Bold) Italic from Windows 7. +- Fix Mongolian Free Variation Selectors shaping with certain fonts. +- Fix Tibetan shorthand contractions shaping. +- Improved list of language tag mappings. +- Unbreak build on Windows CE. +- Make 'glyf' table loading lazy in hb-ot-font. + + +Overview of changes leading to 1.2.6 +Friday, April 8, 2016 +==================================== + +- Blacklist GDEF table of another set of Times New Roman (Bold) Italic. +- DirectWrite backend improvements. Note: DirectWrite backend is + exclusively for our internal testing and should NOT be used in any + production system whatsoever. + + +Overview of changes leading to 1.2.5 +Monday, April 4, 2016 +==================================== + +- Fix GDEF mark-filtering-set, which was broken in 1.2.3. + + +Overview of changes leading to 1.2.4 +Thursday, March 17, 2016 +==================================== + +- Synthesize GDEF glyph class for any glyph that does not have one in GDEF. + I really hope we don't discover broken fonts that shape badly with this + change. +- Misc build and other minor fixes. +- API changes: + - Added HB_NDEBUG. It's fine for production systems to define this to + disable high-overhead debugging checks. However, I also reduced the + overhead of those checks, so it's a non-issue right now. You can + forget it. Just not defining anything at all is fine. + + +Overview of changes leading to 1.2.3 +Thursday, February 25, 2016 +==================================== + +- Blacklist GDEF table of certain versions of Times New Roman (Bold) Italic, + due to bug in glyph class of ASCII double-quote character. This should + address "regression" introduced in 1.2.0 when we switched mark zeroing + in most shapers from BY_UNICODE_LATE to BY_GDEF_LATE. + This fourth release in a week should finally stablize things... + +- hb-ot-font's get_glyph() implementation saw some optimizations. Though, + might be really hard to measure in real-world situations. + +- Also, two rather small API changes: + +We now disable some time-consuming internal bookkeeping if built with NDEBUG +defined. This is a first time that we use NDEBUG to disable debug code. If +there exist production systems that do NOT want to enable NDEBUG, please let +me know and I'll add HB_NDEBUG. + +Added get_nominal_glyph() and get_variation_glyph() instead of get_glyph() + +New API: +- hb_font_get_nominal_glyph_func_t +- hb_font_get_variation_glyph_func_t +- hb_font_funcs_set_nominal_glyph_func() +- hb_font_funcs_set_variation_glyph_func() +- hb_font_get_nominal_glyph() +- hb_font_get_variation_glyph() + +Deprecated API: +- hb_font_get_glyph_func_t +- hb_font_funcs_set_glyph_func() + +Clients that implement their own font-funcs are encouraged to replace +their get_glyph() implementation with a get_nominal_glyph() and +get_variation_glyph() pair. The variation version can assume that +variation_selector argument is not zero. Old (deprecated) functions +will continue working indefinitely using internal gymnastics; it is +just more efficient to use the new functions. + + +Overview of changes leading to 1.2.2 +Wednesday, February 24, 2016 +==================================== + +- Fix regression with mark positioning with fonts that have + non-zero mark advances. This was introduced in 1.2.0 while + trying to make mark and cursive attachments to work together. + I have partially reverted that, so this version is much more + like what we had before. All clients who updated to 1.2.0 + should update to this version. + + +Overview of changes leading to 1.2.1 +Tuesday, February 23, 2016 +==================================== + +- CoreText: Fix bug with wrong scale if font scale was changed later. + https://github.com/libass/libass/issues/212 +- CoreText: Drastically speed up font initialization. +- CoreText: Fix tiny leak. +- Group ZWJ/ZWNJ with previous syllable under cluster-level=0. + https://github.com/harfbuzz/harfbuzz/issues/217 +- Add test/shaping/README.md about how to add tests to the suite. + + +Overview of changes leading to 1.2.0 +Friday, February 19, 2016 +==================================== + +- Fix various issues (hangs mostly) in case of memory allocation failure. +- Change mark zeroing types of most shapers from BY_UNICODE_LATE to + BY_GDEF_LATE. This seems to be what Uniscribe does. +- Change mark zeroing of USE shaper from NONE to BY_GDEF_EARLY. That's + what Windows does. +- Allow GPOS cursive connection on marks, and fix the interaction with + mark attachment. This work resulted in some changes to how mark + attachments work. See: + https://github.com/harfbuzz/harfbuzz/issues/211 + https://github.com/harfbuzz/harfbuzz/commit/86c68c7a2c971efe8e35b1f1bd99401dc8b688d2 +- Graphite2 shaper: improved negative advance handling (eg. Nastaliq). +- Add nmake-based build system for Windows. +- Minor speedup. +- Misc. improvements. + + +Overview of changes leading to 1.1.3 +Monday, January 11, 2016 +==================================== + +- Ported Indic shaper to Unicode 8.0 data. +- Universal Shaping Engine fixes. +- Speed up CoreText shaper when font fallback happens in CoreText. +- Documentation improvements, thanks to Khaled Hosny. +- Very rough directwrite shaper for testing, thanks to Ebrahim Byagowi. +- Misc bug fixes. +- New API: + + * Font extents: + hb_font_extents_t + hb_font_get_font_extents_func_t + hb_font_get_font_h_extents_func_t + hb_font_get_font_v_extents_func_t + hb_font_funcs_set_font_h_extents_func + hb_font_funcs_set_font_v_extents_func + hb_font_get_h_extents + hb_font_get_v_extents + hb_font_get_extents_for_direction + + * Buffer message (aka debug): + hb_buffer_message_func_t + hb_buffer_set_message_func() + Actual message protocol to be fleshed out later. + + +Overview of changes leading to 1.1.2 +Wednesday, November 26, 2015 +==================================== + +- Fix badly-broken fallback shaper that affected terminology. + https://github.com/harfbuzz/harfbuzz/issues/187 +- Fix y_scaling in Graphite shaper. +- API changes: + * An unset glyph_h_origin() function in font-funcs now (sensibly) + implies horizontal origin at 0,0. Ie, the nil callback returns + true instead of false. As such, implementations that have a + glyph_h_origin() that simply returns true, can remove that function + with HarfBuzz >= 1.1.2. This results in a tiny speedup. + + +Overview of changes leading to 1.1.1 +Wednesday, November 24, 2015 +==================================== + +- Build fixes, specially for hb-coretext. + + +Overview of changes leading to 1.1.0 +Wednesday, November 18, 2015 +==================================== + +- Implement 'stch' stretch feature for Syriac Abbreviation Mark. + https://github.com/harfbuzz/harfbuzz/issues/141 +- Disable use of decompose_compatibility() callback. +- Implement "shaping" of various Unicode space characters, even + if the font does not support them. + https://github.com/harfbuzz/harfbuzz/issues/153 +- If font does not support U+2011 NO-BREAK HYPHEN, fallback to + U+2010 HYPHEN. +- Changes resulting from libFuzzer continuous fuzzing: + * Reject font tables that need more than 8 edits, + * Bound buffer growth during shaping to 32x, + * Fix assertions and other issues at OOM / buffer max-growth. +- Misc fixes and optimizations. +- API changes: + * All fonts created with hb_font_create() now inherit from + (ie. have parent) hb_font_get_empty(). + + +Overview of changes leading to 1.0.6 +Thursday, October 15, 2015 +==================================== + +- Reduce max nesting level in OT lookups from 8 to 6. + Should not affect any real font as far as I know. +- Fix memory access issue in ot-font. +- Revert default load-flags of fonts created using hb_ft_font_create() + back to FT_LOAD_DEFAULT|FT_LOAD_NO_HINTING. This was changed in + last release (1.0.5), but caused major issues, so revert. + https://github.com/harfbuzz/harfbuzz/issues/143 + + +Overview of changes leading to 1.0.5 +Tuesday, October 13, 2015 +==================================== + +- Fix multiple memory access bugs discovered using libFuzzer. + https://github.com/harfbuzz/harfbuzz/issues/139 + Everyone should upgrade to this version as soon as possible. + We now have continuous fuzzing set up, to avoid issues like + these creeping in again. +- Misc fixes. + +- New API: + * hb_font_set_parent(). + * hb_ft_font_[sg]et_load_flags() + The default flags for fonts created using hb_ft_font_create() + has changed to default to FT_LOAD_DEFAULT now. Previously it + was defaulting to FT_LOAD_DFEAULT|FT_LOAD_NO_HINTING. + +- API changes: + * Fonts now default to units-per-EM as their scale, instead of 0. + * hb_font_create_sub_font() does NOT make parent font immutable + anymore. hb_font_make_immutable() does. + + +Overview of changes leading to 1.0.4 +Wednesday, September 30, 2015 +==================================== + +- Fix minor out-of-bounds read error. + + +Overview of changes leading to 1.0.3 +Tuesday, September 1, 2015 +==================================== + +- Start of user documentation, from Simon Cozens! +- Implement glyph_extents() for TrueType fonts in hb-ot-font. +- Improve GPOS cursive attachments with conflicting lookups. +- More fixes for cluster-level = 1. +- Uniscribe positioning fix. + + +Overview of changes leading to 1.0.2 +Wednesday, August 19, 2015 +==================================== + +- Fix shaping with cluster-level > 0. +- Fix Uniscribe backend font-size scaling. +- Declare dependencies in harfbuzz.pc. + FreeType is not declared though, to avoid bugs in pkg-config + 0.26 with recursive dependencies. +- Slightly improved debug infrastructure. More to come later. +- Misc build fixes. + + +Overview of changes leading to 1.0.1 +Monday, July 27, 2015 +==================================== + +- Fix out-of-bounds access in USE shaper. + + +Overview of changes leading to 1.0.0 +Sunday, July 26, 2015 +==================================== + +- Implement Universal Shaping Engine: + https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm + http://blogs.windows.com/bloggingwindows/2015/02/23/windows-shapes-the-worlds-languages/ +- Bump version to 1.0.0. The soname was NOT bumped. + + +Overview of changes leading to 0.9.42 +Thursday, July 26, 2015 +===================================== + +- New API to allow for retrieving finer-grained cluster + mappings if the client desires to handle them. Default + behavior is unchanged. +- Fix cluster merging when removing default-ignorables. +- Update to Unicode 8.0 +- hb-graphite2 fixes. +- Misc fixes. +- Removed HB_NO_MERGE_CLUSTERS hack. +- New API: + hb_buffer_cluster_level_t enum + hb_buffer_get_cluster_level() + hb_buffer_set_cluster_level() + hb-shape / hb-view --cluster-level + + +Overview of changes leading to 0.9.41 +Thursday, June 18, 2015 +===================================== + +- Fix hb-coretext with trailing whitespace in right-to-left. +- New API: hb_buffer_reverse_range(). +- Allow implementing atomic ops in config.h. +- Fix hb_language_t in language bindings. +- Misc fixes. + + +Overview of changes leading to 0.9.40 +Friday, March 20, 2015 +===================================== + +- Another hb-coretext crasher fix. Ouch! +- Happy Norouz! + + +Overview of changes leading to 0.9.39 +Wednesday, March 4, 2015 +===================================== + +- Critical hb-coretext fixes. +- Optimizations and refactoring; no functional change + expected. +- Misc build fixes. + + +Overview of changes leading to 0.9.38 +Friday, January 23, 2015 +===================================== + +- Fix minor out-of-bounds access in Indic shaper. +- Change New Tai Lue shaping engine from South-East Asian to default, + reflecting change in Unicode encoding model. +- Add hb-shape --font-size. Can take up to two numbers for separate + x / y size. +- Fix CoreText and FreeType scale issues with negative scales. +- Reject blobs larger than 2GB. This might break some icu-le-hb clients + that need security fixes. See: + http://www.icu-project.org/trac/ticket/11450 +- Avoid accessing font tables during face destruction, in casce rogue + clients released face data already. +- Fix up gobject-introspection a bit. Python bindings kinda working. + See README.python. +- Misc fixes. +- API additions: + hb_ft_face_create_referenced() + hb_ft_font_create_referenced() + + +Overview of changes leading to 0.9.37 +Wednesday, December 17, 2014 +===================================== + +- Fix out-of-bounds access in Context lookup format 3. +- Indic: Allow ZWJ/ZWNJ before syllable modifiers. + + +Overview of changes leading to 0.9.36 +Thursday, November 20, 2014 +===================================== + +- First time that three months went by without a release since + 0.9.2 was released on August 10, 2012! +- Fix performance bug in hb_ot_collect_glyphs(): + https://bugzilla.mozilla.org/show_bug.cgi?id=1090869 +- Add basic vertical-text support to hb-ot-font. +- Misc build fixes. + + +Overview of changes leading to 0.9.35 +Saturday, August 13, 2014 +===================================== + +- Fix major shape-plan caching bug when more than one shaper were + provided to hb_shape_full() (as exercised by XeTeX). + http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1246370.html +- Fix Arabic fallback shaping regression. This was broken in 0.9.32. +- Major hb-coretext fixes. That backend is complete now, including + respecing buffer direction and language, down to vertical writing. +- Build fixes for Windows CE. Should build fine now. +- Misc fixes: + Use atexit() only if it's safe to call from shared library + https://bugs.freedesktop.org/show_bug.cgi?id=82246 + Mandaic had errors in its Unicode Joining_Type + https://bugs.freedesktop.org/show_bug.cgi?id=82306 +- API changes: + + * hb_buffer_clear_contents() does not reset buffer flags now. + + After 763e5466c0a03a7c27020e1e2598e488612529a7, one doesn't + need to set flags for different pieces of text. The flags now + are something the client sets up once, depending on how it + actually uses the buffer. As such, don't clear it in + clear_contents(). + + I don't expect any changes to be needed to any existing client. + + +Overview of changes leading to 0.9.34 +Saturday, August 2, 2014 +===================================== + +- hb_feature_from_string() now accepts CSS font-feature-settings format. +- As a result, hb-shape / hb-view --features also accept CSS-style strings. + Eg, "'liga' off" is accepted now. +- Add old-spec Myanmar shaper: + https://bugs.freedesktop.org/show_bug.cgi?id=81775 +- Don't apply 'calt' in Hangul shaper. +- Fix mark advance zeroing for Hebrew shaper: + https://bugs.freedesktop.org/show_bug.cgi?id=76767 +- Implement Windows-1256 custom Arabic shaping. Only built on Windows, + and requires help from get_glyph(). Used by Firefox. + https://bugzilla.mozilla.org/show_bug.cgi?id=1045139 +- Disable 'liga' in vertical text. +- Build fixes. +- API changes: + + * Make HB_BUFFER_FLAG_BOT/EOT easier to use. + + Previously, we expected users to provide BOT/EOT flags when the + text *segment* was at paragraph boundaries. This meant that for + clients that provide full paragraph to HarfBuzz (eg. Pango), they + had code like this: + + hb_buffer_set_flags (hb_buffer, + (item_offset == 0 ? HB_BUFFER_FLAG_BOT : 0) | + (item_offset + item_length == paragraph_length ? + HB_BUFFER_FLAG_EOT : 0)); + + hb_buffer_add_utf8 (hb_buffer, + paragraph_text, paragraph_length, + item_offset, item_length); + + After this change such clients can simply say: + + hb_buffer_set_flags (hb_buffer, + HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT); + + hb_buffer_add_utf8 (hb_buffer, + paragraph_text, paragraph_length, + item_offset, item_length); + + Ie, HarfBuzz itself checks whether the segment is at the beginning/end + of the paragraph. Clients that only pass item-at-a-time to HarfBuzz + continue not setting any flags whatsoever. + + Another way to put it is: if there's pre-context text in the buffer, + HarfBuzz ignores the BOT flag. If there's post-context, it ignores + EOT flag. + + +Overview of changes leading to 0.9.33 +Tuesday, July 22, 2014 +===================================== + +- Turn off ARabic 'cswh' feature that was accidentally turned on. +- Add HB_TAG_MAX_SIGNED. +- Make hb_face_make_immutable() really make face immutable! +- Windows build fixes. + + +Overview of changes leading to 0.9.32 +Thursday, July 17, 2014 +===================================== + +- Apply Arabic shaping features in spec order exactly. +- Another fix for Mongolian free variation selectors. +- For non-Arabic scripts in Arabic shaper apply 'rlig' and 'calt' + together. +- Minor adjustment to U+FFFD logic. +- Fix hb-coretext build. + + +Overview of changes leading to 0.9.31 +Wednesday, July 16, 2014 +===================================== + +- Only accept valid UTF-8/16/32; we missed many cases before. +- Better shaping of invalid UTF-8/16/32. Falls back to + U+FFFD REPLACEMENT CHARACTER now. +- With all changes in this release, the buffer will contain fully + valid Unicode after hb_buffer_add_utf8/16/32 no matter how + broken the input is. This can be overridden though. See below. +- Fix Mongolian Variation Selectors for fonts without GDEF. +- Fix minor invalid buffer access. +- Accept zh-Hant and zh-Hans language tags. hb_ot_tag_to_language() + now uses these instead of private tags. +- Build fixes. +- New API: + * hb_buffer_add_codepoints(). This does what hb_buffer_add_utf32() + used to do, ie. no validity check on the input at all. add_utf32 + now replaces invalid Unicode codepoints with the replacement + character (see below). + * hb_buffer_set_replacement_codepoint() + * hb_buffer_get_replacement_codepoint() + Previously, in hb_buffer_add_utf8 and hb_buffer_add_utf16, when + we detected broken input, we replaced that with (hb_codepoint_t)-1. + This has changed to use U+FFFD now, but can be changed using these + new API. + + +Overview of changes leading to 0.9.30 +Wednesday, July 9, 2014 +===================================== + +- Update to Unicode 7.0.0: + * New scripts Manichaean and Psalter Pahlavi are shaped using + Arabic shaper. + * All the other new scripts to through the generic shaper for + now. +- Minor Indic improvements. +- Fix graphite2 backend cluster mapping [crasher!] +- API changes: + * New HB_SCRIPT_* values for Unicode 7.0 scripts. + * New function hb_ot_layout_language_get_required_feature(). +- Build fixes. + + +Overview of changes leading to 0.9.29 +Thursday, May 29, 2014 +===================================== + +- Implement cmap in hb-ot-font.h. No variation-selectors yet. +- Myanmar: Allow MedialYa+Asat. +- Various Indic fixes: + * Support most characters in Extended Devanagary and Vedic + Unicode blocks. + * Allow digits and a some punctuation as consonant placeholders. +- Build fixes. + + +Overview of changes leading to 0.9.28 +Monday, April 28, 2014 +===================================== + +- Unbreak old-spec Indic shaping. (bug 76705) +- Fix shaping of U+17DD and U+0FC6. +- Add HB_NO_MERGE_CLUSTERS build option. NOT to be enabled by default + for shipping libraries. It's an option for further experimentation + right now. When we are sure how to do it properly, we will add + public run-time API for the functionality. +- Build fixes. + + +Overview of changes leading to 0.9.27 +Tuesday, March 18, 2014 +===================================== + +- Don't use "register" storage class specifier +- Wrap definition of free_langs() with HAVE_ATEXIT +- Add coretext_aat shaper and hb_coretext_face_create() constructor +- If HAVE_ICU_BUILTIN is defined, use hb-icu Unicode callbacks +- Add Myanmar test case from OpenType Myanmar spec +- Only do fallback Hebrew composition if no GPOS 'mark' available +- Allow bootstrapping without gtk-doc +- Use AM_MISSING_PROG for ragel and git +- Typo in ucdn's Makefile.am +- Improve MemoryBarrier() implementation + + +Overview of changes leading to 0.9.26 +Thursday, January 30, 2014 +===================================== + +- Misc fixes. +- Fix application of 'rtlm' feature. +- Automatically apply frac/numr/dnom around U+2044 FRACTION SLASH. +- New header: hb-ot-shape.h +- Uniscribe: fix scratch-buffer accounting. +- Reorder Tai Tham SAKOT to after tone-marks. +- Add Hangul shaper. +- New files: + hb-ot-shape-complex-hangul.cc + hb-ot-shape-complex-hebrew.cc + hb-ot-shape-complex-tibetan.cc +- Disable 'cswh' feature in Arabic shaper. +- Coretext: better handle surrogate pairs. +- Add HB_TAG_MAX and _HB_SCRIPT_MAX_VALUE. + + +Overview of changes leading to 0.9.25 +Wednesday, December 4, 2013 +===================================== + +- Myanmar shaper improvements. +- Avoid font fallback in CoreText backend. +- Additional OpenType language tag mappiongs. +- More aggressive shape-plan caching. +- Build with / require automake 1.13. +- Build with libtool 2.4.2.418 alpha to support ppc64le. + + +Overview of changes leading to 0.9.24 +Tuesday, November 13, 2013 +===================================== + +- Misc compiler warning fixes with clang. +- No functional changes. + + +Overview of changes leading to 0.9.23 +Monday, October 28, 2013 +===================================== + +- "Udupi HarfBuzz Hackfest", Paris, October 14..18 2013. +- Fix (Chain)Context recursion with non-monotone lookup positions. +- Misc Indic bug fixes. +- New Javanese / Buginese shaping, similar to Windows 8.1. + + +Overview of changes leading to 0.9.22 +Thursday, October 3, 2013 +===================================== + +- Fix use-after-end-of-scope in hb_language_from_string(). +- Fix hiding of default_ignorables if font doesn't have space glyph. +- Protect against out-of-range lookup indices. + +- API Changes: + + * Added hb_ot_layout_table_get_lookup_count() + + +Overview of changes leading to 0.9.21 +Monday, September 16, 2013 +===================================== + +- Rename gobject-introspection library name from harfbuzz to HarfBuzz. +- Remove (long disabled) hb-old and hb-icu-le test shapers. +- Misc gtk-doc and gobject-introspection annotations. +- Misc fixes. +- API changes: + + * Add HB_SET_VALUE_INVALID + +Overview of changes leading to 0.9.20 +Thursday, August 29, 2013 +===================================== + +General: +- Misc substitute_closure() fixes. +- Build fixes. + +Documentation: +- gtk-doc boilerplate integrated. Docs are built now, but + contain no contents. By next release hopefully we have + some content in. Enable using --enable-gtk-doc. + +GObject and Introspection: +- Added harfbuzz-gobject library (hb-gobject.h) that has type + bindings for all HarfBuzz objects and enums. Enable using + --with-gobject. +- Added gobject-introspection boilerplate. Nothing useful + right now. Work in progress. Gets enabled automatically if + --with-gobject is used. Override with --disable-introspection. + +OpenType shaper: +- Apply 'mark' in Myanmar shaper. +- Don't apply 'dlig' by default. + +Uniscribe shaper: +- Support user features. +- Fix loading of fonts that are also installed on the system. +- Fix shaping of Arabic Presentation Forms. +- Fix build with wide chars. + +CoreText shaper: +- Support user features. + +Source changes: +- hb_face_t code moved to hb-face.h / hb-face.cc. +- Added hb-deprecated.h. + +API changes: +- Added HB_DISABLE_DEPRECATED. +- Deprecated HB_SCRIPT_CANADIAN_ABORIGINAL; replaced by + HB_SCRIPT_CANADIAN_SYLLABICS. +- Deprecated HB_BUFFER_FLAGS_DEFAULT; replaced by + HB_BUFFER_FLAG_DEFAULT. +- Deprecated HB_BUFFER_SERIALIZE_FLAGS_DEFAULT; replaced by + HB_BUFFER_SERIALIZE_FLAG_DEFAULT. + + +Overview of changes leading to 0.9.19 +Tuesday, July 16, 2013 +===================================== + +- Build fixes. +- Better handling of multiple variation selectors in a row. +- Pass on variation selector to GSUB if not consumed by cmap. +- Fix undefined memory access. +- Add Javanese config to Indic shaper. +- Misc bug fixes. + +Overview of changes leading to 0.9.18 +Tuesday, May 28, 2013 +===================================== + +New build system: + +- All unneeded code is all disabled by default, + +- Uniscribe and CoreText shapers can be enabled with their --with options, + +- icu_le and old shapers cannot be enabled for now, + +- glib, freetype, and cairo will be detected automatically. + They can be force on/off'ed with their --with options, + +- icu and graphite2 are default off, can be enabled with their --with + options, + +Moreover, ICU support is now build into a separate library: +libharfbuzz-icu.so, and a new harfbuzz-icu.pc is shipped for it. +Distros can enable ICU now without every application on earth +getting linked to via libharfbuzz.so. + +For distros I recommend that they make sure they are building --with-glib +--with-freetype --with-cairo, --with-icu, and optionally --with-graphite2; +And package harfbuzz and harfbuzz-icu separately. + + +Overview of changes leading to 0.9.17 +Monday, May 20, 2013 +===================================== + +- Build fixes. +- Fix bug in hb_set_get_min(). +- Fix regression with Arabic mark positioning / width-zeroing. + +Overview of changes leading to 0.9.16 +Friday, April 19, 2013 +===================================== + +- Major speedup in OpenType lookup processing. With the Amiri + Arabic font, this release is over 3x faster than previous + release. All scripts / languages should see this speedup. + +- New --num-iterations option for hb-shape / hb-view; useful for + profiling. + +Overview of changes leading to 0.9.15 +Friday, April 05, 2013 +===================================== + +- Build fixes. +- Fix crasher in graphite2 shaper. +- Fix Arabic mark width zeroing regression. +- Don't compose Hangul jamo into Unicode syllables. + + +Overview of changes leading to 0.9.14 +Thursday, March 21, 2013 +===================================== + +- Build fixes. +- Fix time-consuming sanitize with malicious fonts. +- Implement hb_buffer_deserialize_glyphs() for both json and text. +- Do not ignore Hangul filler characters. +- Indic fixes: + * Fix Malayalam pre-base reordering interaction with post-forms. + * Further adjust ZWJ handling. Should fix known regressions from + 0.9.13. + + +Overview of changes leading to 0.9.13 +Thursday, February 25, 2013 +===================================== + +- Build fixes. +- Ngapi HarfBuzz Hackfest in London (February 2013): + * Fixed all known Indic bugs, + * New Win8-style Myanmar shaper, + * New South-East Asian shaper for Tai Tham, Cham, and New Tai Lue, + * Smartly ignore Default_Ignorable characters (joiners, etc) wheb + matching GSUB/GPOS lookups, + * Fix 'Phags-Pa U+A872 shaping, + * Fix partial disabling of default-on features, + * Allow disabling of TrueType kerning. +- Fix possible crasher with broken fonts with overlapping tables. +- Removed generated files from git again. So, one needs ragel to + bootstrap from the git tree. + +API changes: +- hb_shape() and related APIs now abort if buffer direction is + HB_DIRECTION_INVALID. Previously, hb_shape() was calling + hb_buffer_guess_segment_properties() on the buffer before + shaping. The heuristics in that function are fragile. If the + user really wants the old behvaior, they can call that function + right before calling hb_shape() to get the old behavior. +- hb_blob_create_sub_blob() always creates sub-blob with + HB_MEMORY_MODE_READONLY. See comments for the reason. + + +Overview of changes leading to 0.9.12 +Thursday, January 18, 2013 +===================================== + +- Build fixes for Sun compiler. +- Minor bug fix. + +Overview of changes leading to 0.9.11 +Thursday, January 10, 2013 +===================================== + +- Build fixes. +- Fix GPOS mark attachment with null Anchor offsets. +- [Indic] Fix old-spec reordering of viramas if sequence ends in one. +- Fix multi-threaded shaper data creation crash. +- Add atomic ops for Solaris. + +API changes: +- Rename hb_buffer_clear() to hb_buffer_clear_contents(). + + +Overview of changes leading to 0.9.10 +Thursday, January 3, 2013 +===================================== + +- [Indic] Fixed rendering of Malayalam dot-reph +- Updated OT language tags. +- Updated graphite2 backend. +- Improved hb_ot_layout_get_size_params() logic. +- Improve hb-shape/hb-view help output. +- Fixed hb-set.h implementation to not crash. +- Fixed various issues with hb_ot_layout_collect_lookups(). +- Various build fixes. + +New API: + +hb_graphite2_face_get_gr_face() +hb_graphite2_font_get_gr_font() +hb_coretext_face_get_cg_font() + +Modified API: + +hb_ot_layout_get_size_params() + + +Overview of changes leading to 0.9.9 +Wednesday, December 5, 2012 +==================================== + +- Fix build on Windows. +- Minor improvements. + + +Overview of changes leading to 0.9.8 +Tuesday, December 4, 2012 +==================================== + + +- Actually implement hb_shape_plan_get_shaper (). +- Make UCDB data tables const. +- Lots of internal refactoring in OTLayout tables. +- Flesh out hb_ot_layout_lookup_collect_glyphs(). + +New API: + +hb_ot_layout_collect_lookups() +hb_ot_layout_get_size_params() + + +Overview of changes leading to 0.9.7 +Sunday, November 21, 2012 +==================================== + + +HarfBuzz "All-You-Can-Eat-Sushi" (aka Vancouver) Hackfest and follow-on fixes. + +- Fix Arabic contextual joining using pre-context text. +- Fix Sinhala "split matra" mess. +- Fix Khmer shaping with broken fonts. +- Implement Thai "PUA" shaping for old fonts. +- Do NOT route Kharoshthi script through the Indic shaper. +- Disable fallback positioning for Indic and Thai shapers. +- Misc fixes. + + +hb-shape / hb-view changes: + +- Add --text-before and --text-after +- Add --bot / --eot / --preserve-default-ignorables +- hb-shape --output-format=json + + +New API: + +hb_buffer_clear() + +hb_buffer_flags_t + +HB_BUFFER_FLAGS_DEFAULT +HB_BUFFER_FLAG_BOT +HB_BUFFER_FLAG_EOT +HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES + +hb_buffer_set_flags() +hb_buffer_get_flags() + +HB_BUFFER_SERIALIZE_FLAGS +hb_buffer_serialize_glyphs() +hb_buffer_deserialize_glyphs() +hb_buffer_serialize_list_formats() + +hb_set_add_range() +hb_set_del_range() +hb_set_get_population() +hb_set_next_range() + +hb_face_[sg]et_glyph_count() + +hb_segment_properties_t +HB_SEGMENT_PROPERTIES_DEFAULT +hb_segment_properties_equal() +hb_segment_properties_hash() + +hb_buffer_set_segment_properties() +hb_buffer_get_segment_properties() + +hb_ot_layout_glyph_class_t +hb_ot_layout_get_glyph_class() +hb_ot_layout_get_glyphs_in_class() + +hb_shape_plan_t +hb_shape_plan_create() +hb_shape_plan_create_cached() +hb_shape_plan_get_empty() +hb_shape_plan_reference() +hb_shape_plan_destroy() +hb_shape_plan_set_user_data() +hb_shape_plan_get_user_data() +hb_shape_plan_execute() +hb_shape_plan_get_shaper() + +hb_ot_shape_plan_collect_lookups() + + +API changes: + +- Remove "mask" parameter from hb_buffer_add(). +- Rename hb_ot_layout_would_substitute_lookup() and hb_ot_layout_substitute_closure_lookup(). +- hb-set.h API const correction. +- Renamed hb_set_min/max() to hb_set_get_min/max(). +- Rename hb_ot_layout_feature_get_lookup_indexes() to hb_ot_layout_feature_get_lookups(). +- Rename hb_buffer_guess_properties() to hb_buffer_guess_segment_properties(). + + + +Overview of changes leading to 0.9.6 +Sunday, November 13, 2012 +==================================== + +- Don't clear pre-context text if no new context is provided. +- Fix ReverseChainingSubstLookup, which was totally borked. +- Adjust output format of hb-shape a bit. +- Include config.h.in in-tree. Makes it easier for alternate build systems. +- Fix hb_buffer_set_length(buffer, 0) invalid memory allocation. +- Use ICU LayoutEngine's C API instead of C++. Avoids much headache. +- Drop glyphs for all of Unicode Default_Ignorable characters. +- Misc build fixes. + +Arabic shaper: +- Enable 'dlig' and 'mset' features in Arabic shaper. +- Implement 'Phags-pa shaping, improve Mongolian. + +Indic shaper: +- Decompose Sinhala split matras the way old HarfBuzz / Pango did. +- Initial support for Consonant Medials. +- Start adding new-style Myanmar shaping. +- Make reph and 'pref' logic introspect the font. +- Route Meetei-Mayek through the Indic shaper. +- Don't apply 'liga' in Indic shaper. +- Improve Malayalam pre-base reordering Ra interaction with Chillus. + + + +Overview of changes leading to 0.9.5 +Sunday, October 14, 2012 +==================================== + +- Synthetic-GSUB Arabic fallback shaping. + +- Misc Indic improvements. + +- Add build system support for pthread. + +- Imported UCDN for in-tree Unicode callbacks implementation. + +- Context-aware Arabic joining. + +- Misc other fixes. + +- New API: + + hb_feature_to/from-string() + hb_buffer_[sg]et_content_type() + + + +Overview of changes leading to 0.9.4 +Tuesday, Sep 03, 2012 +==================================== + +- Indic improvements with old-spec Malayalam. + +- Better fallback glyph positioning, specially with Thai / Lao marks. + +- Implement dotted-circle insertion. + +- Better Arabic fallback shaping / ligation. + +- Added ICU LayoutEngine backend for testing. Call it by the 'icu_le' name. + +- Misc fixes. + + + +Overview of changes leading to 0.9.3 +Friday, Aug 18, 2012 +==================================== + +- Fixed fallback mark positioning for left-to-right text. + +- Improve mark positioning for the remaining combining classes. + +- Unbreak Thai and fallback Arabic shaping. + +- Port Arabic shaper to shape-plan caching. + +- Use new ICU normalizer functions. + + + +Overview of changes leading to 0.9.2 +Friday, Aug 10, 2012 +==================================== + +- Over a thousand commits! This is the first major release of HarfBuzz. + +- HarfBuzz is feature-complete now! It should be in par, or better, than + both Pango's shapers and old HarfBuzz / Qt shapers. + +- New Indic shaper, supporting main Indic scripts, Sinhala, and Khmer. + +- Improved Arabic shaper, with fallback Arabic shaping, supporting Arabic, + Sinhala, N'ko, Mongolian, and Mandaic. + +- New Thai / Lao shaper. + +- Tibetan / Hangul support in the generic shaper. + +- Synthetic GDEF support for fonts without a GDEF table. + +- Fallback mark positioning for fonts without a GPOS table. + +- Unicode normalization shaping heuristic during glyph mapping. + +- New experimental Graphite2 backend. + +- New Uniscribe backend (primarily for testing). + +- New CoreText backend (primarily for testing). + +- Major optimization and speedup. + +- Test suites and testing infrastructure (work in progress). + +- Greatly improved hb-view cmdline tool. + +- hb-shape cmdline tool. + +- Unicode 6.1 support. + +Summary of API changes: + +o Changed API: + + - Users are expected to only include main header files now (ie. hb.h, + hb-glib.h, hb-ft.h, ...) + + - All struct tag names had their initial underscore removed. + Ie. "struct _hb_buffer_t" is "struct hb_buffer_t" now. + + - All set_user_data() functions now take a "replace" boolean parameter. + + - hb_buffer_create() takes zero arguments now. + Use hb_buffer_pre_allocate() to pre-allocate. + + - hb_buffer_add_utf*() now accept -1 for length parameteres, + meaning "nul-terminated". + + - hb_direction_t enum values changed. + + - All *_from_string() APIs now take a length parameter to allow for + non-nul-terminated strings. A -1 length means "nul-terminated". + + - Typedef for hb_language_t changed. + + - hb_get_table_func_t renamed to hb_reference_table_func_t. + + - hb_ot_layout_table_choose_script() + + - Various renames in hb-unicode.h. + +o New API: + + - hb_buffer_guess_properties() + Automatically called by hb_shape(). + + - hb_buffer_normalize_glyphs() + + - hb_tag_from_string() + + - hb-coretext.h + + - hb-uniscribe.h + + - hb_face_reference_blob() + - hb_face_[sg]et_index() + - hb_face_set_upem() + + - hb_font_get_glyph_name_func_t + hb_font_get_glyph_from_name_func_t + hb_font_funcs_set_glyph_name_func() + hb_font_funcs_set_glyph_from_name_func() + hb_font_get_glyph_name() + hb_font_get_glyph_from_name() + hb_font_glyph_to_string() + hb_font_glyph_from_string() + + - hb_font_set_funcs_data() + + - hb_ft_font_set_funcs() + - hb_ft_font_get_face() + + - hb-gobject.h (work in progress) + + - hb_ot_shape_glyphs_closure() + hb_ot_layout_substitute_closure_lookup() + + - hb-set.h + + - hb_shape_full() + + - hb_unicode_combining_class_t + + - hb_unicode_compose_func_t + hb_unicode_decompose_func_t + hb_unicode_decompose_compatibility_func_t + hb_unicode_funcs_set_compose_func() + hb_unicode_funcs_set_decompose_func() + hb_unicode_funcs_set_decompose_compatibility_func() + hb_unicode_compose() + hb_unicode_decompose() + hb_unicode_decompose_compatibility() + +o Removed API: + + - hb_ft_get_font_funcs() + + - hb_ot_layout_substitute_start() + hb_ot_layout_substitute_lookup() + hb_ot_layout_substitute_finish() + hb_ot_layout_position_start() + hb_ot_layout_position_lookup() + hb_ot_layout_position_finish() + + + +Overview of changes leading to 0.6.0 +Friday, May 27, 2011 +==================================== + +- Vertical text support in GPOS +- Almost all API entries have unit tests now, under test/ +- All thread-safety issues are fixed + +Summary of API changes follows. + + +* Simple Types API: + + o New API: + HB_LANGUAGE_INVALID + hb_language_get_default() + hb_direction_to_string() + hb_direction_from_string() + hb_script_get_horizontal_direction() + HB_UNTAG() + + o Renamed API: + hb_category_t renamed to hb_unicode_general_category_t + + o Changed API: + hb_language_t is a typed pointers now + + o Removed API: + HB_TAG_STR() + + +* Use ISO 15924 tags for hb_script_t: + + o New API: + hb_script_from_iso15924_tag() + hb_script_to_iso15924_tag() + hb_script_from_string() + + o Changed API: + HB_SCRIPT_* enum members changed value. + + +* Buffer API streamlined: + + o New API: + hb_buffer_reset() + hb_buffer_set_length() + hb_buffer_allocation_successful() + + o Renamed API: + hb_buffer_ensure() renamed to hb_buffer_pre_allocate() + hb_buffer_add_glyph() renamed to hb_buffer_add() + + o Removed API: + hb_buffer_clear() + hb_buffer_clear_positions() + + o Changed API: + hb_buffer_get_glyph_infos() takes an out length parameter now + hb_buffer_get_glyph_positions() takes an out length parameter now + + +* Blob API streamlined: + + o New API: + hb_blob_get_data() + hb_blob_get_data_writable() + + o Renamed API: + hb_blob_create_empty() renamed to hb_blob_get_empty() + + o Removed API: + hb_blob_lock() + hb_blob_unlock() + hb_blob_is_writable() + hb_blob_try_writable() + + o Changed API: + hb_blob_create() takes user_data before destroy now + + +* Unicode functions API: + + o Unicode function vectors can subclass other unicode function vectors now. + Unimplemented callbacks in the subclass automatically chainup to the parent. + + o All hb_unicode_funcs_t callbacks take a user_data now. Their setters + take a user_data and its respective destroy callback. + + o New API: + hb_unicode_funcs_get_empty() + hb_unicode_funcs_get_default() + hb_unicode_funcs_get_parent() + + o Changed API: + hb_unicode_funcs_create() now takes a parent_funcs. + + o Removed func getter functions: + hb_unicode_funcs_get_mirroring_func() + hb_unicode_funcs_get_general_category_func() + hb_unicode_funcs_get_script_func() + hb_unicode_funcs_get_combining_class_func() + hb_unicode_funcs_get_eastasian_width_func() + + +* Face API: + + o Renamed API: + hb_face_get_table() renamed to hb_face_reference_table() + hb_face_create_for_data() renamed to hb_face_create() + + o Changed API: + hb_face_create_for_tables() takes user_data before destroy now + hb_face_reference_table() returns empty blob instead of NULL + hb_get_table_func_t accepts the face as first parameter now + +* Font API: + + o Fonts can subclass other fonts now. Unimplemented callbacks in the + subclass automatically chainup to the parent. When chaining up, + scale is adjusted if the parent font has a different scale. + + o All hb_font_funcs_t callbacks take a user_data now. Their setters + take a user_data and its respective destroy callback. + + o New API: + hb_font_get_parent() + hb_font_funcs_get_empty() + hb_font_create_sub_font() + + o Removed API: + hb_font_funcs_copy() + hb_font_unset_funcs() + + o Removed func getter functions: + hb_font_funcs_get_glyph_func() + hb_font_funcs_get_glyph_advance_func() + hb_font_funcs_get_glyph_extents_func() + hb_font_funcs_get_contour_point_func() + hb_font_funcs_get_kerning_func() + + o Changed API: + hb_font_create() takes a face and references it now + hb_font_set_funcs() takes user_data before destroy now + hb_font_set_scale() accepts signed integers now + hb_font_get_contour_point_func_t now takes glyph first, then point_index + hb_font_get_glyph_func_t returns a success boolean now + + +* Changed object model: + + o All object types have a _get_empty() now: + hb_blob_get_empty() + hb_buffer_get_empty() + hb_face_get_empty() + hb_font_get_empty() + hb_font_funcs_get_empty() + hb_unicode_funcs_get_empty() + + o Added _set_user_data() and _get_user_data() for all object types: + hb_blob_get_user_data() + hb_blob_set_user_data() + hb_buffer_get_user_data() + hb_buffer_set_user_data() + hb_face_get_user_data() + hb_face_set_user_data() + hb_font_funcs_get_user_data() + hb_font_funcs_set_user_data() + hb_font_get_user_data() + hb_font_set_user_data() + hb_unicode_funcs_get_user_data() + hb_unicode_funcs_set_user_data() + + o Removed the _get_reference_count() from all object types: + hb_blob_get_reference_count() + hb_buffer_get_reference_count() + hb_face_get_reference_count() + hb_font_funcs_get_reference_count() + hb_font_get_reference_count() + hb_unicode_funcs_get_reference_count() + + o Added _make_immutable() and _is_immutable() for all object types except for buffer: + hb_blob_make_immutable() + hb_blob_is_immutable() + hb_face_make_immutable() + hb_face_is_immutable() + + +* Changed API for vertical text support + + o The following callbacks where removed: + hb_font_get_glyph_advance_func_t + hb_font_get_kerning_func_t + + o The following new callbacks added instead: + hb_font_get_glyph_h_advance_func_t + hb_font_get_glyph_v_advance_func_t + hb_font_get_glyph_h_origin_func_t + hb_font_get_glyph_v_origin_func_t + hb_font_get_glyph_h_kerning_func_t + hb_font_get_glyph_v_kerning_func_t + + o The following API removed as such: + hb_font_funcs_set_glyph_advance_func() + hb_font_funcs_set_kerning_func() + hb_font_get_glyph_advance() + hb_font_get_kerning() + + o New API added instead: + hb_font_funcs_set_glyph_h_advance_func() + hb_font_funcs_set_glyph_v_advance_func() + hb_font_funcs_set_glyph_h_origin_func() + hb_font_funcs_set_glyph_v_origin_func() + hb_font_funcs_set_glyph_h_kerning_func() + hb_font_funcs_set_glyph_v_kerning_func() + hb_font_get_glyph_h_advance() + hb_font_get_glyph_v_advance() + hb_font_get_glyph_h_origin() + hb_font_get_glyph_v_origin() + hb_font_get_glyph_h_kerning() + hb_font_get_glyph_v_kerning() + + o The following higher-leve API added for convenience: + hb_font_get_glyph_advance_for_direction() + hb_font_get_glyph_origin_for_direction() + hb_font_add_glyph_origin_for_direction() + hb_font_subtract_glyph_origin_for_direction() + hb_font_get_glyph_kerning_for_direction() + hb_font_get_glyph_extents_for_origin() + hb_font_get_glyph_contour_point_for_origin() + + +* OpenType Layout API: + + o New API: + hb_ot_layout_position_start() + hb_ot_layout_substitute_start() + hb_ot_layout_substitute_finish() + + +* Glue code: + + o New API: + hb_glib_script_to_script() + hb_glib_script_from_script() + hb_icu_script_to_script() + hb_icu_script_from_script() + + +* Version API added: + + o New API: + HB_VERSION_MAJOR + HB_VERSION_MINOR + HB_VERSION_MICRO + HB_VERSION_STRING + HB_VERSION_CHECK() + hb_version() + hb_version_string() + hb_version_check() + + diff --git a/thirdparty/harfbuzz/THANKS b/thirdparty/harfbuzz/THANKS new file mode 100644 index 00000000000..88cb7e9ea12 --- /dev/null +++ b/thirdparty/harfbuzz/THANKS @@ -0,0 +1,7 @@ +Bradley Grainger +Kenichi Ishibashi +Ivan Kuckir +Ryan Lortie +Jeff Muizelaar +suzuki toshiya +Philip Withnall diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-ankr-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-ankr-table.hh new file mode 100644 index 00000000000..f2785a6f588 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-ankr-table.hh @@ -0,0 +1,98 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH +#define HB_AAT_LAYOUT_ANKR_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * ankr -- Anchor Point + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html + */ +#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r') + + +namespace AAT { + +using namespace OT; + + +struct Anchor +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + FWORD xCoordinate; + FWORD yCoordinate; + public: + DEFINE_SIZE_STATIC (4); +}; + +typedef LArrayOf GlyphAnchors; + +struct ankr +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_ankr; + + const Anchor &get_anchor (hb_codepoint_t glyph_id, + unsigned int i, + unsigned int num_glyphs) const + { + const NNOffsetTo *offset = (this+lookupTable).get_value (glyph_id, num_glyphs); + if (!offset) + return Null (Anchor); + const GlyphAnchors &anchors = &(this+anchorData) + *offset; + return anchors[i]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version == 0 && + c->check_range (this, anchorData) && + lookupTable.sanitize (c, this, &(this+anchorData)))); + } + + protected: + HBUINT16 version; /* Version number (set to zero) */ + HBUINT16 flags; /* Flags (currently unused; set to zero) */ + LOffsetTo>> + lookupTable; /* Offset to the table's lookup table */ + LNNOffsetTo + anchorData; /* Offset to the glyph data table */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-bsln-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-bsln-table.hh new file mode 100644 index 00000000000..cd36fc89532 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-bsln-table.hh @@ -0,0 +1,158 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_BSLN_TABLE_HH +#define HB_AAT_LAYOUT_BSLN_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * bsln -- Baseline + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html + */ +#define HB_AAT_TAG_bsln HB_TAG('b','s','l','n') + + +namespace AAT { + + +struct BaselineTableFormat0Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + // Roman, Ideographic centered, Ideographic low, Hanging and Math + // are the default defined ones, but any other maybe accessed also. + HBINT16 deltas[32]; /* These are the FUnit distance deltas from + * the font's natural baseline to the other + * baselines used in the font. */ + public: + DEFINE_SIZE_STATIC (64); +}; + +struct BaselineTableFormat1Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + lookupTable.sanitize (c))); + } + + protected: + HBINT16 deltas[32]; /* ditto */ + Lookup + lookupTable; /* Lookup table that maps glyphs to their + * baseline values. */ + public: + DEFINE_SIZE_MIN (66); +}; + +struct BaselineTableFormat2Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + HBGlyphID stdGlyph; /* The specific glyph index number in this + * font that is used to set the baseline values. + * This is the standard glyph. + * This glyph must contain a set of control points + * (whose numbers are contained in the ctlPoints field) + * that are used to determine baseline distances. */ + HBUINT16 ctlPoints[32]; /* Set of control point numbers, + * associated with the standard glyph. + * A value of 0xFFFF means there is no corresponding + * control point in the standard glyph. */ + public: + DEFINE_SIZE_STATIC (66); +}; + +struct BaselineTableFormat3Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c))); + } + + protected: + HBGlyphID stdGlyph; /* ditto */ + HBUINT16 ctlPoints[32]; /* ditto */ + Lookup + lookupTable; /* Lookup table that maps glyphs to their + * baseline values. */ + public: + DEFINE_SIZE_MIN (68); +}; + +struct bsln +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_bsln; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && defaultBaseline < 32))) + return_trace (false); + + switch (format) + { + case 0: return_trace (parts.format0.sanitize (c)); + case 1: return_trace (parts.format1.sanitize (c)); + case 2: return_trace (parts.format2.sanitize (c)); + case 3: return_trace (parts.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + FixedVersion<>version; /* Version number of the Baseline table. */ + HBUINT16 format; /* Format of the baseline table. Only one baseline + * format may be selected for the font. */ + HBUINT16 defaultBaseline;/* Default baseline value for all glyphs. + * This value can be from 0 through 31. */ + union { + // Distance-Based Formats + BaselineTableFormat0Part format0; + BaselineTableFormat1Part format1; + // Control Point-based Formats + BaselineTableFormat2Part format2; + BaselineTableFormat3Part format3; + } parts; + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_BSLN_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-common.hh b/thirdparty/harfbuzz/src/hb-aat-layout-common.hh new file mode 100644 index 00000000000..75d523f5fcd --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-common.hh @@ -0,0 +1,840 @@ +/* + * Copyright © 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_COMMON_HH +#define HB_AAT_LAYOUT_COMMON_HH + +#include "hb-aat-layout.hh" +#include "hb-open-type.hh" + + +namespace AAT { + +using namespace OT; + + +/* + * Lookup Table + */ + +template struct Lookup; + +template +struct LookupFormat0 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + if (unlikely (glyph_id >= num_glyphs)) return nullptr; + return &arrayZ[glyph_id]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->get_num_glyphs ())); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->get_num_glyphs (), base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 0 */ + UnsizedArrayOf + arrayZ; /* Array of lookup values, indexed by glyph index. */ + public: + DEFINE_SIZE_UNBOUNDED (2); +}; + + +template +struct LookupSegmentSingle +{ + static constexpr unsigned TerminationWordCount = 2u; + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1 ; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c, base)); + } + + HBGlyphID last; /* Last GlyphID in this segment */ + HBGlyphID first; /* First GlyphID in this segment */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template +struct LookupFormat2 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentSingle *v = segments.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + VarSizedBinSearchArrayOf> + segments; /* The actual segments. These must already be sorted, + * according to the first word in each one (the last + * glyph in each segment). */ + public: + DEFINE_SIZE_ARRAY (8, segments); +}; + +template +struct LookupSegmentArray +{ + static constexpr unsigned TerminationWordCount = 2u; + + const T* get_value (hb_codepoint_t glyph_id, const void *base) const + { + return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr; + } + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + first <= last && + valuesZ.sanitize (c, base, last - first + 1)); + } + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + first <= last && + valuesZ.sanitize (c, base, last - first + 1, hb_forward (ds)...)); + } + + HBGlyphID last; /* Last GlyphID in this segment */ + HBGlyphID first; /* First GlyphID in this segment */ + NNOffsetTo> + valuesZ; /* A 16-bit offset from the start of + * the table to the data. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct LookupFormat4 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentArray *v = segments.bsearch (glyph_id); + return v ? v->get_value (glyph_id, this) : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 4 */ + VarSizedBinSearchArrayOf> + segments; /* The actual segments. These must already be sorted, + * according to the first word in each one (the last + * glyph in each segment). */ + public: + DEFINE_SIZE_ARRAY (8, segments); +}; + +template +struct LookupSingle +{ + static constexpr unsigned TerminationWordCount = 1u; + + int cmp (hb_codepoint_t g) const { return glyph.cmp (g); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c, base)); + } + + HBGlyphID glyph; /* Last GlyphID */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (2 + T::static_size); +}; + +template +struct LookupFormat6 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSingle *v = entries.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 6 */ + VarSizedBinSearchArrayOf> + entries; /* The actual entries, sorted by glyph index. */ + public: + DEFINE_SIZE_ARRAY (8, entries); +}; + +template +struct LookupFormat8 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? + &valueArrayZ[glyph_id - firstGlyph] : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 8 */ + HBGlyphID firstGlyph; /* First glyph index included in the trimmed array. */ + HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last + * glyph minus the value of firstGlyph plus 1). */ + UnsizedArrayOf + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (6, valueArrayZ); +}; + +template +struct LookupFormat10 +{ + friend struct Lookup; + + private: + const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const + { + if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount)) + return Null (T); + + const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize]; + + unsigned int v = 0; + unsigned int count = valueSize; + for (unsigned int i = 0; i < count; i++) + v = (v << 8) | *p++; + + return v; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + valueSize <= 4 && + valueArrayZ.sanitize (c, glyphCount * valueSize)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 8 */ + HBUINT16 valueSize; /* Byte size of each value. */ + HBGlyphID firstGlyph; /* First glyph index included in the trimmed array. */ + HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last + * glyph minus the value of firstGlyph plus 1). */ + UnsizedArrayOf + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (8, valueArrayZ); +}; + +template +struct Lookup +{ + const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + switch (u.format) { + case 0: return u.format0.get_value (glyph_id, num_glyphs); + case 2: return u.format2.get_value (glyph_id); + case 4: return u.format4.get_value (glyph_id); + case 6: return u.format6.get_value (glyph_id); + case 8: return u.format8.get_value (glyph_id); + default:return nullptr; + } + } + + const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + switch (u.format) { + /* Format 10 cannot return a pointer. */ + case 10: return u.format10.get_value_or_null (glyph_id); + default: + const T *v = get_value (glyph_id, num_glyphs); + return v ? *v : Null (T); + } + } + + typename T::type get_class (hb_codepoint_t glyph_id, + unsigned int num_glyphs, + unsigned int outOfRange) const + { + const T *v = get_value (glyph_id, num_glyphs); + return v ? *v : outOfRange; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); + case 6: return_trace (u.format6.sanitize (c)); + case 8: return_trace (u.format8.sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + default:return_trace (true); + } + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c, base)); + case 2: return_trace (u.format2.sanitize (c, base)); + case 4: return_trace (u.format4.sanitize (c, base)); + case 6: return_trace (u.format6.sanitize (c, base)); + case 8: return_trace (u.format8.sanitize (c, base)); + case 10: return_trace (false); /* We don't support format10 here currently. */ + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + LookupFormat0 format0; + LookupFormat2 format2; + LookupFormat4 format4; + LookupFormat6 format6; + LookupFormat8 format8; + LookupFormat10 format10; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; +/* Lookup 0 has unbounded size (dependant on num_glyphs). So we need to defined + * special NULL objects for Lookup<> objects, but since it's template our macros + * don't work. So we have to hand-code them here. UGLY. */ +} /* Close namespace. */ +/* Ugly hand-coded null objects for template Lookup<> :(. */ +extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2]; +template +struct Null> { + static AAT::Lookup const & get_null () + { return *reinterpret_cast *> (_hb_Null_AAT_Lookup); } +}; +namespace AAT { + +enum { DELETED_GLYPH = 0xFFFF }; + +/* + * (Extended) State Table + */ + +template +struct Entry +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + /* Note, we don't recurse-sanitize data because we don't access it. + * That said, in our DEFINE_SIZE_STATIC we access T::static_size, + * which ensures that data has a simple sanitize(). To be determined + * if I need to remove that as well. + * + * HOWEVER! Because we are a template, our DEFINE_SIZE_STATIC + * assertion wouldn't be checked, hence the line below. */ + static_assert (T::static_size, ""); + + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 newState; /* Byte offset from beginning of state table + * to the new state. Really?!?! Or just state + * number? The latter in morx for sure. */ + HBUINT16 flags; /* Table specific. */ + T data; /* Optional offsets to per-glyph tables. */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template <> +struct Entry +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int count /*XXX Unused?*/) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 newState; /* Byte offset from beginning of state table to the new state. */ + HBUINT16 flags; /* Table specific. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +template +struct StateTable +{ + typedef typename Types::HBUINT HBUINT; + typedef typename Types::HBUSHORT HBUSHORT; + typedef typename Types::ClassTypeNarrow ClassType; + + enum State + { + STATE_START_OF_TEXT = 0, + STATE_START_OF_LINE = 1, + }; + enum Class + { + CLASS_END_OF_TEXT = 0, + CLASS_OUT_OF_BOUNDS = 1, + CLASS_DELETED_GLYPH = 2, + CLASS_END_OF_LINE = 3, + }; + + int new_state (unsigned int newState) const + { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; } + + unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH; + return (this+classTable).get_class (glyph_id, num_glyphs, 1); + } + + const Entry *get_entries () const + { return (this+entryTable).arrayZ; } + + const Entry &get_entry (int state, unsigned int klass) const + { + if (unlikely (klass >= nClasses)) + klass = StateTable>::CLASS_OUT_OF_BOUNDS; + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int entry = states[state * nClasses + klass]; + DEBUG_MSG (APPLY, nullptr, "e%u", entry); + + return entries[entry]; + } + + bool sanitize (hb_sanitize_context_t *c, + unsigned int *num_entries_out = nullptr) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && + nClasses >= 4 /* Ensure pre-defined classes fit. */ && + classTable.sanitize (c, this)))) return_trace (false); + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int num_classes = nClasses; + if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size))) + return_trace (false); + unsigned int row_stride = num_classes * states[0].static_size; + + /* Apple 'kern' table has this peculiarity: + * + * "Because the stateTableOffset in the state table header is (strictly + * speaking) redundant, some 'kern' tables use it to record an initial + * state where that should not be StartOfText. To determine if this is + * done, calculate what the stateTableOffset should be. If it's different + * from the actual stateTableOffset, use it as the initial state." + * + * We implement this by calling the initial state zero, but allow *negative* + * states if the start state indeed was not the first state. Since the code + * is shared, this will also apply to 'mort' table. The 'kerx' / 'morx' + * tables are not affected since those address states by index, not offset. + */ + + int min_state = 0; + int max_state = 0; + unsigned int num_entries = 0; + + int state_pos = 0; + int state_neg = 0; + unsigned int entry = 0; + while (min_state < state_neg || state_pos <= max_state) + { + if (min_state < state_neg) + { + /* Negative states. */ + if (unlikely (hb_unsigned_mul_overflows (min_state, num_classes))) + return_trace (false); + if (unlikely (!c->check_range (&states[min_state * num_classes], + -min_state, + row_stride))) + return_trace (false); + if ((c->max_ops -= state_neg - min_state) <= 0) + return_trace (false); + { /* Sweep new states. */ + const HBUSHORT *stop = &states[min_state * num_classes]; + if (unlikely (stop > states)) + return_trace (false); + for (const HBUSHORT *p = states; stop < p; p--) + num_entries = hb_max (num_entries, *(p - 1) + 1); + state_neg = min_state; + } + } + + if (state_pos <= max_state) + { + /* Positive states. */ + if (unlikely (!c->check_range (states, + max_state + 1, + row_stride))) + return_trace (false); + if ((c->max_ops -= max_state - state_pos + 1) <= 0) + return_trace (false); + { /* Sweep new states. */ + if (unlikely (hb_unsigned_mul_overflows ((max_state + 1), num_classes))) + return_trace (false); + const HBUSHORT *stop = &states[(max_state + 1) * num_classes]; + if (unlikely (stop < states)) + return_trace (false); + for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++) + num_entries = hb_max (num_entries, *p + 1); + state_pos = max_state + 1; + } + } + + if (unlikely (!c->check_array (entries, num_entries))) + return_trace (false); + if ((c->max_ops -= num_entries - entry) <= 0) + return_trace (false); + { /* Sweep new entries. */ + const Entry *stop = &entries[num_entries]; + for (const Entry *p = &entries[entry]; p < stop; p++) + { + int newState = new_state (p->newState); + min_state = hb_min (min_state, newState); + max_state = hb_max (max_state, newState); + } + entry = num_entries; + } + } + + if (num_entries_out) + *num_entries_out = num_entries; + + return_trace (true); + } + + protected: + HBUINT nClasses; /* Number of classes, which is the number of indices + * in a single line in the state array. */ + NNOffsetTo + classTable; /* Offset to the class table. */ + NNOffsetTo, HBUINT> + stateArrayTable;/* Offset to the state array. */ + NNOffsetTo>, HBUINT> + entryTable; /* Offset to the entry array. */ + + public: + DEFINE_SIZE_STATIC (4 * sizeof (HBUINT)); +}; + +template +struct ClassTable +{ + unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange) const + { + unsigned int i = glyph_id - firstGlyph; + return i >= classArray.len ? outOfRange : classArray.arrayZ[i]; + } + unsigned int get_class (hb_codepoint_t glyph_id, + unsigned int num_glyphs HB_UNUSED, + unsigned int outOfRange) const + { + return get_class (glyph_id, outOfRange); + } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && classArray.sanitize (c)); + } + protected: + HBGlyphID firstGlyph; /* First glyph index included in the trimmed array. */ + ArrayOf classArray; /* The class codes (indexed by glyph index minus + * firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (4, classArray); +}; + +struct ObsoleteTypes +{ + static constexpr bool extended = false; + typedef HBUINT16 HBUINT; + typedef HBUINT8 HBUSHORT; + typedef ClassTable ClassTypeNarrow; + typedef ClassTable ClassTypeWide; + + template + static unsigned int offsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return (offset - ((const char *) array - (const char *) base)) / T::static_size; + } + template + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (offset, base, array); + } + template + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (2 * offset, base, array); + } +}; +struct ExtendedTypes +{ + static constexpr bool extended = true; + typedef HBUINT32 HBUINT; + typedef HBUINT16 HBUSHORT; + typedef Lookup ClassTypeNarrow; + typedef Lookup ClassTypeWide; + + template + static unsigned int offsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } + template + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset / 2; + } + template + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } +}; + +template +struct StateTableDriver +{ + StateTableDriver (const StateTable &machine_, + hb_buffer_t *buffer_, + hb_face_t *face_) : + machine (machine_), + buffer (buffer_), + num_glyphs (face_->get_num_glyphs ()) {} + + template + void drive (context_t *c) + { + if (!c->in_place) + buffer->clear_output (); + + int state = StateTable::STATE_START_OF_TEXT; + for (buffer->idx = 0; buffer->successful;) + { + unsigned int klass = buffer->idx < buffer->len ? + machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) : + (unsigned) StateTable::CLASS_END_OF_TEXT; + DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); + const Entry &entry = machine.get_entry (state, klass); + + /* Unsafe-to-break before this if not in state 0, as things might + * go differently if we start from state 0 here. + * + * Ugh. The indexing here is ugly... */ + if (state && buffer->backtrack_len () && buffer->idx < buffer->len) + { + /* If there's no action and we're just epsilon-transitioning to state 0, + * safe to break. */ + if (c->is_actionable (this, entry) || + !(entry.newState == StateTable::STATE_START_OF_TEXT && + entry.flags == context_t::DontAdvance)) + buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1); + } + + /* Unsafe-to-break if end-of-text would kick in here. */ + if (buffer->idx + 2 <= buffer->len) + { + const Entry &end_entry = machine.get_entry (state, StateTable::CLASS_END_OF_TEXT); + if (c->is_actionable (this, end_entry)) + buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); + } + + c->transition (this, entry); + + state = machine.new_state (entry.newState); + DEBUG_MSG (APPLY, nullptr, "s%d", state); + + if (buffer->idx == buffer->len) + break; + + if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0) + buffer->next_glyph (); + } + + if (!c->in_place) + { + for (; buffer->successful && buffer->idx < buffer->len;) + buffer->next_glyph (); + buffer->swap_buffers (); + } + } + + public: + const StateTable &machine; + hb_buffer_t *buffer; + unsigned int num_glyphs; +}; + + +struct ankr; + +struct hb_aat_apply_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "APPLY"; } + template + return_t dispatch (const T &obj) { return obj.apply (this); } + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + const hb_ot_shape_plan_t *plan; + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + hb_sanitize_context_t sanitizer; + const ankr *ankr_table; + + /* Unused. For debug tracing only. */ + unsigned int lookup_index; + + HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob = const_cast (&Null (hb_blob_t))); + + HB_INTERNAL ~hb_aat_apply_context_t (); + + HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_); + + void set_lookup_index (unsigned int i) { lookup_index = i; } +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_COMMON_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-feat-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-feat-table.hh new file mode 100644 index 00000000000..359e859cfc6 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-feat-table.hh @@ -0,0 +1,222 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_FEAT_TABLE_HH +#define HB_AAT_LAYOUT_FEAT_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * feat -- Feature Name + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html + */ +#define HB_AAT_TAG_feat HB_TAG('f','e','a','t') + + +namespace AAT { + + +struct SettingName +{ + friend struct FeatureName; + + int cmp (hb_aat_layout_feature_selector_t key) const + { return (int) key - (int) setting; } + + hb_aat_layout_feature_selector_t get_selector () const + { return (hb_aat_layout_feature_selector_t) (unsigned) setting; } + + hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const + { + return { + nameIndex, + (hb_aat_layout_feature_selector_t) (unsigned int) setting, + default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID + ? (hb_aat_layout_feature_selector_t) (setting + 1) + : default_selector, + 0 + }; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + HBUINT16 setting; /* The setting. */ + NameID nameIndex; /* The name table index for the setting's name. */ + public: + DEFINE_SIZE_STATIC (4); +}; +DECLARE_NULL_NAMESPACE_BYTES (AAT, SettingName); + +struct feat; + +struct FeatureName +{ + int cmp (hb_aat_layout_feature_type_t key) const + { return (int) key - (int) feature; } + + enum { + Exclusive = 0x8000, /* If set, the feature settings are mutually exclusive. */ + NotDefault = 0x4000, /* If clear, then the setting with an index of 0 in + * the setting name array for this feature should + * be taken as the default for the feature + * (if one is required). If set, then bits 0-15 of this + * featureFlags field contain the index of the setting + * which is to be taken as the default. */ + IndexMask = 0x00FF /* If bits 30 and 31 are set, then these sixteen bits + * indicate the index of the setting in the setting name + * array for this feature which should be taken + * as the default. */ + }; + + unsigned int get_selector_infos (unsigned int start_offset, + unsigned int *selectors_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *pdefault_index, /* OUT. May be NULL. */ + const void *base) const + { + hb_array_t< const SettingName> settings_table = (base+settingTableZ).as_array (nSettings); + + static_assert (Index::NOT_FOUND_INDEX == HB_AAT_LAYOUT_NO_SELECTOR_INDEX, ""); + + hb_aat_layout_feature_selector_t default_selector = HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID; + unsigned int default_index = Index::NOT_FOUND_INDEX; + if (featureFlags & Exclusive) + { + default_index = (featureFlags & NotDefault) ? featureFlags & IndexMask : 0; + default_selector = settings_table[default_index].get_selector (); + } + if (pdefault_index) + *pdefault_index = default_index; + + if (selectors_count) + { + + settings_table.sub_array (start_offset, selectors_count) + | hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); }) + | hb_sink (hb_array (selectors, *selectors_count)) + ; + } + return settings_table.length; + } + + hb_aat_layout_feature_type_t get_feature_type () const + { return (hb_aat_layout_feature_type_t) (unsigned int) feature; } + + hb_ot_name_id_t get_feature_name_id () const { return nameIndex; } + + bool is_exclusive () const { return featureFlags & Exclusive; } + + /* A FeatureName with no settings is meaningless */ + bool has_data () const { return nSettings; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (base+settingTableZ).sanitize (c, nSettings))); + } + + protected: + HBUINT16 feature; /* Feature type. */ + HBUINT16 nSettings; /* The number of records in the setting name array. */ + LNNOffsetTo> + settingTableZ; /* Offset in bytes from the beginning of this table to + * this feature's setting name array. The actual type of + * record this offset refers to will depend on the + * exclusivity value, as described below. */ + HBUINT16 featureFlags; /* Single-bit flags associated with the feature type. */ + HBINT16 nameIndex; /* The name table index for the feature's name. + * This index has values greater than 255 and + * less than 32768. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct feat +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_feat; + + bool has_data () const { return version.to_int (); } + + unsigned int get_feature_types (unsigned int start_offset, + unsigned int *count, + hb_aat_layout_feature_type_t *features) const + { + if (count) + { + + namesZ.as_array (featureNameCount).sub_array (start_offset, count) + | hb_map (&FeatureName::get_feature_type) + | hb_sink (hb_array (features, *count)) + ; + } + return featureNameCount; + } + + bool exposes_feature (hb_aat_layout_feature_type_t feature_type) const + { return get_feature (feature_type).has_data (); } + + const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const + { return namesZ.bsearch (featureNameCount, feature_type); } + + hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const + { return get_feature (feature).get_feature_name_id (); } + + unsigned int get_selector_infos (hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selectors_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */) const + { + return get_feature (feature_type).get_selector_infos (start_offset, selectors_count, selectors, + default_index, this); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version.major == 1 && + namesZ.sanitize (c, featureNameCount, this))); + } + + protected: + FixedVersion<>version; /* Version number of the feature name table + * (0x00010000 for the current version). */ + HBUINT16 featureNameCount; + /* The number of entries in the feature name array. */ + HBUINT16 reserved1; /* Reserved (set to zero). */ + HBUINT32 reserved2; /* Reserved (set to zero). */ + SortedUnsizedArrayOf + namesZ; /* The feature name array. */ + public: + DEFINE_SIZE_ARRAY (12, namesZ); +}; + +} /* namespace AAT */ + +#endif /* HB_AAT_LAYOUT_FEAT_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh new file mode 100644 index 00000000000..49506e9f5ab --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh @@ -0,0 +1,417 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_JUST_TABLE_HH +#define HB_AAT_LAYOUT_JUST_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" + +#include "hb-aat-layout-morx-table.hh" + +/* + * just -- Justification + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html + */ +#define HB_AAT_TAG_just HB_TAG('j','u','s','t') + + +namespace AAT { + +using namespace OT; + + +struct ActionSubrecordHeader +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + HBUINT16 actionClass; /* The JustClass value associated with this + * ActionSubrecord. */ + HBUINT16 actionType; /* The type of postcompensation action. */ + HBUINT16 actionLength; /* Length of this ActionSubrecord record, which + * must be a multiple of 4. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct DecompositionAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + ActionSubrecordHeader + header; + HBFixed lowerLimit; /* If the distance factor is less than this value, + * then the ligature is decomposed. */ + HBFixed upperLimit; /* If the distance factor is greater than this value, + * then the ligature is decomposed. */ + HBUINT16 order; /* Numerical order in which this ligature will + * be decomposed; you may want infrequent ligatures + * to decompose before more frequent ones. The ligatures + * on the line of text will decompose in increasing + * value of this field. */ + ArrayOf + decomposedglyphs; + /* Number of 16-bit glyph indexes that follow; + * the ligature will be decomposed into these glyphs. + * + * Array of decomposed glyphs. */ + public: + DEFINE_SIZE_ARRAY (18, decomposedglyphs); +}; + +struct UnconditionalAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBGlyphID addGlyph; /* Glyph that should be added if the distance factor + * is growing. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ConditionalAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + ActionSubrecordHeader + header; + HBFixed substThreshold; /* Distance growth factor (in ems) at which + * this glyph is replaced and the growth factor + * recalculated. */ + HBGlyphID addGlyph; /* Glyph to be added as kashida. If this value is + * 0xFFFF, no extra glyph will be added. Note that + * generally when a glyph is added, justification + * will need to be redone. */ + HBGlyphID substGlyph; /* Glyph to be substituted for this glyph if the + * growth factor equals or exceeds the value of + * substThreshold. */ + public: + DEFINE_SIZE_STATIC (14); +}; + +struct DuctileGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + ActionSubrecordHeader + header; + HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis. + * This would normally be 0x64756374 ('duct'), + * but you may use any axis the font contains. */ + HBFixed minimumLimit; /* The lowest value for the ductility axis tha + * still yields an acceptable appearance. Normally + * this will be 1.0. */ + HBFixed noStretchValue; /* This is the default value that corresponds to + * no change in appearance. Normally, this will + * be 1.0. */ + HBFixed maximumLimit; /* The highest value for the ductility axis that + * still yields an acceptable appearance. */ + public: + DEFINE_SIZE_STATIC (22); +}; + +struct RepeatedAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + ActionSubrecordHeader + header; + HBUINT16 flags; /* Currently unused; set to 0. */ + HBGlyphID glyph; /* Glyph that should be added if the distance factor + * is growing. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct ActionSubrecord +{ + unsigned int get_length () const { return u.header.actionLength; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (u.header.actionType) + { + case 0: return_trace (u.decompositionAction.sanitize (c)); + case 1: return_trace (u.unconditionalAddGlyphAction.sanitize (c)); + case 2: return_trace (u.conditionalAddGlyphAction.sanitize (c)); + // case 3: return_trace (u.stretchGlyphAction.sanitize (c)); + case 4: return_trace (u.decompositionAction.sanitize (c)); + case 5: return_trace (u.decompositionAction.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + ActionSubrecordHeader header; + DecompositionAction decompositionAction; + UnconditionalAddGlyphAction unconditionalAddGlyphAction; + ConditionalAddGlyphAction conditionalAddGlyphAction; + /* StretchGlyphAction stretchGlyphAction; -- Not supported by CoreText */ + DuctileGlyphAction ductileGlyphAction; + RepeatedAddGlyphAction repeatedAddGlyphAction; + } u; /* Data. The format of this data depends on + * the value of the actionType field. */ + public: + DEFINE_SIZE_UNION (6, header); +}; + +struct PostcompensationActionChain +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + unsigned int offset = min_size; + for (unsigned int i = 0; i < count; i++) + { + const ActionSubrecord& subrecord = StructAtOffset (this, offset); + if (unlikely (!subrecord.sanitize (c))) return_trace (false); + offset += subrecord.get_length (); + } + + return_trace (true); + } + + protected: + HBUINT32 count; + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct JustWidthDeltaEntry +{ + enum Flags + { + Reserved1 =0xE000,/* Reserved. You should set these bits to zero. */ + UnlimiteGap =0x1000,/* The glyph can take unlimited gap. When this + * glyph participates in the justification process, + * it and any other glyphs on the line having this + * bit set absorb all the remaining gap. */ + Reserved2 =0x0FF0,/* Reserved. You should set these bits to zero. */ + Priority =0x000F /* The justification priority of the glyph. */ + }; + + enum Priority + { + Kashida = 0, /* Kashida priority. This is the highest priority + * during justification. */ + Whitespace = 1, /* Whitespace priority. Any whitespace glyphs (as + * identified in the glyph properties table) will + * get this priority. */ + InterCharacter = 2, /* Inter-character priority. Give this to any + * remaining glyphs. */ + NullPriority = 3 /* Null priority. You should set this priority for + * glyphs that only participate in justification + * after the above priorities. Normally all glyphs + * have one of the previous three values. If you + * don't want a glyph to participate in justification, + * and you don't want to set its factors to zero, + * you may instead assign it to the null priority. */ + }; + + protected: + HBFixed beforeGrowLimit;/* The ratio by which the advance width of the + * glyph is permitted to grow on the left or top side. */ + HBFixed beforeShrinkLimit; + /* The ratio by which the advance width of the + * glyph is permitted to shrink on the left or top side. */ + HBFixed afterGrowLimit; /* The ratio by which the advance width of the glyph + * is permitted to shrink on the left or top side. */ + HBFixed afterShrinkLimit; + /* The ratio by which the advance width of the glyph + * is at most permitted to shrink on the right or + * bottom side. */ + HBUINT16 growFlags; /* Flags controlling the grow case. */ + HBUINT16 shrinkFlags; /* Flags controlling the shrink case. */ + + public: + DEFINE_SIZE_STATIC (20); +}; + +struct WidthDeltaPair +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + HBUINT32 justClass; /* The justification category associated + * with the wdRecord field. Only 7 bits of + * this field are used. (The other bits are + * used as padding to guarantee longword + * alignment of the following record). */ + JustWidthDeltaEntry + wdRecord; /* The actual width delta record. */ + + public: + DEFINE_SIZE_STATIC (24); +}; + +typedef OT::LArrayOf WidthDeltaCluster; + +struct JustificationCategory +{ + typedef void EntryData; + + enum Flags + { + SetMark =0x8000,/* If set, make the current glyph the marked + * glyph. */ + DontAdvance =0x4000,/* If set, don't advance to the next glyph before + * going to the new state. */ + MarkCategory =0x3F80,/* The justification category for the marked + * glyph if nonzero. */ + CurrentCategory =0x007F /* The justification category for the current + * glyph if nonzero. */ + }; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + morphHeader.sanitize (c) && + stHeader.sanitize (c))); + } + + protected: + ChainSubtable + morphHeader; /* Metamorphosis-style subtable header. */ + StateTable + stHeader; /* The justification insertion state table header */ + public: + DEFINE_SIZE_STATIC (30); +}; + +struct JustificationHeader +{ + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + justClassTable.sanitize (c, base, base) && + wdcTable.sanitize (c, base) && + pcTable.sanitize (c, base) && + lookupTable.sanitize (c, base))); + } + + protected: + OffsetTo + justClassTable; /* Offset to the justification category state table. */ + OffsetTo + wdcTable; /* Offset from start of justification table to start + * of the subtable containing the width delta factors + * for the glyphs in your font. + * + * The width delta clusters table. */ + OffsetTo + pcTable; /* Offset from start of justification table to start + * of postcompensation subtable (set to zero if none). + * + * The postcompensation subtable, if present in the font. */ + Lookup> + lookupTable; /* Lookup table associating glyphs with width delta + * clusters. See the description of Width Delta Clusters + * table for details on how to interpret the lookup values. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct just +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_just; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (likely (c->check_struct (this) && + version.major == 1 && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this))); + } + + protected: + FixedVersion<>version; /* Version of the justification table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the justification table (set to 0). */ + OffsetTo + horizData; /* Byte offset from the start of the justification table + * to the header for tables that contain justification + * information for horizontal text. + * If you are not including this information, + * store 0. */ + OffsetTo + vertData; /* ditto, vertical */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_JUST_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh new file mode 100644 index 00000000000..1cd412164e7 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh @@ -0,0 +1,999 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_KERX_TABLE_HH +#define HB_AAT_LAYOUT_KERX_TABLE_HH + +#include "hb-kern.hh" +#include "hb-aat-layout-ankr-table.hh" + +/* + * kerx -- Extended Kerning + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + */ +#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x') + + +namespace AAT { + +using namespace OT; + + +static inline int +kerxTupleKern (int value, + unsigned int tupleCount, + const void *base, + hb_aat_apply_context_t *c) +{ + if (likely (!tupleCount || !c)) return value; + + unsigned int offset = value; + const FWORD *pv = &StructAtOffset (base, offset); + if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0; + return *pv; +} + + +struct hb_glyph_pair_t +{ + hb_codepoint_t left; + hb_codepoint_t right; +}; + +struct KernPair +{ + int get_kerning () const { return value; } + + int cmp (const hb_glyph_pair_t &o) const + { + int ret = left.cmp (o.left); + if (ret) return ret; + return right.cmp (o.right); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBGlyphID left; + HBGlyphID right; + FWORD value; + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct KerxSubTableFormat0 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c = nullptr) const + { + hb_glyph_pair_t pair = {left, right}; + int v = pairs.bsearch (pair).get_kerning (); + return kerxTupleKern (v, header.tuple_count (), this, c); + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + struct accelerator_t + { + const KerxSubTableFormat0 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat0 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (pairs.sanitize (c))); + } + + protected: + KernSubTableHeader header; + BinSearchArrayOf + pairs; /* Sorted kern records. */ + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs); +}; + + +template +struct Format1Entry; + +template <> +struct Format1Entry +{ + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Reset = 0x2000, /* If set, reset the kerning data (clear the stack) */ + Reserved = 0x1FFF, /* Not used; set to 0. */ + }; + + struct EntryData + { + HBUINT16 kernActionIndex;/* Index into the kerning value array. If + * this index is 0xFFFF, then no kerning + * is to be performed. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.data.kernActionIndex != 0xFFFF; } + + static unsigned int kernActionIndex (const Entry &entry) + { return entry.data.kernActionIndex; } +}; +template <> +struct Format1Entry +{ + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * value table for the glyphs on the kerning stack. */ + + Reset = 0x0000, /* Not supported? */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int kernActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + +template +struct KerxSubTableFormat1 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + typedef Format1Entry Format1EntryT; + typedef typename Format1EntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum + { + DontAdvance = Format1EntryT::DontAdvance, + }; + + driver_context_t (const KerxSubTableFormat1 *table_, + hb_aat_apply_context_t *c_) : + c (c_), + table (table_), + /* Apparently the offset kernAction is from the beginning of the state-machine, + * similar to offsets in morx table, NOT from beginning of this table, like + * other subtables in kerx. Discovered via testing. */ + kernAction (&table->machine + table->kernAction), + depth (0), + crossStream (table->header.coverage & table->header.CrossStream) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { return Format1EntryT::performAction (entry); } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & Format1EntryT::Reset) + depth = 0; + + if (flags & Format1EntryT::Push) + { + if (likely (depth < ARRAY_LENGTH (stack))) + stack[depth++] = buffer->idx; + else + depth = 0; /* Probably not what CoreText does, but better? */ + } + + if (Format1EntryT::performAction (entry) && depth) + { + unsigned int tuple_count = hb_max (1u, table->header.tuple_count ()); + + unsigned int kern_idx = Format1EntryT::kernActionIndex (entry); + kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ); + const FWORD *actions = &kernAction[kern_idx]; + if (!c->sanitizer.check_array (actions, depth, tuple_count)) + { + depth = 0; + return; + } + + hb_mask_t kern_mask = c->plan->kern_mask; + + /* From Apple 'kern' spec: + * "Each pops one glyph from the kerning stack and applies the kerning value to it. + * The end of the list is marked by an odd value... */ + bool last = false; + while (!last && depth) + { + unsigned int idx = stack[--depth]; + int v = *actions; + actions += tuple_count; + if (idx >= buffer->len) continue; + + /* "The end of the list is marked by an odd value..." */ + last = v & 1; + v &= ~1; + + hb_glyph_position_t &o = buffer->pos[idx]; + + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + if (crossStream) + { + /* The following flag is undocumented in the spec, but described + * in the 'kern' table example. */ + if (v == -0x8000) + { + o.attach_type() = ATTACH_TYPE_NONE; + o.attach_chain() = 0; + o.y_offset = 0; + } + else if (o.attach_type()) + { + o.y_offset += c->font->em_scale_y (v); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + } + else if (buffer->info[idx].mask & kern_mask) + { + o.x_advance += c->font->em_scale_x (v); + o.x_offset += c->font->em_scale_x (v); + } + } + else + { + if (crossStream) + { + /* CoreText doesn't do crossStream kerning in vertical. We do. */ + if (v == -0x8000) + { + o.attach_type() = ATTACH_TYPE_NONE; + o.attach_chain() = 0; + o.x_offset = 0; + } + else if (o.attach_type()) + { + o.x_offset += c->font->em_scale_x (v); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + } + else if (buffer->info[idx].mask & kern_mask) + { + o.y_advance += c->font->em_scale_y (v); + o.y_offset += c->font->em_scale_y (v); + } + } + } + } + } + + private: + hb_aat_apply_context_t *c; + const KerxSubTableFormat1 *table; + const UnsizedArrayOf &kernAction; + unsigned int stack[8]; + unsigned int depth; + bool crossStream; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning && + !(header.coverage & header.CrossStream)) + return false; + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (likely (c->check_struct (this) && + machine.sanitize (c))); + } + + protected: + KernSubTableHeader header; + StateTable machine; + NNOffsetTo, HBUINT> kernAction; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT)); +}; + +template +struct KerxSubTableFormat2 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c) const + { + unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); + unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0); + unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0); + + const UnsizedArrayOf &arrayZ = this+array; + unsigned int kern_idx = l + r; + kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ); + const FWORD *v = &arrayZ[kern_idx]; + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + + return kerxTupleKern (*v, header.tuple_count (), this, c); + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + struct accelerator_t + { + const KerxSubTableFormat2 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat2 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + leftClassTable.sanitize (c, this) && + rightClassTable.sanitize (c, this) && + c->check_range (this, array))); + } + + protected: + KernSubTableHeader header; + HBUINT rowWidth; /* The width, in bytes, of a row in the table. */ + NNOffsetTo + leftClassTable; /* Offset from beginning of this subtable to + * left-hand class table. */ + NNOffsetTo + rightClassTable;/* Offset from beginning of this subtable to + * right-hand class table. */ + NNOffsetTo, HBUINT> + array; /* Offset from beginning of this subtable to + * the start of the kerning array. */ + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT)); +}; + +template +struct KerxSubTableFormat4 +{ + typedef ExtendedTypes Types; + + struct EntryData + { + HBUINT16 ankrActionIndex;/* Either 0xFFFF (for no action) or the index of + * the action to perform. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* Not used; set to 0. */ + }; + + enum SubTableFlags + { + ActionType = 0xC0000000, /* A two-bit field containing the action type. */ + Unused = 0x3F000000, /* Unused - must be zero. */ + Offset = 0x00FFFFFF, /* Masks the offset in bytes from the beginning + * of the subtable to the beginning of the control + * point table. */ + }; + + driver_context_t (const KerxSubTableFormat4 *table, + hb_aat_apply_context_t *c_) : + c (c_), + action_type ((table->flags & ActionType) >> 30), + ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))), + mark_set (false), + mark (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { return entry.data.ankrActionIndex != 0xFFFF; } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len) + { + hb_glyph_position_t &o = buffer->cur_pos(); + switch (action_type) + { + case 0: /* Control Point Actions.*/ + { + /* Indexed into glyph outline. */ + /* Each action (record in ankrData) contains two 16-bit fields, so we must + double the ankrActionIndex to get the correct offset here. */ + const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2]; + if (!c->sanitizer.check_array (data, 2)) return; + unsigned int markControlPoint = *data++; + unsigned int currControlPoint = *data++; + hb_position_t markX = 0; + hb_position_t markY = 0; + hb_position_t currX = 0; + hb_position_t currY = 0; + if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint, + markControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &markX, &markY) || + !c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint, + currControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &currX, &currY)) + return; + + o.x_offset = markX - currX; + o.y_offset = markY - currY; + } + break; + + case 1: /* Anchor Point Actions. */ + { + /* Indexed into 'ankr' table. */ + /* Each action (record in ankrData) contains two 16-bit fields, so we must + double the ankrActionIndex to get the correct offset here. */ + const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2]; + if (!c->sanitizer.check_array (data, 2)) return; + unsigned int markAnchorPoint = *data++; + unsigned int currAnchorPoint = *data++; + const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint, + markAnchorPoint, + c->sanitizer.get_num_glyphs ()); + const Anchor &currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint, + currAnchorPoint, + c->sanitizer.get_num_glyphs ()); + + o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate); + o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate); + } + break; + + case 2: /* Control Point Coordinate Actions. */ + { + /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex + by 4 to get the correct offset for the given action. */ + const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4]; + if (!c->sanitizer.check_array (data, 4)) return; + int markX = *data++; + int markY = *data++; + int currX = *data++; + int currY = *data++; + + o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX); + o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY); + } + break; + } + o.attach_type() = ATTACH_TYPE_MARK; + o.attach_chain() = (int) mark - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + + if (entry.flags & Mark) + { + mark_set = true; + mark = buffer->idx; + } + } + + private: + hb_aat_apply_context_t *c; + unsigned int action_type; + const HBUINT16 *ankrData; + bool mark_set; + unsigned int mark; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (likely (c->check_struct (this) && + machine.sanitize (c))); + } + + protected: + KernSubTableHeader header; + StateTable machine; + HBUINT32 flags; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20); +}; + +template +struct KerxSubTableFormat6 +{ + enum Flags + { + ValuesAreLong = 0x00000001, + }; + + bool is_long () const { return flags & ValuesAreLong; } + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c) const + { + unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); + if (is_long ()) + { + const typename U::Long &t = u.l; + unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs); + unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs); + unsigned int offset = l + r; + if (unlikely (offset < l)) return 0; /* Addition overflow. */ + if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0; + const FWORD32 *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD32)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c); + } + else + { + const typename U::Short &t = u.s; + unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs); + unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs); + unsigned int offset = l + r; + const FWORD *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (is_long () ? + ( + u.l.rowIndexTable.sanitize (c, this) && + u.l.columnIndexTable.sanitize (c, this) && + c->check_range (this, u.l.array) + ) : ( + u.s.rowIndexTable.sanitize (c, this) && + u.s.columnIndexTable.sanitize (c, this) && + c->check_range (this, u.s.array) + )) && + (header.tuple_count () == 0 || + c->check_range (this, vector)))); + } + + struct accelerator_t + { + const KerxSubTableFormat6 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat6 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + protected: + KernSubTableHeader header; + HBUINT32 flags; + HBUINT16 rowCount; + HBUINT16 columnCount; + union U + { + struct Long + { + LNNOffsetTo> rowIndexTable; + LNNOffsetTo> columnIndexTable; + LNNOffsetTo> array; + } l; + struct Short + { + LNNOffsetTo> rowIndexTable; + LNNOffsetTo> columnIndexTable; + LNNOffsetTo> array; + } s; + } u; + LNNOffsetTo> vector; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24); +}; + + +struct KerxSubTableHeader +{ + typedef ExtendedTypes Types; + + unsigned tuple_count () const { return tupleCount; } + bool is_horizontal () const { return !(coverage & Vertical); } + + enum Coverage + { + Vertical = 0x80000000u, /* Set if table has vertical kerning values. */ + CrossStream = 0x40000000u, /* Set if table has cross-stream kerning values. */ + Variation = 0x20000000u, /* Set if table has variation kerning values. */ + Backwards = 0x10000000u, /* If clear, process the glyphs forwards, that + * is, from first to last in the glyph stream. + * If we, process them from last to first. + * This flag only applies to state-table based + * 'kerx' subtables (types 1 and 4). */ + Reserved = 0x0FFFFF00u, /* Reserved, set to zero. */ + SubtableType= 0x000000FFu, /* Subtable type. */ + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + public: + HBUINT32 length; + HBUINT32 coverage; + HBUINT32 tupleCount; + public: + DEFINE_SIZE_STATIC (12); +}; + +struct KerxSubTable +{ + friend struct kerx; + + unsigned int get_size () const { return u.header.length; } + unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.format0, hb_forward (ds)...)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, hb_forward (ds)...)); + case 6: return_trace (c->dispatch (u.format6, hb_forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c) || + u.header.length <= u.header.static_size || + !c->check_range (this, u.header.length)) + return_trace (false); + + return_trace (dispatch (c)); + } + + public: + union { + KerxSubTableHeader header; + KerxSubTableFormat0 format0; + KerxSubTableFormat1 format1; + KerxSubTableFormat2 format2; + KerxSubTableFormat4 format4; + KerxSubTableFormat6 format6; + } u; + public: + DEFINE_SIZE_MIN (12); +}; + + +/* + * The 'kerx' Table + */ + +template +struct KerxTable +{ + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const T* thiz () const { return static_cast (this); } + + bool has_state_machine () const + { + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (st->get_type () == 1) + return true; + st = &StructAfter (*st); + } + return false; + } + + bool has_cross_stream () const + { + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (st->u.header.coverage & st->u.header.CrossStream) + return true; + st = &StructAfter (*st); + } + return false; + } + + int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + typedef typename T::SubTable SubTable; + + int v = 0; + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) || + !st->u.header.is_horizontal ()) + continue; + v += st->get_kerning (left, right); + st = &StructAfter (*st); + } + return v; + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { + typedef typename T::SubTable SubTable; + + bool ret = false; + bool seenCrossStream = false; + c->set_lookup_index (0); + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation)) + goto skip; + + if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ()) + goto skip; + + reverse = bool (st->u.header.coverage & st->u.header.Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start subtable %d", c->lookup_index)) + goto skip; + + if (!seenCrossStream && + (st->u.header.coverage & st->u.header.CrossStream)) + { + /* Attach all glyphs into a chain. */ + seenCrossStream = true; + hb_glyph_position_t *pos = c->buffer->pos; + unsigned int count = c->buffer->len; + for (unsigned int i = 0; i < count; i++) + { + pos[i].attach_type() = ATTACH_TYPE_CURSIVE; + pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1; + /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT, + * since there needs to be a non-zero attachment for post-positioning to + * be needed. */ + } + } + + if (reverse) + c->buffer->reverse (); + + { + /* See comment in sanitize() for conditional here. */ + hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr); + ret |= st->dispatch (c); + } + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end subtable %d", c->lookup_index); + + skip: + st = &StructAfter (*st); + c->set_lookup_index (c->lookup_index + 1); + } + + return ret; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!thiz()->version.sanitize (c) || + (unsigned) thiz()->version < (unsigned) T::minVersion || + !thiz()->tableCount.sanitize (c))) + return_trace (false); + + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (unlikely (!st->u.header.sanitize (c))) + return_trace (false); + /* OpenType kern table has 2-byte subtable lengths. That's limiting. + * MS implementation also only supports one subtable, of format 0, + * anyway. Certain versions of some fonts, like Calibry, contain + * kern subtable that exceeds 64kb. Looks like, the subtable length + * is simply ignored. Which makes sense. It's only needed if you + * have multiple subtables. To handle such fonts, we just ignore + * the length for the last subtable. */ + hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr); + + if (unlikely (!st->sanitize (c))) + return_trace (false); + + st = &StructAfter (*st); + } + + return_trace (true); + } +}; + +struct kerx : KerxTable +{ + friend struct KerxTable; + + static constexpr hb_tag_t tableTag = HB_AAT_TAG_kerx; + static constexpr unsigned minVersion = 2u; + + typedef KerxSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KerxSubTable SubTable; + + bool has_data () const { return version; } + + protected: + HBUINT16 version; /* The version number of the extended kerning table + * (currently 2, 3, or 4). */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 tableCount; /* The number of subtables included in the extended kerning + * table. */ + SubTable firstSubTable; /* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8); +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh new file mode 100644 index 00000000000..04027a61bef --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh @@ -0,0 +1,1157 @@ +/* + * Copyright © 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_MORX_TABLE_HH +#define HB_AAT_LAYOUT_MORX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-common.hh" +#include "hb-aat-map.hh" + +/* + * morx -- Extended Glyph Metamorphosis + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + */ +#define HB_AAT_TAG_morx HB_TAG('m','o','r','x') +#define HB_AAT_TAG_mort HB_TAG('m','o','r','t') + + +namespace AAT { + +using namespace OT; + +template +struct RearrangementSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef void EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + MarkFirst = 0x8000, /* If set, make the current glyph the first + * glyph to be rearranged. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. This means + * that the glyph index doesn't change, even + * if the glyph at that index has changed. */ + MarkLast = 0x2000, /* If set, make the current glyph the last + * glyph to be rearranged. */ + Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */ + Verb = 0x000F, /* The type of rearrangement specified. */ + }; + + driver_context_t (const RearrangementSubtable *table HB_UNUSED) : + ret (false), + start (0), end (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & Verb) && start < end; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & MarkFirst) + start = buffer->idx; + + if (flags & MarkLast) + end = hb_min (buffer->idx + 1, buffer->len); + + if ((flags & Verb) && start < end) + { + /* The following map has two nibbles, for start-side + * and end-side. Values of 0,1,2 mean move that many + * to the other side. Value of 3 means move 2 and + * flip them. */ + const unsigned char map[16] = + { + 0x00, /* 0 no change */ + 0x10, /* 1 Ax => xA */ + 0x01, /* 2 xD => Dx */ + 0x11, /* 3 AxD => DxA */ + 0x20, /* 4 ABx => xAB */ + 0x30, /* 5 ABx => xBA */ + 0x02, /* 6 xCD => CDx */ + 0x03, /* 7 xCD => DCx */ + 0x12, /* 8 AxCD => CDxA */ + 0x13, /* 9 AxCD => DCxA */ + 0x21, /* 10 ABxD => DxAB */ + 0x31, /* 11 ABxD => DxBA */ + 0x22, /* 12 ABxCD => CDxAB */ + 0x32, /* 13 ABxCD => CDxBA */ + 0x23, /* 14 ABxCD => DCxAB */ + 0x33, /* 15 ABxCD => DCxBA */ + }; + + unsigned int m = map[flags & Verb]; + unsigned int l = hb_min (2u, m >> 4); + unsigned int r = hb_min (2u, m & 0x0F); + bool reverse_l = 3 == (m >> 4); + bool reverse_r = 3 == (m & 0x0F); + + if (end - start >= l + r) + { + buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len)); + buffer->merge_clusters (start, end); + + hb_glyph_info_t *info = buffer->info; + hb_glyph_info_t buf[4]; + + memcpy (buf, info + start, l * sizeof (buf[0])); + memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); + + if (l != r) + memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); + + memcpy (info + start, buf + 2, r * sizeof (buf[0])); + memcpy (info + end - l, buf, l * sizeof (buf[0])); + if (reverse_l) + { + buf[0] = info[end - 1]; + info[end - 1] = info[end - 2]; + info[end - 2] = buf[0]; + } + if (reverse_r) + { + buf[0] = info[start]; + info[start] = info[start + 1]; + info[start + 1] = buf[0]; + } + } + } + } + + public: + bool ret; + private: + unsigned int start; + unsigned int end; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (machine.sanitize (c)); + } + + protected: + StateTable machine; + public: + DEFINE_SIZE_STATIC (16); +}; + +template +struct ContextualSubtable +{ + typedef typename Types::HBUINT HBUINT; + + struct EntryData + { + HBUINT16 markIndex; /* Index of the substitution table for the + * marked glyph (use 0xFFFF for none). */ + HBUINT16 currentIndex; /* Index of the substitution table for the + * current glyph (use 0xFFFF for none). */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ + }; + + driver_context_t (const ContextualSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + mark_set (false), + mark (0), + table (table_), + subs (table+table->substitutionTables) {} + + bool is_actionable (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + if (buffer->idx == buffer->len && !mark_set) + return false; + + return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + /* Looks like CoreText applies neither mark nor current substitution for + * end-of-text if mark was not explicitly set. */ + if (buffer->idx == buffer->len && !mark_set) + return; + + const HBGlyphID *replacement; + + replacement = nullptr; + if (Types::extended) + { + if (entry.data.markIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.markIndex]; + replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); + buffer->info[mark].codepoint = *replacement; + ret = true; + } + + replacement = nullptr; + unsigned int idx = hb_min (buffer->idx, buffer->len - 1); + if (Types::extended) + { + if (entry.data.currentIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.currentIndex]; + replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->info[idx].codepoint = *replacement; + ret = true; + } + + if (entry.flags & SetMark) + { + mark_set = true; + mark = buffer->idx; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + bool mark_set; + unsigned int mark; + const ContextualSubtable *table; + const UnsizedOffsetListOf, HBUINT, false> &subs; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + unsigned int num_entries = 0; + if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); + + if (!Types::extended) + return_trace (substitutionTables.sanitize (c, this, 0)); + + unsigned int num_lookups = 0; + + const Entry *entries = machine.get_entries (); + for (unsigned int i = 0; i < num_entries; i++) + { + const EntryData &data = entries[i].data; + + if (data.markIndex != 0xFFFF) + num_lookups = hb_max (num_lookups, 1 + data.markIndex); + if (data.currentIndex != 0xFFFF) + num_lookups = hb_max (num_lookups, 1 + data.currentIndex); + } + + return_trace (substitutionTables.sanitize (c, this, num_lookups)); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT, false>, HBUINT> + substitutionTables; + public: + DEFINE_SIZE_STATIC (20); +}; + + +template +struct LigatureEntry; + +template <> +struct LigatureEntry +{ + enum Flags + { + SetComponent = 0x8000, /* Push this glyph onto the component stack for + * eventual processing. */ + DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the + next iteration. */ + PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature + * group. */ + Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ + }; + + struct EntryData + { + HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry + * for processing this group, if indicated + * by the flags. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.flags & PerformAction; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.data.ligActionIndex; } +}; +template <> +struct LigatureEntry +{ + enum Flags + { + SetComponent = 0x8000, /* Push this glyph onto the component stack for + * eventual processing. */ + DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the + next iteration. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * ligature action list. This value must be a + * multiple of 4. */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + + +template +struct LigatureSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef LigatureEntry LigatureEntryT; + typedef typename LigatureEntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum + { + DontAdvance = LigatureEntryT::DontAdvance, + }; + enum LigActionFlags + { + LigActionLast = 0x80000000, /* This is the last action in the list. This also + * implies storage. */ + LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index + * in the ligature table in place of the marked + * (i.e. currently-popped) glyph. */ + LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits + * and added to the glyph ID, resulting in an index + * into the component table. */ + }; + + driver_context_t (const LigatureSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + table (table_), + ligAction (table+table->ligAction), + component (table+table->component), + ligature (table+table->ligature), + match_length (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return LigatureEntryT::performAction (entry); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); + if (entry.flags & LigatureEntryT::SetComponent) + { + /* Never mark same index twice, in case DontAdvance was used... */ + if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) + match_length--; + + match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; + DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); + } + + if (LigatureEntryT::performAction (entry)) + { + DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); + unsigned int end = buffer->out_len; + + if (unlikely (!match_length)) + return; + + if (buffer->idx >= buffer->len) + return; /* TODO Work on previous instead? */ + + unsigned int cursor = match_length; + + unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); + action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); + const HBUINT32 *actionData = &ligAction[action_idx]; + + unsigned int ligature_idx = 0; + unsigned int action; + do + { + if (unlikely (!cursor)) + { + /* Stack underflow. Clear the stack. */ + DEBUG_MSG (APPLY, nullptr, "Stack underflow"); + match_length = 0; + break; + } + + DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); + buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]); + + if (unlikely (!actionData->sanitize (&c->sanitizer))) break; + action = *actionData; + + uint32_t uoffset = action & LigActionOffset; + if (uoffset & 0x20000000) + uoffset |= 0xC0000000; /* Sign-extend. */ + int32_t offset = (int32_t) uoffset; + unsigned int component_idx = buffer->cur().codepoint + offset; + component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); + const HBUINT16 &componentData = component[component_idx]; + if (unlikely (!componentData.sanitize (&c->sanitizer))) break; + ligature_idx += componentData; + + DEBUG_MSG (APPLY, nullptr, "Action store %u last %u", + bool (action & LigActionStore), + bool (action & LigActionLast)); + if (action & (LigActionStore | LigActionLast)) + { + ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); + const HBGlyphID &ligatureData = ligature[ligature_idx]; + if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; + hb_codepoint_t lig = ligatureData; + + DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); + buffer->replace_glyph (lig); + + unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; + /* Now go and delete all subsequent components. */ + while (match_length - 1u > cursor) + { + DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); + buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]); + buffer->replace_glyph (DELETED_GLYPH); + } + + buffer->move_to (lig_end); + buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len); + } + + actionData++; + } + while (!(action & LigActionLast)); + buffer->move_to (end); + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const LigatureSubtable *table; + const UnsizedArrayOf &ligAction; + const UnsizedArrayOf &component; + const UnsizedArrayOf &ligature; + unsigned int match_length; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + ligAction && component && ligature); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + ligAction; /* Offset to the ligature action table. */ + NNOffsetTo, HBUINT> + component; /* Offset to the component table. */ + NNOffsetTo, HBUINT> + ligature; /* Offset to the actual ligature lists. */ + public: + DEFINE_SIZE_STATIC (28); +}; + +template +struct NoncontextualSubtable +{ + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + bool ret = false; + unsigned int num_glyphs = c->face->get_num_glyphs (); + + hb_glyph_info_t *info = c->buffer->info; + unsigned int count = c->buffer->len; + for (unsigned int i = 0; i < count; i++) + { + const HBGlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs); + if (replacement) + { + info[i].codepoint = *replacement; + ret = true; + } + } + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + protected: + Lookup substitute; + public: + DEFINE_SIZE_MIN (2); +}; + +template +struct InsertionSubtable +{ + typedef typename Types::HBUINT HBUINT; + + struct EntryData + { + HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table. + * The number of glyphs to be inserted is contained + * in the currentInsertCount field in the flags. + * A value of 0xFFFF indicates no insertion is to + * be done. */ + HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table. + * The number of glyphs to be inserted is contained + * in the markedInsertCount field in the flags. + * A value of 0xFFFF indicates no insertion is to + * be done. */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum Flags + { + SetMark = 0x8000, /* If set, mark the current glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. This does not mean + * that the glyph pointed to is the same one as + * before. If you've made insertions immediately + * downstream of the current glyph, the next glyph + * processed would in fact be the first one + * inserted. */ + CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero, + * then the specified glyph list will be inserted + * as a kashida-like insertion, either before or + * after the current glyph (depending on the state + * of the currentInsertBefore flag). If clear, and + * the currentInsertList is nonzero, then the + * specified glyph list will be inserted as a + * split-vowel-like insertion, either before or + * after the current glyph (depending on the state + * of the currentInsertBefore flag). */ + MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero, + * then the specified glyph list will be inserted + * as a kashida-like insertion, either before or + * after the marked glyph (depending on the state + * of the markedInsertBefore flag). If clear, and + * the markedInsertList is nonzero, then the + * specified glyph list will be inserted as a + * split-vowel-like insertion, either before or + * after the marked glyph (depending on the state + * of the markedInsertBefore flag). */ + CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made + * to the left of the current glyph. If clear, + * they're made to the right of the current glyph. */ + MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be + * made to the left of the marked glyph. If clear, + * they're made to the right of the marked glyph. */ + CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the + * number of glyphs to insert at the current + * position. Since zero means no insertions, the + * largest number of insertions at any given + * current location is 31 glyphs. */ + MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the + * number of glyphs to insert at the marked + * position. Since zero means no insertions, the + * largest number of insertions at any given + * marked location is 31 glyphs. */ + }; + + driver_context_t (const InsertionSubtable *table, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + mark (0), + insertionAction (table+table->insertionAction) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && + (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + unsigned mark_loc = buffer->out_len; + + if (entry.data.markedInsertIndex != 0xFFFF) + { + unsigned int count = (flags & MarkedInsertCount); + if (unlikely ((buffer->max_ops -= count) <= 0)) return; + unsigned int start = entry.data.markedInsertIndex; + const HBGlyphID *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & MarkedInsertBefore; + + unsigned int end = buffer->out_len; + buffer->move_to (mark); + + if (buffer->idx < buffer->len && !before) + buffer->copy_glyph (); + /* TODO We ignore KashidaLike setting. */ + for (unsigned int i = 0; i < count; i++) + buffer->output_glyph (glyphs[i]); + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + buffer->move_to (end + count); + + buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len)); + } + + if (flags & SetMark) + mark = mark_loc; + + if (entry.data.currentInsertIndex != 0xFFFF) + { + unsigned int count = (flags & CurrentInsertCount) >> 5; + if (unlikely ((buffer->max_ops -= count) <= 0)) return; + unsigned int start = entry.data.currentInsertIndex; + const HBGlyphID *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & CurrentInsertBefore; + + unsigned int end = buffer->out_len; + + if (buffer->idx < buffer->len && !before) + buffer->copy_glyph (); + /* TODO We ignore KashidaLike setting. */ + for (unsigned int i = 0; i < count; i++) + buffer->output_glyph (glyphs[i]); + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + /* Humm. Not sure where to move to. There's this wording under + * DontAdvance flag: + * + * "If set, don't update the glyph index before going to the new state. + * This does not mean that the glyph pointed to is the same one as + * before. If you've made insertions immediately downstream of the + * current glyph, the next glyph processed would in fact be the first + * one inserted." + * + * This suggests that if DontAdvance is NOT set, we should move to + * end+count. If it *was*, then move to end, such that newly inserted + * glyphs are now visible. + * + * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 + */ + buffer->move_to ((flags & DontAdvance) ? end : end + count); + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + unsigned int mark; + const UnsizedArrayOf &insertionAction; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + insertionAction); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + insertionAction; /* Byte offset from stateHeader to the start of + * the insertion glyph table. */ + public: + DEFINE_SIZE_STATIC (20); +}; + + +struct Feature +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 featureType; /* The type of feature. */ + HBUINT16 featureSetting; /* The feature's setting (aka selector). */ + HBUINT32 enableFlags; /* Flags for the settings that this feature + * and setting enables. */ + HBUINT32 disableFlags; /* Complement of flags for the settings that this + * feature and setting disable. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +template +struct ChainSubtable +{ + typedef typename Types::HBUINT HBUINT; + + template + friend struct Chain; + + unsigned int get_size () const { return length; } + unsigned int get_type () const { return coverage & 0xFF; } + unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } + + enum Coverage + { + Vertical = 0x80, /* If set, this subtable will only be applied + * to vertical text. If clear, this subtable + * will only be applied to horizontal text. */ + Backwards = 0x40, /* If set, this subtable will process glyphs + * in descending order. If clear, it will + * process the glyphs in ascending order. */ + AllDirections = 0x20, /* If set, this subtable will be applied to + * both horizontal and vertical text (i.e. + * the state of bit 0x80000000 is ignored). */ + Logical = 0x10, /* If set, this subtable will process glyphs + * in logical order (or reverse logical order, + * depending on the value of bit 0x80000000). */ + }; + enum Type + { + Rearrangement = 0, + Contextual = 1, + Ligature = 2, + Noncontextual = 4, + Insertion = 5 + }; + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case Rearrangement: return_trace (c->dispatch (u.rearrangement, hb_forward (ds)...)); + case Contextual: return_trace (c->dispatch (u.contextual, hb_forward (ds)...)); + case Ligature: return_trace (c->dispatch (u.ligature, hb_forward (ds)...)); + case Noncontextual: return_trace (c->dispatch (u.noncontextual, hb_forward (ds)...)); + case Insertion: return_trace (c->dispatch (u.insertion, hb_forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_sanitize_with_object_t with (&c->sanitizer, this); + return_trace (dispatch (c)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length <= min_size || + !c->check_range (this, length)) + return_trace (false); + + hb_sanitize_with_object_t with (c, this); + return_trace (dispatch (c)); + } + + protected: + HBUINT length; /* Total subtable length, including this header. */ + HBUINT coverage; /* Coverage flags and subtable type. */ + HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ + union { + RearrangementSubtable rearrangement; + ContextualSubtable contextual; + LigatureSubtable ligature; + NoncontextualSubtable noncontextual; + InsertionSubtable insertion; + } u; + public: + DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); +}; + +template +struct Chain +{ + typedef typename Types::HBUINT HBUINT; + + hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const + { + hb_mask_t flags = defaultFlags; + { + unsigned int count = featureCount; + for (unsigned i = 0; i < count; i++) + { + const Feature &feature = featureZ[i]; + hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; + hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; + retry: + // Check whether this type/setting pair was requested in the map, and if so, apply its flags. + // (The search here only looks at the type and setting fields of feature_info_t.) + hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 }; + if (map->features.bsearch (info)) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) + { + /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ + type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; + setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; + goto retry; + } + } + } + return flags; + } + + void apply (hb_aat_apply_context_t *c, + hb_mask_t flags) const + { + const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (!(subtable->subFeatureFlags & flags)) + goto skip; + + if (!(subtable->get_coverage() & ChainSubtable::AllDirections) && + HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != + bool (subtable->get_coverage() & ChainSubtable::Vertical)) + goto skip; + + /* Buffer contents is always in logical direction. Determine if + * we need to reverse before applying this subtable. We reverse + * back after if we did reverse indeed. + * + * Quoting the spac: + * """ + * Bits 28 and 30 of the coverage field control the order in which + * glyphs are processed when the subtable is run by the layout engine. + * Bit 28 is used to indicate if the glyph processing direction is + * the same as logical order or layout order. Bit 30 is used to + * indicate whether glyphs are processed forwards or backwards within + * that order. + + Bit 30 Bit 28 Interpretation for Horizontal Text + 0 0 The subtable is processed in layout order + (the same order as the glyphs, which is + always left-to-right). + 1 0 The subtable is processed in reverse layout order + (the order opposite that of the glyphs, which is + always right-to-left). + 0 1 The subtable is processed in logical order + (the same order as the characters, which may be + left-to-right or right-to-left). + 1 1 The subtable is processed in reverse logical order + (the order opposite that of the characters, which + may be right-to-left or left-to-right). + */ + reverse = subtable->get_coverage () & ChainSubtable::Logical ? + bool (subtable->get_coverage () & ChainSubtable::Backwards) : + bool (subtable->get_coverage () & ChainSubtable::Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start chainsubtable %d", c->lookup_index)) + goto skip; + + if (reverse) + c->buffer->reverse (); + + subtable->apply (c); + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end chainsubtable %d", c->lookup_index); + + if (unlikely (!c->buffer->successful)) return; + + skip: + subtable = &StructAfter> (*subtable); + c->set_lookup_index (c->lookup_index + 1); + } + } + + unsigned int get_size () const { return length; } + + bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length < min_size || + !c->check_range (this, length)) + return_trace (false); + + if (!c->check_array (featureZ.arrayZ, featureCount)) + return_trace (false); + + const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + if (!subtable->sanitize (c)) + return_trace (false); + subtable = &StructAfter> (*subtable); + } + + return_trace (true); + } + + protected: + HBUINT32 defaultFlags; /* The default specification for subtables. */ + HBUINT32 length; /* Total byte count, including this header. */ + HBUINT featureCount; /* Number of feature subtable entries. */ + HBUINT subtableCount; /* The number of subtables in the chain. */ + + UnsizedArrayOf featureZ; /* Features. */ +/*ChainSubtable firstSubtable;*//* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); +}; + + +/* + * The 'mort'/'morx' Table + */ + +template +struct mortmorx +{ + static constexpr hb_tag_t tableTag = TAG; + + bool has_data () const { return version != 0; } + + void compile_flags (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) const + { + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + map->chain_flags.push (chain->compile_flags (mapper)); + chain = &StructAfter> (*chain); + } + } + + void apply (hb_aat_apply_context_t *c) const + { + if (unlikely (!c->buffer->successful)) return; + c->set_lookup_index (0); + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + chain->apply (c, c->plan->aat_map.chain_flags[i]); + if (unlikely (!c->buffer->successful)) return; + chain = &StructAfter> (*chain); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) + return_trace (false); + + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + if (!chain->sanitize (c, version)) + return_trace (false); + chain = &StructAfter> (*chain); + } + + return_trace (true); + } + + protected: + HBUINT16 version; /* Version number of the glyph metamorphosis table. + * 1, 2, or 3. */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 chainCount; /* Number of metamorphosis chains contained in this + * table. */ + Chain firstChain; /* Chains. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct morx : mortmorx {}; +struct mort : mortmorx {}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-opbd-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-opbd-table.hh new file mode 100644 index 00000000000..8c04a6482ff --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-opbd-table.hh @@ -0,0 +1,173 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH +#define HB_AAT_LAYOUT_OPBD_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-open-type.hh" + +/* + * opbd -- Optical Bounds + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html + */ +#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d') + + +namespace AAT { + +struct OpticalBounds +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + FWORD leftSide; + FWORD topSide; + FWORD rightSide; + FWORD bottomSide; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct opbdFormat0 +{ + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents, const void *base) const + { + const OffsetTo *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ()); + if (!bounds_offset) return false; + const OpticalBounds &bounds = base+*bounds_offset; + + if (extents) + *extents = { + font->em_scale_x (bounds.leftSide), + font->em_scale_y (bounds.topSide), + font->em_scale_x (bounds.rightSide), + font->em_scale_y (bounds.bottomSide) + }; + return true; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base))); + } + + protected: + Lookup> + lookupTable; /* Lookup table associating glyphs with the four + * int16 values for the left-side, top-side, + * right-side, and bottom-side optical bounds. */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct opbdFormat1 +{ + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents, const void *base) const + { + const OffsetTo *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ()); + if (!bounds_offset) return false; + const OpticalBounds &bounds = base+*bounds_offset; + + hb_position_t left = 0, top = 0, right = 0, bottom = 0, ignore; + if (font->get_glyph_contour_point (glyph_id, bounds.leftSide, &left, &ignore) || + font->get_glyph_contour_point (glyph_id, bounds.topSide, &ignore, &top) || + font->get_glyph_contour_point (glyph_id, bounds.rightSide, &right, &ignore) || + font->get_glyph_contour_point (glyph_id, bounds.bottomSide, &ignore, &bottom)) + { + if (extents) + *extents = {left, top, right, bottom}; + return true; + } + return false; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base))); + } + + protected: + Lookup> + lookupTable; /* Lookup table associating glyphs with the four + * int16 values for the left-side, top-side, + * right-side, and bottom-side optical bounds. */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct opbd +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_opbd; + + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents) const + { + switch (format) + { + case 0: return u.format0.get_bounds (font, glyph_id, extents, this); + case 1: return u.format1.get_bounds (font, glyph_id, extents, this); + default:return false; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this) || version.major != 1)) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, this)); + case 1: return_trace (u.format1.sanitize (c, this)); + default:return_trace (true); + } + } + + protected: + FixedVersion<>version; /* Version number of the optical bounds + * table (0x00010000 for the current version). */ + HBUINT16 format; /* Format of the optical bounds table. + * Format 0 indicates distance and Format 1 indicates + * control point. */ + union { + opbdFormat0 format0; + opbdFormat1 format1; + } u; + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_OPBD_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh new file mode 100644 index 00000000000..baa1c72020f --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh @@ -0,0 +1,230 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH +#define HB_AAT_LAYOUT_TRAK_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" + +/* + * trak -- Tracking + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html + */ +#define HB_AAT_TAG_trak HB_TAG('t','r','a','k') + + +namespace AAT { + + +struct TrackTableEntry +{ + friend struct TrackData; + + float get_track_value () const { return track.to_float (); } + + int get_value (const void *base, unsigned int index, + unsigned int table_size) const + { return (base+valuesZ).as_array (table_size)[index]; } + + public: + bool sanitize (hb_sanitize_context_t *c, const void *base, + unsigned int table_size) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (valuesZ.sanitize (c, base, table_size)))); + } + + protected: + HBFixed track; /* Track value for this record. */ + NameID trackNameID; /* The 'name' table index for this track. + * (a short word or phrase like "loose" + * or "very tight") */ + NNOffsetTo> + valuesZ; /* Offset from start of tracking table to + * per-size tracking values for this track. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct TrackData +{ + float interpolate_at (unsigned int idx, + float target_size, + const TrackTableEntry &trackTableEntry, + const void *base) const + { + unsigned int sizes = nSizes; + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + + float s0 = size_table[idx].to_float (); + float s1 = size_table[idx + 1].to_float (); + float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0); + return t * trackTableEntry.get_value (base, idx + 1, sizes) + + (1.f - t) * trackTableEntry.get_value (base, idx, sizes); + } + + int get_tracking (const void *base, float ptem) const + { + /* + * Choose track. + */ + const TrackTableEntry *trackTableEntry = nullptr; + unsigned int count = nTracks; + for (unsigned int i = 0; i < count; i++) + { + /* Note: Seems like the track entries are sorted by values. But the + * spec doesn't explicitly say that. It just mentions it in the example. */ + + /* For now we only seek for track entries with zero tracking value */ + + if (trackTable[i].get_track_value () == 0.f) + { + trackTableEntry = &trackTable[i]; + break; + } + } + if (!trackTableEntry) return 0.; + + /* + * Choose size. + */ + unsigned int sizes = nSizes; + if (!sizes) return 0.; + if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes); + + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + unsigned int size_index; + for (size_index = 0; size_index < sizes - 1; size_index++) + if (size_table[size_index].to_float () >= ptem) + break; + + return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem, + *trackTableEntry, base)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + sizeTable.sanitize (c, base, nSizes) && + trackTable.sanitize (c, nTracks, base, nSizes))); + } + + protected: + HBUINT16 nTracks; /* Number of separate tracks included in this table. */ + HBUINT16 nSizes; /* Number of point sizes included in this table. */ + LNNOffsetTo> + sizeTable; /* Offset from start of the tracking table to + * Array[nSizes] of size values.. */ + UnsizedArrayOf + trackTable; /* Array[nTracks] of TrackTableEntry records. */ + + public: + DEFINE_SIZE_ARRAY (8, trackTable); +}; + +struct trak +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_trak; + + bool has_data () const { return version.to_int (); } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + hb_mask_t trak_mask = c->plan->trak_mask; + + const float ptem = c->font->ptem; + if (unlikely (ptem <= 0.f)) + return_trace (false); + + hb_buffer_t *buffer = c->buffer; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + const TrackData &trackData = this+horizData; + int tracking = trackData.get_tracking (this, ptem); + hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_x (tracking); + foreach_grapheme (buffer, start, end) + { + if (!(buffer->info[start].mask & trak_mask)) continue; + buffer->pos[start].x_advance += advance_to_add; + buffer->pos[start].x_offset += offset_to_add; + } + } + else + { + const TrackData &trackData = this+vertData; + int tracking = trackData.get_tracking (this, ptem); + hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_y (tracking); + foreach_grapheme (buffer, start, end) + { + if (!(buffer->info[start].mask & trak_mask)) continue; + buffer->pos[start].y_advance += advance_to_add; + buffer->pos[start].y_offset += offset_to_add; + } + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (likely (c->check_struct (this) && + version.major == 1 && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this))); + } + + protected: + FixedVersion<>version; /* Version of the tracking table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the tracking table (set to 0). */ + OffsetTo + horizData; /* Offset from start of tracking table to TrackData + * for horizontal text (or 0 if none). */ + OffsetTo + vertData; /* Offset from start of tracking table to TrackData + * for vertical text (or 0 if none). */ + HBUINT16 reserved; /* Reserved. Set to 0. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_TRAK_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout.cc b/thirdparty/harfbuzz/src/hb-aat-layout.cc new file mode 100644 index 00000000000..fac510e9e60 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout.cc @@ -0,0 +1,382 @@ +/* + * Copyright © 2017 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-aat-layout.hh" +#include "hb-aat-layout-ankr-table.hh" +#include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-feat-table.hh" +#include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-kerx-table.hh" +#include "hb-aat-layout-morx-table.hh" +#include "hb-aat-layout-trak-table.hh" +#include "hb-aat-ltag-table.hh" + + +/* + * hb_aat_apply_context_t + */ + +/* Note: This context is used for kerning, even without AAT, hence the condition. */ +#if !defined(HB_NO_AAT) || !defined(HB_NO_OT_KERN) + +AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob) : + plan (plan_), + font (font_), + face (font->face), + buffer (buffer_), + sanitizer (), + ankr_table (&Null (AAT::ankr)), + lookup_index (0) +{ + sanitizer.init (blob); + sanitizer.set_num_glyphs (face->get_num_glyphs ()); + sanitizer.start_processing (); + sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX); +} + +AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t () +{ sanitizer.end_processing (); } + +void +AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_) +{ ankr_table = ankr_table_; } + +#endif + + +/** + * SECTION:hb-aat-layout + * @title: hb-aat-layout + * @short_description: Apple Advanced Typography Layout + * @include: hb-aat.h + * + * Functions for querying OpenType Layout features in the font face. + **/ + + +#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT) + +/* Table data courtesy of Apple. Converted from mnemonics to integers + * when moving to this file. */ +static const hb_aat_feature_mapping_t feature_mappings[] = +{ + {HB_TAG ('a','f','r','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS}, + {HB_TAG ('c','2','p','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE}, + {HB_TAG ('c','2','s','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE}, + {HB_TAG ('c','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF}, + {HB_TAG ('c','a','s','e'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF}, + {HB_TAG ('c','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF}, + {HB_TAG ('c','p','s','p'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF}, + {HB_TAG ('c','s','w','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF}, + {HB_TAG ('d','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF}, + {HB_TAG ('e','x','p','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('f','r','a','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS}, + {HB_TAG ('f','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('h','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('h','i','s','t'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF}, + {HB_TAG ('h','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF}, + {HB_TAG ('h','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF}, + {HB_TAG ('h','n','g','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION, HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION}, + {HB_TAG ('h','o','j','o'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('h','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('i','t','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF}, + {HB_TAG ('j','p','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','7','8'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','8','3'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','9','0'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('l','i','g','a'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF}, + {HB_TAG ('l','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2}, + {HB_TAG ('m','g','r','k'), HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF}, + {HB_TAG ('n','l','c','k'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('o','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2}, + {HB_TAG ('o','r','d','n'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('p','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('p','c','a','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, + {HB_TAG ('p','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, + {HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF}, + {HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, + {HB_TAG ('s','m','p','l'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('s','s','0','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF}, + {HB_TAG ('s','s','0','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF}, + {HB_TAG ('s','s','0','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF}, + {HB_TAG ('s','s','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF}, + {HB_TAG ('s','s','0','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF}, + {HB_TAG ('s','s','0','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF}, + {HB_TAG ('s','s','0','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF}, + {HB_TAG ('s','s','0','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF}, + {HB_TAG ('s','s','0','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF}, + {HB_TAG ('s','s','1','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF}, + {HB_TAG ('s','s','1','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF}, + {HB_TAG ('s','s','1','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF}, + {HB_TAG ('s','s','1','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF}, + {HB_TAG ('s','s','1','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF}, + {HB_TAG ('s','s','1','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF}, + {HB_TAG ('s','s','1','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF}, + {HB_TAG ('s','s','1','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF}, + {HB_TAG ('s','s','1','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF}, + {HB_TAG ('s','s','1','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF}, + {HB_TAG ('s','s','2','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF}, + {HB_TAG ('s','u','b','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','u','p','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','w','s','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF}, + {HB_TAG ('t','i','t','l'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS}, + {HB_TAG ('t','n','a','m'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('t','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, + {HB_TAG ('t','r','a','d'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('t','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('u','n','i','c'), HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE, (hb_aat_layout_feature_selector_t) 14, (hb_aat_layout_feature_selector_t) 15}, + {HB_TAG ('v','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','e','r','t'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF}, + {HB_TAG ('v','h','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF}, + {HB_TAG ('v','p','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','r','t','2'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF}, + {HB_TAG ('z','e','r','o'), HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF}, +}; + +const hb_aat_feature_mapping_t * +hb_aat_layout_find_feature_mapping (hb_tag_t tag) +{ + return hb_sorted_array (feature_mappings).bsearch (tag); +} +#endif + + +#ifndef HB_NO_AAT + +/* + * mort/morx/kerx/trak + */ + + +void +hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) +{ + const AAT::morx& morx = *mapper->face->table.morx; + if (morx.has_data ()) + { + morx.compile_flags (mapper, map); + return; + } + + const AAT::mort& mort = *mapper->face->table.mort; + if (mort.has_data ()) + { + mort.compile_flags (mapper, map); + return; + } +} + + +/* + * hb_aat_layout_has_substitution: + * @face: + * + * Returns: + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_substitution (hb_face_t *face) +{ + return face->table.morx->has_data () || + face->table.mort->has_data (); +} + +void +hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_blob_t *morx_blob = font->face->table.morx.get_blob (); + const AAT::morx& morx = *morx_blob->as (); + if (morx.has_data ()) + { + AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob); + morx.apply (&c); + return; + } + + hb_blob_t *mort_blob = font->face->table.mort.get_blob (); + const AAT::mort& mort = *mort_blob->as (); + if (mort.has_data ()) + { + AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob); + mort.apply (&c); + return; + } +} + +void +hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int i = 0; i < count; i++) + if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH)) + pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; +} + +static bool +is_deleted_glyph (const hb_glyph_info_t *info) +{ + return info->codepoint == AAT::DELETED_GLYPH; +} + +void +hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer) +{ + hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph); +} + +/* + * hb_aat_layout_has_positioning: + * @face: + * + * Returns: + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_positioning (hb_face_t *face) +{ + return face->table.kerx->has_data (); +} + +void +hb_aat_layout_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_blob_t *kerx_blob = font->face->table.kerx.get_blob (); + const AAT::kerx& kerx = *kerx_blob->as (); + + AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob); + c.set_ankr_table (font->face->table.ankr.get ()); + kerx.apply (&c); +} + + +/* + * hb_aat_layout_has_tracking: + * @face: + * + * Returns: + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face) +{ + return face->table.trak->has_data (); +} + +void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const AAT::trak& trak = *font->face->table.trak; + + AAT::hb_aat_apply_context_t c (plan, font, buffer); + trak.apply (&c); +} + +/** + * hb_aat_layout_get_feature_types: + * @face: a face object + * @start_offset: iteration's start offset + * @feature_count:(inout) (allow-none): buffer size as input, filled size as output + * @features: (out caller-allocates) (array length=feature_count): features buffer + * + * Return value: Number of all available feature types. + * + * Since: 2.2.0 + */ +unsigned int +hb_aat_layout_get_feature_types (hb_face_t *face, + unsigned int start_offset, + unsigned int *feature_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */) +{ + return face->table.feat->get_feature_types (start_offset, feature_count, features); +} + +/** + * hb_aat_layout_feature_type_get_name_id: + * @face: a face object + * @feature_type: feature id + * + * Return value: Name ID index + * + * Since: 2.2.0 + */ +hb_ot_name_id_t +hb_aat_layout_feature_type_get_name_id (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type) +{ + return face->table.feat->get_feature_name_id (feature_type); +} + +/** + * hb_aat_layout_feature_type_get_selectors: + * @face: a face object + * @feature_type: feature id + * @start_offset: iteration's start offset + * @selector_count: (inout) (allow-none): buffer size as input, filled size as output + * @selectors: (out caller-allocates) (array length=selector_count): settings buffer + * @default_index: (out) (allow-none): index of default selector if any + * + * If upon return, @default_index is set to #HB_AAT_LAYOUT_NO_SELECTOR_INDEX, then + * the feature type is non-exclusive. Otherwise, @default_index is the index of + * the selector that is selected by default. + * + * Return value: Number of all available feature selectors. + * + * Since: 2.2.0 + */ +unsigned int +hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selector_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */) +{ + return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index); +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-aat-layout.h b/thirdparty/harfbuzz/src/hb-aat-layout.h new file mode 100644 index 00000000000..b617e8b703d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout.h @@ -0,0 +1,486 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_H_IN +#error "Include instead." +#endif + +#ifndef HB_AAT_LAYOUT_H +#define HB_AAT_LAYOUT_H + +#include "hb.h" + +#include "hb-ot.h" + +HB_BEGIN_DECLS + +/** + * hb_aat_layout_feature_type_t: + * + * + * Since: 2.2.0 + */ +typedef enum +{ + HB_AAT_LAYOUT_FEATURE_TYPE_INVALID = 0xFFFF, + + HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC = 0, + HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES = 1, + HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION = 2, + HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE = 3, + HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION = 4, + HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT = 5, + HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING = 6, + HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE = 8, + HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE = 9, + HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION = 10, + HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS = 11, + HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE = 13, + HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS = 14, + HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS = 15, + HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE = 16, + HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES = 17, + HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE = 18, + HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS = 19, + HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE = 20, + HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE = 21, + HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING = 22, + HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION = 23, + HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE = 24, + HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE = 25, + HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE = 26, + HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE = 27, + HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA = 28, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE = 29, + HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE = 30, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE = 31, + HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN = 32, + HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT = 33, + HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA = 34, + HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES = 35, + HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES = 36, + HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE = 37, + HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE = 38, + HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE = 39, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE = 103, + + _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_aat_layout_feature_type_t; + +/** + * hb_aat_layout_feature_selector_t: + * + * + * Since: 2.2.0 + */ +typedef enum +{ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID = 0xFFFF, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON = 14, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF = 15, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON = 16, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF = 17, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON = 18, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF = 19, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON = 20, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF = 21, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE = 0, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS = 1, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE = 2, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS = 3, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS = 4, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS = 5, /* deprecated */ + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF = 9, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS = 4, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF = 11, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF = 11, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS = 6, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES = 0, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1 = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2 = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3 = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4 = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5 = 4, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS = 14, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT = 6, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE = 9, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION= 10, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON = 14, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF = 15, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON = 16, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF = 17, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON = 18, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF = 19, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON = 20, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF = 21, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON = 22, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF = 23, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON = 24, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF = 25, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON = 26, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF = 27, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON = 28, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF = 29, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON = 30, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF = 31, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON = 32, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF = 33, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON = 34, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF = 35, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON = 36, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF = 37, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON = 38, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF = 39, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON = 40, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF = 41, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF= 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN = 3, + + _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_aat_layout_feature_selector_t; + +HB_EXTERN unsigned int +hb_aat_layout_get_feature_types (hb_face_t *face, + unsigned int start_offset, + unsigned int *feature_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */); + +HB_EXTERN hb_ot_name_id_t +hb_aat_layout_feature_type_get_name_id (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type); + +typedef struct hb_aat_layout_feature_selector_info_t +{ + hb_ot_name_id_t name_id; + hb_aat_layout_feature_selector_t enable; + hb_aat_layout_feature_selector_t disable; + /*< private >*/ + unsigned int reserved; +} hb_aat_layout_feature_selector_info_t; + +#define HB_AAT_LAYOUT_NO_SELECTOR_INDEX 0xFFFFu + +HB_EXTERN unsigned int +hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selector_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */); + + +/* + * morx/mort + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_substitution (hb_face_t *face); + + +/* + * kerx + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_positioning (hb_face_t *face); + + +/* + * trak + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face); + + +HB_END_DECLS + +#endif /* HB_AAT_LAYOUT_H */ diff --git a/thirdparty/harfbuzz/src/hb-aat-layout.hh b/thirdparty/harfbuzz/src/hb-aat-layout.hh new file mode 100644 index 00000000000..5e4e3bda154 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-layout.hh @@ -0,0 +1,75 @@ +/* + * Copyright © 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_HH +#define HB_AAT_LAYOUT_HH + +#include "hb.hh" + +#include "hb-ot-shape.hh" +#include "hb-aat-ltag-table.hh" + +struct hb_aat_feature_mapping_t +{ + hb_tag_t otFeatureTag; + hb_aat_layout_feature_type_t aatFeatureType; + hb_aat_layout_feature_selector_t selectorToEnable; + hb_aat_layout_feature_selector_t selectorToDisable; + + int cmp (hb_tag_t key) const + { return key < otFeatureTag ? -1 : key > otFeatureTag ? 1 : 0; } +}; + +HB_INTERNAL const hb_aat_feature_mapping_t * +hb_aat_layout_find_feature_mapping (hb_tag_t tag); + +HB_INTERNAL void +hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map); + +HB_INTERNAL void +hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_AAT_LAYOUT_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-ltag-table.hh b/thirdparty/harfbuzz/src/hb-aat-ltag-table.hh new file mode 100644 index 00000000000..711f9aa6c18 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-ltag-table.hh @@ -0,0 +1,92 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LTAG_TABLE_HH +#define HB_AAT_LTAG_TABLE_HH + +#include "hb-open-type.hh" + +/* + * ltag -- Language Tag + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html + */ +#define HB_AAT_TAG_ltag HB_TAG('l','t','a','g') + + +namespace AAT { + +using namespace OT; + + +struct FTStringRange +{ + friend struct ltag; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (base+tag).sanitize (c, length)); + } + + protected: + NNOffsetTo> + tag; /* Offset from the start of the table to + * the beginning of the string */ + HBUINT16 length; /* String length (in bytes) */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct ltag +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_ltag; + + hb_language_t get_language (unsigned int i) const + { + const FTStringRange &range = tagRanges[i]; + return hb_language_from_string ((const char *) (this+range.tag).arrayZ, + range.length); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version >= 1 && + tagRanges.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Table version; currently 1 */ + HBUINT32 flags; /* Table flags; currently none defined */ + LArrayOf + tagRanges; /* Range for each tag's string */ + public: + DEFINE_SIZE_ARRAY (12, tagRanges); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LTAG_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat-map.cc b/thirdparty/harfbuzz/src/hb-aat-map.cc new file mode 100644 index 00000000000..2c38c350296 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-map.cc @@ -0,0 +1,102 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2010,2011,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_AAT_SHAPE + +#include "hb-aat-map.hh" + +#include "hb-aat-layout.hh" +#include "hb-aat-layout-feat-table.hh" + + +void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value) +{ + if (!face->table.feat->has_data ()) return; + + if (tag == HB_TAG ('a','a','l','t')) + { + if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES)) + return; + feature_info_t *info = features.push(); + info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES; + info->setting = (hb_aat_layout_feature_selector_t) value; + info->seq = features.length; + info->is_exclusive = true; + return; + } + + const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag); + if (!mapping) return; + + const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType); + if (!feature->has_data ()) + { + /* Special case: Chain::compile_flags will fall back to the deprecated version of + * small-caps if necessary, so we need to check for that possibility. + * https://github.com/harfbuzz/harfbuzz/issues/2307 */ + if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE && + mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS) + { + feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE); + if (!feature->has_data ()) return; + } + else return; + } + + feature_info_t *info = features.push(); + info->type = mapping->aatFeatureType; + info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable; + info->seq = features.length; + info->is_exclusive = feature->is_exclusive (); +} + +void +hb_aat_map_builder_t::compile (hb_aat_map_t &m) +{ + /* Sort features and merge duplicates */ + if (features.length) + { + features.qsort (); + unsigned int j = 0; + for (unsigned int i = 1; i < features.length; i++) + if (features[i].type != features[j].type || + /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off + * respectively, so we mask out the low-order bit when checking for "duplicates" + * (selectors referring to the same feature setting) here. */ + (!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1)))) + features[++j] = features[i]; + features.shrink (j + 1); + } + + hb_aat_layout_compile_map (this, &m); +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-aat-map.hh b/thirdparty/harfbuzz/src/hb-aat-map.hh new file mode 100644 index 00000000000..5a0fa705445 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat-map.hh @@ -0,0 +1,96 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_MAP_HH +#define HB_AAT_MAP_HH + +#include "hb.hh" + + +struct hb_aat_map_t +{ + friend struct hb_aat_map_builder_t; + + public: + + void init () + { + memset (this, 0, sizeof (*this)); + chain_flags.init (); + } + void fini () { chain_flags.fini (); } + + public: + hb_vector_t chain_flags; +}; + +struct hb_aat_map_builder_t +{ + public: + + HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_, + const hb_segment_properties_t *props_ HB_UNUSED) : + face (face_) {} + + HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1); + + HB_INTERNAL void compile (hb_aat_map_t &m); + + public: + struct feature_info_t + { + hb_aat_layout_feature_type_t type; + hb_aat_layout_feature_selector_t setting; + bool is_exclusive; + unsigned seq; /* For stable sorting only. */ + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const feature_info_t *a = (const feature_info_t *) pa; + const feature_info_t *b = (const feature_info_t *) pb; + if (a->type != b->type) return (a->type < b->type ? -1 : 1); + if (!a->is_exclusive && + (a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1); + return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); + } + + /* compares type & setting only, not is_exclusive flag or seq number */ + int cmp (const feature_info_t& f) const + { + return (f.type != type) ? (f.type < type ? -1 : 1) : + (f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0; + } + }; + + public: + hb_face_t *face; + + public: + hb_sorted_vector_t features; +}; + + +#endif /* HB_AAT_MAP_HH */ diff --git a/thirdparty/harfbuzz/src/hb-aat.h b/thirdparty/harfbuzz/src/hb-aat.h new file mode 100644 index 00000000000..c14313d1e2e --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-aat.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_H +#define HB_AAT_H +#define HB_AAT_H_IN + +#include "hb.h" + +#include "hb-aat-layout.h" + +HB_BEGIN_DECLS + +HB_END_DECLS + +#undef HB_AAT_H_IN +#endif /* HB_AAT_H */ diff --git a/thirdparty/harfbuzz/src/hb-algs.hh b/thirdparty/harfbuzz/src/hb-algs.hh new file mode 100644 index 00000000000..30b5812e124 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-algs.hh @@ -0,0 +1,1127 @@ +/* + * Copyright © 2017 Google, Inc. + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_ALGS_HH +#define HB_ALGS_HH + +#include "hb.hh" +#include "hb-meta.hh" +#include "hb-null.hh" +#include "hb-number.hh" + + +/* Encodes three unsigned integers in one 64-bit number. If the inputs have more than 21 bits, + * values will be truncated / overlap, and might not decode exactly. */ +#define HB_CODEPOINT_ENCODE3(x,y,z) (((uint64_t) (x) << 42) | ((uint64_t) (y) << 21) | (uint64_t) (z)) +#define HB_CODEPOINT_DECODE3_1(v) ((hb_codepoint_t) ((v) >> 42)) +#define HB_CODEPOINT_DECODE3_2(v) ((hb_codepoint_t) ((v) >> 21) & 0x1FFFFFu) +#define HB_CODEPOINT_DECODE3_3(v) ((hb_codepoint_t) (v) & 0x1FFFFFu) + +/* Custom encoding used by hb-ucd. */ +#define HB_CODEPOINT_ENCODE3_11_7_14(x,y,z) (((uint32_t) ((x) & 0x07FFu) << 21) | (((uint32_t) (y) & 0x007Fu) << 14) | (uint32_t) ((z) & 0x3FFFu)) +#define HB_CODEPOINT_DECODE3_11_7_14_1(v) ((hb_codepoint_t) ((v) >> 21)) +#define HB_CODEPOINT_DECODE3_11_7_14_2(v) ((hb_codepoint_t) (((v) >> 14) & 0x007Fu) | 0x0300) +#define HB_CODEPOINT_DECODE3_11_7_14_3(v) ((hb_codepoint_t) (v) & 0x3FFFu) + +struct +{ + /* Note. This is dangerous in that if it's passed an rvalue, it returns rvalue-reference. */ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN ( hb_forward (v) ) +} +HB_FUNCOBJ (hb_identity); +struct +{ + /* Like identity(), but only retains lvalue-references. Rvalues are returned as rvalues. */ + template constexpr T& + operator () (T& v) const { return v; } + + template constexpr hb_remove_reference + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_lidentity); +struct +{ + /* Like identity(), but always returns rvalue. */ + template constexpr hb_remove_reference + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_ridentity); + +struct +{ + template constexpr bool + operator () (T&& v) const { return bool (hb_forward (v)); } +} +HB_FUNCOBJ (hb_bool); + +struct +{ + private: + + template constexpr auto + impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ()) + + template constexpr auto + impl (const T& v, hb_priority<0>) const HB_AUTO_RETURN + ( + /* Knuth's multiplicative method: */ + (uint32_t) v * 2654435761u + ) + + public: + + template constexpr auto + operator () (const T& v) const HB_RETURN (uint32_t, impl (v, hb_prioritize)) +} +HB_FUNCOBJ (hb_hash); + + +struct +{ + private: + + /* Pointer-to-member-function. */ + template auto + impl (Appl&& a, hb_priority<2>, T &&v, Ts&&... ds) const HB_AUTO_RETURN + ((hb_deref (hb_forward (v)).*hb_forward (a)) (hb_forward (ds)...)) + + /* Pointer-to-member. */ + template auto + impl (Appl&& a, hb_priority<1>, T &&v) const HB_AUTO_RETURN + ((hb_deref (hb_forward (v))).*hb_forward (a)) + + /* Operator(). */ + template auto + impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN + (hb_deref (hb_forward (a)) (hb_forward (ds)...)) + + public: + + template auto + operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN + ( + impl (hb_forward (a), + hb_prioritize, + hb_forward (ds)...) + ) +} +HB_FUNCOBJ (hb_invoke); + +template +struct hb_partial_t +{ + hb_partial_t (Appl a, V v) : a (a), v (v) {} + + static_assert (Pos > 0, ""); + + template auto + operator () (Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (hb_forward (a), + hb_forward (v), + hb_forward (ds)...); + } + template auto + operator () (T0&& d0, Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (T0), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (hb_forward (a), + hb_forward (d0), + hb_forward (v), + hb_forward (ds)...); + } + + private: + hb_reference_wrapper a; + V v; +}; +template +auto hb_partial (Appl&& a, V&& v) HB_AUTO_RETURN +(( hb_partial_t (a, v) )) + +/* The following, HB_PARTIALIZE, macro uses a particular corner-case + * of C++11 that is not particularly well-supported by all compilers. + * What's happening is that it's using "this" in a trailing return-type + * via decltype(). Broken compilers deduce the type of "this" pointer + * in that context differently from what it resolves to in the body + * of the function. + * + * One probable cause of this is that at the time of trailing return + * type declaration, "this" points to an incomplete type, whereas in + * the function body the type is complete. That doesn't justify the + * error in any way, but is probably what's happening. + * + * In the case of MSVC, we get around this by using C++14 "decltype(auto)" + * which deduces the type from the actual return statement. For gcc 4.8 + * we use "+this" instead of "this" which produces an rvalue that seems + * to be deduced as the same type with this particular compiler, and seem + * to be fine as default code path as well. + */ +#ifdef _MSC_VER +/* https://github.com/harfbuzz/harfbuzz/issues/1730 */ \ +#define HB_PARTIALIZE(Pos) \ + template \ + decltype(auto) operator () (_T&& _v) const \ + { return hb_partial (this, hb_forward<_T> (_v)); } \ + static_assert (true, "") +#else +/* https://github.com/harfbuzz/harfbuzz/issues/1724 */ +#define HB_PARTIALIZE(Pos) \ + template \ + auto operator () (_T&& _v) const HB_AUTO_RETURN \ + (hb_partial (+this, hb_forward<_T> (_v))) \ + static_assert (true, "") +#endif + + +struct +{ + private: + + template auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + (hb_deref (hb_forward (p)).has (hb_forward (v))) + + template auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + hb_invoke (hb_forward (p), + hb_forward (v)) + ) + + public: + + template auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (hb_forward (p), + hb_forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_has); + +struct +{ + private: + + template auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_has (hb_forward (p), + hb_forward (v)) + ) + + template auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + hb_forward (p) == hb_forward (v) + ) + + public: + + template auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (hb_forward (p), + hb_forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_match); + +struct +{ + private: + + template auto + impl (Proj&& f, Val &&v, hb_priority<2>) const HB_AUTO_RETURN + (hb_deref (hb_forward (f)).get (hb_forward (v))) + + template auto + impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_invoke (hb_forward (f), + hb_forward (v)) + ) + + template auto + impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + hb_forward (f)[hb_forward (v)] + ) + + public: + + template auto + operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN + ( + impl (hb_forward (f), + hb_forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_get); + + +template +struct hb_pair_t +{ + typedef T1 first_t; + typedef T2 second_t; + typedef hb_pair_t pair_t; + + hb_pair_t (T1 a, T2 b) : first (a), second (b) {} + + template + operator hb_pair_t () { return hb_pair_t (first, second); } + + hb_pair_t reverse () const + { return hb_pair_t (second, first); } + + bool operator == (const pair_t& o) const { return first == o.first && second == o.second; } + bool operator != (const pair_t& o) const { return !(*this == o); } + bool operator < (const pair_t& o) const { return first < o.first || (first == o.first && second < o.second); } + bool operator >= (const pair_t& o) const { return !(*this < o); } + bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); } + bool operator <= (const pair_t& o) const { return !(*this > o); } + + T1 first; + T2 second; +}; +#define hb_pair_t(T1,T2) hb_pair_t +template static inline hb_pair_t +hb_pair (T1&& a, T2&& b) { return hb_pair_t (a, b); } + +struct +{ + template constexpr typename Pair::first_t + operator () (const Pair& pair) const { return pair.first; } +} +HB_FUNCOBJ (hb_first); + +struct +{ + template constexpr typename Pair::second_t + operator () (const Pair& pair) const { return pair.second; } +} +HB_FUNCOBJ (hb_second); + +/* Note. In min/max impl, we can use hb_type_identity for second argument. + * However, that would silently convert between different-signedness integers. + * Instead we accept two different types, such that compiler can err if + * comparing integers of different signedness. */ +struct +{ + template constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (hb_forward (a) <= hb_forward (b) ? hb_forward (a) : hb_forward (b)) +} +HB_FUNCOBJ (hb_min); +struct +{ + template constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (hb_forward (a) >= hb_forward (b) ? hb_forward (a) : hb_forward (b)) +} +HB_FUNCOBJ (hb_max); +struct +{ + template constexpr auto + operator () (T&& x, T2&& min, T3&& max) const HB_AUTO_RETURN + (hb_min (hb_max (hb_forward (x), hb_forward (min)), hb_forward (max))) +} +HB_FUNCOBJ (hb_clamp); + + +/* + * Bithacks. + */ + +/* Return the number of 1 bits in v. */ +template +static inline HB_CONST_FUNC unsigned int +hb_popcount (T v) +{ +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) + if (sizeof (T) <= sizeof (unsigned int)) + return __builtin_popcount (v); + + if (sizeof (T) <= sizeof (unsigned long)) + return __builtin_popcountl (v); + + if (sizeof (T) <= sizeof (unsigned long long)) + return __builtin_popcountll (v); +#endif + + if (sizeof (T) <= 4) + { + /* "HACKMEM 169" */ + uint32_t y; + y = (v >> 1) &033333333333; + y = v - y - ((y >>1) & 033333333333); + return (((y + (y >> 3)) & 030707070707) % 077); + } + + if (sizeof (T) == 8) + { + unsigned int shift = 32; + return hb_popcount ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift)); + } + + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return hb_popcount ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift)); + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + +/* Returns the number of bits needed to store number */ +template +static inline HB_CONST_FUNC unsigned int +hb_bit_storage (T v) +{ + if (unlikely (!v)) return 0; + +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) + if (sizeof (T) <= sizeof (unsigned int)) + return sizeof (unsigned int) * 8 - __builtin_clz (v); + + if (sizeof (T) <= sizeof (unsigned long)) + return sizeof (unsigned long) * 8 - __builtin_clzl (v); + + if (sizeof (T) <= sizeof (unsigned long long)) + return sizeof (unsigned long long) * 8 - __builtin_clzll (v); +#endif + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4)) + if (sizeof (T) <= sizeof (unsigned int)) + { + unsigned long where; + _BitScanReverse (&where, v); + return 1 + where; + } +# if defined(_WIN64) + if (sizeof (T) <= 8) + { + unsigned long where; + _BitScanReverse64 (&where, v); + return 1 + where; + } +# endif +#endif + + if (sizeof (T) <= 4) + { + /* "bithacks" */ + const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000}; + const unsigned int S[] = {1, 2, 4, 8, 16}; + unsigned int r = 0; + for (int i = 4; i >= 0; i--) + if (v & b[i]) + { + v >>= S[i]; + r |= S[i]; + } + return r + 1; + } + if (sizeof (T) <= 8) + { + /* "bithacks" */ + const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; + const unsigned int S[] = {1, 2, 4, 8, 16, 32}; + unsigned int r = 0; + for (int i = 5; i >= 0; i--) + if (v & b[i]) + { + v >>= S[i]; + r |= S[i]; + } + return r + 1; + } + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return (v >> shift) ? hb_bit_storage ((uint64_t) (v >> shift)) + shift : + hb_bit_storage ((uint64_t) v); + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + +/* Returns the number of zero bits in the least significant side of v */ +template +static inline HB_CONST_FUNC unsigned int +hb_ctz (T v) +{ + if (unlikely (!v)) return 8 * sizeof (T); + +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) + if (sizeof (T) <= sizeof (unsigned int)) + return __builtin_ctz (v); + + if (sizeof (T) <= sizeof (unsigned long)) + return __builtin_ctzl (v); + + if (sizeof (T) <= sizeof (unsigned long long)) + return __builtin_ctzll (v); +#endif + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4)) + if (sizeof (T) <= sizeof (unsigned int)) + { + unsigned long where; + _BitScanForward (&where, v); + return where; + } +# if defined(_WIN64) + if (sizeof (T) <= 8) + { + unsigned long where; + _BitScanForward64 (&where, v); + return where; + } +# endif +#endif + + if (sizeof (T) <= 4) + { + /* "bithacks" */ + unsigned int c = 32; + v &= - (int32_t) v; + if (v) c--; + if (v & 0x0000FFFF) c -= 16; + if (v & 0x00FF00FF) c -= 8; + if (v & 0x0F0F0F0F) c -= 4; + if (v & 0x33333333) c -= 2; + if (v & 0x55555555) c -= 1; + return c; + } + if (sizeof (T) <= 8) + { + /* "bithacks" */ + unsigned int c = 64; + v &= - (int64_t) (v); + if (v) c--; + if (v & 0x00000000FFFFFFFFULL) c -= 32; + if (v & 0x0000FFFF0000FFFFULL) c -= 16; + if (v & 0x00FF00FF00FF00FFULL) c -= 8; + if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4; + if (v & 0x3333333333333333ULL) c -= 2; + if (v & 0x5555555555555555ULL) c -= 1; + return c; + } + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return (uint64_t) v ? hb_bit_storage ((uint64_t) v) : + hb_bit_storage ((uint64_t) (v >> shift)) + shift; + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + + +/* + * Tiny stuff. + */ + +/* ASCII tag/character handling */ +static inline bool ISALPHA (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } +static inline bool ISALNUM (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } +static inline bool ISSPACE (unsigned char c) +{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; } +static inline unsigned char TOUPPER (unsigned char c) +{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } +static inline unsigned char TOLOWER (unsigned char c) +{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } +static inline bool ISHEX (unsigned char c) +{ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } +static inline unsigned char TOHEX (uint8_t c) +{ return (c & 0xF) <= 9 ? (c & 0xF) + '0' : (c & 0xF) + 'a' - 10; } +static inline uint8_t FROMHEX (unsigned char c) +{ return (c >= '0' && c <= '9') ? c - '0' : TOLOWER (c) - 'a' + 10; } + +static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b) +{ return (a + (b - 1)) / b; } + + +#undef ARRAY_LENGTH +template +static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; } +/* A const version, but does not detect erratically being called on pointers. */ +#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) + + +static inline int +hb_memcmp (const void *a, const void *b, unsigned int len) +{ + /* It's illegal to pass NULL to memcmp(), even if len is zero. + * So, wrap it. + * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */ + if (unlikely (!len)) return 0; + return memcmp (a, b, len); +} + +static inline void * +hb_memset (void *s, int c, unsigned int n) +{ + /* It's illegal to pass NULL to memset(), even if n is zero. */ + if (unlikely (!n)) return 0; + return memset (s, c, n); +} + +static inline unsigned int +hb_ceil_to_4 (unsigned int v) +{ + return ((v - 1) | 3) + 1; +} + +template static inline bool +hb_in_range (T u, T lo, T hi) +{ + static_assert (!hb_is_signed::value, ""); + + /* The casts below are important as if T is smaller than int, + * the subtract results will become a signed int! */ + return (T)(u - lo) <= (T)(hi - lo); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2) +{ + return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3) +{ + return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3); +} + + +/* + * Overflow checking. + */ + +/* Consider __builtin_mul_overflow use here also */ +static inline bool +hb_unsigned_mul_overflows (unsigned int count, unsigned int size) +{ + return (size > 0) && (count >= ((unsigned int) -1) / size); +} + + +/* + * Sort and search. + */ + +template +static int +_hb_cmp_method (const void *pkey, const void *pval, Ts... ds) +{ + const K& key = * (const K*) pkey; + const V& val = * (const V*) pval; + + return val.cmp (key, ds...); +} + +template +static inline bool +hb_bsearch_impl (unsigned *pos, /* Out */ + const K& key, + V* base, size_t nmemb, size_t stride, + int (*compar)(const void *_key, const void *_item, Ts... _ds), + Ts... ds) +{ + /* This is our *only* bsearch implementation. */ + + int min = 0, max = (int) nmemb - 1; + while (min <= max) + { + int mid = ((unsigned int) min + (unsigned int) max) / 2; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + V* p = (V*) (((const char *) base) + (mid * stride)); +#pragma GCC diagnostic pop + int c = compar ((const void *) hb_addressof (key), (const void *) p, ds...); + if (c < 0) + max = mid - 1; + else if (c > 0) + min = mid + 1; + else + { + *pos = mid; + return true; + } + } + *pos = min; + return false; +} + +template +static inline V* +hb_bsearch (const K& key, V* base, + size_t nmemb, size_t stride = sizeof (V), + int (*compar)(const void *_key, const void *_item) = _hb_cmp_method) +{ + unsigned pos; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar) ? + (V*) (((const char *) base) + (pos * stride)) : nullptr; +#pragma GCC diagnostic pop +} +template +static inline V* +hb_bsearch (const K& key, V* base, + size_t nmemb, size_t stride, + int (*compar)(const void *_key, const void *_item, Ts... _ds), + Ts... ds) +{ + unsigned pos; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar, ds...) ? + (V*) (((const char *) base) + (pos * stride)) : nullptr; +#pragma GCC diagnostic pop +} + + +/* From https://github.com/noporpoise/sort_r + Feb 5, 2019 (c8c65c1e) + Modified to support optional argument using templates */ + +/* Isaac Turner 29 April 2014 Public Domain */ + +/* +hb_qsort function to be exported. +Parameters: + base is the array to be sorted + nel is the number of elements in the array + width is the size in bytes of each element of the array + compar is the comparison function + arg (optional) is a pointer to be passed to the comparison function + +void hb_qsort(void *base, size_t nel, size_t width, + int (*compar)(const void *_a, const void *_b, [void *_arg]), + [void *arg]); +*/ + +#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp)) + +/* swap a and b */ +/* a and b must not be equal! */ +static inline void sort_r_swap(char *__restrict a, char *__restrict b, + size_t w) +{ + char tmp, *end = a+w; + for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); } +} + +/* swap a, b iff a>b */ +/* a and b must not be equal! */ +/* __restrict is same as restrict but better support on old machines */ +template +static inline int sort_r_cmpswap(char *__restrict a, + char *__restrict b, size_t w, + int (*compar)(const void *_a, + const void *_b, + Ts... _ds), + Ts... ds) +{ + if(compar(a, b, ds...) > 0) { + sort_r_swap(a, b, w); + return 1; + } + return 0; +} + +/* +Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr, +with the smallest swap so that the blocks are in the opposite order. Blocks may +be internally re-ordered e.g. + 12345ab -> ab34512 + 123abc -> abc123 + 12abcde -> deabc12 +*/ +static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb) +{ + if(na > 0 && nb > 0) { + if(na > nb) { sort_r_swap(ptr, ptr+na, nb); } + else { sort_r_swap(ptr, ptr+nb, na); } + } +} + +/* Implement recursive quicksort ourselves */ +/* Note: quicksort is not stable, equivalent values may be swapped */ +template +static inline void sort_r_simple(void *base, size_t nel, size_t w, + int (*compar)(const void *_a, + const void *_b, + Ts... _ds), + Ts... ds) +{ + char *b = (char *)base, *end = b + nel*w; + + /* for(size_t i=0; i b && sort_r_cmpswap(pj-w,pj,w,compar,ds...); pj -= w) {} + } + } + else + { + /* nel > 9; Quicksort */ + + int cmp; + char *pl, *ple, *pr, *pre, *pivot; + char *last = b+w*(nel-1), *tmp; + + /* + Use median of second, middle and second-last items as pivot. + First and last may have been swapped with pivot and therefore be extreme + */ + char *l[3]; + l[0] = b + w; + l[1] = b+w*(nel/2); + l[2] = last - w; + + /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */ + + if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } + if(compar(l[1],l[2],ds...) > 0) { + SORT_R_SWAP(l[1], l[2], tmp); + if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } + } + + /* swap mid value (l[1]), and last element to put pivot as last element */ + if(l[1] != last) { sort_r_swap(l[1], last, w); } + + /* + pl is the next item on the left to be compared to the pivot + pr is the last item on the right that was compared to the pivot + ple is the left position to put the next item that equals the pivot + ple is the last right position where we put an item that equals the pivot + v- end (beyond the array) + EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE. + ^- b ^- ple ^- pl ^- pr ^- pre ^- last (where the pivot is) + Pivot comparison key: + E = equal, L = less than, u = unknown, G = greater than, E = equal + */ + pivot = last; + ple = pl = b; + pre = pr = last; + + /* + Strategy: + Loop into the list from the left and right at the same time to find: + - an item on the left that is greater than the pivot + - an item on the right that is less than the pivot + Once found, they are swapped and the loop continues. + Meanwhile items that are equal to the pivot are moved to the edges of the + array. + */ + while(pl < pr) { + /* Move left hand items which are equal to the pivot to the far left. + break when we find an item that is greater than the pivot */ + for(; pl < pr; pl += w) { + cmp = compar(pl, pivot, ds...); + if(cmp > 0) { break; } + else if(cmp == 0) { + if(ple < pl) { sort_r_swap(ple, pl, w); } + ple += w; + } + } + /* break if last batch of left hand items were equal to pivot */ + if(pl >= pr) { break; } + /* Move right hand items which are equal to the pivot to the far right. + break when we find an item that is less than the pivot */ + for(; pl < pr; ) { + pr -= w; /* Move right pointer onto an unprocessed item */ + cmp = compar(pr, pivot, ds...); + if(cmp == 0) { + pre -= w; + if(pr < pre) { sort_r_swap(pr, pre, w); } + } + else if(cmp < 0) { + if(pl < pr) { sort_r_swap(pl, pr, w); } + pl += w; + break; + } + } + } + + pl = pr; /* pr may have gone below pl */ + + /* + Now we need to go from: EEELLLGGGGEEEE + to: LLLEEEEEEEGGGG + Pivot comparison key: + E = equal, L = less than, u = unknown, G = greater than, E = equal + */ + sort_r_swap_blocks(b, ple-b, pl-ple); + sort_r_swap_blocks(pr, pre-pr, end-pre); + + /*for(size_t i=0; i static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2) +{ + for (unsigned int i = 1; i < len; i++) + { + unsigned int j = i; + while (j && compar (&array[j - 1], &array[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + { + T t = array[i]; + memmove (&array[j + 1], &array[j], (i - j) * sizeof (T)); + array[j] = t; + } + if (array2) + { + T3 t = array2[i]; + memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T3)); + array2[j] = t; + } + } +} + +template static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) +{ + hb_stable_sort (array, len, compar, (int *) nullptr); +} + +static inline hb_bool_t +hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) +{ + unsigned int v; + const char *p = s; + const char *end = p + len; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, base))) + return false; + + *out = v; + return true; +} + + +/* Operators. */ + +struct hb_bitwise_and +{ HB_PARTIALIZE(2); + static constexpr bool passthru_left = false; + static constexpr bool passthru_right = false; + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & b) +} +HB_FUNCOBJ (hb_bitwise_and); +struct hb_bitwise_or +{ HB_PARTIALIZE(2); + static constexpr bool passthru_left = true; + static constexpr bool passthru_right = true; + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | b) +} +HB_FUNCOBJ (hb_bitwise_or); +struct hb_bitwise_xor +{ HB_PARTIALIZE(2); + static constexpr bool passthru_left = true; + static constexpr bool passthru_right = true; + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a ^ b) +} +HB_FUNCOBJ (hb_bitwise_xor); +struct hb_bitwise_sub +{ HB_PARTIALIZE(2); + static constexpr bool passthru_left = true; + static constexpr bool passthru_right = false; + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b) +} +HB_FUNCOBJ (hb_bitwise_sub); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (~a) +} +HB_FUNCOBJ (hb_bitwise_neg); + +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b) +} +HB_FUNCOBJ (hb_add); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b) +} +HB_FUNCOBJ (hb_sub); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b) +} +HB_FUNCOBJ (hb_mul); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b) +} +HB_FUNCOBJ (hb_div); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b) +} +HB_FUNCOBJ (hb_mod); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (+a) +} +HB_FUNCOBJ (hb_pos); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (-a) +} +HB_FUNCOBJ (hb_neg); +struct +{ + template constexpr auto + operator () (T &a) const HB_AUTO_RETURN (++a) +} +HB_FUNCOBJ (hb_inc); +struct +{ + template constexpr auto + operator () (T &a) const HB_AUTO_RETURN (--a) +} +HB_FUNCOBJ (hb_dec); + + +/* Compiler-assisted vectorization. */ + +/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), + * basically a fixed-size bitset. */ +template +struct hb_vector_size_t +{ + elt_t& operator [] (unsigned int i) { return v[i]; } + const elt_t& operator [] (unsigned int i) const { return v[i]; } + + void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); } + + template + hb_vector_size_t process (const Op& op) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i]); + return r; + } + template + hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i], o.v[i]); + return r; + } + hb_vector_size_t operator | (const hb_vector_size_t &o) const + { return process (hb_bitwise_or, o); } + hb_vector_size_t operator & (const hb_vector_size_t &o) const + { return process (hb_bitwise_and, o); } + hb_vector_size_t operator ^ (const hb_vector_size_t &o) const + { return process (hb_bitwise_xor, o); } + hb_vector_size_t operator ~ () const + { return process (hb_bitwise_neg); } + + private: + static_assert (0 == byte_size % sizeof (elt_t), ""); + elt_t v[byte_size / sizeof (elt_t)]; +}; + + +#endif /* HB_ALGS_HH */ diff --git a/thirdparty/harfbuzz/src/hb-array.hh b/thirdparty/harfbuzz/src/hb-array.hh new file mode 100644 index 00000000000..568cd02c791 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-array.hh @@ -0,0 +1,408 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ARRAY_HH +#define HB_ARRAY_HH + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-iter.hh" +#include "hb-null.hh" + + +template +struct hb_sorted_array_t; + +template +struct hb_array_t : hb_iter_with_fallback_t, Type&> +{ + /* + * Constructors. + */ + hb_array_t () : arrayZ (nullptr), length (0), backwards_length (0) {} + hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_), backwards_length (0) {} + template + hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_), backwards_length (0) {} + + template + hb_array_t (const hb_array_t &o) : + hb_iter_with_fallback_t (), + arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {} + template + hb_array_t& operator = (const hb_array_t &o) + { arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; } + + /* + * Iterator implementation. + */ + typedef Type& __item_t__; + static constexpr bool is_random_access_iterator = true; + Type& __item_at__ (unsigned i) const + { + if (unlikely (i >= length)) return CrapOrNull (Type); + return arrayZ[i]; + } + void __forward__ (unsigned n) + { + if (unlikely (n > length)) + n = length; + length -= n; + backwards_length += n; + arrayZ += n; + } + void __rewind__ (unsigned n) + { + if (unlikely (n > backwards_length)) + n = backwards_length; + length += n; + backwards_length -= n; + arrayZ -= n; + } + unsigned __len__ () const { return length; } + /* Ouch. The operator== compares the contents of the array. For range-based for loops, + * it's best if we can just compare arrayZ, though comparing contents is still fast, + * but also would require that Type has operator==. As such, we optimize this operator + * for range-based for loop and just compare arrayZ. No need to compare length, as we + * assume we're only compared to .end(). */ + bool operator != (const hb_array_t& o) const + { return arrayZ != o.arrayZ; } + + /* Extra operators. + */ + Type * operator & () const { return arrayZ; } + operator hb_array_t () { return hb_array_t (arrayZ, length); } + template operator T * () const { return arrayZ; } + + HB_INTERNAL bool operator == (const hb_array_t &o) const; + + uint32_t hash () const { + uint32_t current = 0; + for (unsigned int i = 0; i < this->length; i++) { + current = current * 31 + hb_hash (this->arrayZ[i]); + } + return current; + } + + /* + * Compare, Sort, and Search. + */ + + /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */ + int cmp (const hb_array_t &a) const + { + if (length != a.length) + return (int) a.length - (int) length; + return hb_memcmp (a.arrayZ, arrayZ, get_size ()); + } + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + hb_array_t *a = (hb_array_t *) pa; + hb_array_t *b = (hb_array_t *) pb; + return b->cmp (*a); + } + + template + Type *lsearch (const T &x, Type *not_found = nullptr) + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + const Type *lsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + bool lfind (const T &x, unsigned *pos = nullptr) const + { + for (unsigned i = 0; i < length; ++i) + if (!this->arrayZ[i].cmp (x)) + { + if (pos) + *pos = i; + return true; + } + + return false; + } + + hb_sorted_array_t qsort (int (*cmp_)(const void*, const void*)) + { + if (likely (length)) + hb_qsort (arrayZ, length, this->get_item_size (), cmp_); + return hb_sorted_array_t (*this); + } + hb_sorted_array_t qsort () + { + if (likely (length)) + hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp); + return hb_sorted_array_t (*this); + } + void qsort (unsigned int start, unsigned int end) + { + end = hb_min (end, length); + assert (start <= end); + if (likely (start < end)) + hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp); + } + + /* + * Other methods. + */ + + unsigned int get_size () const { return length * this->get_item_size (); } + + /* + * Reverse the order of items in this array in the range [start, end). + */ + void reverse (unsigned start = 0, unsigned end = -1) + { + start = hb_min (start, length); + end = hb_min (end, length); + + if (end < start + 2) + return; + + for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) { + Type temp = arrayZ[rhs]; + arrayZ[rhs] = arrayZ[lhs]; + arrayZ[lhs] = temp; + } + } + + hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const + { + if (!start_offset && !seg_count) + return *this; + + unsigned int count = length; + if (unlikely (start_offset > count)) + count = 0; + else + count -= start_offset; + if (seg_count) + count = *seg_count = hb_min (count, *seg_count); + return hb_array_t (arrayZ + start_offset, count); + } + hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const + { return sub_array (start_offset, &seg_count); } + + hb_array_t truncate (unsigned length) const { return sub_array (0, length); } + + template + const T *as () const + { return length < hb_null_size (T) ? &Null (T) : reinterpret_cast (arrayZ); } + + template + bool check_range (const T *p, unsigned int size = T::static_size) const + { + return arrayZ <= ((const char *) p) + && ((const char *) p) <= arrayZ + length + && (unsigned int) (arrayZ + length - (const char *) p) >= size; + } + + /* Only call if you allocated the underlying array using malloc() or similar. */ + void free () + { ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; } + + template + hb_array_t copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto* out = c->start_embed (arrayZ); + if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ()); + for (unsigned i = 0; i < length; i++) + out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */ + return_trace (hb_array_t (out, length)); + } + + template + bool sanitize (hb_sanitize_context_t *c) const + { return c->check_array (arrayZ, length); } + + /* + * Members + */ + + public: + Type *arrayZ; + unsigned int length; + unsigned int backwards_length; +}; +template inline hb_array_t +hb_array (T *array, unsigned int length) +{ return hb_array_t (array, length); } +template inline hb_array_t +hb_array (T (&array_)[length_]) +{ return hb_array_t (array_); } + +enum hb_bfind_not_found_t +{ + HB_BFIND_NOT_FOUND_DONT_STORE, + HB_BFIND_NOT_FOUND_STORE, + HB_BFIND_NOT_FOUND_STORE_CLOSEST, +}; + +template +struct hb_sorted_array_t : + hb_iter_t, Type&>, + hb_array_t +{ + typedef hb_iter_t iter_base_t; + HB_ITER_USING (iter_base_t); + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + + hb_sorted_array_t () : hb_array_t () {} + hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t (array_, length_) {} + template + hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t (array_) {} + + template + hb_sorted_array_t (const hb_array_t &o) : + hb_iter_t (), + hb_array_t (o) {} + template + hb_sorted_array_t& operator = (const hb_array_t &o) + { hb_array_t (*this) = o; return *this; } + + /* Iterator implementation. */ + bool operator != (const hb_sorted_array_t& o) const + { return this->arrayZ != o.arrayZ || this->length != o.length; } + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const + { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const + { return sub_array (start_offset, &seg_count); } + + hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); } + + template + Type *bsearch (const T &x, Type *not_found = nullptr) + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + const Type *bsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { + unsigned pos; + + if (bsearch_impl (x, &pos)) + { + if (i) + *i = pos; + return true; + } + + if (i) + { + switch (not_found) + { + case HB_BFIND_NOT_FOUND_DONT_STORE: + break; + + case HB_BFIND_NOT_FOUND_STORE: + *i = to_store; + break; + + case HB_BFIND_NOT_FOUND_STORE_CLOSEST: + *i = pos; + break; + } + } + return false; + } + template + bool bsearch_impl (const T &x, unsigned *pos) const + { + return hb_bsearch_impl (pos, + x, + this->arrayZ, + this->length, + sizeof (Type), + _hb_cmp_method); + } +}; +template inline hb_sorted_array_t +hb_sorted_array (T *array, unsigned int length) +{ return hb_sorted_array_t (array, length); } +template inline hb_sorted_array_t +hb_sorted_array (T (&array_)[length_]) +{ return hb_sorted_array_t (array_); } + +template +bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + for (unsigned int i = 0; i < this->length; i++) { + if (this->arrayZ[i] != o.arrayZ[i]) return false; + } + return true; +} + +/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */ + +template <> +inline uint32_t hb_array_t::hash () const { + uint32_t current = 0; + for (unsigned int i = 0; i < this->length; i++) + current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u); + return current; +} + +template <> +inline uint32_t hb_array_t::hash () const { + uint32_t current = 0; + for (unsigned int i = 0; i < this->length; i++) + current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u); + return current; +} + + +typedef hb_array_t hb_bytes_t; +typedef hb_array_t hb_ubytes_t; + + + +#endif /* HB_ARRAY_HH */ diff --git a/thirdparty/harfbuzz/src/hb-atomic.hh b/thirdparty/harfbuzz/src/hb-atomic.hh new file mode 100644 index 00000000000..b3fb296b4e1 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-atomic.hh @@ -0,0 +1,295 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ATOMIC_HH +#define HB_ATOMIC_HH + +#include "hb.hh" +#include "hb-meta.hh" + + +/* + * Atomic integers and pointers. + */ + + +/* We need external help for these */ + +#if defined(hb_atomic_int_impl_add) \ + && defined(hb_atomic_ptr_impl_get) \ + && defined(hb_atomic_ptr_impl_cmpexch) + +/* Defined externally, i.e. in config.h. */ + + +#elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE) + +/* C++11-style GCC primitives. */ + +#define _hb_memory_barrier() __sync_synchronize () + +#define hb_atomic_int_impl_add(AI, V) __atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL) +#define hb_atomic_int_impl_set_relaxed(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELAXED) +#define hb_atomic_int_impl_set(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELEASE) +#define hb_atomic_int_impl_get_relaxed(AI) __atomic_load_n ((AI), __ATOMIC_RELAXED) +#define hb_atomic_int_impl_get(AI) __atomic_load_n ((AI), __ATOMIC_ACQUIRE) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) __atomic_store_n ((P), (V), __ATOMIC_RELAXED) +#define hb_atomic_ptr_impl_get_relaxed(P) __atomic_load_n ((P), __ATOMIC_RELAXED) +#define hb_atomic_ptr_impl_get(P) __atomic_load_n ((P), __ATOMIC_ACQUIRE) +static inline bool +_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) +{ + const void *O = O_; // Need lvalue + return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED); +} +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) + +#elif !defined(HB_NO_MT) && __cplusplus >= 201103L + +/* C++11 atomics. */ + +#include + +#define _hb_memory_barrier() std::atomic_thread_fence(std::memory_order_ack_rel) +#define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire) +#define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release) + +#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) +#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast *> (AI)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast *> (AI)->store ((V), std::memory_order_release)) +#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast const *> (AI)->load (std::memory_order_relaxed)) +#define hb_atomic_int_impl_get(AI) (reinterpret_cast const *> (AI)->load (std::memory_order_acquire)) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast const *> (P)->load (std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get(P) (reinterpret_cast *> (P)->load (std::memory_order_acquire)) +static inline bool +_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) +{ + const void *O = O_; // Need lvalue + return reinterpret_cast *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed); +} +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) + + +#elif !defined(HB_NO_MT) && defined(_WIN32) + +#include + +static inline void _hb_memory_barrier () +{ +#if !defined(MemoryBarrier) && !defined(__MINGW32_VERSION) + /* MinGW has a convoluted history of supporting MemoryBarrier. */ + LONG dummy = 0; + InterlockedExchange (&dummy, 1); +#else + MemoryBarrier (); +#endif +} +#define _hb_memory_barrier() _hb_memory_barrier () + +#define hb_atomic_int_impl_add(AI, V) InterlockedExchangeAdd ((LONG *) (AI), (V)) +static_assert ((sizeof (LONG) == sizeof (int)), ""); + +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (InterlockedCompareExchangePointer ((P), (N), (O)) == (O)) + + +#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) + +#define _hb_memory_barrier() __sync_synchronize () + +#define hb_atomic_int_impl_add(AI, V) __sync_fetch_and_add ((AI), (V)) + +#define hb_atomic_ptr_impl_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N)) + + +#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS) + +#include +#include + +#define _hb_memory_r_barrier() __machine_r_barrier () +#define _hb_memory_w_barrier() __machine_w_barrier () +#define _hb_memory_barrier() __machine_rw_barrier () + +static inline int _hb_fetch_and_add (int *AI, int V) +{ + _hb_memory_w_barrier (); + int result = atomic_add_int_nv ((uint_t *) AI, V) - V; + _hb_memory_r_barrier (); + return result; +} +static inline bool _hb_compare_and_swap_ptr (void **P, void *O, void *N) +{ + _hb_memory_w_barrier (); + bool result = atomic_cas_ptr (P, O, N) == O; + _hb_memory_r_barrier (); + return result; +} + +#define hb_atomic_int_impl_add(AI, V) _hb_fetch_and_add ((AI), (V)) + +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_compare_and_swap_ptr ((P), (O), (N)) + + +#elif !defined(HB_NO_MT) && defined(__APPLE__) + +#include +#ifdef __MAC_OS_X_MIN_REQUIRED +#include +#elif defined(__IPHONE_OS_MIN_REQUIRED) +#include +#endif + +#define _hb_memory_barrier() OSMemoryBarrier () + +#define hb_atomic_int_impl_add(AI, V) (OSAtomicAdd32Barrier ((V), (AI)) - (V)) + +#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((O), (N), (P)) +#else +#if __ppc64__ || __x86_64__ || __aarch64__ +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P)) +#else +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P)) +#endif +#endif + + +#elif !defined(HB_NO_MT) && defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__)) + +#include + +#define _hb_memory_barrier() __lwsync () + +static inline int _hb_fetch_and_add (int *AI, int V) +{ + _hb_memory_barrier (); + int result = __fetch_and_add (AI, V); + _hb_memory_barrier (); + return result; +} +static inline bool _hb_compare_and_swaplp (long *P, long O, long N) +{ + _hb_memory_barrier (); + bool result = __compare_and_swaplp (P, &O, N); + _hb_memory_barrier (); + return result; +} + +#define hb_atomic_int_impl_add(AI, V) _hb_fetch_and_add ((AI), (V)) + +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_compare_and_swaplp ((long *) (P), (long) (O), (long) (N)) +static_assert ((sizeof (long) == sizeof (void *)), ""); + + +#elif defined(HB_NO_MT) + +#define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V)) + +#define _hb_memory_barrier() do {} while (0) + +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) + + +#else + +#error "Could not find any system to define atomic_int macros." +#error "Check hb-atomic.hh for possible resolutions." + +#endif + + +#ifndef _hb_memory_r_barrier +#define _hb_memory_r_barrier() _hb_memory_barrier () +#endif +#ifndef _hb_memory_w_barrier +#define _hb_memory_w_barrier() _hb_memory_barrier () +#endif +#ifndef hb_atomic_int_impl_set_relaxed +#define hb_atomic_int_impl_set_relaxed(AI, V) (*(AI) = (V)) +#endif +#ifndef hb_atomic_int_impl_get_relaxed +#define hb_atomic_int_impl_get_relaxed(AI) (*(AI)) +#endif + +#ifndef hb_atomic_ptr_impl_set_relaxed +#define hb_atomic_ptr_impl_set_relaxed(P, V) (*(P) = (V)) +#endif +#ifndef hb_atomic_ptr_impl_get_relaxed +#define hb_atomic_ptr_impl_get_relaxed(P) (*(P)) +#endif +#ifndef hb_atomic_int_impl_set +inline void hb_atomic_int_impl_set (int *AI, int v) { _hb_memory_w_barrier (); *AI = v; } +#endif +#ifndef hb_atomic_int_impl_get +inline int hb_atomic_int_impl_get (const int *AI) { int v = *AI; _hb_memory_r_barrier (); return v; } +#endif +#ifndef hb_atomic_ptr_impl_get +inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } +#endif + + +#define HB_ATOMIC_INT_INIT(V) {V} +struct hb_atomic_int_t +{ + void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } + void set (int v_) { hb_atomic_int_impl_set (&v, v_); } + int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } + int get () const { return hb_atomic_int_impl_get (&v); } + int inc () { return hb_atomic_int_impl_add (&v, 1); } + int dec () { return hb_atomic_int_impl_add (&v, -1); } + + int v; +}; + + +#define HB_ATOMIC_PTR_INIT(V) {V} +template +struct hb_atomic_ptr_t +{ + typedef hb_remove_pointer

T; + + void init (T* v_ = nullptr) { set_relaxed (v_); } + void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } + T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } + T *get () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } + bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + + T * operator -> () const { return get (); } + template operator C * () const { return get (); } + + T *v; +}; + + +#endif /* HB_ATOMIC_HH */ diff --git a/thirdparty/harfbuzz/src/hb-bimap.hh b/thirdparty/harfbuzz/src/hb-bimap.hh new file mode 100644 index 00000000000..e9f3a6a52df --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-bimap.hh @@ -0,0 +1,166 @@ +/* + * Copyright © 2019 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_BIMAP_HH +#define HB_BIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" + +/* Bi-directional map */ +struct hb_bimap_t +{ + hb_bimap_t () { init (); } + ~hb_bimap_t () { fini (); } + + void init () + { + forw_map.init (); + back_map.init (); + } + + void fini () + { + forw_map.fini (); + back_map.fini (); + } + + void reset () + { + forw_map.reset (); + back_map.reset (); + } + + bool in_error () const { return forw_map.in_error () || back_map.in_error (); } + + void set (hb_codepoint_t lhs, hb_codepoint_t rhs) + { + if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return; + if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; } + forw_map.set (lhs, rhs); + back_map.set (rhs, lhs); + } + + hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); } + hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); } + + hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); } + bool has (hb_codepoint_t lhs, hb_codepoint_t *vp = nullptr) const { return forw_map.has (lhs, vp); } + + void del (hb_codepoint_t lhs) + { + back_map.del (get (lhs)); + forw_map.del (lhs); + } + + void clear () + { + forw_map.clear (); + back_map.clear (); + } + + bool is_empty () const { return get_population () == 0; } + + unsigned int get_population () const { return forw_map.get_population (); } + + protected: + hb_map_t forw_map; + hb_map_t back_map; +}; + +/* Inremental bimap: only lhs is given, rhs is incrementally assigned */ +struct hb_inc_bimap_t : hb_bimap_t +{ + hb_inc_bimap_t () { init (); } + + void init () + { + hb_bimap_t::init (); + next_value = 0; + } + + /* Add a mapping from lhs to rhs with a unique value if lhs is unknown. + * Return the rhs value as the result. + */ + hb_codepoint_t add (hb_codepoint_t lhs) + { + hb_codepoint_t rhs = forw_map[lhs]; + if (rhs == HB_MAP_VALUE_INVALID) + { + rhs = next_value++; + set (lhs, rhs); + } + return rhs; + } + + hb_codepoint_t skip () + { return next_value++; } + + hb_codepoint_t get_next_value () const + { return next_value; } + + void add_set (const hb_set_t *set) + { + hb_codepoint_t i = HB_SET_VALUE_INVALID; + while (hb_set_next (set, &i)) add (i); + } + + /* Create an identity map. */ + bool identity (unsigned int size) + { + clear (); + for (hb_codepoint_t i = 0; i < size; i++) set (i, i); + return !in_error (); + } + + protected: + static int cmp_id (const void* a, const void* b) + { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; } + + public: + /* Optional: after finished adding all mappings in a random order, + * reassign rhs to lhs so that they are in the same order. */ + void sort () + { + hb_codepoint_t count = get_population (); + hb_vector_t work; + work.resize (count); + + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + work[rhs] = back_map[rhs]; + + work.qsort (cmp_id); + + clear (); + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + set (work[rhs], rhs); + } + + protected: + unsigned int next_value; +}; + +#endif /* HB_BIMAP_HH */ diff --git a/thirdparty/harfbuzz/src/hb-blob.cc b/thirdparty/harfbuzz/src/hb-blob.cc new file mode 100644 index 00000000000..94ed50fd3cc --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-blob.cc @@ -0,0 +1,717 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-blob.hh" + +#ifdef HAVE_SYS_MMAN_H +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#endif /* HAVE_SYS_MMAN_H */ + +#include +#include + + +/** + * SECTION: hb-blob + * @title: hb-blob + * @short_description: Binary data containers + * @include: hb.h + * + * Blobs wrap a chunk of binary data to handle lifecycle management of data + * while it is passed between client and HarfBuzz. Blobs are primarily used + * to create font faces, but also to access font face tables, as well as + * pass around other binary data. + **/ + + +/** + * hb_blob_create: (skip) + * @data: Pointer to blob data. + * @length: Length of @data in bytes. + * @mode: Memory mode for @data. + * @user_data: Data parameter to pass to @destroy. + * @destroy: Callback to call when @data is not needed anymore. + * + * Creates a new "blob" object wrapping @data. The @mode parameter is used + * to negotiate ownership and lifecycle of @data. + * + * Return value: New blob, or the empty blob if something failed or if @length is + * zero. Destroy with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_blob_t *blob; + + if (!length || + length >= 1u << 31 || + !(blob = hb_object_create ())) { + if (destroy) + destroy (user_data); + return hb_blob_get_empty (); + } + + blob->data = data; + blob->length = length; + blob->mode = mode; + + blob->user_data = user_data; + blob->destroy = destroy; + + if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { + blob->mode = HB_MEMORY_MODE_READONLY; + if (!blob->try_make_writable ()) { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + return blob; +} + +static void +_hb_blob_destroy (void *data) +{ + hb_blob_destroy ((hb_blob_t *) data); +} + +/** + * hb_blob_create_sub_blob: + * @parent: Parent blob. + * @offset: Start offset of sub-blob within @parent, in bytes. + * @length: Length of sub-blob. + * + * Returns a blob that represents a range of bytes in @parent. The new + * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it + * will never modify data in the parent blob. The parent data is not + * expected to be modified, and will result in undefined behavior if it + * is. + * + * Makes @parent immutable. + * + * Return value: New blob, or the empty blob if something failed or if + * @length is zero or @offset is beyond the end of @parent's data. Destroy + * with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length) +{ + hb_blob_t *blob; + + if (!length || !parent || offset >= parent->length) + return hb_blob_get_empty (); + + hb_blob_make_immutable (parent); + + blob = hb_blob_create (parent->data + offset, + hb_min (length, parent->length - offset), + HB_MEMORY_MODE_READONLY, + hb_blob_reference (parent), + _hb_blob_destroy); + + return blob; +} + +/** + * hb_blob_copy_writable_or_fail: + * @blob: A blob. + * + * Makes a writable copy of @blob. + * + * Return value: New blob, or nullptr if allocation failed. + * + * Since: 1.8.0 + **/ +hb_blob_t * +hb_blob_copy_writable_or_fail (hb_blob_t *blob) +{ + blob = hb_blob_create (blob->data, + blob->length, + HB_MEMORY_MODE_DUPLICATE, + nullptr, + nullptr); + + if (unlikely (blob == hb_blob_get_empty ())) + blob = nullptr; + + return blob; +} + +/** + * hb_blob_get_empty: + * + * Returns the singleton empty blob. + * + * See TODO:link object types for more information. + * + * Return value: (transfer full): the empty blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_get_empty () +{ + return const_cast (&Null (hb_blob_t)); +} + +/** + * hb_blob_reference: (skip) + * @blob: a blob. + * + * Increases the reference count on @blob. + * + * See TODO:link object types for more information. + * + * Return value: @blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_reference (hb_blob_t *blob) +{ + return hb_object_reference (blob); +} + +/** + * hb_blob_destroy: (skip) + * @blob: a blob. + * + * Decreases the reference count on @blob, and if it reaches zero, destroys + * @blob, freeing all memory, possibly calling the destroy-callback the blob + * was created for if it has not been called already. + * + * See TODO:link object types for more information. + * + * Since: 0.9.2 + **/ +void +hb_blob_destroy (hb_blob_t *blob) +{ + if (!hb_object_destroy (blob)) return; + + blob->fini_shallow (); + + free (blob); +} + +/** + * hb_blob_set_user_data: (skip) + * @blob: a blob. + * @key: key for data to set. + * @data: data to set. + * @destroy: callback to call when @data is not needed anymore. + * @replace: whether to replace an existing data with the same key. + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_blob_set_user_data (hb_blob_t *blob, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (blob, key, data, destroy, replace); +} + +/** + * hb_blob_get_user_data: (skip) + * @blob: a blob. + * @key: key for data to get. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_blob_get_user_data (hb_blob_t *blob, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (blob, key); +} + + +/** + * hb_blob_make_immutable: + * @blob: a blob. + * + * + * + * Since: 0.9.2 + **/ +void +hb_blob_make_immutable (hb_blob_t *blob) +{ + if (hb_object_is_immutable (blob)) + return; + + hb_object_make_immutable (blob); +} + +/** + * hb_blob_is_immutable: + * @blob: a blob. + * + * + * + * Return value: TODO + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob) +{ + return hb_object_is_immutable (blob); +} + + +/** + * hb_blob_get_length: + * @blob: a blob. + * + * + * + * Return value: the length of blob data in bytes. + * + * Since: 0.9.2 + **/ +unsigned int +hb_blob_get_length (hb_blob_t *blob) +{ + return blob->length; +} + +/** + * hb_blob_get_data: + * @blob: a blob. + * @length: (out): + * + * + * + * Returns: (transfer none) (array length=length): + * + * Since: 0.9.2 + **/ +const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length) +{ + if (length) + *length = blob->length; + + return blob->data; +} + +/** + * hb_blob_get_data_writable: + * @blob: a blob. + * @length: (out): output length of the writable data. + * + * Tries to make blob data writable (possibly copying it) and + * return pointer to data. + * + * Fails if blob has been made immutable, or if memory allocation + * fails. + * + * Returns: (transfer none) (array length=length): Writable blob data, + * or %NULL if failed. + * + * Since: 0.9.2 + **/ +char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) +{ + if (!blob->try_make_writable ()) { + if (length) + *length = 0; + + return nullptr; + } + + if (length) + *length = blob->length; + + return const_cast (blob->data); +} + + +bool +hb_blob_t::try_make_writable_inplace_unix () +{ +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) + uintptr_t pagesize = -1, mask, length; + const char *addr; + +#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); +#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); +#elif defined(HAVE_GETPAGESIZE) + pagesize = (uintptr_t) getpagesize (); +#endif + + if ((uintptr_t) -1L == pagesize) { + DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno)); + return false; + } + DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize); + + mask = ~(pagesize-1); + addr = (const char *) (((uintptr_t) this->data) & mask); + length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask) - addr; + DEBUG_MSG_FUNC (BLOB, this, + "calling mprotect on [%p..%p] (%lu bytes)", + addr, addr+length, (unsigned long) length); + if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { + DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno)); + return false; + } + + this->mode = HB_MEMORY_MODE_WRITABLE; + + DEBUG_MSG_FUNC (BLOB, this, + "successfully made [%p..%p] (%lu bytes) writable\n", + addr, addr+length, (unsigned long) length); + return true; +#else + return false; +#endif +} + +bool +hb_blob_t::try_make_writable_inplace () +{ + DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n"); + + if (this->try_make_writable_inplace_unix ()) + return true; + + DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n"); + + /* Failed to make writable inplace, mark that */ + this->mode = HB_MEMORY_MODE_READONLY; + return false; +} + +bool +hb_blob_t::try_make_writable () +{ + if (hb_object_is_immutable (this)) + return false; + + if (this->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ()) + return true; + + if (this->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + + DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data); + + char *new_data; + + new_data = (char *) malloc (this->length); + if (unlikely (!new_data)) + return false; + + DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); + + memcpy (new_data, this->data, this->length); + this->destroy_user_data (); + this->mode = HB_MEMORY_MODE_WRITABLE; + this->data = new_data; + this->user_data = new_data; + this->destroy = free; + + return true; +} + +/* + * Mmap + */ + +#ifndef HB_NO_OPEN +#ifdef HAVE_MMAP +# if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__) +# include +# endif +# include +# include +# include +#endif + +#ifdef _WIN32 +# include +#else +# ifndef O_BINARY +# define O_BINARY 0 +# endif +#endif + +#ifndef MAP_NORESERVE +# define MAP_NORESERVE 0 +#endif + +struct hb_mapped_file_t +{ + char *contents; + unsigned long length; +#ifdef _WIN32 + HANDLE mapping; +#endif +}; + +#if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP) +static void +_hb_mapped_file_destroy (void *file_) +{ + hb_mapped_file_t *file = (hb_mapped_file_t *) file_; +#ifdef HAVE_MMAP + munmap (file->contents, file->length); +#elif defined(_WIN32) + UnmapViewOfFile (file->contents); + CloseHandle (file->mapping); +#else + assert (0); // If we don't have mmap we shouldn't reach here +#endif + + free (file); +} +#endif + +#ifdef _PATH_RSRCFORKSPEC +static int +_open_resource_fork (const char *file_name, hb_mapped_file_t *file) +{ + size_t name_len = strlen (file_name); + size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC); + + char *rsrc_name = (char *) malloc (len); + if (unlikely (!rsrc_name)) return -1; + + strncpy (rsrc_name, file_name, name_len); + strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC, + sizeof (_PATH_RSRCFORKSPEC) - 1); + + int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0); + free (rsrc_name); + + if (fd != -1) + { + struct stat st; + if (fstat (fd, &st) != -1) + file->length = (unsigned long) st.st_size; + else + { + close (fd); + fd = -1; + } + } + + return fd; +} +#endif + +/** + * hb_blob_create_from_file: + * @file_name: font filename. + * + * Returns: A hb_blob_t pointer with the content of the file + * + * Since: 1.7.7 + **/ +hb_blob_t * +hb_blob_create_from_file (const char *file_name) +{ + /* Adopted from glib's gmappedfile.c with Matthias Clasen and + Allison Lortie permission but changed a lot to suit our need. */ +#if defined(HAVE_MMAP) && !defined(HB_NO_MMAP) + hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t)); + if (unlikely (!file)) return hb_blob_get_empty (); + + int fd = open (file_name, O_RDONLY | O_BINARY, 0); + if (unlikely (fd == -1)) goto fail_without_close; + + struct stat st; + if (unlikely (fstat (fd, &st) == -1)) goto fail; + + file->length = (unsigned long) st.st_size; + +#ifdef _PATH_RSRCFORKSPEC + if (unlikely (file->length == 0)) + { + int rfd = _open_resource_fork (file_name, file); + if (rfd != -1) + { + close (fd); + fd = rfd; + } + } +#endif + + file->contents = (char *) mmap (nullptr, file->length, PROT_READ, + MAP_PRIVATE | MAP_NORESERVE, fd, 0); + + if (unlikely (file->contents == MAP_FAILED)) goto fail; + + close (fd); + + return hb_blob_create (file->contents, file->length, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, + (hb_destroy_func_t) _hb_mapped_file_destroy); + +fail: + close (fd); +fail_without_close: + free (file); + +#elif defined(_WIN32) && !defined(HB_NO_MMAP) + hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t)); + if (unlikely (!file)) return hb_blob_get_empty (); + + HANDLE fd; + unsigned int size = strlen (file_name) + 1; + wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size); + if (unlikely (!wchar_file_name)) goto fail_without_close; + mbstowcs (wchar_file_name, file_name, size); +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + { + CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; + ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF; + ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000; + ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000; + ceparams.lpSecurityAttributes = nullptr; + ceparams.hTemplateFile = nullptr; + fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, + OPEN_EXISTING, &ceparams); + } +#else + fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, + nullptr); +#endif + free (wchar_file_name); + + if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + { + LARGE_INTEGER length; + GetFileSizeEx (fd, &length); + file->length = length.LowPart; + file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr); + } +#else + file->length = (unsigned long) GetFileSize (fd, nullptr); + file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr); +#endif + if (unlikely (!file->mapping)) goto fail; + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); +#else + file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); +#endif + if (unlikely (!file->contents)) goto fail; + + CloseHandle (fd); + return hb_blob_create (file->contents, file->length, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, + (hb_destroy_func_t) _hb_mapped_file_destroy); + +fail: + CloseHandle (fd); +fail_without_close: + free (file); + +#endif + + /* The following tries to read a file without knowing its size beforehand + It's used as a fallback for systems without mmap or to read from pipes */ + unsigned long len = 0, allocated = BUFSIZ * 16; + char *data = (char *) malloc (allocated); + if (unlikely (!data)) return hb_blob_get_empty (); + + FILE *fp = fopen (file_name, "rb"); + if (unlikely (!fp)) goto fread_fail_without_close; + + while (!feof (fp)) + { + if (allocated - len < BUFSIZ) + { + allocated *= 2; + /* Don't allocate and go more than ~536MB, our mmap reader still + can cover files like that but lets limit our fallback reader */ + if (unlikely (allocated > (2 << 28))) goto fread_fail; + char *new_data = (char *) realloc (data, allocated); + if (unlikely (!new_data)) goto fread_fail; + data = new_data; + } + + unsigned long addition = fread (data + len, 1, allocated - len, fp); + + int err = ferror (fp); +#ifdef EINTR // armcc doesn't have it + if (unlikely (err == EINTR)) continue; +#endif + if (unlikely (err)) goto fread_fail; + + len += addition; + } + fclose (fp); + + return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data, + (hb_destroy_func_t) free); + +fread_fail: + fclose (fp); +fread_fail_without_close: + free (data); + return hb_blob_get_empty (); +} +#endif /* !HB_NO_OPEN */ diff --git a/thirdparty/harfbuzz/src/hb-blob.h b/thirdparty/harfbuzz/src/hb-blob.h new file mode 100644 index 00000000000..f80e9af2d9e --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-blob.h @@ -0,0 +1,131 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_BLOB_H +#define HB_BLOB_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/* + * Note re various memory-modes: + * + * - In no case shall the HarfBuzz client modify memory + * that is passed to HarfBuzz in a blob. If there is + * any such possibility, MODE_DUPLICATE should be used + * such that HarfBuzz makes a copy immediately, + * + * - Use MODE_READONLY otherwise, unless you really really + * really know what you are doing, + * + * - MODE_WRITABLE is appropriate if you really made a + * copy of data solely for the purpose of passing to + * HarfBuzz and doing that just once (no reuse!), + * + * - If the font is mmap()ed, it's ok to use + * READONLY_MAY_MAKE_WRITABLE, however, using that mode + * correctly is very tricky. Use MODE_READONLY instead. + */ +typedef enum { + HB_MEMORY_MODE_DUPLICATE, + HB_MEMORY_MODE_READONLY, + HB_MEMORY_MODE_WRITABLE, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE +} hb_memory_mode_t; + +typedef struct hb_blob_t hb_blob_t; + +HB_EXTERN hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_blob_t * +hb_blob_create_from_file (const char *file_name); + +/* Always creates with MEMORY_MODE_READONLY. + * Even if the parent blob is writable, we don't + * want the user of the sub-blob to be able to + * modify the parent data as that data may be + * shared among multiple sub-blobs. + */ +HB_EXTERN hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length); + +HB_EXTERN hb_blob_t * +hb_blob_copy_writable_or_fail (hb_blob_t *blob); + +HB_EXTERN hb_blob_t * +hb_blob_get_empty (void); + +HB_EXTERN hb_blob_t * +hb_blob_reference (hb_blob_t *blob); + +HB_EXTERN void +hb_blob_destroy (hb_blob_t *blob); + +HB_EXTERN hb_bool_t +hb_blob_set_user_data (hb_blob_t *blob, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_blob_get_user_data (hb_blob_t *blob, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_blob_make_immutable (hb_blob_t *blob); + +HB_EXTERN hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob); + + +HB_EXTERN unsigned int +hb_blob_get_length (hb_blob_t *blob); + +HB_EXTERN const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length); + +HB_EXTERN char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length); + +HB_END_DECLS + +#endif /* HB_BLOB_H */ diff --git a/thirdparty/harfbuzz/src/hb-blob.hh b/thirdparty/harfbuzz/src/hb-blob.hh new file mode 100644 index 00000000000..d85bd823b00 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-blob.hh @@ -0,0 +1,97 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BLOB_HH +#define HB_BLOB_HH + +#include "hb.hh" + + +/* + * hb_blob_t + */ + +struct hb_blob_t +{ + void fini_shallow () { destroy_user_data (); } + + void destroy_user_data () + { + if (destroy) + { + destroy (user_data); + user_data = nullptr; + destroy = nullptr; + } + } + + HB_INTERNAL bool try_make_writable (); + HB_INTERNAL bool try_make_writable_inplace (); + HB_INTERNAL bool try_make_writable_inplace_unix (); + + hb_bytes_t as_bytes () const { return hb_bytes_t (data, length); } + template + const Type* as () const { return as_bytes ().as (); } + + public: + hb_object_header_t header; + + const char *data; + unsigned int length; + hb_memory_mode_t mode; + + void *user_data; + hb_destroy_func_t destroy; +}; + + +/* + * hb_blob_ptr_t + */ + +template +struct hb_blob_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {} + hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; } + const T * operator -> () const { return get (); } + const T & operator * () const { return *get (); } + template operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + const T * get () const { return b->as (); } + hb_blob_t * get_blob () const { return b.get_raw (); } + unsigned int get_length () const { return b.get ()->length; } + void destroy () { hb_blob_destroy (b.get ()); b = nullptr; } + + hb_nonnull_ptr_t b; +}; + + +#endif /* HB_BLOB_HH */ diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh new file mode 100644 index 00000000000..1f9e2e91db8 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh @@ -0,0 +1,643 @@ + +#line 1 "hb-buffer-deserialize-json.rl" +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_JSON_HH +#define HB_BUFFER_DESERIALIZE_JSON_HH + +#include "hb.hh" + + +#line 36 "hb-buffer-deserialize-json.hh" +static const unsigned char _deserialize_json_trans_keys[] = { + 0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, + 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, + 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, + 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, + 65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0 +}; + +static const char _deserialize_json_key_spans[] = { + 0, 115, 26, 7, 2, 1, 50, 49, + 10, 117, 117, 117, 1, 50, 49, 10, + 117, 117, 1, 1, 50, 49, 117, 117, + 2, 1, 50, 49, 10, 117, 117, 1, + 50, 49, 10, 117, 117, 1, 50, 49, + 58, 89, 117, 117, 85, 115, 0 +}; + +static const short _deserialize_json_index_offsets[] = { + 0, 0, 116, 143, 151, 154, 156, 207, + 257, 268, 386, 504, 622, 624, 675, 725, + 736, 854, 972, 974, 976, 1027, 1077, 1195, + 1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, + 1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, + 2119, 2178, 2268, 2386, 2504, 2590, 2706 +}; + +static const char _deserialize_json_indicies[] = { + 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, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 1, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 4, 1, + 5, 1, 6, 7, 1, 1, 8, 1, + 9, 10, 1, 11, 1, 11, 11, 11, + 11, 11, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 11, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 12, 1, + 12, 12, 12, 12, 12, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 12, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 1, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 1, 16, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 1, 18, 18, 18, + 18, 18, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 19, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 20, 1, 21, 21, 21, 21, 21, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 21, 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, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 22, + 1, 18, 18, 18, 18, 18, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 19, 1, 1, 1, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 20, 1, 23, + 1, 23, 23, 23, 23, 23, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 23, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 24, 1, 24, 24, 24, 24, + 24, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 24, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 25, 1, 1, 26, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 1, 28, 29, + 29, 29, 29, 29, 29, 29, 29, 29, + 1, 30, 30, 30, 30, 30, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 30, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 31, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 32, 1, 30, + 30, 30, 30, 30, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 30, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 31, 1, 1, 1, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 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, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 32, 1, 33, 1, 34, + 1, 34, 34, 34, 34, 34, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 35, 1, 35, 35, 35, 35, + 35, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 35, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 36, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 1, 38, 38, + 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 40, 1, 38, 38, 38, 38, + 38, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 40, 1, 42, 43, 1, 44, 1, 44, + 44, 44, 44, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 44, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 45, 1, 45, 45, 45, 45, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 45, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 46, 1, + 1, 47, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 1, 49, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 1, 51, + 51, 51, 51, 51, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 51, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 52, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 51, 51, 51, + 51, 51, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 52, 1, 1, 1, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 53, 1, 54, 1, 54, 54, 54, + 54, 54, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 54, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 55, 1, + 55, 55, 55, 55, 55, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 55, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 56, 1, 1, 57, + 58, 58, 58, 58, 58, 58, 58, 58, + 58, 1, 59, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 1, 61, 61, 61, + 61, 61, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 61, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 62, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 63, 1, 61, 61, 61, 61, 61, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 61, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 62, 1, + 1, 1, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 63, + 1, 64, 1, 64, 64, 64, 64, 64, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 64, 1, 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, 1, 65, 65, + 65, 65, 65, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 65, 1, 66, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 67, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 1, + 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, 1, 1, 1, 1, 1, 1, + 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, 1, 70, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 71, 71, + 1, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 1, 1, 1, 1, 1, + 1, 1, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 1, 1, 1, 1, + 71, 1, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 1, 72, 72, 72, + 72, 72, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 72, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 73, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 74, 1, 72, 72, 72, 72, 72, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 72, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 73, 1, + 1, 1, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 74, + 1, 76, 76, 76, 76, 76, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 76, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 77, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 78, 1, 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, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 0 +}; + +static const char _deserialize_json_trans_targs[] = { + 1, 0, 2, 2, 3, 4, 18, 24, + 37, 5, 12, 6, 7, 8, 9, 11, + 9, 11, 10, 2, 44, 10, 44, 13, + 14, 15, 16, 17, 16, 17, 10, 2, + 44, 19, 20, 21, 22, 23, 10, 2, + 44, 23, 25, 31, 26, 27, 28, 29, + 30, 29, 30, 10, 2, 44, 32, 33, + 34, 35, 36, 35, 36, 10, 2, 44, + 38, 39, 40, 42, 43, 41, 10, 41, + 10, 2, 44, 43, 44, 45, 46 +}; + +static const char _deserialize_json_trans_actions[] = { + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 2, + 0, 0, 3, 3, 4, 0, 5, 0, + 0, 2, 2, 2, 0, 0, 6, 6, + 7, 0, 0, 0, 2, 2, 8, 8, + 9, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 10, 10, 11, 0, 0, + 2, 2, 2, 0, 0, 12, 12, 13, + 0, 0, 0, 2, 2, 2, 14, 0, + 15, 15, 16, 0, 0, 0, 0 +}; + +static const int deserialize_json_start = 1; +static const int deserialize_json_first_final = 44; +static const int deserialize_json_error = 0; + +static const int deserialize_json_en_main = 1; + + +#line 97 "hb-buffer-deserialize-json.rl" + + +static hb_bool_t +_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? ',' : '[')) + { + *end_ptr = ++p; + } + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 466 "hb-buffer-deserialize-json.hh" + { + cs = deserialize_json_start; + } + +#line 471 "hb-buffer-deserialize-json.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_json_trans_keys + (cs<<1); + _inds = _deserialize_json_indicies + _deserialize_json_index_offsets[cs]; + + _slen = _deserialize_json_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_json_trans_targs[_trans]; + + if ( _deserialize_json_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_json_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-json.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + break; + case 5: +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 2: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} + break; + case 14: +#line 55 "hb-buffer-deserialize-json.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 15: +#line 62 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } + break; + case 8: +#line 63 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 10: +#line 64 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 65 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 3: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 6: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 16: +#line 62 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 63 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 65 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 4: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 7: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 624 "hb-buffer-deserialize-json.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 125 "hb-buffer-deserialize-json.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh new file mode 100644 index 00000000000..67f0a1252f8 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh @@ -0,0 +1,571 @@ + +#line 1 "hb-buffer-deserialize-text.rl" +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH +#define HB_BUFFER_DESERIALIZE_TEXT_HH + +#include "hb.hh" + + +#line 36 "hb-buffer-deserialize-text.hh" +static const unsigned char _deserialize_text_trans_keys[] = { + 0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, + 9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 0 +}; + +static const char _deserialize_text_key_spans[] = { + 0, 114, 13, 10, 13, 10, 10, 13, + 10, 1, 13, 10, 14, 116, 116, 0, + 114, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116 +}; + +static const short _deserialize_text_index_offsets[] = { + 0, 0, 115, 129, 140, 154, 165, 176, + 190, 201, 203, 217, 228, 243, 360, 477, + 478, 593, 710, 827, 944, 1061, 1178, 1295, + 1412, 1529, 1646 +}; + +static const char _deserialize_text_indicies[] = { + 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, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 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, 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, 1, 5, 1, 1, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 1, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 1, 10, 1, 1, + 11, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 1, 13, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 1, 15, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 1, 17, 1, 1, 18, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 1, 20, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 1, 22, 1, 23, 1, 1, 24, + 25, 25, 25, 25, 25, 25, 25, 25, + 25, 1, 26, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 1, 22, 1, 1, + 1, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 1, 28, 28, 28, 28, + 28, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 29, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 30, 1, 1, 31, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 32, 1, 1, 1, 1, 1, 1, 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, + 1, 34, 34, 34, 34, 34, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 35, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 36, 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, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 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, 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, 1, 28, 28, 28, 28, 28, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 28, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 29, 1, 1, 1, + 1, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 1, 1, 1, 30, 1, + 1, 31, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 32, 1, + 1, 1, 1, 1, 1, 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, 1, 38, + 38, 38, 38, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 38, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 39, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 40, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 41, 1, 42, 42, 42, 42, + 42, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 42, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 43, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 44, + 1, 42, 42, 42, 42, 42, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 42, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 43, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 44, 1, 38, 38, + 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 1, 1, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 40, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 41, 1, 45, 45, 45, 45, 45, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 45, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 46, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 48, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 49, 1, + 50, 50, 50, 50, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 50, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 51, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 52, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 50, 50, 50, + 50, 50, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 51, + 1, 1, 1, 1, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 52, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 53, 1, 45, 45, 45, 45, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 45, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 46, 1, 1, 1, + 1, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 1, 1, 1, 1, 1, + 1, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 48, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 49, 1, 28, + 28, 28, 28, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 28, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 29, 1, 55, 55, 1, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 1, 1, 1, 30, 1, 1, 31, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 1, 1, 32, 1, 55, 1, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 1, 33, 1, 0 +}; + +static const char _deserialize_text_trans_targs[] = { + 1, 0, 13, 17, 26, 3, 18, 21, + 18, 21, 5, 19, 20, 19, 20, 22, + 25, 8, 9, 12, 9, 12, 10, 11, + 23, 24, 23, 24, 14, 2, 6, 7, + 15, 16, 14, 15, 16, 17, 14, 4, + 15, 16, 14, 15, 16, 14, 2, 7, + 15, 16, 14, 2, 15, 16, 25, 26 +}; + +static const char _deserialize_text_trans_actions[] = { + 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 2, 2, 2, 0, 0, 2, + 2, 2, 2, 2, 0, 0, 3, 2, + 2, 2, 0, 0, 4, 5, 5, 5, + 4, 4, 0, 0, 0, 0, 6, 7, + 6, 6, 8, 8, 8, 9, 10, 10, + 9, 9, 11, 12, 11, 11, 0, 0 +}; + +static const char _deserialize_text_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, + 0, 4, 6, 8, 8, 6, 9, 11, + 11, 9, 4 +}; + +static const int deserialize_text_start = 1; +static const int deserialize_text_first_final = 13; +static const int deserialize_text_error = 0; + +static const int deserialize_text_en_main = 1; + + +#line 91 "hb-buffer-deserialize-text.rl" + + +static hb_bool_t +_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '[')) + { + *end_ptr = ++p; + } + + const char *eof = pe, *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 343 "hb-buffer-deserialize-text.hh" + { + cs = deserialize_text_start; + } + +#line 348 "hb-buffer-deserialize-text.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_trans_keys + (cs<<1); + _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs]; + + _slen = _deserialize_text_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_trans_targs[_trans]; + + if ( _deserialize_text_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_trans_actions[_trans] ) { + case 2: +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 5: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 10: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 3: +#line 63 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 7: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 1: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 4: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 6: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 66 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 480 "hb-buffer-deserialize-text.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_eof_actions[cs] ) { + case 4: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 6: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 66 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 557 "hb-buffer-deserialize-text.hh" + } + } + + _out: {} + } + +#line 119 "hb-buffer-deserialize-text.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/thirdparty/harfbuzz/src/hb-buffer-serialize.cc b/thirdparty/harfbuzz/src/hb-buffer-serialize.cc new file mode 100644 index 00000000000..bc6c978b38c --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-buffer-serialize.cc @@ -0,0 +1,474 @@ +/* + * Copyright © 2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_BUFFER_SERIALIZE + +#include "hb-buffer.hh" + + +static const char *serialize_formats[] = { + "text", + "json", + nullptr +}; + +/** + * hb_buffer_serialize_list_formats: + * + * Returns a list of supported buffer serialization formats. + * + * Return value: (transfer none): + * A string array of buffer serialization formats. Should not be freed. + * + * Since: 0.9.7 + **/ +const char ** +hb_buffer_serialize_list_formats () +{ + return serialize_formats; +} + +/** + * hb_buffer_serialize_format_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is %NULL terminated + * + * Parses a string into an #hb_buffer_serialize_format_t. Does not check if + * @str is a valid buffer serialization format, use + * hb_buffer_serialize_list_formats() to get the list of supported formats. + * + * Return value: + * The parsed #hb_buffer_serialize_format_t. + * + * Since: 0.9.7 + **/ +hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len) +{ + /* Upper-case it. */ + return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020u); +} + +/** + * hb_buffer_serialize_format_to_string: + * @format: an #hb_buffer_serialize_format_t to convert. + * + * Converts @format to the string corresponding it, or %NULL if it is not a valid + * #hb_buffer_serialize_format_t. + * + * Return value: (transfer none): + * A %NULL terminated string corresponding to @format. Should not be freed. + * + * Since: 0.9.7 + **/ +const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) +{ + switch ((unsigned) format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; + case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr; + } +} + +static unsigned int +_hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); + + *buf_consumed = 0; + hb_position_t x = 0, y = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + +#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END + + if (i) + *p++ = ','; + + *p++ = '{'; + + APPEND ("\"g\":"); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + char g[128]; + hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g)); + *p++ = '"'; + for (char *q = g; *q; q++) { + if (*q == '"') + *p++ = '\\'; + *p++ = *q; + } + *p++ = '"'; + } + else + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", + x+pos[i].x_offset, y+pos[i].y_offset)); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", + pos[i].x_advance, pos[i].y_advance)); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) + { + if (info[i].mask & HB_GLYPH_FLAG_DEFINED) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); + } + + *p++ = '}'; + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + + return end - start; +} + +static unsigned int +_hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); + + *buf_consumed = 0; + hb_position_t x = 0, y = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + + if (i) + *p++ = '|'; + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + hb_font_glyph_to_string (font, info[i].codepoint, p, 128); + p += strlen (p); + } + else + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + if (x+pos[i].x_offset || y+pos[i].y_offset) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + *p++ = '+'; + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + if (pos[i].y_advance) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + } + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) + { + if (info[i].mask & HB_GLYPH_FLAG_DEFINED) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + } + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + + return end - start; +} + +/** + * hb_buffer_serialize_glyphs: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf. + * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to + * read glyph names and extents. If %NULL, and empty font will be used. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its glyph content, + * useful for showing the contents of the buffer, for example during debugging. + * There are currently two supported serialization formats: + * + * ## text + * A human-readable, plain text format. + * The serialized glyphs will look something like: + * + * ``` + * [uni0651=0@518,0+0|uni0628=0+1897] + * ``` + * - The serialized glyphs are delimited with `[` and `]`. + * - Glyphs are separated with `|` + * - Each glyph starts with glyph name, or glyph index if + * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. Then, + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, `=` then #hb_glyph_info_t.cluster. + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format: + * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then, + * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then, + * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the + * #hb_glyph_extents_t in the format + * `<x_bearing,y_bearing,width,height>` + * + * ## json + * TODO. + * + * Return value: + * The number of serialized items. + * + * Since: 0.9.7 + **/ +unsigned int +hb_buffer_serialize_glyphs (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + assert (start <= end && end <= buffer->len); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + if (buf_size) + *buf = '\0'; + + assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) || + (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)); + + if (!buffer->have_positions) + flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; + + if (unlikely (start == end)) + return 0; + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_serialize_glyphs_text (buffer, start, end, + buf, buf_size, buf_consumed, + font, flags); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_serialize_glyphs_json (buffer, start, end, + buf, buf_size, buf_consumed, + font, flags); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return 0; + + } +} + +static bool +parse_int (const char *pp, const char *end, int32_t *pv) +{ + int v; + const char *p = pp; + if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */))) + return false; + + *pv = v; + return true; +} + +static bool +parse_uint (const char *pp, const char *end, uint32_t *pv) +{ + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */))) + return false; + + *pv = v; + return true; +} + +#include "hb-buffer-deserialize-json.hh" +#include "hb-buffer-deserialize-text.hh" + +/** + * hb_buffer_deserialize_glyphs: + * @buffer: an #hb_buffer_t buffer. + * @buf: (array length=buf_len): + * @buf_len: + * @end_ptr: (out): + * @font: + * @format: + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format) +{ + const char *end; + if (!end_ptr) + end_ptr = &end; + *end_ptr = buf; + + assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) || + (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)); + + if (buf_len == -1) + buf_len = strlen (buf); + + if (!buf_len) + { + *end_ptr = buf; + return false; + } + + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_deserialize_glyphs_text (buffer, + buf, buf_len, end_ptr, + font); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_deserialize_glyphs_json (buffer, + buf, buf_len, end_ptr, + font); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return false; + + } +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-buffer.cc b/thirdparty/harfbuzz/src/hb-buffer.cc new file mode 100644 index 00000000000..4fadbb78d23 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-buffer.cc @@ -0,0 +1,2004 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-buffer.hh" +#include "hb-utf.hh" + + +/** + * SECTION: hb-buffer + * @title: hb-buffer + * @short_description: Input and output buffers + * @include: hb.h + * + * Buffers serve dual role in HarfBuzz; they hold the input characters that are + * passed to hb_shape(), and after shaping they hold the output glyphs. + **/ + + +/** + * hb_segment_properties_equal: + * @a: first #hb_segment_properties_t to compare. + * @b: second #hb_segment_properties_t to compare. + * + * Checks the equality of two #hb_segment_properties_t's. + * + * Return value: + * %true if all properties of @a equal those of @b, false otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b) +{ + return a->direction == b->direction && + a->script == b->script && + a->language == b->language && + a->reserved1 == b->reserved1 && + a->reserved2 == b->reserved2; + +} + +/** + * hb_segment_properties_hash: + * @p: #hb_segment_properties_t to hash. + * + * Creates a hash representing @p. + * + * Return value: + * A hash of @p. + * + * Since: 0.9.7 + **/ +unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p) +{ + return (unsigned int) p->direction ^ + (unsigned int) p->script ^ + (intptr_t) (p->language); +} + + + +/* Here is how the buffer works internally: + * + * There are two info pointers: info and out_info. They always have + * the same allocated size, but different lengths. + * + * As an optimization, both info and out_info may point to the + * same piece of memory, which is owned by info. This remains the + * case as long as out_len doesn't exceed i at any time. + * In that case, swap_buffers() is no-op and the glyph operations operate + * mostly in-place. + * + * As soon as out_info gets longer than info, out_info is moved over + * to an alternate buffer (which we reuse the pos buffer for!), and its + * current contents (out_len entries) are copied to the new place. + * This should all remain transparent to the user. swap_buffers() then + * switches info and out_info. + */ + + + +/* Internal API */ + +bool +hb_buffer_t::enlarge (unsigned int size) +{ + if (unlikely (!successful)) + return false; + if (unlikely (size > max_len)) + { + successful = false; + return false; + } + + unsigned int new_allocated = allocated; + hb_glyph_position_t *new_pos = nullptr; + hb_glyph_info_t *new_info = nullptr; + bool separate_out = out_info != info; + + if (unlikely (hb_unsigned_mul_overflows (size, sizeof (info[0])))) + goto done; + + while (size >= new_allocated) + new_allocated += (new_allocated >> 1) + 32; + + static_assert ((sizeof (info[0]) == sizeof (pos[0])), ""); + if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0])))) + goto done; + + new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0])); + new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0])); + +done: + if (unlikely (!new_pos || !new_info)) + successful = false; + + if (likely (new_pos)) + pos = new_pos; + + if (likely (new_info)) + info = new_info; + + out_info = separate_out ? (hb_glyph_info_t *) pos : info; + if (likely (successful)) + allocated = new_allocated; + + return likely (successful); +} + +bool +hb_buffer_t::make_room_for (unsigned int num_in, + unsigned int num_out) +{ + if (unlikely (!ensure (out_len + num_out))) return false; + + if (out_info == info && + out_len + num_out > idx + num_in) + { + assert (have_output); + + out_info = (hb_glyph_info_t *) pos; + memcpy (out_info, info, out_len * sizeof (out_info[0])); + } + + return true; +} + +bool +hb_buffer_t::shift_forward (unsigned int count) +{ + assert (have_output); + if (unlikely (!ensure (len + count))) return false; + + memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); + if (idx + count > len) + { + /* Under memory failure we might expose this area. At least + * clean it up. Oh well... + * + * Ideally, we should at least set Default_Ignorable bits on + * these, as well as consistent cluster values. But the former + * is layering violation... */ + memset (info + len, 0, (idx + count - len) * sizeof (info[0])); + } + len += count; + idx += count; + + return true; +} + +hb_buffer_t::scratch_buffer_t * +hb_buffer_t::get_scratch_buffer (unsigned int *size) +{ + have_output = false; + have_positions = false; + + out_len = 0; + out_info = info; + + assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0); + *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t); + return (scratch_buffer_t *) (void *) pos; +} + + + +/* HarfBuzz-Internal API */ + +void +hb_buffer_t::reset () +{ + if (unlikely (hb_object_is_immutable (this))) + return; + + hb_unicode_funcs_destroy (unicode); + unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ()); + flags = HB_BUFFER_FLAG_DEFAULT; + replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + invisible = 0; + + clear (); +} + +void +hb_buffer_t::clear () +{ + if (unlikely (hb_object_is_immutable (this))) + return; + + hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; + props = default_props; + scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; + + content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + successful = true; + have_output = false; + have_positions = false; + + idx = 0; + len = 0; + out_len = 0; + out_info = info; + + serial = 0; + + memset (context, 0, sizeof context); + memset (context_len, 0, sizeof context_len); + + deallocate_var_all (); +} + +void +hb_buffer_t::add (hb_codepoint_t codepoint, + unsigned int cluster) +{ + hb_glyph_info_t *glyph; + + if (unlikely (!ensure (len + 1))) return; + + glyph = &info[len]; + + memset (glyph, 0, sizeof (*glyph)); + glyph->codepoint = codepoint; + glyph->mask = 0; + glyph->cluster = cluster; + + len++; +} + +void +hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) +{ + if (unlikely (!ensure (len + 1))) return; + + info[len] = glyph_info; + + len++; +} + + +void +hb_buffer_t::remove_output () +{ + if (unlikely (hb_object_is_immutable (this))) + return; + + have_output = false; + have_positions = false; + + out_len = 0; + out_info = info; +} + +void +hb_buffer_t::clear_output () +{ + if (unlikely (hb_object_is_immutable (this))) + return; + + have_output = true; + have_positions = false; + + out_len = 0; + out_info = info; +} + +void +hb_buffer_t::clear_positions () +{ + if (unlikely (hb_object_is_immutable (this))) + return; + + have_output = false; + have_positions = true; + + out_len = 0; + out_info = info; + + hb_memset (pos, 0, sizeof (pos[0]) * len); +} + +void +hb_buffer_t::swap_buffers () +{ + if (unlikely (!successful)) return; + + assert (have_output); + have_output = false; + + if (out_info != info) + { + hb_glyph_info_t *tmp_string; + tmp_string = info; + info = out_info; + out_info = tmp_string; + pos = (hb_glyph_position_t *) out_info; + } + + unsigned int tmp; + tmp = len; + len = out_len; + out_len = tmp; + + idx = 0; +} + + +void +hb_buffer_t::replace_glyphs (unsigned int num_in, + unsigned int num_out, + const uint32_t *glyph_data) +{ + if (unlikely (!make_room_for (num_in, num_out))) return; + + assert (idx + num_in <= len); + + merge_clusters (idx, idx + num_in); + + hb_glyph_info_t orig_info = info[idx]; + hb_glyph_info_t *pinfo = &out_info[out_len]; + for (unsigned int i = 0; i < num_out; i++) + { + *pinfo = orig_info; + pinfo->codepoint = glyph_data[i]; + pinfo++; + } + + idx += num_in; + out_len += num_out; +} + +bool +hb_buffer_t::move_to (unsigned int i) +{ + if (!have_output) + { + assert (i <= len); + idx = i; + return true; + } + if (unlikely (!successful)) + return false; + + assert (i <= out_len + (len - idx)); + + if (out_len < i) + { + unsigned int count = i - out_len; + if (unlikely (!make_room_for (count, count))) return false; + + memmove (out_info + out_len, info + idx, count * sizeof (out_info[0])); + idx += count; + out_len += count; + } + else if (out_len > i) + { + /* Tricky part: rewinding... */ + unsigned int count = out_len - i; + + /* This will blow in our face if memory allocation fails later + * in this same lookup... + * + * We used to shift with extra 32 items, instead of the 0 below. + * But that would leave empty slots in the buffer in case of allocation + * failures. Setting to zero for now to avoid other problems (see + * comments in shift_forward(). This can cause O(N^2) behavior more + * severely than adding 32 empty slots can... */ + if (unlikely (idx < count && !shift_forward (count + 0))) return false; + + assert (idx >= count); + + idx -= count; + out_len -= count; + memmove (info + idx, out_info + out_len, count * sizeof (out_info[0])); + } + + return true; +} + + +void +hb_buffer_t::set_masks (hb_mask_t value, + hb_mask_t mask, + unsigned int cluster_start, + unsigned int cluster_end) +{ + hb_mask_t not_mask = ~mask; + value &= mask; + + if (!mask) + return; + + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) + info[i].mask = (info[i].mask & not_mask) | value; +} + +void +hb_buffer_t::reverse_range (unsigned int start, + unsigned int end) +{ + if (end - start < 2) + return; + + hb_array_t (info, len).reverse (start, end); + + if (have_positions) { + hb_array_t (pos, len).reverse (start, end); + } +} + +void +hb_buffer_t::reverse () +{ + if (unlikely (!len)) + return; + + reverse_range (0, len); +} + +void +hb_buffer_t::reverse_clusters () +{ + unsigned int i, start, count, last_cluster; + + if (unlikely (!len)) + return; + + reverse (); + + count = len; + start = 0; + last_cluster = info[0].cluster; + for (i = 1; i < count; i++) { + if (last_cluster != info[i].cluster) { + reverse_range (start, i); + start = i; + last_cluster = info[i].cluster; + } + } + reverse_range (start, i); +} + +void +hb_buffer_t::merge_clusters_impl (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + { + unsafe_to_break (start, end); + return; + } + + unsigned int cluster = info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = hb_min (cluster, info[i].cluster); + + /* Extend end */ + while (end < len && info[end - 1].cluster == info[end].cluster) + end++; + + /* Extend start */ + while (idx < start && info[start - 1].cluster == info[start].cluster) + start--; + + /* If we hit the start of buffer, continue in out-buffer. */ + if (idx == start) + for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) + set_cluster (out_info[i - 1], cluster); + + for (unsigned int i = start; i < end; i++) + set_cluster (info[i], cluster); +} +void +hb_buffer_t::merge_out_clusters (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + return; + + if (unlikely (end - start < 2)) + return; + + unsigned int cluster = out_info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = hb_min (cluster, out_info[i].cluster); + + /* Extend start */ + while (start && out_info[start - 1].cluster == out_info[start].cluster) + start--; + + /* Extend end */ + while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster) + end++; + + /* If we hit the end of out-buffer, continue in buffer. */ + if (end == out_len) + for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++) + set_cluster (info[i], cluster); + + for (unsigned int i = start; i < end; i++) + set_cluster (out_info[i], cluster); +} +void +hb_buffer_t::delete_glyph () +{ + /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */ + + unsigned int cluster = info[idx].cluster; + if (idx + 1 < len && cluster == info[idx + 1].cluster) + { + /* Cluster survives; do nothing. */ + goto done; + } + + if (out_len) + { + /* Merge cluster backward. */ + if (cluster < out_info[out_len - 1].cluster) + { + unsigned int mask = info[idx].mask; + unsigned int old_cluster = out_info[out_len - 1].cluster; + for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--) + set_cluster (out_info[i - 1], cluster, mask); + } + goto done; + } + + if (idx + 1 < len) + { + /* Merge cluster forward. */ + merge_clusters (idx, idx + 2); + goto done; + } + +done: + skip_glyph (); +} + +void +hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end) +{ + unsigned int cluster = UINT_MAX; + cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster); + _unsafe_to_break_set_mask (info, start, end, cluster); +} +void +hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end) +{ + if (!have_output) + { + unsafe_to_break_impl (start, end); + return; + } + + assert (start <= out_len); + assert (idx <= end); + + unsigned int cluster = UINT_MAX; + cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster); + cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster); + _unsafe_to_break_set_mask (out_info, start, out_len, cluster); + _unsafe_to_break_set_mask (info, idx, end, cluster); +} + +void +hb_buffer_t::guess_segment_properties () +{ + assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + + /* If script is set to INVALID, guess from buffer contents */ + if (props.script == HB_SCRIPT_INVALID) { + for (unsigned int i = 0; i < len; i++) { + hb_script_t script = unicode->script (info[i].codepoint); + if (likely (script != HB_SCRIPT_COMMON && + script != HB_SCRIPT_INHERITED && + script != HB_SCRIPT_UNKNOWN)) { + props.script = script; + break; + } + } + } + + /* If direction is set to INVALID, guess from script */ + if (props.direction == HB_DIRECTION_INVALID) { + props.direction = hb_script_get_horizontal_direction (props.script); + if (props.direction == HB_DIRECTION_INVALID) + props.direction = HB_DIRECTION_LTR; + } + + /* If language is not set, use default language from locale */ + if (props.language == HB_LANGUAGE_INVALID) { + /* TODO get_default_for_script? using $LANGUAGE */ + props.language = hb_language_get_default (); + } +} + + +/* Public API */ + +DEFINE_NULL_INSTANCE (hb_buffer_t) = +{ + HB_OBJECT_HEADER_STATIC, + + const_cast (&_hb_Null_hb_unicode_funcs_t), + HB_BUFFER_FLAG_DEFAULT, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, + 0, /* invisible */ + HB_BUFFER_SCRATCH_FLAG_DEFAULT, + HB_BUFFER_MAX_LEN_DEFAULT, + HB_BUFFER_MAX_OPS_DEFAULT, + + HB_BUFFER_CONTENT_TYPE_INVALID, + HB_SEGMENT_PROPERTIES_DEFAULT, + false, /* successful */ + true, /* have_output */ + true /* have_positions */ + + /* Zero is good enough for everything else. */ +}; + + +/** + * hb_buffer_create: (Xconstructor) + * + * Creates a new #hb_buffer_t with all properties to defaults. + * + * Return value: (transfer full): + * A newly allocated #hb_buffer_t with a reference count of 1. The initial + * reference count should be released with hb_buffer_destroy() when you are done + * using the #hb_buffer_t. This function never returns %NULL. If memory cannot + * be allocated, a special #hb_buffer_t object will be returned on which + * hb_buffer_allocation_successful() returns %false. + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_create () +{ + hb_buffer_t *buffer; + + if (!(buffer = hb_object_create ())) + return hb_buffer_get_empty (); + + buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT; + buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT; + + buffer->reset (); + + return buffer; +} + +/** + * hb_buffer_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_get_empty () +{ + return const_cast (&Null (hb_buffer_t)); +} + +/** + * hb_buffer_reference: (skip) + * @buffer: an #hb_buffer_t. + * + * Increases the reference count on @buffer by one. This prevents @buffer from + * being destroyed until a matching call to hb_buffer_destroy() is made. + * + * Return value: (transfer full): + * The referenced #hb_buffer_t. + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer) +{ + return hb_object_reference (buffer); +} + +/** + * hb_buffer_destroy: (skip) + * @buffer: an #hb_buffer_t. + * + * Deallocate the @buffer. + * Decreases the reference count on @buffer by one. If the result is zero, then + * @buffer and all associated resources are freed. See hb_buffer_reference(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_destroy (hb_buffer_t *buffer) +{ + if (!hb_object_destroy (buffer)) return; + + hb_unicode_funcs_destroy (buffer->unicode); + + free (buffer->info); + free (buffer->pos); +#ifndef HB_NO_BUFFER_MESSAGE + if (buffer->message_destroy) + buffer->message_destroy (buffer->message_data); +#endif + + free (buffer); +} + +/** + * hb_buffer_set_user_data: (skip) + * @buffer: an #hb_buffer_t. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_set_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (buffer, key, data, destroy, replace); +} + +/** + * hb_buffer_get_user_data: (skip) + * @buffer: an #hb_buffer_t. + * @key: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +void * +hb_buffer_get_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (buffer, key); +} + + +/** + * hb_buffer_set_content_type: + * @buffer: an #hb_buffer_t. + * @content_type: the type of buffer contents to set + * + * Sets the type of @buffer contents, buffers are either empty, contain + * characters (before shaping) or glyphs (the result of shaping). + * + * Since: 0.9.5 + **/ +void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type) +{ + buffer->content_type = content_type; +} + +/** + * hb_buffer_get_content_type: + * @buffer: an #hb_buffer_t. + * + * see hb_buffer_set_content_type(). + * + * Return value: + * The type of @buffer contents. + * + * Since: 0.9.5 + **/ +hb_buffer_content_type_t +hb_buffer_get_content_type (hb_buffer_t *buffer) +{ + return buffer->content_type; +} + + +/** + * hb_buffer_set_unicode_funcs: + * @buffer: an #hb_buffer_t. + * @unicode_funcs: + * + * + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + if (!unicode_funcs) + unicode_funcs = hb_unicode_funcs_get_default (); + + hb_unicode_funcs_reference (unicode_funcs); + hb_unicode_funcs_destroy (buffer->unicode); + buffer->unicode = unicode_funcs; +} + +/** + * hb_buffer_get_unicode_funcs: + * @buffer: an #hb_buffer_t. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) +{ + return buffer->unicode; +} + +/** + * hb_buffer_set_direction: + * @buffer: an #hb_buffer_t. + * @direction: the #hb_direction_t of the @buffer + * + * Set the text flow direction of the buffer. No shaping can happen without + * setting @buffer direction, and it controls the visual direction for the + * output glyphs; for RTL direction the glyphs will be reversed. Many layout + * features depend on the proper setting of the direction, for example, + * reversing RTL text before shaping, then shaping with LTR direction is not + * the same as keeping the text in logical order and shaping with RTL + * direction. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction) + +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.direction = direction; +} + +/** + * hb_buffer_get_direction: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_direction() + * + * Return value: + * The direction of the @buffer. + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_buffer_get_direction (hb_buffer_t *buffer) +{ + return buffer->props.direction; +} + +/** + * hb_buffer_set_script: + * @buffer: an #hb_buffer_t. + * @script: an #hb_script_t to set. + * + * Sets the script of @buffer to @script. + * + * Script is crucial for choosing the proper shaping behaviour for scripts that + * require it (e.g. Arabic) and the which OpenType features defined in the font + * to be applied. + * + * You can pass one of the predefined #hb_script_t values, or use + * hb_script_from_string() or hb_script_from_iso15924_tag() to get the + * corresponding script from an ISO 15924 script tag. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.script = script; +} + +/** + * hb_buffer_get_script: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_script(). + * + * Return value: + * The #hb_script_t of the @buffer. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_buffer_get_script (hb_buffer_t *buffer) +{ + return buffer->props.script; +} + +/** + * hb_buffer_set_language: + * @buffer: an #hb_buffer_t. + * @language: an hb_language_t to set. + * + * Sets the language of @buffer to @language. + * + * Languages are crucial for selecting which OpenType feature to apply to the + * buffer which can result in applying language-specific behaviour. Languages + * are orthogonal to the scripts, and though they are related, they are + * different concepts and should not be confused with each other. + * + * Use hb_language_from_string() to convert from BCP 47 language tags to + * #hb_language_t. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.language = language; +} + +/** + * hb_buffer_get_language: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_language(). + * + * Return value: (transfer none): + * The #hb_language_t of the buffer. Must not be freed by the caller. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_buffer_get_language (hb_buffer_t *buffer) +{ + return buffer->props.language; +} + +/** + * hb_buffer_set_segment_properties: + * @buffer: an #hb_buffer_t. + * @props: an #hb_segment_properties_t to use. + * + * Sets the segment properties of the buffer, a shortcut for calling + * hb_buffer_set_direction(), hb_buffer_set_script() and + * hb_buffer_set_language() individually. + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props = *props; +} + +/** + * hb_buffer_get_segment_properties: + * @buffer: an #hb_buffer_t. + * @props: (out): the output #hb_segment_properties_t. + * + * Sets @props to the #hb_segment_properties_t of @buffer. + * + * Since: 0.9.7 + **/ +void +hb_buffer_get_segment_properties (hb_buffer_t *buffer, + hb_segment_properties_t *props) +{ + *props = buffer->props; +} + + +/** + * hb_buffer_set_flags: + * @buffer: an #hb_buffer_t. + * @flags: the buffer flags to set. + * + * Sets @buffer flags to @flags. See #hb_buffer_flags_t. + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->flags = flags; +} + +/** + * hb_buffer_get_flags: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_flags(). + * + * Return value: + * The @buffer flags. + * + * Since: 0.9.7 + **/ +hb_buffer_flags_t +hb_buffer_get_flags (hb_buffer_t *buffer) +{ + return buffer->flags; +} + +/** + * hb_buffer_set_cluster_level: + * @buffer: an #hb_buffer_t. + * @cluster_level: + * + * + * + * Since: 0.9.42 + **/ +void +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->cluster_level = cluster_level; +} + +/** + * hb_buffer_get_cluster_level: + * @buffer: an #hb_buffer_t. + * + * + * + * Return value: + * + * Since: 0.9.42 + **/ +hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (hb_buffer_t *buffer) +{ + return buffer->cluster_level; +} + + +/** + * hb_buffer_set_replacement_codepoint: + * @buffer: an #hb_buffer_t. + * @replacement: the replacement #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces invalid entries for a given encoding + * when adding text to @buffer. + * + * Default is %HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT. + * + * Since: 0.9.31 + **/ +void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->replacement = replacement; +} + +/** + * hb_buffer_get_replacement_codepoint: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_replacement_codepoint(). + * + * Return value: + * The @buffer replacement #hb_codepoint_t. + * + * Since: 0.9.31 + **/ +hb_codepoint_t +hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer) +{ + return buffer->replacement; +} + + +/** + * hb_buffer_set_invisible_glyph: + * @buffer: an #hb_buffer_t. + * @invisible: the invisible #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces invisible characters in + * the shaping result. If set to zero (default), the glyph for the + * U+0020 SPACE character is used. Otherwise, this value is used + * verbatim. + * + * Since: 2.0.0 + **/ +void +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->invisible = invisible; +} + +/** + * hb_buffer_get_invisible_glyph: + * @buffer: an #hb_buffer_t. + * + * See hb_buffer_set_invisible_glyph(). + * + * Return value: + * The @buffer invisible #hb_codepoint_t. + * + * Since: 2.0.0 + **/ +hb_codepoint_t +hb_buffer_get_invisible_glyph (hb_buffer_t *buffer) +{ + return buffer->invisible; +} + + +/** + * hb_buffer_reset: + * @buffer: an #hb_buffer_t. + * + * Resets the buffer to its initial status, as if it was just newly created + * with hb_buffer_create(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_reset (hb_buffer_t *buffer) +{ + buffer->reset (); +} + +/** + * hb_buffer_clear_contents: + * @buffer: an #hb_buffer_t. + * + * Similar to hb_buffer_reset(), but does not clear the Unicode functions and + * the replacement code point. + * + * Since: 0.9.11 + **/ +void +hb_buffer_clear_contents (hb_buffer_t *buffer) +{ + buffer->clear (); +} + +/** + * hb_buffer_pre_allocate: + * @buffer: an #hb_buffer_t. + * @size: number of items to pre allocate. + * + * Pre allocates memory for @buffer to fit at least @size number of items. + * + * Return value: + * %true if @buffer memory allocation succeeded, %false otherwise. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) +{ + return buffer->ensure (size); +} + +/** + * hb_buffer_allocation_successful: + * @buffer: an #hb_buffer_t. + * + * Check if allocating memory for the buffer succeeded. + * + * Return value: + * %true if @buffer memory allocation succeeded, %false otherwise. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer) +{ + return buffer->successful; +} + +/** + * hb_buffer_add: + * @buffer: an #hb_buffer_t. + * @codepoint: a Unicode code point. + * @cluster: the cluster value of @codepoint. + * + * Appends a character with the Unicode value of @codepoint to @buffer, and + * gives it the initial cluster value of @cluster. Clusters can be any thing + * the client wants, they are usually used to refer to the index of the + * character in the input text stream and are output in + * #hb_glyph_info_t.cluster field. + * + * This function does not check the validity of @codepoint, it is up to the + * caller to ensure it is a valid Unicode code point. + * + * Since: 0.9.7 + **/ +void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster) +{ + buffer->add (codepoint, cluster); + buffer->clear_context (1); +} + +/** + * hb_buffer_set_length: + * @buffer: an #hb_buffer_t. + * @length: the new length of @buffer. + * + * Similar to hb_buffer_pre_allocate(), but clears any new items added at the + * end. + * + * Return value: + * %true if @buffer memory allocation succeeded, %false otherwise. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return length == 0; + + if (!buffer->ensure (length)) + return false; + + /* Wipe the new space */ + if (length > buffer->len) { + memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + if (buffer->have_positions) + memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + } + + buffer->len = length; + + if (!length) + { + buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + buffer->clear_context (0); + } + buffer->clear_context (1); + + return true; +} + +/** + * hb_buffer_get_length: + * @buffer: an #hb_buffer_t. + * + * Returns the number of items in the buffer. + * + * Return value: + * The @buffer length. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +unsigned int +hb_buffer_get_length (hb_buffer_t *buffer) +{ + return buffer->len; +} + +/** + * hb_buffer_get_glyph_infos: + * @buffer: an #hb_buffer_t. + * @length: (out): output array length. + * + * Returns @buffer glyph information array. Returned pointer + * is valid as long as @buffer contents are not modified. + * + * Return value: (transfer none) (array length=length): + * The @buffer glyph information array. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length) +{ + if (length) + *length = buffer->len; + + return (hb_glyph_info_t *) buffer->info; +} + +/** + * hb_buffer_get_glyph_positions: + * @buffer: an #hb_buffer_t. + * @length: (out): output length. + * + * Returns @buffer glyph position array. Returned pointer + * is valid as long as @buffer contents are not modified. + * + * Return value: (transfer none) (array length=length): + * The @buffer glyph position array. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length) +{ + if (!buffer->have_positions) + buffer->clear_positions (); + + if (length) + *length = buffer->len; + + return (hb_glyph_position_t *) buffer->pos; +} + +/** + * hb_glyph_info_get_glyph_flags: + * @info: a #hb_glyph_info_t. + * + * Returns glyph flags encoded within a #hb_glyph_info_t. + * + * Return value: + * The #hb_glyph_flags_t encoded within @info. + * + * Since: 1.5.0 + **/ +hb_glyph_flags_t +(hb_glyph_info_get_glyph_flags) (const hb_glyph_info_t *info) +{ + return hb_glyph_info_get_glyph_flags (info); +} + +/** + * hb_buffer_reverse: + * @buffer: an #hb_buffer_t. + * + * Reverses buffer contents. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse (hb_buffer_t *buffer) +{ + buffer->reverse (); +} + +/** + * hb_buffer_reverse_range: + * @buffer: an #hb_buffer_t. + * @start: start index. + * @end: end index. + * + * Reverses buffer contents between start to end. + * + * Since: 0.9.41 + **/ +void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + buffer->reverse_range (start, end); +} + +/** + * hb_buffer_reverse_clusters: + * @buffer: an #hb_buffer_t. + * + * Reverses buffer clusters. That is, the buffer contents are + * reversed, then each cluster (consecutive items having the + * same cluster number) are reversed again. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse_clusters (hb_buffer_t *buffer) +{ + buffer->reverse_clusters (); +} + +/** + * hb_buffer_guess_segment_properties: + * @buffer: an #hb_buffer_t. + * + * Sets unset buffer segment properties based on buffer Unicode + * contents. If buffer is not empty, it must have content type + * %HB_BUFFER_CONTENT_TYPE_UNICODE. + * + * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it + * will be set to the Unicode script of the first character in + * the buffer that has a script other than %HB_SCRIPT_COMMON, + * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN. + * + * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID), + * it will be set to the natural horizontal direction of the + * buffer script as returned by hb_script_get_horizontal_direction(). + * If hb_script_get_horizontal_direction() returns %HB_DIRECTION_INVALID, + * then %HB_DIRECTION_LTR is used. + * + * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID), + * it will be set to the process's default language as returned by + * hb_language_get_default(). This may change in the future by + * taking buffer script into consideration when choosing a language. + * Note that hb_language_get_default() is NOT threadsafe the first time + * it is called. See documentation for that function for details. + * + * Since: 0.9.7 + **/ +void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer) +{ + buffer->guess_segment_properties (); +} + +template +static inline void +hb_buffer_add_utf (hb_buffer_t *buffer, + const typename utf_t::codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + typedef typename utf_t::codepoint_t T; + const hb_codepoint_t replacement = buffer->replacement; + + assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + + if (unlikely (hb_object_is_immutable (buffer))) + return; + + if (text_length == -1) + text_length = utf_t::strlen (text); + + if (item_length == -1) + item_length = text_length - item_offset; + + buffer->ensure (buffer->len + item_length * sizeof (T) / 4); + + /* If buffer is empty and pre-context provided, install it. + * This check is written this way, to make sure people can + * provide pre-context in one add_utf() call, then provide + * text in a follow-up call. See: + * + * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 + */ + if (!buffer->len && item_offset > 0) + { + /* Add pre-context */ + buffer->clear_context (0); + const T *prev = text + item_offset; + const T *start = text; + while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + prev = utf_t::prev (prev, start, &u, replacement); + buffer->context[0][buffer->context_len[0]++] = u; + } + } + + const T *next = text + item_offset; + const T *end = next + item_length; + while (next < end) + { + hb_codepoint_t u; + const T *old_next = next; + next = utf_t::next (next, end, &u, replacement); + buffer->add (u, old_next - (const T *) text); + } + + /* Add post-context */ + buffer->clear_context (1); + end = text + text_length; + while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + next = utf_t::next (next, end, &u, replacement); + buffer->context[1][buffer->context_len[1]++] = u; + } + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; +} + +/** + * hb_buffer_add_utf8: + * @buffer: an #hb_buffer_t. + * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 + * characters to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first character to add to the @buffer. + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-8 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, (const uint8_t *) text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_utf16: + * @buffer: an #hb_buffer_t. + * @text: (array length=text_length): an array of UTF-16 characters to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first character to add to the @buffer. + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-16 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_utf32: + * @buffer: an #hb_buffer_t. + * @text: (array length=text_length): an array of UTF-32 characters to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first character to add to the @buffer. + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-32 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_latin1: + * @buffer: an #hb_buffer_t. + * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 + * characters to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first character to add to the @buffer. + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * Similar to hb_buffer_add_codepoints(), but allows only access to first 256 + * Unicode code points that can fit in 8-bit strings. + * + * Has nothing to do with non-Unicode Latin-1 encoding. + * + * Since: 0.9.39 + **/ +void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_codepoints: + * @buffer: a #hb_buffer_t to append characters to. + * @text: (array length=text_length): an array of Unicode code points to append. + * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @item_offset: the offset of the first code point to add to the @buffer. + * @item_length: the number of code points to add to the @buffer, or -1 for the + * end of @text (assuming it is %NULL terminated). + * + * Appends characters from @text array to @buffer. The @item_offset is the + * position of the first character from @text that will be appended, and + * @item_length is the number of character. When shaping part of a larger text + * (e.g. a run of text from a paragraph), instead of passing just the substring + * corresponding to the run, it is preferable to pass the whole + * paragraph and specify the run start and length as @item_offset and + * @item_length, respectively, to give HarfBuzz the full context to be able, + * for example, to do cross-run Arabic shaping or properly handle combining + * marks at stat of run. + * + * This function does not check the validity of @text, it is up to the caller + * to ensure it contains a valid Unicode code points. + * + * Since: 0.9.31 + **/ +void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + + +/** + * hb_buffer_append: + * @buffer: an #hb_buffer_t. + * @source: source #hb_buffer_t. + * @start: start index into source buffer to copy. Use 0 to copy from start of buffer. + * @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer. + * + * Append (part of) contents of another buffer to this buffer. + * + * Since: 1.5.0 + **/ +HB_EXTERN void +hb_buffer_append (hb_buffer_t *buffer, + hb_buffer_t *source, + unsigned int start, + unsigned int end) +{ + assert (!buffer->have_output && !source->have_output); + assert (buffer->have_positions == source->have_positions || + !buffer->len || !source->len); + assert (buffer->content_type == source->content_type || + !buffer->len || !source->len); + + if (end > source->len) + end = source->len; + if (start > end) + start = end; + if (start == end) + return; + + if (!buffer->len) + buffer->content_type = source->content_type; + if (!buffer->have_positions && source->have_positions) + buffer->clear_positions (); + + if (buffer->len + (end - start) < buffer->len) /* Overflows. */ + { + buffer->successful = false; + return; + } + + unsigned int orig_len = buffer->len; + hb_buffer_set_length (buffer, buffer->len + (end - start)); + if (unlikely (!buffer->successful)) + return; + + memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); + if (buffer->have_positions) + memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); +} + + +static int +compare_info_codepoint (const hb_glyph_info_t *pa, + const hb_glyph_info_t *pb) +{ + return (int) pb->codepoint - (int) pa->codepoint; +} + +static inline void +normalize_glyphs_cluster (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool backward) +{ + hb_glyph_position_t *pos = buffer->pos; + + /* Total cluster advance */ + hb_position_t total_x_advance = 0, total_y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + total_x_advance += pos[i].x_advance; + total_y_advance += pos[i].y_advance; + } + + hb_position_t x_advance = 0, y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + pos[i].x_offset += x_advance; + pos[i].y_offset += y_advance; + + x_advance += pos[i].x_advance; + y_advance += pos[i].y_advance; + + pos[i].x_advance = 0; + pos[i].y_advance = 0; + } + + if (backward) + { + /* Transfer all cluster advance to the last glyph. */ + pos[end - 1].x_advance = total_x_advance; + pos[end - 1].y_advance = total_y_advance; + + hb_stable_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); + } else { + /* Transfer all cluster advance to the first glyph. */ + pos[start].x_advance += total_x_advance; + pos[start].y_advance += total_y_advance; + for (unsigned int i = start + 1; i < end; i++) { + pos[i].x_offset -= total_x_advance; + pos[i].y_offset -= total_y_advance; + } + hb_stable_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); + } +} + +/** + * hb_buffer_normalize_glyphs: + * @buffer: an #hb_buffer_t. + * + * Reorders a glyph buffer to have canonical in-cluster glyph order / position. + * The resulting clusters should behave identical to pre-reordering clusters. + * + * This has nothing to do with Unicode normalization. + * + * Since: 0.9.2 + **/ +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer) +{ + assert (buffer->have_positions); + assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) || + (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + foreach_cluster (buffer, start, end) + normalize_glyphs_cluster (buffer, start, end, backward); +} + +void +hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)) +{ + assert (!have_positions); + for (unsigned int i = start + 1; i < end; i++) + { + unsigned int j = i; + while (j > start && compar (&info[j - 1], &info[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + merge_clusters (j, i + 1); + { + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t)); + info[j] = t; + } + } +} + + +/* + * Comparing buffers. + */ + +/** + * hb_buffer_diff: + * @buffer: a buffer. + * @reference: other buffer to compare to. + * @dottedcircle_glyph: glyph id of U+25CC DOTTED CIRCLE, or (hb_codepont_t) -1. + * @position_fuzz: allowed absolute difference in position values. + * + * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT + * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned. This should be used by most + * callers if just comparing two buffers is needed. + * + * Since: 1.5.0 + **/ +hb_buffer_diff_flags_t +hb_buffer_diff (hb_buffer_t *buffer, + hb_buffer_t *reference, + hb_codepoint_t dottedcircle_glyph, + unsigned int position_fuzz) +{ + if (buffer->content_type != reference->content_type && buffer->len && reference->len) + return HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH; + + hb_buffer_diff_flags_t result = HB_BUFFER_DIFF_FLAG_EQUAL; + bool contains = dottedcircle_glyph != (hb_codepoint_t) -1; + + unsigned int count = reference->len; + + if (buffer->len != count) + { + /* + * we can't compare glyph-by-glyph, but we do want to know if there + * are .notdef or dottedcircle glyphs present in the reference buffer + */ + const hb_glyph_info_t *info = reference->info; + unsigned int i; + for (i = 0; i < count; i++) + { + if (contains && info[i].codepoint == dottedcircle_glyph) + result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; + if (contains && info[i].codepoint == 0) + result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; + } + result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH; + return hb_buffer_diff_flags_t (result); + } + + if (!count) + return hb_buffer_diff_flags_t (result); + + const hb_glyph_info_t *buf_info = buffer->info; + const hb_glyph_info_t *ref_info = reference->info; + for (unsigned int i = 0; i < count; i++) + { + if (buf_info->codepoint != ref_info->codepoint) + result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH; + if (buf_info->cluster != ref_info->cluster) + result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH; + if ((buf_info->mask & ~ref_info->mask & HB_GLYPH_FLAG_DEFINED)) + result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH; + if (contains && ref_info->codepoint == dottedcircle_glyph) + result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; + if (contains && ref_info->codepoint == 0) + result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; + buf_info++; + ref_info++; + } + + if (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) + { + assert (buffer->have_positions); + const hb_glyph_position_t *buf_pos = buffer->pos; + const hb_glyph_position_t *ref_pos = reference->pos; + for (unsigned int i = 0; i < count; i++) + { + if ((unsigned int) abs (buf_pos->x_advance - ref_pos->x_advance) > position_fuzz || + (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz || + (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz || + (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz) + { + result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH; + break; + } + buf_pos++; + ref_pos++; + } + } + + return result; +} + + +/* + * Debugging. + */ + +#ifndef HB_NO_BUFFER_MESSAGE +/** + * hb_buffer_set_message_func: + * @buffer: an #hb_buffer_t. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.1.3 + **/ +void +hb_buffer_set_message_func (hb_buffer_t *buffer, + hb_buffer_message_func_t func, + void *user_data, hb_destroy_func_t destroy) +{ + if (buffer->message_destroy) + buffer->message_destroy (buffer->message_data); + + if (func) { + buffer->message_func = func; + buffer->message_data = user_data; + buffer->message_destroy = destroy; + } else { + buffer->message_func = nullptr; + buffer->message_data = nullptr; + buffer->message_destroy = nullptr; + } +} +bool +hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) +{ + char buf[100]; + vsnprintf (buf, sizeof (buf), fmt, ap); + return (bool) this->message_func (this, font, buf, this->message_data); +} +#endif diff --git a/thirdparty/harfbuzz/src/hb-buffer.h b/thirdparty/harfbuzz/src/hb-buffer.h new file mode 100644 index 00000000000..2f581f3c735 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-buffer.h @@ -0,0 +1,586 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_BUFFER_H +#define HB_BUFFER_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +/** + * hb_glyph_info_t: + * @codepoint: either a Unicode code point (before shaping) or a glyph index + * (after shaping). + * @cluster: the index of the character in the original text that corresponds + * to this #hb_glyph_info_t, or whatever the client passes to + * hb_buffer_add(). More than one #hb_glyph_info_t can have the same + * @cluster value, if they resulted from the same character (e.g. one + * to many glyph substitution), and when more than one character gets + * merged in the same glyph (e.g. many to one glyph substitution) the + * #hb_glyph_info_t will have the smallest cluster value of them. + * By default some characters are merged into the same cluster + * (e.g. combining marks have the same cluster as their bases) + * even if they are separate glyphs, hb_buffer_set_cluster_level() + * allow selecting more fine-grained cluster handling. + * + * The #hb_glyph_info_t is the structure that holds information about the + * glyphs and their relation to input text. + */ +typedef struct hb_glyph_info_t +{ + hb_codepoint_t codepoint; + /*< private >*/ + hb_mask_t mask; + /*< public >*/ + uint32_t cluster; + + /*< private >*/ + hb_var_int_t var1; + hb_var_int_t var2; +} hb_glyph_info_t; + +/** + * hb_glyph_flags_t: + * @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the + * beginning of the cluster this glyph is part of, + * then both sides need to be re-shaped, as the + * result might be different. On the flip side, + * it means that when this flag is not present, + * then it's safe to break the glyph-run at the + * beginning of this cluster, and the two sides + * represent the exact same result one would get + * if breaking input text at the beginning of + * this cluster and shaping the two sides + * separately. This can be used to optimize + * paragraph layout, by avoiding re-shaping + * of each line after line-breaking, or limiting + * the reshaping to a small piece around the + * breaking point only. + * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags. + * + * Since: 1.5.0 + */ +typedef enum { /*< flags >*/ + HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + + HB_GLYPH_FLAG_DEFINED = 0x00000001 /* OR of all defined flags */ +} hb_glyph_flags_t; + +HB_EXTERN hb_glyph_flags_t +hb_glyph_info_get_glyph_flags (const hb_glyph_info_t *info); + +#define hb_glyph_info_get_glyph_flags(info) \ + ((hb_glyph_flags_t) ((unsigned int) (info)->mask & HB_GLYPH_FLAG_DEFINED)) + + +/** + * hb_glyph_position_t: + * @x_advance: how much the line advances after drawing this glyph when setting + * text in horizontal direction. + * @y_advance: how much the line advances after drawing this glyph when setting + * text in vertical direction. + * @x_offset: how much the glyph moves on the X-axis before drawing it, this + * should not affect how much the line advances. + * @y_offset: how much the glyph moves on the Y-axis before drawing it, this + * should not affect how much the line advances. + * + * The #hb_glyph_position_t is the structure that holds the positions of the + * glyph in both horizontal and vertical directions. All positions in + * #hb_glyph_position_t are relative to the current point. + * + */ +typedef struct hb_glyph_position_t { + hb_position_t x_advance; + hb_position_t y_advance; + hb_position_t x_offset; + hb_position_t y_offset; + + /*< private >*/ + hb_var_int_t var; +} hb_glyph_position_t; + +/** + * hb_segment_properties_t: + * @direction: the #hb_direction_t of the buffer, see hb_buffer_set_direction(). + * @script: the #hb_script_t of the buffer, see hb_buffer_set_script(). + * @language: the #hb_language_t of the buffer, see hb_buffer_set_language(). + * + * The structure that holds various text properties of an #hb_buffer_t. Can be + * set and retrieved using hb_buffer_set_segment_properties() and + * hb_buffer_get_segment_properties(), respectively. + */ +typedef struct hb_segment_properties_t { + hb_direction_t direction; + hb_script_t script; + hb_language_t language; + /*< private >*/ + void *reserved1; + void *reserved2; +} hb_segment_properties_t; + +#define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \ + HB_SCRIPT_INVALID, \ + HB_LANGUAGE_INVALID, \ + (void *) 0, \ + (void *) 0} + +HB_EXTERN hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b); + +HB_EXTERN unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p); + + + +/** + * hb_buffer_t: + * + * The main structure holding the input text and its properties before shaping, + * and output glyphs and their information after shaping. + */ + +typedef struct hb_buffer_t hb_buffer_t; + +HB_EXTERN hb_buffer_t * +hb_buffer_create (void); + +HB_EXTERN hb_buffer_t * +hb_buffer_get_empty (void); + +HB_EXTERN hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_destroy (hb_buffer_t *buffer); + +HB_EXTERN hb_bool_t +hb_buffer_set_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_buffer_get_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key); + + +/** + * hb_buffer_content_type_t: + * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer. + * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping). + * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping). + */ +typedef enum { + HB_BUFFER_CONTENT_TYPE_INVALID = 0, + HB_BUFFER_CONTENT_TYPE_UNICODE, + HB_BUFFER_CONTENT_TYPE_GLYPHS +} hb_buffer_content_type_t; + +HB_EXTERN void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type); + +HB_EXTERN hb_buffer_content_type_t +hb_buffer_get_content_type (hb_buffer_t *buffer); + + +HB_EXTERN void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs); + +HB_EXTERN hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction); + +HB_EXTERN hb_direction_t +hb_buffer_get_direction (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script); + +HB_EXTERN hb_script_t +hb_buffer_get_script (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language); + + +HB_EXTERN hb_language_t +hb_buffer_get_language (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props); + +HB_EXTERN void +hb_buffer_get_segment_properties (hb_buffer_t *buffer, + hb_segment_properties_t *props); + +HB_EXTERN void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer); + + +/** + * hb_buffer_flags_t: + * @HB_BUFFER_FLAG_DEFAULT: the default buffer flag. + * @HB_BUFFER_FLAG_BOT: flag indicating that special handling of the beginning + * of text paragraph can be applied to this buffer. Should usually + * be set, unless you are passing to the buffer only part + * of the text without the full context. + * @HB_BUFFER_FLAG_EOT: flag indicating that special handling of the end of text + * paragraph can be applied to this buffer, similar to + * @HB_BUFFER_FLAG_BOT. + * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES: + * flag indication that character with Default_Ignorable + * Unicode property should use the corresponding glyph + * from the font, instead of hiding them (done by + * replacing them with the space glyph and zeroing the + * advance width.) This flag takes precedence over + * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES. + * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES: + * flag indication that character with Default_Ignorable + * Unicode property should be removed from glyph string + * instead of hiding them (done by replacing them with the + * space glyph and zeroing the advance width.) + * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes + * precedence over this flag. Since: 1.8.0 + * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: + * flag indicating that a dotted circle should + * not be inserted in the rendering of incorrect + * character sequences (such at <0905 093E>). Since: 2.4 + * + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ + HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ + HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u, + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u, + HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u +} hb_buffer_flags_t; + +HB_EXTERN void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags); + +HB_EXTERN hb_buffer_flags_t +hb_buffer_get_flags (hb_buffer_t *buffer); + +/** + * hb_buffer_cluster_level_t: + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES: Return cluster values grouped by graphemes into + * monotone order. + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS: Return cluster values grouped into monotone order. + * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values. + * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level, + * equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES. + * + * Since: 0.9.42 + */ +typedef enum { + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES = 0, + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = 1, + HB_BUFFER_CLUSTER_LEVEL_CHARACTERS = 2, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES +} hb_buffer_cluster_level_t; + +HB_EXTERN void +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level); + +HB_EXTERN hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (hb_buffer_t *buffer); + +/** + * HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT: + * + * The default code point for replacing invalid characters in a given encoding. + * Set to U+FFFD REPLACEMENT CHARACTER. + * + * Since: 0.9.31 + */ +#define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu + +HB_EXTERN void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_invisible_glyph (hb_buffer_t *buffer); + + +HB_EXTERN void +hb_buffer_reset (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_clear_contents (hb_buffer_t *buffer); + +HB_EXTERN hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, + unsigned int size); + + +HB_EXTERN hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_reverse (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end); + +HB_EXTERN void +hb_buffer_reverse_clusters (hb_buffer_t *buffer); + + +/* Filling the buffer in */ + +HB_EXTERN void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster); + +HB_EXTERN void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_append (hb_buffer_t *buffer, + hb_buffer_t *source, + unsigned int start, + unsigned int end); + +HB_EXTERN hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length); + +HB_EXTERN unsigned int +hb_buffer_get_length (hb_buffer_t *buffer); + +/* Getting glyphs out of the buffer */ + +HB_EXTERN hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length); + +HB_EXTERN hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length); + + +HB_EXTERN void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer); + + +/* + * Serialize + */ + +/** + * hb_buffer_serialize_flags_t: + * @HB_BUFFER_SERIALIZE_FLAG_DEFAULT: serialize glyph names, clusters and positions. + * @HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS: do not serialize glyph cluster. + * @HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS: do not serialize glyph position information. + * @HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES: do no serialize glyph name. + * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents. + * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0 + * @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances, + * glyph offsets will reflect absolute glyph positions. Since: 1.8.0 + * + * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs(). + * + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_SERIALIZE_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS = 0x00000001u, + HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS = 0x00000002u, + HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 0x00000004u, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS = 0x00000008u, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS = 0x00000010u, + HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES = 0x00000020u +} hb_buffer_serialize_flags_t; + +/** + * hb_buffer_serialize_format_t: + * @HB_BUFFER_SERIALIZE_FORMAT_TEXT: a human-readable, plain text format. + * @HB_BUFFER_SERIALIZE_FORMAT_JSON: a machine-readable JSON format. + * @HB_BUFFER_SERIALIZE_FORMAT_INVALID: invalid format. + * + * The buffer serialization and de-serialization format used in + * hb_buffer_serialize_glyphs() and hb_buffer_deserialize_glyphs(). + * + * Since: 0.9.2 + */ +typedef enum { + HB_BUFFER_SERIALIZE_FORMAT_TEXT = HB_TAG('T','E','X','T'), + HB_BUFFER_SERIALIZE_FORMAT_JSON = HB_TAG('J','S','O','N'), + HB_BUFFER_SERIALIZE_FORMAT_INVALID = HB_TAG_NONE +} hb_buffer_serialize_format_t; + +HB_EXTERN hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format); + +HB_EXTERN const char ** +hb_buffer_serialize_list_formats (void); + +HB_EXTERN unsigned int +hb_buffer_serialize_glyphs (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +HB_EXTERN hb_bool_t +hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, + const char *buf, + int buf_len, + const char **end_ptr, + hb_font_t *font, + hb_buffer_serialize_format_t format); + + +/* + * Compare buffers + */ + +typedef enum { /*< flags >*/ + HB_BUFFER_DIFF_FLAG_EQUAL = 0x0000, + + /* Buffers with different content_type cannot be meaningfully compared + * in any further detail. */ + HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH = 0x0001, + + /* For buffers with differing length, the per-glyph comparison is not + * attempted, though we do still scan reference for dottedcircle / .notdef + * glyphs. */ + HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH = 0x0002, + + /* We want to know if dottedcircle / .notdef glyphs are present in the + * reference, as we may not care so much about other differences in this + * case. */ + HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT = 0x0004, + HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT = 0x0008, + + /* If the buffers have the same length, we compare them glyph-by-glyph + * and report which aspect(s) of the glyph info/position are different. */ + HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH = 0x0010, + HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH = 0x0020, + HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH = 0x0040, + HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH = 0x0080 + +} hb_buffer_diff_flags_t; + +/* Compare the contents of two buffers, report types of differences. */ +HB_EXTERN hb_buffer_diff_flags_t +hb_buffer_diff (hb_buffer_t *buffer, + hb_buffer_t *reference, + hb_codepoint_t dottedcircle_glyph, + unsigned int position_fuzz); + + +/* + * Debugging. + */ + +typedef hb_bool_t (*hb_buffer_message_func_t) (hb_buffer_t *buffer, + hb_font_t *font, + const char *message, + void *user_data); + +HB_EXTERN void +hb_buffer_set_message_func (hb_buffer_t *buffer, + hb_buffer_message_func_t func, + void *user_data, hb_destroy_func_t destroy); + + +HB_END_DECLS + +#endif /* HB_BUFFER_H */ diff --git a/thirdparty/harfbuzz/src/hb-buffer.hh b/thirdparty/harfbuzz/src/hb-buffer.hh new file mode 100644 index 00000000000..3420ba434ae --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-buffer.hh @@ -0,0 +1,451 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_HH +#define HB_BUFFER_HH + +#include "hb.hh" +#include "hb-unicode.hh" + + +#ifndef HB_BUFFER_MAX_LEN_FACTOR +#define HB_BUFFER_MAX_LEN_FACTOR 32 +#endif +#ifndef HB_BUFFER_MAX_LEN_MIN +#define HB_BUFFER_MAX_LEN_MIN 8192 +#endif +#ifndef HB_BUFFER_MAX_LEN_DEFAULT +#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ +#endif + +#ifndef HB_BUFFER_MAX_OPS_FACTOR +#define HB_BUFFER_MAX_OPS_FACTOR 64 +#endif +#ifndef HB_BUFFER_MAX_OPS_MIN +#define HB_BUFFER_MAX_OPS_MIN 1024 +#endif +#ifndef HB_BUFFER_MAX_OPS_DEFAULT +#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ +#endif + +static_assert ((sizeof (hb_glyph_info_t) == 20), ""); +static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), ""); + +HB_MARK_AS_FLAG_T (hb_buffer_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t); + +enum hb_buffer_scratch_flags_t { + HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, + HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, + HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, + HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, + HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK = 0x00000010u, + HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000020u, + + /* Reserved for complex shapers' internal use. */ + HB_BUFFER_SCRATCH_FLAG_COMPLEX0 = 0x01000000u, + HB_BUFFER_SCRATCH_FLAG_COMPLEX1 = 0x02000000u, + HB_BUFFER_SCRATCH_FLAG_COMPLEX2 = 0x04000000u, + HB_BUFFER_SCRATCH_FLAG_COMPLEX3 = 0x08000000u, +}; +HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t); + + +/* + * hb_buffer_t + */ + +struct hb_buffer_t +{ + hb_object_header_t header; + + /* Information about how the text in the buffer should be treated */ + hb_unicode_funcs_t *unicode; /* Unicode functions */ + hb_buffer_flags_t flags; /* BOT / EOT / etc. */ + hb_buffer_cluster_level_t cluster_level; + hb_codepoint_t replacement; /* U+FFFD or something else. */ + hb_codepoint_t invisible; /* 0 or something else. */ + hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */ + unsigned int max_len; /* Maximum allowed len. */ + int max_ops; /* Maximum allowed operations. */ + + /* Buffer contents */ + hb_buffer_content_type_t content_type; + hb_segment_properties_t props; /* Script, language, direction */ + + bool successful; /* Allocations successful */ + bool have_output; /* Whether we have an output buffer going on */ + bool have_positions; /* Whether we have positions */ + + unsigned int idx; /* Cursor into ->info and ->pos arrays */ + unsigned int len; /* Length of ->info and ->pos arrays */ + unsigned int out_len; /* Length of ->out array if have_output */ + + unsigned int allocated; /* Length of allocated arrays */ + hb_glyph_info_t *info; + hb_glyph_info_t *out_info; + hb_glyph_position_t *pos; + + unsigned int serial; + + /* Text before / after the main buffer contents. + * Always in Unicode, and ordered outward. + * Index 0 is for "pre-context", 1 for "post-context". */ + static constexpr unsigned CONTEXT_LENGTH = 5u; + hb_codepoint_t context[2][CONTEXT_LENGTH]; + unsigned int context_len[2]; + + /* Debugging API */ +#ifndef HB_NO_BUFFER_MESSAGE + hb_buffer_message_func_t message_func; + void *message_data; + hb_destroy_func_t message_destroy; +#endif + + /* Internal debugging. */ + /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ +#ifndef HB_NDEBUG + uint8_t allocated_var_bits; +#endif + + + /* Methods */ + + bool in_error () const { return !successful; } + + void allocate_var (unsigned int start, unsigned int count) + { +#ifndef HB_NDEBUG + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u<len, \ + start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \ + start < _count; \ + start = end, end = _next_cluster (buffer, start)) + +static inline unsigned int +_next_cluster (hb_buffer_t *buffer, unsigned int start) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + + unsigned int cluster = info[start].cluster; + while (++start < count && cluster == info[start].cluster) + ; + + return start; +} + + +#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \ + b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ + sizeof (b->info[0].var)) +#define HB_BUFFER_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var ()) +#define HB_BUFFER_DEALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ()) +#define HB_BUFFER_ASSERT_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, assert_var, var ()) + + +#endif /* HB_BUFFER_HH */ diff --git a/thirdparty/harfbuzz/src/hb-cache.hh b/thirdparty/harfbuzz/src/hb-cache.hh new file mode 100644 index 00000000000..bf26d96be46 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-cache.hh @@ -0,0 +1,80 @@ +/* + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_CACHE_HH +#define HB_CACHE_HH + +#include "hb.hh" + + +/* Implements a lock-free cache for int->int functions. */ + +template +struct hb_cache_t +{ + static_assert ((key_bits >= cache_bits), ""); + static_assert ((key_bits + value_bits - cache_bits <= 8 * sizeof (hb_atomic_int_t)), ""); + static_assert (sizeof (hb_atomic_int_t) == sizeof (unsigned int), ""); + + void init () { clear (); } + void fini () {} + + void clear () + { + for (unsigned i = 0; i < ARRAY_LENGTH (values); i++) + values[i].set_relaxed (-1); + } + + bool get (unsigned int key, unsigned int *value) const + { + unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits)) + return false; + *value = v & ((1u<> key_bits) || (value >> value_bits))) + return false; /* Overflows */ + unsigned int k = key & ((1u<>cache_bits)< hb_cmap_cache_t; +typedef hb_cache_t<16, 24, 8> hb_advance_cache_t; + + +#endif /* HB_CACHE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh new file mode 100644 index 00000000000..91a9b7d0d16 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh @@ -0,0 +1,688 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_COMMON_HH +#define HB_CFF_INTERP_COMMON_HH + +namespace CFF { + +using namespace OT; + +typedef unsigned int op_code_t; + + +/* === Dict operators === */ + +/* One byte operators (0-31) */ +#define OpCode_version 0 /* CFF Top */ +#define OpCode_Notice 1 /* CFF Top */ +#define OpCode_FullName 2 /* CFF Top */ +#define OpCode_FamilyName 3 /* CFF Top */ +#define OpCode_Weight 4 /* CFF Top */ +#define OpCode_FontBBox 5 /* CFF Top */ +#define OpCode_BlueValues 6 /* CFF Private, CFF2 Private */ +#define OpCode_OtherBlues 7 /* CFF Private, CFF2 Private */ +#define OpCode_FamilyBlues 8 /* CFF Private, CFF2 Private */ +#define OpCode_FamilyOtherBlues 9 /* CFF Private, CFF2 Private */ +#define OpCode_StdHW 10 /* CFF Private, CFF2 Private */ +#define OpCode_StdVW 11 /* CFF Private, CFF2 Private */ +#define OpCode_escape 12 /* All. Shared with CS */ +#define OpCode_UniqueID 13 /* CFF Top */ +#define OpCode_XUID 14 /* CFF Top */ +#define OpCode_charset 15 /* CFF Top (0) */ +#define OpCode_Encoding 16 /* CFF Top (0) */ +#define OpCode_CharStrings 17 /* CFF Top, CFF2 Top */ +#define OpCode_Private 18 /* CFF Top, CFF2 FD */ +#define OpCode_Subrs 19 /* CFF Private, CFF2 Private */ +#define OpCode_defaultWidthX 20 /* CFF Private (0) */ +#define OpCode_nominalWidthX 21 /* CFF Private (0) */ +#define OpCode_vsindexdict 22 /* CFF2 Private/CS */ +#define OpCode_blenddict 23 /* CFF2 Private/CS */ +#define OpCode_vstore 24 /* CFF2 Top */ +#define OpCode_reserved25 25 +#define OpCode_reserved26 26 +#define OpCode_reserved27 27 + +/* Numbers */ +#define OpCode_shortint 28 /* 16-bit integer, All */ +#define OpCode_longintdict 29 /* 32-bit integer, All */ +#define OpCode_BCD 30 /* Real number, CFF2 Top/FD */ +#define OpCode_reserved31 31 + +/* 1-byte integers */ +#define OpCode_OneByteIntFirst 32 /* All. beginning of the range of first byte ints */ +#define OpCode_OneByteIntLast 246 /* All. ending of the range of first byte int */ + +/* 2-byte integers */ +#define OpCode_TwoBytePosInt0 247 /* All. first byte of two byte positive int (+108 to +1131) */ +#define OpCode_TwoBytePosInt1 248 +#define OpCode_TwoBytePosInt2 249 +#define OpCode_TwoBytePosInt3 250 + +#define OpCode_TwoByteNegInt0 251 /* All. first byte of two byte negative int (-1131 to -108) */ +#define OpCode_TwoByteNegInt1 252 +#define OpCode_TwoByteNegInt2 253 +#define OpCode_TwoByteNegInt3 254 + +/* Two byte escape operators 12, (0-41) */ +#define OpCode_ESC_Base 256 +#define Make_OpCode_ESC(byte2) ((op_code_t)(OpCode_ESC_Base + (byte2))) + +inline op_code_t Unmake_OpCode_ESC (op_code_t op) { return (op_code_t)(op - OpCode_ESC_Base); } +inline bool Is_OpCode_ESC (op_code_t op) { return op >= OpCode_ESC_Base; } +inline unsigned int OpCode_Size (op_code_t op) { return Is_OpCode_ESC (op) ? 2: 1; } + +#define OpCode_Copyright Make_OpCode_ESC(0) /* CFF Top */ +#define OpCode_isFixedPitch Make_OpCode_ESC(1) /* CFF Top (false) */ +#define OpCode_ItalicAngle Make_OpCode_ESC(2) /* CFF Top (0) */ +#define OpCode_UnderlinePosition Make_OpCode_ESC(3) /* CFF Top (-100) */ +#define OpCode_UnderlineThickness Make_OpCode_ESC(4) /* CFF Top (50) */ +#define OpCode_PaintType Make_OpCode_ESC(5) /* CFF Top (0) */ +#define OpCode_CharstringType Make_OpCode_ESC(6) /* CFF Top (2) */ +#define OpCode_FontMatrix Make_OpCode_ESC(7) /* CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/ +#define OpCode_StrokeWidth Make_OpCode_ESC(8) /* CFF Top (0) */ +#define OpCode_BlueScale Make_OpCode_ESC(9) /* CFF Private, CFF2 Private (0.039625) */ +#define OpCode_BlueShift Make_OpCode_ESC(10) /* CFF Private, CFF2 Private (7) */ +#define OpCode_BlueFuzz Make_OpCode_ESC(11) /* CFF Private, CFF2 Private (1) */ +#define OpCode_StemSnapH Make_OpCode_ESC(12) /* CFF Private, CFF2 Private */ +#define OpCode_StemSnapV Make_OpCode_ESC(13) /* CFF Private, CFF2 Private */ +#define OpCode_ForceBold Make_OpCode_ESC(14) /* CFF Private (false) */ +#define OpCode_reservedESC15 Make_OpCode_ESC(15) +#define OpCode_reservedESC16 Make_OpCode_ESC(16) +#define OpCode_LanguageGroup Make_OpCode_ESC(17) /* CFF Private, CFF2 Private (0) */ +#define OpCode_ExpansionFactor Make_OpCode_ESC(18) /* CFF Private, CFF2 Private (0.06) */ +#define OpCode_initialRandomSeed Make_OpCode_ESC(19) /* CFF Private (0) */ +#define OpCode_SyntheticBase Make_OpCode_ESC(20) /* CFF Top */ +#define OpCode_PostScript Make_OpCode_ESC(21) /* CFF Top */ +#define OpCode_BaseFontName Make_OpCode_ESC(22) /* CFF Top */ +#define OpCode_BaseFontBlend Make_OpCode_ESC(23) /* CFF Top */ +#define OpCode_reservedESC24 Make_OpCode_ESC(24) +#define OpCode_reservedESC25 Make_OpCode_ESC(25) +#define OpCode_reservedESC26 Make_OpCode_ESC(26) +#define OpCode_reservedESC27 Make_OpCode_ESC(27) +#define OpCode_reservedESC28 Make_OpCode_ESC(28) +#define OpCode_reservedESC29 Make_OpCode_ESC(29) +#define OpCode_ROS Make_OpCode_ESC(30) /* CFF Top_CID */ +#define OpCode_CIDFontVersion Make_OpCode_ESC(31) /* CFF Top_CID (0) */ +#define OpCode_CIDFontRevision Make_OpCode_ESC(32) /* CFF Top_CID (0) */ +#define OpCode_CIDFontType Make_OpCode_ESC(33) /* CFF Top_CID (0) */ +#define OpCode_CIDCount Make_OpCode_ESC(34) /* CFF Top_CID (8720) */ +#define OpCode_UIDBase Make_OpCode_ESC(35) /* CFF Top_CID */ +#define OpCode_FDArray Make_OpCode_ESC(36) /* CFF Top_CID, CFF2 Top */ +#define OpCode_FDSelect Make_OpCode_ESC(37) /* CFF Top_CID, CFF2 Top */ +#define OpCode_FontName Make_OpCode_ESC(38) /* CFF Top_CID */ + + +/* === CharString operators === */ + +#define OpCode_hstem 1 /* CFF, CFF2 */ +#define OpCode_Reserved2 2 +#define OpCode_vstem 3 /* CFF, CFF2 */ +#define OpCode_vmoveto 4 /* CFF, CFF2 */ +#define OpCode_rlineto 5 /* CFF, CFF2 */ +#define OpCode_hlineto 6 /* CFF, CFF2 */ +#define OpCode_vlineto 7 /* CFF, CFF2 */ +#define OpCode_rrcurveto 8 /* CFF, CFF2 */ +#define OpCode_Reserved9 9 +#define OpCode_callsubr 10 /* CFF, CFF2 */ +#define OpCode_return 11 /* CFF */ +//#define OpCode_escape 12 /* CFF, CFF2 */ +#define OpCode_Reserved13 13 +#define OpCode_endchar 14 /* CFF */ +#define OpCode_vsindexcs 15 /* CFF2 */ +#define OpCode_blendcs 16 /* CFF2 */ +#define OpCode_Reserved17 17 +#define OpCode_hstemhm 18 /* CFF, CFF2 */ +#define OpCode_hintmask 19 /* CFF, CFF2 */ +#define OpCode_cntrmask 20 /* CFF, CFF2 */ +#define OpCode_rmoveto 21 /* CFF, CFF2 */ +#define OpCode_hmoveto 22 /* CFF, CFF2 */ +#define OpCode_vstemhm 23 /* CFF, CFF2 */ +#define OpCode_rcurveline 24 /* CFF, CFF2 */ +#define OpCode_rlinecurve 25 /* CFF, CFF2 */ +#define OpCode_vvcurveto 26 /* CFF, CFF2 */ +#define OpCode_hhcurveto 27 /* CFF, CFF2 */ +//#define OpCode_shortint 28 /* CFF, CFF2 */ +#define OpCode_callgsubr 29 /* CFF, CFF2 */ +#define OpCode_vhcurveto 30 /* CFF, CFF2 */ +#define OpCode_hvcurveto 31 /* CFF, CFF2 */ + +#define OpCode_fixedcs 255 /* 32-bit fixed */ + +/* Two byte escape operators 12, (0-41) */ +#define OpCode_dotsection Make_OpCode_ESC(0) /* CFF (obsoleted) */ +#define OpCode_ReservedESC1 Make_OpCode_ESC(1) +#define OpCode_ReservedESC2 Make_OpCode_ESC(2) +#define OpCode_and Make_OpCode_ESC(3) /* CFF */ +#define OpCode_or Make_OpCode_ESC(4) /* CFF */ +#define OpCode_not Make_OpCode_ESC(5) /* CFF */ +#define OpCode_ReservedESC6 Make_OpCode_ESC(6) +#define OpCode_ReservedESC7 Make_OpCode_ESC(7) +#define OpCode_ReservedESC8 Make_OpCode_ESC(8) +#define OpCode_abs Make_OpCode_ESC(9) /* CFF */ +#define OpCode_add Make_OpCode_ESC(10) /* CFF */ +#define OpCode_sub Make_OpCode_ESC(11) /* CFF */ +#define OpCode_div Make_OpCode_ESC(12) /* CFF */ +#define OpCode_ReservedESC13 Make_OpCode_ESC(13) +#define OpCode_neg Make_OpCode_ESC(14) /* CFF */ +#define OpCode_eq Make_OpCode_ESC(15) /* CFF */ +#define OpCode_ReservedESC16 Make_OpCode_ESC(16) +#define OpCode_ReservedESC17 Make_OpCode_ESC(17) +#define OpCode_drop Make_OpCode_ESC(18) /* CFF */ +#define OpCode_ReservedESC19 Make_OpCode_ESC(19) +#define OpCode_put Make_OpCode_ESC(20) /* CFF */ +#define OpCode_get Make_OpCode_ESC(21) /* CFF */ +#define OpCode_ifelse Make_OpCode_ESC(22) /* CFF */ +#define OpCode_random Make_OpCode_ESC(23) /* CFF */ +#define OpCode_mul Make_OpCode_ESC(24) /* CFF */ +//#define OpCode_reservedESC25 Make_OpCode_ESC(25) +#define OpCode_sqrt Make_OpCode_ESC(26) /* CFF */ +#define OpCode_dup Make_OpCode_ESC(27) /* CFF */ +#define OpCode_exch Make_OpCode_ESC(28) /* CFF */ +#define OpCode_index Make_OpCode_ESC(29) /* CFF */ +#define OpCode_roll Make_OpCode_ESC(30) /* CFF */ +#define OpCode_reservedESC31 Make_OpCode_ESC(31) +#define OpCode_reservedESC32 Make_OpCode_ESC(32) +#define OpCode_reservedESC33 Make_OpCode_ESC(33) +#define OpCode_hflex Make_OpCode_ESC(34) /* CFF, CFF2 */ +#define OpCode_flex Make_OpCode_ESC(35) /* CFF, CFF2 */ +#define OpCode_hflex1 Make_OpCode_ESC(36) /* CFF, CFF2 */ +#define OpCode_flex1 Make_OpCode_ESC(37) /* CFF, CFF2 */ + + +#define OpCode_Invalid 0xFFFFu + + +struct number_t +{ + void init () { set_real (0.0); } + void fini () {} + + void set_int (int v) { value = v; } + int to_int () const { return value; } + + void set_fixed (int32_t v) { value = v / 65536.0; } + int32_t to_fixed () const { return value * 65536.0; } + + void set_real (double v) { value = v; } + double to_real () const { return value; } + + bool in_int_range () const + { return ((double) (int16_t) to_int () == value); } + + bool operator > (const number_t &n) const { return value > n.to_real (); } + bool operator < (const number_t &n) const { return n > *this; } + bool operator >= (const number_t &n) const { return !(*this < n); } + bool operator <= (const number_t &n) const { return !(*this > n); } + + const number_t &operator += (const number_t &n) + { + set_real (to_real () + n.to_real ()); + + return *this; + } + + protected: + double value; +}; + +/* byte string */ +struct UnsizedByteStr : UnsizedArrayOf +{ + // encode 2-byte int (Dict/CharString) or 4-byte int (Dict) + template + static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value) + { + TRACE_SERIALIZE (this); + + HBUINT8 *p = c->allocate_size (1); + if (unlikely (!p)) return_trace (false); + *p = intOp; + + T *ip = c->allocate_size (T::static_size); + if (unlikely (!ip)) return_trace (false); + return_trace (c->check_assign (*ip, value)); + } + + template + static bool serialize_int4 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_longintdict, value); } + + template + static bool serialize_int2 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_shortint, value); } + + /* Defining null_size allows a Null object may be created. Should be safe because: + * A descendent struct Dict uses a Null pointer to indicate a missing table, + * checked before access. + * byte_str_t, a wrapper struct pairing a byte pointer along with its length, always + * checks the length before access. A Null pointer is used as the initial pointer + * along with zero length by the default ctor. + */ + DEFINE_SIZE_MIN(0); +}; + +/* Holder of a section of byte string within a CFFIndex entry */ +struct byte_str_t : hb_ubytes_t +{ + byte_str_t () + : hb_ubytes_t () {} + byte_str_t (const UnsizedByteStr& s, unsigned int l) + : hb_ubytes_t ((const unsigned char*)&s, l) {} + byte_str_t (const unsigned char *s, unsigned int l) + : hb_ubytes_t (s, l) {} + byte_str_t (const hb_ubytes_t &ub) /* conversion from hb_ubytes_t */ + : hb_ubytes_t (ub) {} + + /* sub-string */ + byte_str_t sub_str (unsigned int offset, unsigned int len_) const + { return byte_str_t (hb_ubytes_t::sub_array (offset, len_)); } + + bool check_limit (unsigned int offset, unsigned int count) const + { return (offset + count <= length); } +}; + +/* A byte string associated with the current offset and an error condition */ +struct byte_str_ref_t +{ + byte_str_ref_t () { init (); } + + void init () + { + str = byte_str_t (); + offset = 0; + error = false; + } + + void fini () {} + + byte_str_ref_t (const byte_str_t &str_, unsigned int offset_ = 0) + : str (str_), offset (offset_), error (false) {} + + void reset (const byte_str_t &str_, unsigned int offset_ = 0) + { + str = str_; + offset = offset_; + error = false; + } + + const unsigned char& operator [] (int i) { + if (unlikely ((unsigned int) (offset + i) >= str.length)) + { + set_error (); + return Null (unsigned char); + } + return str[offset + i]; + } + + /* Conversion to byte_str_t */ + operator byte_str_t () const { return str.sub_str (offset, str.length - offset); } + + byte_str_t sub_str (unsigned int offset_, unsigned int len_) const + { return str.sub_str (offset_, len_); } + + bool avail (unsigned int count=1) const + { return (!in_error () && str.check_limit (offset, count)); } + void inc (unsigned int count=1) + { + if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length))) + { + offset += count; + } + else + { + offset = str.length; + set_error (); + } + } + + void set_error () { error = true; } + bool in_error () const { return error; } + + byte_str_t str; + unsigned int offset; /* beginning of the sub-string within str */ + + protected: + bool error; +}; + +typedef hb_vector_t byte_str_array_t; + +/* stack */ +template +struct cff_stack_t +{ + void init () + { + error = false; + count = 0; + elements.init (); + elements.resize (kSizeLimit); + for (unsigned int i = 0; i < elements.length; i++) + elements[i].init (); + } + void fini () { elements.fini_deep (); } + + ELEM& operator [] (unsigned int i) + { + if (unlikely (i >= count)) set_error (); + return elements[i]; + } + + void push (const ELEM &v) + { + if (likely (count < elements.length)) + elements[count++] = v; + else + set_error (); + } + ELEM &push () + { + if (likely (count < elements.length)) + return elements[count++]; + else + { + set_error (); + return Crap (ELEM); + } + } + + ELEM& pop () + { + if (likely (count > 0)) + return elements[--count]; + else + { + set_error (); + return Crap (ELEM); + } + } + void pop (unsigned int n) + { + if (likely (count >= n)) + count -= n; + else + set_error (); + } + + const ELEM& peek () + { + if (unlikely (count < 0)) + { + set_error (); + return Null (ELEM); + } + return elements[count - 1]; + } + + void unpop () + { + if (likely (count < elements.length)) + count++; + else + set_error (); + } + + void clear () { count = 0; } + + bool in_error () const { return (error || elements.in_error ()); } + void set_error () { error = true; } + + unsigned int get_count () const { return count; } + bool is_empty () const { return !count; } + + static constexpr unsigned kSizeLimit = LIMIT; + + protected: + bool error; + unsigned int count; + hb_vector_t elements; +}; + +/* argument stack */ +template +struct arg_stack_t : cff_stack_t +{ + void push_int (int v) + { + ARG &n = S::push (); + n.set_int (v); + } + + void push_fixed (int32_t v) + { + ARG &n = S::push (); + n.set_fixed (v); + } + + void push_real (double v) + { + ARG &n = S::push (); + n.set_real (v); + } + + ARG& pop_num () { return this->pop (); } + + int pop_int () { return this->pop ().to_int (); } + + unsigned int pop_uint () + { + int i = pop_int (); + if (unlikely (i < 0)) + { + i = 0; + S::set_error (); + } + return (unsigned) i; + } + + void push_longint_from_substr (byte_str_ref_t& str_ref) + { + push_int ((str_ref[0] << 24) | (str_ref[1] << 16) | (str_ref[2] << 8) | (str_ref[3])); + str_ref.inc (4); + } + + bool push_fixed_from_substr (byte_str_ref_t& str_ref) + { + if (unlikely (!str_ref.avail (4))) + return false; + push_fixed ((int32_t)*(const HBUINT32*)&str_ref[0]); + str_ref.inc (4); + return true; + } + + hb_array_t get_subarray (unsigned int start) const + { return S::elements.sub_array (start); } + + private: + typedef cff_stack_t S; +}; + +/* an operator prefixed by its operands in a byte string */ +struct op_str_t +{ + void init () {} + void fini () {} + + op_code_t op; + byte_str_t str; +}; + +/* base of OP_SERIALIZER */ +struct op_serializer_t +{ + protected: + bool copy_opstr (hb_serialize_context_t *c, const op_str_t& opstr) const + { + TRACE_SERIALIZE (this); + + HBUINT8 *d = c->allocate_size (opstr.str.length); + if (unlikely (!d)) return_trace (false); + memcpy (d, &opstr.str[0], opstr.str.length); + return_trace (true); + } +}; + +template +struct parsed_values_t +{ + void init () + { + opStart = 0; + values.init (); + } + void fini () { values.fini_deep (); } + + void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ()) + { + VAL *val = values.push (); + val->op = op; + val->str = str_ref.str.sub_str (opStart, str_ref.offset - opStart); + opStart = str_ref.offset; + } + + void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v) + { + VAL *val = values.push (v); + val->op = op; + val->str = str_ref.sub_str ( opStart, str_ref.offset - opStart); + opStart = str_ref.offset; + } + + bool has_op (op_code_t op) const + { + for (unsigned int i = 0; i < get_count (); i++) + if (get_value (i).op == op) return true; + return false; + } + + unsigned get_count () const { return values.length; } + const VAL &get_value (unsigned int i) const { return values[i]; } + const VAL &operator [] (unsigned int i) const { return get_value (i); } + + unsigned int opStart; + hb_vector_t values; +}; + +template +struct interp_env_t +{ + void init (const byte_str_t &str_) + { + str_ref.reset (str_); + argStack.init (); + error = false; + } + void fini () { argStack.fini (); } + + bool in_error () const + { return error || str_ref.in_error () || argStack.in_error (); } + + void set_error () { error = true; } + + op_code_t fetch_op () + { + op_code_t op = OpCode_Invalid; + if (unlikely (!str_ref.avail ())) + return OpCode_Invalid; + op = (op_code_t)(unsigned char)str_ref[0]; + if (op == OpCode_escape) { + if (unlikely (!str_ref.avail ())) + return OpCode_Invalid; + op = Make_OpCode_ESC(str_ref[1]); + str_ref.inc (); + } + str_ref.inc (); + return op; + } + + const ARG& eval_arg (unsigned int i) { return argStack[i]; } + + ARG& pop_arg () { return argStack.pop (); } + void pop_n_args (unsigned int n) { argStack.pop (n); } + + void clear_args () { pop_n_args (argStack.get_count ()); } + + byte_str_ref_t + str_ref; + arg_stack_t + argStack; + protected: + bool error; +}; + +typedef interp_env_t<> num_interp_env_t; + +template +struct opset_t +{ + static void process_op (op_code_t op, interp_env_t& env) + { + switch (op) { + case OpCode_shortint: + env.argStack.push_int ((int16_t)((env.str_ref[0] << 8) | env.str_ref[1])); + env.str_ref.inc (2); + break; + + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.str_ref[0] + 108)); + env.str_ref.inc (); + break; + + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); + env.str_ref.inc (); + break; + + default: + /* 1-byte integer */ + if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast))) + { + env.argStack.push_int ((int)op - 139); + } else { + /* invalid unknown operator */ + env.clear_args (); + env.set_error (); + } + break; + } + } +}; + +template +struct interpreter_t +{ + ~interpreter_t() { fini (); } + + void fini () { env.fini (); } + + ENV env; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_COMMON_HH */ diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh new file mode 100644 index 00000000000..52d778ffe29 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh @@ -0,0 +1,911 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_CS_COMMON_HH +#define HB_CFF_INTERP_CS_COMMON_HH + +#include "hb.hh" +#include "hb-cff-interp-common.hh" + +namespace CFF { + +using namespace OT; + +enum cs_type_t { + CSType_CharString, + CSType_GlobalSubr, + CSType_LocalSubr +}; + +struct call_context_t +{ + void init (const byte_str_ref_t substr_=byte_str_ref_t (), cs_type_t type_=CSType_CharString, unsigned int subr_num_=0) + { + str_ref = substr_; + type = type_; + subr_num = subr_num_; + } + + void fini () {} + + byte_str_ref_t str_ref; + cs_type_t type; + unsigned int subr_num; +}; + +/* call stack */ +const unsigned int kMaxCallLimit = 10; +struct call_stack_t : cff_stack_t {}; + +template +struct biased_subrs_t +{ + void init (const SUBRS *subrs_) + { + subrs = subrs_; + unsigned int nSubrs = get_count (); + if (nSubrs < 1240) + bias = 107; + else if (nSubrs < 33900) + bias = 1131; + else + bias = 32768; + } + + void fini () {} + + unsigned int get_count () const { return subrs ? subrs->count : 0; } + unsigned int get_bias () const { return bias; } + + byte_str_t operator [] (unsigned int index) const + { + if (unlikely (!subrs || index >= subrs->count)) + return Null (byte_str_t); + else + return (*subrs)[index]; + } + + protected: + unsigned int bias; + const SUBRS *subrs; +}; + +struct point_t +{ + void init () + { + x.init (); + y.init (); + } + + void set_int (int _x, int _y) + { + x.set_int (_x); + y.set_int (_y); + } + + void move_x (const number_t &dx) { x += dx; } + void move_y (const number_t &dy) { y += dy; } + void move (const number_t &dx, const number_t &dy) { move_x (dx); move_y (dy); } + void move (const point_t &d) { move_x (d.x); move_y (d.y); } + + number_t x; + number_t y; +}; + +template +struct cs_interp_env_t : interp_env_t +{ + void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) + { + interp_env_t::init (str); + + context.init (str, CSType_CharString); + seen_moveto = true; + seen_hintmask = false; + hstem_count = 0; + vstem_count = 0; + hintmask_size = 0; + pt.init (); + callStack.init (); + globalSubrs.init (globalSubrs_); + localSubrs.init (localSubrs_); + } + void fini () + { + interp_env_t::fini (); + + callStack.fini (); + globalSubrs.fini (); + localSubrs.fini (); + } + + bool in_error () const + { + return callStack.in_error () || SUPER::in_error (); + } + + bool pop_subr_num (const biased_subrs_t& biasedSubrs, unsigned int &subr_num) + { + subr_num = 0; + int n = SUPER::argStack.pop_int (); + n += biasedSubrs.get_bias (); + if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ()))) + return false; + + subr_num = (unsigned int)n; + return true; + } + + void call_subr (const biased_subrs_t& biasedSubrs, cs_type_t type) + { + unsigned int subr_num = 0; + + if (unlikely (!pop_subr_num (biasedSubrs, subr_num) + || callStack.get_count () >= kMaxCallLimit)) + { + SUPER::set_error (); + return; + } + context.str_ref = SUPER::str_ref; + callStack.push (context); + + context.init ( biasedSubrs[subr_num], type, subr_num); + SUPER::str_ref = context.str_ref; + } + + void return_from_subr () + { + if (unlikely (SUPER::str_ref.in_error ())) + SUPER::set_error (); + context = callStack.pop (); + SUPER::str_ref = context.str_ref; + } + + void determine_hintmask_size () + { + if (!seen_hintmask) + { + vstem_count += SUPER::argStack.get_count() / 2; + hintmask_size = (hstem_count + vstem_count + 7) >> 3; + seen_hintmask = true; + } + } + + void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; } + bool is_endchar () const { return endchar_flag; } + + const number_t &get_x () const { return pt.x; } + const number_t &get_y () const { return pt.y; } + const point_t &get_pt () const { return pt; } + + void moveto (const point_t &pt_ ) { pt = pt_; } + + public: + call_context_t context; + bool endchar_flag; + bool seen_moveto; + bool seen_hintmask; + + unsigned int hstem_count; + unsigned int vstem_count; + unsigned int hintmask_size; + call_stack_t callStack; + biased_subrs_t globalSubrs; + biased_subrs_t localSubrs; + + private: + point_t pt; + + typedef interp_env_t SUPER; +}; + +template +struct path_procs_null_t +{ + static void rmoveto (ENV &env, PARAM& param) {} + static void hmoveto (ENV &env, PARAM& param) {} + static void vmoveto (ENV &env, PARAM& param) {} + static void rlineto (ENV &env, PARAM& param) {} + static void hlineto (ENV &env, PARAM& param) {} + static void vlineto (ENV &env, PARAM& param) {} + static void rrcurveto (ENV &env, PARAM& param) {} + static void rcurveline (ENV &env, PARAM& param) {} + static void rlinecurve (ENV &env, PARAM& param) {} + static void vvcurveto (ENV &env, PARAM& param) {} + static void hhcurveto (ENV &env, PARAM& param) {} + static void vhcurveto (ENV &env, PARAM& param) {} + static void hvcurveto (ENV &env, PARAM& param) {} + static void moveto (ENV &env, PARAM& param, const point_t &pt) {} + static void line (ENV &env, PARAM& param, const point_t &pt1) {} + static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) {} + static void hflex (ENV &env, PARAM& param) {} + static void flex (ENV &env, PARAM& param) {} + static void hflex1 (ENV &env, PARAM& param) {} + static void flex1 (ENV &env, PARAM& param) {} +}; + +template > +struct cs_opset_t : opset_t +{ + static void process_op (op_code_t op, ENV &env, PARAM& param) + { + switch (op) { + + case OpCode_return: + env.return_from_subr (); + break; + case OpCode_endchar: + OPSET::check_width (op, env, param); + env.set_endchar (true); + OPSET::flush_args_and_op (op, env, param); + break; + + case OpCode_fixedcs: + env.argStack.push_fixed_from_substr (env.str_ref); + break; + + case OpCode_callsubr: + env.call_subr (env.localSubrs, CSType_LocalSubr); + break; + + case OpCode_callgsubr: + env.call_subr (env.globalSubrs, CSType_GlobalSubr); + break; + + case OpCode_hstem: + case OpCode_hstemhm: + OPSET::check_width (op, env, param); + OPSET::process_hstem (op, env, param); + break; + case OpCode_vstem: + case OpCode_vstemhm: + OPSET::check_width (op, env, param); + OPSET::process_vstem (op, env, param); + break; + case OpCode_hintmask: + case OpCode_cntrmask: + OPSET::check_width (op, env, param); + OPSET::process_hintmask (op, env, param); + break; + case OpCode_rmoveto: + OPSET::check_width (op, env, param); + PATH::rmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_hmoveto: + OPSET::check_width (op, env, param); + PATH::hmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_vmoveto: + OPSET::check_width (op, env, param); + PATH::vmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_rlineto: + PATH::rlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hlineto: + PATH::hlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_vlineto: + PATH::vlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_rrcurveto: + PATH::rrcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_rcurveline: + PATH::rcurveline (env, param); + process_post_path (op, env, param); + break; + case OpCode_rlinecurve: + PATH::rlinecurve (env, param); + process_post_path (op, env, param); + break; + case OpCode_vvcurveto: + PATH::vvcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hhcurveto: + PATH::hhcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_vhcurveto: + PATH::vhcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hvcurveto: + PATH::hvcurveto (env, param); + process_post_path (op, env, param); + break; + + case OpCode_hflex: + PATH::hflex (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_flex: + PATH::flex (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_hflex1: + PATH::hflex1 (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_flex1: + PATH::flex1 (env, param); + OPSET::process_post_flex (op, env, param); + break; + + default: + SUPER::process_op (op, env); + break; + } + } + + static void process_hstem (op_code_t op, ENV &env, PARAM& param) + { + env.hstem_count += env.argStack.get_count () / 2; + OPSET::flush_args_and_op (op, env, param); + } + + static void process_vstem (op_code_t op, ENV &env, PARAM& param) + { + env.vstem_count += env.argStack.get_count () / 2; + OPSET::flush_args_and_op (op, env, param); + } + + static void process_hintmask (op_code_t op, ENV &env, PARAM& param) + { + env.determine_hintmask_size (); + if (likely (env.str_ref.avail (env.hintmask_size))) + { + OPSET::flush_hintmask (op, env, param); + env.str_ref.inc (env.hintmask_size); + } + } + + static void process_post_flex (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static void check_width (op_code_t op, ENV &env, PARAM& param) + {} + + static void process_post_move (op_code_t op, ENV &env, PARAM& param) + { + if (!env.seen_moveto) + { + env.determine_hintmask_size (); + env.seen_moveto = true; + } + OPSET::flush_args_and_op (op, env, param); + } + + static void process_post_path (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static void flush_args_and_op (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args (env, param); + OPSET::flush_op (op, env, param); + } + + static void flush_args (ENV &env, PARAM& param) + { + env.pop_n_args (env.argStack.get_count ()); + } + + static void flush_op (op_code_t op, ENV &env, PARAM& param) + { + } + + static void flush_hintmask (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static bool is_number_op (op_code_t op) + { + switch (op) + { + case OpCode_shortint: + case OpCode_fixedcs: + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + return true; + + default: + /* 1-byte integer */ + return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast); + } + } + + protected: + typedef opset_t SUPER; +}; + +template +struct path_procs_t +{ + static void rmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + const number_t &dy = env.pop_arg (); + const number_t &dx = env.pop_arg (); + pt1.move (dx, dy); + PATH::moveto (env, param, pt1); + } + + static void hmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.pop_arg ()); + PATH::moveto (env, param, pt1); + } + + static void vmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + pt1.move_y (env.pop_arg ()); + PATH::moveto (env, param, pt1); + } + + static void rlineto (ENV &env, PARAM& param) + { + for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + } + + static void hlineto (ENV &env, PARAM& param) + { + point_t pt1; + unsigned int i = 0; + for (; i + 2 <= env.argStack.get_count (); i += 2) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + PATH::line (env, param, pt1); + pt1.move_y (env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + if (i < env.argStack.get_count ()) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + PATH::line (env, param, pt1); + } + } + + static void vlineto (ENV &env, PARAM& param) + { + point_t pt1; + unsigned int i = 0; + for (; i + 2 <= env.argStack.get_count (); i += 2) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + PATH::line (env, param, pt1); + pt1.move_x (env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + if (i < env.argStack.get_count ()) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + PATH::line (env, param, pt1); + } + } + + static void rrcurveto (ENV &env, PARAM& param) + { + for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + + static void rcurveline (ENV &env, PARAM& param) + { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + + unsigned int i = 0; + unsigned int curve_limit = arg_count - 2; + for (; i + 6 <= curve_limit; i += 6) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + + static void rlinecurve (ENV &env, PARAM& param) + { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + + unsigned int i = 0; + unsigned int line_limit = arg_count - 6; + for (; i + 2 <= line_limit; i += 2) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + + static void vvcurveto (ENV &env, PARAM& param) + { + unsigned int i = 0; + point_t pt1 = env.get_pt (); + if ((env.argStack.get_count () & 1) != 0) + pt1.move_x (env.eval_arg (i++)); + for (; i + 4 <= env.argStack.get_count (); i += 4) + { + pt1.move_y (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + } + } + + static void hhcurveto (ENV &env, PARAM& param) + { + unsigned int i = 0; + point_t pt1 = env.get_pt (); + if ((env.argStack.get_count () & 1) != 0) + pt1.move_y (env.eval_arg (i++)); + for (; i + 4 <= env.argStack.get_count (); i += 4) + { + pt1.move_x (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + } + } + + static void vhcurveto (ENV &env, PARAM& param) + { + point_t pt1, pt2, pt3; + unsigned int i = 0; + if ((env.argStack.get_count () % 8) >= 4) + { + point_t pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + i += 4; + + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_y (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+7)); + } + if (i < env.argStack.get_count ()) + pt3.move_y (env.eval_arg (i)); + PATH::curve (env, param, pt1, pt2, pt3); + } + else + { + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_x (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+7)); + if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) + pt3.move_x (env.eval_arg (i+8)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + } + + static void hvcurveto (ENV &env, PARAM& param) + { + point_t pt1, pt2, pt3; + unsigned int i = 0; + if ((env.argStack.get_count () % 8) >= 4) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + i += 4; + + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_x (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+7)); + } + if (i < env.argStack.get_count ()) + pt3.move_x (env.eval_arg (i)); + PATH::curve (env, param, pt1, pt2, pt3); + } + else + { + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_y (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+7)); + if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) + pt3.move_y (env.eval_arg (i+8)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + } + + /* default actions to be overridden */ + static void moveto (ENV &env, PARAM& param, const point_t &pt) + { env.moveto (pt); } + + static void line (ENV &env, PARAM& param, const point_t &pt1) + { PATH::moveto (env, param, pt1); } + + static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { PATH::moveto (env, param, pt3); } + + static void hflex (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 7)) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (0)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (1), env.eval_arg (2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (3)); + point_t pt4 = pt3; + pt4.move_x (env.eval_arg (4)); + point_t pt5 = pt4; + pt5.move_x (env.eval_arg (5)); + pt5.y = pt1.y; + point_t pt6 = pt5; + pt6.move_x (env.eval_arg (6)); + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void flex (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 13)) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (4), env.eval_arg (5)); + point_t pt4 = pt3; + pt4.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (8), env.eval_arg (9)); + point_t pt6 = pt5; + pt6.move (env.eval_arg (10), env.eval_arg (11)); + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void hflex1 (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 9)) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (4)); + point_t pt4 = pt3; + pt4.move_x (env.eval_arg (5)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt6 = pt5; + pt6.move_x (env.eval_arg (8)); + pt6.y = env.get_pt ().y; + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void flex1 (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 11)) + { + point_t d; + d.init (); + for (unsigned int i = 0; i < 10; i += 2) + d.move (env.eval_arg (i), env.eval_arg (i+1)); + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (4), env.eval_arg (5)); + point_t pt4 = pt3; + pt4.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (8), env.eval_arg (9)); + point_t pt6 = pt5; + + if (fabs (d.x.to_real ()) > fabs (d.y.to_real ())) + { + pt6.move_x (env.eval_arg (10)); + pt6.y = env.get_pt ().y; + } + else + { + pt6.x = env.get_pt ().x; + pt6.move_y (env.eval_arg (10)); + } + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + protected: + static void curve2 (ENV &env, PARAM& param, + const point_t &pt1, const point_t &pt2, const point_t &pt3, + const point_t &pt4, const point_t &pt5, const point_t &pt6) + { + PATH::curve (env, param, pt1, pt2, pt3); + PATH::curve (env, param, pt4, pt5, pt6); + } +}; + +template +struct cs_interpreter_t : interpreter_t +{ + bool interpret (PARAM& param) + { + SUPER::env.set_endchar (false); + + for (;;) { + OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); + if (unlikely (SUPER::env.in_error ())) + return false; + if (SUPER::env.is_endchar ()) + break; + } + + return true; + } + + private: + typedef interpreter_t SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_CS_COMMON_HH */ diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh new file mode 100644 index 00000000000..a520ca3bce5 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh @@ -0,0 +1,201 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_DICT_COMMON_HH +#define HB_CFF_INTERP_DICT_COMMON_HH + +#include "hb-cff-interp-common.hh" + +namespace CFF { + +using namespace OT; + +/* an opstr and the parsed out dict value(s) */ +struct dict_val_t : op_str_t +{ + void init () { single_val.set_int (0); } + void fini () {} + + number_t single_val; +}; + +typedef dict_val_t num_dict_val_t; + +template struct dict_values_t : parsed_values_t {}; + +template +struct top_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + charStringsOffset = 0; + FDArrayOffset = 0; + } + void fini () { dict_values_t::fini (); } + + unsigned int charStringsOffset; + unsigned int FDArrayOffset; +}; + +struct dict_opset_t : opset_t +{ + static void process_op (op_code_t op, interp_env_t& env) + { + switch (op) { + case OpCode_longintdict: /* 5-byte integer */ + env.argStack.push_longint_from_substr (env.str_ref); + break; + + case OpCode_BCD: /* real number */ + env.argStack.push_real (parse_bcd (env.str_ref)); + break; + + default: + opset_t::process_op (op, env); + break; + } + } + + /* Turns CFF's BCD format into strtod understandable string */ + static double parse_bcd (byte_str_ref_t& str_ref) + { + if (unlikely (str_ref.in_error ())) return .0; + + enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END }; + + char buf[32]; + unsigned char byte = 0; + for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count) + { + unsigned nibble; + if (!(i & 1)) + { + if (unlikely (!str_ref.avail ())) break; + + byte = str_ref[0]; + str_ref.inc (); + nibble = byte >> 4; + } + else + nibble = byte & 0x0F; + + if (unlikely (nibble == RESERVED)) break; + else if (nibble == END) + { + const char *p = buf; + double pv; + if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */))) + break; + return pv; + } + else + { + buf[count] = "0123456789.EE?-?"[nibble]; + if (nibble == EXP_NEG) + { + ++count; + if (unlikely (count == ARRAY_LENGTH (buf))) break; + buf[count] = '-'; + } + } + } + + str_ref.set_error (); + return .0; + } + + static bool is_hint_op (op_code_t op) + { + switch (op) + { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + return true; + default: + return false; + } + } +}; + +template +struct top_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, interp_env_t& env, top_dict_values_t & dictval) + { + switch (op) { + case OpCode_CharStrings: + dictval.charStringsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FDArray: + dictval.FDArrayOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FontMatrix: + env.clear_args (); + break; + default: + dict_opset_t::process_op (op, env); + break; + } + } +}; + +template +struct dict_interpreter_t : interpreter_t +{ + bool interpret (PARAM& param) + { + param.init (); + while (SUPER::env.str_ref.avail ()) + { + OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); + if (unlikely (SUPER::env.in_error ())) + return false; + } + + return true; + } + + private: + typedef interpreter_t SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_DICT_COMMON_HH */ diff --git a/thirdparty/harfbuzz/src/hb-cff1-interp-cs.hh b/thirdparty/harfbuzz/src/hb-cff1-interp-cs.hh new file mode 100644 index 00000000000..1c8762c172b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-cff1-interp-cs.hh @@ -0,0 +1,161 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF1_INTERP_CS_HH +#define HB_CFF1_INTERP_CS_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +using namespace OT; + +typedef biased_subrs_t cff1_biased_subrs_t; + +struct cff1_cs_interp_env_t : cs_interp_env_t +{ + template + void init (const byte_str_t &str, ACC &acc, unsigned int fd) + { + SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs); + processed_width = false; + has_width = false; + arg_start = 0; + in_seac = false; + } + + void fini () { SUPER::fini (); } + + void set_width (bool has_width_) + { + if (likely (!processed_width && (SUPER::argStack.get_count () > 0))) + { + if (has_width_) + { + width = SUPER::argStack[0]; + has_width = true; + arg_start = 1; + } + } + processed_width = true; + } + + void clear_args () + { + arg_start = 0; + SUPER::clear_args (); + } + + void set_in_seac (bool _in_seac) { in_seac = _in_seac; } + + bool processed_width; + bool has_width; + unsigned int arg_start; + number_t width; + bool in_seac; + + private: + typedef cs_interp_env_t SUPER; +}; + +template > +struct cff1_cs_opset_t : cs_opset_t +{ + /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */ + /* Type 1-originated deprecated opcodes, seac behavior of endchar and dotsection are supported */ + + static void process_op (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param) + { + switch (op) { + case OpCode_dotsection: + SUPER::flush_args_and_op (op, env, param); + break; + + case OpCode_endchar: + OPSET::check_width (op, env, param); + if (env.argStack.get_count () >= 4) + { + OPSET::process_seac (env, param); + } + OPSET::flush_args_and_op (op, env, param); + env.set_endchar (true); + break; + + default: + SUPER::process_op (op, env, param); + } + } + + static void check_width (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param) + { + if (!env.processed_width) + { + bool has_width = false; + switch (op) + { + case OpCode_endchar: + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + has_width = ((env.argStack.get_count () & 1) != 0); + break; + case OpCode_hmoveto: + case OpCode_vmoveto: + has_width = (env.argStack.get_count () > 1); + break; + case OpCode_rmoveto: + has_width = (env.argStack.get_count () > 2); + break; + default: + return; + } + env.set_width (has_width); + } + } + + static void process_seac (cff1_cs_interp_env_t &env, PARAM& param) + { + } + + static void flush_args (cff1_cs_interp_env_t &env, PARAM& param) + { + SUPER::flush_args (env, param); + env.clear_args (); /* pop off width */ + } + + private: + typedef cs_opset_t SUPER; +}; + +template +struct cff1_cs_interpreter_t : cs_interpreter_t {}; + +} /* namespace CFF */ + +#endif /* HB_CFF1_INTERP_CS_HH */ diff --git a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh new file mode 100644 index 00000000000..332ece31cd4 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh @@ -0,0 +1,272 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF2_INTERP_CS_HH +#define HB_CFF2_INTERP_CS_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +using namespace OT; + +struct blend_arg_t : number_t +{ + void init () + { + number_t::init (); + deltas.init (); + } + + void fini () + { + number_t::fini (); + deltas.fini_deep (); + } + + void set_int (int v) { reset_blends (); number_t::set_int (v); } + void set_fixed (int32_t v) { reset_blends (); number_t::set_fixed (v); } + void set_real (double v) { reset_blends (); number_t::set_real (v); } + + void set_blends (unsigned int numValues_, unsigned int valueIndex_, + unsigned int numBlends, hb_array_t blends_) + { + numValues = numValues_; + valueIndex = valueIndex_; + deltas.resize (numBlends); + for (unsigned int i = 0; i < numBlends; i++) + deltas[i] = blends_[i]; + } + + bool blending () const { return deltas.length > 0; } + void reset_blends () + { + numValues = valueIndex = 0; + deltas.resize (0); + } + + unsigned int numValues; + unsigned int valueIndex; + hb_vector_t deltas; +}; + +typedef interp_env_t BlendInterpEnv; +typedef biased_subrs_t cff2_biased_subrs_t; + +struct cff2_cs_interp_env_t : cs_interp_env_t +{ + template + void init (const byte_str_t &str, ACC &acc, unsigned int fd, + const int *coords_=nullptr, unsigned int num_coords_=0) + { + SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs); + + coords = coords_; + num_coords = num_coords_; + varStore = acc.varStore; + seen_blend = false; + seen_vsindex_ = false; + scalars.init (); + do_blend = num_coords && coords && varStore->size; + set_ivs (acc.privateDicts[fd].ivs); + } + + void fini () + { + scalars.fini (); + SUPER::fini (); + } + + op_code_t fetch_op () + { + if (this->str_ref.avail ()) + return SUPER::fetch_op (); + + /* make up return or endchar op */ + if (this->callStack.is_empty ()) + return OpCode_endchar; + else + return OpCode_return; + } + + const blend_arg_t& eval_arg (unsigned int i) + { + blend_arg_t &arg = argStack[i]; + blend_arg (arg); + return arg; + } + + const blend_arg_t& pop_arg () + { + blend_arg_t &arg = argStack.pop (); + blend_arg (arg); + return arg; + } + + void process_blend () + { + if (!seen_blend) + { + region_count = varStore->varStore.get_region_index_count (get_ivs ()); + if (do_blend) + { + if (unlikely (!scalars.resize (region_count))) + set_error (); + else + varStore->varStore.get_scalars (get_ivs (), coords, num_coords, + &scalars[0], region_count); + } + seen_blend = true; + } + } + + void process_vsindex () + { + unsigned int index = argStack.pop_uint (); + if (unlikely (seen_vsindex () || seen_blend)) + { + set_error (); + } + else + { + set_ivs (index); + } + seen_vsindex_ = true; + } + + unsigned int get_region_count () const { return region_count; } + void set_region_count (unsigned int region_count_) { region_count = region_count_; } + unsigned int get_ivs () const { return ivs; } + void set_ivs (unsigned int ivs_) { ivs = ivs_; } + bool seen_vsindex () const { return seen_vsindex_; } + + protected: + void blend_arg (blend_arg_t &arg) + { + if (do_blend && arg.blending ()) + { + if (likely (scalars.length == arg.deltas.length)) + { + double v = arg.to_real (); + for (unsigned int i = 0; i < scalars.length; i++) + { + v += (double)scalars[i] * arg.deltas[i].to_real (); + } + arg.set_real (v); + arg.deltas.resize (0); + } + } + } + + protected: + const int *coords; + unsigned int num_coords; + const CFF2VariationStore *varStore; + unsigned int region_count; + unsigned int ivs; + hb_vector_t scalars; + bool do_blend; + bool seen_vsindex_; + bool seen_blend; + + typedef cs_interp_env_t SUPER; +}; +template > +struct cff2_cs_opset_t : cs_opset_t +{ + static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param) + { + switch (op) { + case OpCode_callsubr: + case OpCode_callgsubr: + /* a subroutine number shoudln't be a blended value */ + if (unlikely (env.argStack.peek ().blending ())) + { + env.set_error (); + break; + } + SUPER::process_op (op, env, param); + break; + + case OpCode_blendcs: + OPSET::process_blend (env, param); + break; + + case OpCode_vsindexcs: + if (unlikely (env.argStack.peek ().blending ())) + { + env.set_error (); + break; + } + OPSET::process_vsindex (env, param); + break; + + default: + SUPER::process_op (op, env, param); + } + } + + static void process_blend (cff2_cs_interp_env_t &env, PARAM& param) + { + unsigned int n, k; + + env.process_blend (); + k = env.get_region_count (); + n = env.argStack.pop_uint (); + /* copy the blend values into blend array of the default values */ + unsigned int start = env.argStack.get_count () - ((k+1) * n); + /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */ + if (unlikely (start > env.argStack.get_count ())) + { + env.set_error (); + return; + } + for (unsigned int i = 0; i < n; i++) + { + const hb_array_t blends = env.argStack.get_subarray (start + n + (i * k)); + env.argStack[start + i].set_blends (n, i, k, blends); + } + + /* pop off blend values leaving default values now adorned with blend values */ + env.argStack.pop (k * n); + } + + static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param) + { + env.process_vsindex (); + env.clear_args (); + } + + private: + typedef cs_opset_t SUPER; +}; + +template +struct cff2_cs_interpreter_t : cs_interpreter_t {}; + +} /* namespace CFF */ + +#endif /* HB_CFF2_INTERP_CS_HH */ diff --git a/thirdparty/harfbuzz/src/hb-common.cc b/thirdparty/harfbuzz/src/hb-common.cc new file mode 100644 index 00000000000..5acfa78431a --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-common.cc @@ -0,0 +1,1098 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-machinery.hh" + +#include + +#ifdef HB_NO_SETLOCALE +#define setlocale(Category, Locale) "C" +#endif + +/** + * SECTION:hb-common + * @title: hb-common + * @short_description: Common data types + * @include: hb.h + * + * Common data types used across HarfBuzz are defined here. + **/ + + +/* hb_options_t */ + +hb_atomic_int_t _hb_options; + +void +_hb_options_init () +{ + hb_options_union_t u; + u.i = 0; + u.opts.initialized = true; + + const char *c = getenv ("HB_OPTIONS"); + if (c) + { + while (*c) + { + const char *p = strchr (c, ':'); + if (!p) + p = c + strlen (c); + +#define OPTION(name, symbol) \ + if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast(p - c)) do { u.opts.symbol = true; } while (0) + + OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); + +#undef OPTION + + c = *p ? p + 1 : p; + } + + } + + /* This is idempotent and threadsafe. */ + _hb_options.set_relaxed (u.i); +} + + +/* hb_tag_t */ + +/** + * hb_tag_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_tag_from_string (const char *str, int len) +{ + char tag[4]; + unsigned int i; + + if (!str || !len || !*str) + return HB_TAG_NONE; + + if (len < 0 || len > 4) + len = 4; + for (i = 0; i < (unsigned) len && str[i]; i++) + tag[i] = str[i]; + for (; i < 4; i++) + tag[i] = ' '; + + return HB_TAG (tag[0], tag[1], tag[2], tag[3]); +} + +/** + * hb_tag_to_string: + * @tag: + * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): + * + * + * + * Since: 0.9.5 + **/ +void +hb_tag_to_string (hb_tag_t tag, char *buf) +{ + buf[0] = (char) (uint8_t) (tag >> 24); + buf[1] = (char) (uint8_t) (tag >> 16); + buf[2] = (char) (uint8_t) (tag >> 8); + buf[3] = (char) (uint8_t) (tag >> 0); +} + + +/* hb_direction_t */ + +const char direction_strings[][4] = { + "ltr", + "rtl", + "ttb", + "btt" +}; + +/** + * hb_direction_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_direction_from_string (const char *str, int len) +{ + if (unlikely (!str || !len || !*str)) + return HB_DIRECTION_INVALID; + + /* Lets match loosely: just match the first letter, such that + * all of "ltr", "left-to-right", etc work! + */ + char c = TOLOWER (str[0]); + for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) + if (c == direction_strings[i][0]) + return (hb_direction_t) (HB_DIRECTION_LTR + i); + + return HB_DIRECTION_INVALID; +} + +/** + * hb_direction_to_string: + * @direction: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +const char * +hb_direction_to_string (hb_direction_t direction) +{ + if (likely ((unsigned int) (direction - HB_DIRECTION_LTR) + < ARRAY_LENGTH (direction_strings))) + return direction_strings[direction - HB_DIRECTION_LTR]; + + return "invalid"; +} + + +/* hb_language_t */ + +struct hb_language_impl_t { + const char s[1]; +}; + +static const char canon_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 +}; + +static bool +lang_equal (hb_language_t v1, + const void *v2) +{ + const unsigned char *p1 = (const unsigned char *) v1; + const unsigned char *p2 = (const unsigned char *) v2; + + while (*p1 && *p1 == canon_map[*p2]) { + p1++; + p2++; + } + + return *p1 == canon_map[*p2]; +} + +#if 0 +static unsigned int +lang_hash (const void *key) +{ + const unsigned char *p = key; + unsigned int h = 0; + while (canon_map[*p]) + { + h = (h << 5) - h + canon_map[*p]; + p++; + } + + return h; +} +#endif + + +struct hb_language_item_t { + + struct hb_language_item_t *next; + hb_language_t lang; + + bool operator == (const char *s) const + { return lang_equal (lang, s); } + + hb_language_item_t & operator = (const char *s) { + /* If a custom allocated is used calling strdup() pairs + badly with a call to the custom free() in fini() below. + Therefore don't call strdup(), implement its behavior. + */ + size_t len = strlen(s) + 1; + lang = (hb_language_t) malloc(len); + if (likely (lang)) + { + memcpy((unsigned char *) lang, s, len); + for (unsigned char *p = (unsigned char *) lang; *p; p++) + *p = canon_map[*p]; + } + + return *this; + } + + void fini () { free ((void *) lang); } +}; + + +/* Thread-safe lock-free language list */ + +static hb_atomic_ptr_t langs; + +#if HB_USE_ATEXIT +static void +free_langs () +{ +retry: + hb_language_item_t *first_lang = langs; + if (unlikely (!langs.cmpexch (first_lang, nullptr))) + goto retry; + + while (first_lang) { + hb_language_item_t *next = first_lang->next; + first_lang->fini (); + free (first_lang); + first_lang = next; + } +} +#endif + +static hb_language_item_t * +lang_find_or_insert (const char *key) +{ +retry: + hb_language_item_t *first_lang = langs; + + for (hb_language_item_t *lang = first_lang; lang; lang = lang->next) + if (*lang == key) + return lang; + + /* Not found; allocate one. */ + hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); + if (unlikely (!lang)) + return nullptr; + lang->next = first_lang; + *lang = key; + if (unlikely (!lang->lang)) + { + free (lang); + return nullptr; + } + + if (unlikely (!langs.cmpexch (first_lang, lang))) + { + lang->fini (); + free (lang); + goto retry; + } + +#if HB_USE_ATEXIT + if (!first_lang) + atexit (free_langs); /* First person registers atexit() callback. */ +#endif + + return lang; +} + + +/** + * hb_language_from_string: + * @str: (array length=len) (element-type uint8_t): a string representing + * a BCP 47 language tag + * @len: length of the @str, or -1 if it is %NULL-terminated. + * + * Converts @str representing a BCP 47 language tag to the corresponding + * #hb_language_t. + * + * Return value: (transfer none): + * The #hb_language_t corresponding to the BCP 47 language tag. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_from_string (const char *str, int len) +{ + if (!str || !len || !*str) + return HB_LANGUAGE_INVALID; + + hb_language_item_t *item = nullptr; + if (len >= 0) + { + /* NUL-terminate it. */ + char strbuf[64]; + len = hb_min (len, (int) sizeof (strbuf) - 1); + memcpy (strbuf, str, len); + strbuf[len] = '\0'; + item = lang_find_or_insert (strbuf); + } + else + item = lang_find_or_insert (str); + + return likely (item) ? item->lang : HB_LANGUAGE_INVALID; +} + +/** + * hb_language_to_string: + * @language: an #hb_language_t to convert. + * + * See hb_language_from_string(). + * + * Return value: (transfer none): + * A %NULL-terminated string representing the @language. Must not be freed by + * the caller. + * + * Since: 0.9.2 + **/ +const char * +hb_language_to_string (hb_language_t language) +{ + if (unlikely (!language)) return nullptr; + + return language->s; +} + +/** + * hb_language_get_default: + * + * Get default language from current locale. + * + * Note that the first time this function is called, it calls + * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying + * setlocale function is, in many implementations, NOT threadsafe. To avoid + * problems, call this function once before multiple threads can call it. + * This function is only used from hb_buffer_guess_segment_properties() by + * HarfBuzz itself. + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_get_default () +{ + static hb_atomic_ptr_t default_language; + + hb_language_t language = default_language; + if (unlikely (language == HB_LANGUAGE_INVALID)) + { + language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1); + (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language); + } + + return language; +} + + +/* hb_script_t */ + +/** + * hb_script_from_iso15924_tag: + * @tag: an #hb_tag_t representing an ISO 15924 tag. + * + * Converts an ISO 15924 script tag to a corresponding #hb_script_t. + * + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag) +{ + if (unlikely (tag == HB_TAG_NONE)) + return HB_SCRIPT_INVALID; + + /* Be lenient, adjust case (one capital letter followed by three small letters) */ + tag = (tag & 0xDFDFDFDFu) | 0x00202020u; + + switch (tag) { + + /* These graduated from the 'Q' private-area codes, but + * the old code is still aliased by Unicode, and the Qaai + * one in use by ICU. */ + case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; + case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; + + /* Script variants from https://unicode.org/iso15924/ */ + case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; + case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; + case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; + case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; + } + + /* If it looks right, just use the tag as a script */ + if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u) + return (hb_script_t) tag; + + /* Otherwise, return unknown */ + return HB_SCRIPT_UNKNOWN; +} + +/** + * hb_script_from_string: + * @str: (array length=len) (element-type uint8_t): a string representing an + * ISO 15924 tag. + * @len: length of the @str, or -1 if it is %NULL-terminated. + * + * Converts a string @str representing an ISO 15924 script tag to a + * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then + * hb_script_from_iso15924_tag(). + * + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_string (const char *str, int len) +{ + return hb_script_from_iso15924_tag (hb_tag_from_string (str, len)); +} + +/** + * hb_script_to_iso15924_tag: + * @script: an #hb_script_t to convert. + * + * See hb_script_from_iso15924_tag(). + * + * Return value: + * An #hb_tag_t representing an ISO 15924 script tag. + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script) +{ + return (hb_tag_t) script; +} + +/** + * hb_script_get_horizontal_direction: + * @script: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_script_get_horizontal_direction (hb_script_t script) +{ + /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ + switch ((hb_tag_t) script) + { + /* Unicode-1.1 additions */ + case HB_SCRIPT_ARABIC: + case HB_SCRIPT_HEBREW: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_SYRIAC: + case HB_SCRIPT_THAANA: + + /* Unicode-4.0 additions */ + case HB_SCRIPT_CYPRIOT: + + /* Unicode-4.1 additions */ + case HB_SCRIPT_KHAROSHTHI: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_PHOENICIAN: + case HB_SCRIPT_NKO: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_LYDIAN: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_AVESTAN: + case HB_SCRIPT_IMPERIAL_ARAMAIC: + case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: + case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: + case HB_SCRIPT_OLD_SOUTH_ARABIAN: + case HB_SCRIPT_OLD_TURKIC: + case HB_SCRIPT_SAMARITAN: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_MANDAIC: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_MEROITIC_CURSIVE: + case HB_SCRIPT_MEROITIC_HIEROGLYPHS: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MENDE_KIKAKUI: + case HB_SCRIPT_NABATAEAN: + case HB_SCRIPT_OLD_NORTH_ARABIAN: + case HB_SCRIPT_PALMYRENE: + case HB_SCRIPT_PSALTER_PAHLAVI: + + /* Unicode-8.0 additions */ + case HB_SCRIPT_HATRAN: + + /* Unicode-9.0 additions */ + case HB_SCRIPT_ADLAM: + + /* Unicode-11.0 additions */ + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_OLD_SOGDIAN: + case HB_SCRIPT_SOGDIAN: + + /* Unicode-12.0 additions */ + case HB_SCRIPT_ELYMAIC: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_YEZIDI: + + return HB_DIRECTION_RTL; + + + /* https://github.com/harfbuzz/harfbuzz/issues/1000 */ + case HB_SCRIPT_OLD_HUNGARIAN: + case HB_SCRIPT_OLD_ITALIC: + case HB_SCRIPT_RUNIC: + + return HB_DIRECTION_INVALID; + } + + return HB_DIRECTION_LTR; +} + + +/* hb_version */ + + +/** + * SECTION:hb-version + * @title: hb-version + * @short_description: Information about the version of HarfBuzz in use + * @include: hb.h + * + * These functions and macros allow accessing version of the HarfBuzz + * library used at compile- as well as run-time, and to direct code + * conditionally based on those versions, again, at compile- or run-time. + **/ + + +/** + * hb_version: + * @major: (out): Library major version component. + * @minor: (out): Library minor version component. + * @micro: (out): Library micro version component. + * + * Returns library version as three integer components. + * + * Since: 0.9.2 + **/ +void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro) +{ + *major = HB_VERSION_MAJOR; + *minor = HB_VERSION_MINOR; + *micro = HB_VERSION_MICRO; +} + +/** + * hb_version_string: + * + * Returns library version as a string with three components. + * + * Return value: library version string. + * + * Since: 0.9.2 + **/ +const char * +hb_version_string () +{ + return HB_VERSION_STRING; +} + +/** + * hb_version_atleast: + * @major: + * @minor: + * @micro: + * + * + * + * Return value: + * + * Since: 0.9.30 + **/ +hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro) +{ + return HB_VERSION_ATLEAST (major, minor, micro); +} + + + +/* hb_feature_t and hb_variation_t */ + +static bool +parse_space (const char **pp, const char *end) +{ + while (*pp < end && ISSPACE (**pp)) + (*pp)++; + return true; +} + +static bool +parse_char (const char **pp, const char *end, char c) +{ + parse_space (pp, end); + + if (*pp == end || **pp != c) + return false; + + (*pp)++; + return true; +} + +static bool +parse_uint (const char **pp, const char *end, unsigned int *pv) +{ + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; + + *pv = v; + return true; +} + +static bool +parse_uint32 (const char **pp, const char *end, uint32_t *pv) +{ + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; + + *pv = v; + return true; +} + +static bool +parse_bool (const char **pp, const char *end, uint32_t *pv) +{ + parse_space (pp, end); + + const char *p = *pp; + while (*pp < end && ISALPHA(**pp)) + (*pp)++; + + /* CSS allows on/off as aliases 1/0. */ + if (*pp - p == 2 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'n') + *pv = 1; + else if (*pp - p == 3 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'f' + && TOLOWER (p[2]) == 'f') + *pv = 0; + else + return false; + + return true; +} + +/* hb_feature_t */ + +static bool +parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) +{ + if (parse_char (pp, end, '-')) + feature->value = 0; + else { + parse_char (pp, end, '+'); + feature->value = 1; + } + + return true; +} + +static bool +parse_tag (const char **pp, const char *end, hb_tag_t *tag) +{ + parse_space (pp, end); + + char quote = 0; + + if (*pp < end && (**pp == '\'' || **pp == '"')) + { + quote = **pp; + (*pp)++; + } + + const char *p = *pp; + while (*pp < end && (ISALNUM(**pp) || **pp == '_')) + (*pp)++; + + if (p == *pp || *pp - p > 4) + return false; + + *tag = hb_tag_from_string (p, *pp - p); + + if (quote) + { + /* CSS expects exactly four bytes. And we only allow quotations for + * CSS compatibility. So, enforce the length. */ + if (*pp - p != 4) + return false; + if (*pp == end || **pp != quote) + return false; + (*pp)++; + } + + return true; +} + +static bool +parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) +{ + parse_space (pp, end); + + bool has_start; + + feature->start = HB_FEATURE_GLOBAL_START; + feature->end = HB_FEATURE_GLOBAL_END; + + if (!parse_char (pp, end, '[')) + return true; + + has_start = parse_uint (pp, end, &feature->start); + + if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) { + parse_uint (pp, end, &feature->end); + } else { + if (has_start) + feature->end = feature->start + 1; + } + + return parse_char (pp, end, ']'); +} + +static bool +parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) +{ + bool had_equal = parse_char (pp, end, '='); + bool had_value = parse_uint32 (pp, end, &feature->value) || + parse_bool (pp, end, &feature->value); + /* CSS doesn't use equal-sign between tag and value. + * If there was an equal-sign, then there *must* be a value. + * A value without an equal-sign is ok, but not required. */ + return !had_equal || had_value; +} + +static bool +parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) +{ + return parse_feature_value_prefix (pp, end, feature) && + parse_tag (pp, end, &feature->tag) && + parse_feature_indices (pp, end, feature) && + parse_feature_value_postfix (pp, end, feature) && + parse_space (pp, end) && + *pp == end; +} + +/** + * hb_feature_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is %NULL terminated + * @feature: (out): the #hb_feature_t to initialize with the parsed values + * + * Parses a string into a #hb_feature_t. + * + * The format for specifying feature strings follows. All valid CSS + * font-feature-settings values other than 'normal' and the global values are + * also accepted, though not documented below. CSS string escapes are not + * supported. + * + * The range indices refer to the positions between Unicode characters. The + * position before the first character is always 0. + * + * The format is Python-esque. Here is how it all works: + * + * + * + * + * Syntax Value Start End + * + * + * Setting value: + * kern 1 0 Turn feature on + * +kern 1 0 Turn feature on + * -kern 0 0 Turn feature off + * kern=0 0 0 Turn feature off + * kern=1 1 0 Turn feature on + * aalt=2 2 0 Choose 2nd alternate + * Setting index: + * kern[] 1 0 Turn feature on + * kern[:] 1 0 Turn feature on + * kern[5:] 1 5 Turn feature on, partial + * kern[:5] 1 0 5 Turn feature on, partial + * kern[3:5] 1 3 5 Turn feature on, range + * kern[3] 1 3 3+1 Turn feature on, single char + * Mixing it all: + * aalt[3:5]=2 2 3 5 Turn 2nd alternate on for range + * + * + * + * + * Return value: + * %true if @str is successfully parsed, %false otherwise. + * + * Since: 0.9.5 + **/ +hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature) +{ + hb_feature_t feat; + + if (len < 0) + len = strlen (str); + + if (likely (parse_one_feature (&str, str + len, &feat))) + { + if (feature) + *feature = feat; + return true; + } + + if (feature) + memset (feature, 0, sizeof (*feature)); + return false; +} + +/** + * hb_feature_to_string: + * @feature: an #hb_feature_t to convert + * @buf: (array length=size) (out): output string + * @size: the allocated size of @buf + * + * Converts a #hb_feature_t into a %NULL-terminated string in the format + * understood by hb_feature_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. + * + * Since: 0.9.5 + **/ +void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size) +{ + if (unlikely (!size)) return; + + char s[128]; + unsigned int len = 0; + if (feature->value == 0) + s[len++] = '-'; + hb_tag_to_string (feature->tag, s + len); + len += 4; + while (len && s[len - 1] == ' ') + len--; + if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END) + { + s[len++] = '['; + if (feature->start) + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + if (feature->end != feature->start + 1) { + s[len++] = ':'; + if (feature->end != HB_FEATURE_GLOBAL_END) + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + } + s[len++] = ']'; + } + if (feature->value > 1) + { + s[len++] = '='; + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + } + assert (len < ARRAY_LENGTH (s)); + len = hb_min (len, size - 1); + memcpy (buf, s, len); + buf[len] = '\0'; +} + +/* hb_variation_t */ + +static bool +parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) +{ + parse_char (pp, end, '='); /* Optional. */ + double v; + if (unlikely (!hb_parse_double (pp, end, &v))) return false; + + variation->value = v; + return true; +} + +static bool +parse_one_variation (const char **pp, const char *end, hb_variation_t *variation) +{ + return parse_tag (pp, end, &variation->tag) && + parse_variation_value (pp, end, variation) && + parse_space (pp, end) && + *pp == end; +} + +/** + * hb_variation_from_string: + * + * Since: 1.4.2 + */ +hb_bool_t +hb_variation_from_string (const char *str, int len, + hb_variation_t *variation) +{ + hb_variation_t var; + + if (len < 0) + len = strlen (str); + + if (likely (parse_one_variation (&str, str + len, &var))) + { + if (variation) + *variation = var; + return true; + } + + if (variation) + memset (variation, 0, sizeof (*variation)); + return false; +} + +/** + * hb_variation_to_string: + * + * Since: 1.4.2 + */ +void +hb_variation_to_string (hb_variation_t *variation, + char *buf, unsigned int size) +{ + if (unlikely (!size)) return; + + char s[128]; + unsigned int len = 0; + hb_tag_to_string (variation->tag, s + len); + len += 4; + while (len && s[len - 1] == ' ') + len--; + s[len++] = '='; + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); + + assert (len < ARRAY_LENGTH (s)); + len = hb_min (len, size - 1); + memcpy (buf, s, len); + buf[len] = '\0'; +} + +/** + * hb_color_get_alpha: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Alpha channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_alpha) (hb_color_t color) +{ + return hb_color_get_alpha (color); +} + +/** + * hb_color_get_red: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Red channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_red) (hb_color_t color) +{ + return hb_color_get_red (color); +} + +/** + * hb_color_get_green: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Green channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_green) (hb_color_t color) +{ + return hb_color_get_green (color); +} + +/** + * hb_color_get_blue: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Blue channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_blue) (hb_color_t color) +{ + return hb_color_get_blue (color); +} + + +/* If there is no visibility control, then hb-static.cc will NOT + * define anything. Instead, we get it to define one set in here + * only, so only libharfbuzz.so defines them, not other libs. */ +#ifdef HB_NO_VISIBILITY +#undef HB_NO_VISIBILITY +#include "hb-static.cc" +#define HB_NO_VISIBILITY 1 +#endif diff --git a/thirdparty/harfbuzz/src/hb-common.h b/thirdparty/harfbuzz/src/hb-common.h new file mode 100644 index 00000000000..a97a5f5a04b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-common.h @@ -0,0 +1,513 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_COMMON_H +#define HB_COMMON_H + +#ifndef HB_EXTERN +#define HB_EXTERN extern +#endif + +#ifndef HB_BEGIN_DECLS +# ifdef __cplusplus +# define HB_BEGIN_DECLS extern "C" { +# define HB_END_DECLS } +# else /* !__cplusplus */ +# define HB_BEGIN_DECLS +# define HB_END_DECLS +# endif /* !__cplusplus */ +#endif + +#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ + defined (_sgi) || defined (__sun) || defined (sun) || \ + defined (__digital__) || defined (__HP_cc) +# include +#elif defined (_AIX) +# include +#elif defined (_MSC_VER) && _MSC_VER < 1600 +/* VS 2010 (_MSC_VER 1600) has stdint.h */ +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#elif defined (__KERNEL__) +# include +#else +# include +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define HB_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +#define HB_DEPRECATED __declspec(deprecated) +#else +#define HB_DEPRECATED +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define HB_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead"))) +#elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320) +#define HB_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead")) +#else +#define HB_DEPRECATED_FOR(f) HB_DEPRECATED +#endif + + +HB_BEGIN_DECLS + + +typedef int hb_bool_t; + +typedef uint32_t hb_codepoint_t; +typedef int32_t hb_position_t; +typedef uint32_t hb_mask_t; + +typedef union _hb_var_int_t { + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + int8_t i8[4]; +} hb_var_int_t; + + +/* hb_tag_t */ + +typedef uint32_t hb_tag_t; + +#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF))) +#define HB_UNTAG(tag) (uint8_t)(((tag)>>24)&0xFF), (uint8_t)(((tag)>>16)&0xFF), (uint8_t)(((tag)>>8)&0xFF), (uint8_t)((tag)&0xFF) + +#define HB_TAG_NONE HB_TAG(0,0,0,0) +#define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff) +#define HB_TAG_MAX_SIGNED HB_TAG(0x7f,0xff,0xff,0xff) + +/* len=-1 means str is NUL-terminated. */ +HB_EXTERN hb_tag_t +hb_tag_from_string (const char *str, int len); + +/* buf should have 4 bytes. */ +HB_EXTERN void +hb_tag_to_string (hb_tag_t tag, char *buf); + + +/** + * hb_direction_t: + * @HB_DIRECTION_INVALID: Initial, unset direction. + * @HB_DIRECTION_LTR: Text is set horizontally from left to right. + * @HB_DIRECTION_RTL: Text is set horizontally from right to left. + * @HB_DIRECTION_TTB: Text is set vertically from top to bottom. + * @HB_DIRECTION_BTT: Text is set vertically from bottom to top. + */ +typedef enum { + HB_DIRECTION_INVALID = 0, + HB_DIRECTION_LTR = 4, + HB_DIRECTION_RTL, + HB_DIRECTION_TTB, + HB_DIRECTION_BTT +} hb_direction_t; + +/* len=-1 means str is NUL-terminated */ +HB_EXTERN hb_direction_t +hb_direction_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_direction_to_string (hb_direction_t direction); + +#define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) +/* Direction must be valid for the following */ +#define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4) +#define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6) +#define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4) +#define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5) +#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) + + +/* hb_language_t */ + +typedef const struct hb_language_impl_t *hb_language_t; + +HB_EXTERN hb_language_t +hb_language_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_language_to_string (hb_language_t language); + +#define HB_LANGUAGE_INVALID ((hb_language_t) 0) + +HB_EXTERN hb_language_t +hb_language_get_default (void); + + +/* hb_script_t */ + +/* https://unicode.org/iso15924/ */ +/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ +/* Unicode Character Database property: Script (sc) */ +typedef enum +{ + /*1.1*/ HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), + /*1.1*/ HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), + /*5.0*/ HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), + + /*1.1*/ HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), + /*1.1*/ HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), + /*1.1*/ HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), + /*1.1*/ HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), + /*1.1*/ HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), + /*1.1*/ HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), + /*1.1*/ HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), + /*1.1*/ HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), + /*1.1*/ HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), + /*1.1*/ HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), + /*1.1*/ HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), + /*1.1*/ HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), + /*1.1*/ HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), + /*1.1*/ HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), + /*1.1*/ HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), + /*1.1*/ HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), + /*1.1*/ HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), + /*1.1*/ HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), + /*1.1*/ HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), + /*1.1*/ HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), + /*1.1*/ HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), + /*1.1*/ HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), + + /*2.0*/ HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), + + /*3.0*/ HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), + /*3.0*/ HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), + /*3.0*/ HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), + /*3.0*/ HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), + /*3.0*/ HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), + /*3.0*/ HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), + /*3.0*/ HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), + /*3.0*/ HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), + /*3.0*/ HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), + /*3.0*/ HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), + /*3.0*/ HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), + /*3.0*/ HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), + /*3.0*/ HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), + /*3.0*/ HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), + + /*3.1*/ HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), + /*3.1*/ HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), + /*3.1*/ HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), + + /*3.2*/ HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), + /*3.2*/ HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), + /*3.2*/ HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), + /*3.2*/ HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), + + /*4.0*/ HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), + /*4.0*/ HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), + /*4.0*/ HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), + /*4.0*/ HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), + /*4.0*/ HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), + /*4.0*/ HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), + /*4.0*/ HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), + + /*4.1*/ HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), + /*4.1*/ HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), + /*4.1*/ HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), + /*4.1*/ HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), + /*4.1*/ HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), + /*4.1*/ HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), + /*4.1*/ HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), + /*4.1*/ HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), + + /*5.0*/ HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), + /*5.0*/ HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), + /*5.0*/ HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), + /*5.0*/ HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), + /*5.0*/ HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), + + /*5.1*/ HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), + /*5.1*/ HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), + /*5.1*/ HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), + /*5.1*/ HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), + /*5.1*/ HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), + /*5.1*/ HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), + /*5.1*/ HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), + /*5.1*/ HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), + /*5.1*/ HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), + /*5.1*/ HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), + /*5.1*/ HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), + + /*5.2*/ HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), + /*5.2*/ HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), + /*5.2*/ HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), + /*5.2*/ HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), + /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), + /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), + /*5.2*/ HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), + /*5.2*/ HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), + /*5.2*/ HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), + /*5.2*/ HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), + /*5.2*/ HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), + /*5.2*/ HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), + /*5.2*/ HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), + /*5.2*/ HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), + /*5.2*/ HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), + + /*6.0*/ HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), + /*6.0*/ HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), + /*6.0*/ HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), + + /*6.1*/ HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), + /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), + /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), + /*6.1*/ HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), + /*6.1*/ HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), + /*6.1*/ HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), + /*6.1*/ HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), + + /* + * Since: 0.9.30 + */ + /*7.0*/ HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), + /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), + /*7.0*/ HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), + /*7.0*/ HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), + /*7.0*/ HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), + /*7.0*/ HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), + /*7.0*/ HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), + /*7.0*/ HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), + /*7.0*/ HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), + /*7.0*/ HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), + /*7.0*/ HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), + /*7.0*/ HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), + /*7.0*/ HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), + /*7.0*/ HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), + /*7.0*/ HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), + /*7.0*/ HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), + /*7.0*/ HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), + /*7.0*/ HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), + /*7.0*/ HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), + /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), + /*7.0*/ HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), + /*7.0*/ HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), + /*7.0*/ HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), + + /*8.0*/ HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), + /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), + /*8.0*/ HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), + /*8.0*/ HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), + /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), + /*8.0*/ HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), + + /* + * Since 1.3.0 + */ + /*9.0*/ HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), + /*9.0*/ HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), + /*9.0*/ HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), + /*9.0*/ HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), + /*9.0*/ HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), + /*9.0*/ HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), + + /* + * Since 1.6.0 + */ + /*10.0*/HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), + /*10.0*/HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), + /*10.0*/HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), + /*10.0*/HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), + + /* + * Since 1.8.0 + */ + /*11.0*/HB_SCRIPT_DOGRA = HB_TAG ('D','o','g','r'), + /*11.0*/HB_SCRIPT_GUNJALA_GONDI = HB_TAG ('G','o','n','g'), + /*11.0*/HB_SCRIPT_HANIFI_ROHINGYA = HB_TAG ('R','o','h','g'), + /*11.0*/HB_SCRIPT_MAKASAR = HB_TAG ('M','a','k','a'), + /*11.0*/HB_SCRIPT_MEDEFAIDRIN = HB_TAG ('M','e','d','f'), + /*11.0*/HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), + /*11.0*/HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), + + /* + * Since 2.4.0 + */ + /*12.0*/HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), + /*12.0*/HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), + /*12.0*/HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), + /*12.0*/HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), + + /* + * Since 2.6.7 + */ + /*13.0*/HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), + /*13.0*/HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), + /*13.0*/HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), + /*13.0*/HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), + + /* No script set. */ + HB_SCRIPT_INVALID = HB_TAG_NONE, + + /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t + * without risking undefined behavior. We have two, for historical reasons. + * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed + * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well. + * + * See this thread for technicalities: + * + * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + */ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ + +} hb_script_t; + + +/* Script functions */ + +HB_EXTERN hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag); + +HB_EXTERN hb_script_t +hb_script_from_string (const char *str, int len); + +HB_EXTERN hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script); + +HB_EXTERN hb_direction_t +hb_script_get_horizontal_direction (hb_script_t script); + + +/* User data */ + +typedef struct hb_user_data_key_t { + /*< private >*/ + char unused; +} hb_user_data_key_t; + +typedef void (*hb_destroy_func_t) (void *user_data); + + +/* Font features and variations. */ + +/** + * HB_FEATURE_GLOBAL_START + * + * Since: 2.0.0 + */ +#define HB_FEATURE_GLOBAL_START 0 +/** + * HB_FEATURE_GLOBAL_END + * + * Since: 2.0.0 + */ +#define HB_FEATURE_GLOBAL_END ((unsigned int) -1) + +/** + * hb_feature_t: + * @tag: a feature tag + * @value: 0 disables the feature, non-zero (usually 1) enables the feature. + * For features implemented as lookup type 3 (like 'salt') the @value is a one + * based index into the alternates. + * @start: the cluster to start applying this feature setting (inclusive). + * @end: the cluster to end applying this feature setting (exclusive). + * + * The #hb_feature_t is the structure that holds information about requested + * feature application. The feature will be applied with the given value to all + * glyphs which are in clusters between @start (inclusive) and @end (exclusive). + * Setting start to @HB_FEATURE_GLOBAL_START and end to @HB_FEATURE_GLOBAL_END + * specifies that the feature always applies to the entire buffer. + */ +typedef struct hb_feature_t { + hb_tag_t tag; + uint32_t value; + unsigned int start; + unsigned int end; +} hb_feature_t; + +HB_EXTERN hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature); + +HB_EXTERN void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size); + +/** + * hb_variation_t: + * + * Since: 1.4.2 + */ +typedef struct hb_variation_t { + hb_tag_t tag; + float value; +} hb_variation_t; + +HB_EXTERN hb_bool_t +hb_variation_from_string (const char *str, int len, + hb_variation_t *variation); + +HB_EXTERN void +hb_variation_to_string (hb_variation_t *variation, + char *buf, unsigned int size); + +/** + * hb_color_t: + * + * Data type for holding color values. + * + * Since: 2.1.0 + */ +typedef uint32_t hb_color_t; + +#define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a))) + +HB_EXTERN uint8_t +hb_color_get_alpha (hb_color_t color); +#define hb_color_get_alpha(color) ((color) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_red (hb_color_t color); +#define hb_color_get_red(color) (((color) >> 8) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_green (hb_color_t color); +#define hb_color_get_green(color) (((color) >> 16) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_blue (hb_color_t color); +#define hb_color_get_blue(color) (((color) >> 24) & 0xFF) + +HB_END_DECLS + +#endif /* HB_COMMON_H */ diff --git a/thirdparty/harfbuzz/src/hb-config.hh b/thirdparty/harfbuzz/src/hb-config.hh new file mode 100644 index 00000000000..fc8d424bfb0 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-config.hh @@ -0,0 +1,163 @@ +/* + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_CONFIG_HH +#define HB_CONFIG_HH + +#if 0 /* Make test happy. */ +#include "hb.hh" +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#ifdef HB_TINY +#define HB_LEAN +#define HB_MINI +#define HB_NO_MT +#define HB_NO_UCD_UNASSIGNED +#ifndef NDEBUG +#define NDEBUG +#endif +#ifndef __OPTIMIZE_SIZE__ +#define __OPTIMIZE_SIZE__ +#endif +#endif + +#ifdef HB_LEAN +#define HB_DISABLE_DEPRECATED +#define HB_NDEBUG +#define HB_NO_ATEXIT +#define HB_NO_BUFFER_MESSAGE +#define HB_NO_BUFFER_SERIALIZE +#define HB_NO_BITMAP +#define HB_NO_CFF +#define HB_NO_COLOR +#define HB_NO_DRAW +#define HB_NO_ERRNO +#define HB_NO_FACE_COLLECT_UNICODES +#define HB_NO_GETENV +#define HB_NO_HINTING +#define HB_NO_LANGUAGE_PRIVATE_SUBTAG +#define HB_NO_LAYOUT_FEATURE_PARAMS +#define HB_NO_LAYOUT_COLLECT_GLYPHS +#define HB_NO_LAYOUT_UNUSED +#define HB_NO_MATH +#define HB_NO_META +#define HB_NO_METRICS +#define HB_NO_MMAP +#define HB_NO_NAME +#define HB_NO_OPEN +#define HB_NO_SETLOCALE +#define HB_NO_OT_FONT_GLYPH_NAMES +#define HB_NO_OT_SHAPE_FRACTIONS +#define HB_NO_STYLE +#define HB_NO_SUBSET_LAYOUT +#define HB_NO_VAR +#endif + +#ifdef HB_MINI +#define HB_NO_AAT +#define HB_NO_LEGACY +#endif + + +/* Closure of options. */ + +#ifdef HB_DISABLE_DEPRECATED +#define HB_IF_NOT_DEPRECATED(x) +#else +#define HB_IF_NOT_DEPRECATED(x) x +#endif + +#ifdef HB_NO_AAT +#define HB_NO_OT_NAME_LANGUAGE_AAT +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_BITMAP +#define HB_NO_OT_FONT_BITMAP +#endif + +#ifdef HB_NO_CFF +#define HB_NO_OT_FONT_CFF +#define HB_NO_SUBSET_CFF +#endif + +#ifdef HB_NO_GETENV +#define HB_NO_UNISCRIBE_BUG_COMPATIBLE +#endif + +#ifdef HB_NO_LEGACY +#define HB_NO_CMAP_LEGACY_SUBTABLES +#define HB_NO_FALLBACK_SHAPE +#define HB_NO_OT_KERN +#define HB_NO_OT_LAYOUT_BLACKLIST +#define HB_NO_OT_SHAPE_FALLBACK +#endif + +#ifdef HB_NO_NAME +#define HB_NO_OT_NAME_LANGUAGE +#endif + +#ifdef HB_NO_OT +#define HB_NO_OT_FONT +#define HB_NO_OT_LAYOUT +#define HB_NO_OT_TAG +#define HB_NO_OT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS +#endif + +#ifdef NDEBUG +#ifndef HB_NDEBUG +#define HB_NDEBUG +#endif +#endif + +#ifdef __OPTIMIZE_SIZE__ +#ifndef HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE +#endif +#endif + +#ifdef HAVE_CONFIG_OVERRIDE_H +#include "config-override.h" +#endif + + +#endif /* HB_CONFIG_HH */ diff --git a/thirdparty/harfbuzz/src/hb-coretext.cc b/thirdparty/harfbuzz/src/hb-coretext.cc new file mode 100644 index 00000000000..7b6b2bd5ef8 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-coretext.cc @@ -0,0 +1,1194 @@ +/* + * Copyright © 2012,2013 Mozilla Foundation. + * Copyright © 2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_CORETEXT + +#include "hb-shaper-impl.hh" + +#include "hb-coretext.h" +#include "hb-aat-layout.hh" +#include + + +/** + * SECTION:hb-coretext + * @title: hb-coretext + * @short_description: CoreText integration + * @include: hb-coretext.h + * + * Functions for using HarfBuzz with the CoreText fonts. + **/ + +/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ +#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f + +static void +release_table_data (void *user_data) +{ + CFDataRef cf_data = reinterpret_cast (user_data); + CFRelease(cf_data); +} + +static hb_blob_t * +_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + CGFontRef cg_font = reinterpret_cast (user_data); + CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); + if (unlikely (!cf_data)) + return nullptr; + + const char *data = reinterpret_cast (CFDataGetBytePtr (cf_data)); + const size_t length = CFDataGetLength (cf_data); + if (!data || !length) + { + CFRelease (cf_data); + return nullptr; + } + + return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, + reinterpret_cast (const_cast<__CFData *> (cf_data)), + release_table_data); +} + +static void +_hb_cg_font_release (void *data) +{ + CGFontRelease ((CGFontRef) data); +} + + +static CTFontDescriptorRef +get_last_resort_font_desc () +{ + // TODO Handle allocation failures? + CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0); + CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault, + (const void **) &last_resort, + 1, + &kCFTypeArrayCallBacks); + CFRelease (last_resort); + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontCascadeListAttribute, + (const void **) &cascade_list, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (cascade_list); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + return font_desc; +} + +static void +release_data (void *info, const void *data, size_t size) +{ + assert (hb_blob_get_length ((hb_blob_t *) info) == size && + hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); + + hb_blob_destroy ((hb_blob_t *) info); +} + +static CGFontRef +create_cg_font (hb_face_t *face) +{ + CGFontRef cg_font = nullptr; + if (face->destroy == _hb_cg_font_release) + { + cg_font = CGFontRetain ((CGFontRef) face->user_data); + } + else + { + hb_blob_t *blob = hb_face_reference_blob (face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (CORETEXT, face, "Face has empty blob"); + + CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); + if (likely (provider)) + { + cg_font = CGFontCreateWithDataProvider (provider); + if (unlikely (!cg_font)) + DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); + CGDataProviderRelease (provider); + } + } + return cg_font; +} + +static CTFontRef +create_ct_font (CGFontRef cg_font, CGFloat font_size) +{ + CTFontRef ct_font = nullptr; + + /* CoreText does not enable trak table usage / tracking when creating a CTFont + * using CTFontCreateWithGraphicsFont. The only way of enabling tracking seems + * to be through the CTFontCreateUIFontForLanguage call. */ + CFStringRef cg_postscript_name = CGFontCopyPostScriptName (cg_font); + if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) || + CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay"))) + { +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +# define kCTFontUIFontSystem kCTFontSystemFontType +# define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType +#endif + CTFontUIFontType font_type = kCTFontUIFontSystem; + if (CFStringHasSuffix (cg_postscript_name, CFSTR ("-Bold"))) + font_type = kCTFontUIFontEmphasizedSystem; + + ct_font = CTFontCreateUIFontForLanguage (font_type, font_size, nullptr); + CFStringRef ct_result_name = CTFontCopyPostScriptName(ct_font); + if (CFStringCompare (ct_result_name, cg_postscript_name, 0) != kCFCompareEqualTo) + { + CFRelease(ct_font); + ct_font = nullptr; + } + CFRelease (ct_result_name); + } + CFRelease (cg_postscript_name); + + if (!ct_font) + ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, nullptr, nullptr); + + if (unlikely (!ct_font)) { + DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed"); + return nullptr; + } + + /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter + * bug indicate that the cascade list reconfiguration occasionally causes + * crashes in CoreText on OS X 10.9, thus let's skip this step on older + * operating system versions. Except for the emoji font, where _not_ + * reconfiguring the cascade list causes CoreText crashes. For details, see + * crbug.com/549610 */ + // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h + if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) { + CFStringRef fontName = CTFontCopyPostScriptName (ct_font); + bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; + CFRelease (fontName); + if (!isEmojiFont) + return ct_font; + } + + CFURLRef original_url = nullptr; +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + ATSFontRef atsFont; + FSRef fsref; + OSStatus status; + atsFont = CTFontGetPlatformFont (ct_font, NULL); + status = ATSFontGetFileReference (atsFont, &fsref); + if (status == noErr) + original_url = CFURLCreateFromFSRef (NULL, &fsref); +#else + original_url = (CFURLRef) CTFontCopyAttribute (ct_font, kCTFontURLAttribute); +#endif + + /* Create font copy with cascade list that has LastResort first; this speeds up CoreText + * font fallback which we don't need anyway. */ + { + CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc (); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, last_resort_font_desc); + CFRelease (last_resort_font_desc); + if (new_ct_font) + { + /* The CTFontCreateCopyWithAttributes call fails to stay on the same font + * when reconfiguring the cascade list and may switch to a different font + * when there are fonts that go by the same name, since the descriptor is + * just name and size. + * + * Avoid reconfiguring the cascade lists if the new font is outside the + * system locations that we cannot access from the sandboxed renderer + * process in Blink. This can be detected by the new file URL location + * that the newly found font points to. */ + CFURLRef new_url = nullptr; +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + atsFont = CTFontGetPlatformFont (new_ct_font, NULL); + status = ATSFontGetFileReference (atsFont, &fsref); + if (status == noErr) + new_url = CFURLCreateFromFSRef (NULL, &fsref); +#else + new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); +#endif + // Keep reconfigured font if URL cannot be retrieved (seems to be the case + // on Mac OS 10.12 Sierra), speculative fix for crbug.com/625606 + if (!original_url || !new_url || CFEqual (original_url, new_url)) { + CFRelease (ct_font); + ct_font = new_ct_font; + } else { + CFRelease (new_ct_font); + DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); + } + if (new_url) + CFRelease (new_url); + } + else + DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed"); + } + + if (original_url) + CFRelease (original_url); + return ct_font; +} + +hb_coretext_face_data_t * +_hb_coretext_shaper_face_data_create (hb_face_t *face) +{ + CGFontRef cg_font = create_cg_font (face); + + if (unlikely (!cg_font)) + { + DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); + return nullptr; + } + + return (hb_coretext_face_data_t *) cg_font; +} + +void +_hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data) +{ + CFRelease ((CGFontRef) data); +} + +/** + * hb_coretext_face_create: + * @cg_font: The CGFontRef to work upon + * + * Creates an #hb_face_t face object from the specified + * CGFontRef. + * + * Return value: the new #hb_face_t face object + * + * Since: 0.9.10 + */ +hb_face_t * +hb_coretext_face_create (CGFontRef cg_font) +{ + return hb_face_create_for_tables (_hb_cg_reference_table, CGFontRetain (cg_font), _hb_cg_font_release); +} + +/** + * hb_coretext_face_get_cg_font: + * @face: The #hb_face_t to work upon + * + * Fetches the CGFontRef associated with an #hb_face_t + * face object + * + * Return value: the CGFontRef found + * + * Since: 0.9.10 + */ +CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face) +{ + return (CGFontRef) (const void *) face->data.coretext; +} + + +hb_coretext_font_data_t * +_hb_coretext_shaper_font_data_create (hb_font_t *font) +{ + hb_face_t *face = font->face; + const hb_coretext_face_data_t *face_data = face->data.coretext; + if (unlikely (!face_data)) return nullptr; + CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; + + CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem); + CTFontRef ct_font = create_ct_font (cg_font, font_size); + + if (unlikely (!ct_font)) + { + DEBUG_MSG (CORETEXT, font, "CGFont creation failed.."); + return nullptr; + } + + return (hb_coretext_font_data_t *) ct_font; +} + +void +_hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data) +{ + CFRelease ((CTFontRef) data); +} + +static const hb_coretext_font_data_t * +hb_coretext_font_data_sync (hb_font_t *font) +{ +retry: + const hb_coretext_font_data_t *data = font->data.coretext; + if (unlikely (!data)) return nullptr; + + if (fabs (CTFontGetSize ((CTFontRef) data) - (CGFloat) font->ptem) > .5) + { + /* XXX-MT-bug + * Note that evaluating condition above can be dangerous if another thread + * got here first and destructed data. That's, as always, bad use pattern. + * If you modify the font (change font size), other threads must not be + * using it at the same time. However, since this check is delayed to + * when one actually tries to shape something, this is a XXX race condition + * (and the only one we have that I know of) right now. Ie. you modify the + * font size in one thread, then (supposedly safely) try to use it from two + * or more threads and BOOM! I'm not sure how to fix this. We want RCU. + */ + + /* Drop and recreate. */ + /* If someone dropped it in the mean time, throw it away and don't touch it. + * Otherwise, destruct it. */ + if (likely (font->data.coretext.cmpexch (const_cast (data), nullptr))) + _hb_coretext_shaper_font_data_destroy (const_cast (data)); + else + goto retry; + } + return font->data.coretext; +} + +/** + * hb_coretext_font_create: + * @ct_font: The CTFontRef to work upon + * + * Creates an #hb_font_t font object from the specified + * CTFontRef. + * + * Return value: the new #hb_font_t font object + * + * Since: 1.7.2 + **/ +hb_font_t * +hb_coretext_font_create (CTFontRef ct_font) +{ + CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, nullptr); + hb_face_t *face = hb_coretext_face_create (cg_font); + CFRelease (cg_font); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + + if (unlikely (hb_object_is_immutable (font))) + return font; + + hb_font_set_ptem (font, CTFontGetSize (ct_font)); + + /* Let there be dragons here... */ + font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font)); + + return font; +} + +/** + * hb_coretext_face_get_ct_font: + * @font: #hb_font_t to work upon + * + * Fetches the CTFontRef associated with the specified + * #hb_font_t font object. + * + * Return value: the CTFontRef found + * + * Since: 0.9.10 + */ +CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font) +{ + const hb_coretext_font_data_t *data = hb_coretext_font_data_sync (font); + return data ? (CTFontRef) data : nullptr; +} + + +/* + * shaper + */ + +struct feature_record_t { + unsigned int feature; + unsigned int setting; +}; + +struct active_feature_t { + feature_record_t rec; + unsigned int order; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const active_feature_t *a = (const active_feature_t *) pa; + const active_feature_t *b = (const active_feature_t *) pb; + return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : + 0; + } + bool operator== (const active_feature_t *f) { + return cmp (this, f) == 0; + } +}; + +struct feature_event_t { + unsigned int index; + bool start; + active_feature_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const feature_event_t *a = (const feature_event_t *) pa; + const feature_event_t *b = (const feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct range_record_t { + CTFontRef font; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + + +hb_bool_t +_hb_coretext_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; + CTFontRef ct_font = (CTFontRef) hb_coretext_font_data_sync (font); + + CGFloat ct_font_size = CTFontGetSize (ct_font); + CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; + CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; + + /* Attach marks to their bases, to match the 'ot' shaper. + * Adapted from a very old version of hb-ot-shape:hb_form_clusters(). + * Note that this only makes us be closer to the 'ot' shaper, + * but by no means the same. For example, if there's + * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will + * continue pointing to B2 even though B2 was merged into B1's + * cluster... */ + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + { + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint))) + buffer->merge_clusters (i - 1, i + 1); + } + + hb_vector_t feature_records; + hb_vector_t range_records; + + /* + * Set up features. + * (copied + modified from code from hb-uniscribe.cc) + */ + if (num_features) + { + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + active_feature_t feature; + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag); + if (!mapping) + continue; + + feature.rec.feature = mapping->aatFeatureType; + feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; +#else + feature.rec.feature = features[i].tag; + feature.rec.setting = features[i].value; +#endif + feature.order = i; + + feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + active_feature_t feature; + feature.rec.feature = HB_TAG_NONE; + feature.rec.setting = 0; + feature.order = num_features + 1; + + feature_event_t *event = feature_events.push (); + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) + { + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + range_record_t *range = range_records.push (); + + if (active_features.length) + { + CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + /* TODO sort and resolve conflicting features? */ + /* active_features.qsort (); */ + for (unsigned int j = 0; j < active_features.length; j++) + { +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + CFStringRef keys[] = { + kCTFontFeatureTypeIdentifierKey, + kCTFontFeatureSelectorIdentifierKey + }; + CFNumberRef values[] = { + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; +#else + char tag[5] = {HB_UNTAG (active_features[j].rec.feature)}; + CFTypeRef keys[] = { + kCTFontOpenTypeFeatureTag, + kCTFontOpenTypeFeatureValue + }; + CFTypeRef values[] = { + CFStringCreateWithCString (kCFAllocatorDefault, tag, kCFStringEncodingASCII), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; +#endif + static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); + CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) keys, + (const void **) values, + ARRAY_LENGTH (keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++) + CFRelease (values[i]); + + CFArrayAppendValue (features_array, dict); + CFRelease (dict); + + } + + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontFeatureSettingsAttribute, + (const void **) &features_array, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (features_array); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + + range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, font_desc); + CFRelease (font_desc); + } + else + { + range->font = nullptr; + } + + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } else { + active_feature_t *feature = active_features.find (&event->feature); + if (feature) + active_features.remove (feature - active_features.arrayZ); + } + } + } + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + +#define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + if (unlikely (_consumed > scratch_size)) \ + { \ + on_no_room; \ + assert (0); \ + } \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + + ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, ((void)nullptr) /*nothing*/); + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) { + hb_codepoint_t c = buffer->info[i].codepoint; + if (likely (c <= 0xFFFFu)) + pchars[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; + else { + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, ((void)nullptr) /*nothing*/); + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ + ret = false; \ + goto fail; \ + } HB_STMT_END + + bool ret = true; + CFStringRef string_ref = nullptr; + CTLineRef line = nullptr; + + if (false) + { +resize_and_retry: + DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); + /* string_ref uses the scratch-buffer for backing store, and line references + * string_ref (via attr_string). We must release those before resizing buffer. */ + assert (string_ref); + assert (line); + CFRelease (string_ref); + CFRelease (line); + string_ref = nullptr; + line = nullptr; + + /* Get previous start-of-scratch-area, that we use later for readjusting + * our existing scratch arrays. */ + unsigned int old_scratch_used; + hb_buffer_t::scratch_buffer_t *old_scratch; + old_scratch = buffer->get_scratch_buffer (&old_scratch_used); + old_scratch_used = scratch - old_scratch; + + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + FAIL ("Buffer resize failed"); + + /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the + * cleanest way to do without completely restructuring the rest of this shaper. */ + scratch = buffer->get_scratch_buffer (&scratch_size); + pchars = reinterpret_cast (((char *) scratch + ((char *) pchars - (char *) old_scratch))); + log_clusters = reinterpret_cast (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); + scratch += old_scratch_used; + scratch_size -= old_scratch_used; + } + { + string_ref = CFStringCreateWithCharactersNoCopy (nullptr, + pchars, chars_len, + kCFAllocatorNull); + if (unlikely (!string_ref)) + FAIL ("CFStringCreateWithCharactersNoCopy failed"); + + /* Create an attributed string, populate it, and create a line from it, then release attributed string. */ + { + CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault, + chars_len); + if (unlikely (!attr_string)) + FAIL ("CFAttributedStringCreateMutable failed"); + CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + { + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTVerticalFormsAttributeName, kCFBooleanTrue); + } + + if (buffer->props.language) + { +/* What's the iOS equivalent of this check? + * The symbols was introduced in iOS 7.0. + * At any rate, our fallback is safe and works fine. */ +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +# define kCTLanguageAttributeName CFSTR ("NSLanguage") +#endif + CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, + hb_language_to_string (buffer->props.language), + kCFStringEncodingUTF8, + kCFAllocatorNull); + if (unlikely (!lang)) + { + CFRelease (attr_string); + FAIL ("CFStringCreateWithCStringNoCopy failed"); + } + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTLanguageAttributeName, lang); + CFRelease (lang); + } + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTFontAttributeName, ct_font); + + if (num_features && range_records.length) + { + unsigned int start = 0; + range_record_t *last_range = &range_records[0]; + for (unsigned int k = 0; k < chars_len; k++) + { + range_record_t *range = last_range; + while (log_clusters[k] < range->index_first) + range--; + while (log_clusters[k] > range->index_last) + range++; + if (range != last_range) + { + if (last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start), + kCTFontAttributeName, last_range->font); + + start = k; + } + + last_range = range; + } + if (start != chars_len && last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), + kCTFontAttributeName, last_range->font); + } + /* Enable/disable kern if requested. + * + * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText. + */ + if (num_features) + { + unsigned int zeroint = 0; + CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint); + for (unsigned int i = 0; i < num_features; i++) + { + const hb_feature_t &feature = features[i]; + if (feature.tag == HB_TAG('k','e','r','n') && + feature.start < chars_len && feature.start < feature.end) + { + CFRange feature_range = CFRangeMake (feature.start, + hb_min (feature.end, chars_len) - feature.start); + if (feature.value) + CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); + else + CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero); + } + } + CFRelease (zero); + } + + int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; + CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; +#endif + CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, + (const void **) &level_number, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (level_number); + if (unlikely (!options)) + { + CFRelease (attr_string); + FAIL ("CFDictionaryCreate failed"); + } + + CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); + CFRelease (options); + CFRelease (attr_string); + if (unlikely (!typesetter)) + FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed"); + + line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0)); + CFRelease (typesetter); + if (unlikely (!line)) + FAIL ("CTTypesetterCreateLine failed"); + } + + CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); + unsigned int num_runs = CFArrayGetCount (glyph_runs); + DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs); + + buffer->len = 0; + uint32_t status_and = ~0, status_or = 0; + double advances_so_far = 0; + /* For right-to-left runs, CoreText returns the glyphs positioned such that + * any trailing whitespace is to the left of (0,0). Adjust coordinate system + * to fix for that. Test with any RTL string with trailing spaces. + * https://crbug.com/469028 + */ + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + { + advances_so_far -= CTLineGetTrailingWhitespaceWidth (line); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + advances_so_far = -advances_so_far; + } + + const CFRange range_all = CFRangeMake (0, 0); + + for (unsigned int i = 0; i < num_runs; i++) + { + CTRunRef run = static_cast(CFArrayGetValueAtIndex (glyph_runs, i)); + CTRunStatus run_status = CTRunGetStatus (run); + status_or |= run_status; + status_and &= run_status; + DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); + double run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + run_advance = -run_advance; + DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); + + /* CoreText does automatic font fallback (AKA "cascading") for characters + * not supported by the requested font, and provides no way to turn it off, + * so we must detect if the returned run uses a font other than the requested + * one and fill in the buffer with .notdef glyphs instead of random glyph + * indices from a different font. + */ + CFDictionaryRef attributes = CTRunGetAttributes (run); + CTFontRef run_ct_font = static_cast(CFDictionaryGetValue (attributes, kCTFontAttributeName)); + if (!CFEqual (run_ct_font, ct_font)) + { + /* The run doesn't use our main font instance. We have to figure out + * whether font fallback happened, or this is just CoreText giving us + * another CTFont using the same underlying CGFont. CoreText seems + * to do that in a variety of situations, one of which being vertical + * text, but also perhaps for caching reasons. + * + * First, see if it uses any of our subfonts created to set font features... + * + * Next, compare the CGFont to the one we used to create our fonts. + * Even this doesn't work all the time. + * + * Finally, we compare PS names, which I don't think are unique... + * + * Looks like if we really want to be sure here we have to modify the + * font to change the name table, similar to what we do in the uniscribe + * backend. + * + * However, even that wouldn't work if we were passed in the CGFont to + * construct a hb_face to begin with. + * + * See: https://github.com/harfbuzz/harfbuzz/pull/36 + * + * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 + */ + bool matched = false; + for (unsigned int i = 0; i < range_records.length; i++) + if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) + { + matched = true; + break; + } + if (!matched) + { + CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr); + if (run_cg_font) + { + matched = CFEqual (run_cg_font, cg_font); + CFRelease (run_cg_font); + } + } + if (!matched) + { + CFStringRef font_ps_name = CTFontCopyName (ct_font, kCTFontPostScriptNameKey); + CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); + CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); + CFRelease (run_ps_name); + CFRelease (font_ps_name); + if (result == kCFCompareEqualTo) + matched = true; + } + if (!matched) + { + CFRange range = CTRunGetStringRange (run); + DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", + range.location, range.location + range.length); + if (!buffer->ensure_inplace (buffer->len + range.length)) + goto resize_and_retry; + hb_glyph_info_t *info = buffer->info + buffer->len; + + hb_codepoint_t notdef = 0; + hb_direction_t dir = buffer->props.direction; + hb_position_t x_advance, y_advance, x_offset, y_offset; + hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance); + hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset); + hb_position_t advance = x_advance + y_advance; + x_offset = -x_offset; + y_offset = -y_offset; + + unsigned int old_len = buffer->len; + for (CFIndex j = range.location; j < range.location + range.length; j++) + { + UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); + if (hb_in_range (ch, 0xDC00u, 0xDFFFu) && range.location < j) + { + ch = CFStringGetCharacterAtIndex (string_ref, j - 1); + if (hb_in_range (ch, 0xD800u, 0xDBFFu)) + /* This is the second of a surrogate pair. Don't need .notdef + * for this one. */ + continue; + } + if (buffer->unicode->is_default_ignorable (ch)) + continue; + + info->codepoint = notdef; + info->cluster = log_clusters[j]; + + info->mask = advance; + info->var1.i32 = x_offset; + info->var2.i32 = y_offset; + + info++; + buffer->len++; + } + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + buffer->reverse_range (old_len, buffer->len); + advances_so_far += run_advance; + continue; + } + } + + unsigned int num_glyphs = CTRunGetGlyphCount (run); + if (num_glyphs == 0) + continue; + + if (!buffer->ensure_inplace (buffer->len + num_glyphs)) + goto resize_and_retry; + + hb_glyph_info_t *run_info = buffer->info + buffer->len; + + /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always + * succeed, and so copying data to our own buffer will be rare. Reports + * have it that this changed in OS X 10.10 Yosemite, and nullptr is returned + * frequently. At any rate, we can test that codepath by setting USE_PTR + * to false. */ + +#define USE_PTR true + +#define SCRATCH_SAVE() \ + unsigned int scratch_size_saved = scratch_size; \ + hb_buffer_t::scratch_buffer_t *scratch_saved = scratch + +#define SCRATCH_RESTORE() \ + scratch_size = scratch_size_saved; \ + scratch = scratch_saved + + { /* Setup glyphs */ + SCRATCH_SAVE(); + const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr; + if (!glyphs) { + ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); + CTRunGetGlyphs (run, range_all, glyph_buf); + glyphs = glyph_buf; + } + const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : nullptr; + if (!string_indices) { + ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); + CTRunGetStringIndices (run, range_all, index_buf); + string_indices = index_buf; + } + hb_glyph_info_t *info = run_info; + for (unsigned int j = 0; j < num_glyphs; j++) + { + info->codepoint = glyphs[j]; + info->cluster = log_clusters[string_indices[j]]; + info++; + } + SCRATCH_RESTORE(); + } + { + /* Setup positions. + * Note that CoreText does not return advances for glyphs. As such, + * for all but last glyph, we use the delta position to next glyph as + * advance (in the advance direction only), and for last glyph we set + * whatever is needed to make the whole run's advance add up. */ + SCRATCH_SAVE(); + const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr; + if (!positions) { + ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); + CTRunGetPositions (run, range_all, position_buf); + positions = position_buf; + } + hb_glyph_info_t *info = run_info; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult; + for (unsigned int j = 0; j < num_glyphs; j++) + { + double advance; + if (likely (j + 1 < num_glyphs)) + advance = positions[j + 1].x - positions[j].x; + else /* last glyph */ + advance = run_advance - (positions[j].x - positions[0].x); + info->mask = advance * x_mult; + info->var1.i32 = x_offset; + info->var2.i32 = positions[j].y * y_mult; + info++; + } + } + else + { + hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult; + for (unsigned int j = 0; j < num_glyphs; j++) + { + double advance; + if (likely (j + 1 < num_glyphs)) + advance = positions[j + 1].y - positions[j].y; + else /* last glyph */ + advance = run_advance - (positions[j].y - positions[0].y); + info->mask = advance * y_mult; + info->var1.i32 = positions[j].x * x_mult; + info->var2.i32 = y_offset; + info++; + } + } + SCRATCH_RESTORE(); + advances_so_far += run_advance; + } +#undef SCRATCH_RESTORE +#undef SCRATCH_SAVE +#undef USE_PTR +#undef ALLOCATE_ARRAY + + buffer->len += num_glyphs; + } + + /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, + * or if it does, it doesn't respect it. So we get runs with wrong + * directions. As such, disable the assert... It wouldn't crash, but + * cursoring will be off... + * + * https://crbug.com/419769 + */ + if (false) + { + /* Make sure all runs had the expected direction. */ + HB_UNUSED bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + assert (bool (status_and & kCTRunStatusRightToLeft) == backward); + assert (bool (status_or & kCTRunStatusRightToLeft) == backward); + } + + buffer->clear_positions (); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + for (unsigned int i = 0; i < count; i++) + { + pos->x_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + + info++, pos++; + } + else + for (unsigned int i = 0; i < count; i++) + { + pos->y_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + + info++, pos++; + } + + /* Fix up clusters so that we never return out-of-order indices; + * if core text has reordered glyphs, we'll merge them to the + * beginning of the reordered cluster. CoreText is nice enough + * to tell us whenever it has produced nonmonotonic results... + * Note that we assume the input clusters were nonmonotonic to + * begin with. + * + * This does *not* mean we'll form the same clusters as Uniscribe + * or the native OT backend, only that the cluster indices will be + * monotonic in the output buffer. */ + if (count > 1 && (status_or & kCTRunStatusNonMonotonic)) + { + hb_glyph_info_t *info = buffer->info; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) + { + unsigned int cluster = info[count - 1].cluster; + for (unsigned int i = count - 1; i > 0; i--) + { + cluster = hb_min (cluster, info[i - 1].cluster); + info[i - 1].cluster = cluster; + } + } + else + { + unsigned int cluster = info[0].cluster; + for (unsigned int i = 1; i < count; i++) + { + cluster = hb_min (cluster, info[i].cluster); + info[i].cluster = cluster; + } + } + } + } + + buffer->unsafe_to_break_all (); + +#undef FAIL + +fail: + if (string_ref) + CFRelease (string_ref); + if (line) + CFRelease (line); + + for (unsigned int i = 0; i < range_records.length; i++) + if (range_records[i].font) + CFRelease (range_records[i].font); + + return ret; +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-coretext.h b/thirdparty/harfbuzz/src/hb-coretext.h new file mode 100644 index 00000000000..e53dbaf2c7f --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-coretext.h @@ -0,0 +1,96 @@ +/* + * Copyright © 2012 Mozilla Foundation. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Mozilla Author(s): Jonathan Kew + */ + +#ifndef HB_CORETEXT_H +#define HB_CORETEXT_H + +#include "hb.h" + +#include +#if TARGET_OS_IPHONE +# include +# include +#else +# include +#endif + +HB_BEGIN_DECLS + + +/** + * HB_CORETEXT_TAG_MORT: + * + * The #hb_tag_t tag for the `mort` (glyph metamorphosis) table, + * which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + * + **/ +#define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') + +/** + * HB_CORETEXT_TAG_MORX: + * + * The #hb_tag_t tag for the `morx` (extended glyph metamorphosis) + * table, which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * + **/ +#define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') + +/** + * HB_CORETEXT_TAG_KERX: + * + * The #hb_tag_t tag for the `kerx` (extended kerning) table, which + * holds AAT kerning information. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + * + **/ +#define HB_CORETEXT_TAG_KERX HB_TAG('k','e','r','x') + + +HB_EXTERN hb_face_t * +hb_coretext_face_create (CGFontRef cg_font); + +HB_EXTERN hb_font_t * +hb_coretext_font_create (CTFontRef ct_font); + + +HB_EXTERN CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face); + +HB_EXTERN CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_CORETEXT_H */ diff --git a/thirdparty/harfbuzz/src/hb-debug.hh b/thirdparty/harfbuzz/src/hb-debug.hh new file mode 100644 index 00000000000..ec3a1ff2111 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-debug.hh @@ -0,0 +1,459 @@ +/* + * Copyright © 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_DEBUG_HH +#define HB_DEBUG_HH + +#include "hb.hh" +#include "hb-atomic.hh" +#include "hb-algs.hh" + + +#ifndef HB_DEBUG +#define HB_DEBUG 0 +#endif + + +/* + * Global runtime options. + */ + +struct hb_options_t +{ + bool unused : 1; /* In-case sign bit is here. */ + bool initialized : 1; + bool uniscribe_bug_compatible : 1; +}; + +union hb_options_union_t { + int i; + hb_options_t opts; +}; +static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), ""); + +HB_INTERNAL void +_hb_options_init (); + +extern HB_INTERNAL hb_atomic_int_t _hb_options; + +static inline hb_options_t +hb_options () +{ +#ifdef HB_NO_GETENV + return hb_options_t (); +#endif + /* Make a local copy, so we can access bitfield threadsafely. */ + hb_options_union_t u; + u.i = _hb_options.get_relaxed (); + + if (unlikely (!u.i)) + { + _hb_options_init (); + u.i = _hb_options.get_relaxed (); + } + + return u.opts; +} + + +/* + * Debug output (needs enabling at compile time.) + */ + +static inline bool +_hb_debug (unsigned int level, + unsigned int max_level) +{ + return level < max_level; +} + +#define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT)) +#define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0)) + +static inline void +_hb_print_func (const char *func) +{ + if (func) + { + unsigned int func_len = strlen (func); + /* Skip "static" */ + if (0 == strncmp (func, "static ", 7)) + func += 7; + /* Skip "typename" */ + if (0 == strncmp (func, "typename ", 9)) + func += 9; + /* Skip return type */ + const char *space = strchr (func, ' '); + if (space) + func = space + 1; + /* Skip parameter list */ + const char *paren = strchr (func, '('); + if (paren) + func_len = paren - func; + fprintf (stderr, "%.*s", func_len, func); + } +} + +template static inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) HB_PRINTF_FUNC(7, 0); +template static inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) +{ + if (!_hb_debug (level, max_level)) + return; + + fprintf (stderr, "%-10s", what ? what : ""); + + if (obj) + fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj); + else + fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), ""); + + if (indented) { +#define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ +#define VRBAR "\342\224\234" /* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +#define DLBAR "\342\225\256" /* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */ +#define ULBAR "\342\225\257" /* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */ +#define LBAR "\342\225\264" /* U+2574 BOX DRAWINGS LIGHT LEFT */ + static const char bars[] = + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; + fprintf (stderr, "%2u %s" VRBAR "%s", + level, + bars + sizeof (bars) - 1 - hb_min ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), + level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); + } else + fprintf (stderr, " " VRBAR LBAR); + + _hb_print_func (func); + + if (message) + { + fprintf (stderr, ": "); + vfprintf (stderr, message, ap); + } + + fprintf (stderr, "\n"); +} +template <> inline void HB_PRINTF_FUNC(7, 0) +_hb_debug_msg_va<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + va_list ap HB_UNUSED) {} + +template static inline void +_hb_debug_msg (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + ...) HB_PRINTF_FUNC(7, 8); +template static inline void HB_PRINTF_FUNC(7, 8) +_hb_debug_msg (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + ...) +{ + va_list ap; + va_start (ap, message); + _hb_debug_msg_va (what, obj, func, indented, level, level_dir, message, ap); + va_end (ap); +} +template <> inline void +_hb_debug_msg<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + ...) HB_PRINTF_FUNC(7, 8); +template <> inline void HB_PRINTF_FUNC(7, 8) +_hb_debug_msg<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + ...) {} + +#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__) +#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__) +#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__) + + +/* + * Printer + */ + +template +struct hb_printer_t { + const char *print (const T&) { return "something"; } +}; + +template <> +struct hb_printer_t { + const char *print (bool v) { return v ? "true" : "false"; } +}; + +template <> +struct hb_printer_t { + const char *print (hb_empty_t) { return ""; } +}; + + +/* + * Trace + */ + +template +static inline void _hb_warn_no_return (bool returned) +{ + if (unlikely (!returned)) { + fprintf (stderr, "OUCH, returned with no call to return_trace(). This is a bug, please report.\n"); + } +} +template <> +/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) +{} + +template +struct hb_auto_trace_t +{ + explicit inline hb_auto_trace_t (unsigned int *plevel_, + const char *what_, + const void *obj_, + const char *func, + const char *message, + ...) HB_PRINTF_FUNC(6, 7) + : plevel (plevel_), what (what_), obj (obj_), returned (false) + { + if (plevel) ++*plevel; + + va_list ap; + va_start (ap, message); + _hb_debug_msg_va (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap); + va_end (ap); + } + ~hb_auto_trace_t () + { + _hb_warn_no_return (returned); + if (!returned) { + _hb_debug_msg (what, obj, nullptr, true, plevel ? *plevel : 1, -1, " "); + } + if (plevel) --*plevel; + } + + template + T ret (T&& v, + const char *func = "", + unsigned int line = 0) + { + if (unlikely (returned)) { + fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); + return hb_forward (v); + } + + _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1, + "return %s (line %d)", + hb_printer_t().print (v), line); + if (plevel) --*plevel; + plevel = nullptr; + returned = true; + return hb_forward (v); + } + + private: + unsigned int *plevel; + const char *what; + const void *obj; + bool returned; +}; +template /* Make sure we don't use hb_auto_trace_t when not tracing. */ +struct hb_auto_trace_t<0, ret_t> +{ + explicit inline hb_auto_trace_t (unsigned int *plevel_, + const char *what_, + const void *obj_, + const char *func, + const char *message, + ...) HB_PRINTF_FUNC(6, 7) {} + + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return hb_forward (v); } +}; + +/* For disabled tracing; optimize out everything. + * https://github.com/harfbuzz/harfbuzz/pull/605 */ +template +struct hb_no_trace_t { + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return hb_forward (v); } +}; + +#define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__) + + +/* + * Instances. + */ + +#ifndef HB_DEBUG_ARABIC +#define HB_DEBUG_ARABIC (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_BLOB +#define HB_DEBUG_BLOB (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_CORETEXT +#define HB_DEBUG_CORETEXT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_DIRECTWRITE +#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_FT +#define HB_DEBUG_FT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_OBJECT +#define HB_DEBUG_OBJECT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_SHAPE_PLAN +#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_UNISCRIBE +#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0) +#endif + +/* + * With tracing. + */ + +#ifndef HB_DEBUG_APPLY +#define HB_DEBUG_APPLY (HB_DEBUG+0) +#endif +#if HB_DEBUG_APPLY +#define TRACE_APPLY(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "idx %d gid %u lookup %d", \ + c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index) +#else +#define TRACE_APPLY(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_SANITIZE +#define HB_DEBUG_SANITIZE (HB_DEBUG+0) +#endif +#if HB_DEBUG_SANITIZE +#define TRACE_SANITIZE(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_SANITIZE(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_SERIALIZE +#define HB_DEBUG_SERIALIZE (HB_DEBUG+0) +#endif +#if HB_DEBUG_SERIALIZE +#define TRACE_SERIALIZE(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ + " ") +#else +#define TRACE_SERIALIZE(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_SUBSET +#define HB_DEBUG_SUBSET (HB_DEBUG+0) +#endif +#if HB_DEBUG_SUBSET +#define TRACE_SUBSET(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_SUBSET(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_DISPATCH +#define HB_DEBUG_DISPATCH ( \ + HB_DEBUG_APPLY + \ + HB_DEBUG_SANITIZE + \ + HB_DEBUG_SERIALIZE + \ + HB_DEBUG_SUBSET + \ + 0) +#endif +#if HB_DEBUG_DISPATCH +#define TRACE_DISPATCH(this, format) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "format %d", (int) format) +#else +#define TRACE_DISPATCH(this, format) hb_no_trace_t trace +#endif + + +#endif /* HB_DEBUG_HH */ diff --git a/thirdparty/harfbuzz/src/hb-deprecated.h b/thirdparty/harfbuzz/src/hb-deprecated.h new file mode 100644 index 00000000000..43f89a4c4e0 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-deprecated.h @@ -0,0 +1,195 @@ +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_DEPRECATED_H +#define HB_DEPRECATED_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" +#include "hb-set.h" + + +/** + * SECTION:hb-deprecated + * @title: hb-deprecated + * @short_description: Deprecated API + * @include: hb.h + * + * These API have been deprecated in favor of newer API, or because they + * were deemed unnecessary. + **/ + + +HB_BEGIN_DECLS + +#ifndef HB_DISABLE_DEPRECATED + + +#define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS + +#define HB_BUFFER_FLAGS_DEFAULT HB_BUFFER_FLAG_DEFAULT +#define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT HB_BUFFER_SERIALIZE_FLAG_DEFAULT + +typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data); + +HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN HB_DEPRECATED void +hb_set_invert (hb_set_t *set); + +/** + * hb_unicode_eastasian_width_func_t: + * + * Deprecated: 2.0.0 + */ +typedef unsigned int (*hb_unicode_eastasian_width_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_funcs_set_eastasian_width_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED void +hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_eastasian_width_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_eastasian_width: + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED unsigned int +hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + + +/** + * hb_unicode_decompose_compatibility_func_t: + * @ufuncs: a Unicode function structure + * @u: codepoint to decompose + * @decomposed: address of codepoint array (of length %HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into + * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func() + * + * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed. + * The complete length of the decomposition will be returned. + * + * If @u has no compatibility decomposition, zero should be returned. + * + * The Unicode standard guarantees that a buffer of length %HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any + * compatibility decomposition plus an terminating value of 0. Consequently, @decompose must be allocated by the caller to be at least this length. Implementations + * of this function type must ensure that they do not write past the provided array. + * + * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available. + * + * Deprecated: 2.0.0 + */ +typedef unsigned int (*hb_unicode_decompose_compatibility_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed, + void *user_data); + +/** + * HB_UNICODE_MAX_DECOMPOSITION_LEN: + * + * See Unicode 6.1 for details on the maximum decomposition length. + * + * Deprecated: 2.0.0 + */ +#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */ + +/** + * hb_unicode_funcs_set_decompose_compatibility_func: + * @ufuncs: a Unicode function structure + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED void +hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_decompose_compatibility_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN HB_DEPRECATED unsigned int +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed); + + +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; + +/** + * hb_font_funcs_set_glyph_v_kerning_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); + +#endif + +HB_END_DECLS + +#endif /* HB_DEPRECATED_H */ diff --git a/thirdparty/harfbuzz/src/hb-directwrite.cc b/thirdparty/harfbuzz/src/hb-directwrite.cc new file mode 100644 index 00000000000..f2fce073e05 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-directwrite.cc @@ -0,0 +1,979 @@ +/* + * Copyright © 2015-2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifdef HAVE_DIRECTWRITE + +#include "hb-shaper-impl.hh" + +#include + +#include "hb-directwrite.h" + + +/* Declare object creator for dynamic support of DWRITE */ +typedef HRESULT (* WINAPI t_DWriteCreateFactory)( + DWRITE_FACTORY_TYPE factoryType, + REFIID iid, + IUnknown **factory +); + +/* + * hb-directwrite uses new/delete syntatically but as we let users + * to override malloc/free, we will redefine new/delete so users + * won't need to do that by their own. + */ +void* operator new (size_t size) { return malloc (size); } +void* operator new [] (size_t size) { return malloc (size); } +void operator delete (void* pointer) { free (pointer); } +void operator delete [] (void* pointer) { free (pointer); } + + +/* + * DirectWrite font stream helpers + */ + +// This is a font loader which provides only one font (unlike its original design). +// For a better implementation which was also source of this +// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla +class DWriteFontFileLoader : public IDWriteFontFileLoader +{ +private: + IDWriteFontFileStream *mFontFileStream; +public: + DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream) + { mFontFileStream = fontFileStream; } + + // IUnknown interface + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // IDWriteFontFileLoader methods + virtual HRESULT STDMETHODCALLTYPE + CreateStreamFromKey (void const* fontFileReferenceKey, + uint32_t fontFileReferenceKeySize, + OUT IDWriteFontFileStream** fontFileStream) + { + *fontFileStream = mFontFileStream; + return S_OK; + } + + virtual ~DWriteFontFileLoader() {} +}; + +class DWriteFontFileStream : public IDWriteFontFileStream +{ +private: + uint8_t *mData; + uint32_t mSize; +public: + DWriteFontFileStream (uint8_t *aData, uint32_t aSize) + { + mData = aData; + mSize = aSize; + } + + // IUnknown interface + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // IDWriteFontFileStream methods + virtual HRESULT STDMETHODCALLTYPE + ReadFileFragment (void const** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void** fragmentContext) + { + // We are required to do bounds checking. + if (fileOffset + fragmentSize > mSize) return E_FAIL; + + // truncate the 64 bit fileOffset to size_t sized index into mData + size_t index = static_cast (fileOffset); + + // We should be alive for the duration of this. + *fragmentStart = &mData[index]; + *fragmentContext = nullptr; + return S_OK; + } + + virtual void STDMETHODCALLTYPE + ReleaseFileFragment (void* fragmentContext) {} + + virtual HRESULT STDMETHODCALLTYPE + GetFileSize (OUT UINT64* fileSize) + { + *fileSize = mSize; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE + GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; } + + virtual ~DWriteFontFileStream() {} +}; + + +/* +* shaper face data +*/ + +struct hb_directwrite_face_data_t +{ + HMODULE dwrite_dll; + IDWriteFactory *dwriteFactory; + IDWriteFontFile *fontFile; + DWriteFontFileStream *fontFileStream; + DWriteFontFileLoader *fontFileLoader; + IDWriteFontFace *fontFace; + hb_blob_t *faceBlob; +}; + +hb_directwrite_face_data_t * +_hb_directwrite_shaper_face_data_create (hb_face_t *face) +{ + hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t; + if (unlikely (!data)) + return nullptr; + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return nullptr; \ + } HB_STMT_END + + data->dwrite_dll = LoadLibrary (TEXT ("DWRITE")); + if (unlikely (!data->dwrite_dll)) + FAIL ("Cannot find DWrite.DLL"); + + t_DWriteCreateFactory p_DWriteCreateFactory; + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + + p_DWriteCreateFactory = (t_DWriteCreateFactory) + GetProcAddress (data->dwrite_dll, "DWriteCreateFactory"); + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + if (unlikely (!p_DWriteCreateFactory)) + FAIL ("Cannot find DWriteCreateFactory()."); + + HRESULT hr; + + // TODO: factory and fontFileLoader should be cached separately + IDWriteFactory* dwriteFactory; + hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), + (IUnknown**) &dwriteFactory); + + if (unlikely (hr != S_OK)) + FAIL ("Failed to run DWriteCreateFactory()."); + + hb_blob_t *blob = hb_face_reference_blob (face); + DWriteFontFileStream *fontFileStream; + fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr), + hb_blob_get_length (blob)); + + DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream); + dwriteFactory->RegisterFontFileLoader (fontFileLoader); + + IDWriteFontFile *fontFile; + uint64_t fontFileKey = 0; + hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey), + fontFileLoader, &fontFile); + + if (FAILED (hr)) + FAIL ("Failed to load font file from data!"); + + BOOL isSupported; + DWRITE_FONT_FILE_TYPE fileType; + DWRITE_FONT_FACE_TYPE faceType; + uint32_t numberOfFaces; + hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces); + if (FAILED (hr) || !isSupported) + FAIL ("Font file is not supported."); + +#undef FAIL + + IDWriteFontFace *fontFace; + dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0, + DWRITE_FONT_SIMULATIONS_NONE, &fontFace); + + data->dwriteFactory = dwriteFactory; + data->fontFile = fontFile; + data->fontFileStream = fontFileStream; + data->fontFileLoader = fontFileLoader; + data->fontFace = fontFace; + data->faceBlob = blob; + + return data; +} + +void +_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data) +{ + if (data->fontFace) + data->fontFace->Release (); + if (data->fontFile) + data->fontFile->Release (); + if (data->dwriteFactory) + { + if (data->fontFileLoader) + data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader); + data->dwriteFactory->Release (); + } + if (data->fontFileLoader) + delete data->fontFileLoader; + if (data->fontFileStream) + delete data->fontFileStream; + if (data->faceBlob) + hb_blob_destroy (data->faceBlob); + if (data->dwrite_dll) + FreeLibrary (data->dwrite_dll); + if (data) + delete data; +} + + +/* + * shaper font data + */ + +struct hb_directwrite_font_data_t {}; + +hb_directwrite_font_data_t * +_hb_directwrite_shaper_font_data_create (hb_font_t *font) +{ + hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t; + if (unlikely (!data)) + return nullptr; + + return data; +} + +void +_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data) +{ + delete data; +} + + +// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project +// but now is relicensed to MIT for HarfBuzz use +class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink +{ +public: + + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // A single contiguous run of characters containing the same analysis + // results. + struct Run + { + uint32_t mTextStart; // starting text position of this run + uint32_t mTextLength; // number of contiguous code units covered + uint32_t mGlyphStart; // starting glyph in the glyphs array + uint32_t mGlyphCount; // number of glyphs associated with this run + // text + DWRITE_SCRIPT_ANALYSIS mScript; + uint8_t mBidiLevel; + bool mIsSideways; + + bool ContainsTextPosition (uint32_t aTextPosition) const + { + return aTextPosition >= mTextStart && + aTextPosition < mTextStart + mTextLength; + } + + Run *nextRun; + }; + +public: + TextAnalysis (const wchar_t* text, uint32_t textLength, + const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection) + : mTextLength (textLength), mText (text), mLocaleName (localeName), + mReadingDirection (readingDirection), mCurrentRun (nullptr) {} + ~TextAnalysis () + { + // delete runs, except mRunHead which is part of the TextAnalysis object + for (Run *run = mRunHead.nextRun; run;) + { + Run *origRun = run; + run = run->nextRun; + delete origRun; + } + } + + STDMETHODIMP + GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead) + { + // Analyzes the text using the script analyzer and returns + // the result as a series of runs. + + HRESULT hr = S_OK; + + // Initially start out with one result that covers the entire range. + // This result will be subdivided by the analysis processes. + mRunHead.mTextStart = 0; + mRunHead.mTextLength = mTextLength; + mRunHead.mBidiLevel = + (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + mRunHead.nextRun = nullptr; + mCurrentRun = &mRunHead; + + // Call each of the analyzers in sequence, recording their results. + if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this))) + *runHead = &mRunHead; + + return hr; + } + + // IDWriteTextAnalysisSource implementation + + IFACEMETHODIMP + GetTextAtPosition (uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition >= mTextLength) + { + // No text at this position, valid query though. + *textString = nullptr; + *textLength = 0; + } + else + { + *textString = mText + textPosition; + *textLength = mTextLength - textPosition; + } + return S_OK; + } + + IFACEMETHODIMP + GetTextBeforePosition (uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition == 0 || textPosition > mTextLength) + { + // Either there is no text before here (== 0), or this + // is an invalid position. The query is considered valid though. + *textString = nullptr; + *textLength = 0; + } + else + { + *textString = mText; + *textLength = textPosition; + } + return S_OK; + } + + IFACEMETHODIMP_ (DWRITE_READING_DIRECTION) + GetParagraphReadingDirection () { return mReadingDirection; } + + IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength, + wchar_t const** localeName) + { return S_OK; } + + IFACEMETHODIMP + GetNumberSubstitution (uint32_t textPosition, + OUT uint32_t* textLength, + OUT IDWriteNumberSubstitution** numberSubstitution) + { + // We do not support number substitution. + *numberSubstitution = nullptr; + *textLength = mTextLength - textPosition; + + return S_OK; + } + + // IDWriteTextAnalysisSink implementation + + IFACEMETHODIMP + SetScriptAnalysis (uint32_t textPosition, uint32_t textLength, + DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) + { + SetCurrentRun (textPosition); + SplitCurrentRun (textPosition); + while (textLength > 0) + { + Run *run = FetchNextRun (&textLength); + run->mScript = *scriptAnalysis; + } + + return S_OK; + } + + IFACEMETHODIMP + SetLineBreakpoints (uint32_t textPosition, + uint32_t textLength, + const DWRITE_LINE_BREAKPOINT* lineBreakpoints) + { return S_OK; } + + IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength, + uint8_t explicitLevel, uint8_t resolvedLevel) + { return S_OK; } + + IFACEMETHODIMP + SetNumberSubstitution (uint32_t textPosition, uint32_t textLength, + IDWriteNumberSubstitution* numberSubstitution) + { return S_OK; } + +protected: + Run *FetchNextRun (IN OUT uint32_t* textLength) + { + // Used by the sink setters, this returns a reference to the next run. + // Position and length are adjusted to now point after the current run + // being returned. + + Run *origRun = mCurrentRun; + // Split the tail if needed (the length remaining is less than the + // current run's size). + if (*textLength < mCurrentRun->mTextLength) + SplitCurrentRun (mCurrentRun->mTextStart + *textLength); + else + // Just advance the current run. + mCurrentRun = mCurrentRun->nextRun; + *textLength -= origRun->mTextLength; + + // Return a reference to the run that was just current. + return origRun; + } + + void SetCurrentRun (uint32_t textPosition) + { + // Move the current run to the given position. + // Since the analyzers generally return results in a forward manner, + // this will usually just return early. If not, find the + // corresponding run for the text position. + + if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition)) + return; + + for (Run *run = &mRunHead; run; run = run->nextRun) + if (run->ContainsTextPosition (textPosition)) + { + mCurrentRun = run; + return; + } + assert (0); // We should always be able to find the text position in one of our runs + } + + void SplitCurrentRun (uint32_t splitPosition) + { + if (!mCurrentRun) + { + assert (0); // SplitCurrentRun called without current run + // Shouldn't be calling this when no current run is set! + return; + } + // Split the current run. + if (splitPosition <= mCurrentRun->mTextStart) + { + // No need to split, already the start of a run + // or before it. Usually the first. + return; + } + Run *newRun = new Run; + + *newRun = *mCurrentRun; + + // Insert the new run in our linked list. + newRun->nextRun = mCurrentRun->nextRun; + mCurrentRun->nextRun = newRun; + + // Adjust runs' text positions and lengths. + uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart; + newRun->mTextStart += splitPoint; + newRun->mTextLength -= splitPoint; + mCurrentRun->mTextLength = splitPoint; + mCurrentRun = newRun; + } + +protected: + // Input + // (weak references are fine here, since this class is a transient + // stack-based helper that doesn't need to copy data) + uint32_t mTextLength; + const wchar_t* mText; + const wchar_t* mLocaleName; + DWRITE_READING_DIRECTION mReadingDirection; + + // Current processing state. + Run *mCurrentRun; + + // Output is a list of runs starting here + Run mRunHead; +}; + +/* + * shaper + */ + +static hb_bool_t +_hb_directwrite_shape_full (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + float lineWidth) +{ + hb_face_t *face = font->face; + const hb_directwrite_face_data_t *face_data = face->data.directwrite; + IDWriteFactory *dwriteFactory = face_data->dwriteFactory; + IDWriteFontFace *fontFace = face_data->fontFace; + + IDWriteTextAnalyzer* analyzer; + dwriteFactory->CreateTextAnalyzer (&analyzer); + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + +#define utf16_index() var1.u32 + + ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2); + + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + buffer->info[i].utf16_index () = chars_len; + if (likely (c <= 0xFFFFu)) + textString[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + textString[chars_len++] = 0xFFFDu; + else + { + textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (WORD, log_clusters, chars_len); + /* Need log_clusters to assign features. */ + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + + // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES + + DWRITE_READING_DIRECTION readingDirection; + readingDirection = buffer->props.direction ? + DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : + DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; + + /* + * There's an internal 16-bit limit on some things inside the analyzer, + * but we never attempt to shape a word longer than 64K characters + * in a single gfxShapedWord, so we cannot exceed that limit. + */ + uint32_t textLength = buffer->len; + + TextAnalysis analysis (textString, textLength, nullptr, readingDirection); + TextAnalysis::Run *runHead; + HRESULT hr; + hr = analysis.GenerateResults (analyzer, &runHead); + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return false; \ + } HB_STMT_END + + if (FAILED (hr)) + FAIL ("Analyzer failed to generate results."); + + uint32_t maxGlyphCount = 3 * textLength / 2 + 16; + uint32_t glyphCount; + bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + const wchar_t localeName[20] = {0}; + if (buffer->props.language) + mbstowcs ((wchar_t*) localeName, + hb_language_to_string (buffer->props.language), 20); + + // TODO: it does work but doesn't care about ranges + DWRITE_TYPOGRAPHIC_FEATURES typographic_features; + typographic_features.featureCount = num_features; + if (num_features) + { + typographic_features.features = new DWRITE_FONT_FEATURE[num_features]; + for (unsigned int i = 0; i < num_features; ++i) + { + typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG) + hb_uint32_swap (features[i].tag); + typographic_features.features[i].parameter = features[i].value; + } + } + const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures; + dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features; + const uint32_t featureRangeLengths[] = { textLength }; + // + + uint16_t* clusterMap; + clusterMap = new uint16_t[textLength]; + DWRITE_SHAPING_TEXT_PROPERTIES* textProperties; + textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength]; +retry_getglyphs: + uint16_t* glyphIndices = new uint16_t[maxGlyphCount]; + DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties; + glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount]; + + hr = analyzer->GetGlyphs (textString, textLength, fontFace, false, + isRightToLeft, &runHead->mScript, localeName, + nullptr, &dwFeatures, featureRangeLengths, 1, + maxGlyphCount, clusterMap, textProperties, + glyphIndices, glyphProperties, &glyphCount); + + if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) + { + delete [] glyphIndices; + delete [] glyphProperties; + + maxGlyphCount *= 2; + + goto retry_getglyphs; + } + if (FAILED (hr)) + FAIL ("Analyzer failed to get glyphs."); + + float* glyphAdvances = new float[maxGlyphCount]; + DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount]; + + /* The -2 in the following is to compensate for possible + * alignment needed after the WORD array. sizeof (WORD) == 2. */ + unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) + / (sizeof (WORD) + + sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) + + sizeof (int) + + sizeof (DWRITE_GLYPH_OFFSET) + + sizeof (uint32_t)); + ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); + +#undef ALLOCATE_ARRAY + + int fontEmSize = font->face->get_upem (); + if (fontEmSize < 0) fontEmSize = -fontEmSize; + + if (fontEmSize < 0) fontEmSize = -fontEmSize; + double x_mult = (double) font->x_scale / fontEmSize; + double y_mult = (double) font->y_scale / fontEmSize; + + hr = analyzer->GetGlyphPlacements (textString, clusterMap, textProperties, + textLength, glyphIndices, glyphProperties, + glyphCount, fontFace, fontEmSize, + false, isRightToLeft, &runHead->mScript, localeName, + &dwFeatures, featureRangeLengths, 1, + glyphAdvances, glyphOffsets); + + if (FAILED (hr)) + FAIL ("Analyzer failed to get glyph placements."); + + IDWriteTextAnalyzer1* analyzer1; + analyzer->QueryInterface (&analyzer1); + + if (analyzer1 && lineWidth) + { + DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities = + new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount]; + hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript, + textLength, glyphCount, textString, + clusterMap, glyphProperties, + justificationOpportunities); + + if (FAILED (hr)) + FAIL ("Analyzer failed to get justification opportunities."); + + float* justifiedGlyphAdvances = new float[maxGlyphCount]; + DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount]; + hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities, + glyphAdvances, glyphOffsets, justifiedGlyphAdvances, + justifiedGlyphOffsets); + + if (FAILED (hr)) FAIL ("Analyzer failed to get justify glyph advances."); + + DWRITE_SCRIPT_PROPERTIES scriptProperties; + hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties); + if (FAILED (hr)) FAIL ("Analyzer failed to get script properties."); + uint32_t justificationCharacter = scriptProperties.justificationCharacter; + + // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs + if (justificationCharacter != 32) + { + uint16_t* modifiedClusterMap = new uint16_t[textLength]; + retry_getjustifiedglyphs: + uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount]; + float* modifiedGlyphAdvances = new float[maxGlyphCount]; + DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount]; + uint32_t actualGlyphsCount; + hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript, + textLength, glyphCount, maxGlyphCount, + clusterMap, glyphIndices, glyphAdvances, + justifiedGlyphAdvances, justifiedGlyphOffsets, + glyphProperties, &actualGlyphsCount, + modifiedClusterMap, modifiedGlyphIndices, + modifiedGlyphAdvances, modifiedGlyphOffsets); + + if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)) + { + maxGlyphCount = actualGlyphsCount; + delete [] modifiedGlyphIndices; + delete [] modifiedGlyphAdvances; + delete [] modifiedGlyphOffsets; + + maxGlyphCount = actualGlyphsCount; + + goto retry_getjustifiedglyphs; + } + if (FAILED (hr)) + FAIL ("Analyzer failed to get justified glyphs."); + + delete [] clusterMap; + delete [] glyphIndices; + delete [] glyphAdvances; + delete [] glyphOffsets; + + glyphCount = actualGlyphsCount; + clusterMap = modifiedClusterMap; + glyphIndices = modifiedGlyphIndices; + glyphAdvances = modifiedGlyphAdvances; + glyphOffsets = modifiedGlyphOffsets; + + delete [] justifiedGlyphAdvances; + delete [] justifiedGlyphOffsets; + } + else + { + delete [] glyphAdvances; + delete [] glyphOffsets; + + glyphAdvances = justifiedGlyphAdvances; + glyphOffsets = justifiedGlyphOffsets; + } + + delete [] justificationOpportunities; + } + + /* Ok, we've got everything we need, now compose output buffer, + * very, *very*, carefully! */ + + /* Calculate visual-clusters. That's what we ship. */ + for (unsigned int i = 0; i < glyphCount; i++) + vis_clusters[i] = (uint32_t) -1; + for (unsigned int i = 0; i < buffer->len; i++) + { + uint32_t *p = + &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]]; + *p = hb_min (*p, buffer->info[i].cluster); + } + for (unsigned int i = 1; i < glyphCount; i++) + if (vis_clusters[i] == (uint32_t) -1) + vis_clusters[i] = vis_clusters[i - 1]; + +#undef utf16_index + + if (unlikely (!buffer->ensure (glyphCount))) + FAIL ("Buffer in error"); + +#undef FAIL + + /* Set glyph infos */ + buffer->len = 0; + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[buffer->len++]; + + info->codepoint = glyphIndices[i]; + info->cluster = vis_clusters[i]; + + /* The rest is crap. Let's store position info there for now. */ + info->mask = glyphAdvances[i]; + info->var1.i32 = glyphOffsets[i].advanceOffset; + info->var2.i32 = glyphOffsets[i].ascenderOffset; + } + + /* Set glyph positions */ + buffer->clear_positions (); + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[i]; + hb_glyph_position_t *pos = &buffer->pos[i]; + + /* TODO vertical */ + pos->x_advance = x_mult * (int32_t) info->mask; + pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; + } + + if (isRightToLeft) hb_buffer_reverse (buffer); + + delete [] clusterMap; + delete [] glyphIndices; + delete [] textProperties; + delete [] glyphProperties; + delete [] glyphAdvances; + delete [] glyphOffsets; + + if (num_features) + delete [] typographic_features.features; + + /* Wow, done! */ + return true; +} + +hb_bool_t +_hb_directwrite_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + return _hb_directwrite_shape_full (shape_plan, font, buffer, + features, num_features, 0); +} + +HB_UNUSED static bool +_hb_directwrite_shape_experimental_width (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + float width) +{ + static const char *shapers = "directwrite"; + hb_shape_plan_t *shape_plan; + shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, + features, num_features, &shapers); + hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer, + features, num_features, width); + + buffer->unsafe_to_break_all (); + + return res; +} + +struct _hb_directwrite_font_table_context { + IDWriteFontFace *face; + void *table_context; +}; + +static void +_hb_directwrite_table_data_release (void *data) +{ + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data; + context->face->ReleaseFontTable (context->table_context); + delete context; +} + +static hb_blob_t * +_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data); + const void *data; + uint32_t length; + void *table_context; + BOOL exists; + if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data, + &length, &table_context, &exists))) + return nullptr; + + if (!data || !exists || !length) + { + dw_face->ReleaseFontTable (table_context); + return nullptr; + } + + _hb_directwrite_font_table_context *context = new _hb_directwrite_font_table_context; + context->face = dw_face; + context->table_context = table_context; + + return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY, + context, _hb_directwrite_table_data_release); +} + +static void +_hb_directwrite_font_release (void *data) +{ + if (data) + ((IDWriteFontFace *) data)->Release (); +} + +/** + * hb_directwrite_face_create: + * @font_face: a DirectWrite IDWriteFontFace object. + * + * Return value: #hb_face_t object corresponding to the given input + * + * Since: 2.4.0 + **/ +hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face) +{ + if (font_face) + font_face->AddRef (); + return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face, + _hb_directwrite_font_release); +} + +/** +* hb_directwrite_face_get_font_face: +* @face: a #hb_face_t object +* +* Return value: DirectWrite IDWriteFontFace object corresponding to the given input +* +* Since: 2.5.0 +**/ +IDWriteFontFace * +hb_directwrite_face_get_font_face (hb_face_t *face) +{ + return face->data.directwrite->fontFace; +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-directwrite.h b/thirdparty/harfbuzz/src/hb-directwrite.h new file mode 100644 index 00000000000..f837627a286 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-directwrite.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2015-2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_DIRECTWRITE_H +#define HB_DIRECTWRITE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face); + +HB_EXTERN IDWriteFontFace * +hb_directwrite_face_get_font_face (hb_face_t *face); + +HB_END_DECLS + +#endif /* HB_DIRECTWRITE_H */ diff --git a/thirdparty/harfbuzz/src/hb-dispatch.hh b/thirdparty/harfbuzz/src/hb-dispatch.hh new file mode 100644 index 00000000000..7eace86e546 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-dispatch.hh @@ -0,0 +1,61 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_DISPATCH_HH +#define HB_DISPATCH_HH + +#include "hb.hh" + +/* + * Dispatch + */ + +template +struct hb_dispatch_context_t +{ + hb_dispatch_context_t () : debug_depth (0) {} + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const Context* thiz () const { return static_cast (this); } + Context* thiz () { return static_cast< Context *> (this); } + public: + const char *get_name () { return "UNKNOWN"; } + static constexpr unsigned max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } + template + return_t dispatch (const T &obj, Ts&&... ds) + { return obj.dispatch (thiz (), hb_forward (ds)...); } + static return_t no_dispatch_return_value () { return Context::default_return_value (); } + static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } + unsigned debug_depth; +}; + + +#endif /* HB_DISPATCH_HH */ diff --git a/thirdparty/harfbuzz/src/hb-draw.cc b/thirdparty/harfbuzz/src/hb-draw.cc new file mode 100644 index 00000000000..1a5f9c8c6bd --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-draw.cc @@ -0,0 +1,261 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_DRAW +#ifdef HB_EXPERIMENTAL_API + +#include "hb-draw.hh" +#include "hb-ot.h" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" + +/** + * hb_draw_funcs_set_move_to_func: + * @funcs: draw functions object + * @move_to: move-to callback + * + * Sets move-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs, + hb_draw_move_to_func_t move_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->move_to = move_to; +} + +/** + * hb_draw_funcs_set_line_to_func: + * @funcs: draw functions object + * @line_to: line-to callback + * + * Sets line-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs, + hb_draw_line_to_func_t line_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->line_to = line_to; +} + +/** + * hb_draw_funcs_set_quadratic_to_func: + * @funcs: draw functions object + * @move_to: quadratic-to callback + * + * Sets quadratic-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs, + hb_draw_quadratic_to_func_t quadratic_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->quadratic_to = quadratic_to; + funcs->is_quadratic_to_set = true; +} + +/** + * hb_draw_funcs_set_cubic_to_func: + * @funcs: draw functions + * @cubic_to: cubic-to callback + * + * Sets cubic-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs, + hb_draw_cubic_to_func_t cubic_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->cubic_to = cubic_to; +} + +/** + * hb_draw_funcs_set_close_path_func: + * @funcs: draw functions object + * @close_path: close-path callback + * + * Sets close-path callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs, + hb_draw_close_path_func_t close_path) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->close_path = close_path; +} + +static void +_move_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} + +static void +_line_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} + +static void +_quadratic_to_nil (hb_position_t control_x HB_UNUSED, hb_position_t control_y HB_UNUSED, + hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +_cubic_to_nil (hb_position_t control1_x HB_UNUSED, hb_position_t control1_y HB_UNUSED, + hb_position_t control2_x HB_UNUSED, hb_position_t control2_y HB_UNUSED, + hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +_close_path_nil (void *user_data HB_UNUSED) {} + +/** + * hb_draw_funcs_create: + * + * Creates a new draw callbacks object. + * + * Since: EXPERIMENTAL + **/ +hb_draw_funcs_t * +hb_draw_funcs_create () +{ + hb_draw_funcs_t *funcs; + if (unlikely (!(funcs = hb_object_create ()))) + return const_cast (&Null (hb_draw_funcs_t)); + + funcs->move_to = (hb_draw_move_to_func_t) _move_to_nil; + funcs->line_to = (hb_draw_line_to_func_t) _line_to_nil; + funcs->quadratic_to = (hb_draw_quadratic_to_func_t) _quadratic_to_nil; + funcs->is_quadratic_to_set = false; + funcs->cubic_to = (hb_draw_cubic_to_func_t) _cubic_to_nil; + funcs->close_path = (hb_draw_close_path_func_t) _close_path_nil; + return funcs; +} + +/** + * hb_draw_funcs_reference: + * @funcs: draw functions + * + * Add to callbacks object refcount. + * + * Returns: The same object. + * Since: EXPERIMENTAL + **/ +hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *funcs) +{ + return hb_object_reference (funcs); +} + +/** + * hb_draw_funcs_destroy: + * @funcs: draw functions + * + * Decreases refcount of callbacks object and deletes the object if it reaches + * to zero. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_destroy (hb_draw_funcs_t *funcs) +{ + if (!hb_object_destroy (funcs)) return; + + free (funcs); +} + +/** + * hb_draw_funcs_make_immutable: + * @funcs: draw functions + * + * Makes funcs object immutable. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs) +{ + if (hb_object_is_immutable (funcs)) + return; + + hb_object_make_immutable (funcs); +} + +/** + * hb_draw_funcs_is_immutable: + * @funcs: draw functions + * + * Checks whether funcs is immutable. + * + * Returns: If is immutable. + * Since: EXPERIMENTAL + **/ +hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs) +{ + return hb_object_is_immutable (funcs); +} + +/** + * hb_font_draw_glyph: + * @font: a font object + * @glyph: a glyph id + * @funcs: draw callbacks object + * @user_data: parameter you like be passed to the callbacks when are called + * + * Draw a glyph. + * + * Returns: Whether the font had the glyph and the operation completed successfully. + * Since: EXPERIMENTAL + **/ +hb_bool_t +hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph, + const hb_draw_funcs_t *funcs, + void *user_data) +{ + if (unlikely (funcs == &Null (hb_draw_funcs_t) || + glyph >= font->face->get_num_glyphs ())) + return false; + + draw_helper_t draw_helper (funcs, user_data); + if (font->face->table.glyf->get_path (font, glyph, draw_helper)) return true; +#ifndef HB_NO_CFF + if (font->face->table.cff1->get_path (font, glyph, draw_helper)) return true; + if (font->face->table.cff2->get_path (font, glyph, draw_helper)) return true; +#endif + + return false; +} + +#endif +#endif diff --git a/thirdparty/harfbuzz/src/hb-draw.h b/thirdparty/harfbuzz/src/hb-draw.h new file mode 100644 index 00000000000..98eccf4c0c0 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-draw.h @@ -0,0 +1,98 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_DRAW_H +#define HB_DRAW_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#ifdef HB_EXPERIMENTAL_API +typedef void (*hb_draw_move_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data); +typedef void (*hb_draw_line_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data); +typedef void (*hb_draw_quadratic_to_func_t) (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y, + void *user_data); +typedef void (*hb_draw_cubic_to_func_t) (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y, + void *user_data); +typedef void (*hb_draw_close_path_func_t) (void *user_data); + +/** + * hb_draw_funcs_t: + * + * Glyph draw callbacks. + * + * _move_to, _line_to and _cubic_to calls are nessecary to be defined but we + * translate _quadratic_to calls to _cubic_to if the callback isn't defined. + * + * Since: EXPERIMENTAL + **/ +typedef struct hb_draw_funcs_t hb_draw_funcs_t; + +HB_EXTERN void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs, + hb_draw_move_to_func_t move_to); + +HB_EXTERN void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs, + hb_draw_line_to_func_t line_to); + +HB_EXTERN void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs, + hb_draw_quadratic_to_func_t quadratic_to); + +HB_EXTERN void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs, + hb_draw_cubic_to_func_t cubic_to); + +HB_EXTERN void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs, + hb_draw_close_path_func_t close_path); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_create (void); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *funcs); + +HB_EXTERN void +hb_draw_funcs_destroy (hb_draw_funcs_t *funcs); + +HB_EXTERN void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs); + +HB_EXTERN hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs); +#endif + +HB_END_DECLS + +#endif /* HB_DRAW_H */ diff --git a/thirdparty/harfbuzz/src/hb-draw.hh b/thirdparty/harfbuzz/src/hb-draw.hh new file mode 100644 index 00000000000..2aa0a5b4db4 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-draw.hh @@ -0,0 +1,139 @@ +/* + * Copyright © 2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_DRAW_HH +#define HB_DRAW_HH + +#include "hb.hh" + +#ifdef HB_EXPERIMENTAL_API +struct hb_draw_funcs_t +{ + hb_object_header_t header; + + hb_draw_move_to_func_t move_to; + hb_draw_line_to_func_t line_to; + hb_draw_quadratic_to_func_t quadratic_to; + bool is_quadratic_to_set; + hb_draw_cubic_to_func_t cubic_to; + hb_draw_close_path_func_t close_path; +}; + +struct draw_helper_t +{ + draw_helper_t (const hb_draw_funcs_t *funcs_, void *user_data_) + { + funcs = funcs_; + user_data = user_data_; + path_open = false; + path_start_x = current_x = path_start_y = current_y = 0; + } + ~draw_helper_t () { end_path (); } + + void move_to (hb_position_t x, hb_position_t y) + { + if (path_open) end_path (); + current_x = path_start_x = x; + current_y = path_start_y = y; + } + + void line_to (hb_position_t x, hb_position_t y) + { + if (equal_to_current (x, y)) return; + if (!path_open) start_path (); + funcs->line_to (x, y, user_data); + current_x = x; + current_y = y; + } + + void + quadratic_to (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y) + { + if (equal_to_current (control_x, control_y) && equal_to_current (to_x, to_y)) + return; + if (!path_open) start_path (); + if (funcs->is_quadratic_to_set) + funcs->quadratic_to (control_x, control_y, to_x, to_y, user_data); + else + funcs->cubic_to (roundf ((current_x + 2.f * control_x) / 3.f), + roundf ((current_y + 2.f * control_y) / 3.f), + roundf ((to_x + 2.f * control_x) / 3.f), + roundf ((to_y + 2.f * control_y) / 3.f), + to_x, to_y, user_data); + current_x = to_x; + current_y = to_y; + } + + void + cubic_to (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y) + { + if (equal_to_current (control1_x, control1_y) && + equal_to_current (control2_x, control2_y) && + equal_to_current (to_x, to_y)) + return; + if (!path_open) start_path (); + funcs->cubic_to (control1_x, control1_y, control2_x, control2_y, to_x, to_y, user_data); + current_x = to_x; + current_y = to_y; + } + + void end_path () + { + if (path_open) + { + if ((path_start_x != current_x) || (path_start_y != current_y)) + funcs->line_to (path_start_x, path_start_y, user_data); + funcs->close_path (user_data); + } + path_open = false; + path_start_x = current_x = path_start_y = current_y = 0; + } + + protected: + bool equal_to_current (hb_position_t x, hb_position_t y) + { return current_x == x && current_y == y; } + + void start_path () + { + if (path_open) end_path (); + path_open = true; + funcs->move_to (path_start_x, path_start_y, user_data); + } + + hb_position_t path_start_x; + hb_position_t path_start_y; + + hb_position_t current_x; + hb_position_t current_y; + + bool path_open; + const hb_draw_funcs_t *funcs; + void *user_data; +}; +#endif + +#endif /* HB_DRAW_HH */ diff --git a/thirdparty/harfbuzz/src/hb-face.cc b/thirdparty/harfbuzz/src/hb-face.cc new file mode 100644 index 00000000000..7bde50df5ba --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-face.cc @@ -0,0 +1,733 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-blob.hh" +#include "hb-open-file.hh" +#include "hb-ot-face.hh" +#include "hb-ot-cmap-table.hh" + + +/** + * SECTION:hb-face + * @title: hb-face + * @short_description: Font face objects + * @include: hb.h + * + * Font face is objects represent a single face in a font family. + * More exactly, a font face represents a single face in a binary font file. + * Font faces are typically built from a binary blob and a face index. + * Font faces are used to create fonts. + **/ + + +/** + * hb_face_count: + * @blob: a blob. + * + * Get number of faces in a blob. + * + * Return value: Number of faces in @blob + * + * Since: 1.7.7 + **/ +unsigned int +hb_face_count (hb_blob_t *blob) +{ + if (unlikely (!blob)) + return 0; + + /* TODO We shouldn't be sanitizing blob. Port to run sanitizer and return if not sane. */ + /* Make API signature const after. */ + hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + const OT::OpenTypeFontFile& ot = *sanitized->as (); + unsigned int ret = ot.get_face_count (); + hb_blob_destroy (sanitized); + + return ret; +} + +/* + * hb_face_t + */ + +DEFINE_NULL_INSTANCE (hb_face_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, /* reference_table_func */ + nullptr, /* user_data */ + nullptr, /* destroy */ + + 0, /* index */ + HB_ATOMIC_INT_INIT (1000), /* upem */ + HB_ATOMIC_INT_INIT (0), /* num_glyphs */ + + /* Zero for the rest is fine. */ +}; + + +/** + * hb_face_create_for_tables: + * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!reference_table_func || !(face = hb_object_create ())) { + if (destroy) + destroy (user_data); + return hb_face_get_empty (); + } + + face->reference_table_func = reference_table_func; + face->user_data = user_data; + face->destroy = destroy; + + face->num_glyphs.set_relaxed (-1); + + face->data.init0 (face); + face->table.init0 (face); + + return face; +} + + +typedef struct hb_face_for_data_closure_t { + hb_blob_t *blob; + unsigned int index; +} hb_face_for_data_closure_t; + +static hb_face_for_data_closure_t * +_hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) +{ + hb_face_for_data_closure_t *closure; + + closure = (hb_face_for_data_closure_t *) calloc (1, sizeof (hb_face_for_data_closure_t)); + if (unlikely (!closure)) + return nullptr; + + closure->blob = blob; + closure->index = index; + + return closure; +} + +static void +_hb_face_for_data_closure_destroy (void *data) +{ + hb_face_for_data_closure_t *closure = (hb_face_for_data_closure_t *) data; + + hb_blob_destroy (closure->blob); + free (closure); +} + +static hb_blob_t * +_hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data; + + if (tag == HB_TAG_NONE) + return hb_blob_reference (data->blob); + + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); + unsigned int base_offset; + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index, &base_offset); + + const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag); + + hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, base_offset + table.offset, table.length); + + return blob; +} + +/** + * hb_face_create: (Xconstructor) + * @blob: + * @index: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index) +{ + hb_face_t *face; + + if (unlikely (!blob)) + blob = hb_blob_get_empty (); + + blob = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (blob, index); + + if (unlikely (!closure)) + { + hb_blob_destroy (blob); + return hb_face_get_empty (); + } + + face = hb_face_create_for_tables (_hb_face_for_data_reference_table, + closure, + _hb_face_for_data_closure_destroy); + + face->index = index; + + return face; +} + +/** + * hb_face_get_empty: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_get_empty () +{ + return const_cast (&Null (hb_face_t)); +} + + +/** + * hb_face_reference: (skip) + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_reference (hb_face_t *face) +{ + return hb_object_reference (face); +} + +/** + * hb_face_destroy: (skip) + * @face: a face. + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_destroy (hb_face_t *face) +{ + if (!hb_object_destroy (face)) return; + + for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) + { + hb_face_t::plan_node_t *next = node->next; + hb_shape_plan_destroy (node->shape_plan); + free (node); + node = next; + } + + face->data.fini (); + face->table.fini (); + + if (face->destroy) + face->destroy (face->user_data); + + free (face); +} + +/** + * hb_face_set_user_data: (skip) + * @face: a face. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_set_user_data (hb_face_t *face, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (face, key, data, destroy, replace); +} + +/** + * hb_face_get_user_data: (skip) + * @face: a face. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_face_get_user_data (const hb_face_t *face, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (face, key); +} + +/** + * hb_face_make_immutable: + * @face: a face. + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_make_immutable (hb_face_t *face) +{ + if (hb_object_is_immutable (face)) + return; + + hb_object_make_immutable (face); +} + +/** + * hb_face_is_immutable: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_is_immutable (const hb_face_t *face) +{ + return hb_object_is_immutable (face); +} + + +/** + * hb_face_reference_table: + * @face: a face. + * @tag: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_table (const hb_face_t *face, + hb_tag_t tag) +{ + if (unlikely (tag == HB_TAG_NONE)) + return hb_blob_get_empty (); + + return face->reference_table (tag); +} + +/** + * hb_face_reference_blob: + * @face: a face. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_blob (hb_face_t *face) +{ + return face->reference_table (HB_TAG_NONE); +} + +/** + * hb_face_set_index: + * @face: a face. + * @index: + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_set_index (hb_face_t *face, + unsigned int index) +{ + if (hb_object_is_immutable (face)) + return; + + face->index = index; +} + +/** + * hb_face_get_index: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_index (const hb_face_t *face) +{ + return face->index; +} + +/** + * hb_face_set_upem: + * @face: a face. + * @upem: + * + * + * + * Since: 0.9.2 + **/ +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem) +{ + if (hb_object_is_immutable (face)) + return; + + face->upem.set_relaxed (upem); +} + +/** + * hb_face_get_upem: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_upem (const hb_face_t *face) +{ + return face->get_upem (); +} + +/** + * hb_face_set_glyph_count: + * @face: a face. + * @glyph_count: + * + * + * + * Since: 0.9.7 + **/ +void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count) +{ + if (hb_object_is_immutable (face)) + return; + + face->num_glyphs.set_relaxed (glyph_count); +} + +/** + * hb_face_get_glyph_count: + * @face: a face. + * + * + * + * Return value: + * + * Since: 0.9.7 + **/ +unsigned int +hb_face_get_glyph_count (const hb_face_t *face) +{ + return face->get_num_glyphs (); +} + +/** + * hb_face_get_table_tags: + * @face: a face. + * @start_offset: index of first tag to return. + * @table_count: input length of @table_tags array, output number of items written. + * @table_tags: array to write tags into. + * + * Retrieves table tags for a face, if possible. + * + * Return value: total number of tables, or 0 if not possible to list. + * + * Since: 1.6.0 + **/ +unsigned int +hb_face_get_table_tags (const hb_face_t *face, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) +{ + if (face->destroy != (hb_destroy_func_t) _hb_face_for_data_closure_destroy) + { + if (table_count) + *table_count = 0; + return 0; + } + + hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data; + + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); + + return ot_face.get_table_tags (start_offset, table_count, table_tags); +} + + +/* + * Character set. + */ + + +#ifndef HB_NO_FACE_COLLECT_UNICODES +/** + * hb_face_collect_unicodes: + * @face: font face. + * @out: set to add Unicode characters covered by @face to. + * + * Since: 1.9.0 + */ +void +hb_face_collect_unicodes (hb_face_t *face, + hb_set_t *out) +{ + face->table.cmap->collect_unicodes (out, face->get_num_glyphs ()); +} +/** + * hb_face_collect_variation_selectors: + * @face: font face. + * @out: set to add Variation Selector characters covered by @face to. + * + * + * + * Since: 1.9.0 + */ +void +hb_face_collect_variation_selectors (hb_face_t *face, + hb_set_t *out) +{ + face->table.cmap->collect_variation_selectors (out); +} +/** + * hb_face_collect_variation_unicodes: + * @face: font face. + * @out: set to add Unicode characters for @variation_selector covered by @face to. + * + * + * + * Since: 1.9.0 + */ +void +hb_face_collect_variation_unicodes (hb_face_t *face, + hb_codepoint_t variation_selector, + hb_set_t *out) +{ + face->table.cmap->collect_variation_unicodes (variation_selector, out); +} +#endif + + +/* + * face-builder: A face that has add_table(). + */ + +struct hb_face_builder_data_t +{ + struct table_entry_t + { + int cmp (hb_tag_t t) const + { + if (t < tag) return -1; + if (t > tag) return -1; + return 0; + } + + hb_tag_t tag; + hb_blob_t *blob; + }; + + hb_vector_t tables; +}; + +static hb_face_builder_data_t * +_hb_face_builder_data_create () +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) calloc (1, sizeof (hb_face_builder_data_t)); + if (unlikely (!data)) + return nullptr; + + data->tables.init (); + + return data; +} + +static void +_hb_face_builder_data_destroy (void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + for (unsigned int i = 0; i < data->tables.length; i++) + hb_blob_destroy (data->tables[i].blob); + + data->tables.fini (); + + free (data); +} + +static hb_blob_t * +_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data) +{ + + unsigned int table_count = data->tables.length; + unsigned int face_length = table_count * 16 + 12; + + for (unsigned int i = 0; i < table_count; i++) + face_length += hb_ceil_to_4 (hb_blob_get_length (data->tables[i].blob)); + + char *buf = (char *) malloc (face_length); + if (unlikely (!buf)) + return nullptr; + + hb_serialize_context_t c (buf, face_length); + c.propagate_error (data->tables); + OT::OpenTypeFontFile *f = c.start_serialize (); + + bool is_cff = data->tables.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2')); + hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag; + + bool ret = f->serialize_single (&c, sfnt_tag, data->tables.as_array ()); + + c.end_serialize (); + + if (unlikely (!ret)) + { + free (buf); + return nullptr; + } + + return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, free); +} + +static hb_blob_t * +_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + if (!tag) + return _hb_face_builder_data_reference_blob (data); + + hb_face_builder_data_t::table_entry_t *entry = data->tables.lsearch (tag); + if (entry) + return hb_blob_reference (entry->blob); + + return nullptr; +} + + +/** + * hb_face_builder_create: + * + * Creates a #hb_face_t that can be used with hb_face_builder_add_table(). + * After tables are added to the face, it can be compiled to a binary + * font file by calling hb_face_reference_blob(). + * + * Return value: (transfer full): New face. + * + * Since: 1.9.0 + **/ +hb_face_t * +hb_face_builder_create () +{ + hb_face_builder_data_t *data = _hb_face_builder_data_create (); + if (unlikely (!data)) return hb_face_get_empty (); + + return hb_face_create_for_tables (_hb_face_builder_reference_table, + data, + _hb_face_builder_data_destroy); +} + +/** + * hb_face_builder_add_table: + * + * Add table for @tag with data provided by @blob to the face. @face must + * be created using hb_face_builder_create(). + * + * Since: 1.9.0 + **/ +hb_bool_t +hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) +{ + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return false; + + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + hb_face_builder_data_t::table_entry_t *entry = data->tables.push (); + if (data->tables.in_error()) + return false; + + entry->tag = tag; + entry->blob = hb_blob_reference (blob); + + return true; +} diff --git a/thirdparty/harfbuzz/src/hb-face.h b/thirdparty/harfbuzz/src/hb-face.h new file mode 100644 index 00000000000..e8ff090d55f --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-face.h @@ -0,0 +1,158 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_FACE_H +#define HB_FACE_H + +#include "hb-common.h" +#include "hb-blob.h" +#include "hb-set.h" + +HB_BEGIN_DECLS + + +HB_EXTERN unsigned int +hb_face_count (hb_blob_t *blob); + + +/* + * hb_face_t + */ + +typedef struct hb_face_t hb_face_t; + +HB_EXTERN hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index); + +typedef hb_blob_t * (*hb_reference_table_func_t) (hb_face_t *face, hb_tag_t tag, void *user_data); + +/* calls destroy() when not needing user_data anymore */ +HB_EXTERN hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_face_t * +hb_face_get_empty (void); + +HB_EXTERN hb_face_t * +hb_face_reference (hb_face_t *face); + +HB_EXTERN void +hb_face_destroy (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_face_set_user_data (hb_face_t *face, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_face_get_user_data (const hb_face_t *face, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_face_make_immutable (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_face_is_immutable (const hb_face_t *face); + + +HB_EXTERN hb_blob_t * +hb_face_reference_table (const hb_face_t *face, + hb_tag_t tag); + +HB_EXTERN hb_blob_t * +hb_face_reference_blob (hb_face_t *face); + +HB_EXTERN void +hb_face_set_index (hb_face_t *face, + unsigned int index); + +HB_EXTERN unsigned int +hb_face_get_index (const hb_face_t *face); + +HB_EXTERN void +hb_face_set_upem (hb_face_t *face, + unsigned int upem); + +HB_EXTERN unsigned int +hb_face_get_upem (const hb_face_t *face); + +HB_EXTERN void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count); + +HB_EXTERN unsigned int +hb_face_get_glyph_count (const hb_face_t *face); + +HB_EXTERN unsigned int +hb_face_get_table_tags (const hb_face_t *face, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */); + + +/* + * Character set. + */ + +HB_EXTERN void +hb_face_collect_unicodes (hb_face_t *face, + hb_set_t *out); + +HB_EXTERN void +hb_face_collect_variation_selectors (hb_face_t *face, + hb_set_t *out); + +HB_EXTERN void +hb_face_collect_variation_unicodes (hb_face_t *face, + hb_codepoint_t variation_selector, + hb_set_t *out); + + +/* + * Builder face. + */ + +HB_EXTERN hb_face_t * +hb_face_builder_create (void); + +HB_EXTERN hb_bool_t +hb_face_builder_add_table (hb_face_t *face, + hb_tag_t tag, + hb_blob_t *blob); + + +HB_END_DECLS + +#endif /* HB_FACE_H */ diff --git a/thirdparty/harfbuzz/src/hb-face.hh b/thirdparty/harfbuzz/src/hb-face.hh new file mode 100644 index 00000000000..f1b472ccf39 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-face.hh @@ -0,0 +1,109 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FACE_HH +#define HB_FACE_HH + +#include "hb.hh" + +#include "hb-shaper.hh" +#include "hb-shape-plan.hh" +#include "hb-ot-face.hh" + + +/* + * hb_face_t + */ + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_face_t +{ + hb_object_header_t header; + + hb_reference_table_func_t reference_table_func; + void *user_data; + hb_destroy_func_t destroy; + + unsigned int index; /* Face index in a collection, zero-based. */ + mutable hb_atomic_int_t upem; /* Units-per-EM. */ + mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */ + + hb_shaper_object_dataset_t data;/* Various shaper data. */ + hb_ot_face_t table; /* All the face's tables. */ + + /* Cache */ + struct plan_node_t + { + hb_shape_plan_t *shape_plan; + plan_node_t *next; + }; + hb_atomic_ptr_t shape_plans; + + hb_blob_t *reference_table (hb_tag_t tag) const + { + hb_blob_t *blob; + + if (unlikely (!reference_table_func)) + return hb_blob_get_empty (); + + blob = reference_table_func (/*XXX*/const_cast (this), tag, user_data); + if (unlikely (!blob)) + return hb_blob_get_empty (); + + return blob; + } + + HB_PURE_FUNC unsigned int get_upem () const + { + unsigned int ret = upem.get_relaxed (); + if (unlikely (!ret)) + { + return load_upem (); + } + return ret; + } + + unsigned int get_num_glyphs () const + { + unsigned int ret = num_glyphs.get_relaxed (); + if (unlikely (ret == UINT_MAX)) + return load_num_glyphs (); + return ret; + } + + private: + HB_INTERNAL unsigned int load_upem () const; + HB_INTERNAL unsigned int load_num_glyphs () const; +}; +DECLARE_NULL_INSTANCE (hb_face_t); + + +#endif /* HB_FACE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-fallback-shape.cc b/thirdparty/harfbuzz/src/hb-fallback-shape.cc new file mode 100644 index 00000000000..c5b7c2c2307 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-fallback-shape.cc @@ -0,0 +1,125 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-shaper-impl.hh" + +#ifndef HB_NO_FALLBACK_SHAPE + +/* + * shaper face data + */ + +struct hb_fallback_face_data_t {}; + +hb_fallback_face_data_t * +_hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED) +{ + return (hb_fallback_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_face_data_destroy (hb_fallback_face_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper font data + */ + +struct hb_fallback_font_data_t {}; + +hb_fallback_font_data_t * +_hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_fallback_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_font_data_destroy (hb_fallback_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +hb_bool_t +_hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features HB_UNUSED, + unsigned int num_features HB_UNUSED) +{ + /* TODO + * + * - Apply fallback kern. + * - Handle Variation Selectors? + * - Apply normalization? + * + * This will make the fallback shaper into a dumb "TrueType" + * shaper which many people unfortunately still request. + */ + + hb_codepoint_t space; + bool has_space = (bool) font->get_nominal_glyph (' ', &space); + + buffer->clear_positions (); + + hb_direction_t direction = buffer->props.direction; + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int i = 0; i < count; i++) + { + if (has_space && unicode->is_default_ignorable (info[i].codepoint)) { + info[i].codepoint = space; + pos[i].x_advance = 0; + pos[i].y_advance = 0; + continue; + } + (void) font->get_nominal_glyph (info[i].codepoint, &info[i].codepoint); + font->get_glyph_advance_for_direction (info[i].codepoint, + direction, + &pos[i].x_advance, + &pos[i].y_advance); + font->subtract_glyph_origin_for_direction (info[i].codepoint, + direction, + &pos[i].x_offset, + &pos[i].y_offset); + } + + if (HB_DIRECTION_IS_BACKWARD (direction)) + hb_buffer_reverse (buffer); + + buffer->safe_to_break_all (); + + return true; +} + +#endif diff --git a/thirdparty/harfbuzz/src/hb-font.cc b/thirdparty/harfbuzz/src/hb-font.cc new file mode 100644 index 00000000000..27959487dc9 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-font.cc @@ -0,0 +1,2186 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-font.hh" +#include "hb-machinery.hh" + +#include "hb-ot.h" + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" + + +/** + * SECTION:hb-font + * @title: hb-font + * @short_description: Font objects + * @include: hb.h + * + * Font objects represent a font face at a certain size and other + * parameters (pixels per EM, points per EM, variation settings.) + * Fonts are created from font faces, and are used as input to + * hb_shape() among other things. + **/ + + +/* + * hb_font_funcs_t + */ + +static hb_bool_t +hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) +{ + memset (extents, 0, sizeof (*extents)); + return false; +} +static hb_bool_t +hb_font_get_font_h_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_font_h_extents (extents); + if (ret) { + extents->ascender = font->parent_scale_y_distance (extents->ascender); + extents->descender = font->parent_scale_y_distance (extents->descender); + extents->line_gap = font->parent_scale_y_distance (extents->line_gap); + } + return ret; +} + +static hb_bool_t +hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) +{ + memset (extents, 0, sizeof (*extents)); + return false; +} +static hb_bool_t +hb_font_get_font_v_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_font_v_extents (extents); + if (ret) { + extents->ascender = font->parent_scale_x_distance (extents->ascender); + extents->descender = font->parent_scale_x_distance (extents->descender); + extents->line_gap = font->parent_scale_x_distance (extents->line_gap); + } + return ret; +} + +static hb_bool_t +hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} +static hb_bool_t +hb_font_get_nominal_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + if (font->has_nominal_glyphs_func_set ()) + { + return font->get_nominal_glyphs (1, &unicode, 0, glyph, 0); + } + return font->parent->get_nominal_glyph (unicode, glyph); +} + +#define hb_font_get_nominal_glyphs_nil hb_font_get_nominal_glyphs_default +static unsigned int +hb_font_get_nominal_glyphs_default (hb_font_t *font, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + if (font->has_nominal_glyph_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + if (!font->get_nominal_glyph (*first_unicode, first_glyph)) + return i; + + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + return count; + } + + return font->parent->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + +static hb_bool_t +hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + hb_codepoint_t variation_selector HB_UNUSED, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} +static hb_bool_t +hb_font_get_variation_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + return font->parent->get_variation_glyph (unicode, variation_selector, glyph); +} + + +static hb_position_t +hb_font_get_glyph_h_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + return font->x_scale; +} +static hb_position_t +hb_font_get_glyph_h_advance_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_h_advances_func_set ()) + { + hb_position_t ret; + font->get_glyph_h_advances (1, &glyph, 0, &ret, 0); + return ret; + } + return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); +} + +static hb_position_t +hb_font_get_glyph_v_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* TODO use font_extents.ascender+descender */ + return font->y_scale; +} +static hb_position_t +hb_font_get_glyph_v_advance_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_v_advances_func_set ()) + { + hb_position_t ret; + font->get_glyph_v_advances (1, &glyph, 0, &ret, 0); + return ret; + } + return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); +} + +#define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default +static void +hb_font_get_glyph_h_advances_default (hb_font_t* font, + void* font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_h_advance_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->get_glyph_h_advance (*first_glyph); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + + font->parent->get_glyph_h_advances (count, + first_glyph, glyph_stride, + first_advance, advance_stride); + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->parent_scale_x_distance (*first_advance); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +#define hb_font_get_glyph_v_advances_nil hb_font_get_glyph_v_advances_default +static void +hb_font_get_glyph_v_advances_default (hb_font_t* font, + void* font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_v_advance_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->get_glyph_v_advance (*first_glyph); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + + font->parent->get_glyph_v_advances (count, + first_glyph, glyph_stride, + first_advance, advance_stride); + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->parent_scale_y_distance (*first_advance); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +static hb_bool_t +hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + *x = *y = 0; + return true; +} +static hb_bool_t +hb_font_get_glyph_h_origin_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +static hb_bool_t +hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + *x = *y = 0; + return false; +} +static hb_bool_t +hb_font_get_glyph_v_origin_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +static hb_position_t +hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph HB_UNUSED, + hb_codepoint_t right_glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 0; +} +static hb_position_t +hb_font_get_glyph_h_kerning_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); +} + +#ifndef HB_DISABLE_DEPRECATED +static hb_position_t +hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph HB_UNUSED, + hb_codepoint_t bottom_glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 0; +} +static hb_position_t +hb_font_get_glyph_v_kerning_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph, + void *user_data HB_UNUSED) +{ + return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); +} +#endif + +static hb_bool_t +hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + memset (extents, 0, sizeof (*extents)); + return false; +} +static hb_bool_t +hb_font_get_glyph_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents); + if (ret) { + font->parent_scale_position (&extents->x_bearing, &extents->y_bearing); + font->parent_scale_distance (&extents->width, &extents->height); + } + return ret; +} + +static hb_bool_t +hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + unsigned int point_index HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + *x = *y = 0; + return false; +} +static hb_bool_t +hb_font_get_glyph_contour_point_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +static hb_bool_t +hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + if (size) *name = '\0'; + return false; +} +static hb_bool_t +hb_font_get_glyph_name_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + return font->parent->get_glyph_name (glyph, name, size); +} + +static hb_bool_t +hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + const char *name HB_UNUSED, + int len HB_UNUSED, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} +static hb_bool_t +hb_font_get_glyph_from_name_default (hb_font_t *font, + void *font_data HB_UNUSED, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + return font->parent->get_glyph_from_name (name, len, glyph); +} + +DEFINE_NULL_INSTANCE (hb_font_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + { +#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + }, + { +#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + }, + { + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + } +}; + +static const hb_font_funcs_t _hb_font_funcs_default = { + HB_OBJECT_HEADER_STATIC, + + { +#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + }, + { +#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + }, + { + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + } +}; + + +/** + * hb_font_funcs_create: (Xconstructor) + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_create () +{ + hb_font_funcs_t *ffuncs; + + if (!(ffuncs = hb_object_create ())) + return hb_font_funcs_get_empty (); + + ffuncs->get = _hb_font_funcs_default.get; + + return ffuncs; +} + +/** + * hb_font_funcs_get_empty: + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_get_empty () +{ + return const_cast (&_hb_font_funcs_default); +} + +/** + * hb_font_funcs_reference: (skip) + * @ffuncs: font functions. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs) +{ + return hb_object_reference (ffuncs); +} + +/** + * hb_font_funcs_destroy: (skip) + * @ffuncs: font functions. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) +{ + if (!hb_object_destroy (ffuncs)) return; + +#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy.name) \ + ffuncs->destroy.name (ffuncs->user_data.name); + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + free (ffuncs); +} + +/** + * hb_font_funcs_set_user_data: (skip) + * @ffuncs: font functions. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (ffuncs, key, data, destroy, replace); +} + +/** + * hb_font_funcs_get_user_data: (skip) + * @ffuncs: font functions. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (ffuncs, key); +} + + +/** + * hb_font_funcs_make_immutable: + * @ffuncs: font functions. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) +{ + if (hb_object_is_immutable (ffuncs)) + return; + + hb_object_make_immutable (ffuncs); +} + +/** + * hb_font_funcs_is_immutable: + * @ffuncs: font functions. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) +{ + return hb_object_is_immutable (ffuncs); +} + + +#define HB_FONT_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ + hb_font_get_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (hb_object_is_immutable (ffuncs)) \ + { \ + if (destroy) \ + destroy (user_data); \ + return; \ + } \ + \ + if (ffuncs->destroy.name) \ + ffuncs->destroy.name (ffuncs->user_data.name); \ + \ + if (func) { \ + ffuncs->get.f.name = func; \ + ffuncs->user_data.name = user_data; \ + ffuncs->destroy.name = destroy; \ + } else { \ + ffuncs->get.f.name = hb_font_get_##name##_default; \ + ffuncs->user_data.name = nullptr; \ + ffuncs->destroy.name = nullptr; \ + } \ +} + +HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + +bool +hb_font_t::has_func_set (unsigned int i) +{ + return this->klass->get.array[i] != _hb_font_funcs_default.get.array[i]; +} + +bool +hb_font_t::has_func (unsigned int i) +{ + return has_func_set (i) || + (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i)); +} + +/* Public getters */ + +/** + * hb_font_get_h_extents: + * @font: a font. + * @extents: (out): + * + * + * + * Return value: + * + * Since: 1.1.3 + **/ +hb_bool_t +hb_font_get_h_extents (hb_font_t *font, + hb_font_extents_t *extents) +{ + return font->get_font_h_extents (extents); +} + +/** + * hb_font_get_v_extents: + * @font: a font. + * @extents: (out): + * + * + * + * Return value: + * + * Since: 1.1.3 + **/ +hb_bool_t +hb_font_get_v_extents (hb_font_t *font, + hb_font_extents_t *extents) +{ + return font->get_font_v_extents (extents); +} + +/** + * hb_font_get_glyph: + * @font: a font. + * @unicode: + * @variation_selector: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) +{ + if (unlikely (variation_selector)) + return font->get_variation_glyph (unicode, variation_selector, glyph); + return font->get_nominal_glyph (unicode, glyph); +} + +/** + * hb_font_get_nominal_glyph: + * @font: a font. + * @unicode: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 1.2.3 + **/ +hb_bool_t +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t *glyph) +{ + return font->get_nominal_glyph (unicode, glyph); +} + +/** + * hb_font_get_nominal_glyphs: + * @font: a font. + * + * + * + * Return value: + * + * Since: 2.6.3 + **/ +unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) +{ + return font->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + +/** + * hb_font_get_variation_glyph: + * @font: a font. + * @unicode: + * @variation_selector: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 1.2.3 + **/ +hb_bool_t +hb_font_get_variation_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) +{ + return font->get_variation_glyph (unicode, variation_selector, glyph); +} + +/** + * hb_font_get_glyph_h_advance: + * @font: a font. + * @glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->get_glyph_h_advance (glyph); +} + +/** + * hb_font_get_glyph_v_advance: + * @font: a font. + * @glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->get_glyph_v_advance (glyph); +} + +/** + * hb_font_get_glyph_h_advances: + * @font: a font. + * + * + * + * Since: 1.8.6 + **/ +void +hb_font_get_glyph_h_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); +} +/** + * hb_font_get_glyph_v_advances: + * @font: a font. + * + * + * + * Since: 1.8.6 + **/ +void +hb_font_get_glyph_v_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); +} + +/** + * hb_font_get_glyph_h_origin: + * @font: a font. + * @glyph: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_h_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_v_origin: + * @font: a font. + * @glyph: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_v_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_h_kerning: + * @font: a font. + * @left_glyph: + * @right_glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) +{ + return font->get_glyph_h_kerning (left_glyph, right_glyph); +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_font_get_glyph_v_kerning: + * @font: a font. + * @top_glyph: + * @bottom_glyph: + * + * + * + * Return value: + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) +{ + return font->get_glyph_v_kerning (top_glyph, bottom_glyph); +} +#endif + +/** + * hb_font_get_glyph_extents: + * @font: a font. + * @glyph: + * @extents: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) +{ + return font->get_glyph_extents (glyph, extents); +} + +/** + * hb_font_get_glyph_contour_point: + * @font: a font. + * @glyph: + * @point_index: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_contour_point (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_contour_point (glyph, point_index, x, y); +} + +/** + * hb_font_get_glyph_name: + * @font: a font. + * @glyph: + * @name: (array length=size): + * @size: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size) +{ + return font->get_glyph_name (glyph, name, size); +} + +/** + * hb_font_get_glyph_from_name: + * @font: a font. + * @name: (array length=len): + * @len: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) +{ + return font->get_glyph_from_name (name, len, glyph); +} + + +/* A bit higher-level, and with fallback */ + +/** + * hb_font_get_extents_for_direction: + * @font: a font. + * @direction: + * @extents: (out): + * + * + * + * Since: 1.1.3 + **/ +void +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, + hb_font_extents_t *extents) +{ + return font->get_extents_for_direction (direction, extents); +} +/** + * hb_font_get_glyph_advance_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_advance_for_direction (glyph, direction, x, y); +} +/** + * hb_font_get_glyph_advances_for_direction: + * @font: a font. + * @direction: + * + * + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_get_glyph_advances_for_direction (hb_font_t* font, + hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride); +} + +/** + * hb_font_get_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_add_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->add_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_subtract_glyph_origin_for_direction: + * @font: a font. + * @glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->subtract_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_kerning_for_direction: + * @font: a font. + * @first_glyph: + * @second_glyph: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_extents_for_origin: + * @font: a font. + * @glyph: + * @direction: + * @extents: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) +{ + return font->get_glyph_extents_for_origin (glyph, direction, extents); +} + +/** + * hb_font_get_glyph_contour_point_for_origin: + * @font: a font. + * @glyph: + * @point_index: + * @direction: + * @x: (out): + * @y: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) +{ + return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y); +} + +/* Generates gidDDD if glyph has no name. */ +/** + * hb_font_glyph_to_string: + * @font: a font. + * @glyph: + * @s: (array length=size): + * @size: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size) +{ + font->glyph_to_string (glyph, s, size); +} + +/* Parses gidDDD and uniUUUU strings automatically. */ +/** + * hb_font_glyph_from_string: + * @font: a font. + * @s: (array length=len) (element-type uint8_t): + * @len: + * @glyph: (out): + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) +{ + return font->glyph_from_string (s, len, glyph); +} + + +/* + * hb_font_t + */ + +DEFINE_NULL_INSTANCE (hb_font_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, /* parent */ + const_cast (&_hb_Null_hb_face_t), + + 1000, /* x_scale */ + 1000, /* y_scale */ + 1<<16, /* x_mult */ + 1<<16, /* y_mult */ + + 0, /* x_ppem */ + 0, /* y_ppem */ + 0, /* ptem */ + + 0, /* num_coords */ + nullptr, /* coords */ + nullptr, /* design_coords */ + + const_cast (&_hb_Null_hb_font_funcs_t), + + /* Zero for the rest is fine. */ +}; + + +static hb_font_t * +_hb_font_create (hb_face_t *face) +{ + hb_font_t *font; + + if (unlikely (!face)) + face = hb_face_get_empty (); + if (!(font = hb_object_create ())) + return hb_font_get_empty (); + + hb_face_make_immutable (face); + font->parent = hb_font_get_empty (); + font->face = hb_face_reference (face); + font->klass = hb_font_funcs_get_empty (); + font->data.init0 (font); + font->x_scale = font->y_scale = hb_face_get_upem (face); + font->x_mult = font->y_mult = 1 << 16; + + return font; +} + +/** + * hb_font_create: (Xconstructor) + * @face: a face. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create (hb_face_t *face) +{ + hb_font_t *font = _hb_font_create (face); + +#ifndef HB_NO_OT_FONT + /* Install our in-house, very lightweight, funcs. */ + hb_ot_font_set_funcs (font); +#endif + + return font; +} + +static void +_hb_font_adopt_var_coords (hb_font_t *font, + int *coords, /* 2.14 normalized */ + float *design_coords, + unsigned int coords_length) +{ + free (font->coords); + free (font->design_coords); + + font->coords = coords; + font->design_coords = design_coords; + font->num_coords = coords_length; +} + +/** + * hb_font_create_sub_font: + * @parent: parent font. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create_sub_font (hb_font_t *parent) +{ + if (unlikely (!parent)) + parent = hb_font_get_empty (); + + hb_font_t *font = _hb_font_create (parent->face); + + if (unlikely (hb_object_is_immutable (font))) + return font; + + font->parent = hb_font_reference (parent); + + font->x_scale = parent->x_scale; + font->y_scale = parent->y_scale; + font->mults_changed (); + font->x_ppem = parent->x_ppem; + font->y_ppem = parent->y_ppem; + font->ptem = parent->ptem; + + unsigned int num_coords = parent->num_coords; + if (num_coords) + { + int *coords = (int *) calloc (num_coords, sizeof (parent->coords[0])); + float *design_coords = (float *) calloc (num_coords, sizeof (parent->design_coords[0])); + if (likely (coords && design_coords)) + { + memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); + } + else + { + free (coords); + free (design_coords); + } + } + + return font; +} + +/** + * hb_font_get_empty: + * + * + * + * Return value: (transfer full) + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_empty () +{ + return const_cast (&Null (hb_font_t)); +} + +/** + * hb_font_reference: (skip) + * @font: a font. + * + * + * + * Return value: (transfer full): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_reference (hb_font_t *font) +{ + return hb_object_reference (font); +} + +/** + * hb_font_destroy: (skip) + * @font: a font. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_destroy (hb_font_t *font) +{ + if (!hb_object_destroy (font)) return; + + font->data.fini (); + + if (font->destroy) + font->destroy (font->user_data); + + hb_font_destroy (font->parent); + hb_face_destroy (font->face); + hb_font_funcs_destroy (font->klass); + + free (font->coords); + free (font->design_coords); + + free (font); +} + +/** + * hb_font_set_user_data: (skip) + * @font: a font. + * @key: + * @data: + * @destroy: + * @replace: + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_set_user_data (hb_font_t *font, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (font, key, data, destroy, replace); +} + +/** + * hb_font_get_user_data: (skip) + * @font: a font. + * @key: + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +void * +hb_font_get_user_data (hb_font_t *font, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (font, key); +} + +/** + * hb_font_make_immutable: + * @font: a font. + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_make_immutable (hb_font_t *font) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->parent) + hb_font_make_immutable (font->parent); + + hb_object_make_immutable (font); +} + +/** + * hb_font_is_immutable: + * @font: a font. + * + * + * + * Return value: + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_is_immutable (hb_font_t *font) +{ + return hb_object_is_immutable (font); +} + +/** + * hb_font_set_parent: + * @font: a font. + * @parent: new parent. + * + * Sets parent font of @font. + * + * Since: 1.0.5 + **/ +void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent) +{ + if (hb_object_is_immutable (font)) + return; + + if (!parent) + parent = hb_font_get_empty (); + + hb_font_t *old = font->parent; + + font->parent = hb_font_reference (parent); + + hb_font_destroy (old); +} + +/** + * hb_font_get_parent: + * @font: a font. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_parent (hb_font_t *font) +{ + return font->parent; +} + +/** + * hb_font_set_face: + * @font: a font. + * @face: new face. + * + * Sets font-face of @font. + * + * Since: 1.4.3 + **/ +void +hb_font_set_face (hb_font_t *font, + hb_face_t *face) +{ + if (hb_object_is_immutable (font)) + return; + + if (unlikely (!face)) + face = hb_face_get_empty (); + + hb_face_t *old = font->face; + + hb_face_make_immutable (face); + font->face = hb_face_reference (face); + font->mults_changed (); + + hb_face_destroy (old); +} + +/** + * hb_font_get_face: + * @font: a font. + * + * + * + * Return value: (transfer none): + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_font_get_face (hb_font_t *font) +{ + return font->face; +} + + +/** + * hb_font_set_funcs: + * @font: a font. + * @klass: (closure font_data) (destroy destroy) (scope notified): + * @font_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy) +{ + if (hb_object_is_immutable (font)) + { + if (destroy) + destroy (font_data); + return; + } + + if (font->destroy) + font->destroy (font->user_data); + + if (!klass) + klass = hb_font_funcs_get_empty (); + + hb_font_funcs_reference (klass); + hb_font_funcs_destroy (font->klass); + font->klass = klass; + font->user_data = font_data; + font->destroy = destroy; +} + +/** + * hb_font_set_funcs_data: + * @font: a font. + * @font_data: (destroy destroy) (scope notified): + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy) +{ + /* Destroy user_data? */ + if (hb_object_is_immutable (font)) + { + if (destroy) + destroy (font_data); + return; + } + + if (font->destroy) + font->destroy (font->user_data); + + font->user_data = font_data; + font->destroy = destroy; +} + + +/** + * hb_font_set_scale: + * @font: a font. + * @x_scale: + * @y_scale: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale) +{ + if (hb_object_is_immutable (font)) + return; + + font->x_scale = x_scale; + font->y_scale = y_scale; + font->mults_changed (); +} + +/** + * hb_font_get_scale: + * @font: a font. + * @x_scale: (out): + * @y_scale: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale) +{ + if (x_scale) *x_scale = font->x_scale; + if (y_scale) *y_scale = font->y_scale; +} + +/** + * hb_font_set_ppem: + * @font: a font. + * @x_ppem: + * @y_ppem: + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem) +{ + if (hb_object_is_immutable (font)) + return; + + font->x_ppem = x_ppem; + font->y_ppem = y_ppem; +} + +/** + * hb_font_get_ppem: + * @font: a font. + * @x_ppem: (out): + * @y_ppem: (out): + * + * + * + * Since: 0.9.2 + **/ +void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem) +{ + if (x_ppem) *x_ppem = font->x_ppem; + if (y_ppem) *y_ppem = font->y_ppem; +} + +/** + * hb_font_set_ptem: + * @font: a font. + * @ptem: font size in points. + * + * Sets "point size" of the font. Set to 0 to unset. + * + * There are 72 points in an inch. + * + * Since: 1.6.0 + **/ +void +hb_font_set_ptem (hb_font_t *font, float ptem) +{ + if (hb_object_is_immutable (font)) + return; + + font->ptem = ptem; +} + +/** + * hb_font_get_ptem: + * @font: a font. + * + * Gets the "point size" of the font. A value of 0 means unset. + * + * Return value: Point size. + * + * Since: 0.9.2 + **/ +float +hb_font_get_ptem (hb_font_t *font) +{ + return font->ptem; +} + +#ifndef HB_NO_VAR +/* + * Variations + */ + +/** + * hb_font_set_variations: + * + * Since: 1.4.2 + */ +void +hb_font_set_variations (hb_font_t *font, + const hb_variation_t *variations, + unsigned int variations_length) +{ + if (hb_object_is_immutable (font)) + return; + + if (!variations_length) + { + hb_font_set_var_coords_normalized (font, nullptr, 0); + return; + } + + unsigned int coords_length = hb_ot_var_get_axis_count (font->face); + + int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + free (normalized); + free (design_coords); + return; + } + + const OT::fvar &fvar = *font->face->table.fvar; + for (unsigned int i = 0; i < variations_length; i++) + { + hb_ot_var_axis_info_t info; + if (hb_ot_var_find_axis_info (font->face, variations[i].tag, &info) && + info.axis_index < coords_length) + { + float v = variations[i].value; + design_coords[info.axis_index] = v; + normalized[info.axis_index] = fvar.normalize_axis_value (info.axis_index, v); + } + } + font->face->table.avar->map_coords (normalized, coords_length); + + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_var_coords_design: + * + * Since: 1.4.2 + */ +void +hb_font_set_var_coords_design (hb_font_t *font, + const float *coords, + unsigned int coords_length) +{ + if (hb_object_is_immutable (font)) + return; + + int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + free (normalized); + free (design_coords); + return; + } + + if (coords_length) + memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); + + hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_var_named_instance: + * @font: a font. + * @instance_index: named instance index. + * + * Sets design coords of a font from a named instance index. + * + * Since: 2.6.0 + */ +void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned instance_index) +{ + if (hb_object_is_immutable (font)) + return; + + unsigned int coords_length = hb_ot_var_named_instance_get_design_coords (font->face, instance_index, nullptr, nullptr); + + float *coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + if (unlikely (coords_length && !coords)) + return; + + hb_ot_var_named_instance_get_design_coords (font->face, instance_index, &coords_length, coords); + hb_font_set_var_coords_design (font, coords, coords_length); + free (coords); +} + +/** + * hb_font_set_var_coords_normalized: + * + * Since: 1.4.2 + */ +void +hb_font_set_var_coords_normalized (hb_font_t *font, + const int *coords, /* 2.14 normalized */ + unsigned int coords_length) +{ + if (hb_object_is_immutable (font)) + return; + + int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; + int *unmapped = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (design_coords[0])) : nullptr; + + if (unlikely (coords_length && !(copy && unmapped && design_coords))) + { + free (copy); + free (unmapped); + free (design_coords); + return; + } + + if (coords_length) + { + memcpy (copy, coords, coords_length * sizeof (coords[0])); + memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + } + + /* Best effort design coords simulation */ + font->face->table.avar->unmap_coords (unmapped, coords_length); + for (unsigned int i = 0; i < coords_length; ++i) + design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); + free (unmapped); + + _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); +} + +/** + * hb_font_get_var_coords_normalized: + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Since: 1.4.2 + */ +const int * +hb_font_get_var_coords_normalized (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->coords; +} + +#ifdef HB_EXPERIMENTAL_API +/** + * hb_font_get_var_coords_design: + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Since: EXPERIMENTAL + */ +const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->design_coords; +} +#endif +#endif + +#ifndef HB_DISABLE_DEPRECATED +/* + * Deprecated get_glyph_func(): + */ + +struct hb_trampoline_closure_t +{ + void *user_data; + hb_destroy_func_t destroy; + unsigned int ref_count; +}; + +template +struct hb_trampoline_t +{ + hb_trampoline_closure_t closure; /* Must be first. */ + FuncType func; +}; + +template +static hb_trampoline_t * +trampoline_create (FuncType func, + void *user_data, + hb_destroy_func_t destroy) +{ + typedef hb_trampoline_t trampoline_t; + + trampoline_t *trampoline = (trampoline_t *) calloc (1, sizeof (trampoline_t)); + + if (unlikely (!trampoline)) + return nullptr; + + trampoline->closure.user_data = user_data; + trampoline->closure.destroy = destroy; + trampoline->closure.ref_count = 1; + trampoline->func = func; + + return trampoline; +} + +static void +trampoline_reference (hb_trampoline_closure_t *closure) +{ + closure->ref_count++; +} + +static void +trampoline_destroy (void *user_data) +{ + hb_trampoline_closure_t *closure = (hb_trampoline_closure_t *) user_data; + + if (--closure->ref_count) + return; + + if (closure->destroy) + closure->destroy (closure->user_data); + free (closure); +} + +typedef hb_trampoline_t hb_font_get_glyph_trampoline_t; + +static hb_bool_t +hb_font_get_nominal_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data) +{ + hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; + return trampoline->func (font, font_data, unicode, 0, glyph, trampoline->closure.user_data); +} + +static hb_bool_t +hb_font_get_variation_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data) +{ + hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; + return trampoline->func (font, font_data, unicode, variation_selector, glyph, trampoline->closure.user_data); +} + +/** + * hb_font_funcs_set_glyph_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): callback function. + * @user_data: data to pass to @func. + * @destroy: function to call when @user_data is not needed anymore. + * + * Deprecated. Use hb_font_funcs_set_nominal_glyph_func() and + * hb_font_funcs_set_variation_glyph_func() instead. + * + * Since: 0.9.2 + * Deprecated: 1.2.3 + **/ +void +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + + hb_font_get_glyph_trampoline_t *trampoline; + + trampoline = trampoline_create (func, user_data, destroy); + if (unlikely (!trampoline)) + { + if (destroy) + destroy (user_data); + return; + } + + hb_font_funcs_set_nominal_glyph_func (ffuncs, + hb_font_get_nominal_glyph_trampoline, + trampoline, + trampoline_destroy); + + trampoline_reference (&trampoline->closure); + hb_font_funcs_set_variation_glyph_func (ffuncs, + hb_font_get_variation_glyph_trampoline, + trampoline, + trampoline_destroy); +} +#endif diff --git a/thirdparty/harfbuzz/src/hb-font.h b/thirdparty/harfbuzz/src/hb-font.h new file mode 100644 index 00000000000..e1a5719f1dd --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-font.h @@ -0,0 +1,735 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_FONT_H +#define HB_FONT_H + +#include "hb-common.h" +#include "hb-face.h" +#include "hb-draw.h" + +HB_BEGIN_DECLS + + +typedef struct hb_font_t hb_font_t; + + +/* + * hb_font_funcs_t + */ + +typedef struct hb_font_funcs_t hb_font_funcs_t; + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_create (void); + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_get_empty (void); + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs); + +HB_EXTERN void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs); + +HB_EXTERN hb_bool_t +hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs); + +HB_EXTERN hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); + + +/* font and glyph extents */ + +/* Note that typically ascender is positive and descender negative in coordinate systems that grow up. */ +typedef struct hb_font_extents_t +{ + hb_position_t ascender; /* typographic ascender. */ + hb_position_t descender; /* typographic descender. */ + hb_position_t line_gap; /* suggested line spacing gap. */ + /*< private >*/ + hb_position_t reserved9; + hb_position_t reserved8; + hb_position_t reserved7; + hb_position_t reserved6; + hb_position_t reserved5; + hb_position_t reserved4; + hb_position_t reserved3; + hb_position_t reserved2; + hb_position_t reserved1; +} hb_font_extents_t; + +/* Note that height is negative in coordinate systems that grow up. */ +typedef struct hb_glyph_extents_t +{ + hb_position_t x_bearing; /* left side of glyph from origin. */ + hb_position_t y_bearing; /* top side of glyph from origin. */ + hb_position_t width; /* distance from left to right side. */ + hb_position_t height; /* distance from top to bottom side. */ +} hb_glyph_extents_t; + +/* func types */ + +typedef hb_bool_t (*hb_font_get_font_extents_func_t) (hb_font_t *font, void *font_data, + hb_font_extents_t *extents, + void *user_data); +typedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t; +typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t; + + +typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data); +typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data); + +typedef unsigned int (*hb_font_get_nominal_glyphs_func_t) (hb_font_t *font, void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data); + + +typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + void *user_data); +typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t; +typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t; + +typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data); +typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t; +typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t; + +typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y, + void *user_data); +typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; +typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; + +typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + void *user_data); +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; + + +typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data); +typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y, + void *user_data); + + +typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data); +typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data); + + +/* func setters */ + +/** + * hb_font_funcs_set_font_h_extents_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.1.2 + **/ +HB_EXTERN void +hb_font_funcs_set_font_h_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_font_h_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_font_v_extents_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.1.2 + **/ +HB_EXTERN void +hb_font_funcs_set_font_v_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_font_v_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_nominal_glyph_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.2.3 + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_nominal_glyphs_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 2.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyphs_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyphs_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_variation_glyph_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.2.3 + **/ +HB_EXTERN void +hb_font_funcs_set_variation_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_variation_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_advance_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advance_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_advances_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_advances_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advances_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advances_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_advances_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advances_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_origin_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_origin_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_kerning_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_extents_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_contour_point_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_contour_point_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_name_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_from_name_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_from_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/* func dispatch */ + +HB_EXTERN hb_bool_t +hb_font_get_h_extents (hb_font_t *font, + hb_font_extents_t *extents); +HB_EXTERN hb_bool_t +hb_font_get_v_extents (hb_font_t *font, + hb_font_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t *glyph); +HB_EXTERN hb_bool_t +hb_font_get_variation_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph); + +HB_EXTERN unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride); + +HB_EXTERN hb_position_t +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph); +HB_EXTERN hb_position_t +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN void +hb_font_get_glyph_h_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); +HB_EXTERN void +hb_font_get_glyph_v_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y); +HB_EXTERN hb_bool_t +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_contour_point (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size); +HB_EXTERN hb_bool_t +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + + +/* high-level funcs, with fallback */ + +/* Calls either hb_font_get_nominal_glyph() if variation_selector is 0, + * otherwise calls hb_font_get_variation_glyph(). */ +HB_EXTERN hb_bool_t +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph); + +HB_EXTERN void +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, + hb_font_extents_t *extents); +HB_EXTERN void +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_get_glyph_advances_for_direction (hb_font_t* font, + hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); +HB_EXTERN void +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +/* Generates gidDDD if glyph has no name. */ +HB_EXTERN void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size); +/* Parses gidDDD and uniUUUU strings automatically. */ +HB_EXTERN hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + + +/* + * hb_font_t + */ + +/* Fonts are very light-weight objects */ + +HB_EXTERN hb_font_t * +hb_font_create (hb_face_t *face); + +HB_EXTERN hb_font_t * +hb_font_create_sub_font (hb_font_t *parent); + +HB_EXTERN hb_font_t * +hb_font_get_empty (void); + +HB_EXTERN hb_font_t * +hb_font_reference (hb_font_t *font); + +HB_EXTERN void +hb_font_destroy (hb_font_t *font); + +HB_EXTERN hb_bool_t +hb_font_set_user_data (hb_font_t *font, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_font_get_user_data (hb_font_t *font, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_font_make_immutable (hb_font_t *font); + +HB_EXTERN hb_bool_t +hb_font_is_immutable (hb_font_t *font); + +HB_EXTERN void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent); + +HB_EXTERN hb_font_t * +hb_font_get_parent (hb_font_t *font); + +HB_EXTERN void +hb_font_set_face (hb_font_t *font, + hb_face_t *face); + +HB_EXTERN hb_face_t * +hb_font_get_face (hb_font_t *font); + + +HB_EXTERN void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy); + +/* Be *very* careful with this function! */ +HB_EXTERN void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy); + + +HB_EXTERN void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale); + +HB_EXTERN void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale); + +/* + * A zero value means "no hinting in that direction" + */ +HB_EXTERN void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem); + +HB_EXTERN void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem); + +/* + * Point size per EM. Used for optical-sizing in CoreText. + * A value of zero means "not set". + */ +HB_EXTERN void +hb_font_set_ptem (hb_font_t *font, float ptem); + +HB_EXTERN float +hb_font_get_ptem (hb_font_t *font); + +HB_EXTERN void +hb_font_set_variations (hb_font_t *font, + const hb_variation_t *variations, + unsigned int variations_length); + +HB_EXTERN void +hb_font_set_var_coords_design (hb_font_t *font, + const float *coords, + unsigned int coords_length); + +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length); +#endif + +HB_EXTERN void +hb_font_set_var_coords_normalized (hb_font_t *font, + const int *coords, /* 2.14 normalized */ + unsigned int coords_length); + +HB_EXTERN const int * +hb_font_get_var_coords_normalized (hb_font_t *font, + unsigned int *length); + +HB_EXTERN void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned instance_index); + +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN hb_bool_t +hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph, + const hb_draw_funcs_t *funcs, void *user_data); +#endif + +HB_END_DECLS + +#endif /* HB_FONT_H */ diff --git a/thirdparty/harfbuzz/src/hb-font.hh b/thirdparty/harfbuzz/src/hb-font.hh new file mode 100644 index 00000000000..8fc7f44d44b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-font.hh @@ -0,0 +1,632 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FONT_HH +#define HB_FONT_HH + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-shaper.hh" + + +/* + * hb_font_funcs_t + */ + +#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ + HB_FONT_FUNC_IMPLEMENT (font_h_extents) \ + HB_FONT_FUNC_IMPLEMENT (font_v_extents) \ + HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \ + HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \ + HB_FONT_FUNC_IMPLEMENT (variation_glyph) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ + HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning)) \ + HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ + HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ + HB_FONT_FUNC_IMPLEMENT (glyph_name) \ + HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ + /* ^--- Add new callbacks here */ + +struct hb_font_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) void *name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } user_data; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } destroy; + + /* Don't access these directly. Call font->get_*() instead. */ + union get_t { + struct get_funcs_t { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } f; + void (*array[0 +#define HB_FONT_FUNC_IMPLEMENT(name) +1 + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + ]) (); + } get; +}; +DECLARE_NULL_INSTANCE (hb_font_funcs_t); + + +/* + * hb_font_t + */ + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, font); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_font_t +{ + hb_object_header_t header; + + hb_font_t *parent; + hb_face_t *face; + + int32_t x_scale; + int32_t y_scale; + int64_t x_mult; + int64_t y_mult; + + unsigned int x_ppem; + unsigned int y_ppem; + + float ptem; + + /* Font variation coordinates. */ + unsigned int num_coords; + int *coords; + float *design_coords; + + hb_font_funcs_t *klass; + void *user_data; + hb_destroy_func_t destroy; + + hb_shaper_object_dataset_t data; /* Various shaper data. */ + + + /* Convert from font-space to user-space */ + int64_t dir_mult (hb_direction_t direction) + { return HB_DIRECTION_IS_VERTICAL(direction) ? y_mult : x_mult; } + hb_position_t em_scale_x (int16_t v) { return em_mult (v, x_mult); } + hb_position_t em_scale_y (int16_t v) { return em_mult (v, y_mult); } + hb_position_t em_scalef_x (float v) { return em_scalef (v, x_scale); } + hb_position_t em_scalef_y (float v) { return em_scalef (v, y_scale); } + float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); } + float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); } + hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) + { return em_mult (v, dir_mult (direction)); } + + /* Convert from parent-font user-space to our user-space */ + hb_position_t parent_scale_x_distance (hb_position_t v) + { + if (unlikely (parent && parent->x_scale != x_scale)) + return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale); + return v; + } + hb_position_t parent_scale_y_distance (hb_position_t v) + { + if (unlikely (parent && parent->y_scale != y_scale)) + return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale); + return v; + } + hb_position_t parent_scale_x_position (hb_position_t v) + { return parent_scale_x_distance (v); } + hb_position_t parent_scale_y_position (hb_position_t v) + { return parent_scale_y_distance (v); } + + void parent_scale_distance (hb_position_t *x, hb_position_t *y) + { + *x = parent_scale_x_distance (*x); + *y = parent_scale_y_distance (*y); + } + void parent_scale_position (hb_position_t *x, hb_position_t *y) + { + *x = parent_scale_x_position (*x); + *y = parent_scale_y_position (*y); + } + + + /* Public getters */ + + HB_INTERNAL bool has_func (unsigned int i); + HB_INTERNAL bool has_func_set (unsigned int i); + + /* has_* ... */ +#define HB_FONT_FUNC_IMPLEMENT(name) \ + bool \ + has_##name##_func () \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func (i); \ + } \ + bool \ + has_##name##_func_set () \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func_set (i); \ + } + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + hb_bool_t get_font_h_extents (hb_font_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_h_extents (this, user_data, + extents, + klass->user_data.font_h_extents); + } + hb_bool_t get_font_v_extents (hb_font_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_v_extents (this, user_data, + extents, + klass->user_data.font_v_extents); + } + + bool has_glyph (hb_codepoint_t unicode) + { + hb_codepoint_t glyph; + return get_nominal_glyph (unicode, &glyph); + } + + hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph) + { + *glyph = 0; + return klass->get.f.nominal_glyph (this, user_data, + unicode, glyph, + klass->user_data.nominal_glyph); + } + unsigned int get_nominal_glyphs (unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) + { + return klass->get.f.nominal_glyphs (this, user_data, + count, + first_unicode, unicode_stride, + first_glyph, glyph_stride, + klass->user_data.nominal_glyphs); + } + + hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) + { + *glyph = 0; + return klass->get.f.variation_glyph (this, user_data, + unicode, variation_selector, glyph, + klass->user_data.variation_glyph); + } + + hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_h_advance (this, user_data, + glyph, + klass->user_data.glyph_h_advance); + } + + hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_v_advance (this, user_data, + glyph, + klass->user_data.glyph_v_advance); + } + + void get_glyph_h_advances (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride) + { + return klass->get.f.glyph_h_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + klass->user_data.glyph_h_advances); + } + + void get_glyph_v_advances (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride) + { + return klass->get.f.glyph_v_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + klass->user_data.glyph_v_advances); + } + + hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_h_origin (this, user_data, + glyph, x, y, + klass->user_data.glyph_h_origin); + } + + hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_v_origin (this, user_data, + glyph, x, y, + klass->user_data.glyph_v_origin); + } + + hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph) + { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else + return klass->get.f.glyph_h_kerning (this, user_data, + left_glyph, right_glyph, + klass->user_data.glyph_h_kerning); +#endif + } + + hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph) + { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else + return klass->get.f.glyph_v_kerning (this, user_data, + top_glyph, bottom_glyph, + klass->user_data.glyph_v_kerning); +#endif + } + + hb_bool_t get_glyph_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + klass->user_data.glyph_extents); + } + + hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_contour_point (this, user_data, + glyph, point_index, + x, y, + klass->user_data.glyph_contour_point); + } + + hb_bool_t get_glyph_name (hb_codepoint_t glyph, + char *name, unsigned int size) + { + if (size) *name = '\0'; + return klass->get.f.glyph_name (this, user_data, + glyph, + name, size, + klass->user_data.glyph_name); + } + + hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + *glyph = 0; + if (len == -1) len = strlen (name); + return klass->get.f.glyph_from_name (this, user_data, + name, len, + glyph, + klass->user_data.glyph_from_name); + } + + + /* A bit higher-level, and with fallback */ + + void get_h_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_h_extents (extents)) + { + extents->ascender = y_scale * .8; + extents->descender = extents->ascender - y_scale; + extents->line_gap = 0; + } + } + void get_v_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_v_extents (extents)) + { + extents->ascender = x_scale / 2; + extents->descender = extents->ascender - x_scale; + extents->line_gap = 0; + } + } + + void get_extents_for_direction (hb_direction_t direction, + hb_font_extents_t *extents) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_h_extents_with_fallback (extents); + else + get_v_extents_with_fallback (extents); + } + + void get_glyph_advance_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + *x = get_glyph_h_advance (glyph); + else + *y = get_glyph_v_advance (glyph); + } + void get_glyph_advances_for_direction (hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); + else + get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); + } + + void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = get_glyph_h_advance (glyph) / 2; + + /* TODO cache this somehow?! */ + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + *y = extents.ascender; + } + + void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_h_origin (glyph, x, y) && + get_glyph_v_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x -= dx; *y -= dy; + } + } + void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_v_origin (glyph, x, y) && + get_glyph_h_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x += dx; *y += dy; + } + } + + void get_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_origin_with_fallback (glyph, x, y); + else + get_glyph_v_origin_with_fallback (glyph, x, y); + } + + void add_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + void add_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + void add_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + + void subtract_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + void subtract_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + + void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { + *y = 0; + *x = get_glyph_h_kerning (first_glyph, second_glyph); + } else { + *x = 0; + *y = get_glyph_v_kerning (first_glyph, second_glyph); + } + } + + hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) + { + hb_bool_t ret = get_glyph_extents (glyph, extents); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing); + + return ret; + } + + hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, x, y); + + return ret; + } + + /* Generates gidDDD if glyph has no name. */ + void + glyph_to_string (hb_codepoint_t glyph, + char *s, unsigned int size) + { + if (get_glyph_name (glyph, s, size)) return; + + if (size && snprintf (s, size, "gid%u", glyph) < 0) + *s = '\0'; + } + + /* Parses gidDDD and uniUUUU strings automatically. */ + hb_bool_t + glyph_from_string (const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + if (get_glyph_from_name (s, len, glyph)) return true; + + if (len == -1) len = strlen (s); + + /* Straight glyph index. */ + if (hb_codepoint_parse (s, len, 10, glyph)) + return true; + + if (len > 3) + { + /* gidDDD syntax for glyph indices. */ + if (0 == strncmp (s, "gid", 3) && + hb_codepoint_parse (s + 3, len - 3, 10, glyph)) + return true; + + /* uniUUUU and other Unicode character indices. */ + hb_codepoint_t unichar; + if (0 == strncmp (s, "uni", 3) && + hb_codepoint_parse (s + 3, len - 3, 16, &unichar) && + get_nominal_glyph (unichar, glyph)) + return true; + } + + return false; + } + + void mults_changed () + { + signed upem = face->get_upem (); + x_mult = ((int64_t) x_scale << 16) / upem; + y_mult = ((int64_t) y_scale << 16) / upem; + } + + hb_position_t em_mult (int16_t v, int64_t mult) + { + return (hb_position_t) ((v * mult) >> 16); + } + hb_position_t em_scalef (float v, int scale) + { return (hb_position_t) roundf (v * scale / face->get_upem ()); } + float em_fscale (int16_t v, int scale) + { return (float) v * scale / face->get_upem (); } +}; +DECLARE_NULL_INSTANCE (hb_font_t); + + +#endif /* HB_FONT_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ft.cc b/thirdparty/harfbuzz/src/hb-ft.cc new file mode 100644 index 00000000000..2680873c279 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ft.cc @@ -0,0 +1,1042 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * Copyright © 2015 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_FREETYPE + +#include "hb-ft.h" + +#include "hb-font.hh" +#include "hb-machinery.hh" +#include "hb-cache.hh" + +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_TRUETYPE_TABLES_H + + +/** + * SECTION:hb-ft + * @title: hb-ft + * @short_description: FreeType integration + * @include: hb-ft.h + * + * Functions for using HarfBuzz with the FreeType library. + * + * HarfBuzz supports using FreeType to provide face and + * font data. + * + * Note that FreeType is not thread-safe, therefore these + * functions are not thread-safe either. + **/ + + +/* TODO: + * + * In general, this file does a fine job of what it's supposed to do. + * There are, however, things that need more work: + * + * - FreeType works in 26.6 mode. Clients can decide to use that mode, and everything + * would work fine. However, we also abuse this API for performing in font-space, + * but don't pass the correct flags to FreeType. We just abuse the no-hinting mode + * for that, such that no rounding etc happens. As such, we don't set ppem, and + * pass NO_HINTING as load_flags. Would be much better to use NO_SCALE, and scale + * ourselves. + * + * - We don't handle / allow for emboldening / obliqueing. + * + * - In the future, we should add constructors to create fonts in font space? + */ + + +struct hb_ft_font_t +{ + mutable hb_mutex_t lock; + FT_Face ft_face; + int load_flags; + bool symbol; /* Whether selected cmap is symbol cmap. */ + bool unref; /* Whether to destroy ft_face when done. */ + + mutable hb_atomic_int_t cached_x_scale; + mutable hb_advance_cache_t advance_cache; +}; + +static hb_ft_font_t * +_hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t)); + if (unlikely (!ft_font)) return nullptr; + + ft_font->lock.init (); + ft_font->ft_face = ft_face; + ft_font->symbol = symbol; + ft_font->unref = unref; + + ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; + + ft_font->cached_x_scale.set_relaxed (0); + ft_font->advance_cache.init (); + + return ft_font; +} + +static void +_hb_ft_face_destroy (void *data) +{ + FT_Done_Face ((FT_Face) data); +} + +static void +_hb_ft_font_destroy (void *data) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) data; + + ft_font->advance_cache.fini (); + + if (ft_font->unref) + _hb_ft_face_destroy (ft_font->ft_face); + + ft_font->lock.fini (); + + free (ft_font); +} + +/** + * hb_ft_font_set_load_flags: + * @font: #hb_font_t to work upon + * @load_flags: The FreeType load flags to set + * + * Sets the FT_Load_Glyph load flags for the specified #hb_font_t. + * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) +{ + if (hb_object_is_immutable (font)) + return; + + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + ft_font->load_flags = load_flags; +} + +/** + * hb_ft_font_get_load_flags: + * @font: #hb_font_t to work upon + * + * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. + * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * Return value: FT_Load_Glyph flags found + * + * Since: 1.0.5 + **/ +int +hb_ft_font_get_load_flags (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return 0; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->load_flags; +} + +/** + * hb_ft_get_face: + * @font: #hb_font_t to work upon + * + * Fetches the FT_Face associated with the specified #hb_font_t + * font object. + * + * Return value: the FT_Face found + * + * Since: 0.9.2 + **/ +FT_Face +hb_ft_font_get_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->ft_face; +} + +/** + * hb_ft_font_lock_face: + * @font: + * + * + * + * Return value: + * Since: 2.6.5 + **/ +FT_Face +hb_ft_font_lock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.lock (); + + return ft_font->ft_face; +} + +/** + * hb_ft_font_unlock_face: + * @font: + * + * + * + * Return value: + * Since: 2.6.5 + **/ +void +hb_ft_font_unlock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.unlock (); +} + + +static hb_bool_t +hb_ft_get_nominal_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode); + + if (unlikely (!g)) + { + if (unlikely (ft_font->symbol) && unicode <= 0x00FFu) + { + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/recom + * under "Non-Standard (Symbol) Fonts". */ + g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode); + if (!g) + return false; + } + else + return false; + } + + *glyph = g; + return true; +} + +static unsigned int +hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int done; + for (done = 0; + done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode)); + done++) + { + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + /* We don't need to do ft_font->symbol dance here, since HB calls the singular + * nominal_glyph() for what we don't handle here. */ + return done; +} + + +static hb_bool_t +hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector); + + if (unlikely (!g)) + return false; + + *glyph = g; + return true; +} + +static void +hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + int load_flags = ft_font->load_flags; + int mult = font->x_scale < 0 ? -1 : +1; + + if (font->x_scale != ft_font->cached_x_scale.get ()) + { + ft_font->advance_cache.clear (); + ft_font->cached_x_scale.set (font->x_scale); + } + + for (unsigned int i = 0; i < count; i++) + { + FT_Fixed v = 0; + hb_codepoint_t glyph = *first_glyph; + + unsigned int cv; + if (ft_font->advance_cache.get (glyph, &cv)) + v = cv; + else + { + FT_Get_Advance (ft_face, glyph, load_flags, &v); + ft_font->advance_cache.set (glyph, v); + } + + *first_advance = (v * mult + (1<<9)) >> 10; + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +static hb_position_t +hb_ft_get_glyph_v_advance (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Fixed v; + + if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) + return 0; + + if (font->y_scale < 0) + v = -v; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + return (-v + (1<<9)) >> 10; +} + +static hb_bool_t +hb_ft_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; + *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); + + if (font->x_scale < 0) + *x = -*x; + if (font->y_scale < 0) + *y = -*y; + + return true; +} + +#ifndef HB_NO_OT_SHAPE_FALLBACK +static hb_position_t +hb_ft_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Vector kerningv; + + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) + return 0; + + return kerningv.x; +} +#endif + +static hb_bool_t +hb_ft_get_glyph_extents (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + extents->x_bearing = ft_face->glyph->metrics.horiBearingX; + extents->y_bearing = ft_face->glyph->metrics.horiBearingY; + extents->width = ft_face->glyph->metrics.width; + extents->height = -ft_face->glyph->metrics.height; + if (font->x_scale < 0) + { + extents->x_bearing = -extents->x_bearing; + extents->width = -extents->width; + } + if (font->y_scale < 0) + { + extents->y_bearing = -extents->y_bearing; + extents->height = -extents->height; + } + return true; +} + +static hb_bool_t +hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) + return false; + + if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) + return false; + + *x = ft_face->glyph->outline.points[point_index].x; + *y = ft_face->glyph->outline.points[point_index].y; + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); + if (ret && (size && !*name)) + ret = false; + + return ret; +} + +static hb_bool_t +hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (len < 0) + *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name); + else { + /* Make a nul-terminated version. */ + char buf[128]; + len = hb_min (len, (int) sizeof (buf) - 1); + strncpy (buf, name, len); + buf[len] = '\0'; + *glyph = FT_Get_Name_Index (ft_face, buf); + } + + if (*glyph == 0) + { + /* Check whether the given name was actually the name of glyph 0. */ + char buf[128]; + if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) && + len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len)) + return true; + } + + return *glyph != 0; +} + +static hb_bool_t +hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale); + metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale); + metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender); + if (font->y_scale < 0) + { + metrics->ascender = -metrics->ascender; + metrics->descender = -metrics->descender; + metrics->line_gap = -metrics->line_gap; + } + return true; +} + +#if HB_USE_ATEXIT +static void free_static_ft_funcs (); +#endif + +static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t +{ + static hb_font_funcs_t *create () + { + hb_font_funcs_t *funcs = hb_font_funcs_create (); + + hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr); + //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ft_get_nominal_glyphs, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr); +#ifndef HB_NO_OT_SHAPE_FALLBACK + hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr); +#endif + //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, nullptr, nullptr); + hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr); + hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr); + hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr); + + hb_font_funcs_make_immutable (funcs); + +#if HB_USE_ATEXIT + atexit (free_static_ft_funcs); +#endif + + return funcs; + } +} static_ft_funcs; + +#if HB_USE_ATEXIT +static +void free_static_ft_funcs () +{ + static_ft_funcs.free_instance (); +} +#endif + +static hb_font_funcs_t * +_hb_ft_get_font_funcs () +{ + return static_ft_funcs.get_unconst (); +} + +static void +_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref) +{ + bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; + + hb_ft_font_t *ft_font = _hb_ft_font_create (ft_face, symbol, unref); + if (unlikely (!ft_font)) return; + + hb_font_set_funcs (font, + _hb_ft_get_font_funcs (), + ft_font, + _hb_ft_font_destroy); +} + + +static hb_blob_t * +_hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + FT_Face ft_face = (FT_Face) user_data; + FT_Byte *buffer; + FT_ULong length = 0; + FT_Error error; + + /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length); + if (error) + return nullptr; + + buffer = (FT_Byte *) malloc (length); + if (!buffer) + return nullptr; + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); + if (error) + { + free (buffer); + return nullptr; + } + + return hb_blob_create ((const char *) buffer, length, + HB_MEMORY_MODE_WRITABLE, + buffer, free); +} + +/** + * hb_ft_face_create: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: A callback to call when the face object is not needed anymore + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * This variant of the function does not provide any life-cycle management. + * + * Most client programs should use hb_ft_face_create_referenced() + * (or, perhaps, hb_ft_face_create_cached()) instead. + * + * If you know you have valid reasons not to use hb_ft_face_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!ft_face->stream->read) { + hb_blob_t *blob; + + blob = hb_blob_create ((const char *) ft_face->stream->base, + (unsigned int) ft_face->stream->size, + HB_MEMORY_MODE_READONLY, + ft_face, destroy); + face = hb_face_create (blob, ft_face->face_index); + hb_blob_destroy (blob); + } else { + face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy); + } + + hb_face_set_index (face, ft_face->face_index); + hb_face_set_upem (face, ft_face->units_per_EM); + + return face; +} + +/** + * hb_ft_face_create_referenced: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * This is the preferred variant of the hb_ft_face_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_face_t face object remains alive. Also calls FT_Done_Face() + * when the #hb_face_t face object is destroyed. + * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.38 + **/ +hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_face_create (ft_face, _hb_ft_face_destroy); +} + +static void +hb_ft_face_finalize (FT_Face ft_face) +{ + hb_face_destroy ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_face_create_cached: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * This variant of the function caches the newly created #hb_face_t + * face object, using the @generic pointer of @ft_face. Subsequent function + * calls that are passed the same @ft_face parameter will have the same + * #hb_face_t returned to them, and that #hb_face_t will be correctly + * reference counted. + * + * However, client programs are still responsible for destroying + * @ft_face after the last #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face) +{ + if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) + { + if (ft_face->generic.finalizer) + ft_face->generic.finalizer (ft_face); + + ft_face->generic.data = hb_ft_face_create (ft_face, nullptr); + ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; + } + + return hb_face_reference ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_font_create: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (optional): A callback to call when the font object is not needed anymore + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create() on it. Otherwise, HarfBuzz will not pick up + * the face size. + * + * This variant of the function does not provide any life-cycle management. + * + * Most client programs should use hb_ft_font_create_referenced() + * instead. + * + * If you know you have valid reasons not to use hb_ft_font_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_font_t font object has been destroyed. + * + * HarfBuzz will use the @destroy callback on the #hb_font_t font object + * if it is supplied when you use this function. However, even if @destroy + * is provided, it is the client program's responsibility to destroy @ft_face, + * and it is the client program's responsibility to ensure that @ft_face is + * destroyed only after the #hb_font_t font object has been destroyed. + * + * Return value: (transfer full): the new #hb_font_t font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_font_t *font; + hb_face_t *face; + + face = hb_ft_face_create (ft_face, destroy); + font = hb_font_create (face); + hb_face_destroy (face); + _hb_ft_font_set_funcs (font, ft_face, false); + hb_ft_font_changed (font); + return font; +} + +/** + * hb_ft_font_has_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of @font when the underlying FT_Face has changed. + * This function should be called after changing the size or + * variation-axis settings on the FT_Face. + * + * Since: 1.0.5 + **/ +void +hb_ft_font_changed (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + FT_Face ft_face = ft_font->ft_face; + + hb_font_set_scale (font, + (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16), + (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16)); +#if 0 /* hb-ft works in no-hinting model */ + hb_font_set_ppem (font, + ft_face->size->metrics.x_ppem, + ft_face->size->metrics.y_ppem); +#endif + +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) + FT_MM_Var *mm_var = nullptr; + if (!FT_Get_MM_Var (ft_face, &mm_var)) + { + FT_Fixed *ft_coords = (FT_Fixed *) calloc (mm_var->num_axis, sizeof (FT_Fixed)); + int *coords = (int *) calloc (mm_var->num_axis, sizeof (int)); + if (coords && ft_coords) + { + if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords)) + { + bool nonzero = false; + + for (unsigned int i = 0; i < mm_var->num_axis; ++i) + { + coords[i] = ft_coords[i] >>= 2; + nonzero = nonzero || coords[i]; + } + + if (nonzero) + hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis); + else + hb_font_set_var_coords_normalized (font, nullptr, 0); + } + } + free (coords); + free (ft_coords); +#ifdef HAVE_FT_DONE_MM_VAR + FT_Done_MM_Var (ft_face->glyph->library, mm_var); +#else + free (mm_var); +#endif + } +#endif +} + +/** + * hb_ft_font_create_referenced: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create_references() on it. Otherwise, HarfBuzz will not pick up + * the face size. + * + * This is the preferred variant of the hb_ft_font_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_font_t font object remains alive. + * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_font_t font object + * + * Since: 0.9.38 + **/ +hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_font_create (ft_face, _hb_ft_face_destroy); +} + +#if HB_USE_ATEXIT +static void free_static_ft_library (); +#endif + +static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t, + hb_ft_library_lazy_loader_t> +{ + static FT_Library create () + { + FT_Library l; + if (FT_Init_FreeType (&l)) + return nullptr; + +#if HB_USE_ATEXIT + atexit (free_static_ft_library); +#endif + + return l; + } + static void destroy (FT_Library l) + { + FT_Done_FreeType (l); + } + static FT_Library get_null () + { + return nullptr; + } +} static_ft_library; + +#if HB_USE_ATEXIT +static +void free_static_ft_library () +{ + static_ft_library.free_instance (); +} +#endif + +static FT_Library +get_ft_library () +{ + return static_ft_library.get_unconst (); +} + +static void +_release_blob (FT_Face ft_face) +{ + hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); +} + +/** + * hb_ft_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Configures the font-functions structure of the specified + * #hb_font_t font object to use FreeType font functions. + * + * In particular, you can use this function to configure an + * existing #hb_face_t face object for use with FreeType font + * functions even if that #hb_face_t face object was initially + * created with hb_face_create(), and therefore was not + * initially configured to use FreeType font functions. + * + * An #hb_face_t face object created with hb_ft_face_create() + * is preconfigured for FreeType font functions and does not + * require this function to be used. + * + * Note: Internally, this function creates an FT_Face. +* + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_funcs (hb_font_t *font) +{ + hb_blob_t *blob = hb_face_reference_blob (font->face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (FT, font, "Font face has empty blob"); + + FT_Face ft_face = nullptr; + FT_Error err = FT_New_Memory_Face (get_ft_library (), + (const FT_Byte *) blob_data, + blob_length, + hb_face_get_index (font->face), + &ft_face); + + if (unlikely (err)) { + hb_blob_destroy (blob); + DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed"); + return; + } + + if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) + FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); + + FT_Set_Char_Size (ft_face, + abs (font->x_scale), abs (font->y_scale), + 0, 0); +#if 0 + font->x_ppem * 72 * 64 / font->x_scale, + font->y_ppem * 72 * 64 / font->y_scale); +#endif + if (font->x_scale < 0 || font->y_scale < 0) + { + FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0, + 0, font->y_scale < 0 ? -1 : +1}; + FT_Set_Transform (ft_face, &matrix, nullptr); + } + +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) + unsigned int num_coords; + const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); + if (num_coords) + { + FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed)); + if (ft_coords) + { + for (unsigned int i = 0; i < num_coords; i++) + ft_coords[i] = coords[i] * 4; + FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords); + free (ft_coords); + } + } +#endif + + ft_face->generic.data = blob; + ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; + + _hb_ft_font_set_funcs (font, ft_face, true); + hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-ft.h b/thirdparty/harfbuzz/src/hb-ft.h new file mode 100644 index 00000000000..bf07115ab92 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ft.h @@ -0,0 +1,138 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2015 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FT_H +#define HB_FT_H + +#include "hb.h" + +#include +#include FT_FREETYPE_H + +HB_BEGIN_DECLS + +/* + * Note: FreeType is not thread-safe. + * Hence, these functions are not either. + */ + +/* + * hb-face from ft-face. + */ + +/* This one creates a new hb-face for given ft-face. + * When the returned hb-face is destroyed, the destroy + * callback is called (if not NULL), with the ft-face passed + * to it. + * + * The client is responsible to make sure that ft-face is + * destroyed after hb-face is destroyed. + * + * Most often you don't want this function. You should use either + * hb_ft_face_create_cached(), or hb_ft_face_create_referenced(). + * In particular, if you are going to pass NULL as destroy, you + * probably should use (the more recent) hb_ft_face_create_referenced() + * instead. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* This version is like hb_ft_face_create(), except that it caches + * the hb-face using the generic pointer of the ft-face. This means + * that subsequent calls to this function with the same ft-face will + * return the same hb-face (correctly referenced). + * + * Client is still responsible for making sure that ft-face is destroyed + * after hb-face is. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face); + +/* This version is like hb_ft_face_create(), except that it calls + * FT_Reference_Face() on ft-face, as such keeping ft-face alive + * as long as the hb-face is. + * + * This is the most convenient version to use. Use it unless you have + * very good reasons not to. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face); + + +/* + * hb-font from ft-face. + */ + +/* + * Note: + * + * Set face size on ft-face before creating hb-font from it. + * Otherwise hb-ft would NOT pick up the font size correctly. + */ + +/* See notes on hb_ft_face_create(). Same issues re lifecycle-management + * apply here. Use hb_ft_font_create_referenced() if you can. */ +HB_EXTERN hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* See notes on hb_ft_face_create_referenced() re lifecycle-management + * issues. */ +HB_EXTERN hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face); + +HB_EXTERN FT_Face +hb_ft_font_get_face (hb_font_t *font); + +HB_EXTERN FT_Face +hb_ft_font_lock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_unlock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags); + +HB_EXTERN int +hb_ft_font_get_load_flags (hb_font_t *font); + +/* Call when size or variations settings on underlying FT_Face change. */ +HB_EXTERN void +hb_ft_font_changed (hb_font_t *font); + +/* Makes an hb_font_t use FreeType internally to implement font functions. + * Note: this internally creates an FT_Face. Use it when you create your + * hb_face_t using hb_face_create(). */ +HB_EXTERN void +hb_ft_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_FT_H */ diff --git a/thirdparty/harfbuzz/src/hb-gdi.cc b/thirdparty/harfbuzz/src/hb-gdi.cc new file mode 100644 index 00000000000..f6306ef89fa --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-gdi.cc @@ -0,0 +1,73 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifdef HAVE_GDI + +#include "hb-gdi.h" + +static hb_blob_t * +_hb_gdi_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + char *buffer = nullptr; + DWORD length = 0; + + HDC hdc = GetDC (nullptr); + if (unlikely (!SelectObject (hdc, (HFONT) user_data))) goto fail; + + length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); + if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc; + + buffer = (char *) malloc (length); + if (unlikely (!buffer)) goto fail_with_releasedc; + length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); + if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc_and_free; + ReleaseDC (nullptr, hdc); + + return hb_blob_create ((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, buffer, free); + +fail_with_releasedc_and_free: + free (buffer); +fail_with_releasedc: + ReleaseDC (nullptr, hdc); +fail: + return hb_blob_get_empty (); +} + +/** + * hb_gdi_face_create: + * @hfont: a HFONT object. + * + * Return value: #hb_face_t object corresponding to the given input + * + * Since: 2.6.0 + **/ +hb_face_t * +hb_gdi_face_create (HFONT hfont) +{ + return hb_face_create_for_tables (_hb_gdi_reference_table, (void *) hfont, nullptr); +} + +#endif diff --git a/thirdparty/harfbuzz/src/hb-gdi.h b/thirdparty/harfbuzz/src/hb-gdi.h new file mode 100644 index 00000000000..68cc43917e0 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-gdi.h @@ -0,0 +1,39 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_GDI_H +#define HB_GDI_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_gdi_face_create (HFONT hfont); + +HB_END_DECLS + +#endif /* HB_GDI_H */ diff --git a/thirdparty/harfbuzz/src/hb-glib.cc b/thirdparty/harfbuzz/src/hb-glib.cc new file mode 100644 index 00000000000..f93bb8853ca --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-glib.cc @@ -0,0 +1,307 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GLIB + +#include "hb-glib.h" + +#include "hb-machinery.hh" + + +/** + * SECTION:hb-glib + * @title: hb-glib + * @short_description: GLib integration + * @include: hb-glib.h + * + * Functions for using HarfBuzz with the GLib library. + * + * HarfBuzz supports using GLib to provide Unicode data, by attaching + * GLib functions to the virtual methods in a #hb_unicode_funcs_t function + * structure. + **/ + + +/** + * hb_glib_script_to_script: + * @script: The GUnicodeScript identifier to query + * + * Fetches the #hb_script_t script that corresponds to the + * specified GUnicodeScript identifier. + * + * Return value: the #hb_script_t script found + * + * Since: 0.9.38 + **/ +hb_script_t +hb_glib_script_to_script (GUnicodeScript script) +{ + return (hb_script_t) g_unicode_script_to_iso15924 (script); +} + +/** + * hb_glib_script_from_script: + * @script: The #hb_script_t to query + * + * Fetches the GUnicodeScript identifier that corresponds to the + * specified #hb_script_t script. + * + * Return value: the GUnicodeScript identifier found + * + * Since: 0.9.38 + **/ +GUnicodeScript +hb_glib_script_from_script (hb_script_t script) +{ + return g_unicode_script_from_iso15924 (script); +} + + +static hb_unicode_combining_class_t +hb_glib_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode); +} + +static hb_unicode_general_category_t +hb_glib_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + /* hb_unicode_general_category_t and GUnicodeType are identical */ + return (hb_unicode_general_category_t) g_unichar_type (unicode); +} + +static hb_codepoint_t +hb_glib_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + g_unichar_get_mirror_char (unicode, &unicode); + return unicode; +} + +static hb_script_t +hb_glib_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return hb_glib_script_to_script (g_unichar_get_script (unicode)); +} + +static hb_bool_t +hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_compose (a, b, ab); +#endif + + /* We don't ifdef-out the fallback code such that compiler always + * sees it and makes sure it's compilable. */ + + gchar utf8[12]; + gchar *normalized; + int len; + hb_bool_t ret; + + len = g_unichar_to_utf8 (a, utf8); + len += g_unichar_to_utf8 (b, utf8 + len); + normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFC); + len = g_utf8_strlen (normalized, -1); + if (unlikely (!len)) + return false; + + if (len == 1) { + *ab = g_utf8_get_char (normalized); + ret = true; + } else { + ret = false; + } + + g_free (normalized); + return ret; +} + +static hb_bool_t +hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_decompose (ab, a, b); +#endif + + /* We don't ifdef-out the fallback code such that compiler always + * sees it and makes sure it's compilable. */ + + gchar utf8[6]; + gchar *normalized; + int len; + hb_bool_t ret; + + len = g_unichar_to_utf8 (ab, utf8); + normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFD); + len = g_utf8_strlen (normalized, -1); + if (unlikely (!len)) + return false; + + if (len == 1) { + *a = g_utf8_get_char (normalized); + *b = 0; + ret = *a != ab; + } else if (len == 2) { + *a = g_utf8_get_char (normalized); + *b = g_utf8_get_char (g_utf8_next_char (normalized)); + /* Here's the ugly part: if ab decomposes to a single character and + * that character decomposes again, we have to detect that and undo + * the second part :-(. */ + gchar *recomposed = g_utf8_normalize (normalized, -1, G_NORMALIZE_NFC); + hb_codepoint_t c = g_utf8_get_char (recomposed); + if (c != ab && c != *a) { + *a = c; + *b = 0; + } + g_free (recomposed); + ret = true; + } else { + /* If decomposed to more than two characters, take the last one, + * and recompose the rest to get the first component. */ + gchar *end = g_utf8_offset_to_pointer (normalized, len - 1); + gchar *recomposed; + *b = g_utf8_get_char (end); + recomposed = g_utf8_normalize (normalized, end - normalized, G_NORMALIZE_NFC); + /* We expect that recomposed has exactly one character now. */ + *a = g_utf8_get_char (recomposed); + g_free (recomposed); + ret = true; + } + + g_free (normalized); + return ret; +} + + +#if HB_USE_ATEXIT +static void free_static_glib_funcs (); +#endif + +static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_glib_unicode_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_glib_unicode_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_glib_unicode_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_glib_unicode_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_glib_unicode_compose, nullptr, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_glib_unicode_decompose, nullptr, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + +#if HB_USE_ATEXIT + atexit (free_static_glib_funcs); +#endif + + return funcs; + } +} static_glib_funcs; + +#if HB_USE_ATEXIT +static +void free_static_glib_funcs () +{ + static_glib_funcs.free_instance (); +} +#endif + +/** + * hb_glib_get_unicode_funcs: + * + * Fetches a Unicode-functions structure that is populated + * with the appropriate GLib function for each method. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.38 + **/ +hb_unicode_funcs_t * +hb_glib_get_unicode_funcs () +{ + return static_glib_funcs.get_unconst (); +} + + + +#if GLIB_CHECK_VERSION(2,31,10) + +static void +_hb_g_bytes_unref (void *data) +{ + g_bytes_unref ((GBytes *) data); +} + +/** + * hb_glib_blob_create: + * @gbytes: the GBytes structure to work upon + * + * Creates an #hb_blob_t blob from the specified + * GBytes data structure. + * + * Return value: (transfer full): the new #hb_blob_t blob object + * + * Since: 0.9.38 + **/ +hb_blob_t * +hb_glib_blob_create (GBytes *gbytes) +{ + gsize size = 0; + gconstpointer data = g_bytes_get_data (gbytes, &size); + return hb_blob_create ((const char *) data, + size, + HB_MEMORY_MODE_READONLY, + g_bytes_ref (gbytes), + _hb_g_bytes_unref); +} +#endif + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-glib.h b/thirdparty/harfbuzz/src/hb-glib.h new file mode 100644 index 00000000000..5f04183ba19 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-glib.h @@ -0,0 +1,56 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_GLIB_H +#define HB_GLIB_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_glib_script_to_script (GUnicodeScript script); + +HB_EXTERN GUnicodeScript +hb_glib_script_from_script (hb_script_t script); + + +HB_EXTERN hb_unicode_funcs_t * +hb_glib_get_unicode_funcs (void); + +#if GLIB_CHECK_VERSION(2,31,10) +HB_EXTERN hb_blob_t * +hb_glib_blob_create (GBytes *gbytes); +#endif + +HB_END_DECLS + +#endif /* HB_GLIB_H */ diff --git a/thirdparty/harfbuzz/src/hb-gobject-structs.cc b/thirdparty/harfbuzz/src/hb-gobject-structs.cc new file mode 100644 index 00000000000..7c46e264007 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-gobject-structs.cc @@ -0,0 +1,110 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GOBJECT + + +/** + * SECTION:hb-gobject + * @title: hb-gobject + * @short_description: GObject integration support + * @include: hb-gobject.h + * + * Support for using HarfBuzz with the GObject library to provide + * type data. + * + * The types and functions listed here are solely a linkage between + * HarfBuzz's public data types and the GTypes used by the GObject framework. + * HarfBuzz uses GObject introspection to generate its Python bindings + * (and potentially other language bindings); client programs should never need + * to access the GObject-integration mechanics. + * + * For client programs using the GNOME and GTK software stack, please see the + * GLib and FreeType integration pages. + **/ + + +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif + +#include "hb-gobject.h" + +#define HB_DEFINE_BOXED_TYPE(name,copy_func,free_func) \ +GType \ +hb_gobject_##name##_get_type () \ +{ \ + static gsize type_id = 0; \ + if (g_once_init_enter (&type_id)) { \ + GType id = g_boxed_type_register_static (g_intern_static_string ("hb_" #name "_t"), \ + (GBoxedCopyFunc) copy_func, \ + (GBoxedFreeFunc) free_func); \ + g_once_init_leave (&type_id, id); \ + } \ + return type_id; \ +} + +#define HB_DEFINE_OBJECT_TYPE(name) \ + HB_DEFINE_BOXED_TYPE (name, hb_##name##_reference, hb_##name##_destroy) + +#define HB_DEFINE_VALUE_TYPE(name) \ + static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \ + { \ + hb_##name##_t *c = (hb_##name##_t *) calloc (1, sizeof (hb_##name##_t)); \ + if (unlikely (!c)) return nullptr; \ + *c = *l; \ + return c; \ + } \ + static void _hb_##name##_destroy (hb_##name##_t *l) { free (l); } \ + HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy) + +HB_DEFINE_OBJECT_TYPE (buffer) +HB_DEFINE_OBJECT_TYPE (blob) +HB_DEFINE_OBJECT_TYPE (face) +HB_DEFINE_OBJECT_TYPE (font) +HB_DEFINE_OBJECT_TYPE (font_funcs) +HB_DEFINE_OBJECT_TYPE (set) +HB_DEFINE_OBJECT_TYPE (map) +HB_DEFINE_OBJECT_TYPE (shape_plan) +HB_DEFINE_OBJECT_TYPE (unicode_funcs) +HB_DEFINE_VALUE_TYPE (feature) +HB_DEFINE_VALUE_TYPE (glyph_info) +HB_DEFINE_VALUE_TYPE (glyph_position) +HB_DEFINE_VALUE_TYPE (segment_properties) +HB_DEFINE_VALUE_TYPE (user_data_key) + +HB_DEFINE_VALUE_TYPE (ot_math_glyph_variant) +HB_DEFINE_VALUE_TYPE (ot_math_glyph_part) + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-gobject-structs.h b/thirdparty/harfbuzz/src/hb-gobject-structs.h new file mode 100644 index 00000000000..6fad8d70194 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-gobject-structs.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_GOBJECT_H_IN +#error "Include instead." +#endif + +#ifndef HB_GOBJECT_STRUCTS_H +#define HB_GOBJECT_STRUCTS_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +/* Object types */ + +/** + * hb_gobject_blob_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType +hb_gobject_blob_get_type (void); +#define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ()) + +/** + * hb_gobject_buffer_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType +hb_gobject_buffer_get_type (void); +#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ()) + +/** + * hb_gobject_face_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType +hb_gobject_face_get_type (void); +#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ()) + +/** + * hb_gobject_font_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType +hb_gobject_font_get_type (void); +#define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ()) + +/** + * hb_gobject_font_funcs_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType +hb_gobject_font_funcs_get_type (void); +#define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_set_get_type (void); +#define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ()) + +HB_EXTERN GType +hb_gobject_map_get_type (void); +#define HB_GOBJECT_TYPE_MAP (hb_gobject_map_get_type ()) + +HB_EXTERN GType +hb_gobject_shape_plan_get_type (void); +#define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ()) + +/** + * hb_gobject_unicode_funcs_get_type: + * + * Since: 0.9.2 + **/ +HB_EXTERN GType +hb_gobject_unicode_funcs_get_type (void); +#define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ()) + +/* Value types */ + +HB_EXTERN GType +hb_gobject_feature_get_type (void); +#define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ()) + +HB_EXTERN GType +hb_gobject_glyph_info_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ()) + +HB_EXTERN GType +hb_gobject_glyph_position_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ()) + +HB_EXTERN GType +hb_gobject_segment_properties_get_type (void); +#define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ()) + +HB_EXTERN GType +hb_gobject_user_data_key_get_type (void); +#define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_math_glyph_variant_get_type (void); +#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT (hb_gobject_ot_math_glyph_variant_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_math_glyph_part_get_type (void); +#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART (hb_gobject_ot_math_glyph_part_get_type ()) + + +HB_END_DECLS + +#endif /* HB_GOBJECT_H */ diff --git a/thirdparty/harfbuzz/src/hb-gobject.h b/thirdparty/harfbuzz/src/hb-gobject.h new file mode 100644 index 00000000000..8891aa0ee79 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-gobject.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_GOBJECT_H +#define HB_GOBJECT_H +#define HB_GOBJECT_H_IN + +#include "hb.h" + +#include "hb-gobject-enums.h" +#include "hb-gobject-structs.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_GOBJECT_H_IN +#endif /* HB_GOBJECT_H */ diff --git a/thirdparty/harfbuzz/src/hb-graphite2.cc b/thirdparty/harfbuzz/src/hb-graphite2.cc new file mode 100644 index 00000000000..d8a72dc2f18 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-graphite2.cc @@ -0,0 +1,442 @@ +/* + * Copyright © 2011 Martin Hosken + * Copyright © 2011 SIL International + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GRAPHITE2 + +#include "hb-shaper-impl.hh" + +#include "hb-graphite2.h" + +#include + +#include "hb-ot-layout.h" + + +/** + * SECTION:hb-graphite2 + * @title: hb-graphite2 + * @short_description: Graphite2 integration + * @include: hb-graphite2.h + * + * Functions for using HarfBuzz with fonts that include Graphite features. + * + * For Graphite features to work, you must be sure that HarfBuzz was compiled + * with the `graphite2` shaping engine enabled. Currently, the default is to + * not enable `graphite2` shaping. + **/ + + +/* + * shaper face data + */ + +typedef struct hb_graphite2_tablelist_t +{ + struct hb_graphite2_tablelist_t *next; + hb_blob_t *blob; + unsigned int tag; +} hb_graphite2_tablelist_t; + +struct hb_graphite2_face_data_t +{ + hb_face_t *face; + gr_face *grface; + hb_atomic_ptr_t tlist; +}; + +static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) +{ + hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data; + hb_graphite2_tablelist_t *tlist = face_data->tlist; + + hb_blob_t *blob = nullptr; + + for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) + if (p->tag == tag) { + blob = p->blob; + break; + } + + if (unlikely (!blob)) + { + blob = face_data->face->reference_table (tag); + + hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t)); + if (unlikely (!p)) { + hb_blob_destroy (blob); + return nullptr; + } + p->blob = blob; + p->tag = tag; + +retry: + hb_graphite2_tablelist_t *tlist = face_data->tlist; + p->next = tlist; + + if (unlikely (!face_data->tlist.cmpexch (tlist, p))) + goto retry; + } + + unsigned int tlen; + const char *d = hb_blob_get_data (blob, &tlen); + *len = tlen; + return d; +} + +hb_graphite2_face_data_t * +_hb_graphite2_shaper_face_data_create (hb_face_t *face) +{ + hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF); + /* Umm, we just reference the table to check whether it exists. + * Maybe add better API for this? */ + if (!hb_blob_get_length (silf_blob)) + { + hb_blob_destroy (silf_blob); + return nullptr; + } + hb_blob_destroy (silf_blob); + + hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) calloc (1, sizeof (hb_graphite2_face_data_t)); + if (unlikely (!data)) + return nullptr; + + data->face = face; + data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll); + + if (unlikely (!data->grface)) { + free (data); + return nullptr; + } + + return data; +} + +void +_hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data) +{ + hb_graphite2_tablelist_t *tlist = data->tlist; + + while (tlist) + { + hb_graphite2_tablelist_t *old = tlist; + hb_blob_destroy (tlist->blob); + tlist = tlist->next; + free (old); + } + + gr_face_destroy (data->grface); + + free (data); +} + +/** + * hb_graphite2_face_get_gr_face: + * @face: @hb_face_t to query + * + * Fetches the Graphite2 gr_face corresponding to the specified + * #hb_face_t face object. + * + * Return value: the gr_face found + * + * Since: 0.9.10 + */ +gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face) +{ + const hb_graphite2_face_data_t *data = face->data.graphite2; + return data ? data->grface : nullptr; +} + + +/* + * shaper font data + */ + +struct hb_graphite2_font_data_t {}; + +hb_graphite2_font_data_t * +_hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_graphite2_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_graphite2_shaper_font_data_destroy (hb_graphite2_font_data_t *data HB_UNUSED) +{ +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_graphite2_font_get_gr_font: + * + * Since: 0.9.10 + * Deprecated: 1.4.2 + */ +gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font HB_UNUSED) +{ + return nullptr; +} +#endif + + +/* + * shaper + */ + +struct hb_graphite2_cluster_t { + unsigned int base_char; + unsigned int num_chars; + unsigned int base_glyph; + unsigned int num_glyphs; + unsigned int cluster; + unsigned int advance; +}; + +hb_bool_t +_hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + gr_face *grface = face->data.graphite2->grface; + + const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); + const char *lang_end = lang ? strchr (lang, '-') : nullptr; + int lang_len = lang_end ? lang_end - lang : -1; + gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); + + for (unsigned int i = 0; i < num_features; i++) + { + const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag); + if (fref) + gr_fref_set_feature_value (fref, features[i].value, feats); + } + + gr_segment *seg = nullptr; + const gr_slot *is; + unsigned int ci = 0, ic = 0; + unsigned int curradvx = 0, curradvy = 0; + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + + uint32_t *chars = (uint32_t *) scratch; + + for (unsigned int i = 0; i < buffer->len; ++i) + chars[i] = buffer->info[i].codepoint; + + /* TODO ensure_native_direction. */ + + hb_tag_t script_tag[HB_OT_MAX_TAGS_PER_SCRIPT]; + unsigned int count = HB_OT_MAX_TAGS_PER_SCRIPT; + hb_ot_tags_from_script_and_language (hb_buffer_get_script (buffer), + HB_LANGUAGE_INVALID, + &count, + script_tag, + nullptr, nullptr); + + seg = gr_make_seg (nullptr, grface, + count ? script_tag[count - 1] : HB_OT_TAG_DEFAULT_SCRIPT, + feats, + gr_utf32, chars, buffer->len, + 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0)); + + if (unlikely (!seg)) { + if (feats) gr_featureval_destroy (feats); + return false; + } + + unsigned int glyph_count = gr_seg_n_slots (seg); + if (unlikely (!glyph_count)) { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + buffer->len = 0; + return true; + } + + buffer->ensure (glyph_count); + scratch = buffer->get_scratch_buffer (&scratch_size); + while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) + + DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size) + { + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + return false; + } + scratch = buffer->get_scratch_buffer (&scratch_size); + } + +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + + ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len); + ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count); + +#undef ALLOCATE_ARRAY + + memset (clusters, 0, sizeof (clusters[0]) * buffer->len); + + hb_codepoint_t *pg = gids; + clusters[0].cluster = buffer->info[0].cluster; + unsigned int upem = hb_face_get_upem (face); + float xscale = (float) font->x_scale / upem; + float yscale = (float) font->y_scale / upem; + yscale *= yscale / xscale; + unsigned int curradv = 0; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale; + clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv; + } + else + clusters[0].advance = 0; + for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++) + { + unsigned int before = gr_slot_before (is); + unsigned int after = gr_slot_after (is); + *pg = gr_slot_gid (is); + pg++; + while (clusters[ci].base_char > before && ci) + { + clusters[ci-1].num_chars += clusters[ci].num_chars; + clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; + clusters[ci-1].advance += clusters[ci].advance; + ci--; + } + + if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) + { + hb_graphite2_cluster_t *c = clusters + ci + 1; + c->base_char = clusters[ci].base_char + clusters[ci].num_chars; + c->cluster = buffer->info[c->base_char].cluster; + c->num_chars = before - c->base_char; + c->base_glyph = ic; + c->num_glyphs = 0; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + c->advance = curradv - gr_slot_origin_X(is) * xscale; + curradv -= c->advance; + } + else + { + c->advance = 0; + clusters[ci].advance += gr_slot_origin_X(is) * xscale - curradv; + curradv += clusters[ci].advance; + } + ci++; + } + clusters[ci].num_glyphs++; + + if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) + clusters[ci].num_chars = after + 1 - clusters[ci].base_char; + } + + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + clusters[ci].advance += curradv; + else + clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv; + ci++; + + for (unsigned int i = 0; i < ci; ++i) + { + for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j) + { + hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j]; + info->codepoint = gids[clusters[i].base_glyph + j]; + info->cluster = clusters[i].cluster; + info->var1.i32 = clusters[i].advance; // all glyphs in the cluster get the same advance + } + } + buffer->len = glyph_count; + + /* Positioning. */ + unsigned int currclus = UINT_MAX; + const hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr); + if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + curradvx = 0; + for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is)) + { + pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + if (info->cluster != currclus) { + pPos->x_advance = info->var1.i32; + curradvx += pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; + curradvy += pPos->y_advance; + } + } + else + { + curradvx = gr_seg_advance_X(seg) * xscale; + for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is)) + { + if (info->cluster != currclus) + { + pPos->x_advance = info->var1.i32; + curradvx -= pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; + curradvy -= pPos->y_advance; + pPos->x_offset = gr_slot_origin_X (is) * xscale - info->var1.i32 - curradvx + pPos->x_advance; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + } + hb_buffer_reverse_clusters (buffer); + } + + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + + buffer->unsafe_to_break_all (); + + return true; +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-graphite2.h b/thirdparty/harfbuzz/src/hb-graphite2.h new file mode 100644 index 00000000000..f299da9f71c --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-graphite2.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2011 Martin Hosken + * Copyright © 2011 SIL International + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_GRAPHITE2_H +#define HB_GRAPHITE2_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +/** + * HB_GRAPHITE2_TAG_SILF: + * + * The #hb_tag_t tag for the `Silf` table, which holds Graphite + * features. + * + * For more information, see http://graphite.sil.org/ + * + **/ +#define HB_GRAPHITE2_TAG_SILF HB_TAG('S','i','l','f') + + +HB_EXTERN gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face); + +#ifndef HB_DISABLE_DEPRECATED + +HB_EXTERN HB_DEPRECATED_FOR (hb_graphite2_face_get_gr_face) gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font); + +#endif + + +HB_END_DECLS + +#endif /* HB_GRAPHITE2_H */ diff --git a/thirdparty/harfbuzz/src/hb-icu.cc b/thirdparty/harfbuzz/src/hb-icu.cc new file mode 100644 index 00000000000..008a39e414c --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-icu.cc @@ -0,0 +1,296 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_ICU + +#include "hb-icu.h" + +#include "hb-machinery.hh" + +#include +#include +#include +#include +#include + +/* ICU extra semicolon, fixed since 65, https://github.com/unicode-org/icu/commit/480bec3 */ +#if U_ICU_VERSION_MAJOR_NUM < 65 && (defined(__GNUC__) || defined(__clang__)) +#define HB_ICU_EXTRA_SEMI_IGNORED +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wextra-semi-stmt" +#endif + +/** + * SECTION:hb-icu + * @title: hb-icu + * @short_description: ICU integration + * @include: hb-icu.h + * + * Functions for using HarfBuzz with the International Components for Unicode + * (ICU) library. HarfBuzz supports using ICU to provide Unicode data, by attaching + * ICU functions to the virtual methods in a #hb_unicode_funcs_t function + * structure. + **/ + +/** + * hb_icu_script_to_script: + * @script: The UScriptCode identifier to query + * + * Fetches the #hb_script_t script that corresponds to the + * specified UScriptCode identifier. + * + * Return value: the #hb_script_t script found + * + **/ + +hb_script_t +hb_icu_script_to_script (UScriptCode script) +{ + if (unlikely (script == USCRIPT_INVALID_CODE)) + return HB_SCRIPT_INVALID; + + return hb_script_from_string (uscript_getShortName (script), -1); +} + +/** + * hb_icu_script_from_script: + * @script: The #hb_script_t script to query + * + * Fetches the UScriptCode identifier that corresponds to the + * specified #hb_script_t script. + * + * Return value: the UScriptCode identifier found + * + **/ +UScriptCode +hb_icu_script_from_script (hb_script_t script) +{ + if (unlikely (script == HB_SCRIPT_INVALID)) + return USCRIPT_INVALID_CODE; + + unsigned int numScriptCode = 1 + u_getIntPropertyMaxValue (UCHAR_SCRIPT); + for (unsigned int i = 0; i < numScriptCode; i++) + if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script)) + return (UScriptCode) i; + + return USCRIPT_UNKNOWN; +} + + +static hb_unicode_combining_class_t +hb_icu_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) u_getCombiningClass (unicode); +} + +static hb_unicode_general_category_t +hb_icu_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY)) + { + case U_UNASSIGNED: return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; + + case U_UPPERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER; + case U_LOWERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER; + case U_TITLECASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER; + case U_MODIFIER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER; + case U_OTHER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; + + case U_NON_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK; + case U_ENCLOSING_MARK: return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK; + case U_COMBINING_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK; + + case U_DECIMAL_DIGIT_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER; + case U_LETTER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER; + case U_OTHER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER; + + case U_SPACE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR; + case U_LINE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR; + case U_PARAGRAPH_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR; + + case U_CONTROL_CHAR: return HB_UNICODE_GENERAL_CATEGORY_CONTROL; + case U_FORMAT_CHAR: return HB_UNICODE_GENERAL_CATEGORY_FORMAT; + case U_PRIVATE_USE_CHAR: return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE; + case U_SURROGATE: return HB_UNICODE_GENERAL_CATEGORY_SURROGATE; + + + case U_DASH_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION; + case U_START_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION; + case U_END_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION; + case U_CONNECTOR_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION; + case U_OTHER_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION; + + case U_MATH_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL; + case U_CURRENCY_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL; + case U_MODIFIER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL; + case U_OTHER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL; + + case U_INITIAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION; + case U_FINAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION; + } + + return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; +} + +static hb_codepoint_t +hb_icu_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return u_charMirror(unicode); +} + +static hb_script_t +hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + UErrorCode status = U_ZERO_ERROR; + UScriptCode scriptCode = uscript_getScript(unicode, &status); + + if (unlikely (U_FAILURE (status))) + return HB_SCRIPT_UNKNOWN; + + return hb_icu_script_to_script (scriptCode); +} + +static hb_bool_t +hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data) +{ + const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data; + UChar32 ret = unorm2_composePair (normalizer, a, b); + if (ret < 0) return false; + *ab = ret; + return true; +} + +static hb_bool_t +hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data) +{ + const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data; + UChar decomposed[4]; + int len; + UErrorCode icu_err = U_ZERO_ERROR; + len = unorm2_getRawDecomposition (normalizer, ab, decomposed, + ARRAY_LENGTH (decomposed), &icu_err); + if (U_FAILURE (icu_err) || len < 0) return false; + + len = u_countChar32 (decomposed, len); + if (len == 1) + { + U16_GET_UNSAFE (decomposed, 0, *a); + *b = 0; + return *a != ab; + } + else if (len == 2) + { + len = 0; + U16_NEXT_UNSAFE (decomposed, len, *a); + U16_NEXT_UNSAFE (decomposed, len, *b); + } + return true; +} + + +#if HB_USE_ATEXIT +static void free_static_icu_funcs (); +#endif + +static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + void *user_data = nullptr; + UErrorCode icu_err = U_ZERO_ERROR; + user_data = (void *) unorm2_getNFCInstance (&icu_err); + assert (user_data); + + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_icu_unicode_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_icu_unicode_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_icu_unicode_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_icu_unicode_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_icu_unicode_compose, user_data, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_icu_unicode_decompose, user_data, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + +#if HB_USE_ATEXIT + atexit (free_static_icu_funcs); +#endif + + return funcs; + } +} static_icu_funcs; + +#if HB_USE_ATEXIT +static +void free_static_icu_funcs () +{ + static_icu_funcs.free_instance (); +} +#endif + +/** + * hb_icu_get_unicode_funcs: + * + * Fetches a Unicode-functions structure that is populated + * with the appropriate ICU function for each method. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.38 + **/ +hb_unicode_funcs_t * +hb_icu_get_unicode_funcs () +{ + return static_icu_funcs.get_unconst (); +} + +#ifdef HB_ICU_EXTRA_SEMI_IGNORED +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/thirdparty/harfbuzz/src/hb-icu.h b/thirdparty/harfbuzz/src/hb-icu.h new file mode 100644 index 00000000000..2db6a7b6797 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-icu.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ICU_H +#define HB_ICU_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_icu_script_to_script (UScriptCode script); + +HB_EXTERN UScriptCode +hb_icu_script_from_script (hb_script_t script); + + +HB_EXTERN hb_unicode_funcs_t * +hb_icu_get_unicode_funcs (void); + + +HB_END_DECLS + +#endif /* HB_ICU_H */ diff --git a/thirdparty/harfbuzz/src/hb-iter.hh b/thirdparty/harfbuzz/src/hb-iter.hh new file mode 100644 index 00000000000..981c5c218c4 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-iter.hh @@ -0,0 +1,939 @@ +/* + * Copyright © 2018 Google, Inc. + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_ITER_HH +#define HB_ITER_HH + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-meta.hh" + + +/* Unified iterator object. + * + * The goal of this template is to make the same iterator interface + * available to all types, and make it very easy and compact to use. + * hb_iter_tator objects are small, light-weight, objects that can be + * copied by value. If the collection / object being iterated on + * is writable, then the iterator returns lvalues, otherwise it + * returns rvalues. + * + * TODO Document more. + * + * If iterator implementation implements operator!=, then can be + * used in range-based for loop. That comes free if the iterator + * is random-access. Otherwise, the range-based for loop incurs + * one traversal to find end(), which can be avoided if written + * as a while-style for loop, or if iterator implements a faster + * __end__() method. + * TODO When opting in for C++17, address this by changing return + * type of .end()? + */ + +/* + * Base classes for iterators. + */ + +/* Base class for all iterators. */ +template +struct hb_iter_t +{ + typedef Item item_t; + constexpr unsigned get_item_size () const { return hb_static_size (Item); } + static constexpr bool is_iterator = true; + static constexpr bool is_random_access_iterator = false; + static constexpr bool is_sorted_iterator = false; + + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast (this); } + iter_t* thiz () { return static_cast< iter_t *> (this); } + public: + + /* TODO: + * Port operators below to use hb_enable_if to sniff which method implements + * an operator and use it, and remove hb_iter_fallback_mixin_t completely. */ + + /* Operators. */ + iter_t iter () const { return *thiz(); } + iter_t operator + () const { return *thiz(); } + iter_t begin () const { return *thiz(); } + iter_t end () const { return thiz()->__end__ (); } + explicit operator bool () const { return thiz()->__more__ (); } + unsigned len () const { return thiz()->__len__ (); } + /* The following can only be enabled if item_t is reference type. Otherwise + * it will be returning pointer to temporary rvalue. + * TODO Use a wrapper return type to fix for non-reference type. */ + template + hb_remove_reference* operator -> () const { return hb_addressof (**thiz()); } + item_t operator * () const { return thiz()->__item__ (); } + item_t operator * () { return thiz()->__item__ (); } + item_t operator [] (unsigned i) const { return thiz()->__item_at__ (i); } + item_t operator [] (unsigned i) { return thiz()->__item_at__ (i); } + iter_t& operator += (unsigned count) & { thiz()->__forward__ (count); return *thiz(); } + iter_t operator += (unsigned count) && { thiz()->__forward__ (count); return *thiz(); } + iter_t& operator ++ () & { thiz()->__next__ (); return *thiz(); } + iter_t operator ++ () && { thiz()->__next__ (); return *thiz(); } + iter_t& operator -= (unsigned count) & { thiz()->__rewind__ (count); return *thiz(); } + iter_t operator -= (unsigned count) && { thiz()->__rewind__ (count); return *thiz(); } + iter_t& operator -- () & { thiz()->__prev__ (); return *thiz(); } + iter_t operator -- () && { thiz()->__prev__ (); return *thiz(); } + iter_t operator + (unsigned count) const { auto c = thiz()->iter (); c += count; return c; } + friend iter_t operator + (unsigned count, const iter_t &it) { return it + count; } + iter_t operator ++ (int) { iter_t c (*thiz()); ++*thiz(); return c; } + iter_t operator - (unsigned count) const { auto c = thiz()->iter (); c -= count; return c; } + iter_t operator -- (int) { iter_t c (*thiz()); --*thiz(); return c; } + template + iter_t& operator >> (T &v) & { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t operator >> (T &v) && { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t& operator << (const T v) & { **thiz() = v; ++*thiz(); return *thiz(); } + template + iter_t operator << (const T v) && { **thiz() = v; ++*thiz(); return *thiz(); } + + protected: + hb_iter_t () = default; + hb_iter_t (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t (hb_iter_t &&o HB_UNUSED) = default; + hb_iter_t& operator = (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t& operator = (hb_iter_t &&o HB_UNUSED) = default; +}; + +#define HB_ITER_USING(Name) \ + using item_t = typename Name::item_t; \ + using Name::begin; \ + using Name::end; \ + using Name::get_item_size; \ + using Name::is_iterator; \ + using Name::iter; \ + using Name::operator bool; \ + using Name::len; \ + using Name::operator ->; \ + using Name::operator *; \ + using Name::operator []; \ + using Name::operator +=; \ + using Name::operator ++; \ + using Name::operator -=; \ + using Name::operator --; \ + using Name::operator +; \ + using Name::operator -; \ + using Name::operator >>; \ + using Name::operator <<; \ + static_assert (true, "") + +/* Returns iterator / item type of a type. */ +template +using hb_iter_type = decltype (hb_deref (hb_declval (Iterable)).iter ()); +template +using hb_item_type = decltype (*hb_deref (hb_declval (Iterable)).iter ()); + + +template struct hb_array_t; +template struct hb_sorted_array_t; + +struct +{ + template hb_iter_type + operator () (T&& c) const + { return hb_deref (hb_forward (c)).iter (); } + + /* Specialization for C arrays. */ + + template inline hb_array_t + operator () (Type *array, unsigned int length) const + { return hb_array_t (array, length); } + + template hb_array_t + operator () (Type (&array)[length]) const + { return hb_array_t (array, length); } + +} +HB_FUNCOBJ (hb_iter); +struct +{ + template unsigned + operator () (T&& c) const + { return c.len (); } + +} +HB_FUNCOBJ (hb_len); + +/* Mixin to fill in what the subclass doesn't provide. */ +template +struct hb_iter_fallback_mixin_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast (this); } + iter_t* thiz () { return static_cast< iter_t *> (this); } + public: + + /* Access: Implement __item__(), or __item_at__() if random-access. */ + item_t __item__ () const { return (*thiz())[0]; } + item_t __item_at__ (unsigned i) const { return *(*thiz() + i); } + + /* Termination: Implement __more__(), or __len__() if random-access. */ + bool __more__ () const { return bool (thiz()->len ()); } + unsigned __len__ () const + { iter_t c (*thiz()); unsigned l = 0; while (c) { c++; l++; } return l; } + + /* Advancing: Implement __next__(), or __forward__() if random-access. */ + void __next__ () { *thiz() += 1; } + void __forward__ (unsigned n) { while (*thiz() && n--) ++*thiz(); } + + /* Rewinding: Implement __prev__() or __rewind__() if bidirectional. */ + void __prev__ () { *thiz() -= 1; } + void __rewind__ (unsigned n) { while (*thiz() && n--) --*thiz(); } + + /* Range-based for: Implement __end__() if can be done faster, + * and operator!=. */ + iter_t __end__ () const + { + if (thiz()->is_random_access_iterator) + return *thiz() + thiz()->len (); + /* Above expression loops twice. Following loops once. */ + auto it = *thiz(); + while (it) ++it; + return it; + } + + protected: + hb_iter_fallback_mixin_t () = default; + hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; +}; + +template +struct hb_iter_with_fallback_t : + hb_iter_t, + hb_iter_fallback_mixin_t +{ + protected: + hb_iter_with_fallback_t () = default; + hb_iter_with_fallback_t (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t (hb_iter_with_fallback_t &&o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (hb_iter_with_fallback_t &&o HB_UNUSED) = default; +}; + +/* + * Meta-programming predicates. + */ + +/* hb_is_iterator() / hb_is_iterator_of() */ + +template +struct hb_is_iterator_of +{ + template + static hb_true_type impl (hb_priority<2>, hb_iter_t> *); + static hb_false_type impl (hb_priority<0>, const void *); + + public: + static constexpr bool value = decltype (impl (hb_prioritize, hb_declval (Iter*)))::value; +}; +#define hb_is_iterator_of(Iter, Item) hb_is_iterator_of::value +#define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t) + +/* hb_is_iterable() */ + +template +struct hb_is_iterable +{ + private: + + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_type ()); + + template + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_iterable(Iterable) hb_is_iterable::value + +/* hb_is_source_of() / hb_is_sink_of() */ + +template +struct hb_is_source_of +{ + private: + template >))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) >> hb_declval (Item &), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_source_of(Iter, Item) hb_is_source_of::value + +template +struct hb_is_sink_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) << hb_declval (Item), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_sink_of(Iter, Item) hb_is_sink_of::value + +/* This is commonly used, so define: */ +#define hb_is_sorted_source_of(Iter, Item) \ + (hb_is_source_of(Iter, Item) && Iter::is_sorted_iterator) + + +/* Range-based 'for' for iterables. */ + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +/* begin()/end() are NOT looked up non-ADL. So each namespace must declare them. + * Do it for namespace OT. */ +namespace OT { + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +} + + +/* + * Adaptors, combiners, etc. + */ + +template +static inline auto +operator | (Lhs&& lhs, Rhs&& rhs) HB_AUTO_RETURN (hb_forward (rhs) (hb_forward (lhs))) + +/* hb_map(), hb_filter(), hb_reduce() */ + +enum class hb_function_sortedness_t { + NOT_SORTED, + RETAINS_SORTING, + SORTED, +}; + +template +struct hb_map_iter_t : + hb_iter_t, + decltype (hb_get (hb_declval (Proj), *hb_declval (Iter)))> +{ + hb_map_iter_t (const Iter& it, Proj f_) : it (it), f (f_) {} + + typedef decltype (hb_get (hb_declval (Proj), *hb_declval (Iter))) __item_t__; + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = + Sorted == hb_function_sortedness_t::SORTED ? true : + Sorted == hb_function_sortedness_t::RETAINS_SORTING ? Iter::is_sorted_iterator : + false; + __item_t__ __item__ () const { return hb_get (f.get (), *it); } + __item_t__ __item_at__ (unsigned i) const { return hb_get (f.get (), it[i]); } + bool __more__ () const { return bool (it); } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + hb_map_iter_t __end__ () const { return hb_map_iter_t (it.end (), f); } + bool operator != (const hb_map_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper f; +}; + +template +struct hb_map_iter_factory_t +{ + hb_map_iter_factory_t (Proj f) : f (f) {} + + template + hb_map_iter_t + operator () (Iter it) + { return hb_map_iter_t (it, f); } + + private: + Proj f; +}; +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_retains_sorting); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_sorted); + +template +struct hb_filter_iter_t : + hb_iter_with_fallback_t, + typename Iter::item_t> +{ + hb_filter_iter_t (const Iter& it_, Pred p_, Proj f_) : it (it_), p (p_), f (f_) + { while (it && !hb_has (p.get (), hb_get (f.get (), *it))) ++it; } + + typedef typename Iter::item_t __item_t__; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + __item_t__ __item__ () const { return *it; } + bool __more__ () const { return bool (it); } + void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it.end (), p, f); } + bool operator != (const hb_filter_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper p; + hb_reference_wrapper f; +}; +template +struct hb_filter_iter_factory_t +{ + hb_filter_iter_factory_t (Pred p, Proj f) : p (p), f (f) {} + + template + hb_filter_iter_t + operator () (Iter it) + { return hb_filter_iter_t (it, p, f); } + + private: + Pred p; + Proj f; +}; +struct +{ + template + hb_filter_iter_factory_t + operator () (Pred&& p = hb_identity, Proj&& f = hb_identity) const + { return hb_filter_iter_factory_t (p, f); } +} +HB_FUNCOBJ (hb_filter); + +template +struct hb_reduce_t +{ + hb_reduce_t (Redu r, InitT init_value) : r (r), init_value (init_value) {} + + template > + AccuT + operator () (Iter it) + { + AccuT value = init_value; + for (; it; ++it) + value = r (value, *it); + return value; + } + + private: + Redu r; + InitT init_value; +}; +struct +{ + template + hb_reduce_t + operator () (Redu&& r, InitT init_value) const + { return hb_reduce_t (r, init_value); } +} +HB_FUNCOBJ (hb_reduce); + + +/* hb_zip() */ + +template +struct hb_zip_iter_t : + hb_iter_t, + hb_pair_t> +{ + hb_zip_iter_t () {} + hb_zip_iter_t (const A& a, const B& b) : a (a), b (b) {} + + typedef hb_pair_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + /* Note. The following categorization is only valid if A is strictly sorted, + * ie. does NOT have duplicates. Previously I tried to categorize sortedness + * more granularly, see commits: + * + * 513762849a683914fc266a17ddf38f133cccf072 + * 4d3cf2adb669c345cc43832d11689271995e160a + * + * However, that was not enough, since hb_sorted_array_t, hb_sorted_vector_t, + * SortedArrayOf, etc all needed to be updated to add more variants. At that + * point I saw it not worth the effort, and instead we now deem all sorted + * collections as essentially strictly-sorted for the purposes of zip. + * + * The above assumption is not as bad as it sounds. Our "sorted" comes with + * no guarantees. It's just a contract, put in place to help you remember, + * and think about, whether an iterator you receive is expected to be + * sorted or not. As such, it's not perfect by definition, and should not + * be treated so. The inaccuracy here just errs in the direction of being + * more permissive, so your code compiles instead of erring on the side of + * marking your zipped iterator unsorted in which case your code won't + * compile. + * + * This semantical limitation does NOT affect logic in any other place I + * know of as of this writing. + */ + static constexpr bool is_sorted_iterator = A::is_sorted_iterator; + + __item_t__ __item__ () const { return __item_t__ (*a, *b); } + __item_t__ __item_at__ (unsigned i) const { return __item_t__ (a[i], b[i]); } + bool __more__ () const { return bool (a) && bool (b); } + unsigned __len__ () const { return hb_min (a.len (), b.len ()); } + void __next__ () { ++a; ++b; } + void __forward__ (unsigned n) { a += n; b += n; } + void __prev__ () { --a; --b; } + void __rewind__ (unsigned n) { a -= n; b -= n; } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); } + /* Note, we should stop if ANY of the iters reaches end. As such two compare + * unequal if both items are unequal, NOT if either is unequal. */ + bool operator != (const hb_zip_iter_t& o) const + { return a != o.a && b != o.b; } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_zip_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_zip_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_zip); + +/* hb_apply() */ + +template +struct hb_apply_t +{ + hb_apply_t (Appl a) : a (a) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + (void) hb_invoke (a, *it); + } + + private: + Appl a; +}; +struct +{ + template hb_apply_t + operator () (Appl&& a) const + { return hb_apply_t (a); } + + template hb_apply_t + operator () (Appl *a) const + { return hb_apply_t (*a); } +} +HB_FUNCOBJ (hb_apply); + +/* hb_range()/hb_iota()/hb_repeat() */ + +template +struct hb_range_iter_t : + hb_iter_t, T> +{ + hb_range_iter_t (T start, T end_, S step) : v (start), end_ (end_for (start, end_, step)), step (step) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + __item_t__ __item_at__ (unsigned j) const { return v + j * step; } + bool __more__ () const { return v != end_; } + unsigned __len__ () const { return !step ? UINT_MAX : (end_ - v) / step; } + void __next__ () { v += step; } + void __forward__ (unsigned n) { v += n * step; } + void __prev__ () { v -= step; } + void __rewind__ (unsigned n) { v -= n * step; } + hb_range_iter_t __end__ () const { return hb_range_iter_t (end_, end_, step); } + bool operator != (const hb_range_iter_t& o) const + { return v != o.v; } + + private: + static inline T end_for (T start, T end_, S step) + { + if (!step) + return end_; + auto res = (end_ - start) % step; + if (!res) + return end_; + end_ += step - res; + return end_; + } + + private: + T v; + T end_; + S step; +}; +struct +{ + template hb_range_iter_t + operator () (T end = (unsigned) -1) const + { return hb_range_iter_t (0, end, 1u); } + + template hb_range_iter_t + operator () (T start, T end, S step = 1u) const + { return hb_range_iter_t (start, end, step); } +} +HB_FUNCOBJ (hb_range); + +template +struct hb_iota_iter_t : + hb_iter_with_fallback_t, T> +{ + hb_iota_iter_t (T start, S step) : v (start), step (step) {} + + private: + + template + auto + inc (hb_type_identity s, hb_priority<1>) + -> hb_void_t (s), hb_declval ()))> + { v = hb_invoke (hb_forward (s), v); } + + void + inc (S s, hb_priority<0>) + { v += s; } + + public: + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () { inc (step, hb_prioritize); } + void __prev__ () { v -= step; } + hb_iota_iter_t __end__ () const { return *this; } + bool operator != (const hb_iota_iter_t& o) const { return true; } + + private: + T v; + S step; +}; +struct +{ + template hb_iota_iter_t + operator () (T start = 0u, S step = 1u) const + { return hb_iota_iter_t (start, step); } +} +HB_FUNCOBJ (hb_iota); + +template +struct hb_repeat_iter_t : + hb_iter_t, T> +{ + hb_repeat_iter_t (T value) : v (value) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return v; } + __item_t__ __item_at__ (unsigned j) const { return v; } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () {} + void __forward__ (unsigned) {} + void __prev__ () {} + void __rewind__ (unsigned) {} + hb_repeat_iter_t __end__ () const { return *this; } + bool operator != (const hb_repeat_iter_t& o) const { return true; } + + private: + T v; +}; +struct +{ + template hb_repeat_iter_t + operator () (T value) const + { return hb_repeat_iter_t (value); } +} +HB_FUNCOBJ (hb_repeat); + +/* hb_enumerate()/hb_take() */ + +struct +{ + template + auto operator () (Iterable&& it, Index start = 0u) const HB_AUTO_RETURN + ( hb_zip (hb_iota (start), it) ) +} +HB_FUNCOBJ (hb_enumerate); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN + ( hb_zip (hb_range (count), it) | hb_map (hb_second) ) + + /* Specialization arrays. */ + + template inline hb_array_t + operator () (hb_array_t array, unsigned count) const + { return array.sub_array (0, count); } + + template inline hb_sorted_array_t + operator () (hb_sorted_array_t array, unsigned count) const + { return array.sub_array (0, count); } +} +HB_FUNCOBJ (hb_take); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iter it, unsigned count) const HB_AUTO_RETURN + ( + + hb_iota (it, hb_add (count)) + | hb_map (hb_take (count)) + | hb_take ((hb_len (it) + count - 1) / count) + ) +} +HB_FUNCOBJ (hb_chop); + +/* hb_sink() */ + +template +struct hb_sink_t +{ + hb_sink_t (Sink s) : s (s) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + s << *it; + } + + private: + Sink s; +}; +struct +{ + template hb_sink_t + operator () (Sink&& s) const + { return hb_sink_t (s); } + + template hb_sink_t + operator () (Sink *s) const + { return hb_sink_t (*s); } +} +HB_FUNCOBJ (hb_sink); + +/* hb-drain: hb_sink to void / blackhole / /dev/null. */ + +struct +{ + template + void operator () (Iter it) const + { + for (; it; ++it) + (void) *it; + } +} +HB_FUNCOBJ (hb_drain); + +/* hb_unzip(): unzip and sink to two sinks. */ + +template +struct hb_unzip_t +{ + hb_unzip_t (Sink1 s1, Sink2 s2) : s1 (s1), s2 (s2) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + { + const auto &v = *it; + s1 << v.first; + s2 << v.second; + } + } + + private: + Sink1 s1; + Sink2 s2; +}; +struct +{ + template hb_unzip_t + operator () (Sink1&& s1, Sink2&& s2) const + { return hb_unzip_t (s1, s2); } + + template hb_unzip_t + operator () (Sink1 *s1, Sink2 *s2) const + { return hb_unzip_t (*s1, *s2); } +} +HB_FUNCOBJ (hb_unzip); + + +/* hb-all, hb-any, hb-none. */ + +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (!hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_all); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return true; + return false; + } +} +HB_FUNCOBJ (hb_any); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_none); + +/* + * Algorithms operating on iterators. + */ + +template +inline void +hb_fill (C& c, const V &v) +{ + for (auto i = hb_iter (c); i; i++) + *i = v; +} + +template +inline void +hb_copy (S&& is, D&& id) +{ + hb_iter (is) | hb_sink (id); +} + + +#endif /* HB_ITER_HH */ diff --git a/thirdparty/harfbuzz/src/hb-kern.hh b/thirdparty/harfbuzz/src/hb-kern.hh new file mode 100644 index 00000000000..3f952fe7fce --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-kern.hh @@ -0,0 +1,138 @@ +/* + * Copyright © 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_KERN_HH +#define HB_KERN_HH + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-gpos-table.hh" + + +namespace OT { + + +template +struct hb_kern_machine_t +{ + hb_kern_machine_t (const Driver &driver_, + bool crossStream_ = false) : + driver (driver_), + crossStream (crossStream_) {} + + HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW + void kern (hb_font_t *font, + hb_buffer_t *buffer, + hb_mask_t kern_mask, + bool scale = true) const + { + OT::hb_ot_apply_context_t c (1, font, buffer); + c.set_lookup_mask (kern_mask); + c.set_lookup_props (OT::LookupFlag::IgnoreMarks); + auto &skippy_iter = c.iter_input; + + bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction); + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int idx = 0; idx < count;) + { + if (!(info[idx].mask & kern_mask)) + { + idx++; + continue; + } + + skippy_iter.reset (idx, 1); + if (!skippy_iter.next ()) + { + idx++; + continue; + } + + unsigned int i = idx; + unsigned int j = skippy_iter.idx; + + hb_position_t kern = driver.get_kerning (info[i].codepoint, + info[j].codepoint); + + + if (likely (!kern)) + goto skip; + + if (horizontal) + { + if (scale) + kern = font->em_scale_x (kern); + if (crossStream) + { + pos[j].y_offset = kern; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + else + { + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + pos[i].x_advance += kern1; + pos[j].x_advance += kern2; + pos[j].x_offset += kern2; + } + } + else + { + if (scale) + kern = font->em_scale_y (kern); + if (crossStream) + { + pos[j].x_offset = kern; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + else + { + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + pos[i].y_advance += kern1; + pos[j].y_advance += kern2; + pos[j].y_offset += kern2; + } + } + + buffer->unsafe_to_break (i, j + 1); + + skip: + idx = skippy_iter.idx; + } + } + + const Driver &driver; + bool crossStream; +}; + + +} /* namespace OT */ + + +#endif /* HB_KERN_HH */ diff --git a/thirdparty/harfbuzz/src/hb-machinery.hh b/thirdparty/harfbuzz/src/hb-machinery.hh new file mode 100644 index 00000000000..54bc60d4c8b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-machinery.hh @@ -0,0 +1,307 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MACHINERY_HH +#define HB_MACHINERY_HH + +#include "hb.hh" +#include "hb-blob.hh" + +#include "hb-dispatch.hh" +#include "hb-sanitize.hh" +#include "hb-serialize.hh" + + +/* + * Casts + */ + +/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory + * location pointed to by P plus Ofs bytes. */ +template +static inline const Type& StructAtOffset(const void *P, unsigned int offset) +{ return * reinterpret_cast ((const char *) P + offset); } +template +static inline Type& StructAtOffset(void *P, unsigned int offset) +{ return * reinterpret_cast ((char *) P + offset); } +template +static inline const Type& StructAtOffsetUnaligned(const void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((const char *) P + offset); +#pragma GCC diagnostic pop +} +template +static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((char *) P + offset); +#pragma GCC diagnostic pop +} + +/* StructAfter(X) returns the struct T& that is placed after X. + * Works with X of variable size also. X must implement get_size() */ +template +static inline const Type& StructAfter(const TObject &X) +{ return StructAtOffset(&X, X.get_size()); } +template +static inline Type& StructAfter(TObject &X) +{ return StructAtOffset(&X, X.get_size()); } + + +/* + * Size checking + */ + +/* Check _assertion in a method environment */ +#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ + void _instance_assertion_on_line_##_line () const \ + { static_assert ((_assertion), ""); } +# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) +# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) + +/* Check that _code compiles in a method environment */ +#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ + void _compiles_assertion_on_line_##_line () const \ + { _code; } +# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) +# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) + + +#define DEFINE_SIZE_STATIC(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)) \ + unsigned int get_size () const { return (size); } \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size); \ + static constexpr unsigned static_size = (size) + +#define DEFINE_SIZE_UNION(size, _member) \ + DEFINE_COMPILES_ASSERTION ((void) this->u._member.static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof(this->u._member) == (size)) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_MIN(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_UNBOUNDED(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_ARRAY(size, array) \ + DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + (HB_VAR_ARRAY+0) * sizeof ((array)[0])) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_ARRAY_SIZED(size, array) \ + unsigned int get_size () const { return (size - (array).min_size + (array).get_size ()); } \ + DEFINE_SIZE_ARRAY(size, array) + + + +/* + * Lazy loaders. + */ + +template +struct hb_data_wrapper_t +{ + static_assert (WheresData > 0, ""); + + Data * get_data () const + { return *(((Data **) (void *) this) - WheresData); } + + bool is_inert () const { return !get_data (); } + + template + Stored * call_create () const { return Subclass::create (get_data ()); } +}; +template <> +struct hb_data_wrapper_t +{ + bool is_inert () const { return false; } + + template + Stored * call_create () const { return Funcs::create (); } +}; + +template struct hb_non_void_t { typedef T1 value; }; +template struct hb_non_void_t { typedef T2 value; }; + +template +struct hb_lazy_loader_t : hb_data_wrapper_t +{ + typedef typename hb_non_void_t + >::value Funcs; + + void init0 () {} /* Init, when memory is already set to 0. No-op for us. */ + void init () { instance.set_relaxed (nullptr); } + void fini () { do_destroy (instance.get ()); } + + void free_instance () + { + retry: + Stored *p = instance.get (); + if (unlikely (p && !cmpexch (p, nullptr))) + goto retry; + do_destroy (p); + } + + static void do_destroy (Stored *p) + { + if (p && p != const_cast (Funcs::get_null ())) + Funcs::destroy (p); + } + + const Returned * operator -> () const { return get (); } + const Returned & operator * () const { return *get (); } + explicit operator bool () const + { return get_stored () != Funcs::get_null (); } + template operator const C * () const { return get (); } + + Stored * get_stored () const + { + retry: + Stored *p = this->instance.get (); + if (unlikely (!p)) + { + if (unlikely (this->is_inert ())) + return const_cast (Funcs::get_null ()); + + p = this->template call_create (); + if (unlikely (!p)) + p = const_cast (Funcs::get_null ()); + + if (unlikely (!cmpexch (nullptr, p))) + { + do_destroy (p); + goto retry; + } + } + return p; + } + Stored * get_stored_relaxed () const + { + return this->instance.get_relaxed (); + } + + bool cmpexch (Stored *current, Stored *value) const + { + /* This *must* be called when there are no other threads accessing. */ + return this->instance.cmpexch (current, value); + } + + const Returned * get () const { return Funcs::convert (get_stored ()); } + const Returned * get_relaxed () const { return Funcs::convert (get_stored_relaxed ()); } + Returned * get_unconst () const { return const_cast (Funcs::convert (get_stored ())); } + + /* To be possibly overloaded by subclasses. */ + static Returned* convert (Stored *p) { return p; } + + /* By default null/init/fini the object. */ + static const Stored* get_null () { return &Null (Stored); } + static Stored *create (Data *data) + { + Stored *p = (Stored *) calloc (1, sizeof (Stored)); + if (likely (p)) + p->init (data); + return p; + } + static Stored *create () + { + Stored *p = (Stored *) calloc (1, sizeof (Stored)); + if (likely (p)) + p->init (); + return p; + } + static void destroy (Stored *p) + { + p->fini (); + free (p); + } + +// private: + /* Must only have one pointer. */ + hb_atomic_ptr_t instance; +}; + +/* Specializations. */ + +template +struct hb_face_lazy_loader_t : hb_lazy_loader_t, + hb_face_t, WheresFace> {}; + +template +struct hb_table_lazy_loader_t : hb_lazy_loader_t, + hb_face_t, WheresFace, + hb_blob_t> +{ + static hb_blob_t *create (hb_face_t *face) + { return hb_sanitize_context_t ().reference_table (face); } + static void destroy (hb_blob_t *p) { hb_blob_destroy (p); } + + static const hb_blob_t *get_null () + { return hb_blob_get_empty (); } + + static const T* convert (const hb_blob_t *blob) + { return blob->as (); } + + hb_blob_t* get_blob () const { return this->get_stored (); } +}; + +template +struct hb_font_funcs_lazy_loader_t : hb_lazy_loader_t +{ + static void destroy (hb_font_funcs_t *p) + { hb_font_funcs_destroy (p); } + static const hb_font_funcs_t *get_null () + { return hb_font_funcs_get_empty (); } +}; +template +struct hb_unicode_funcs_lazy_loader_t : hb_lazy_loader_t +{ + static void destroy (hb_unicode_funcs_t *p) + { hb_unicode_funcs_destroy (p); } + static const hb_unicode_funcs_t *get_null () + { return hb_unicode_funcs_get_empty (); } +}; + + +#endif /* HB_MACHINERY_HH */ diff --git a/thirdparty/harfbuzz/src/hb-map.cc b/thirdparty/harfbuzz/src/hb-map.cc new file mode 100644 index 00000000000..191be143727 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-map.cc @@ -0,0 +1,268 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-map.hh" + + +/** + * SECTION:hb-map + * @title: hb-map + * @short_description: Object representing integer to integer mapping + * @include: hb.h + * + * Map objects are integer-to-integer hash-maps. Currently they are + * not used in the HarfBuzz public API, but are provided for client's + * use if desired. + **/ + + +/** + * hb_map_create: (Xconstructor) + * + * Return value: (transfer full): + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_create () +{ + hb_map_t *map; + + if (!(map = hb_object_create ())) + return hb_map_get_empty (); + + map->init_shallow (); + + return map; +} + +/** + * hb_map_get_empty: + * + * Return value: (transfer full): + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_get_empty () +{ + return const_cast (&Null (hb_map_t)); +} + +/** + * hb_map_reference: (skip) + * @map: a map. + * + * Return value: (transfer full): + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_reference (hb_map_t *map) +{ + return hb_object_reference (map); +} + +/** + * hb_map_destroy: (skip) + * @map: a map. + * + * Since: 1.7.7 + **/ +void +hb_map_destroy (hb_map_t *map) +{ + if (!hb_object_destroy (map)) return; + + map->fini_shallow (); + + free (map); +} + +/** + * hb_map_set_user_data: (skip) + * @map: a map. + * @key: + * @data: + * @destroy: + * @replace: + * + * Return value: + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_set_user_data (hb_map_t *map, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (map, key, data, destroy, replace); +} + +/** + * hb_map_get_user_data: (skip) + * @map: a map. + * @key: + * + * Return value: (transfer none): + * + * Since: 1.7.7 + **/ +void * +hb_map_get_user_data (hb_map_t *map, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (map, key); +} + + +/** + * hb_map_allocation_successful: + * @map: a map. + * + * + * + * Return value: + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_allocation_successful (const hb_map_t *map) +{ + return map->successful; +} + + +/** + * hb_map_set: + * @map: a map. + * @key: + * @value: + * + * + * + * Since: 1.7.7 + **/ +void +hb_map_set (hb_map_t *map, + hb_codepoint_t key, + hb_codepoint_t value) +{ + map->set (key, value); +} + +/** + * hb_map_get: + * @map: a map. + * @key: + * + * + * + * Since: 1.7.7 + **/ +hb_codepoint_t +hb_map_get (const hb_map_t *map, + hb_codepoint_t key) +{ + return map->get (key); +} + +/** + * hb_map_del: + * @map: a map. + * @key: + * + * + * + * Since: 1.7.7 + **/ +void +hb_map_del (hb_map_t *map, + hb_codepoint_t key) +{ + map->del (key); +} + +/** + * hb_map_has: + * @map: a map. + * @key: + * + * + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_has (const hb_map_t *map, + hb_codepoint_t key) +{ + return map->has (key); +} + + +/** + * hb_map_clear: + * @map: a map. + * + * + * + * Since: 1.7.7 + **/ +void +hb_map_clear (hb_map_t *map) +{ + return map->clear (); +} + +/** + * hb_map_is_empty: + * @map: a map. + * + * + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_is_empty (const hb_map_t *map) +{ + return map->is_empty (); +} + +/** + * hb_map_get_population: + * @map: a map. + * + * + * + * Since: 1.7.7 + **/ +unsigned int +hb_map_get_population (const hb_map_t *map) +{ + return map->get_population (); +} diff --git a/thirdparty/harfbuzz/src/hb-map.h b/thirdparty/harfbuzz/src/hb-map.h new file mode 100644 index 00000000000..b77843c2baf --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-map.h @@ -0,0 +1,104 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_MAP_H +#define HB_MAP_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/* + * Since: 1.7.7 + */ +#define HB_MAP_VALUE_INVALID ((hb_codepoint_t) -1) + +typedef struct hb_map_t hb_map_t; + + +HB_EXTERN hb_map_t * +hb_map_create (void); + +HB_EXTERN hb_map_t * +hb_map_get_empty (void); + +HB_EXTERN hb_map_t * +hb_map_reference (hb_map_t *map); + +HB_EXTERN void +hb_map_destroy (hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_set_user_data (hb_map_t *map, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_map_get_user_data (hb_map_t *map, + hb_user_data_key_t *key); + + +/* Returns false if allocation has failed before */ +HB_EXTERN hb_bool_t +hb_map_allocation_successful (const hb_map_t *map); + +HB_EXTERN void +hb_map_clear (hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_is_empty (const hb_map_t *map); + +HB_EXTERN unsigned int +hb_map_get_population (const hb_map_t *map); + +HB_EXTERN void +hb_map_set (hb_map_t *map, + hb_codepoint_t key, + hb_codepoint_t value); + +HB_EXTERN hb_codepoint_t +hb_map_get (const hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN void +hb_map_del (hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN hb_bool_t +hb_map_has (const hb_map_t *map, + hb_codepoint_t key); + + +HB_END_DECLS + +#endif /* HB_MAP_H */ diff --git a/thirdparty/harfbuzz/src/hb-map.hh b/thirdparty/harfbuzz/src/hb-map.hh new file mode 100644 index 00000000000..92c1bd67e58 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-map.hh @@ -0,0 +1,326 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MAP_HH +#define HB_MAP_HH + +#include "hb.hh" + + +/* + * hb_hashmap_t + */ + +template +struct hb_hashmap_t +{ + HB_DELETE_COPY_ASSIGN (hb_hashmap_t); + hb_hashmap_t () { init (); } + ~hb_hashmap_t () { fini (); } + + static_assert (hb_is_integral (K) || hb_is_pointer (K), ""); + static_assert (hb_is_integral (V) || hb_is_pointer (V), ""); + + struct item_t + { + K key; + V value; + uint32_t hash; + + void clear () { key = kINVALID; value = vINVALID; hash = 0; } + + bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); } + bool operator == (const item_t &o) { return *this == o.key; } + bool is_unused () const { return key == kINVALID; } + bool is_tombstone () const { return key != kINVALID && value == vINVALID; } + bool is_real () const { return key != kINVALID && value != vINVALID; } + hb_pair_t get_pair() const { return hb_pair_t (key, value); } + }; + + hb_object_header_t header; + bool successful; /* Allocations successful */ + unsigned int population; /* Not including tombstones. */ + unsigned int occupancy; /* Including tombstones. */ + unsigned int mask; + unsigned int prime; + item_t *items; + + void init_shallow () + { + successful = true; + population = occupancy = 0; + mask = 0; + prime = 0; + items = nullptr; + } + void init () + { + hb_object_init (this); + init_shallow (); + } + void fini_shallow () + { + free (items); + items = nullptr; + population = occupancy = 0; + } + void fini () + { + hb_object_fini (this); + fini_shallow (); + } + + void reset () + { + if (unlikely (hb_object_is_immutable (this))) + return; + successful = true; + clear (); + } + + bool in_error () const { return !successful; } + + bool resize () + { + if (unlikely (!successful)) return false; + + unsigned int power = hb_bit_storage (population * 2 + 8); + unsigned int new_size = 1u << power; + item_t *new_items = (item_t *) malloc ((size_t) new_size * sizeof (item_t)); + if (unlikely (!new_items)) + { + successful = false; + return false; + } + for (auto &_ : hb_iter (new_items, new_size)) + _.clear (); + + unsigned int old_size = mask + 1; + item_t *old_items = items; + + /* Switch to new, empty, array. */ + population = occupancy = 0; + mask = new_size - 1; + prime = prime_for (power); + items = new_items; + + /* Insert back old items. */ + if (old_items) + for (unsigned int i = 0; i < old_size; i++) + if (old_items[i].is_real ()) + set_with_hash (old_items[i].key, + old_items[i].hash, + old_items[i].value); + + free (old_items); + + return true; + } + + void set (K key, V value) + { + set_with_hash (key, hb_hash (key), value); + } + + V get (K key) const + { + if (unlikely (!items)) return vINVALID; + unsigned int i = bucket_for (key); + return items[i].is_real () && items[i] == key ? items[i].value : vINVALID; + } + + void del (K key) { set (key, vINVALID); } + + /* Has interface. */ + static constexpr V SENTINEL = vINVALID; + typedef V value_t; + value_t operator [] (K k) const { return get (k); } + bool has (K k, V *vp = nullptr) const + { + V v = (*this)[k]; + if (vp) *vp = v; + return v != SENTINEL; + } + /* Projection. */ + V operator () (K k) const { return get (k); } + + void clear () + { + if (unlikely (hb_object_is_immutable (this))) + return; + if (items) + for (auto &_ : hb_iter (items, mask + 1)) + _.clear (); + + population = occupancy = 0; + } + + bool is_empty () const { return population == 0; } + + unsigned int get_population () const { return population; } + + /* + * Iterator + */ + auto iter () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::get_pair) + ) + auto keys () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::key) + | hb_map (hb_ridentity) + ) + auto values () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::value) + | hb_map (hb_ridentity) + ) + + /* Sink interface. */ + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, v.second); return *this; } + + protected: + + void set_with_hash (K key, uint32_t hash, V value) + { + if (unlikely (!successful)) return; + if (unlikely (key == kINVALID)) return; + if ((occupancy + occupancy / 2) >= mask && !resize ()) return; + unsigned int i = bucket_for_hash (key, hash); + + if (value == vINVALID && items[i].key != key) + return; /* Trying to delete non-existent key. */ + + if (!items[i].is_unused ()) + { + occupancy--; + if (items[i].is_tombstone ()) + population--; + } + + items[i].key = key; + items[i].value = value; + items[i].hash = hash; + + occupancy++; + if (!items[i].is_tombstone ()) + population++; + } + + unsigned int bucket_for (K key) const + { + return bucket_for_hash (key, hb_hash (key)); + } + + unsigned int bucket_for_hash (K key, uint32_t hash) const + { + unsigned int i = hash % prime; + unsigned int step = 0; + unsigned int tombstone = (unsigned) -1; + while (!items[i].is_unused ()) + { + if (items[i].hash == hash && items[i] == key) + return i; + if (tombstone == (unsigned) -1 && items[i].is_tombstone ()) + tombstone = i; + i = (i + ++step) & mask; + } + return tombstone == (unsigned) -1 ? i : tombstone; + } + + static unsigned int prime_for (unsigned int shift) + { + /* Following comment and table copied from glib. */ + /* Each table size has an associated prime modulo (the first prime + * lower than the table size) used to find the initial bucket. Probing + * then works modulo 2^n. The prime modulo is necessary to get a + * good distribution with poor hash functions. + */ + /* Not declaring static to make all kinds of compilers happy... */ + /*static*/ const unsigned int prime_mod [32] = + { + 1, /* For 1 << 0 */ + 2, + 3, + 7, + 13, + 31, + 61, + 127, + 251, + 509, + 1021, + 2039, + 4093, + 8191, + 16381, + 32749, + 65521, /* For 1 << 16 */ + 131071, + 262139, + 524287, + 1048573, + 2097143, + 4194301, + 8388593, + 16777213, + 33554393, + 67108859, + 134217689, + 268435399, + 536870909, + 1073741789, + 2147483647 /* For 1 << 31 */ + }; + + if (unlikely (shift >= ARRAY_LENGTH (prime_mod))) + return prime_mod[ARRAY_LENGTH (prime_mod) - 1]; + + return prime_mod[shift]; + } +}; + +/* + * hb_map_t + */ + +struct hb_map_t : hb_hashmap_t {}; + + +#endif /* HB_MAP_HH */ diff --git a/thirdparty/harfbuzz/src/hb-meta.hh b/thirdparty/harfbuzz/src/hb-meta.hh new file mode 100644 index 00000000000..4c0898b1b75 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-meta.hh @@ -0,0 +1,410 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_META_HH +#define HB_META_HH + +#include "hb.hh" + + +/* + * C++ template meta-programming & fundamentals used with them. + */ + +/* Void! For when we need a expression-type of void. */ +struct hb_empty_t {}; + +/* https://en.cppreference.com/w/cpp/types/void_t */ +template struct _hb_void_t { typedef void type; }; +template using hb_void_t = typename _hb_void_t::type; + +template struct _hb_head_t { typedef Head type; }; +template using hb_head_t = typename _hb_head_t::type; + +template struct hb_integral_constant { static constexpr T value = v; }; +template using hb_bool_constant = hb_integral_constant; +using hb_true_type = hb_bool_constant; +using hb_false_type = hb_bool_constant; + + +/* Basic type SFINAE. */ + +template struct hb_enable_if {}; +template struct hb_enable_if { typedef T type; }; +#define hb_enable_if(Cond) typename hb_enable_if<(Cond)>::type* = nullptr +/* Concepts/Requires alias: */ +#define hb_requires(Cond) hb_enable_if((Cond)) + +template struct hb_is_same : hb_false_type {}; +template struct hb_is_same : hb_true_type {}; +#define hb_is_same(T, T2) hb_is_same::value + +/* Function overloading SFINAE and priority. */ + +#define HB_RETURN(Ret, E) -> hb_head_t { return (E); } +#define HB_AUTO_RETURN(E) -> decltype ((E)) { return (E); } +#define HB_VOID_RETURN(E) -> hb_void_t { (E); } + +template struct hb_priority : hb_priority {}; +template <> struct hb_priority<0> {}; +#define hb_prioritize hb_priority<16> () + +#define HB_FUNCOBJ(x) static_const x HB_UNUSED + + +template struct hb_type_identity_t { typedef T type; }; +template using hb_type_identity = typename hb_type_identity_t::type; + +struct +{ + template constexpr T* + operator () (T& arg) const + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + /* https://en.cppreference.com/w/cpp/memory/addressof */ + return reinterpret_cast ( + &const_cast ( + reinterpret_cast (arg))); +#pragma GCC diagnostic pop + } +} +HB_FUNCOBJ (hb_addressof); + +template static inline T hb_declval (); +#define hb_declval(T) (hb_declval ()) + +template struct hb_match_const : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_const : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_const = typename hb_match_const::type; +template using hb_add_const = const T; +#define hb_is_const(T) hb_match_const::value +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant {}; +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_reference = typename hb_match_reference::type; +template auto _hb_try_add_lvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_lvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_lvalue_reference = decltype (_hb_try_add_lvalue_reference (hb_prioritize)); +template auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference (hb_prioritize)); +#define hb_is_reference(T) hb_match_reference::value +template struct hb_match_pointer : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_pointer : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_pointer = typename hb_match_pointer::type; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity*>; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity; +template using hb_add_pointer = decltype (_hb_try_add_pointer (hb_prioritize)); +#define hb_is_pointer(T) hb_match_pointer::value + + +/* TODO Add feature-parity to std::decay. */ +template using hb_decay = hb_remove_const>; + + +template +struct _hb_conditional { typedef T type; }; +template +struct _hb_conditional { typedef F type; }; +template +using hb_conditional = typename _hb_conditional::type; + + +template +struct hb_is_convertible +{ + private: + static constexpr bool from_void = hb_is_same (void, hb_decay); + static constexpr bool to_void = hb_is_same (void, hb_decay ); + static constexpr bool either_void = from_void || to_void; + static constexpr bool both_void = from_void && to_void; + + static hb_true_type impl2 (hb_conditional); + + template + static auto impl (hb_priority<1>) -> decltype (impl2 (hb_declval (T))); + template + static hb_false_type impl (hb_priority<0>); + public: + static constexpr bool value = both_void || + (!either_void && + decltype (impl> (hb_prioritize))::value); +}; +#define hb_is_convertible(From,To) hb_is_convertible::value + +template +using hb_is_base_of = hb_is_convertible *, hb_decay *>; +#define hb_is_base_of(Base,Derived) hb_is_base_of::value + +template +using hb_is_cr_convertible = hb_bool_constant< + hb_is_same (hb_decay, hb_decay) && + (!hb_is_const (From) || hb_is_const (To)) && + (!hb_is_reference (To) || hb_is_const (To) || hb_is_reference (To)) +>; +#define hb_is_cr_convertible(From,To) hb_is_cr_convertible::value + +/* std::move and std::forward */ + +template +static constexpr hb_remove_reference&& hb_move (T&& t) { return (hb_remove_reference&&) (t); } + +template +static constexpr T&& hb_forward (hb_remove_reference& t) { return (T&&) t; } +template +static constexpr T&& hb_forward (hb_remove_reference&& t) { return (T&&) t; } + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (hb_forward (v)) + + template constexpr auto + operator () (T *v) const HB_AUTO_RETURN (*v) +} +HB_FUNCOBJ (hb_deref); + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (hb_forward (v)) + + template constexpr auto + operator () (T& v) const HB_AUTO_RETURN (hb_addressof (v)) +} +HB_FUNCOBJ (hb_ref); + +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T v) : v (v) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T () const { return v; } + T get () const { return v; } + T v; +}; +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T& v) : v (hb_addressof (v)) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T& () const { return *v; } + T& get () const { return *v; } + T* v; +}; + + +template +using hb_is_integral = hb_bool_constant< + hb_is_same (hb_decay, char) || + hb_is_same (hb_decay, signed char) || + hb_is_same (hb_decay, unsigned char) || + hb_is_same (hb_decay, signed int) || + hb_is_same (hb_decay, unsigned int) || + hb_is_same (hb_decay, signed short) || + hb_is_same (hb_decay, unsigned short) || + hb_is_same (hb_decay, signed long) || + hb_is_same (hb_decay, unsigned long) || + hb_is_same (hb_decay, signed long long) || + hb_is_same (hb_decay, unsigned long long) || + false +>; +#define hb_is_integral(T) hb_is_integral::value +template +using hb_is_floating_point = hb_bool_constant< + hb_is_same (hb_decay, float) || + hb_is_same (hb_decay, double) || + hb_is_same (hb_decay, long double) || + false +>; +#define hb_is_floating_point(T) hb_is_floating_point::value +template +using hb_is_arithmetic = hb_bool_constant< + hb_is_integral (T) || + hb_is_floating_point (T) || + false +>; +#define hb_is_arithmetic(T) hb_is_arithmetic::value + + +template +using hb_is_signed = hb_conditional, + hb_false_type>; +#define hb_is_signed(T) hb_is_signed::value +template +using hb_is_unsigned = hb_conditional, + hb_false_type>; +#define hb_is_unsigned(T) hb_is_unsigned::value + +template struct hb_int_min; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +#define hb_int_min(T) hb_int_min::value +template struct hb_int_max; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +#define hb_int_max(T) hb_int_max::value + + + +template +struct _hb_is_destructible : hb_false_type {}; +template +struct _hb_is_destructible> : hb_true_type {}; +template +using hb_is_destructible = _hb_is_destructible; +#define hb_is_destructible(T) hb_is_destructible::value + +template +struct _hb_is_constructible : hb_false_type {}; +template +struct _hb_is_constructible, Ts...> : hb_true_type {}; +template +using hb_is_constructible = _hb_is_constructible; +#define hb_is_constructible(...) hb_is_constructible<__VA_ARGS__>::value + +template +using hb_is_default_constructible = hb_is_constructible; +#define hb_is_default_constructible(T) hb_is_default_constructible::value + +template +using hb_is_copy_constructible = hb_is_constructible>>; +#define hb_is_copy_constructible(T) hb_is_copy_constructible::value + +template +using hb_is_move_constructible = hb_is_constructible>>; +#define hb_is_move_constructible(T) hb_is_move_constructible::value + +template +struct _hb_is_assignable : hb_false_type {}; +template +struct _hb_is_assignable> : hb_true_type {}; +template +using hb_is_assignable = _hb_is_assignable; +#define hb_is_assignable(T,U) hb_is_assignable::value + +template +using hb_is_copy_assignable = hb_is_assignable, + hb_add_lvalue_reference>>; +#define hb_is_copy_assignable(T) hb_is_copy_assignable::value + +template +using hb_is_move_assignable = hb_is_assignable, + hb_add_rvalue_reference>; +#define hb_is_move_assignable(T) hb_is_move_assignable::value + +/* Trivial versions. */ + +template union hb_trivial { T value; }; + +template +using hb_is_trivially_destructible= hb_is_destructible>; +#define hb_is_trivially_destructible(T) hb_is_trivially_destructible::value + +/* Don't know how to do the following. */ +//template +//using hb_is_trivially_constructible= hb_is_constructible, hb_trivial...>; +//#define hb_is_trivially_constructible(...) hb_is_trivially_constructible<__VA_ARGS__>::value + +template +using hb_is_trivially_default_constructible= hb_is_default_constructible>; +#define hb_is_trivially_default_constructible(T) hb_is_trivially_default_constructible::value + +template +using hb_is_trivially_copy_constructible= hb_is_copy_constructible>; +#define hb_is_trivially_copy_constructible(T) hb_is_trivially_copy_constructible::value + +template +using hb_is_trivially_move_constructible= hb_is_move_constructible>; +#define hb_is_trivially_move_constructible(T) hb_is_trivially_move_constructible::value + +/* Don't know how to do the following. */ +//template +//using hb_is_trivially_assignable= hb_is_assignable, hb_trivial>; +//#define hb_is_trivially_assignable(T,U) hb_is_trivially_assignable::value + +template +using hb_is_trivially_copy_assignable= hb_is_copy_assignable>; +#define hb_is_trivially_copy_assignable(T) hb_is_trivially_copy_assignable::value + +template +using hb_is_trivially_move_assignable= hb_is_move_assignable>; +#define hb_is_trivially_move_assignable(T) hb_is_trivially_move_assignable::value + +template +using hb_is_trivially_copyable= hb_bool_constant< + hb_is_trivially_destructible (T) && + (!hb_is_move_assignable (T) || hb_is_trivially_move_assignable (T)) && + (!hb_is_move_constructible (T) || hb_is_trivially_move_constructible (T)) && + (!hb_is_copy_assignable (T) || hb_is_trivially_copy_assignable (T)) && + (!hb_is_copy_constructible (T) || hb_is_trivially_copy_constructible (T)) && + true +>; +#define hb_is_trivially_copyable(T) hb_is_trivially_copyable::value + +template +using hb_is_trivial= hb_bool_constant< + hb_is_trivially_copyable (T) && + hb_is_trivially_default_constructible (T) +>; +#define hb_is_trivial(T) hb_is_trivial::value + +/* hb_unwrap_type (T) + * If T has no T::type, returns T. Otherwise calls itself on T::type recursively. + */ + +template +struct _hb_unwrap_type : hb_type_identity_t {}; +template +struct _hb_unwrap_type> : _hb_unwrap_type {}; +template +using hb_unwrap_type = _hb_unwrap_type; +#define hb_unwrap_type(T) typename hb_unwrap_type::type + +#endif /* HB_META_HH */ diff --git a/thirdparty/harfbuzz/src/hb-mutex.hh b/thirdparty/harfbuzz/src/hb-mutex.hh new file mode 100644 index 00000000000..56392d049b1 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-mutex.hh @@ -0,0 +1,133 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MUTEX_HH +#define HB_MUTEX_HH + +#include "hb.hh" + + +/* mutex */ + +/* We need external help for these */ + +#if defined(HB_MUTEX_IMPL_INIT) \ + && defined(hb_mutex_impl_init) \ + && defined(hb_mutex_impl_lock) \ + && defined(hb_mutex_impl_unlock) \ + && defined(hb_mutex_impl_finish) + +/* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ + + +#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include +typedef pthread_mutex_t hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER +#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) +#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) +#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) +#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) + + +#elif !defined(HB_NO_MT) && defined(_WIN32) + +typedef CRITICAL_SECTION hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT {0} +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) +#else +#define hb_mutex_impl_init(M) InitializeCriticalSection (M) +#endif +#define hb_mutex_impl_lock(M) EnterCriticalSection (M) +#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M) +#define hb_mutex_impl_finish(M) DeleteCriticalSection (M) + + +#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) + +#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) +# include +# define HB_SCHED_YIELD() sched_yield () +#else +# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END +#endif + +/* This actually is not a totally awful implementation. */ +typedef volatile int hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT 0 +#define hb_mutex_impl_init(M) *(M) = 0 +#define hb_mutex_impl_lock(M) HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END +#define hb_mutex_impl_unlock(M) __sync_lock_release (M) +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END + + +#elif defined(HB_NO_MT) + +typedef int hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT 0 +#define hb_mutex_impl_init(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_lock(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_unlock(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END + + +#else + +#error "Could not find any system to define mutex macros." +#error "Check hb-mutex.hh for possible resolutions." + +#endif + + +#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT} + +struct hb_mutex_t +{ + hb_mutex_impl_t m; + + void init () { hb_mutex_impl_init (&m); } + void lock () { hb_mutex_impl_lock (&m); } + void unlock () { hb_mutex_impl_unlock (&m); } + void fini () { hb_mutex_impl_finish (&m); } +}; + +struct hb_lock_t +{ + hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); } + ~hb_lock_t () { mutex.unlock (); } + private: + hb_mutex_t &mutex; +}; + + +#endif /* HB_MUTEX_HH */ diff --git a/thirdparty/harfbuzz/src/hb-null.hh b/thirdparty/harfbuzz/src/hb-null.hh new file mode 100644 index 00000000000..9853939b077 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-null.hh @@ -0,0 +1,184 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_NULL_HH +#define HB_NULL_HH + +#include "hb.hh" +#include "hb-meta.hh" + + +/* + * Static pools + */ + +/* Global nul-content Null pool. Enlarge as necessary. */ + +#define HB_NULL_POOL_SIZE 384 + +/* Use SFINAE to sniff whether T has min_size; in which case return T::null_size, + * otherwise return sizeof(T). */ + +/* The hard way... + * https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol + */ + +template +struct _hb_null_size : hb_integral_constant {}; +template +struct _hb_null_size> : hb_integral_constant {}; + +template +using hb_null_size = _hb_null_size; +#define hb_null_size(T) hb_null_size::value + +/* These doesn't belong here, but since is copy/paste from above, put it here. */ + +/* hb_static_size (T) + * Returns T::static_size if T::min_size is defined, or sizeof (T) otherwise. */ + +template +struct _hb_static_size : hb_integral_constant {}; +template +struct _hb_static_size> : hb_integral_constant {}; +template +using hb_static_size = _hb_static_size; +#define hb_static_size(T) hb_static_size::value + + +/* + * Null() + */ + +extern HB_INTERNAL +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; + +/* Generic nul-content Null objects. */ +template +struct Null { + static Type const & get_null () + { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + return *reinterpret_cast (_hb_NullPool); + } +}; +template +struct NullHelper +{ + typedef hb_remove_const> Type; + static const Type & get_null () { return Null::get_null (); } +}; +#define Null(Type) NullHelper::get_null () + +/* Specializations for arbitrary-content Null objects expressed in bytes. */ +#define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \ + } /* Close namespace. */ \ + extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]; \ + template <> \ + struct Null { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ + namespace Namespace { \ + static_assert (true, "") /* Require semicolon after. */ +#define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ + const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size] + +/* Specializations for arbitrary-content Null objects expressed as struct initializer. */ +#define DECLARE_NULL_INSTANCE(Type) \ + extern HB_INTERNAL const Type _hb_Null_##Type; \ + template <> \ + struct Null { \ + static Type const & get_null () { \ + return _hb_Null_##Type; \ + } \ + }; \ + static_assert (true, "") /* Require semicolon after. */ +#define DEFINE_NULL_INSTANCE(Type) \ + const Type _hb_Null_##Type + +/* Global writable pool. Enlarge as necessary. */ + +/* To be fully correct, CrapPool must be thread_local. However, we do not rely on CrapPool + * for correct operation. It only exist to catch and divert program logic bugs instead of + * causing bad memory access. So, races there are not actually introducing incorrectness + * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */ +extern HB_INTERNAL +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; + +/* CRAP pool: Common Region for Access Protection. */ +template +static inline Type& Crap () { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + Type *obj = reinterpret_cast (_hb_CrapPool); + memcpy (obj, &Null (Type), sizeof (*obj)); + return *obj; +} +template +struct CrapHelper +{ + typedef hb_remove_const> Type; + static Type & get_crap () { return Crap (); } +}; +#define Crap(Type) CrapHelper::get_crap () + +template +struct CrapOrNullHelper { + static Type & get () { return Crap (Type); } +}; +template +struct CrapOrNullHelper { + static const Type & get () { return Null (Type); } +}; +#define CrapOrNull(Type) CrapOrNullHelper::get () + + +/* + * hb_nonnull_ptr_t + */ + +template +struct hb_nonnull_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {} + T * operator = (T *v_) { return v = v_; } + T * operator -> () const { return get (); } + T & operator * () const { return *get (); } + T ** operator & () const { return &v; } + /* Only auto-cast to const types. */ + template operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + T * get () const { return v ? v : const_cast (&Null (T)); } + T * get_raw () const { return v; } + + T *v; +}; + + +#endif /* HB_NULL_HH */ diff --git a/thirdparty/harfbuzz/src/hb-number-parser.hh b/thirdparty/harfbuzz/src/hb-number-parser.hh new file mode 100644 index 00000000000..1a9dbba6dd3 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-number-parser.hh @@ -0,0 +1,237 @@ + +#line 1 "hb-number-parser.rl" +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_NUMBER_PARSER_HH +#define HB_NUMBER_PARSER_HH + +#include "hb.hh" + + +#line 35 "hb-number-parser.hh" +static const unsigned char _double_parser_trans_keys[] = { + 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, + 46u, 101u, 0 +}; + +static const char _double_parser_key_spans[] = { + 0, 15, 12, 10, 15, 10, 54, 10, + 56 +}; + +static const unsigned char _double_parser_index_offsets[] = { + 0, 0, 16, 29, 40, 56, 67, 122, + 133 +}; + +static const char _double_parser_indicies[] = { + 0, 1, 2, 3, 1, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 1, 3, 1, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 1, 6, 1, 7, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 1, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 1, 3, 1, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 0 +}; + +static const char _double_parser_trans_targs[] = { + 2, 0, 2, 3, 8, 6, 5, 5, + 7, 4 +}; + +static const char _double_parser_trans_actions[] = { + 0, 0, 1, 0, 2, 3, 0, 4, + 5, 0 +}; + +static const int double_parser_start = 1; +static const int double_parser_first_final = 6; +static const int double_parser_error = 0; + +static const int double_parser_en_main = 1; + + +#line 68 "hb-number-parser.rl" + + +/* Works only for n < 512 */ +static inline double +_pow10 (unsigned exponent) +{ + static const double _powers_of_10[] = + { + 1.0e+256, + 1.0e+128, + 1.0e+64, + 1.0e+32, + 1.0e+16, + 1.0e+8, + 10000., + 100., + 10. + }; + unsigned mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1); + double result = 1; + for (const double *power = _powers_of_10; mask; ++power, mask >>= 1) + if (exponent & mask) result *= *power; + return result; +} + +/* a variant of strtod that also gets end of buffer in its second argument */ +static inline double +strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) +{ + double value = 0; + double frac = 0; + double frac_count = 0; + unsigned exp = 0; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 2^52-1 */ + const unsigned MAX_EXP = 0x7FFu; /* 2^11-1 */ + + const char *pe = *end_ptr; + while (p < pe && ISSPACE (*p)) + p++; + + int cs; + +#line 139 "hb-number-parser.hh" + { + cs = double_parser_start; + } + +#line 144 "hb-number-parser.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _double_parser_trans_keys + (cs<<1); + _inds = _double_parser_indicies + _double_parser_index_offsets[cs]; + + _slen = _double_parser_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _double_parser_trans_targs[_trans]; + + if ( _double_parser_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _double_parser_trans_actions[_trans] ) { + case 1: +#line 37 "hb-number-parser.rl" + { neg = true; } + break; + case 4: +#line 38 "hb-number-parser.rl" + { exp_neg = true; } + break; + case 2: +#line 40 "hb-number-parser.rl" + { + value = value * 10. + ((*p) - '0'); +} + break; + case 3: +#line 43 "hb-number-parser.rl" + { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + ((*p) - '0'); + ++frac_count; + } +} + break; + case 5: +#line 50 "hb-number-parser.rl" + { + if (likely (exp * 10 + ((*p) - '0') <= MAX_EXP)) + exp = exp * 10 + ((*p) - '0'); + else + exp_overflow = true; +} + break; +#line 202 "hb-number-parser.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 113 "hb-number-parser.rl" + + + *end_ptr = p; + + if (frac_count) value += frac / _pow10 (frac_count); + if (neg) value *= -1.; + + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + + if (exp) + { + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); + } + + return value; +} + +#endif /* HB_NUMBER_PARSER_HH */ diff --git a/thirdparty/harfbuzz/src/hb-number.cc b/thirdparty/harfbuzz/src/hb-number.cc new file mode 100644 index 00000000000..6e4f3f7ebd0 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-number.cc @@ -0,0 +1,80 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#include "hb.hh" +#include "hb-machinery.hh" +#include "hb-number.hh" +#include "hb-number-parser.hh" + +template +static bool +_parse_number (const char **pp, const char *end, T *pv, + bool whole_buffer, Func f) +{ + char buf[32]; + unsigned len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + + errno = 0; + *pv = f (p, &pend); + if (unlikely (errno || p == pend || + /* Check if consumed whole buffer if is requested */ + (whole_buffer && pend - p != end - *pp))) + return false; + + *pp += pend - p; + return true; +} + +bool +hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer) +{ + return _parse_number (pp, end, pv, whole_buffer, + [] (const char *p, char **end) + { return strtol (p, end, 10); }); +} + +bool +hb_parse_uint (const char **pp, const char *end, unsigned *pv, + bool whole_buffer, int base) +{ + return _parse_number (pp, end, pv, whole_buffer, + [base] (const char *p, char **end) + { return strtoul (p, end, base); }); +} + +bool +hb_parse_double (const char **pp, const char *end, double *pv, bool whole_buffer) +{ + const char *pend = end; + *pv = strtod_rl (*pp, &pend); + if (unlikely (*pp == pend)) return false; + *pp = pend; + return !whole_buffer || end == pend; +} diff --git a/thirdparty/harfbuzz/src/hb-number.hh b/thirdparty/harfbuzz/src/hb-number.hh new file mode 100644 index 00000000000..14d1260aa3d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-number.hh @@ -0,0 +1,41 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_NUMBER_HH +#define HB_NUMBER_HH + +HB_INTERNAL bool +hb_parse_int (const char **pp, const char *end, int *pv, + bool whole_buffer = false); + +HB_INTERNAL bool +hb_parse_uint (const char **pp, const char *end, unsigned int *pv, + bool whole_buffer = false, int base = 10); + +HB_INTERNAL bool +hb_parse_double (const char **pp, const char *end, double *pv, + bool whole_buffer = false); + +#endif /* HB_NUMBER_HH */ diff --git a/thirdparty/harfbuzz/src/hb-object.hh b/thirdparty/harfbuzz/src/hb-object.hh new file mode 100644 index 00000000000..39845a70e79 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-object.hh @@ -0,0 +1,342 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OBJECT_HH +#define HB_OBJECT_HH + +#include "hb.hh" +#include "hb-atomic.hh" +#include "hb-mutex.hh" +#include "hb-vector.hh" + + +/* + * Lockable set + */ + +template +struct hb_lockable_set_t +{ + hb_vector_t items; + + void init () { items.init (); } + + template + item_t *replace_or_insert (T v, lock_t &l, bool replace) + { + l.lock (); + item_t *item = items.find (v); + if (item) { + if (replace) { + item_t old = *item; + *item = v; + l.unlock (); + old.fini (); + } + else { + item = nullptr; + l.unlock (); + } + } else { + item = items.push (v); + l.unlock (); + } + return item; + } + + template + void remove (T v, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (item) + { + item_t old = *item; + *item = items[items.length - 1]; + items.pop (); + l.unlock (); + old.fini (); + } else { + l.unlock (); + } + } + + template + bool find (T v, item_t *i, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (item) + *i = *item; + l.unlock (); + return !!item; + } + + template + item_t *find_or_insert (T v, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (!item) { + item = items.push (v); + } + l.unlock (); + return item; + } + + void fini (lock_t &l) + { + if (!items.length) + { + /* No need to lock. */ + items.fini (); + return; + } + l.lock (); + while (items.length) + { + item_t old = items[items.length - 1]; + items.pop (); + l.unlock (); + old.fini (); + l.lock (); + } + items.fini (); + l.unlock (); + } + +}; + + +/* + * Reference-count. + */ + +#define HB_REFERENCE_COUNT_INERT_VALUE 0 +#define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD +#define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT (HB_REFERENCE_COUNT_INERT_VALUE)} + +struct hb_reference_count_t +{ + mutable hb_atomic_int_t ref_count; + + void init (int v = 1) { ref_count.set_relaxed (v); } + int get_relaxed () const { return ref_count.get_relaxed (); } + int inc () const { return ref_count.inc (); } + int dec () const { return ref_count.dec (); } + void fini () { ref_count.set_relaxed (HB_REFERENCE_COUNT_POISON_VALUE); } + + bool is_inert () const { return ref_count.get_relaxed () == HB_REFERENCE_COUNT_INERT_VALUE; } + bool is_valid () const { return ref_count.get_relaxed () > 0; } +}; + + +/* user_data */ + +struct hb_user_data_array_t +{ + struct hb_user_data_item_t { + hb_user_data_key_t *key; + void *data; + hb_destroy_func_t destroy; + + bool operator == (const hb_user_data_key_t *other_key) const { return key == other_key; } + bool operator == (const hb_user_data_item_t &other) const { return key == other.key; } + + void fini () { if (destroy) destroy (data); } + }; + + hb_mutex_t lock; + hb_lockable_set_t items; + + void init () { lock.init (); items.init (); } + + HB_INTERNAL bool set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + HB_INTERNAL void *get (hb_user_data_key_t *key); + + void fini () { items.fini (lock); lock.fini (); } +}; + + +/* + * Object header + */ + +struct hb_object_header_t +{ + hb_reference_count_t ref_count; + mutable hb_atomic_int_t writable; + hb_atomic_ptr_t user_data; +}; +#define HB_OBJECT_HEADER_STATIC \ + { \ + HB_REFERENCE_COUNT_INIT, \ + HB_ATOMIC_INT_INIT (false), \ + HB_ATOMIC_PTR_INIT (nullptr) \ + } + + +/* + * Object + */ + +template +static inline void hb_object_trace (const Type *obj, const char *function) +{ + DEBUG_MSG (OBJECT, (void *) obj, + "%s refcount=%d", + function, + obj ? obj->header.ref_count.get_relaxed () : 0); +} + +template +static inline Type *hb_object_create () +{ + Type *obj = (Type *) calloc (1, sizeof (Type)); + + if (unlikely (!obj)) + return obj; + + hb_object_init (obj); + hb_object_trace (obj, HB_FUNC); + return obj; +} +template +static inline void hb_object_init (Type *obj) +{ + obj->header.ref_count.init (); + obj->header.writable.set_relaxed (true); + obj->header.user_data.init (); +} +template +static inline bool hb_object_is_inert (const Type *obj) +{ + return unlikely (obj->header.ref_count.is_inert ()); +} +template +static inline bool hb_object_is_valid (const Type *obj) +{ + return likely (obj->header.ref_count.is_valid ()); +} +template +static inline bool hb_object_is_immutable (const Type *obj) +{ + return !obj->header.writable.get_relaxed (); +} +template +static inline void hb_object_make_immutable (const Type *obj) +{ + obj->header.writable.set_relaxed (false); +} +template +static inline Type *hb_object_reference (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || hb_object_is_inert (obj))) + return obj; + assert (hb_object_is_valid (obj)); + obj->header.ref_count.inc (); + return obj; +} +template +static inline bool hb_object_destroy (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || hb_object_is_inert (obj))) + return false; + assert (hb_object_is_valid (obj)); + if (obj->header.ref_count.dec () != 1) + return false; + + hb_object_fini (obj); + return true; +} +template +static inline void hb_object_fini (Type *obj) +{ + obj->header.ref_count.fini (); /* Do this before user_data */ + hb_user_data_array_t *user_data = obj->header.user_data.get (); + if (user_data) + { + user_data->fini (); + free (user_data); + user_data = nullptr; + } +} +template +static inline bool hb_object_set_user_data (Type *obj, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (unlikely (!obj || hb_object_is_inert (obj))) + return false; + assert (hb_object_is_valid (obj)); + +retry: + hb_user_data_array_t *user_data = obj->header.user_data.get (); + if (unlikely (!user_data)) + { + user_data = (hb_user_data_array_t *) calloc (sizeof (hb_user_data_array_t), 1); + if (unlikely (!user_data)) + return false; + user_data->init (); + if (unlikely (!obj->header.user_data.cmpexch (nullptr, user_data))) + { + user_data->fini (); + free (user_data); + goto retry; + } + } + + return user_data->set (key, data, destroy, replace); +} + +template +static inline void *hb_object_get_user_data (Type *obj, + hb_user_data_key_t *key) +{ + if (unlikely (!obj || hb_object_is_inert (obj))) + return nullptr; + assert (hb_object_is_valid (obj)); + hb_user_data_array_t *user_data = obj->header.user_data.get (); + if (!user_data) + return nullptr; + return user_data->get (key); +} + + +#endif /* HB_OBJECT_HH */ diff --git a/thirdparty/harfbuzz/src/hb-open-file.hh b/thirdparty/harfbuzz/src/hb-open-file.hh new file mode 100644 index 00000000000..ac13dd23c38 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-open-file.hh @@ -0,0 +1,521 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_FILE_HH +#define HB_OPEN_FILE_HH + +#include "hb-open-type.hh" +#include "hb-ot-head-table.hh" + + +namespace OT { + + +/* + * + * The OpenType Font File + * + */ + + +/* + * Organization of an OpenType Font + */ + +struct OpenTypeFontFile; +struct OffsetTable; +struct TTCHeader; + + +typedef struct TableRecord +{ + int cmp (Tag t) const { return -t.cmp (tag); } + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const TableRecord *a = (const TableRecord *) pa; + const TableRecord *b = (const TableRecord *) pb; + return b->cmp (a->tag); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + Tag tag; /* 4-byte identifier. */ + CheckSum checkSum; /* CheckSum for this table. */ + Offset32 offset; /* Offset from beginning of TrueType font + * file. */ + HBUINT32 length; /* Length of this table. */ + public: + DEFINE_SIZE_STATIC (16); +} OpenTypeTable; + +typedef struct OffsetTable +{ + friend struct OpenTypeFontFile; + + unsigned int get_table_count () const { return tables.len; } + const TableRecord& get_table (unsigned int i) const + { return tables[i]; } + unsigned int get_table_tags (unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) const + { + if (table_count) + { + + tables.sub_array (start_offset, table_count) + | hb_map (&TableRecord::tag) + | hb_sink (hb_array (table_tags, *table_count)) + ; + } + return tables.len; + } + bool find_table_index (hb_tag_t tag, unsigned int *table_index) const + { + Tag t; + t = tag; + return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + } + const TableRecord& get_table_by_tag (hb_tag_t tag) const + { + unsigned int table_index; + find_table_index (tag, &table_index); + return get_table (table_index); + } + + public: + + template + bool serialize (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + hb_array_t items) + { + TRACE_SERIALIZE (this); + /* Alloc 12 for the OTHeader. */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + /* Write sfntVersion (bytes 0..3). */ + sfnt_version = sfnt_tag; + /* Take space for numTables, searchRange, entrySelector, RangeShift + * and the TableRecords themselves. */ + if (unlikely (!tables.serialize (c, items.length))) return_trace (false); + + const char *dir_end = (const char *) c->head; + HBUINT32 *checksum_adjustment = nullptr; + + /* Write OffsetTables, alloc for and write actual table blobs. */ + for (unsigned int i = 0; i < tables.len; i++) + { + TableRecord &rec = tables.arrayZ[i]; + hb_blob_t *blob = items[i].blob; + rec.tag = items[i].tag; + rec.length = blob->length; + rec.offset.serialize (c, this); + + /* Allocate room for the table and copy it. */ + char *start = (char *) c->allocate_size (rec.length); + if (unlikely (!start)) return false; + + if (likely (rec.length)) + memcpy (start, blob->data, rec.length); + + /* 4-byte alignment. */ + c->align (4); + const char *end = (const char *) c->head; + + if (items[i].tag == HB_OT_TAG_head && + (unsigned) (end - start) >= head::static_size) + { + head *h = (head *) start; + checksum_adjustment = &h->checkSumAdjustment; + *checksum_adjustment = 0; + } + + rec.checkSum.set_for_data (start, end - start); + } + + tables.qsort (); + + if (checksum_adjustment) + { + CheckSum checksum; + + /* The following line is a slower version of the following block. */ + //checksum.set_for_data (this, (const char *) c->head - (const char *) this); + checksum.set_for_data (this, dir_end - (const char *) this); + for (unsigned int i = 0; i < items.length; i++) + { + TableRecord &rec = tables.arrayZ[i]; + checksum = checksum + rec.checkSum; + } + + *checksum_adjustment = 0xB1B0AFBAu - checksum; + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && tables.sanitize (c)); + } + + protected: + Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ + BinSearchArrayOf + tables; + public: + DEFINE_SIZE_ARRAY (12, tables); +} OpenTypeFontFace; + + +/* + * TrueType Collections + */ + +struct TTCHeaderVersion1 +{ + friend struct TTCHeader; + + unsigned int get_face_count () const { return table.len; } + const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (table.sanitize (c, this)); + } + + protected: + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0), + * 0x00010000u */ + LArrayOf> + table; /* Array of offsets to the OffsetTable for each font + * from the beginning of the file */ + public: + DEFINE_SIZE_ARRAY (12, table); +}; + +struct TTCHeader +{ + friend struct OpenTypeFontFile; + + private: + + unsigned int get_face_count () const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face_count (); + default:return 0; + } + } + const OpenTypeFontFace& get_face (unsigned int i) const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face (i); + default:return Null (OpenTypeFontFace); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.version.sanitize (c))) return_trace (false); + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return_trace (u.version1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + struct { + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), + * 0x00010000u or 0x00020000u */ + } header; + TTCHeaderVersion1 version1; + } u; +}; + +/* + * Mac Resource Fork + * + * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html + */ + +struct ResourceRecord +{ + const OpenTypeFontFace & get_face (const void *data_base) const + { return * reinterpret_cast ((data_base+offset).arrayZ); } + + bool sanitize (hb_sanitize_context_t *c, + const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + offset.sanitize (c, data_base) && + get_face (data_base).sanitize (c)); + } + + protected: + HBUINT16 id; /* Resource ID. */ + HBINT16 nameOffset; /* Offset from beginning of resource name list + * to resource name, -1 means there is none. */ + HBUINT8 attrs; /* Resource attributes */ + NNOffsetTo, HBUINT24> + offset; /* Offset from beginning of data block to + * data for this resource */ + HBUINT32 reserved; /* Reserved for handle to resource */ + public: + DEFINE_SIZE_STATIC (12); +}; + +#define HB_TAG_sfnt HB_TAG ('s','f','n','t') + +struct ResourceTypeRecord +{ + unsigned int get_resource_count () const + { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; } + + bool is_sfnt () const { return tag == HB_TAG_sfnt; } + + const ResourceRecord& get_resource_record (unsigned int i, + const void *type_base) const + { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; } + + bool sanitize (hb_sanitize_context_t *c, + const void *type_base, + const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + resourcesZ.sanitize (c, type_base, + get_resource_count (), + data_base)); + } + + protected: + Tag tag; /* Resource type. */ + HBUINT16 resCountM1; /* Number of resources minus 1. */ + NNOffsetTo> + resourcesZ; /* Offset from beginning of resource type list + * to reference item list for this type. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ResourceMap +{ + unsigned int get_face_count () const + { + unsigned int count = get_type_count (); + for (unsigned int i = 0; i < count; i++) + { + const ResourceTypeRecord& type = get_type_record (i); + if (type.is_sfnt ()) + return type.get_resource_count (); + } + return 0; + } + + const OpenTypeFontFace& get_face (unsigned int idx, + const void *data_base) const + { + unsigned int count = get_type_count (); + for (unsigned int i = 0; i < count; i++) + { + const ResourceTypeRecord& type = get_type_record (i); + /* The check for idx < count is here because ResourceRecord is NOT null-safe. + * Because an offset of 0 there does NOT mean null. */ + if (type.is_sfnt () && idx < type.get_resource_count ()) + return type.get_resource_record (idx, &(this+typeList)).get_face (data_base); + } + return Null (OpenTypeFontFace); + } + + bool sanitize (hb_sanitize_context_t *c, const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + typeList.sanitize (c, this, + &(this+typeList), + data_base)); + } + + private: + unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; } + + const ResourceTypeRecord& get_type_record (unsigned int i) const + { return (this+typeList)[i]; } + + protected: + HBUINT8 reserved0[16]; /* Reserved for copy of resource header */ + HBUINT32 reserved1; /* Reserved for handle to next resource map */ + HBUINT16 resreved2; /* Reserved for file reference number */ + HBUINT16 attrs; /* Resource fork attribute */ + NNOffsetTo> + typeList; /* Offset from beginning of map to + * resource type list */ + Offset16 nameList; /* Offset from beginning of map to + * resource name list */ + public: + DEFINE_SIZE_STATIC (28); +}; + +struct ResourceForkHeader +{ + unsigned int get_face_count () const + { return (this+map).get_face_count (); } + + const OpenTypeFontFace& get_face (unsigned int idx, + unsigned int *base_offset = nullptr) const + { + const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data)); + if (base_offset) + *base_offset = (const char *) &face - (const char *) this; + return face; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + data.sanitize (c, this, dataLen) && + map.sanitize (c, this, &(this+data))); + } + + protected: + LNNOffsetTo> + data; /* Offset from beginning of resource fork + * to resource data */ + LNNOffsetTo + map; /* Offset from beginning of resource fork + * to resource map */ + HBUINT32 dataLen; /* Length of resource data */ + HBUINT32 mapLen; /* Length of resource map */ + public: + DEFINE_SIZE_STATIC (16); +}; + +/* + * OpenType Font File + */ + +struct OpenTypeFontFile +{ + enum { + CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */ + TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */ + TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */ + DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */ + TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */ + Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ + }; + + hb_tag_t get_tag () const { return u.tag; } + + unsigned int get_face_count () const + { + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return 1; + case TTCTag: return u.ttcHeader.get_face_count (); + case DFontTag: return u.rfHeader.get_face_count (); + default: return 0; + } + } + const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const + { + if (base_offset) + *base_offset = 0; + switch (u.tag) { + /* Note: for non-collection SFNT data we ignore index. This is because + * Apple dfont container is a container of SFNT's. So each SFNT is a + * non-TTC, but the index is more than zero. */ + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return u.fontFace; + case TTCTag: return u.ttcHeader.get_face (i); + case DFontTag: return u.rfHeader.get_face (i, base_offset); + default: return Null (OpenTypeFontFace); + } + } + + template + bool serialize_single (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + hb_array_t items) + { + TRACE_SERIALIZE (this); + assert (sfnt_tag != TTCTag); + if (unlikely (!c->extend_min (*this))) return_trace (false); + return_trace (u.fontFace.serialize (c, sfnt_tag, items)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.tag.sanitize (c))) return_trace (false); + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); + case TTCTag: return_trace (u.ttcHeader.sanitize (c)); + case DFontTag: return_trace (u.rfHeader.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + Tag tag; /* 4-byte identifier. */ + OpenTypeFontFace fontFace; + TTCHeader ttcHeader; + ResourceForkHeader rfHeader; + } u; + public: + DEFINE_SIZE_UNION (4, tag); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_FILE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-open-type.hh b/thirdparty/harfbuzz/src/hb-open-type.hh new file mode 100644 index 00000000000..50558cf8d3d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-open-type.hh @@ -0,0 +1,1078 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_TYPE_HH +#define HB_OPEN_TYPE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-face.hh" +#include "hb-machinery.hh" +#include "hb-subset.hh" + + +namespace OT { + + +/* + * + * The OpenType Font File: Data Types + */ + + +/* "The following data types are used in the OpenType font file. + * All OpenType fonts use Motorola-style byte ordering (Big Endian):" */ + +/* + * Int types + */ + +/* Integer types in big-endian order and no alignment requirement */ +template +struct IntType +{ + typedef Type type; + typedef hb_conditional wide_type; + + IntType& operator = (wide_type i) { v = i; return *this; } + operator wide_type () const { return v; } + bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } + bool operator != (const IntType &o) const { return !(*this == o); } + + IntType& operator += (unsigned count) { *this = *this + count; return *this; } + IntType& operator -= (unsigned count) { *this = *this - count; return *this; } + IntType& operator ++ () { *this += 1; return *this; } + IntType& operator -- () { *this -= 1; return *this; } + IntType operator ++ (int) { IntType c (*this); ++*this; return c; } + IntType operator -- (int) { IntType c (*this); --*this; return c; } + + HB_INTERNAL static int cmp (const IntType *a, const IntType *b) + { return b->cmp (*a); } + HB_INTERNAL static int cmp (const void *a, const void *b) + { + IntType *pa = (IntType *) a; + IntType *pb = (IntType *) b; + + return pb->cmp (*pa); + } + template + int cmp (Type2 a) const + { + Type b = v; + if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int)) + return (int) a - (int) b; + else + return a < b ? -1 : a == b ? 0 : +1; + } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + protected: + BEInt v; + public: + DEFINE_SIZE_STATIC (Size); +}; + +typedef IntType HBUINT8; /* 8-bit unsigned integer. */ +typedef IntType HBINT8; /* 8-bit signed integer. */ +typedef IntType HBUINT16; /* 16-bit unsigned integer. */ +typedef IntType HBINT16; /* 16-bit signed integer. */ +typedef IntType HBUINT32; /* 32-bit unsigned integer. */ +typedef IntType HBINT32; /* 32-bit signed integer. */ +/* Note: we cannot defined a signed HBINT24 because there's no corresponding C type. + * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */ +typedef IntType HBUINT24; /* 24-bit unsigned integer. */ + +/* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */ +typedef HBINT16 FWORD; + +/* 32-bit signed integer (HBINT32) that describes a quantity in FUnits. */ +typedef HBINT32 FWORD32; + +/* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */ +typedef HBUINT16 UFWORD; + +/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ +struct F2DOT14 : HBINT16 +{ + F2DOT14& operator = (uint16_t i ) { HBINT16::operator= (i); return *this; } + // 16384 means 1<<14 + float to_float () const { return ((int32_t) v) / 16384.f; } + void set_float (float f) { v = roundf (f * 16384.f); } + public: + DEFINE_SIZE_STATIC (2); +}; + +/* 32-bit signed fixed-point number (16.16). */ +struct HBFixed : HBINT32 +{ + HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; } + // 65536 means 1<<16 + float to_float () const { return ((int32_t) v) / 65536.f; } + void set_float (float f) { v = roundf (f * 65536.f); } + public: + DEFINE_SIZE_STATIC (4); +}; + +/* Date represented in number of seconds since 12:00 midnight, January 1, + * 1904. The value is represented as a signed 64-bit integer. */ +struct LONGDATETIME +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + protected: + HBINT32 major; + HBUINT32 minor; + public: + DEFINE_SIZE_STATIC (8); +}; + +/* Array of four uint8s (length = 32 bits) used to identify a script, language + * system, feature, or baseline */ +struct Tag : HBUINT32 +{ + Tag& operator = (hb_tag_t i) { HBUINT32::operator= (i); return *this; } + /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ + operator const char* () const { return reinterpret_cast (&this->v); } + operator char* () { return reinterpret_cast (&this->v); } + public: + DEFINE_SIZE_STATIC (4); +}; + +/* Glyph index number, same as uint16 (length = 16 bits) */ +struct HBGlyphID : HBUINT16 +{ + HBGlyphID& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; + +/* Script/language-system/feature index */ +struct Index : HBUINT16 { + static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFu; + Index& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, Index); + +typedef Index NameID; + +/* Offset, Null offset = 0 */ +template +struct Offset : Type +{ + Offset& operator = (typename Type::type i) { Type::operator= (i); return *this; } + + typedef Type type; + + bool is_null () const { return has_null && 0 == *this; } + + void *serialize (hb_serialize_context_t *c, const void *base) + { + void *t = c->start_embed (); + c->check_assign (*this, (unsigned) ((char *) t - (char *) base)); + return t; + } + + public: + DEFINE_SIZE_STATIC (sizeof (Type)); +}; + +typedef Offset Offset16; +typedef Offset Offset32; + + +/* CheckSum */ +struct CheckSum : HBUINT32 +{ + CheckSum& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } + + /* This is reference implementation from the spec. */ + static uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length) + { + uint32_t Sum = 0L; + assert (0 == (Length & 3)); + const HBUINT32 *EndPtr = Table + Length / HBUINT32::static_size; + + while (Table < EndPtr) + Sum += *Table++; + return Sum; + } + + /* Note: data should be 4byte aligned and have 4byte padding at the end. */ + void set_for_data (const void *data, unsigned int length) + { *this = CalcTableChecksum ((const HBUINT32 *) data, length); } + + public: + DEFINE_SIZE_STATIC (4); +}; + + +/* + * Version Numbers + */ + +template +struct FixedVersion +{ + uint32_t to_int () const { return (major << (sizeof (FixedType) * 8)) + minor; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FixedType major; + FixedType minor; + public: + DEFINE_SIZE_STATIC (2 * sizeof (FixedType)); +}; + + +/* + * Template subclasses of Offset that do the dereferencing. + * Use: (base+offset) + */ + +template +struct _hb_has_null +{ + static const Type *get_null () { return nullptr; } + static Type *get_crap () { return nullptr; } +}; +template +struct _hb_has_null +{ + static const Type *get_null () { return &Null (Type); } + static Type *get_crap () { return &Crap (Type); } +}; + +template +struct OffsetTo : Offset +{ + HB_DELETE_COPY_ASSIGN (OffsetTo); + OffsetTo () = default; + + OffsetTo& operator = (typename OffsetType::type i) { OffsetType::operator= (i); return *this; } + + const Type& operator () (const void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null::get_null (); + return StructAtOffset (base, *this); + } + Type& operator () (void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null::get_crap (); + return StructAtOffset (base, *this); + } + + template + friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } + template + friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } + template + friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } + template + friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } + + Type& serialize (hb_serialize_context_t *c, const void *base) + { + return * (Type *) Offset::serialize (c, base); + } + + template + bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, + const void *src_base, Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + auto *s = c->serializer; + + s->push (); + + bool ret = c->dispatch (src_base+src, hb_forward (ds)...); + + if (ret || !has_null) + s->add_link (*this, s->pop_pack ()); + else + s->pop_discard (); + + return ret; + } + + /* TODO: Somehow merge this with previous function into a serialize_dispatch(). */ + /* Workaround clang bug: https://bugs.llvm.org/show_bug.cgi?id=23029 + * Can't compile: whence = hb_serialize_context_t::Head followed by Ts&&... + */ + template + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias, + hb_serialize_context_t::whence_t whence, + Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + c->push (); + + bool ret = c->copy (src_base+src, hb_forward (ds)...); + + c->add_link (*this, c->pop_pack (), whence, dst_bias); + + return ret; + } + + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias = 0) + { return serialize_copy (c, src, src_base, dst_bias, hb_serialize_context_t::Head); } + + bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + if (unlikely (this->is_null ())) return_trace (true); + if (unlikely (!c->check_range (base, *this))) return_trace (false); + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (sanitize_shallow (c, base) && + (this->is_null () || + c->dispatch (StructAtOffset (base, *this), hb_forward (ds)...) || + neuter (c))); + } + + /* Set the offset to Null */ + bool neuter (hb_sanitize_context_t *c) const + { + if (!has_null) return false; + return c->try_set (this, 0); + } + DEFINE_SIZE_STATIC (sizeof (OffsetType)); +}; +/* Partial specializations. */ +template +using LOffsetTo = OffsetTo; +template +using NNOffsetTo = OffsetTo; +template +using LNNOffsetTo = LOffsetTo; + + +/* + * Array Types + */ + +template +struct UnsizedArrayOf +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + + HB_DELETE_CREATE_COPY_ASSIGN (UnsizedArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + const Type *p = &arrayZ[i]; + if (unlikely (p < arrayZ)) return Null (Type); /* Overflowed. */ + return *p; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + Type *p = &arrayZ[i]; + if (unlikely (p < arrayZ)) return Crap (Type); /* Overflowed. */ + return *p; + } + + unsigned int get_size (unsigned int len) const + { return len * Type::static_size; } + + template operator T * () { return arrayZ; } + template operator const T * () const { return arrayZ; } + hb_array_t as_array (unsigned int len) + { return hb_array (arrayZ, len); } + hb_array_t as_array (unsigned int len) const + { return hb_array (arrayZ, len); } + operator hb_array_t< Type> () { return as_array (); } + operator hb_array_t () const { return as_array (); } + + template + Type &lsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).lsearch (x, ¬_found); } + template + const Type &lsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).lsearch (x, ¬_found); } + template + bool lfind (unsigned int len, const T &x, unsigned *pos = nullptr) const + { return as_array (len).lfind (x, pos); } + + void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) + { as_array (len).qsort (start, end); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend (*this, items_len))) return_trace (false); + return_trace (true); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + UnsizedArrayOf* copy (hb_serialize_context_t *c, unsigned count) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!as_array (count).copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) + return_trace (false); + return_trace (true); + } + + bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_array (arrayZ, count)); + } + + public: + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_UNBOUNDED (0); +}; + +/* Unsized array of offset's */ +template +using UnsizedOffsetArrayOf = UnsizedArrayOf>; + +/* Unsized array of offsets relative to the beginning of the array itself. */ +template +struct UnsizedOffsetListOf : UnsizedOffsetArrayOf +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + const OffsetTo *p = &this->arrayZ[i]; + if (unlikely (p < this->arrayZ)) return Null (Type); /* Overflowed. */ + return this+*p; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + const OffsetTo *p = &this->arrayZ[i]; + if (unlikely (p < this->arrayZ)) return Crap (Type); /* Overflowed. */ + return this+*p; + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((UnsizedOffsetArrayOf + ::sanitize (c, count, this, hb_forward (ds)...))); + } +}; + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedUnsizedArrayOf : UnsizedArrayOf +{ + hb_sorted_array_t as_array (unsigned int len) + { return hb_sorted_array (this->arrayZ, len); } + hb_sorted_array_t as_array (unsigned int len) const + { return hb_sorted_array (this->arrayZ, len); } + operator hb_sorted_array_t () { return as_array (); } + operator hb_sorted_array_t () const { return as_array (); } + + template + Type &bsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).bsearch (x, ¬_found); } + template + const Type &bsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).bsearch (x, ¬_found); } + template + bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr, + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array (len).bfind (x, i, not_found, to_store); } +}; + + +/* An array with a number of elements. */ +template +struct ArrayOf +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= len)) return Null (Type); + return arrayZ[i]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= len)) return Crap (Type); + return arrayZ[i]; + } + + unsigned int get_size () const + { return len.static_size + len * Type::static_size; } + + explicit operator bool () const { return len; } + + void pop () { len--; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, len); } + hb_array_t as_array () const { return hb_array (arrayZ, len); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + hb_array_t sub_array (unsigned int start_offset, unsigned int count) const + { return as_array ().sub_array (start_offset, count); } + hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const + { return as_array ().sub_array (start_offset, count); } + hb_array_t sub_array (unsigned int start_offset, unsigned int count) + { return as_array ().sub_array (start_offset, count); } + hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) + { return as_array ().sub_array (start_offset, count); } + + hb_success_t serialize (hb_serialize_context_t *c, unsigned items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + c->check_assign (len, items_len); + if (unlikely (!c->extend (*this))) return_trace (false); + return_trace (true); + } + template + hb_success_t serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + Type* serialize_append (hb_serialize_context_t *c) + { + TRACE_SERIALIZE (this); + len++; + if (unlikely (!len || !c->extend (*this))) + { + len--; + return_trace (nullptr); + } + return_trace (&arrayZ[len - 1]); + } + + ArrayOf* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (nullptr); + c->check_assign (out->len, len); + if (unlikely (!as_array ().copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) + return_trace (false); + return_trace (true); + } + + template + Type &lsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().lsearch (x, ¬_found); } + template + const Type &lsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().lsearch (x, ¬_found); } + template + bool lfind (const T &x, unsigned *pos = nullptr) const + { return as_array ().lfind (x, pos); } + + void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) + { as_array ().qsort (start, end); } + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (len.sanitize (c) && c->check_array (arrayZ, len)); + } + + public: + LenType len; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; +template +using LArrayOf = ArrayOf; +using PString = ArrayOf; + +/* Array of Offset's */ +template +using OffsetArrayOf = ArrayOf>; +template +using LOffsetArrayOf = ArrayOf>; +template +using LOffsetLArrayOf = ArrayOf, HBUINT32>; + +/* Array of offsets relative to the beginning of the array itself. */ +template +struct OffsetListOf : OffsetArrayOf +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= this->len)) return Null (Type); + return this+this->arrayZ[i]; + } + const Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= this->len)) return Crap (Type); + return this+this->arrayZ[i]; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + struct OffsetListOf *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + unsigned int count = this->len; + for (unsigned int i = 0; i < count; i++) + out->arrayZ[i].serialize_subset (c, this->arrayZ[i], this, out); + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (OffsetArrayOf::sanitize (c, this, hb_forward (ds)...)); + } +}; + +/* An array starting at second element. */ +template +struct HeadlessArrayOf +{ + static constexpr unsigned item_size = Type::static_size; + + HB_DELETE_CREATE_COPY_ASSIGN (HeadlessArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= lenP1 || !i)) return Null (Type); + return arrayZ[i-1]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= lenP1 || !i)) return Crap (Type); + return arrayZ[i-1]; + } + unsigned int get_size () const + { return lenP1.static_size + get_length () * Type::static_size; } + + unsigned get_length () const { return lenP1 ? lenP1 - 1 : 0; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, get_length ()); } + hb_array_t as_array () const { return hb_array (arrayZ, get_length ()); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + c->check_assign (lenP1, items_len + 1); + if (unlikely (!c->extend (*this))) return_trace (false); + return_trace (true); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) + return_trace (false); + return_trace (true); + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (lenP1.sanitize (c) && + (!lenP1 || c->check_array (arrayZ, lenP1 - 1))); + } + + public: + LenType lenP1; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; + +/* An array storing length-1. */ +template +struct ArrayOfM1 +{ + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOfM1); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i > lenM1)) return Null (Type); + return arrayZ[i]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i > lenM1)) return Crap (Type); + return arrayZ[i]; + } + unsigned int get_size () const + { return lenM1.static_size + (lenM1 + 1) * Type::static_size; } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + unsigned int count = lenM1 + 1; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) + return_trace (false); + return_trace (true); + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (lenM1.sanitize (c) && + (c->check_array (arrayZ, lenM1 + 1))); + } + + public: + LenType lenM1; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedArrayOf : ArrayOf +{ + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->len); } + hb_sorted_array_t as_array () const { return hb_sorted_array (this->arrayZ, this->len); } + + /* Iterator. */ + typedef hb_sorted_array_t iter_t; + typedef hb_sorted_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) const + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) + { return as_array ().sub_array (start_offset, count); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items_len); + return_trace (ret); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items); + return_trace (ret); + } + + template + Type &bsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().bsearch (x, ¬_found); } + template + const Type &bsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().bsearch (x, ¬_found); } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().bfind (x, i, not_found, to_store); } +}; + +/* + * Binary-search arrays + */ + +template +struct BinSearchHeader +{ + operator uint32_t () const { return len; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + BinSearchHeader& operator = (unsigned int v) + { + len = v; + assert (len == v); + entrySelector = hb_max (1u, hb_bit_storage (v)) - 1; + searchRange = 16 * (1u << entrySelector); + rangeShift = v * 16 > searchRange + ? 16 * v - searchRange + : 0; + return *this; + } + + protected: + LenType len; + LenType searchRange; + LenType entrySelector; + LenType rangeShift; + + public: + DEFINE_SIZE_STATIC (8); +}; + +template +using BinSearchArrayOf = SortedArrayOf>; + + +struct VarSizedBinSearchHeader +{ + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 unitSize; /* Size of a lookup unit for this search in bytes. */ + HBUINT16 nUnits; /* Number of units of the preceding size to be searched. */ + HBUINT16 searchRange; /* The value of unitSize times the largest power of 2 + * that is less than or equal to the value of nUnits. */ + HBUINT16 entrySelector; /* The log base 2 of the largest power of 2 less than + * or equal to the value of nUnits. */ + HBUINT16 rangeShift; /* The value of unitSize times the difference of the + * value of nUnits minus the largest power of 2 less + * than or equal to the value of nUnits. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +template +struct VarSizedBinSearchArrayOf +{ + static constexpr unsigned item_size = Type::static_size; + + HB_DELETE_CREATE_COPY_ASSIGN (VarSizedBinSearchArrayOf); + + bool last_is_terminator () const + { + if (unlikely (!header.nUnits)) return false; + + /* Gah. + * + * "The number of termination values that need to be included is table-specific. + * The value that indicates binary search termination is 0xFFFF." */ + const HBUINT16 *words = &StructAtOffset (&bytesZ, (header.nUnits - 1) * header.unitSize); + unsigned int count = Type::TerminationWordCount; + for (unsigned int i = 0; i < count; i++) + if (words[i] != 0xFFFFu) + return false; + return true; + } + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= get_length ())) return Null (Type); + return StructAtOffset (&bytesZ, i * header.unitSize); + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= get_length ())) return Crap (Type); + return StructAtOffset (&bytesZ, i * header.unitSize); + } + unsigned int get_length () const + { return header.nUnits - last_is_terminator (); } + unsigned int get_size () const + { return header.static_size + header.nUnits * header.unitSize; } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(*this)[i].sanitize (c, hb_forward (ds)...))) + return_trace (false); + return_trace (true); + } + + template + const Type *bsearch (const T &key) const + { + unsigned pos; + return hb_bsearch_impl (&pos, + key, + (const void *) bytesZ, + get_length (), + header.unitSize, + _hb_cmp_method) + ? (const Type *) (((const char *) &bytesZ) + (pos * header.unitSize)) + : nullptr; + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (header.sanitize (c) && + Type::static_size <= header.unitSize && + c->check_range (bytesZ.arrayZ, + header.nUnits, + header.unitSize)); + } + + protected: + VarSizedBinSearchHeader header; + UnsizedArrayOf bytesZ; + public: + DEFINE_SIZE_ARRAY (10, bytesZ); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_TYPE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh new file mode 100644 index 00000000000..e5286cd792d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh @@ -0,0 +1,622 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_OT_CFF_COMMON_HH +#define HB_OT_CFF_COMMON_HH + +#include "hb-open-type.hh" +#include "hb-bimap.hh" +#include "hb-ot-layout-common.hh" +#include "hb-cff-interp-dict-common.hh" +#include "hb-subset-plan.hh" + +namespace CFF { + +using namespace OT; + +#define CFF_UNDEF_CODE 0xFFFFFFFF + +using objidx_t = hb_serialize_context_t::objidx_t; +using whence_t = hb_serialize_context_t::whence_t; + +/* utility macro */ +template +static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) +{ return offset ? StructAtOffset (P, offset) : Null (Type); } + +inline unsigned int calcOffSize (unsigned int dataSize) +{ + unsigned int size = 1; + unsigned int offset = dataSize + 1; + while (offset & ~0xFF) + { + size++; + offset >>= 8; + } + /* format does not support size > 4; caller should handle it as an error */ + return size; +} + +struct code_pair_t +{ + hb_codepoint_t code; + hb_codepoint_t glyph; +}; + +typedef hb_vector_t str_buff_t; +struct str_buff_vec_t : hb_vector_t +{ + void fini () { SUPER::fini_deep (); } + + unsigned int total_size () const + { + unsigned int size = 0; + for (unsigned int i = 0; i < length; i++) + size += (*this)[i].length; + return size; + } + + private: + typedef hb_vector_t SUPER; +}; + +/* CFF INDEX */ +template +struct CFFIndex +{ + static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count) + { return offSize * (count + 1); } + + unsigned int offset_array_size () const + { return calculate_offset_array_size (offSize, count); } + + CFFIndex *copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + unsigned int size = get_size (); + CFFIndex *out = c->allocate_size (size); + if (likely (out)) + memcpy (out, this, size); + return_trace (out); + } + + bool serialize (hb_serialize_context_t *c, const CFFIndex &src) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (); + CFFIndex *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const byte_str_array_t &byteArray) + { + TRACE_SERIALIZE (this); + if (byteArray.length == 0) + { + COUNT *dest = c->allocate_min (); + if (unlikely (!dest)) return_trace (false); + *dest = 0; + } + else + { + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + this->count = byteArray.length; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (byteArray.length + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (; i < byteArray.length; i++) + { + set_offset_at (i, offset); + offset += byteArray[i].get_size (); + } + set_offset_at (i, offset); + + /* serialize data */ + for (unsigned int i = 0; i < byteArray.length; i++) + { + const byte_str_t &bs = byteArray[i]; + unsigned char *dest = c->allocate_size (bs.length); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &bs[0], bs.length); + } + } + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const str_buff_vec_t &buffArray) + { + byte_str_array_t byteArray; + byteArray.init (); + byteArray.resize (buffArray.length); + for (unsigned int i = 0; i < byteArray.length; i++) + byteArray[i] = byte_str_t (buffArray[i].arrayZ, buffArray[i].length); + bool result = this->serialize (c, offSize_, byteArray); + byteArray.fini (); + return result; + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + if (it.len () == 0) + { + COUNT *dest = c->allocate_min (); + if (unlikely (!dest)) return_trace (false); + *dest = 0; + } + else + { + serialize_header(c, + it | hb_map ([] (const byte_str_t &_) { return _.length; })); + for (const byte_str_t &_ : +it) + _.copy (c); + } + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + const byte_str_array_t &byteArray) + { return serialize (c, + hb_iter (byteArray)); } + + bool serialize (hb_serialize_context_t *c, + const str_buff_vec_t &buffArray) + { + auto it = + + hb_iter (buffArray) + | hb_map ([] (const str_buff_t &_) { return byte_str_t (_.arrayZ, _.length); }) + ; + return serialize (c, it); + } + + template + bool serialize_header (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + + unsigned total = + it | hb_reduce (hb_add, 0); + unsigned off_size = calcOffSize (total); + + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + this->count = it.len (); + this->offSize = off_size; + if (unlikely (!c->allocate_size (off_size * (it.len () + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (unsigned _ : +it) + { + CFFIndex::set_offset_at (i++, offset); + offset += _; + } + CFFIndex::set_offset_at (i, offset); + + return_trace (true); + } + + void set_offset_at (unsigned int index, unsigned int offset) + { + HBUINT8 *p = offsets + offSize * index + offSize; + unsigned int size = offSize; + for (; size; size--) + { + --p; + *p = offset & 0xFF; + offset >>= 8; + } + } + + unsigned int offset_at (unsigned int index) const + { + assert (index <= count); + const HBUINT8 *p = offsets + offSize * index; + unsigned int size = offSize; + unsigned int offset = 0; + for (; size; size--) + offset = (offset << 8) + *p++; + return offset; + } + + unsigned int length_at (unsigned int index) const + { + if (unlikely ((offset_at (index + 1) < offset_at (index)) || + (offset_at (index + 1) > offset_at (count)))) + return 0; + return offset_at (index + 1) - offset_at (index); + } + + const unsigned char *data_base () const + { return (const unsigned char *) this + min_size + offset_array_size (); } + + unsigned int data_size () const { return HBINT8::static_size; } + + byte_str_t operator [] (unsigned int index) const + { + if (unlikely (index >= count)) return Null (byte_str_t); + return byte_str_t (data_base () + offset_at (index) - 1, length_at (index)); + } + + unsigned int get_size () const + { + if (this == &Null (CFFIndex)) return 0; + if (count > 0) + return min_size + offset_array_size () + (offset_at (count) - 1); + return count.static_size; /* empty CFFIndex contains count only */ + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely ((c->check_struct (this) && count == 0) || /* empty INDEX */ + (c->check_struct (this) && offSize >= 1 && offSize <= 4 && + c->check_array (offsets, offSize, count + 1) && + c->check_array ((const HBUINT8*) data_base (), 1, max_offset () - 1)))); + } + + protected: + unsigned int max_offset () const + { + unsigned int max = 0; + for (unsigned int i = 0; i < count + 1u; i++) + { + unsigned int off = offset_at (i); + if (off > max) max = off; + } + return max; + } + + public: + COUNT count; /* Number of object data. Note there are (count+1) offsets */ + HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ + HBUINT8 offsets[HB_VAR_ARRAY]; + /* The array of (count + 1) offsets into objects array (1-base). */ + /* HBUINT8 data[HB_VAR_ARRAY]; Object data */ + public: + DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets); +}; + +template +struct CFFIndexOf : CFFIndex +{ + const byte_str_t operator [] (unsigned int index) const + { + if (likely (index < CFFIndex::count)) + return byte_str_t (CFFIndex::data_base () + CFFIndex::offset_at (index) - 1, CFFIndex::length_at (index)); + return Null (byte_str_t); + } + + template + bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const DATA *dataArray, + unsigned int dataArrayLen, + const hb_vector_t &dataSizeArray, + const PARAM1 ¶m1, + const PARAM2 ¶m2) + { + TRACE_SERIALIZE (this); + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + this->count = dataArrayLen; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (; i < dataArrayLen; i++) + { + CFFIndex::set_offset_at (i, offset); + offset += dataSizeArray[i]; + } + CFFIndex::set_offset_at (i, offset); + + /* serialize data */ + for (unsigned int i = 0; i < dataArrayLen; i++) + { + TYPE *dest = c->start_embed (); + if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2))) + return_trace (false); + } + return_trace (true); + } +}; + +/* Top Dict, Font Dict, Private Dict */ +struct Dict : UnsizedByteStr +{ + template + bool serialize (hb_serialize_context_t *c, + const DICTVAL &dictval, + OP_SERIALIZER& opszr, + Ts&&... ds) + { + TRACE_SERIALIZE (this); + for (unsigned int i = 0; i < dictval.get_count (); i++) + if (unlikely (!opszr.serialize (c, dictval[i], hb_forward (ds)...))) + return_trace (false); + + return_trace (true); + } + + template + static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) + { + // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation + if (/*unlikely*/ (!serialize_int (c, intOp, value))) + return false; + + TRACE_SERIALIZE (this); + /* serialize the opcode */ + HBUINT8 *p = c->allocate_size (OpCode_Size (op)); + if (unlikely (!p)) return_trace (false); + if (Is_OpCode_ESC (op)) + { + *p = OpCode_escape; + op = Unmake_OpCode_ESC (op); + p++; + } + *p = op; + return_trace (true); + } + + template + static bool serialize_int4_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_longintdict); } + + template + static bool serialize_int2_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_shortint); } + + template + static bool serialize_link_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence) + { + T &ofs = *(T *) (c->head + OpCode_Size (int_op)); + if (unlikely (!serialize_int_op (c, op, 0, int_op))) return false; + c->add_link (ofs, link, whence); + return true; + } + + static bool serialize_link4_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } + + static bool serialize_link2_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } +}; + +struct TopDict : Dict {}; +struct FontDict : Dict {}; +struct PrivateDict : Dict {}; + +struct table_info_t +{ + void init () { offset = size = 0; link = 0; } + + unsigned int offset; + unsigned int size; + objidx_t link; +}; + +template +struct FDArray : CFFIndexOf +{ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + OP_SERIALIZER& opszr) + { + TRACE_SERIALIZE (this); + + /* serialize INDEX data */ + hb_vector_t sizes; + c->push (); + + it + | hb_map ([&] (const hb_pair_t &_) + { + FontDict *dict = c->start_embed (); + dict->serialize (c, _.first, opszr, _.second); + return c->head - (const char*)dict; + }) + | hb_sink (sizes) + ; + c->pop_pack (false); + + /* serialize INDEX header */ + return_trace (CFFIndex::serialize_header (c, hb_iter (sizes))); + } +}; + +/* FDSelect */ +struct FDSelect0 { + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this)))) + return_trace (false); + for (unsigned int i = 0; i < c->get_num_glyphs (); i++) + if (unlikely (!fds[i].sanitize (c))) + return_trace (false); + + return_trace (true); + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { return (hb_codepoint_t) fds[glyph]; } + + unsigned int get_size (unsigned int num_glyphs) const + { return HBUINT8::static_size * num_glyphs; } + + HBUINT8 fds[HB_VAR_ARRAY]; + + DEFINE_SIZE_MIN (0); +}; + +template +struct FDSelect3_4_Range +{ + bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + return_trace (first < c->get_num_glyphs () && (fd < fdcount)); + } + + GID_TYPE first; + FD_TYPE fd; + public: + DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size); +}; + +template +struct FDSelect3_4 +{ + unsigned int get_size () const + { return GID_TYPE::static_size * 2 + ranges.get_size (); } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) || + (nRanges () == 0) || ranges[0].first != 0)) + return_trace (false); + + for (unsigned int i = 1; i < nRanges (); i++) + if (unlikely (ranges[i - 1].first >= ranges[i].first)) + return_trace (false); + + if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ()))) + return_trace (false); + + return_trace (true); + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + unsigned int i; + for (i = 1; i < nRanges (); i++) + if (glyph < ranges[i].first) + break; + + return (hb_codepoint_t) ranges[i - 1].fd; + } + + GID_TYPE &nRanges () { return ranges.len; } + GID_TYPE nRanges () const { return ranges.len; } + GID_TYPE &sentinel () { return StructAfter (ranges[nRanges () - 1]); } + const GID_TYPE &sentinel () const { return StructAfter (ranges[nRanges () - 1]); } + + ArrayOf, GID_TYPE> ranges; + /* GID_TYPE sentinel */ + + DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges); +}; + +typedef FDSelect3_4 FDSelect3; +typedef FDSelect3_4_Range FDSelect3_Range; + +struct FDSelect +{ + bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + FDSelect *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + default:return 0; + } + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + if (this == &Null (FDSelect)) return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + FDSelect0 format0; + FDSelect3 format3; + } u; + public: + DEFINE_SIZE_MIN (1); +}; + +template +struct Subrs : CFFIndex +{ + typedef COUNT count_type; + typedef CFFIndex SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_OT_CFF_COMMON_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-cff1-std-str.hh b/thirdparty/harfbuzz/src/hb-ot-cff1-std-str.hh new file mode 100644 index 00000000000..65d56ae18b5 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-cff1-std-str.hh @@ -0,0 +1,425 @@ +/* + * Copyright © 2019 Adobe, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_STD_STR_HH +#if 0 /* Make checks happy. */ +#define HB_OT_CFF1_STD_STR_HH +#include "hb.hh" +#endif + +_S(".notdef") +_S("space") +_S("exclam") +_S("quotedbl") +_S("numbersign") +_S("dollar") +_S("percent") +_S("ampersand") +_S("quoteright") +_S("parenleft") +_S("parenright") +_S("asterisk") +_S("plus") +_S("comma") +_S("hyphen") +_S("period") +_S("slash") +_S("zero") +_S("one") +_S("two") +_S("three") +_S("four") +_S("five") +_S("six") +_S("seven") +_S("eight") +_S("nine") +_S("colon") +_S("semicolon") +_S("less") +_S("equal") +_S("greater") +_S("question") +_S("at") +_S("A") +_S("B") +_S("C") +_S("D") +_S("E") +_S("F") +_S("G") +_S("H") +_S("I") +_S("J") +_S("K") +_S("L") +_S("M") +_S("N") +_S("O") +_S("P") +_S("Q") +_S("R") +_S("S") +_S("T") +_S("U") +_S("V") +_S("W") +_S("X") +_S("Y") +_S("Z") +_S("bracketleft") +_S("backslash") +_S("bracketright") +_S("asciicircum") +_S("underscore") +_S("quoteleft") +_S("a") +_S("b") +_S("c") +_S("d") +_S("e") +_S("f") +_S("g") +_S("h") +_S("i") +_S("j") +_S("k") +_S("l") +_S("m") +_S("n") +_S("o") +_S("p") +_S("q") +_S("r") +_S("s") +_S("t") +_S("u") +_S("v") +_S("w") +_S("x") +_S("y") +_S("z") +_S("braceleft") +_S("bar") +_S("braceright") +_S("asciitilde") +_S("exclamdown") +_S("cent") +_S("sterling") +_S("fraction") +_S("yen") +_S("florin") +_S("section") +_S("currency") +_S("quotesingle") +_S("quotedblleft") +_S("guillemotleft") +_S("guilsinglleft") +_S("guilsinglright") +_S("fi") +_S("fl") +_S("endash") +_S("dagger") +_S("daggerdbl") +_S("periodcentered") +_S("paragraph") +_S("bullet") +_S("quotesinglbase") +_S("quotedblbase") +_S("quotedblright") +_S("guillemotright") +_S("ellipsis") +_S("perthousand") +_S("questiondown") +_S("grave") +_S("acute") +_S("circumflex") +_S("tilde") +_S("macron") +_S("breve") +_S("dotaccent") +_S("dieresis") +_S("ring") +_S("cedilla") +_S("hungarumlaut") +_S("ogonek") +_S("caron") +_S("emdash") +_S("AE") +_S("ordfeminine") +_S("Lslash") +_S("Oslash") +_S("OE") +_S("ordmasculine") +_S("ae") +_S("dotlessi") +_S("lslash") +_S("oslash") +_S("oe") +_S("germandbls") +_S("onesuperior") +_S("logicalnot") +_S("mu") +_S("trademark") +_S("Eth") +_S("onehalf") +_S("plusminus") +_S("Thorn") +_S("onequarter") +_S("divide") +_S("brokenbar") +_S("degree") +_S("thorn") +_S("threequarters") +_S("twosuperior") +_S("registered") +_S("minus") +_S("eth") +_S("multiply") +_S("threesuperior") +_S("copyright") +_S("Aacute") +_S("Acircumflex") +_S("Adieresis") +_S("Agrave") +_S("Aring") +_S("Atilde") +_S("Ccedilla") +_S("Eacute") +_S("Ecircumflex") +_S("Edieresis") +_S("Egrave") +_S("Iacute") +_S("Icircumflex") +_S("Idieresis") +_S("Igrave") +_S("Ntilde") +_S("Oacute") +_S("Ocircumflex") +_S("Odieresis") +_S("Ograve") +_S("Otilde") +_S("Scaron") +_S("Uacute") +_S("Ucircumflex") +_S("Udieresis") +_S("Ugrave") +_S("Yacute") +_S("Ydieresis") +_S("Zcaron") +_S("aacute") +_S("acircumflex") +_S("adieresis") +_S("agrave") +_S("aring") +_S("atilde") +_S("ccedilla") +_S("eacute") +_S("ecircumflex") +_S("edieresis") +_S("egrave") +_S("iacute") +_S("icircumflex") +_S("idieresis") +_S("igrave") +_S("ntilde") +_S("oacute") +_S("ocircumflex") +_S("odieresis") +_S("ograve") +_S("otilde") +_S("scaron") +_S("uacute") +_S("ucircumflex") +_S("udieresis") +_S("ugrave") +_S("yacute") +_S("ydieresis") +_S("zcaron") +_S("exclamsmall") +_S("Hungarumlautsmall") +_S("dollaroldstyle") +_S("dollarsuperior") +_S("ampersandsmall") +_S("Acutesmall") +_S("parenleftsuperior") +_S("parenrightsuperior") +_S("twodotenleader") +_S("onedotenleader") +_S("zerooldstyle") +_S("oneoldstyle") +_S("twooldstyle") +_S("threeoldstyle") +_S("fouroldstyle") +_S("fiveoldstyle") +_S("sixoldstyle") +_S("sevenoldstyle") +_S("eightoldstyle") +_S("nineoldstyle") +_S("commasuperior") +_S("threequartersemdash") +_S("periodsuperior") +_S("questionsmall") +_S("asuperior") +_S("bsuperior") +_S("centsuperior") +_S("dsuperior") +_S("esuperior") +_S("isuperior") +_S("lsuperior") +_S("msuperior") +_S("nsuperior") +_S("osuperior") +_S("rsuperior") +_S("ssuperior") +_S("tsuperior") +_S("ff") +_S("ffi") +_S("ffl") +_S("parenleftinferior") +_S("parenrightinferior") +_S("Circumflexsmall") +_S("hyphensuperior") +_S("Gravesmall") +_S("Asmall") +_S("Bsmall") +_S("Csmall") +_S("Dsmall") +_S("Esmall") +_S("Fsmall") +_S("Gsmall") +_S("Hsmall") +_S("Ismall") +_S("Jsmall") +_S("Ksmall") +_S("Lsmall") +_S("Msmall") +_S("Nsmall") +_S("Osmall") +_S("Psmall") +_S("Qsmall") +_S("Rsmall") +_S("Ssmall") +_S("Tsmall") +_S("Usmall") +_S("Vsmall") +_S("Wsmall") +_S("Xsmall") +_S("Ysmall") +_S("Zsmall") +_S("colonmonetary") +_S("onefitted") +_S("rupiah") +_S("Tildesmall") +_S("exclamdownsmall") +_S("centoldstyle") +_S("Lslashsmall") +_S("Scaronsmall") +_S("Zcaronsmall") +_S("Dieresissmall") +_S("Brevesmall") +_S("Caronsmall") +_S("Dotaccentsmall") +_S("Macronsmall") +_S("figuredash") +_S("hypheninferior") +_S("Ogoneksmall") +_S("Ringsmall") +_S("Cedillasmall") +_S("questiondownsmall") +_S("oneeighth") +_S("threeeighths") +_S("fiveeighths") +_S("seveneighths") +_S("onethird") +_S("twothirds") +_S("zerosuperior") +_S("foursuperior") +_S("fivesuperior") +_S("sixsuperior") +_S("sevensuperior") +_S("eightsuperior") +_S("ninesuperior") +_S("zeroinferior") +_S("oneinferior") +_S("twoinferior") +_S("threeinferior") +_S("fourinferior") +_S("fiveinferior") +_S("sixinferior") +_S("seveninferior") +_S("eightinferior") +_S("nineinferior") +_S("centinferior") +_S("dollarinferior") +_S("periodinferior") +_S("commainferior") +_S("Agravesmall") +_S("Aacutesmall") +_S("Acircumflexsmall") +_S("Atildesmall") +_S("Adieresissmall") +_S("Aringsmall") +_S("AEsmall") +_S("Ccedillasmall") +_S("Egravesmall") +_S("Eacutesmall") +_S("Ecircumflexsmall") +_S("Edieresissmall") +_S("Igravesmall") +_S("Iacutesmall") +_S("Icircumflexsmall") +_S("Idieresissmall") +_S("Ethsmall") +_S("Ntildesmall") +_S("Ogravesmall") +_S("Oacutesmall") +_S("Ocircumflexsmall") +_S("Otildesmall") +_S("Odieresissmall") +_S("OEsmall") +_S("Oslashsmall") +_S("Ugravesmall") +_S("Uacutesmall") +_S("Ucircumflexsmall") +_S("Udieresissmall") +_S("Yacutesmall") +_S("Thornsmall") +_S("Ydieresissmall") +_S("001.000") +_S("001.001") +_S("001.002") +_S("001.003") +_S("Black") +_S("Bold") +_S("Book") +_S("Light") +_S("Medium") +_S("Regular") +_S("Roman") +_S("Semibold") + +#endif /* HB_OT_CFF1_STD_STR_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc b/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc new file mode 100644 index 00000000000..66b9c8c907a --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc @@ -0,0 +1,620 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_CFF + +#include "hb-draw.hh" +#include "hb-algs.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-cff1-interp-cs.hh" + +using namespace CFF; + +struct sid_to_gid_t +{ + uint16_t sid; + uint8_t gid; + + int cmp (uint16_t a) const + { + if (a == sid) return 0; + return (a < sid) ? -1 : 1; + } +}; + +/* SID to code */ +static const uint8_t standard_encoding_to_code [] = +{ + 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, + 178, 179, 180, 182, 183, 184, 185, 186, 187, 188, 189, 191, 193, 194, 195, 196, + 197, 198, 199, 200, 202, 203, 205, 206, 207, 208, 225, 227, 232, 233, 234, 235, + 241, 245, 248, 249, 250, 251 +}; + +/* SID to code */ +static const uint8_t expert_encoding_to_code [] = +{ + 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 45, 46, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 189, 0, 0, 188, 0, + 0, 0, 0, 190, 202, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 63, 65, 66, 67, + 68, 69, 73, 76, 77, 78, 79, 82, 83, 84, 86, 89, 90, 91, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 161, 162, 163, 166, 167, 168, 169, 170, 172, 175, 178, 179, 182, 183, 184, 191, + 192, 193, 194, 195, 196, 197, 200, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +/* glyph ID to SID */ +static const uint16_t expert_charset_to_sid [] = +{ + 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150, + 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378 +}; + +/* glyph ID to SID */ +static const uint16_t expert_subset_charset_to_sid [] = +{ + 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272, + 300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326, + 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346 +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 12 }, { 14, 13 }, { 15, 14 }, + { 27, 26 }, { 28, 27 }, { 99, 15 }, { 109, 46 }, + { 110, 47 }, { 150, 111 }, { 155, 101 }, { 158, 100 }, + { 163, 102 }, { 164, 112 }, { 169, 113 }, { 229, 2 }, + { 230, 3 }, { 231, 4 }, { 232, 5 }, { 233, 6 }, + { 234, 7 }, { 235, 8 }, { 236, 9 }, { 237, 10 }, + { 238, 11 }, { 239, 16 }, { 240, 17 }, { 241, 18 }, + { 242, 19 }, { 243, 20 }, { 244, 21 }, { 245, 22 }, + { 246, 23 }, { 247, 24 }, { 248, 25 }, { 249, 28 }, + { 250, 29 }, { 251, 30 }, { 252, 31 }, { 253, 32 }, + { 254, 33 }, { 255, 34 }, { 256, 35 }, { 257, 36 }, + { 258, 37 }, { 259, 38 }, { 260, 39 }, { 261, 40 }, + { 262, 41 }, { 263, 42 }, { 264, 43 }, { 265, 44 }, + { 266, 45 }, { 267, 48 }, { 268, 49 }, { 269, 50 }, + { 270, 51 }, { 271, 52 }, { 272, 53 }, { 273, 54 }, + { 274, 55 }, { 275, 56 }, { 276, 57 }, { 277, 58 }, + { 278, 59 }, { 279, 60 }, { 280, 61 }, { 281, 62 }, + { 282, 63 }, { 283, 64 }, { 284, 65 }, { 285, 66 }, + { 286, 67 }, { 287, 68 }, { 288, 69 }, { 289, 70 }, + { 290, 71 }, { 291, 72 }, { 292, 73 }, { 293, 74 }, + { 294, 75 }, { 295, 76 }, { 296, 77 }, { 297, 78 }, + { 298, 79 }, { 299, 80 }, { 300, 81 }, { 301, 82 }, + { 302, 83 }, { 303, 84 }, { 304, 85 }, { 305, 86 }, + { 306, 87 }, { 307, 88 }, { 308, 89 }, { 309, 90 }, + { 310, 91 }, { 311, 92 }, { 312, 93 }, { 313, 94 }, + { 314, 95 }, { 315, 96 }, { 316, 97 }, { 317, 98 }, + { 318, 99 }, { 319, 103 }, { 320, 104 }, { 321, 105 }, + { 322, 106 }, { 323, 107 }, { 324, 108 }, { 325, 109 }, + { 326, 110 }, { 327, 114 }, { 328, 115 }, { 329, 116 }, + { 330, 117 }, { 331, 118 }, { 332, 119 }, { 333, 120 }, + { 334, 121 }, { 335, 122 }, { 336, 123 }, { 337, 124 }, + { 338, 125 }, { 339, 126 }, { 340, 127 }, { 341, 128 }, + { 342, 129 }, { 343, 130 }, { 344, 131 }, { 345, 132 }, + { 346, 133 }, { 347, 134 }, { 348, 135 }, { 349, 136 }, + { 350, 137 }, { 351, 138 }, { 352, 139 }, { 353, 140 }, + { 354, 141 }, { 355, 142 }, { 356, 143 }, { 357, 144 }, + { 358, 145 }, { 359, 146 }, { 360, 147 }, { 361, 148 }, + { 362, 149 }, { 363, 150 }, { 364, 151 }, { 365, 152 }, + { 366, 153 }, { 367, 154 }, { 368, 155 }, { 369, 156 }, + { 370, 157 }, { 371, 158 }, { 372, 159 }, { 373, 160 }, + { 374, 161 }, { 375, 162 }, { 376, 163 }, { 377, 164 }, + { 378, 165 } +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_subset_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 8 }, { 14, 9 }, { 15, 10 }, + { 27, 22 }, { 28, 23 }, { 99, 11 }, { 109, 41 }, + { 110, 42 }, { 150, 64 }, { 155, 55 }, { 158, 54 }, + { 163, 56 }, { 164, 65 }, { 169, 66 }, { 231, 2 }, + { 232, 3 }, { 235, 4 }, { 236, 5 }, { 237, 6 }, + { 238, 7 }, { 239, 12 }, { 240, 13 }, { 241, 14 }, + { 242, 15 }, { 243, 16 }, { 244, 17 }, { 245, 18 }, + { 246, 19 }, { 247, 20 }, { 248, 21 }, { 249, 24 }, + { 250, 25 }, { 251, 26 }, { 253, 27 }, { 254, 28 }, + { 255, 29 }, { 256, 30 }, { 257, 31 }, { 258, 32 }, + { 259, 33 }, { 260, 34 }, { 261, 35 }, { 262, 36 }, + { 263, 37 }, { 264, 38 }, { 265, 39 }, { 266, 40 }, + { 267, 43 }, { 268, 44 }, { 269, 45 }, { 270, 46 }, + { 272, 47 }, { 300, 48 }, { 301, 49 }, { 302, 50 }, + { 305, 51 }, { 314, 52 }, { 315, 53 }, { 320, 57 }, + { 321, 58 }, { 322, 59 }, { 323, 60 }, { 324, 61 }, + { 325, 62 }, { 326, 63 }, { 327, 67 }, { 328, 68 }, + { 329, 69 }, { 330, 70 }, { 331, 71 }, { 332, 72 }, + { 333, 73 }, { 334, 74 }, { 335, 75 }, { 336, 76 }, + { 337, 77 }, { 338, 78 }, { 339, 79 }, { 340, 80 }, + { 341, 81 }, { 342, 82 }, { 343, 83 }, { 344, 84 }, + { 345, 85 }, { 346, 86 } +}; + +/* code to SID */ +static const uint8_t standard_encoding_to_sid [] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 0, 111, 112, 113, 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 123, + 0, 124, 125, 126, 127, 128, 129, 130, 131, 0, 132, 133, 0, 134, 135, 136, + 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143, 0, 0, 0, 0, + 0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0 +}; + +hb_codepoint_t OT::cff1::lookup_standard_encoding_for_code (hb_codepoint_t sid) +{ + if (sid < ARRAY_LENGTH (standard_encoding_to_code)) + return (hb_codepoint_t)standard_encoding_to_code[sid]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_encoding_for_code (hb_codepoint_t sid) +{ + if (sid < ARRAY_LENGTH (expert_encoding_to_code)) + return (hb_codepoint_t)expert_encoding_to_code[sid]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_charset_for_sid (hb_codepoint_t glyph) +{ + if (glyph < ARRAY_LENGTH (expert_charset_to_sid)) + return (hb_codepoint_t)expert_charset_to_sid[glyph]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph) +{ + if (glyph < ARRAY_LENGTH (expert_subset_charset_to_sid)) + return (hb_codepoint_t)expert_subset_charset_to_sid[glyph]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_subset_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code) +{ + if (code < ARRAY_LENGTH (standard_encoding_to_sid)) + return (hb_codepoint_t)standard_encoding_to_sid[code]; + else + return CFF_UNDEF_SID; +} + +struct bounds_t +{ + void init () + { + min.set_int (INT_MAX, INT_MAX); + max.set_int (INT_MIN, INT_MIN); + } + + void update (const point_t &pt) + { + if (pt.x < min.x) min.x = pt.x; + if (pt.x > max.x) max.x = pt.x; + if (pt.y < min.y) min.y = pt.y; + if (pt.y > max.y) max.y = pt.y; + } + + void merge (const bounds_t &b) + { + if (empty ()) + *this = b; + else if (!b.empty ()) + { + if (b.min.x < min.x) min.x = b.min.x; + if (b.max.x > max.x) max.x = b.max.x; + if (b.min.y < min.y) min.y = b.min.y; + if (b.max.y > max.y) max.y = b.max.y; + } + } + + void offset (const point_t &delta) + { + if (!empty ()) + { + min.move (delta); + max.move (delta); + } + } + + bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } + + point_t min; + point_t max; +}; + +struct cff1_extents_param_t +{ + void init (const OT::cff1::accelerator_t *_cff) + { + path_open = false; + cff = _cff; + bounds.init (); + } + + void start_path () { path_open = true; } + void end_path () { path_open = false; } + bool is_path_open () const { return path_open; } + + bool path_open; + bounds_t bounds; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_extents_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt) + { + param.end_path (); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.bounds.update (env.get_pt ()); + } + env.moveto (pt1); + param.bounds.update (env.get_pt ()); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.bounds.update (env.get_pt ()); + } + /* include control points */ + param.bounds.update (pt1); + param.bounds.update (pt2); + env.moveto (pt3); + param.bounds.update (env.get_pt ()); + } +}; + +static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac=false); + +struct cff1_cs_opset_extents_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_extents_param_t& param) + { + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + bounds_t base_bounds, accent_bounds; + if (likely (!env.in_seac && base && accent + && _get_bounds (param.cff, base, base_bounds, true) + && _get_bounds (param.cff, accent, accent_bounds, true))) + { + param.bounds.merge (base_bounds); + accent_bounds.offset (delta); + param.bounds.merge (accent_bounds); + } + else + env.set_error (); + } +}; + +bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac) +{ + bounds.init (); + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + cff1_cs_interpreter_t interp; + const byte_str_t str = (*cff->charStrings)[glyph]; + interp.env.init (str, *cff, fd); + interp.env.set_in_seac (in_seac); + cff1_extents_param_t param; + param.init (cff); + if (unlikely (!interp.interpret (param))) return false; + bounds = param.bounds; + return true; +} + +bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + bounds_t bounds; + + if (!_get_bounds (this, glyph, bounds)) + return false; + + if (bounds.min.x >= bounds.max.x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = font->em_scalef_x (bounds.min.x.to_real ()); + extents->width = font->em_scalef_x (bounds.max.x.to_real () - bounds.min.x.to_real ()); + } + if (bounds.min.y >= bounds.max.y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ()); + extents->height = font->em_scalef_y (bounds.min.y.to_real () - bounds.max.y.to_real ()); + } + + return true; +} + +#ifdef HB_EXPERIMENTAL_API +struct cff1_path_param_t +{ + cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_, + draw_helper_t &draw_helper_, point_t *delta_) + { + draw_helper = &draw_helper_; + cff = cff_; + font = font_; + delta = delta_; + } + + void move_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_helper->move_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ())); + } + + void line_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_helper->line_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ())); + } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + point_t point1 = p1, point2 = p2, point3 = p3; + if (delta) + { + point1.move (*delta); + point2.move (*delta); + point3.move (*delta); + } + draw_helper->cubic_to (font->em_scalef_x (point1.x.to_real ()), font->em_scalef_y (point1.y.to_real ()), + font->em_scalef_x (point2.x.to_real ()), font->em_scalef_y (point2.y.to_real ()), + font->em_scalef_x (point3.x.to_real ()), font->em_scalef_y (point3.y.to_real ())); + } + + void end_path () { draw_helper->end_path (); } + + hb_font_t *font; + draw_helper_t *draw_helper; + point_t *delta; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_path_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + draw_helper_t &draw_helper, bool in_seac = false, point_t *delta = nullptr); + +struct cff1_cs_opset_path_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_path_param_t& param) + { + /* End previous path */ + param.end_path (); + + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + if (unlikely (!(!env.in_seac && base && accent + && _get_path (param.cff, param.font, base, *param.draw_helper, true) + && _get_path (param.cff, param.font, accent, *param.draw_helper, true, &delta)))) + env.set_error (); + } +}; + +bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + draw_helper_t &draw_helper, bool in_seac, point_t *delta) +{ + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + cff1_cs_interpreter_t interp; + const byte_str_t str = (*cff->charStrings)[glyph]; + interp.env.init (str, *cff, fd); + interp.env.set_in_seac (in_seac); + cff1_path_param_t param (cff, font, draw_helper, delta); + if (unlikely (!interp.interpret (param))) return false; + + /* Let's end the path specially since it is called inside seac also */ + param.end_path (); + + return true; +} + +bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + return _get_path (this, font, glyph, draw_helper); +} +#endif + +struct get_seac_param_t +{ + void init (const OT::cff1::accelerator_t *_cff) + { + cff = _cff; + base = 0; + accent = 0; + } + + bool has_seac () const { return base && accent; } + + const OT::cff1::accelerator_t *cff; + hb_codepoint_t base; + hb_codepoint_t accent; +}; + +struct cff1_cs_opset_seac_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, get_seac_param_t& param) + { + unsigned int n = env.argStack.get_count (); + hb_codepoint_t base_char = (hb_codepoint_t)env.argStack[n-2].to_int (); + hb_codepoint_t accent_char = (hb_codepoint_t)env.argStack[n-1].to_int (); + + param.base = param.cff->std_code_to_glyph (base_char); + param.accent = param.cff->std_code_to_glyph (accent_char); + } +}; + +bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const +{ + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + cff1_cs_interpreter_t interp; + const byte_str_t str = (*charStrings)[glyph]; + interp.env.init (str, *this, fd); + get_seac_param_t param; + param.init (this); + if (unlikely (!interp.interpret (param))) return false; + + if (param.has_seac ()) + { + *base = param.base; + *accent = param.accent; + return true; + } + return false; +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh new file mode 100644 index 00000000000..7228f777272 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh @@ -0,0 +1,1403 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_TABLE_HH +#define HB_OT_CFF1_TABLE_HH + +#include "hb-ot-cff-common.hh" +#include "hb-subset-cff1.hh" +#include "hb-draw.hh" + +#define HB_STRING_ARRAY_NAME cff1_std_strings +#define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh" +#include "hb-string-array.hh" +#undef HB_STRING_ARRAY_LIST +#undef HB_STRING_ARRAY_NAME + +namespace CFF { + +/* + * CFF -- Compact Font Format (CFF) + * https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf + */ +#define HB_OT_TAG_cff1 HB_TAG('C','F','F',' ') + +#define CFF_UNDEF_SID CFF_UNDEF_CODE + +enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 }; +enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 }; + +typedef CFFIndex CFF1Index; +template struct CFF1IndexOf : CFFIndexOf {}; + +typedef CFFIndex CFF1Index; +typedef CFF1Index CFF1CharStrings; +typedef Subrs CFF1Subrs; + +struct CFF1FDSelect : FDSelect {}; + +/* Encoding */ +struct Encoding0 { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (codes.sanitize (c)); + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + assert (glyph > 0); + glyph--; + if (glyph < nCodes ()) + { + return (hb_codepoint_t)codes[glyph]; + } + else + return CFF_UNDEF_CODE; + } + + HBUINT8 &nCodes () { return codes.len; } + HBUINT8 nCodes () const { return codes.len; } + + ArrayOf codes; + + DEFINE_SIZE_ARRAY_SIZED (1, codes); +}; + +struct Encoding1_Range { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 first; + HBUINT8 nLeft; + + DEFINE_SIZE_STATIC (2); +}; + +struct Encoding1 { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ranges.sanitize (c)); + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + assert (glyph > 0); + glyph--; + for (unsigned int i = 0; i < nRanges (); i++) + { + if (glyph <= ranges[i].nLeft) + { + hb_codepoint_t code = (hb_codepoint_t) ranges[i].first + glyph; + return (likely (code < 0x100) ? code: CFF_UNDEF_CODE); + } + glyph -= (ranges[i].nLeft + 1); + } + return CFF_UNDEF_CODE; + } + + HBUINT8 &nRanges () { return ranges.len; } + HBUINT8 nRanges () const { return ranges.len; } + + ArrayOf ranges; + + DEFINE_SIZE_ARRAY_SIZED (1, ranges); +}; + +struct SuppEncoding { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 code; + HBUINT16 glyph; + + DEFINE_SIZE_STATIC (3); +}; + +struct CFF1SuppEncData { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (supps.sanitize (c)); + } + + void get_codes (hb_codepoint_t sid, hb_vector_t &codes) const + { + for (unsigned int i = 0; i < nSups (); i++) + if (sid == supps[i].glyph) + codes.push (supps[i].code); + } + + HBUINT8 &nSups () { return supps.len; } + HBUINT8 nSups () const { return supps.len; } + + ArrayOf supps; + + DEFINE_SIZE_ARRAY_SIZED (1, supps); +}; + +struct Encoding +{ + /* serialize a fullset Encoding */ + bool serialize (hb_serialize_context_t *c, const Encoding &src) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (); + Encoding *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + /* serialize a subset Encoding */ + bool serialize (hb_serialize_context_t *c, + uint8_t format, + unsigned int enc_count, + const hb_vector_t& code_ranges, + const hb_vector_t& supp_codes) + { + TRACE_SERIALIZE (this); + Encoding *dest = c->extend_min (*this); + if (unlikely (!dest)) return_trace (false); + dest->format = format | ((supp_codes.length > 0) ? 0x80 : 0); + switch (format) { + case 0: + { + Encoding0 *fmt0 = c->allocate_size (Encoding0::min_size + HBUINT8::static_size * enc_count); + if (unlikely (!fmt0)) return_trace (false); + fmt0->nCodes () = enc_count; + unsigned int glyph = 0; + for (unsigned int i = 0; i < code_ranges.length; i++) + { + hb_codepoint_t code = code_ranges[i].code; + for (int left = (int)code_ranges[i].glyph; left >= 0; left--) + fmt0->codes[glyph++] = code++; + if (unlikely (!((glyph <= 0x100) && (code <= 0x100)))) + return_trace (false); + } + } + break; + + case 1: + { + Encoding1 *fmt1 = c->allocate_size (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + fmt1->nRanges () = code_ranges.length; + for (unsigned int i = 0; i < code_ranges.length; i++) + { + if (unlikely (!((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF)))) + return_trace (false); + fmt1->ranges[i].first = code_ranges[i].code; + fmt1->ranges[i].nLeft = code_ranges[i].glyph; + } + } + break; + + } + + if (supp_codes.length) + { + CFF1SuppEncData *suppData = c->allocate_size (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.length); + if (unlikely (!suppData)) return_trace (false); + suppData->nSups () = supp_codes.length; + for (unsigned int i = 0; i < supp_codes.length; i++) + { + suppData->supps[i].code = supp_codes[i].code; + suppData->supps[i].glyph = supp_codes[i].glyph; /* actually SID */ + } + } + + return_trace (true); + } + + unsigned int get_size () const + { + unsigned int size = min_size; + switch (table_format ()) + { + case 0: size += u.format0.get_size (); break; + case 1: size += u.format1.get_size (); break; + } + if (has_supplement ()) + size += suppEncData ().get_size (); + return size; + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + switch (table_format ()) + { + case 0: return u.format0.get_code (glyph); + case 1: return u.format1.get_code (glyph); + default:return 0; + } + } + + uint8_t table_format () const { return format & 0x7F; } + bool has_supplement () const { return format & 0x80; } + + void get_supplement_codes (hb_codepoint_t sid, hb_vector_t &codes) const + { + codes.resize (0); + if (has_supplement ()) + suppEncData().get_codes (sid, codes); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (table_format ()) + { + case 0: if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; + case 1: if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; + default:return_trace (false); + } + return_trace (likely (!has_supplement () || suppEncData ().sanitize (c))); + } + + protected: + const CFF1SuppEncData &suppEncData () const + { + switch (table_format ()) + { + case 0: return StructAfter (u.format0.codes[u.format0.nCodes ()-1]); + case 1: return StructAfter (u.format1.ranges[u.format1.nRanges ()-1]); + default:return Null (CFF1SuppEncData); + } + } + + public: + HBUINT8 format; + union { + Encoding0 format0; + Encoding1 format1; + } u; + /* CFF1SuppEncData suppEncData; */ + + DEFINE_SIZE_MIN (1); +}; + +/* Charset */ +struct Charset0 { + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c)); + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph) const + { + if (glyph == 0) + return 0; + else + return sids[glyph - 1]; + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + if (sid == 0) + return 0; + + for (unsigned int glyph = 1; glyph < num_glyphs; glyph++) + { + if (sids[glyph-1] == sid) + return glyph; + } + return 0; + } + + unsigned int get_size (unsigned int num_glyphs) const + { + assert (num_glyphs > 0); + return HBUINT16::static_size * (num_glyphs - 1); + } + + HBUINT16 sids[HB_VAR_ARRAY]; + + DEFINE_SIZE_ARRAY(0, sids); +}; + +template +struct Charset_Range { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 first; + TYPE nLeft; + + DEFINE_SIZE_STATIC (HBUINT16::static_size + TYPE::static_size); +}; + +template +struct Charset1_2 { + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + num_glyphs--; + for (unsigned int i = 0; num_glyphs > 0; i++) + { + if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1))) + return_trace (false); + num_glyphs -= (ranges[i].nLeft + 1); + } + return_trace (true); + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph) const + { + if (glyph == 0) return 0; + glyph--; + for (unsigned int i = 0;; i++) + { + if (glyph <= ranges[i].nLeft) + return (hb_codepoint_t)ranges[i].first + glyph; + glyph -= (ranges[i].nLeft + 1); + } + + return 0; + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + if (sid == 0) return 0; + hb_codepoint_t glyph = 1; + for (unsigned int i = 0;; i++) + { + if (glyph >= num_glyphs) + return 0; + if ((ranges[i].first <= sid) && (sid <= ranges[i].first + ranges[i].nLeft)) + return glyph + (sid - ranges[i].first); + glyph += (ranges[i].nLeft + 1); + } + + return 0; + } + + unsigned int get_size (unsigned int num_glyphs) const + { + unsigned int size = HBUINT8::static_size; + int glyph = (int)num_glyphs; + + assert (glyph > 0); + glyph--; + for (unsigned int i = 0; glyph > 0; i++) + { + glyph -= (ranges[i].nLeft + 1); + size += Charset_Range::static_size; + } + + return size; + } + + Charset_Range ranges[HB_VAR_ARRAY]; + + DEFINE_SIZE_ARRAY (0, ranges); +}; + +typedef Charset1_2 Charset1; +typedef Charset1_2 Charset2; +typedef Charset_Range Charset1_Range; +typedef Charset_Range Charset2_Range; + +struct Charset +{ + /* serialize a fullset Charset */ + bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + Charset *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + /* serialize a subset Charset */ + bool serialize (hb_serialize_context_t *c, + uint8_t format, + unsigned int num_glyphs, + const hb_vector_t& sid_ranges) + { + TRACE_SERIALIZE (this); + Charset *dest = c->extend_min (*this); + if (unlikely (!dest)) return_trace (false); + dest->format = format; + switch (format) + { + case 0: + { + Charset0 *fmt0 = c->allocate_size (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1)); + if (unlikely (!fmt0)) return_trace (false); + unsigned int glyph = 0; + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + hb_codepoint_t sid = sid_ranges[i].code; + for (int left = (int)sid_ranges[i].glyph; left >= 0; left--) + fmt0->sids[glyph++] = sid++; + } + } + break; + + case 1: + { + Charset1 *fmt1 = c->allocate_size (Charset1::min_size + Charset1_Range::static_size * sid_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + if (unlikely (!(sid_ranges[i].glyph <= 0xFF))) + return_trace (false); + fmt1->ranges[i].first = sid_ranges[i].code; + fmt1->ranges[i].nLeft = sid_ranges[i].glyph; + } + } + break; + + case 2: + { + Charset2 *fmt2 = c->allocate_size (Charset2::min_size + Charset2_Range::static_size * sid_ranges.length); + if (unlikely (!fmt2)) return_trace (false); + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF))) + return_trace (false); + fmt2->ranges[i].first = sid_ranges[i].code; + fmt2->ranges[i].nLeft = sid_ranges[i].glyph; + } + } + break; + + } + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return min_size + u.format0.get_size (num_glyphs); + case 1: return min_size + u.format1.get_size (num_glyphs); + case 2: return min_size + u.format2.get_size (num_glyphs); + default:return 0; + } + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const + { + if (unlikely (glyph >= num_glyphs)) return 0; + switch (format) + { + case 0: return u.format0.get_sid (glyph); + case 1: return u.format1.get_sid (glyph); + case 2: return u.format2.get_sid (glyph); + default:return 0; + } + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + switch (format) + { + case 0: return u.format0.get_glyph (sid, num_glyphs); + case 1: return u.format1.get_glyph (sid, num_glyphs); + case 2: return u.format2.get_glyph (sid, num_glyphs); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs ())); + case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs ())); + case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs ())); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + Charset0 format0; + Charset1 format1; + Charset2 format2; + } u; + + DEFINE_SIZE_MIN (1); +}; + +struct CFF1StringIndex : CFF1Index +{ + bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, + const hb_inc_bimap_t &sidmap) + { + TRACE_SERIALIZE (this); + if (unlikely ((strings.count == 0) || (sidmap.get_population () == 0))) + { + if (unlikely (!c->extend_min (this->count))) + return_trace (false); + count = 0; + return_trace (true); + } + + byte_str_array_t bytesArray; + bytesArray.init (); + if (!bytesArray.resize (sidmap.get_population ())) + return_trace (false); + for (unsigned int i = 0; i < strings.count; i++) + { + hb_codepoint_t j = sidmap[i]; + if (j != HB_MAP_VALUE_INVALID) + bytesArray[j] = strings[i]; + } + + bool result = CFF1Index::serialize (c, bytesArray); + bytesArray.fini (); + return_trace (result); + } +}; + +struct cff1_top_dict_interp_env_t : num_interp_env_t +{ + cff1_top_dict_interp_env_t () + : num_interp_env_t(), prev_offset(0), last_offset(0) {} + + unsigned int prev_offset; + unsigned int last_offset; +}; + +struct name_dict_values_t +{ + enum name_dict_val_index_t + { + version, + notice, + copyright, + fullName, + familyName, + weight, + postscript, + fontName, + baseFontName, + registry, + ordering, + + ValCount + }; + + void init () + { + for (unsigned int i = 0; i < ValCount; i++) + values[i] = CFF_UNDEF_SID; + } + + unsigned int& operator[] (unsigned int i) + { assert (i < ValCount); return values[i]; } + + unsigned int operator[] (unsigned int i) const + { assert (i < ValCount); return values[i]; } + + static enum name_dict_val_index_t name_op_to_index (op_code_t op) + { + switch (op) { + default: // can't happen - just make some compiler happy + case OpCode_version: + return version; + case OpCode_Notice: + return notice; + case OpCode_Copyright: + return copyright; + case OpCode_FullName: + return fullName; + case OpCode_FamilyName: + return familyName; + case OpCode_Weight: + return weight; + case OpCode_PostScript: + return postscript; + case OpCode_FontName: + return fontName; + case OpCode_BaseFontName: + return baseFontName; + } + } + + unsigned int values[ValCount]; +}; + +struct cff1_top_dict_val_t : op_str_t +{ + unsigned int last_arg_offset; +}; + +struct cff1_top_dict_values_t : top_dict_values_t +{ + void init () + { + top_dict_values_t::init (); + + nameSIDs.init (); + ros_supplement = 0; + cidCount = 8720; + EncodingOffset = 0; + CharsetOffset = 0; + FDSelectOffset = 0; + privateDictInfo.init (); + } + void fini () { top_dict_values_t::fini (); } + + bool is_CID () const + { return nameSIDs[name_dict_values_t::registry] != CFF_UNDEF_SID; } + + name_dict_values_t nameSIDs; + unsigned int ros_supplement_offset; + unsigned int ros_supplement; + unsigned int cidCount; + + unsigned int EncodingOffset; + unsigned int CharsetOffset; + unsigned int FDSelectOffset; + table_info_t privateDictInfo; +}; + +struct cff1_top_dict_opset_t : top_dict_opset_t +{ + static void process_op (op_code_t op, cff1_top_dict_interp_env_t& env, cff1_top_dict_values_t& dictval) + { + cff1_top_dict_val_t val; + val.last_arg_offset = (env.last_offset-1) - dictval.opStart; /* offset to the last argument */ + + switch (op) { + case OpCode_version: + case OpCode_Notice: + case OpCode_Copyright: + case OpCode_FullName: + case OpCode_FamilyName: + case OpCode_Weight: + case OpCode_PostScript: + case OpCode_BaseFontName: + dictval.nameSIDs[name_dict_values_t::name_op_to_index (op)] = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_isFixedPitch: + case OpCode_ItalicAngle: + case OpCode_UnderlinePosition: + case OpCode_UnderlineThickness: + case OpCode_PaintType: + case OpCode_CharstringType: + case OpCode_UniqueID: + case OpCode_StrokeWidth: + case OpCode_SyntheticBase: + case OpCode_CIDFontVersion: + case OpCode_CIDFontRevision: + case OpCode_CIDFontType: + case OpCode_UIDBase: + case OpCode_FontBBox: + case OpCode_XUID: + case OpCode_BaseFontBlend: + env.clear_args (); + break; + + case OpCode_CIDCount: + dictval.cidCount = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_ROS: + dictval.ros_supplement = env.argStack.pop_uint (); + dictval.nameSIDs[name_dict_values_t::ordering] = env.argStack.pop_uint (); + dictval.nameSIDs[name_dict_values_t::registry] = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_Encoding: + dictval.EncodingOffset = env.argStack.pop_uint (); + env.clear_args (); + if (unlikely (dictval.EncodingOffset == 0)) return; + break; + + case OpCode_charset: + dictval.CharsetOffset = env.argStack.pop_uint (); + env.clear_args (); + if (unlikely (dictval.CharsetOffset == 0)) return; + break; + + case OpCode_FDSelect: + dictval.FDSelectOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + env.last_offset = env.str_ref.offset; + top_dict_opset_t::process_op (op, env, dictval); + /* Record this operand below if stack is empty, otherwise done */ + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff1_font_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + privateDictInfo.init (); + fontName = CFF_UNDEF_SID; + } + void fini () { dict_values_t::fini (); } + + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct cff1_font_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_font_dict_values_t& dictval) + { + switch (op) { + case OpCode_FontName: + dictval.fontName = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FontMatrix: + case OpCode_PaintType: + env.clear_args (); + break; + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } +}; + +template +struct cff1_private_dict_values_base_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + subrsOffset = 0; + localSubrs = &Null (CFF1Subrs); + } + void fini () { dict_values_t::fini (); } + + unsigned int subrsOffset; + const CFF1Subrs *localSubrs; +}; + +typedef cff1_private_dict_values_base_t cff1_private_dict_values_subset_t; +typedef cff1_private_dict_values_base_t cff1_private_dict_values_t; + +struct cff1_private_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_t& dictval) + { + num_dict_val_t val; + val.init (); + + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + env.clear_args (); + break; + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + case OpCode_initialRandomSeed: + case OpCode_defaultWidthX: + case OpCode_nominalWidthX: + val.single_val = env.argStack.pop_num (); + env.clear_args (); + break; + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff1_private_dict_opset_subset : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_subset_t& dictval) + { + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + case OpCode_initialRandomSeed: + case OpCode_defaultWidthX: + case OpCode_nominalWidthX: + env.clear_args (); + break; + + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } +}; + +typedef dict_interpreter_t cff1_top_dict_interpreter_t; +typedef dict_interpreter_t cff1_font_dict_interpreter_t; + +typedef CFF1Index CFF1NameIndex; +typedef CFF1IndexOf CFF1TopDictIndex; + +struct cff1_font_dict_values_mod_t +{ + cff1_font_dict_values_mod_t() { init (); } + + void init () { init ( &Null (cff1_font_dict_values_t), CFF_UNDEF_SID ); } + + void init (const cff1_font_dict_values_t *base_, + unsigned int fontName_) + { + base = base_; + fontName = fontName_; + privateDictInfo.init (); + } + + unsigned get_count () const { return base->get_count (); } + + const op_str_t &operator [] (unsigned int i) const { return (*base)[i]; } + + const cff1_font_dict_values_t *base; + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct CFF1FDArray : FDArray +{ + /* FDArray::serialize() requires this partial specialization to compile */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + +} /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff1 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cff1; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 1)); + } + + template + struct accelerator_templ_t + { + void init (hb_face_t *face) + { + topDict.init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff1 *cff = this->blob->template as (); + + if (cff == &Null (OT::cff1)) + { fini (); return; } + + nameIndex = &cff->nameIndex (cff); + if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) + { fini (); return; } + + topDictIndex = &StructAtOffset (nameIndex, nameIndex->get_size ()); + if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) + { fini (); return; } + + { /* parse top dict */ + const byte_str_t topDictStr = (*topDictIndex)[0]; + if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; } + cff1_top_dict_interpreter_t top_interp; + top_interp.env.init (topDictStr); + topDict.init (); + if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } + } + + if (is_predef_charset ()) + charset = &Null (Charset); + else + { + charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset); + if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; } + } + + fdCount = 1; + if (is_CID ()) + { + fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset); + if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || + (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + { fini (); return; } + + fdCount = fdArray->count; + } + else + { + fdArray = &Null (CFF1FDArray); + fdSelect = &Null (CFF1FDSelect); + } + + encoding = &Null (Encoding); + if (is_CID ()) + { + if (unlikely (charset == &Null (Charset))) { fini (); return; } + } + else + { + if (!is_predef_encoding ()) + { + encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); + if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; } + } + } + + stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); + if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) + { fini (); return; } + + globalSubrs = &StructAtOffset (stringIndex, stringIndex->get_size ()); + if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) + { fini (); return; } + + charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset); + + if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) + { fini (); return; } + + num_glyphs = charStrings->count; + if (num_glyphs != sc.get_num_glyphs ()) + { fini (); return; } + + if (unlikely (!privateDicts.resize (fdCount))) + { fini (); return; } + for (unsigned int i = 0; i < fdCount; i++) + privateDicts[i].init (); + + // parse CID font dicts and gather private dicts + if (is_CID ()) + { + for (unsigned int i = 0; i < fdCount; i++) + { + byte_str_t fontDictStr = (*fdArray)[i]; + if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; } + cff1_font_dict_values_t *font; + cff1_font_dict_interpreter_t font_interp; + font_interp.env.init (fontDictStr); + font = fontDicts.push (); + if (unlikely (font == &Crap (cff1_font_dict_values_t))) { fini (); return; } + font->init (); + if (unlikely (!font_interp.interpret (*font))) { fini (); return; } + PRIVDICTVAL *priv = &privateDicts[i]; + const byte_str_t privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } + dict_interpreter_t priv_interp; + priv_interp.env.init (privDictStr); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } + + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + else /* non-CID */ + { + cff1_top_dict_values_t *font = &topDict; + PRIVDICTVAL *priv = &privateDicts[0]; + + const byte_str_t privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } + dict_interpreter_t priv_interp; + priv_interp.env.init (privDictStr); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } + + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + + void fini () + { + sc.end_processing (); + topDict.fini (); + fontDicts.fini_deep (); + privateDicts.fini_deep (); + hb_blob_destroy (blob); + blob = nullptr; + } + + bool is_valid () const { return blob; } + bool is_CID () const { return topDict.is_CID (); } + + bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; } + + unsigned int std_code_to_glyph (hb_codepoint_t code) const + { + hb_codepoint_t sid = lookup_standard_encoding_for_sid (code); + if (unlikely (sid == CFF_UNDEF_SID)) + return 0; + + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); + else if ((topDict.CharsetOffset == ISOAdobeCharset) + && (code <= 228 /*zcaron*/)) return sid; + return 0; + } + + bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } + + hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const + { + if (encoding != &Null (Encoding)) + return encoding->get_code (glyph); + else + { + hb_codepoint_t sid = glyph_to_sid (glyph); + if (sid == 0) return 0; + hb_codepoint_t code = 0; + switch (topDict.EncodingOffset) + { + case StandardEncoding: + code = lookup_standard_encoding_for_code (sid); + break; + case ExpertEncoding: + code = lookup_expert_encoding_for_code (sid); + break; + default: + break; + } + return code; + } + } + + hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const + { + if (charset != &Null (Charset)) + return charset->get_sid (glyph, num_glyphs); + else + { + hb_codepoint_t sid = 0; + switch (topDict.CharsetOffset) + { + case ISOAdobeCharset: + if (glyph <= 228 /*zcaron*/) sid = glyph; + break; + case ExpertCharset: + sid = lookup_expert_charset_for_sid (glyph); + break; + case ExpertSubsetCharset: + sid = lookup_expert_subset_charset_for_sid (glyph); + break; + default: + break; + } + return sid; + } + } + + hb_codepoint_t sid_to_glyph (hb_codepoint_t sid) const + { + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); + else + { + hb_codepoint_t glyph = 0; + switch (topDict.CharsetOffset) + { + case ISOAdobeCharset: + if (sid <= 228 /*zcaron*/) glyph = sid; + break; + case ExpertCharset: + glyph = lookup_expert_charset_for_glyph (sid); + break; + case ExpertSubsetCharset: + glyph = lookup_expert_subset_charset_for_glyph (sid); + break; + default: + break; + } + return glyph; + } + } + + protected: + hb_blob_t *blob; + hb_sanitize_context_t sc; + + public: + const Encoding *encoding; + const Charset *charset; + const CFF1NameIndex *nameIndex; + const CFF1TopDictIndex *topDictIndex; + const CFF1StringIndex *stringIndex; + const CFF1Subrs *globalSubrs; + const CFF1CharStrings *charStrings; + const CFF1FDArray *fdArray; + const CFF1FDSelect *fdSelect; + unsigned int fdCount; + + cff1_top_dict_values_t topDict; + hb_vector_t + fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs; + }; + + struct accelerator_t : accelerator_templ_t + { + void init (hb_face_t *face) + { + SUPER::init (face); + + if (!is_valid ()) return; + if (is_CID ()) return; + + /* fill glyph_names */ + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t sid = glyph_to_sid (gid); + gname_t gname; + gname.sid = sid; + if (sid < cff1_std_strings_length) + gname.name = cff1_std_strings (sid); + else + { + byte_str_t ustr = (*stringIndex)[sid - cff1_std_strings_length]; + gname.name = hb_bytes_t ((const char*)ustr.arrayZ, ustr.length); + } + if (unlikely (!gname.name.arrayZ)) { fini (); return; } + glyph_names.push (gname); + } + glyph_names.qsort (); + } + + void fini () + { + glyph_names.fini (); + + SUPER::fini (); + } + + bool get_glyph_name (hb_codepoint_t glyph, + char *buf, unsigned int buf_len) const + { + if (!buf) return true; + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + hb_codepoint_t sid = glyph_to_sid (glyph); + const char *str; + size_t str_len; + if (sid < cff1_std_strings_length) + { + hb_bytes_t byte_str = cff1_std_strings (sid); + str = byte_str.arrayZ; + str_len = byte_str.length; + } + else + { + byte_str_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length]; + str = (const char *)ubyte_str.arrayZ; + str_len = ubyte_str.length; + } + if (!str_len) return false; + unsigned int len = hb_min (buf_len - 1, str_len); + strncpy (buf, (const char*)str, len); + buf[len] = '\0'; + return true; + } + + bool get_glyph_from_name (const char *name, int len, + hb_codepoint_t *glyph) const + { + if (len < 0) len = strlen (name); + if (unlikely (!len)) return false; + + gname_t key = { hb_bytes_t (name, len), 0 }; + const gname_t *gname = glyph_names.bsearch (key); + if (!gname) return false; + hb_codepoint_t gid = sid_to_glyph (gname->sid); + if (!gid && gname->sid) return false; + *glyph = gid; + return true; + } + + HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; +#ifdef HB_EXPERIMENTAL_API + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const; +#endif + + private: + struct gname_t + { + hb_bytes_t name; + uint16_t sid; + + static int cmp (const void *a_, const void *b_) + { + const gname_t *a = (const gname_t *)a_; + const gname_t *b = (const gname_t *)b_; + int minlen = hb_min (a->name.length, b->name.length); + int ret = strncmp (a->name.arrayZ, b->name.arrayZ, minlen); + if (ret) return ret; + return a->name.length - b->name.length; + } + + int cmp (const gname_t &a) const { return cmp (&a, this); } + }; + + hb_sorted_vector_t glyph_names; + + typedef accelerator_templ_t SUPER; + }; + + struct accelerator_subset_t : accelerator_templ_t {}; + + bool subset (hb_subset_context_t *c) const { return hb_subset_cff1 (c); } + + protected: + HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code); + + public: + FixedVersion version; /* Version of CFF table. set to 0x0100u */ + OffsetTo nameIndex; /* headerSize = Offset to Name INDEX. */ + HBUINT8 offSize; /* offset size (unused?) */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct cff1_accelerator_t : cff1::accelerator_t {}; +} /* namespace OT */ + +#endif /* HB_OT_CFF1_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-cff2-table.cc b/thirdparty/harfbuzz/src/hb-ot-cff2-table.cc new file mode 100644 index 00000000000..ac0feeee21b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-cff2-table.cc @@ -0,0 +1,215 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_FONT_CFF + +#include "hb-ot-cff2-table.hh" +#include "hb-cff2-interp-cs.hh" +#include "hb-draw.hh" + +using namespace CFF; + +struct cff2_extents_param_t +{ + void init () + { + path_open = false; + min_x.set_int (INT_MAX); + min_y.set_int (INT_MAX); + max_x.set_int (INT_MIN); + max_y.set_int (INT_MIN); + } + + void start_path () { path_open = true; } + void end_path () { path_open = false; } + bool is_path_open () const { return path_open; } + + void update_bounds (const point_t &pt) + { + if (pt.x < min_x) min_x = pt.x; + if (pt.x > max_x) max_x = pt.x; + if (pt.y < min_y) min_y = pt.y; + if (pt.y > max_y) max_y = pt.y; + } + + bool path_open; + number_t min_x; + number_t min_y; + number_t max_x; + number_t max_y; +}; + +struct cff2_path_procs_extents_t : path_procs_t +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt) + { + param.end_path (); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + env.moveto (pt1); + param.update_bounds (env.get_pt ()); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + /* include control points */ + param.update_bounds (pt1); + param.update_bounds (pt2); + env.moveto (pt3); + param.update_bounds (env.get_pt ()); + } +}; + +struct cff2_cs_opset_extents_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + cff2_cs_interpreter_t interp; + const byte_str_t str = (*charStrings)[glyph]; + interp.env.init (str, *this, fd, font->coords, font->num_coords); + cff2_extents_param_t param; + param.init (); + if (unlikely (!interp.interpret (param))) return false; + + if (param.min_x >= param.max_x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = font->em_scalef_x (param.min_x.to_real ()); + extents->width = font->em_scalef_x (param.max_x.to_real () - param.min_x.to_real ()); + } + if (param.min_y >= param.max_y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = font->em_scalef_y (param.max_y.to_real ()); + extents->height = font->em_scalef_y (param.min_y.to_real () - param.max_y.to_real ()); + } + + return true; +} + +#ifdef HB_EXPERIMENTAL_API +struct cff2_path_param_t +{ + cff2_path_param_t (hb_font_t *font_, draw_helper_t &draw_helper_) + { + draw_helper = &draw_helper_; + font = font_; + } + + void move_to (const point_t &p) + { draw_helper->move_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); } + + void line_to (const point_t &p) + { draw_helper->line_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + draw_helper->cubic_to (font->em_scalef_x (p1.x.to_real ()), font->em_scalef_y (p1.y.to_real ()), + font->em_scalef_x (p2.x.to_real ()), font->em_scalef_y (p2.y.to_real ()), + font->em_scalef_x (p3.x.to_real ()), font->em_scalef_y (p3.y.to_real ())); + } + + protected: + draw_helper_t *draw_helper; + hb_font_t *font; +}; + +struct cff2_path_procs_path_t : path_procs_t +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +struct cff2_cs_opset_path_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + cff2_cs_interpreter_t interp; + const byte_str_t str = (*charStrings)[glyph]; + interp.env.init (str, *this, fd, font->coords, font->num_coords); + cff2_path_param_t param (font, draw_helper); + if (unlikely (!interp.interpret (param))) return false; + return true; +} +#endif + +#endif diff --git a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh new file mode 100644 index 00000000000..829217feaa2 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh @@ -0,0 +1,531 @@ +/* + * Copyright © 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF2_TABLE_HH +#define HB_OT_CFF2_TABLE_HH + +#include "hb-ot-cff-common.hh" +#include "hb-subset-cff2.hh" +#include "hb-draw.hh" + +namespace CFF { + +/* + * CFF2 -- Compact Font Format (CFF) Version 2 + * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2 + */ +#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2') + +typedef CFFIndex CFF2Index; +template struct CFF2IndexOf : CFFIndexOf {}; + +typedef CFF2Index CFF2CharStrings; +typedef Subrs CFF2Subrs; + +typedef FDSelect3_4 FDSelect4; +typedef FDSelect3_4_Range FDSelect4_Range; + +struct CFF2FDSelect +{ + bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + CFF2FDSelect *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + case 4: return format.static_size + u.format4.get_size (); + default:return 0; + } + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + if (this == &Null (CFF2FDSelect)) + return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + case 4: return u.format4.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + case 4: return_trace (u.format4.sanitize (c, fdcount)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + FDSelect0 format0; + FDSelect3 format3; + FDSelect4 format4; + } u; + public: + DEFINE_SIZE_MIN (2); +}; + +struct CFF2VariationStore +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c)); + } + + bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) + { + TRACE_SERIALIZE (this); + unsigned int size_ = varStore->get_size (); + CFF2VariationStore *dest = c->allocate_size (size_); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, varStore, size_); + return_trace (true); + } + + unsigned int get_size () const { return HBUINT16::static_size + size; } + + HBUINT16 size; + VariationStore varStore; + + DEFINE_SIZE_MIN (2 + VariationStore::min_size); +}; + +struct cff2_top_dict_values_t : top_dict_values_t<> +{ + void init () + { + top_dict_values_t<>::init (); + vstoreOffset = 0; + FDSelectOffset = 0; + } + void fini () { top_dict_values_t<>::fini (); } + + unsigned int vstoreOffset; + unsigned int FDSelectOffset; +}; + +struct cff2_top_dict_opset_t : top_dict_opset_t<> +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff2_top_dict_values_t& dictval) + { + switch (op) { + case OpCode_FontMatrix: + { + dict_val_t val; + val.init (); + dictval.add_op (op, env.str_ref); + env.clear_args (); + } + break; + + case OpCode_vstore: + dictval.vstoreOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FDSelect: + dictval.FDSelectOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env, dictval); + /* Record this operand below if stack is empty, otherwise done */ + if (!env.argStack.is_empty ()) return; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + typedef top_dict_opset_t<> SUPER; +}; + +struct cff2_font_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + privateDictInfo.init (); + } + void fini () { dict_values_t::fini (); } + + table_info_t privateDictInfo; +}; + +struct cff2_font_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff2_font_dict_values_t& dictval) + { + switch (op) { + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env); + if (!env.argStack.is_empty ()) + return; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + private: + typedef dict_opset_t SUPER; +}; + +template +struct cff2_private_dict_values_base_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + subrsOffset = 0; + localSubrs = &Null (CFF2Subrs); + ivs = 0; + } + void fini () { dict_values_t::fini (); } + + unsigned int subrsOffset; + const CFF2Subrs *localSubrs; + unsigned int ivs; +}; + +typedef cff2_private_dict_values_base_t cff2_private_dict_values_subset_t; +typedef cff2_private_dict_values_base_t cff2_private_dict_values_t; + +struct cff2_priv_dict_interp_env_t : num_interp_env_t +{ + void init (const byte_str_t &str) + { + num_interp_env_t::init (str); + ivs = 0; + seen_vsindex = false; + } + + void process_vsindex () + { + if (likely (!seen_vsindex)) + { + set_ivs (argStack.pop_uint ()); + } + seen_vsindex = true; + } + + unsigned int get_ivs () const { return ivs; } + void set_ivs (unsigned int ivs_) { ivs = ivs_; } + + protected: + unsigned int ivs; + bool seen_vsindex; +}; + +struct cff2_private_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_t& dictval) + { + num_dict_val_t val; + val.init (); + + switch (op) { + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ExpansionFactor: + case OpCode_LanguageGroup: + val.single_val = env.argStack.pop_num (); + env.clear_args (); + break; + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + env.clear_args (); + break; + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_vsindexdict: + env.process_vsindex (); + dictval.ivs = env.get_ivs (); + env.clear_args (); + break; + case OpCode_blenddict: + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff2_private_dict_opset_subset_t : dict_opset_t +{ + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_subset_t& dictval) + { + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + env.clear_args (); + break; + + case OpCode_blenddict: + env.clear_args (); + return; + + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + private: + typedef dict_opset_t SUPER; +}; + +typedef dict_interpreter_t cff2_top_dict_interpreter_t; +typedef dict_interpreter_t cff2_font_dict_interpreter_t; + +struct CFF2FDArray : FDArray +{ + /* FDArray::serialize does not compile without this partial specialization */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + +} /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff2 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cff2; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2)); + } + + template + struct accelerator_templ_t + { + void init (hb_face_t *face) + { + topDict.init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff2 *cff2 = this->blob->template as (); + + if (cff2 == &Null (OT::cff2)) + { fini (); return; } + + { /* parse top dict */ + byte_str_t topDictStr (cff2 + cff2->topDict, cff2->topDictSize); + if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; } + cff2_top_dict_interpreter_t top_interp; + top_interp.env.init (topDictStr); + topDict.init (); + if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } + } + + globalSubrs = &StructAtOffset (cff2, cff2->topDict + cff2->topDictSize); + varStore = &StructAtOffsetOrNull (cff2, topDict.vstoreOffset); + charStrings = &StructAtOffsetOrNull (cff2, topDict.charStringsOffset); + fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset); + + if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || + (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || + (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || + (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || + (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) + { fini (); return; } + + num_glyphs = charStrings->count; + if (num_glyphs != sc.get_num_glyphs ()) + { fini (); return; } + + fdCount = fdArray->count; + if (!privateDicts.resize (fdCount)) + { fini (); return; } + + /* parse font dicts and gather private dicts */ + for (unsigned int i = 0; i < fdCount; i++) + { + const byte_str_t fontDictStr = (*fdArray)[i]; + if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; } + cff2_font_dict_values_t *font; + cff2_font_dict_interpreter_t font_interp; + font_interp.env.init (fontDictStr); + font = fontDicts.push (); + if (unlikely (font == &Crap (cff2_font_dict_values_t))) { fini (); return; } + font->init (); + if (unlikely (!font_interp.interpret (*font))) { fini (); return; } + + const byte_str_t privDictStr (StructAtOffsetOrNull (cff2, font->privateDictInfo.offset), font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } + dict_interpreter_t priv_interp; + priv_interp.env.init(privDictStr); + privateDicts[i].init (); + if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; } + + privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset); + if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && + unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + + void fini () + { + sc.end_processing (); + topDict.fini (); + fontDicts.fini_deep (); + privateDicts.fini_deep (); + hb_blob_destroy (blob); + blob = nullptr; + } + + bool is_valid () const { return blob; } + + protected: + hb_blob_t *blob; + hb_sanitize_context_t sc; + + public: + cff2_top_dict_values_t topDict; + const CFF2Subrs *globalSubrs; + const CFF2VariationStore *varStore; + const CFF2CharStrings *charStrings; + const CFF2FDArray *fdArray; + const CFF2FDSelect *fdSelect; + unsigned int fdCount; + + hb_vector_t fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs; + }; + + struct accelerator_t : accelerator_templ_t + { + HB_INTERNAL bool get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const; +#ifdef HB_EXPERIMENTAL_API + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const; +#endif + }; + + typedef accelerator_templ_t accelerator_subset_t; + + bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); } + + public: + FixedVersion version; /* Version of CFF2 table. set to 0x0200u */ + NNOffsetTo topDict; /* headerSize = Offset to Top DICT. */ + HBUINT16 topDictSize; /* Top DICT size */ + + public: + DEFINE_SIZE_STATIC (5); +}; + +struct cff2_accelerator_t : cff2::accelerator_t {}; +} /* namespace OT */ + +#endif /* HB_OT_CFF2_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh new file mode 100644 index 00000000000..cc48379bb8a --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh @@ -0,0 +1,1711 @@ +/* + * Copyright © 2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_CMAP_TABLE_HH +#define HB_OT_CMAP_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-set.hh" + +/* + * cmap -- Character to Glyph Index Mapping + * https://docs.microsoft.com/en-us/typography/opentype/spec/cmap + */ +#define HB_OT_TAG_cmap HB_TAG('c','m','a','p') + +namespace OT { + + +struct CmapSubtableFormat0 +{ + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; + if (!gid) + return false; + *glyph = gid; + return true; + } + void collect_unicodes (hb_set_t *out) const + { + for (unsigned int i = 0; i < 256; i++) + if (glyphIdArray[i]) + out->add (i); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (unsigned i = 0; i < 256; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t glyph = glyphIdArray[i]; + unicodes->add (i); + mapping->set (i, glyph); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format number is set to 0. */ + HBUINT16 length; /* Byte length of this subtable. */ + HBUINT16 language; /* Ignore. */ + HBUINT8 glyphIdArray[256];/* An array that maps character + * code to glyph index values. */ + public: + DEFINE_SIZE_STATIC (6 + 256); +}; + +struct CmapSubtableFormat4 +{ + + template + HBUINT16* serialize_endcode_array (hb_serialize_context_t *c, + Iterator it) + { + HBUINT16 *endCode = c->start_embed (); + hb_codepoint_t prev_endcp = 0xFFFF; + + for (const hb_item_type _ : +it) + { + if (prev_endcp != 0xFFFF && prev_endcp + 1u != _.first) + { + HBUINT16 end_code; + end_code = prev_endcp; + c->copy (end_code); + } + prev_endcp = _.first; + } + + { + // last endCode + HBUINT16 endcode; + endcode = prev_endcp; + if (unlikely (!c->copy (endcode))) return nullptr; + // There must be a final entry with end_code == 0xFFFF. + if (prev_endcp != 0xFFFF) + { + HBUINT16 finalcode; + finalcode = 0xFFFF; + if (unlikely (!c->copy (finalcode))) return nullptr; + } + } + + return endCode; + } + + template + HBUINT16* serialize_startcode_array (hb_serialize_context_t *c, + Iterator it) + { + HBUINT16 *startCode = c->start_embed (); + hb_codepoint_t prev_cp = 0xFFFF; + + for (const hb_item_type _ : +it) + { + if (prev_cp == 0xFFFF || prev_cp + 1u != _.first) + { + HBUINT16 start_code; + start_code = _.first; + c->copy (start_code); + } + + prev_cp = _.first; + } + + // There must be a final entry with end_code == 0xFFFF. + if (it.len () == 0 || prev_cp != 0xFFFF) + { + HBUINT16 finalcode; + finalcode = 0xFFFF; + if (unlikely (!c->copy (finalcode))) return nullptr; + } + + return startCode; + } + + template + HBINT16* serialize_idDelta_array (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + unsigned segcount) + { + unsigned i = 0; + hb_codepoint_t last_gid = 0, start_gid = 0, last_cp = 0xFFFF; + bool use_delta = true; + + HBINT16 *idDelta = c->start_embed (); + if ((char *)idDelta - (char *)startCode != (int) segcount * (int) HBINT16::static_size) + return nullptr; + + for (const hb_item_type _ : +it) + { + if (_.first == startCode[i]) + { + use_delta = true; + start_gid = _.second; + } + else if (_.second != last_gid + 1) use_delta = false; + + if (_.first == endCode[i]) + { + HBINT16 delta; + if (use_delta) delta = (int)start_gid - (int)startCode[i]; + else delta = 0; + c->copy (delta); + + i++; + } + + last_gid = _.second; + last_cp = _.first; + } + + if (it.len () == 0 || last_cp != 0xFFFF) + { + HBINT16 delta; + delta = 1; + if (unlikely (!c->copy (delta))) return nullptr; + } + + return idDelta; + } + + template + HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + HBINT16 *idDelta, + unsigned segcount) + { + HBUINT16 *idRangeOffset = c->allocate_size (HBUINT16::static_size * segcount); + if (unlikely (!c->check_success (idRangeOffset))) return nullptr; + if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr; + + + hb_range (segcount) + | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }) + | hb_apply ([&] (const unsigned i) + { + idRangeOffset[i] = 2 * (c->start_embed () - idRangeOffset - i); + + + it + | hb_filter ([&] (const hb_item_type _) { return _.first >= startCode[i] && _.first <= endCode[i]; }) + | hb_apply ([&] (const hb_item_type _) + { + HBUINT16 glyID; + glyID = _.second; + c->copy (glyID); + }) + ; + + + }) + ; + + return idRangeOffset; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + auto format4_iter = + + it + | hb_filter ([&] (const hb_pair_t _) + { return _.first <= 0xFFFF; }) + ; + + if (format4_iter.len () == 0) return; + + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (*this))) return; + this->format = 4; + + //serialize endCode[] + HBUINT16 *endCode = serialize_endcode_array (c, format4_iter); + if (unlikely (!endCode)) return; + + unsigned segcount = (c->length () - min_size) / HBUINT16::static_size; + + // 2 bytes of padding. + if (unlikely (!c->allocate_size (HBUINT16::static_size))) return; // 2 bytes of padding. + + // serialize startCode[] + HBUINT16 *startCode = serialize_startcode_array (c, format4_iter); + if (unlikely (!startCode)) return; + + //serialize idDelta[] + HBINT16 *idDelta = serialize_idDelta_array (c, format4_iter, endCode, startCode, segcount); + if (unlikely (!idDelta)) return; + + HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, format4_iter, endCode, startCode, idDelta, segcount); + if (unlikely (!c->check_success (idRangeOffset))) return; + + if (unlikely (!c->check_assign(this->length, c->length () - table_initpos))) return; + this->segCountX2 = segcount * 2; + this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; + this->searchRange = 2 * (1u << this->entrySelector); + this->rangeShift = segcount * 2 > this->searchRange + ? 2 * segcount - this->searchRange + : 0; + } + + struct accelerator_t + { + accelerator_t () {} + accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); } + ~accelerator_t () { fini (); } + + void init (const CmapSubtableFormat4 *subtable) + { + segCount = subtable->segCountX2 / 2; + endCount = subtable->values.arrayZ; + startCount = endCount + segCount + 1; + idDelta = startCount + segCount; + idRangeOffset = idDelta + segCount; + glyphIdArray = idRangeOffset + segCount; + glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; + } + void fini () {} + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + struct CustomRange + { + int cmp (hb_codepoint_t k, + unsigned distance) const + { + if (k > last) return +1; + if (k < (&last)[distance]) return -1; + return 0; + } + HBUINT16 last; + }; + + const HBUINT16 *found = hb_bsearch (codepoint, + this->endCount, + this->segCount, + 2, + _hb_cmp_method, + this->segCount + 1); + if (!found) + return false; + unsigned int i = found - endCount; + + hb_codepoint_t gid; + unsigned int rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + gid = codepoint + this->idDelta[i]; + else + { + /* Somebody has been smoking... */ + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + return false; + gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + return false; + gid += this->idDelta[i]; + } + gid &= 0xFFFFu; + if (!gid) + return false; + *glyph = gid; + return true; + } + + HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); } + + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned int rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + out->add (codepoint); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + break; + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + continue; + out->add (codepoint); + } + } + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + unsigned count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + break; + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + } + } + + const HBUINT16 *endCount; + const HBUINT16 *startCount; + const HBUINT16 *idDelta; + const HBUINT16 *idRangeOffset; + const HBUINT16 *glyphIdArray; + unsigned int segCount; + unsigned int glyphIdArrayLength; + }; + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + accelerator_t accel (this); + return accel.get_glyph_func (&accel, codepoint, glyph); + } + void collect_unicodes (hb_set_t *out) const + { + accelerator_t accel (this); + accel.collect_unicodes (out); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + accelerator_t accel (this); + accel.collect_mapping (unicodes, mapping); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + if (unlikely (!c->check_range (this, length))) + { + /* Some broken fonts have too long of a "length" value. + * If that is the case, just change the value to truncate + * the subtable at the end of the blob. */ + uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); + if (!c->try_set (&length, new_length)) + return_trace (false); + } + + return_trace (16 + 4 * (unsigned int) segCountX2 <= length); + } + + + + protected: + HBUINT16 format; /* Format number is set to 4. */ + HBUINT16 length; /* This is the length in bytes of the + * subtable. */ + HBUINT16 language; /* Ignore. */ + HBUINT16 segCountX2; /* 2 x segCount. */ + HBUINT16 searchRange; /* 2 * (2**floor(log2(segCount))) */ + HBUINT16 entrySelector; /* log2(searchRange/2) */ + HBUINT16 rangeShift; /* 2 x segCount - searchRange */ + + UnsizedArrayOf + values; +#if 0 + HBUINT16 endCount[segCount]; /* End characterCode for each segment, + * last=0xFFFFu. */ + HBUINT16 reservedPad; /* Set to 0. */ + HBUINT16 startCount[segCount]; /* Start character code for each segment. */ + HBINT16 idDelta[segCount]; /* Delta for all character codes in segment. */ + HBUINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ + UnsizedArrayOf + glyphIdArray; /* Glyph index array (arbitrary length) */ +#endif + + public: + DEFINE_SIZE_ARRAY (14, values); +}; + +struct CmapSubtableLongGroup +{ + friend struct CmapSubtableFormat12; + friend struct CmapSubtableFormat13; + template + friend struct CmapSubtableLongSegmented; + friend struct cmap; + + int cmp (hb_codepoint_t codepoint) const + { + if (codepoint < startCharCode) return -1; + if (codepoint > endCharCode) return +1; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + private: + HBUINT32 startCharCode; /* First character code in this group. */ + HBUINT32 endCharCode; /* Last character code in this group. */ + HBUINT32 glyphID; /* Glyph index; interpretation depends on + * subtable format. */ + public: + DEFINE_SIZE_STATIC (12); +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup); + +template +struct CmapSubtableTrimmed +{ + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + /* Rely on our implicit array bound-checking. */ + hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode]; + if (!gid) + return false; + *glyph = gid; + return true; + } + void collect_unicodes (hb_set_t *out) const + { + hb_codepoint_t start = startCharCode; + unsigned int count = glyphIdArray.len; + for (unsigned int i = 0; i < count; i++) + if (glyphIdArray[i]) + out->add (start + i); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + hb_codepoint_t start_cp = startCharCode; + unsigned count = glyphIdArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t unicode = start_cp + i; + hb_codepoint_t glyphid = glyphIdArray[i]; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); + } + + protected: + UINT formatReserved; /* Subtable format and (maybe) padding. */ + UINT length; /* Byte length of this subtable. */ + UINT language; /* Ignore. */ + UINT startCharCode; /* First character code covered. */ + ArrayOf + glyphIdArray; /* Array of glyph index values for character + * codes in the range. */ + public: + DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); +}; + +struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; + +template +struct CmapSubtableLongSegmented +{ + friend struct cmap; + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint); + if (!gid) + return false; + *glyph = gid; + return true; + } + + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { + for (unsigned int i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + out->add_range (start, end); + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs) const + { + for (unsigned i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + for (unsigned cp = start; cp <= end; cp++) + { + unicodes->add (cp); + mapping->set (cp, gid); + gid++; + } + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && groups.sanitize (c)); + } + + protected: + HBUINT16 format; /* Subtable format; set to 12. */ + HBUINT16 reserved; /* Reserved; set to 0. */ + HBUINT32 length; /* Byte length of this subtable. */ + HBUINT32 language; /* Ignore. */ + SortedArrayOf + groups; /* Groupings. */ + public: + DEFINE_SIZE_ARRAY (16, groups); +}; + +struct CmapSubtableFormat12 : CmapSubtableLongSegmented +{ + static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u) + { return likely (group.startCharCode <= group.endCharCode) ? + group.glyphID + (u - group.startCharCode) : 0; } + + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + if (it.len () == 0) return; + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (*this))) return; + + hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF; + hb_codepoint_t glyphID = 0; + + for (const hb_item_type _ : +it) + { + if (startCharCode == 0xFFFF) + { + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second)) + { + CmapSubtableLongGroup grouprecord; + grouprecord.startCharCode = startCharCode; + grouprecord.endCharCode = endCharCode; + grouprecord.glyphID = glyphID; + c->copy (grouprecord); + + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else + endCharCode = _.first; + } + + CmapSubtableLongGroup record; + record.startCharCode = startCharCode; + record.endCharCode = endCharCode; + record.glyphID = glyphID; + c->copy (record); + + this->format = 12; + this->reserved = 0; + this->length = c->length () - table_initpos; + this->groups.len = (this->length - min_size)/CmapSubtableLongGroup::static_size; + } + + static size_t get_sub_table_size (const hb_sorted_vector_t &groups_data) + { return 16 + 12 * groups_data.length; } + + private: + static bool _is_gid_consecutive (hb_codepoint_t endCharCode, + hb_codepoint_t startCharCode, + hb_codepoint_t glyphID, + hb_codepoint_t cp, + hb_codepoint_t new_gid) + { + return (cp - 1 == endCharCode) && + new_gid == glyphID + (cp - startCharCode); + } + +}; + +struct CmapSubtableFormat13 : CmapSubtableLongSegmented +{ + static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u HB_UNUSED) + { return group.glyphID; } +}; + +typedef enum +{ + GLYPH_VARIANT_NOT_FOUND = 0, + GLYPH_VARIANT_FOUND = 1, + GLYPH_VARIANT_USE_DEFAULT = 2 +} glyph_variant_t; + +struct UnicodeValueRange +{ + int cmp (const hb_codepoint_t &codepoint) const + { + if (codepoint < startUnicodeValue) return -1; + if (codepoint > startUnicodeValue + additionalCount) return +1; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT24 startUnicodeValue; /* First value in this range. */ + HBUINT8 additionalCount; /* Number of additional values in this + * range. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct DefaultUVS : SortedArrayOf +{ + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t first = arrayZ[i].startUnicodeValue; + hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), + (hb_codepoint_t) HB_UNICODE_MAX); + out->add_range (first, last); + } + } + + DefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes) const + { + DefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + auto snap = c->snapshot (); + + HBUINT32 len; + len = 0; + if (unlikely (!c->copy (len))) return nullptr; + unsigned init_len = c->length (); + + hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID; + int count = -1; + + for (const UnicodeValueRange& _ : as_array ()) + { + for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1)) + { + unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt; + if (!unicodes->has (curEntry)) continue; + count += 1; + if (lastCode == HB_MAP_VALUE_INVALID) + lastCode = curEntry; + else if (lastCode + count != curEntry) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count - 1; + c->copy (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy (rec); + } + + if (c->length () - init_len == 0) + { + c->revert (snap); + return nullptr; + } + else + { + if (unlikely (!c->check_assign (out->len, (c->length () - init_len) / UnicodeValueRange::static_size))) return nullptr; + return out; + } + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; + +struct UVSMapping +{ + int cmp (const hb_codepoint_t &codepoint) const + { return unicodeValue.cmp (codepoint); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT24 unicodeValue; /* Base Unicode value of the UVS */ + HBGlyphID glyphID; /* Glyph ID of the UVS */ + public: + DEFINE_SIZE_STATIC (5); +}; + +struct NonDefaultUVS : SortedArrayOf +{ + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + out->add (arrayZ[i].unicodeValue); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t unicode = arrayZ[i].unicodeValue; + hb_codepoint_t glyphid = arrayZ[i].glyphID; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + as_array () + | hb_filter (unicodes, &UVSMapping::unicodeValue) + | hb_map (&UVSMapping::glyphID) + | hb_sink (glyphset) + ; + } + + NonDefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map) const + { + NonDefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + + auto it = + + as_array () + | hb_filter ([&] (const UVSMapping& _) + { + return unicodes->has (_.unicodeValue) || glyphs_requested->has (_.glyphID); + }) + ; + + if (!it) return nullptr; + + HBUINT32 len; + len = it.len (); + if (unlikely (!c->copy (len))) return nullptr; + + for (const UVSMapping& _ : it) + { + UVSMapping mapping; + mapping.unicodeValue = _.unicodeValue; + mapping.glyphID = glyph_map->get (_.glyphID); + c->copy (mapping); + } + + return out; + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; + +struct VariationSelectorRecord +{ + glyph_variant_t get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph, + const void *base) const + { + if ((base+defaultUVS).bfind (codepoint)) + return GLYPH_VARIANT_USE_DEFAULT; + const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint); + if (nonDefault.glyphID) + { + *glyph = nonDefault.glyphID; + return GLYPH_VARIANT_FOUND; + } + return GLYPH_VARIANT_NOT_FOUND; + } + + VariationSelectorRecord(const VariationSelectorRecord& other) + { + *this = other; + } + + void operator= (const VariationSelectorRecord& other) + { + varSelector = other.varSelector; + HBUINT32 offset = other.defaultUVS; + defaultUVS = offset; + offset = other.nonDefaultUVS; + nonDefaultUVS = offset; + } + + void collect_unicodes (hb_set_t *out, const void *base) const + { + (base+defaultUVS).collect_unicodes (out); + (base+nonDefaultUVS).collect_unicodes (out); + } + + void collect_mapping (const void *base, + hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + (base+defaultUVS).collect_unicodes (unicodes); + (base+nonDefaultUVS).collect_mapping (unicodes, mapping); + } + + int cmp (const hb_codepoint_t &variation_selector) const + { return varSelector.cmp (variation_selector); } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + defaultUVS.sanitize (c, base) && + nonDefaultUVS.sanitize (c, base)); + } + + hb_pair_t + copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) const + { + auto snap = c->snapshot (); + auto *out = c->embed (*this); + if (unlikely (!out)) return hb_pair (0, 0); + + out->defaultUVS = 0; + out->nonDefaultUVS = 0; + + unsigned non_default_uvs_objidx = 0; + if (nonDefaultUVS != 0) + { + c->push (); + if (c->copy (base+nonDefaultUVS, unicodes, glyphs_requested, glyph_map)) + non_default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + unsigned default_uvs_objidx = 0; + if (defaultUVS != 0) + { + c->push (); + if (c->copy (base+defaultUVS, unicodes)) + default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + + if (!default_uvs_objidx && !non_default_uvs_objidx) + c->revert (snap); + + return hb_pair (default_uvs_objidx, non_default_uvs_objidx); + } + + HBUINT24 varSelector; /* Variation selector. */ + LOffsetTo + defaultUVS; /* Offset to Default UVS Table. May be 0. */ + LOffsetTo + nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ + public: + DEFINE_SIZE_STATIC (11); +}; + +struct CmapSubtableFormat14 +{ + glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); } + + void collect_variation_selectors (hb_set_t *out) const + { + unsigned int count = record.len; + for (unsigned int i = 0; i < count; i++) + out->add (record.arrayZ[i].varSelector); + } + void collect_variation_unicodes (hb_codepoint_t variation_selector, + hb_set_t *out) const + { record.bsearch (variation_selector).collect_unicodes (out, this); } + + void serialize (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) + { + auto snap = c->snapshot (); + unsigned table_initpos = c->length (); + const char* init_tail = c->tail; + + if (unlikely (!c->extend_min (*this))) return; + this->format = 14; + + auto src_tbl = reinterpret_cast (base); + + /* + * Some versions of OTS require that offsets are in order. Due to the use + * of push()/pop_pack() serializing the variation records in order results + * in the offsets being in reverse order (first record has the largest + * offset). While this is perfectly valid, it will cause some versions of + * OTS to consider this table bad. + * + * So to prevent this issue we serialize the variation records in reverse + * order, so that the offsets are ordered from small to large. Since + * variation records are supposed to be in increasing order of varSelector + * we then have to reverse the order of the written variation selector + * records after everything is finalized. + */ + hb_vector_t> obj_indices; + for (int i = src_tbl->record.len - 1; i >= 0; i--) + { + hb_pair_t result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base); + if (result.first || result.second) + obj_indices.push (result); + } + + if (c->length () - table_initpos == CmapSubtableFormat14::min_size) + { + c->revert (snap); + return; + } + + if (unlikely (!c->check_success (!obj_indices.in_error ()))) + return; + + int tail_len = init_tail - c->tail; + c->check_assign (this->length, c->length () - table_initpos + tail_len); + c->check_assign (this->record.len, + (c->length () - table_initpos - CmapSubtableFormat14::min_size) / + VariationSelectorRecord::static_size); + + /* Correct the incorrect write order by reversing the order of the variation + records array. */ + _reverse_variation_records (); + + /* Now that records are in the right order, we can set up the offsets. */ + _add_links_to_variation_records (c, obj_indices); + } + + void _reverse_variation_records () + { + record.as_array ().reverse (); + } + + void _add_links_to_variation_records (hb_serialize_context_t *c, + const hb_vector_t>& obj_indices) + { + for (unsigned i = 0; i < obj_indices.length; i++) + { + /* + * Since the record array has been reversed (see comments in copy()) + * but obj_indices has not been, the indices at obj_indices[i] + * are for the variation record at record[j]. + */ + int j = obj_indices.length - 1 - i; + c->add_link (record[j].defaultUVS, obj_indices[i].first); + c->add_link (record[j].nonDefaultUVS, obj_indices[i].second); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (record) + | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS) + | hb_map (&VariationSelectorRecord::nonDefaultUVS) + | hb_map (hb_add (this)) + | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); }) + ; + } + + void collect_unicodes (hb_set_t *out) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_unicodes (out, this); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_mapping (this, unicodes, mapping); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + record.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format number is set to 14. */ + HBUINT32 length; /* Byte length of this subtable. */ + SortedArrayOf + record; /* Variation selector records; sorted + * in increasing order of `varSelector'. */ + public: + DEFINE_SIZE_ARRAY (10, record); +}; + +struct CmapSubtable +{ + /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ + + bool get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph) const + { + switch (u.format) { + case 0: return u.format0 .get_glyph (codepoint, glyph); + case 4: return u.format4 .get_glyph (codepoint, glyph); + case 6: return u.format6 .get_glyph (codepoint, glyph); + case 10: return u.format10.get_glyph (codepoint, glyph); + case 12: return u.format12.get_glyph (codepoint, glyph); + case 13: return u.format13.get_glyph (codepoint, glyph); + case 14: + default: return false; + } + } + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_unicodes (out); return; + case 4: u.format4 .collect_unicodes (out); return; + case 6: u.format6 .collect_unicodes (out); return; + case 10: u.format10.collect_unicodes (out); return; + case 12: u.format12.collect_unicodes (out, num_glyphs); return; + case 13: u.format13.collect_unicodes (out, num_glyphs); return; + case 14: + default: return; + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_mapping (unicodes, mapping); return; + case 4: u.format4 .collect_mapping (unicodes, mapping); return; + case 6: u.format6 .collect_mapping (unicodes, mapping); return; + case 10: u.format10.collect_mapping (unicodes, mapping); return; + case 12: u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; + case 13: u.format13.collect_mapping (unicodes, mapping, num_glyphs); return; + case 14: + default: return; + } + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const hb_subset_plan_t *plan, + const void *base) + { + switch (format) { + case 4: return u.format4.serialize (c, it); + case 12: return u.format12.serialize (c, it); + case 14: return u.format14.serialize (c, plan->unicodes, plan->glyphs_requested, plan->glyph_map, base); + default: return; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0 .sanitize (c)); + case 4: return_trace (u.format4 .sanitize (c)); + case 6: return_trace (u.format6 .sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + case 12: return_trace (u.format12.sanitize (c)); + case 13: return_trace (u.format13.sanitize (c)); + case 14: return_trace (u.format14.sanitize (c)); + default:return_trace (true); + } + } + + public: + union { + HBUINT16 format; /* Format identifier */ + CmapSubtableFormat0 format0; + CmapSubtableFormat4 format4; + CmapSubtableFormat6 format6; + CmapSubtableFormat10 format10; + CmapSubtableFormat12 format12; + CmapSubtableFormat13 format13; + CmapSubtableFormat14 format14; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +struct EncodingRecord +{ + int cmp (const EncodingRecord &other) const + { + int ret; + ret = platformID.cmp (other.platformID); + if (ret) return ret; + ret = encodingID.cmp (other.encodingID); + if (ret) return ret; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + subtable.sanitize (c, base)); + } + + template + EncodingRecord* copy (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const void *base, + const hb_subset_plan_t *plan, + /* INOUT */ unsigned *objidx) const + { + TRACE_SERIALIZE (this); + auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->subtable = 0; + + if (*objidx == 0) + { + CmapSubtable *cmapsubtable = c->push (); + unsigned origin_length = c->length (); + cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); + if (c->length () - origin_length > 0) *objidx = c->pop_pack (); + else c->pop_discard (); + } + + if (*objidx == 0) + { + c->revert (snap); + return_trace (nullptr); + } + + c->add_link (out->subtable, *objidx); + return_trace (out); + } + + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + LOffsetTo + subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct cmap +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + EncodingRecIter encodingrec_iter, + const void *base, + const hb_subset_plan_t *plan) + { + if (unlikely (!c->extend_min ((*this)))) return; + this->version = 0; + + unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; + + for (const EncodingRecord& _ : encodingrec_iter) + { + unsigned format = (base+_.subtable).u.format; + if (!plan->glyphs_requested->is_empty ()) + { + hb_set_t unicodes_set; + hb_map_t cp_glyphid_map; + (base+_.subtable).collect_mapping (&unicodes_set, &cp_glyphid_map); + + auto table_iter = + + hb_zip (unicodes_set.iter(), unicodes_set.iter() | hb_map(cp_glyphid_map)) + | hb_filter (plan->_glyphset, hb_second) + | hb_filter ([plan] (const hb_pair_t& p) + { + return plan->unicodes->has (p.first) || + plan->glyphs_requested->has (p.second); + }) + | hb_map ([plan] (const hb_pair_t& p_org) + { + return hb_pair_t (p_org.first, plan->glyph_map->get(p_org.second)); + }) + ; + + if (format == 4) c->copy (_, table_iter, 4u, base, plan, &format4objidx); + else if (format == 12) c->copy (_, table_iter, 12u, base, plan, &format12objidx); + else if (format == 14) c->copy (_, table_iter, 14u, base, plan, &format14objidx); + } + /* when --gids option is not used, we iterate input unicodes instead of + * all codepoints in each subtable, which is more efficient */ + else + { + hb_set_t unicodes_set; + (base+_.subtable).collect_unicodes (&unicodes_set); + + if (format == 4) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx); + else if (format == 12) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 12u, base, plan, &format12objidx); + else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); + } + } + + c->check_assign(this->encodingRecord.len, (c->length () - cmap::min_size)/EncodingRecord::static_size); + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) + ; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + cmap *cmap_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false); + + auto encodingrec_iter = + + hb_iter (encodingRecord) + | hb_filter ([&] (const EncodingRecord& _) + { + if ((_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (this + _.subtable).u.format == 14) + return true; + + return false; + }) + ; + + if (unlikely (!encodingrec_iter.len ())) return_trace (false); + + const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr; + bool has_format12 = false; + + for (const EncodingRecord& _ : encodingrec_iter) + { + unsigned format = (this + _.subtable).u.format; + if (format == 12) has_format12 = true; + + const EncodingRecord *table = hb_addressof (_); + if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table; + else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table; + else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table; + else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table; + } + + if (unlikely (!has_format12 && !unicode_bmp && !ms_bmp)) return_trace (false); + if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false); + + auto it = + + hb_iter (c->plan->unicodes) + | hb_map ([&] (hb_codepoint_t _) + { + hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID; + c->plan->new_gid_for_codepoint (_, &new_gid); + return hb_pair_t (_, new_gid); + }) + | hb_filter ([&] (const hb_pair_t _) + { return (_.second != HB_MAP_VALUE_INVALID); }) + ; + cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan); + return_trace (true); + } + + const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const + { + if (symbol) *symbol = false; + + const CmapSubtable *subtable; + + /* Symbol subtable. + * Prefer symbol if available. + * https://github.com/harfbuzz/harfbuzz/issues/1918 */ + if ((subtable = this->find_subtable (3, 0))) + { + if (symbol) *symbol = true; + return subtable; + } + + /* 32-bit subtables. */ + if ((subtable = this->find_subtable (3, 10))) return subtable; + if ((subtable = this->find_subtable (0, 6))) return subtable; + if ((subtable = this->find_subtable (0, 4))) return subtable; + + /* 16-bit subtables. */ + if ((subtable = this->find_subtable (3, 1))) return subtable; + if ((subtable = this->find_subtable (0, 3))) return subtable; + if ((subtable = this->find_subtable (0, 2))) return subtable; + if ((subtable = this->find_subtable (0, 1))) return subtable; + if ((subtable = this->find_subtable (0, 0))) return subtable; + + /* Meh. */ + return &Null (CmapSubtable); + } + + struct accelerator_t + { + void init (hb_face_t *face) + { + this->table = hb_sanitize_context_t ().reference_table (face); + bool symbol; + this->subtable = table->find_best_subtable (&symbol); + this->subtable_uvs = &Null (CmapSubtableFormat14); + { + const CmapSubtable *st = table->find_subtable (0, 5); + if (st && st->u.format == 14) + subtable_uvs = &st->u.format14; + } + + this->get_glyph_data = subtable; + if (unlikely (symbol)) + this->get_glyph_funcZ = get_glyph_from_symbol; + else + { + switch (subtable->u.format) { + /* Accelerate format 4 and format 12. */ + default: + this->get_glyph_funcZ = get_glyph_from; + break; + case 12: + this->get_glyph_funcZ = get_glyph_from; + break; + case 4: + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = this->format4_accel.get_glyph_func; + break; + } + } + } + } + + void fini () { this->table.destroy (); } + + bool get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph) const + { + if (unlikely (!this->get_glyph_funcZ)) return false; + return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); + } + unsigned int get_nominal_glyphs (unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) const + { + if (unlikely (!this->get_glyph_funcZ)) return 0; + + hb_cmap_get_glyph_func_t get_glyph_funcZ = this->get_glyph_funcZ; + const void *get_glyph_data = this->get_glyph_data; + + unsigned int done; + for (done = 0; + done < count && get_glyph_funcZ (get_glyph_data, *first_unicode, first_glyph); + done++) + { + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + return done; + } + + bool get_variation_glyph (hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + switch (this->subtable_uvs->get_glyph_variant (unicode, + variation_selector, + glyph)) + { + case GLYPH_VARIANT_NOT_FOUND: return false; + case GLYPH_VARIANT_FOUND: return true; + case GLYPH_VARIANT_USE_DEFAULT: break; + } + + return get_nominal_glyph (unicode, glyph); + } + + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { subtable->collect_unicodes (out, num_glyphs); } + void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping, + unsigned num_glyphs = UINT_MAX) const + { subtable->collect_mapping (unicodes, mapping, num_glyphs); } + void collect_variation_selectors (hb_set_t *out) const + { subtable_uvs->collect_variation_selectors (out); } + void collect_variation_unicodes (hb_codepoint_t variation_selector, + hb_set_t *out) const + { subtable_uvs->collect_variation_unicodes (variation_selector, out); } + + protected: + typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph); + + template + HB_INTERNAL static bool get_glyph_from (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + return typed_obj->get_glyph (codepoint, glyph); + } + + template + HB_INTERNAL static bool get_glyph_from_symbol (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + if (likely (typed_obj->get_glyph (codepoint, glyph))) + return true; + + if (codepoint <= 0x00FFu) + { + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/recom + * under "Non-Standard (Symbol) Fonts". */ + return typed_obj->get_glyph (0xF000u + codepoint, glyph); + } + + return false; + } + + private: + hb_nonnull_ptr_t subtable; + hb_nonnull_ptr_t subtable_uvs; + + hb_cmap_get_glyph_func_t get_glyph_funcZ; + const void *get_glyph_data; + + CmapSubtableFormat4::accelerator_t format4_accel; + + public: + hb_blob_ptr_t table; + }; + + protected: + + const CmapSubtable *find_subtable (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + const EncodingRecord &result = encodingRecord.bsearch (key); + if (!result.subtable) + return nullptr; + + return &(this+result.subtable); + } + + const EncodingRecord *find_encodingrec (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + return encodingRecord.as_array ().bsearch (key); + } + + bool find_subtable (unsigned format) const + { + auto it = + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) + ; + + return it.len (); + } + + public: + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version == 0) && + encodingRecord.sanitize (c, this)); + } + + protected: + HBUINT16 version; /* Table version number (0). */ + SortedArrayOf + encodingRecord; /* Encoding tables. */ + public: + DEFINE_SIZE_ARRAY (4, encodingRecord); +}; + +struct cmap_accelerator_t : cmap::accelerator_t {}; + +} /* namespace OT */ + + +#endif /* HB_OT_CMAP_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh new file mode 100644 index 00000000000..aaa1c37c64b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh @@ -0,0 +1,985 @@ +/* + * Copyright © 2016 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Seigo Nonaka, Calder Kitagawa + */ + +#ifndef HB_OT_COLOR_CBDT_TABLE_HH +#define HB_OT_COLOR_CBDT_TABLE_HH + +#include "hb-open-type.hh" + +/* + * CBLC -- Color Bitmap Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/cblc + * https://docs.microsoft.com/en-us/typography/opentype/spec/eblc + * CBDT -- Color Bitmap Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt + * https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt + */ +#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C') +#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T') + + +namespace OT { + +struct cblc_bitmap_size_subset_context_t +{ + const char *cbdt; + unsigned int cbdt_length; + hb_vector_t *cbdt_prime; + unsigned int size; /* INOUT + * Input: old size of IndexSubtable + * Output: new size of IndexSubtable + */ + unsigned int num_tables; /* INOUT + * Input: old number of subtables. + * Output: new number of subtables. + */ + hb_codepoint_t start_glyph; /* OUT */ + hb_codepoint_t end_glyph; /* OUT */ +}; + +static inline bool +_copy_data_to_cbdt (hb_vector_t *cbdt_prime, + const void *data, + unsigned length) +{ + unsigned int new_len = cbdt_prime->length + length; + if (unlikely (!cbdt_prime->alloc (new_len))) return false; + memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); + cbdt_prime->length = new_len; + return true; +} + +struct SmallGlyphMetrics +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const + { + extents->x_bearing = font->em_scale_x (bearingX); + extents->y_bearing = font->em_scale_y (bearingY); + extents->width = font->em_scale_x (width); + extents->height = font->em_scale_y (-static_cast(height)); + } + + HBUINT8 height; + HBUINT8 width; + HBINT8 bearingX; + HBINT8 bearingY; + HBUINT8 advance; + public: + DEFINE_SIZE_STATIC (5); +}; + +struct BigGlyphMetrics : SmallGlyphMetrics +{ + HBINT8 vertBearingX; + HBINT8 vertBearingY; + HBUINT8 vertAdvance; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct SBitLineMetrics +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBINT8 ascender; + HBINT8 decender; + HBUINT8 widthMax; + HBINT8 caretSlopeNumerator; + HBINT8 caretSlopeDenominator; + HBINT8 caretOffset; + HBINT8 minOriginSB; + HBINT8 minAdvanceSB; + HBINT8 maxBeforeBL; + HBINT8 minAfterBL; + HBINT8 padding1; + HBINT8 padding2; + public: + DEFINE_SIZE_STATIC (12); +}; + + +/* + * Index Subtables. + */ + +struct IndexSubtableHeader +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 indexFormat; + HBUINT16 imageFormat; + HBUINT32 imageDataOffset; + public: + DEFINE_SIZE_STATIC (8); +}; + +template +struct IndexSubtableFormat1Or3 +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + offsetArrayZ.sanitize (c, glyph_count + 1)); + } + + bool get_image_data (unsigned int idx, + unsigned int *offset, + unsigned int *length) const + { + if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx])) + return false; + + *offset = header.imageDataOffset + offsetArrayZ[idx]; + *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx]; + return true; + } + + bool add_offset (hb_serialize_context_t *c, + unsigned int offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + Offset embedded_offset; + embedded_offset = offset; + *size += sizeof (OffsetType); + auto *o = c->embed (embedded_offset); + return_trace ((bool) o); + } + + IndexSubtableHeader header; + UnsizedArrayOf> + offsetArrayZ; + public: + DEFINE_SIZE_ARRAY (8, offsetArrayZ); +}; + +struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; +struct IndexSubtableFormat3 : IndexSubtableFormat1Or3 {}; + +struct IndexSubtable +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c)) return_trace (false); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.sanitize (c, glyph_count)); + case 3: return_trace (u.format3.sanitize (c, glyph_count)); + default:return_trace (true); + } + } + + bool + finish_subtable (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_glyphs, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: { + if (!u.format3.add_offset (c, local_offset, size)) + return_trace (false); + if (!(num_glyphs & 0x01)) // Pad to 32-bit alignment if needed. + return_trace (u.format3.add_offset (c, 0, size)); + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: case 4: // No-op. + case 5: // Pad to 32-bit aligned. + default: return_trace (false); + } + } + + bool + fill_missing_glyphs (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_missing, + unsigned int *size /* OUT (accumulated) */, + unsigned int *num_glyphs /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format1.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + case 3: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format3.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: // Add empty space in cbdt_prime?. + case 4: case 5: // No-op as sparse is supported. + default: return_trace (false); + } + } + + bool + copy_glyph_at_idx (hb_serialize_context_t *c, unsigned int idx, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */, + IndexSubtable *subtable_prime /* INOUT */, + unsigned int *size /* OUT (accumulated) */) const + { + TRACE_SERIALIZE (this); + + unsigned int offset, length, format; + if (unlikely (!get_image_data (idx, &offset, &length, &format))) return_trace (false); + if (unlikely (offset > cbdt_length || cbdt_length - offset < length)) return_trace (false); + + auto *header_prime = subtable_prime->get_header (); + unsigned int new_local_offset = cbdt_prime->length - (unsigned int) header_prime->imageDataOffset; + if (unlikely (!_copy_data_to_cbdt (cbdt_prime, cbdt + offset, length))) return_trace (false); + + return_trace (subtable_prime->add_offset (c, new_local_offset, size)); + } + + bool + add_offset (hb_serialize_context_t *c, unsigned int local_offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: return_trace (u.format3.add_offset (c, local_offset, size)); + // TODO: Implement tables 2, 4, 5 + case 2: // Should be a no-op. + case 4: case 5: // Handle sparse cases. + default: return_trace (false); + } + } + + bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const + { + switch (u.header.indexFormat) + { + case 2: case 5: /* TODO */ + case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ + default:return (false); + } + } + + bool + get_image_data (unsigned int idx, unsigned int *offset, + unsigned int *length, unsigned int *format) const + { + *format = u.header.imageFormat; + switch (u.header.indexFormat) + { + case 1: return u.format1.get_image_data (idx, offset, length); + case 3: return u.format3.get_image_data (idx, offset, length); + default: return false; + } + } + + const IndexSubtableHeader* get_header () const { return &u.header; } + + void populate_header (unsigned index_format, + unsigned image_format, + unsigned int image_data_offset, + unsigned int *size) + { + u.header.indexFormat = index_format; + u.header.imageFormat = image_format; + u.header.imageDataOffset = image_data_offset; + switch (u.header.indexFormat) + { + case 1: *size += IndexSubtableFormat1::min_size; break; + case 3: *size += IndexSubtableFormat3::min_size; break; + } + } + + protected: + union { + IndexSubtableHeader header; + IndexSubtableFormat1 format1; + IndexSubtableFormat3 format3; + /* TODO: Format 2, 4, 5. */ + } u; + public: + DEFINE_SIZE_UNION (8, header); +}; + +struct IndexSubtableRecord +{ + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + firstGlyphIndex <= lastGlyphIndex && + offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1)); + } + + const IndexSubtable* get_subtable (const void *base) const + { + return &(base+offsetToSubtable); + } + + bool add_new_subtable (hb_subset_context_t* c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + IndexSubtableRecord *record, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start /* INOUT */) const + { + TRACE_SERIALIZE (this); + + auto *subtable = c->serializer->start_embed (); + if (unlikely (!subtable)) return_trace (false); + if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false); + + auto *old_subtable = get_subtable (base); + auto *old_header = old_subtable->get_header (); + + subtable->populate_header (old_header->indexFormat, + old_header->imageFormat, + bitmap_size_context->cbdt_prime->length, + &bitmap_size_context->size); + + unsigned int num_glyphs = 0; + bool early_exit = false; + for (unsigned int i = *start; i < lookup->length; i++) + { + hb_codepoint_t new_gid = (*lookup)[i].first; + const IndexSubtableRecord *next_record = (*lookup)[i].second; + const IndexSubtable *next_subtable = next_record->get_subtable (base); + auto *next_header = next_subtable->get_header (); + if (next_header != old_header) + { + *start = i; + early_exit = true; + break; + } + unsigned int num_missing = record->add_glyph_for_subset (new_gid); + if (unlikely (!subtable->fill_missing_glyphs (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_missing, + &bitmap_size_context->size, + &num_glyphs))) + return_trace (false); + + hb_codepoint_t old_gid = 0; + c->plan->old_gid_for_new_gid (new_gid, &old_gid); + if (old_gid < next_record->firstGlyphIndex) + return_trace (false); + + unsigned int old_idx = (unsigned int) old_gid - next_record->firstGlyphIndex; + if (unlikely (!next_subtable->copy_glyph_at_idx (c->serializer, + old_idx, + bitmap_size_context->cbdt, + bitmap_size_context->cbdt_length, + bitmap_size_context->cbdt_prime, + subtable, + &bitmap_size_context->size))) + return_trace (false); + num_glyphs += 1; + } + if (!early_exit) + *start = lookup->length; + if (unlikely (!subtable->finish_subtable (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_glyphs, + &bitmap_size_context->size))) + return_trace (false); + return_trace (true); + } + + bool add_new_record (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start, /* INOUT */ + hb_vector_t* records /* INOUT */) const + { + TRACE_SERIALIZE (this); + auto snap = c->serializer->snapshot (); + unsigned int old_size = bitmap_size_context->size; + unsigned int old_cbdt_prime_length = bitmap_size_context->cbdt_prime->length; + + // Set to invalid state to indicate filling glyphs is not yet started. + if (unlikely (!records->resize (records->length + 1))) + return_trace (c->serializer->check_success (false)); + + (*records)[records->length - 1].firstGlyphIndex = 1; + (*records)[records->length - 1].lastGlyphIndex = 0; + bitmap_size_context->size += IndexSubtableRecord::min_size; + + c->serializer->push (); + + if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), lookup, base, start))) + { + c->serializer->pop_discard (); + c->serializer->revert (snap); + bitmap_size_context->cbdt_prime->shrink (old_cbdt_prime_length); + bitmap_size_context->size = old_size; + records->resize (records->length - 1); + return_trace (false); + } + + bitmap_size_context->num_tables += 1; + return_trace (true); + } + + unsigned int add_glyph_for_subset (hb_codepoint_t gid) + { + if (firstGlyphIndex > lastGlyphIndex) + { + firstGlyphIndex = gid; + lastGlyphIndex = gid; + return 0; + } + // TODO maybe assert? this shouldn't occur. + if (lastGlyphIndex > gid) + return 0; + unsigned int num_missing = (unsigned int) (gid - lastGlyphIndex - 1); + lastGlyphIndex = gid; + return num_missing; + } + + bool get_extents (hb_glyph_extents_t *extents, const void *base) const + { return (base+offsetToSubtable).get_extents (extents); } + + bool get_image_data (unsigned int gid, + const void *base, + unsigned int *offset, + unsigned int *length, + unsigned int *format) const + { + if (gid < firstGlyphIndex || gid > lastGlyphIndex) return false; + return (base+offsetToSubtable).get_image_data (gid - firstGlyphIndex, + offset, length, format); + } + + HBGlyphID firstGlyphIndex; + HBGlyphID lastGlyphIndex; + LOffsetTo offsetToSubtable; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct IndexSubtableArray +{ + friend struct CBDT; + + bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (indexSubtablesZ.sanitize (c, count, this)); + } + + void + build_lookup (hb_subset_context_t *c, cblc_bitmap_size_subset_context_t *bitmap_size_context, + hb_vector_t> *lookup /* OUT */) const + { + bool start_glyph_is_set = false; + for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_glyphs (); new_gid++) + { + hb_codepoint_t old_gid; + if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue; + + const IndexSubtableRecord* record = find_table (old_gid, bitmap_size_context->num_tables); + if (unlikely (!record)) continue; + + // Don't add gaps to the lookup. The best way to determine if a glyph is a + // gap is that it has no image data. + unsigned int offset, length, format; + if (unlikely (!record->get_image_data (old_gid, this, &offset, &length, &format))) continue; + + lookup->push (hb_pair_t (new_gid, record)); + + if (!start_glyph_is_set) + { + bitmap_size_context->start_glyph = new_gid; + start_glyph_is_set = true; + } + + bitmap_size_context->end_glyph = new_gid; + } + } + + bool + subset (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context) const + { + TRACE_SUBSET (this); + + auto *dst = c->serializer->start_embed (); + if (unlikely (!dst)) return_trace (false); + + hb_vector_t> lookup; + build_lookup (c, bitmap_size_context, &lookup); + if (unlikely (lookup.in_error ())) + return c->serializer->check_success (false); + + bitmap_size_context->size = 0; + bitmap_size_context->num_tables = 0; + hb_vector_t records; + for (unsigned int start = 0; start < lookup.length;) + { + if (unlikely (!lookup[start].second->add_new_record (c, bitmap_size_context, &lookup, this, &start, &records))) + { + // Discard any leftover pushes to the serializer from successful records. + for (unsigned int i = 0; i < records.length; i++) + c->serializer->pop_discard (); + return_trace (false); + } + } + + /* Workaround to ensure offset ordering is from least to greatest when + * resolving links. */ + hb_vector_t objidxs; + for (unsigned int i = 0; i < records.length; i++) + objidxs.push (c->serializer->pop_pack ()); + for (unsigned int i = 0; i < records.length; i++) + { + IndexSubtableRecord* record = c->serializer->embed (records[i]); + if (unlikely (!record)) return_trace (false); + c->serializer->add_link (record->offsetToSubtable, objidxs[records.length - 1 - i]); + } + return_trace (true); + } + + public: + const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const + { + for (unsigned int i = 0; i < numTables; ++i) + { + unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex; + unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex; + if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) + return &indexSubtablesZ[i]; + } + return nullptr; + } + + protected: + UnsizedArrayOf indexSubtablesZ; +}; + +struct BitmapSizeTable +{ + friend struct CBLC; + friend struct CBDT; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) && + horizontal.sanitize (c) && + vertical.sanitize (c)); + } + + const IndexSubtableRecord * + find_table (hb_codepoint_t glyph, const void *base, const void **out_base) const + { + *out_base = &(base+indexSubtableArrayOffset); + return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); + } + + bool + subset (hb_subset_context_t *c, const void *base, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + auto *out_table = c->serializer->embed (this); + if (unlikely (!out_table)) return_trace (false); + + cblc_bitmap_size_subset_context_t bitmap_size_context; + bitmap_size_context.cbdt = cbdt; + bitmap_size_context.cbdt_length = cbdt_length; + bitmap_size_context.cbdt_prime = cbdt_prime; + bitmap_size_context.size = indexTablesSize; + bitmap_size_context.num_tables = numberOfIndexSubtables; + bitmap_size_context.start_glyph = 1; + bitmap_size_context.end_glyph = 0; + + if (!out_table->indexSubtableArrayOffset.serialize_subset (c, + indexSubtableArrayOffset, + base, + &bitmap_size_context)) + return_trace (false); + if (!bitmap_size_context.size || + !bitmap_size_context.num_tables || + bitmap_size_context.start_glyph > bitmap_size_context.end_glyph) + return_trace (false); + + out_table->indexTablesSize = bitmap_size_context.size; + out_table->numberOfIndexSubtables = bitmap_size_context.num_tables; + out_table->startGlyphIndex = bitmap_size_context.start_glyph; + out_table->endGlyphIndex = bitmap_size_context.end_glyph; + return_trace (true); + } + + protected: + LNNOffsetTo + indexSubtableArrayOffset; + HBUINT32 indexTablesSize; + HBUINT32 numberOfIndexSubtables; + HBUINT32 colorRef; + SBitLineMetrics horizontal; + SBitLineMetrics vertical; + HBGlyphID startGlyphIndex; + HBGlyphID endGlyphIndex; + HBUINT8 ppemX; + HBUINT8 ppemY; + HBUINT8 bitDepth; + HBINT8 flags; + public: + DEFINE_SIZE_STATIC (48); +}; + + +/* + * Glyph Bitmap Data Formats. + */ + +struct GlyphBitmapDataFormat17 +{ + SmallGlyphMetrics glyphMetrics; + LArrayOf data; + public: + DEFINE_SIZE_ARRAY (9, data); +}; + +struct GlyphBitmapDataFormat18 +{ + BigGlyphMetrics glyphMetrics; + LArrayOf data; + public: + DEFINE_SIZE_ARRAY (12, data); +}; + +struct GlyphBitmapDataFormat19 +{ + LArrayOf data; + public: + DEFINE_SIZE_ARRAY (4, data); +}; + +struct CBLC +{ + friend struct CBDT; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_CBLC; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3) && + sizeTables.sanitize (c, this)); + } + + static bool + sink_cbdt (hb_subset_context_t *c, hb_vector_t* cbdt_prime) + { + hb_blob_t *cbdt_prime_blob = hb_blob_create (cbdt_prime->arrayZ, + cbdt_prime->length, + HB_MEMORY_MODE_WRITABLE, + cbdt_prime->arrayZ, + free); + cbdt_prime->init (); // Leak arrayZ to the blob. + bool ret = c->plan->add_table (HB_OT_TAG_CBDT, cbdt_prime_blob); + hb_blob_destroy (cbdt_prime_blob); + return ret; + } + + bool + subset_size_table (hb_subset_context_t *c, const BitmapSizeTable& table, + const char *cbdt /* IN */, unsigned int cbdt_length, + CBLC *cblc_prime /* INOUT */, hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + cblc_prime->sizeTables.len++; + + auto snap = c->serializer->snapshot (); + auto cbdt_prime_len = cbdt_prime->length; + + if (!table.subset (c, this, cbdt, cbdt_length, cbdt_prime)) + { + cblc_prime->sizeTables.len--; + c->serializer->revert (snap); + cbdt_prime->shrink (cbdt_prime_len); + return_trace (false); + } + return_trace (true); + } + + // Implemented in cc file as it depends on definition of CBDT. + HB_INTERNAL bool subset (hb_subset_context_t *c) const; + + protected: + const BitmapSizeTable &choose_strike (hb_font_t *font) const + { + unsigned count = sizeTables.len; + if (unlikely (!count)) + return Null (BitmapSizeTable); + + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); + if (!requested_ppem) + requested_ppem = 1<<30; /* Choose largest strike. */ + unsigned int best_i = 0; + unsigned int best_ppem = hb_max (sizeTables[0].ppemX, sizeTables[0].ppemY); + + for (unsigned int i = 1; i < count; i++) + { + unsigned int ppem = hb_max (sizeTables[i].ppemX, sizeTables[i].ppemY); + if ((requested_ppem <= ppem && ppem < best_ppem) || + (requested_ppem > best_ppem && ppem > best_ppem)) + { + best_i = i; + best_ppem = ppem; + } + } + + return sizeTables[best_i]; + } + + protected: + FixedVersion<> version; + LArrayOf sizeTables; + public: + DEFINE_SIZE_ARRAY (8, sizeTables); +}; + +struct CBDT +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CBDT; + + struct accelerator_t + { + void init (hb_face_t *face) + { + cblc = hb_sanitize_context_t ().reference_table (face); + cbdt = hb_sanitize_context_t ().reference_table (face); + + upem = hb_face_get_upem (face); + } + + void fini () + { + this->cblc.destroy (); + this->cbdt.destroy (); + } + + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + { + const void *base; + const BitmapSizeTable &strike = this->cblc->choose_strike (font); + const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base); + if (!subtable_record || !strike.ppemX || !strike.ppemY) + return false; + + if (subtable_record->get_extents (extents, base)) + return true; + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) + return false; + + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return false; + + switch (image_format) + { + case 17: { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) + return false; + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + glyphFormat17.glyphMetrics.get_extents (font, extents); + break; + } + case 18: { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return false; + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + glyphFormat18.glyphMetrics.get_extents (font, extents); + break; + } + default: return false; /* TODO: Support other image formats. */ + } + + /* Convert to font units. */ + float x_scale = upem / (float) strike.ppemX; + float y_scale = upem / (float) strike.ppemY; + extents->x_bearing = roundf (extents->x_bearing * x_scale); + extents->y_bearing = roundf (extents->y_bearing * y_scale); + extents->width = roundf (extents->width * x_scale); + extents->height = roundf (extents->height * y_scale); + + return true; + } + + hb_blob_t* + reference_png (hb_font_t *font, hb_codepoint_t glyph) const + { + const void *base; + const BitmapSizeTable &strike = this->cblc->choose_strike (font); + const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base); + if (!subtable_record || !strike.ppemX || !strike.ppemY) + return hb_blob_get_empty (); + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) + return hb_blob_get_empty (); + + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return hb_blob_get_empty (); + + switch (image_format) + { + case 17: + { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat17::min_size, + glyphFormat17.data.len); + } + case 18: + { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat18::min_size, + glyphFormat18.data.len); + } + case 19: + { + if (unlikely (image_length < GlyphBitmapDataFormat19::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat19 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat19::min_size, + glyphFormat19.data.len); + } + default: return hb_blob_get_empty (); /* TODO: Support other image formats. */ + } + } + + bool has_data () const { return cbdt.get_length (); } + + private: + hb_blob_ptr_t cblc; + hb_blob_ptr_t cbdt; + + unsigned int upem; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3)); + } + + protected: + FixedVersion<> version; + UnsizedArrayOf dataZ; + public: + DEFINE_SIZE_ARRAY (4, dataZ); +}; + +inline bool +CBLC::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + auto *cblc_prime = c->serializer->start_embed (); + + // Use a vector as a secondary buffer as the tables need to be built in parallel. + hb_vector_t cbdt_prime; + + if (unlikely (!cblc_prime)) return_trace (false); + if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false); + cblc_prime->version = version; + + hb_blob_t* cbdt_blob = hb_sanitize_context_t ().reference_table (c->plan->source); + unsigned int cbdt_length; + CBDT* cbdt = (CBDT *) hb_blob_get_data (cbdt_blob, &cbdt_length); + if (unlikely (cbdt_length < CBDT::min_size)) + { + hb_blob_destroy (cbdt_blob); + return_trace (false); + } + _copy_data_to_cbdt (&cbdt_prime, cbdt, CBDT::min_size); + + for (const BitmapSizeTable& table : + sizeTables.iter ()) + subset_size_table (c, table, (const char *) cbdt, cbdt_length, cblc_prime, &cbdt_prime); + + hb_blob_destroy (cbdt_blob); + + return_trace (CBLC::sink_cbdt (c, &cbdt_prime)); +} + +struct CBDT_accelerator_t : CBDT::accelerator_t {}; + +} /* namespace OT */ + +#endif /* HB_OT_COLOR_CBDT_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh new file mode 100644 index 00000000000..92a49bb4f45 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh @@ -0,0 +1,278 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Calder Kitagawa + */ + +#ifndef HB_OT_COLOR_COLR_TABLE_HH +#define HB_OT_COLOR_COLR_TABLE_HH + +#include "hb-open-type.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +#define HB_OT_TAG_COLR HB_TAG('C','O','L','R') + + +namespace OT { + + +struct LayerRecord +{ + operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBGlyphID glyphId; /* Glyph ID of layer glyph */ + Index colorIdx; /* Index value to use with a + * selected color palette. + * An index value of 0xFFFF + * is a special case indicating + * that the text foreground + * color (defined by a + * higher-level client) should + * be used and shall not be + * treated as actual index + * into CPAL ColorRecord array. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseGlyphRecord +{ + int cmp (hb_codepoint_t g) const + { return g < glyphId ? -1 : g > glyphId ? 1 : 0; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + public: + HBGlyphID glyphId; /* Glyph ID of reference glyph */ + HBUINT16 firstLayerIdx; /* Index (from beginning of + * the Layer Records) to the + * layer record. There will be + * numLayers consecutive entries + * for this base glyph. */ + HBUINT16 numLayers; /* Number of color layers + * associated with this glyph */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct COLR +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; + + bool has_data () const { return numBaseGlyphs; } + + unsigned int get_glyph_layers (hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */) const + { + const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph); + + hb_array_t all_layers = (this+layersZ).as_array (numLayers); + hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, + record.numLayers); + if (count) + { + + glyph_layers.sub_array (start_offset, count) + | hb_sink (hb_array (layers, *count)) + ; + } + return glyph_layers.length; + } + + struct accelerator_t + { + accelerator_t () {} + ~accelerator_t () { fini (); } + + void init (hb_face_t *face) + { colr = hb_sanitize_context_t ().reference_table (face); } + + void fini () { this->colr.destroy (); } + + bool is_valid () { return colr.get_blob ()->length; } + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { colr->closure_glyphs (glyph, related_ids); } + + private: + hb_blob_ptr_t colr; + }; + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (!record) return; + + auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx, + record->numLayers); + if (!glyph_layers.length) return; + related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && + (this+layersZ).sanitize (c, numLayers))); + } + + template + bool serialize (hb_serialize_context_t *c, + unsigned version, + BaseIterator base_it, + LayerIterator layer_it) + { + TRACE_SERIALIZE (this); + if (unlikely (base_it.len () != layer_it.len ())) + return_trace (false); + + if (unlikely (!c->extend_min (this))) return_trace (false); + this->version = version; + numLayers = 0; + numBaseGlyphs = base_it.len (); + baseGlyphsZ = COLR::min_size; + layersZ = COLR::min_size + numBaseGlyphs * BaseGlyphRecord::min_size; + + for (const hb_item_type _ : + base_it.iter ()) + { + auto* record = c->embed (_); + if (unlikely (!record)) return_trace (false); + record->firstLayerIdx = numLayers; + numLayers += record->numLayers; + } + + for (const hb_item_type& _ : + layer_it.iter ()) + _.as_array ().copy (c); + + return_trace (true); + } + + const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const + { + if ((unsigned int) gid == 0) // Ignore notdef. + return nullptr; + const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid); + if ((record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + + auto base_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map_retains_sorting ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + if (unlikely (!old_record)) + return hb_pair_t (false, Null (BaseGlyphRecord)); + + BaseGlyphRecord new_record; + new_record.glyphId = new_gid; + new_record.numLayers = old_record->numLayers; + return hb_pair_t (true, new_record); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + auto layer_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (reverse_glyph_map) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + hb_vector_t out_layers; + + if (unlikely (!old_record || + old_record->firstLayerIdx >= numLayers || + old_record->firstLayerIdx + old_record->numLayers > numLayers)) + return hb_pair_t> (false, out_layers); + + auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx, + old_record->numLayers); + out_layers.resize (layers.length); + for (unsigned int i = 0; i < layers.length; i++) { + out_layers[i] = layers[i]; + hb_codepoint_t new_gid = 0; + if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid))) + return hb_pair_t> (false, out_layers); + out_layers[i].glyphId = new_gid; + } + + return hb_pair_t> (true, out_layers); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + if (unlikely (!base_it || !layer_it || base_it.len () != layer_it.len ())) + return_trace (false); + + COLR *colr_prime = c->serializer->start_embed (); + return_trace (colr_prime->serialize (c->serializer, version, base_it, layer_it)); + } + + protected: + HBUINT16 version; /* Table version number (starts at 0). */ + HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ + LNNOffsetTo> + baseGlyphsZ; /* Offset to Base Glyph records. */ + LNNOffsetTo> + layersZ; /* Offset to Layer Records. */ + HBUINT16 numLayers; /* Number of Layer Records. */ + public: + DEFINE_SIZE_STATIC (14); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_COLOR_COLR_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-color-cpal-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-cpal-table.hh new file mode 100644 index 00000000000..fa7d3207be6 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-color-cpal-table.hh @@ -0,0 +1,190 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Sascha Brawer + */ + +#ifndef HB_OT_COLOR_CPAL_TABLE_HH +#define HB_OT_COLOR_CPAL_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-color.h" +#include "hb-ot-name.h" + + +/* + * CPAL -- Color Palette + * https://docs.microsoft.com/en-us/typography/opentype/spec/cpal + */ +#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L') + + +namespace OT { + + +struct CPALV1Tail +{ + friend struct CPAL; + + private: + hb_ot_color_palette_flags_t get_palette_flags (const void *base, + unsigned int palette_index, + unsigned int palette_count) const + { + if (!paletteFlagsZ) return HB_OT_COLOR_PALETTE_FLAG_DEFAULT; + return (hb_ot_color_palette_flags_t) (uint32_t) + (base+paletteFlagsZ).as_array (palette_count)[palette_index]; + } + + hb_ot_name_id_t get_palette_name_id (const void *base, + unsigned int palette_index, + unsigned int palette_count) const + { + if (!paletteLabelsZ) return HB_OT_NAME_ID_INVALID; + return (base+paletteLabelsZ).as_array (palette_count)[palette_index]; + } + + hb_ot_name_id_t get_color_name_id (const void *base, + unsigned int color_index, + unsigned int color_count) const + { + if (!colorLabelsZ) return HB_OT_NAME_ID_INVALID; + return (base+colorLabelsZ).as_array (color_count)[color_index]; + } + + public: + bool sanitize (hb_sanitize_context_t *c, + const void *base, + unsigned int palette_count, + unsigned int color_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (!paletteFlagsZ || (base+paletteFlagsZ).sanitize (c, palette_count)) && + (!paletteLabelsZ || (base+paletteLabelsZ).sanitize (c, palette_count)) && + (!colorLabelsZ || (base+colorLabelsZ).sanitize (c, color_count))); + } + + protected: + LNNOffsetTo> + paletteFlagsZ; /* Offset from the beginning of CPAL table to + * the Palette Type Array. Set to 0 if no array + * is provided. */ + LNNOffsetTo> + paletteLabelsZ; /* Offset from the beginning of CPAL table to + * the palette labels array. Set to 0 if no + * array is provided. */ + LNNOffsetTo> + colorLabelsZ; /* Offset from the beginning of CPAL table to + * the color labels array. Set to 0 + * if no array is provided. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +typedef HBUINT32 BGRAColor; + +struct CPAL +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CPAL; + + bool has_data () const { return numPalettes; } + + unsigned int get_size () const + { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); } + + unsigned int get_palette_count () const { return numPalettes; } + unsigned int get_color_count () const { return numColors; } + + hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const + { return v1 ().get_palette_flags (this, palette_index, numPalettes); } + + hb_ot_name_id_t get_palette_name_id (unsigned int palette_index) const + { return v1 ().get_palette_name_id (this, palette_index, numPalettes); } + + hb_ot_name_id_t get_color_name_id (unsigned int color_index) const + { return v1 ().get_color_name_id (this, color_index, numColors); } + + unsigned int get_palette_colors (unsigned int palette_index, + unsigned int start_offset, + unsigned int *color_count, /* IN/OUT. May be NULL. */ + hb_color_t *colors /* OUT. May be NULL. */) const + { + if (unlikely (palette_index >= numPalettes)) + { + if (color_count) *color_count = 0; + return 0; + } + unsigned int start_index = colorRecordIndicesZ[palette_index]; + hb_array_t all_colors ((this+colorRecordsZ).arrayZ, numColorRecords); + hb_array_t palette_colors = all_colors.sub_array (start_index, + numColors); + if (color_count) + { + + palette_colors.sub_array (start_offset, color_count) + | hb_sink (hb_array (colors, *color_count)) + ; + } + return numColors; + } + + private: + const CPALV1Tail& v1 () const + { + if (version == 0) return Null (CPALV1Tail); + return StructAfter (*this); + } + + public: + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (this+colorRecordsZ).sanitize (c, numColorRecords) && + colorRecordIndicesZ.sanitize (c, numPalettes) && + (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors))); + } + + protected: + HBUINT16 version; /* Table version number */ + /* Version 0 */ + HBUINT16 numColors; /* Number of colors in each palette. */ + HBUINT16 numPalettes; /* Number of palettes in the table. */ + HBUINT16 numColorRecords; /* Total number of color records, combined for + * all palettes. */ + LNNOffsetTo> + colorRecordsZ; /* Offset from the beginning of CPAL table to + * the first ColorRecord. */ + UnsizedArrayOf + colorRecordIndicesZ; /* Index of each palette’s first color record in + * the combined color record array. */ +/*CPALV1Tail v1;*/ + public: + DEFINE_SIZE_ARRAY (12, colorRecordIndicesZ); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_COLOR_CPAL_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh new file mode 100644 index 00000000000..09da11597da --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh @@ -0,0 +1,414 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Calder Kitagawa + */ + +#ifndef HB_OT_COLOR_SBIX_TABLE_HH +#define HB_OT_COLOR_SBIX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" + +/* + * sbix -- Standard Bitmap Graphics + * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html + */ +#define HB_OT_TAG_sbix HB_TAG('s','b','i','x') + + +namespace OT { + + +struct SBIXGlyph +{ + SBIXGlyph* copy (hb_serialize_context_t *c, unsigned int data_length) const + { + TRACE_SERIALIZE (this); + SBIXGlyph* new_glyph = c->start_embed (); + if (unlikely (!new_glyph)) return_trace (nullptr); + if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr); + + new_glyph->xOffset = xOffset; + new_glyph->yOffset = yOffset; + new_glyph->graphicType = graphicType; + data.copy (c, data_length); + return_trace (new_glyph); + } + + HBINT16 xOffset; /* The horizontal (x-axis) offset from the left + * edge of the graphic to the glyph’s origin. + * That is, the x-coordinate of the point on the + * baseline at the left edge of the glyph. */ + HBINT16 yOffset; /* The vertical (y-axis) offset from the bottom + * edge of the graphic to the glyph’s origin. + * That is, the y-coordinate of the point on the + * baseline at the left edge of the glyph. */ + Tag graphicType; /* Indicates the format of the embedded graphic + * data: one of 'jpg ', 'png ' or 'tiff', or the + * special format 'dupe'. */ + UnsizedArrayOf + data; /* The actual embedded graphic data. The total + * length is inferred from sequential entries in + * the glyphDataOffsets array and the fixed size + * (8 bytes) of the preceding fields. */ + public: + DEFINE_SIZE_ARRAY (8, data); +}; + +struct SBIXStrike +{ + static unsigned int get_size (unsigned num_glyphs) + { return min_size + num_glyphs * HBUINT32::static_size; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1)); + } + + hb_blob_t *get_glyph_blob (unsigned int glyph_id, + hb_blob_t *sbix_blob, + hb_tag_t file_type, + int *x_offset, + int *y_offset, + unsigned int num_glyphs, + unsigned int *strike_ppem) const + { + if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */ + + unsigned int retry_count = 8; + unsigned int sbix_len = sbix_blob->length; + unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data; + assert (strike_offset < sbix_len); + + retry: + if (unlikely (glyph_id >= num_glyphs || + imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] || + imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size || + (unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset)) + return hb_blob_get_empty (); + + unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size; + unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size; + + const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]); + + if (glyph->graphicType == HB_TAG ('d','u','p','e')) + { + if (glyph_length >= 2) + { + glyph_id = *((HBUINT16 *) &glyph->data); + if (retry_count--) + goto retry; + } + return hb_blob_get_empty (); + } + + if (unlikely (file_type != glyph->graphicType)) + return hb_blob_get_empty (); + + if (strike_ppem) *strike_ppem = ppem; + if (x_offset) *x_offset = glyph->xOffset; + if (y_offset) *y_offset = glyph->yOffset; + return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length); + } + + bool subset (hb_subset_context_t *c, unsigned int available_len) const + { + TRACE_SUBSET (this); + unsigned int num_output_glyphs = c->plan->num_output_glyphs (); + + auto* out = c->serializer->start_embed (); + if (unlikely (!out)) return_trace (false); + auto snap = c->serializer->snapshot (); + if (unlikely (!c->serializer->extend (*out, num_output_glyphs + 1))) return_trace (false); + out->ppem = ppem; + out->resolution = resolution; + HBUINT32 head; + head = get_size (num_output_glyphs + 1); + + bool has_glyphs = false; + for (unsigned new_gid = 0; new_gid < num_output_glyphs; new_gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid) || + unlikely (imageOffsetsZ[old_gid].is_null () || + imageOffsetsZ[old_gid + 1].is_null () || + imageOffsetsZ[old_gid + 1] <= imageOffsetsZ[old_gid] || + imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid] <= SBIXGlyph::min_size) || + (unsigned int) imageOffsetsZ[old_gid + 1] > available_len) + { + out->imageOffsetsZ[new_gid] = head; + continue; + } + has_glyphs = true; + unsigned int delta = imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid]; + unsigned int glyph_data_length = delta - SBIXGlyph::min_size; + if (!(this+imageOffsetsZ[old_gid]).copy (c->serializer, glyph_data_length)) + return_trace (false); + out->imageOffsetsZ[new_gid] = head; + head += delta; + } + if (has_glyphs) + out->imageOffsetsZ[num_output_glyphs] = head; + else + c->serializer->revert (snap); + return_trace (has_glyphs); + } + + public: + HBUINT16 ppem; /* The PPEM size for which this strike was designed. */ + HBUINT16 resolution; /* The device pixel density (in PPI) for which this + * strike was designed. (E.g., 96 PPI, 192 PPI.) */ + protected: + UnsizedArrayOf> + imageOffsetsZ; /* Offset from the beginning of the strike data header + * to bitmap data for an individual glyph ID. */ + public: + DEFINE_SIZE_ARRAY (4, imageOffsetsZ); +}; + +struct sbix +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_sbix; + + bool has_data () const { return version; } + + const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; } + + struct accelerator_t + { + void init (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face); + num_glyphs = face->get_num_glyphs (); + } + void fini () { table.destroy (); } + + bool has_data () const { return table->has_data (); } + + bool get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + /* We only support PNG right now, and following function checks type. */ + return get_png_extents (font, glyph, extents); + } + + hb_blob_t *reference_png (hb_font_t *font, + hb_codepoint_t glyph_id, + int *x_offset, + int *y_offset, + unsigned int *available_ppem) const + { + return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (), + HB_TAG ('p','n','g',' '), + x_offset, y_offset, + num_glyphs, available_ppem); + } + + private: + + const SBIXStrike &choose_strike (hb_font_t *font) const + { + unsigned count = table->strikes.len; + if (unlikely (!count)) + return Null (SBIXStrike); + + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); + if (!requested_ppem) + requested_ppem = 1<<30; /* Choose largest strike. */ + /* TODO Add DPI sensitivity as well? */ + unsigned int best_i = 0; + unsigned int best_ppem = table->get_strike (0).ppem; + + for (unsigned int i = 1; i < count; i++) + { + unsigned int ppem = (table->get_strike (i)).ppem; + if ((requested_ppem <= ppem && ppem < best_ppem) || + (requested_ppem > best_ppem && ppem > best_ppem)) + { + best_i = i; + best_ppem = ppem; + } + } + + return table->get_strike (best_i); + } + + struct PNGHeader + { + HBUINT8 signature[8]; + struct + { + struct + { + HBUINT32 length; + Tag type; + } header; + HBUINT32 width; + HBUINT32 height; + HBUINT8 bitDepth; + HBUINT8 colorType; + HBUINT8 compressionMethod; + HBUINT8 filterMethod; + HBUINT8 interlaceMethod; + } IHDR; + + public: + DEFINE_SIZE_STATIC (29); + }; + + bool get_png_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + /* Following code is safe to call even without data. + * But faster to short-circuit. */ + if (!has_data ()) + return false; + + int x_offset = 0, y_offset = 0; + unsigned int strike_ppem = 0; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + + const PNGHeader &png = *blob->as(); + + extents->x_bearing = x_offset; + extents->y_bearing = png.IHDR.height + y_offset; + extents->width = png.IHDR.width; + extents->height = -1 * png.IHDR.height; + + /* Convert to font units. */ + if (strike_ppem) + { + float scale = font->face->get_upem () / (float) strike_ppem; + extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale); + extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale); + extents->width = font->em_scalef_x (extents->width * scale); + extents->height = font->em_scalef_y (extents->height * scale); + } + else + { + extents->x_bearing = font->em_scale_x (extents->x_bearing); + extents->y_bearing = font->em_scale_y (extents->y_bearing); + extents->width = font->em_scale_x (extents->width); + extents->height = font->em_scale_y (extents->height); + } + + hb_blob_destroy (blob); + + return strike_ppem; + } + + private: + hb_blob_ptr_t table; + + unsigned int num_glyphs; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version >= 1 && + strikes.sanitize (c, this))); + } + + bool + add_strike (hb_subset_context_t *c, unsigned i) const + { + if (strikes[i].is_null () || c->source_blob->length < (unsigned) strikes[i]) + return false; + + return (this+strikes[i]).subset (c, c->source_blob->length - (unsigned) strikes[i]); + } + + bool serialize_strike_offsets (hb_subset_context_t *c) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed> (); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_vector_t*> new_strikes; + hb_vector_t objidxs; + for (int i = strikes.len - 1; i >= 0; --i) + { + auto* o = out->serialize_append (c->serializer); + if (unlikely (!o)) return_trace (false); + *o = 0; + auto snap = c->serializer->snapshot (); + c->serializer->push (); + bool ret = add_strike (c, i); + if (!ret) + { + c->serializer->pop_discard (); + out->pop (); + c->serializer->revert (snap); + } + else + { + objidxs.push (c->serializer->pop_pack ()); + new_strikes.push (o); + } + } + for (unsigned int i = 0; i < new_strikes.length; ++i) + c->serializer->add_link (*new_strikes[i], objidxs[new_strikes.length - 1 - i]); + + return_trace (true); + } + + bool subset (hb_subset_context_t* c) const + { + TRACE_SUBSET (this); + + sbix *sbix_prime = c->serializer->start_embed (); + if (unlikely (!sbix_prime)) return_trace (false); + if (unlikely (!c->serializer->embed (this->version))) return_trace (false); + if (unlikely (!c->serializer->embed (this->flags))) return_trace (false); + + return_trace (serialize_strike_offsets (c)); + } + + protected: + HBUINT16 version; /* Table version number — set to 1 */ + HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines. + * Bits 2 to 15: reserved (set to 0). */ + LOffsetLArrayOf + strikes; /* Offsets from the beginning of the 'sbix' + * table to data for each individual bitmap strike. */ + public: + DEFINE_SIZE_ARRAY (8, strikes); +}; + +struct sbix_accelerator_t : sbix::accelerator_t {}; + +} /* namespace OT */ + +#endif /* HB_OT_COLOR_SBIX_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh new file mode 100644 index 00000000000..1cc40ae53fa --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh @@ -0,0 +1,124 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_COLOR_SVG_TABLE_HH +#define HB_OT_COLOR_SVG_TABLE_HH + +#include "hb-open-type.hh" + +/* + * SVG -- SVG (Scalable Vector Graphics) + * https://docs.microsoft.com/en-us/typography/opentype/spec/svg + */ + +#define HB_OT_TAG_SVG HB_TAG('S','V','G',' ') + + +namespace OT { + + +struct SVGDocumentIndexEntry +{ + int cmp (hb_codepoint_t g) const + { return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; } + + hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const + { + return hb_blob_create_sub_blob (svg_blob, + index_offset + (unsigned int) svgDoc, + svgDocLength); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + svgDoc.sanitize (c, base, svgDocLength)); + } + + protected: + HBUINT16 startGlyphID; /* The first glyph ID in the range described by + * this index entry. */ + HBUINT16 endGlyphID; /* The last glyph ID in the range described by + * this index entry. Must be >= startGlyphID. */ + LNNOffsetTo> + svgDoc; /* Offset from the beginning of the SVG Document Index + * to an SVG document. Must be non-zero. */ + HBUINT32 svgDocLength; /* Length of the SVG document. + * Must be non-zero. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct SVG +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_SVG; + + bool has_data () const { return svgDocEntries; } + + struct accelerator_t + { + void init (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + void fini () { table.destroy (); } + + hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const + { + return table->get_glyph_entry (glyph_id).reference_blob (table.get_blob (), + table->svgDocEntries); + } + + bool has_data () const { return table->has_data (); } + + private: + hb_blob_ptr_t table; + }; + + const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const + { return (this+svgDocEntries).bsearch (glyph_id); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (this+svgDocEntries).sanitize_shallow (c))); + } + + protected: + HBUINT16 version; /* Table version (starting at 0). */ + LOffsetTo> + svgDocEntries; /* Offset (relative to the start of the SVG table) to the + * SVG Documents Index. Must be non-zero. */ + /* Array of SVG Document Index Entries. */ + HBUINT32 reserved; /* Set to 0. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct SVG_accelerator_t : SVG::accelerator_t {}; + +} /* namespace OT */ + + +#endif /* HB_OT_COLOR_SVG_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-color.cc b/thirdparty/harfbuzz/src/hb-ot-color.cc new file mode 100644 index 00000000000..0e7203a88bf --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-color.cc @@ -0,0 +1,321 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Sascha Brawer, Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_COLOR + +#include "hb-ot.h" + +#include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-color-colr-table.hh" +#include "hb-ot-color-cpal-table.hh" +#include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-svg-table.hh" + +#include +#include + + +/** + * SECTION:hb-ot-color + * @title: hb-ot-color + * @short_description: OpenType Color Fonts + * @include: hb-ot.h + * + * Functions for fetching color-font information from OpenType font faces. + * + * HarfBuzz supports `COLR`/`CPAL`, `sbix`, `CBDT`, and `SVG` color fonts. + **/ + + +/* + * CPAL + */ + + +/** + * hb_ot_color_has_palettes: + * @face: #hb_face_t to work upon + * + * Tests whether a face includes a `CPAL` color-palette table. + * + * Return value: true if data found, false otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_palettes (hb_face_t *face) +{ + return face->table.CPAL->has_data (); +} + +/** + * hb_ot_color_palette_get_count: + * @face: #hb_face_t to work upon + * + * Fetches the number of color palettes in a face. + * + * Return value: the number of palettes found + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_palette_get_count (hb_face_t *face) +{ + return face->table.CPAL->get_palette_count (); +} + +/** + * hb_ot_color_palette_get_name_id: + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the `name` table Name ID that provides display names for + * a `CPAL` color palette. + * + * Palette display names can be generic (e.g., "Default") or provide + * specific, themed names (e.g., "Spring", "Summer", "Fall", and "Winter"). + * + * Return value: the Named ID found for the palette. + * If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID. + * + * Since: 2.1.0 + */ +hb_ot_name_id_t +hb_ot_color_palette_get_name_id (hb_face_t *face, + unsigned int palette_index) +{ + return face->table.CPAL->get_palette_name_id (palette_index); +} + +/** + * hb_ot_color_palette_color_get_name_id: + * @face: #hb_face_t to work upon + * @color_index: The index of the color + * + * Fetches the `name` table Name ID that provides display names for + * the specificed color in a face's `CPAL` color palette. + * + * Display names can be generic (e.g., "Background") or specific + * (e.g., "Eye color"). + * + * Return value: the Name ID found for the color. + * + * Since: 2.1.0 + */ +hb_ot_name_id_t +hb_ot_color_palette_color_get_name_id (hb_face_t *face, + unsigned int color_index) +{ + return face->table.CPAL->get_color_name_id (color_index); +} + +/** + * hb_ot_color_palette_get_flags: + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the flags defined for a color palette. + * + * Return value: the #hb_ot_color_palette_flags_t of the requested color palette + * + * Since: 2.1.0 + */ +hb_ot_color_palette_flags_t +hb_ot_color_palette_get_flags (hb_face_t *face, + unsigned int palette_index) +{ + return face->table.CPAL->get_palette_flags (palette_index); +} + +/** + * hb_ot_color_palette_get_colors: + * @face: #hb_face_t to work upon + * @palette_index: the index of the color palette to query + * @start_offset: offset of the first color to retrieve + * @color_count: (inout) (optional): Input = the maximum number of colors to return; + * Output = the actual number of colors returned (may be zero) + * @colors: (out) (array length=color_count) (nullable): The array of #hb_color_t records found + * + * Fetches a list of the colors in a color palette. + * + * After calling this function, @colors will be filled with the palette + * colors. If @colors is NULL, the function will just return the number + * of total colors without storing any actual colors; this can be used + * for allocating a buffer of suitable size before calling + * hb_ot_color_palette_get_colors() a second time. + * + * Return value: the total number of colors in the palette + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_palette_get_colors (hb_face_t *face, + unsigned int palette_index, + unsigned int start_offset, + unsigned int *colors_count /* IN/OUT. May be NULL. */, + hb_color_t *colors /* OUT. May be NULL. */) +{ + return face->table.CPAL->get_palette_colors (palette_index, start_offset, colors_count, colors); +} + + +/* + * COLR + */ + +/** + * hb_ot_color_has_layers: + * @face: #hb_face_t to work upon + * + * Tests whether a face includes any `COLR` color layers. + * + * Return value: true if data found, false otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_layers (hb_face_t *face) +{ + return face->table.COLR->has_data (); +} + +/** + * hb_ot_color_glyph_get_layers: + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * @start_offset: offset of the first layer to retrieve + * @layer_count: (inout) (optional): Input = the maximum number of layers to return; + * Output = the actual number of layers returned (may be zero) + * @layers: (out) (array length=layer_count) (nullable): The array of layers found + * + * Fetches a list of all color layers for the specified glyph index in the specified + * face. The list returned will begin at the offset provided. + * + * Return value: Total number of layers available for the glyph index queried + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_glyph_get_layers (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *layer_count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */) +{ + return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers); +} + + +/* + * SVG + */ + +/** + * hb_ot_color_has_svg: + * @face: #hb_face_t to work upon. + * + * Tests whether a face includes any `SVG` glyph images. + * + * Return value: true if data found, false otherwise. + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_svg (hb_face_t *face) +{ + return face->table.SVG->has_data (); +} + +/** + * hb_ot_color_glyph_reference_svg: + * @face: #hb_face_t to work upon + * @glyph: a svg glyph index + * + * Fetches the SVG document for a glyph. The blob may be either plain text or gzip-encoded. + * + * Return value: (transfer full): An #hb_blob_t containing the SVG document of the glyph, if available + * + * Since: 2.1.0 + */ +hb_blob_t * +hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph) +{ + return face->table.SVG->reference_blob_for_glyph (glyph); +} + + +/* + * PNG: CBDT or sbix + */ + +/** + * hb_ot_color_has_png: + * @face: #hb_face_t to work upon + * + * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables). + * + * Return value: true if data found, false otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_png (hb_face_t *face) +{ + return face->table.CBDT->has_data () || face->table.sbix->has_data (); +} + +/** + * hb_ot_color_glyph_reference_png: + * @font: #hb_font_t to work upon + * @glyph: a glyph index + * + * Fetches the PNG image for a glyph. This function takes a font object, not a face object, + * as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font + * object. If UPEM is unset, the blob returned will be the largest PNG available. + * + * Return value: (transfer full): An #hb_blob_t containing the PNG image for the glyph, if available + * + * Since: 2.1.0 + */ +hb_blob_t * +hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph) +{ + hb_blob_t *blob = hb_blob_get_empty (); + + if (font->face->table.sbix->has_data ()) + blob = font->face->table.sbix->reference_png (font, glyph, nullptr, nullptr, nullptr); + + if (!blob->length && font->face->table.CBDT->has_data ()) + blob = font->face->table.CBDT->reference_png (font, glyph); + + return blob; +} + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-ot-color.h b/thirdparty/harfbuzz/src/hb-ot-color.h new file mode 100644 index 00000000000..63ef20a1a08 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-color.h @@ -0,0 +1,139 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Khaled Hosny + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Sascha Brawer, Behdad Esfahbod + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_COLOR_H +#define HB_OT_COLOR_H + +#include "hb.h" +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/* + * Color palettes. + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_palettes (hb_face_t *face); + +HB_EXTERN unsigned int +hb_ot_color_palette_get_count (hb_face_t *face); + +HB_EXTERN hb_ot_name_id_t +hb_ot_color_palette_get_name_id (hb_face_t *face, + unsigned int palette_index); + +HB_EXTERN hb_ot_name_id_t +hb_ot_color_palette_color_get_name_id (hb_face_t *face, + unsigned int color_index); + +/** + * hb_ot_color_palette_flags_t: + * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: Default indicating that there is nothing special + * to note about a color palette. + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: Flag indicating that the color + * palette is appropriate to use when displaying the font on a light background such as white. + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: Flag indicating that the color + * palette is appropriate to use when displaying the font on a dark background such as black. + * + * Since: 2.1.0 + */ +typedef enum { /*< flags >*/ + HB_OT_COLOR_PALETTE_FLAG_DEFAULT = 0x00000000u, + HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND = 0x00000001u, + HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND = 0x00000002u +} hb_ot_color_palette_flags_t; + +HB_EXTERN hb_ot_color_palette_flags_t +hb_ot_color_palette_get_flags (hb_face_t *face, + unsigned int palette_index); + +HB_EXTERN unsigned int +hb_ot_color_palette_get_colors (hb_face_t *face, + unsigned int palette_index, + unsigned int start_offset, + unsigned int *color_count, /* IN/OUT. May be NULL. */ + hb_color_t *colors /* OUT. May be NULL. */); + + +/* + * Color layers. + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_layers (hb_face_t *face); + +/** + * hb_ot_color_layer_t: + * + * Pairs of glyph and color index. + * + * Since: 2.1.0 + **/ +typedef struct hb_ot_color_layer_t +{ + hb_codepoint_t glyph; + unsigned int color_index; +} hb_ot_color_layer_t; + +HB_EXTERN unsigned int +hb_ot_color_glyph_get_layers (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *layer_count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */); + +/* + * SVG + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_svg (hb_face_t *face); + +HB_EXTERN hb_blob_t * +hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph); + +/* + * PNG: CBDT or sbix + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_png (hb_face_t *face); + +HB_EXTERN hb_blob_t * +hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph); + + +HB_END_DECLS + +#endif /* HB_OT_COLOR_H */ diff --git a/thirdparty/harfbuzz/src/hb-ot-deprecated.h b/thirdparty/harfbuzz/src/hb-ot-deprecated.h new file mode 100644 index 00000000000..bc72f8a7016 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-deprecated.h @@ -0,0 +1,111 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_DEPRECATED_H +#define HB_OT_DEPRECATED_H + +#include "hb.h" +#include "hb-ot-name.h" + + +HB_BEGIN_DECLS + +#ifndef HB_DISABLE_DEPRECATED + + +/* https://github.com/harfbuzz/harfbuzz/issues/1734 */ +#define HB_MATH_GLYPH_PART_FLAG_EXTENDER HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER + + +/* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */ +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) hb_bool_t +hb_ot_layout_table_choose_script (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *script_tags, + unsigned int *script_index, + hb_tag_t *chosen_script); + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) hb_bool_t +hb_ot_layout_script_find_language (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + hb_tag_t language_tag, + unsigned int *language_index); + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) void +hb_ot_tags_from_script (hb_script_t script, + hb_tag_t *script_tag_1, + hb_tag_t *script_tag_2); + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) hb_tag_t +hb_ot_tag_from_language (hb_language_t language); + + +/** + * HB_OT_VAR_NO_AXIS_INDEX: + * + * Since: 1.4.2 + * Deprecated: 2.2.0 + */ +#define HB_OT_VAR_NO_AXIS_INDEX 0xFFFFFFFFu + +/** + * hb_ot_var_axis_t: + * + * Since: 1.4.2 + * Deprecated: 2.2.0 + */ +typedef struct hb_ot_var_axis_t +{ + hb_tag_t tag; + hb_ot_name_id_t name_id; + float min_value; + float default_value; + float max_value; +} hb_ot_var_axis_t; + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) unsigned int +hb_ot_var_get_axes (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_t *axes_array /* OUT */); + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) hb_bool_t +hb_ot_var_find_axis (hb_face_t *face, + hb_tag_t axis_tag, + unsigned int *axis_index, + hb_ot_var_axis_t *axis_info); + + +#endif + +HB_END_DECLS + +#endif /* HB_OT_DEPRECATED_H */ diff --git a/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh b/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh new file mode 100644 index 00000000000..367e143fdfb --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh @@ -0,0 +1,138 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 Google, Inc. + * Copyright © 2019, Facebook Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_TABLE_LIST_HH +#define HB_OT_FACE_TABLE_LIST_HH +#endif /* HB_OT_FACE_TABLE_LIST_HH */ /* Dummy header guards */ + +#ifndef HB_OT_ACCELERATOR +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_ACCELERATOR_UNDEF +#endif + + +/* This lists font tables that the hb_face_t will contain and lazily + * load. Don't add a table unless it's used though. This is not + * exactly free. */ + +/* v--- Add new tables in the right place here. */ + + +/* OpenType fundamentals. */ +HB_OT_TABLE (OT, head) +#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) +HB_OT_ACCELERATOR (OT, cmap) +#endif +HB_OT_TABLE (OT, hhea) +HB_OT_ACCELERATOR (OT, hmtx) +HB_OT_TABLE (OT, OS2) +#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE) +HB_OT_ACCELERATOR (OT, post) +#endif +#ifndef HB_NO_NAME +HB_OT_ACCELERATOR (OT, name) +#endif +#ifndef HB_NO_STYLE +HB_OT_TABLE (OT, STAT) +#endif +#ifndef HB_NO_META +HB_OT_ACCELERATOR (OT, meta) +#endif + +/* Vertical layout. */ +HB_OT_TABLE (OT, vhea) +HB_OT_ACCELERATOR (OT, vmtx) + +/* TrueType outlines. */ +HB_OT_ACCELERATOR (OT, glyf) + +/* CFF outlines. */ +#ifndef HB_NO_CFF +HB_OT_ACCELERATOR (OT, cff1) +HB_OT_ACCELERATOR (OT, cff2) +HB_OT_TABLE (OT, VORG) +#endif + +/* OpenType variations. */ +#ifndef HB_NO_VAR +HB_OT_TABLE (OT, fvar) +HB_OT_TABLE (OT, avar) +HB_OT_ACCELERATOR (OT, gvar) +HB_OT_TABLE (OT, MVAR) +#endif + +/* Legacy kern. */ +#ifndef HB_NO_OT_KERN +HB_OT_TABLE (OT, kern) +#endif + +/* OpenType shaping. */ +#ifndef HB_NO_OT_LAYOUT +HB_OT_ACCELERATOR (OT, GDEF) +HB_OT_ACCELERATOR (OT, GSUB) +HB_OT_ACCELERATOR (OT, GPOS) +//HB_OT_TABLE (OT, JSTF) +#endif + +/* OpenType baseline. */ +#ifndef HB_NO_BASE +HB_OT_TABLE (OT, BASE) +#endif + +/* AAT shaping. */ +#ifndef HB_NO_AAT +HB_OT_TABLE (AAT, morx) +HB_OT_TABLE (AAT, mort) +HB_OT_TABLE (AAT, kerx) +HB_OT_TABLE (AAT, ankr) +HB_OT_TABLE (AAT, trak) +HB_OT_TABLE (AAT, ltag) +HB_OT_TABLE (AAT, feat) +// HB_OT_TABLE (AAT, opbd) +#endif + +/* OpenType color fonts. */ +#ifndef HB_NO_COLOR +HB_OT_TABLE (OT, COLR) +HB_OT_TABLE (OT, CPAL) +HB_OT_ACCELERATOR (OT, CBDT) +HB_OT_ACCELERATOR (OT, sbix) +HB_OT_ACCELERATOR (OT, SVG) +#endif + +/* OpenType math. */ +#ifndef HB_NO_MATH +HB_OT_TABLE (OT, MATH) +#endif + + +#ifdef _HB_OT_ACCELERATOR_UNDEF +#undef HB_OT_ACCELERATOR +#endif diff --git a/thirdparty/harfbuzz/src/hb-ot-face.cc b/thirdparty/harfbuzz/src/hb-ot-face.cc new file mode 100644 index 00000000000..5ef8df43ce7 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-face.cc @@ -0,0 +1,58 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-face.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-kern-table.hh" +#include "hb-ot-meta-table.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-svg-table.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-gpos-table.hh" + + +void hb_ot_face_t::init0 (hb_face_t *face) +{ + this->face = face; +#define HB_OT_TABLE(Namespace, Type) Type.init0 (); +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE +} +void hb_ot_face_t::fini () +{ +#define HB_OT_TABLE(Namespace, Type) Type.fini (); +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE +} diff --git a/thirdparty/harfbuzz/src/hb-ot-face.hh b/thirdparty/harfbuzz/src/hb-ot-face.hh new file mode 100644 index 00000000000..e24d380bca8 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-face.hh @@ -0,0 +1,74 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_HH +#define HB_OT_FACE_HH + +#include "hb.hh" + +#include "hb-machinery.hh" + + +/* + * hb_ot_face_t + */ + +/* Declare tables. */ +#define HB_OT_TABLE(Namespace, Type) namespace Namespace { struct Type; } +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type##_accelerator_t) +#include "hb-ot-face-table-list.hh" +#undef HB_OT_ACCELERATOR +#undef HB_OT_TABLE + +struct hb_ot_face_t +{ + HB_INTERNAL void init0 (hb_face_t *face); + HB_INTERNAL void fini (); + +#define HB_OT_TABLE_ORDER(Namespace, Type) \ + HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type))) + enum order_t + { + ORDER_ZERO, +#define HB_OT_TABLE(Namespace, Type) HB_OT_TABLE_ORDER (Namespace, Type), +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE + }; + + hb_face_t *face; /* MUST be JUST before the lazy loaders. */ +#define HB_OT_TABLE(Namespace, Type) \ + hb_table_lazy_loader_t Type; +#define HB_OT_ACCELERATOR(Namespace, Type) \ + hb_face_lazy_loader_t Type; +#include "hb-ot-face-table-list.hh" +#undef HB_OT_ACCELERATOR +#undef HB_OT_TABLE +}; + + +#endif /* HB_OT_FACE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-font.cc b/thirdparty/harfbuzz/src/hb-ot-font.cc new file mode 100644 index 00000000000..a1dc88603a2 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-font.cc @@ -0,0 +1,336 @@ +/* + * Copyright © 2011,2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_FONT + +#include "hb-ot.h" + +#include "hb-font.hh" +#include "hb-machinery.hh" +#include "hb-ot-face.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-vorg-table.hh" +#include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-color-sbix-table.hh" + + +/** + * SECTION:hb-ot-font + * @title: hb-ot-font + * @short_description: OpenType font implementation + * @include: hb-ot.h + * + * Functions for using OpenType fonts with hb_shape(). Note that fonts returned + * by hb_font_create() default to using these functions, so most clients would + * never need to call these functions directly. + **/ + + +static hb_bool_t +hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + return ot_face->cmap->get_nominal_glyph (unicode, glyph); +} + +static unsigned int +hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + return ot_face->cmap->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + +static hb_bool_t +hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + return ot_face->cmap->get_variation_glyph (unicode, variation_selector, glyph); +} + +static void +hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; + + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_x (hmtx.get_advance (*first_glyph, font)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +static void +hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_y (-(int) vmtx.get_advance (*first_glyph, font)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +static hb_bool_t +hb_ot_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + + *x = font->get_glyph_h_advance (glyph) / 2; + +#ifndef HB_NO_OT_FONT_CFF + const OT::VORG &VORG = *ot_face->VORG; + if (VORG.has_data ()) + { + *y = font->em_scale_y (VORG.get_y_origin (glyph)); + return true; + } +#endif + + hb_glyph_extents_t extents = {0}; + if (ot_face->glyf->get_extents (font, glyph, &extents)) + { + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + hb_position_t tsb = vmtx.get_side_bearing (font, glyph); + *y = extents.y_bearing + font->em_scale_y (tsb); + return true; + } + + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + *y = font_extents.ascender; + + return true; +} + +static hb_bool_t +hb_ot_get_glyph_extents (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->sbix->get_extents (font, glyph, extents)) return true; +#endif + if (ot_face->glyf->get_extents (font, glyph, extents)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_extents (font, glyph, extents)) return true; + if (ot_face->cff2->get_extents (font, glyph, extents)) return true; +#endif +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; +#endif + + // TODO Hook up side-bearings variations. + return false; +} + +#ifndef HB_NO_OT_FONT_GLYPH_NAMES +static hb_bool_t +hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + if (ot_face->post->get_glyph_name (glyph, name, size)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_name (glyph, name, size)) return true; +#endif + return false; +} +static hb_bool_t +hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; + if (ot_face->post->get_glyph_from_name (name, len, glyph)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_from_name (name, len, glyph)) return true; +#endif + return false; +} +#endif + +static hb_bool_t +hb_ot_get_font_h_extents (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); +} + +static hb_bool_t +hb_ot_get_font_v_extents (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_LINE_GAP, &metrics->line_gap); +} + +#if HB_USE_ATEXIT +static void free_static_ot_funcs (); +#endif + +static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t +{ + static hb_font_funcs_t *create () + { + hb_font_funcs_t *funcs = hb_font_funcs_create (); + + hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr); + hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ot_get_nominal_glyphs, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ot_get_glyph_h_advances, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advances_func (funcs, hb_ot_get_glyph_v_advances, nullptr, nullptr); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr); + hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, nullptr, nullptr); + //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, nullptr, nullptr); +#ifndef HB_NO_OT_FONT_GLYPH_NAMES + hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, nullptr, nullptr); + hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, nullptr, nullptr); +#endif + + hb_font_funcs_make_immutable (funcs); + +#if HB_USE_ATEXIT + atexit (free_static_ot_funcs); +#endif + + return funcs; + } +} static_ot_funcs; + +#if HB_USE_ATEXIT +static +void free_static_ot_funcs () +{ + static_ot_funcs.free_instance (); +} +#endif + +static hb_font_funcs_t * +_hb_ot_get_font_funcs () +{ + return static_ot_funcs.get_unconst (); +} + + +/** + * hb_ot_font_set_funcs: + * + * Since: 0.9.28 + **/ +void +hb_ot_font_set_funcs (hb_font_t *font) +{ + hb_font_set_funcs (font, + _hb_ot_get_font_funcs (), + &font->face->table, + nullptr); +} + +#ifndef HB_NO_VAR +int +_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +{ + return font->face->table.glyf->get_side_bearing_var (font, glyph, is_vertical); +} + +unsigned +_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +{ + return font->face->table.glyf->get_advance_var (font, glyph, is_vertical); +} +#endif + + +#endif diff --git a/thirdparty/harfbuzz/src/hb-ot-font.h b/thirdparty/harfbuzz/src/hb-ot-font.h new file mode 100644 index 00000000000..80eaa54b1ad --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-font.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_FONT_H +#define HB_OT_FONT_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +HB_EXTERN void +hb_ot_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_OT_FONT_H */ diff --git a/thirdparty/harfbuzz/src/hb-ot-gasp-table.hh b/thirdparty/harfbuzz/src/hb-ot-gasp-table.hh new file mode 100644 index 00000000000..4f291924afd --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-gasp-table.hh @@ -0,0 +1,84 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_GASP_TABLE_HH +#define HB_OT_GASP_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-var-hvar-table.hh" + +/* + * gasp -- Grid-fitting and Scan-conversion Procedure + * https://docs.microsoft.com/en-us/typography/opentype/spec/gasp + */ +#define HB_OT_TAG_gasp HB_TAG('g','a','s','p') + + +namespace OT { + +struct GaspRange +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 rangeMaxPPEM; /* Upper limit of range, in PPEM */ + HBUINT16 rangeGaspBehavior; + /* Flags describing desired rasterizer behavior. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct gasp +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gasp; + + const GaspRange &get_gasp_range (unsigned int i) const + { return gaspRanges[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + gaspRanges.sanitize (c)); + } + + protected: + HBUINT16 version; /* Version number (set to 1) */ + ArrayOf + gaspRanges; /* Number of records to follow + * Sorted by ppem */ + public: + DEFINE_SIZE_ARRAY (4, gaspRanges); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_GASP_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh b/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh new file mode 100644 index 00000000000..5470bd96da3 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh @@ -0,0 +1,1261 @@ +/* + * Copyright © 2015 Google, Inc. + * Copyright © 2019 Adobe Inc. + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Garret Rieger, Roderick Sheeter + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_GLYF_TABLE_HH +#define HB_OT_GLYF_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-var-gvar-table.hh" +#include "hb-draw.hh" + +namespace OT { + + +/* + * loca -- Index to Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/loca + */ +#define HB_OT_TAG_loca HB_TAG('l','o','c','a') + + +struct loca +{ + friend struct glyf; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_loca; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + return_trace (true); + } + + protected: + UnsizedArrayOf + dataZ; /* Location data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + + +/* + * glyf -- TrueType Glyph Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf + */ +#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') + + +struct glyf +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* Runtime checks as eager sanitizing each glyph is costy */ + return_trace (true); + } + + template + static bool + _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets) + { + unsigned max_offset = + + padded_offsets + | hb_reduce (hb_add, 0) + ; + unsigned num_offsets = padded_offsets.len () + 1; + bool use_short_loca = max_offset < 0x1FFFF; + unsigned entry_size = use_short_loca ? 2 : 4; + char *loca_prime_data = (char *) calloc (entry_size, num_offsets); + + if (unlikely (!loca_prime_data)) return false; + + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d " + "max_offset %d size %d", + entry_size, num_offsets, max_offset, entry_size * num_offsets); + + if (use_short_loca) + _write_loca (padded_offsets, 1, hb_array ((HBUINT16 *) loca_prime_data, num_offsets)); + else + _write_loca (padded_offsets, 0, hb_array ((HBUINT32 *) loca_prime_data, num_offsets)); + + hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, + entry_size * num_offsets, + HB_MEMORY_MODE_WRITABLE, + loca_prime_data, + free); + + bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) + && _add_head_and_set_loca_version (plan, use_short_loca); + + hb_blob_destroy (loca_blob); + return result; + } + + template + static void + _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest) + { + unsigned int offset = 0; + dest << 0; + + it + | hb_map ([=, &offset] (unsigned int padded_size) + { + offset += padded_size; + DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset); + return offset >> right_shift; + }) + | hb_sink (dest) + ; + } + + /* requires source of SubsetGlyph complains the identifier isn't declared */ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + unsigned init_len = c->length (); + for (const auto &_ : it) _.serialize (c, plan); + + /* As a special case when all glyph in the font are empty, add a zero byte + * to the table, so that OTS doesn’t reject it, and to make the table work + * on Windows as well. + * See https://github.com/khaledhosny/ots/issues/52 */ + if (init_len == c->length ()) + { + HBUINT8 empty_byte; + empty_byte = 0; + c->copy (empty_byte); + } + return_trace (true); + } + + /* Byte region(s) per glyph to output + unpadded, hints removed if so requested + If we fail to process a glyph we produce an empty (0-length) glyph */ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + glyf *glyf_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); + + hb_vector_t glyphs; + _populate_subset_glyphs (c->plan, &glyphs); + + glyf_prime->serialize (c->serializer, hb_iter (glyphs), c->plan); + + auto padded_offsets = + + hb_iter (glyphs) + | hb_map (&SubsetGlyph::padded_size) + ; + + if (c->serializer->in_error ()) return_trace (false); + return_trace (c->serializer->check_success (_add_loca_and_head (c->plan, + padded_offsets))); + } + + template + void + _populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_vector_t *glyphs /* OUT */) const + { + OT::glyf::accelerator_t glyf; + glyf.init (plan->source); + + + hb_range (plan->num_output_glyphs ()) + | hb_map ([&] (hb_codepoint_t new_gid) + { + SubsetGlyph subset_glyph = {0}; + subset_glyph.new_gid = new_gid; + + /* should never fail: all old gids should be mapped */ + if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) + return subset_glyph; + + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); + if (plan->drop_hints) subset_glyph.drop_hints_bytes (); + else subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + + return subset_glyph; + }) + | hb_sink (glyphs) + ; + + glyf.fini (); + } + + static bool + _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) + { + hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (plan->source); + hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob); + hb_blob_destroy (head_blob); + + if (unlikely (!head_prime_blob)) + return false; + + head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); + head_prime->indexToLocFormat = use_short_loca ? 0 : 1; + bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + return success; + } + + struct CompositeGlyphChain + { + protected: + enum composite_glyph_flag_t + { + ARG_1_AND_2_ARE_WORDS = 0x0001, + ARGS_ARE_XY_VALUES = 0x0002, + ROUND_XY_TO_GRID = 0x0004, + WE_HAVE_A_SCALE = 0x0008, + MORE_COMPONENTS = 0x0020, + WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, + WE_HAVE_A_TWO_BY_TWO = 0x0080, + WE_HAVE_INSTRUCTIONS = 0x0100, + USE_MY_METRICS = 0x0200, + OVERLAP_COMPOUND = 0x0400, + SCALED_COMPONENT_OFFSET = 0x0800, + UNSCALED_COMPONENT_OFFSET = 0x1000 + }; + + public: + unsigned int get_size () const + { + unsigned int size = min_size; + /* arg1 and 2 are int16 */ + if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; + /* arg1 and 2 are int8 */ + else size += 2; + + /* One x 16 bit (scale) */ + if (flags & WE_HAVE_A_SCALE) size += 2; + /* Two x 16 bit (xscale, yscale) */ + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; + /* Four x 16 bit (xscale, scale01, scale10, yscale) */ + else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; + + return size; + } + + void set_glyph_index (hb_codepoint_t new_gid) { glyphIndex = new_gid; } + hb_codepoint_t get_glyph_index () const { return glyphIndex; } + + void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } + + bool has_more () const { return flags & MORE_COMPONENTS; } + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const + { + const HBUINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *) p)[0]; + point2 = ((const HBUINT16 *) p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } + + void transform_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + if (get_transformation (matrix, trans)) + { + if (scaled_offsets ()) + { + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); + } + } + } + + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } + + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const + { + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + + int tx, ty; + const HBINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + tx = *(const HBINT16 *) p; + p += HBINT16::static_size; + ty = *(const HBINT16 *) p; + p += HBINT16::static_size; + } + else + { + tx = *p++; + ty = *p++; + } + if (is_anchored ()) tx = ty = 0; + + trans.init ((float) tx, (float) ty); + + { + const F2DOT14 *points = (const F2DOT14 *) p; + if (flags & WE_HAVE_A_SCALE) + { + matrix[0] = matrix[3] = points[0].to_float (); + return true; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + matrix[0] = points[0].to_float (); + matrix[3] = points[1].to_float (); + return true; + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + matrix[0] = points[0].to_float (); + matrix[1] = points[1].to_float (); + matrix[2] = points[2].to_float (); + matrix[3] = points[3].to_float (); + return true; + } + } + return tx || ty; + } + + protected: + HBUINT16 flags; + HBGlyphID glyphIndex; + public: + DEFINE_SIZE_MIN (4); + }; + + struct composite_iter_t : hb_iter_with_fallback_t + { + typedef const CompositeGlyphChain *__item_t__; + composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (current_) + { if (!check_range (current)) current = nullptr; } + composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr) {} + + const CompositeGlyphChain &__item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () + { + if (!current->has_more ()) { current = nullptr; return; } + + const CompositeGlyphChain *possible = &StructAfter (*current); + if (!check_range (possible)) { current = nullptr; return; } + current = possible; + } + bool operator != (const composite_iter_t& o) const + { return glyph != o.glyph || current != o.current; } + + bool check_range (const CompositeGlyphChain *composite) const + { + return glyph.check_range (composite, CompositeGlyphChain::min_size) + && glyph.check_range (composite, composite->get_size ()); + } + + private: + hb_bytes_t glyph; + __item_t__ current; + }; + + enum phantom_point_index_t + { + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 + }; + + struct accelerator_t; + + struct Glyph + { + enum simple_glyph_flag_t + { + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, + FLAG_RESERVED1 = 0x40, + FLAG_RESERVED2 = 0x80 + }; + + private: + struct GlyphHeader + { + bool has_data () const { return numberOfContours; } + + bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ + /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ + extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid)); + extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); + extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); + extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); + + return true; + } + + HBINT16 numberOfContours; + /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + public: + DEFINE_SIZE_STATIC (10); + }; + + struct SimpleGlyph + { + const GlyphHeader &header; + hb_bytes_t bytes; + SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + unsigned int instruction_len_offset () const + { return GlyphHeader::static_size + 2 * header.numberOfContours; } + + unsigned int length (unsigned int instruction_len) const + { return instruction_len_offset () + 2 + instruction_len; } + + unsigned int instructions_length () const + { + unsigned int instruction_length_offset = instruction_len_offset (); + if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; + + const HBUINT16 &instructionLength = StructAtOffset (&bytes, instruction_length_offset); + /* Out of bounds of the current glyph */ + if (unlikely (length (instructionLength) > bytes.length)) return 0; + return instructionLength; + } + + const Glyph trim_padding () const + { + /* based on FontTools _g_l_y_f.py::trim */ + const char *glyph = bytes.arrayZ; + const char *glyph_end = glyph + bytes.length; + /* simple glyph w/contours, possibly trimmable */ + glyph += instruction_len_offset (); + + if (unlikely (glyph + 2 >= glyph_end)) return Glyph (); + unsigned int num_coordinates = StructAtOffset (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset (glyph, 0); + + glyph += 2 + num_instructions; + + unsigned int coord_bytes = 0; + unsigned int coords_with_flags = 0; + while (glyph < glyph_end) + { + uint8_t flag = *glyph; + glyph++; + + unsigned int repeat = 1; + if (flag & FLAG_REPEAT) + { + if (unlikely (glyph >= glyph_end)) return Glyph (); + repeat = *glyph + 1; + glyph++; + } + + unsigned int xBytes, yBytes; + xBytes = yBytes = 0; + if (flag & FLAG_X_SHORT) xBytes = 1; + else if ((flag & FLAG_X_SAME) == 0) xBytes = 2; + + if (flag & FLAG_Y_SHORT) yBytes = 1; + else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; + + coord_bytes += (xBytes + yBytes) * repeat; + coords_with_flags += repeat; + if (coords_with_flags >= num_coordinates) break; + } + + if (unlikely (coords_with_flags != num_coordinates)) return Glyph (); + return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph))); + } + + /* zero instruction length */ + void drop_hints () + { + GlyphHeader &glyph_header = const_cast (header); + (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0; + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + unsigned int instructions_len = instructions_length (); + unsigned int glyph_length = length (instructions_len); + dest_start = bytes.sub_array (0, glyph_length - instructions_len); + dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); + } + + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const hb_bytes_t &bytes, + void (* setter) (contour_point_t &_, float v), + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag) + { + float v = 0; + for (unsigned i = 0; i < points_.length; i++) + { + uint8_t flag = points_[i].flag; + if (flag & short_flag) + { + if (unlikely (!bytes.check_range (p))) return false; + if (flag & same_flag) + v += *p++; + else + v -= *p++; + } + else + { + if (!(flag & same_flag)) + { + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false; + v += *(const HBINT16 *) p; + p += HBINT16::static_size; + } + } + setter (points_[i], v); + } + return true; + } + + bool get_contour_points (contour_point_vector_t &points_ /* OUT */, + bool phantom_only = false) const + { + const HBUINT16 *endPtsOfContours = &StructAfter (header); + int num_contours = header.numberOfContours; + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false; + unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; + + points_.resize (num_points); + for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); + if (phantom_only) return true; + + for (int i = 0; i < num_contours; i++) + points_[endPtsOfContours[i]].is_end_point = true; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], + endPtsOfContours[num_contours]); + + /* Read flags */ + for (unsigned int i = 0; i < num_points; i++) + { + if (unlikely (!bytes.check_range (p))) return false; + uint8_t flag = *p++; + points_[i].flag = flag; + if (flag & FLAG_REPEAT) + { + if (unlikely (!bytes.check_range (p))) return false; + unsigned int repeat_count = *p++; + while ((repeat_count-- > 0) && (++i < num_points)) + points_[i].flag = flag; + } + } + + /* Read x & y coordinates */ + return read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.x = v; }, + FLAG_X_SHORT, FLAG_X_SAME) + && read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.y = v; }, + FLAG_Y_SHORT, FLAG_Y_SAME); + } + }; + + struct CompositeGlyph + { + const GlyphHeader &header; + hb_bytes_t bytes; + CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + composite_iter_t get_iterator () const + { return composite_iter_t (bytes, &StructAfter (header)); } + + unsigned int instructions_length (hb_bytes_t bytes) const + { + unsigned int start = bytes.length; + unsigned int end = bytes.length; + const CompositeGlyphChain *last = nullptr; + for (auto &item : get_iterator ()) + last = &item; + if (unlikely (!last)) return 0; + + if (last->has_instructions ()) + start = (char *) last - &bytes + last->get_size (); + if (unlikely (start > end)) return 0; + return end - start; + } + + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + const Glyph trim_padding () const { return Glyph (bytes); } + + void drop_hints () + { + for (const auto &_ : get_iterator ()) + const_cast (_).drop_instructions_flag (); + } + + /* Chop instructions off the end */ + void drop_hints_bytes (hb_bytes_t &dest_start) const + { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } + }; + + enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; + + public: + composite_iter_t get_composite_iterator () const + { + if (type != COMPOSITE) return composite_iter_t (); + return CompositeGlyph (*header, bytes).get_iterator (); + } + + const Glyph trim_padding () const + { + switch (type) { + case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); + case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); + default: return bytes; + } + } + + void drop_hints () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; + default: return; + } + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; + default: return; + } + } + + /* Note: Recursively calls itself. + * all_points includes phantom points + */ + bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, + contour_point_vector_t &all_points /* OUT */, + bool phantom_only = false, + unsigned int depth = 0) const + { + if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + contour_point_vector_t points; + + switch (type) { + case COMPOSITE: + { + /* pseudo component points for each component in composite glyph */ + unsigned num_points = hb_len (CompositeGlyph (*header, bytes).get_iterator ()); + if (unlikely (!points.resize (num_points))) return false; + for (unsigned i = 0; i < points.length; i++) + points[i].init (); + break; + } + case SIMPLE: + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + { + for (unsigned i = 0; i < PHANTOM_COUNT; ++i) phantoms[i].init (); + int h_delta = (int) header->xMin - glyf_accelerator.hmtx->get_side_bearing (gid); + int v_orig = (int) header->yMax + glyf_accelerator.vmtx->get_side_bearing (gid); + unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid); + unsigned v_adv = glyf_accelerator.vmtx->get_advance (gid); + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + +#ifndef HB_NO_VAR + if (unlikely (!glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()))) + return false; +#endif + + switch (type) { + case SIMPLE: + all_points.extend (points.as_array ()); + break; + case COMPOSITE: + { + unsigned int comp_index = 0; + for (auto &item : get_composite_iterator ()) + { + contour_point_vector_t comp_points; + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_glyph_index ()) + .get_points (font, glyf_accelerator, comp_points, + phantom_only, depth + 1) + || comp_points.length < PHANTOM_COUNT)) + return false; + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + /* Apply component transformation & translation */ + item.transform_points (comp_points); + + /* Apply translation from gvar */ + comp_points.translate (points[comp_index]); + + if (item.is_anchored ()) + { + unsigned int p1, p2; + item.get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + + all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); + + comp_index++; + } + + all_points.extend (phantoms); + } break; + default: + all_points.extend (phantoms); + } + + if (depth == 0) /* Apply at top level */ + { + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); + if (delta.x) all_points.translate (delta); + } + + return true; + } + + bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const + { + if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + return header->get_extents (font, glyf_accelerator, gid, extents); + } + + hb_bytes_t get_bytes () const { return bytes; } + + Glyph (hb_bytes_t bytes_ = hb_bytes_t (), + hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_), + header (bytes.as ()) + { + int num_contours = header->numberOfContours; + if (unlikely (num_contours == 0)) type = EMPTY; + else if (num_contours > 0) type = SIMPLE; + else type = COMPOSITE; /* negative numbers */ + } + + protected: + hb_bytes_t bytes; + hb_codepoint_t gid; + const GlyphHeader *header; + unsigned type; + }; + + struct accelerator_t + { + void init (hb_face_t *face_) + { + short_offset = false; + num_glyphs = 0; + loca_table = nullptr; + glyf_table = nullptr; +#ifndef HB_NO_VAR + gvar = nullptr; +#endif + hmtx = nullptr; + vmtx = nullptr; + face = face_; + const OT::head &head = *face->table.head; + if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0) + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + return; + short_offset = 0 == head.indexToLocFormat; + + loca_table = hb_sanitize_context_t ().reference_table (face); + glyf_table = hb_sanitize_context_t ().reference_table (face); +#ifndef HB_NO_VAR + gvar = face->table.gvar; +#endif + hmtx = face->table.hmtx; + vmtx = face->table.vmtx; + + num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); + } + + void fini () + { + loca_table.destroy (); + glyf_table.destroy (); + } + + protected: + template + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const + { + if (gid >= num_glyphs) return false; + + /* Making this alloc free is not that easy + https://github.com/harfbuzz/harfbuzz/issues/2095 + mostly because of gvar handling in VF fonts, + perhaps a separate path for non-VF fonts can be considered */ + contour_point_vector_t all_points; + + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only))) + return false; + + if (consumer.is_consuming_contour_points ()) + { + for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index) + consumer.consume_point (all_points[point_index]); + consumer.points_end (); + } + + /* Where to write phantoms, nullptr if not requested */ + contour_point_t *phantoms = consumer.get_phantoms_sink (); + if (phantoms) + for (unsigned i = 0; i < PHANTOM_COUNT; ++i) + phantoms[i] = all_points[all_points.length - PHANTOM_COUNT + i]; + + return true; + } + +#ifndef HB_NO_VAR + struct points_aggregator_t + { + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; + + struct contour_bounds_t + { + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + + void add (const contour_point_t &p) + { + min_x = hb_min (min_x, p.x); + min_y = hb_min (min_y, p.y); + max_x = hb_max (max_x, p.x); + max_y = hb_max (max_y, p.y); + } + + bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) + { + if (unlikely (empty ())) + { + extents->width = 0; + extents->x_bearing = 0; + extents->height = 0; + extents->y_bearing = 0; + return; + } + extents->x_bearing = font->em_scalef_x (min_x); + extents->width = font->em_scalef_x (max_x - min_x); + extents->y_bearing = font->em_scalef_y (max_y); + extents->height = font->em_scalef_y (min_y - max_y); + } + + protected: + float min_x, min_y, max_x, max_y; + } bounds; + + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) + { + font = font_; + extents = extents_; + phantoms = phantoms_; + if (extents) bounds = contour_bounds_t (); + } + + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents); } + + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; + + public: + unsigned + get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; + + contour_point_t phantoms[PHANTOM_COUNT]; + if (likely (font->num_coords == gvar->get_axis_count ())) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); + + if (unlikely (!success)) + return is_vertical ? vmtx->get_advance (gid) : hmtx->get_advance (gid); + + float result = is_vertical + ? phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y + : phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x; + return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); + } + + int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + hb_glyph_extents_t extents; + + contour_point_t phantoms[PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) + return is_vertical ? vmtx->get_side_bearing (gid) : hmtx->get_side_bearing (gid); + + return is_vertical + ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing + : floorf (phantoms[PHANTOM_LEFT].x); + } +#endif + + public: + bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + if (unlikely (gid >= num_glyphs)) return false; + +#ifndef HB_NO_VAR + if (font->num_coords && font->num_coords == gvar->get_axis_count ()) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); +#endif + return glyph_for_gid (gid).get_extents (font, *this, extents); + } + + const Glyph + glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const + { + if (unlikely (gid >= num_glyphs)) return Glyph (); + + unsigned int start_offset, end_offset; + + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; + start_offset = 2 * offsets[gid]; + end_offset = 2 * offsets[gid + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + start_offset = offsets[gid]; + end_offset = offsets[gid + 1]; + } + + if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) + return Glyph (); + + Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, + end_offset - start_offset), gid); + return needs_padding_removal ? glyph.trim_padding () : glyph; + } + + void + add_gid_and_children (hb_codepoint_t gid, hb_set_t *gids_to_retain, + unsigned int depth = 0) const + { + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return; + /* Check if is already visited */ + if (gids_to_retain->has (gid)) return; + + gids_to_retain->add (gid); + + for (auto &item : glyph_for_gid (gid).get_composite_iterator ()) + add_gid_and_children (item.get_glyph_index (), gids_to_retain, depth); + } + +#ifdef HB_EXPERIMENTAL_API + struct path_builder_t + { + hb_font_t *font; + draw_helper_t *draw_helper; + + struct optional_point_t + { + optional_point_t () { has_data = false; } + optional_point_t (float x_, float y_) { x = x_; y = y_; has_data = true; } + + bool has_data; + float x; + float y; + + optional_point_t lerp (optional_point_t p, float t) + { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } + } first_oncurve, first_offcurve, last_offcurve; + + path_builder_t (hb_font_t *font_, draw_helper_t &draw_helper_) + { + font = font_; + draw_helper = &draw_helper_; + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + } + + /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 + See also: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html + * https://stackoverflow.com/a/20772557 */ + void consume_point (const contour_point_t &point) + { + /* Skip empty contours */ + if (unlikely (point.is_end_point && !first_oncurve.has_data && !first_offcurve.has_data)) + return; + + bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE; + optional_point_t p (point.x, point.y); + if (!first_oncurve.has_data) + { + if (is_on_curve) + { + first_oncurve = p; + draw_helper->move_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + } + else + { + if (first_offcurve.has_data) + { + optional_point_t mid = first_offcurve.lerp (p, .5f); + first_oncurve = mid; + last_offcurve = p; + draw_helper->move_to (font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve.has_data) + { + if (is_on_curve) + { + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + last_offcurve = optional_point_t (); + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + last_offcurve = p; + } + } + else + { + if (is_on_curve) + draw_helper->line_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + else + last_offcurve = p; + } + } + + if (point.is_end_point) + { + if (first_offcurve.has_data && last_offcurve.has_data) + { + optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f); + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + last_offcurve = optional_point_t (); + /* now check the rest */ + } + + if (first_offcurve.has_data && first_oncurve.has_data) + draw_helper->quadratic_to (font->em_scalef_x (first_offcurve.x), font->em_scalef_y (first_offcurve.y), + font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + else if (last_offcurve.has_data && first_oncurve.has_data) + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + else if (first_oncurve.has_data) + draw_helper->line_to (font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + draw_helper->end_path (); + } + } + void points_end () {} + + bool is_consuming_contour_points () { return true; } + contour_point_t *get_phantoms_sink () { return nullptr; } + }; + + bool + get_path (hb_font_t *font, hb_codepoint_t gid, draw_helper_t &draw_helper) const + { return get_points (font, gid, path_builder_t (font, draw_helper)); } +#endif + +#ifndef HB_NO_VAR + const gvar_accelerator_t *gvar; +#endif + const hmtx_accelerator_t *hmtx; + const vmtx_accelerator_t *vmtx; + + private: + bool short_offset; + unsigned int num_glyphs; + hb_blob_ptr_t loca_table; + hb_blob_ptr_t glyf_table; + hb_face_t *face; + }; + + struct SubsetGlyph + { + hb_codepoint_t new_gid; + hb_codepoint_t old_gid; + Glyph source_glyph; + hb_bytes_t dest_start; /* region of source_glyph to copy first */ + hb_bytes_t dest_end; /* region of source_glyph to copy second */ + + bool serialize (hb_serialize_context_t *c, + const hb_subset_plan_t *plan) const + { + TRACE_SERIALIZE (this); + + hb_bytes_t dest_glyph = dest_start.copy (c); + dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); + unsigned int pad_length = padding (); + DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + + HBUINT8 pad; + pad = 0; + while (pad_length > 0) + { + c->embed (pad); + pad_length--; + } + + if (unlikely (!dest_glyph.length)) return_trace (true); + + /* update components gids */ + for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_glyph_index (), &new_gid)) + const_cast (_).set_glyph_index (new_gid); + } + + if (plan->drop_hints) Glyph (dest_glyph).drop_hints (); + + return_trace (true); + } + + void drop_hints_bytes () + { source_glyph.drop_hints_bytes (dest_start, dest_end); } + + unsigned int length () const { return dest_start.length + dest_end.length; } + /* pad to 2 to ensure 2-byte loca will be ok */ + unsigned int padding () const { return length () % 2; } + unsigned int padded_size () const { return length () + padding (); } + }; + + protected: + UnsizedArrayOf + dataZ; /* Glyphs data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + +struct glyf_accelerator_t : glyf::accelerator_t {}; + +} /* namespace OT */ + + +#endif /* HB_OT_GLYF_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh b/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh new file mode 100644 index 00000000000..c9c391bad5f --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh @@ -0,0 +1,177 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef HB_OT_HDMX_TABLE_HH +#define HB_OT_HDMX_TABLE_HH + +#include "hb-open-type.hh" + +/* + * hdmx -- Horizontal Device Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hdmx + */ +#define HB_OT_TAG_hdmx HB_TAG('h','d','m','x') + + +namespace OT { + + +struct DeviceRecord +{ + static unsigned int get_size (unsigned count) + { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); } + + template + bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it) + { + TRACE_SERIALIZE (this); + + unsigned length = it.len (); + + if (unlikely (!c->extend (*this, length))) return_trace (false); + + this->pixelSize = pixelSize; + this->maxWidth = + + it + | hb_reduce (hb_max, 0u); + + + it + | hb_sink (widthsZ.as_array (length)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + c->check_range (this, sizeDeviceRecord))); + } + + HBUINT8 pixelSize; /* Pixel size for following widths (as ppem). */ + HBUINT8 maxWidth; /* Maximum width. */ + UnsizedArrayOf widthsZ; /* Array of widths (numGlyphs is from the 'maxp' table). */ + public: + DEFINE_SIZE_ARRAY (2, widthsZ); +}; + + +struct hdmx +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_hdmx; + + unsigned int get_size () const + { return min_size + numRecords * sizeDeviceRecord; } + + const DeviceRecord& operator [] (unsigned int i) const + { + /* XXX Null(DeviceRecord) is NOT safe as it's num-glyphs lengthed. + * https://github.com/harfbuzz/harfbuzz/issues/1300 */ + if (unlikely (i >= numRecords)) return Null (DeviceRecord); + return StructAtOffset (&this->firstDeviceRecord, i * sizeDeviceRecord); + } + + template + bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->version = version; + this->numRecords = it.len (); + this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0); + + for (const hb_item_type& _ : +it) + c->start_embed ()->serialize (c, _.first, _.second); + + return_trace (c->successful); + } + + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + hdmx *hdmx_prime = c->serializer->start_embed (); + if (unlikely (!hdmx_prime)) return_trace (false); + + auto it = + + hb_range ((unsigned) numRecords) + | hb_map ([c, this] (unsigned _) + { + const DeviceRecord *device_record = + &StructAtOffset (&firstDeviceRecord, + _ * sizeDeviceRecord); + auto row = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (c->plan->reverse_glyph_map) + | hb_map ([this, c, device_record] (hb_codepoint_t _) + { + if (c->plan->is_empty_glyph (_)) + return Null (HBUINT8); + return device_record->widthsZ.as_array (get_num_glyphs ()) [_]; + }) + ; + return hb_pair ((unsigned) device_record->pixelSize, +row); + }) + ; + + hdmx_prime->serialize (c->serializer, version, it); + return_trace (true); + } + + unsigned get_num_glyphs () const + { + return sizeDeviceRecord - DeviceRecord::min_size; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) && + sizeDeviceRecord >= DeviceRecord::min_size && + c->check_range (this, get_size ())); + } + + protected: + HBUINT16 version; /* Table version number (0) */ + HBUINT16 numRecords; /* Number of device records. */ + HBUINT32 sizeDeviceRecord; + /* Size of a device record, 32-bit aligned. */ + DeviceRecord firstDeviceRecord; + /* Array of device records. */ + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HDMX_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-head-table.hh b/thirdparty/harfbuzz/src/hb-ot-head-table.hh new file mode 100644 index 00000000000..5613a96dbfd --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-head-table.hh @@ -0,0 +1,179 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HEAD_TABLE_HH +#define HB_OT_HEAD_TABLE_HH + +#include "hb-open-type.hh" + +/* + * head -- Font Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/head + */ +#define HB_OT_TAG_head HB_TAG('h','e','a','d') + + +namespace OT { + + +struct head +{ + friend struct OffsetTable; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_head; + + unsigned int get_upem () const + { + unsigned int upem = unitsPerEm; + /* If no valid head table found, assume 1000, which matches typical Type1 usage. */ + return 16 <= upem && upem <= 16384 ? upem : 1000; + } + + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace ((bool) c->embed (this)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace (serialize (c->serializer)); + } + + enum mac_style_flag_t { + BOLD = 1u<<0, + ITALIC = 1u<<1, + UNDERLINE = 1u<<2, + OUTLINE = 1u<<3, + SHADOW = 1u<<4, + CONDENSED = 1u<<5 + }; + + bool is_bold () const { return macStyle & BOLD; } + bool is_italic () const { return macStyle & ITALIC; } + bool is_condensed () const { return macStyle & CONDENSED; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + version.major == 1 && + magicNumber == 0x5F0F3CF5u); + } + + protected: + FixedVersion<>version; /* Version of the head table--currently + * 0x00010000u for version 1.0. */ + FixedVersion<>fontRevision; /* Set by font manufacturer. */ + HBUINT32 checkSumAdjustment; /* To compute: set it to 0, sum the + * entire font as HBUINT32, then store + * 0xB1B0AFBAu - sum. */ + HBUINT32 magicNumber; /* Set to 0x5F0F3CF5u. */ + HBUINT16 flags; /* Bit 0: Baseline for font at y=0; + * Bit 1: Left sidebearing point at x=0; + * Bit 2: Instructions may depend on point size; + * Bit 3: Force ppem to integer values for all + * internal scaler math; may use fractional + * ppem sizes if this bit is clear; + * Bit 4: Instructions may alter advance width + * (the advance widths might not scale linearly); + * Bits 5-10: These should be set according to + * Apple's specification. However, they are not + * implemented in OpenType. + * Bit 5: This bit should be set in fonts that are + * intended to e laid out vertically, and in + * which the glyphs have been drawn such that an + * x-coordinate of 0 corresponds to the desired + * vertical baseline. + * Bit 6: This bit must be set to zero. + * Bit 7: This bit should be set if the font + * requires layout for correct linguistic + * rendering (e.g. Arabic fonts). + * Bit 8: This bit should be set for a GX font + * which has one or more metamorphosis effects + * designated as happening by default. + * Bit 9: This bit should be set if the font + * contains any strong right-to-left glyphs. + * Bit 10: This bit should be set if the font + * contains Indic-style rearrangement effects. + * Bit 11: Font data is 'lossless,' as a result + * of having been compressed and decompressed + * with the Agfa MicroType Express engine. + * Bit 12: Font converted (produce compatible metrics) + * Bit 13: Font optimized for ClearType™. + * Note, fonts that rely on embedded bitmaps (EBDT) + * for rendering should not be considered optimized + * for ClearType, and therefore should keep this bit + * cleared. + * Bit 14: Last Resort font. If set, indicates that + * the glyphs encoded in the cmap subtables are simply + * generic symbolic representations of code point + * ranges and don’t truly represent support for those + * code points. If unset, indicates that the glyphs + * encoded in the cmap subtables represent proper + * support for those code points. + * Bit 15: Reserved, set to 0. */ + HBUINT16 unitsPerEm; /* Valid range is from 16 to 16384. This value + * should be a power of 2 for fonts that have + * TrueType outlines. */ + LONGDATETIME created; /* Number of seconds since 12:00 midnight, + January 1, 1904. 64-bit integer */ + LONGDATETIME modified; /* Number of seconds since 12:00 midnight, + January 1, 1904. 64-bit integer */ + HBINT16 xMin; /* For all glyph bounding boxes. */ + HBINT16 yMin; /* For all glyph bounding boxes. */ + HBINT16 xMax; /* For all glyph bounding boxes. */ + HBINT16 yMax; /* For all glyph bounding boxes. */ + HBUINT16 macStyle; /* Bit 0: Bold (if set to 1); + * Bit 1: Italic (if set to 1) + * Bit 2: Underline (if set to 1) + * Bit 3: Outline (if set to 1) + * Bit 4: Shadow (if set to 1) + * Bit 5: Condensed (if set to 1) + * Bit 6: Extended (if set to 1) + * Bits 7-15: Reserved (set to 0). */ + HBUINT16 lowestRecPPEM; /* Smallest readable size in pixels. */ + HBINT16 fontDirectionHint; /* Deprecated (Set to 2). + * 0: Fully mixed directional glyphs; + * 1: Only strongly left to right; + * 2: Like 1 but also contains neutrals; + * -1: Only strongly right to left; + * -2: Like -1 but also contains neutrals. */ + public: + HBUINT16 indexToLocFormat; /* 0 for short offsets, 1 for long. */ + HBUINT16 glyphDataFormat; /* 0 for current format. */ + + DEFINE_SIZE_STATIC (54); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HEAD_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-hhea-table.hh b/thirdparty/harfbuzz/src/hb-ot-hhea-table.hh new file mode 100644 index 00000000000..d9c9bd35377 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-hhea-table.hh @@ -0,0 +1,104 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HHEA_TABLE_HH +#define HB_OT_HHEA_TABLE_HH + +#include "hb-open-type.hh" + +/* + * hhea -- Horizontal Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/hhea + * vhea -- Vertical Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/vhea + */ +#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') +#define HB_OT_TAG_vhea HB_TAG('v','h','e','a') + + +namespace OT { + + +template +struct _hea +{ + bool has_data () const { return version.major; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && likely (version.major == 1)); + } + + public: + FixedVersion<>version; /* 0x00010000u for version 1.0. */ + FWORD ascender; /* Typographic ascent. */ + FWORD descender; /* Typographic descent. */ + FWORD lineGap; /* Typographic line gap. */ + UFWORD advanceMax; /* Maximum advance width/height value in + * metrics table. */ + FWORD minLeadingBearing; + /* Minimum left/top sidebearing value in + * metrics table. */ + FWORD minTrailingBearing; + /* Minimum right/bottom sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)) for horizontal. */ + FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), + * vertical: minLeadingBearing+(yMax-yMin). */ + HBINT16 caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical caret, + * 0 for horizontal.*/ + HBINT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ + HBINT16 caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non-slanted fonts. */ + HBINT16 reserved1; /* Set to 0. */ + HBINT16 reserved2; /* Set to 0. */ + HBINT16 reserved3; /* Set to 0. */ + HBINT16 reserved4; /* Set to 0. */ + HBINT16 metricDataFormat;/* 0 for current format. */ + HBUINT16 numberOfLongMetrics; + /* Number of LongMetric entries in metric + * table. */ + public: + DEFINE_SIZE_STATIC (36); +}; + +struct hhea : _hea { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hhea; +}; +struct vhea : _hea { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vhea; +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HHEA_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh new file mode 100644 index 00000000000..d06c0fa4a46 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh @@ -0,0 +1,340 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Roderick Sheeter + */ + +#ifndef HB_OT_HMTX_TABLE_HH +#define HB_OT_HMTX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-var-hvar-table.hh" +#include "hb-ot-metrics.hh" + +/* + * hmtx -- Horizontal Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx + * vmtx -- Vertical Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/vmtx + */ +#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') +#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') + + +HB_INTERNAL int +_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + +HB_INTERNAL unsigned +_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + + +namespace OT { + + +struct LongMetric +{ + UFWORD advance; /* Advance width/height. */ + FWORD sb; /* Leading (left/top) side bearing. */ + public: + DEFINE_SIZE_STATIC (4); +}; + + +template +struct hmtxvmtx +{ + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* We don't check for anything specific here. The users of the + * struct do all the hard work... */ + return_trace (true); + } + + + bool subset_update_header (hb_subset_plan_t *plan, + unsigned int num_hmetrics) const + { + hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (plan->source, H::tableTag); + hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob); + hb_blob_destroy (src_blob); + + if (unlikely (!dest_blob)) { + return false; + } + + unsigned int length; + H *table = (H *) hb_blob_get_data (dest_blob, &length); + table->numberOfLongMetrics = num_hmetrics; + + bool result = plan->add_table (H::tableTag, dest_blob); + hb_blob_destroy (dest_blob); + + return result; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned num_advances) + { + unsigned idx = 0; + for (auto _ : it) + { + if (idx < num_advances) + { + LongMetric lm; + lm.advance = _.first; + lm.sb = _.second; + if (unlikely (!c->embed (&lm))) return; + } + else + { + FWORD *sb = c->allocate_size (FWORD::static_size); + if (unlikely (!sb)) return; + *sb = _.second; + } + idx++; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + T *table_prime = c->serializer->start_embed (); + if (unlikely (!table_prime)) return_trace (false); + + accelerator_t _mtx; + _mtx.init (c->plan->source); + unsigned num_advances = _mtx.num_advances_for_subset (c->plan); + + auto it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map ([c, &_mtx] (unsigned _) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (_, &old_gid)) + return hb_pair (0u, 0); + return hb_pair (_mtx.get_advance (old_gid), _mtx.get_side_bearing (old_gid)); + }) + ; + + table_prime->serialize (c->serializer, it, num_advances); + + _mtx.fini (); + + if (unlikely (c->serializer->ran_out_of_room || c->serializer->in_error ())) + return_trace (false); + + // Amend header num hmetrics + if (unlikely (!subset_update_header (c->plan, num_advances))) + return_trace (false); + + return_trace (true); + } + + struct accelerator_t + { + friend struct hmtxvmtx; + + void init (hb_face_t *face, + unsigned int default_advance_ = 0) + { + default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face); + + num_advances = T::is_horizontal ? face->table.hhea->numberOfLongMetrics : face->table.vhea->numberOfLongMetrics; + + table = hb_sanitize_context_t ().reference_table (face, T::tableTag); + + /* Cap num_metrics() and num_advances() based on table length. */ + unsigned int len = table.get_length (); + if (unlikely (num_advances * 4 > len)) + num_advances = len / 4; + num_metrics = num_advances + (len - 4 * num_advances) / 2; + + /* We MUST set num_metrics to zero if num_advances is zero. + * Our get_advance() depends on that. */ + if (unlikely (!num_advances)) + { + num_metrics = num_advances = 0; + table.destroy (); + table = hb_blob_get_empty (); + } + + var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag); + } + + void fini () + { + table.destroy (); + var_table.destroy (); + } + + int get_side_bearing (hb_codepoint_t glyph) const + { + if (glyph < num_advances) + return table->longMetricZ[glyph].sb; + + if (unlikely (glyph >= num_metrics)) + return 0; + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_advances]; + return bearings[glyph - num_advances]; + } + + int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const + { + int side_bearing = get_side_bearing (glyph); + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_metrics) || !font->num_coords) + return side_bearing; + + if (var_table.get_length ()) + return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?! + + return _glyf_get_side_bearing_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return side_bearing; +#endif + } + + unsigned int get_advance (hb_codepoint_t glyph) const + { + if (unlikely (glyph >= num_metrics)) + { + /* If num_metrics is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, it means that the + * glyph index is out of bound: return zero. */ + if (num_metrics) + return 0; + else + return default_advance; + } + + return table->longMetricZ[hb_min (glyph, (uint32_t) num_advances - 1)].advance; + } + + unsigned int get_advance (hb_codepoint_t glyph, + hb_font_t *font) const + { + unsigned int advance = get_advance (glyph); + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_metrics) || !font->num_coords) + return advance; + + if (var_table.get_length ()) + return advance + roundf (var_table->get_advance_var (glyph, font)); // TODO Optimize?! + + return _glyf_get_advance_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return advance; +#endif + } + + unsigned int num_advances_for_subset (const hb_subset_plan_t *plan) const + { + unsigned int num_advances = plan->num_output_glyphs (); + unsigned int last_advance = _advance_for_new_gid (plan, + num_advances - 1); + while (num_advances > 1 && + last_advance == _advance_for_new_gid (plan, + num_advances - 2)) + { + num_advances--; + } + + return num_advances; + } + + private: + unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan, + hb_codepoint_t new_gid) const + { + hb_codepoint_t old_gid; + if (!plan->old_gid_for_new_gid (new_gid, &old_gid)) + return 0; + + return get_advance (old_gid); + } + + protected: + unsigned int num_metrics; + unsigned int num_advances; + unsigned int default_advance; + + private: + hb_blob_ptr_t table; + hb_blob_ptr_t var_table; + }; + + protected: + UnsizedArrayOf + longMetricZ; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ +/*UnsizedArrayOf leadingBearingX;*/ + /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ + public: + DEFINE_SIZE_ARRAY (0, longMetricZ); +}; + +struct hmtx : hmtxvmtx { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR; + static constexpr bool is_horizontal = true; +}; +struct vmtx : hmtxvmtx { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR; + static constexpr bool is_horizontal = false; +}; + +struct hmtx_accelerator_t : hmtx::accelerator_t {}; +struct vmtx_accelerator_t : vmtx::accelerator_t {}; + +} /* namespace OT */ + + +#endif /* HB_OT_HMTX_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-kern-table.hh b/thirdparty/harfbuzz/src/hb-ot-kern-table.hh new file mode 100644 index 00000000000..3563cab8bd8 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-kern-table.hh @@ -0,0 +1,359 @@ +/* + * Copyright © 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_KERN_TABLE_HH +#define HB_OT_KERN_TABLE_HH + +#include "hb-aat-layout-kerx-table.hh" + + +/* + * kern -- Kerning + * https://docs.microsoft.com/en-us/typography/opentype/spec/kern + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html + */ +#define HB_OT_TAG_kern HB_TAG('k','e','r','n') + + +namespace OT { + + +template +struct KernSubTableFormat3 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + hb_array_t kernValue = kernValueZ.as_array (kernValueCount); + hb_array_t leftClass = StructAfter> (kernValue).as_array (glyphCount); + hb_array_t rightClass = StructAfter> (leftClass).as_array (glyphCount); + hb_array_t kernIndex = StructAfter> (rightClass).as_array (leftClassCount * rightClassCount); + + unsigned int leftC = leftClass[left]; + unsigned int rightC = rightClass[right]; + if (unlikely (leftC >= leftClassCount || rightC >= rightClassCount)) + return 0; + unsigned int i = leftC * rightClassCount + rightC; + return kernValue[kernIndex[i]]; + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + hb_kern_machine_t machine (*this, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_range (kernValueZ, + kernValueCount * sizeof (FWORD) + + glyphCount * 2 + + leftClassCount * rightClassCount)); + } + + protected: + KernSubTableHeader + header; + HBUINT16 glyphCount; /* The number of glyphs in this font. */ + HBUINT8 kernValueCount; /* The number of kerning values. */ + HBUINT8 leftClassCount; /* The number of left-hand classes. */ + HBUINT8 rightClassCount;/* The number of right-hand classes. */ + HBUINT8 flags; /* Set to zero (reserved for future use). */ + UnsizedArrayOf + kernValueZ; /* The kerning values. + * Length kernValueCount. */ +#if 0 + UnsizedArrayOf + leftClass; /* The left-hand classes. + * Length glyphCount. */ + UnsizedArrayOf + rightClass; /* The right-hand classes. + * Length glyphCount. */ + UnsizedArrayOfkernIndex; + /* The indices into the kernValue array. + * Length leftClassCount * rightClassCount */ +#endif + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ); +}; + +template +struct KernSubTable +{ + unsigned int get_size () const { return u.header.length; } + unsigned int get_type () const { return u.header.format; } + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + switch (get_type ()) { + /* This method hooks up to hb_font_t's get_h_kerning. Only support Format0. */ + case 0: return u.format0.get_kerning (left, right); + default:return 0; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.format0)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (u.header.apple ? c->dispatch (u.format1, hb_forward (ds)...) : c->default_return_value ()); +#endif + case 2: return_trace (c->dispatch (u.format2)); +#ifndef HB_NO_AAT_SHAPE + case 3: return_trace (u.header.apple ? c->dispatch (u.format3, hb_forward (ds)...) : c->default_return_value ()); +#endif + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.sanitize (c) || + u.header.length < u.header.min_size || + !c->check_range (this, u.header.length))) return_trace (false); + + return_trace (dispatch (c)); + } + + public: + union { + KernSubTableHeader header; + AAT::KerxSubTableFormat0 format0; + AAT::KerxSubTableFormat1 format1; + AAT::KerxSubTableFormat2 format2; + KernSubTableFormat3 format3; + } u; + public: + DEFINE_SIZE_MIN (KernSubTableHeader::static_size); +}; + + +struct KernOTSubTableHeader +{ + static constexpr bool apple = false; + typedef AAT::ObsoleteTypes Types; + + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return (coverage & Horizontal); } + + enum Coverage + { + Horizontal = 0x01u, + Minimum = 0x02u, + CrossStream = 0x04u, + Override = 0x08u, + + /* Not supported: */ + Backwards = 0x00u, + Variation = 0x00u, + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 versionZ; /* Unused. */ + HBUINT16 length; /* Length of the subtable (including this header). */ + HBUINT8 format; /* Subtable format. */ + HBUINT8 coverage; /* Coverage bits. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct KernOT : AAT::KerxTable +{ + friend struct AAT::KerxTable; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0u; + + typedef KernOTSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable SubTable; + + protected: + HBUINT16 version; /* Version--0x0000u */ + HBUINT16 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ + public: + DEFINE_SIZE_MIN (4); +}; + + +struct KernAATSubTableHeader +{ + static constexpr bool apple = true; + typedef AAT::ObsoleteTypes Types; + + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return !(coverage & Vertical); } + + enum Coverage + { + Vertical = 0x80u, + CrossStream = 0x40u, + Variation = 0x20u, + + /* Not supported: */ + Backwards = 0x00u, + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT32 length; /* Length of the subtable (including this header). */ + HBUINT8 coverage; /* Coverage bits. */ + HBUINT8 format; /* Subtable format. */ + HBUINT16 tupleIndex; /* The tuple index (used for variations fonts). + * This value specifies which tuple this subtable covers. + * Note: We don't implement. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct KernAAT : AAT::KerxTable +{ + friend struct AAT::KerxTable; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0x00010000u; + + typedef KernAATSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable SubTable; + + protected: + HBUINT32 version; /* Version--0x00010000u */ + HBUINT32 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ + public: + DEFINE_SIZE_MIN (8); +}; + +struct kern +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + + bool has_data () const { return u.version32; } + unsigned get_type () const { return u.major; } + + bool has_state_machine () const + { + switch (get_type ()) { + case 0: return u.ot.has_state_machine (); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.has_state_machine (); +#endif + default:return false; + } + } + + bool has_cross_stream () const + { + switch (get_type ()) { + case 0: return u.ot.has_cross_stream (); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.has_cross_stream (); +#endif + default:return false; + } + } + + int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + switch (get_type ()) { + case 0: return u.ot.get_h_kerning (left, right); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.get_h_kerning (left, right); +#endif + default:return 0; + } + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { return dispatch (c); } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.ot, hb_forward (ds)...)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (c->dispatch (u.aat, hb_forward (ds)...)); +#endif + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.version32.sanitize (c)) return_trace (false); + return_trace (dispatch (c)); + } + + protected: + union { + HBUINT32 version32; + HBUINT16 major; + KernOT ot; +#ifndef HB_NO_AAT_SHAPE + KernAAT aat; +#endif + } u; + public: + DEFINE_SIZE_UNION (4, version32); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_KERN_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh new file mode 100644 index 00000000000..02fe14fa062 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh @@ -0,0 +1,509 @@ +/* + * Copyright © 2016 Elie Roux + * Copyright © 2018 Google, Inc. + * Copyright © 2018-2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_LAYOUT_BASE_TABLE_HH +#define HB_OT_LAYOUT_BASE_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" + +namespace OT { + +/* + * BASE -- Baseline + * https://docs.microsoft.com/en-us/typography/opentype/spec/base + */ + +struct BaseCoordFormat1 +{ + hb_position_t get_coord () const { return coordinate; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + FWORD coordinate; /* X or Y value, in design units */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseCoordFormat2 +{ + hb_position_t get_coord () const + { + /* TODO */ + return coordinate; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + FWORD coordinate; /* X or Y value, in design units */ + HBGlyphID referenceGlyph; /* Glyph ID of control glyph */ + HBUINT16 coordPoint; /* Index of contour point on the + * reference glyph */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct BaseCoordFormat3 +{ + hb_position_t get_coord (hb_font_t *font, + const VariationStore &var_store, + hb_direction_t direction) const + { + const Device &device = this+deviceTable; + return coordinate + (HB_DIRECTION_IS_VERTICAL (direction) ? + device.get_y_delta (font, var_store) : + device.get_x_delta (font, var_store)); + } + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + deviceTable.sanitize (c, this))); + } + + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + FWORD coordinate; /* X or Y value, in design units */ + OffsetTo + deviceTable; /* Offset to Device table for X or + * Y value, from beginning of + * BaseCoord table (may be NULL). */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseCoord +{ + bool has_data () const { return u.format; } + + hb_position_t get_coord (hb_font_t *font, + const VariationStore &var_store, + hb_direction_t direction) const + { + switch (u.format) { + case 1: return u.format1.get_coord (); + case 2: return u.format2.get_coord (); + case 3: return u.format3.get_coord (font, var_store, direction); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.format.sanitize (c))) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (false); + } + } + + protected: + union { + HBUINT16 format; + BaseCoordFormat1 format1; + BaseCoordFormat2 format2; + BaseCoordFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct FeatMinMaxRecord +{ + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + bool has_data () const { return tag; } + + void get_min_max (const BaseCoord **min, const BaseCoord **max) const + { + if (likely (min)) *min = &(this+minCoord); + if (likely (max)) *max = &(this+maxCoord); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minCoord.sanitize (c, this) && + maxCoord.sanitize (c, this))); + } + + protected: + Tag tag; /* 4-byte feature identification tag--must + * match feature tag in FeatureList */ + OffsetTo + minCoord; /* Offset to BaseCoord table that defines + * the minimum extent value, from beginning + * of MinMax table (may be NULL) */ + OffsetTo + maxCoord; /* Offset to BaseCoord table that defines + * the maximum extent value, from beginning + * of MinMax table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (8); + +}; + +struct MinMax +{ + void get_min_max (hb_tag_t feature_tag, + const BaseCoord **min, + const BaseCoord **max) const + { + const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag); + if (minMaxCoord.has_data ()) + minMaxCoord.get_min_max (min, max); + else + { + if (likely (min)) *min = &(this+minCoord); + if (likely (max)) *max = &(this+maxCoord); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minCoord.sanitize (c, this) && + maxCoord.sanitize (c, this) && + featMinMaxRecords.sanitize (c, this))); + } + + protected: + OffsetTo + minCoord; /* Offset to BaseCoord table that defines + * minimum extent value, from the beginning + * of MinMax table (may be NULL) */ + OffsetTo + maxCoord; /* Offset to BaseCoord table that defines + * maximum extent value, from the beginning + * of MinMax table (may be NULL) */ + SortedArrayOf + featMinMaxRecords; + /* Array of FeatMinMaxRecords, in alphabetical + * order by featureTableTag */ + public: + DEFINE_SIZE_ARRAY (6, featMinMaxRecords); +}; + +struct BaseValues +{ + const BaseCoord &get_base_coord (int baseline_tag_index) const + { + if (baseline_tag_index == -1) baseline_tag_index = defaultIndex; + return this+baseCoords[baseline_tag_index]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseCoords.sanitize (c, this))); + } + + protected: + Index defaultIndex; /* Index number of default baseline for this + * script — equals index position of baseline tag + * in baselineTags array of the BaseTagList */ + OffsetArrayOf + baseCoords; /* Number of BaseCoord tables defined — should equal + * baseTagCount in the BaseTagList + * + * Array of offsets to BaseCoord tables, from beginning of + * BaseValues table — order matches baselineTags array in + * the BaseTagList */ + public: + DEFINE_SIZE_ARRAY (4, baseCoords); +}; + +struct BaseLangSysRecord +{ + int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); } + + bool has_data () const { return baseLangSysTag; } + + const MinMax &get_min_max () const { return this+minMax; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minMax.sanitize (c, this))); + } + + protected: + Tag baseLangSysTag; /* 4-byte language system identification tag */ + OffsetTo + minMax; /* Offset to MinMax table, from beginning + * of BaseScript table */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScript +{ + const MinMax &get_min_max (hb_tag_t language_tag) const + { + const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag); + return record.has_data () ? record.get_min_max () : this+defaultMinMax; + } + + const BaseCoord &get_base_coord (int baseline_tag_index) const + { return (this+baseValues).get_base_coord (baseline_tag_index); } + + bool has_data () const { return baseValues; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseValues.sanitize (c, this) && + defaultMinMax.sanitize (c, this) && + baseLangSysRecords.sanitize (c, this))); + } + + protected: + OffsetTo + baseValues; /* Offset to BaseValues table, from beginning + * of BaseScript table (may be NULL) */ + OffsetTo + defaultMinMax; /* Offset to MinMax table, from beginning of + * BaseScript table (may be NULL) */ + SortedArrayOf + baseLangSysRecords; + /* Number of BaseLangSysRecords + * defined — may be zero (0) */ + + public: + DEFINE_SIZE_ARRAY (6, baseLangSysRecords); +}; + +struct BaseScriptList; +struct BaseScriptRecord +{ + int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); } + + bool has_data () const { return baseScriptTag; } + + const BaseScript &get_base_script (const BaseScriptList *list) const + { return list+baseScript; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseScript.sanitize (c, base))); + } + + protected: + Tag baseScriptTag; /* 4-byte script identification tag */ + OffsetTo + baseScript; /* Offset to BaseScript table, from beginning + * of BaseScriptList */ + + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScriptList +{ + const BaseScript &get_base_script (hb_tag_t script) const + { + const BaseScriptRecord *record = &baseScriptRecords.bsearch (script); + if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T')); + return record->has_data () ? record->get_base_script (this) : Null (BaseScript); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + baseScriptRecords.sanitize (c, this)); + } + + protected: + SortedArrayOf + baseScriptRecords; + + public: + DEFINE_SIZE_ARRAY (2, baseScriptRecords); +}; + +struct Axis +{ + bool get_baseline (hb_tag_t baseline_tag, + hb_tag_t script_tag, + hb_tag_t language_tag, + const BaseCoord **coord) const + { + const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); + if (!base_script.has_data ()) return false; + + if (likely (coord)) + { + unsigned int tag_index = 0; + (this+baseTagList).bfind (baseline_tag, &tag_index); + *coord = &base_script.get_base_coord (tag_index); + } + + return true; + } + + bool get_min_max (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_tag_t feature_tag, + const BaseCoord **min_coord, + const BaseCoord **max_coord) const + { + const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); + if (!base_script.has_data ()) return false; + + base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord); + + return true; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (this+baseTagList).sanitize (c) && + (this+baseScriptList).sanitize (c))); + } + + protected: + OffsetTo> + baseTagList; /* Offset to BaseTagList table, from beginning + * of Axis table (may be NULL) + * Array of 4-byte baseline identification tags — must + * be in alphabetical order */ + OffsetTo + baseScriptList; /* Offset to BaseScriptList table, from beginning + * of Axis table + * Array of BaseScriptRecords, in alphabetical order + * by baseScriptTag */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BASE +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE; + + const Axis &get_axis (hb_direction_t direction) const + { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; } + + const VariationStore &get_var_store () const + { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } + + bool get_baseline (hb_font_t *font, + hb_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *base) const + { + const BaseCoord *base_coord = nullptr; + if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) || + !base_coord || !base_coord->has_data ())) + return false; + + if (likely (base)) + *base = base_coord->get_coord (font, get_var_store (), direction); + + return true; + } + + /* TODO: Expose this separately sometime? */ + bool get_min_max (hb_font_t *font, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_tag_t feature_tag, + hb_position_t *min, + hb_position_t *max) + { + const BaseCoord *min_coord, *max_coord; + if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag, + &min_coord, &max_coord)) + return false; + + const VariationStore &var_store = get_var_store (); + if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction); + if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction); + return true; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + likely (version.major == 1) && + hAxis.sanitize (c, this) && + vAxis.sanitize (c, this) && + (version.to_int () < 0x00010001u || varStore.sanitize (c, this)))); + } + + protected: + FixedVersion<>version; /* Version of the BASE table */ + OffsetTohAxis; /* Offset to horizontal Axis table, from beginning + * of BASE table (may be NULL) */ + OffsetTovAxis; /* Offset to vertical Axis table, from beginning + * of BASE table (may be NULL) */ + LOffsetTo + varStore; /* Offset to the table of Item Variation + * Store--from beginning of BASE + * header (may be NULL). Introduced + * in version 0x00010001. */ + public: + DEFINE_SIZE_MIN (8); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_BASE_TABLE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh new file mode 100644 index 00000000000..3140dd6328d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh @@ -0,0 +1,3178 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_LAYOUT_COMMON_HH +#define HB_OT_LAYOUT_COMMON_HH + +#include "hb.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" +#include "hb-set.hh" +#include "hb-bimap.hh" + + +#ifndef HB_MAX_NESTING_LEVEL +#define HB_MAX_NESTING_LEVEL 6 +#endif +#ifndef HB_MAX_CONTEXT_LENGTH +#define HB_MAX_CONTEXT_LENGTH 64 +#endif +#ifndef HB_CLOSURE_MAX_STAGES +/* + * The maximum number of times a lookup can be applied during shaping. + * Used to limit the number of iterations of the closure algorithm. + * This must be larger than the number of times add_pause() is + * called in a collect_features call of any shaper. + */ +#define HB_CLOSURE_MAX_STAGES 32 +#endif + +#ifndef HB_MAX_SCRIPTS +#define HB_MAX_SCRIPTS 500 +#endif + +#ifndef HB_MAX_LANGSYS +#define HB_MAX_LANGSYS 2000 +#endif + +#ifndef HB_MAX_FEATURES +#define HB_MAX_FEATURES 750 +#endif + +#ifndef HB_MAX_FEATURE_INDICES +#define HB_MAX_FEATURE_INDICES 1500 +#endif + +#ifndef HB_MAX_LOOKUP_INDICES +#define HB_MAX_LOOKUP_INDICES 20000 +#endif + + +namespace OT { + + +#define NOT_COVERED ((unsigned int) -1) + + +template +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +template +static inline void ClassDef_serialize (hb_serialize_context_t *c, + Iterator it); + +static void ClassDef_remap_and_serialize (hb_serialize_context_t *c, + const hb_set_t &glyphset, + const hb_map_t &gid_klass_map, + hb_sorted_vector_t &glyphs, + const hb_set_t &klasses, + hb_map_t *klass_map /*INOUT*/); + +struct hb_subset_layout_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "SUBSET_LAYOUT"; } + static return_t default_return_value () { return hb_empty_t (); } + + bool visitScript () + { + return script_count++ < HB_MAX_SCRIPTS; + } + + bool visitLangSys () + { + return langsys_count++ < HB_MAX_LANGSYS; + } + + bool visitFeatureIndex (int count) + { + feature_index_count += count; + return feature_index_count < HB_MAX_FEATURE_INDICES; + } + + bool visitLookupIndex() + { + lookup_index_count++; + return lookup_index_count < HB_MAX_LOOKUP_INDICES; + } + + hb_subset_context_t *subset_context; + const hb_tag_t table_tag; + const hb_map_t *lookup_index_map; + const hb_map_t *feature_index_map; + + hb_subset_layout_context_t (hb_subset_context_t *c_, + hb_tag_t tag_, + hb_map_t *lookup_map_, + hb_map_t *feature_map_) : + subset_context (c_), + table_tag (tag_), + lookup_index_map (lookup_map_), + feature_index_map (feature_map_), + script_count (0), + langsys_count (0), + feature_index_count (0), + lookup_index_count (0) + {} + + private: + unsigned script_count; + unsigned langsys_count; + unsigned feature_index_count; + unsigned lookup_index_count; +}; + +struct hb_collect_variation_indices_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) { obj.collect_variation_indices (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + hb_set_t *layout_variation_indices; + const hb_set_t *glyph_set; + const hb_map_t *gpos_lookups; + + hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_, + const hb_set_t *glyph_set_, + const hb_map_t *gpos_lookups_) : + layout_variation_indices (layout_variation_indices_), + glyph_set (glyph_set_), + gpos_lookups (gpos_lookups_) {} +}; + +template +struct subset_offset_array_t +{ + subset_offset_array_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_) : subset_context (subset_context_), + out (out_), base (base_) {} + + template + bool operator () (T&& offset) + { + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + auto snap = subset_context->serializer->snapshot (); + bool ret = o->serialize_subset (subset_context, offset, base); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; +}; + + +template +struct subset_offset_array_arg_t +{ + subset_offset_array_arg_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_, + Arg &&arg_) : subset_context (subset_context_), out (out_), + base (base_), arg (arg_) {} + + template + bool operator () (T&& offset) + { + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + auto snap = subset_context->serializer->snapshot (); + bool ret = o->serialize_subset (subset_context, offset, base, arg); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; + Arg &&arg; +}; + +/* + * Helper to subset an array of offsets. Subsets the thing pointed to by each offset + * and discards the offset in the array if the subset operation results in an empty + * thing. + */ +struct +{ + template + subset_offset_array_t + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base) const + { return subset_offset_array_t (subset_context, out, base); } + + /* Variant with one extra argument passed to serialize_subset */ + template + subset_offset_array_arg_t + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base, Arg &&arg) const + { return subset_offset_array_arg_t (subset_context, out, base, arg); } +} +HB_FUNCOBJ (subset_offset_array); + +template +struct subset_record_array_t +{ + subset_record_array_t (hb_subset_layout_context_t *c_, OutputArray* out_, + const void *base_) : subset_layout_context (c_), + out (out_), base (base_) {} + + template + void + operator () (T&& record) + { + auto snap = subset_layout_context->subset_context->serializer->snapshot (); + bool ret = record.subset (subset_layout_context, base); + if (!ret) subset_layout_context->subset_context->serializer->revert (snap); + else out->len++; + } + + private: + hb_subset_layout_context_t *subset_layout_context; + OutputArray *out; + const void *base; +}; + +/* + * Helper to subset a RecordList/record array. Subsets each Record in the array and + * discards the record if the subset operation returns false. + */ +struct +{ + template + subset_record_array_t + operator () (hb_subset_layout_context_t *c, OutputArray* out, + const void *base) const + { return subset_record_array_t (c, out, base); } +} +HB_FUNCOBJ (subset_record_array); + +/* + * + * OpenType Layout Common Table Formats + * + */ + + +/* + * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList + */ + +struct Record_sanitize_closure_t { + hb_tag_t tag; + const void *list_base; +}; + +template +struct Record +{ + int cmp (hb_tag_t a) const { return tag.cmp (a); } + + bool subset (hb_subset_layout_context_t *c, const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + bool ret = out->offset.serialize_subset (c->subset_context, offset, base, c, &tag); + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + const Record_sanitize_closure_t closure = {tag, base}; + return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); + } + + Tag tag; /* 4-byte Tag identifier */ + OffsetTo + offset; /* Offset from beginning of object holding + * the Record */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct RecordArrayOf : SortedArrayOf> +{ + const OffsetTo& get_offset (unsigned int i) const + { return (*this)[i].offset; } + OffsetTo& get_offset (unsigned int i) + { return (*this)[i].offset; } + const Tag& get_tag (unsigned int i) const + { return (*this)[i].tag; } + unsigned int get_tags (unsigned int start_offset, + unsigned int *record_count /* IN/OUT */, + hb_tag_t *record_tags /* OUT */) const + { + if (record_count) + { + + this->sub_array (start_offset, record_count) + | hb_map (&Record::tag) + | hb_sink (hb_array (record_tags, *record_count)) + ; + } + return this->len; + } + bool find_index (hb_tag_t tag, unsigned int *index) const + { + return this->bfind (tag, index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + } +}; + +template +struct RecordListOf : RecordArrayOf +{ + const Type& operator [] (unsigned int i) const + { return this+this->get_offset (i); } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + this->iter () + | hb_apply (subset_record_array (l, out, this)) + ; + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (RecordArrayOf::sanitize (c, this)); + } +}; + +struct Feature; + +struct RecordListOfFeature : RecordListOf +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + unsigned count = this->len; + + hb_zip (*this, hb_range (count)) + | hb_filter (l->feature_index_map, hb_second) + | hb_map (hb_first) + | hb_apply (subset_record_array (l, out, this)) + ; + return_trace (true); + } +}; + +struct RangeRecord +{ + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return glyphs->intersects (first, last); } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_range (first, last); } + + HBGlyphID first; /* First GlyphID in the range */ + HBGlyphID last; /* Last GlyphID in the range */ + HBUINT16 value; /* Value */ + public: + DEFINE_SIZE_STATIC (6); +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, RangeRecord); + + +struct IndexArray : ArrayOf +{ + bool intersects (const hb_map_t *indexes) const + { return hb_any (*this, indexes); } + + template + void serialize (hb_serialize_context_t *c, + hb_subset_layout_context_t *l, + Iterator it) + { + if (!it) return; + if (unlikely (!c->extend_min ((*this)))) return; + + for (const auto _ : it) + { + if (!l->visitLookupIndex()) break; + + Index i; + i = _; + c->copy (i); + this->len++; + } + } + + unsigned int get_indexes (unsigned int start_offset, + unsigned int *_count /* IN/OUT */, + unsigned int *_indexes /* OUT */) const + { + if (_count) + { + + this->sub_array (start_offset, _count) + | hb_sink (hb_array (_indexes, *_count)) + ; + } + return this->len; + } + + void add_indexes_to (hb_set_t* output /* OUT */) const + { + output->add_array (arrayZ, len); + } +}; + + +struct LangSys +{ + unsigned int get_feature_count () const + { return featureIndex.len; } + hb_tag_t get_feature_index (unsigned int i) const + { return featureIndex[i]; } + unsigned int get_feature_indexes (unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + unsigned int *feature_indexes /* OUT */) const + { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } + void add_feature_indexes_to (hb_set_t *feature_indexes) const + { featureIndex.add_indexes_to (feature_indexes); } + + bool has_required_feature () const { return reqFeatureIndex != 0xFFFFu; } + unsigned int get_required_feature_index () const + { + if (reqFeatureIndex == 0xFFFFu) + return Index::NOT_FOUND_INDEX; + return reqFeatureIndex; + } + + LangSys* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (*this)); + } + + bool operator == (const LangSys& o) const + { + if (featureIndex.len != o.featureIndex.len || + reqFeatureIndex != o.reqFeatureIndex) + return false; + + for (const auto _ : + hb_zip (featureIndex, o.featureIndex)) + if (_.first != _.second) return false; + + return true; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex) ? l->feature_index_map->get (reqFeatureIndex) : 0xFFFFu; + + if (!l->visitFeatureIndex (featureIndex.len)) + return_trace (false); + + auto it = + + hb_iter (featureIndex) + | hb_filter (l->feature_index_map) + | hb_map (l->feature_index_map) + ; + + bool ret = bool (it); + out->featureIndex.serialize (c->serializer, l, it); + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t * = nullptr) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && featureIndex.sanitize (c)); + } + + Offset16 lookupOrderZ; /* = Null (reserved for an offset to a + * reordering table) */ + HBUINT16 reqFeatureIndex;/* Index of a feature required for this + * language system--if no required features + * = 0xFFFFu */ + IndexArray featureIndex; /* Array of indices into the FeatureList */ + public: + DEFINE_SIZE_ARRAY_SIZED (6, featureIndex); +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, LangSys); + +struct Script +{ + unsigned int get_lang_sys_count () const + { return langSys.len; } + const Tag& get_lang_sys_tag (unsigned int i) const + { return langSys.get_tag (i); } + unsigned int get_lang_sys_tags (unsigned int start_offset, + unsigned int *lang_sys_count /* IN/OUT */, + hb_tag_t *lang_sys_tags /* OUT */) const + { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } + const LangSys& get_lang_sys (unsigned int i) const + { + if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); + return this+langSys[i].offset; + } + bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const + { return langSys.find_index (tag, index); } + + bool has_default_lang_sys () const { return defaultLangSys != 0; } + const LangSys& get_default_lang_sys () const { return this+defaultLangSys; } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag) const + { + TRACE_SUBSET (this); + if (!l->visitScript ()) return_trace (false); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + bool defaultLang = false; + if (has_default_lang_sys ()) + { + c->serializer->push (); + const LangSys& ls = this+defaultLangSys; + bool ret = ls.subset (c, l); + if (!ret && tag && *tag != HB_TAG ('D', 'F', 'L', 'T')) + { + c->serializer->pop_discard (); + out->defaultLangSys = 0; + } + else + { + c->serializer->add_link (out->defaultLangSys, c->serializer->pop_pack ()); + defaultLang = true; + } + } + + + langSys.iter () + | hb_filter ([=] (const Record& record) {return l->visitLangSys (); }) + | hb_filter ([&] (const Record& record) + { + const LangSys& d = this+defaultLangSys; + const LangSys& l = this+record.offset; + return !(l == d); + }) + | hb_apply (subset_record_array (l, &(out->langSys), this)) + ; + + return_trace (bool (out->langSys.len) || defaultLang || l->table_tag == HB_OT_TAG_GSUB); + } + + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t * = nullptr) const + { + TRACE_SANITIZE (this); + return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this)); + } + + protected: + OffsetTo + defaultLangSys; /* Offset to DefaultLangSys table--from + * beginning of Script table--may be Null */ + RecordArrayOf + langSys; /* Array of LangSysRecords--listed + * alphabetically by LangSysTag */ + public: + DEFINE_SIZE_ARRAY_SIZED (4, langSys); +}; + +typedef RecordListOf