Implemented seac for extents & subset along with API tests

This commit is contained in:
Michiharu Ariza 2018-11-14 13:38:03 -08:00
parent 41a8bc7fd9
commit 3787c07856
11 changed files with 701 additions and 75 deletions

View File

@ -1 +1,320 @@
EXTRA_DIST = # -*- mode: makefile -*-
#
# gtk-doc.make - make rules for gtk-doc
# Copyright (C) 2003 James Henstridge
# 2004-2007 Damon Chaplin
# 2007-2017 Stefan Sauer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
####################################
# Everything below here is generic #
####################################
if GTK_DOC_USE_LIBTOOL
GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
GTKDOC_RUN = $(LIBTOOL) --mode=execute
else
GTKDOC_CC = $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
GTKDOC_LD = $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
GTKDOC_RUN =
endif
# We set GPATH here; this gives us semantics for GNU make
# which are more like other make's VPATH, when it comes to
# whether a source that is a target of one rule is then
# searched for in VPATH/GPATH.
#
GPATH = $(srcdir)
TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)
SETUP_FILES = \
$(content_files) \
$(expand_content_files) \
$(DOC_MAIN_SGML_FILE) \
$(DOC_MODULE)-sections.txt \
$(DOC_MODULE)-overrides.txt
EXTRA_DIST = \
$(HTML_IMAGES) \
$(SETUP_FILES)
DOC_STAMPS=setup-build.stamp scan-build.stamp sgml-build.stamp \
html-build.stamp pdf-build.stamp \
sgml.stamp html.stamp pdf.stamp
SCANOBJ_FILES = \
$(DOC_MODULE).args \
$(DOC_MODULE).hierarchy \
$(DOC_MODULE).interfaces \
$(DOC_MODULE).prerequisites \
$(DOC_MODULE).signals
REPORT_FILES = \
$(DOC_MODULE)-undocumented.txt \
$(DOC_MODULE)-undeclared.txt \
$(DOC_MODULE)-unused.txt
gtkdoc-check.test: Makefile
$(AM_V_GEN)echo "#!/bin/sh -e" > $@; \
echo "$(GTKDOC_CHECK_PATH) || exit 1" >> $@; \
chmod +x $@
CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) gtkdoc-check.test
if GTK_DOC_BUILD_HTML
HTML_BUILD_STAMP=html-build.stamp
else
HTML_BUILD_STAMP=
endif
if GTK_DOC_BUILD_PDF
PDF_BUILD_STAMP=pdf-build.stamp
else
PDF_BUILD_STAMP=
endif
all-gtk-doc: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP)
.PHONY: all-gtk-doc
if ENABLE_GTK_DOC
all-local: all-gtk-doc
endif
docs: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP)
$(REPORT_FILES): sgml-build.stamp
#### setup ####
GTK_DOC_V_SETUP=$(GTK_DOC_V_SETUP_@AM_V@)
GTK_DOC_V_SETUP_=$(GTK_DOC_V_SETUP_@AM_DEFAULT_V@)
GTK_DOC_V_SETUP_0=@echo " DOC Preparing build";
setup-build.stamp:
-$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \
files=`echo $(SETUP_FILES) $(DOC_MODULE).types`; \
if test "x$$files" != "x" ; then \
for file in $$files ; do \
destdir=`dirname $(abs_builddir)/$$file`; \
test -d "$$destdir" || mkdir -p "$$destdir"; \
test -f $(abs_srcdir)/$$file && \
cp -pf $(abs_srcdir)/$$file $(abs_builddir)/$$file || true; \
done; \
fi; \
fi
$(AM_V_at)touch setup-build.stamp
#### scan ####
GTK_DOC_V_SCAN=$(GTK_DOC_V_SCAN_@AM_V@)
GTK_DOC_V_SCAN_=$(GTK_DOC_V_SCAN_@AM_DEFAULT_V@)
GTK_DOC_V_SCAN_0=@echo " DOC Scanning header files";
GTK_DOC_V_INTROSPECT=$(GTK_DOC_V_INTROSPECT_@AM_V@)
GTK_DOC_V_INTROSPECT_=$(GTK_DOC_V_INTROSPECT_@AM_DEFAULT_V@)
GTK_DOC_V_INTROSPECT_0=@echo " DOC Introspecting gobjects";
scan-build.stamp: setup-build.stamp $(HFILE_GLOB) $(CFILE_GLOB)
$(GTK_DOC_V_SCAN)_source_dir='' ; \
for i in $(DOC_SOURCE_DIR) ; do \
_source_dir="$${_source_dir} --source-dir=$$i" ; \
done ; \
gtkdoc-scan --module=$(DOC_MODULE) --ignore-headers="$(IGNORE_HFILES)" $${_source_dir} $(SCAN_OPTIONS) $(EXTRA_HFILES)
$(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \
scanobj_options=""; \
gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \
if test "$$?" = "0"; then \
if test "x$(V)" = "x1"; then \
scanobj_options="--verbose"; \
fi; \
fi; \
CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \
gtkdoc-scangobj $(SCANGOBJ_OPTIONS) $$scanobj_options --module=$(DOC_MODULE); \
else \
for i in $(SCANOBJ_FILES) ; do \
test -f $$i || touch $$i ; \
done \
fi
$(AM_V_at)touch scan-build.stamp
$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp
@true
#### xml ####
GTK_DOC_V_XML=$(GTK_DOC_V_XML_@AM_V@)
GTK_DOC_V_XML_=$(GTK_DOC_V_XML_@AM_DEFAULT_V@)
GTK_DOC_V_XML_0=@echo " DOC Building XML";
sgml-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt $(expand_content_files) xml/gtkdocentities.ent
$(GTK_DOC_V_XML)_source_dir='' ; \
for i in $(DOC_SOURCE_DIR) ; do \
_source_dir="$${_source_dir} --source-dir=$$i" ; \
done ; \
gtkdoc-mkdb --module=$(DOC_MODULE) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $${_source_dir} $(MKDB_OPTIONS)
$(AM_V_at)touch sgml-build.stamp
sgml.stamp: sgml-build.stamp
@true
$(DOC_MAIN_SGML_FILE): sgml-build.stamp
@true
xml/gtkdocentities.ent: Makefile
$(GTK_DOC_V_XML)$(MKDIR_P) $(@D) && ( \
echo "<!ENTITY package \"$(PACKAGE)\">"; \
echo "<!ENTITY package_bugreport \"$(PACKAGE_BUGREPORT)\">"; \
echo "<!ENTITY package_name \"$(PACKAGE_NAME)\">"; \
echo "<!ENTITY package_string \"$(PACKAGE_STRING)\">"; \
echo "<!ENTITY package_tarname \"$(PACKAGE_TARNAME)\">"; \
echo "<!ENTITY package_url \"$(PACKAGE_URL)\">"; \
echo "<!ENTITY package_version \"$(PACKAGE_VERSION)\">"; \
) > $@
#### html ####
GTK_DOC_V_HTML=$(GTK_DOC_V_HTML_@AM_V@)
GTK_DOC_V_HTML_=$(GTK_DOC_V_HTML_@AM_DEFAULT_V@)
GTK_DOC_V_HTML_0=@echo " DOC Building HTML";
GTK_DOC_V_XREF=$(GTK_DOC_V_XREF_@AM_V@)
GTK_DOC_V_XREF_=$(GTK_DOC_V_XREF_@AM_DEFAULT_V@)
GTK_DOC_V_XREF_0=@echo " DOC Fixing cross-references";
html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files)
$(GTK_DOC_V_HTML)rm -rf html && mkdir html && \
mkhtml_options=""; \
gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \
if test "$$?" = "0"; then \
if test "x$(V)" = "x1"; then \
mkhtml_options="$$mkhtml_options --verbose"; \
fi; \
fi; \
gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \
if test "$$?" = "0"; then \
mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \
fi; \
cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE)
-@test "x$(HTML_IMAGES)" = "x" || \
for file in $(HTML_IMAGES) ; do \
test -f $(abs_srcdir)/$$file && cp $(abs_srcdir)/$$file $(abs_builddir)/html; \
test -f $(abs_builddir)/$$file && cp $(abs_builddir)/$$file $(abs_builddir)/html; \
done;
$(GTK_DOC_V_XREF)gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS)
$(AM_V_at)touch html-build.stamp
#### pdf ####
GTK_DOC_V_PDF=$(GTK_DOC_V_PDF_@AM_V@)
GTK_DOC_V_PDF_=$(GTK_DOC_V_PDF_@AM_DEFAULT_V@)
GTK_DOC_V_PDF_0=@echo " DOC Building PDF";
pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files)
$(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \
mkpdf_options=""; \
gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \
if test "$$?" = "0"; then \
if test "x$(V)" = "x1"; then \
mkpdf_options="$$mkpdf_options --verbose"; \
fi; \
fi; \
if test "x$(HTML_IMAGES)" != "x"; then \
for img in $(HTML_IMAGES); do \
part=`dirname $$img`; \
echo $$mkpdf_options | grep >/dev/null "\-\-imgdir=$$part "; \
if test $$? != 0; then \
mkpdf_options="$$mkpdf_options --imgdir=$$part"; \
fi; \
done; \
fi; \
gtkdoc-mkpdf --path="$(abs_srcdir)" $$mkpdf_options $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) $(MKPDF_OPTIONS)
$(AM_V_at)touch pdf-build.stamp
##############
clean-local:
@rm -f *~ *.bak
@rm -rf .libs
@if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-types" ; then \
rm -f $(DOC_MODULE).types; \
fi
@if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-sections" ; then \
rm -f $(DOC_MODULE)-sections.txt; \
fi
distclean-local:
@rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \
$(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt
@if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \
rm -f $(SETUP_FILES) $(DOC_MODULE).types; \
fi
maintainer-clean-local:
@rm -rf xml html
install-data-local:
@installfiles=`echo $(builddir)/html/*`; \
if test "$$installfiles" = '$(builddir)/html/*'; \
then echo 1>&2 'Nothing to install' ; \
else \
if test -n "$(DOC_MODULE_VERSION)"; then \
installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \
else \
installdir="$(DESTDIR)$(TARGET_DIR)"; \
fi; \
$(mkinstalldirs) $${installdir} ; \
for i in $$installfiles; do \
echo ' $(INSTALL_DATA) '$$i ; \
$(INSTALL_DATA) $$i $${installdir}; \
done; \
if test -n "$(DOC_MODULE_VERSION)"; then \
mv -f $${installdir}/$(DOC_MODULE).devhelp2 \
$${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \
fi; \
$(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir}; \
fi
uninstall-local:
@if test -n "$(DOC_MODULE_VERSION)"; then \
installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \
else \
installdir="$(DESTDIR)$(TARGET_DIR)"; \
fi; \
rm -rf $${installdir}
#
# Require gtk-doc when making dist
#
if HAVE_GTK_DOC
dist-check-gtkdoc: docs
else
dist-check-gtkdoc:
@echo "*** gtk-doc is needed to run 'make dist'. ***"
@echo "*** gtk-doc was not found when 'configure' ran. ***"
@echo "*** please install gtk-doc and rerun 'configure'. ***"
@false
endif
dist-hook: dist-check-gtkdoc all-gtk-doc dist-hook-local
@mkdir $(distdir)/html
@cp ./html/* $(distdir)/html
@-cp ./$(DOC_MODULE).pdf $(distdir)/
@-cp ./$(DOC_MODULE).types $(distdir)/
@-cp ./$(DOC_MODULE)-sections.txt $(distdir)/
@cd $(distdir) && rm -f $(DISTCLEANFILES)
@$(GTKDOC_REBASE) --online --relative --html-dir=$(distdir)/html
.PHONY : dist-hook-local docs

View File

@ -169,7 +169,7 @@ inline unsigned int OpCode_Size (OpCode op) { return Is_OpCode_ESC (op)? 2: 1; }
#define OpCode_fixedcs 255 /* 32-bit fixed */ #define OpCode_fixedcs 255 /* 32-bit fixed */
/* Two byte escape operators 12, (0-41) */ /* Two byte escape operators 12, (0-41) */
#define OpCode_ReservedESC0 Make_OpCode_ESC(0) #define OpCode_dotsection Make_OpCode_ESC(0) /* CFF (obsoleted) */
#define OpCode_ReservedESC1 Make_OpCode_ESC(1) #define OpCode_ReservedESC1 Make_OpCode_ESC(1)
#define OpCode_ReservedESC2 Make_OpCode_ESC(2) #define OpCode_ReservedESC2 Make_OpCode_ESC(2)
#define OpCode_and Make_OpCode_ESC(3) /* CFF */ #define OpCode_and Make_OpCode_ESC(3) /* CFF */

View File

@ -88,9 +88,16 @@ struct Point
y.init (); y.init ();
} }
inline void set_int (int _x, int _y)
{
x.set_int (_x);
y.set_int (_y);
}
inline void move_x (const Number &dx) { x += dx; } inline void move_x (const Number &dx) { x += dx; }
inline void move_y (const Number &dy) { y += dy; } inline void move_y (const Number &dy) { y += dy; }
inline void move (const Number &dx, const Number &dy) { move_x (dx); move_y (dy); } inline void move (const Number &dx, const Number &dy) { move_x (dx); move_y (dy); }
inline void move (const Point &d) { move_x (d.x); move_y (d.y); }
Number x; Number x;
Number y; Number y;

