Bug 1726416 - Update HarfBuzz to 2.9.1. r=jfkthame

Differential Revision: https://phabricator.services.mozilla.com/D122989
This commit is contained in:
Ryan VanderMeulen 2021-09-09 14:48:31 +00:00
parent a9b982bfec
commit b35a31e2b9
133 changed files with 5105 additions and 2409 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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
====================================

View File

@ -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

View File

@ -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/)

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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))

View File

@ -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

View File

@ -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"

View File

@ -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::kerx> ();
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");
}

View File

@ -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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> constexpr auto
operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | ~b)
}
HB_FUNCOBJ (hb_bitwise_ge);
struct
{
template <typename T> constexpr auto
@ -1195,6 +1221,12 @@ struct
}
HB_FUNCOBJ (hb_sub);
struct
{ HB_PARTIALIZE(2);
template <typename T, typename T2> constexpr auto
operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (b - a)
}
HB_FUNCOBJ (hb_rsub);
struct
{ HB_PARTIALIZE(2);
template <typename T, typename T2> constexpr auto
operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b)

View File

@ -36,6 +36,14 @@
template <typename Type>
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 <typename Type>
struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
{
@ -139,7 +147,9 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
return lfind (x, &i) ? &this->arrayZ[i] : not_found;
}
template <typename T>
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<hb_array_t<Type>, 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<hb_array_t<Type>, 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 <typename hb_serialize_context_t>
hb_array_t copy (hb_serialize_context_t *c) const
@ -266,13 +292,6 @@ template <typename T, unsigned int length_> inline hb_array_t<T>
hb_array (T (&array_)[length_])
{ return hb_array_t<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 <typename Type>
struct hb_sorted_array_t :
hb_iter_t<hb_sorted_array_t<Type>, Type&>,
@ -323,7 +342,7 @@ struct hb_sorted_array_t :
}
template <typename T>
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;
}

View File

@ -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); }

View File

@ -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<elt_t, PAGE_BITS / 8> 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 */

View File

@ -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 <typename T>
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 <typename T>
void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); }
/* Might return false if array looks unsorted.
* Used for faster rejection of corrupt data. */
template <typename T>
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 <typename T>
bool add_sorted_array (const hb_sorted_array_t<const 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<hb_codepoint_t, hb_codepoint_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<hb_codepoint_t, hb_codepoint_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 <typename Op>
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<iter_t, hb_codepoint_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 */

View File

@ -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_t> page_map;
hb_vector_t<page_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 <typename T>
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<T> (array, stride);
count--;
}
while (count && (g = *array, start <= g && g < end));
}
}
template <typename T>
void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
{ set_array (true, array, count, stride); }
template <typename T>
void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); }
template <typename T>
void del_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
{ set_array (false, array, count, stride); }
template <typename T>
void del_array (const hb_array_t<const T>& arr) { del_array (&arr, arr.len ()); }
/* Might return false if array looks unsorted.
* Used for faster rejection of corrupt data. */
template <typename T>
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 <typename T>
bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
{ return set_sorted_array (true, array, count, stride); }
template <typename T>
bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
template <typename T>
bool del_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
{ return set_sorted_array (false, array, count, stride); }
template <typename T>
bool del_sorted_array (const hb_sorted_array_t<const 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<unsigned> 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<hb_codepoint_t, hb_codepoint_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<unsigned>& 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<unsigned>& workspace,
unsigned int length)
{
assert(workspace.length == pages.length);
hb_vector_t<unsigned>& 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<unsigned>& 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 <typename Op>
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<unsigned> 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 &current = 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 &current = 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<iter_t, hb_codepoint_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 */

View File

@ -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<hb_blob_t> ())) {
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<hb_blob_t> ()))
{
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 */

View File

@ -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

View File

@ -88,7 +88,7 @@ struct hb_blob_ptr_t
const T * get () const { return b->as<T> (); }
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<hb_blob_t> b;

View File

@ -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];
}
}

View File

@ -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
}

View File

