diff --git a/gfx/harfbuzz/COPYING b/gfx/harfbuzz/COPYING index 57343164f212..48d1b30f90dc 100644 --- a/gfx/harfbuzz/COPYING +++ b/gfx/harfbuzz/COPYING @@ -4,14 +4,14 @@ 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 © 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,2006,2020,2021 Behdad Esfahbod Copyright © 2005 David Turner Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. Copyright © 1998-2004 David Turner and Werner Lemberg diff --git a/gfx/harfbuzz/Makefile.am b/gfx/harfbuzz/Makefile.am index aee0f530c771..95ef32f0514d 100644 --- a/gfx/harfbuzz/Makefile.am +++ b/gfx/harfbuzz/Makefile.am @@ -19,13 +19,11 @@ EXTRA_DIST = \ replace-enum-strings.cmake \ meson.build \ meson_options.txt \ - subprojects/expat.wrap \ + subprojects/cairo.wrap \ subprojects/freetype2.wrap \ subprojects/glib.wrap \ - subprojects/libffi.wrap \ - subprojects/proxy-libintl.wrap \ - subprojects/zlib.wrap \ subprojects/google-benchmark.wrap \ + subprojects/ttf-parser.wrap \ perf/meson.build \ perf/perf-draw.hh \ perf/perf-extents.hh \ diff --git a/gfx/harfbuzz/NEWS b/gfx/harfbuzz/NEWS index a05ff67cfee8..f5b40046efe5 100644 --- a/gfx/harfbuzz/NEWS +++ b/gfx/harfbuzz/NEWS @@ -1,3 +1,57 @@ +Overview of changes leading to 2.9.1 +Tuesday, September 7, 2021 +==================================== +- Final subset API is in place and if no issues are discovered, it will be the + stable subset API of HarfBuzz 3.0.0. Old API is kept to ease transition, but + will be removed in 3.0.0. +- Various fuzzer-found bug fixes. +- hb_buffer_append() now handles the pre- and post-context which previously + were left unchanged in the destination buffer. +- hb-view / hb-shape now accept following new arguments: + o --unicodes: takes a list of hex numbers that represent Unicode + codepoints. +- Undeprecated API: + hb_set_invert() + + +Overview of changes leading to 2.9.0 +Wednesday, August 18, 2021 +History Repeats Itself (Afghanistan) +==================================== +- Subsetter API is being stabilized, with the first stable API to happen in + 3.0.0 release (https://github.com/harfbuzz/harfbuzz/issues/3078). +- Support multiple variation axes with same tag, aka HOI. +- The “coretext” testing shaper now passes font variations to CoreText. +- hb-shape/hb-view does not break line at new lines unless text is read from + file. +- hb-view and hb-subset has a --batch now, similar to hb-shape. +- The --batch mode now uses ; as argument separator instead of : used previously. +- The --batch in hb-shape does not expect 0th argument anymore. That is, the + lines read are interpreted as argv[1:], instead of argv[0:]. +- The --batch option has been undocumented. We are ready to document it; send + feedback if you find it useful. +- hb-subset got arguments revamps. Added much-requested --gids-file, --glyphs, + --glyphs-file, --unicodes-file, supporting ranges in --unicodes. +- Various bug fixes. + + +Overview of changes leading to 2.8.2 +Tuesday, July 8, 2021 +==================================== +- Shaping LTR digits for RTL scripts now makes the native direction of the + digits LTR, applying shaping and positioning rules on the same glyph order as + Uniscribe. (Jonathan Kew, Khaled Hosny). +- Subsetting COLR v1 and CPAL tables is now supported. (Garret Rieger, Qunxin Liu) +- Various fixes and improvements to the subsetter. (Garret Rieger, Qunxin Liu, Behdad) +- When applying morx table, mark glyph widths should not be zeroed. (Jonathan Kew) +- GPOS is preferred over kerx, if GSUB was applied. (Behdad) +- Regional_Indicator pairs are grouped together when clustering. (Behdad) +- New API: ++hb_blob_create_or_fail() ++hb_blob_create_from_file_or_fail() ++hb_set_copy() + + Overview of changes leading to 2.8.1 Tuesday, May 4, 2021 ==================================== @@ -29,6 +83,7 @@ Sunday, December 27, 2020 tarball. - Documentation updates. + Overview of changes leading to 2.7.3 Wednesday, December 23, 2020 ==================================== diff --git a/gfx/harfbuzz/README-mozilla b/gfx/harfbuzz/README-mozilla index 9222010785ef..1a821690b853 100644 --- a/gfx/harfbuzz/README-mozilla +++ b/gfx/harfbuzz/README-mozilla @@ -1,7 +1,7 @@ This directory contains the HarfBuzz source from the upstream repo: https://github.com/harfbuzz/harfbuzz -Current version: 2.8.1 [commit b37f03f16b39d397a626f097858e9ae550234ca0] +Current version: 2.9.1 [commit 505df5abf8032f3a2295ded417dca9bfb14ea7b8] !!!Please Note!!! Because LLVM added in D100581 support for -Wunused-but-set-parameter and -Wunused-but-set-variable diff --git a/gfx/harfbuzz/README.md b/gfx/harfbuzz/README.md index 0df556de37fa..9deb32c1e897 100644 --- a/gfx/harfbuzz/README.md +++ b/gfx/harfbuzz/README.md @@ -1,9 +1,9 @@ [![Linux CI Status](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg)](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg) -[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/master.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/master) +[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main) [![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html) [![Coverity Code Health](https://img.shields.io/coverity/scan/5450.svg)](https://scan.coverity.com/projects/behdad-harfbuzz) [![Codacy Code Health](https://api.codacy.com/project/badge/Grade/f17f1708783c447488bc8dd317150eaa)](https://app.codacy.com/app/behdad/harfbuzz) -[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/master/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz) +[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz) [![Coverals Code Coverage](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz) [![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions) [ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/) diff --git a/gfx/harfbuzz/configure.ac b/gfx/harfbuzz/configure.ac index 043f6ed61b59..5071857351cd 100644 --- a/gfx/harfbuzz/configure.ac +++ b/gfx/harfbuzz/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.64]) AC_INIT([HarfBuzz], - [2.8.1], + [2.9.1], [https://github.com/harfbuzz/harfbuzz/issues/new], [harfbuzz], [http://harfbuzz.org/]) @@ -425,11 +425,11 @@ util/Makefile test/Makefile test/api/Makefile test/fuzzing/Makefile -test/shaping/Makefile -test/shaping/data/Makefile -test/shaping/data/aots/Makefile -test/shaping/data/in-house/Makefile -test/shaping/data/text-rendering-tests/Makefile +test/shape/Makefile +test/shape/data/Makefile +test/shape/data/aots/Makefile +test/shape/data/in-house/Makefile +test/shape/data/text-rendering-tests/Makefile test/subset/Makefile test/subset/data/Makefile test/subset/data/repack_tests/Makefile diff --git a/gfx/harfbuzz/src/Makefile.am b/gfx/harfbuzz/src/Makefile.am index 7a0ca29851a2..f405331d6d4f 100644 --- a/gfx/harfbuzz/src/Makefile.am +++ b/gfx/harfbuzz/src/Makefile.am @@ -410,6 +410,10 @@ TESTS_ENVIRONMENT = \ MAKE="$(MAKE) $(AM_MAKEFLAGS)" \ HBSOURCES="$(HBSOURCES)" \ HBHEADERS="$(HBHEADERS)" \ + LDD="$(LDD)" \ + NM="$(NM)" \ + OBJDUMP="$(OBJDUMP)" \ + OTOOL="$(OTOOL)" \ $(NULL) if HAVE_INTROSPECTION diff --git a/gfx/harfbuzz/src/Makefile.sources b/gfx/harfbuzz/src/Makefile.sources index 14c97996b000..28fbf2c52445 100644 --- a/gfx/harfbuzz/src/Makefile.sources +++ b/gfx/harfbuzz/src/Makefile.sources @@ -19,6 +19,9 @@ HB_BASE_sources = \ hb-array.hh \ hb-atomic.hh \ hb-bimap.hh \ + hb-bit-page.hh \ + hb-bit-set.hh \ + hb-bit-set-invertible.hh \ hb-blob.cc \ hb-blob.hh \ hb-buffer-serialize.cc \ @@ -47,6 +50,8 @@ HB_BASE_sources = \ hb-map.cc \ hb-map.hh \ hb-meta.hh \ + hb-ms-feature-ranges.cc \ + hb-ms-feature-ranges.hh \ hb-mutex.hh \ hb-null.hh \ hb-number.cc \ @@ -256,6 +261,8 @@ HB_SUBSET_sources = \ hb-number.hh \ hb-ot-cff1-table.cc \ hb-ot-cff2-table.cc \ + hb-ot-color-colrv1-closure.hh \ + hb-ot-post-table-v2subset.hh \ hb-static.cc \ hb-subset-cff-common.cc \ hb-subset-cff-common.hh \ diff --git a/gfx/harfbuzz/src/check-libstdc++.py b/gfx/harfbuzz/src/check-libstdc++.py index 200c68374905..85b7265312f3 100755 --- a/gfx/harfbuzz/src/check-libstdc++.py +++ b/gfx/harfbuzz/src/check-libstdc++.py @@ -6,13 +6,11 @@ os.chdir (os.getenv ('srcdir', os.path.dirname (__file__))) libs = os.getenv ('libs', '.libs') -ldd = shutil.which ('ldd') -if ldd: - ldd = [ldd] -else: - ldd = shutil.which ('otool') - if ldd: - ldd = [ldd, '-L'] # otool -L +ldd = os.getenv ('LDD', shutil.which ('ldd')) +if not ldd: + otool = os.getenv ('OTOOL', shutil.which ('otool')) + if otool: + ldd = otool + ' -L' else: print ('check-libstdc++.py: \'ldd\' not found; skipping test') sys.exit (77) @@ -27,7 +25,7 @@ for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-gobject']: if not os.path.exists (so): continue print ('Checking that we are not linking to libstdc++ or libc++ in %s' % so) - ldd_result = subprocess.check_output (ldd + [so]) + ldd_result = subprocess.check_output (ldd.split() + [so]) if (b'libstdc++' in ldd_result) or (b'libc++' in ldd_result): print ('Ouch, %s is linked to libstdc++ or libc++' % so) stat = 1 diff --git a/gfx/harfbuzz/src/check-static-inits.py b/gfx/harfbuzz/src/check-static-inits.py index 1c88f22ca2ba..37e0590fae5d 100755 --- a/gfx/harfbuzz/src/check-static-inits.py +++ b/gfx/harfbuzz/src/check-static-inits.py @@ -5,7 +5,7 @@ import sys, os, shutil, subprocess, glob, re builddir = os.getenv ('builddir', os.path.dirname (__file__)) libs = os.getenv ('libs', '.libs') -objdump = shutil.which ('objdump') +objdump = os.getenv ('OBJDUMP', shutil.which ('objdump')) if not objdump: print ('check-static-inits.py: \'ldd\' not found; skipping test') sys.exit (77) @@ -20,9 +20,20 @@ if not OBJS: sys.exit (77) stat = 0 +tested = 0 for obj in OBJS: - result = subprocess.check_output ([objdump, '-t', obj]).decode ('utf-8') + result = subprocess.run(objdump.split () + ['-t', obj], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + if result.returncode: + if result.stderr.find (b'not recognized') != -1: + # https://github.com/harfbuzz/harfbuzz/issues/3019 + print ('objdump %s returned "not recognized", skipping' % obj) + continue + print ('objdump %s returned error:\n%s' % (obj, result.stderr.decode ('utf-8'))) + stat = 2 + + result = result.stdout.decode ('utf-8') # Checking that no object file has static initializers for l in re.findall (r'^.*\.[cd]tors.*$', result, re.MULTILINE): @@ -35,4 +46,6 @@ for obj in OBJS: print ('Ouch, %s has lazy static C++ constructors/destructors or other such stuff' % obj) stat = 1 -sys.exit (stat) + tested += 1 + +sys.exit (stat if tested else 77) diff --git a/gfx/harfbuzz/src/check-symbols.py b/gfx/harfbuzz/src/check-symbols.py index c366dc0aafd7..385959bdef78 100755 --- a/gfx/harfbuzz/src/check-symbols.py +++ b/gfx/harfbuzz/src/check-symbols.py @@ -9,9 +9,10 @@ libs = os.getenv ('libs', '.libs') IGNORED_SYMBOLS = '|'.join(['_fini', '_init', '_fdata', '_ftext', '_fbss', '__bss_start', '__bss_start__', '__bss_end__', '_edata', '_end', '_bss_end__', - '__end__', '__gcov_.*', 'llvm_.*', 'flush_fn_list', 'writeout_fn_list', 'mangle_path']) + '__end__', '__gcov_.*', 'llvm_.*', 'flush_fn_list', 'writeout_fn_list', 'mangle_path', + 'lprofDirMode', 'reset_fn_list']) -nm = shutil.which ('nm') +nm = os.getenv ('NM', shutil.which ('nm')) if not nm: print ('check-symbols.py: \'nm\' not found; skipping test') sys.exit (77) @@ -30,8 +31,8 @@ for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-icu', 'harfbuzz-gobject' symprefix = '_' if suffix == 'dylib' else '' EXPORTED_SYMBOLS = [s.split ()[2] - for s in re.findall (r'^.+ [BCDGIRST] .+$', subprocess.check_output ([nm, so]).decode ('utf-8'), re.MULTILINE) - if not re.match (r'.* %s(%s)\b' % (symprefix, IGNORED_SYMBOLS), s)] + for s in re.findall (r'^.+ [BCDGIRST] .+$', subprocess.check_output (nm.split() + [so]).decode ('utf-8'), re.MULTILINE) + if not re.match (r'.* %s(%s)\b' % (symprefix, IGNORED_SYMBOLS), s)] # run again c++flit also if is available if cxxflit: @@ -67,7 +68,7 @@ for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-icu', 'harfbuzz-gobject' tested = True if not tested: - print ('check-symbols.sh: no shared libraries found; skipping test') + print ('check-symbols.py: no shared libraries found; skipping test') sys.exit (77) sys.exit (stat) diff --git a/gfx/harfbuzz/src/gen-emoji-table.py b/gfx/harfbuzz/src/gen-emoji-table.py index 551bc9c7ced7..0ee8fecfa48e 100755 --- a/gfx/harfbuzz/src/gen-emoji-table.py +++ b/gfx/harfbuzz/src/gen-emoji-table.py @@ -1,16 +1,17 @@ #!/usr/bin/env python3 -"""usage: ./gen-emoji-table.py emoji-data.txt +"""usage: ./gen-emoji-table.py emoji-data.txt emoji-test.txt Input file: * https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt +* https://www.unicode.org/Public/emoji/latest/emoji-test.txt """ import sys from collections import OrderedDict import packTab -if len (sys.argv) != 2: +if len (sys.argv) != 3: sys.exit (__doc__) f = open(sys.argv[1]) @@ -61,7 +62,7 @@ for typ, s in ranges.items(): arr = dict() for start,end in s: - for i in range(start,end): + for i in range(start, end + 1): arr[i] = 1 sol = packTab.pack_table(arr, 0, compression=3) @@ -74,3 +75,24 @@ print () print ("#endif /* HB_UNICODE_EMOJI_TABLE_HH */") print () print ("/* == End of generated table == */") + + +# Generate test file. +sequences = [] +with open(sys.argv[2]) as f: + for line in f.readlines(): + if "#" in line: + line = line[:line.index("#")] + if ";" in line: + line = line[:line.index(";")] + line = line.strip() + line = line.split(" ") + if len(line) < 2: + continue + sequences.append(line) + +with open("../test/shaping/data/in-house/tests/emoji-clusters.tests", "w") as f: + for sequence in sequences: + f.write("../fonts/AdobeBlank2.ttf:--no-glyph-names --no-positions --font-funcs=ot") + f.write(":" + ",".join(sequence)) + f.write(":[" + "|".join("1=0" for c in sequence) + "]\n") diff --git a/gfx/harfbuzz/src/gen-ragel-artifacts.py b/gfx/harfbuzz/src/gen-ragel-artifacts.py index b60ec3bff58f..d22e03ae3d7a 100755 --- a/gfx/harfbuzz/src/gen-ragel-artifacts.py +++ b/gfx/harfbuzz/src/gen-ragel-artifacts.py @@ -4,7 +4,7 @@ import os, os.path, sys, subprocess, shutil -ragel = shutil.which ('ragel') +ragel = os.getenv ('RAGEL', shutil.which ('ragel')) if not ragel: sys.exit ('You have to install ragel if you are going to develop HarfBuzz itself') @@ -19,7 +19,7 @@ outdir = os.path.dirname (OUTPUT) shutil.copy (INPUT, outdir) rl = os.path.basename (INPUT) hh = rl.replace ('.rl', '.hh') -subprocess.Popen ([ragel, '-e', '-F1', '-o', hh, rl], cwd=outdir).wait () +subprocess.Popen (ragel.split() + ['-e', '-F1', '-o', hh, rl], cwd=outdir).wait () # copy it also to src/ shutil.copyfile (os.path.join (outdir, hh), os.path.join (CURRENT_SOURCE_DIR, hh)) diff --git a/gfx/harfbuzz/src/gen-use-table.py b/gfx/harfbuzz/src/gen-use-table.py index 35d9abf1e44b..930300364d90 100755 --- a/gfx/harfbuzz/src/gen-use-table.py +++ b/gfx/harfbuzz/src/gen-use-table.py @@ -18,7 +18,7 @@ import sys if len (sys.argv) != 8: sys.exit (__doc__) -BLACKLISTED_BLOCKS = [ +DISABLED_BLOCKS = [ 'Samaritan', 'Thai', 'Lao', @@ -137,7 +137,7 @@ for i,d in enumerate (data): if not u in combined: combined[u] = list (defaults) combined[u][i] = v -combined = {k:v for k,v in combined.items() if v[4] not in BLACKLISTED_BLOCKS} +combined = {k:v for k,v in combined.items() if v[4] not in DISABLED_BLOCKS} data = combined del combined diff --git a/gfx/harfbuzz/src/harfbuzz.cc b/gfx/harfbuzz/src/harfbuzz.cc index fe00100974ed..14ee6f5e8ae9 100644 --- a/gfx/harfbuzz/src/harfbuzz.cc +++ b/gfx/harfbuzz/src/harfbuzz.cc @@ -9,6 +9,7 @@ #include "hb-fallback-shape.cc" #include "hb-font.cc" #include "hb-map.cc" +#include "hb-ms-feature-ranges.cc" #include "hb-number.cc" #include "hb-ot-cff1-table.cc" #include "hb-ot-cff2-table.cc" diff --git a/gfx/harfbuzz/src/hb-aat-layout.cc b/gfx/harfbuzz/src/hb-aat-layout.cc index 63f602855aa7..e2d4de2ccdd1 100644 --- a/gfx/harfbuzz/src/hb-aat-layout.cc +++ b/gfx/harfbuzz/src/hb-aat-layout.cc @@ -249,7 +249,9 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, if (morx.has_data ()) { AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob); + if (!buffer->message (font, "start table morx")) return; morx.apply (&c); + (void) buffer->message (font, "end table morx"); return; } @@ -258,7 +260,9 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, if (mort.has_data ()) { AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob); + if (!buffer->message (font, "start table mort")) return; mort.apply (&c); + (void) buffer->message (font, "end table mort"); return; } } @@ -314,8 +318,10 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan, const AAT::kerx& kerx = *kerx_blob->as (); AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob); + if (!buffer->message (font, "start table kerx")) return; c.set_ankr_table (font->face->table.ankr.get ()); kerx.apply (&c); + (void) buffer->message (font, "end table kerx"); } diff --git a/gfx/harfbuzz/src/hb-algs.hh b/gfx/harfbuzz/src/hb-algs.hh index bc170b0546c0..bbe097fe015d 100644 --- a/gfx/harfbuzz/src/hb-algs.hh +++ b/gfx/harfbuzz/src/hb-algs.hh @@ -760,6 +760,14 @@ static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; } #define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) +static inline void * +hb_memcpy (void *__restrict dst, const void *__restrict src, size_t len) +{ + /* It's illegal to pass 0 as size to memcpy. */ + if (unlikely (!len)) return dst; + return memcpy (dst, src, len); +} + static inline int hb_memcmp (const void *a, const void *b, unsigned int len) { @@ -1151,30 +1159,48 @@ hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *o /* Operators. */ -struct hb_bitwise_and +struct { HB_PARTIALIZE(2); 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 +struct { HB_PARTIALIZE(2); 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 +struct { HB_PARTIALIZE(2); 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 +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (~a & b) +} +HB_FUNCOBJ (hb_bitwise_lt); +struct { HB_PARTIALIZE(2); template constexpr auto operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b) } -HB_FUNCOBJ (hb_bitwise_sub); +HB_FUNCOBJ (hb_bitwise_gt); // aka sub +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (~a | b) +} +HB_FUNCOBJ (hb_bitwise_le); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | ~b) +} +HB_FUNCOBJ (hb_bitwise_ge); struct { template constexpr auto @@ -1195,6 +1221,12 @@ struct } HB_FUNCOBJ (hb_sub); struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (b - a) +} +HB_FUNCOBJ (hb_rsub); +struct { HB_PARTIALIZE(2); template constexpr auto operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b) diff --git a/gfx/harfbuzz/src/hb-array.hh b/gfx/harfbuzz/src/hb-array.hh index 9af98b103c08..ab0dd6ebe3ee 100644 --- a/gfx/harfbuzz/src/hb-array.hh +++ b/gfx/harfbuzz/src/hb-array.hh @@ -36,6 +36,14 @@ template struct hb_sorted_array_t; +enum hb_not_found_t +{ + HB_NOT_FOUND_DONT_STORE, + HB_NOT_FOUND_STORE, + HB_NOT_FOUND_STORE_CLOSEST, +}; + + template struct hb_array_t : hb_iter_with_fallback_t, Type&> { @@ -139,7 +147,9 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> return lfind (x, &i) ? &this->arrayZ[i] : not_found; } template - bool lfind (const T &x, unsigned *pos = nullptr) const + bool lfind (const T &x, unsigned *pos = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const { for (unsigned i = 0; i < length; ++i) if (hb_equal (x, this->arrayZ[i])) @@ -149,6 +159,22 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> return true; } + if (pos) + { + switch (not_found) + { + case HB_NOT_FOUND_DONT_STORE: + break; + + case HB_NOT_FOUND_STORE: + *pos = to_store; + break; + + case HB_NOT_FOUND_STORE_CLOSEST: + *pos = length; + break; + } + } return false; } @@ -231,9 +257,9 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> && (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; } + /* Only call if you allocated the underlying array using hb_malloc() or similar. */ + void fini () + { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } template hb_array_t copy (hb_serialize_context_t *c) const @@ -266,13 +292,6 @@ 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&>, @@ -323,7 +342,7 @@ struct hb_sorted_array_t : } template bool bfind (const T &x, unsigned int *i = nullptr, - hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, unsigned int to_store = (unsigned int) -1) const { unsigned pos; @@ -339,14 +358,14 @@ struct hb_sorted_array_t : { switch (not_found) { - case HB_BFIND_NOT_FOUND_DONT_STORE: + case HB_NOT_FOUND_DONT_STORE: break; - case HB_BFIND_NOT_FOUND_STORE: + case HB_NOT_FOUND_STORE: *i = to_store; break; - case HB_BFIND_NOT_FOUND_STORE_CLOSEST: + case HB_NOT_FOUND_STORE_CLOSEST: *i = pos; break; } diff --git a/gfx/harfbuzz/src/hb-bimap.hh b/gfx/harfbuzz/src/hb-bimap.hh index e9f3a6a52df7..d40988075197 100644 --- a/gfx/harfbuzz/src/hb-bimap.hh +++ b/gfx/harfbuzz/src/hb-bimap.hh @@ -58,10 +58,15 @@ struct hb_bimap_t void set (hb_codepoint_t lhs, hb_codepoint_t rhs) { + if (in_error ()) return; if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return; if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; } + forw_map.set (lhs, rhs); + if (in_error ()) return; + back_map.set (rhs, lhs); + if (in_error ()) forw_map.del (lhs); } hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); } diff --git a/gfx/harfbuzz/src/hb-bit-page.hh b/gfx/harfbuzz/src/hb-bit-page.hh new file mode 100644 index 000000000000..263be3d0447a --- /dev/null +++ b/gfx/harfbuzz/src/hb-bit-page.hh @@ -0,0 +1,203 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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_BIT_PAGE_HH +#define HB_BIT_PAGE_HH + +#include "hb.hh" + +struct hb_bit_page_t +{ + void init0 () { v.clear (); } + void init1 () { v.clear (0xFF); } + + constexpr unsigned len () const + { return ARRAY_LENGTH_CONST (v); } + + bool is_empty () const + { + for (unsigned int i = 0; i < len (); i++) + if (v[i]) + return false; + return true; + } + + void add (hb_codepoint_t g) { elt (g) |= mask (g); } + void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } + void set (hb_codepoint_t g, bool v) { if (v) add (g); else del (g); } + bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } + + void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la |= (mask (b) << 1) - mask(a); + else + { + *la |= ~(mask (a) - 1); + la++; + + memset (la, 0xff, (char *) lb - (char *) la); + + *lb |= ((mask (b) << 1) - 1); + } + } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la &= ~((mask (b) << 1) - mask(a)); + else + { + *la &= mask (a) - 1; + la++; + + memset (la, 0, (char *) lb - (char *) la); + + *lb &= ~((mask (b) << 1) - 1); + } + } + void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v) + { if (v) add_range (a, b); else del_range (a, b); } + + bool is_equal (const hb_bit_page_t &other) const + { + return 0 == hb_memcmp (&v, &other.v, sizeof (v)); + } + bool is_subset (const hb_bit_page_t &larger_page) const + { + for (unsigned i = 0; i < len (); i++) + if (~larger_page.v[i] & v[i]) + return false; + return true; + } + + unsigned int get_population () const + { + unsigned int pop = 0; + for (unsigned int i = 0; i < len (); i++) + pop += hb_popcount (v[i]); + return pop; + } + + bool next (hb_codepoint_t *codepoint) const + { + unsigned int m = (*codepoint + 1) & MASK; + if (!m) + { + *codepoint = INVALID; + return false; + } + unsigned int i = m / ELT_BITS; + unsigned int j = m & ELT_MASK; + + const elt_t vv = v[i] & ~((elt_t (1) << j) - 1); + for (const elt_t *p = &vv; i < len (); p = &v[++i]) + if (*p) + { + *codepoint = i * ELT_BITS + elt_get_min (*p); + return true; + } + + *codepoint = INVALID; + return false; + } + bool previous (hb_codepoint_t *codepoint) const + { + unsigned int m = (*codepoint - 1) & MASK; + if (m == MASK) + { + *codepoint = INVALID; + return false; + } + unsigned int i = m / ELT_BITS; + unsigned int j = m & ELT_MASK; + + /* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */ + const elt_t mask = j < 8 * sizeof (elt_t) - 1 ? + ((elt_t (1) << (j + 1)) - 1) : + (elt_t) -1; + const elt_t vv = v[i] & mask; + const elt_t *p = &vv; + while (true) + { + if (*p) + { + *codepoint = i * ELT_BITS + elt_get_max (*p); + return true; + } + if ((int) i <= 0) break; + p = &v[--i]; + } + + *codepoint = INVALID; + return false; + } + hb_codepoint_t get_min () const + { + for (unsigned int i = 0; i < len (); i++) + if (v[i]) + return i * ELT_BITS + elt_get_min (v[i]); + return INVALID; + } + hb_codepoint_t get_max () const + { + for (int i = len () - 1; i >= 0; i--) + if (v[i]) + return i * ELT_BITS + elt_get_max (v[i]); + return 0; + } + + static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; + + typedef unsigned long long elt_t; + static constexpr unsigned PAGE_BITS = 512; + static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, ""); + + static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); } + static unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; } + + typedef hb_vector_size_t vector_t; + + static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8; + static constexpr unsigned ELT_MASK = ELT_BITS - 1; + static constexpr unsigned BITS = sizeof (vector_t) * 8; + static constexpr unsigned MASK = BITS - 1; + static_assert ((unsigned) PAGE_BITS == (unsigned) BITS, ""); + + elt_t &elt (hb_codepoint_t g) { return v[(g & MASK) / ELT_BITS]; } + const elt_t& elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; } + static constexpr elt_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); } + + vector_t v; +}; +static_assert (hb_bit_page_t::PAGE_BITS == sizeof (hb_bit_page_t) * 8, ""); + + +#endif /* HB_BIT_PAGE_HH */ diff --git a/gfx/harfbuzz/src/hb-bit-set-invertible.hh b/gfx/harfbuzz/src/hb-bit-set-invertible.hh new file mode 100644 index 000000000000..f48b72fe6300 --- /dev/null +++ b/gfx/harfbuzz/src/hb-bit-set-invertible.hh @@ -0,0 +1,354 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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_BIT_SET_INVERTIBLE_HH +#define HB_BIT_SET_INVERTIBLE_HH + +#include "hb.hh" +#include "hb-bit-set.hh" + + +struct hb_bit_set_invertible_t +{ + hb_bit_set_t s; + bool inverted; + + hb_bit_set_invertible_t () { init (); } + ~hb_bit_set_invertible_t () { fini (); } + + void init () { s.init (); inverted = false; } + void fini () { s.fini (); } + void err () { s.err (); } + bool in_error () const { return s.in_error (); } + explicit operator bool () const { return !is_empty (); } + + void reset () + { + s.reset (); + inverted = false; + } + void clear () + { + s.clear (); + if (likely (s.successful)) + inverted = false; + } + void invert () + { + if (likely (s.successful)) + inverted = !inverted; + } + + bool is_empty () const + { + hb_codepoint_t v = INVALID; + next (&v); + return v == INVALID; + } + hb_codepoint_t get_min () const + { + hb_codepoint_t v = INVALID; + next (&v); + return v; + } + hb_codepoint_t get_max () const + { + hb_codepoint_t v = INVALID; + previous (&v); + return v; + } + unsigned int get_population () const + { return inverted ? INVALID - s.get_population () : s.get_population (); } + + + void add (hb_codepoint_t g) { unlikely (inverted) ? s.del (g) : s.add (g); } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { return unlikely (inverted) ? (s.del_range (a, b), true) : s.add_range (a, b); } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { inverted ? s.del_array (array, count, stride) : s.add_array (array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return inverted ? s.del_sorted_array (array, count, stride) : s.add_sorted_array (array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) { unlikely (inverted) ? s.add (g) : s.del (g); } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); } + + bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; } + + /* Has interface. */ + static constexpr bool SENTINEL = false; + typedef bool value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_bit_set_invertible_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_bit_set_invertible_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { + hb_codepoint_t c = first - 1; + return next (&c) && c <= last; + } + + void set (const hb_bit_set_invertible_t &other) + { + s.set (other.s); + if (likely (s.successful)) + inverted = other.inverted; + } + + bool is_equal (const hb_bit_set_invertible_t &other) const + { + if (likely (inverted == other.inverted)) + return s.is_equal (other.s); + else + { + /* TODO Add iter_ranges() and use here. */ + auto it1 = iter (); + auto it2 = other.iter (); + return hb_all (+ hb_zip (it1, it2) + | hb_map ([](hb_pair_t _) { return _.first == _.second; })); + } + } + + bool is_subset (const hb_bit_set_invertible_t &larger_set) const + { + if (unlikely (inverted != larger_set.inverted)) + return hb_all (hb_iter (s) | hb_map (larger_set.s)); + else + return unlikely (inverted) ? larger_set.s.is_subset (s) : s.is_subset (larger_set.s); + } + + protected: + template + void process (const Op& op, const hb_bit_set_invertible_t &other) + { s.process (op, other.s); } + public: + void union_ (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_and, other); + else + process (hb_bitwise_or, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_gt, other); + else + process (hb_bitwise_lt, other); + } + if (likely (s.successful)) + inverted = inverted || other.inverted; + } + void intersect (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_or, other); + else + process (hb_bitwise_and, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_lt, other); + else + process (hb_bitwise_gt, other); + } + if (likely (s.successful)) + inverted = inverted && other.inverted; + } + void subtract (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_lt, other); + else + process (hb_bitwise_gt, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_or, other); + else + process (hb_bitwise_and, other); + } + if (likely (s.successful)) + inverted = inverted && !other.inverted; + } + void symmetric_difference (const hb_bit_set_invertible_t &other) + { + process (hb_bitwise_xor, other); + if (likely (s.successful)) + inverted = inverted ^ other.inverted; + } + + bool next (hb_codepoint_t *codepoint) const + { + if (likely (!inverted)) + return s.next (codepoint); + + auto old = *codepoint; + if (unlikely (old + 1 == INVALID)) + { + *codepoint = INVALID; + return false; + } + + auto v = old; + s.next (&v); + if (old + 1 < v) + { + *codepoint = old + 1; + return true; + } + + v = old; + s.next_range (&old, &v); + + *codepoint = v + 1; + return *codepoint != INVALID; + } + bool previous (hb_codepoint_t *codepoint) const + { + if (likely (!inverted)) + return s.previous (codepoint); + + auto old = *codepoint; + if (unlikely (old - 1 == INVALID)) + { + *codepoint = INVALID; + return false; + } + + auto v = old; + s.previous (&v); + + if (old - 1 > v || v == INVALID) + { + *codepoint = old - 1; + return true; + } + + v = old; + s.previous_range (&v, &old); + + *codepoint = v - 1; + return *codepoint != INVALID; + } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + if (likely (!inverted)) + return s.next_range (first, last); + + if (!next (last)) + { + *last = *first = INVALID; + return false; + } + + *first = *last; + s.next (last); + --*last; + return true; + } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + if (likely (!inverted)) + return s.previous_range (first, last); + + if (!previous (first)) + { + *last = *first = INVALID; + return false; + } + + *last = *first; + s.previous (first); + ++*first; + return true; + } + + static constexpr hb_codepoint_t INVALID = hb_bit_set_t::INVALID; + + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_bit_set_invertible_t &s_ = Null (hb_bit_set_invertible_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } + + protected: + const hb_bit_set_invertible_t *s; + hb_codepoint_t v; + unsigned l; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } +}; + + +#endif /* HB_BIT_SET_INVERTIBLE_HH */ diff --git a/gfx/harfbuzz/src/hb-bit-set.hh b/gfx/harfbuzz/src/hb-bit-set.hh new file mode 100644 index 000000000000..c21778d88e7c --- /dev/null +++ b/gfx/harfbuzz/src/hb-bit-set.hh @@ -0,0 +1,808 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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_BIT_SET_HH +#define HB_BIT_SET_HH + +#include "hb.hh" +#include "hb-bit-page.hh" +#include "hb-machinery.hh" + + +struct hb_bit_set_t +{ + hb_bit_set_t () { init (); } + ~hb_bit_set_t () { fini (); } + + hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other); } + void operator= (const hb_bit_set_t& other) { set (other); } + // TODO Add move construtor/assign + // TODO Add constructor for Iterator; with specialization for (sorted) vector / array? + + void init () + { + successful = true; + population = 0; + last_page_lookup = 0; + page_map.init (); + pages.init (); + } + void fini () + { + page_map.fini (); + pages.fini (); + } + + using page_t = hb_bit_page_t; + struct page_map_t + { + int cmp (const page_map_t &o) const { return cmp (o.major); } + int cmp (uint32_t o_major) const { return (int) o_major - (int) major; } + + uint32_t major; + uint32_t index; + }; + + bool successful; /* Allocations successful */ + mutable unsigned int population; + mutable unsigned int last_page_lookup; + hb_sorted_vector_t page_map; + hb_vector_t pages; + + void err () { if (successful) successful = false; } /* TODO Remove */ + bool in_error () const { return !successful; } + + bool resize (unsigned int count) + { + if (unlikely (!successful)) return false; + if (unlikely (!pages.resize (count) || !page_map.resize (count))) + { + pages.resize (page_map.length); + successful = false; + return false; + } + return true; + } + + void reset () + { + successful = true; + clear (); + } + + void clear () + { + resize (0); + if (likely (successful)) + population = 0; + } + bool is_empty () const + { + unsigned int count = pages.length; + for (unsigned int i = 0; i < count; i++) + if (!pages[i].is_empty ()) + return false; + return true; + } + explicit operator bool () const { return !is_empty (); } + + private: + void dirty () { population = UINT_MAX; } + public: + + void add (hb_codepoint_t g) + { + if (unlikely (!successful)) return; + if (unlikely (g == INVALID)) return; + dirty (); + page_t *page = page_for (g, true); if (unlikely (!page)) return; + page->add (g); + } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ + if (unlikely (a > b || a == INVALID || b == INVALID)) return false; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + if (ma == mb) + { + page_t *page = page_for (a, true); if (unlikely (!page)) return false; + page->add_range (a, b); + } + else + { + page_t *page = page_for (a, true); if (unlikely (!page)) return false; + page->add_range (a, major_start (ma + 1) - 1); + + for (unsigned int m = ma + 1; m < mb; m++) + { + page = page_for (major_start (m), true); if (unlikely (!page)) return false; + page->init1 (); + } + + page = page_for (b, true); if (unlikely (!page)) return false; + page->add_range (major_start (mb), b); + } + return true; + } + + template + void set_array (bool v, const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + if (unlikely (!successful)) return; + if (!count) return; + dirty (); + hb_codepoint_t g = *array; + while (count) + { + unsigned int m = get_major (g); + page_t *page = page_for (g, v); if (unlikely (v && !page)) return; + unsigned int start = major_start (m); + unsigned int end = major_start (m + 1); + do + { + if (v || page) /* The v check is to optimize out the page check if v is true. */ + page->set (g, v); + + array = &StructAtOffsetUnaligned (array, stride); + count--; + } + while (count && (g = *array, start <= g && g < end)); + } + } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (true, array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + template + void del_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (false, array, count, stride); } + template + void del_array (const hb_array_t& arr) { del_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool set_sorted_array (bool v, const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ + if (!count) return true; + dirty (); + hb_codepoint_t g = *array; + hb_codepoint_t last_g = g; + while (count) + { + unsigned int m = get_major (g); + page_t *page = page_for (g, v); if (unlikely (v && !page)) return false; + unsigned int end = major_start (m + 1); + do + { + /* If we try harder we can change the following comparison to <=; + * Not sure if it's worth it. */ + if (g < last_g) return false; + last_g = g; + + if (v || page) /* The v check is to optimize out the page check if v is true. */ + page->add (g); + + array = (const T *) ((const char *) array + stride); + count--; + } + while (count && (g = *array, g < end)); + } + return true; + } + + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (true, array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + template + bool del_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (false, array, count, stride); } + template + bool del_sorted_array (const hb_sorted_array_t& arr) { return del_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) + { + if (unlikely (!successful)) return; + page_t *page = page_for (g); + if (!page) + return; + dirty (); + page->del (g); + } + + private: + void del_pages (int ds, int de) + { + if (ds <= de) + { + // Pre-allocate the workspace that compact() will need so we can bail on allocation failure + // before attempting to rewrite the page map. + hb_vector_t compact_workspace; + if (unlikely (!allocate_compact_workspace (compact_workspace))) return; + + unsigned int write_index = 0; + for (unsigned int i = 0; i < page_map.length; i++) + { + int m = (int) page_map[i].major; + if (m < ds || de < m) + page_map[write_index++] = page_map[i]; + } + compact (compact_workspace, write_index); + resize (write_index); + } + } + + + public: + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!successful)) return; + if (unlikely (a > b || a == INVALID)) return; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + /* Delete pages from ds through de if ds <= de. */ + int ds = (a == major_start (ma))? (int) ma: (int) (ma + 1); + int de = (b + 1 == major_start (mb + 1))? (int) mb: ((int) mb - 1); + if (ds > de || (int) ma < ds) + { + page_t *page = page_for (a); + if (page) + { + if (ma == mb) + page->del_range (a, b); + else + page->del_range (a, major_start (ma + 1) - 1); + } + } + if (de < (int) mb && ma != mb) + { + page_t *page = page_for (b); + if (page) + page->del_range (major_start (mb), b); + } + del_pages (ds, de); + } + + bool get (hb_codepoint_t g) const + { + const page_t *page = page_for (g); + if (!page) + return false; + return page->get (g); + } + + /* Has interface. */ + static constexpr bool SENTINEL = false; + typedef bool value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_bit_set_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_bit_set_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { + hb_codepoint_t c = first - 1; + return next (&c) && c <= last; + } + void set (const hb_bit_set_t &other) + { + if (unlikely (!successful)) return; + unsigned int count = other.pages.length; + if (unlikely (!resize (count))) + return; + population = other.population; + + /* TODO switch to vector operator =. */ + hb_memcpy ((void *) pages, (const void *) other.pages, count * pages.item_size); + hb_memcpy ((void *) page_map, (const void *) other.page_map, count * page_map.item_size); + } + + bool is_equal (const hb_bit_set_t &other) const + { + if (has_population () && other.has_population () && + get_population () != other.get_population ()) + return false; + + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + + unsigned int a = 0, b = 0; + for (; a < na && b < nb; ) + { + if (page_at (a).is_empty ()) { a++; continue; } + if (other.page_at (b).is_empty ()) { b++; continue; } + if (page_map[a].major != other.page_map[b].major || + !page_at (a).is_equal (other.page_at (b))) + return false; + a++; + b++; + } + for (; a < na; a++) + if (!page_at (a).is_empty ()) { return false; } + for (; b < nb; b++) + if (!other.page_at (b).is_empty ()) { return false; } + + return true; + } + + bool is_subset (const hb_bit_set_t &larger_set) const + { + if (has_population () && larger_set.has_population () && + get_population () != larger_set.get_population ()) + return false; + + uint32_t spi = 0; + for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++) + { + uint32_t spm = page_map[spi].major; + uint32_t lpm = larger_set.page_map[lpi].major; + auto sp = page_at (spi); + auto lp = larger_set.page_at (lpi); + + if (spm < lpm && !sp.is_empty ()) + return false; + + if (lpm < spm) + continue; + + if (!sp.is_subset (lp)) + return false; + + spi++; + } + + while (spi < page_map.length) + if (!page_at (spi++).is_empty ()) + return false; + + return true; + } + + private: + bool allocate_compact_workspace (hb_vector_t& workspace) + { + if (unlikely (!workspace.resize (pages.length))) + { + successful = false; + return false; + } + + return true; + } + + /* + * workspace should be a pre-sized vector allocated to hold at exactly pages.length + * elements. + */ + void compact (hb_vector_t& workspace, + unsigned int length) + { + assert(workspace.length == pages.length); + hb_vector_t& old_index_to_page_map_index = workspace; + + hb_fill (old_index_to_page_map_index.writer(), 0xFFFFFFFF); + for (unsigned i = 0; i < length; i++) + old_index_to_page_map_index[page_map[i].index] = i; + + compact_pages (old_index_to_page_map_index); + } + void compact_pages (const hb_vector_t& old_index_to_page_map_index) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < pages.length; i++) + { + if (old_index_to_page_map_index[i] == 0xFFFFFFFF) continue; + + if (write_index < i) + pages[write_index] = pages[i]; + + page_map[old_index_to_page_map_index[i]].index = write_index; + write_index++; + } + } + public: + + template + void process (const Op& op, const hb_bit_set_t &other) + { + const bool passthru_left = op (1, 0); + const bool passthru_right = op (0, 1); + + if (unlikely (!successful)) return; + + dirty (); + + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + unsigned int next_page = na; + + unsigned int count = 0, newCount = 0; + unsigned int a = 0, b = 0; + unsigned int write_index = 0; + + // Pre-allocate the workspace that compact() will need so we can bail on allocation failure + // before attempting to rewrite the page map. + hb_vector_t compact_workspace; + if (!passthru_left && unlikely (!allocate_compact_workspace (compact_workspace))) return; + + for (; a < na && b < nb; ) + { + if (page_map[a].major == other.page_map[b].major) + { + if (!passthru_left) + { + // Move page_map entries that we're keeping from the left side set + // to the front of the page_map vector. This isn't necessary if + // passthru_left is set since no left side pages will be removed + // in that case. + if (write_index < a) + page_map[write_index] = page_map[a]; + write_index++; + } + + count++; + a++; + b++; + } + else if (page_map[a].major < other.page_map[b].major) + { + if (passthru_left) + count++; + a++; + } + else + { + if (passthru_right) + count++; + b++; + } + } + if (passthru_left) + count += na - a; + if (passthru_right) + count += nb - b; + + if (!passthru_left) + { + na = write_index; + next_page = write_index; + compact (compact_workspace, write_index); + } + + if (unlikely (!resize (count))) + return; + + newCount = count; + + /* Process in-place backward. */ + a = na; + b = nb; + for (; a && b; ) + { + if (page_map[a - 1].major == other.page_map[b - 1].major) + { + a--; + b--; + count--; + page_map[count] = page_map[a]; + page_at (count).v = op (page_at (a).v, other.page_at (b).v); + } + else if (page_map[a - 1].major > other.page_map[b - 1].major) + { + a--; + if (passthru_left) + { + count--; + page_map[count] = page_map[a]; + } + } + else + { + b--; + if (passthru_right) + { + count--; + page_map[count].major = other.page_map[b].major; + page_map[count].index = next_page++; + page_at (count).v = other.page_at (b).v; + } + } + } + if (passthru_left) + while (a) + { + a--; + count--; + page_map[count] = page_map [a]; + } + if (passthru_right) + while (b) + { + b--; + count--; + page_map[count].major = other.page_map[b].major; + page_map[count].index = next_page++; + page_at (count).v = other.page_at (b).v; + } + assert (!count); + resize (newCount); + } + + void union_ (const hb_bit_set_t &other) { process (hb_bitwise_or, other); } + void intersect (const hb_bit_set_t &other) { process (hb_bitwise_and, other); } + void subtract (const hb_bit_set_t &other) { process (hb_bitwise_gt, other); } + void symmetric_difference (const hb_bit_set_t &other) { process (hb_bitwise_xor, other); } + + bool next (hb_codepoint_t *codepoint) const + { + // TODO: this should be merged with prev() as both implementations + // are very similar. + if (unlikely (*codepoint == INVALID)) { + *codepoint = get_min (); + return *codepoint != INVALID; + } + + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (*codepoint); + unsigned int i = last_page_lookup; + + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i >= page_map.length) { + *codepoint = INVALID; + return false; + } + } + + const auto* pages_array = pages.arrayZ; + const page_map_t ¤t = page_map_array[i]; + if (likely (current.major == major)) + { + if (pages_array[current.index].next (codepoint)) + { + *codepoint += current.major * page_t::PAGE_BITS; + last_page_lookup = i; + return true; + } + i++; + } + + for (; i < page_map.length; i++) + { + const page_map_t ¤t = page_map.arrayZ[i]; + hb_codepoint_t m = pages_array[current.index].get_min (); + if (m != INVALID) + { + *codepoint = current.major * page_t::PAGE_BITS + m; + last_page_lookup = i; + return true; + } + } + last_page_lookup = 0; + *codepoint = INVALID; + return false; + } + bool previous (hb_codepoint_t *codepoint) const + { + if (unlikely (*codepoint == INVALID)) { + *codepoint = get_max (); + return *codepoint != INVALID; + } + + page_map_t map = {get_major (*codepoint), 0}; + unsigned int i; + page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i < page_map.length && page_map[i].major == map.major) + { + if (pages[page_map[i].index].previous (codepoint)) + { + *codepoint += page_map[i].major * page_t::PAGE_BITS; + return true; + } + } + i--; + for (; (int) i >= 0; i--) + { + hb_codepoint_t m = pages[page_map[i].index].get_max (); + if (m != INVALID) + { + *codepoint = page_map[i].major * page_t::PAGE_BITS + m; + return true; + } + } + *codepoint = INVALID; + return false; + } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *last; + if (!next (&i)) + { + *last = *first = INVALID; + return false; + } + + /* TODO Speed up. */ + *last = *first = i; + while (next (&i) && i == *last + 1) + (*last)++; + + return true; + } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *first; + if (!previous (&i)) + { + *last = *first = INVALID; + return false; + } + + /* TODO Speed up. */ + *last = *first = i; + while (previous (&i) && i == *first - 1) + (*first)--; + + return true; + } + + bool has_population () const { return population != UINT_MAX; } + unsigned int get_population () const + { + if (has_population ()) + return population; + + unsigned int pop = 0; + unsigned int count = pages.length; + for (unsigned int i = 0; i < count; i++) + pop += pages[i].get_population (); + + population = pop; + return pop; + } + hb_codepoint_t get_min () const + { + unsigned count = pages.length; + for (unsigned i = 0; i < count; i++) + { + const auto& map = page_map[i]; + const auto& page = pages[map.index]; + + if (!page.is_empty ()) + return map.major * page_t::PAGE_BITS + page.get_min (); + } + return INVALID; + } + hb_codepoint_t get_max () const + { + unsigned count = pages.length; + for (signed i = count - 1; i >= 0; i--) + { + const auto& map = page_map[(unsigned) i]; + const auto& page = pages[map.index]; + + if (!page.is_empty ()) + return map.major * page_t::PAGE_BITS + page.get_max (); + } + return INVALID; + } + + static constexpr hb_codepoint_t INVALID = page_t::INVALID; + + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_bit_set_t &s_ = Null (hb_bit_set_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } + + protected: + const hb_bit_set_t *s; + hb_codepoint_t v; + unsigned l; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } + + protected: + + page_t *page_for (hb_codepoint_t g, bool insert = false) + { + page_map_t map = {get_major (g), pages.length}; + unsigned int i; + if (!page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST)) + { + if (!insert) + return nullptr; + + if (unlikely (!resize (pages.length + 1))) + return nullptr; + + pages[map.index].init0 (); + memmove (page_map + i + 1, + page_map + i, + (page_map.length - 1 - i) * page_map.item_size); + page_map[i] = map; + } + return &pages[page_map[i].index]; + } + const page_t *page_for (hb_codepoint_t g) const + { + page_map_t key = {get_major (g)}; + const page_map_t *found = page_map.bsearch (key); + if (found) + return &pages[found->index]; + return nullptr; + } + page_t &page_at (unsigned int i) { return pages[page_map[i].index]; } + const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; } + unsigned int get_major (hb_codepoint_t g) const { return g / page_t::PAGE_BITS; } + hb_codepoint_t major_start (unsigned int major) const { return major * page_t::PAGE_BITS; } +}; + + +#endif /* HB_BIT_SET_HH */ diff --git a/gfx/harfbuzz/src/hb-blob.cc b/gfx/harfbuzz/src/hb-blob.cc index 71b1b1fc4f86..f120002d17b4 100644 --- a/gfx/harfbuzz/src/hb-blob.cc +++ b/gfx/harfbuzz/src/hb-blob.cc @@ -72,16 +72,54 @@ hb_blob_create (const char *data, void *user_data, hb_destroy_func_t destroy) { - hb_blob_t *blob; - - if (!length || - length >= 1u << 31 || - !(blob = hb_object_create ())) { + if (!length) + { if (destroy) destroy (user_data); return hb_blob_get_empty (); } + hb_blob_t *blob = hb_blob_create_or_fail (data, length, mode, + user_data, destroy); + return likely (blob) ? blob : hb_blob_get_empty (); +} + +/** + * hb_blob_create_or_fail: (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: (nullable): 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. + * + * Note that this function returns a freshly-allocated empty blob even if @length + * is zero. This is in contrast to hb_blob_create(), which returns the singleton + * empty blob (as returned by hb_blob_get_empty()) if @length is zero. + * + * Return value: New blob, or %NULL if failed. Destroy with hb_blob_destroy(). + * + * Since: 2.8.2 + **/ +hb_blob_t * +hb_blob_create_or_fail (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 >= 1u << 31 || + !(blob = hb_object_create ())) + { + if (destroy) + destroy (user_data); + return nullptr; + } + blob->data = data; blob->length = length; blob->mode = mode; @@ -91,9 +129,10 @@ hb_blob_create (const char *data, if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { blob->mode = HB_MEMORY_MODE_READONLY; - if (!blob->try_make_writable ()) { + if (!blob->try_make_writable ()) + { hb_blob_destroy (blob); - return hb_blob_get_empty (); + return nullptr; } } @@ -226,7 +265,7 @@ hb_blob_destroy (hb_blob_t *blob) blob->fini_shallow (); - free (blob); + hb_free (blob); } /** @@ -452,7 +491,7 @@ hb_blob_t::try_make_writable () char *new_data; - new_data = (char *) malloc (this->length); + new_data = (char *) hb_malloc (this->length); if (unlikely (!new_data)) return false; @@ -463,7 +502,7 @@ hb_blob_t::try_make_writable () this->mode = HB_MEMORY_MODE_WRITABLE; this->data = new_data; this->user_data = new_data; - this->destroy = free; + this->destroy = hb_free; return true; } @@ -517,7 +556,7 @@ _hb_mapped_file_destroy (void *file_) assert (0); // If we don't have mmap we shouldn't reach here #endif - free (file); + hb_free (file); } #endif @@ -528,7 +567,7 @@ _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); + char *rsrc_name = (char *) hb_malloc (len); if (unlikely (!rsrc_name)) return -1; strncpy (rsrc_name, file_name, name_len); @@ -536,7 +575,7 @@ _open_resource_fork (const char *file_name, hb_mapped_file_t *file) sizeof (_PATH_RSRCFORKSPEC) - 1); int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0); - free (rsrc_name); + hb_free (rsrc_name); if (fd != -1) { @@ -561,17 +600,37 @@ _open_resource_fork (const char *file_name, hb_mapped_file_t *file) * Creates a new blob containing the data from the * specified binary font file. * - * Returns: An #hb_blob_t pointer with the content of the file + * Returns: An #hb_blob_t pointer with the content of the file, + * or hb_blob_get_empty() if failed. * * Since: 1.7.7 **/ hb_blob_t * hb_blob_create_from_file (const char *file_name) +{ + hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name); + return likely (blob) ? blob : hb_blob_get_empty (); +} + +/** + * hb_blob_create_from_file_or_fail: + * @file_name: A font filename + * + * Creates a new blob containing the data from the + * specified binary font file. + * + * Returns: An #hb_blob_t pointer with the content of the file, + * or %NULL if failed. + * + * Since: 2.8.2 + **/ +hb_blob_t * +hb_blob_create_from_file_or_fail (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)); + hb_mapped_file_t *file = (hb_mapped_file_t *) hb_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); @@ -601,22 +660,22 @@ hb_blob_create_from_file (const char *file_name) 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); + return hb_blob_create_or_fail (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); + hb_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)); + hb_mapped_file_t *file = (hb_mapped_file_t *) hb_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); + wchar_t * wchar_file_name = (wchar_t *) hb_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) @@ -636,7 +695,7 @@ fail_without_close: OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, nullptr); #endif - free (wchar_file_name); + hb_free (wchar_file_name); if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; @@ -661,22 +720,22 @@ fail_without_close: 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); + return hb_blob_create_or_fail (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); + hb_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 (); + char *data = (char *) hb_malloc (allocated); + if (unlikely (!data)) return nullptr; FILE *fp = fopen (file_name, "rb"); if (unlikely (!fp)) goto fread_fail_without_close; @@ -689,7 +748,7 @@ fail_without_close: /* 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); + char *new_data = (char *) hb_realloc (data, allocated); if (unlikely (!new_data)) goto fread_fail; data = new_data; } @@ -706,13 +765,13 @@ fail_without_close: } fclose (fp); - return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data, - (hb_destroy_func_t) free); + return hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, data, + (hb_destroy_func_t) hb_free); fread_fail: fclose (fp); fread_fail_without_close: - free (data); - return hb_blob_get_empty (); + hb_free (data); + return nullptr; } #endif /* !HB_NO_OPEN */ diff --git a/gfx/harfbuzz/src/hb-blob.h b/gfx/harfbuzz/src/hb-blob.h index 86f12788d20b..203f9e19ddfc 100644 --- a/gfx/harfbuzz/src/hb-blob.h +++ b/gfx/harfbuzz/src/hb-blob.h @@ -90,9 +90,19 @@ hb_blob_create (const char *data, void *user_data, hb_destroy_func_t destroy); +HB_EXTERN hb_blob_t * +hb_blob_create_or_fail (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); +HB_EXTERN hb_blob_t * +hb_blob_create_from_file_or_fail (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 diff --git a/gfx/harfbuzz/src/hb-blob.hh b/gfx/harfbuzz/src/hb-blob.hh index b03dfc138097..a3683a681e78 100644 --- a/gfx/harfbuzz/src/hb-blob.hh +++ b/gfx/harfbuzz/src/hb-blob.hh @@ -88,7 +88,7 @@ struct hb_blob_ptr_t 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; } + void destroy () { hb_blob_destroy (b.get_raw ()); b = nullptr; } private: hb_nonnull_ptr_t b; diff --git a/gfx/harfbuzz/src/hb-buffer.cc b/gfx/harfbuzz/src/hb-buffer.cc index 8cad6ab8e651..c6591ca2309f 100644 --- a/gfx/harfbuzz/src/hb-buffer.cc +++ b/gfx/harfbuzz/src/hb-buffer.cc @@ -96,14 +96,15 @@ hb_segment_properties_hash (const hb_segment_properties_t *p) * 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. + * In that case, swap_buffers() is mostly 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 + * 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. + * switches info over to out_info and does housekeeping. */ @@ -136,8 +137,8 @@ hb_buffer_t::enlarge (unsigned int size) 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])); + new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_allocated * sizeof (pos[0])); + new_info = (hb_glyph_info_t *) hb_realloc (info, new_allocated * sizeof (info[0])); done: if (unlikely (!new_pos || !new_info)) @@ -281,22 +282,13 @@ hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) } -void -hb_buffer_t::remove_output () -{ - have_output = false; - have_positions = false; - - out_len = 0; - out_info = info; -} - void hb_buffer_t::clear_output () { have_output = true; have_positions = false; + idx = 0; out_len = 0; out_info = info; } @@ -316,29 +308,23 @@ hb_buffer_t::clear_positions () void hb_buffer_t::swap_buffers () { - if (unlikely (!successful)) return; + assert (have_output); assert (idx <= len); - if (unlikely (!next_glyphs (len - idx))) return; - assert (have_output); - have_output = false; + if (unlikely (!successful || !next_glyphs (len - idx))) + goto reset; if (out_info != info) { - hb_glyph_info_t *tmp; - tmp = info; + pos = (hb_glyph_position_t *) info; info = out_info; - out_info = tmp; - - pos = (hb_glyph_position_t *) out_info; } - - unsigned int tmp; - tmp = len; len = out_len; - out_len = tmp; +reset: + have_output = false; + out_len = 0; idx = 0; } @@ -373,12 +359,11 @@ hb_buffer_t::move_to (unsigned int 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. + * We used to shift with extra 32 items. * 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; + * failures. 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 - idx))) return false; assert (idx >= count); @@ -630,7 +615,7 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) = HB_BUFFER_CONTENT_TYPE_INVALID, HB_SEGMENT_PROPERTIES_DEFAULT, false, /* successful */ - true, /* have_output */ + false, /* have_output */ true /* have_positions */ /* Zero is good enough for everything else. */ @@ -717,14 +702,14 @@ hb_buffer_destroy (hb_buffer_t *buffer) hb_unicode_funcs_destroy (buffer->unicode); - free (buffer->info); - free (buffer->pos); + hb_free (buffer->info); + hb_free (buffer->pos); #ifndef HB_NO_BUFFER_MESSAGE if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); #endif - free (buffer); + hb_free (buffer); } /** @@ -1363,6 +1348,11 @@ hb_buffer_get_glyph_infos (hb_buffer_t *buffer, * Returns @buffer glyph position array. Returned pointer * is valid as long as @buffer contents are not modified. * + * If buffer did not have positions before, the positions will be + * initialized to zeros, unless this function is called from + * within a buffer message callback (see hb_buffer_set_message_func()), + * in which case %NULL is returned. + * * Return value: (transfer none) (array length=length): * The @buffer glyph position array. * The value valid as long as buffer has not been modified. @@ -1373,12 +1363,17 @@ 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; + if (!buffer->have_positions) + { + if (unlikely (buffer->message_depth)) + return nullptr; + + buffer->clear_positions (); + } + return (hb_glyph_position_t *) buffer->pos; } @@ -1760,6 +1755,28 @@ hb_buffer_append (hb_buffer_t *buffer, 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])); + + if (source->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) + { + /* See similar logic in add_utf. */ + + /* pre-context */ + if (!orig_len && start + source->context_len[0] > 0) + { + buffer->clear_context (0); + while (start > 0 && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + buffer->context[0][buffer->context_len[0]++] = source->info[--start].codepoint; + for (auto i = 0u; i < source->context_len[0] && buffer->context_len[0] < buffer->CONTEXT_LENGTH; i++) + buffer->context[0][buffer->context_len[0]++] = source->context[0][i]; + } + + /* post-context */ + buffer->clear_context (1); + while (end < source->len && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + buffer->context[1][buffer->context_len[1]++] = source->info[end++].codepoint; + for (auto i = 0u; i < source->context_len[1] && buffer->context_len[1] < buffer->CONTEXT_LENGTH; i++) + buffer->context[1][buffer->context_len[1]++] = source->context[1][i]; + } } diff --git a/gfx/harfbuzz/src/hb-buffer.hh b/gfx/harfbuzz/src/hb-buffer.hh index 8b432b5f96f8..8635ebd35fd8 100644 --- a/gfx/harfbuzz/src/hb-buffer.hh +++ b/gfx/harfbuzz/src/hb-buffer.hh @@ -107,7 +107,7 @@ struct hb_buffer_t 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 out_len; /* Length of ->out_info array if have_output */ unsigned int allocated; /* Length of allocated arrays */ hb_glyph_info_t *info; @@ -128,6 +128,9 @@ struct hb_buffer_t hb_buffer_message_func_t message_func; void *message_data; hb_destroy_func_t message_destroy; + unsigned message_depth; /* How deeply are we inside a message callback? */ +#else + static constexpr unsigned message_depth = 0u; #endif /* Internal debugging. */ @@ -186,13 +189,10 @@ struct hb_buffer_t hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; } hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; } - HB_NODISCARD bool has_separate_output () const { return info != out_info; } - - HB_INTERNAL void reset (); HB_INTERNAL void clear (); - unsigned int backtrack_len () const { return have_output? out_len : idx; } + unsigned int backtrack_len () const { return have_output ? out_len : idx; } unsigned int lookahead_len () const { return len - idx; } unsigned int next_serial () { return serial++; } @@ -206,7 +206,6 @@ struct hb_buffer_t HB_INTERNAL void guess_segment_properties (); HB_INTERNAL void swap_buffers (); - HB_INTERNAL void remove_output (); HB_INTERNAL void clear_output (); HB_INTERNAL void clear_positions (); @@ -400,10 +399,16 @@ struct hb_buffer_t #else if (!messaging ()) return true; + + message_depth++; + va_list ap; va_start (ap, fmt); bool ret = message_impl (font, fmt, ap); va_end (ap); + + message_depth--; + return ret; #endif } diff --git a/gfx/harfbuzz/src/hb-cache.hh b/gfx/harfbuzz/src/hb-cache.hh index bf26d96be46b..e617b75de90a 100644 --- a/gfx/harfbuzz/src/hb-cache.hh +++ b/gfx/harfbuzz/src/hb-cache.hh @@ -30,7 +30,7 @@ #include "hb.hh" -/* Implements a lock-free cache for int->int functions. */ +/* Implements a lockfree cache for int->int functions. */ template struct hb_cache_t diff --git a/gfx/harfbuzz/src/hb-cff2-interp-cs.hh b/gfx/harfbuzz/src/hb-cff2-interp-cs.hh index 332ece31cd4f..d9615664471c 100644 --- a/gfx/harfbuzz/src/hb-cff2-interp-cs.hh +++ b/gfx/harfbuzz/src/hb-cff2-interp-cs.hh @@ -136,8 +136,8 @@ struct cff2_cs_interp_env_t : cs_interp_env_t if (unlikely (!scalars.resize (region_count))) set_error (); else - varStore->varStore.get_scalars (get_ivs (), coords, num_coords, - &scalars[0], region_count); + varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, + &scalars[0], region_count); } seen_blend = true; } diff --git a/gfx/harfbuzz/src/hb-common.cc b/gfx/harfbuzz/src/hb-common.cc index 7bb878b21701..cbba1a794210 100644 --- a/gfx/harfbuzz/src/hb-common.cc +++ b/gfx/harfbuzz/src/hb-common.cc @@ -257,13 +257,11 @@ struct hb_language_item_t { 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. - */ + hb_language_item_t & operator = (const char *s) + { + /* We can't call strdup(), because we allow custom allocators. */ size_t len = strlen(s) + 1; - lang = (hb_language_t) malloc(len); + lang = (hb_language_t) hb_malloc(len); if (likely (lang)) { memcpy((unsigned char *) lang, s, len); @@ -274,11 +272,11 @@ struct hb_language_item_t { return *this; } - void fini () { free ((void *) lang); } + void fini () { hb_free ((void *) lang); } }; -/* Thread-safe lock-free language list */ +/* Thread-safe lockfree language list */ static hb_atomic_ptr_t langs; @@ -294,7 +292,7 @@ retry: while (first_lang) { hb_language_item_t *next = first_lang->next; first_lang->fini (); - free (first_lang); + hb_free (first_lang); first_lang = next; } } @@ -311,21 +309,21 @@ retry: return lang; /* Not found; allocate one. */ - hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); + hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t)); if (unlikely (!lang)) return nullptr; lang->next = first_lang; *lang = key; if (unlikely (!lang->lang)) { - free (lang); + hb_free (lang); return nullptr; } if (unlikely (!langs.cmpexch (first_lang, lang))) { lang->fini (); - free (lang); + hb_free (lang); goto retry; } diff --git a/gfx/harfbuzz/src/hb-config.hh b/gfx/harfbuzz/src/hb-config.hh index 111058c8e6ed..ad800f0f74de 100644 --- a/gfx/harfbuzz/src/hb-config.hh +++ b/gfx/harfbuzz/src/hb-config.hh @@ -120,7 +120,7 @@ #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_LAYOUT_BLOCKLIST #define HB_NO_OT_SHAPE_FALLBACK #endif diff --git a/gfx/harfbuzz/src/hb-coretext.cc b/gfx/harfbuzz/src/hb-coretext.cc index 461bd20e6571..4b6c67c1ee25 100644 --- a/gfx/harfbuzz/src/hb-coretext.cc +++ b/gfx/harfbuzz/src/hb-coretext.cc @@ -332,6 +332,44 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font) return nullptr; } + if (font->coords) + { + CFMutableDictionaryRef variations = + CFDictionaryCreateMutable (kCFAllocatorDefault, + font->num_coords, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + for (unsigned i = 0; i < font->num_coords; i++) + { + if (font->coords[i] == 0.) continue; + + hb_ot_var_axis_info_t info; + unsigned int c = 1; + hb_ot_var_get_axis_infos (font->face, i, &c, &info); + CFDictionarySetValue (variations, + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag), + CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &font->design_coords[i]) + ); + } + + CFDictionaryRef attributes = + CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontVariationAttribute, + (const void **) &variations, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + CTFontDescriptorRef varDesc = CTFontDescriptorCreateWithAttributes (attributes); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0, nullptr, varDesc); + + CFRelease (ct_font); + CFRelease (attributes); + CFRelease (variations); + ct_font = new_ct_font; + } + return (hb_coretext_font_data_t *) ct_font; } @@ -1061,7 +1099,7 @@ resize_and_retry: 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; + hb_position_t x_offset = round ((positions[0].x - advances_so_far) * x_mult); for (unsigned int j = 0; j < num_glyphs; j++) { CGFloat advance; @@ -1069,15 +1107,15 @@ resize_and_retry: 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->mask = round (advance * x_mult); info->var1.i32 = x_offset; - info->var2.i32 = positions[j].y * y_mult; + info->var2.i32 = round (positions[j].y * y_mult); info++; } } else { - hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult; + hb_position_t y_offset = round ((positions[0].y - advances_so_far) * y_mult); for (unsigned int j = 0; j < num_glyphs; j++) { CGFloat advance; @@ -1085,8 +1123,8 @@ resize_and_retry: 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->mask = round (advance * y_mult); + info->var1.i32 = round (positions[j].x * x_mult); info->var2.i32 = y_offset; info++; } diff --git a/gfx/harfbuzz/src/hb-debug.hh b/gfx/harfbuzz/src/hb-debug.hh index a92614d01d4d..f80c8980ba88 100644 --- a/gfx/harfbuzz/src/hb-debug.hh +++ b/gfx/harfbuzz/src/hb-debug.hh @@ -307,7 +307,7 @@ struct hb_auto_trace_t _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1, "return %s (line %d)", - hb_printer_t().print (v), line); + hb_printer_t>().print (v), line); if (plevel) --*plevel; plevel = nullptr; returned = true; diff --git a/gfx/harfbuzz/src/hb-deprecated.h b/gfx/harfbuzz/src/hb-deprecated.h index 5f19125789dc..a130d77f7707 100644 --- a/gfx/harfbuzz/src/hb-deprecated.h +++ b/gfx/harfbuzz/src/hb-deprecated.h @@ -107,9 +107,6 @@ 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: * @ufuncs: A Unicode-functions structure diff --git a/gfx/harfbuzz/src/hb-directwrite.cc b/gfx/harfbuzz/src/hb-directwrite.cc index aebe9e054196..8f6d73b63967 100644 --- a/gfx/harfbuzz/src/hb-directwrite.cc +++ b/gfx/harfbuzz/src/hb-directwrite.cc @@ -32,6 +32,7 @@ #include "hb-directwrite.h" +#include "hb-ms-feature-ranges.hh" /** * SECTION:hb-directwrite @@ -42,24 +43,6 @@ * Functions for using HarfBuzz with DirectWrite fonts. **/ -/* 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 */ @@ -154,7 +137,6 @@ public: struct hb_directwrite_face_data_t { - HMODULE dwrite_dll; IDWriteFactory *dwriteFactory; IDWriteFontFile *fontFile; DWriteFontFileStream *fontFileStream; @@ -176,33 +158,12 @@ _hb_directwrite_shaper_face_data_create (hb_face_t *face) 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); + hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), + (IUnknown**) &dwriteFactory); if (unlikely (hr != S_OK)) FAIL ("Failed to run DWriteCreateFactory()."); @@ -266,8 +227,6 @@ _hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data) delete data->fontFileStream; if (data->faceBlob) hb_blob_destroy (data->faceBlob); - if (data->dwrite_dll) - FreeLibrary (data->dwrite_dll); if (data) delete data; } @@ -552,13 +511,12 @@ protected: * 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_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) { hb_face_t *face = font->face; const hb_directwrite_face_data_t *face_data = face->data.directwrite; @@ -611,8 +569,6 @@ _hb_directwrite_shape_full (hb_shape_plan_t *shape_plan, 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 : @@ -648,38 +604,54 @@ _hb_directwrite_shape_full (hb_shape_plan_t *shape_plan, 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; + /* + * Set up features. + */ + static_assert ((sizeof (DWRITE_TYPOGRAPHIC_FEATURES) == sizeof (hb_ms_features_t)), ""); + static_assert ((sizeof (DWRITE_FONT_FEATURE) == sizeof (hb_ms_feature_t)), ""); + hb_vector_t range_features; + hb_vector_t range_char_counts; 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; - } + hb_vector_t feature_records; + hb_vector_t range_records; + if (hb_ms_setup_features (features, num_features, feature_records, range_records)) + hb_ms_make_feature_ranges (feature_records, + range_records, + 0, + chars_len, + log_clusters, + range_features, + range_char_counts); } - 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); + hr = analyzer->GetGlyphs (textString, + chars_len, + fontFace, + false, + isRightToLeft, + &runHead->mScript, + localeName, + nullptr, + (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ, + range_char_counts.arrayZ, + range_features.length, + maxGlyphCount, + clusterMap, + textProperties, + glyphIndices, + glyphProperties, + &glyphCount); if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) { @@ -715,101 +687,28 @@ retry_getglyphs: 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); + hr = analyzer->GetGlyphPlacements (textString, + clusterMap, + textProperties, + chars_len, + glyphIndices, + glyphProperties, + glyphCount, + fontFace, + fontEmSize, + false, + isRightToLeft, + &runHead->mScript, + localeName, + (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ, + range_char_counts.arrayZ, + range_features.length, + 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! */ @@ -870,43 +769,10 @@ retry_getglyphs: 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; @@ -917,7 +783,7 @@ _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; + hb_free (context); } static hb_blob_t * @@ -938,7 +804,7 @@ _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void * return nullptr; } - _hb_directwrite_font_table_context *context = new _hb_directwrite_font_table_context; + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) hb_malloc (sizeof (_hb_directwrite_font_table_context)); context->face = dw_face; context->table_context = table_context; diff --git a/gfx/harfbuzz/src/hb-draw.cc b/gfx/harfbuzz/src/hb-draw.cc index 1a5f9c8c6bd1..c0af6ce0137b 100644 --- a/gfx/harfbuzz/src/hb-draw.cc +++ b/gfx/harfbuzz/src/hb-draw.cc @@ -191,7 +191,7 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *funcs) { if (!hb_object_destroy (funcs)) return; - free (funcs); + hb_free (funcs); } /** diff --git a/gfx/harfbuzz/src/hb-face.cc b/gfx/harfbuzz/src/hb-face.cc index 61bd4af7b1de..2c0087370c26 100644 --- a/gfx/harfbuzz/src/hb-face.cc +++ b/gfx/harfbuzz/src/hb-face.cc @@ -33,6 +33,7 @@ #include "hb-open-file.hh" #include "hb-ot-face.hh" #include "hb-ot-cmap-table.hh" +#include "hb-map.hh" /** @@ -106,9 +107,9 @@ DEFINE_NULL_INSTANCE (hb_face_t) = * convenient to provide data for individual tables instead of the whole font * data. With the caveat that hb_face_get_table_tags() does not currently work * with faces created this way. - * + * * Creates a new face object from the specified @user_data and @reference_table_func, - * with the @destroy callback. + * with the @destroy callback. * * Return value: (transfer full): The new face object * @@ -150,7 +151,7 @@ _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)); + closure = (hb_face_for_data_closure_t *) hb_calloc (1, sizeof (hb_face_for_data_closure_t)); if (unlikely (!closure)) return nullptr; @@ -166,7 +167,7 @@ _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); + hb_free (closure); } static hb_blob_t * @@ -265,7 +266,7 @@ hb_face_reference (hb_face_t *face) /** * hb_face_destroy: (skip) * @face: A face object - * + * * Decreases the reference count on a face object. When the * reference count reaches zero, the face is destroyed, * freeing all memory. @@ -281,7 +282,7 @@ hb_face_destroy (hb_face_t *face) { hb_face_t::plan_node_t *next = node->next; hb_shape_plan_destroy (node->shape_plan); - free (node); + hb_free (node); node = next; } @@ -291,7 +292,7 @@ hb_face_destroy (hb_face_t *face) if (face->destroy) face->destroy (face->user_data); - free (face); + hb_free (face); } /** @@ -302,7 +303,7 @@ hb_face_destroy (hb_face_t *face) * @destroy: (nullable): A callback to call when @data is not needed anymore * @replace: Whether to replace an existing data with the same key * - * Attaches a user-data key/data pair to the given face object. + * Attaches a user-data key/data pair to the given face object. * * Return value: %true if success, %false otherwise * @@ -441,7 +442,7 @@ hb_face_set_index (hb_face_t *face, * * Note: face indices within a collection are zero-based. * - * Return value: The index of @face. + * Return value: The index of @face. * * Since: 0.9.2 **/ @@ -623,26 +624,26 @@ hb_face_collect_variation_unicodes (hb_face_t *face, 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; + hb_hashmap_t tables; }; +static int compare_entries (const void* pa, const void* pb) +{ + const auto& a = * (const hb_pair_t *) pa; + const auto& b = * (const hb_pair_t *) pb; + + /* Order by blob size first (smallest to largest) and then table tag */ + + if (a.second->length != b.second->length) + return a.second->length < b.second->length ? -1 : +1; + + return a.first < b.first ? -1 : a.first == b.first ? 0 : +1; +} + 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)); + hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t)); if (unlikely (!data)) return nullptr; @@ -656,25 +657,25 @@ _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); + for (hb_blob_t* b : data->tables.values()) + hb_blob_destroy (b); data->tables.fini (); - free (data); + hb_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 table_count = data->tables.get_population (); 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)); + for (hb_blob_t* b : data->tables.values()) + face_length += hb_ceil_to_4 (hb_blob_get_length (b)); - char *buf = (char *) malloc (face_length); + char *buf = (char *) hb_malloc (face_length); if (unlikely (!buf)) return nullptr; @@ -682,20 +683,31 @@ _hb_face_builder_data_reference_blob (hb_face_builder_data_t *data) 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')); + bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' ')) + || data->tables.has (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 ()); + // Sort the tags so that produced face is deterministic. + hb_vector_t> sorted_entries; + data->tables.iter () | hb_sink (sorted_entries); + if (unlikely (sorted_entries.in_error ())) + { + hb_free (buf); + return nullptr; + } + + sorted_entries.qsort (compare_entries); + bool ret = f->serialize_single (&c, sfnt_tag, + sorted_entries.iter()); c.end_serialize (); if (unlikely (!ret)) { - free (buf); + hb_free (buf); return nullptr; } - return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, free); + return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free); } static hb_blob_t * @@ -706,11 +718,7 @@ _hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void 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; + return hb_blob_reference (data->tables[tag]); } @@ -750,17 +758,21 @@ hb_face_builder_create () hb_bool_t hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) { + if (tag == HB_MAP_VALUE_INVALID) + return false; + 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 (unlikely (data->tables.in_error())) + hb_blob_t* previous = data->tables.get (tag); + if (!data->tables.set (tag, hb_blob_reference (blob))) + { + hb_blob_destroy (blob); return false; + } - entry->tag = tag; - entry->blob = hb_blob_reference (blob); - + hb_blob_destroy (previous); return true; } diff --git a/gfx/harfbuzz/src/hb-font.cc b/gfx/harfbuzz/src/hb-font.cc index 37a0e7fe8542..fa8da9639516 100644 --- a/gfx/harfbuzz/src/hb-font.cc +++ b/gfx/harfbuzz/src/hb-font.cc @@ -620,7 +620,7 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT - free (ffuncs); + hb_free (ffuncs); } /** @@ -1544,8 +1544,8 @@ _hb_font_adopt_var_coords (hb_font_t *font, float *design_coords, unsigned int coords_length) { - free (font->coords); - free (font->design_coords); + hb_free (font->coords); + hb_free (font->design_coords); font->coords = coords; font->design_coords = design_coords; @@ -1586,8 +1586,8 @@ hb_font_create_sub_font (hb_font_t *parent) 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])); + int *coords = (int *) hb_calloc (num_coords, sizeof (parent->coords[0])); + float *design_coords = (float *) hb_calloc (num_coords, sizeof (parent->design_coords[0])); if (likely (coords && design_coords)) { memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); @@ -1596,8 +1596,8 @@ hb_font_create_sub_font (hb_font_t *parent) } else { - free (coords); - free (design_coords); + hb_free (coords); + hb_free (design_coords); } } @@ -1659,10 +1659,10 @@ hb_font_destroy (hb_font_t *font) hb_face_destroy (font->face); hb_font_funcs_destroy (font->klass); - free (font->coords); - free (font->design_coords); + hb_free (font->coords); + hb_free (font->design_coords); - free (font); + hb_free (font); } /** @@ -2052,29 +2052,30 @@ hb_font_set_variations (hb_font_t *font, return; } - unsigned int coords_length = hb_ot_var_get_axis_count (font->face); + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + const unsigned coords_length = axes.length; - int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; if (unlikely (coords_length && !(normalized && design_coords))) { - free (normalized); - free (design_coords); + hb_free (normalized); + hb_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); - } + const auto tag = variations[i].tag; + const auto v = variations[i].value; + for (unsigned axis_index = 0; axis_index < coords_length; axis_index++) + if (axes[axis_index].axisTag == tag) + { + design_coords[axis_index] = v; + normalized[axis_index] = fvar.normalize_axis_value (axis_index, v); + } } font->face->table.avar->map_coords (normalized, coords_length); @@ -2100,13 +2101,13 @@ hb_font_set_var_coords_design (hb_font_t *font, 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; + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; if (unlikely (coords_length && !(normalized && design_coords))) { - free (normalized); - free (design_coords); + hb_free (normalized); + hb_free (design_coords); return; } @@ -2135,13 +2136,13 @@ hb_font_set_var_named_instance (hb_font_t *font, 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; + float *coords = coords_length ? (float *) hb_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_free (coords); } /** @@ -2165,15 +2166,15 @@ hb_font_set_var_coords_normalized (hb_font_t *font, 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; + int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; + int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr; if (unlikely (coords_length && !(copy && unmapped && design_coords))) { - free (copy); - free (unmapped); - free (design_coords); + hb_free (copy); + hb_free (unmapped); + hb_free (design_coords); return; } @@ -2187,7 +2188,7 @@ hb_font_set_var_coords_normalized (hb_font_t *font, 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_free (unmapped); _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); } @@ -2267,7 +2268,7 @@ trampoline_create (FuncType func, { typedef hb_trampoline_t trampoline_t; - trampoline_t *trampoline = (trampoline_t *) calloc (1, sizeof (trampoline_t)); + trampoline_t *trampoline = (trampoline_t *) hb_calloc (1, sizeof (trampoline_t)); if (unlikely (!trampoline)) return nullptr; @@ -2296,7 +2297,7 @@ trampoline_destroy (void *user_data) if (closure->destroy) closure->destroy (closure->user_data); - free (closure); + hb_free (closure); } typedef hb_trampoline_t hb_font_get_glyph_trampoline_t; diff --git a/gfx/harfbuzz/src/hb-ft.cc b/gfx/harfbuzz/src/hb-ft.cc index b82c1a67bd0e..a6beb9f0fe1c 100644 --- a/gfx/harfbuzz/src/hb-ft.cc +++ b/gfx/harfbuzz/src/hb-ft.cc @@ -91,7 +91,7 @@ struct hb_ft_font_t 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)); + hb_ft_font_t *ft_font = (hb_ft_font_t *) hb_calloc (1, sizeof (hb_ft_font_t)); if (unlikely (!ft_font)) return nullptr; ft_font->lock.init (); @@ -125,7 +125,7 @@ _hb_ft_font_destroy (void *data) ft_font->lock.fini (); - free (ft_font); + hb_free (ft_font); } /** @@ -642,20 +642,20 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data if (error) return nullptr; - buffer = (FT_Byte *) malloc (length); + buffer = (FT_Byte *) hb_malloc (length); if (!buffer) return nullptr; error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); if (error) { - free (buffer); + hb_free (buffer); return nullptr; } return hb_blob_create ((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, - buffer, free); + buffer, hb_free); } /** @@ -846,8 +846,8 @@ hb_ft_font_changed (hb_font_t *font) 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)); + FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (mm_var->num_axis, sizeof (FT_Fixed)); + int *coords = (int *) hb_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)) @@ -866,12 +866,12 @@ hb_ft_font_changed (hb_font_t *font) hb_font_set_var_coords_normalized (font, nullptr, 0); } } - free (coords); - free (ft_coords); + hb_free (coords); + hb_free (ft_coords); #ifdef HAVE_FT_DONE_MM_VAR FT_Done_MM_Var (ft_face->glyph->library, mm_var); #else - free (mm_var); + hb_free (mm_var); #endif } #endif @@ -1020,13 +1020,13 @@ hb_ft_font_set_funcs (hb_font_t *font) 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)); + FT_Fixed *ft_coords = (FT_Fixed *) hb_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); + hb_free (ft_coords); } } #endif diff --git a/gfx/harfbuzz/src/hb-gdi.cc b/gfx/harfbuzz/src/hb-gdi.cc index dc4659c7f626..8e7589beacb9 100644 --- a/gfx/harfbuzz/src/hb-gdi.cc +++ b/gfx/harfbuzz/src/hb-gdi.cc @@ -50,16 +50,16 @@ _hb_gdi_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_dat length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc; - buffer = (char *) malloc (length); + buffer = (char *) hb_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); + return hb_blob_create ((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, buffer, hb_free); fail_with_releasedc_and_free: - free (buffer); + hb_free (buffer); fail_with_releasedc: ReleaseDC (nullptr, hdc); fail: diff --git a/gfx/harfbuzz/src/hb-gobject-structs.cc b/gfx/harfbuzz/src/hb-gobject-structs.cc index 7c46e2640076..540b11f911d2 100644 --- a/gfx/harfbuzz/src/hb-gobject-structs.cc +++ b/gfx/harfbuzz/src/hb-gobject-structs.cc @@ -80,12 +80,12 @@ hb_gobject_##name##_get_type () \ #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)); \ + hb_##name##_t *c = (hb_##name##_t *) hb_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); } \ + static void _hb_##name##_destroy (hb_##name##_t *l) { hb_free (l); } \ HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy) HB_DEFINE_OBJECT_TYPE (buffer) diff --git a/gfx/harfbuzz/src/hb-graphite2.cc b/gfx/harfbuzz/src/hb-graphite2.cc index 9dafe654c87e..209207f1e513 100644 --- a/gfx/harfbuzz/src/hb-graphite2.cc +++ b/gfx/harfbuzz/src/hb-graphite2.cc @@ -88,7 +88,7 @@ static const void *hb_graphite2_get_table (const void *data, unsigned int tag, s { blob = face_data->face->reference_table (tag); - hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t)); + hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) hb_calloc (1, sizeof (hb_graphite2_tablelist_t)); if (unlikely (!p)) { hb_blob_destroy (blob); return nullptr; @@ -123,15 +123,16 @@ _hb_graphite2_shaper_face_data_create (hb_face_t *face) } hb_blob_destroy (silf_blob); - hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) calloc (1, sizeof (hb_graphite2_face_data_t)); + hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) hb_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); + const gr_face_ops ops = {sizeof(gr_face_ops), &hb_graphite2_get_table, NULL}; + data->grface = gr_make_face_with_ops (data, &ops, gr_face_preloadAll); if (unlikely (!data->grface)) { - free (data); + hb_free (data); return nullptr; } @@ -148,12 +149,12 @@ _hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data) hb_graphite2_tablelist_t *old = tlist; hb_blob_destroy (tlist->blob); tlist = tlist->next; - free (old); + hb_free (old); } gr_face_destroy (data->grface); - free (data); + hb_free (data); } /** diff --git a/gfx/harfbuzz/src/hb-iter.hh b/gfx/harfbuzz/src/hb-iter.hh index f7018150e472..b8c447263645 100644 --- a/gfx/harfbuzz/src/hb-iter.hh +++ b/gfx/harfbuzz/src/hb-iter.hh @@ -46,7 +46,7 @@ * TODO Document more. * * If iterator implementation implements operator!=, then can be - * used in range-based for loop. That comes free if the iterator + * used in range-based for loop. That already happens 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 diff --git a/gfx/harfbuzz/src/hb-machinery.hh b/gfx/harfbuzz/src/hb-machinery.hh index 3bd5a979b05c..010c2570d7a4 100644 --- a/gfx/harfbuzz/src/hb-machinery.hh +++ b/gfx/harfbuzz/src/hb-machinery.hh @@ -242,14 +242,14 @@ struct hb_lazy_loader_t : hb_data_wrapper_t static const Stored* get_null () { return &Null (Stored); } static Stored *create (Data *data) { - Stored *p = (Stored *) calloc (1, sizeof (Stored)); + Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); if (likely (p)) p->init (data); return p; } static Stored *create () { - Stored *p = (Stored *) calloc (1, sizeof (Stored)); + Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); if (likely (p)) p->init (); return p; @@ -257,7 +257,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t static void destroy (Stored *p) { p->fini (); - free (p); + hb_free (p); } // private: diff --git a/gfx/harfbuzz/src/hb-map.cc b/gfx/harfbuzz/src/hb-map.cc index f115da2bb876..9f1ac42846da 100644 --- a/gfx/harfbuzz/src/hb-map.cc +++ b/gfx/harfbuzz/src/hb-map.cc @@ -109,7 +109,7 @@ hb_map_destroy (hb_map_t *map) map->fini_shallow (); - free (map); + hb_free (map); } /** @@ -188,6 +188,7 @@ hb_map_set (hb_map_t *map, hb_codepoint_t key, hb_codepoint_t value) { + /* Immutable-safe. */ map->set (key, value); } @@ -220,6 +221,7 @@ void hb_map_del (hb_map_t *map, hb_codepoint_t key) { + /* Immutable-safe. */ map->del (key); } @@ -253,9 +255,6 @@ hb_map_has (const hb_map_t *map, void hb_map_clear (hb_map_t *map) { - if (unlikely (hb_object_is_immutable (map))) - return; - return map->clear (); } diff --git a/gfx/harfbuzz/src/hb-map.hh b/gfx/harfbuzz/src/hb-map.hh index ab9c17eb1c7a..dcd5267d748a 100644 --- a/gfx/harfbuzz/src/hb-map.hh +++ b/gfx/harfbuzz/src/hb-map.hh @@ -85,7 +85,7 @@ struct hb_hashmap_t } void fini_shallow () { - free (items); + hb_free (items); items = nullptr; population = occupancy = 0; } @@ -109,7 +109,7 @@ struct hb_hashmap_t 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)); + item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t)); if (unlikely (!new_items)) { successful = false; @@ -135,7 +135,7 @@ struct hb_hashmap_t old_items[i].hash, old_items[i].value); - free (old_items); + hb_free (old_items); return true; } @@ -169,6 +169,8 @@ struct hb_hashmap_t void clear () { + if (unlikely (!successful)) return; + if (items) for (auto &_ : hb_iter (items, mask + 1)) _.clear (); @@ -224,7 +226,7 @@ struct hb_hashmap_t if (!items[i].is_unused ()) { occupancy--; - if (items[i].is_tombstone ()) + if (!items[i].is_tombstone ()) population--; } diff --git a/gfx/harfbuzz/src/hb-meta.hh b/gfx/harfbuzz/src/hb-meta.hh index e40d9fd178c2..a714bc2bf027 100644 --- a/gfx/harfbuzz/src/hb-meta.hh +++ b/gfx/harfbuzz/src/hb-meta.hh @@ -101,14 +101,14 @@ 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 struct hb_match_const : hb_type_identity_t, hb_false_type {}; +template struct hb_match_const : hb_type_identity_t, hb_true_type {}; 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 struct hb_match_reference : hb_type_identity_t, hb_false_type {}; +template struct hb_match_reference : hb_type_identity_t, hb_true_type {}; +template struct hb_match_reference : hb_type_identity_t, hb_true_type {}; 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; @@ -117,8 +117,8 @@ template auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_t 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 struct hb_match_pointer : hb_type_identity_t, hb_false_type {}; +template struct hb_match_pointer : hb_type_identity_t, hb_true_type {}; 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; @@ -259,15 +259,15 @@ using hb_is_arithmetic = hb_bool_constant< #define hb_is_arithmetic(T) hb_is_arithmetic::value -template -using hb_is_signed = hb_conditional, - hb_false_type>; +template struct hb_is_signed_; +template struct hb_is_signed_ : hb_false_type {}; +template struct hb_is_signed_ : hb_bool_constant<(T) -1 < (T) 0> {}; +template struct hb_is_signed : hb_is_signed_ {}; #define hb_is_signed(T) hb_is_signed::value -template -using hb_is_unsigned = hb_conditional, - hb_false_type>; +template struct hb_is_unsigned_; +template struct hb_is_unsigned_ : hb_false_type {}; +template struct hb_is_unsigned_ : hb_bool_constant<(T) 0 < (T) -1> {}; +template struct hb_is_unsigned : hb_is_unsigned_ {}; #define hb_is_unsigned(T) hb_is_unsigned::value template struct hb_int_min; @@ -282,6 +282,7 @@ template <> struct hb_int_min : hb_integral_constant 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 {}; diff --git a/gfx/harfbuzz/src/hb-ms-feature-ranges.cc b/gfx/harfbuzz/src/hb-ms-feature-ranges.cc new file mode 100644 index 000000000000..6d09b252d84a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ms-feature-ranges.cc @@ -0,0 +1,177 @@ +/* + * Copyright © 2011,2012,2013 Google, Inc. + * Copyright © 2021 Khaled Hosny + * + * 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-ms-feature-ranges.hh" + +bool +hb_ms_setup_features (const hb_feature_t *features, + unsigned int num_features, + hb_vector_t &feature_records, /* OUT */ + hb_vector_t &range_records /* OUT */) +{ + feature_records.shrink(0); + range_records.shrink(0); + + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + hb_ms_active_feature_t feature; + feature.fea.tag_le = hb_uint32_swap (features[i].tag); + feature.fea.value = features[i].value; + feature.order = i; + + hb_ms_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. */ + { + hb_ms_active_feature_t feature; + feature.fea.tag_le = 0; + feature.fea.value = 0; + feature.order = num_features + 1; + + auto *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++) + { + auto *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + auto *range = range_records.push (); + auto offset = feature_records.length; + + active_features.qsort (); + for (unsigned int j = 0; j < active_features.length; j++) + { + if (!j || active_features[j].fea.tag_le != feature_records[feature_records.length - 1].tag_le) + { + feature_records.push (active_features[j].fea); + } + else + { + /* Overrides value for existing feature. */ + feature_records[feature_records.length - 1].value = active_features[j].fea.value; + } + } + + /* Will convert to pointer after all is ready, since feature_records.array + * may move as we grow it. */ + range->features.features = reinterpret_cast (offset); + range->features.num_features = feature_records.length - offset; + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } + else + { + auto *feature = active_features.find (&event->feature); + if (feature) + active_features.remove (feature - active_features.arrayZ); + } + } + + if (!range_records.length) /* No active feature found. */ + num_features = 0; + + /* Fixup the pointers. */ + for (unsigned int i = 0; i < range_records.length; i++) + { + auto *range = &range_records[i]; + range->features.features = (hb_ms_feature_t *) feature_records + reinterpret_cast (range->features.features); + } + + return !!num_features; +} + +void +hb_ms_make_feature_ranges (hb_vector_t &feature_records, + hb_vector_t &range_records, + unsigned int chars_offset, + unsigned int chars_len, + uint16_t *log_clusters, + hb_vector_t &range_features, /* OUT */ + hb_vector_t &range_counts /* OUT */) +{ + range_features.shrink (0); + range_counts.shrink (0); + + auto *last_range = &range_records[0]; + for (unsigned int i = chars_offset; i < chars_len; i++) + { + auto *range = last_range; + while (log_clusters[i] < range->index_first) + range--; + while (log_clusters[i] > range->index_last) + range++; + if (!range_features.length || + &range->features != range_features[range_features.length - 1]) + { + auto **features = range_features.push (); + auto *c = range_counts.push (); + if (unlikely (!features || !c)) + { + range_features.shrink (0); + range_counts.shrink (0); + break; + } + *features = &range->features; + *c = 1; + } + else + { + range_counts[range_counts.length - 1]++; + } + + last_range = range; + } +} diff --git a/gfx/harfbuzz/src/hb-ms-feature-ranges.hh b/gfx/harfbuzz/src/hb-ms-feature-ranges.hh new file mode 100644 index 000000000000..401d1e1d9788 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ms-feature-ranges.hh @@ -0,0 +1,96 @@ +/* + * Copyright © 2011,2012,2013 Google, Inc. + * Copyright © 2021 Khaled Hosny + * + * 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_MS_FEATURE_RANGES_HH +#define HB_MS_FEATURE_RANGES_HH + +#include "hb.hh" + +typedef struct hb_ms_feature_t { + uint32_t tag_le; + uint32_t value; +} hb_ms_feature_t; + +typedef struct hb_ms_features_t { + hb_ms_feature_t *features; + uint32_t num_features; +} hb_ms_features_t; + +struct hb_ms_active_feature_t { + hb_ms_feature_t fea; + unsigned int order; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const auto *a = (const hb_ms_active_feature_t *) pa; + const auto *b = (const hb_ms_active_feature_t *) pb; + return a->fea.tag_le < b->fea.tag_le ? -1 : a->fea.tag_le > b->fea.tag_le ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->fea.value < b->fea.value ? -1 : a->fea.value > b->fea.value ? 1 : + 0; + } + bool operator== (const hb_ms_active_feature_t *f) + { return cmp (this, f) == 0; } +}; + +struct hb_ms_feature_event_t { + unsigned int index; + bool start; + hb_ms_active_feature_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const auto *a = (const hb_ms_feature_event_t *) pa; + const auto *b = (const hb_ms_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 : + hb_ms_active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct hb_ms_range_record_t { + hb_ms_features_t features; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + +HB_INTERNAL bool +hb_ms_setup_features (const hb_feature_t *features, + unsigned int num_features, + hb_vector_t &feature_records, /* OUT */ + hb_vector_t &range_records /* OUT */); + + +HB_INTERNAL void +hb_ms_make_feature_ranges (hb_vector_t &feature_records, + hb_vector_t &range_records, + unsigned int chars_offset, + unsigned int chars_len, + uint16_t *log_clusters, + hb_vector_t &range_features, /* OUT */ + hb_vector_t &range_counts /* OUT */); + +#endif /* HB_MS_FEATURE_RANGES_HH */ diff --git a/gfx/harfbuzz/src/hb-mutex.hh b/gfx/harfbuzz/src/hb-mutex.hh index 2fc8d7ee58d3..a9227a741d8e 100644 --- a/gfx/harfbuzz/src/hb-mutex.hh +++ b/gfx/harfbuzz/src/hb-mutex.hh @@ -39,8 +39,7 @@ /* We need external help for these */ -#if defined(HB_MUTEX_IMPL_INIT) \ - && defined(hb_mutex_impl_init) \ +#if defined(hb_mutex_impl_init) \ && defined(hb_mutex_impl_lock) \ && defined(hb_mutex_impl_unlock) \ && defined(hb_mutex_impl_finish) @@ -52,7 +51,6 @@ #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) @@ -62,7 +60,6 @@ typedef pthread_mutex_t hb_mutex_impl_t; #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 @@ -76,7 +73,6 @@ typedef CRITICAL_SECTION hb_mutex_impl_t; #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 @@ -91,8 +87,6 @@ typedef int hb_mutex_impl_t; #endif -#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT} - struct hb_mutex_t { hb_mutex_impl_t m; diff --git a/gfx/harfbuzz/src/hb-object.hh b/gfx/harfbuzz/src/hb-object.hh index f3048b1c3ef9..0e15cb12c410 100644 --- a/gfx/harfbuzz/src/hb-object.hh +++ b/gfx/harfbuzz/src/hb-object.hh @@ -140,8 +140,6 @@ struct hb_lockable_set_t * Reference-count. */ -#define HB_REFERENCE_COUNT_INIT {0} - struct hb_reference_count_t { mutable hb_atomic_int_t ref_count; @@ -197,6 +195,8 @@ struct hb_object_header_t hb_reference_count_t ref_count; mutable hb_atomic_int_t writable = 0; hb_atomic_ptr_t user_data; + + bool is_inert () const { return !ref_count.get_relaxed (); } }; #define HB_OBJECT_HEADER_STATIC {} @@ -217,7 +217,7 @@ static inline void hb_object_trace (const Type *obj, const char *function) template static inline Type *hb_object_create () { - Type *obj = (Type *) calloc (1, sizeof (Type)); + Type *obj = (Type *) hb_calloc (1, sizeof (Type)); if (unlikely (!obj)) return obj; @@ -234,11 +234,6 @@ static inline void hb_object_init (Type *obj) 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 ()); @@ -257,7 +252,7 @@ template static inline Type *hb_object_reference (Type *obj) { hb_object_trace (obj, HB_FUNC); - if (unlikely (!obj || hb_object_is_inert (obj))) + if (unlikely (!obj || obj->header.is_inert ())) return obj; assert (hb_object_is_valid (obj)); obj->header.ref_count.inc (); @@ -267,7 +262,7 @@ template static inline bool hb_object_destroy (Type *obj) { hb_object_trace (obj, HB_FUNC); - if (unlikely (!obj || hb_object_is_inert (obj))) + if (unlikely (!obj || obj->header.is_inert ())) return false; assert (hb_object_is_valid (obj)); if (obj->header.ref_count.dec () != 1) @@ -284,7 +279,7 @@ static inline void hb_object_fini (Type *obj) if (user_data) { user_data->fini (); - free (user_data); + hb_free (user_data); user_data = nullptr; } } @@ -295,7 +290,7 @@ static inline bool hb_object_set_user_data (Type *obj, hb_destroy_func_t destroy, hb_bool_t replace) { - if (unlikely (!obj || hb_object_is_inert (obj))) + if (unlikely (!obj || obj->header.is_inert ())) return false; assert (hb_object_is_valid (obj)); @@ -303,14 +298,14 @@ 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); + user_data = (hb_user_data_array_t *) hb_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); + hb_free (user_data); goto retry; } } @@ -322,7 +317,7 @@ 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))) + if (unlikely (!obj || obj->header.is_inert ())) return nullptr; assert (hb_object_is_valid (obj)); hb_user_data_array_t *user_data = obj->header.user_data.get (); diff --git a/gfx/harfbuzz/src/hb-open-file.hh b/gfx/harfbuzz/src/hb-open-file.hh index 66ca03076bac..6eee5827c18a 100644 --- a/gfx/harfbuzz/src/hb-open-file.hh +++ b/gfx/harfbuzz/src/hb-open-file.hh @@ -35,7 +35,6 @@ namespace OT { - /* * * The OpenType Font File @@ -102,7 +101,13 @@ typedef struct OpenTypeOffsetTable { Tag t; t = tag; - return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + /* Use lfind for small fonts; there are fonts that have unsorted table entries; + * those tend to work in other tools, so tolerate them. + * https://github.com/harfbuzz/harfbuzz/issues/3065 */ + if (tables.len < 16) + return tables.lfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + else + return tables.bfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); } const TableRecord& get_table_by_tag (hb_tag_t tag) const { @@ -113,44 +118,53 @@ typedef struct OpenTypeOffsetTable public: - template + template >::value))> bool serialize (hb_serialize_context_t *c, hb_tag_t sfnt_tag, - hb_array_t items) + Iterator it) { TRACE_SERIALIZE (this); /* Alloc 12 for the OTHeader. */ - if (unlikely (!c->extend_min (*this))) return_trace (false); + 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); + unsigned num_items = it.len (); + if (unlikely (!tables.serialize (c, num_items))) 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++) + unsigned i = 0; + for (hb_pair_t entry : it) { - 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); + hb_blob_t *blob = entry.second; + unsigned len = blob->length; /* Allocate room for the table and copy it. */ - char *start = (char *) c->allocate_size (rec.length); + char *start = (char *) c->allocate_size (len); if (unlikely (!start)) return false; - if (likely (rec.length)) - memcpy (start, blob->data, rec.length); + TableRecord &rec = tables.arrayZ[i]; + rec.tag = entry.first; + rec.length = len; + rec.offset = 0; + if (unlikely (!c->check_assign (rec.offset, + (unsigned) ((char *) start - (char *) this), + HB_SERIALIZE_ERROR_OFFSET_OVERFLOW))) + return_trace (false); + + if (likely (len)) + memcpy (start, blob->data, len); /* 4-byte alignment. */ c->align (4); const char *end = (const char *) c->head; - if (items[i].tag == HB_OT_TAG_head && + if (entry.first == HB_OT_TAG_head && (unsigned) (end - start) >= head::static_size) { head *h = (head *) start; @@ -159,6 +173,7 @@ typedef struct OpenTypeOffsetTable } rec.checkSum.set_for_data (start, end - start); + i++; } tables.qsort (); @@ -170,7 +185,7 @@ typedef struct OpenTypeOffsetTable /* 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++) + for (unsigned int i = 0; i < num_items; i++) { TableRecord &rec = tables.arrayZ[i]; checksum = checksum + rec.checkSum; @@ -477,14 +492,15 @@ struct OpenTypeFontFile } } - template + template >::value))> bool serialize_single (hb_serialize_context_t *c, hb_tag_t sfnt_tag, - hb_array_t items) + Iterator items) { TRACE_SERIALIZE (this); assert (sfnt_tag != TTCTag); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); return_trace (u.fontFace.serialize (c, sfnt_tag, items)); } diff --git a/gfx/harfbuzz/src/hb-open-type.hh b/gfx/harfbuzz/src/hb-open-type.hh index 42eb8af05a9a..49653ce97e40 100644 --- a/gfx/harfbuzz/src/hb-open-type.hh +++ b/gfx/harfbuzz/src/hb-open-type.hh @@ -212,15 +212,6 @@ struct Offset : 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), - HB_SERIALIZE_ERROR_OFFSET_OVERFLOW); - return t; - } - public: DEFINE_SIZE_STATIC (sizeof (Type)); }; @@ -328,10 +319,6 @@ struct OffsetTo : Offset hb_enable_if (hb_is_convertible (Base, void *))> 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, @@ -355,6 +342,23 @@ struct OffsetTo : Offset return ret; } + + template + bool serialize_serialize (hb_serialize_context_t *c, Ts&&... ds) + { + *this = 0; + + Type* obj = c->push (); + bool ret = obj->serialize (c, hb_forward (ds)...); + + if (ret) + c->add_link (*this, c->pop_pack ()); + else + c->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&&... @@ -464,8 +468,10 @@ struct UnsizedArrayOf 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); } + bool lfind (unsigned int len, const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array (len).lfind (x, i, not_found, to_store); } void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) { as_array (len).qsort (start, end); } @@ -473,7 +479,7 @@ struct UnsizedArrayOf bool serialize (hb_serialize_context_t *c, unsigned int items_len) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend (*this, items_len))) return_trace (false); + if (unlikely (!c->extend (this, items_len))) return_trace (false); return_trace (true); } template { 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, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, unsigned int to_store = (unsigned int) -1) const { return as_array (len).bfind (x, i, not_found, to_store); } }; @@ -635,8 +641,10 @@ struct ArrayOf 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); } + bool lfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().lfind (x, i, not_found, to_store); } void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) { as_array ().qsort (start, end); } @@ -644,9 +652,9 @@ struct ArrayOf HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); c->check_assign (len, items_len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); - if (unlikely (!c->extend (*this))) return_trace (false); + if (unlikely (!c->extend (this))) return_trace (false); return_trace (true); } template extend (*this))) + if (unlikely (!len || !c->extend (this))) { len--; return_trace (nullptr); @@ -794,9 +802,9 @@ struct HeadlessArrayOf bool serialize (hb_serialize_context_t *c, unsigned int items_len) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); c->check_assign (lenP1, items_len + 1, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); - if (unlikely (!c->extend (*this))) return_trace (false); + if (unlikely (!c->extend (this))) return_trace (false); return_trace (true); } template { 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, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, unsigned int to_store = (unsigned int) -1) const { return as_array ().bfind (x, i, not_found, to_store); } }; diff --git a/gfx/harfbuzz/src/hb-ot-cff-common.hh b/gfx/harfbuzz/src/hb-ot-cff-common.hh index 864a27f45848..eaaf5e12ec53 100644 --- a/gfx/harfbuzz/src/hb-ot-cff-common.hh +++ b/gfx/harfbuzz/src/hb-ot-cff-common.hh @@ -126,7 +126,7 @@ struct CFFIndex else { /* serialize CFFIndex header */ - if (unlikely (!c->extend_min (*this))) return_trace (false); + 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)))) @@ -214,7 +214,7 @@ struct CFFIndex unsigned off_size = calcOffSize (total); /* serialize CFFIndex header */ - if (unlikely (!c->extend_min (*this))) return_trace (false); + 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)))) @@ -335,7 +335,7 @@ struct CFFIndexOf : CFFIndex { TRACE_SERIALIZE (this); /* serialize CFFIndex header */ - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); this->count = dataArrayLen; this->offSize = offSize_; if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1)))) diff --git a/gfx/harfbuzz/src/hb-ot-cff1-table.hh b/gfx/harfbuzz/src/hb-ot-cff1-table.hh index 157483b7f65b..5dd183e3a0ef 100644 --- a/gfx/harfbuzz/src/hb-ot-cff1-table.hh +++ b/gfx/harfbuzz/src/hb-ot-cff1-table.hh @@ -187,7 +187,7 @@ struct Encoding const hb_vector_t& supp_codes) { TRACE_SERIALIZE (this); - Encoding *dest = c->extend_min (*this); + Encoding *dest = c->extend_min (this); if (unlikely (!dest)) return_trace (false); dest->format = format | ((supp_codes.length > 0) ? 0x80 : 0); switch (format) { @@ -457,7 +457,7 @@ struct Charset const hb_vector_t& sid_ranges) { TRACE_SERIALIZE (this); - Charset *dest = c->extend_min (*this); + Charset *dest = c->extend_min (this); if (unlikely (!dest)) return_trace (false); dest->format = format; switch (format) @@ -713,6 +713,7 @@ struct cff1_top_dict_opset_t : top_dict_opset_t case OpCode_Notice: case OpCode_Copyright: case OpCode_FullName: + case OpCode_FontName: case OpCode_FamilyName: case OpCode_Weight: case OpCode_PostScript: diff --git a/gfx/harfbuzz/src/hb-ot-cmap-table.hh b/gfx/harfbuzz/src/hb-ot-cmap-table.hh index add21f11550a..b904bb46a822 100644 --- a/gfx/harfbuzz/src/hb-ot-cmap-table.hh +++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh @@ -49,6 +49,12 @@ struct CmapSubtableFormat0 *glyph = gid; return true; } + + unsigned get_language () const + { + return language; + } + void collect_unicodes (hb_set_t *out) const { for (unsigned int i = 0; i < 256; i++) @@ -212,29 +218,24 @@ struct CmapSubtableFormat4 HBINT16 *idDelta, unsigned segcount) { + hb_hashmap_t cp_to_gid; + + it | hb_sink (cp_to_gid); + 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); - }) - ; - - - }) - ; + for (unsigned i : + hb_range (segcount) + | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; })) + { + idRangeOffset[i] = 2 * (c->start_embed () - idRangeOffset - i); + for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++) + { + HBUINT16 gid; + gid = cp_to_gid[cp]; + c->copy (gid); + } + } return idRangeOffset; } @@ -253,7 +254,7 @@ struct CmapSubtableFormat4 if (format4_iter.len () == 0) return; unsigned table_initpos = c->length (); - if (unlikely (!c->extend_min (*this))) return; + if (unlikely (!c->extend_min (this))) return; this->format = 4; //serialize endCode[] @@ -276,9 +277,17 @@ struct CmapSubtableFormat4 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, - HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + this->length = c->length () - table_initpos; + if ((long long) this->length != (long long) c->length () - table_initpos) + { + // Length overflowed. Discard the current object before setting the error condition, otherwise + // discard is a noop which prevents the higher level code from reverting the serializer to the + // pre-error state in cmap4 overflow handling code. + c->pop_discard (); + c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW); + return; + } + this->segCountX2 = segcount * 2; this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; this->searchRange = 2 * (1u << this->entrySelector); @@ -287,6 +296,11 @@ struct CmapSubtableFormat4 : 0; } + unsigned get_language () const + { + return language; + } + struct accelerator_t { accelerator_t () {} @@ -549,6 +563,12 @@ struct CmapSubtableTrimmed *glyph = gid; return true; } + + unsigned get_language () const + { + return language; + } + void collect_unicodes (hb_set_t *out) const { hb_codepoint_t start = startCharCode; @@ -608,6 +628,11 @@ struct CmapSubtableLongSegmented return true; } + unsigned get_language () const + { + return language; + } + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const { for (unsigned int i = 0; i < this->groups.len; i++) @@ -693,7 +718,7 @@ struct CmapSubtableFormat12 : CmapSubtableLongSegmented { if (it.len () == 0) return; unsigned table_initpos = c->length (); - if (unlikely (!c->extend_min (*this))) return; + if (unlikely (!c->extend_min (this))) return; hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF; hb_codepoint_t glyphID = 0; @@ -1077,7 +1102,7 @@ struct CmapSubtableFormat14 unsigned table_initpos = c->length (); const char* init_tail = c->tail; - if (unlikely (!c->extend_min (*this))) return; + if (unlikely (!c->extend_min (this))) return; this->format = 14; auto src_tbl = reinterpret_cast (base); @@ -1238,6 +1263,20 @@ struct CmapSubtable } } + unsigned get_language () const + { + switch (u.format) { + case 0: return u.format0 .get_language (); + case 4: return u.format4 .get_language (); + case 6: return u.format6 .get_language (); + case 10: return u.format10.get_language (); + case 12: return u.format12.get_language (); + case 13: return u.format13.get_language (); + case 14: + default: return 0; + } + } + template void serialize (hb_serialize_context_t *c, @@ -1353,60 +1392,112 @@ struct cmap template - void serialize (hb_serialize_context_t *c, + bool serialize (hb_serialize_context_t *c, Iterator it, EncodingRecIter encodingrec_iter, const void *base, - const hb_subset_plan_t *plan) + const hb_subset_plan_t *plan, + bool drop_format_4 = false) { - if (unlikely (!c->extend_min ((*this)))) return; + if (unlikely (!c->extend_min ((*this)))) return false; this->version = 0; unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; + auto snap = c->snapshot (); for (const EncodingRecord& _ : encodingrec_iter) { + if (c->in_error ()) + return false; + unsigned format = (base+_.subtable).u.format; - if (!plan->glyphs_requested->is_empty ()) + if (format != 4 && format != 12 && format != 14) continue; + + hb_set_t unicodes_set; + (base+_.subtable).collect_unicodes (&unicodes_set); + + if (!drop_format_4 && format == 4) { - 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); + c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx); + if (c->in_error () && c->only_overflow ()) + { + // cmap4 overflowed, reset and retry serialization without format 4 subtables. + c->revert (snap); + return serialize (c, it, + encodingrec_iter, + base, + plan, + true); + } } - /* when --gids option is not used, we iterate input unicodes instead of - * all codepoints in each subtable, which is more efficient */ - else + + else if (format == 12) { - 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); + if (_can_drop (_, unicodes_set, base, + it | hb_map (hb_first), encodingrec_iter)) continue; + 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, HB_SERIALIZE_ERROR_INT_OVERFLOW); + + // Fail if format 4 was dropped and there is no cmap12. + return !drop_format_4 || format12objidx; + } + + template + bool _can_drop (const EncodingRecord& cmap12, + const hb_set_t& cmap12_unicodes, + const void* base, + Iterator subset_unicodes, + EncodingRecordIterator encoding_records) + { + for (auto cp : + subset_unicodes | hb_filter (cmap12_unicodes)) + { + if (cp >= 0x10000) return false; + } + + unsigned target_platform; + unsigned target_encoding; + unsigned target_language = (base+cmap12.subtable).get_language (); + + if (cmap12.platformID == 0 && cmap12.encodingID == 4) + { + target_platform = 0; + target_encoding = 3; + } else if (cmap12.platformID == 3 && cmap12.encodingID == 10) { + target_platform = 3; + target_encoding = 1; + } else { + return false; + } + + for (const auto& _ : encoding_records) + { + if (_.platformID != target_platform + || _.encodingID != target_encoding + || (base+_.subtable).get_language() != target_language) + continue; + + hb_set_t sibling_unicodes; + (base+_.subtable).collect_unicodes (&sibling_unicodes); + + auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes); + auto sibling = + subset_unicodes | hb_filter (sibling_unicodes); + for (; cmap12 && sibling; cmap12++, sibling++) + { + unsigned a = *cmap12; + unsigned b = *sibling; + if (a != b) return false; + } + + return !cmap12 && !sibling; + } + + return false; } void closure_glyphs (const hb_set_t *unicodes, @@ -1473,8 +1564,8 @@ struct cmap | 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); + + return_trace (cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan)); } const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const diff --git a/gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh b/gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh index 265d8309daae..6c31d1b53e73 100644 --- a/gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh +++ b/gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh @@ -738,7 +738,7 @@ struct CBLC cbdt_prime->length, HB_MEMORY_MODE_WRITABLE, cbdt_prime->arrayZ, - free); + hb_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); diff --git a/gfx/harfbuzz/src/hb-ot-color-colr-table.hh b/gfx/harfbuzz/src/hb-ot-color-colr-table.hh index c792147095aa..007ff3f47b8b 100644 --- a/gfx/harfbuzz/src/hb-ot-color-colr-table.hh +++ b/gfx/harfbuzz/src/hb-ot-color-colr-table.hh @@ -37,9 +37,79 @@ */ #define HB_OT_TAG_COLR HB_TAG('C','O','L','R') +#ifndef COLRV1_MAX_NESTING_LEVEL +#define COLRV1_MAX_NESTING_LEVEL 100 +#endif + +#ifndef COLRV1_ENABLE_SUBSETTING +#define COLRV1_ENABLE_SUBSETTING 0 +#endif namespace OT { +struct COLR; +struct hb_colrv1_closure_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) + { + if (unlikely (nesting_level_left == 0)) + return hb_empty_t (); + + if (paint_visited (&obj)) + return hb_empty_t (); + + nesting_level_left--; + obj.closurev1 (this); + nesting_level_left++; + return hb_empty_t (); + } + static return_t default_return_value () { return hb_empty_t (); } + + bool paint_visited (const void *paint) + { + hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) paint - (uintptr_t) base); + if (visited_paint.has (delta)) + return true; + + visited_paint.add (delta); + return false; + } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + + void add_glyph (unsigned glyph_id) + { glyphs->add (glyph_id); } + + void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers) + { layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); } + + void add_palette_index (unsigned palette_index) + { palette_indices->add (palette_index); } + + public: + const void *base; + hb_set_t visited_paint; + hb_set_t *glyphs; + hb_set_t *layer_indices; + hb_set_t *palette_indices; + unsigned nesting_level_left; + + hb_colrv1_closure_context_t (const void *base_, + hb_set_t *glyphs_, + hb_set_t *layer_indices_, + hb_set_t *palette_indices_, + unsigned nesting_level_left_ = COLRV1_MAX_NESTING_LEVEL) : + base (base_), + glyphs (glyphs_), + layer_indices (layer_indices_), + palette_indices (palette_indices_), + nesting_level_left (nesting_level_left_) + {} +}; + struct LayerRecord { operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; } @@ -125,6 +195,15 @@ struct NoVariable template class Var> struct ColorIndex { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -140,6 +219,13 @@ struct ColorIndex template class Var> struct ColorStop { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (unlikely (!c->serializer->embed (stopOffset))) return_trace (false); + return_trace (color.subset (c)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -166,6 +252,23 @@ struct Extend : HBUINT8 template class Var> struct ColorLine { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + if (!c->serializer->check_assign (out->stops.len, stops.len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)) return_trace (false); + + for (const auto& stop : stops.iter ()) + { + if (!stop.subset (c)) return_trace (false); + } + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -202,7 +305,7 @@ struct CompositeMode : HBUINT8 COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor COMPOSITE_PLUS = 12, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus - + // Blend modes // https://www.w3.org/TR/compositing-1/#blending COMPOSITE_SCREEN = 13, // https://www.w3.org/TR/compositing-1/#blendingscreen @@ -216,7 +319,7 @@ struct CompositeMode : HBUINT8 COMPOSITE_DIFFERENCE = 21, // https://www.w3.org/TR/compositing-1/#blendingdifference COMPOSITE_EXCLUSION = 22, // https://www.w3.org/TR/compositing-1/#blendingexclusion COMPOSITE_MULTIPLY = 23, // https://www.w3.org/TR/compositing-1/#blendingmultiply - + // Modes that, uniquely, do not operate on components // https://www.w3.org/TR/compositing-1/#blendingnonseparable COMPOSITE_HSL_HUE = 24, // https://www.w3.org/TR/compositing-1/#blendinghue @@ -249,6 +352,19 @@ struct Affine2x3 struct PaintColrLayers { + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers->get (firstLayerIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -265,6 +381,16 @@ struct PaintColrLayers template class Var> struct PaintSolid { + void closurev1 (hb_colrv1_closure_context_t* c) const + { c->add_palette_index (color.paletteIndex); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (unlikely (!c->serializer->embed (format))) return_trace (false); + return_trace (color.subset (c)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -280,6 +406,21 @@ struct PaintSolid template class Var> struct PaintLinearGradient { + void closurev1 (hb_colrv1_closure_context_t* c) const + { + for (const auto &stop : (this+colorLine).stops.iter ()) + c->add_palette_index (stop.color.paletteIndex); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->colorLine.serialize_subset (c, colorLine, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -302,6 +443,21 @@ struct PaintLinearGradient template class Var> struct PaintRadialGradient { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->colorLine.serialize_subset (c, colorLine, this)); + } + + void closurev1 (hb_colrv1_closure_context_t* c) const + { + for (const auto &stop : (this+colorLine).stops.iter ()) + c->add_palette_index (stop.color.paletteIndex); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -324,6 +480,21 @@ struct PaintRadialGradient template class Var> struct PaintSweepGradient { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->colorLine.serialize_subset (c, colorLine, this)); + } + + void closurev1 (hb_colrv1_closure_context_t* c) const + { + for (const auto &stop : (this+colorLine).stops.iter ()) + c->add_palette_index (stop.color.paletteIndex); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -345,6 +516,21 @@ struct Paint; // Paint a non-COLR glyph, filled as indicated by paint. struct PaintGlyph { + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (! c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + return_trace (out->paint.serialize_subset (c, paint, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -360,6 +546,18 @@ struct PaintGlyph struct PaintColrGlyph { + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -375,6 +573,17 @@ struct PaintColrGlyph template class Var> struct PaintTransform { + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -391,6 +600,17 @@ struct PaintTransform template class Var> struct PaintTranslate { + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -408,6 +628,17 @@ struct PaintTranslate template class Var> struct PaintRotate { + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -426,6 +657,17 @@ struct PaintRotate template class Var> struct PaintSkew { + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -444,6 +686,18 @@ struct PaintSkew struct PaintComposite { + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (!out->src.serialize_subset (c, src, this)) return_trace (false); + return_trace (out->backdrop.serialize_subset (c, backdrop, this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -523,27 +777,83 @@ struct BaseGlyphV1Record int cmp (hb_codepoint_t g) const { return g < glyphId ? -1 : g > glyphId ? 1 : 0; } - bool sanitize (hb_sanitize_context_t *c) const + bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map, + const void* src_base, hb_subset_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = s->embed (this); + if (unlikely (!out)) return_trace (false); + if (!s->check_assign (out->glyphId, glyph_map->get (glyphId), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + return_trace (out->paint.serialize_subset (c, paint, src_base)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this) && paint.sanitize (c, this))); + return_trace (likely (c->check_struct (this) && paint.sanitize (c, base))); } public: HBGlyphID glyphId; /* Glyph ID of reference glyph */ - Offset32To paint; /* Offset (from beginning of BaseGlyphV1Record) to Paint, + Offset32To paint; /* Offset (from beginning of BaseGlyphV1Record array) to Paint, * Typically PaintColrLayers */ public: DEFINE_SIZE_STATIC (6); }; -typedef SortedArray32Of BaseGlyphV1List; +struct BaseGlyphV1List : SortedArray32Of +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + const hb_set_t* glyphset = c->plan->_glyphset; + + for (const auto& _ : as_array ()) + { + unsigned gid = _.glyphId; + if (!glyphset->has (gid)) continue; + + if (_.serialize (c->serializer, c->plan->glyph_map, this, c)) out->len++; + else return_trace (false); + } + + return_trace (out->len != 0); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (SortedArray32Of::sanitize (c, this)); + } +}; struct LayerV1List : Array32OfOffset32To { const Paint& get_paint (unsigned i) const { return this+(*this)[i]; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const auto& _ : + hb_enumerate (*this) + | hb_filter (c->plan->colrv1_layers, hb_first)) + + { + auto *o = out->serialize_append (c->serializer); + if (unlikely (!o) || !o->serialize_subset (c, _.second, this)) + return_trace (false); + } + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -592,6 +902,15 @@ struct COLR hb_set_t *related_ids /* OUT */) const { colr->closure_glyphs (glyph, related_ids); } + void closure_V0palette_indices (const hb_set_t *glyphs, + hb_set_t *palettes /* OUT */) const + { colr->closure_V0palette_indices (glyphs, palettes); } + + void closure_forV1 (hb_set_t *glyphset, + hb_set_t *layer_indices, + hb_set_t *palette_indices) const + { colr->closure_forV1 (glyphset, layer_indices, palette_indices); } + private: hb_blob_ptr_t colr; }; @@ -608,25 +927,70 @@ struct COLR related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size); } + void closure_V0palette_indices (const hb_set_t *glyphs, + hb_set_t *palettes /* OUT */) const + { + if (!numBaseGlyphs || !numLayers) return; + hb_array_t baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs); + hb_array_t all_layers = (this+layersZ).as_array (numLayers); + + for (const BaseGlyphRecord record : baseGlyphs) + { + if (!glyphs->has (record.glyphId)) continue; + hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, + record.numLayers); + for (const LayerRecord layer : glyph_layers) + palettes->add (layer.colorIdx); + } + } + + void closure_forV1 (hb_set_t *glyphset, + hb_set_t *layer_indices, + hb_set_t *palette_indices) const + { + if (version != 1) return; + hb_set_t visited_glyphs; + + hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices); + const BaseGlyphV1List &baseglyphV1_records = this+baseGlyphsV1List; + + for (const BaseGlyphV1Record &baseglyphV1record: baseglyphV1_records.iter ()) + { + unsigned gid = baseglyphV1record.glyphId; + if (!glyphset->has (gid)) continue; + + const Paint &paint = &baseglyphV1_records+baseglyphV1record.paint; + paint.dispatch (&c); + } + hb_set_union (glyphset, &visited_glyphs); + } + + const LayerV1List& get_layerV1List () const + { return (this+layersV1); } + + const BaseGlyphV1List& get_baseglyphV1List () const + { return (this+baseGlyphsV1List); } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && (this+layersZ).sanitize (c, numLayers) && - (version == 0 || (version == 1 && - baseGlyphsV1List.sanitize (c, this) && - layersV1.sanitize (c, this) && - varStore.sanitize (c, this)))); + (version == 0 || + (COLRV1_ENABLE_SUBSETTING && version == 1 && + baseGlyphsV1List.sanitize (c, this) && + layersV1.sanitize (c, this) && + varStore.sanitize (c, this)))); } template - bool serialize (hb_serialize_context_t *c, - unsigned version, - BaseIterator base_it, - LayerIterator layer_it) + bool serialize_V0 (hb_serialize_context_t *c, + unsigned version, + BaseIterator base_it, + LayerIterator layer_it) { TRACE_SERIALIZE (this); if (unlikely (base_it.len () != layer_it.len ())) @@ -636,6 +1000,12 @@ struct COLR this->version = version; numLayers = 0; numBaseGlyphs = base_it.len (); + if (base_it.len () == 0) + { + baseGlyphsZ = 0; + layersZ = 0; + return_trace (true); + } baseGlyphsZ = COLR::min_size; layersZ = COLR::min_size + numBaseGlyphs * BaseGlyphRecord::min_size; @@ -663,6 +1033,14 @@ struct COLR return record; } + const BaseGlyphV1Record* get_base_glyphV1_record (hb_codepoint_t gid) const + { + const BaseGlyphV1Record* record = &(this+baseGlyphsV1List).bsearch ((unsigned) gid); + if ((record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); @@ -710,6 +1088,7 @@ struct COLR 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; + out_layers[i].colorIdx = c->plan->colr_palettes->get (layers[i].colorIdx); } return hb_pair_t> (true, out_layers); @@ -718,11 +1097,29 @@ struct COLR | hb_map_retains_sorting (hb_second) ; - if (unlikely (!base_it || !layer_it || base_it.len () != layer_it.len ())) + if (version == 0 && (!base_it || !layer_it)) return_trace (false); COLR *colr_prime = c->serializer->start_embed (); - return_trace (colr_prime->serialize (c->serializer, version, base_it, layer_it)); + bool ret = colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it); + + if (version == 0) return_trace (ret); + auto snap = c->serializer->snapshot (); + if (!c->serializer->allocate_size (3 * HBUINT32::static_size)) return_trace (false); + if (!colr_prime->baseGlyphsV1List.serialize_subset (c, baseGlyphsV1List, this)) + { + if (c->serializer->in_error ()) return_trace (false); + //no more COLRv1 glyphs: downgrade to version 0 + c->serializer->revert (snap); + colr_prime->version = 0; + return_trace (true); + } + + if (!colr_prime->layersV1.serialize_subset (c, layersV1, this)) return_trace (false); + + colr_prime->varStore = 0; + //TODO: subset varStore once it's implemented in fonttools + return_trace (true); } protected: diff --git a/gfx/harfbuzz/src/hb-ot-color-colrv1-closure.hh b/gfx/harfbuzz/src/hb-ot-color-colrv1-closure.hh new file mode 100644 index 000000000000..4124efe0b14f --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-color-colrv1-closure.hh @@ -0,0 +1,101 @@ +/* + * 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. + * + */ + +#ifndef HB_OT_COLR_COLRV1_CLOSURE_HH +#define HB_OT_COLR_COLRV1_CLOSURE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-color-colr-table.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +namespace OT { + +HB_INTERNAL void PaintColrLayers::closurev1 (hb_colrv1_closure_context_t* c) const +{ + c->add_layer_indices (firstLayerIndex, numLayers); + const LayerV1List &paint_offset_lists = c->get_colr_table ()->get_layerV1List (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + const Paint &paint = hb_addressof (paint_offset_lists) + paint_offset_lists[i]; + paint.dispatch (c); + } +} + +HB_INTERNAL void PaintGlyph::closurev1 (hb_colrv1_closure_context_t* c) const +{ + c->add_glyph (gid); + (this+paint).dispatch (c); +} + +HB_INTERNAL void PaintColrGlyph::closurev1 (hb_colrv1_closure_context_t* c) const +{ + const COLR *colr_table = c->get_colr_table (); + const BaseGlyphV1Record* baseglyphV1_record = colr_table->get_base_glyphV1_record (gid); + if (!baseglyphV1_record) return; + c->add_glyph (gid); + + const BaseGlyphV1List &baseglyphV1_list = colr_table->get_baseglyphV1List (); + (&baseglyphV1_list+baseglyphV1_record->paint).dispatch (c); +} + +template class Var> +HB_INTERNAL void PaintTransform::closurev1 (hb_colrv1_closure_context_t* c) const +{ + (this+src).dispatch (c); +} + +template class Var> +HB_INTERNAL void PaintTranslate::closurev1 (hb_colrv1_closure_context_t* c) const +{ + (this+src).dispatch (c); +} + +template class Var> +HB_INTERNAL void PaintRotate::closurev1 (hb_colrv1_closure_context_t* c) const +{ + (this+src).dispatch (c); +} + +template class Var> +HB_INTERNAL void PaintSkew::closurev1 (hb_colrv1_closure_context_t* c) const +{ + (this+src).dispatch (c); +} + +HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) const +{ + (this+src).dispatch (c); + (this+backdrop).dispatch (c); +} + +} /* namespace OT */ + + +#endif /* HB_OT_COLR_COLRV1_CLOSURE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-color-cpal-table.hh b/gfx/harfbuzz/src/hb-ot-color-cpal-table.hh index 2fa99dead747..a9deeba9a7a8 100644 --- a/gfx/harfbuzz/src/hb-ot-color-cpal-table.hh +++ b/gfx/harfbuzz/src/hb-ot-color-cpal-table.hh @@ -39,7 +39,6 @@ */ #define HB_OT_TAG_CPAL HB_TAG('C','P','A','L') - namespace OT { @@ -74,6 +73,44 @@ struct CPALV1Tail } public: + bool serialize (hb_serialize_context_t *c, + unsigned palette_count, + unsigned color_count, + const void *base, + const hb_map_t *color_index_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->allocate_size (static_size); + if (unlikely (!out)) return_trace (false); + + out->paletteFlagsZ = 0; + if (paletteFlagsZ) + out->paletteFlagsZ.serialize_copy (c, paletteFlagsZ, base, 0, hb_serialize_context_t::Head, palette_count); + + out->paletteLabelsZ = 0; + if (paletteLabelsZ) + out->paletteLabelsZ.serialize_copy (c, paletteLabelsZ, base, 0, hb_serialize_context_t::Head, palette_count); + + const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count); + if (colorLabelsZ) + { + c->push (); + for (const auto _ : colorLabels) + { + if (!color_index_map->has (_)) continue; + NameID new_color_idx; + new_color_idx = color_index_map->get (_); + if (!c->copy (new_color_idx)) + { + c->pop_discard (); + return_trace (false); + } + } + c->add_link (out->colorLabelsZ, c->pop_pack ()); + } + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c, const void *base, unsigned int palette_count, @@ -87,6 +124,8 @@ struct CPALV1Tail } protected: + // TODO(garretrieger): these offsets can hold nulls so we should not be using non-null offsets + // here. Currently they are needed since UnsizedArrayOf doesn't define null_size NNOffset32To> paletteFlagsZ; /* Offset from the beginning of CPAL table to * the Palette Type Array. Set to 0 if no array @@ -157,6 +196,84 @@ struct CPAL } public: + bool serialize (hb_serialize_context_t *c, + const hb_array_t &color_records, + const hb_array_t &color_record_indices, + const hb_map_t &color_record_index_map, + const hb_set_t &retained_color_record_indices) const + { + TRACE_SERIALIZE (this); + + for (const auto idx : color_record_indices) + { + HBUINT16 new_idx; + if (idx == 0) new_idx = 0; + else new_idx = color_record_index_map.get (idx); + if (!c->copy (new_idx)) return_trace (false); + } + + c->push (); + for (const auto _ : retained_color_record_indices.iter ()) + { + if (!c->copy (color_records[_])) + { + c->pop_discard (); + return_trace (false); + } + } + c->add_link (colorRecordsZ, c->pop_pack ()); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_map_t *color_index_map = c->plan->colr_palettes; + if (color_index_map->is_empty ()) return_trace (false); + + hb_set_t retained_color_indices; + for (const auto _ : color_index_map->keys ()) + { + if (_ == 0xFFFF) continue; + retained_color_indices.add (_); + } + if (retained_color_indices.is_empty ()) return_trace (false); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->version = version; + out->numColors = retained_color_indices.get_population (); + out->numPalettes = numPalettes; + + const hb_array_t colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes); + hb_map_t color_record_index_map; + hb_set_t retained_color_record_indices; + + unsigned record_count = 0; + for (const auto first_color_record_idx : colorRecordIndices) + { + for (unsigned retained_color_idx : retained_color_indices.iter ()) + { + unsigned color_record_idx = first_color_record_idx + retained_color_idx; + if (color_record_index_map.has (color_record_idx)) continue; + color_record_index_map.set (color_record_idx, record_count); + retained_color_record_indices.add (color_record_idx); + record_count++; + } + } + + out->numColorRecords = record_count; + const hb_array_t color_records = (this+colorRecordsZ).as_array (numColorRecords); + if (!out->serialize (c->serializer, color_records, colorRecordIndices, color_record_index_map, retained_color_record_indices)) + return_trace (false); + + if (version == 1) + return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map)); + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); diff --git a/gfx/harfbuzz/src/hb-ot-color-sbix-table.hh b/gfx/harfbuzz/src/hb-ot-color-sbix-table.hh index af1e4a5dbeb0..d2911f19e63f 100644 --- a/gfx/harfbuzz/src/hb-ot-color-sbix-table.hh +++ b/gfx/harfbuzz/src/hb-ot-color-sbix-table.hh @@ -145,7 +145,7 @@ struct SBIXStrike 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); + if (unlikely (!c->serializer->extend (out, num_output_glyphs + 1))) return_trace (false); out->ppem = ppem; out->resolution = resolution; HBUINT32 head; diff --git a/gfx/harfbuzz/src/hb-ot-face-table-list.hh b/gfx/harfbuzz/src/hb-ot-face-table-list.hh index 367e143fdfb9..ffbbb1bc539b 100644 --- a/gfx/harfbuzz/src/hb-ot-face-table-list.hh +++ b/gfx/harfbuzz/src/hb-ot-face-table-list.hh @@ -40,7 +40,7 @@ /* 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. */ + * exactly zero-cost. */ /* v--- Add new tables in the right place here. */ diff --git a/gfx/harfbuzz/src/hb-ot-glyf-table.hh b/gfx/harfbuzz/src/hb-ot-glyf-table.hh index 5352156f0246..ff7b9b2d25b6 100644 --- a/gfx/harfbuzz/src/hb-ot-glyf-table.hh +++ b/gfx/harfbuzz/src/hb-ot-glyf-table.hh @@ -45,6 +45,10 @@ namespace OT { */ #define HB_OT_TAG_loca HB_TAG('l','o','c','a') +#ifndef HB_MAX_COMPOSITE_OPERATIONS +#define HB_MAX_COMPOSITE_OPERATIONS 100000 +#endif + struct loca { @@ -98,7 +102,7 @@ struct glyf 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); + char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets); if (unlikely (!loca_prime_data)) return false; @@ -115,7 +119,7 @@ struct glyf entry_size * num_offsets, HB_MEMORY_MODE_WRITABLE, loca_prime_data, - free); + hb_free); bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) && _add_head_and_set_loca_version (plan, use_short_loca); @@ -209,10 +213,15 @@ struct glyf 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 (); - + if (new_gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + subset_glyph.source_glyph = Glyph (); + else + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + subset_glyph.drop_hints_bytes (); + else + subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); return subset_glyph; }) | hb_sink (glyphs) @@ -281,6 +290,11 @@ struct glyf hb_codepoint_t get_glyph_index () const { return glyphIndex; } void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + void set_overlaps_flag () + { + flags = (uint16_t) flags | OVERLAP_COMPOUND; + } + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } bool has_more () const { return flags & MORE_COMPONENTS; } @@ -383,9 +397,12 @@ struct glyf { 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) {} + glyph (glyph_), current (nullptr), current_size (0) + { + set_next (current_); + } + + composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} const CompositeGlyphChain &__item__ () const { return *current; } bool __more__ () const { return current; } @@ -393,23 +410,36 @@ struct glyf { if (!current->has_more ()) { current = nullptr; return; } - const CompositeGlyphChain *possible = &StructAfter (*current); - if (!check_range (possible)) { current = nullptr; return; } - current = possible; + set_next (&StructAtOffset (current, current_size)); } bool operator != (const composite_iter_t& o) const { return glyph != o.glyph || current != o.current; } - bool check_range (const CompositeGlyphChain *composite) const + + void set_next (const CompositeGlyphChain *composite) { - return glyph.check_range (composite, CompositeGlyphChain::min_size) - && glyph.check_range (composite, composite->get_size ()); + if (!glyph.check_range (composite, CompositeGlyphChain::min_size)) + { + current = nullptr; + current_size = 0; + return; + } + unsigned size = composite->get_size (); + if (!glyph.check_range (composite, size)) + { + current = nullptr; + current_size = 0; + return; + } + + current = composite; + current_size = size; } private: hb_bytes_t glyph; __item_t__ current; + unsigned current_size; }; enum phantom_point_index_t @@ -427,14 +457,14 @@ struct glyf { 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 + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, + FLAG_OVERLAP_SIMPLE = 0x40, + FLAG_RESERVED2 = 0x80 }; private: @@ -495,8 +525,8 @@ struct glyf 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; + const uint8_t *glyph = (uint8_t*) bytes.arrayZ; + const uint8_t *glyph_end = glyph + bytes.length; /* simple glyph w/contours, possibly trimmable */ glyph += instruction_len_offset (); @@ -553,6 +583,17 @@ struct glyf dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); } + void set_overlaps_flag () + { + if (unlikely (!header.numberOfContours)) return; + + unsigned flags_offset = length (instructions_length ()); + if (unlikely (length (flags_offset + 1) > bytes.length)) return; + + HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset (&bytes, flags_offset); + first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE; + } + static bool read_points (const HBUINT8 *&p /* IN/OUT */, contour_point_vector_t &points_ /* IN/OUT */, const hb_bytes_t &bytes, @@ -666,6 +707,12 @@ struct glyf /* 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)); } + + void set_overlaps_flag () + { + const_cast (StructAfter (header)) + .set_overlaps_flag (); + } }; enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; @@ -695,6 +742,15 @@ struct glyf } } + void set_overlaps_flag () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return; + case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return; + default: return; + } + } + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const { switch (type) { @@ -886,7 +942,7 @@ struct glyf { if (gid >= num_glyphs) return false; - /* Making this alloc free is not that easy + /* Making this allocfree 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 */ @@ -1045,18 +1101,28 @@ struct glyf 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 + unsigned + add_gid_and_children (hb_codepoint_t gid, + hb_set_t *gids_to_retain, + unsigned depth = 0, + unsigned operation_count = 0) const { - if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return; + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count; + if (unlikely (operation_count++ > HB_MAX_COMPOSITE_OPERATIONS)) return operation_count; /* Check if is already visited */ - if (gids_to_retain->has (gid)) return; + if (gids_to_retain->has (gid)) return operation_count; 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); + auto it = glyph_for_gid (gid).get_composite_iterator (); + while (it) + { + auto item = *(it++); + operation_count += + add_gid_and_children (item.get_glyph_index (), gids_to_retain, depth, operation_count); + } + + return operation_count; } #ifdef HB_EXPERIMENTAL_API @@ -1230,7 +1296,11 @@ struct glyf const_cast (_).set_glyph_index (new_gid); } - if (plan->drop_hints) Glyph (dest_glyph).drop_hints (); + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + Glyph (dest_glyph).drop_hints (); + + if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG) + Glyph (dest_glyph).set_overlaps_flag (); return_trace (true); } diff --git a/gfx/harfbuzz/src/hb-ot-hdmx-table.hh b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh index 590fa154ba13..dea2b7e29a97 100644 --- a/gfx/harfbuzz/src/hb-ot-hdmx-table.hh +++ b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh @@ -52,7 +52,7 @@ struct DeviceRecord unsigned length = it.len (); - if (unlikely (!c->extend (*this, length))) return_trace (false); + if (unlikely (!c->extend (this, length))) return_trace (false); this->pixelSize = pixelSize; this->maxWidth = diff --git a/gfx/harfbuzz/src/hb-ot-layout-common.hh b/gfx/harfbuzz/src/hb-ot-layout-common.hh index 0fc66d2cbd11..65f499a00dc3 100644 --- a/gfx/harfbuzz/src/hb-ot-layout-common.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-common.hh @@ -415,7 +415,7 @@ struct RecordArrayOf : SortedArray16Of> } 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); + return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); } }; @@ -1262,13 +1262,13 @@ struct Lookup unsigned int num_subtables) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); lookupType = lookup_type; lookupFlag = lookup_props & 0xFFFFu; if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false); if (lookupFlag & LookupFlag::UseMarkFilteringSet) { - if (unlikely (!c->extend (*this))) return_trace (false); + if (unlikely (!c->extend (this))) return_trace (false); HBUINT16 &markFilteringSet = StructAfter (subTable); markFilteringSet = lookup_props >> 16; } @@ -1393,7 +1393,7 @@ struct CoverageFormat1 unsigned int get_coverage (hb_codepoint_t glyph_id) const { unsigned int i; - glyphArray.bfind (glyph_id, &i, HB_BFIND_NOT_FOUND_STORE, NOT_COVERED); + glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED); return i; } @@ -1478,7 +1478,7 @@ struct CoverageFormat2 bool serialize (hb_serialize_context_t *c, Iterator glyphs) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); if (unlikely (!glyphs)) { @@ -1657,7 +1657,7 @@ struct Coverage bool serialize (hb_serialize_context_t *c, Iterator glyphs) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); unsigned count = 0; unsigned num_ranges = 0; @@ -1890,7 +1890,7 @@ struct ClassDefFormat1 Iterator it) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); if (unlikely (!it)) { @@ -2069,7 +2069,7 @@ struct ClassDefFormat2 Iterator it) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); if (unlikely (!it)) { @@ -2311,7 +2311,7 @@ struct ClassDef bool serialize (hb_serialize_context_t *c, Iterator it_with_class_zero) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); auto it = + it_with_class_zero | hb_filter (hb_second); @@ -2516,19 +2516,19 @@ struct VarRegionList bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount)); + return_trace (c->check_struct (this) && axesZ.sanitize (c, axisCount * regionCount)); } bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_bimap_t ®ion_map) { TRACE_SERIALIZE (this); - VarRegionList *out = c->allocate_min (); - if (unlikely (!out)) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); axisCount = src->axisCount; regionCount = region_map.get_population (); - if (unlikely (!c->allocate_size (get_size () - min_size))) return_trace (false); - unsigned int region_count = src->get_region_count (); + if (unlikely (hb_unsigned_mul_overflows (axisCount * regionCount, + VarRegionAxis::static_size))) return_trace (false); + if (unlikely (!c->extend (this))) return_trace (false); + unsigned int region_count = src->regionCount; for (unsigned int r = 0; r < regionCount; r++) { unsigned int backward = region_map.backward (r); @@ -2540,11 +2540,11 @@ struct VarRegionList } unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; } - unsigned int get_region_count () const { return regionCount; } - protected: + public: HBUINT16 axisCount; HBUINT16 regionCount; + protected: UnsizedArrayOf axesZ; public: @@ -2560,7 +2560,10 @@ struct VarData { return shortCount + regionIndices.len; } unsigned int get_size () const - { return itemCount * get_row_size (); } + { return min_size + - regionIndices.min_size + regionIndices.get_size () + + itemCount * get_row_size (); + } float get_delta (unsigned int inner, const int *coords, unsigned int coord_count, @@ -2594,10 +2597,10 @@ struct VarData return delta; } - void get_scalars (const int *coords, unsigned int coord_count, - const VarRegionList ®ions, - float *scalars /*OUT */, - unsigned int num_scalars) const + void get_region_scalars (const int *coords, unsigned int coord_count, + const VarRegionList ®ions, + float *scalars /*OUT */, + unsigned int num_scalars) const { unsigned count = hb_min (num_scalars, regionIndices.len); for (unsigned int i = 0; i < count; i++) @@ -2623,7 +2626,7 @@ struct VarData const hb_bimap_t ®ion_map) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); itemCount = inner_map.get_next_value (); /* Optimize short count */ @@ -2665,9 +2668,7 @@ struct VarData shortCount = new_short_count; regionIndices.len = new_ri_count; - unsigned int size = regionIndices.get_size () - HBUINT16::static_size/*regionIndices.len*/ + (get_row_size () * itemCount); - if (unlikely (!c->allocate_size (size))) - return_trace (false); + if (unlikely (!c->extend (this))) return_trace (false); for (r = 0; r < ri_count; r++) if (delta_sz[r]) regionIndices[ri_map[r]] = region_map[src->regionIndices[r]]; @@ -2682,16 +2683,16 @@ struct VarData return_trace (true); } - void collect_region_refs (hb_inc_bimap_t ®ion_map, const hb_inc_bimap_t &inner_map) const + void collect_region_refs (hb_set_t ®ion_indices, const hb_inc_bimap_t &inner_map) const { for (unsigned int r = 0; r < regionIndices.len; r++) { unsigned int region = regionIndices[r]; - if (region_map.has (region)) continue; + if (region_indices.has (region)) continue; for (unsigned int i = 0; i < inner_map.get_next_value (); i++) if (get_item_delta (inner_map.backward (i), r) != 0) { - region_map.add (region); + region_indices.add (region); break; } } @@ -2777,32 +2778,48 @@ struct VariationStore const hb_array_t &inner_maps) { TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + unsigned int set_count = 0; for (unsigned int i = 0; i < inner_maps.length; i++) - if (inner_maps[i].get_population () > 0) set_count++; + if (inner_maps[i].get_population ()) + set_count++; - unsigned int size = min_size + HBUINT32::static_size * set_count; - if (unlikely (!c->allocate_size (size))) return_trace (false); format = 1; - hb_inc_bimap_t region_map; - for (unsigned int i = 0; i < inner_maps.length; i++) - (src+src->dataSets[i]).collect_region_refs (region_map, inner_maps[i]); - region_map.sort (); + const auto &src_regions = src+src->regions; - if (unlikely (!regions.serialize (c, this) - .serialize (c, &(src+src->regions), region_map))) return_trace (false); + hb_set_t region_indices; + for (unsigned int i = 0; i < inner_maps.length; i++) + (src+src->dataSets[i]).collect_region_refs (region_indices, inner_maps[i]); + + if (region_indices.in_error ()) + return_trace (false); + + region_indices.del_range ((src_regions).regionCount, hb_set_t::INVALID); + + /* TODO use constructor when our data-structures support that. */ + hb_inc_bimap_t region_map; + + hb_iter (region_indices) + | hb_apply ([®ion_map] (unsigned _) { region_map.add(_); }) + ; + if (region_map.in_error()) + return_trace (false); + + if (unlikely (!regions.serialize_serialize (c, &src_regions, region_map))) + return_trace (false); + + dataSets.len = set_count; + if (unlikely (!c->extend (dataSets))) return_trace (false); /* TODO: The following code could be simplified when - * List16OfOffset16To::subset () can take a custom param to be passed to VarData::serialize () - */ - dataSets.len = set_count; + * List16OfOffset16To::subset () can take a custom param to be passed to VarData::serialize () */ unsigned int set_index = 0; for (unsigned int i = 0; i < inner_maps.length; i++) { - if (inner_maps[i].get_population () == 0) continue; - if (unlikely (!dataSets[set_index++].serialize (c, this) - .serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map))) + if (!inner_maps[i].get_population ()) continue; + if (unlikely (!dataSets[set_index++] + .serialize_serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map))) return_trace (false); } @@ -2847,13 +2864,13 @@ struct VariationStore && varstore_prime->dataSets); } - unsigned int get_region_index_count (unsigned int ivs) const - { return (this+dataSets[ivs]).get_region_index_count (); } + unsigned int get_region_index_count (unsigned int major) const + { return (this+dataSets[major]).get_region_index_count (); } - void get_scalars (unsigned int ivs, - const int *coords, unsigned int coord_count, - float *scalars /*OUT*/, - unsigned int num_scalars) const + void get_region_scalars (unsigned int major, + const int *coords, unsigned int coord_count, + float *scalars /*OUT*/, + unsigned int num_scalars) const { #ifdef HB_NO_VAR for (unsigned i = 0; i < num_scalars; i++) @@ -2861,8 +2878,9 @@ struct VariationStore return; #endif - (this+dataSets[ivs]).get_scalars (coords, coord_count, this+regions, - &scalars[0], num_scalars); + (this+dataSets[major]).get_region_scalars (coords, coord_count, + this+regions, + &scalars[0], num_scalars); } unsigned int get_sub_table_count () const { return dataSets.len; } @@ -2872,7 +2890,7 @@ struct VariationStore Offset32To regions; Array16OfOffset32To dataSets; public: - DEFINE_SIZE_ARRAY (8, dataSets); + DEFINE_SIZE_ARRAY_SIZED (8, dataSets); }; /* diff --git a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh index edb35dabf94f..31a4a3e84cb0 100644 --- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh @@ -98,8 +98,7 @@ struct AttachList | hb_map (glyph_map) | hb_sink (new_coverage) ; - out->coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ()); + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (bool (new_coverage)); } @@ -386,8 +385,7 @@ struct LigCaretList | hb_map (glyph_map) | hb_sink (new_coverage) ; - out->coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ()); + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (bool (new_coverage)); } diff --git a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh index 4354f8f0d9bd..1e305518f506 100644 --- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh @@ -538,7 +538,7 @@ struct Anchor switch (u.format) { case 1: return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); case 2: - if (c->plan->drop_hints) + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) { // AnchorFormat 2 just containins extra hinting information, so // if hints are being dropped convert to format 1. @@ -805,7 +805,7 @@ struct SinglePosFormat1 ValueFormat newFormat, const hb_map_t *layout_variation_idx_map) { - if (unlikely (!c->extend_min (*this))) return; + if (unlikely (!c->extend_min (this))) return; if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; @@ -823,8 +823,7 @@ struct SinglePosFormat1 | hb_map_retains_sorting (hb_first) ; - // TODO(garretrieger): serialize_subset this. - coverage.serialize (c, this).serialize (c, glyphs); + coverage.serialize_serialize (c, glyphs); } bool subset (hb_subset_context_t *c) const @@ -926,7 +925,7 @@ struct SinglePosFormat2 ValueFormat newFormat, const hb_map_t *layout_variation_idx_map) { - auto out = c->extend_min (*this); + auto out = c->extend_min (this); if (unlikely (!out)) return; if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; if (unlikely (!c->check_assign (valueCount, it.len (), HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) return; @@ -942,7 +941,7 @@ struct SinglePosFormat2 | hb_map_retains_sorting (hb_first) ; - coverage.serialize (c, this).serialize (c, glyphs); + coverage.serialize_serialize (c, glyphs); } bool subset (hb_subset_context_t *c) const @@ -1374,7 +1373,7 @@ struct PairPosFormat1 out->format = format; out->valueFormat[0] = valueFormat[0]; out->valueFormat[1] = valueFormat[1]; - if (c->plan->drop_hints) + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) { hb_pair_t newFormats = compute_effective_value_formats (glyphset); out->valueFormat[0] = newFormats.first; @@ -1404,8 +1403,7 @@ struct PairPosFormat1 | hb_sink (new_coverage) ; - out->coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ()); + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (bool (new_coverage)); } @@ -1593,7 +1591,7 @@ struct PairPosFormat2 unsigned len2 = valueFormat2.get_len (); hb_pair_t newFormats = hb_pair (valueFormat1, valueFormat2); - if (c->plan->drop_hints) + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) newFormats = compute_effective_value_formats (klass1_map, klass2_map); out->valueFormat1 = newFormats.first; @@ -1618,7 +1616,7 @@ struct PairPosFormat2 | hb_map_retains_sorting (glyph_map) ; - out->coverage.serialize (c->serializer, out).serialize (c->serializer, it); + out->coverage.serialize_serialize (c->serializer, it); return_trace (out->class1Count && out->class2Count && bool (it)); } @@ -1882,7 +1880,7 @@ struct CursivePosFormat1 else pos[child].x_offset = x_offset; - /* If parent was attached to child, break them free. + /* If parent was attached to child, separate them. * https://github.com/harfbuzz/harfbuzz/issues/2469 */ if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) @@ -1911,7 +1909,7 @@ struct CursivePosFormat1 | hb_map_retains_sorting (hb_first) ; - coverage.serialize (c->serializer, this).serialize (c->serializer, glyphs); + coverage.serialize_serialize (c->serializer, glyphs); } bool subset (hb_subset_context_t *c) const @@ -2118,8 +2116,7 @@ struct MarkBasePosFormat1 | hb_sink (new_coverage) ; - if (!out->markCoverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ())) + if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) return_trace (false); out->markArray.serialize_subset (c, markArray, this, @@ -2139,8 +2136,7 @@ struct MarkBasePosFormat1 | hb_sink (new_coverage) ; - if (!out->baseCoverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ())) + if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) return_trace (false); hb_sorted_vector_t base_indexes; @@ -2377,8 +2373,7 @@ struct MarkLigPosFormat1 | hb_map_retains_sorting (glyph_map) ; - if (!out->markCoverage.serialize (c->serializer, out) - .serialize (c->serializer, new_mark_coverage)) + if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage)) return_trace (false); out->markArray.serialize_subset (c, markArray, this, @@ -2391,8 +2386,7 @@ struct MarkLigPosFormat1 | hb_map_retains_sorting (glyph_map) ; - if (!out->ligatureCoverage.serialize (c->serializer, out) - .serialize (c->serializer, new_ligature_coverage)) + if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage)) return_trace (false); out->ligatureArray.serialize_subset (c, ligatureArray, this, @@ -2581,8 +2575,7 @@ struct MarkMarkPosFormat1 | hb_sink (new_coverage) ; - if (!out->mark1Coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ())) + if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) return_trace (false); out->mark1Array.serialize_subset (c, mark1Array, this, @@ -2602,8 +2595,7 @@ struct MarkMarkPosFormat1 | hb_sink (new_coverage) ; - if (!out->mark2Coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ())) + if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) return_trace (false); hb_sorted_vector_t mark2_indexes; diff --git a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh index 25538d52f690..393ada13512b 100644 --- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh @@ -100,8 +100,8 @@ struct SingleSubstFormat1 unsigned delta) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false); c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW); return_trace (true); } @@ -209,9 +209,9 @@ struct SingleSubstFormat2 + it | hb_map_retains_sorting (hb_first) ; - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false); - if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false); + if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false); return_trace (true); } @@ -343,9 +343,14 @@ struct Sequence unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ? HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0; + unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur()); - for (unsigned int i = 0; i < count; i++) { - _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i); + for (unsigned int i = 0; i < count; i++) + { + /* If is attached to a ligature, don't disturb that. + * https://github.com/harfbuzz/harfbuzz/issues/3069 */ + if (!lig_id) + _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i); c->output_glyph_for_component (substitute.arrayZ[i], klass); } c->buffer->skip_glyph (); @@ -408,7 +413,6 @@ struct MultipleSubstFormat1 | hb_map (hb_add (this)) | hb_apply ([c] (const Sequence &_) { _.closure (c); }) ; - } void closure_lookups (hb_closure_lookups_context_t *c) const {} @@ -444,17 +448,17 @@ struct MultipleSubstFormat1 hb_array_t substitute_glyphs_list) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false); for (unsigned int i = 0; i < glyphs.length; i++) { unsigned int substitute_len = substitute_len_list[i]; - if (unlikely (!sequence[i].serialize (c, this) - .serialize (c, substitute_glyphs_list.sub_array (0, substitute_len)))) + if (unlikely (!sequence[i] + .serialize_serialize (c, substitute_glyphs_list.sub_array (0, substitute_len)))) return_trace (false); substitute_glyphs_list += substitute_len; } - return_trace (coverage.serialize (c, this).serialize (c, glyphs)); + return_trace (coverage.serialize_serialize (c, glyphs)); } bool subset (hb_subset_context_t *c) const @@ -475,8 +479,7 @@ struct MultipleSubstFormat1 | hb_map (glyph_map) | hb_sink (new_coverage) ; - out->coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ()); + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (bool (new_coverage)); } @@ -560,7 +563,12 @@ struct AlternateSet /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */ if (alt_index == HB_OT_MAP_MAX_VALUE && c->random) + { + /* Maybe we can do better than unsafe-to-break all; but since we are + * changing random state, it would be hard to track that. Good 'nough. */ + c->buffer->unsafe_to_break_all (); alt_index = c->random_number () % count + 1; + } if (unlikely (alt_index > count || alt_index == 0)) return_trace (false); @@ -683,17 +691,17 @@ struct AlternateSubstFormat1 hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false); for (unsigned int i = 0; i < glyphs.length; i++) { unsigned int alternate_len = alternate_len_list[i]; - if (unlikely (!alternateSet[i].serialize (c, this) - .serialize (c, alternate_glyphs_list.sub_array (0, alternate_len)))) + if (unlikely (!alternateSet[i] + .serialize_serialize (c, alternate_glyphs_list.sub_array (0, alternate_len)))) return_trace (false); alternate_glyphs_list += alternate_len; } - return_trace (coverage.serialize (c, this).serialize (c, glyphs)); + return_trace (coverage.serialize_serialize (c, glyphs)); } bool subset (hb_subset_context_t *c) const @@ -714,8 +722,7 @@ struct AlternateSubstFormat1 | hb_map (glyph_map) | hb_sink (new_coverage) ; - out->coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ()); + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (bool (new_coverage)); } @@ -848,7 +855,7 @@ struct Ligature Iterator components /* Starting from second */) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); ligGlyph = ligature; if (unlikely (!component.serialize (c, components))) return_trace (false); return_trace (true); @@ -947,15 +954,14 @@ struct LigatureSet hb_array_t &component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false); for (unsigned int i = 0; i < ligatures.length; i++) { unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0); - if (unlikely (!ligature[i].serialize (c, this) - .serialize (c, - ligatures[i], - component_list.sub_array (0, component_count)))) + if (unlikely (!ligature[i].serialize_serialize (c, + ligatures[i], + component_list.sub_array (0, component_count)))) return_trace (false); component_list += component_count; } @@ -1060,20 +1066,20 @@ struct LigatureSubstFormat1 hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false); for (unsigned int i = 0; i < first_glyphs.length; i++) { unsigned int ligature_count = ligature_per_first_glyph_count_list[i]; - if (unlikely (!ligatureSet[i].serialize (c, this) - .serialize (c, - ligatures_list.sub_array (0, ligature_count), - component_count_list.sub_array (0, ligature_count), - component_list))) return_trace (false); + if (unlikely (!ligatureSet[i] + .serialize_serialize (c, + ligatures_list.sub_array (0, ligature_count), + component_count_list.sub_array (0, ligature_count), + component_list))) return_trace (false); ligatures_list += ligature_count; component_count_list += ligature_count; } - return_trace (coverage.serialize (c, this).serialize (c, first_glyphs)); + return_trace (coverage.serialize_serialize (c, first_glyphs)); } bool subset (hb_subset_context_t *c) const @@ -1094,8 +1100,7 @@ struct LigatureSubstFormat1 | hb_map (glyph_map) | hb_sink (new_coverage) ; - out->coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ()); + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (bool (new_coverage)); } @@ -1325,7 +1330,7 @@ struct ReverseChainSingleSubstFormat1 if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes)))) return_trace (false); - if (unlikely (!out->coverage.serialize (c->serializer, out).serialize (c->serializer, glyphs))) + if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs))) return_trace (false); return_trace (true); } @@ -1554,10 +1559,6 @@ struct SubstLookup : Lookup static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); - SubTable& serialize_subtable (hb_serialize_context_t *c, - unsigned int i) - { return get_subtables ()[i].serialize (c, this); } - bool serialize_single (hb_serialize_context_t *c, uint32_t lookup_props, hb_sorted_array_t glyphs, @@ -1565,8 +1566,13 @@ struct SubstLookup : Lookup { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.single. - serialize (c, hb_zip (glyphs, substitutes))); + if (c->push ()->u.single.serialize (c, hb_zip (glyphs, substitutes))) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); } bool serialize_multiple (hb_serialize_context_t *c, @@ -1577,11 +1583,17 @@ struct SubstLookup : Lookup { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.multiple. - serialize (c, - glyphs, - substitute_len_list, - substitute_glyphs_list)); + if (c->push ()->u.multiple. + serialize (c, + glyphs, + substitute_len_list, + substitute_glyphs_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); } bool serialize_alternate (hb_serialize_context_t *c, @@ -1592,11 +1604,18 @@ struct SubstLookup : Lookup { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.alternate. - serialize (c, - glyphs, - alternate_len_list, - alternate_glyphs_list)); + + if (c->push ()->u.alternate. + serialize (c, + glyphs, + alternate_len_list, + alternate_glyphs_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); } bool serialize_ligature (hb_serialize_context_t *c, @@ -1609,13 +1628,19 @@ struct SubstLookup : Lookup { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.ligature. - serialize (c, - first_glyphs, - ligature_per_first_glyph_count_list, - ligatures_list, - component_count_list, - component_list)); + if (c->push ()->u.ligature. + serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); } template diff --git a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh index b537ba0055a6..a68abb533d1e 100644 --- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh @@ -122,7 +122,7 @@ struct hb_closure_context_t : hb_set_t *covered_glyph_set = done_lookups_glyph_set->get (lookup_index); if (unlikely (covered_glyph_set->in_error ())) return true; - if (parent_active_glyphs ()->is_subset (covered_glyph_set)) + if (parent_active_glyphs ()->is_subset (*covered_glyph_set)) return true; hb_set_union (covered_glyph_set, parent_active_glyphs ()); @@ -183,7 +183,7 @@ struct hb_closure_context_t : void flush () { - hb_set_del_range (output, face->get_num_glyphs (), hb_set_get_max (output)); /* Remove invalid glyphs. */ + hb_set_del_range (output, face->get_num_glyphs (), HB_SET_VALUE_INVALID); /* Remove invalid glyphs. */ hb_set_union (glyphs, output); hb_set_clear (output); active_glyphs_stack.pop (); @@ -1898,8 +1898,7 @@ struct ContextFormat1 | hb_sink (new_coverage) ; - out->coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ()); + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (bool (new_coverage)); } @@ -2866,8 +2865,7 @@ struct ChainContextFormat1 | hb_sink (new_coverage) ; - out->coverage.serialize (c->serializer, out) - .serialize (c->serializer, new_coverage.iter ()); + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (bool (new_coverage)); } @@ -3721,8 +3719,9 @@ struct GSUBGPOS hb_set_t alternate_feature_indices; if (version.to_int () >= 0x00010001u) (this+featureVars).closure_features (lookup_indices, &alternate_feature_indices); - if (unlikely (alternate_feature_indices.in_error())) { - feature_indices->successful = false; + if (unlikely (alternate_feature_indices.in_error())) + { + feature_indices->err (); return; } #endif @@ -3788,7 +3787,7 @@ struct GSUBGPOS this->lookup_count = table->get_lookup_count (); - this->accels = (hb_ot_layout_lookup_accelerator_t *) calloc (this->lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t)); + this->accels = (hb_ot_layout_lookup_accelerator_t *) hb_calloc (this->lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t)); if (unlikely (!this->accels)) { this->lookup_count = 0; @@ -3804,7 +3803,7 @@ struct GSUBGPOS { for (unsigned int i = 0; i < this->lookup_count; i++) this->accels[i].fini (); - free (this->accels); + hb_free (this->accels); this->table.destroy (); } diff --git a/gfx/harfbuzz/src/hb-ot-layout.cc b/gfx/harfbuzz/src/hb-ot-layout.cc index 55a6587cd05a..0454af20632a 100644 --- a/gfx/harfbuzz/src/hb-ot-layout.cc +++ b/gfx/harfbuzz/src/hb-ot-layout.cc @@ -131,7 +131,9 @@ hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); + if (!buffer->message (font, "start table kern")) return; kern.apply (&c); + (void) buffer->message (font, "end table kern"); } #endif @@ -144,7 +146,7 @@ bool OT::GDEF::is_blocklisted (hb_blob_t *blob, hb_face_t *face) const { -#ifdef HB_NO_OT_LAYOUT_BLACKLIST +#ifdef HB_NO_OT_LAYOUT_BLOCKLIST return false; #endif /* The ugly business of blocklisting individual fonts' tables happen here! @@ -383,7 +385,7 @@ bool OT::GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED, hb_face_t *face) const { -#ifdef HB_NO_OT_LAYOUT_BLACKLIST +#ifdef HB_NO_OT_LAYOUT_BLOCKLIST return false; #endif return false; @@ -393,7 +395,7 @@ bool OT::GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED, hb_face_t *face HB_UNUSED) const { -#ifdef HB_NO_OT_LAYOUT_BLACKLIST +#ifdef HB_NO_OT_LAYOUT_BLOCKLIST return false; #endif return false; @@ -991,10 +993,46 @@ struct hb_collect_features_context_t { hb_collect_features_context_t (hb_face_t *face, hb_tag_t table_tag, - hb_set_t *feature_indexes_) + hb_set_t *feature_indices_, + const hb_tag_t *features) + : g (get_gsubgpos_table (face, table_tag)), - feature_indexes (feature_indexes_), - script_count (0),langsys_count (0), feature_index_count (0) {} + feature_indices (feature_indices_), + has_feature_filter (false), + script_count (0),langsys_count (0), feature_index_count (0) + { + compute_feature_filter (features); + } + + void compute_feature_filter (const hb_tag_t *features) + { + if (features == nullptr) + { + has_feature_filter = false; + return; + } + + has_feature_filter = true; + for (; *features; features++) + { + hb_tag_t tag = *features; + unsigned index; + g.find_feature_index (tag, &index); + if (index == OT::Index::NOT_FOUND_INDEX) continue; + + feature_indices_filter.add(index); + for (int i = (int) index - 1; i >= 0; i--) + { + if (g.get_feature_tag (i) != tag) break; + feature_indices_filter.add(i); + } + for (unsigned i = index + 1; i < g.get_feature_count (); i++) + { + if (g.get_feature_tag (i) != tag) break; + feature_indices_filter.add(i); + } + } + } bool visited (const OT::Script &s) { @@ -1043,7 +1081,9 @@ struct hb_collect_features_context_t public: const OT::GSUBGPOS &g; - hb_set_t *feature_indexes; + hb_set_t *feature_indices; + hb_set_t feature_indices_filter; + bool has_feature_filter; private: hb_set_t visited_script; @@ -1055,37 +1095,31 @@ struct hb_collect_features_context_t static void langsys_collect_features (hb_collect_features_context_t *c, - const OT::LangSys &l, - const hb_tag_t *features) + const OT::LangSys &l) { if (c->visited (l)) return; - if (!features) + if (!c->has_feature_filter) { /* All features. */ if (l.has_required_feature () && !c->visited_feature_indices (1)) - c->feature_indexes->add (l.get_required_feature_index ()); + c->feature_indices->add (l.get_required_feature_index ()); + // TODO(garretrieger): filter out indices >= feature count? if (!c->visited_feature_indices (l.featureIndex.len)) - l.add_feature_indexes_to (c->feature_indexes); + l.add_feature_indexes_to (c->feature_indices); } else { - /* Ugh. Any faster way? */ - for (; *features; features++) + if (c->feature_indices_filter.is_empty()) return; + unsigned int num_features = l.get_feature_count (); + for (unsigned int i = 0; i < num_features; i++) { - hb_tag_t feature_tag = *features; - unsigned int num_features = l.get_feature_count (); - for (unsigned int i = 0; i < num_features; i++) - { - unsigned int feature_index = l.get_feature_index (i); + unsigned int feature_index = l.get_feature_index (i); + if (!c->feature_indices_filter.has (feature_index)) continue; - if (feature_tag == c->g.get_feature_tag (feature_index)) - { - c->feature_indexes->add (feature_index); - break; - } - } + c->feature_indices->add (feature_index); + c->feature_indices_filter.del (feature_index); } } } @@ -1093,8 +1127,7 @@ langsys_collect_features (hb_collect_features_context_t *c, static void script_collect_features (hb_collect_features_context_t *c, const OT::Script &s, - const hb_tag_t *languages, - const hb_tag_t *features) + const hb_tag_t *languages) { if (c->visited (s)) return; @@ -1103,14 +1136,13 @@ script_collect_features (hb_collect_features_context_t *c, /* All languages. */ if (s.has_default_lang_sys ()) langsys_collect_features (c, - s.get_default_lang_sys (), - features); + s.get_default_lang_sys ()); + unsigned int count = s.get_lang_sys_count (); for (unsigned int language_index = 0; language_index < count; language_index++) langsys_collect_features (c, - s.get_lang_sys (language_index), - features); + s.get_lang_sys (language_index)); } else { @@ -1119,8 +1151,8 @@ script_collect_features (hb_collect_features_context_t *c, unsigned int language_index; if (s.find_lang_sys_index (*languages, &language_index)) langsys_collect_features (c, - s.get_lang_sys (language_index), - features); + s.get_lang_sys (language_index)); + } } } @@ -1151,7 +1183,7 @@ hb_ot_layout_collect_features (hb_face_t *face, const hb_tag_t *features, hb_set_t *feature_indexes /* OUT */) { - hb_collect_features_context_t c (face, table_tag, feature_indexes); + hb_collect_features_context_t c (face, table_tag, feature_indexes, features); if (!scripts) { /* All scripts. */ @@ -1159,8 +1191,7 @@ hb_ot_layout_collect_features (hb_face_t *face, for (unsigned int script_index = 0; script_index < count; script_index++) script_collect_features (&c, c.g.get_script (script_index), - languages, - features); + languages); } else { @@ -1170,8 +1201,7 @@ hb_ot_layout_collect_features (hb_face_t *face, if (c.g.find_script_index (*scripts, &script_index)) script_collect_features (&c, c.g.get_script (script_index), - languages, - features); + languages); } } } @@ -1358,7 +1388,8 @@ hb_ot_layout_has_substitution (hb_face_t *face) * @lookup_index: The index of the lookup to query * @glyphs: The sequence of glyphs to query for substitution * @glyphs_length: The length of the glyph sequence - * @zero_context: #hb_bool_t indicating whether substitutions should be context-free + * @zero_context: #hb_bool_t indicating whether pre-/post-context are disallowed + * in substitutions * * Tests whether a specified lookup in the specified face would * trigger a substitution on the given glyph sequence. @@ -1855,27 +1886,20 @@ apply_string (OT::hb_ot_apply_context_t *c, if (likely (!lookup.is_reverse ())) { /* in/out forward substitution/positioning */ - if (Proxy::table_index == 0u) + if (!Proxy::inplace) buffer->clear_output (); - buffer->idx = 0; - bool ret; - ret = apply_forward (c, accel); - if (ret) - { - if (!Proxy::inplace) - buffer->swap_buffers (); - else - assert (!buffer->has_separate_output ()); - } + buffer->idx = 0; + apply_forward (c, accel); + + if (!Proxy::inplace) + buffer->swap_buffers (); } else { /* in-place backward substitution/positioning */ - if (Proxy::table_index == 0u) - buffer->remove_output (); + assert (!buffer->have_output); buffer->idx = buffer->len - 1; - apply_backward (c, accel); } } @@ -1891,7 +1915,8 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, OT::hb_ot_apply_context_t c (table_index, font, buffer); c.set_recurse_func (Proxy::Lookup::apply_recurse_func); - for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++) { + for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++) + { const stage_map_t *stage = &stages[table_index][stage_index]; for (; i < stage->last_lookup; i++) { @@ -1901,11 +1926,8 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, c.set_lookup_mask (lookups[table_index][i].mask); c.set_auto_zwj (lookups[table_index][i].auto_zwj); c.set_auto_zwnj (lookups[table_index][i].auto_zwnj); - if (lookups[table_index][i].random) - { - c.set_random (true); - buffer->unsafe_to_break_all (); - } + c.set_random (lookups[table_index][i].random); + apply_string (&c, proxy.table.get_lookup (lookup_index), proxy.accels[lookup_index]); @@ -1913,10 +1935,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, } if (stage->pause_func) - { - buffer->clear_output (); stage->pause_func (plan, font, buffer); - } } } diff --git a/gfx/harfbuzz/src/hb-ot-map.cc b/gfx/harfbuzz/src/hb-ot-map.cc index e4bb4b636619..12ceea57850a 100644 --- a/gfx/harfbuzz/src/hb-ot-map.cc +++ b/gfx/harfbuzz/src/hb-ot-map.cc @@ -54,7 +54,6 @@ hb_ot_map_builder_t::hb_ot_map_builder_t (hb_face_t *face_, face = face_; props = *props_; - /* Fetch script/language indices for GSUB/GPOS. We need these later to skip * features not available in either table and not waste precious bits for them. */ @@ -63,12 +62,28 @@ hb_ot_map_builder_t::hb_ot_map_builder_t (hb_face_t *face_, hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; - hb_ot_tags_from_script_and_language (props.script, props.language, &script_count, script_tags, &language_count, language_tags); + hb_ot_tags_from_script_and_language (props.script, + props.language, + &script_count, + script_tags, + &language_count, + language_tags); - for (unsigned int table_index = 0; table_index < 2; table_index++) { + for (unsigned int table_index = 0; table_index < 2; table_index++) + { hb_tag_t table_tag = table_tags[table_index]; - found_script[table_index] = (bool) hb_ot_layout_table_select_script (face, table_tag, script_count, script_tags, &script_index[table_index], &chosen_script[table_index]); - hb_ot_layout_script_select_language (face, table_tag, script_index[table_index], language_count, language_tags, &language_index[table_index]); + found_script[table_index] = (bool) hb_ot_layout_table_select_script (face, + table_tag, + script_count, + script_tags, + &script_index[table_index], + &chosen_script[table_index]); + hb_ot_layout_script_select_language (face, + table_tag, + script_index[table_index], + language_count, + language_tags, + &language_index[table_index]); } } @@ -150,9 +165,8 @@ void hb_ot_map_builder_t::compile (hb_ot_map_t &m, const hb_ot_shape_plan_key_t &key) { - static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), ""); - unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1; - unsigned int global_bit_shift = hb_popcount (HB_GLYPH_FLAG_DEFINED); + unsigned int global_bit_shift = 8 * sizeof (hb_mask_t) - 1; + unsigned int global_bit_mask = 1u << global_bit_shift; m.global_mask = global_bit_mask; @@ -205,7 +219,8 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, /* Allocate bits now */ - unsigned int next_bit = global_bit_shift + 1; + static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), ""); + unsigned int next_bit = hb_popcount (HB_GLYPH_FLAG_DEFINED) + 1; for (unsigned int i = 0; i < feature_infos.length; i++) { @@ -220,7 +235,7 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, /* Limit bits per feature. */ bits_needed = hb_min (HB_OT_MAP_MAX_BITS, hb_bit_storage (info->max_value)); - if (!info->max_value || next_bit + bits_needed > 8 * sizeof (hb_mask_t)) + if (!info->max_value || next_bit + bits_needed >= global_bit_shift) continue; /* Feature disabled, or not enough bits. */ @@ -274,7 +289,6 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, } map->_1_mask = (1u << map->shift) & map->mask; map->needs_fallback = !found; - } feature_infos.shrink (0); /* Done with these */ diff --git a/gfx/harfbuzz/src/hb-ot-maxp-table.hh b/gfx/harfbuzz/src/hb-ot-maxp-table.hh index 929956d12bf5..3a019ef7821e 100644 --- a/gfx/harfbuzz/src/hb-ot-maxp-table.hh +++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh @@ -107,7 +107,7 @@ struct maxp maxpV1Tail *dest_v1 = c->serializer->embed (src_v1); if (unlikely (!dest_v1)) return_trace (false); - if (c->plan->drop_hints) + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) drop_hint_fields (dest_v1); } diff --git a/gfx/harfbuzz/src/hb-ot-name-table.hh b/gfx/harfbuzz/src/hb-ot-name-table.hh index 5c5c58eac047..c17bb4abb81a 100644 --- a/gfx/harfbuzz/src/hb-ot-name-table.hh +++ b/gfx/harfbuzz/src/hb-ot-name-table.hh @@ -214,7 +214,7 @@ struct name this->format = 0; this->count = it.len (); - NameRecord *name_records = (NameRecord *) calloc (it.len (), NameRecord::static_size); + NameRecord *name_records = (NameRecord *) hb_calloc (it.len (), NameRecord::static_size); if (unlikely (!name_records)) return_trace (false); hb_array_t records (name_records, it.len ()); @@ -228,7 +228,7 @@ struct name records.qsort (); c->copy_all (records, src_string_pool); - free (records.arrayZ); + hb_free (records.arrayZ); if (unlikely (c->ran_out_of_room ())) return_trace (false); @@ -249,7 +249,11 @@ struct name + nameRecordZ.as_array (count) | hb_filter (c->plan->name_ids, &NameRecord::nameID) | hb_filter (c->plan->name_languages, &NameRecord::languageID) - | hb_filter ([&] (const NameRecord& namerecord) { return c->plan->name_legacy || namerecord.isUnicode (); }) + | hb_filter ([&] (const NameRecord& namerecord) { + return + (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY) + || namerecord.isUnicode (); + }) ; name_prime->serialize (c->serializer, it, hb_addressof (this + stringOffset)); diff --git a/gfx/harfbuzz/src/hb-ot-name.cc b/gfx/harfbuzz/src/hb-ot-name.cc index 4588226e6e92..eff46ef227bc 100644 --- a/gfx/harfbuzz/src/hb-ot-name.cc +++ b/gfx/harfbuzz/src/hb-ot-name.cc @@ -156,7 +156,8 @@ hb_ot_name_get_utf (hb_face_t *face, * * Fetches a font name from the OpenType 'name' table. * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. - * Returns string in UTF-8 encoding. + * Returns string in UTF-8 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. * * Returns: full length of the requested string, or 0 if not found. * Since: 2.1.0 @@ -183,7 +184,8 @@ hb_ot_name_get_utf8 (hb_face_t *face, * * Fetches a font name from the OpenType 'name' table. * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. - * Returns string in UTF-16 encoding. + * Returns string in UTF-16 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. * * Returns: full length of the requested string, or 0 if not found. * Since: 2.1.0 @@ -209,7 +211,8 @@ hb_ot_name_get_utf16 (hb_face_t *face, * * Fetches a font name from the OpenType 'name' table. * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. - * Returns string in UTF-32 encoding. + * Returns string in UTF-32 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. * * Returns: full length of the requested string, or 0 if not found. * Since: 2.1.0 diff --git a/gfx/harfbuzz/src/hb-ot-os2-table.hh b/gfx/harfbuzz/src/hb-ot-os2-table.hh index 8e98f87f4e9f..f0035e2f04ef 100644 --- a/gfx/harfbuzz/src/hb-ot-os2-table.hh +++ b/gfx/harfbuzz/src/hb-ot-os2-table.hh @@ -30,7 +30,6 @@ #include "hb-open-type.hh" #include "hb-ot-os2-unicode-ranges.hh" -#include "hb-ot-cmap-table.hh" #include "hb-set.hh" @@ -172,33 +171,17 @@ struct OS2 TRACE_SUBSET (this); OS2 *os2_prime = c->serializer->embed (this); if (unlikely (!os2_prime)) return_trace (false); + if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) + return_trace (true); - hb_set_t unicodes; - if (!c->plan->glyphs_requested->is_empty ()) - { - hb_map_t unicode_glyphid_map; - - OT::cmap::accelerator_t cmap; - cmap.init (c->plan->source); - cmap.collect_mapping (&unicodes, &unicode_glyphid_map); - cmap.fini (); - - hb_set_set (&unicodes, c->plan->unicodes); - - + unicode_glyphid_map.iter () - | hb_filter (c->plan->glyphs_requested, hb_second) - | hb_map (hb_first) - | hb_sink (unicodes) - ; - } /* when --gids option is not used, no need to do collect_mapping that is * iterating all codepoints in each subtable, which is not efficient */ uint16_t min_cp, max_cp; - find_min_and_max_codepoint (unicodes.is_empty () ? c->plan->unicodes : &unicodes, &min_cp, &max_cp); + find_min_and_max_codepoint (c->plan->unicodes, &min_cp, &max_cp); os2_prime->usFirstCharIndex = min_cp; os2_prime->usLastCharIndex = max_cp; - _update_unicode_ranges (unicodes.is_empty () ? c->plan->unicodes : &unicodes, os2_prime->ulUnicodeRange); + _update_unicode_ranges (c->plan->unicodes, os2_prime->ulUnicodeRange); return_trace (true); } diff --git a/gfx/harfbuzz/src/hb-ot-post-table-v2subset.hh b/gfx/harfbuzz/src/hb-ot-post-table-v2subset.hh new file mode 100644 index 000000000000..94450eb53ada --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-post-table-v2subset.hh @@ -0,0 +1,130 @@ +/* + * Copyright © 2021 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. + * + */ + +#ifndef HB_OT_POST_TABLE_V2SUBSET_HH +#define HB_OT_POST_TABLE_V2SUBSET_HH + +#include "hb-open-type.hh" +#include "hb-ot-post-table.hh" + +/* + * post -- PostScript + * https://docs.microsoft.com/en-us/typography/opentype/spec/post + */ + +namespace OT { +template +HB_INTERNAL bool postV2Tail::serialize (hb_serialize_context_t *c, + Iterator it, + const void* _post) const +{ + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->check_success (out))) return_trace (false); + if (!out->glyphNameIndex.serialize (c, + it + | hb_map (hb_second))) + return_trace (false); + + hb_set_t copied_indices; + for (const auto& _ : + it ) + { + unsigned glyph_id = _.first; + unsigned new_index = _.second; + + if (new_index < 258) continue; + if (copied_indices.has (new_index)) continue; + copied_indices.add (new_index); + + hb_bytes_t s = reinterpret_cast (_post)->find_glyph_name (glyph_id); + HBUINT8 *o = c->allocate_size (HBUINT8::static_size * (s.length + 1)); + if (unlikely (!o)) return_trace (false); + if (!c->check_assign (o[0], s.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + memcpy (o+1, s.arrayZ, HBUINT8::static_size * s.length); + } + + return_trace (true); +} + +HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + unsigned num_glyphs = c->plan->num_output_glyphs (); + hb_map_t old_new_index_map, old_gid_new_index_map; + unsigned i = 0; + + post::accelerator_t _post; + _post.init (c->plan->source); + + for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + unsigned old_index = glyphNameIndex[old_gid]; + + unsigned new_index; + if (old_index <= 257) new_index = old_index; + else if (old_new_index_map.has (old_index)) new_index = old_new_index_map.get (old_index); + else + { + hb_bytes_t s = _post.find_glyph_name (old_gid); + int standard_glyph_index = -1; + for (unsigned i = 0; i < format1_names_length; i++) + { + if (s == format1_names (i)) + { + standard_glyph_index = i; + break; + } + } + if (standard_glyph_index == -1) + { + new_index = 258 + i; + i++; + } + else + { new_index = standard_glyph_index; } + old_new_index_map.set (old_index, new_index); + } + old_gid_new_index_map.set (old_gid, new_index); + } + + auto index_iter = + + hb_range (num_glyphs) + | hb_map (reverse_glyph_map) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + unsigned new_index = old_gid_new_index_map.get (old_gid); + return hb_pair_t (old_gid, new_index); + }) + ; + + bool ret = serialize (c->serializer, index_iter, &_post); + _post.fini (); + return_trace (ret); +} + +} /* namespace OT */ +#endif /* HB_OT_POST_TABLE_V2SUBSET_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-post-table.hh b/gfx/harfbuzz/src/hb-ot-post-table.hh index d14626494143..39de6717072d 100644 --- a/gfx/harfbuzz/src/hb-ot-post-table.hh +++ b/gfx/harfbuzz/src/hb-ot-post-table.hh @@ -55,6 +55,13 @@ struct postV2Tail return_trace (glyphNameIndex.sanitize (c)); } + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void* _post) const; + + bool subset (hb_subset_context_t *c) const; + protected: Array16Of glyphNameIndex; /* This is not an offset, but is the * ordinal number of the glyph in 'post' @@ -71,13 +78,18 @@ struct post { static constexpr hb_tag_t tableTag = HB_OT_TAG_post; - void serialize (hb_serialize_context_t *c) const + bool serialize (hb_serialize_context_t *c, bool glyph_names) const { + TRACE_SERIALIZE (this); post *post_prime = c->allocate_min (); - if (unlikely (!post_prime)) return; + if (unlikely (!post_prime)) return_trace (false); memcpy (post_prime, this, post::min_size); - post_prime->version.major = 3; // Version 3 does not have any glyph names. + if (!glyph_names) + return_trace (c->check_assign (post_prime->version.major, 3, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); // Version 3 does not have any glyph names. + + return_trace (true); } bool subset (hb_subset_context_t *c) const @@ -86,13 +98,19 @@ struct post post *post_prime = c->serializer->start_embed (); if (unlikely (!post_prime)) return_trace (false); - serialize (c->serializer); + bool glyph_names = c->plan->flags & HB_SUBSET_FLAGS_GLYPH_NAMES; + if (!serialize (c->serializer, glyph_names)) + return_trace (false); + + if (glyph_names && version.major == 2) + return_trace (v2X.subset (c)); return_trace (true); } struct accelerator_t { + friend struct postV2Tail; void init (hb_face_t *face) { index_to_offset.init (); @@ -117,7 +135,7 @@ struct post void fini () { index_to_offset.fini (); - free (gids_sorted_by_name.get ()); + hb_free (gids_sorted_by_name.get ()); table.destroy (); } @@ -148,7 +166,7 @@ struct post if (unlikely (!gids)) { - gids = (uint16_t *) malloc (count * sizeof (gids[0])); + gids = (uint16_t *) hb_malloc (count * sizeof (gids[0])); if (unlikely (!gids)) return false; /* Anything better?! */ @@ -158,7 +176,7 @@ struct post if (unlikely (!gids_sorted_by_name.cmpexch (nullptr, gids))) { - free (gids); + hb_free (gids); goto retry; } } diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh index a04011902027..2b3b134ae32b 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh @@ -290,7 +290,7 @@ static arabic_fallback_plan_t * arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, hb_font_t *font) { - arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t)); + arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_calloc (1, sizeof (arabic_fallback_plan_t)); if (unlikely (!fallback_plan)) return const_cast (&Null (arabic_fallback_plan_t)); @@ -308,7 +308,7 @@ arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, return fallback_plan; assert (fallback_plan->num_lookups == 0); - free (fallback_plan); + hb_free (fallback_plan); return const_cast (&Null (arabic_fallback_plan_t)); } @@ -323,10 +323,10 @@ arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) { fallback_plan->accel_array[i].fini (); if (fallback_plan->free_lookups) - free (fallback_plan->lookup_array[i]); + hb_free (fallback_plan->lookup_array[i]); } - free (fallback_plan); + hb_free (fallback_plan); } static void diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc index 1f244f940cbd..1f8c1410fc65 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc @@ -259,7 +259,7 @@ struct arabic_shape_plan_t void * data_create_arabic (const hb_ot_shape_plan_t *plan) { - arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (arabic_shape_plan_t)); + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) hb_calloc (1, sizeof (arabic_shape_plan_t)); if (unlikely (!arabic_plan)) return nullptr; @@ -282,7 +282,7 @@ data_destroy_arabic (void *data) arabic_fallback_plan_destroy (arabic_plan->fallback_plan); - free (data); + hb_free (data); } static void diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc index dbedd6af0c60..0d84a76b85f3 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc @@ -80,7 +80,7 @@ struct hangul_shape_plan_t static void * data_create_hangul (const hb_ot_shape_plan_t *plan) { - hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) calloc (1, sizeof (hangul_shape_plan_t)); + hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) hb_calloc (1, sizeof (hangul_shape_plan_t)); if (unlikely (!hangul_plan)) return nullptr; @@ -93,7 +93,7 @@ data_create_hangul (const hb_ot_shape_plan_t *plan) static void data_destroy_hangul (void *data) { - free (data); + hb_free (data); } /* Constants for algorithmic hangul syllable [de]composition. */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc index a4f2d9a847ce..0983a32848fc 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc @@ -106,7 +106,8 @@ indic_features[] = { /* * Basic features. - * These features are applied in order, one at a time, after initial_reordering. + * These features are applied in order, one at a time, after initial_reordering, + * constrained to the syllable. */ {HB_TAG('n','u','k','t'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('a','k','h','n'), F_GLOBAL_MANUAL_JOINERS}, @@ -121,8 +122,8 @@ indic_features[] = {HB_TAG('c','j','c','t'), F_GLOBAL_MANUAL_JOINERS}, /* * Other features. - * These features are applied all at once, after final_reordering - * but before clearing syllables. + * These features are applied all at once, after final_reordering, constrained + * to the syllable. * Default Bengali font in Windows for example has intermixed * lookups for init,pres,abvs,blws features. */ @@ -257,7 +258,7 @@ struct indic_shape_plan_t static void * data_create_indic (const hb_ot_shape_plan_t *plan) { - indic_shape_plan_t *indic_plan = (indic_shape_plan_t *) calloc (1, sizeof (indic_shape_plan_t)); + indic_shape_plan_t *indic_plan = (indic_shape_plan_t *) hb_calloc (1, sizeof (indic_shape_plan_t)); if (unlikely (!indic_plan)) return nullptr; @@ -300,7 +301,7 @@ data_create_indic (const hb_ot_shape_plan_t *plan) static void data_destroy_indic (void *data) { - free (data); + hb_free (data); } static indic_position_t @@ -960,7 +961,8 @@ initial_reordering_indic (const hb_ot_shape_plan_t *plan, hb_syllabic_insert_dotted_circles (font, buffer, indic_broken_cluster, OT_DOTTEDCIRCLE, - OT_Repha); + OT_Repha, + POS_END); foreach_syllable (buffer, start, end) initial_reordering_syllable_indic (plan, font->face, buffer, start, end); diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc index dddba142a3f4..77878868576a 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc @@ -42,7 +42,8 @@ khmer_features[] = { /* * Basic features. - * These features are applied in order, one at a time, after reordering. + * These features are applied all at once, before reordering, constrained + * to the syllable. */ {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS}, {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS}, @@ -147,7 +148,7 @@ struct khmer_shape_plan_t static void * data_create_khmer (const hb_ot_shape_plan_t *plan) { - khmer_shape_plan_t *khmer_plan = (khmer_shape_plan_t *) calloc (1, sizeof (khmer_shape_plan_t)); + khmer_shape_plan_t *khmer_plan = (khmer_shape_plan_t *) hb_calloc (1, sizeof (khmer_shape_plan_t)); if (unlikely (!khmer_plan)) return nullptr; @@ -161,7 +162,7 @@ data_create_khmer (const hb_ot_shape_plan_t *plan) static void data_destroy_khmer (void *data) { - free (data); + hb_free (data); } static void diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc index bc5dcb904cbc..6e92a9b0ae5e 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc @@ -41,7 +41,8 @@ myanmar_basic_features[] = { /* * Basic features. - * These features are applied in order, one at a time, after reordering. + * These features are applied in order, one at a time, after reordering, + * constrained to the syllable. */ HB_TAG('r','p','h','f'), HB_TAG('p','r','e','f'), diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-syllabic.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-syllabic.cc index 46509abee231..5a08f878dc0c 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-syllabic.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-syllabic.cc @@ -34,7 +34,8 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font, hb_buffer_t *buffer, unsigned int broken_syllable_type, unsigned int dottedcircle_category, - int repha_category) + int repha_category, + int dottedcircle_position) { if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) return; @@ -61,6 +62,8 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font, hb_glyph_info_t dottedcircle = {0}; dottedcircle.codepoint = 0x25CCu; dottedcircle.complex_var_u8_category() = dottedcircle_category; + if (dottedcircle_position != -1) + dottedcircle.complex_var_u8_auxiliary() = dottedcircle_position; dottedcircle.codepoint = dottedcircle_glyph; buffer->clear_output (); diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-syllabic.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-syllabic.hh index c80b8fee1d14..b901a660d364 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-syllabic.hh +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-syllabic.hh @@ -35,7 +35,8 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font, hb_buffer_t *buffer, unsigned int broken_syllable_type, unsigned int dottedcircle_category, - int repha_category = -1); + int repha_category = -1, + int dottedcircle_position = -1); #endif /* HB_OT_SHAPE_COMPLEX_SYLLABIC_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh index b4b2b7510053..96bebcef0ca1 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh @@ -375,7 +375,9 @@ hb_iter_with_fallback_t, typename Iter::item_t> { machine_index_t (const Iter& it) : it (it) {} - machine_index_t (const machine_index_t& o) : it (o.it) {} + machine_index_t (const machine_index_t& o) : + hb_iter_with_fallback_t, typename Iter::item_t>(o), + it (o.it) {} static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc index 0d0b7e771e5a..1e4804c4a25d 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc @@ -47,7 +47,8 @@ use_basic_features[] = { /* * Basic features. - * These features are applied all at once, before reordering. + * These features are applied all at once, before reordering, constrained + * to the syllable. */ HB_TAG('r','k','r','f'), HB_TAG('a','b','v','f'), @@ -154,7 +155,7 @@ struct use_shape_plan_t static void * data_create_use (const hb_ot_shape_plan_t *plan) { - use_shape_plan_t *use_plan = (use_shape_plan_t *) calloc (1, sizeof (use_shape_plan_t)); + use_shape_plan_t *use_plan = (use_shape_plan_t *) hb_calloc (1, sizeof (use_shape_plan_t)); if (unlikely (!use_plan)) return nullptr; @@ -165,7 +166,7 @@ data_create_use (const hb_ot_shape_plan_t *plan) use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan); if (unlikely (!use_plan->arabic_plan)) { - free (use_plan); + hb_free (use_plan); return nullptr; } } @@ -181,7 +182,7 @@ data_destroy_use (void *data) if (use_plan->arabic_plan) data_destroy_arabic (use_plan->arabic_plan); - free (data); + hb_free (data); } static void diff --git a/gfx/harfbuzz/src/hb-ot-shape.cc b/gfx/harfbuzz/src/hb-ot-shape.cc index 68c512c7d56f..7edb1ea71ed8 100644 --- a/gfx/harfbuzz/src/hb-ot-shape.cc +++ b/gfx/harfbuzz/src/hb-ot-shape.cc @@ -149,13 +149,17 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, * Decide who does positioning. GPOS, kerx, kern, or fallback. */ - if (0) +#ifndef HB_NO_AAT_SHAPE + bool has_gsub = hb_ot_layout_has_substitution (face); +#endif + bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face); + if (false) ; #ifndef HB_NO_AAT_SHAPE - else if (hb_aat_layout_has_positioning (face)) + else if (hb_aat_layout_has_positioning (face) && !(has_gsub && has_gpos)) plan.apply_kerx = true; #endif - else if (!apply_morx && !disable_gpos && hb_ot_layout_has_positioning (face)) + else if (!apply_morx && has_gpos) plan.apply_gpos = true; if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) @@ -172,6 +176,8 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, #endif } + plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); + plan.zero_marks = script_zero_marks && !plan.apply_kerx && (!plan.apply_kern @@ -272,11 +278,12 @@ hb_ot_shape_plan_t::position (hb_font_t *font, else if (this->apply_kerx) hb_aat_layout_position (this, font, buffer); #endif + #ifndef HB_NO_OT_KERN - else if (this->apply_kern) + if (this->apply_kern) hb_ot_layout_kern (this, font, buffer); #endif - else + else if (this->apply_fallback_kern) _hb_ot_shape_fallback_kern (this, font, buffer); #ifndef HB_NO_AAT_SHAPE @@ -312,16 +319,17 @@ horizontal_features[] = }; static void -hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, - const hb_feature_t *user_features, - unsigned int num_user_features) +hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, + const hb_feature_t *user_features, + unsigned int num_user_features) { hb_ot_map_builder_t *map = &planner->map; map->enable_feature (HB_TAG('r','v','r','n')); map->add_gsub_pause (nullptr); - switch (planner->props.direction) { + switch (planner->props.direction) + { case HB_DIRECTION_LTR: map->enable_feature (HB_TAG ('l','t','r','a')); map->enable_feature (HB_TAG ('l','t','r','m')); @@ -369,6 +377,10 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, map->add_feature (horizontal_features[i]); else { + /* We only apply `vert` feature. See: + * https://github.com/harfbuzz/harfbuzz/commit/d71c0df2d17f4590d5611239577a6cb532c26528 + * https://lists.freedesktop.org/archives/harfbuzz/2013-August/003490.html */ + /* We really want to find a 'vert' feature if there's any in the font, no * matter which script/langsys it is listed (or not) under. * See various bugs referenced from: @@ -484,6 +496,14 @@ hb_set_unicode_props (hb_buffer_t *buffer) if (unlikely (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL && hb_in_range (info[i].codepoint, 0x1F3FBu, 0x1F3FFu))) { + _hb_glyph_info_set_continuation (&info[i]); + } + /* Regional_Indicators are hairy as hell... + * https://github.com/harfbuzz/harfbuzz/issues/2265 */ + else if (unlikely (i && hb_in_range (info[i].codepoint, 0x1F1E6u, 0x1F1FFu))) + { + if (hb_in_range (info[i - 1].codepoint, 0x1F1E6u, 0x1F1FFu) && + !_hb_glyph_info_is_continuation (&info[i - 1])) _hb_glyph_info_set_continuation (&info[i]); } #ifndef HB_NO_EMOJI_SEQUENCES @@ -541,6 +561,7 @@ hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) info.cluster = buffer->cur().cluster; info.mask = buffer->cur().mask; (void) buffer->output_info (info); + buffer->swap_buffers (); } @@ -564,6 +585,36 @@ hb_ensure_native_direction (hb_buffer_t *buffer) hb_direction_t direction = buffer->props.direction; hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); + /* Numeric runs in natively-RTL scripts are actually native-LTR, so we reset + * the horiz_dir if the run contains at least one decimal-number char, and no + * letter chars (ideally we should be checking for chars with strong + * directionality but hb-unicode currently lacks bidi categories). + * + * This allows digit sequences in Arabic etc to be shaped in "native" + * direction, so that features like ligatures will work as intended. + * + * https://github.com/harfbuzz/harfbuzz/issues/501 + */ + if (unlikely (horiz_dir == HB_DIRECTION_RTL && direction == HB_DIRECTION_LTR)) + { + bool found_number = false, found_letter = false; + const auto* info = buffer->info; + const auto count = buffer->len; + for (unsigned i = 0; i < count; i++) + { + auto gc = _hb_glyph_info_get_general_category (&info[i]); + if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + found_number = true; + else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) + { + found_letter = true; + break; + } + } + if (found_number && !found_letter) + horiz_dir = HB_DIRECTION_LTR; + } + /* TODO vertical: * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType * Ogham fonts are supposed to be implemented BTT or not. Need to research that @@ -1117,8 +1168,6 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c) _hb_buffer_allocate_unicode_vars (c->buffer); - c->buffer->clear_output (); - hb_ot_shape_initialize_masks (c); hb_set_unicode_props (c->buffer); hb_insert_dotted_circle (c->buffer, c->font); @@ -1128,7 +1177,8 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c) hb_ensure_native_direction (c->buffer); if (c->plan->shaper->preprocess_text && - c->buffer->message(c->font, "start preprocess-text")) { + c->buffer->message(c->font, "start preprocess-text")) + { c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); (void) c->buffer->message(c->font, "end preprocess-text"); } diff --git a/gfx/harfbuzz/src/hb-ot-shape.hh b/gfx/harfbuzz/src/hb-ot-shape.hh index acc98772a95f..e8c81015c792 100644 --- a/gfx/harfbuzz/src/hb-ot-shape.hh +++ b/gfx/harfbuzz/src/hb-ot-shape.hh @@ -112,6 +112,7 @@ struct hb_ot_shape_plan_t #else static constexpr bool apply_kern = false; #endif + bool apply_fallback_kern : 1; #ifndef HB_NO_AAT_SHAPE bool apply_kerx : 1; bool apply_morx : 1; diff --git a/gfx/harfbuzz/src/hb-ot-tag-table.hh b/gfx/harfbuzz/src/hb-ot-tag-table.hh index 87830b546266..fc9bffc23f57 100644 --- a/gfx/harfbuzz/src/hb-ot-tag-table.hh +++ b/gfx/harfbuzz/src/hb-ot-tag-table.hh @@ -6,8 +6,8 @@ * * on files with these headers: * - * - * File-Date: 2021-03-05 + * + * File-Date: 2021-08-06 */ #ifndef HB_OT_TAG_TABLE_HH @@ -93,6 +93,7 @@ static const LangTag ot_languages[] = { {"auz", HB_TAG('A','R','A',' ')}, /* Uzbeki Arabic -> Arabic */ {"av", HB_TAG('A','V','R',' ')}, /* Avaric -> Avar */ {"avl", HB_TAG('A','R','A',' ')}, /* Eastern Egyptian Bedawi Arabic -> Arabic */ +/*{"avn", HB_TAG('A','V','N',' ')},*/ /* Avatime */ /*{"awa", HB_TAG('A','W','A',' ')},*/ /* Awadhi */ {"ay", HB_TAG('A','Y','M',' ')}, /* Aymara [macrolanguage] */ {"ayc", HB_TAG('A','Y','M',' ')}, /* Southern Aymara -> Aymara */ @@ -345,6 +346,7 @@ static const LangTag ot_languages[] = { {"cth", HB_TAG('Q','I','N',' ')}, /* Thaiphum Chin -> Chin */ {"ctl", HB_TAG('C','C','H','N')}, /* Tlacoatzintepec Chinantec -> Chinantec */ {"cts", HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol -> Bikol */ +/*{"ctt", HB_TAG('C','T','T',' ')},*/ /* Wayanad Chetti */ {"ctu", HB_TAG('M','Y','N',' ')}, /* Chol -> Mayan */ {"cu", HB_TAG('C','S','L',' ')}, /* Church Slavonic */ {"cuc", HB_TAG('C','C','H','N')}, /* Usila Chinantec -> Chinantec */ @@ -537,23 +539,27 @@ static const LangTag ot_languages[] = { {"ha", HB_TAG('H','A','U',' ')}, /* Hausa */ {"haa", HB_TAG('A','T','H',' ')}, /* Han -> Athapaskan */ {"hae", HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */ - {"hai", HB_TAG_NONE }, /* Haida [macrolanguage] != Haitian (Haitian Creole) */ + {"hai", HB_TAG('H','A','I','0')}, /* Haida [macrolanguage] */ {"hak", HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese, Simplified */ {"hal", HB_TAG_NONE }, /* Halang != Halam (Falam Chin) */ {"har", HB_TAG('H','R','I',' ')}, /* Harari */ /*{"haw", HB_TAG('H','A','W',' ')},*/ /* Hawaiian */ + {"hax", HB_TAG('H','A','I','0')}, /* Southern Haida -> Haida */ /*{"hay", HB_TAG('H','A','Y',' ')},*/ /* Haya */ /*{"haz", HB_TAG('H','A','Z',' ')},*/ /* Hazaragi */ {"hbn", HB_TAG_NONE }, /* Heiban != Hammer-Banna */ {"hca", HB_TAG('C','P','P',' ')}, /* Andaman Creole Hindi -> Creoles */ + {"hdn", HB_TAG('H','A','I','0')}, /* Northern Haida -> Haida */ {"he", HB_TAG('I','W','R',' ')}, /* Hebrew */ {"hea", HB_TAG('H','M','N',' ')}, /* Northern Qiandong Miao -> Hmong */ +/*{"hei", HB_TAG('H','E','I',' ')},*/ /* Heiltsuk */ {"hi", HB_TAG('H','I','N',' ')}, /* Hindi */ /*{"hil", HB_TAG('H','I','L',' ')},*/ /* Hiligaynon */ {"hji", HB_TAG('M','L','Y',' ')}, /* Haji -> Malay */ {"hlt", HB_TAG('Q','I','N',' ')}, /* Matu Chin -> Chin */ {"hma", HB_TAG('H','M','N',' ')}, /* Southern Mashan Hmong -> Hmong */ {"hmc", HB_TAG('H','M','N',' ')}, /* Central Huishui Hmong -> Hmong */ + {"hmd", HB_TAG('H','M','D',' ')}, /* Large Flowery Miao -> A-Hmao */ {"hmd", HB_TAG('H','M','N',' ')}, /* Large Flowery Miao -> Hmong */ {"hme", HB_TAG('H','M','N',' ')}, /* Eastern Huishui Hmong -> Hmong */ {"hmg", HB_TAG('H','M','N',' ')}, /* Southwestern Guiyang Hmong -> Hmong */ @@ -569,6 +575,7 @@ static const LangTag ot_languages[] = { {"hms", HB_TAG('H','M','N',' ')}, /* Southern Qiandong Miao -> Hmong */ {"hmw", HB_TAG('H','M','N',' ')}, /* Western Mashan Hmong -> Hmong */ {"hmy", HB_TAG('H','M','N',' ')}, /* Southern Guiyang Hmong -> Hmong */ + {"hmz", HB_TAG('H','M','Z',' ')}, /* Hmong Shua -> Hmong Shuat */ {"hmz", HB_TAG('H','M','N',' ')}, /* Hmong Shua -> Hmong */ /*{"hnd", HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */ {"hne", HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */ @@ -625,6 +632,7 @@ static const LangTag ot_languages[] = { {"inh", HB_TAG('I','N','G',' ')}, /* Ingush */ {"io", HB_TAG('I','D','O',' ')}, /* Ido */ {"iri", HB_TAG_NONE }, /* Rigwe != Irish */ +/*{"iru", HB_TAG('I','R','U',' ')},*/ /* Irula */ {"is", HB_TAG('I','S','L',' ')}, /* Icelandic */ {"ism", HB_TAG_NONE }, /* Masimasi != Inari Sami */ {"it", HB_TAG('I','T','A',' ')}, /* Italian */ @@ -660,6 +668,7 @@ static const LangTag ot_languages[] = { {"kac", HB_TAG_NONE }, /* Kachin != Kachchi */ {"kam", HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ {"kar", HB_TAG('K','R','N',' ')}, /* Karen [family] */ +/*{"kaw", HB_TAG('K','A','W',' ')},*/ /* Kawi (Old Javanese) */ {"kbd", HB_TAG('K','A','B',' ')}, /* Kabardian */ {"kby", HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */ {"kca", HB_TAG('K','H','K',' ')}, /* Khanty -> Khanty-Kazim */ @@ -779,6 +788,7 @@ static const LangTag ot_languages[] = { {"kvu", HB_TAG('K','R','N',' ')}, /* Yinbaw Karen -> Karen */ {"kvy", HB_TAG('K','R','N',' ')}, /* Yintale Karen -> Karen */ {"kw", HB_TAG('C','O','R',' ')}, /* Cornish */ +/*{"kwk", HB_TAG('K','W','K',' ')},*/ /* Kwakiutl -> Kwakʼwala */ {"kww", HB_TAG('C','P','P',' ')}, /* Kwinti -> Creoles */ {"kwy", HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */ {"kxc", HB_TAG('K','M','S',' ')}, /* Konso -> Komso */ @@ -806,6 +816,7 @@ static const LangTag ot_languages[] = { {"lcf", HB_TAG('M','L','Y',' ')}, /* Lubu -> Malay */ {"ldi", HB_TAG('K','O','N','0')}, /* Laari -> Kongo */ {"ldk", HB_TAG_NONE }, /* Leelau != Ladakhi */ +/*{"lef", HB_TAG('L','E','F',' ')},*/ /* Lelemi */ /*{"lez", HB_TAG('L','E','Z',' ')},*/ /* Lezghian -> Lezgi */ {"lg", HB_TAG('L','U','G',' ')}, /* Ganda */ {"li", HB_TAG('L','I','M',' ')}, /* Limburgish */ @@ -832,6 +843,7 @@ static const LangTag ot_languages[] = { {"lo", HB_TAG('L','A','O',' ')}, /* Lao */ /*{"lom", HB_TAG('L','O','M',' ')},*/ /* Loma (Liberia) */ {"lou", HB_TAG('C','P','P',' ')}, /* Louisiana Creole -> Creoles */ +/*{"lpo", HB_TAG('L','P','O',' ')},*/ /* Lipo */ /*{"lrc", HB_TAG('L','R','C',' ')},*/ /* Northern Luri -> Luri */ {"lri", HB_TAG('L','U','H',' ')}, /* Marachi -> Luyia */ {"lrm", HB_TAG('L','U','H',' ')}, /* Marama -> Luyia */ @@ -1231,6 +1243,7 @@ static const LangTag ot_languages[] = { {"rbl", HB_TAG('B','I','K',' ')}, /* Miraya Bikol -> Bikol */ {"rcf", HB_TAG('C','P','P',' ')}, /* Réunion Creole French -> Creoles */ /*{"rej", HB_TAG('R','E','J',' ')},*/ /* Rejang */ +/*{"rhg", HB_TAG('R','H','G',' ')},*/ /* Rohingya */ /*{"ria", HB_TAG('R','I','A',' ')},*/ /* Riang (India) */ {"rif", HB_TAG('R','I','F',' ')}, /* Tarifit */ {"rif", HB_TAG('B','B','R',' ')}, /* Tarifit -> Berber */ @@ -1286,6 +1299,7 @@ static const LangTag ot_languages[] = { {"sek", HB_TAG('A','T','H',' ')}, /* Sekani -> Athapaskan */ /*{"sel", HB_TAG('S','E','L',' ')},*/ /* Selkup */ {"sez", HB_TAG('Q','I','N',' ')}, /* Senthang Chin -> Chin */ + {"sfm", HB_TAG('S','F','M',' ')}, /* Small Flowery Miao */ {"sfm", HB_TAG('H','M','N',' ')}, /* Small Flowery Miao -> Hmong */ {"sg", HB_TAG('S','G','O',' ')}, /* Sango */ /*{"sga", HB_TAG('S','G','A',' ')},*/ /* Old Irish (to 900) */ @@ -1413,6 +1427,7 @@ static const LangTag ot_languages[] = { {"tkg", HB_TAG('M','L','G',' ')}, /* Tesaka Malagasy -> Malagasy */ {"tkm", HB_TAG_NONE }, /* Takelma != Turkmen */ {"tl", HB_TAG('T','G','L',' ')}, /* Tagalog */ +/*{"tli", HB_TAG('T','L','I',' ')},*/ /* Tlingit */ {"tmg", HB_TAG('C','P','P',' ')}, /* Ternateño -> Creoles */ {"tmh", HB_TAG('T','M','H',' ')}, /* Tamashek [macrolanguage] */ {"tmh", HB_TAG('B','B','R',' ')}, /* Tamashek [macrolanguage] -> Berber */ @@ -1499,6 +1514,7 @@ static const LangTag ot_languages[] = { {"wbm", HB_TAG('W','A',' ',' ')}, /* Wa */ {"wbr", HB_TAG('W','A','G',' ')}, /* Wagdi */ {"wbr", HB_TAG('R','A','J',' ')}, /* Wagdi -> Rajasthani */ +/*{"wci", HB_TAG('W','C','I',' ')},*/ /* Waci Gbe */ {"wea", HB_TAG('K','R','N',' ')}, /* Wewaw -> Karen */ {"wes", HB_TAG('C','P','P',' ')}, /* Cameroon Pidgin -> Creoles */ {"weu", HB_TAG('Q','I','N',' ')}, /* Rawngtu Chin -> Chin */ @@ -1533,6 +1549,8 @@ static const LangTag ot_languages[] = { {"xsl", HB_TAG('S','L','A',' ')}, /* South Slavey -> Slavey */ {"xsl", HB_TAG('A','T','H',' ')}, /* South Slavey -> Athapaskan */ {"xst", HB_TAG('S','I','G',' ')}, /* Silt'e (retired code) -> Silte Gurage */ +/*{"xub", HB_TAG('X','U','B',' ')},*/ /* Betta Kurumba -> Bette Kuruma */ +/*{"xuj", HB_TAG('X','U','J',' ')},*/ /* Jennu Kurumba -> Jennu Kuruma */ {"xup", HB_TAG('A','T','H',' ')}, /* Upper Umpqua -> Athapaskan */ {"xwo", HB_TAG('T','O','D',' ')}, /* Written Oirat -> Todo */ {"yaj", HB_TAG('B','A','D','0')}, /* Banda-Yangere -> Banda */ @@ -1543,13 +1561,16 @@ static const LangTag ot_languages[] = { {"ybb", HB_TAG('B','M','L',' ')}, /* Yemba -> Bamileke */ {"ybd", HB_TAG('A','R','K',' ')}, /* Yangbye (retired code) -> Rakhine */ {"ydd", HB_TAG('J','I','I',' ')}, /* Eastern Yiddish -> Yiddish */ +/*{"ygp", HB_TAG('Y','G','P',' ')},*/ /* Gepo */ {"yi", HB_TAG('J','I','I',' ')}, /* Yiddish [macrolanguage] */ {"yih", HB_TAG('J','I','I',' ')}, /* Western Yiddish -> Yiddish */ {"yim", HB_TAG_NONE }, /* Yimchungru Naga != Yi Modern */ +/*{"yna", HB_TAG('Y','N','A',' ')},*/ /* Aluo */ {"yo", HB_TAG('Y','B','A',' ')}, /* Yoruba */ {"yos", HB_TAG('Q','I','N',' ')}, /* Yos (retired code) -> Chin */ {"yua", HB_TAG('M','Y','N',' ')}, /* Yucateco -> Mayan */ {"yue", HB_TAG('Z','H','H',' ')}, /* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */ +/*{"ywq", HB_TAG('Y','W','Q',' ')},*/ /* Wuding-Luquan Yi */ {"za", HB_TAG('Z','H','A',' ')}, /* Zhuang [macrolanguage] */ {"zch", HB_TAG('Z','H','A',' ')}, /* Central Hongshuihe Zhuang -> Zhuang */ {"zdj", HB_TAG('C','M','R',' ')}, /* Ngazidja Comorian -> Comorian */ diff --git a/gfx/harfbuzz/src/hb-ot-tag.cc b/gfx/harfbuzz/src/hb-ot-tag.cc index fc145a41f796..1837063af85d 100644 --- a/gfx/harfbuzz/src/hb-ot-tag.cc +++ b/gfx/harfbuzz/src/hb-ot-tag.cc @@ -522,7 +522,7 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, unsigned char *buf; const char *lang_str = hb_language_to_string (*language); size_t len = strlen (lang_str); - buf = (unsigned char *) malloc (len + 16); + buf = (unsigned char *) hb_malloc (len + 16); if (unlikely (!buf)) { *language = nullptr; @@ -544,7 +544,7 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, for (shift = 28; shift >= 0; shift -= 4) buf[len++] = TOHEX (script_tag >> shift); *language = hb_language_from_string ((char *) buf, len); - free (buf); + hb_free (buf); } } } diff --git a/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh index ceacb4f256e3..05f289db269d 100644 --- a/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh @@ -142,11 +142,13 @@ struct AxisRecord max = hb_max (default_, maxValue / 65536.f); } - protected: + public: Tag axisTag; /* Tag identifying the design variation for the axis. */ + protected: HBFixed minValue; /* The minimum coordinate value for the axis. */ HBFixed defaultValue; /* The default coordinate value for the axis. */ HBFixed maxValue; /* The maximum coordinate value for the axis. */ + public: HBUINT16 flags; /* Axis flags. */ NameID axisNameID; /* The name ID for entries in the 'name' table that * provide a display name for this axis. */ @@ -214,7 +216,6 @@ struct fvar return axes.lfind (tag, axis_index) && (axes[*axis_index].get_axis_deprecated (info), true); } #endif - bool find_axis_info (hb_tag_t tag, hb_ot_var_axis_info_t *info) const { @@ -289,7 +290,7 @@ struct fvar ; } - protected: + public: hb_array_t get_axes () const { return hb_array (&(this+firstAxis), axisCount); } diff --git a/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh index 157dfd7a5da9..49b5532d40fe 100644 --- a/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh @@ -419,7 +419,9 @@ struct gvar out->glyphCount = num_glyphs; unsigned int subset_data_size = 0; - for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + for (hb_codepoint_t gid = (c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE) ? 0 : 1; + gid < num_glyphs; + gid++) { hb_codepoint_t old_gid; if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue; @@ -449,7 +451,9 @@ struct gvar out->dataZ = subset_data - (char *) out; unsigned int glyph_offset = 0; - for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + for (hb_codepoint_t gid = (c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE) ? 0 : 1; + gid < num_glyphs; + gid++) { hb_codepoint_t old_gid; hb_bytes_t var_data_bytes = c->plan->old_gid_for_new_gid (gid, &old_gid) diff --git a/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh index 62bd1aa6868a..72217e7f29fc 100644 --- a/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh @@ -54,7 +54,7 @@ struct DeltaSetIndexMap TRACE_SERIALIZE (this); if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) return_trace (false); - if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); format = ((width-1)<<4)|(inner_bit_count-1); mapCount = output_map.length; @@ -272,7 +272,7 @@ struct hvarvvar_subset_plan_t index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan); if (index_maps[0] == &Null (DeltaSetIndexMap)) { - retain_adv_map = plan->retain_gids; + retain_adv_map = plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS; outer_map.add (0); for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) { @@ -367,15 +367,15 @@ struct HVARVVAR TRACE_SERIALIZE (this); if (im_plans[index_map_subset_plan_t::ADV_INDEX].is_identity ()) advMap = 0; - else if (unlikely (!advMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::ADV_INDEX]))) + else if (unlikely (!advMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::ADV_INDEX]))) return_trace (false); if (im_plans[index_map_subset_plan_t::LSB_INDEX].is_identity ()) lsbMap = 0; - else if (unlikely (!lsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::LSB_INDEX]))) + else if (unlikely (!lsbMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::LSB_INDEX]))) return_trace (false); if (im_plans[index_map_subset_plan_t::RSB_INDEX].is_identity ()) rsbMap = 0; - else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::RSB_INDEX]))) + else if (unlikely (!rsbMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::RSB_INDEX]))) return_trace (false); return_trace (true); @@ -398,8 +398,10 @@ struct HVARVVAR out->version.major = 1; out->version.minor = 0; - if (unlikely (!out->varStore.serialize (c->serializer, out) - .serialize (c->serializer, hvar_plan.var_store, hvar_plan.inner_maps.as_array ()))) + if (unlikely (!out->varStore + .serialize_serialize (c->serializer, + hvar_plan.var_store, + hvar_plan.inner_maps.as_array ()))) return_trace (false); return_trace (out->T::serialize_index_maps (c->serializer, @@ -466,7 +468,7 @@ struct VVAR : HVARVVAR { return_trace (false); if (!im_plans[index_map_subset_plan_t::VORG_INDEX].get_map_count ()) vorgMap = 0; - else if (unlikely (!vorgMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::VORG_INDEX]))) + else if (unlikely (!vorgMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::VORG_INDEX]))) return_trace (false); return_trace (true); diff --git a/gfx/harfbuzz/src/hb-pool.hh b/gfx/harfbuzz/src/hb-pool.hh index dcf0faf2a936..dcf8f6627d00 100644 --- a/gfx/harfbuzz/src/hb-pool.hh +++ b/gfx/harfbuzz/src/hb-pool.hh @@ -41,7 +41,7 @@ struct hb_pool_t { next = nullptr; - for (chunk_t *_ : chunks) ::free (_); + for (chunk_t *_ : chunks) hb_free (_); chunks.fini (); } @@ -51,7 +51,7 @@ struct hb_pool_t if (unlikely (!next)) { if (unlikely (!chunks.alloc (chunks.length + 1))) return nullptr; - chunk_t *chunk = (chunk_t *) calloc (1, sizeof (chunk_t)); + chunk_t *chunk = (chunk_t *) hb_calloc (1, sizeof (chunk_t)); if (unlikely (!chunk)) return nullptr; chunks.push (chunk); next = chunk->thread (); @@ -65,7 +65,7 @@ struct hb_pool_t return obj; } - void free (T* obj) + void release (T* obj) { * (T**) obj = next; next = obj; diff --git a/gfx/harfbuzz/src/hb-repacker.hh b/gfx/harfbuzz/src/hb-repacker.hh index 6211dedb9ccf..b02128b5c4c3 100644 --- a/gfx/harfbuzz/src/hb-repacker.hh +++ b/gfx/harfbuzz/src/hb-repacker.hh @@ -102,7 +102,7 @@ struct graph_t { fini (); unsigned size = object.tail - object.head; - head = (char*) malloc (size); + head = (char*) hb_malloc (size); if (!head) return false; memcpy (head, object.head, size); @@ -116,7 +116,7 @@ struct graph_t void fini () { if (!head) return; - free (head); + hb_free (head); head = nullptr; } }; @@ -531,7 +531,7 @@ struct graph_t const auto& child = vertices_[link.objidx].obj; int64_t child_weight = child.tail - child.head + - (!link.is_wide ? (1 << 16) : ((int64_t) 1 << 32)); + ((int64_t) 1 << (link.width * 8)); int64_t child_distance = next_distance + child_weight; if (child_distance < vertices_[link.objidx].distance) @@ -578,15 +578,17 @@ struct graph_t { if (link.is_signed) { - if (link.is_wide) + if (link.width == 4) return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31); else return offset >= -(1 << 15) && offset < (1 << 15); } else { - if (link.is_wide) + if (link.width == 4) return offset >= 0 && offset < ((int64_t) 1 << 32); + else if (link.width == 3) + return offset >= 0 && offset < ((int32_t) 1 << 24); else return offset >= 0 && offset < (1 << 16); } @@ -627,21 +629,30 @@ struct graph_t char* head, hb_serialize_context_t* c) const { - if (link.is_wide) + switch (link.width) { + case 4: if (link.is_signed) { serialize_link_of_type (link, head, c); } else { serialize_link_of_type (link, head, c); } - } else { + return; + case 2: if (link.is_signed) { serialize_link_of_type (link, head, c); } else { serialize_link_of_type (link, head, c); } + return; + case 3: + serialize_link_of_type (link, head, c); + return; + default: + // Unexpected link width. + assert (0); } } diff --git a/gfx/harfbuzz/src/hb-serialize.hh b/gfx/harfbuzz/src/hb-serialize.hh index 3ec055b7b0b2..7212d9872af4 100644 --- a/gfx/harfbuzz/src/hb-serialize.hh +++ b/gfx/harfbuzz/src/hb-serialize.hh @@ -82,7 +82,7 @@ struct hb_serialize_context_t struct link_t { - bool is_wide: 1; + unsigned width: 3; bool is_signed: 1; unsigned whence: 2; unsigned position: 28; @@ -102,10 +102,11 @@ struct hb_serialize_context_t char *tail; object_t *current; // Just for sanity check unsigned num_links; + hb_serialize_error_t errors; }; snapshot_t snapshot () - { return snapshot_t { head, tail, current, current->links.length }; } + { return snapshot_t { head, tail, current, current->links.length, errors }; } hb_serialize_context_t (void *start_, unsigned int size) : start ((char *) start_), @@ -136,6 +137,12 @@ struct hb_serialize_context_t HB_NODISCARD bool ran_out_of_room () const { return errors & HB_SERIALIZE_ERROR_OUT_OF_ROOM; } HB_NODISCARD bool offset_overflow () const { return errors & HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; } HB_NODISCARD bool only_offset_overflow () const { return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; } + HB_NODISCARD bool only_overflow () const + { + return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW + || errors == HB_SERIALIZE_ERROR_INT_OVERFLOW + || errors == HB_SERIALIZE_ERROR_ARRAY_OVERFLOW; + } void reset (void *start_, unsigned int size) { @@ -254,7 +261,7 @@ struct hb_serialize_context_t current = current->next; revert (obj->head, obj->tail); obj->fini (); - object_pool.free (obj); + object_pool.release (obj); } /* Set share to false when an object is unlikely sharable with others @@ -317,9 +324,11 @@ struct hb_serialize_context_t void revert (snapshot_t snap) { - if (unlikely (in_error ())) return; + // Overflows that happened after the snapshot will be erased by the revert. + if (unlikely (in_error () && !only_overflow ())) return; assert (snap.current == current); current->links.shrink (snap.num_links); + errors = snap.errors; revert (snap.head, snap.tail); } @@ -354,7 +363,6 @@ struct hb_serialize_context_t whence_t whence = Head, unsigned bias = 0) { - static_assert (sizeof (T) == 2 || sizeof (T) == 4, ""); if (unlikely (in_error ())) return; if (!objidx) @@ -364,8 +372,10 @@ struct hb_serialize_context_t assert (current->head <= (const char *) &ofs); auto& link = *current->links.push (); + if (current->links.in_error ()) + err (HB_SERIALIZE_ERROR_OTHER); - link.is_wide = sizeof (T) == 4; + link.width = sizeof (T); link.is_signed = hb_is_signed (hb_unwrap_type (T)); link.whence = (unsigned) whence; link.position = (const char *) &ofs - current->head; @@ -405,15 +415,19 @@ struct hb_serialize_context_t offset -= link.bias; if (link.is_signed) { - if (link.is_wide) + assert (link.width == 2 || link.width == 4); + if (link.width == 4) assign_offset (parent, link, offset); else assign_offset (parent, link, offset); } else { - if (link.is_wide) + assert (link.width == 2 || link.width == 3 || link.width == 4); + if (link.width == 4) assign_offset (parent, link, offset); + else if (link.width == 3) + assign_offset (parent, link, offset); else assign_offset (parent, link, offset); } @@ -446,16 +460,16 @@ struct hb_serialize_context_t } template - Type *allocate_size (unsigned int size) + Type *allocate_size (size_t size) { if (unlikely (in_error ())) return nullptr; - if (this->tail - this->head < ptrdiff_t (size)) + if (unlikely (size > INT_MAX || this->tail - this->head < ptrdiff_t (size))) { err (HB_SERIALIZE_ERROR_OUT_OF_ROOM); return nullptr; } - memset (this->head, 0, size); + hb_memset (this->head, 0, size); char *ret = this->head; this->head += size; return reinterpret_cast (ret); @@ -510,18 +524,19 @@ struct hb_serialize_context_t hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; } template - Type *extend_size (Type *obj, unsigned int size) + Type *extend_size (Type *obj, size_t size) { if (unlikely (in_error ())) return nullptr; assert (this->start <= (char *) obj); assert ((char *) obj <= this->head); - assert ((char *) obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) obj) + size - this->head))) return nullptr; + assert ((size_t) (this->head - (char *) obj) <= size); + if (unlikely (((char *) obj + size < (char *) obj) || + !this->allocate_size (((char *) obj) + size - this->head))) return nullptr; return reinterpret_cast (obj); } template - Type *extend_size (Type &obj, unsigned int size) + Type *extend_size (Type &obj, size_t size) { return extend_size (hb_addressof (obj), size); } template @@ -544,7 +559,11 @@ struct hb_serialize_context_t unsigned int len = (this->head - this->start) + (this->end - this->tail); - char *p = (char *) malloc (len); + // If len is zero don't hb_malloc as the memory won't get properly + // cleaned up later. + if (!len) return hb_bytes_t (); + + char *p = (char *) hb_malloc (len); if (unlikely (!p)) return hb_bytes_t (); memcpy (p, this->start, this->head - this->start); @@ -559,17 +578,17 @@ struct hb_serialize_context_t hb_bytes_t b = copy_bytes (); return hb_blob_create (b.arrayZ, b.length, HB_MEMORY_MODE_WRITABLE, - (char *) b.arrayZ, free); + (char *) b.arrayZ, hb_free); } const hb_vector_t& object_graph() const { return packed; } private: - template + template void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset) { - auto &off = * ((BEInt *) (parent->head + link.position)); + auto &off = * ((BEInt *) (parent->head + link.position)); assert (0 == off); check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW); } diff --git a/gfx/harfbuzz/src/hb-set.cc b/gfx/harfbuzz/src/hb-set.cc index 86bf70034c92..204dbb56454d 100644 --- a/gfx/harfbuzz/src/hb-set.cc +++ b/gfx/harfbuzz/src/hb-set.cc @@ -109,7 +109,7 @@ hb_set_destroy (hb_set_t *set) set->fini_shallow (); - free (set); + hb_free (set); } /** @@ -169,7 +169,25 @@ hb_set_get_user_data (hb_set_t *set, hb_bool_t hb_set_allocation_successful (const hb_set_t *set) { - return set->successful; + return !set->in_error (); +} + +/** + * hb_set_copy: + * @set: A set + * + * Allocate a copy of @set. + * + * Return value: Newly-allocated set. + * + * Since: 2.8.2 + **/ +hb_set_t * +hb_set_copy (const hb_set_t *set) +{ + hb_set_t *copy = hb_set_create (); + copy->set (*set); + return copy; } /** @@ -183,9 +201,7 @@ hb_set_allocation_successful (const hb_set_t *set) void hb_set_clear (hb_set_t *set) { - if (unlikely (hb_object_is_immutable (set))) - return; - + /* Immutible-safe. */ set->clear (); } @@ -236,6 +252,7 @@ void hb_set_add (hb_set_t *set, hb_codepoint_t codepoint) { + /* Immutible-safe. */ set->add (codepoint); } @@ -255,6 +272,7 @@ hb_set_add_range (hb_set_t *set, hb_codepoint_t first, hb_codepoint_t last) { + /* Immutible-safe. */ set->add_range (first, last); } @@ -271,6 +289,7 @@ void hb_set_del (hb_set_t *set, hb_codepoint_t codepoint) { + /* Immutible-safe. */ set->del (codepoint); } @@ -283,6 +302,9 @@ hb_set_del (hb_set_t *set, * Removes all of the elements from @first to @last * (inclusive) from @set. * + * If @last is #HB_SET_VALUE_INVALID, then all values + * greater than or equal to @first are removed. + * * Since: 0.9.7 **/ void @@ -290,6 +312,7 @@ hb_set_del_range (hb_set_t *set, hb_codepoint_t first, hb_codepoint_t last) { + /* Immutible-safe. */ set->del_range (first, last); } @@ -309,7 +332,7 @@ hb_bool_t hb_set_is_equal (const hb_set_t *set, const hb_set_t *other) { - return set->is_equal (other); + return set->is_equal (*other); } /** @@ -327,7 +350,7 @@ hb_bool_t hb_set_is_subset (const hb_set_t *set, const hb_set_t *larger_set) { - return set->is_subset (larger_set); + return set->is_subset (*larger_set); } /** @@ -343,7 +366,8 @@ void hb_set_set (hb_set_t *set, const hb_set_t *other) { - set->set (other); + /* Immutible-safe. */ + set->set (*other); } /** @@ -359,7 +383,8 @@ void hb_set_union (hb_set_t *set, const hb_set_t *other) { - set->union_ (other); + /* Immutible-safe. */ + set->union_ (*other); } /** @@ -375,7 +400,8 @@ void hb_set_intersect (hb_set_t *set, const hb_set_t *other) { - set->intersect (other); + /* Immutible-safe. */ + set->intersect (*other); } /** @@ -391,7 +417,8 @@ void hb_set_subtract (hb_set_t *set, const hb_set_t *other) { - set->subtract (other); + /* Immutible-safe. */ + set->subtract (*other); } /** @@ -408,25 +435,24 @@ void hb_set_symmetric_difference (hb_set_t *set, const hb_set_t *other) { - set->symmetric_difference (other); + /* Immutible-safe. */ + set->symmetric_difference (*other); } -#ifndef HB_DISABLE_DEPRECATED /** * hb_set_invert: * @set: A set * * Inverts the contents of @set. * - * Since: 0.9.10 - * - * Deprecated: 1.6.1 + * Since: 3.0.0 **/ void -hb_set_invert (hb_set_t *set HB_UNUSED) +hb_set_invert (hb_set_t *set) { + /* Immutible-safe. */ + set->invert (); } -#endif /** * hb_set_get_population: diff --git a/gfx/harfbuzz/src/hb-set.h b/gfx/harfbuzz/src/hb-set.h index 0ad27f4bbdab..423225bf96b2 100644 --- a/gfx/harfbuzz/src/hb-set.h +++ b/gfx/harfbuzz/src/hb-set.h @@ -85,12 +85,18 @@ hb_set_get_user_data (hb_set_t *set, HB_EXTERN hb_bool_t hb_set_allocation_successful (const hb_set_t *set); +HB_EXTERN hb_set_t * +hb_set_copy (const hb_set_t *set); + HB_EXTERN void hb_set_clear (hb_set_t *set); HB_EXTERN hb_bool_t hb_set_is_empty (const hb_set_t *set); +HB_EXTERN void +hb_set_invert (hb_set_t *set); + HB_EXTERN hb_bool_t hb_set_has (const hb_set_t *set, hb_codepoint_t codepoint); diff --git a/gfx/harfbuzz/src/hb-set.hh b/gfx/harfbuzz/src/hb-set.hh index 9404ba2615b8..437e23436163 100644 --- a/gfx/harfbuzz/src/hb-set.hh +++ b/gfx/harfbuzz/src/hb-set.hh @@ -1,5 +1,6 @@ /* * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod * * This is part of HarfBuzz, a text shaping library. * @@ -28,315 +29,52 @@ #define HB_SET_HH #include "hb.hh" -#include "hb-machinery.hh" +#include "hb-bit-set-invertible.hh" -/* - * hb_set_t - */ - -/* TODO Keep a free-list so we can free pages that are completely zeroed. At that - * point maybe also use a sentinel value for "all-1" pages? */ - -struct hb_set_t +template +struct hb_sparseset_t { - HB_DELETE_COPY_ASSIGN (hb_set_t); - hb_set_t () { init (); } - ~hb_set_t () { fini (); } - - struct page_map_t - { - int cmp (const page_map_t &o) const { return (int) o.major - (int) major; } - - uint32_t major; - uint32_t index; - }; - - struct page_t - { - void init0 () { v.clear (); } - void init1 () { v.clear (0xFF); } - - unsigned int len () const - { return ARRAY_LENGTH_CONST (v); } - - bool is_empty () const - { - for (unsigned int i = 0; i < len (); i++) - if (v[i]) - return false; - return true; - } - - void add (hb_codepoint_t g) { elt (g) |= mask (g); } - void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } - bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } - - void add_range (hb_codepoint_t a, hb_codepoint_t b) - { - elt_t *la = &elt (a); - elt_t *lb = &elt (b); - if (la == lb) - *la |= (mask (b) << 1) - mask(a); - else - { - *la |= ~(mask (a) - 1); - la++; - - memset (la, 0xff, (char *) lb - (char *) la); - - *lb |= ((mask (b) << 1) - 1); - } - } - - void del_range (hb_codepoint_t a, hb_codepoint_t b) - { - elt_t *la = &elt (a); - elt_t *lb = &elt (b); - if (la == lb) - *la &= ~((mask (b) << 1) - mask(a)); - else - { - *la &= mask (a) - 1; - la++; - - memset (la, 0, (char *) lb - (char *) la); - - *lb &= ~((mask (b) << 1) - 1); - } - } - - bool is_equal (const page_t *other) const - { - return 0 == hb_memcmp (&v, &other->v, sizeof (v)); - } - - unsigned int get_population () const - { - unsigned int pop = 0; - for (unsigned int i = 0; i < len (); i++) - pop += hb_popcount (v[i]); - return pop; - } - - bool next (hb_codepoint_t *codepoint) const - { - unsigned int m = (*codepoint + 1) & MASK; - if (!m) - { - *codepoint = INVALID; - return false; - } - unsigned int i = m / ELT_BITS; - unsigned int j = m & ELT_MASK; - - const elt_t vv = v[i] & ~((elt_t (1) << j) - 1); - for (const elt_t *p = &vv; i < len (); p = &v[++i]) - if (*p) - { - *codepoint = i * ELT_BITS + elt_get_min (*p); - return true; - } - - *codepoint = INVALID; - return false; - } - bool previous (hb_codepoint_t *codepoint) const - { - unsigned int m = (*codepoint - 1) & MASK; - if (m == MASK) - { - *codepoint = INVALID; - return false; - } - unsigned int i = m / ELT_BITS; - unsigned int j = m & ELT_MASK; - - /* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */ - const elt_t mask = j < 8 * sizeof (elt_t) - 1 ? - ((elt_t (1) << (j + 1)) - 1) : - (elt_t) -1; - const elt_t vv = v[i] & mask; - const elt_t *p = &vv; - while (true) - { - if (*p) - { - *codepoint = i * ELT_BITS + elt_get_max (*p); - return true; - } - if ((int) i <= 0) break; - p = &v[--i]; - } - - *codepoint = INVALID; - return false; - } - hb_codepoint_t get_min () const - { - for (unsigned int i = 0; i < len (); i++) - if (v[i]) - return i * ELT_BITS + elt_get_min (v[i]); - return INVALID; - } - hb_codepoint_t get_max () const - { - for (int i = len () - 1; i >= 0; i--) - if (v[i]) - return i * ELT_BITS + elt_get_max (v[i]); - return 0; - } - - typedef unsigned long long elt_t; - static constexpr unsigned PAGE_BITS = 512; - static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, ""); - - static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); } - static unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; } - - typedef hb_vector_size_t vector_t; - - static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8; - static constexpr unsigned ELT_MASK = ELT_BITS - 1; - static constexpr unsigned BITS = sizeof (vector_t) * 8; - static constexpr unsigned MASK = BITS - 1; - static_assert ((unsigned) PAGE_BITS == (unsigned) BITS, ""); - - elt_t &elt (hb_codepoint_t g) { return v[(g & MASK) / ELT_BITS]; } - elt_t const &elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; } - elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & ELT_MASK); } - - vector_t v; - }; - static_assert (page_t::PAGE_BITS == sizeof (page_t) * 8, ""); - hb_object_header_t header; - bool successful; /* Allocations successful */ - mutable unsigned int population; - hb_sorted_vector_t page_map; - hb_vector_t pages; + impl_t s; - void init_shallow () - { - successful = true; - population = 0; - page_map.init (); - pages.init (); - } + hb_sparseset_t () { init (); } + ~hb_sparseset_t () { fini (); } + + hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); } + void operator= (const hb_sparseset_t& other) { set (other); } + // TODO Add move construtor/assign + // TODO Add constructor for Iterator + + void init_shallow () { s.init (); } void init () { hb_object_init (this); init_shallow (); } - void fini_shallow () - { - population = 0; - page_map.fini (); - pages.fini (); - } + void fini_shallow () { s.fini (); } void fini () { hb_object_fini (this); fini_shallow (); } - bool in_error () const { return !successful; } - - bool resize (unsigned int count) - { - if (unlikely (count > pages.length && !successful)) return false; - if (!pages.resize (count) || !page_map.resize (count)) - { - pages.resize (page_map.length); - successful = false; - return false; - } - return true; - } - - void reset () - { - successful = true; - clear (); - } - - void clear () - { - if (resize (0)) - population = 0; - } - bool is_empty () const - { - unsigned int count = pages.length; - for (unsigned int i = 0; i < count; i++) - if (!pages[i].is_empty ()) - return false; - return true; - } explicit operator bool () const { return !is_empty (); } - void dirty () { population = UINT_MAX; } + void err () { s.err (); } + bool in_error () const { return s.in_error (); } - void add (hb_codepoint_t g) - { - if (unlikely (!successful)) return; - if (unlikely (g == INVALID)) return; - dirty (); - page_t *page = page_for_insert (g); if (unlikely (!page)) return; - page->add (g); - } - bool add_range (hb_codepoint_t a, hb_codepoint_t b) - { - if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ - if (unlikely (a > b || a == INVALID || b == INVALID)) return false; - dirty (); - unsigned int ma = get_major (a); - unsigned int mb = get_major (b); - if (ma == mb) - { - page_t *page = page_for_insert (a); if (unlikely (!page)) return false; - page->add_range (a, b); - } - else - { - page_t *page = page_for_insert (a); if (unlikely (!page)) return false; - page->add_range (a, major_start (ma + 1) - 1); + void reset () { s.reset (); } + void clear () { s.clear (); } + void invert () { s.invert (); } + bool is_empty () const { return s.is_empty (); } - for (unsigned int m = ma + 1; m < mb; m++) - { - page = page_for_insert (major_start (m)); if (unlikely (!page)) return false; - page->init1 (); - } - - page = page_for_insert (b); if (unlikely (!page)) return false; - page->add_range (major_start (mb), b); - } - return true; - } + void add (hb_codepoint_t g) { s.add (g); } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) { return s.add_range (a, b); } template void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) - { - if (unlikely (!successful)) return; - if (!count) return; - dirty (); - hb_codepoint_t g = *array; - while (count) - { - unsigned int m = get_major (g); - page_t *page = page_for_insert (g); if (unlikely (!page)) return; - unsigned int start = major_start (m); - unsigned int end = major_start (m + 1); - do - { - page->add (g); - - array = &StructAtOffsetUnaligned (array, stride); - count--; - } - while (count && (g = *array, start <= g && g < end)); - } - } + { s.add_array (array, count, stride); } template void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } @@ -344,108 +82,14 @@ struct hb_set_t * Used for faster rejection of corrupt data. */ template bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) - { - if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ - if (!count) return true; - dirty (); - hb_codepoint_t g = *array; - hb_codepoint_t last_g = g; - while (count) - { - unsigned int m = get_major (g); - page_t *page = page_for_insert (g); if (unlikely (!page)) return false; - unsigned int end = major_start (m + 1); - do - { - /* If we try harder we can change the following comparison to <=; - * Not sure if it's worth it. */ - if (g < last_g) return false; - last_g = g; - page->add (g); - - array = (const T *) ((const char *) array + stride); - count--; - } - while (count && (g = *array, g < end)); - } - return true; - } + { return s.add_sorted_array (array, count, stride); } template bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } - void del (hb_codepoint_t g) - { - /* TODO perform op even if !successful. */ - if (unlikely (!successful)) return; - page_t *page = page_for (g); - if (!page) - return; - dirty (); - page->del (g); - } + void del (hb_codepoint_t g) { s.del (g); } + void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); } - private: - void del_pages (int ds, int de) - { - if (ds <= de) - { - // Pre-allocate the workspace that compact() will need so we can bail on allocation failure - // before attempting to rewrite the page map. - hb_vector_t compact_workspace; - if (unlikely (!allocate_compact_workspace (compact_workspace))) return; - - unsigned int write_index = 0; - for (unsigned int i = 0; i < page_map.length; i++) - { - int m = (int) page_map[i].major; - if (m < ds || de < m) - page_map[write_index++] = page_map[i]; - } - compact (compact_workspace, write_index); - resize (write_index); - } - } - - - public: - void del_range (hb_codepoint_t a, hb_codepoint_t b) - { - /* TODO perform op even if !successful. */ - if (unlikely (!successful)) return; - if (unlikely (a > b || a == INVALID || b == INVALID)) return; - dirty (); - unsigned int ma = get_major (a); - unsigned int mb = get_major (b); - /* Delete pages from ds through de if ds <= de. */ - int ds = (a == major_start (ma))? (int) ma: (int) (ma + 1); - int de = (b + 1 == major_start (mb + 1))? (int) mb: ((int) mb - 1); - if (ds > de || (int) ma < ds) - { - page_t *page = page_for (a); - if (page) - { - if (ma == mb) - page->del_range (a, b); - else - page->del_range (a, major_start (ma + 1) - 1); - } - } - if (de < (int) mb && ma != mb) - { - page_t *page = page_for (b); - if (page) - page->del_range (major_start (mb), b); - } - del_pages (ds, de); - } - - bool get (hb_codepoint_t g) const - { - const page_t *page = page_for (g); - if (!page) - return false; - return page->get (g); - } + bool get (hb_codepoint_t g) const { return s.get (g); } /* Has interface. */ static constexpr bool SENTINEL = false; @@ -456,464 +100,49 @@ struct hb_set_t bool operator () (hb_codepoint_t k) const { return has (k); } /* Sink interface. */ - hb_set_t& operator << (hb_codepoint_t v) + hb_sparseset_t& operator << (hb_codepoint_t v) { add (v); return *this; } - hb_set_t& operator << (const hb_pair_t& range) + hb_sparseset_t& operator << (const hb_pair_t& range) { add_range (range.first, range.second); return *this; } bool intersects (hb_codepoint_t first, hb_codepoint_t last) const - { - hb_codepoint_t c = first - 1; - return next (&c) && c <= last; - } - void set (const hb_set_t *other) - { - if (unlikely (!successful)) return; - unsigned int count = other->pages.length; - if (!resize (count)) - return; - population = other->population; - memcpy ((void *) pages, (const void *) other->pages, count * pages.item_size); - memcpy ((void *) page_map, (const void *) other->page_map, count * page_map.item_size); - } + { return s.intersects (first, last); } - bool is_equal (const hb_set_t *other) const - { - if (get_population () != other->get_population ()) - return false; + void set (const hb_sparseset_t &other) { s.set (other.s); } - unsigned int na = pages.length; - unsigned int nb = other->pages.length; + bool is_equal (const hb_sparseset_t &other) const { return s.is_equal (other.s); } - unsigned int a = 0, b = 0; - for (; a < na && b < nb; ) - { - if (page_at (a).is_empty ()) { a++; continue; } - if (other->page_at (b).is_empty ()) { b++; continue; } - if (page_map[a].major != other->page_map[b].major || - !page_at (a).is_equal (&other->page_at (b))) - return false; - a++; - b++; - } - for (; a < na; a++) - if (!page_at (a).is_empty ()) { return false; } - for (; b < nb; b++) - if (!other->page_at (b).is_empty ()) { return false; } + bool is_subset (const hb_sparseset_t &larger_set) const { return s.is_subset (larger_set.s); } - return true; - } + void union_ (const hb_sparseset_t &other) { s.union_ (other.s); } + void intersect (const hb_sparseset_t &other) { s.intersect (other.s); } + void subtract (const hb_sparseset_t &other) { s.subtract (other.s); } + void symmetric_difference (const hb_sparseset_t &other) { s.symmetric_difference (other.s); } - bool is_subset (const hb_set_t *larger_set) const - { - if (get_population () > larger_set->get_population ()) - return false; - - /* TODO Optimize to use pages. */ - hb_codepoint_t c = INVALID; - while (next (&c)) - if (!larger_set->has (c)) - return false; - - return true; - } - - bool allocate_compact_workspace(hb_vector_t& workspace) - { - if (unlikely(!workspace.resize (pages.length))) - { - successful = false; - return false; - } - - return true; - } - - - /* - * workspace should be a pre-sized vector allocated to hold at exactly pages.length - * elements. - */ - void compact (hb_vector_t& workspace, - unsigned int length) - { - assert(workspace.length == pages.length); - hb_vector_t& old_index_to_page_map_index = workspace; - - hb_fill (old_index_to_page_map_index.writer(), 0xFFFFFFFF); - /* TODO(iter) Rewrite as dagger? */ - for (unsigned i = 0; i < length; i++) - old_index_to_page_map_index[page_map[i].index] = i; - - compact_pages (old_index_to_page_map_index); - } - - void compact_pages (const hb_vector_t& old_index_to_page_map_index) - { - unsigned int write_index = 0; - for (unsigned int i = 0; i < pages.length; i++) - { - if (old_index_to_page_map_index[i] == 0xFFFFFFFF) continue; - - if (write_index < i) - pages[write_index] = pages[i]; - - page_map[old_index_to_page_map_index[i]].index = write_index; - write_index++; - } - } - - template - void process (const Op& op, const hb_set_t *other) - { - const bool passthru_left = op (1, 0); - const bool passthru_right = op (0, 1); - - if (unlikely (!successful)) return; - - dirty (); - - unsigned int na = pages.length; - unsigned int nb = other->pages.length; - unsigned int next_page = na; - - unsigned int count = 0, newCount = 0; - unsigned int a = 0, b = 0; - unsigned int write_index = 0; - - // Pre-allocate the workspace that compact() will need so we can bail on allocation failure - // before attempting to rewrite the page map. - hb_vector_t compact_workspace; - if (!passthru_left && unlikely (!allocate_compact_workspace (compact_workspace))) return; - - for (; a < na && b < nb; ) - { - if (page_map[a].major == other->page_map[b].major) - { - if (!passthru_left) - { - // Move page_map entries that we're keeping from the left side set - // to the front of the page_map vector. This isn't necessary if - // passthru_left is set since no left side pages will be removed - // in that case. - if (write_index < a) - page_map[write_index] = page_map[a]; - write_index++; - } - - count++; - a++; - b++; - } - else if (page_map[a].major < other->page_map[b].major) - { - if (passthru_left) - count++; - a++; - } - else - { - if (passthru_right) - count++; - b++; - } - } - if (passthru_left) - count += na - a; - if (passthru_right) - count += nb - b; - - if (!passthru_left) - { - na = write_index; - next_page = write_index; - compact (compact_workspace, write_index); - } - - if (!resize (count)) - return; - - newCount = count; - - /* Process in-place backward. */ - a = na; - b = nb; - for (; a && b; ) - { - if (page_map[a - 1].major == other->page_map[b - 1].major) - { - a--; - b--; - count--; - page_map[count] = page_map[a]; - page_at (count).v = op (page_at (a).v, other->page_at (b).v); - } - else if (page_map[a - 1].major > other->page_map[b - 1].major) - { - a--; - if (passthru_left) - { - count--; - page_map[count] = page_map[a]; - } - } - else - { - b--; - if (passthru_right) - { - count--; - page_map[count].major = other->page_map[b].major; - page_map[count].index = next_page++; - page_at (count).v = other->page_at (b).v; - } - } - } - if (passthru_left) - while (a) - { - a--; - count--; - page_map[count] = page_map [a]; - } - if (passthru_right) - while (b) - { - b--; - count--; - page_map[count].major = other->page_map[b].major; - page_map[count].index = next_page++; - page_at (count).v = other->page_at (b).v; - } - assert (!count); - if (pages.length > newCount) - // This resize() doesn't need to be checked because we can't get here - // if the set is currently in_error() and this only resizes downwards - // which will always succeed if the set is not in_error(). - resize (newCount); - } - - void union_ (const hb_set_t *other) - { - process (hb_bitwise_or, other); - } - void intersect (const hb_set_t *other) - { - process (hb_bitwise_and, other); - } - void subtract (const hb_set_t *other) - { - process (hb_bitwise_sub, other); - } - void symmetric_difference (const hb_set_t *other) - { - process (hb_bitwise_xor, other); - } - bool next (hb_codepoint_t *codepoint) const - { - if (unlikely (*codepoint == INVALID)) { - *codepoint = get_min (); - return *codepoint != INVALID; - } - - page_map_t map = {get_major (*codepoint), 0}; - unsigned int i; - page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST); - if (i < page_map.length && page_map[i].major == map.major) - { - if (pages[page_map[i].index].next (codepoint)) - { - *codepoint += page_map[i].major * page_t::PAGE_BITS; - return true; - } - i++; - } - for (; i < page_map.length; i++) - { - hb_codepoint_t m = pages[page_map[i].index].get_min (); - if (m != INVALID) - { - *codepoint = page_map[i].major * page_t::PAGE_BITS + m; - return true; - } - } - *codepoint = INVALID; - return false; - } - bool previous (hb_codepoint_t *codepoint) const - { - if (unlikely (*codepoint == INVALID)) { - *codepoint = get_max (); - return *codepoint != INVALID; - } - - page_map_t map = {get_major (*codepoint), 0}; - unsigned int i; - page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST); - if (i < page_map.length && page_map[i].major == map.major) - { - if (pages[page_map[i].index].previous (codepoint)) - { - *codepoint += page_map[i].major * page_t::PAGE_BITS; - return true; - } - } - i--; - for (; (int) i >= 0; i--) - { - hb_codepoint_t m = pages[page_map[i].index].get_max (); - if (m != INVALID) - { - *codepoint = page_map[i].major * page_t::PAGE_BITS + m; - return true; - } - } - *codepoint = INVALID; - return false; - } + bool next (hb_codepoint_t *codepoint) const { return s.next (codepoint); } + bool previous (hb_codepoint_t *codepoint) const { return s.previous (codepoint); } bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const - { - hb_codepoint_t i; - - i = *last; - if (!next (&i)) - { - *last = *first = INVALID; - return false; - } - - /* TODO Speed up. */ - *last = *first = i; - while (next (&i) && i == *last + 1) - (*last)++; - - return true; - } + { return s.next_range (first, last); } bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const - { - hb_codepoint_t i; + { return s.previous_range (first, last); } - i = *first; - if (!previous (&i)) - { - *last = *first = INVALID; - return false; - } + unsigned int get_population () const { return s.get_population (); } + hb_codepoint_t get_min () const { return s.get_min (); } + hb_codepoint_t get_max () const { return s.get_max (); } - /* TODO Speed up. */ - *last = *first = i; - while (previous (&i) && i == *first - 1) - (*first)--; - - return true; - } - - unsigned int get_population () const - { - if (population != UINT_MAX) - return population; - - unsigned int pop = 0; - unsigned int count = pages.length; - for (unsigned int i = 0; i < count; i++) - pop += pages[i].get_population (); - - population = pop; - return pop; - } - hb_codepoint_t get_min () const - { - unsigned int count = pages.length; - for (unsigned int i = 0; i < count; i++) - if (!page_at (i).is_empty ()) - return page_map[i].major * page_t::PAGE_BITS + page_at (i).get_min (); - return INVALID; - } - hb_codepoint_t get_max () const - { - unsigned int count = pages.length; - for (int i = count - 1; i >= 0; i--) - if (!page_at (i).is_empty ()) - return page_map[(unsigned) i].major * page_t::PAGE_BITS + page_at (i).get_max (); - return INVALID; - } - - static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; + static constexpr hb_codepoint_t INVALID = impl_t::INVALID; /* * Iterator implementation. */ - struct iter_t : hb_iter_with_fallback_t - { - static constexpr bool is_sorted_iterator = true; - iter_t (const hb_set_t &s_ = Null (hb_set_t), - bool init = true) : s (&s_), v (INVALID), l(0) - { - if (init) - { - l = s->get_population () + 1; - __next__ (); - } - } - - typedef hb_codepoint_t __item_t__; - hb_codepoint_t __item__ () const { return v; } - bool __more__ () const { return v != INVALID; } - void __next__ () { s->next (&v); if (l) l--; } - void __prev__ () { s->previous (&v); } - unsigned __len__ () const { return l; } - iter_t end () const { return iter_t (*s, false); } - bool operator != (const iter_t& o) const - { return s != o.s || v != o.v; } - - protected: - const hb_set_t *s; - hb_codepoint_t v; - unsigned l; - }; - iter_t iter () const { return iter_t (*this); } + using iter_t = typename impl_t::iter_t; + iter_t iter () const { return iter_t (this->s); } operator iter_t () const { return iter (); } - - protected: - - page_t *page_for_insert (hb_codepoint_t g) - { - page_map_t map = {get_major (g), pages.length}; - unsigned int i; - if (!page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST)) - { - if (!resize (pages.length + 1)) - return nullptr; - - pages[map.index].init0 (); - memmove (page_map + i + 1, - page_map + i, - (page_map.length - 1 - i) * page_map.item_size); - page_map[i] = map; - } - return &pages[page_map[i].index]; - } - page_t *page_for (hb_codepoint_t g) - { - page_map_t key = {get_major (g)}; - const page_map_t *found = page_map.bsearch (key); - if (found) - return &pages[found->index]; - return nullptr; - } - const page_t *page_for (hb_codepoint_t g) const - { - page_map_t key = {get_major (g)}; - const page_map_t *found = page_map.bsearch (key); - if (found) - return &pages[found->index]; - return nullptr; - } - page_t &page_at (unsigned int i) { return pages[page_map[i].index]; } - const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; } - unsigned int get_major (hb_codepoint_t g) const { return g / page_t::PAGE_BITS; } - hb_codepoint_t major_start (unsigned int major) const { return major * page_t::PAGE_BITS; } }; +struct hb_set_t : hb_sparseset_t {}; + +static_assert (hb_set_t::INVALID == HB_SET_VALUE_INVALID, ""); + #endif /* HB_SET_HH */ diff --git a/gfx/harfbuzz/src/hb-shape-plan.cc b/gfx/harfbuzz/src/hb-shape-plan.cc index 0d9eaddaa693..66332165c37c 100644 --- a/gfx/harfbuzz/src/hb-shape-plan.cc +++ b/gfx/harfbuzz/src/hb-shape-plan.cc @@ -66,7 +66,7 @@ hb_shape_plan_key_t::init (bool copy, const char * const *shaper_list) { hb_feature_t *features = nullptr; - if (copy && num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t)))) + if (copy && num_user_features && !(features = (hb_feature_t *) hb_calloc (num_user_features, sizeof (hb_feature_t)))) goto bail; this->props = *props; @@ -130,7 +130,7 @@ hb_shape_plan_key_t::init (bool copy, #undef HB_SHAPER_PLAN bail: - ::free (features); + ::hb_free (features); return false; } @@ -264,9 +264,9 @@ hb_shape_plan_create2 (hb_face_t *face, #ifndef HB_NO_OT_SHAPE bail3: #endif - shape_plan->key.free (); + shape_plan->key.fini (); bail2: - free (shape_plan); + hb_free (shape_plan); bail: return hb_shape_plan_get_empty (); } @@ -320,8 +320,8 @@ hb_shape_plan_destroy (hb_shape_plan_t *shape_plan) #ifndef HB_NO_OT_SHAPE shape_plan->ot.fini (); #endif - shape_plan->key.free (); - free (shape_plan); + shape_plan->key.fini (); + hb_free (shape_plan); } /** @@ -404,7 +404,7 @@ _hb_shape_plan_execute_internal (hb_shape_plan_t *shape_plan, buffer->assert_unicode (); - if (unlikely (hb_object_is_inert (shape_plan))) + if (unlikely (!hb_object_is_valid (shape_plan))) return false; assert (shape_plan->face_unsafe == font->face); @@ -529,7 +529,7 @@ hb_shape_plan_create_cached2 (hb_face_t *face, retry: hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans; - bool dont_cache = hb_object_is_inert (face); + bool dont_cache = !hb_object_is_valid (face); if (likely (!dont_cache)) { @@ -560,7 +560,7 @@ retry: if (unlikely (dont_cache)) return shape_plan; - hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t)); + hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) hb_calloc (1, sizeof (hb_face_t::plan_node_t)); if (unlikely (!node)) return shape_plan; @@ -570,7 +570,7 @@ retry: if (unlikely (!face->shape_plans.cmpexch (cached_plan_nodes, node))) { hb_shape_plan_destroy (shape_plan); - free (node); + hb_free (node); goto retry; } DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache"); diff --git a/gfx/harfbuzz/src/hb-shape-plan.hh b/gfx/harfbuzz/src/hb-shape-plan.hh index 6da7edb2f876..8cb4ddb927c4 100644 --- a/gfx/harfbuzz/src/hb-shape-plan.hh +++ b/gfx/harfbuzz/src/hb-shape-plan.hh @@ -55,7 +55,7 @@ struct hb_shape_plan_key_t unsigned int num_coords, const char * const *shaper_list); - HB_INTERNAL void free () { ::free ((void *) user_features); } + HB_INTERNAL void fini () { hb_free ((void *) user_features); } HB_INTERNAL bool user_features_match (const hb_shape_plan_key_t *other); diff --git a/gfx/harfbuzz/src/hb-shape.cc b/gfx/harfbuzz/src/hb-shape.cc index c442f4403bb4..f303a569fd6f 100644 --- a/gfx/harfbuzz/src/hb-shape.cc +++ b/gfx/harfbuzz/src/hb-shape.cc @@ -59,7 +59,7 @@ static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t interp; interp.env.init (str, acc, fd); - flatten_param_t param = { flat_charstrings[i], plan->drop_hints }; + flatten_param_t param = { + flat_charstrings[i], + (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + }; if (unlikely (!interp.interpret (param))) return false; } @@ -636,7 +639,7 @@ struct subr_subsetter_t param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - plan->drop_hints); + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); if (unlikely (!interp.interpret (param))) return false; @@ -645,7 +648,7 @@ struct subr_subsetter_t SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]); } - if (plan->drop_hints) + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) { /* mark hint ops and arguments for drop */ for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) @@ -660,7 +663,7 @@ struct subr_subsetter_t param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - plan->drop_hints); + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); drop_hints_param_t drop; if (drop_hints_in_str (parsed_charstrings[i], param, drop)) @@ -685,7 +688,7 @@ struct subr_subsetter_t param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - plan->drop_hints); + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); collect_subr_refs_in_str (parsed_charstrings[i], param); } } diff --git a/gfx/harfbuzz/src/hb-subset-cff1.cc b/gfx/harfbuzz/src/hb-subset-cff1.cc index 35dae7b1f11f..b4e24122c982 100644 --- a/gfx/harfbuzz/src/hb-subset-cff1.cc +++ b/gfx/harfbuzz/src/hb-subset-cff1.cc @@ -543,8 +543,8 @@ struct cff_subset_plan { num_glyphs = plan->num_output_glyphs (); orig_fdcount = acc.fdCount; - drop_hints = plan->drop_hints; - desubroutinize = plan->desubroutinize; + drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE; /* check whether the subset renumbers any glyph IDs */ gid_renum = false; @@ -917,12 +917,6 @@ _hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc, return _serialize_cff1 (c->serializer, cff_plan, acc, c->plan->num_output_glyphs ()); } -/** - * hb_subset_cff1: - * Subsets the CFF table according to a provided plan. - * - * Return value: subsetted cff table. - **/ bool hb_subset_cff1 (hb_subset_context_t *c) { diff --git a/gfx/harfbuzz/src/hb-subset-cff2.cc b/gfx/harfbuzz/src/hb-subset-cff2.cc index 17ee040deb6c..896ae6401696 100644 --- a/gfx/harfbuzz/src/hb-subset-cff2.cc +++ b/gfx/harfbuzz/src/hb-subset-cff2.cc @@ -262,8 +262,8 @@ struct cff2_subset_plan { { orig_fdcount = acc.fdArray->count; - drop_hints = plan->drop_hints; - desubroutinize = plan->desubroutinize; + drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE; if (desubroutinize) { @@ -470,10 +470,6 @@ _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc, return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ()); } -/** - * hb_subset_cff2: - * Subsets the CFF2 table according to a provided subset context. - **/ bool hb_subset_cff2 (hb_subset_context_t *c) { diff --git a/gfx/harfbuzz/src/hb-subset-input.cc b/gfx/harfbuzz/src/hb-subset-input.cc index 1f64bc99a0cc..498f92d224f9 100644 --- a/gfx/harfbuzz/src/hb-subset-input.cc +++ b/gfx/harfbuzz/src/hb-subset-input.cc @@ -30,29 +30,34 @@ /** * hb_subset_input_create_or_fail: * - * Return value: New subset input. + * Creates a new subset input object. + * + * Return value: (transfer full): New subset input, or %NULL if failed. Destroy + * with hb_subset_input_destroy(). * * Since: 1.8.0 **/ hb_subset_input_t * -hb_subset_input_create_or_fail () +hb_subset_input_create_or_fail (void) { hb_subset_input_t *input = hb_object_create(); if (unlikely (!input)) return nullptr; - input->unicodes = hb_set_create (); - input->glyphs = hb_set_create (); - input->name_ids = hb_set_create (); - hb_set_add_range (input->name_ids, 0, 6); - input->name_languages = hb_set_create (); - hb_set_add (input->name_languages, 0x0409); - input->drop_tables = hb_set_create (); - input->drop_hints = false; - input->desubroutinize = false; - input->retain_gids = false; - input->name_legacy = false; + for (auto& set : input->sets_iter ()) + set = hb_set_create (); + + if (input->in_error ()) + { + hb_subset_input_destroy (input); + return nullptr; + } + + input->flags = HB_SUBSET_FLAGS_DEFAULT; + + hb_set_add_range (input->sets.name_ids, 0, 6); + hb_set_add (input->sets.name_languages, 0x0409); hb_tag_t default_drop_tables[] = { // Layout disabled by default @@ -78,149 +83,483 @@ hb_subset_input_create_or_fail () HB_TAG ('S', 'i', 'l', 'f'), HB_TAG ('S', 'i', 'l', 'l'), }; + input->sets.drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables)); - input->drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables)); + hb_tag_t default_no_subset_tables[] = { + HB_TAG ('a', 'v', 'a', 'r'), + HB_TAG ('f', 'v', 'a', 'r'), + HB_TAG ('g', 'a', 's', 'p'), + HB_TAG ('c', 'v', 't', ' '), + HB_TAG ('f', 'p', 'g', 'm'), + HB_TAG ('p', 'r', 'e', 'p'), + HB_TAG ('V', 'D', 'M', 'X'), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('M', 'V', 'A', 'R'), + HB_TAG ('c', 'v', 'a', 'r'), + HB_TAG ('S', 'T', 'A', 'T'), + }; + input->sets.no_subset_tables->add_array (default_no_subset_tables, + ARRAY_LENGTH (default_no_subset_tables)); + //copied from _layout_features_groups in fonttools + hb_tag_t default_layout_features[] = { + // default shaper + // common + HB_TAG ('r', 'v', 'r', 'n'), + HB_TAG ('c', 'c', 'm', 'p'), + HB_TAG ('l', 'i', 'g', 'a'), + HB_TAG ('l', 'o', 'c', 'l'), + HB_TAG ('m', 'a', 'r', 'k'), + HB_TAG ('m', 'k', 'm', 'k'), + HB_TAG ('r', 'l', 'i', 'g'), + + //fractions + HB_TAG ('f', 'r', 'a', 'c'), + HB_TAG ('n', 'u', 'm', 'r'), + HB_TAG ('d', 'n', 'o', 'm'), + + //horizontal + HB_TAG ('c', 'a', 'l', 't'), + HB_TAG ('c', 'l', 'i', 'g'), + HB_TAG ('c', 'u', 'r', 's'), + HB_TAG ('k', 'e', 'r', 'n'), + HB_TAG ('r', 'c', 'l', 't'), + + //vertical + HB_TAG ('v', 'a', 'l', 't'), + HB_TAG ('v', 'e', 'r', 't'), + HB_TAG ('v', 'k', 'r', 'n'), + HB_TAG ('v', 'p', 'a', 'l'), + HB_TAG ('v', 'r', 't', '2'), + + //ltr + HB_TAG ('l', 't', 'r', 'a'), + HB_TAG ('l', 't', 'r', 'm'), + + //rtl + HB_TAG ('r', 't', 'l', 'a'), + HB_TAG ('r', 't', 'l', 'm'), + + //Complex shapers + //arabic + HB_TAG ('i', 'n', 'i', 't'), + HB_TAG ('m', 'e', 'd', 'i'), + HB_TAG ('f', 'i', 'n', 'a'), + HB_TAG ('i', 's', 'o', 'l'), + HB_TAG ('m', 'e', 'd', '2'), + HB_TAG ('f', 'i', 'n', '2'), + HB_TAG ('f', 'i', 'n', '3'), + HB_TAG ('c', 's', 'w', 'h'), + HB_TAG ('m', 's', 'e', 't'), + HB_TAG ('s', 't', 'c', 'h'), + + //hangul + HB_TAG ('l', 'j', 'm', 'o'), + HB_TAG ('v', 'j', 'm', 'o'), + HB_TAG ('t', 'j', 'm', 'o'), + + //tibetan + HB_TAG ('a', 'b', 'v', 's'), + HB_TAG ('b', 'l', 'w', 's'), + HB_TAG ('a', 'b', 'v', 'm'), + HB_TAG ('b', 'l', 'w', 'm'), + + //indic + HB_TAG ('n', 'u', 'k', 't'), + HB_TAG ('a', 'k', 'h', 'n'), + HB_TAG ('r', 'p', 'h', 'f'), + HB_TAG ('r', 'k', 'r', 'f'), + HB_TAG ('p', 'r', 'e', 'f'), + HB_TAG ('b', 'l', 'w', 'f'), + HB_TAG ('h', 'a', 'l', 'f'), + HB_TAG ('a', 'b', 'v', 'f'), + HB_TAG ('p', 's', 't', 'f'), + HB_TAG ('c', 'f', 'a', 'r'), + HB_TAG ('v', 'a', 't', 'u'), + HB_TAG ('c', 'j', 'c', 't'), + HB_TAG ('i', 'n', 'i', 't'), + HB_TAG ('p', 'r', 'e', 's'), + HB_TAG ('a', 'b', 'v', 's'), + HB_TAG ('b', 'l', 'w', 's'), + HB_TAG ('p', 's', 't', 's'), + HB_TAG ('h', 'a', 'l', 'n'), + HB_TAG ('d', 'i', 's', 't'), + HB_TAG ('a', 'b', 'v', 'm'), + HB_TAG ('b', 'l', 'w', 'm'), + }; + + input->sets.layout_features->add_array (default_layout_features, ARRAY_LENGTH (default_layout_features)); + + if (input->in_error ()) + { + hb_subset_input_destroy (input); + return nullptr; + } return input; } /** * hb_subset_input_reference: (skip) - * @subset_input: a subset_input. + * @input: a #hb_subset_input_t object. * + * Increases the reference count on @input. * - * - * Return value: + * Return value: @input. * * Since: 1.8.0 **/ hb_subset_input_t * -hb_subset_input_reference (hb_subset_input_t *subset_input) +hb_subset_input_reference (hb_subset_input_t *input) { - return hb_object_reference (subset_input); + return hb_object_reference (input); } /** * hb_subset_input_destroy: - * @subset_input: a subset_input. + * @input: a #hb_subset_input_t object. + * + * Decreases the reference count on @input, and if it reaches zero, destroys + * @input, freeing all memory. * * Since: 1.8.0 **/ void -hb_subset_input_destroy (hb_subset_input_t *subset_input) +hb_subset_input_destroy (hb_subset_input_t *input) { - if (!hb_object_destroy (subset_input)) return; + if (!hb_object_destroy (input)) return; - hb_set_destroy (subset_input->unicodes); - hb_set_destroy (subset_input->glyphs); - hb_set_destroy (subset_input->name_ids); - hb_set_destroy (subset_input->name_languages); - hb_set_destroy (subset_input->drop_tables); + for (hb_set_t* set : input->sets_iter ()) + hb_set_destroy (set); - free (subset_input); + hb_free (input); } /** * hb_subset_input_unicode_set: - * @subset_input: a subset_input. + * @input: a #hb_subset_input_t object. + * + * Gets the set of Unicode code points to retain, the caller should modify the + * set as needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of Unicode code + * points. * * Since: 1.8.0 **/ HB_EXTERN hb_set_t * -hb_subset_input_unicode_set (hb_subset_input_t *subset_input) +hb_subset_input_unicode_set (hb_subset_input_t *input) { - return subset_input->unicodes; + return input->sets.unicodes; } /** * hb_subset_input_glyph_set: - * @subset_input: a subset_input. + * @input: a #hb_subset_input_t object. + * + * Gets the set of glyph IDs to retain, the caller should modify the set as + * needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of glyph IDs. * * Since: 1.8.0 **/ HB_EXTERN hb_set_t * -hb_subset_input_glyph_set (hb_subset_input_t *subset_input) +hb_subset_input_glyph_set (hb_subset_input_t *input) { - return subset_input->glyphs; + return input->sets.glyphs; } +/** + * hb_subset_input_nameid_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of name table name IDs to retain, the caller should modify the + * set as needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of name IDs. + * + * Since: 2.9.0 + **/ HB_EXTERN hb_set_t * -hb_subset_input_nameid_set (hb_subset_input_t *subset_input) +hb_subset_input_nameid_set (hb_subset_input_t *input) { - return subset_input->name_ids; + return input->sets.name_ids; } +/** + * hb_subset_input_namelangid_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of name table language IDs to retain, the caller should modify + * the set as needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of language IDs. + * + * Since: 2.9.0 + **/ HB_EXTERN hb_set_t * -hb_subset_input_namelangid_set (hb_subset_input_t *subset_input) +hb_subset_input_namelangid_set (hb_subset_input_t *input) { - return subset_input->name_languages; + return input->sets.name_languages; } + +/** + * hb_subset_input_layout_features_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of layout feature tags to retain, the caller should modify the + * set as needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of feature tags. + * + * Since: 2.9.0 + **/ HB_EXTERN hb_set_t * -hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input) +hb_subset_input_layout_features_set (hb_subset_input_t *input) { - return subset_input->drop_tables; + return input->sets.layout_features; } +/** + * hb_subset_input_drop_tables_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of table tags to drop, the caller should modify the set as + * needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of table tags. + * + * Since: 2.9.0 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_drop_tables_set (hb_subset_input_t *input) +{ + return input->sets.drop_tables; +} + +/** + * hb_subset_input_set: + * @input: a #hb_subset_input_t object. + * @set_type: a #hb_subset_sets_t set type. + * + * Gets the set of the specified type. + * + * Return value: (transfer none): pointer to the #hb_set_t of the specified type. + * + * Since: 2.9.1 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_set (hb_subset_input_t *input, hb_subset_sets_t set_type) +{ + return input->sets_iter () [set_type]; +} + +/** + * hb_subset_input_no_subset_tables_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of table tags which specifies tables that should not be + * subsetted, the caller should modify the set as needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of table tags. + * + * Since: 2.9.0 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_no_subset_tables_set (hb_subset_input_t *input) +{ + return input->sets.no_subset_tables; +} + + +/** + * hb_subset_input_get_flags: + * @input: a #hb_subset_input_t object. + * + * Gets all of the subsetting flags in the input object. + * + * Return value: the subsetting flags bit field. + * + * Since: 2.9.0 + **/ +HB_EXTERN hb_subset_flags_t +hb_subset_input_get_flags (hb_subset_input_t *input) +{ + return (hb_subset_flags_t) input->flags; +} + +/** + * hb_subset_input_set_flags: + * @input: a #hb_subset_input_t object. + * @value: bit field of flags + * + * Sets all of the flags in the input object to the values specified by the bit + * field. + * + * Since: 2.9.0 + **/ HB_EXTERN void +hb_subset_input_set_flags (hb_subset_input_t *input, + unsigned value) +{ + input->flags = (hb_subset_flags_t) value; +} + +/** + * hb_subset_input_set_user_data: (skip) + * @input: a #hb_subset_input_t object. + * @key: The user-data key to set + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the given subset input object. + * + * Return value: %true if success, %false otherwise + * + * Since: 2.9.0 + **/ +hb_bool_t +hb_subset_input_set_user_data (hb_subset_input_t *input, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (input, key, data, destroy, replace); +} + +/** + * hb_subset_input_get_user_data: (skip) + * @input: a #hb_subset_input_t object. + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified subset input object. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 2.9.0 + **/ +void * +hb_subset_input_get_user_data (const hb_subset_input_t *input, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (input, key); +} + + +static void set_flag_value (hb_subset_input_t *input, hb_subset_flags_t flag, hb_bool_t value) +{ + hb_subset_input_set_flags (input, + value + ? hb_subset_input_get_flags (input) | flag + : hb_subset_input_get_flags (input) & ~flag); +} + +void hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_bool_t drop_hints) { - subset_input->drop_hints = drop_hints; + return set_flag_value (subset_input, + HB_SUBSET_FLAGS_NO_HINTING, + drop_hints); } -HB_EXTERN hb_bool_t +hb_bool_t hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input) { - return subset_input->drop_hints; + return (bool) (hb_subset_input_get_flags (subset_input) & HB_SUBSET_FLAGS_NO_HINTING); } -HB_EXTERN void +void hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, hb_bool_t desubroutinize) { - subset_input->desubroutinize = desubroutinize; + return set_flag_value (subset_input, + HB_SUBSET_FLAGS_DESUBROUTINIZE, + desubroutinize); } -HB_EXTERN hb_bool_t +hb_bool_t hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input) { - return subset_input->desubroutinize; + return (bool) (hb_subset_input_get_flags (subset_input) & HB_SUBSET_FLAGS_DESUBROUTINIZE); } -/** - * hb_subset_input_set_retain_gids: - * @subset_input: a subset_input. - * @retain_gids: If true the subsetter will not renumber glyph ids. - * Since: 2.4.0 - **/ -HB_EXTERN void +void hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input, hb_bool_t retain_gids) { - subset_input->retain_gids = retain_gids; + return set_flag_value (subset_input, + HB_SUBSET_FLAGS_RETAIN_GIDS, + retain_gids); } -/** - * hb_subset_input_get_retain_gids: - * Returns: value of retain_gids. - * Since: 2.4.0 - **/ -HB_EXTERN hb_bool_t +hb_bool_t hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input) { - return subset_input->retain_gids; + return (bool) (hb_subset_input_get_flags (subset_input) & HB_SUBSET_FLAGS_RETAIN_GIDS); } -HB_EXTERN void +void hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input, hb_bool_t name_legacy) { - subset_input->name_legacy = name_legacy; + return set_flag_value (subset_input, + HB_SUBSET_FLAGS_NAME_LEGACY, + name_legacy); } -HB_EXTERN hb_bool_t +hb_bool_t hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input) { - return subset_input->name_legacy; + return (bool) (hb_subset_input_get_flags (subset_input) & HB_SUBSET_FLAGS_NAME_LEGACY); +} + +void +hb_subset_input_set_overlaps_flag (hb_subset_input_t *subset_input, + hb_bool_t overlaps_flag) +{ + return set_flag_value (subset_input, + HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG, + overlaps_flag); +} + +hb_bool_t +hb_subset_input_get_overlaps_flag (hb_subset_input_t *subset_input) +{ + return (bool) (hb_subset_input_get_flags (subset_input) & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG); +} + +void +hb_subset_input_set_notdef_outline (hb_subset_input_t *subset_input, + hb_bool_t notdef_outline) +{ + return set_flag_value (subset_input, + HB_SUBSET_FLAGS_NOTDEF_OUTLINE, + notdef_outline); +} + +hb_bool_t +hb_subset_input_get_notdef_outline (hb_subset_input_t *subset_input) +{ + return (bool) (hb_subset_input_get_flags (subset_input) & HB_SUBSET_FLAGS_NOTDEF_OUTLINE); +} + +void +hb_subset_input_set_no_prune_unicode_ranges (hb_subset_input_t *subset_input, + hb_bool_t no_prune_unicode_ranges) +{ + return set_flag_value (subset_input, + HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES, + no_prune_unicode_ranges); +} + + +hb_bool_t +hb_subset_input_get_no_prune_unicode_ranges (hb_subset_input_t *subset_input) +{ + return (bool) (hb_subset_input_get_flags (subset_input) & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES); } diff --git a/gfx/harfbuzz/src/hb-subset-input.hh b/gfx/harfbuzz/src/hb-subset-input.hh index 0aeb96695b77..a3e28b05627d 100644 --- a/gfx/harfbuzz/src/hb-subset-input.hh +++ b/gfx/harfbuzz/src/hb-subset-input.hh @@ -31,30 +31,51 @@ #include "hb.hh" #include "hb-subset.h" +#include "hb-map.hh" +#include "hb-set.hh" #include "hb-font.hh" +HB_MARK_AS_FLAG_T (hb_subset_flags_t); + struct hb_subset_input_t { hb_object_header_t header; - hb_set_t *unicodes; - hb_set_t *glyphs; - hb_set_t *name_ids; - hb_set_t *name_languages; - hb_set_t *drop_tables; + union { + struct { + hb_set_t *glyphs; + hb_set_t *unicodes; + hb_set_t *no_subset_tables; + hb_set_t *drop_tables; + hb_set_t *name_ids; + hb_set_t *name_languages; + hb_set_t *layout_features; + } sets; + hb_set_t* set_ptrs[sizeof (sets) / sizeof (hb_set_t*)]; + }; - bool drop_hints; - bool desubroutinize; - bool retain_gids; - bool name_legacy; - /* TODO - * - * features - * lookups - * name_ids - * ... - */ + unsigned flags; + + inline unsigned num_sets () const + { + return sizeof (set_ptrs) / sizeof (hb_set_t*); + } + + inline hb_array_t sets_iter () + { + return hb_array_t (set_ptrs, num_sets ()); + } + + bool in_error () const + { + for (unsigned i = 0; i < num_sets (); i++) + { + if (unlikely (set_ptrs[i]->in_error ())) + return true; + } + return false; + } }; diff --git a/gfx/harfbuzz/src/hb-subset-plan.cc b/gfx/harfbuzz/src/hb-subset-plan.cc index dfb0d42f38d2..677df35fad23 100644 --- a/gfx/harfbuzz/src/hb-subset-plan.cc +++ b/gfx/harfbuzz/src/hb-subset-plan.cc @@ -35,6 +35,7 @@ #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-color-colr-table.hh" +#include "hb-ot-color-colrv1-closure.hh" #include "hb-ot-var-fvar-table.hh" #include "hb-ot-stat-table.hh" @@ -55,7 +56,23 @@ _add_cff_seac_components (const OT::cff1::accelerator_t &cff, } #endif -#ifndef HB_NO_SUBSET_LAYOUT +static void +_remap_palette_indexes (const hb_set_t *palette_indexes, + hb_map_t *mapping /* OUT */) +{ + unsigned new_idx = 0; + for (unsigned palette_index : palette_indexes->iter ()) + { + if (palette_index == 0xFFFF) + { + mapping->set (palette_index, palette_index); + continue; + } + mapping->set (palette_index, new_idx); + new_idx++; + } +} + static void _remap_indexes (const hb_set_t *indexes, hb_map_t *mapping /* OUT */) @@ -67,88 +84,100 @@ _remap_indexes (const hb_set_t *indexes, } -static inline void -_gsub_closure_glyphs_lookups_features (hb_face_t *face, - hb_set_t *gids_to_retain, - hb_map_t *gsub_lookups, - hb_map_t *gsub_features, - script_langsys_map *gsub_langsys) +#ifndef HB_NO_SUBSET_LAYOUT +typedef void (*layout_collect_func_t) (hb_face_t *face, hb_tag_t table_tag, const hb_tag_t *scripts, const hb_tag_t *languages, const hb_tag_t *features, hb_set_t *lookup_indexes /* OUT */); + + +template +static void _collect_layout_indices (hb_face_t *face, + const T& table, + const hb_set_t *layout_features_to_retain, + layout_collect_func_t layout_collect_func, + hb_set_t *indices /* OUT */) { + hb_vector_t features; + if (!features.alloc (table.get_feature_count () + 1)) + return; + + for (unsigned i = 0; i < table.get_feature_count (); i++) + { + hb_tag_t tag = table.get_feature_tag (i); + if (tag && layout_features_to_retain->has (tag)) + features.push (tag); + } + + if (!features) + return; + + // The collect function needs a null element to signal end of the array. + features.push (0); + + if (features.get_size () == table.get_feature_count () + 1) + { + // Looking for all features, trigger the faster collection method. + layout_collect_func (face, + T::tableTag, + nullptr, + nullptr, + nullptr, + indices); + return; + } + + layout_collect_func (face, + T::tableTag, + nullptr, + nullptr, + features.arrayZ, + indices); +} + +template +static inline void +_closure_glyphs_lookups_features (hb_face_t *face, + hb_set_t *gids_to_retain, + const hb_set_t *layout_features_to_retain, + hb_map_t *lookups, + hb_map_t *features, + script_langsys_map *langsys_map) +{ + hb_blob_ptr_t table = hb_sanitize_context_t ().reference_table (face); + hb_tag_t table_tag = table->tableTag; hb_set_t lookup_indices; - hb_ot_layout_collect_lookups (face, - HB_OT_TAG_GSUB, - nullptr, - nullptr, - nullptr, - &lookup_indices); - hb_ot_layout_lookups_substitute_closure (face, - &lookup_indices, - gids_to_retain); - hb_blob_ptr_t gsub = hb_sanitize_context_t ().reference_table (face); - gsub->closure_lookups (face, - gids_to_retain, + _collect_layout_indices (face, + *table, + layout_features_to_retain, + hb_ot_layout_collect_lookups, + &lookup_indices); + + if (table_tag == HB_OT_TAG_GSUB) + hb_ot_layout_lookups_substitute_closure (face, + &lookup_indices, + gids_to_retain); + table->closure_lookups (face, + gids_to_retain, &lookup_indices); - _remap_indexes (&lookup_indices, gsub_lookups); + _remap_indexes (&lookup_indices, lookups); // Collect and prune features hb_set_t feature_indices; - hb_ot_layout_collect_features (face, - HB_OT_TAG_GSUB, - nullptr, - nullptr, - nullptr, - &feature_indices); + _collect_layout_indices (face, + *table, + layout_features_to_retain, + hb_ot_layout_collect_features, + &feature_indices); - gsub->prune_features (gsub_lookups, &feature_indices); + table->prune_features (lookups, &feature_indices); hb_map_t duplicate_feature_map; - gsub->find_duplicate_features (gsub_lookups, &feature_indices, &duplicate_feature_map); + table->find_duplicate_features (lookups, &feature_indices, &duplicate_feature_map); feature_indices.clear (); - gsub->prune_langsys (&duplicate_feature_map, gsub_langsys, &feature_indices); - _remap_indexes (&feature_indices, gsub_features); + table->prune_langsys (&duplicate_feature_map, langsys_map, &feature_indices); + _remap_indexes (&feature_indices, features); - gsub.destroy (); + table.destroy (); } -static inline void -_gpos_closure_lookups_features (hb_face_t *face, - const hb_set_t *gids_to_retain, - hb_map_t *gpos_lookups, - hb_map_t *gpos_features, - script_langsys_map *gpos_langsys) -{ - hb_set_t lookup_indices; - hb_ot_layout_collect_lookups (face, - HB_OT_TAG_GPOS, - nullptr, - nullptr, - nullptr, - &lookup_indices); - hb_blob_ptr_t gpos = hb_sanitize_context_t ().reference_table (face); - gpos->closure_lookups (face, - gids_to_retain, - &lookup_indices); - _remap_indexes (&lookup_indices, gpos_lookups); - - // Collect and prune features - hb_set_t feature_indices; - hb_ot_layout_collect_features (face, - HB_OT_TAG_GPOS, - nullptr, - nullptr, - nullptr, - &feature_indices); - - gpos->prune_features (gpos_lookups, &feature_indices); - hb_map_t duplicate_feature_map; - gpos->find_duplicate_features (gpos_lookups, &feature_indices, &duplicate_feature_map); - - feature_indices.clear (); - gpos->prune_langsys (&duplicate_feature_map, gpos_langsys, &feature_indices); - _remap_indexes (&feature_indices, gpos_features); - - gpos.destroy (); -} #endif #ifndef HB_NO_VAR @@ -182,9 +211,9 @@ static inline void #endif static inline void -_cmap_closure (hb_face_t *face, - const hb_set_t *unicodes, - hb_set_t *glyphset) +_cmap_closure (hb_face_t *face, + const hb_set_t *unicodes, + hb_set_t *glyphset) { OT::cmap::accelerator_t cmap; cmap.init (face); @@ -204,21 +233,75 @@ _remove_invalid_gids (hb_set_t *glyphs, } } +static void +_populate_unicodes_to_retain (const hb_set_t *unicodes, + const hb_set_t *glyphs, + hb_subset_plan_t *plan) +{ + OT::cmap::accelerator_t cmap; + cmap.init (plan->source); + + constexpr static const int size_threshold = 4096; + + if (glyphs->is_empty () && unicodes->get_population () < size_threshold) + { + /* This is the fast path if it's anticipated that size of unicodes + * is << than the number of codepoints in the font. */ + for (hb_codepoint_t cp : *unicodes) + { + hb_codepoint_t gid; + if (!cmap.get_nominal_glyph (cp, &gid)) + { + DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); + continue; + } + + plan->codepoint_to_glyph->set (cp, gid); + } + } + else + { + hb_map_t unicode_glyphid_map; + cmap.collect_mapping (hb_set_get_empty (), &unicode_glyphid_map); + + for (hb_pair_t cp_gid : + + unicode_glyphid_map.iter ()) + { + if (!unicodes->has (cp_gid.first) && !glyphs->has (cp_gid.second)) + continue; + + plan->codepoint_to_glyph->set (cp_gid.first, cp_gid.second); + } + + /* Add gids which where requested, but not mapped in cmap */ + // TODO(garretrieger): + // Once https://github.com/harfbuzz/harfbuzz/issues/3169 + // is implemented, this can be done with union and del_range + for (hb_codepoint_t gid : glyphs->iter ()) + { + if (gid >= plan->source->get_num_glyphs ()) + break; + plan->_glyphset_gsub->add (gid); + } + } + + + plan->codepoint_to_glyph->keys () | hb_sink (plan->unicodes); + + plan->codepoint_to_glyph->values () | hb_sink (plan->_glyphset_gsub); + + cmap.fini (); +} + static void _populate_gids_to_retain (hb_subset_plan_t* plan, - const hb_set_t *unicodes, - const hb_set_t *input_glyphs_to_retain, bool close_over_gsub, bool close_over_gpos, bool close_over_gdef) { - OT::cmap::accelerator_t cmap; OT::glyf::accelerator_t glyf; #ifndef HB_NO_SUBSET_CFF OT::cff1::accelerator_t cff; #endif OT::COLR::accelerator_t colr; - cmap.init (plan->source); glyf.init (plan->source); #ifndef HB_NO_SUBSET_CFF cff.init (plan->source); @@ -226,73 +309,88 @@ _populate_gids_to_retain (hb_subset_plan_t* plan, colr.init (plan->source); plan->_glyphset_gsub->add (0); // Not-def - hb_set_union (plan->_glyphset_gsub, input_glyphs_to_retain); - - hb_codepoint_t cp = HB_SET_VALUE_INVALID; - while (unicodes->next (&cp)) - { - hb_codepoint_t gid; - if (!cmap.get_nominal_glyph (cp, &gid)) - { - DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); - continue; - } - plan->unicodes->add (cp); - plan->codepoint_to_glyph->set (cp, gid); - plan->_glyphset_gsub->add (gid); - } _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub); #ifndef HB_NO_SUBSET_LAYOUT if (close_over_gsub) // closure all glyphs/lookups/features needed for GSUB substitutions. - _gsub_closure_glyphs_lookups_features (plan->source, plan->_glyphset_gsub, plan->gsub_lookups, plan->gsub_features, plan->gsub_langsys); + _closure_glyphs_lookups_features ( + plan->source, + plan->_glyphset_gsub, + plan->layout_features, + plan->gsub_lookups, + plan->gsub_features, + plan->gsub_langsys); if (close_over_gpos) - _gpos_closure_lookups_features (plan->source, plan->_glyphset_gsub, plan->gpos_lookups, plan->gpos_features, plan->gpos_langsys); + _closure_glyphs_lookups_features ( + plan->source, + plan->_glyphset_gsub, + plan->layout_features, + plan->gpos_lookups, + plan->gpos_features, + plan->gpos_langsys); #endif _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ()); + // Collect all glyphs referenced by COLRv0 + hb_set_t* cur_glyphset = plan->_glyphset_gsub; + hb_set_t glyphset_colrv0; + if (colr.is_valid ()) + { + glyphset_colrv0.union_ (*cur_glyphset); + for (hb_codepoint_t gid : cur_glyphset->iter ()) + colr.closure_glyphs (gid, &glyphset_colrv0); + cur_glyphset = &glyphset_colrv0; + } + + hb_set_t palette_indices; + colr.closure_V0palette_indices (cur_glyphset, &palette_indices); + + hb_set_t layer_indices; + colr.closure_forV1 (cur_glyphset, &layer_indices, &palette_indices); + _remap_indexes (&layer_indices, plan->colrv1_layers); + _remap_palette_indexes (&palette_indices, plan->colr_palettes); + colr.fini (); + _remove_invalid_gids (cur_glyphset, plan->source->get_num_glyphs ()); + // Populate a full set of glyphs to retain by adding all referenced // composite glyphs. - hb_codepoint_t gid = HB_SET_VALUE_INVALID; - while (plan->_glyphset_gsub->next (&gid)) + for (hb_codepoint_t gid : cur_glyphset->iter ()) { glyf.add_gid_and_children (gid, plan->_glyphset); #ifndef HB_NO_SUBSET_CFF if (cff.is_valid ()) _add_cff_seac_components (cff, gid, plan->_glyphset); #endif - if (colr.is_valid ()) - colr.closure_glyphs (gid, plan->_glyphset); } _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ()); + #ifndef HB_NO_VAR if (close_over_gdef) _collect_layout_variation_indices (plan->source, - plan->_glyphset_gsub, - plan->gpos_lookups, - plan->layout_variation_indices, - plan->layout_variation_idx_map); + plan->_glyphset_gsub, + plan->gpos_lookups, + plan->layout_variation_indices, + plan->layout_variation_idx_map); #endif #ifndef HB_NO_SUBSET_CFF cff.fini (); #endif glyf.fini (); - cmap.fini (); } static void _create_old_gid_to_new_gid_map (const hb_face_t *face, - bool retain_gids, - const hb_set_t *all_gids_to_retain, - hb_map_t *glyph_map, /* OUT */ - hb_map_t *reverse_glyph_map, /* OUT */ - unsigned int *num_glyphs /* OUT */) + bool retain_gids, + const hb_set_t *all_gids_to_retain, + hb_map_t *glyph_map, /* OUT */ + hb_map_t *reverse_glyph_map, /* OUT */ + unsigned int *num_glyphs /* OUT */) { if (!retain_gids) { @@ -335,33 +433,36 @@ _nameid_closure (hb_face_t *face, /** * hb_subset_plan_create: + * @face: font face to create the plan for. + * @input: a #hb_subset_input_t input. + * * Computes a plan for subsetting the supplied face according * to a provided input. The plan describes * which tables and glyphs should be retained. * - * Return value: New subset plan. + * Return value: (transfer full): New subset plan. Destroy with + * hb_subset_plan_destroy(). * * Since: 1.7.5 **/ hb_subset_plan_t * -hb_subset_plan_create (hb_face_t *face, - hb_subset_input_t *input) +hb_subset_plan_create (hb_face_t *face, + const hb_subset_input_t *input) { hb_subset_plan_t *plan; if (unlikely (!(plan = hb_object_create ()))) return const_cast (&Null (hb_subset_plan_t)); plan->successful = true; - plan->drop_hints = input->drop_hints; - plan->desubroutinize = input->desubroutinize; - plan->retain_gids = input->retain_gids; - plan->name_legacy = input->name_legacy; + plan->flags = input->flags; plan->unicodes = hb_set_create (); - plan->name_ids = hb_set_reference (input->name_ids); + plan->name_ids = hb_set_copy (input->sets.name_ids); _nameid_closure (face, plan->name_ids); - plan->name_languages = hb_set_reference (input->name_languages); - plan->glyphs_requested = hb_set_reference (input->glyphs); - plan->drop_tables = hb_set_reference (input->drop_tables); + plan->name_languages = hb_set_copy (input->sets.name_languages); + plan->layout_features = hb_set_copy (input->sets.layout_features); + plan->glyphs_requested = hb_set_copy (input->sets.glyphs); + plan->drop_tables = hb_set_copy (input->sets.drop_tables); + plan->no_subset_tables = hb_set_copy (input->sets.no_subset_tables); plan->source = hb_face_reference (face); plan->dest = hb_face_builder_create (); @@ -380,6 +481,8 @@ hb_subset_plan_create (hb_face_t *face, plan->gsub_features = hb_map_create (); plan->gpos_features = hb_map_create (); + plan->colrv1_layers = hb_map_create (); + plan->colr_palettes = hb_map_create (); plan->layout_variation_indices = hb_set_create (); plan->layout_variation_idx_map = hb_map_create (); @@ -387,15 +490,15 @@ hb_subset_plan_create (hb_face_t *face, return plan; } + _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, plan); + _populate_gids_to_retain (plan, - input->unicodes, - input->glyphs, - !input->drop_tables->has (HB_OT_TAG_GSUB), - !input->drop_tables->has (HB_OT_TAG_GPOS), - !input->drop_tables->has (HB_OT_TAG_GDEF)); + !input->sets.drop_tables->has (HB_OT_TAG_GSUB), + !input->sets.drop_tables->has (HB_OT_TAG_GPOS), + !input->sets.drop_tables->has (HB_OT_TAG_GDEF)); _create_old_gid_to_new_gid_map (face, - input->retain_gids, + input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS, plan->_glyphset, plan->glyph_map, plan->reverse_glyph_map, @@ -406,6 +509,10 @@ hb_subset_plan_create (hb_face_t *face, /** * hb_subset_plan_destroy: + * @plan: a #hb_subset_plan_t + * + * Decreases the reference count on @plan, and if it reaches zero, destroys + * @plan, freeing all memory. * * Since: 1.7.5 **/ @@ -417,8 +524,10 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) hb_set_destroy (plan->unicodes); hb_set_destroy (plan->name_ids); hb_set_destroy (plan->name_languages); + hb_set_destroy (plan->layout_features); hb_set_destroy (plan->glyphs_requested); hb_set_destroy (plan->drop_tables); + hb_set_destroy (plan->no_subset_tables); hb_face_destroy (plan->source); hb_face_destroy (plan->dest); hb_map_destroy (plan->codepoint_to_glyph); @@ -430,6 +539,8 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) hb_map_destroy (plan->gpos_lookups); hb_map_destroy (plan->gsub_features); hb_map_destroy (plan->gpos_features); + hb_map_destroy (plan->colrv1_layers); + hb_map_destroy (plan->colr_palettes); hb_set_destroy (plan->layout_variation_indices); hb_map_destroy (plan->layout_variation_idx_map); @@ -440,7 +551,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) hb_object_destroy (plan->gsub_langsys); plan->gsub_langsys->fini_shallow (); - free (plan->gsub_langsys); + hb_free (plan->gsub_langsys); } if (plan->gpos_langsys) @@ -450,8 +561,8 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) hb_object_destroy (plan->gpos_langsys); plan->gpos_langsys->fini_shallow (); - free (plan->gpos_langsys); + hb_free (plan->gpos_langsys); } - free (plan); + hb_free (plan); } diff --git a/gfx/harfbuzz/src/hb-subset-plan.hh b/gfx/harfbuzz/src/hb-subset-plan.hh index 1c070f91388a..92a4e27ccca9 100644 --- a/gfx/harfbuzz/src/hb-subset-plan.hh +++ b/gfx/harfbuzz/src/hb-subset-plan.hh @@ -39,11 +39,8 @@ struct hb_subset_plan_t { hb_object_header_t header; - bool successful : 1; - bool drop_hints : 1; - bool desubroutinize : 1; - bool retain_gids : 1; - bool name_legacy : 1; + bool successful; + unsigned flags; // For each cp that we'd like to retain maps to the corresponding gid. hb_set_t *unicodes; @@ -54,9 +51,15 @@ struct hb_subset_plan_t // name_languages we would like to retain hb_set_t *name_languages; + //layout features which will be preserved + hb_set_t *layout_features; + //glyph ids requested to retain hb_set_t *glyphs_requested; + // Tables which should not be processed, just pass them through. + hb_set_t *no_subset_tables; + // Tables which should be dropped. hb_set_t *drop_tables; @@ -87,6 +90,10 @@ struct hb_subset_plan_t hb_map_t *gsub_features; hb_map_t *gpos_features; + //active layers/palettes we'd like to retain + hb_map_t *colrv1_layers; + hb_map_t *colr_palettes; + //The set of layout item variation store delta set indices to be retained hb_set_t *layout_variation_indices; //Old -> New layout item variation store delta set index mapping @@ -193,7 +200,7 @@ typedef struct hb_subset_plan_t hb_subset_plan_t; HB_INTERNAL hb_subset_plan_t * hb_subset_plan_create (hb_face_t *face, - hb_subset_input_t *input); + const hb_subset_input_t *input); HB_INTERNAL void hb_subset_plan_destroy (hb_subset_plan_t *plan); diff --git a/gfx/harfbuzz/src/hb-subset.cc b/gfx/harfbuzz/src/hb-subset.cc index d443d248be57..9c4d64413f77 100644 --- a/gfx/harfbuzz/src/hb-subset.cc +++ b/gfx/harfbuzz/src/hb-subset.cc @@ -39,8 +39,10 @@ #include "hb-ot-maxp-table.hh" #include "hb-ot-color-sbix-table.hh" #include "hb-ot-color-colr-table.hh" +#include "hb-ot-color-cpal-table.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-post-table.hh" +#include "hb-ot-post-table-v2subset.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-vorg-table.hh" @@ -52,6 +54,26 @@ #include "hb-ot-var-hvar-table.hh" #include "hb-repacker.hh" +/** + * SECTION:hb-subset + * @title: hb-subset + * @short_description: Subsets font files. + * @include: hb-subset.h + * + * Subsetting reduces the codepoint coverage of font files and removes all data + * that is no longer needed. A subset input describes the desired subset. The input is + * provided along with a font to the subsetting operation. Output is a new font file + * containing only the data specified in the input. + * + * Currently most outline and bitmap tables are supported: glyf, CFF, CFF2, sbix, + * COLR, and CBDT/CBLC. This also includes fonts with variable outlines via OpenType + * variations. Notably EBDT/EBLC and SVG are not supported. Layout subsetting is supported + * only for OpenType Layout tables (GSUB, GPOS, GDEF). Notably subsetting of graphite or AAT tables + * is not yet supported. + * + * Fonts with graphite or AAT tables may still be subsetted but will likely need to use the + * retain glyph ids option and configure the subset to pass through the layout tables untouched. + */ static unsigned _plan_estimate_subset_table_size (hb_subset_plan_t *plan, unsigned table_len) @@ -106,6 +128,7 @@ _try_subset (const TableType *table, hb_subset_context_t* c /* OUT */) { c->serializer->start_serialize (); + if (c->serializer->in_error ()) return false; bool needed = table->subset (c); if (!c->serializer->ran_out_of_room ()) @@ -223,7 +246,7 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */ case HB_TAG ('h','d','m','x'): /* hint table, fallthrough */ case HB_TAG ('V','D','M','X'): /* hint table, fallthrough */ - return plan->drop_hints; + return plan->flags & HB_SUBSET_FLAGS_NO_HINTING; #ifdef HB_NO_SUBSET_LAYOUT // Drop Layout Tables if requested. @@ -242,9 +265,22 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) } } +static bool +_passthrough (hb_subset_plan_t *plan, hb_tag_t tag) +{ + hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); + bool result = plan->add_table (tag, source_table); + hb_blob_destroy (source_table); + return result; +} + static bool _subset_table (hb_subset_plan_t *plan, hb_tag_t tag) { + if (plan->no_subset_tables->has (tag)) { + return _passthrough (plan, tag); + } + DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag)); switch (tag) { @@ -266,6 +302,7 @@ _subset_table (hb_subset_plan_t *plan, hb_tag_t tag) case HB_OT_TAG_OS2 : return _subset (plan); case HB_OT_TAG_post: return _subset (plan); case HB_OT_TAG_COLR: return _subset (plan); + case HB_OT_TAG_CPAL: return _subset (plan); case HB_OT_TAG_CBLC: return _subset (plan); case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ @@ -285,29 +322,41 @@ _subset_table (hb_subset_plan_t *plan, hb_tag_t tag) #endif default: - hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); - bool result = plan->add_table (tag, source_table); - hb_blob_destroy (source_table); - return result; + if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) + return _passthrough (plan, tag); + + // Drop table + return true; } } +hb_face_t * +hb_subset (hb_face_t *source, hb_subset_input_t *input) +{ + hb_face_t* face = hb_subset_or_fail (source, input); + if (face) return face; + return hb_face_get_empty (); +} + /** - * hb_subset: + * hb_subset_or_fail: * @source: font face data to be subset. * @input: input to use for the subsetting. * - * Subsets a font according to provided input. + * Subsets a font according to provided input. Returns nullptr + * if the subset operation fails. + * + * Since: 2.9.0 **/ hb_face_t * -hb_subset (hb_face_t *source, hb_subset_input_t *input) +hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input) { if (unlikely (!input || !source)) return hb_face_get_empty (); hb_subset_plan_t *plan = hb_subset_plan_create (source, input); if (unlikely (plan->in_error ())) { hb_subset_plan_destroy (plan); - return hb_face_get_empty (); + return nullptr; } hb_set_t tags_set; @@ -328,7 +377,7 @@ hb_subset (hb_face_t *source, hb_subset_input_t *input) } end: - hb_face_t *result = success ? hb_face_reference (plan->dest) : hb_face_get_empty (); + hb_face_t *result = success ? hb_face_reference (plan->dest) : nullptr; hb_subset_plan_destroy (plan); return result; diff --git a/gfx/harfbuzz/src/hb-subset.h b/gfx/harfbuzz/src/hb-subset.h index ddf4409734e4..569594dbb587 100644 --- a/gfx/harfbuzz/src/hb-subset.h +++ b/gfx/harfbuzz/src/hb-subset.h @@ -31,37 +31,141 @@ HB_BEGIN_DECLS -/* - * hb_subset_input_t +/** + * hb_subset_input_t: * * Things that change based on the input. Characters to keep, etc. */ typedef struct hb_subset_input_t hb_subset_input_t; +/** + * hb_subset_flags_t: + * @HB_SUBSET_FLAGS_DEFAULT: all flags at their default value of false. + * @HB_SUBSET_FLAGS_NO_HINTING: If set hinting instructions will be dropped in + * the produced subset. Otherwise hinting instructions will be retained. + * @HB_SUBSET_FLAGS_RETAIN_GIDS: If set glyph indices will not be modified in + * the produced subset. If glyphs are dropped their indices will be retained + * as an empty glyph. + * @HB_SUBSET_FLAGS_DESUBROUTINIZE: If set and subsetting a CFF font the + * subsetter will attempt to remove subroutines from the CFF glyphs. + * @HB_SUBSET_FLAGS_NAME_LEGACY: If set non-unicode name records will be + * retained in the subset. + * @HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG: If set the subsetter will set the + * OVERLAP_SIMPLE flag on each simple glyph. + * @HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED: If set the subsetter will not + * drop unrecognized tables and instead pass them through untouched. + * @HB_SUBSET_FLAGS_NOTDEF_OUTLINE: If set the notdef glyph outline will be + * retained in the final subset. + * @HB_SUBSET_FLAGS_GLYPH_NAMES: If set the PS glyph names will be retained + * in the final subset. + * @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in + * OS/2 will not be recalculated. + * + * List of boolean properties that can be configured on the subset input. + * + * Since: 2.9.0 + **/ +typedef enum { /*< flags >*/ + HB_SUBSET_FLAGS_DEFAULT = 0x00000000u, + HB_SUBSET_FLAGS_NO_HINTING = 0x00000001u, + HB_SUBSET_FLAGS_RETAIN_GIDS = 0x00000002u, + HB_SUBSET_FLAGS_DESUBROUTINIZE = 0x00000004u, + HB_SUBSET_FLAGS_NAME_LEGACY = 0x00000008u, + HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG = 0x00000010u, + HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED = 0x00000020u, + HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 0x00000040u, + HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u, + HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u, +} hb_subset_flags_t; + +/** + * hb_subset_sets_t: + * HB_SUBSET_SETS_GLYPH_INDEX: the set of glyph indexes to retain in the subset. + * HB_SUBSET_SETS_UNICODE: the set of unicode codepoints to retain in the subset. + * @HB_SUBSET_SETS_NO_SUBSET_TABLE_TAG: the set of table tags which specifies tables that should not be + * subsetted. + * @HB_SUBSET_SETS_DROP_TABLE_TAG: the set of table tags which specifies tables which will be dropped + * in the subset. + * @HB_SUBSET_SETS_NAME_ID: the set of name ids that will be retained. + * @HB_SUBSET_SETS_NAME_LANG_ID: the set of name lang ids that will be retained. + * @HB_SUBSET_SETS_LAYOUT_FEATURE_TAG: the set of layout feature tags that will be retained + * in the subset. + * + * List of sets that can be configured on the subset input. + * + * Since: 2.9.1 + **/ +typedef enum { + HB_SUBSET_SETS_GLYPH_INDEX = 0, + HB_SUBSET_SETS_UNICODE, + HB_SUBSET_SETS_NO_SUBSET_TABLE_TAG, + HB_SUBSET_SETS_DROP_TABLE_TAG, + HB_SUBSET_SETS_NAME_ID, + HB_SUBSET_SETS_NAME_LANG_ID, + HB_SUBSET_SETS_LAYOUT_FEATURE_TAG, +} hb_subset_sets_t; + HB_EXTERN hb_subset_input_t * hb_subset_input_create_or_fail (void); HB_EXTERN hb_subset_input_t * -hb_subset_input_reference (hb_subset_input_t *subset_input); +hb_subset_input_reference (hb_subset_input_t *input); HB_EXTERN void -hb_subset_input_destroy (hb_subset_input_t *subset_input); +hb_subset_input_destroy (hb_subset_input_t *input); + +HB_EXTERN hb_bool_t +hb_subset_input_set_user_data (hb_subset_input_t *input, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_subset_input_get_user_data (const hb_subset_input_t *input, + hb_user_data_key_t *key); HB_EXTERN hb_set_t * -hb_subset_input_unicode_set (hb_subset_input_t *subset_input); +hb_subset_input_unicode_set (hb_subset_input_t *input); HB_EXTERN hb_set_t * -hb_subset_input_glyph_set (hb_subset_input_t *subset_input); +hb_subset_input_glyph_set (hb_subset_input_t *input); HB_EXTERN hb_set_t * -hb_subset_input_nameid_set (hb_subset_input_t *subset_input); +hb_subset_input_nameid_set (hb_subset_input_t *input); HB_EXTERN hb_set_t * -hb_subset_input_namelangid_set (hb_subset_input_t *subset_input); +hb_subset_input_namelangid_set (hb_subset_input_t *input); HB_EXTERN hb_set_t * -hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input); +hb_subset_input_layout_features_set (hb_subset_input_t *input); + +HB_EXTERN hb_set_t * +hb_subset_input_no_subset_tables_set (hb_subset_input_t *input); + +HB_EXTERN hb_set_t * +hb_subset_input_drop_tables_set (hb_subset_input_t *input); + +HB_EXTERN hb_set_t * +hb_subset_input_set (hb_subset_input_t *input, hb_subset_sets_t set_type); + +HB_EXTERN hb_subset_flags_t +hb_subset_input_get_flags (hb_subset_input_t *input); + +HB_EXTERN void +hb_subset_input_set_flags (hb_subset_input_t *input, + unsigned value); + +HB_EXTERN hb_face_t * +hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input); + + +/* + * ### Legacy API ### + * The methods below are part of the legacy harfbuzz subsetting API and will be + * Removed as of version 3.0.0 + */ HB_EXTERN void hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, @@ -87,11 +191,30 @@ hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input, HB_EXTERN hb_bool_t hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input); -/* hb_subset () */ +HB_EXTERN void +hb_subset_input_set_overlaps_flag (hb_subset_input_t *subset_input, + hb_bool_t overlaps_flag); + +HB_EXTERN hb_bool_t +hb_subset_input_get_overlaps_flag (hb_subset_input_t *subset_input); + +HB_EXTERN void +hb_subset_input_set_notdef_outline (hb_subset_input_t *subset_input, + hb_bool_t notdef_outline); + +HB_EXTERN hb_bool_t +hb_subset_input_get_notdef_outline (hb_subset_input_t *subset_input); + +HB_EXTERN void +hb_subset_input_set_no_prune_unicode_ranges (hb_subset_input_t *subset_input, + hb_bool_t no_prune_unicode_ranges); + +HB_EXTERN hb_bool_t +hb_subset_input_get_no_prune_unicode_ranges (hb_subset_input_t *subset_input); + HB_EXTERN hb_face_t * hb_subset (hb_face_t *source, hb_subset_input_t *input); - HB_END_DECLS #endif /* HB_SUBSET_H */ diff --git a/gfx/harfbuzz/src/hb-unicode-emoji-table.hh b/gfx/harfbuzz/src/hb-unicode-emoji-table.hh index eb7776eecbe3..41878697c928 100644 --- a/gfx/harfbuzz/src/hb-unicode-emoji-table.hh +++ b/gfx/harfbuzz/src/hb-unicode-emoji-table.hh @@ -24,36 +24,42 @@ #include "hb-unicode.hh" static const uint8_t -_hb_emoji_u8[448] = +_hb_emoji_u8[544] = { - 0, 0, 0, 0, 33, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 17, 17, 17, 50, 20, 21, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,118,152, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84,118, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 3, - 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 7, 9, 10, 11, 0, - 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, - 7, 7, 7, 14, 15, 16, 17, 18, 19, 20, 7, 7, 7, 7, 7, 21, - 7, 7, 7, 7, 22, 23, 7, 7, 7, 24, 7, 14, 0, 25, 0, 26, - 27, 28, 29, 14, 30, 31, 7, 7, 7, 7, 7, 14, 0, 0, 0, 0, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240, 1, 0, 2, 0, 0, - 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 7, 3, - 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, - 159,255,243,255,255,255,255,255,255,255,255,255,255,255,255,255, - 31, 0,255,255,255,255,255,255, 31,255, 3, 0, 0, 0, 8, 0, - 0, 0, 24, 0,120, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 16, 0, 96, 0, 0, 8, 0, 0, 0, 0, - 255,255,255,255,255,255,255,127, 0, 96, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,240, 1, 64, 0, 0,254, 3, 0,224,255,255, - 255,255,255,255, 31, 0, 0, 0,254,127, 0, 0, 0, 0,252,115, - 0,254,255,255,255,255,255,255,255,255,255,255,255,255,255, 3, - 255,255,255,255,255,255,255, 31,192,255,255,255,255,255,255,255, - 255,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240,127, - 0, 0,224,255,255,255,255,127, 0,112, 0, 0, 0, 0, 0, 0, - 0,127, 0,124, 0, 0, 0, 0, 0,127, 0, 0, 0,192,255,255, - 0,240,255,255,255,255,255,243,159,255,255,255,255,255,255,255, + 2, 3, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 6, 0, 7, 8, + 0, 0, 0, 9, 0, 0, 10, 11, 12, 13, 14, 13, 15, 16, 17, 0, + 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 20, 0, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, + 13, 13, 13, 13, 23, 24, 25, 26, 27, 28, 13, 13, 13, 13, 13, 29, + 13, 13, 13, 13, 30, 31, 13, 13, 13, 32, 13, 13, 0, 33, 0, 34, + 35, 36, 37, 13, 38, 39, 13, 13, 13, 13, 13, 13, 0, 0, 0, 0, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 30, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 2, 0, 0,240, 3, 0, 6, 0, 0, + 0, 0, 0, 12, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0,128, 0, 0, 0,254, 15, 7, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 64, 0, 1, 0, 0, 0, 0, 0, 0,120, + 191,255,247,255,255,255,255,255,255,255,255,255,255,255,255,255, + 63, 0,255,255,255,255,255,255, 63,255, 87, 32, 2, 1, 24, 0, + 144, 80,184, 0,248, 0, 0, 0, 0, 0,224, 0, 2, 0, 1,128, + 0, 0, 0, 0, 0, 0, 48, 0,224, 0, 0, 24, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, + 0, 0,128, 2, 0, 0, 0, 0, 0,224, 0, 0, 0,128, 0, 0, + 0, 0, 0, 0, 0,240, 3,192, 0, 64,254, 7, 0,224,255,255, + 255,255,255,255, 63, 0, 0, 0,254,255, 0, 4, 0,128,252,247, + 0,254,255,255,255,255,255,255,255,255,255,255,255,255,255, 7, + 255,255,255,255,255,255,255, 63,192,255,255,255,255,255,255,255, + 255,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240,255, + 0, 0,224,255,255,255,255,255, 0,240, 0, 0, 0, 0, 0, 0, + 0,255, 0,252, 0, 0, 0, 0, 0,255, 0, 0, 0,192,255,255, + 0,240,255,255,255,255,255,247,191,255,255,255,255,255,255,255, }; static inline unsigned @@ -69,7 +75,7 @@ _hb_emoji_b1 (const uint8_t* a, unsigned i) static inline uint_fast8_t _hb_emoji_is_Extended_Pictographic (unsigned u) { - return u<131069u?_hb_emoji_b1(192+_hb_emoji_u8,((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>6>>4))<<4)+((u>>6)&15u))])<<6)+((u)&63u)):0; + return u<131070u?_hb_emoji_b1(224+_hb_emoji_u8,((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>6>>4))<<4)+((u>>6)&15u))])<<6)+((u)&63u)):0; } diff --git a/gfx/harfbuzz/src/hb-unicode.cc b/gfx/harfbuzz/src/hb-unicode.cc index 7470bb1b6ef7..83ead6398b3e 100644 --- a/gfx/harfbuzz/src/hb-unicode.cc +++ b/gfx/harfbuzz/src/hb-unicode.cc @@ -268,7 +268,7 @@ hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs) hb_unicode_funcs_destroy (ufuncs->parent); - free (ufuncs); + hb_free (ufuncs); } /** diff --git a/gfx/harfbuzz/src/hb-unicode.hh b/gfx/harfbuzz/src/hb-unicode.hh index 5c0cb859deff..0a799441077e 100644 --- a/gfx/harfbuzz/src/hb-unicode.hh +++ b/gfx/harfbuzz/src/hb-unicode.hh @@ -359,6 +359,13 @@ DECLARE_NULL_INSTANCE (hb_unicode_funcs_t); FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) +#define HB_UNICODE_GENERAL_CATEGORY_IS_LETTER(gen_cat) \ + (FLAG_UNSAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER))) /* * Ranges, used for bsearch tables. diff --git a/gfx/harfbuzz/src/hb-uniscribe.cc b/gfx/harfbuzz/src/hb-uniscribe.cc index 48a5dc50adfe..2e14211cdfdb 100644 --- a/gfx/harfbuzz/src/hb-uniscribe.cc +++ b/gfx/harfbuzz/src/hb-uniscribe.cc @@ -44,6 +44,7 @@ #include "hb-uniscribe.h" +#include "hb-ms-feature-ranges.hh" #include "hb-open-file.hh" #include "hb-ot-name-table.hh" #include "hb-ot-layout.h" @@ -247,7 +248,7 @@ static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_trec.tagFeature < b->rec.tagFeature ? -1 : a->rec.tagFeature > b->rec.tagFeature ? 1 : - a->order < b->order ? -1 : a->order > b->order ? 1 : - a->rec.lParameter < b->rec.lParameter ? -1 : a->rec.lParameter > b->rec.lParameter ? 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 { - TEXTRANGE_PROPERTIES props; - unsigned int index_first; /* == start */ - unsigned int index_last; /* == end - 1 */ -}; - - /* * shaper face data */ @@ -391,7 +354,7 @@ _hb_rename_font (hb_blob_t *blob, wchar_t *new_name) unsigned int name_table_offset = (length + 3) & ~3; new_length = name_table_offset + padded_name_table_length; - void *new_sfnt_data = calloc (1, new_length); + void *new_sfnt_data = hb_calloc (1, new_length); if (!new_sfnt_data) { hb_blob_destroy (blob); @@ -441,7 +404,7 @@ _hb_rename_font (hb_blob_t *blob, wchar_t *new_name) } else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */ { - free (new_sfnt_data); + hb_free (new_sfnt_data); hb_blob_destroy (blob); return nullptr; } @@ -453,20 +416,20 @@ _hb_rename_font (hb_blob_t *blob, wchar_t *new_name) hb_blob_destroy (blob); return hb_blob_create ((const char *) new_sfnt_data, new_length, - HB_MEMORY_MODE_WRITABLE, new_sfnt_data, free); + HB_MEMORY_MODE_WRITABLE, new_sfnt_data, hb_free); } hb_uniscribe_face_data_t * _hb_uniscribe_shaper_face_data_create (hb_face_t *face) { - hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) calloc (1, sizeof (hb_uniscribe_face_data_t)); + hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) hb_calloc (1, sizeof (hb_uniscribe_face_data_t)); if (unlikely (!data)) return nullptr; data->funcs = hb_uniscribe_shaper_get_funcs (); if (unlikely (!data->funcs)) { - free (data); + hb_free (data); return nullptr; } @@ -477,7 +440,7 @@ _hb_uniscribe_shaper_face_data_create (hb_face_t *face) blob = _hb_rename_font (blob, data->face_name); if (unlikely (!blob)) { - free (data); + hb_free (data); return nullptr; } @@ -488,7 +451,7 @@ _hb_uniscribe_shaper_face_data_create (hb_face_t *face) if (unlikely (!data->fh)) { DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed"); - free (data); + hb_free (data); return nullptr; } @@ -499,7 +462,7 @@ void _hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_face_data_t *data) { RemoveFontMemResourceEx (data->fh); - free (data); + hb_free (data); } @@ -533,7 +496,7 @@ populate_log_font (LOGFONTW *lf, hb_uniscribe_font_data_t * _hb_uniscribe_shaper_font_data_create (hb_font_t *font) { - hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t)); + hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) hb_calloc (1, sizeof (hb_uniscribe_font_data_t)); if (unlikely (!data)) return nullptr; @@ -580,7 +543,7 @@ _hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_font_data_t *data) DeleteObject (data->hfont); if (data->script_cache) ScriptFreeCache (&data->script_cache); - free (data); + hb_free (data); } /** @@ -635,109 +598,6 @@ _hb_uniscribe_shape (hb_shape_plan_t *shape_plan, const hb_uniscribe_font_data_t *font_data = font->data.uniscribe; hb_uniscribe_shaper_funcs_t *funcs = face_data->funcs; - /* - * Set up features. - */ - hb_vector_t feature_records; - hb_vector_t range_records; - 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; - feature.rec.tagFeature = hb_uint32_swap (features[i].tag); - feature.rec.lParameter = features[i].value; - 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.tagFeature = 0; - feature.rec.lParameter = 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 (); - - unsigned int offset = feature_records.length; - - active_features.qsort (); - for (unsigned int j = 0; j < active_features.length; j++) - { - if (!j || active_features[j].rec.tagFeature != feature_records[feature_records.length - 1].tagFeature) - { - feature_records.push (active_features[j].rec); - } - else - { - /* Overrides value for existing feature. */ - feature_records[feature_records.length - 1].lParameter = active_features[j].rec.lParameter; - } - } - - /* Will convert to pointer after all is ready, since feature_records.array - * may move as we grow it. */ - range->props.potfRecords = reinterpret_cast (offset); - range->props.cotfRecords = feature_records.length - offset; - 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); - } - } - - if (!range_records.length) /* No active feature found. */ - num_features = 0; - - /* Fixup the pointers. */ - for (unsigned int i = 0; i < range_records.length; i++) - { - range_record_t *range = &range_records[i]; - range->props.potfRecords = (OPENTYPE_FEATURE_RECORD *) feature_records + reinterpret_cast (range->props.potfRecords); - } - } - #define FAIL(...) \ HB_STMT_START { \ DEBUG_MSG (UNISCRIBE, nullptr, __VA_ARGS__); \ @@ -856,8 +716,23 @@ retry: nullptr, nullptr, &lang_count, &lang_tag); OPENTYPE_TAG language_tag = hb_uint32_swap (lang_count ? lang_tag : HB_TAG_NONE); - hb_vector_t range_properties; - hb_vector_t range_char_counts; + + /* + * Set up features. + */ + static_assert ((sizeof (TEXTRANGE_PROPERTIES) == sizeof (hb_ms_features_t)), ""); + static_assert ((sizeof (OPENTYPE_FEATURE_RECORD) == sizeof (hb_ms_feature_t)), ""); + hb_vector_t feature_records; + hb_vector_t range_records; + bool has_features = false; + if (num_features) + has_features = hb_ms_setup_features (features, + num_features, + feature_records, + range_records); + + hb_vector_t range_properties; + hb_vector_t range_char_counts; unsigned int glyphs_offset = 0; unsigned int glyphs_len; @@ -867,42 +742,14 @@ retry: unsigned int chars_offset = items[i].iCharPos; unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset; - if (num_features) - { - range_properties.shrink (0); - range_char_counts.shrink (0); - - range_record_t *last_range = &range_records[0]; - - for (unsigned int k = chars_offset; k < chars_offset + item_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_properties.length || - &range->props != range_properties[range_properties.length - 1]) - { - TEXTRANGE_PROPERTIES **props = range_properties.push (); - int *c = range_char_counts.push (); - if (unlikely (!props || !c)) - { - range_properties.shrink (0); - range_char_counts.shrink (0); - break; - } - *props = &range->props; - *c = 1; - } - else - { - range_char_counts[range_char_counts.length - 1]++; - } - - last_range = range; - } - } + if (has_features) + hb_ms_make_feature_ranges (feature_records, + range_records, + item_chars_len, + chars_offset, + log_clusters, + range_properties, + range_char_counts); /* Asking for glyphs in logical order circumvents at least * one bug in Uniscribe. */ @@ -914,8 +761,8 @@ retry: &items[i].a, script_tags[i], language_tag, - range_char_counts.arrayZ, - range_properties.arrayZ, + (int *) range_char_counts.arrayZ, + (TEXTRANGE_PROPERTIES**) range_properties.arrayZ, range_properties.length, pchars + chars_offset, item_chars_len, @@ -955,8 +802,8 @@ retry: &items[i].a, script_tags[i], language_tag, - range_char_counts.arrayZ, - range_properties.arrayZ, + (int *) range_char_counts.arrayZ, + (TEXTRANGE_PROPERTIES**) range_properties.arrayZ, range_properties.length, pchars + chars_offset, log_clusters + chars_offset, diff --git a/gfx/harfbuzz/src/hb-vector.hh b/gfx/harfbuzz/src/hb-vector.hh index 17f7d486fa9c..110d457cafa5 100644 --- a/gfx/harfbuzz/src/hb-vector.hh +++ b/gfx/harfbuzz/src/hb-vector.hh @@ -69,7 +69,7 @@ struct hb_vector_t void fini () { - free (arrayZ); + hb_free (arrayZ); init (); } void fini_deep () @@ -209,7 +209,7 @@ struct hb_vector_t (new_allocated < (unsigned) allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type)); if (likely (!overflows)) - new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type)); + new_array = (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type)); if (unlikely (!new_array)) { @@ -315,7 +315,7 @@ struct hb_sorted_vector_t : hb_vector_t { return as_array ().bsearch (x, 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, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, unsigned int to_store = (unsigned int) -1) const { return as_array ().bfind (x, i, not_found, to_store); } }; diff --git a/gfx/harfbuzz/src/hb-version.h b/gfx/harfbuzz/src/hb-version.h index 3c33bd4fe686..34d10d665806 100644 --- a/gfx/harfbuzz/src/hb-version.h +++ b/gfx/harfbuzz/src/hb-version.h @@ -47,7 +47,7 @@ HB_BEGIN_DECLS * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 8 +#define HB_VERSION_MINOR 9 /** * HB_VERSION_MICRO: * @@ -60,7 +60,7 @@ HB_BEGIN_DECLS * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "2.8.1" +#define HB_VERSION_STRING "2.9.1" /** * HB_VERSION_ATLEAST: diff --git a/gfx/harfbuzz/src/hb.hh b/gfx/harfbuzz/src/hb.hh index 4d90e4d576d1..052cabed9f5c 100644 --- a/gfx/harfbuzz/src/hb.hh +++ b/gfx/harfbuzz/src/hb.hh @@ -223,10 +223,15 @@ extern "C" void* hb_malloc_impl(size_t size); extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); extern "C" void* hb_realloc_impl(void *ptr, size_t size); extern "C" void hb_free_impl(void *ptr); -#define malloc hb_malloc_impl -#define calloc hb_calloc_impl -#define realloc hb_realloc_impl -#define free hb_free_impl +#define hb_malloc hb_malloc_impl +#define hb_calloc hb_calloc_impl +#define hb_realloc hb_realloc_impl +#define hb_free hb_free_impl +#else +#define hb_malloc malloc +#define hb_calloc calloc +#define hb_realloc realloc +#define hb_free free #endif @@ -378,7 +383,7 @@ extern "C" void hb_free_impl(void *ptr); # define HB_NO_SETLOCALE # define HB_NO_ERRNO # endif -# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# elif !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) # ifndef HB_NO_GETENV # define HB_NO_GETENV # endif diff --git a/gfx/harfbuzz/src/main.cc b/gfx/harfbuzz/src/main.cc index e898e6e4b605..1ab013cd3a87 100644 --- a/gfx/harfbuzz/src/main.cc +++ b/gfx/harfbuzz/src/main.cc @@ -33,12 +33,13 @@ #include "hb.h" #include "hb-ot.h" -#include -#include -#include +#include +#include +#include +#include #ifdef HB_NO_OPEN -#define hb_blob_create_from_file(x) hb_blob_get_empty () +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () #endif #if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) && defined(HB_EXPERIMENTAL_API) @@ -505,7 +506,8 @@ main (int argc, char **argv) exit (1); } - hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); printf ("Opened font file %s: %d bytes long\n", argv[1], hb_blob_get_length (blob)); #ifndef MAIN_CC_NO_PRIVATE_API print_layout_info_using_private_api (blob); diff --git a/gfx/harfbuzz/src/meson.build b/gfx/harfbuzz/src/meson.build index dddafe9c21c2..cbe57e9eba45 100644 --- a/gfx/harfbuzz/src/meson.build +++ b/gfx/harfbuzz/src/meson.build @@ -25,6 +25,7 @@ hb_base_sources = files( 'hb-array.hh', 'hb-atomic.hh', 'hb-bimap.hh', + 'hb-bit-page.hh', 'hb-blob.cc', 'hb-blob.hh', 'hb-buffer-serialize.cc', @@ -53,6 +54,8 @@ hb_base_sources = files( 'hb-map.cc', 'hb-map.hh', 'hb-meta.hh', + 'hb-ms-feature-ranges.cc', + 'hb-ms-feature-ranges.hh', 'hb-mutex.hh', 'hb-null.hh', 'hb-number.cc', diff --git a/gfx/harfbuzz/src/moz.build b/gfx/harfbuzz/src/moz.build index ad28b2fc89cd..599f10827f5a 100644 --- a/gfx/harfbuzz/src/moz.build +++ b/gfx/harfbuzz/src/moz.build @@ -46,6 +46,7 @@ UNIFIED_SOURCES += [ 'hb-draw.cc', 'hb-face.cc', 'hb-font.cc', + 'hb-ms-feature-ranges.cc', 'hb-number.cc', 'hb-ot-cff1-table.cc', 'hb-ot-cff2-table.cc', diff --git a/gfx/harfbuzz/src/test-buffer-serialize.cc b/gfx/harfbuzz/src/test-buffer-serialize.cc index 4a3d4bc107be..8d5a69427570 100644 --- a/gfx/harfbuzz/src/test-buffer-serialize.cc +++ b/gfx/harfbuzz/src/test-buffer-serialize.cc @@ -33,7 +33,7 @@ #endif #ifdef HB_NO_OPEN -#define hb_blob_create_from_file(x) hb_blob_get_empty () +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () #endif int @@ -48,7 +48,8 @@ main (int argc, char **argv) exit (1); } - hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); hb_face_t *face = hb_face_create (blob, 0 /* first face */); hb_blob_destroy (blob); blob = nullptr; diff --git a/gfx/harfbuzz/src/test-gpos-size-params.cc b/gfx/harfbuzz/src/test-gpos-size-params.cc index 549f5ae06a04..b96381ddcb5a 100644 --- a/gfx/harfbuzz/src/test-gpos-size-params.cc +++ b/gfx/harfbuzz/src/test-gpos-size-params.cc @@ -30,7 +30,7 @@ #include "hb-ot.h" #ifdef HB_NO_OPEN -#define hb_blob_create_from_file(x) hb_blob_get_empty () +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () #endif int @@ -42,7 +42,8 @@ main (int argc, char **argv) } /* Create the face */ - hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); hb_face_t *face = hb_face_create (blob, 0 /* first face */); hb_blob_destroy (blob); blob = nullptr; diff --git a/gfx/harfbuzz/src/test-gsub-would-substitute.cc b/gfx/harfbuzz/src/test-gsub-would-substitute.cc index 508217dde9ee..87123030ed3b 100644 --- a/gfx/harfbuzz/src/test-gsub-would-substitute.cc +++ b/gfx/harfbuzz/src/test-gsub-would-substitute.cc @@ -34,7 +34,7 @@ #endif #ifdef HB_NO_OPEN -#define hb_blob_create_from_file(x) hb_blob_get_empty () +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () #endif int @@ -46,7 +46,8 @@ main (int argc, char **argv) } /* Create the face */ - hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); hb_face_t *face = hb_face_create (blob, 0 /* first face */); hb_blob_destroy (blob); blob = nullptr; diff --git a/gfx/harfbuzz/src/test-ot-glyphname.cc b/gfx/harfbuzz/src/test-ot-glyphname.cc index b2d4b3bcf020..50d023166f91 100644 --- a/gfx/harfbuzz/src/test-ot-glyphname.cc +++ b/gfx/harfbuzz/src/test-ot-glyphname.cc @@ -28,7 +28,7 @@ #include "hb-ot.h" #ifdef HB_NO_OPEN -#define hb_blob_create_from_file(x) hb_blob_get_empty () +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () #endif int @@ -39,7 +39,8 @@ main (int argc, char **argv) exit (1); } - hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); hb_face_t *face = hb_face_create (blob, 0 /* first face */); hb_font_t *font = hb_font_create (face); hb_blob_destroy (blob); diff --git a/gfx/harfbuzz/src/test-ot-meta.cc b/gfx/harfbuzz/src/test-ot-meta.cc index f7a755cd599c..7cf69dbcc34f 100644 --- a/gfx/harfbuzz/src/test-ot-meta.cc +++ b/gfx/harfbuzz/src/test-ot-meta.cc @@ -26,7 +26,7 @@ #include "hb-ot.h" #ifdef HB_NO_OPEN -#define hb_blob_create_from_file(x) hb_blob_get_empty () +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () #endif int @@ -37,7 +37,8 @@ main (int argc, char **argv) exit (1); } - hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); hb_face_t *face = hb_face_create (blob, 0 /* first face */); hb_blob_destroy (blob); blob = nullptr; diff --git a/gfx/harfbuzz/src/test-ot-name.cc b/gfx/harfbuzz/src/test-ot-name.cc index f331161fa4d1..bfa654a7c1d8 100644 --- a/gfx/harfbuzz/src/test-ot-name.cc +++ b/gfx/harfbuzz/src/test-ot-name.cc @@ -28,7 +28,7 @@ #include "hb-ot.h" #ifdef HB_NO_OPEN -#define hb_blob_create_from_file(x) hb_blob_get_empty () +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () #endif int @@ -39,7 +39,8 @@ main (int argc, char **argv) exit (1); } - hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); hb_face_t *face = hb_face_create (blob, 0 /* first face */); hb_blob_destroy (blob); blob = nullptr; diff --git a/gfx/harfbuzz/src/test-repacker.cc b/gfx/harfbuzz/src/test-repacker.cc index a8cc6395f9ea..6f46b0428763 100644 --- a/gfx/harfbuzz/src/test-repacker.cc +++ b/gfx/harfbuzz/src/test-repacker.cc @@ -377,8 +377,8 @@ test_serialize () assert (actual == expected); - actual.free (); - expected.free (); + actual.fini (); + expected.fini (); free (buffer_1); free (buffer_2); } @@ -438,7 +438,7 @@ static void test_resolve_overflows_via_sort () hb_bytes_t result = out.copy_bytes (); assert (result.length == (80000 + 3 + 3 * 2)); - result.free (); + result.fini (); free (buffer); free (out_buffer); } @@ -459,7 +459,7 @@ static void test_resolve_overflows_via_duplication () hb_bytes_t result = out.copy_bytes (); assert (result.length == (10000 + 2 * 2 + 60000 + 2 + 3 * 2)); - result.free (); + result.fini (); free (buffer); free (out_buffer); } diff --git a/gfx/harfbuzz/src/test.cc b/gfx/harfbuzz/src/test.cc index 347b71d38b53..d848cf106225 100644 --- a/gfx/harfbuzz/src/test.cc +++ b/gfx/harfbuzz/src/test.cc @@ -31,7 +31,7 @@ #endif #ifdef HB_NO_OPEN -#define hb_blob_create_from_file(x) hb_blob_get_empty () +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () #endif int @@ -42,7 +42,8 @@ main (int argc, char **argv) exit (1); } - hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); printf ("Opened font file %s: %u bytes long\n", argv[1], hb_blob_get_length (blob)); /* Create the face */ diff --git a/gfx/harfbuzz/update.sh b/gfx/harfbuzz/update.sh index a8b0259f2a7a..b344f042d020 100755 --- a/gfx/harfbuzz/update.sh +++ b/gfx/harfbuzz/update.sh @@ -5,7 +5,7 @@ MY_TEMP_DIR=`mktemp -d -t harfbuzz_update.XXXXXX` || exit 1 -VERSION=2.8.1 +VERSION=2.9.1 git clone https://github.com/harfbuzz/harfbuzz ${MY_TEMP_DIR}/harfbuzz git -C ${MY_TEMP_DIR}/harfbuzz checkout ${VERSION}