View File

@ -84,6 +84,29 @@ template <typename OPSET, typename PARAM, typename PATH=PathProcsNull<CFF1CSInte
struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH> struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>
{ {
/* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */ /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */
/* Type 1-originated deprecated opcodes, seac behavior of endchar and dotsection are supported */
static inline void process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
{
switch (op) {
case OpCode_dotsection:
SUPER::flush_args_and_op (op, env, param);
break;
case OpCode_endchar:
OPSET::check_width (op, env, param);
if (env.argStack.get_count () >= 4)
{
OPSET::process_seac (env, param);
}
OPSET::flush_args_and_op (op, env, param);
env.set_endchar (true);
break;
default:
SUPER::process_op (op, env, param);
}
}
static inline void check_width (OpCode op, CFF1CSInterpEnv &env, PARAM& param) static inline void check_width (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
{ {
@ -93,8 +116,6 @@ struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>
switch (op) switch (op)
{ {
case OpCode_endchar: case OpCode_endchar:
has_width = (env.argStack.get_count () > 0);
break;
case OpCode_hstem: case OpCode_hstem:
case OpCode_hstemhm: case OpCode_hstemhm:
case OpCode_vstem: case OpCode_vstem:
@ -117,6 +138,10 @@ struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>
} }
} }
static inline void process_seac (CFF1CSInterpEnv &env, PARAM& param)
{
}
static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param) static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param)
{ {
SUPER::flush_args (env, param); SUPER::flush_args (env, param);

View File

@ -30,7 +30,7 @@
using namespace CFF; using namespace CFF;
/* SID to code */ /* SID to code */
static const uint8_t standard_encoding [] = static const uint8_t standard_encoding_to_code [] =
{ {
0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
@ -45,7 +45,7 @@ static const uint8_t standard_encoding [] =
}; };
/* SID to code */ /* SID to code */
static const uint8_t expert_encoding [] = static const uint8_t expert_encoding_to_code [] =
{ {
0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 45, 46, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 45, 46,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 0, 0, 0,
@ -74,7 +74,7 @@ static const uint8_t expert_encoding [] =
}; };
/* glyph ID to SID */ /* glyph ID to SID */
static const uint16_t expert_charset [] = static const uint16_t expert_charset_to_sid [] =
{ {
0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99, 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99,
239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252,
@ -90,7 +90,7 @@ static const uint16_t expert_charset [] =
}; };
/* glyph ID to SID */ /* glyph ID to SID */
static const uint16_t expert_subset_charset [] = static const uint16_t expert_subset_charset_to_sid [] =
{ {
0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242,
243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257,
@ -100,66 +100,131 @@ static const uint16_t expert_subset_charset [] =
340, 341, 342, 343, 344, 345, 346 340, 341, 342, 343, 344, 345, 346
}; };
hb_codepoint_t OT::cff1::lookup_standard_encoding (hb_codepoint_t sid) /* code to SID */
static const uint8_t standard_encoding_to_sid [] =
{ {
if (sid < ARRAY_LENGTH (standard_encoding)) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
return (hb_codepoint_t)standard_encoding[sid]; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
0, 111, 112, 113, 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 123,
0, 124, 125, 126, 127, 128, 129, 130, 131, 0, 132, 133, 0, 134, 135, 136,
137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143, 0, 0, 0, 0,
0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0
};
hb_codepoint_t OT::cff1::lookup_standard_encoding_for_code (hb_codepoint_t sid)
{
if (sid < ARRAY_LENGTH (standard_encoding_to_code))
return (hb_codepoint_t)standard_encoding_to_code[sid];
else else
return 0; return 0;
} }
hb_codepoint_t OT::cff1::lookup_expert_encoding (hb_codepoint_t sid) hb_codepoint_t OT::cff1::lookup_expert_encoding_for_code (hb_codepoint_t sid)
{ {
if (sid < ARRAY_LENGTH (expert_encoding)) if (sid < ARRAY_LENGTH (expert_encoding_to_code))
return (hb_codepoint_t)expert_encoding[sid]; return (hb_codepoint_t)expert_encoding_to_code[sid];
else else
return 0; return 0;
} }
hb_codepoint_t OT::cff1::lookup_expert_charset (hb_codepoint_t glyph) hb_codepoint_t OT::cff1::lookup_expert_charset_for_sid (hb_codepoint_t glyph)
{ {
if (glyph < ARRAY_LENGTH (expert_charset)) if (glyph < ARRAY_LENGTH (expert_charset_to_sid))
return (hb_codepoint_t)expert_charset[glyph]; return (hb_codepoint_t)expert_charset_to_sid[glyph];
else else
return 0; return 0;
} }
hb_codepoint_t OT::cff1::lookup_expert_subset_charset (hb_codepoint_t glyph) hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph)
{ {
if (glyph < ARRAY_LENGTH (expert_subset_charset)) if (glyph < ARRAY_LENGTH (expert_subset_charset_to_sid))
return (hb_codepoint_t)expert_subset_charset[glyph]; return (hb_codepoint_t)expert_subset_charset_to_sid[glyph];
else else
return 0; return 0;
} }
struct ExtentsParam hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code)
{
if (code < ARRAY_LENGTH (standard_encoding_to_sid))
return (hb_codepoint_t)standard_encoding_to_sid[code];
else
return CFF_UNDEF_SID;
}
struct Bounds
{ {
inline void init (void) inline void init (void)
{
min.set_int (0x7FFFFFFF, 0x7FFFFFFF);
max.set_int (-0x80000000, -0x80000000);
}
inline void update (const Point &pt)
{
if (pt.x < min.x) min.x = pt.x;
if (pt.x > max.x) max.x = pt.x;
if (pt.y < min.y) min.y = pt.y;
if (pt.y > max.y) max.y = pt.y;
}
inline void merge (const Bounds &b)
{
if (empty ())
*this = b;
else if (!b.empty ())
{
if (b.min.x < min.x) min.x = b.min.x;
if (b.max.x > max.x) max.x = b.max.x;
if (b.min.y < min.y) min.y = b.min.y;
if (b.max.y > max.y) max.y = b.max.y;
}
}
inline void offset (const Point &delta)
{
if (!empty ())
{
min.move (delta);
max.move (delta);
}
}
inline bool empty (void) const
{
return (min.x >= max.x) || (min.y >= max.y);
}
Point min;
Point max;
};
struct ExtentsParam
{
inline void init (const OT::cff1::accelerator_t *_cff)
{ {
path_open = false; path_open = false;
min_x.set_int (0x7FFFFFFF); cff = _cff;
min_y.set_int (0x7FFFFFFF); bounds.init ();
max_x.set_int (-0x80000000);
max_y.set_int (-0x80000000);
} }
inline void start_path (void) { path_open = true; } inline void start_path (void) { path_open = true; }
inline void end_path (void) { path_open = false; } inline void end_path (void) { path_open = false; }
inline bool is_path_open (void) const { return path_open; } inline bool is_path_open (void) const { return path_open; }
inline void update_bounds (const Point &pt)
{
if (pt.x < min_x) min_x = pt.x;
if (pt.x > max_x) max_x = pt.x;
if (pt.y < min_y) min_y = pt.y;
if (pt.y > max_y) max_y = pt.y;
}
bool path_open; bool path_open;
Number min_x; Bounds bounds;
Number min_y;
Number max_x; const OT::cff1::accelerator_t *cff;
Number max_y;
}; };
struct CFF1PathProcs_Extents : PathProcs<CFF1PathProcs_Extents, CFF1CSInterpEnv, ExtentsParam> struct CFF1PathProcs_Extents : PathProcs<CFF1PathProcs_Extents, CFF1CSInterpEnv, ExtentsParam>
@ -175,10 +240,10 @@ struct CFF1PathProcs_Extents : PathProcs<CFF1PathProcs_Extents, CFF1CSInterpEnv,
if (!param.is_path_open ()) if (!param.is_path_open ())
{ {
param.start_path (); param.start_path ();
param.update_bounds (env.get_pt ()); param.bounds.update (env.get_pt ());
} }
env.moveto (pt1); env.moveto (pt1);
param.update_bounds (env.get_pt ()); param.bounds.update (env.get_pt ());
} }
static inline void curve (CFF1CSInterpEnv &env, ExtentsParam& param, const Point &pt1, const Point &pt2, const Point &pt3) static inline void curve (CFF1CSInterpEnv &env, ExtentsParam& param, const Point &pt1, const Point &pt2, const Point &pt3)
@ -186,50 +251,137 @@ struct CFF1PathProcs_Extents : PathProcs<CFF1PathProcs_Extents, CFF1CSInterpEnv,
if (!param.is_path_open ()) if (!param.is_path_open ())
{ {
param.start_path (); param.start_path ();
param.update_bounds (env.get_pt ()); param.bounds.update (env.get_pt ());
} }
/* include control points */ /* include control points */
param.update_bounds (pt1); param.bounds.update (pt1);
param.update_bounds (pt2); param.bounds.update (pt2);
env.moveto (pt3); env.moveto (pt3);
param.update_bounds (env.get_pt ()); param.bounds.update (env.get_pt ());
} }
}; };
struct CFF1CSOpSet_Extents : CFF1CSOpSet<CFF1CSOpSet_Extents, ExtentsParam, CFF1PathProcs_Extents> {}; static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, Bounds &bounds);
struct CFF1CSOpSet_Extents : CFF1CSOpSet<CFF1CSOpSet_Extents, ExtentsParam, CFF1PathProcs_Extents>
{
static inline void process_seac (CFF1CSInterpEnv &env, ExtentsParam& param)
{
unsigned int n = env.argStack.get_count ();
Point delta;
delta.x = env.argStack[n-4];
delta.y = env.argStack[n-3];
hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ());
hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ());
Bounds base_bounds, accent_bounds;
if (likely (base && accent
&& _get_bounds (param.cff, base, base_bounds)
&& _get_bounds (param.cff, accent, accent_bounds)))
{
param.bounds.merge (base_bounds);
accent_bounds.offset (delta);
param.bounds.merge (accent_bounds);
}
else
env.set_error ();
}
};
bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, Bounds &bounds)
{
bounds.init ();
if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
unsigned int fd = cff->fdSelect->get_fd (glyph);
CFF1CSInterpreter<CFF1CSOpSet_Extents, ExtentsParam> interp;
const ByteStr str = (*cff->charStrings)[glyph];
interp.env.init (str, *cff, fd);
ExtentsParam param;
param.init (cff);
if (unlikely (!interp.interpret (param))) return false;
bounds = param.bounds;
return true;
}
bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
{ {
if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; Bounds bounds;
unsigned int fd = fdSelect->get_fd (glyph); if (!_get_bounds (this, glyph, bounds))
CFF1CSInterpreter<CFF1CSOpSet_Extents, ExtentsParam> interp; return false;
const ByteStr str = (*charStrings)[glyph];
interp.env.init (str, *this, fd);
ExtentsParam param;
param.init ();
if (unlikely (!interp.interpret (param))) return false;
if (param.min_x >= param.max_x) if (bounds.min.x >= bounds.max.x)
{ {
extents->width = 0; extents->width = 0;
extents->x_bearing = 0; extents->x_bearing = 0;
} }
else else
{ {
extents->x_bearing = (int32_t)param.min_x.floor (); extents->x_bearing = (int32_t)bounds.min.x.floor ();
extents->width = (int32_t)param.max_x.ceil () - extents->x_bearing; extents->width = (int32_t)bounds.max.x.ceil () - extents->x_bearing;
} }
if (param.min_y >= param.max_y) if (bounds.min.y >= bounds.max.y)
{ {
extents->height = 0; extents->height = 0;
extents->y_bearing = 0; extents->y_bearing = 0;
} }
else else
{ {
extents->y_bearing = (int32_t)param.max_y.ceil (); extents->y_bearing = (int32_t)bounds.max.y.ceil ();
extents->height = (int32_t)param.min_y.floor () - extents->y_bearing; extents->height = (int32_t)bounds.min.y.floor () - extents->y_bearing;
} }
return true; return true;
} }
struct GetSeacParam
{
inline void init (const OT::cff1::accelerator_t *_cff)
{
cff = _cff;
base = 0;
accent = 0;
}
inline bool has_seac (void) const
{ return base && accent; }
const OT::cff1::accelerator_t *cff;
hb_codepoint_t base;
hb_codepoint_t accent;
};
struct CFF1CSOpSet_Seac : CFF1CSOpSet<CFF1CSOpSet_Seac, GetSeacParam>
{
static inline void process_seac (CFF1CSInterpEnv &env, GetSeacParam& param)
{
unsigned int n = env.argStack.get_count ();
hb_codepoint_t base_char = (hb_codepoint_t)env.argStack[n-2].to_int ();
hb_codepoint_t accent_char = (hb_codepoint_t)env.argStack[n-1].to_int ();
param.base = param.cff->std_code_to_glyph (base_char);
param.accent = param.cff->std_code_to_glyph (accent_char);
}
};
bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const
{
if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
unsigned int fd = fdSelect->get_fd (glyph);
CFF1CSInterpreter<CFF1CSOpSet_Seac, GetSeacParam> interp;
const ByteStr str = (*charStrings)[glyph];
interp.env.init (str, *this, fd);
GetSeacParam param;
param.init (this);
if (unlikely (!interp.interpret (param))) return false;
if (param.has_seac ())
{
*base = param.base;
*accent = param.accent;
return true;
}
return false;
}