@ -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 <unsigned int key_bits, unsigned int value_bits, unsigned int cache_bits>
struct hb_cache_t

View File

@ -136,8 +136,8 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
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;
}

View File

@ -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 <hb_language_item_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;
}

View File

@ -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

View File

@ -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++;
}

View File

@ -307,7 +307,7 @@ struct hb_auto_trace_t
_hb_debug_msg<max_level> (what, obj, func, true, plevel ? *plevel : 1, -1,
"return %s (line %d)",
hb_printer_t<decltype (v)>().print (v), line);
hb_printer_t<hb_decay<decltype (v)>>().print (v), line);
if (plevel) --*plevel;
plevel = nullptr;
returned = true;

View File

@ -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

View File

@ -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<hb_ms_features_t *> range_features;
hb_vector_t<uint32_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<hb_ms_feature_t> feature_records;
hb_vector_t<hb_ms_range_record_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;

View File

@ -191,7 +191,7 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *funcs)
{
if (!hb_object_destroy (funcs)) return;
free (funcs);
hb_free (funcs);
}
/**

View File

@ -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>Note: face indices within a collection are zero-based.</note>
*
* 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<table_entry_t> tables;
hb_hashmap_t<hb_tag_t, hb_blob_t *> tables;
};
static int compare_entries (const void* pa, const void* pb)
{
const auto& a = * (const hb_pair_t<hb_tag_t, hb_blob_t*> *) pa;
const auto& b = * (const hb_pair_t<hb_tag_t, hb_blob_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<OT::OpenTypeFontFile> ();
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<hb_pair_t <hb_tag_t, hb_blob_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;
}

View File

@ -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<FuncType> 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_func_t> hb_font_get_glyph_trampoline_t;

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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);
}
/**

View File

@ -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

View File

@ -242,14 +242,14 @@ struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData>
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<Data, WheresData>
static void destroy (Stored *p)
{
p->fini ();
free (p);
hb_free (p);
}
// private:

View File

@ -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 ();
}

View File

@ -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--;
}

View File

@ -101,14 +101,14 @@ HB_FUNCOBJ (hb_addressof);
template <typename T> static inline T hb_declval ();
#define hb_declval(T) (hb_declval<T> ())
template <typename T> struct hb_match_const : hb_type_identity_t<T>, hb_bool_constant<false>{};
template <typename T> struct hb_match_const<const T> : hb_type_identity_t<T>, hb_bool_constant<true> {};
template <typename T> struct hb_match_const : hb_type_identity_t<T>, hb_false_type {};
template <typename T> struct hb_match_const<const T> : hb_type_identity_t<T>, hb_true_type {};
template <typename T> using hb_remove_const = typename hb_match_const<T>::type;
template <typename T> using hb_add_const = const T;
#define hb_is_const(T) hb_match_const<T>::value
template <typename T> struct hb_match_reference : hb_type_identity_t<T>, hb_bool_constant<false>{};
template <typename T> struct hb_match_reference<T &> : hb_type_identity_t<T>, hb_bool_constant<true> {};
template <typename T> struct hb_match_reference<T &&> : hb_type_identity_t<T>, hb_bool_constant<true> {};
template <typename T> struct hb_match_reference : hb_type_identity_t<T>, hb_false_type {};
template <typename T> struct hb_match_reference<T &> : hb_type_identity_t<T>, hb_true_type {};
template <typename T> struct hb_match_reference<T &&> : hb_type_identity_t<T>, hb_true_type {};
template <typename T> using hb_remove_reference = typename hb_match_reference<T>::type;
template <typename T> auto _hb_try_add_lvalue_reference (hb_priority<1>) -> hb_type_identity<T&>;
template <typename T> auto _hb_try_add_lvalue_reference (hb_priority<0>) -> hb_type_identity<T>;
@ -117,8 +117,8 @@ template <typename T> auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_t
template <typename T> auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity<T>;
template <typename T> using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference<T> (hb_prioritize));
#define hb_is_reference(T) hb_match_reference<T>::value
template <typename T> struct hb_match_pointer : hb_type_identity_t<T>, hb_bool_constant<false>{};
template <typename T> struct hb_match_pointer<T *> : hb_type_identity_t<T>, hb_bool_constant<true> {};
template <typename T> struct hb_match_pointer : hb_type_identity_t<T>, hb_false_type {};
template <typename T> struct hb_match_pointer<T *> : hb_type_identity_t<T>, hb_true_type {};
template <typename T> using hb_remove_pointer = typename hb_match_pointer<T>::type;
template <typename T> auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity<hb_remove_reference<T>*>;
template <typename T> auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity<T>;
@ -259,15 +259,15 @@ using hb_is_arithmetic = hb_bool_constant<
#define hb_is_arithmetic(T) hb_is_arithmetic<T>::value
template <typename T>
using hb_is_signed = hb_conditional<hb_is_arithmetic (T),
hb_bool_constant<(T) -1 < (T) 0>,
hb_false_type>;
template <typename T, bool is_arithmetic> struct hb_is_signed_;
template <typename T> struct hb_is_signed_<T, false> : hb_false_type {};
template <typename T> struct hb_is_signed_<T, true> : hb_bool_constant<(T) -1 < (T) 0> {};
template <typename T> struct hb_is_signed : hb_is_signed_<T, hb_is_arithmetic (T)> {};
#define hb_is_signed(T) hb_is_signed<T>::value
template <typename T>
using hb_is_unsigned = hb_conditional<hb_is_arithmetic (T),
hb_bool_constant<(T) 0 < (T) -1>,
hb_false_type>;
template <typename T, bool is_arithmetic> struct hb_is_unsigned_;
template <typename T> struct hb_is_unsigned_<T, false> : hb_false_type {};
template <typename T> struct hb_is_unsigned_<T, true> : hb_bool_constant<(T) 0 < (T) -1> {};
template <typename T> struct hb_is_unsigned : hb_is_unsigned_<T, hb_is_arithmetic (T)> {};
#define hb_is_unsigned(T) hb_is_unsigned<T>::value
template <typename T> struct hb_int_min;
@ -282,6 +282,7 @@ template <> struct hb_int_min<signed long> : hb_integral_constant<signed long,
template <> struct hb_int_min<unsigned long> : hb_integral_constant<unsigned long, 0> {};
template <> struct hb_int_min<signed long long> : hb_integral_constant<signed long long, LLONG_MIN> {};
template <> struct hb_int_min<unsigned long long> : hb_integral_constant<unsigned long long, 0> {};
template <typename T> struct hb_int_min<T *> : hb_integral_constant<T *, nullptr> {};
#define hb_int_min(T) hb_int_min<T>::value
template <typename T> struct hb_int_max;
template <> struct hb_int_max<char> : hb_integral_constant<char, CHAR_MAX> {};

View File

@ -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<hb_ms_feature_t> &feature_records, /* OUT */
hb_vector_t<hb_ms_range_record_t> &range_records /* OUT */)
{
feature_records.shrink(0);
range_records.shrink(0);
/* Sort features by start/end events. */
hb_vector_t<hb_ms_feature_event_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<hb_ms_active_feature_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<hb_ms_feature_t *> (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<uintptr_t> (range->features.features);
}
return !!num_features;
}
void
hb_ms_make_feature_ranges (hb_vector_t<hb_ms_feature_t> &feature_records,
hb_vector_t<hb_ms_range_record_t> &range_records,
unsigned int chars_offset,
unsigned int chars_len,
uint16_t *log_clusters,
hb_vector_t<hb_ms_features_t*> &range_features, /* OUT */
hb_vector_t<uint32_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;
}
}