View File

@ -323,6 +323,19 @@ struct Charset0 {
return sids[glyph - 1]; return sids[glyph - 1];
} }
inline hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
{
if (sid == 0)
return 0;
for (unsigned int glyph = 1; glyph < num_glyphs; glyph++)
{
if (sids[glyph-1] == sid)
return glyph;
}
return 0;
}
inline unsigned int get_size (unsigned int num_glyphs) const inline unsigned int get_size (unsigned int num_glyphs) const
{ {
assert (num_glyphs > 0); assert (num_glyphs > 0);
@ -379,6 +392,20 @@ struct Charset1_2 {
return 0; return 0;
} }
inline hb_codepoint_t get_glyph (hb_codepoint_t sid) const
{
if (sid == 0) return 0;
hb_codepoint_t glyph = 1;
for (unsigned int i = 0;; i++)
{
if ((ranges[i].first <= sid) && sid <= ranges[i].first + ranges[i].nLeft)
return glyph + (sid - ranges[i].first);
glyph += (ranges[i].nLeft + 1);
}
return 0;
}
inline unsigned int get_size (unsigned int num_glyphs) const inline unsigned int get_size (unsigned int num_glyphs) const
{ {
unsigned int size = HBUINT8::static_size; unsigned int size = HBUINT8::static_size;
@ -518,6 +545,16 @@ struct Charset {
return u.format2.get_sid (glyph); return u.format2.get_sid (glyph);
} }
inline hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
{
if (format == 0)
return u.format0.get_glyph (sid, num_glyphs);
else if (format == 1)
return u.format1.get_glyph (sid);
else
return u.format2.get_glyph (sid);
}
HBUINT8 format; HBUINT8 format;
union { union {
Charset0 format0; Charset0 format0;
@ -997,6 +1034,14 @@ struct cff1
if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
} }
if (is_predef_charset ())
charset = &Null(Charset);
else
{
charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset);
if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; }
}
fdCount = 1; fdCount = 1;
if (is_CID ()) if (is_CID ())
{ {
@ -1094,11 +1139,27 @@ struct cff1
inline bool is_valid (void) const { return blob != nullptr; } inline bool is_valid (void) const { return blob != nullptr; }
inline bool is_CID (void) const { return topDict.is_CID (); } inline bool is_CID (void) const { return topDict.is_CID (); }
inline bool is_predef_charset (void) const { return topDict.CharsetOffset <= ExpertSubsetCharset; }
inline unsigned int std_code_to_glyph (hb_codepoint_t code) const
{
hb_codepoint_t sid = lookup_standard_encoding_for_sid (code);
if (unlikely (sid == CFF_UNDEF_SID))
return 0;
if (charset != &Null(Charset))
return charset->get_glyph (sid, num_glyphs);
else if ((topDict.CharsetOffset == ISOAdobeCharset)
&& (code <= 228 /*zcaron*/)) return sid;
return 0;
}
protected: protected:
hb_blob_t *blob; hb_blob_t *blob;
hb_sanitize_context_t sc; hb_sanitize_context_t sc;
public: public:
const Charset *charset;
const CFF1NameIndex *nameIndex; const CFF1NameIndex *nameIndex;
const CFF1TopDictIndex *topDictIndex; const CFF1TopDictIndex *topDictIndex;
const CFF1StringIndex *stringIndex; const CFF1StringIndex *stringIndex;
@ -1118,6 +1179,7 @@ struct cff1
struct accelerator_t : accelerator_templ_t<CFF1PrivateDictOpSet, CFF1PrivateDictValues> struct accelerator_t : accelerator_templ_t<CFF1PrivateDictOpSet, CFF1PrivateDictValues>
{ {
HB_INTERNAL bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; HB_INTERNAL bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
}; };
struct accelerator_subset_t : accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset> struct accelerator_subset_t : accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset>
@ -1129,13 +1191,6 @@ struct cff1
const OT::cff1 *cff = this->blob->as<OT::cff1> (); const OT::cff1 *cff = this->blob->as<OT::cff1> ();
encoding = &Null(Encoding); encoding = &Null(Encoding);
if (is_predef_charset ())
charset = &Null(Charset);
else
{
charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset);
if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; }
}
if (is_CID ()) if (is_CID ())
{ {
if (unlikely (charset == &Null(Charset))) { fini (); return; } if (unlikely (charset == &Null(Charset))) { fini (); return; }
@ -1151,7 +1206,6 @@ struct cff1
} }
inline bool is_predef_encoding (void) const { return topDict.EncodingOffset <= ExpertEncoding; } inline bool is_predef_encoding (void) const { return topDict.EncodingOffset <= ExpertEncoding; }
inline bool is_predef_charset (void) const { return topDict.CharsetOffset <= ExpertSubsetCharset; }
inline hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const inline hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const
{ {
@ -1165,10 +1219,10 @@ struct cff1
switch (topDict.EncodingOffset) switch (topDict.EncodingOffset)
{ {
case StandardEncoding: case StandardEncoding:
code = lookup_standard_encoding (sid); code = lookup_standard_encoding_for_code (sid);
break; break;
case ExpertEncoding: case ExpertEncoding:
code = lookup_expert_encoding (sid); code = lookup_expert_encoding_for_code (sid);
break; break;
default: default:
break; break;
@ -1190,10 +1244,10 @@ struct cff1
if (glyph <= 228 /*zcaron*/) sid = glyph; if (glyph <= 228 /*zcaron*/) sid = glyph;
break; break;
case ExpertCharset: case ExpertCharset:
sid = lookup_expert_charset (glyph); sid = lookup_expert_charset_for_sid (glyph);
break; break;
case ExpertSubsetCharset: case ExpertSubsetCharset:
sid = lookup_expert_subset_charset (glyph); sid = lookup_expert_subset_charset_for_sid (glyph);
break; break;
default: default:
break; break;
@ -1203,7 +1257,6 @@ struct cff1
} }
const Encoding *encoding; const Encoding *encoding;
const Charset *charset;
private: private:
typedef accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset> SUPER; typedef accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset> SUPER;
@ -1228,10 +1281,11 @@ struct cff1
} }
protected: protected:
HB_INTERNAL static hb_codepoint_t lookup_standard_encoding (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid);
HB_INTERNAL static hb_codepoint_t lookup_expert_encoding (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid);
HB_INTERNAL static hb_codepoint_t lookup_expert_charset (hb_codepoint_t glyph); HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph);
HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset (hb_codepoint_t glyph); HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph);
HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code);
public: public:
FixedVersion<HBUINT8> version; /* Version of CFF table. set to 0x0100u */ FixedVersion<HBUINT8> version; /* Version of CFF table. set to 0x0100u */