View File

@ -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<hb_ms_feature_t> &feature_records, /* OUT */
hb_vector_t<hb_ms_range_record_t> &range_records /* OUT */);
HB_INTERNAL void
hb_ms_make_feature_ranges (hb_vector_t<hb_ms_feature_t> &feature_records,
hb_vector_t<hb_ms_range_record_t> &range_records,
unsigned int chars_offset,
unsigned int chars_len,
uint16_t *log_clusters,
hb_vector_t<hb_ms_features_t*> &range_features, /* OUT */
hb_vector_t<uint32_t> &range_counts /* OUT */);
#endif /* HB_MS_FEATURE_RANGES_HH */

View File

@ -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 <pthread.h>
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;

View File

@ -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<hb_user_data_array_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 <typename Type>
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 <typename Type>
static inline bool hb_object_is_inert (const Type *obj)
{
return unlikely (obj->header.ref_count.is_inert ());
}
template <typename Type>
static inline bool hb_object_is_valid (const Type *obj)
{
return likely (obj->header.ref_count.is_valid ());
@ -257,7 +252,7 @@ template <typename Type>
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 <typename Type>
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 <typename Type>
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 ();

View File

@ -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 <typename item_t>
template <typename Iterator,
hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::value))>
bool serialize (hb_serialize_context_t *c,
hb_tag_t sfnt_tag,
hb_array_t<item_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<hb_tag_t, hb_blob_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<void> (rec.length);
char *start = (char *) c->allocate_size<void> (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 <typename item_t>
template <typename Iterator,
hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::value))>
bool serialize_single (hb_serialize_context_t *c,
hb_tag_t sfnt_tag,
hb_array_t<item_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));
}

View File

@ -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<void> ();
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<OffsetType, has_null>
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<OffsetType>::serialize (c, base);
}
template <typename ...Ts>
bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src,
@ -355,6 +342,23 @@ struct OffsetTo : Offset<OffsetType, has_null>
return ret;
}
template <typename ...Ts>
bool serialize_serialize (hb_serialize_context_t *c, Ts&&... ds)
{
*this = 0;
Type* obj = c->push<Type> ();
bool ret = obj->serialize (c, hb_forward<Ts> (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 &not_found = Null (Type)) const
{ return *as_array (len).lsearch (x, &not_found); }
template <typename T>
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 <typename Iterator,
@ -573,7 +579,7 @@ struct SortedUnsizedArrayOf : UnsizedArrayOf<Type>
{ return *as_array (len).bsearch (x, &not_found); }
template <typename T>
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 &not_found = Null (Type)) const
{ return *as_array ().lsearch (x, &not_found); }
template <typename T>
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 <typename Iterator,
@ -667,7 +675,7 @@ struct ArrayOf
{
TRACE_SERIALIZE (this);
len++;
if (unlikely (!len || !c->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 <typename Iterator,
@ -937,7 +945,7 @@ struct SortedArrayOf : ArrayOf<Type, LenType>
{ return *as_array ().bsearch (x, &not_found); }
template <typename T>
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); }
};

View File

@ -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<HBUINT8> (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<HBUINT8> (off_size * (it.len () + 1))))
@ -335,7 +335,7 @@ struct CFFIndexOf : CFFIndex<COUNT>
{
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<HBUINT8> (offSize_ * (dataArrayLen + 1))))

View File

@ -187,7 +187,7 @@ struct Encoding
const hb_vector_t<code_pair_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<code_pair_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<cff1_top_dict_val_t>
case OpCode_Notice:
case OpCode_Copyright:
case OpCode_FullName:
case OpCode_FontName:
case OpCode_FamilyName:
case OpCode_Weight:
case OpCode_PostScript:

View File