View File

@ -30,6 +30,7 @@
#include "hb-ot-cmap-table.hh" #include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh" #include "hb-ot-glyf-table.hh"
#include "hb-ot-cff1-table.hh"
static void static void
_add_gid_and_children (const OT::glyf::accelerator_t &glyf, _add_gid_and_children (const OT::glyf::accelerator_t &glyf,
@ -52,6 +53,19 @@ _add_gid_and_children (const OT::glyf::accelerator_t &glyf,
} }
} }
static void
_add_cff_seac_components (const OT::cff1::accelerator_t &cff,
hb_codepoint_t gid,
hb_set_t *gids_to_retain)
{
hb_codepoint_t base_gid, accent_gid;
if (cff.get_seac_components (gid, &base_gid, &accent_gid))
{
hb_set_add (gids_to_retain, base_gid);
hb_set_add (gids_to_retain, accent_gid);
}
}
static void static void
_gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain) _gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain)
{ {
@ -78,8 +92,10 @@ _populate_gids_to_retain (hb_face_t *face,
{ {
OT::cmap::accelerator_t cmap; OT::cmap::accelerator_t cmap;
OT::glyf::accelerator_t glyf; OT::glyf::accelerator_t glyf;
OT::cff1::accelerator_t cff;
cmap.init (face); cmap.init (face);
glyf.init (face); glyf.init (face);
cff.init (face);
hb_set_t *initial_gids_to_retain = hb_set_create (); hb_set_t *initial_gids_to_retain = hb_set_create ();
initial_gids_to_retain->add (0); // Not-def initial_gids_to_retain->add (0); // Not-def
@ -109,6 +125,8 @@ _populate_gids_to_retain (hb_face_t *face,
while (initial_gids_to_retain->next (&gid)) while (initial_gids_to_retain->next (&gid))
{ {
_add_gid_and_children (glyf, gid, all_gids_to_retain); _add_gid_and_children (glyf, gid, all_gids_to_retain);
if (cff.is_valid ())
_add_cff_seac_components (cff, gid, all_gids_to_retain);
} }
hb_set_destroy (initial_gids_to_retain); hb_set_destroy (initial_gids_to_retain);
@ -117,6 +135,7 @@ _populate_gids_to_retain (hb_face_t *face,
while (all_gids_to_retain->next (&gid)) while (all_gids_to_retain->next (&gid))
glyphs->push (gid); glyphs->push (gid);
cff.fini ();
glyf.fini (); glyf.fini ();
cmap.fini (); cmap.fini ();

Binary file not shown.

Binary file not shown.

View File

@ -90,6 +90,36 @@ test_extents_cff1_flex (void)
hb_font_destroy (font); hb_font_destroy (font);
} }
static void
test_extents_cff1_seac (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/cff1_seac.otf");
g_assert (face);
hb_font_t *font = hb_font_create (face);
hb_face_destroy (face);
g_assert (font);
hb_ot_font_set_funcs (font);
hb_glyph_extents_t extents;
hb_bool_t result = hb_font_get_glyph_extents (font, 3, &extents); /* Agrave */
g_assert (result);
g_assert_cmpint (extents.x_bearing, ==, 3);
g_assert_cmpint (extents.y_bearing, ==, 861);
g_assert_cmpint (extents.width, ==, 538);
g_assert_cmpint (extents.height, ==, -861);
result = hb_font_get_glyph_extents (font, 4, &extents); /* Udieresis */
g_assert (result);
g_assert_cmpint (extents.x_bearing, ==, 87);
g_assert_cmpint (extents.y_bearing, ==, 827);
g_assert_cmpint (extents.width, ==, 471);
g_assert_cmpint (extents.height, ==, -839);
hb_font_destroy (font);
}
static void static void
test_extents_cff2 (void) test_extents_cff2 (void)
{ {
@ -161,6 +191,7 @@ main (int argc, char **argv)
hb_test_add (test_extents_cff1); hb_test_add (test_extents_cff1);
hb_test_add (test_extents_cff1_flex); hb_test_add (test_extents_cff1_flex);
hb_test_add (test_extents_cff1_seac);
hb_test_add (test_extents_cff2); hb_test_add (test_extents_cff2);
hb_test_add (test_extents_cff2_vsindex); hb_test_add (test_extents_cff2_vsindex);

View File

@ -248,6 +248,24 @@ test_subset_cff1_expert (void)
hb_face_destroy (face); hb_face_destroy (face);
} }
static void
test_subset_cff1_seac (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/cff1_seac.otf");
hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_seac.C0.otf");
hb_set_t *codepoints = hb_set_create ();
hb_set_add (codepoints, 0xC0); /* Agrave */
hb_face_t *face_test = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints));
hb_set_destroy (codepoints);
hb_subset_test_check (face_subset, face_test, HB_TAG ('C','F','F',' '));
hb_face_destroy (face_test);
hb_face_destroy (face_subset);
hb_face_destroy (face);
}
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
@ -263,6 +281,7 @@ main (int argc, char **argv)
hb_test_add (test_subset_cff1_j_desubr); hb_test_add (test_subset_cff1_j_desubr);
hb_test_add (test_subset_cff1_j_desubr_strip_hints); hb_test_add (test_subset_cff1_j_desubr_strip_hints);
hb_test_add (test_subset_cff1_expert); hb_test_add (test_subset_cff1_expert);
hb_test_add (test_subset_cff1_seac);
return hb_test_run (); return hb_test_run ();
} }