@ -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<hb_codepoint_t, hb_codepoint_t> cp_to_gid;
+ it | hb_sink (cp_to_gid);
HBUINT16 *idRangeOffset = c->allocate_size<HBUINT16> (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<HBUINT16> () - idRangeOffset - i);
+ it
| hb_filter ([&] (const hb_item_type<Iterator> _) { return _.first >= startCode[i] && _.first <= endCode[i]; })
| hb_apply ([&] (const hb_item_type<Iterator> _)
{
HBUINT16 glyID;
glyID = _.second;
c->copy<HBUINT16> (glyID);
})
;
})
;
for (unsigned i : + hb_range (segcount)
| hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }))
{
idRangeOffset[i] = 2 * (c->start_embed<HBUINT16> () - idRangeOffset - i);
for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++)
{
HBUINT16 gid;
gid = cp_to_gid[cp];
c->copy<HBUINT16> (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<CmapSubtableFormat12>
{
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<const CmapSubtableFormat14*> (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<typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
void serialize (hb_serialize_context_t *c,
@ -1353,60 +1392,112 @@ struct cmap
template<typename Iterator, typename EncodingRecIter,
hb_requires (hb_is_iterator (EncodingRecIter))>
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<hb_codepoint_t, hb_codepoint_t>& p)
{
return plan->unicodes->has (p.first) ||
plan->glyphs_requested->has (p.second);
})
| hb_map ([plan] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& p_org)
{
return hb_pair_t<hb_codepoint_t, hb_codepoint_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<typename Iterator, typename EncodingRecordIterator,
hb_requires (hb_is_iterator (Iterator)),
hb_requires (hb_is_iterator (EncodingRecordIterator))>
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<hb_codepoint_t, hb_codepoint_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

View File

@ -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);

View File

@ -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<hb_colrv1_closure_context_t>
{
template <typename T>
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<const COLR *> (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 <template<typename> 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 <template<typename> 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 <template<typename> 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 <template<typename> 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 <template<typename> 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 <template<typename> 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 <template<typename> 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 <template<typename> 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 <template<typename> 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 <template<typename> 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 <template<typename> 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> paint; /* Offset (from beginning of BaseGlyphV1Record) to Paint,
Offset32To<Paint> paint; /* Offset (from beginning of BaseGlyphV1Record array) to Paint,
* Typically PaintColrLayers */
public:
DEFINE_SIZE_STATIC (6);
};
typedef SortedArray32Of<BaseGlyphV1Record> BaseGlyphV1List;
struct BaseGlyphV1List : SortedArray32Of<BaseGlyphV1Record>
{
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<BaseGlyphV1Record>::sanitize (c, this));
}
};
struct LayerV1List : Array32OfOffset32To<Paint>
{
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> 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<const BaseGlyphRecord> baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs);
hb_array_t<const LayerRecord> all_layers = (this+layersZ).as_array (numLayers);
for (const BaseGlyphRecord record : baseGlyphs)
{
if (!glyphs->has (record.glyphId)) continue;
hb_array_t<const LayerRecord> 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<typename BaseIterator, typename LayerIterator,
hb_requires (hb_is_iterator (BaseIterator)),
hb_requires (hb_is_iterator (LayerIterator))>
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<bool, hb_vector_t<LayerRecord>> (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<bool, hb_vector_t<LayerRecord>> (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<COLR> ();
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<void> (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:

View File

@ -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 <template<typename> class Var>
HB_INTERNAL void PaintTransform<Var>::closurev1 (hb_colrv1_closure_context_t* c) const
{
(this+src).dispatch (c);
}
template <template<typename> class Var>
HB_INTERNAL void PaintTranslate<Var>::closurev1 (hb_colrv1_closure_context_t* c) const
{
(this+src).dispatch (c);
}
template <template<typename> class Var>
HB_INTERNAL void PaintRotate<Var>::closurev1 (hb_colrv1_closure_context_t* c) const
{
(this+src).dispatch (c);
}
template <template<typename> class Var>
HB_INTERNAL void PaintSkew<Var>::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 */

View File

@ -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<CPALV1Tail> (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<const NameID> 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<NameID> (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<UnsizedArrayOf<HBUINT32>>
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<const BGRAColor> &color_records,
const hb_array_t<const HBUINT16> &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<HBUINT16> (new_idx)) return_trace (false);
}
c->push ();
for (const auto _ : retained_color_record_indices.iter ())
{
if (!c->copy<BGRAColor> (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<const HBUINT16> 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<const BGRAColor> 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);

View File

@ -145,7 +145,7 @@ struct SBIXStrike
auto* out = c->serializer->start_embed<SBIXStrike> ();
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;

View File

@ -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. */

View File

@ -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<CompositeGlyphChain,
CompositeGlyphChain> (*current);
if (!check_range (possible)) { current = nullptr; return; }
current = possible;
set_next (&StructAtOffset<CompositeGlyphChain> (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<HBUINT16> (&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<CompositeGlyphChain &> (StructAfter<CompositeGlyphChain, GlyphHeader> (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<CompositeGlyphChain &> (_).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);
}

View File

@ -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 =

View File

@ -415,7 +415,7 @@ struct RecordArrayOf : SortedArray16Of<Record<Type>>
}
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<HBUINT16> (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 &region_map)
{
TRACE_SERIALIZE (this);
VarRegionList *out = c->allocate_min<VarRegionList> ();
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<VarRegionList> (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<VarRegionAxis>
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 &regions,
float *scalars /*OUT */,
unsigned int num_scalars) const
void get_region_scalars (const int *coords, unsigned int coord_count,
const VarRegionList &regions,
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 &region_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<HBUINT8> (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 &region_map, const hb_inc_bimap_t &inner_map) const
void collect_region_refs (hb_set_t &region_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 <hb_inc_bimap_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<HBUINT32> (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 ([&region_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<VarRegionList> regions;
Array16OfOffset32To<VarData> dataSets;
public:
DEFINE_SIZE_ARRAY (8, dataSets);
DEFINE_SIZE_ARRAY_SIZED (8, dataSets);
};
/*

View File

@ -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));
}

View File

@ -538,7 +538,7 @@ struct Anchor
switch (u.format) {
case 1: return_trace (bool (reinterpret_cast<Anchor *> (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<unsigned, unsigned> 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<unsigned, unsigned> 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<unsigned> 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<unsigned> mark2_indexes;

View File

@ -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<const HBGlyphID> 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<const HBGlyphID> 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<const HBGlyphID> &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<const HBGlyphID> 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<SubTable> ()[i].serialize (c, this); }
bool serialize_single (hb_serialize_context_t *c,
uint32_t lookup_props,
hb_sorted_array_t<const HBGlyphID> 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<SubTable> ()->u.single.serialize (c, hb_zip (glyphs, substitutes)))
{
c->add_link (get_subtables<SubTable> ()[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<SubTable> ()->u.multiple.
serialize (c,
glyphs,
substitute_len_list,
substitute_glyphs_list))
{
c->add_link (get_subtables<SubTable> ()[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<SubTable> ()->u.alternate.
serialize (c,
glyphs,
alternate_len_list,
alternate_glyphs_list))
{
c->add_link (get_subtables<SubTable> ()[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<SubTable> ()->u.ligature.
serialize (c,
first_glyphs,
ligature_per_first_glyph_count_list,
ligatures_list,
component_count_list,
component_list))
{
c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
return_trace (true);
}
c->pop_discard ();
return_trace (false);
}
template <typename context_t>

View File

@ -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 ();
}

View File

@ -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<Proxy> (&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);
}
}
}

View File

@ -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 */

View File

@ -107,7 +107,7 @@ struct maxp
maxpV1Tail *dest_v1 = c->serializer->embed<maxpV1Tail> (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);
}

View File

@ -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<NameRecord> 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));

View File

@ -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

View File

@ -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);
}

View File

@ -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<typename Iterator>
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<const post::accelerator_t*> (_post)->find_glyph_name (glyph_id);
HBUINT8 *o = c->allocate_size<HBUINT8> (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<unsigned, unsigned> (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 */

View File

@ -55,6 +55,13 @@ struct postV2Tail
return_trace (glyphNameIndex.sanitize (c));
}
template<typename Iterator>
bool serialize (hb_serialize_context_t *c,
Iterator it,
const void* _post) const;
bool subset (hb_subset_context_t *c) const;
protected:
Array16Of<HBUINT16> 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<post> ();
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<post> ();
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;
}
}

View File

@ -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<arabic_fallback_plan_t *> (&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<arabic_fallback_plan_t *> (&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

View File

@ -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

View File

@ -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. */

View File

@ -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);

View File

@ -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

View File

@ -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'),

View File

@ -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 ();

View File

@ -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 */

View File

@ -375,7 +375,9 @@ hb_iter_with_fallback_t<machine_index_t<Iter>,
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<machine_index_t<Iter>, 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;

View File

@ -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

View File

@ -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<hb_codepoint_t> (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<hb_codepoint_t> (info[i].codepoint, 0x1F1E6u, 0x1F1FFu)))
{
if (hb_in_range<hb_codepoint_t> (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");
}

View File

@ -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;

View File

@ -6,8 +6,8 @@
*
* on files with these headers:
*
* <meta name="updated_at" content="2021-02-12 04:08 PM" />
* File-Date: 2021-03-05
* <meta name="updated_at" content="2021-09-02 09:40 PM" />
* 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 */

View File

@ -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);
}
}
}

View File

@ -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<const AxisRecord> get_axes () const
{ return hb_array (&(this+firstAxis), axisCount); }

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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<OT::HBINT32> (link, head, c);
} else {
serialize_link_of_type<OT::HBUINT32> (link, head, c);
}
} else {
return;
case 2:
if (link.is_signed)
{
serialize_link_of_type<OT::HBINT16> (link, head, c);
} else {
serialize_link_of_type<OT::HBUINT16> (link, head, c);
}
return;
case 3:
serialize_link_of_type<OT::HBUINT24> (link, head, c);
return;
default:
// Unexpected link width.
assert (0);
}
}

View File

@ -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<int32_t> (parent, link, offset);
else
assign_offset<int16_t> (parent, link, offset);
}
else
{
if (link.is_wide)
assert (link.width == 2 || link.width == 3 || link.width == 4);
if (link.width == 4)
assign_offset<uint32_t> (parent, link, offset);
else if (link.width == 3)
assign_offset<uint32_t, 3> (parent, link, offset);
else
assign_offset<uint16_t> (parent, link, offset);
}
@ -446,16 +460,16 @@ struct hb_serialize_context_t
}
template <typename Type>
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<Type *> (ret);
@ -510,18 +524,19 @@ struct hb_serialize_context_t
hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; }
template <typename Type>
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<Type> (((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<Type> (((char *) obj) + size - this->head))) return nullptr;
return reinterpret_cast<Type *> (obj);
}
template <typename Type>
Type *extend_size (Type &obj, unsigned int size)
Type *extend_size (Type &obj, size_t size)
{ return extend_size (hb_addressof (obj), size); }
template <typename Type>
@ -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_t *>& object_graph() const
{ return packed; }
private:
template <typename T>
template <typename T, unsigned Size = sizeof (T)>
void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset)
{
auto &off = * ((BEInt<T> *) (parent->head + link.position));
auto &off = * ((BEInt<T, Size> *) (parent->head + link.position));
assert (0 == off);
check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW);
}

View File

@ -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:

View File

@ -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);

View File

@ -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 <typename impl_t>
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<elt_t, PAGE_BITS / 8> 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_t> page_map;
hb_vector_t<page_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 <typename T>
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<T> (array, stride);
count--;
}
while (count && (g = *array, start <= g && g < end));
}
}
{ s.add_array (array, count, stride); }
template <typename T>
void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); }
@ -344,108 +82,14 @@ struct hb_set_t
* Used for faster rejection of corrupt data. */
template <typename T>
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 <typename T>
bool add_sorted_array (const hb_sorted_array_t<const 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<unsigned> 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<hb_codepoint_t, hb_codepoint_t>& range)
hb_sparseset_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_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<unsigned>& 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<unsigned>& workspace,
unsigned int length)
{
assert(workspace.length == pages.length);
hb_vector_t<unsigned>& 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<unsigned>& 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 <typename Op>
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<unsigned> 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<iter_t, hb_codepoint_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<hb_bit_set_invertible_t> {};
static_assert (hb_set_t::INVALID == HB_SET_VALUE_INVALID, "");
#endif /* HB_SET_HH */

Some files were not shown because too many files have changed in this diff Show More