Merge pull request #1113 from harfbuzz/cff-subset

CFF/CFF2 subsetter
This commit is contained in:
Behdad Esfahbod 2018-11-30 22:40:54 -05:00 committed by GitHub
commit 5e64e0f532
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
107 changed files with 18717 additions and 7 deletions

320
gtk-doc.make Normal file
View File

@ -0,0 +1,320 @@
# -*- 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

8369
m4/libtool.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

437
m4/ltoptions.m4 vendored Normal file
View File

@ -0,0 +1,437 @@
# Helper functions for option handling. -*- Autoconf -*-
#
# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 8 ltoptions.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
# ------------------------------------------
m4_define([_LT_MANGLE_OPTION],
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
# ---------------------------------------
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
# saved as a flag.
m4_define([_LT_SET_OPTION],
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
_LT_MANGLE_DEFUN([$1], [$2]),
[m4_warning([Unknown $1 option '$2'])])[]dnl
])
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
# ------------------------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
m4_define([_LT_IF_OPTION],
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
# -------------------------------------------------------
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
# are set.
m4_define([_LT_UNLESS_OPTIONS],
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
[m4_define([$0_found])])])[]dnl
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
])[]dnl
])
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
# ----------------------------------------
# OPTION-LIST is a space-separated list of Libtool options associated
# with MACRO-NAME. If any OPTION has a matching handler declared with
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
# the unknown option and exit.
m4_defun([_LT_SET_OPTIONS],
[# Set options
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[_LT_SET_OPTION([$1], _LT_Option)])
m4_if([$1],[LT_INIT],[
dnl
dnl Simply set some default values (i.e off) if boolean options were not
dnl specified:
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
])
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
])
dnl
dnl If no reference was made to various pairs of opposing options, then
dnl we run the default mode handler for the pair. For example, if neither
dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
dnl archives by default:
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
[_LT_ENABLE_FAST_INSTALL])
_LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
[_LT_WITH_AIX_SONAME([aix])])
])
])# _LT_SET_OPTIONS
## --------------------------------- ##
## Macros to handle LT_INIT options. ##
## --------------------------------- ##
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
# -----------------------------------------
m4_define([_LT_MANGLE_DEFUN],
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
# -----------------------------------------------
m4_define([LT_OPTION_DEFINE],
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
])# LT_OPTION_DEFINE
# dlopen
# ------
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
])
AU_DEFUN([AC_LIBTOOL_DLOPEN],
[_LT_SET_OPTION([LT_INIT], [dlopen])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'dlopen' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
# win32-dll
# ---------
# Declare package support for building win32 dll's.
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
[enable_win32_dll=yes
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
AC_CHECK_TOOL(AS, as, false)
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
AC_CHECK_TOOL(OBJDUMP, objdump, false)
;;
esac
test -z "$AS" && AS=as
_LT_DECL([], [AS], [1], [Assembler program])dnl
test -z "$DLLTOOL" && DLLTOOL=dlltool
_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
test -z "$OBJDUMP" && OBJDUMP=objdump
_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
])# win32-dll
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
_LT_SET_OPTION([LT_INIT], [win32-dll])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'win32-dll' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
# _LT_ENABLE_SHARED([DEFAULT])
# ----------------------------
# implement the --enable-shared flag, and supports the 'shared' and
# 'disable-shared' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_SHARED],
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([shared],
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
_LT_DECL([build_libtool_libs], [enable_shared], [0],
[Whether or not to build shared libraries])
])# _LT_ENABLE_SHARED
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
# Old names:
AC_DEFUN([AC_ENABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
])
AC_DEFUN([AC_DISABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], [disable-shared])
])
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
# _LT_ENABLE_STATIC([DEFAULT])
# ----------------------------
# implement the --enable-static flag, and support the 'static' and
# 'disable-static' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_STATIC],
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([static],
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
_LT_DECL([build_old_libs], [enable_static], [0],
[Whether or not to build static libraries])
])# _LT_ENABLE_STATIC
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
# Old names:
AC_DEFUN([AC_ENABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
])
AC_DEFUN([AC_DISABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], [disable-static])
])
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
# ----------------------------------
# implement the --enable-fast-install flag, and support the 'fast-install'
# and 'disable-fast-install' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_FAST_INSTALL],
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([fast-install],
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
_LT_DECL([fast_install], [enable_fast_install], [0],
[Whether or not to optimize for fast installation])dnl
])# _LT_ENABLE_FAST_INSTALL
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
# Old names:
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the 'fast-install' option into LT_INIT's first parameter.])
])
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the 'disable-fast-install' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
# _LT_WITH_AIX_SONAME([DEFAULT])
# ----------------------------------
# implement the --with-aix-soname flag, and support the `aix-soname=aix'
# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
m4_define([_LT_WITH_AIX_SONAME],
[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
shared_archive_member_spec=
case $host,$enable_shared in
power*-*-aix[[5-9]]*,yes)
AC_MSG_CHECKING([which variant of shared library versioning to provide])
AC_ARG_WITH([aix-soname],
[AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
[shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
[case $withval in
aix|svr4|both)
;;
*)
AC_MSG_ERROR([Unknown argument to --with-aix-soname])
;;
esac
lt_cv_with_aix_soname=$with_aix_soname],
[AC_CACHE_VAL([lt_cv_with_aix_soname],
[lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
with_aix_soname=$lt_cv_with_aix_soname])
AC_MSG_RESULT([$with_aix_soname])
if test aix != "$with_aix_soname"; then
# For the AIX way of multilib, we name the shared archive member
# based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
# and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
# Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
# the AIX toolchain works better with OBJECT_MODE set (default 32).
if test 64 = "${OBJECT_MODE-32}"; then
shared_archive_member_spec=shr_64
else
shared_archive_member_spec=shr
fi
fi
;;
*)
with_aix_soname=aix
;;
esac
_LT_DECL([], [shared_archive_member_spec], [0],
[Shared archive member basename, for filename based shared library versioning on AIX])dnl
])# _LT_WITH_AIX_SONAME
LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
# _LT_WITH_PIC([MODE])
# --------------------
# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
# LT_INIT options.
# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
m4_define([_LT_WITH_PIC],
[AC_ARG_WITH([pic],
[AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
[lt_p=${PACKAGE-default}
case $withval in
yes|no) pic_mode=$withval ;;
*)
pic_mode=default
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for lt_pkg in $withval; do
IFS=$lt_save_ifs
if test "X$lt_pkg" = "X$lt_p"; then
pic_mode=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[pic_mode=m4_default([$1], [default])])
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
])# _LT_WITH_PIC
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
# Old name:
AU_DEFUN([AC_LIBTOOL_PICMODE],
[_LT_SET_OPTION([LT_INIT], [pic-only])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'pic-only' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
## ----------------- ##
## LTDL_INIT Options ##
## ----------------- ##
m4_define([_LTDL_MODE], [])
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
[m4_define([_LTDL_MODE], [nonrecursive])])
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
[m4_define([_LTDL_MODE], [recursive])])
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
[m4_define([_LTDL_MODE], [subproject])])
m4_define([_LTDL_TYPE], [])
LT_OPTION_DEFINE([LTDL_INIT], [installable],
[m4_define([_LTDL_TYPE], [installable])])
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
[m4_define([_LTDL_TYPE], [convenience])])

124
m4/ltsugar.m4 vendored Normal file
View File

@ -0,0 +1,124 @@
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltsugar.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
# lt_join(SEP, ARG1, [ARG2...])
# -----------------------------
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
# associated separator.
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
# versions in m4sugar had bugs.
m4_define([lt_join],
[m4_if([$#], [1], [],
[$#], [2], [[$2]],
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
m4_define([_lt_join],
[m4_if([$#$2], [2], [],
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
# lt_car(LIST)
# lt_cdr(LIST)
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
# Autoconf-2.59, which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
[$#], 1, [],
[m4_dquote(m4_shift($@))])])
m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
# than defined and empty).
#
# This macro is needed until we can rely on Autoconf 2.62, since earlier
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
m4_define([lt_append],
[m4_define([$1],
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
# ----------------------------------------------------------
# Produce a SEP delimited list of all paired combinations of elements of
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
# has the form PREFIXmINFIXSUFFIXn.
# Needed until we can rely on m4_combine added in Autoconf 2.62.
m4_define([lt_combine],
[m4_if(m4_eval([$# > 3]), [1],
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
[[m4_foreach([_Lt_prefix], [$2],
[m4_foreach([_Lt_suffix],
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
# -----------------------------------------------------------------------
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
m4_define([lt_if_append_uniq],
[m4_ifdef([$1],
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
[lt_append([$1], [$2], [$3])$4],
[$5])],
[lt_append([$1], [$2], [$3])$4])])
# lt_dict_add(DICT, KEY, VALUE)
# -----------------------------
m4_define([lt_dict_add],
[m4_define([$1($2)], [$3])])
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
# --------------------------------------------
m4_define([lt_dict_add_subkey],
[m4_define([$1($2:$3)], [$4])])
# lt_dict_fetch(DICT, KEY, [SUBKEY])
# ----------------------------------
m4_define([lt_dict_fetch],
[m4_ifval([$3],
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
# -----------------------------------------------------------------
m4_define([lt_if_dict_fetch],
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
[$5],
[$6])])
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
# --------------------------------------------------------------
m4_define([lt_dict_filter],
[m4_if([$5], [], [],
[lt_join(m4_quote(m4_default([$4], [[, ]])),
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
])

23
m4/ltversion.m4 vendored Normal file
View File

@ -0,0 +1,23 @@
# ltversion.m4 -- version numbers -*- Autoconf -*-
#
# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# @configure_input@
# serial 4179 ltversion.m4
# This file is part of GNU Libtool
m4_define([LT_PACKAGE_VERSION], [2.4.6])
m4_define([LT_PACKAGE_REVISION], [2.4.6])
AC_DEFUN([LTVERSION_VERSION],
[macro_version='2.4.6'
macro_revision='2.4.6'
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
_LT_DECL(, macro_revision, 0)
])

99
m4/lt~obsolete.m4 vendored Normal file
View File

@ -0,0 +1,99 @@
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
#
# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
# Foundation, Inc.
# Written by Scott James Remnant, 2004.
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 5 lt~obsolete.m4
# These exist entirely to fool aclocal when bootstrapping libtool.
#
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
# which have later been changed to m4_define as they aren't part of the
# exported API, or moved to Autoconf or Automake where they belong.
#
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
# using a macro with the same name in our local m4/libtool.m4 it'll
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
# and doesn't know about Autoconf macros at all.)
#
# So we provide this file, which has a silly filename so it's always
# included after everything else. This provides aclocal with the
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
# because those macros already exist, or will be overwritten later.
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
#
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
# Yes, that means every name once taken will need to remain here until
# we give up compatibility with versions before 1.7, at which point
# we need to keep only those names which we still refer to.
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])

View File

@ -28,6 +28,11 @@ HB_BASE_sources = \
hb-ot-color-cbdt-table.hh \
hb-ot-cmap-table.hh \
hb-ot-glyf-table.hh \
hb-ot-cff1-table.hh \
hb-ot-cff1-table.cc \
hb-ot-cff2-table.hh \
hb-ot-cff2-table.cc \
hb-ot-vorg-table.hh \
hb-ot-hdmx-table.hh \
hb-ot-head-table.hh \
hb-ot-hhea-table.hh \
@ -164,6 +169,12 @@ HB_OT_sources = \
hb-ot-var-fvar-table.hh \
hb-ot-var-hvar-table.hh \
hb-ot-var-mvar-table.hh \
hb-ot-cff-common.hh \
hb-cff-interp-common.hh \
hb-cff-interp-cs-common.hh \
hb-cff1-interp-cs.hh \
hb-cff2-interp-cs.hh \
hb-cff-interp-dict-common.hh \
hb-ot-vorg-table.hh \
$(NULL)
@ -230,6 +241,11 @@ HB_SUBSET_sources = \
hb-subset.hh \
hb-subset-glyf.cc \
hb-subset-glyf.hh \
hb-subset-cff1.cc \
hb-subset-cff2.cc \
hb-subset-cff-common.cc \
hb-ot-cff1-table.cc \
hb-ot-cff2-table.cc \
hb-subset-input.cc \
hb-subset-input.hh \
hb-subset-plan.cc \
@ -238,6 +254,12 @@ HB_SUBSET_sources = \
HB_SUBSET_headers = \
hb-subset.h \
hb-subset-glyf.hh \
hb-subset-cff1.hh \
hb-subset-cff2.hh \
hb-subset-cff-common.hh \
hb-subset-plan.hh \
hb-subset.hh \
$(NULL)
HB_GOBJECT_DIST_sources = hb-gobject-structs.cc

812
src/hb-cff-interp-common.hh Normal file
View File

@ -0,0 +1,812 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_CFF_INTERP_COMMON_HH
#define HB_CFF_INTERP_COMMON_HH
namespace CFF {
using namespace OT;
typedef unsigned int OpCode;
/* === Dict operators === */
/* One byte operators (0-31) */
#define OpCode_version 0 /* CFF Top */
#define OpCode_Notice 1 /* CFF Top */
#define OpCode_FullName 2 /* CFF Top */
#define OpCode_FamilyName 3 /* CFF Top */
#define OpCode_Weight 4 /* CFF Top */
#define OpCode_FontBBox 5 /* CFF Top */
#define OpCode_BlueValues 6 /* CFF Private, CFF2 Private */
#define OpCode_OtherBlues 7 /* CFF Private, CFF2 Private */
#define OpCode_FamilyBlues 8 /* CFF Private, CFF2 Private */
#define OpCode_FamilyOtherBlues 9 /* CFF Private, CFF2 Private */
#define OpCode_StdHW 10 /* CFF Private, CFF2 Private */
#define OpCode_StdVW 11 /* CFF Private, CFF2 Private */
#define OpCode_escape 12 /* All. Shared with CS */
#define OpCode_UniqueID 13 /* CFF Top */
#define OpCode_XUID 14 /* CFF Top */
#define OpCode_charset 15 /* CFF Top (0) */
#define OpCode_Encoding 16 /* CFF Top (0) */
#define OpCode_CharStrings 17 /* CFF Top, CFF2 Top */
#define OpCode_Private 18 /* CFF Top, CFF2 FD */
#define OpCode_Subrs 19 /* CFF Private, CFF2 Private */
#define OpCode_defaultWidthX 20 /* CFF Private (0) */
#define OpCode_nominalWidthX 21 /* CFF Private (0) */
#define OpCode_vsindexdict 22 /* CFF2 Private/CS */
#define OpCode_blenddict 23 /* CFF2 Private/CS */
#define OpCode_vstore 24 /* CFF2 Top */
#define OpCode_reserved25 25
#define OpCode_reserved26 26
#define OpCode_reserved27 27
/* Numbers */
#define OpCode_shortint 28 /* 16-bit integer, All */
#define OpCode_longintdict 29 /* 32-bit integer, All */
#define OpCode_BCD 30 /* real number, CFF2 Top/FD */
#define OpCode_reserved31 31
/* 1-byte integers */
#define OpCode_OneByteIntFirst 32 /* All. beginning of the range of first byte ints */
#define OpCode_OneByteIntLast 246 /* All. ending of the range of first byte int */
/* 2-byte integers */
#define OpCode_TwoBytePosInt0 247 /* All. first byte of two byte positive int (+108 to +1131) */
#define OpCode_TwoBytePosInt1 248
#define OpCode_TwoBytePosInt2 249
#define OpCode_TwoBytePosInt3 250
#define OpCode_TwoByteNegInt0 251 /* All. first byte of two byte negative int (-1131 to -108) */
#define OpCode_TwoByteNegInt1 252
#define OpCode_TwoByteNegInt2 253
#define OpCode_TwoByteNegInt3 254
/* Two byte escape operators 12, (0-41) */
#define OpCode_ESC_Base 256
#define Make_OpCode_ESC(byte2) ((OpCode)(OpCode_ESC_Base + (byte2)))
inline OpCode Unmake_OpCode_ESC (OpCode op) { return (OpCode)(op - OpCode_ESC_Base); }
inline bool Is_OpCode_ESC (OpCode op) { return op >= OpCode_ESC_Base; }
inline unsigned int OpCode_Size (OpCode op) { return Is_OpCode_ESC (op)? 2: 1; }
#define OpCode_Copyright Make_OpCode_ESC(0) /* CFF Top */
#define OpCode_isFixedPitch Make_OpCode_ESC(1) /* CFF Top (false) */
#define OpCode_ItalicAngle Make_OpCode_ESC(2) /* CFF Top (0) */
#define OpCode_UnderlinePosition Make_OpCode_ESC(3) /* CFF Top (-100) */
#define OpCode_UnderlineThickness Make_OpCode_ESC(4) /* CFF Top (50) */
#define OpCode_PaintType Make_OpCode_ESC(5) /* CFF Top (0) */
#define OpCode_CharstringType Make_OpCode_ESC(6) /* CFF Top (2) */
#define OpCode_FontMatrix Make_OpCode_ESC(7) /* CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/
#define OpCode_StrokeWidth Make_OpCode_ESC(8) /* CFF Top (0) */
#define OpCode_BlueScale Make_OpCode_ESC(9) /* CFF Private, CFF2 Private (0.039625) */
#define OpCode_BlueShift Make_OpCode_ESC(10) /* CFF Private, CFF2 Private (7) */
#define OpCode_BlueFuzz Make_OpCode_ESC(11) /* CFF Private, CFF2 Private (1) */
#define OpCode_StemSnapH Make_OpCode_ESC(12) /* CFF Private, CFF2 Private */
#define OpCode_StemSnapV Make_OpCode_ESC(13) /* CFF Private, CFF2 Private */
#define OpCode_ForceBold Make_OpCode_ESC(14) /* CFF Private (false) */
#define OpCode_reservedESC15 Make_OpCode_ESC(15)
#define OpCode_reservedESC16 Make_OpCode_ESC(16)
#define OpCode_LanguageGroup Make_OpCode_ESC(17) /* CFF Private, CFF2 Private (0) */
#define OpCode_ExpansionFactor Make_OpCode_ESC(18) /* CFF Private, CFF2 Private (0.06) */
#define OpCode_initialRandomSeed Make_OpCode_ESC(19) /* CFF Private (0) */
#define OpCode_SyntheticBase Make_OpCode_ESC(20) /* CFF Top */
#define OpCode_PostScript Make_OpCode_ESC(21) /* CFF Top */
#define OpCode_BaseFontName Make_OpCode_ESC(22) /* CFF Top */
#define OpCode_BaseFontBlend Make_OpCode_ESC(23) /* CFF Top */
#define OpCode_reservedESC24 Make_OpCode_ESC(24)
#define OpCode_reservedESC25 Make_OpCode_ESC(25)
#define OpCode_reservedESC26 Make_OpCode_ESC(26)
#define OpCode_reservedESC27 Make_OpCode_ESC(27)
#define OpCode_reservedESC28 Make_OpCode_ESC(28)
#define OpCode_reservedESC29 Make_OpCode_ESC(29)
#define OpCode_ROS Make_OpCode_ESC(30) /* CFF Top_CID */
#define OpCode_CIDFontVersion Make_OpCode_ESC(31) /* CFF Top_CID (0) */
#define OpCode_CIDFontRevision Make_OpCode_ESC(32) /* CFF Top_CID (0) */
#define OpCode_CIDFontType Make_OpCode_ESC(33) /* CFF Top_CID (0) */
#define OpCode_CIDCount Make_OpCode_ESC(34) /* CFF Top_CID (8720) */
#define OpCode_UIDBase Make_OpCode_ESC(35) /* CFF Top_CID */
#define OpCode_FDArray Make_OpCode_ESC(36) /* CFF Top_CID, CFF2 Top */
#define OpCode_FDSelect Make_OpCode_ESC(37) /* CFF Top_CID, CFF2 Top */
#define OpCode_FontName Make_OpCode_ESC(38) /* CFF Top_CID */
/* === CharString operators === */
#define OpCode_hstem 1 /* CFF, CFF2 */
#define OpCode_Reserved2 2
#define OpCode_vstem 3 /* CFF, CFF2 */
#define OpCode_vmoveto 4 /* CFF, CFF2 */
#define OpCode_rlineto 5 /* CFF, CFF2 */
#define OpCode_hlineto 6 /* CFF, CFF2 */
#define OpCode_vlineto 7 /* CFF, CFF2 */
#define OpCode_rrcurveto 8 /* CFF, CFF2 */
#define OpCode_Reserved9 9
#define OpCode_callsubr 10 /* CFF, CFF2 */
#define OpCode_return 11 /* CFF */
// #define OpCode_escape 12 /* CFF, CFF2 */
#define OpCode_Reserved13 13
#define OpCode_endchar 14 /* CFF */
#define OpCode_vsindexcs 15 /* CFF2 */
#define OpCode_blendcs 16 /* CFF2 */
#define OpCode_Reserved17 17
#define OpCode_hstemhm 18 /* CFF, CFF2 */
#define OpCode_hintmask 19 /* CFF, CFF2 */
#define OpCode_cntrmask 20 /* CFF, CFF2 */
#define OpCode_rmoveto 21 /* CFF, CFF2 */
#define OpCode_hmoveto 22 /* CFF, CFF2 */
#define OpCode_vstemhm 23 /* CFF, CFF2 */
#define OpCode_rcurveline 24 /* CFF, CFF2 */
#define OpCode_rlinecurve 25 /* CFF, CFF2 */
#define OpCode_vvcurveto 26 /* CFF, CFF2 */
#define OpCode_hhcurveto 27 /* CFF, CFF2 */
// #define OpCode_shortint 28 /* CFF, CFF2 */
#define OpCode_callgsubr 29 /* CFF, CFF2 */
#define OpCode_vhcurveto 30 /* CFF, CFF2 */
#define OpCode_hvcurveto 31 /* CFF, CFF2 */
#define OpCode_fixedcs 255 /* 32-bit fixed */
/* Two byte escape operators 12, (0-41) */
#define OpCode_dotsection Make_OpCode_ESC(0) /* CFF (obsoleted) */
#define OpCode_ReservedESC1 Make_OpCode_ESC(1)
#define OpCode_ReservedESC2 Make_OpCode_ESC(2)
#define OpCode_and Make_OpCode_ESC(3) /* CFF */
#define OpCode_or Make_OpCode_ESC(4) /* CFF */
#define OpCode_not Make_OpCode_ESC(5) /* CFF */
#define OpCode_ReservedESC6 Make_OpCode_ESC(6)
#define OpCode_ReservedESC7 Make_OpCode_ESC(7)
#define OpCode_ReservedESC8 Make_OpCode_ESC(8)
#define OpCode_abs Make_OpCode_ESC(9) /* CFF */
#define OpCode_add Make_OpCode_ESC(10) /* CFF */
#define OpCode_sub Make_OpCode_ESC(11) /* CFF */
#define OpCode_div Make_OpCode_ESC(12) /* CFF */
#define OpCode_ReservedESC13 Make_OpCode_ESC(13)
#define OpCode_neg Make_OpCode_ESC(14) /* CFF */
#define OpCode_eq Make_OpCode_ESC(15) /* CFF */
#define OpCode_ReservedESC16 Make_OpCode_ESC(16)
#define OpCode_ReservedESC17 Make_OpCode_ESC(17)
#define OpCode_drop Make_OpCode_ESC(18) /* CFF */
#define OpCode_ReservedESC19 Make_OpCode_ESC(19)
#define OpCode_put Make_OpCode_ESC(20) /* CFF */
#define OpCode_get Make_OpCode_ESC(21) /* CFF */
#define OpCode_ifelse Make_OpCode_ESC(22) /* CFF */
#define OpCode_random Make_OpCode_ESC(23) /* CFF */
#define OpCode_mul Make_OpCode_ESC(24) /* CFF */
// #define OpCode_reservedESC25 Make_OpCode_ESC(25)
#define OpCode_sqrt Make_OpCode_ESC(26) /* CFF */
#define OpCode_dup Make_OpCode_ESC(27) /* CFF */
#define OpCode_exch Make_OpCode_ESC(28) /* CFF */
#define OpCode_index Make_OpCode_ESC(29) /* CFF */
#define OpCode_roll Make_OpCode_ESC(30) /* CFF */
#define OpCode_reservedESC31 Make_OpCode_ESC(31)
#define OpCode_reservedESC32 Make_OpCode_ESC(32)
#define OpCode_reservedESC33 Make_OpCode_ESC(33)
#define OpCode_hflex Make_OpCode_ESC(34) /* CFF, CFF2 */
#define OpCode_flex Make_OpCode_ESC(35) /* CFF, CFF2 */
#define OpCode_hflex1 Make_OpCode_ESC(36) /* CFF, CFF2 */
#define OpCode_flex1 Make_OpCode_ESC(37) /* CFF, CFF2 */
#define OpCode_Invalid 65535
struct Number
{
inline void init (void)
{ set_int (0); }
inline void fini (void)
{}
inline void set_int (int v) { format = NumInt; u.int_val = v; }
inline int to_int (void) const { return is_int ()? u.int_val: (int)to_real (); }
inline void set_fixed (int32_t v) { format = NumFixed; u.fixed_val = v; }
inline int32_t to_fixed (void) const
{
if (is_fixed ())
return u.fixed_val;
else if (is_real ())
return (int32_t)(u.real_val * 65536.0f);
else
return (int32_t)(u.int_val << 16);
}
inline void set_real (float v) { format = NumReal; u.real_val = v; }
inline float to_real (void) const
{
if (is_real ())
return u.real_val;
if (is_fixed ())
return u.fixed_val / 65536.0f;
else
return (float)u.int_val;
}
inline int ceil (void) const
{
switch (format)
{
default:
case NumInt:
return u.int_val;
case NumFixed:
return (u.fixed_val + 0xFFFF) >> 16;
case NumReal:
return (int)ceilf (u.real_val);
}
}
inline int floor (void) const
{
switch (format)
{
default:
case NumInt:
return u.int_val;
case NumFixed:
return u.fixed_val >> 16;
case NumReal:
return (int)floorf (u.real_val);
}
}
inline bool in_int_range (void) const
{
if (is_int ())
return true;
if (is_fixed () && ((u.fixed_val & 0xFFFF) == 0))
return true;
else
return ((float)(int16_t)to_int () == u.real_val);
}
inline bool operator > (const Number &n) const
{
switch (format)
{
default:
case NumInt: return u.int_val > n.to_int ();
case NumFixed: return u.fixed_val > n.to_fixed ();
case NumReal: return u.real_val > n.to_real ();
}
}
inline bool operator < (const Number &n) const
{ return n > *this; }
inline bool operator >= (const Number &n) const
{ return ! (*this < n); }
inline bool operator <= (const Number &n) const
{ return ! (*this > n); }
inline const Number &operator += (const Number &n)
{
if (format == NumReal || n.format == NumReal)
set_real (to_real () + n.to_real ());
else if (format == NumFixed || n.format == NumFixed)
set_fixed (to_fixed () + n.to_fixed ());
else
set_int (to_int () + n.to_int ());
return *this;
}
protected:
enum NumFormat {
NumInt,
NumFixed,
NumReal
};
NumFormat format;
union {
int int_val;
int32_t fixed_val;
float real_val;
} u;
inline bool is_int (void) const { return format == NumInt; }
inline bool is_fixed (void) const { return format == NumFixed; }
inline bool is_real (void) const { return format == NumReal; }
};
/* byte string */
struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
{
// encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
template <typename INTTYPE, int minVal, int maxVal>
inline static bool serialize_int (hb_serialize_context_t *c, OpCode intOp, int value)
{
TRACE_SERIALIZE (this);
if (unlikely ((value < minVal || value > maxVal)))
return_trace (false);
HBUINT8 *p = c->allocate_size<HBUINT8> (1);
if (unlikely (p == nullptr)) return_trace (false);
p->set (intOp);
INTTYPE *ip = c->allocate_size<INTTYPE> (INTTYPE::static_size);
if (unlikely (ip == nullptr)) return_trace (false);
ip->set ((unsigned int)value);
return_trace (true);
}
inline static bool serialize_int4 (hb_serialize_context_t *c, int value)
{ return serialize_int<HBUINT32, 0, 0x7FFFFFFF> (c, OpCode_longintdict, value); }
inline static bool serialize_int2 (hb_serialize_context_t *c, int value)
{ return serialize_int<HBUINT16, 0, 0x7FFF> (c, OpCode_shortint, value); }
/* Defining null_size allows a Null object may be created. Should be safe because:
* A descendent struct Dict uses a Null pointer to indicate a missing table,
* checked before access.
* ByteStr, a wrapper struct pairing a byte pointer along with its length, always
* checks the length before access. A Null pointer is used as the initial pointer
* along with zero length by the default ctor.
*/
DEFINE_SIZE_MIN(0);
};
struct ByteStr
{
inline ByteStr (void)
: str (&Null(UnsizedByteStr)), len (0) {}
inline ByteStr (const UnsizedByteStr& s, unsigned int l)
: str (&s), len (l) {}
inline ByteStr (const char *s, unsigned int l=0)
: str ((const UnsizedByteStr *)s), len (l) {}
/* sub-string */
inline ByteStr (const ByteStr &bs, unsigned int offset, unsigned int len_)
{
str = (const UnsizedByteStr *)&bs.str[offset];
len = len_;
}
inline bool sanitize (hb_sanitize_context_t *c) const { return str->sanitize (c, len); }
inline const HBUINT8& operator [] (unsigned int i) const {
assert (str && (i < len));
return (*str)[i];
}
inline bool serialize (hb_serialize_context_t *c, const ByteStr &src)
{
TRACE_SERIALIZE (this);
HBUINT8 *dest = c->allocate_size<HBUINT8> (src.len);
if (unlikely (dest == nullptr))
return_trace (false);
memcpy (dest, src.str, src.len);
return_trace (true);
}
inline unsigned int get_size (void) const { return len; }
inline bool check_limit (unsigned int offset, unsigned int count) const
{ return (offset + count <= len); }
const UnsizedByteStr *str;
unsigned int len;
};
struct SubByteStr
{
inline SubByteStr (void)
{ init (); }
inline void init (void)
{
str = ByteStr (0);
offset = 0;
error = false;
}
inline void fini (void) {}
inline SubByteStr (const ByteStr &str_, unsigned int offset_ = 0)
: str (str_), offset (offset_), error (false) {}
inline void reset (const ByteStr &str_, unsigned int offset_ = 0)
{
str = str_;
offset = offset_;
error = false;
}
inline const HBUINT8& operator [] (int i) {
if (unlikely ((unsigned int)(offset + i) >= str.len))
{
set_error ();
return Null(HBUINT8);
}
else
return str[offset + i];
}
inline operator ByteStr (void) const { return ByteStr (str, offset, str.len - offset); }
inline bool avail (unsigned int count=1) const { return str.check_limit (offset, count); }
inline void inc (unsigned int count=1) { offset += count; assert (count <= str.len); }
inline void set_error (void) { error = true; }
inline bool in_error (void) const { return error; }
ByteStr str;
unsigned int offset; /* beginning of the sub-string within str */
protected:
bool error;
};
typedef hb_vector_t<ByteStr> ByteStrArray;
/* stack */
template <typename ELEM, int LIMIT>
struct Stack
{
inline void init (void)
{
error = false;
count = 0;
elements.init ();
elements.resize (kSizeLimit);
for (unsigned int i = 0; i < elements.len; i++)
elements[i].init ();
}
inline void fini (void)
{
elements.fini_deep ();
}
inline ELEM& operator [] (unsigned int i)
{
if (unlikely (i >= count)) set_error ();
return elements[i];
}
inline void push (const ELEM &v)
{
if (likely (count < elements.len))
elements[count++] = v;
else
set_error ();
}
inline ELEM &push (void)
{
if (likely (count < elements.len))
return elements[count++];
else
{
set_error ();
return Crap(ELEM);
}
}
inline ELEM& pop (void)
{
if (likely (count > 0))
return elements[--count];
else
{
set_error ();
return Crap(ELEM);
}
}
inline void pop (unsigned int n)
{
if (likely (count >= n))
count -= n;
else
set_error ();
}
inline const ELEM& peek (void)
{
if (likely (count > 0))
return elements[count-1];
else
{
set_error ();
return Null(ELEM);
}
}
inline void unpop (void)
{
if (likely (count < elements.len))
count++;
else
set_error ();
}
inline void clear (void) { count = 0; }
inline bool in_error (void) const { return (error || elements.in_error ()); }
inline void set_error (void) { error = true; }
inline unsigned int get_count (void) const { return count; }
inline bool is_empty (void) const { return count == 0; }
static const unsigned int kSizeLimit = LIMIT;
protected:
bool error;
unsigned int count;
hb_vector_t<ELEM, kSizeLimit> elements;
};
/* argument stack */
template <typename ARG=Number>
struct ArgStack : Stack<ARG, 513>
{
inline void push_int (int v)
{
ARG &n = S::push ();
n.set_int (v);
}
inline void push_fixed (int32_t v)
{
ARG &n = S::push ();
n.set_fixed (v);
}
inline void push_real (float v)
{
ARG &n = S::push ();
n.set_real (v);
}
inline ARG& pop_num (void)
{
return this->pop ();
}
inline int pop_int (void)
{
return this->pop ().to_int ();
}
inline unsigned int pop_uint (void)
{
int i = pop_int ();
if (unlikely (i < 0))
{
i = 0;
S::set_error ();
}
return (unsigned)i;
}
inline void push_longint_from_substr (SubByteStr& substr)
{
push_int ((substr[0] << 24) | (substr[1] << 16) | (substr[2] << 8) | (substr[3]));
substr.inc (4);
}
inline bool push_fixed_from_substr (SubByteStr& substr)
{
if (unlikely (!substr.avail (4)))
return false;
push_fixed ((int32_t)*(const HBUINT32*)&substr[0]);
substr.inc (4);
return true;
}
private:
typedef Stack<ARG, 513> S;
};
/* an operator prefixed by its operands in a byte string */
struct OpStr
{
inline void init (void) {}
inline void fini (void) {}
OpCode op;
ByteStr str;
};
/* base of OP_SERIALIZER */
struct OpSerializer
{
protected:
inline bool copy_opstr (hb_serialize_context_t *c, const OpStr& opstr) const
{
TRACE_SERIALIZE (this);
HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
if (unlikely (d == nullptr)) return_trace (false);
memcpy (d, &opstr.str.str[0], opstr.str.len);
return_trace (true);
}
};
template <typename VAL>
struct ParsedValues
{
inline void init (void)
{
opStart = 0;
values.init ();
}
inline void fini (void)
{
values.fini_deep ();
}
inline void add_op (OpCode op, const SubByteStr& substr = SubByteStr ())
{
VAL *val = values.push ();
val->op = op;
assert (substr.offset >= opStart);
val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
opStart = substr.offset;
}
inline void add_op (OpCode op, const SubByteStr& substr, const VAL &v)
{
VAL *val = values.push (v);
val->op = op;
assert (substr.offset >= opStart);
val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
opStart = substr.offset;
}
inline bool has_op (OpCode op) const
{
for (unsigned int i = 0; i < get_count (); i++)
if (get_value (i).op == op) return true;
return false;
}
inline unsigned get_count (void) const { return values.len; }
inline const VAL &get_value (unsigned int i) const { return values[i]; }
inline const VAL &operator [] (unsigned int i) const { return get_value (i); }
unsigned int opStart;
hb_vector_t<VAL> values;
};
template <typename ARG=Number>
struct InterpEnv
{
inline void init (const ByteStr &str_)
{
substr.reset (str_);
argStack.init ();
error = false;
}
inline void fini (void)
{
argStack.fini ();
}
inline bool in_error (void) const
{
return error || substr.in_error () || argStack.in_error ();
}
inline void set_error (void) { error = true; }
inline OpCode fetch_op (void)
{
OpCode op = OpCode_Invalid;
if (unlikely (!substr.avail ()))
return OpCode_Invalid;
op = (OpCode)(unsigned char)substr[0];
if (op == OpCode_escape) {
if (unlikely (!substr.avail ()))
return OpCode_Invalid;
op = Make_OpCode_ESC(substr[1]);
substr.inc ();
}
substr.inc ();
return op;
}
inline const ARG& eval_arg (unsigned int i)
{
return argStack[i];
}
inline ARG& pop_arg (void)
{
return argStack.pop ();
}
inline void pop_n_args (unsigned int n)
{
assert (n <= argStack.get_count ());
argStack.pop (n);
}
inline void clear_args (void)
{
pop_n_args (argStack.get_count ());
}
SubByteStr substr;
ArgStack<ARG> argStack;
protected:
bool error;
};
typedef InterpEnv<> NumInterpEnv;
template <typename ARG=Number>
struct OpSet
{
static inline void process_op (OpCode op, InterpEnv<ARG>& env)
{
switch (op) {
case OpCode_shortint:
env.argStack.push_int ((int16_t)((env.substr[0] << 8) | env.substr[1]));
env.substr.inc (2);
break;
case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.substr[0] + 108));
env.substr.inc ();
break;
case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.substr[0] - 108));
env.substr.inc ();
break;
default:
/* 1-byte integer */
if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast)))
{
env.argStack.push_int ((int)op - 139);
} else {
/* invalid unknown operator */
env.clear_args ();
env.set_error ();
}
break;
}
}
};
template <typename ENV>
struct Interpreter {
inline ~Interpreter(void) { fini (); }
inline void fini (void) { env.fini (); }
ENV env;
};
} /* namespace CFF */
#endif /* HB_CFF_INTERP_COMMON_HH */

View File

@ -0,0 +1,892 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_CFF_INTERP_CS_COMMON_HH
#define HB_CFF_INTERP_CS_COMMON_HH
#include "hb.hh"
#include "hb-cff-interp-common.hh"
namespace CFF {
using namespace OT;
enum CSType {
CSType_CharString,
CSType_GlobalSubr,
CSType_LocalSubr
};
struct CallContext
{
inline void init (const SubByteStr substr_=SubByteStr (), CSType type_=CSType_CharString, unsigned int subr_num_=0)
{
substr = substr_;
type = type_;
subr_num = subr_num_;
}
inline void fini (void) {}
SubByteStr substr;
CSType type;
unsigned int subr_num;
};
/* call stack */
const unsigned int kMaxCallLimit = 10;
struct CallStack : Stack<CallContext, kMaxCallLimit> {};
template <typename SUBRS>
struct BiasedSubrs
{
inline void init (const SUBRS &subrs_)
{
subrs = &subrs_;
unsigned int nSubrs = subrs_.count;
if (nSubrs < 1240)
bias = 107;
else if (nSubrs < 33900)
bias = 1131;
else
bias = 32768;
}
inline void fini (void) {}
const SUBRS *subrs;
unsigned int bias;
};
struct Point
{
inline void init (void)
{
x.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_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 Point &d) { move_x (d.x); move_y (d.y); }
Number x;
Number y;
};
template <typename ARG, typename SUBRS>
struct CSInterpEnv : InterpEnv<ARG>
{
inline void init (const ByteStr &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_)
{
InterpEnv<ARG>::init (str);
context.init (str, CSType_CharString);
seen_moveto = true;
seen_hintmask = false;
hstem_count = 0;
vstem_count = 0;
pt.init ();
callStack.init ();
globalSubrs.init (globalSubrs_);
localSubrs.init (localSubrs_);
}
inline void fini (void)
{
InterpEnv<ARG>::fini ();
callStack.fini ();
globalSubrs.fini ();
localSubrs.fini ();
}
inline bool in_error (void) const
{
return callStack.in_error () || SUPER::in_error ();
}
inline bool popSubrNum (const BiasedSubrs<SUBRS>& biasedSubrs, unsigned int &subr_num)
{
int n = SUPER::argStack.pop_int ();
n += biasedSubrs.bias;
if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.subrs->count)))
return false;
subr_num = (unsigned int)n;
return true;
}
inline void callSubr (const BiasedSubrs<SUBRS>& biasedSubrs, CSType type)
{
unsigned int subr_num;
if (unlikely (!popSubrNum (biasedSubrs, subr_num)
|| callStack.get_count () >= kMaxCallLimit))
{
SUPER::set_error ();
return;
}
context.substr = SUPER::substr;
callStack.push (context);
context.init ( (*biasedSubrs.subrs)[subr_num], type, subr_num);
SUPER::substr = context.substr;
}
inline void returnFromSubr (void)
{
if (unlikely (SUPER::substr.in_error ()))
SUPER::set_error ();
context = callStack.pop ();
SUPER::substr = context.substr;
}
inline void determine_hintmask_size (void)
{
if (!seen_hintmask)
{
vstem_count += SUPER::argStack.get_count() / 2;
hintmask_size = (hstem_count + vstem_count + 7) >> 3;
seen_hintmask = true;
}
}
inline void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
inline bool is_endchar (void) const { return endchar_flag; }
inline const Number &get_x (void) const { return pt.x; }
inline const Number &get_y (void) const { return pt.y; }
inline const Point &get_pt (void) const { return pt; }
inline void moveto (const Point &pt_ ) { pt = pt_; }
public:
CallContext context;
bool endchar_flag;
bool seen_moveto;
bool seen_hintmask;
unsigned int hstem_count;
unsigned int vstem_count;
unsigned int hintmask_size;
CallStack callStack;
BiasedSubrs<SUBRS> globalSubrs;
BiasedSubrs<SUBRS> localSubrs;
private:
Point pt;
typedef InterpEnv<ARG> SUPER;
};
template <typename ENV, typename PARAM>
struct PathProcsNull
{
static inline void rmoveto (ENV &env, PARAM& param) {}
static inline void hmoveto (ENV &env, PARAM& param) {}
static inline void vmoveto (ENV &env, PARAM& param) {}
static inline void rlineto (ENV &env, PARAM& param) {}
static inline void hlineto (ENV &env, PARAM& param) {}
static inline void vlineto (ENV &env, PARAM& param) {}
static inline void rrcurveto (ENV &env, PARAM& param) {}
static inline void rcurveline (ENV &env, PARAM& param) {}
static inline void rlinecurve (ENV &env, PARAM& param) {}
static inline void vvcurveto (ENV &env, PARAM& param) {}
static inline void hhcurveto (ENV &env, PARAM& param) {}
static inline void vhcurveto (ENV &env, PARAM& param) {}
static inline void hvcurveto (ENV &env, PARAM& param) {}
static inline void moveto (ENV &env, PARAM& param, const Point &pt) {}
static inline void line (ENV &env, PARAM& param, const Point &pt1) {}
static inline void curve (ENV &env, PARAM& param, const Point &pt1, const Point &pt2, const Point &pt3) {}
static inline void hflex (ENV &env, PARAM& param) {}
static inline void flex (ENV &env, PARAM& param) {}
static inline void hflex1 (ENV &env, PARAM& param) {}
static inline void flex1 (ENV &env, PARAM& param) {}
};
template <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=PathProcsNull<ENV, PARAM> >
struct CSOpSet : OpSet<ARG>
{
static inline void process_op (OpCode op, ENV &env, PARAM& param)
{
switch (op) {
case OpCode_return:
env.returnFromSubr ();
break;
case OpCode_endchar:
OPSET::check_width (op, env, param);
env.set_endchar (true);
OPSET::flush_args_and_op (op, env, param);
break;
case OpCode_fixedcs:
env.argStack.push_fixed_from_substr (env.substr);
break;
case OpCode_callsubr:
env.callSubr (env.localSubrs, CSType_LocalSubr);
break;
case OpCode_callgsubr:
env.callSubr (env.globalSubrs, CSType_GlobalSubr);
break;
case OpCode_hstem:
case OpCode_hstemhm:
OPSET::check_width (op, env, param);
OPSET::process_hstem (op, env, param);
break;
case OpCode_vstem:
case OpCode_vstemhm:
OPSET::check_width (op, env, param);
OPSET::process_vstem (op, env, param);
break;
case OpCode_hintmask:
case OpCode_cntrmask:
OPSET::check_width (op, env, param);
OPSET::process_hintmask (op, env, param);
break;
case OpCode_rmoveto:
OPSET::check_width (op, env, param);
PATH::rmoveto (env, param);
OPSET::process_post_move (op, env, param);
break;
case OpCode_hmoveto:
OPSET::check_width (op, env, param);
PATH::hmoveto (env, param);
OPSET::process_post_move (op, env, param);
break;
case OpCode_vmoveto:
OPSET::check_width (op, env, param);
PATH::vmoveto (env, param);
OPSET::process_post_move (op, env, param);
break;
case OpCode_rlineto:
PATH::rlineto (env, param);
process_post_path (op, env, param);
break;
case OpCode_hlineto:
PATH::hlineto (env, param);
process_post_path (op, env, param);
break;
case OpCode_vlineto:
PATH::vlineto (env, param);
process_post_path (op, env, param);
break;
case OpCode_rrcurveto:
PATH::rrcurveto (env, param);
process_post_path (op, env, param);
break;
case OpCode_rcurveline:
PATH::rcurveline (env, param);
process_post_path (op, env, param);
break;
case OpCode_rlinecurve:
PATH::rlinecurve (env, param);
process_post_path (op, env, param);
break;
case OpCode_vvcurveto:
PATH::vvcurveto (env, param);
process_post_path (op, env, param);
break;
case OpCode_hhcurveto:
PATH::hhcurveto (env, param);
process_post_path (op, env, param);
break;
case OpCode_vhcurveto:
PATH::vhcurveto (env, param);
process_post_path (op, env, param);
break;
case OpCode_hvcurveto:
PATH::hvcurveto (env, param);
process_post_path (op, env, param);
break;
case OpCode_hflex:
PATH::hflex (env, param);
OPSET::process_post_flex (op, env, param);
break;
case OpCode_flex:
PATH::flex (env, param);
OPSET::process_post_flex (op, env, param);
break;
case OpCode_hflex1:
PATH::hflex1 (env, param);
OPSET::process_post_flex (op, env, param);
break;
case OpCode_flex1:
PATH::flex1 (env, param);
OPSET::process_post_flex (op, env, param);
break;
default:
SUPER::process_op (op, env);
break;
}
}
static inline void process_hstem (OpCode op, ENV &env, PARAM& param)
{
env.hstem_count += env.argStack.get_count () / 2;
OPSET::flush_args_and_op (op, env, param);
}
static inline void process_vstem (OpCode op, ENV &env, PARAM& param)
{
env.vstem_count += env.argStack.get_count () / 2;
OPSET::flush_args_and_op (op, env, param);
}
static inline void process_hintmask (OpCode op, ENV &env, PARAM& param)
{
env.determine_hintmask_size ();
if (likely (env.substr.avail (env.hintmask_size)))
{
OPSET::flush_hintmask (op, env, param);
env.substr.inc (env.hintmask_size);
}
}
static inline void process_post_flex (OpCode op, ENV &env, PARAM& param)
{
OPSET::flush_args_and_op (op, env, param);
}
static inline void check_width (OpCode op, ENV &env, PARAM& param)
{}
static inline void process_post_move (OpCode op, ENV &env, PARAM& param)
{
if (!env.seen_moveto)
{
env.determine_hintmask_size ();
env.seen_moveto = true;
}
OPSET::flush_args_and_op (op, env, param);
}
static inline void process_post_path (OpCode op, ENV &env, PARAM& param)
{
OPSET::flush_args_and_op (op, env, param);
}
static inline void flush_args_and_op (OpCode op, ENV &env, PARAM& param)
{
OPSET::flush_args (env, param);
OPSET::flush_op (op, env, param);
}
static inline void flush_args (ENV &env, PARAM& param)
{
env.pop_n_args (env.argStack.get_count ());
}
static inline void flush_op (OpCode op, ENV &env, PARAM& param)
{
}
static inline void flush_hintmask (OpCode op, ENV &env, PARAM& param)
{
OPSET::flush_args_and_op (op, env, param);
}
static inline bool is_number_op (OpCode op)
{
switch (op)
{
case OpCode_shortint:
case OpCode_fixedcs:
case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
return true;
default:
/* 1-byte integer */
return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast);
}
}
protected:
typedef OpSet<ARG> SUPER;
};
template <typename PATH, typename ENV, typename PARAM>
struct PathProcs
{
static inline void rmoveto (ENV &env, PARAM& param)
{
Point pt1 = env.get_pt ();
const Number &dy = env.pop_arg ();
const Number &dx = env.pop_arg ();
pt1.move (dx, dy);
PATH::moveto (env, param, pt1);
}
static inline void hmoveto (ENV &env, PARAM& param)
{
Point pt1 = env.get_pt ();
pt1.move_x (env.pop_arg ());
PATH::moveto (env, param, pt1);
}
static inline void vmoveto (ENV &env, PARAM& param)
{
Point pt1 = env.get_pt ();
pt1.move_y (env.pop_arg ());
PATH::moveto (env, param, pt1);
}
static inline void rlineto (ENV &env, PARAM& param)
{
for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2)
{
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
PATH::line (env, param, pt1);
}
}
static inline void hlineto (ENV &env, PARAM& param)
{
Point pt1;
unsigned int i = 0;
for (; i + 2 <= env.argStack.get_count (); i += 2)
{
pt1 = env.get_pt ();
pt1.move_x (env.eval_arg (i));
PATH::line (env, param, pt1);
pt1.move_y (env.eval_arg (i+1));
PATH::line (env, param, pt1);
}
if (i < env.argStack.get_count ())
{
pt1 = env.get_pt ();
pt1.move_x (env.eval_arg (i));
PATH::line (env, param, pt1);
}
}
static inline void vlineto (ENV &env, PARAM& param)
{
Point pt1;
unsigned int i = 0;
for (; i + 2 <= env.argStack.get_count (); i += 2)
{
pt1 = env.get_pt ();
pt1.move_y (env.eval_arg (i));
PATH::line (env, param, pt1);
pt1.move_x (env.eval_arg (i+1));
PATH::line (env, param, pt1);
}
if (i < env.argStack.get_count ())
{
pt1 = env.get_pt ();
pt1.move_y (env.eval_arg (i));
PATH::line (env, param, pt1);
}
}
static inline void rrcurveto (ENV &env, PARAM& param)
{
for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6)
{
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
Point pt2 = pt1;
pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
Point pt3 = pt2;
pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
PATH::curve (env, param, pt1, pt2, pt3);
}
}
static inline void rcurveline (ENV &env, PARAM& param)
{
unsigned int i = 0;
for (; i + 6 <= env.argStack.get_count (); i += 6)
{
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
Point pt2 = pt1;
pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
Point pt3 = pt2;
pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
PATH::curve (env, param, pt1, pt2, pt3);
}
for (; i + 2 <= env.argStack.get_count (); i += 2)
{
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
PATH::line (env, param, pt1);
}
}
static inline void rlinecurve (ENV &env, PARAM& param)
{
unsigned int i = 0;
unsigned int line_limit = (env.argStack.get_count () % 6);
for (; i + 2 <= line_limit; i += 2)
{
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
PATH::line (env, param, pt1);
}
for (; i + 6 <= env.argStack.get_count (); i += 6)
{
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
Point pt2 = pt1;
pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
Point pt3 = pt2;
pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
PATH::curve (env, param, pt1, pt2, pt3);
}
}
static inline void vvcurveto (ENV &env, PARAM& param)
{
unsigned int i = 0;
Point pt1 = env.get_pt ();
if ((env.argStack.get_count () & 1) != 0)
pt1.move_x (env.eval_arg (i++));
for (; i + 4 <= env.argStack.get_count (); i += 4)
{
pt1.move_y (env.eval_arg (i));
Point pt2 = pt1;
pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
Point pt3 = pt2;
pt3.move_y (env.eval_arg (i+3));
PATH::curve (env, param, pt1, pt2, pt3);
pt1 = env.get_pt ();
}
}
static inline void hhcurveto (ENV &env, PARAM& param)
{
unsigned int i = 0;
Point pt1 = env.get_pt ();
if ((env.argStack.get_count () & 1) != 0)
pt1.move_y (env.eval_arg (i++));
for (; i + 4 <= env.argStack.get_count (); i += 4)
{
pt1.move_x (env.eval_arg (i));
Point pt2 = pt1;
pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
Point pt3 = pt2;
pt3.move_x (env.eval_arg (i+3));
PATH::curve (env, param, pt1, pt2, pt3);
pt1 = env.get_pt ();
}
}
static inline void vhcurveto (ENV &env, PARAM& param)
{
Point pt1, pt2, pt3;
unsigned int i = 0;
if ((env.argStack.get_count () % 8) >= 4)
{
Point pt1 = env.get_pt ();
pt1.move_y (env.eval_arg (i));
Point pt2 = pt1;
pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
Point pt3 = pt2;
pt3.move_x (env.eval_arg (i+3));
i += 4;
for (; i + 8 <= env.argStack.get_count (); i += 8)
{
PATH::curve (env, param, pt1, pt2, pt3);
pt1 = env.get_pt ();
pt1.move_x (env.eval_arg (i));
pt2 = pt1;
pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
pt3 = pt2;
pt3.move_y (env.eval_arg (i+3));
PATH::curve (env, param, pt1, pt2, pt3);
pt1 = pt3;
pt1.move_y (env.eval_arg (i+4));
pt2 = pt1;
pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
pt3 = pt2;
pt3.move_x (env.eval_arg (i+7));
}
if (i < env.argStack.get_count ())
pt3.move_y (env.eval_arg (i));
PATH::curve (env, param, pt1, pt2, pt3);
}
else
{
for (; i + 8 <= env.argStack.get_count (); i += 8)
{
pt1 = env.get_pt ();
pt1.move_y (env.eval_arg (i));
pt2 = pt1;
pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
pt3 = pt2;
pt3.move_x (env.eval_arg (i+3));
PATH::curve (env, param, pt1, pt2, pt3);
pt1 = pt3;
pt1.move_x (env.eval_arg (i+4));
pt2 = pt1;
pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
pt3 = pt2;
pt3.move_y (env.eval_arg (i+7));
if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
pt3.move_x (env.eval_arg (i+8));
PATH::curve (env, param, pt1, pt2, pt3);
}
}
}
static inline void hvcurveto (ENV &env, PARAM& param)
{
Point pt1, pt2, pt3;
unsigned int i = 0;
if ((env.argStack.get_count () % 8) >= 4)
{
Point pt1 = env.get_pt ();
pt1.move_x (env.eval_arg (i));
Point pt2 = pt1;
pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
Point pt3 = pt2;
pt3.move_y (env.eval_arg (i+3));
i += 4;
for (; i + 8 <= env.argStack.get_count (); i += 8)
{
PATH::curve (env, param, pt1, pt2, pt3);
pt1 = env.get_pt ();
pt1.move_y (env.eval_arg (i));
pt2 = pt1;
pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
pt3 = pt2;
pt3.move_x (env.eval_arg (i+3));
PATH::curve (env, param, pt1, pt2, pt3);
pt1 = pt3;
pt1.move_x (env.eval_arg (i+4));
pt2 = pt1;
pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
pt3 = pt2;
pt3.move_y (env.eval_arg (i+7));
}
if (i < env.argStack.get_count ())
pt3.move_x (env.eval_arg (i));
PATH::curve (env, param, pt1, pt2, pt3);
}
else
{
for (; i + 8 <= env.argStack.get_count (); i += 8)
{
pt1 = env.get_pt ();
pt1.move_x (env.eval_arg (i));
pt2 = pt1;
pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
pt3 = pt2;
pt3.move_y (env.eval_arg (i+3));
PATH::curve (env, param, pt1, pt2, pt3);
pt1 = pt3;
pt1.move_y (env.eval_arg (i+4));
pt2 = pt1;
pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
pt3 = pt2;
pt3.move_x (env.eval_arg (i+7));
if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
pt3.move_y (env.eval_arg (i+8));
PATH::curve (env, param, pt1, pt2, pt3);
}
}
}
/* default actions to be overridden */
static inline void moveto (ENV &env, PARAM& param, const Point &pt)
{ env.moveto (pt); }
static inline void line (ENV &env, PARAM& param, const Point &pt1)
{ PATH::moveto (env, param, pt1); }
static inline void curve (ENV &env, PARAM& param, const Point &pt1, const Point &pt2, const Point &pt3)
{ PATH::moveto (env, param, pt3); }
static inline void hflex (ENV &env, PARAM& param)
{
if (likely (env.argStack.get_count () == 7))
{
Point pt1 = env.get_pt ();
pt1.move_x (env.eval_arg (0));
Point pt2 = pt1;
pt2.move (env.eval_arg (1), env.eval_arg (2));
Point pt3 = pt2;
pt3.move_x (env.eval_arg (3));
Point pt4 = pt3;
pt4.move_x (env.eval_arg (4));
Point pt5 = pt4;
pt5.move_x (env.eval_arg (5));
pt5.y = pt1.y;
Point pt6 = pt5;
pt6.move_x (env.eval_arg (6));
curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
}
else
env.set_error ();
}
static inline void flex (ENV &env, PARAM& param)
{
if (likely (env.argStack.get_count () == 13))
{
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (0), env.eval_arg (1));
Point pt2 = pt1;
pt2.move (env.eval_arg (2), env.eval_arg (3));
Point pt3 = pt2;
pt3.move (env.eval_arg (4), env.eval_arg (5));
Point pt4 = pt3;
pt4.move (env.eval_arg (6), env.eval_arg (7));
Point pt5 = pt4;
pt5.move (env.eval_arg (8), env.eval_arg (9));
Point pt6 = pt5;
pt6.move (env.eval_arg (10), env.eval_arg (11));
curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
}
else
env.set_error ();
}
static inline void hflex1 (ENV &env, PARAM& param)
{
if (likely (env.argStack.get_count () == 9))
{
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (0), env.eval_arg (1));
Point pt2 = pt1;
pt2.move (env.eval_arg (2), env.eval_arg (3));
Point pt3 = pt2;
pt3.move_x (env.eval_arg (4));
Point pt4 = pt3;
pt4.move_x (env.eval_arg (5));
Point pt5 = pt4;
pt5.move (env.eval_arg (6), env.eval_arg (7));
Point pt6 = pt5;
pt6.move_x (env.eval_arg (8));
pt6.y = env.get_pt ().y;
curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
}
else
env.set_error ();
}
static inline void flex1 (ENV &env, PARAM& param)
{
if (likely (env.argStack.get_count () == 11))
{
Point d;
d.init ();
for (unsigned int i = 0; i < 10; i += 2)
d.move (env.eval_arg (i), env.eval_arg (i+1));
Point pt1 = env.get_pt ();
pt1.move (env.eval_arg (0), env.eval_arg (1));
Point pt2 = pt1;
pt2.move (env.eval_arg (2), env.eval_arg (3));
Point pt3 = pt2;
pt3.move (env.eval_arg (4), env.eval_arg (5));
Point pt4 = pt3;
pt4.move (env.eval_arg (6), env.eval_arg (7));
Point pt5 = pt4;
pt5.move (env.eval_arg (8), env.eval_arg (9));
Point pt6 = pt5;
if (fabs (d.x.to_real ()) > fabs (d.y.to_real ()))
{
pt6.move_x (env.eval_arg (10));
pt6.y = env.get_pt ().y;
}
else
{
pt6.x = env.get_pt ().x;
pt6.move_y (env.eval_arg (10));
}
curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
}
else
env.set_error ();
}
protected:
static inline void curve2 (ENV &env, PARAM& param,
const Point &pt1, const Point &pt2, const Point &pt3,
const Point &pt4, const Point &pt5, const Point &pt6)
{
PATH::curve (env, param, pt1, pt2, pt3);
PATH::curve (env, param, pt4, pt5, pt6);
}
};
template <typename ENV, typename OPSET, typename PARAM>
struct CSInterpreter : Interpreter<ENV>
{
inline bool interpret (PARAM& param)
{
SUPER::env.set_endchar (false);
for (;;) {
OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
if (unlikely (SUPER::env.in_error ()))
return false;
if (SUPER::env.is_endchar ())
break;
}
return true;
}
private:
typedef Interpreter<ENV> SUPER;
};
} /* namespace CFF */
#endif /* HB_CFF_INTERP_CS_COMMON_HH */

View File

@ -0,0 +1,281 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_CFF_INTERP_DICT_COMMON_HH
#define HB_CFF_INTERP_DICT_COMMON_HH
#include "hb-cff-interp-common.hh"
#include <math.h>
namespace CFF {
using namespace OT;
/* an opstr and the parsed out dict value(s) */
struct DictVal : OpStr
{
inline void init (void)
{
single_val.set_int (0);
}
inline void fini (void)
{
}
Number single_val;
};
typedef DictVal NumDictVal;
template <typename VAL> struct DictValues : ParsedValues<VAL> {};
template <typename OPSTR=OpStr>
struct TopDictValues : DictValues<OPSTR>
{
inline void init (void)
{
DictValues<OPSTR>::init ();
charStringsOffset = 0;
FDArrayOffset = 0;
}
inline void fini (void)
{
DictValues<OPSTR>::fini ();
}
inline unsigned int calculate_serialized_op_size (const OPSTR& opstr) const
{
switch (opstr.op)
{
case OpCode_CharStrings:
case OpCode_FDArray:
return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
default:
return opstr.str.len;
}
}
unsigned int charStringsOffset;
unsigned int FDArrayOffset;
};
struct DictOpSet : OpSet<Number>
{
static inline void process_op (OpCode op, InterpEnv<Number>& env)
{
switch (op) {
case OpCode_longintdict: /* 5-byte integer */
env.argStack.push_longint_from_substr (env.substr);
break;
case OpCode_BCD: /* real number */
env.argStack.push_real (parse_bcd (env.substr));
break;
default:
OpSet<Number>::process_op (op, env);
break;
}
}
static inline float parse_bcd (SubByteStr& substr)
{
float v = 0.0f;
bool neg = false;
double int_part = 0;
long frac_part = 0;
unsigned int frac_count = 0;
bool exp_neg = false;
unsigned int exp_part = 0;
enum Part { INT_PART=0, FRAC_PART, EXP_PART } part = INT_PART;
enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
double value = 0.0;
unsigned char byte = 0;
for (unsigned int i = 0;; i++)
{
char d;
if ((i & 1) == 0)
{
if (!substr.avail ())
{
substr.set_error ();
return 0.0f;
}
byte = substr[0];
substr.inc ();
d = byte >> 4;
}
else
d = byte & 0x0F;
switch (d)
{
case RESERVED:
substr.set_error ();
return v;
case END:
value = (double)(neg? -int_part: int_part);
if (frac_count > 0)
value += (frac_part / pow (10.0, (double)frac_count));
if (exp_part != 0)
{
if (exp_neg)
value /= pow (10.0, (double)exp_part);
else
value *= pow (10.0, (double)exp_part);
}
return (float)value;
case NEG:
if (i != 0)
{
substr.set_error ();
return 0.0f;
}
neg = true;
break;
case DECIMAL:
if (part != INT_PART)
{
substr.set_error ();
return v;
}
part = FRAC_PART;
break;
case EXP_NEG:
exp_neg = true;
HB_FALLTHROUGH;
case EXP_POS:
if (part == EXP_PART)
{
substr.set_error ();
return v;
}
part = EXP_PART;
break;
default:
switch (part) {
default:
case INT_PART:
int_part = (int_part * 10) + d;
break;
case FRAC_PART:
frac_part = (frac_part * 10) + d;
frac_count++;
break;
case EXP_PART:
exp_part = (exp_part * 10) + d;
break;
}
}
}
return v;
}
static inline bool is_hint_op (OpCode op)
{
switch (op)
{
case OpCode_BlueValues:
case OpCode_OtherBlues:
case OpCode_FamilyBlues:
case OpCode_FamilyOtherBlues:
case OpCode_StemSnapH:
case OpCode_StemSnapV:
case OpCode_StdHW:
case OpCode_StdVW:
case OpCode_BlueScale:
case OpCode_BlueShift:
case OpCode_BlueFuzz:
case OpCode_ForceBold:
case OpCode_LanguageGroup:
case OpCode_ExpansionFactor:
return true;
default:
return false;
}
}
};
template <typename VAL=OpStr>
struct TopDictOpSet : DictOpSet
{
static inline void process_op (OpCode op, InterpEnv<Number>& env, TopDictValues<VAL> & dictval)
{
switch (op) {
case OpCode_CharStrings:
dictval.charStringsOffset = env.argStack.pop_uint ();
env.clear_args ();
break;
case OpCode_FDArray:
dictval.FDArrayOffset = env.argStack.pop_uint ();
env.clear_args ();
break;
case OpCode_FontMatrix:
env.clear_args ();
break;
default:
DictOpSet::process_op (op, env);
break;
}
}
};
template <typename OPSET, typename PARAM, typename ENV=NumInterpEnv>
struct DictInterpreter : Interpreter<ENV>
{
inline bool interpret (PARAM& param)
{
param.init ();
while (SUPER::env.substr.avail ())
{
OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
if (unlikely (SUPER::env.in_error ()))
return false;
}
return true;
}
private:
typedef Interpreter<ENV> SUPER;
};
} /* namespace CFF */
#endif /* HB_CFF_INTERP_DICT_COMMON_HH */

164
src/hb-cff1-interp-cs.hh Normal file
View File

@ -0,0 +1,164 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_CFF1_INTERP_CS_HH
#define HB_CFF1_INTERP_CS_HH
#include "hb.hh"
#include "hb-cff-interp-cs-common.hh"
namespace CFF {
using namespace OT;
typedef BiasedSubrs<CFF1Subrs> CFF1BiasedSubrs;
struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
{
template <typename ACC>
inline void init (const ByteStr &str, ACC &acc, unsigned int fd)
{
SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
processed_width = false;
has_width = false;
arg_start = 0;
in_seac = false;
}
inline void fini (void)
{
SUPER::fini ();
}
inline void set_width (bool has_width_)
{
if (likely (!processed_width && (SUPER::argStack.get_count () > 0)))
{
if (has_width_)
{
width = SUPER::argStack[0];
has_width = true;
arg_start = 1;
}
}
processed_width = true;
}
inline void clear_args (void)
{
arg_start = 0;
SUPER::clear_args ();
}
inline void set_in_seac (bool _in_seac) { in_seac = _in_seac; }
bool processed_width;
bool has_width;
unsigned int arg_start;
Number width;
bool in_seac;
private:
typedef CSInterpEnv<Number, CFF1Subrs> SUPER;
};
template <typename OPSET, typename PARAM, typename PATH=PathProcsNull<CFF1CSInterpEnv, PARAM> >
struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>
{
/* 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)
{
if (!env.processed_width)
{
bool has_width = false;
switch (op)
{
case OpCode_endchar:
case OpCode_hstem:
case OpCode_hstemhm:
case OpCode_vstem:
case OpCode_vstemhm:
case OpCode_hintmask:
case OpCode_cntrmask:
has_width = ((env.argStack.get_count () & 1) != 0);
break;
case OpCode_hmoveto:
case OpCode_vmoveto:
has_width = (env.argStack.get_count () > 1);
break;
case OpCode_rmoveto:
has_width = (env.argStack.get_count () > 2);
break;
default:
return;
}
env.set_width (has_width);
}
}
static inline void process_seac (CFF1CSInterpEnv &env, PARAM& param)
{
}
static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param)
{
SUPER::flush_args (env, param);
env.clear_args (); /* pop off width */
}
private:
typedef CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH> SUPER;
};
template <typename OPSET, typename PARAM>
struct CFF1CSInterpreter : CSInterpreter<CFF1CSInterpEnv, OPSET, PARAM> {};
} /* namespace CFF */
#endif /* HB_CFF1_INTERP_CS_HH */

262
src/hb-cff2-interp-cs.hh Normal file
View File

@ -0,0 +1,262 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_CFF2_INTERP_CS_HH
#define HB_CFF2_INTERP_CS_HH
#include "hb.hh"
#include "hb-cff-interp-cs-common.hh"
namespace CFF {
using namespace OT;
struct BlendArg : Number
{
inline void init (void)
{
Number::init ();
deltas.init ();
}
inline void fini (void)
{
Number::fini ();
deltas.fini_deep ();
}
inline void set_int (int v) { reset_blends (); Number::set_int (v); }
inline void set_fixed (int32_t v) { reset_blends (); Number::set_fixed (v); }
inline void set_real (float v) { reset_blends (); Number::set_real (v); }
inline void set_blends (unsigned int numValues_, unsigned int valueIndex_,
unsigned int numBlends, const BlendArg *blends_)
{
numValues = numValues_;
valueIndex = valueIndex_;
deltas.resize (numBlends);
for (unsigned int i = 0; i < numBlends; i++)
deltas[i] = blends_[i];
}
inline bool blending (void) const { return deltas.len > 0; }
inline void reset_blends (void)
{
numValues = valueIndex = 0;
deltas.resize (0);
}
unsigned int numValues;
unsigned int valueIndex;
hb_vector_t<Number> deltas;
};
typedef InterpEnv<BlendArg> BlendInterpEnv;
typedef BiasedSubrs<CFF2Subrs> CFF2BiasedSubrs;
struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
{
template <typename ACC>
inline void init (const ByteStr &str, ACC &acc, unsigned int fd,
const int *coords_=nullptr, unsigned int num_coords_=0)
{
SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
coords = coords_;
num_coords = num_coords_;
varStore = acc.varStore;
seen_blend = false;
seen_vsindex_ = false;
scalars.init ();
do_blend = (coords != nullptr) && num_coords && (varStore != &Null(CFF2VariationStore));
set_ivs (acc.privateDicts[fd].ivs);
}
inline void fini (void)
{
scalars.fini ();
SUPER::fini ();
}
inline OpCode fetch_op (void)
{
if (this->substr.avail ())
return SUPER::fetch_op ();
/* make up return or endchar op */
if (this->callStack.is_empty ())
return OpCode_endchar;
else
return OpCode_return;
}
inline const BlendArg& eval_arg (unsigned int i)
{
BlendArg &arg = argStack[i];
blend_arg (arg);
return arg;
}
inline const BlendArg& pop_arg (void)
{
BlendArg &arg = argStack.pop ();
blend_arg (arg);
return arg;
}
inline void process_blend (void)
{
if (!seen_blend)
{
region_count = varStore->varStore.get_region_index_count (get_ivs ());
if (do_blend)
{
scalars.resize (region_count);
varStore->varStore.get_scalars (get_ivs (),
(int *)coords, num_coords,
&scalars[0], region_count);
}
seen_blend = true;
}
}
inline void process_vsindex (void)
{
unsigned int index = argStack.pop_uint ();
if (unlikely (seen_vsindex () || seen_blend))
{
set_error ();
}
else
{
set_ivs (index);
}
seen_vsindex_ = true;
}
inline unsigned int get_region_count (void) const { return region_count; }
inline void set_region_count (unsigned int region_count_) { region_count = region_count_; }
inline unsigned int get_ivs (void) const { return ivs; }
inline void set_ivs (unsigned int ivs_) { ivs = ivs_; }
inline bool seen_vsindex (void) const { return seen_vsindex_; }
protected:
inline void blend_arg (BlendArg &arg)
{
if (do_blend && arg.blending ())
{
if (likely (scalars.len == arg.deltas.len))
{
float v = arg.to_real ();
for (unsigned int i = 0; i < scalars.len; i++)
{
v += scalars[i] * arg.deltas[i].to_real ();
}
arg.set_real (v);
arg.deltas.resize (0);
}
}
}
protected:
const int *coords;
unsigned int num_coords;
const CFF2VariationStore *varStore;
unsigned int region_count;
unsigned int ivs;
hb_vector_t<float> scalars;
bool do_blend;
bool seen_vsindex_;
bool seen_blend;
typedef CSInterpEnv<BlendArg, CFF2Subrs> SUPER;
};
template <typename OPSET, typename PARAM, typename PATH=PathProcsNull<CFF2CSInterpEnv, PARAM> >
struct CFF2CSOpSet : CSOpSet<BlendArg, OPSET, CFF2CSInterpEnv, PARAM, PATH>
{
static inline void process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
{
switch (op) {
case OpCode_callsubr:
case OpCode_callgsubr:
/* a subroutine number shoudln't be a blended value */
if (unlikely (env.argStack.peek ().blending ()))
{
env.set_error ();
break;
}
SUPER::process_op (op, env, param);
break;
case OpCode_blendcs:
OPSET::process_blend (env, param);
break;
case OpCode_vsindexcs:
if (unlikely (env.argStack.peek ().blending ()))
{
env.set_error ();
break;
}
OPSET::process_vsindex (env, param);
break;
default:
SUPER::process_op (op, env, param);
}
}
static inline void process_blend (CFF2CSInterpEnv &env, PARAM& param)
{
unsigned int n, k;
env.process_blend ();
k = env.get_region_count ();
n = env.argStack.pop_uint ();
/* copy the blend values into blend array of the default values */
unsigned int start = env.argStack.get_count () - ((k+1) * n);
for (unsigned int i = 0; i < n; i++)
env.argStack[start + i].set_blends (n, i, k, &env.argStack[start + n + (i * k)]);
/* pop off blend values leaving default values now adorned with blend values */
env.argStack.pop (k * n);
}
static inline void process_vsindex (CFF2CSInterpEnv &env, PARAM& param)
{
env.process_vsindex ();
env.clear_args ();
}
private:
typedef CSOpSet<BlendArg, OPSET, CFF2CSInterpEnv, PARAM, PATH> SUPER;
};
template <typename OPSET, typename PARAM>
struct CFF2CSInterpreter : CSInterpreter<CFF2CSInterpEnv, OPSET, PARAM> {};
} /* namespace CFF */
#endif /* HB_CFF2_INTERP_CS_HH */

View File

@ -36,7 +36,7 @@
/* Global nul-content Null pool. Enlarge as necessary. */
#define HB_NULL_POOL_SIZE 1024
#define HB_NULL_POOL_SIZE 9880
/* Use SFINAE to sniff whether T has min_size; in which case return T::null_size,
* otherwise return sizeof(T). */

712
src/hb-ot-cff-common.hh Normal file
View File

@ -0,0 +1,712 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_OT_CFF_COMMON_HH
#define HB_OT_CFF_COMMON_HH
#include "hb-open-type.hh"
#include "hb-ot-layout-common.hh"
#include "hb-cff-interp-dict-common.hh"
#include "hb-subset-plan.hh"
namespace CFF {
using namespace OT;
#define CFF_UNDEF_CODE 0xFFFFFFFF
/* utility macro */
template<typename Type>
static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset)
{ return offset? (* reinterpret_cast<const Type*> ((const char *) P + offset)): Null(Type); }
inline unsigned int calcOffSize(unsigned int dataSize)
{
unsigned int size = 1;
unsigned int offset = dataSize + 1;
while ((offset & ~0xFF) != 0)
{
size++;
offset >>= 8;
}
assert (size <= 4);
return size;
}
struct code_pair
{
hb_codepoint_t code;
hb_codepoint_t glyph;
};
typedef hb_vector_t<char, 1> StrBuff;
struct StrBuffArray : hb_vector_t<StrBuff>
{
inline void fini (void)
{
SUPER::fini_deep ();
}
inline unsigned int total_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < len; i++)
size += (*this)[i].len;
return size;
}
private:
typedef hb_vector_t<StrBuff> SUPER;
};
/* CFF INDEX */
template <typename COUNT>
struct CFFIndex
{
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely ((count.sanitize (c) && count == 0) || /* empty INDEX */
(c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
c->check_array (offsets, offSize, count + 1) &&
c->check_array ((const HBUINT8*)data_base (), 1, max_offset () - 1))));
}
inline static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
{ return offSize * (count + 1); }
inline unsigned int offset_array_size (void) const
{ return calculate_offset_array_size (offSize, count); }
inline static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize)
{
if (count == 0)
return COUNT::static_size;
else
return min_size + calculate_offset_array_size (offSize, count) + dataSize;
}
inline bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
{
TRACE_SERIALIZE (this);
unsigned int size = src.get_size ();
CFFIndex *dest = c->allocate_size<CFFIndex> (size);
if (unlikely (dest == nullptr)) return_trace (false);
memcpy (dest, &src, size);
return_trace (true);
}
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_,
const ByteStrArray &byteArray)
{
TRACE_SERIALIZE (this);
if (byteArray.len == 0)
{
COUNT *dest = c->allocate_min<COUNT> ();
if (unlikely (dest == nullptr)) return_trace (false);
dest->set (0);
}
else
{
/* serialize CFFIndex header */
if (unlikely (!c->extend_min (*this))) return_trace (false);
this->count.set (byteArray.len);
this->offSize.set (offSize_);
if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.len + 1))))
return_trace (false);
/* serialize indices */
unsigned int offset = 1;
unsigned int i = 0;
for (; i < byteArray.len; i++)
{
set_offset_at (i, offset);
offset += byteArray[i].get_size ();
}
set_offset_at (i, offset);
/* serialize data */
for (unsigned int i = 0; i < byteArray.len; i++)
{
ByteStr *dest = c->start_embed<ByteStr> ();
if (unlikely (dest == nullptr ||
!dest->serialize (c, byteArray[i])))
return_trace (false);
}
}
return_trace (true);
}
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_,
const StrBuffArray &buffArray)
{
ByteStrArray byteArray;
byteArray.init ();
byteArray.resize (buffArray.len);
for (unsigned int i = 0; i < byteArray.len; i++)
{
byteArray[i] = ByteStr (buffArray[i].arrayZ (), buffArray[i].len);
}
bool result = this->serialize (c, offSize_, byteArray);
byteArray.fini ();
return result;
}
inline void set_offset_at (unsigned int index, unsigned int offset)
{
HBUINT8 *p = offsets + offSize * index + offSize;
unsigned int size = offSize;
for (; size; size--)
{
--p;
p->set (offset & 0xFF);
offset >>= 8;
}
}
inline unsigned int offset_at (unsigned int index) const
{
assert (index <= count);
const HBUINT8 *p = offsets + offSize * index;
unsigned int size = offSize;
unsigned int offset = 0;
for (; size; size--)
offset = (offset << 8) + *p++;
return offset;
}
inline unsigned int length_at (unsigned int index) const
{ return offset_at (index + 1) - offset_at (index); }
inline const char *data_base (void) const
{ return (const char *)this + min_size + offset_array_size (); }
inline unsigned int data_size (void) const
{ return HBINT8::static_size; }
ByteStr operator [] (unsigned int index) const
{
if (likely (index < count))
return ByteStr (data_base () + offset_at (index) - 1, offset_at (index + 1) - offset_at (index));
else
return Null(ByteStr);
}
inline unsigned int get_size (void) const
{
if (this != &Null(CFFIndex))
{
if (count > 0)
return min_size + offset_array_size () + (offset_at (count) - 1);
else
return count.static_size; /* empty CFFIndex contains count only */
}
else
return 0;
}
protected:
inline unsigned int max_offset (void) const
{
unsigned int max = 0;
for (unsigned int i = 0; i <= count; i++)
{
unsigned int off = offset_at (i);
if (off > max) max = off;
}
return max;
}
public:
COUNT count; /* Number of object data. Note there are (count+1) offsets */
HBUINT8 offSize; /* The byte size of each offset in the offsets array. */
HBUINT8 offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */
/* HBUINT8 data[VAR]; Object data */
public:
DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
};
template <typename COUNT, typename TYPE>
struct CFFIndexOf : CFFIndex<COUNT>
{
inline const ByteStr operator [] (unsigned int index) const
{
if (likely (index < CFFIndex<COUNT>::count))
return ByteStr (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
return Null(ByteStr);
}
template <typename DATA, typename PARAM1, typename PARAM2>
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_,
const DATA *dataArray,
unsigned int dataArrayLen,
const hb_vector_t<unsigned int> &dataSizeArray,
const PARAM1 &param1,
const PARAM2 &param2)
{
TRACE_SERIALIZE (this);
/* serialize CFFIndex header */
if (unlikely (!c->extend_min (*this))) return_trace (false);
this->count.set (dataArrayLen);
this->offSize.set (offSize_);
if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1))))
return_trace (false);
/* serialize indices */
unsigned int offset = 1;
unsigned int i = 0;
for (; i < dataArrayLen; i++)
{
CFFIndex<COUNT>::set_offset_at (i, offset);
offset += dataSizeArray[i];
}
CFFIndex<COUNT>::set_offset_at (i, offset);
/* serialize data */
for (unsigned int i = 0; i < dataArrayLen; i++)
{
TYPE *dest = c->start_embed<TYPE> ();
if (unlikely (dest == nullptr ||
!dest->serialize (c, dataArray[i], param1, param2)))
return_trace (false);
}
return_trace (true);
}
/* in parallel to above */
template <typename DATA, typename PARAM>
inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
const DATA *dataArray,
unsigned int dataArrayLen,
hb_vector_t<unsigned int> &dataSizeArray, /* OUT */
const PARAM &param)
{
/* determine offset size */
unsigned int totalDataSize = 0;
for (unsigned int i = 0; i < dataArrayLen; i++)
{
unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param);
dataSizeArray[i] = dataSize;
totalDataSize += dataSize;
}
offSize_ = calcOffSize (totalDataSize);
return CFFIndex<COUNT>::calculate_serialized_size (offSize_, dataArrayLen, totalDataSize);
}
};
/* Top Dict, Font Dict, Private Dict */
struct Dict : UnsizedByteStr
{
template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
inline bool serialize (hb_serialize_context_t *c,
const DICTVAL &dictval,
OP_SERIALIZER& opszr,
PARAM& param)
{
TRACE_SERIALIZE (this);
for (unsigned int i = 0; i < dictval.get_count (); i++)
{
if (unlikely (!opszr.serialize (c, dictval[i], param)))
return_trace (false);
}
return_trace (true);
}
/* in parallel to above */
template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
inline static unsigned int calculate_serialized_size (const DICTVAL &dictval,
OP_SERIALIZER& opszr,
PARAM& param)
{
unsigned int size = 0;
for (unsigned int i = 0; i < dictval.get_count (); i++)
size += opszr.calculate_serialized_size (dictval[i], param);
return size;
}
template <typename DICTVAL, typename OP_SERIALIZER>
inline static unsigned int calculate_serialized_size (const DICTVAL &dictval,
OP_SERIALIZER& opszr)
{
unsigned int size = 0;
for (unsigned int i = 0; i < dictval.get_count (); i++)
size += opszr.calculate_serialized_size (dictval[i]);
return size;
}
template <typename INTTYPE, int minVal, int maxVal>
inline static bool serialize_int_op (hb_serialize_context_t *c, OpCode op, int value, OpCode intOp)
{
// XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
if (/*unlikely*/ (!serialize_int<INTTYPE, minVal, maxVal> (c, intOp, value)))
return false;
TRACE_SERIALIZE (this);
/* serialize the opcode */
HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op));
if (unlikely (p == nullptr)) return_trace (false);
if (Is_OpCode_ESC (op))
{
p->set (OpCode_escape);
op = Unmake_OpCode_ESC (op);
p++;
}
p->set (op);
return_trace (true);
}
inline static bool serialize_uint4_op (hb_serialize_context_t *c, OpCode op, int value)
{ return serialize_int_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longintdict); }
inline static bool serialize_uint2_op (hb_serialize_context_t *c, OpCode op, int value)
{ return serialize_int_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
inline static bool serialize_offset4_op (hb_serialize_context_t *c, OpCode op, int value)
{
return serialize_uint4_op (c, op, value);
}
inline static bool serialize_offset2_op (hb_serialize_context_t *c, OpCode op, int value)
{
return serialize_uint2_op (c, op, value);
}
};
struct TopDict : Dict {};
struct FontDict : Dict {};
struct PrivateDict : Dict {};
struct TableInfo
{
void init (void) { offSize = offset = size = 0; }
unsigned int offset;
unsigned int size;
unsigned int offSize;
};
/* used to remap font index or SID from fullset to subset.
* set to CFF_UNDEF_CODE if excluded from subset */
struct Remap : hb_vector_t<hb_codepoint_t>
{
inline void init (void)
{ SUPER::init (); }
inline void fini (void)
{ SUPER::fini (); }
inline bool reset (unsigned int size)
{
if (unlikely (!SUPER::resize (size)))
return false;
for (unsigned int i = 0; i < len; i++)
(*this)[i] = CFF_UNDEF_CODE;
count = 0;
return true;
}
inline bool identity (unsigned int size)
{
if (unlikely (!SUPER::resize (size)))
return false;
unsigned int i;
for (i = 0; i < len; i++)
(*this)[i] = i;
count = i;
return true;
}
inline bool excludes (hb_codepoint_t id) const
{ return (id < len) && ((*this)[id] == CFF_UNDEF_CODE); }
inline bool includes (hb_codepoint_t id) const
{ return !excludes (id); }
inline unsigned int add (unsigned int i)
{
if ((*this)[i] == CFF_UNDEF_CODE)
(*this)[i] = count++;
return (*this)[i];
}
inline hb_codepoint_t get_count (void) const
{ return count; }
protected:
hb_codepoint_t count;
private:
typedef hb_vector_t<hb_codepoint_t> SUPER;
};
template <typename COUNT>
struct FDArray : CFFIndexOf<COUNT, FontDict>
{
/* used by CFF1 */
template <typename DICTVAL, typename OP_SERIALIZER>
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_,
const hb_vector_t<DICTVAL> &fontDicts,
OP_SERIALIZER& opszr)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false);
this->count.set (fontDicts.len);
this->offSize.set (offSize_);
if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fontDicts.len + 1))))
return_trace (false);
/* serialize font dict offsets */
unsigned int offset = 1;
unsigned int fid = 0;
for (; fid < fontDicts.len; fid++)
{
CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
offset += FontDict::calculate_serialized_size (fontDicts[fid], opszr);
}
CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
/* serialize font dicts */
for (unsigned int i = 0; i < fontDicts.len; i++)
{
FontDict *dict = c->start_embed<FontDict> ();
if (unlikely (!dict->serialize (c, fontDicts[i], opszr, fontDicts[i])))
return_trace (false);
}
return_trace (true);
}
/* used by CFF2 */
template <typename DICTVAL, typename OP_SERIALIZER>
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_,
const hb_vector_t<DICTVAL> &fontDicts,
unsigned int fdCount,
const Remap &fdmap,
OP_SERIALIZER& opszr,
const hb_vector_t<TableInfo> &privateInfos)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false);
this->count.set (fdCount);
this->offSize.set (offSize_);
if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fdCount + 1))))
return_trace (false);
/* serialize font dict offsets */
unsigned int offset = 1;
unsigned int fid = 0;
for (unsigned i = 0; i < fontDicts.len; i++)
if (fdmap.includes (i))
{
CFFIndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
}
CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
/* serialize font dicts */
for (unsigned int i = 0; i < fontDicts.len; i++)
if (fdmap.includes (i))
{
FontDict *dict = c->start_embed<FontDict> ();
if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]])))
return_trace (false);
}
return_trace (true);
}
/* in parallel to above */
template <typename OP_SERIALIZER, typename DICTVAL>
inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
const hb_vector_t<DICTVAL> &fontDicts,
unsigned int fdCount,
const Remap &fdmap,
OP_SERIALIZER& opszr)
{
unsigned int dictsSize = 0;
for (unsigned int i = 0; i < fontDicts.len; i++)
if (fdmap.includes (i))
dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
offSize_ = calcOffSize (dictsSize);
return CFFIndex<COUNT>::calculate_serialized_size (offSize_, fdCount, dictsSize);
}
};
/* FDSelect */
struct FDSelect0 {
inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
{
TRACE_SANITIZE (this);
if (unlikely (!(c->check_struct (this))))
return_trace (false);
for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
if (unlikely (!fds[i].sanitize (c)))
return_trace (false);
return_trace (true);
}
inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
{
return (hb_codepoint_t)fds[glyph];
}
inline unsigned int get_size (unsigned int num_glyphs) const
{ return HBUINT8::static_size * num_glyphs; }
HBUINT8 fds[VAR];
DEFINE_SIZE_MIN (1);
};
template <typename GID_TYPE, typename FD_TYPE>
struct FDSelect3_4_Range {
inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && (first < c->get_num_glyphs ()) && (fd < fdcount)));
}
GID_TYPE first;
FD_TYPE fd;
DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
};
template <typename GID_TYPE, typename FD_TYPE>
struct FDSelect3_4 {
inline unsigned int get_size (void) const
{ return GID_TYPE::static_size * 2 + FDSelect3_4_Range<GID_TYPE, FD_TYPE>::static_size * nRanges; }
inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
{
TRACE_SANITIZE (this);
if (unlikely (!(c->check_struct (this) && (nRanges > 0) && (ranges[0].first == 0))))
return_trace (false);
for (unsigned int i = 0; i < nRanges; i++)
{
if (unlikely (!ranges[i].sanitize (c, fdcount)))
return_trace (false);
if ((0 < i) && unlikely (ranges[i - 1].first >= ranges[i].first))
return_trace (false);
}
if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
return_trace (false);
return_trace (true);
}
inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
{
unsigned int i;
for (i = 1; i < nRanges; i++)
if (glyph < ranges[i].first)
break;
return (hb_codepoint_t)ranges[i - 1].fd;
}
inline GID_TYPE &sentinel (void) { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
inline const GID_TYPE &sentinel (void) const { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
GID_TYPE nRanges;
FDSelect3_4_Range<GID_TYPE, FD_TYPE> ranges[VAR];
/* GID_TYPE sentinel */
DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges);
};
typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;
struct FDSelect {
inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) &&
(format == 0)?
u.format0.sanitize (c, fdcount):
u.format3.sanitize (c, fdcount)));
}
inline bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
unsigned int size = src.get_size (num_glyphs);
FDSelect *dest = c->allocate_size<FDSelect> (size);
if (unlikely (dest == nullptr)) return_trace (false);
memcpy (dest, &src, size);
return_trace (true);
}
inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
{ return get_size (num_glyphs); }
inline unsigned int get_size (unsigned int num_glyphs) const
{
unsigned int size = format.static_size;
if (format == 0)
size += u.format0.get_size (num_glyphs);
else
size += u.format3.get_size ();
return size;
}
inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
{
if (this == &Null(FDSelect))
return 0;
if (format == 0)
return u.format0.get_fd (glyph);
else
return u.format3.get_fd (glyph);
}
HBUINT8 format;
union {
FDSelect0 format0;
FDSelect3 format3;
} u;
DEFINE_SIZE_MIN (1);
};
template <typename COUNT>
struct Subrs : CFFIndex<COUNT>
{
typedef COUNT count_type;
typedef CFFIndex<COUNT> SUPER;
};
} /* namespace CFF */
#endif /* HB_OT_CFF_COMMON_HH */

388
src/hb-ot-cff1-table.cc Normal file
View File

@ -0,0 +1,388 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#include "hb-ot-cff1-table.hh"
#include "hb-cff1-interp-cs.hh"
using namespace CFF;
/* SID to code */
static const uint8_t standard_encoding_to_code [] =
{
0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177,
178, 179, 180, 182, 183, 184, 185, 186, 187, 188, 189, 191, 193, 194, 195, 196,
197, 198, 199, 200, 202, 203, 205, 206, 207, 208, 225, 227, 232, 233, 234, 235,
241, 245, 248, 249, 250, 251
};
/* SID to code */
static const uint8_t expert_encoding_to_code [] =
{
0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 45, 46,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 189, 0, 0, 188, 0,
0, 0, 0, 190, 202, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 63, 65, 66, 67,
68, 69, 73, 76, 77, 78, 79, 82, 83, 84, 86, 89, 90, 91, 93, 94,
95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
161, 162, 163, 166, 167, 168, 169, 170, 172, 175, 178, 179, 182, 183, 184, 191,
192, 193, 194, 195, 196, 197, 200, 204, 205, 206, 207, 208, 209, 210, 211, 212,
213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228,
229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
};
/* glyph ID to SID */
static const uint16_t expert_charset_to_sid [] =
{
0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99,
239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252,
253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110,
267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150,
164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356,
357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
373, 374, 375, 376, 377, 378
};
/* glyph ID to SID */
static const uint16_t expert_subset_charset_to_sid [] =
{
0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242,
243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257,
258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272,
300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326,
150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
340, 341, 342, 343, 344, 345, 346
};
/* code to SID */
static const uint8_t standard_encoding_to_sid [] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
0, 111, 112, 113, 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 123,
0, 124, 125, 126, 127, 128, 129, 130, 131, 0, 132, 133, 0, 134, 135, 136,
137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143, 0, 0, 0, 0,
0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0
};
hb_codepoint_t OT::cff1::lookup_standard_encoding_for_code (hb_codepoint_t sid)
{
if (sid < ARRAY_LENGTH (standard_encoding_to_code))
return (hb_codepoint_t)standard_encoding_to_code[sid];
else
return 0;
}
hb_codepoint_t OT::cff1::lookup_expert_encoding_for_code (hb_codepoint_t sid)
{
if (sid < ARRAY_LENGTH (expert_encoding_to_code))
return (hb_codepoint_t)expert_encoding_to_code[sid];
else
return 0;
}
hb_codepoint_t OT::cff1::lookup_expert_charset_for_sid (hb_codepoint_t glyph)
{
if (glyph < ARRAY_LENGTH (expert_charset_to_sid))
return (hb_codepoint_t)expert_charset_to_sid[glyph];
else
return 0;
}
hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph)
{
if (glyph < ARRAY_LENGTH (expert_subset_charset_to_sid))
return (hb_codepoint_t)expert_subset_charset_to_sid[glyph];
else
return 0;
}
hb_codepoint_t OT::cff1::lookup_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)
{
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;
cff = _cff;
bounds.init ();
}
inline void start_path (void) { path_open = true; }
inline void end_path (void) { path_open = false; }
inline bool is_path_open (void) const { return path_open; }
bool path_open;
Bounds bounds;
const OT::cff1::accelerator_t *cff;
};
struct CFF1PathProcs_Extents : PathProcs<CFF1PathProcs_Extents, CFF1CSInterpEnv, ExtentsParam>
{
static inline void moveto (CFF1CSInterpEnv &env, ExtentsParam& param, const Point &pt)
{
param.end_path ();
env.moveto (pt);
}
static inline void line (CFF1CSInterpEnv &env, ExtentsParam& param, const Point &pt1)
{
if (!param.is_path_open ())
{
param.start_path ();
param.bounds.update (env.get_pt ());
}
env.moveto (pt1);
param.bounds.update (env.get_pt ());
}
static inline void curve (CFF1CSInterpEnv &env, ExtentsParam& param, const Point &pt1, const Point &pt2, const Point &pt3)
{
if (!param.is_path_open ())
{
param.start_path ();
param.bounds.update (env.get_pt ());
}
/* include control points */
param.bounds.update (pt1);
param.bounds.update (pt2);
env.moveto (pt3);
param.bounds.update (env.get_pt ());
}
};
static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, Bounds &bounds, bool in_seac=false);
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 (!env.in_seac && base && accent
&& _get_bounds (param.cff, base, base_bounds, true)
&& _get_bounds (param.cff, accent, accent_bounds, true)))
{
param.bounds.merge (base_bounds);
accent_bounds.offset (delta);
param.bounds.merge (accent_bounds);
}
else
env.set_error ();
}
};
bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, Bounds &bounds, bool in_seac)
{
bounds.init ();
if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
unsigned int fd = cff->fdSelect->get_fd (glyph);
CFF1CSInterpreter<CFF1CSOpSet_Extents, ExtentsParam> interp;
const ByteStr str = (*cff->charStrings)[glyph];
interp.env.init (str, *cff, fd);
interp.env.set_in_seac (in_seac);
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
{
Bounds bounds;
if (!_get_bounds (this, glyph, bounds))
return false;
if (bounds.min.x >= bounds.max.x)
{
extents->width = 0;
extents->x_bearing = 0;
}
else
{
extents->x_bearing = (int32_t)bounds.min.x.floor ();
extents->width = (int32_t)bounds.max.x.ceil () - extents->x_bearing;
}
if (bounds.min.y >= bounds.max.y)
{
extents->height = 0;
extents->y_bearing = 0;
}
else
{
extents->y_bearing = (int32_t)bounds.max.y.ceil ();
extents->height = (int32_t)bounds.min.y.floor () - extents->y_bearing;
}
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;
}

1304
src/hb-ot-cff1-table.hh Normal file

File diff suppressed because it is too large Load Diff

136
src/hb-ot-cff2-table.cc Normal file
View File

@ -0,0 +1,136 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#include "hb-ot-cff2-table.hh"
#include "hb-cff2-interp-cs.hh"
using namespace CFF;
struct ExtentsParam
{
inline void init (void)
{
path_open = false;
min_x.set_int (0x7FFFFFFF);
min_y.set_int (0x7FFFFFFF);
max_x.set_int (-0x80000000);
max_y.set_int (-0x80000000);
}
inline void start_path (void) { path_open = true; }
inline void end_path (void) { path_open = false; }
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;
Number min_x;
Number min_y;
Number max_x;
Number max_y;
};
struct CFF2PathProcs_Extents : PathProcs<CFF2PathProcs_Extents, CFF2CSInterpEnv, ExtentsParam>
{
static inline void moveto (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt)
{
param.end_path ();
env.moveto (pt);
}
static inline void line (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt1)
{
if (!param.is_path_open ())
{
param.start_path ();
param.update_bounds (env.get_pt ());
}
env.moveto (pt1);
param.update_bounds (env.get_pt ());
}
static inline void curve (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt1, const Point &pt2, const Point &pt3)
{
if (!param.is_path_open ())
{
param.start_path ();
param.update_bounds (env.get_pt ());
}
/* include control points */
param.update_bounds (pt1);
param.update_bounds (pt2);
env.moveto (pt3);
param.update_bounds (env.get_pt ());
}
};
struct CFF2CSOpSet_Extents : CFF2CSOpSet<CFF2CSOpSet_Extents, ExtentsParam, CFF2PathProcs_Extents> {};
bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const
{
if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
unsigned int num_coords;
const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
unsigned int fd = fdSelect->get_fd (glyph);
CFF2CSInterpreter<CFF2CSOpSet_Extents, ExtentsParam> interp;
const ByteStr str = (*charStrings)[glyph];
interp.env.init (str, *this, fd, coords, num_coords);
ExtentsParam param;
param.init ();
if (unlikely (!interp.interpret (param))) return false;
if (param.min_x >= param.max_x)
{
extents->width = 0;
extents->x_bearing = 0;
}
else
{
extents->x_bearing = (int32_t)param.min_x.floor ();
extents->width = (int32_t)param.max_x.ceil () - extents->x_bearing;
}
if (param.min_y >= param.max_y)
{
extents->height = 0;
extents->y_bearing = 0;
}
else
{
extents->y_bearing = (int32_t)param.max_y.ceil ();
extents->height = (int32_t)param.min_y.floor () - extents->y_bearing;
}
return true;
}

575
src/hb-ot-cff2-table.hh Normal file
View File

@ -0,0 +1,575 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_OT_CFF2_TABLE_HH
#define HB_OT_CFF2_TABLE_HH
#include "hb-ot-head-table.hh"
#include "hb-ot-cff-common.hh"
#include "hb-subset-cff2.hh"
namespace CFF {
/*
* CFF2 -- Compact Font Format (CFF) Version 2
* https://docs.microsoft.com/en-us/typography/opentype/spec/cff2
*/
#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')
typedef CFFIndex<HBUINT32> CFF2Index;
template <typename Type> struct CFF2IndexOf : CFFIndexOf<HBUINT32, Type> {};
typedef CFF2Index CFF2CharStrings;
typedef FDArray<HBUINT32> CFF2FDArray;
typedef Subrs<HBUINT32> CFF2Subrs;
typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;
struct CFF2FDSelect
{
inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && (format == 0 || format == 3 || format == 4) &&
(format == 0)?
u.format0.sanitize (c, fdcount):
((format == 3)?
u.format3.sanitize (c, fdcount):
u.format4.sanitize (c, fdcount))));
}
inline bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
unsigned int size = src.get_size (num_glyphs);
CFF2FDSelect *dest = c->allocate_size<CFF2FDSelect> (size);
if (unlikely (dest == nullptr)) return_trace (false);
memcpy (dest, &src, size);
return_trace (true);
}
inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
{ return get_size (num_glyphs); }
inline unsigned int get_size (unsigned int num_glyphs) const
{
unsigned int size = format.static_size;
if (format == 0)
size += u.format0.get_size (num_glyphs);
else if (format == 3)
size += u.format3.get_size ();
else
size += u.format4.get_size ();
return size;
}
inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
{
if (this == &Null(CFF2FDSelect))
return 0;
if (format == 0)
return u.format0.get_fd (glyph);
else if (format == 3)
return u.format3.get_fd (glyph);
else
return u.format4.get_fd (glyph);
}
HBUINT8 format;
union {
FDSelect0 format0;
FDSelect3 format3;
FDSelect4 format4;
} u;
DEFINE_SIZE_MIN (2);
};
struct CFF2VariationStore
{
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)) && varStore.sanitize (c));
}
inline bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore)
{
TRACE_SERIALIZE (this);
unsigned int size_ = varStore->get_size ();
CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_);
if (unlikely (dest == nullptr)) return_trace (false);
memcpy (dest, varStore, size_);
return_trace (true);
}
inline unsigned int get_size (void) const { return HBUINT16::static_size + size; }
HBUINT16 size;
VariationStore varStore;
DEFINE_SIZE_MIN (2 + VariationStore::min_size);
};
struct CFF2TopDictValues : TopDictValues<>
{
inline void init (void)
{
TopDictValues<>::init ();
vstoreOffset = 0;
FDSelectOffset = 0;
}
inline void fini (void)
{
TopDictValues<>::fini ();
}
inline unsigned int calculate_serialized_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < get_count (); i++)
{
OpCode op = get_value (i).op;
switch (op)
{
case OpCode_vstore:
case OpCode_FDSelect:
size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
break;
default:
size += TopDictValues<>::calculate_serialized_op_size (get_value (i));
break;
}
}
return size;
}
unsigned int vstoreOffset;
unsigned int FDSelectOffset;
};
struct CFF2TopDictOpSet : TopDictOpSet<>
{
static inline void process_op (OpCode op, NumInterpEnv& env, CFF2TopDictValues& dictval)
{
switch (op) {
case OpCode_FontMatrix:
{
DictVal val;
val.init ();
dictval.add_op (op, env.substr);
env.clear_args ();
}
break;
case OpCode_vstore:
dictval.vstoreOffset = env.argStack.pop_uint ();
env.clear_args ();
break;
case OpCode_FDSelect:
dictval.FDSelectOffset = env.argStack.pop_uint ();
env.clear_args ();
break;
default:
SUPER::process_op (op, env, dictval);
/* Record this operand below if stack is empty, otherwise done */
if (!env.argStack.is_empty ()) return;
}
if (unlikely (env.in_error ())) return;
dictval.add_op (op, env.substr);
}
typedef TopDictOpSet<> SUPER;
};
struct CFF2FontDictValues : DictValues<OpStr>
{
inline void init (void)
{
DictValues<OpStr>::init ();
privateDictInfo.init ();
}
inline void fini (void)
{
DictValues<OpStr>::fini ();
}
TableInfo privateDictInfo;
};
struct CFF2FontDictOpSet : DictOpSet
{
static inline void process_op (OpCode op, NumInterpEnv& env, CFF2FontDictValues& dictval)
{
switch (op) {
case OpCode_Private:
dictval.privateDictInfo.offset = env.argStack.pop_uint ();
dictval.privateDictInfo.size = env.argStack.pop_uint ();
env.clear_args ();
break;
default:
SUPER::process_op (op, env);
if (!env.argStack.is_empty ())
return;
}
if (unlikely (env.in_error ())) return;
dictval.add_op (op, env.substr);
}
private:
typedef DictOpSet SUPER;
};
template <typename VAL>
struct CFF2PrivateDictValues_Base : DictValues<VAL>
{
inline void init (void)
{
DictValues<VAL>::init ();
subrsOffset = 0;
localSubrs = &Null(CFF2Subrs);
ivs = 0;
}
inline void fini (void)
{
DictValues<VAL>::fini ();
}
inline unsigned int calculate_serialized_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
else
size += DictValues<VAL>::get_value (i).str.len;
return size;
}
unsigned int subrsOffset;
const CFF2Subrs *localSubrs;
unsigned int ivs;
};
typedef CFF2PrivateDictValues_Base<OpStr> CFF2PrivateDictValues_Subset;
typedef CFF2PrivateDictValues_Base<NumDictVal> CFF2PrivateDictValues;
struct CFF2PrivDictInterpEnv : NumInterpEnv
{
inline void init (const ByteStr &str)
{
NumInterpEnv::init (str);
ivs = 0;
seen_vsindex = false;
}
inline void process_vsindex (void)
{
if (likely (!seen_vsindex))
{
set_ivs (argStack.pop_uint ());
}
seen_vsindex = true;
}
inline unsigned int get_ivs (void) const { return ivs; }
inline void set_ivs (unsigned int ivs_) { ivs = ivs_; }
protected:
unsigned int ivs;
bool seen_vsindex;
};
struct CFF2PrivateDictOpSet : DictOpSet
{
static inline void process_op (OpCode op, CFF2PrivDictInterpEnv& env, CFF2PrivateDictValues& dictval)
{
NumDictVal val;
val.init ();
switch (op) {
case OpCode_StdHW:
case OpCode_StdVW:
case OpCode_BlueScale:
case OpCode_BlueShift:
case OpCode_BlueFuzz:
case OpCode_ExpansionFactor:
case OpCode_LanguageGroup:
val.single_val = env.argStack.pop_num ();
env.clear_args ();
break;
case OpCode_BlueValues:
case OpCode_OtherBlues:
case OpCode_FamilyBlues:
case OpCode_FamilyOtherBlues:
case OpCode_StemSnapH:
case OpCode_StemSnapV:
env.clear_args ();
break;
case OpCode_Subrs:
dictval.subrsOffset = env.argStack.pop_uint ();
env.clear_args ();
break;
case OpCode_vsindexdict:
env.process_vsindex ();
dictval.ivs = env.get_ivs ();
env.clear_args ();
break;
case OpCode_blenddict:
break;
default:
DictOpSet::process_op (op, env);
if (!env.argStack.is_empty ()) return;
break;
}
if (unlikely (env.in_error ())) return;
dictval.add_op (op, env.substr, val);
}
};
struct CFF2PrivateDictOpSet_Subset : DictOpSet
{
static inline void process_op (OpCode op, CFF2PrivDictInterpEnv& env, CFF2PrivateDictValues_Subset& dictval)
{
switch (op) {
case OpCode_BlueValues:
case OpCode_OtherBlues:
case OpCode_FamilyBlues:
case OpCode_FamilyOtherBlues:
case OpCode_StdHW:
case OpCode_StdVW:
case OpCode_BlueScale:
case OpCode_BlueShift:
case OpCode_BlueFuzz:
case OpCode_StemSnapH:
case OpCode_StemSnapV:
case OpCode_LanguageGroup:
case OpCode_ExpansionFactor:
env.clear_args ();
break;
case OpCode_blenddict:
env.clear_args ();
return;
case OpCode_Subrs:
dictval.subrsOffset = env.argStack.pop_uint ();
env.clear_args ();
break;
default:
SUPER::process_op (op, env);
if (!env.argStack.is_empty ()) return;
break;
}
if (unlikely (env.in_error ())) return;
dictval.add_op (op, env.substr);
}
private:
typedef DictOpSet SUPER;
};
typedef DictInterpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter;
typedef DictInterpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter;
}; /* namespace CFF */
namespace OT {
using namespace CFF;
struct cff2
{
static const hb_tag_t tableTag = HB_OT_TAG_cff2;
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
likely (version.major == 2));
}
template <typename PRIVOPSET, typename PRIVDICTVAL>
struct accelerator_templ_t
{
inline void init (hb_face_t *face)
{
topDict.init ();
fontDicts.init ();
privateDicts.init ();
this->blob = sc.reference_table<cff2> (face);
/* setup for run-time santization */
sc.init (this->blob);
sc.start_processing ();
const OT::cff2 *cff2 = this->blob->template as<OT::cff2> ();
if (cff2 == &Null(OT::cff2))
{ fini (); return; }
{ /* parse top dict */
ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
CFF2TopDict_Interpreter top_interp;
top_interp.env.init (topDictStr);
topDict.init ();
if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
}
globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize);
varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset);
charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset);
fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset);
fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset);
if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) ||
(charStrings == &Null(CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) ||
(fdArray == &Null(CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) ||
(((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
{ fini (); return; }
num_glyphs = charStrings->count;
if (num_glyphs != sc.get_num_glyphs ())
{ fini (); return; }
fdCount = fdArray->count;
privateDicts.resize (fdCount);
/* parse font dicts and gather private dicts */
for (unsigned int i = 0; i < fdCount; i++)
{
const ByteStr fontDictStr = (*fdArray)[i];
if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
CFF2FontDictValues *font;
CFF2FontDict_Interpreter font_interp;
font_interp.env.init (fontDictStr);
font = fontDicts.push ();
font->init ();
if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL, CFF2PrivDictInterpEnv> priv_interp;
priv_interp.env.init(privDictStr);
privateDicts[i].init ();
if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset);
if (privateDicts[i].localSubrs != &Null(CFF2Subrs) &&
unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
{ fini (); return; }
}
}
inline void fini (void)
{
sc.end_processing ();
fontDicts.fini ();
privateDicts.fini_deep ();
hb_blob_destroy (blob);
blob = nullptr;
}
inline bool is_valid (void) const { return blob != nullptr; }
protected:
hb_blob_t *blob;
hb_sanitize_context_t sc;
public:
CFF2TopDictValues topDict;
const CFF2Subrs *globalSubrs;
const CFF2VariationStore *varStore;
const CFF2CharStrings *charStrings;
const CFF2FDArray *fdArray;
const CFF2FDSelect *fdSelect;
unsigned int fdCount;
hb_vector_t<CFF2FontDictValues> fontDicts;
hb_vector_t<PRIVDICTVAL> privateDicts;
unsigned int num_glyphs;
};
struct accelerator_t : accelerator_templ_t<CFF2PrivateDictOpSet, CFF2PrivateDictValues>
{
HB_INTERNAL bool get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const;
};
typedef accelerator_templ_t<CFF2PrivateDictOpSet_Subset, CFF2PrivateDictValues_Subset> accelerator_subset_t;
inline bool subset (hb_subset_plan_t *plan) const
{
hb_blob_t *cff2_prime = nullptr;
bool success = true;
if (hb_subset_cff2 (plan, &cff2_prime)) {
success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime);
hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (plan->source);
success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob);
hb_blob_destroy (head_blob);
} else {
success = false;
}
hb_blob_destroy (cff2_prime);
return success;
}
public:
FixedVersion<HBUINT8> version; /* Version of CFF2 table. set to 0x0200u */
OffsetTo<TopDict, HBUINT8, false> topDict; /* headerSize = Offset to Top DICT. */
HBUINT16 topDictSize; /* Top DICT size */
public:
DEFINE_SIZE_STATIC (5);
};
struct cff2_accelerator_t : cff2::accelerator_t {};
} /* namespace OT */
#endif /* HB_OT_CFF2_TABLE_HH */

View File

@ -28,6 +28,8 @@
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-kern-table.hh"
#include "hb-ot-name-table.hh"

View File

@ -47,6 +47,8 @@
HB_OT_ACCELERATOR(OT, post) \
HB_OT_TABLE(OT, kern) \
HB_OT_ACCELERATOR(OT, glyf) \
HB_OT_ACCELERATOR(OT, cff1) \
HB_OT_ACCELERATOR(OT, cff2) \
HB_OT_TABLE(OT, VORG) \
HB_OT_ACCELERATOR(OT, name) \
HB_OT_TABLE(OT, OS2) \

View File

@ -34,6 +34,8 @@
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-kern-table.hh"
#include "hb-ot-os2-table.hh"
@ -181,6 +183,10 @@ hb_ot_get_glyph_extents (hb_font_t *font,
bool ret = ot_face->sbix->get_extents (font, glyph, extents);
if (!ret)
ret = ot_face->glyf->get_extents (glyph, extents);
if (!ret)
ret = ot_face->cff1->get_extents (glyph, extents);
if (!ret)
ret = ot_face->cff2->get_extents (font, glyph, extents);
if (!ret)
ret = ot_face->CBDT->get_extents (font, glyph, extents);
// TODO Hook up side-bearings variations.

View File

@ -1488,6 +1488,9 @@ struct VarRegionList
axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount));
}
inline unsigned int get_region_count (void) const
{ return regionCount; }
protected:
HBUINT16 axisCount;
HBUINT16 regionCount;
@ -1499,6 +1502,9 @@ struct VarRegionList
struct VarData
{
inline unsigned int get_region_index_count (void) const
{ return regionIndices.len; }
inline unsigned int get_row_size (void) const
{ return shortCount + regionIndices.len; }
@ -1537,6 +1543,18 @@ struct VarData
return delta;
}
inline void get_scalars (int *coords, unsigned int coord_count,
const VarRegionList &regions,
float *scalars /*OUT */,
unsigned int num_scalars) const
{
assert (num_scalars == regionIndices.len);
for (unsigned int i = 0; i < num_scalars; i++)
{
scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count);
}
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -1587,6 +1605,18 @@ struct VariationStore
dataSets.sanitize (c, this));
}
inline unsigned int get_region_index_count (unsigned int ivs) const
{ return (this+dataSets[ivs]).get_region_index_count (); }
inline void get_scalars (unsigned int ivs,
int *coords, unsigned int coord_count,
float *scalars /*OUT*/,
unsigned int num_scalars) const
{
(this+dataSets[ivs]).get_scalars (coords, coord_count, this+regions,
&scalars[0], num_scalars);
}
protected:
HBUINT16 format;
LOffsetTo<VarRegionList> regions;

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2018 Adobe Systems Incorporated.
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
@ -93,7 +93,7 @@ struct VORG
unsigned int size = VertOriginMetric::static_size * subset_metrics.len;
VertOriginMetric *metrics = c.allocate_size<VertOriginMetric> (size);
if (likely (metrics != nullptr))
memcpy (metrics, &subset_metrics[0u], size);
memcpy (metrics, &subset_metrics[0], size);
else
success = false;
}

224
src/hb-subset-cff-common.cc Normal file
View File

@ -0,0 +1,224 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#include "hb-ot-cff-common.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-subset-cff-common.hh"
/* Disable FDSelect format 0 for compatibility with fonttools which doesn't seem choose it.
* Rarely any/much smaller than format 3 anyway. */
#define CFF_SERIALIZE_FDSELECT_0 0
using namespace CFF;
/**
* hb_plan_subset_cff_fdselect
* Determine an optimal FDSelect format according to a provided plan.
*
* Return value: FDSelect format, size, and ranges for the most compact subset FDSelect
* along with a font index remapping table
**/
bool
hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
unsigned int fdCount,
const FDSelect &src, /* IN */
unsigned int &subset_fd_count /* OUT */,
unsigned int &subset_fdselect_size /* OUT */,
unsigned int &subset_fdselect_format /* OUT */,
hb_vector_t<code_pair> &fdselect_ranges /* OUT */,
Remap &fdmap /* OUT */)
{
subset_fd_count = 0;
subset_fdselect_size = 0;
subset_fdselect_format = 0;
unsigned int num_ranges = 0;
unsigned int subset_num_glyphs = glyphs.len;
if (subset_num_glyphs == 0)
return true;
{
/* use hb_set to determine the subset of font dicts */
hb_set_t *set = hb_set_create ();
if (set == &Null (hb_set_t))
return false;
hb_codepoint_t prev_fd = CFF_UNDEF_CODE;
for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
{
hb_codepoint_t fd = src.get_fd (glyphs[i]);
set->add (fd);
if (fd != prev_fd)
{
num_ranges++;
prev_fd = fd;
code_pair pair = { fd, i };
fdselect_ranges.push (pair);
}
}
subset_fd_count = set->get_population ();
if (subset_fd_count == fdCount)
{
/* all font dicts belong to the subset. no need to subset FDSelect & FDArray */
fdmap.identity (fdCount);
hb_set_destroy (set);
}
else
{
/* create a fdmap */
if (!fdmap.reset (fdCount))
{
hb_set_destroy (set);
return false;
}
hb_codepoint_t fd = CFF_UNDEF_CODE;
while (set->next (&fd))
fdmap.add (fd);
assert (fdmap.get_count () == subset_fd_count);
hb_set_destroy (set);
}
/* update each font dict index stored as "code" in fdselect_ranges */
for (unsigned int i = 0; i < fdselect_ranges.len; i++)
fdselect_ranges[i].code = fdmap[fdselect_ranges[i].code];
}
/* determine which FDSelect format is most compact */
if (subset_fd_count > 0xFF)
{
assert (src.format == 4);
subset_fdselect_format = 4;
subset_fdselect_size = FDSelect::min_size + FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges + HBUINT32::static_size;
}
else
{
#if CFF_SERIALIZE_FDSELECT_0
unsigned int format0_size = FDSelect::min_size + FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs;
#endif
unsigned int format3_size = FDSelect::min_size + FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges + HBUINT16::static_size;
#if CFF_SERIALIZE_FDSELECT_0
if (format0_size <= format3_size)
{
// subset_fdselect_format = 0;
subset_fdselect_size = format0_size;
}
else
#endif
{
subset_fdselect_format = 3;
subset_fdselect_size = format3_size;
}
}
return true;
}
template <typename FDSELECT3_4>
static inline bool
serialize_fdselect_3_4 (hb_serialize_context_t *c,
const unsigned int num_glyphs,
const FDSelect &src,
unsigned int size,
const hb_vector_t<code_pair> &fdselect_ranges)
{
TRACE_SERIALIZE (this);
FDSELECT3_4 *p = c->allocate_size<FDSELECT3_4> (size);
if (unlikely (p == nullptr)) return_trace (false);
p->nRanges.set (fdselect_ranges.len);
for (unsigned int i = 0; i < fdselect_ranges.len; i++)
{
p->ranges[i].first.set (fdselect_ranges[i].glyph);
p->ranges[i].fd.set (fdselect_ranges[i].code);
}
p->sentinel().set (num_glyphs);
return_trace (true);
}
/**
* hb_serialize_cff_fdselect
* Serialize a subset FDSelect format planned above.
**/
bool
hb_serialize_cff_fdselect (hb_serialize_context_t *c,
const unsigned int num_glyphs,
const FDSelect &src,
unsigned int fd_count,
unsigned int fdselect_format,
unsigned int size,
const hb_vector_t<code_pair> &fdselect_ranges)
{
TRACE_SERIALIZE (this);
FDSelect *p = c->allocate_min<FDSelect> ();
if (unlikely (p == nullptr)) return_trace (false);
p->format.set (fdselect_format);
size -= FDSelect::min_size;
switch (fdselect_format)
{
#if CFF_SERIALIZE_FDSELECT_0
case 0:
{
FDSelect0 *p = c->allocate_size<FDSelect0> (size);
if (unlikely (p == nullptr)) return_trace (false);
unsigned int range_index = 0;
unsigned int fd = fdselect_ranges[range_index++].code;
for (unsigned int i = 0; i < num_glyphs; i++)
{
if ((range_index < fdselect_ranges.len) &&
(i >= fdselect_ranges[range_index].glyph))
{
fd = fdselect_ranges[range_index++].code;
}
p->fds[i].set (fd);
}
break;
}
#endif /* CFF_SERIALIZE_FDSELECT_0 */
case 3:
return serialize_fdselect_3_4<FDSelect3> (c,
num_glyphs,
src,
size,
fdselect_ranges);
case 4:
return serialize_fdselect_3_4<FDSelect4> (c,
num_glyphs,
src,
size,
fdselect_ranges);
default:
assert(false);
}
return_trace (true);
}

993
src/hb-subset-cff-common.hh Normal file
View File

@ -0,0 +1,993 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_SUBSET_CFF_COMMON_HH
#define HB_SUBSET_CFF_COMMON_HH
#include "hb.hh"
#include "hb-subset-plan.hh"
#include "hb-cff-interp-cs-common.hh"
namespace CFF {
/* Used for writing a temporary charstring */
struct StrEncoder
{
inline StrEncoder (StrBuff &buff_)
: buff (buff_), error (false)
{}
inline void reset (void)
{
buff.resize (0);
}
inline void encode_byte (unsigned char b)
{
if (unlikely (buff.push ((const char)b) == &Crap(char)))
set_error ();
}
inline void encode_int (int v)
{
if ((-1131 <= v) && (v <= 1131))
{
if ((-107 <= v) && (v <= 107))
encode_byte (v + 139);
else if (v > 0)
{
v -= 108;
encode_byte ((v >> 8) + OpCode_TwoBytePosInt0);
encode_byte (v & 0xFF);
}
else
{
v = -v - 108;
encode_byte ((v >> 8) + OpCode_TwoByteNegInt0);
encode_byte (v & 0xFF);
}
}
else
{
if (unlikely (v < -32768))
v = -32768;
else if (unlikely (v > 32767))
v = 32767;
encode_byte (OpCode_shortint);
encode_byte ((v >> 8) & 0xFF);
encode_byte (v & 0xFF);
}
}
inline void encode_num (const Number& n)
{
if (n.in_int_range ())
{
encode_int (n.to_int ());
}
else
{
int32_t v = n.to_fixed ();
encode_byte (OpCode_fixedcs);
encode_byte ((v >> 24) & 0xFF);
encode_byte ((v >> 16) & 0xFF);
encode_byte ((v >> 8) & 0xFF);
encode_byte (v & 0xFF);
}
}
inline void encode_op (OpCode op)
{
if (Is_OpCode_ESC (op))
{
encode_byte (OpCode_escape);
encode_byte (Unmake_OpCode_ESC (op));
}
else
encode_byte (op);
}
inline void copy_str (const ByteStr &str)
{
unsigned int offset = buff.len;
buff.resize (offset + str.len);
if (unlikely (buff.len < offset + str.len))
{
set_error ();
return;
}
memcpy (&buff[offset], &str.str[0], str.len);
}
inline bool is_error (void) const { return error; }
protected:
inline void set_error (void) { error = true; }
StrBuff &buff;
bool error;
};
struct CFFSubTableOffsets {
inline CFFSubTableOffsets (void)
: privateDictsOffset (0)
{
topDictInfo.init ();
FDSelectInfo.init ();
FDArrayInfo.init ();
charStringsInfo.init ();
globalSubrsInfo.init ();
localSubrsInfos.init ();
}
inline ~CFFSubTableOffsets (void)
{
localSubrsInfos.fini ();
}
TableInfo topDictInfo;
TableInfo FDSelectInfo;
TableInfo FDArrayInfo;
TableInfo charStringsInfo;
unsigned int privateDictsOffset;
TableInfo globalSubrsInfo;
hb_vector_t<TableInfo> localSubrsInfos;
};
template <typename OPSTR=OpStr>
struct CFFTopDict_OpSerializer : OpSerializer
{
inline bool serialize (hb_serialize_context_t *c,
const OPSTR &opstr,
const CFFSubTableOffsets &offsets) const
{
TRACE_SERIALIZE (this);
switch (opstr.op)
{
case OpCode_CharStrings:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset));
case OpCode_FDArray:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset));
case OpCode_FDSelect:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
default:
return_trace (copy_opstr (c, opstr));
}
return_trace (true);
}
inline unsigned int calculate_serialized_size (const OPSTR &opstr) const
{
switch (opstr.op)
{
case OpCode_CharStrings:
case OpCode_FDArray:
case OpCode_FDSelect:
return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
default:
return opstr.str.len;
}
}
};
struct CFFFontDict_OpSerializer : OpSerializer
{
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
const TableInfo &privateDictInfo) const
{
TRACE_SERIALIZE (this);
if (opstr.op == OpCode_Private)
{
/* serialize the private dict size & offset as 2-byte & 4-byte integers */
if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) ||
!UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset)))
return_trace (false);
/* serialize the opcode */
HBUINT8 *p = c->allocate_size<HBUINT8> (1);
if (unlikely (p == nullptr)) return_trace (false);
p->set (OpCode_Private);
return_trace (true);
}
else
{
HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
if (unlikely (d == nullptr)) return_trace (false);
memcpy (d, &opstr.str.str[0], opstr.str.len);
}
return_trace (true);
}
inline unsigned int calculate_serialized_size (const OpStr &opstr) const
{
if (opstr.op == OpCode_Private)
return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
else
return opstr.str.len;
}
};
struct CFFPrivateDict_OpSerializer : OpSerializer
{
inline CFFPrivateDict_OpSerializer (bool desubroutinize_, bool drop_hints_)
: desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
const unsigned int subrsOffset) const
{
TRACE_SERIALIZE (this);
if (drop_hints && DictOpSet::is_hint_op (opstr.op))
return true;
if (opstr.op == OpCode_Subrs)
{
if (desubroutinize || (subrsOffset == 0))
return_trace (true);
else
return_trace (FontDict::serialize_offset2_op (c, opstr.op, subrsOffset));
}
else
return_trace (copy_opstr (c, opstr));
}
inline unsigned int calculate_serialized_size (const OpStr &opstr,
bool has_localsubr=true) const
{
if (drop_hints && DictOpSet::is_hint_op (opstr.op))
return 0;
if (opstr.op == OpCode_Subrs)
{
if (desubroutinize || !has_localsubr)
return 0;
else
return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (opstr.op);
}
else
return opstr.str.len;
}
protected:
const bool desubroutinize;
const bool drop_hints;
};
struct FlattenParam
{
StrBuff &flatStr;
bool drop_hints;
};
template <typename ACC, typename ENV, typename OPSET>
struct SubrFlattener
{
inline SubrFlattener (const ACC &acc_,
const hb_vector_t<hb_codepoint_t> &glyphs_,
bool drop_hints_)
: acc (acc_),
glyphs (glyphs_),
drop_hints (drop_hints_)
{}
inline bool flatten (StrBuffArray &flat_charstrings)
{
if (!flat_charstrings.resize (glyphs.len))
return false;
for (unsigned int i = 0; i < glyphs.len; i++)
flat_charstrings[i].init ();
for (unsigned int i = 0; i < glyphs.len; i++)
{
hb_codepoint_t glyph = glyphs[i];
const ByteStr str = (*acc.charStrings)[glyph];
unsigned int fd = acc.fdSelect->get_fd (glyph);
CSInterpreter<ENV, OPSET, FlattenParam> interp;
interp.env.init (str, acc, fd);
FlattenParam param = { flat_charstrings[i], drop_hints };
if (unlikely (!interp.interpret (param)))
return false;
}
return true;
}
const ACC &acc;
const hb_vector_t<hb_codepoint_t> &glyphs;
bool drop_hints;
};
struct SubrClosures
{
inline SubrClosures (void)
: valid (false),
global_closure (nullptr)
{
local_closures.init ();
}
inline void init (unsigned int fd_count)
{
valid = true;
global_closure = hb_set_create ();
if (global_closure == hb_set_get_empty ())
valid = false;
if (!local_closures.resize (fd_count))
valid = false;
for (unsigned int i = 0; i < local_closures.len; i++)
{
local_closures[i] = hb_set_create ();
if (local_closures[i] == hb_set_get_empty ())
valid = false;
}
}
inline void fini (void)
{
hb_set_destroy (global_closure);
for (unsigned int i = 0; i < local_closures.len; i++)
hb_set_destroy (local_closures[i]);
local_closures.fini ();
}
inline void reset (void)
{
hb_set_clear (global_closure);
for (unsigned int i = 0; i < local_closures.len; i++)
hb_set_clear (local_closures[i]);
}
bool is_valid (void) const { return valid; }
bool valid;
hb_set_t *global_closure;
hb_vector_t<hb_set_t *> local_closures;
};
struct ParsedCSOp : OpStr
{
inline void init (unsigned int subr_num_ = 0)
{
OpStr::init ();
subr_num = subr_num_;
drop_flag = false;
keep_flag = false;
skip_flag = false;
}
inline void fini (void)
{
OpStr::fini ();
}
inline bool for_drop (void) const { return drop_flag; }
inline void set_drop (void) { if (!for_keep ()) drop_flag = true; }
inline bool for_keep (void) const { return keep_flag; }
inline void set_keep (void) { keep_flag = true; }
inline bool for_skip (void) const { return skip_flag; }
inline void set_skip (void) { skip_flag = true; }
unsigned int subr_num;
protected:
bool drop_flag : 1;
bool keep_flag : 1;
bool skip_flag : 1;
};
struct ParsedCStr : ParsedValues<ParsedCSOp>
{
inline void init (void)
{
SUPER::init ();
parsed = false;
hint_dropped = false;
has_prefix_ = false;
}
inline void add_op (OpCode op, const SubByteStr& substr)
{
if (!is_parsed ())
SUPER::add_op (op, substr);
}
inline void add_call_op (OpCode op, const SubByteStr& substr, unsigned int subr_num)
{
if (!is_parsed ())
{
unsigned int parsed_len = get_count ();
if (likely (parsed_len > 0))
values[parsed_len-1].set_skip ();
ParsedCSOp val;
val.init (subr_num);
SUPER::add_op (op, substr, val);
}
}
inline void set_prefix (const Number &num, OpCode op = OpCode_Invalid)
{
has_prefix_ = true;
prefix_op_ = op;
prefix_num_ = num;
}
inline bool at_end (unsigned int pos) const
{
return ((pos + 1 >= values.len) /* CFF2 */
|| (values[pos + 1].op == OpCode_return));
}
inline bool is_parsed (void) const { return parsed; }
inline void set_parsed (void) { parsed = true; }
inline bool is_hint_dropped (void) const { return hint_dropped; }
inline void set_hint_dropped (void) { hint_dropped = true; }
inline bool is_vsindex_dropped (void) const { return vsindex_dropped; }
inline void set_vsindex_dropped (void) { vsindex_dropped = true; }
inline bool has_prefix (void) const { return has_prefix_; }
inline OpCode prefix_op (void) const { return prefix_op_; }
inline const Number &prefix_num (void) const { return prefix_num_; }
protected:
bool parsed;
bool hint_dropped;
bool vsindex_dropped;
bool has_prefix_;
OpCode prefix_op_;
Number prefix_num_;
private:
typedef ParsedValues<ParsedCSOp> SUPER;
};
struct ParsedCStrs : hb_vector_t<ParsedCStr>
{
inline void init (unsigned int len_ = 0)
{
SUPER::init ();
resize (len_);
for (unsigned int i = 0; i < len; i++)
(*this)[i].init ();
}
inline void fini (void)
{
SUPER::fini_deep ();
}
private:
typedef hb_vector_t<ParsedCStr> SUPER;
};
struct SubrSubsetParam
{
inline void init (ParsedCStr *parsed_charstring_,
ParsedCStrs *parsed_global_subrs_, ParsedCStrs *parsed_local_subrs_,
hb_set_t *global_closure_, hb_set_t *local_closure_,
bool drop_hints_)
{
parsed_charstring = parsed_charstring_;
current_parsed_str = parsed_charstring;
parsed_global_subrs = parsed_global_subrs_;
parsed_local_subrs = parsed_local_subrs_;
global_closure = global_closure_;
local_closure = local_closure_;
drop_hints = drop_hints_;
}
inline ParsedCStr *get_parsed_str_for_context (CallContext &context)
{
switch (context.type)
{
case CSType_CharString:
return parsed_charstring;
case CSType_LocalSubr:
if (likely (context.subr_num < parsed_local_subrs->len))
return &(*parsed_local_subrs)[context.subr_num];
break;
case CSType_GlobalSubr:
if (likely (context.subr_num < parsed_global_subrs->len))
return &(*parsed_global_subrs)[context.subr_num];
break;
}
return nullptr;
}
template <typename ENV>
inline void set_current_str (ENV &env)
{
ParsedCStr *parsed_str = get_parsed_str_for_context (env.context);
if (likely (parsed_str != nullptr))
current_parsed_str = parsed_str;
else
env.set_error ();
}
ParsedCStr *current_parsed_str;
ParsedCStr *parsed_charstring;
ParsedCStrs *parsed_global_subrs;
ParsedCStrs *parsed_local_subrs;
hb_set_t *global_closure;
hb_set_t *local_closure;
bool drop_hints;
};
struct SubrRemap : Remap
{
inline void create (hb_set_t *closure)
{
/* create a remapping of subroutine numbers from old to new.
* no optimization based on usage counts. fonttools doesn't appear doing that either.
*/
reset (closure->get_max () + 1);
for (hb_codepoint_t old_num = 0; old_num < len; old_num++)
{
if (hb_set_has (closure, old_num))
add (old_num);
}
if (get_count () < 1240)
bias = 107;
else if (get_count () < 33900)
bias = 1131;
else
bias = 32768;
}
inline hb_codepoint_t operator[] (unsigned int old_num) const
{
if (old_num >= len)
return CFF_UNDEF_CODE;
else
return Remap::operator[] (old_num);
}
inline int biased_num (unsigned int old_num) const
{
hb_codepoint_t new_num = (*this)[old_num];
assert (new_num != CFF_UNDEF_CODE);
return (int)new_num - bias;
}
protected:
int bias;
};
struct SubrRemaps
{
inline SubrRemaps (void)
{
global_remap.init ();
local_remaps.init ();
}
inline ~SubrRemaps (void)
{
fini ();
}
inline void init (unsigned int fdCount)
{
local_remaps.resize (fdCount);
for (unsigned int i = 0; i < fdCount; i++)
local_remaps[i].init ();
}
inline void create (SubrClosures& closures)
{
global_remap.create (closures.global_closure);
for (unsigned int i = 0; i < local_remaps.len; i++)
local_remaps[i].create (closures.local_closures[i]);
}
inline void fini (void)
{
global_remap.fini ();
local_remaps.fini_deep ();
}
SubrRemap global_remap;
hb_vector_t<SubrRemap> local_remaps;
};
template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET>
struct SubrSubsetter
{
inline SubrSubsetter (void)
{
parsed_charstrings.init ();
parsed_global_subrs.init ();
parsed_local_subrs.init ();
}
inline ~SubrSubsetter (void)
{
closures.fini ();
remaps.fini ();
parsed_charstrings.fini_deep ();
parsed_global_subrs.fini_deep ();
parsed_local_subrs.fini_deep ();
}
/* Subroutine subsetting with --no-desubroutinize runs in phases:
*
* 1. execute charstrings/subroutines to determine subroutine closures
* 2. parse out all operators and numbers
* 3. mark hint operators and operands for removal if --no-hinting
* 4. re-encode all charstrings and subroutines with new subroutine numbers
*
* Phases #1 and #2 are done at the same time in collect_subrs ().
* Phase #3 walks charstrings/subroutines forward then backward (hence parsing required),
* because we can't tell if a number belongs to a hint op until we see the first moveto.
*
* Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number
* within the same charstring/subroutine, e.g., not split across a charstring and a subroutine.
*/
inline bool subset (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, bool drop_hints)
{
closures.init (acc.fdCount);
remaps.init (acc.fdCount);
parsed_charstrings.init (glyphs.len);
parsed_global_subrs.init (acc.globalSubrs->count);
parsed_local_subrs.resize (acc.fdCount);
for (unsigned int i = 0; i < acc.fdCount; i++)
{
parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count);
}
if (unlikely (!closures.valid))
return false;
/* phase 1 & 2 */
for (unsigned int i = 0; i < glyphs.len; i++)
{
hb_codepoint_t glyph = glyphs[i];
const ByteStr str = (*acc.charStrings)[glyph];
unsigned int fd = acc.fdSelect->get_fd (glyph);
CSInterpreter<ENV, OPSET, SubrSubsetParam> interp;
interp.env.init (str, acc, fd);
SubrSubsetParam param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
closures.global_closure, closures.local_closures[fd],
drop_hints);
if (unlikely (!interp.interpret (param)))
return false;
/* finalize parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
SUBSETTER::finalize_parsed_str (interp.env, param, parsed_charstrings[i]);
}
if (drop_hints)
{
/* mark hint ops and arguments for drop */
for (unsigned int i = 0; i < glyphs.len; i++)
{
unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
SubrSubsetParam param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
closures.global_closure, closures.local_closures[fd],
drop_hints);
DropHintsParam drop;
if (drop_hints_in_str (parsed_charstrings[i], param, drop))
{
parsed_charstrings[i].set_hint_dropped ();
if (drop.vsindex_dropped)
parsed_charstrings[i].set_vsindex_dropped ();
}
}
/* after dropping hints recreate closures of actually used subrs */
closures.reset ();
for (unsigned int i = 0; i < glyphs.len; i++)
{
unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
SubrSubsetParam param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
closures.global_closure, closures.local_closures[fd],
drop_hints);
collect_subr_refs_in_str (parsed_charstrings[i], param);
}
}
remaps.create (closures);
return true;
}
inline bool encode_charstrings (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, StrBuffArray &buffArray) const
{
if (unlikely (!buffArray.resize (glyphs.len)))
return false;
for (unsigned int i = 0; i < glyphs.len; i++)
{
unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i])))
return false;
}
return true;
}
inline bool encode_subrs (const ParsedCStrs &subrs, const SubrRemap& remap, unsigned int fd, StrBuffArray &buffArray) const
{
unsigned int count = remap.get_count ();
if (unlikely (!buffArray.resize (count)))
return false;
for (unsigned int old_num = 0; old_num < subrs.len; old_num++)
{
hb_codepoint_t new_num = remap[old_num];
if (new_num != CFF_UNDEF_CODE)
{
if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num])))
return false;
}
}
return true;
}
inline bool encode_globalsubrs (StrBuffArray &buffArray)
{
return encode_subrs (parsed_global_subrs, remaps.global_remap, 0, buffArray);
}
inline bool encode_localsubrs (unsigned int fd, StrBuffArray &buffArray) const
{
return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], fd, buffArray);
}
protected:
struct DropHintsParam
{
inline DropHintsParam (void)
: seen_moveto (false),
ends_in_hint (false),
vsindex_dropped (false) {}
bool seen_moveto;
bool ends_in_hint;
bool vsindex_dropped;
};
inline bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos,
ParsedCStrs &subrs, unsigned int subr_num,
const SubrSubsetParam &param, DropHintsParam &drop)
{
drop.ends_in_hint = false;
bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop);
/* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto),
* then this entire subroutine must be a hint. drop its call. */
if (drop.ends_in_hint)
{
str.values[pos].set_drop ();
/* if this subr call is at the end of the parent subr, propagate the flag
* otherwise reset the flag */
if (!str.at_end (pos))
drop.ends_in_hint = false;
}
return has_hint;
}
/* returns true if it sees a hint op before the first moveto */
inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam &param, DropHintsParam &drop)
{
bool seen_hint = false;
for (unsigned int pos = 0; pos < str.values.len; pos++)
{
bool has_hint = false;
switch (str.values[pos].op)
{
case OpCode_callsubr:
has_hint = drop_hints_in_subr (str, pos,
*param.parsed_local_subrs, str.values[pos].subr_num,
param, drop);
break;
case OpCode_callgsubr:
has_hint = drop_hints_in_subr (str, pos,
*param.parsed_global_subrs, str.values[pos].subr_num,
param, drop);
break;
case OpCode_rmoveto:
case OpCode_hmoveto:
case OpCode_vmoveto:
drop.seen_moveto = true;
break;
case OpCode_hintmask:
case OpCode_cntrmask:
if (drop.seen_moveto)
{
str.values[pos].set_drop ();
break;
}
HB_FALLTHROUGH;
case OpCode_hstemhm:
case OpCode_vstemhm:
case OpCode_hstem:
case OpCode_vstem:
has_hint = true;
str.values[pos].set_drop ();
if (str.at_end (pos))
drop.ends_in_hint = true;
break;
case OpCode_dotsection:
str.values[pos].set_drop ();
break;
default:
/* NONE */
break;
}
if (has_hint)
{
for (int i = pos - 1; i >= 0; i--)
{
ParsedCSOp &csop = str.values[(unsigned)i];
if (csop.for_drop ())
break;
csop.set_drop ();
if (csop.op == OpCode_vsindexcs)
drop.vsindex_dropped = true;
}
seen_hint |= has_hint;
}
}
return seen_hint;
}
inline void collect_subr_refs_in_subr (ParsedCStr &str, unsigned int pos,
unsigned int subr_num, ParsedCStrs &subrs,
hb_set_t *closure,
const SubrSubsetParam &param)
{
hb_set_add (closure, subr_num);
collect_subr_refs_in_str (subrs[subr_num], param);
}
inline void collect_subr_refs_in_str (ParsedCStr &str, const SubrSubsetParam &param)
{
for (unsigned int pos = 0; pos < str.values.len; pos++)
{
if (!str.values[pos].for_drop ())
{
switch (str.values[pos].op)
{
case OpCode_callsubr:
collect_subr_refs_in_subr (str, pos,
str.values[pos].subr_num, *param.parsed_local_subrs,
param.local_closure, param);
break;
case OpCode_callgsubr:
collect_subr_refs_in_subr (str, pos,
str.values[pos].subr_num, *param.parsed_global_subrs,
param.global_closure, param);
break;
default: break;
}
}
}
}
inline bool encode_str (const ParsedCStr &str, const unsigned int fd, StrBuff &buff) const
{
buff.init ();
StrEncoder encoder (buff);
encoder.reset ();
/* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
* re-insert it at the beginning of charstreing */
if (str.has_prefix () && str.is_hint_dropped ())
{
encoder.encode_num (str.prefix_num ());
if (str.prefix_op () != OpCode_Invalid)
encoder.encode_op (str.prefix_op ());
}
for (unsigned int i = 0; i < str.get_count(); i++)
{
const ParsedCSOp &opstr = str.values[i];
if (!opstr.for_drop () && !opstr.for_skip ())
{
switch (opstr.op)
{
case OpCode_callsubr:
encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num));
encoder.encode_op (OpCode_callsubr);
break;
case OpCode_callgsubr:
encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num));
encoder.encode_op (OpCode_callgsubr);
break;
default:
encoder.copy_str (opstr.str);
break;
}
}
}
return !encoder.is_error ();
}
protected:
SubrClosures closures;
ParsedCStrs parsed_charstrings;
ParsedCStrs parsed_global_subrs;
hb_vector_t<ParsedCStrs> parsed_local_subrs;
SubrRemaps remaps;
private:
typedef typename SUBRS::count_type subr_count_type;
};
}; /* namespace CFF */
HB_INTERNAL bool
hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
unsigned int fdCount,
const CFF::FDSelect &src, /* IN */
unsigned int &subset_fd_count /* OUT */,
unsigned int &subset_fdselect_size /* OUT */,
unsigned int &subset_fdselect_format /* OUT */,
hb_vector_t<CFF::code_pair> &fdselect_ranges /* OUT */,
CFF::Remap &fdmap /* OUT */);
HB_INTERNAL bool
hb_serialize_cff_fdselect (hb_serialize_context_t *c,
unsigned int num_glyphs,
const CFF::FDSelect &src,
unsigned int fd_count,
unsigned int fdselect_format,
unsigned int size,
const hb_vector_t<CFF::code_pair> &fdselect_ranges);
#endif /* HB_SUBSET_CFF_COMMON_HH */

1105
src/hb-subset-cff1.cc Normal file

File diff suppressed because it is too large Load Diff

38
src/hb-subset-cff1.hh Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_SUBSET_CFF1_HH
#define HB_SUBSET_CFF1_HH
#include "hb.hh"
#include "hb-subset-plan.hh"
HB_INTERNAL bool
hb_subset_cff1 (hb_subset_plan_t *plan,
hb_blob_t **cff_prime /* OUT */);
#endif /* HB_SUBSET_CFF1_HH */

616
src/hb-subset-cff2.cc Normal file
View File

@ -0,0 +1,616 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#include "hb-open-type.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-set.h"
#include "hb-subset-cff2.hh"
#include "hb-subset-plan.hh"
#include "hb-subset-cff-common.hh"
#include "hb-cff2-interp-cs.hh"
using namespace CFF;
struct CFF2SubTableOffsets : CFFSubTableOffsets
{
inline CFF2SubTableOffsets (void)
: CFFSubTableOffsets (),
varStoreOffset (0)
{}
unsigned int varStoreOffset;
};
struct CFF2TopDict_OpSerializer : CFFTopDict_OpSerializer<>
{
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
const CFF2SubTableOffsets &offsets) const
{
TRACE_SERIALIZE (this);
switch (opstr.op)
{
case OpCode_vstore:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset));
default:
return_trace (CFFTopDict_OpSerializer<>::serialize (c, opstr, offsets));
}
}
inline unsigned int calculate_serialized_size (const OpStr &opstr) const
{
switch (opstr.op)
{
case OpCode_vstore:
return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
default:
return CFFTopDict_OpSerializer<>::calculate_serialized_size (opstr);
}
}
};
struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
{
static inline void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
{
switch (op)
{
case OpCode_return:
case OpCode_endchar:
/* dummy opcodes in CFF2. ignore */
break;
case OpCode_hstem:
case OpCode_hstemhm:
case OpCode_vstem:
case OpCode_vstemhm:
case OpCode_hintmask:
case OpCode_cntrmask:
if (param.drop_hints)
{
env.clear_args ();
return;
}
HB_FALLTHROUGH;
default:
SUPER::flush_args_and_op (op, env, param);
break;
}
}
static inline void flush_args (CFF2CSInterpEnv &env, FlattenParam& param)
{
for (unsigned int i = 0; i < env.argStack.get_count ();)
{
const BlendArg &arg = env.argStack[i];
if (arg.blending ())
{
assert ((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues));
flatten_blends (arg, i, env, param);
i += arg.numValues;
}
else
{
StrEncoder encoder (param.flatStr);
encoder.encode_num (arg);
i++;
}
}
SUPER::flush_args (env, param);
}
static inline void flatten_blends (const BlendArg &arg, unsigned int i, CFF2CSInterpEnv &env, FlattenParam& param)
{
/* flatten the default values */
StrEncoder encoder (param.flatStr);
for (unsigned int j = 0; j < arg.numValues; j++)
{
const BlendArg &arg1 = env.argStack[i + j];
assert (arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
(arg1.deltas.len == env.get_region_count ()));
encoder.encode_num (arg1);
}
/* flatten deltas for each value */
for (unsigned int j = 0; j < arg.numValues; j++)
{
const BlendArg &arg1 = env.argStack[i + j];
for (unsigned int k = 0; k < arg1.deltas.len; k++)
encoder.encode_num (arg1.deltas[k]);
}
/* flatten the number of values followed by blend operator */
encoder.encode_int (arg.numValues);
encoder.encode_op (OpCode_blendcs);
}
static inline void flush_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
{
switch (op)
{
case OpCode_return:
case OpCode_endchar:
return;
default:
StrEncoder encoder (param.flatStr);
encoder.encode_op (op);
}
}
private:
typedef CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam> SUPER;
typedef CSOpSet<BlendArg, CFF2CSOpSet_Flatten, CFF2CSOpSet_Flatten, CFF2CSInterpEnv, FlattenParam> CSOPSET;
};
struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet<CFF2CSOpSet_SubrSubset, SubrSubsetParam>
{
static inline void process_op (OpCode op, CFF2CSInterpEnv &env, SubrSubsetParam& param)
{
switch (op) {
case OpCode_return:
param.current_parsed_str->set_parsed ();
env.returnFromSubr ();
param.set_current_str (env);
break;
case OpCode_endchar:
param.current_parsed_str->set_parsed ();
SUPER::process_op (op, env, param);
break;
case OpCode_callsubr:
process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
break;
case OpCode_callgsubr:
process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
break;
default:
SUPER::process_op (op, env, param);
param.current_parsed_str->add_op (op, env.substr);
break;
}
}
protected:
static inline void process_call_subr (OpCode op, CSType type,
CFF2CSInterpEnv &env, SubrSubsetParam& param,
CFF2BiasedSubrs& subrs, hb_set_t *closure)
{
SubByteStr substr = env.substr;
env.callSubr (subrs, type);
param.current_parsed_str->add_call_op (op, substr, env.context.subr_num);
hb_set_add (closure, env.context.subr_num);
param.set_current_str (env);
}
private:
typedef CFF2CSOpSet<CFF2CSOpSet_SubrSubset, SubrSubsetParam> SUPER;
};
struct CFF2SubrSubsetter : SubrSubsetter<CFF2SubrSubsetter, CFF2Subrs, const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubrSubset>
{
static inline void finalize_parsed_str (CFF2CSInterpEnv &env, SubrSubsetParam& param, ParsedCStr &charstring)
{
/* vsindex is inserted at the beginning of the charstring as necessary */
if (env.seen_vsindex ())
{
Number ivs;
ivs.set_int ((int)env.get_ivs ());
charstring.set_prefix (ivs, OpCode_vsindexcs);
}
}
};
struct cff2_subset_plan {
inline cff2_subset_plan (void)
: final_size (0),
orig_fdcount (0),
subset_fdcount(1),
subset_fdselect_format (0),
drop_hints (false),
desubroutinize (false)
{
subset_fdselect_ranges.init ();
fdmap.init ();
subset_charstrings.init ();
subset_globalsubrs.init ();
subset_localsubrs.init ();
privateDictInfos.init ();
}
inline ~cff2_subset_plan (void)
{
subset_fdselect_ranges.fini ();
fdmap.fini ();
subset_charstrings.fini_deep ();
subset_globalsubrs.fini_deep ();
subset_localsubrs.fini_deep ();
privateDictInfos.fini ();
}
inline bool create (const OT::cff2::accelerator_subset_t &acc,
hb_subset_plan_t *plan)
{
final_size = 0;
orig_fdcount = acc.fdArray->count;
drop_hints = plan->drop_hints;
desubroutinize = plan->desubroutinize;
/* CFF2 header */
final_size += OT::cff2::static_size;
/* top dict */
{
CFF2TopDict_OpSerializer topSzr;
offsets.topDictInfo.size = TopDict::calculate_serialized_size (acc.topDict, topSzr);
final_size += offsets.topDictInfo.size;
}
if (desubroutinize)
{
/* Flatten global & local subrs */
SubrFlattener<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_Flatten>
flattener(acc, plan->glyphs, plan->drop_hints);
if (!flattener.flatten (subset_charstrings))
return false;
/* no global/local subroutines */
offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (1, 0, 0);
}
else
{
/* Subset subrs: collect used subroutines, leaving all unused ones behind */
if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
return false;
/* encode charstrings, global subrs, local subrs with new subroutine numbers */
if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
return false;
if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
return false;
/* global subrs */
unsigned int dataSize = subset_globalsubrs.total_size ();
offsets.globalSubrsInfo.offSize = calcOffSize (dataSize);
offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.len, dataSize);
/* local subrs */
if (!offsets.localSubrsInfos.resize (orig_fdcount))
return false;
if (!subset_localsubrs.resize (orig_fdcount))
return false;
for (unsigned int fd = 0; fd < orig_fdcount; fd++)
{
subset_localsubrs[fd].init ();
offsets.localSubrsInfos[fd].init ();
if (fdmap.includes (fd))
{
if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
return false;
unsigned int dataSize = subset_localsubrs[fd].total_size ();
if (dataSize > 0)
{
offsets.localSubrsInfos[fd].offset = final_size;
offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize);
offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize);
}
}
}
}
/* global subrs */
offsets.globalSubrsInfo.offset = final_size;
final_size += offsets.globalSubrsInfo.size;
/* variation store */
if (acc.varStore != &Null(CFF2VariationStore))
{
offsets.varStoreOffset = final_size;
final_size += acc.varStore->get_size ();
}
/* FDSelect */
if (acc.fdSelect != &Null(CFF2FDSelect))
{
offsets.FDSelectInfo.offset = final_size;
if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
orig_fdcount,
*(const FDSelect *)acc.fdSelect,
subset_fdcount,
offsets.FDSelectInfo.size,
subset_fdselect_format,
subset_fdselect_ranges,
fdmap)))
return false;
final_size += offsets.FDSelectInfo.size;
}
else
fdmap.identity (1);
/* FDArray (FDIndex) */
{
offsets.FDArrayInfo.offset = final_size;
CFFFontDict_OpSerializer fontSzr;
unsigned int dictsSize = 0;
for (unsigned int i = 0; i < acc.fontDicts.len; i++)
if (fdmap.includes (i))
dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
offsets.FDArrayInfo.offSize = calcOffSize (dictsSize);
final_size += CFF2Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize);
}
/* CharStrings */
{
offsets.charStringsInfo.offset = final_size;
unsigned int dataSize = subset_charstrings.total_size ();
offsets.charStringsInfo.offSize = calcOffSize (dataSize);
final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
}
/* private dicts & local subrs */
offsets.privateDictsOffset = final_size;
for (unsigned int i = 0; i < orig_fdcount; i++)
{
if (fdmap.includes (i))
{
bool has_localsubrs = offsets.localSubrsInfos[i].size > 0;
CFFPrivateDict_OpSerializer privSzr (desubroutinize, drop_hints);
unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs);
TableInfo privInfo = { final_size, priv_size, 0 };
privateDictInfos.push (privInfo);
final_size += privInfo.size;
if (!plan->desubroutinize && has_localsubrs)
{
offsets.localSubrsInfos[i].offset = final_size;
final_size += offsets.localSubrsInfos[i].size;
}
}
}
return true;
}
inline unsigned int get_final_size (void) const { return final_size; }
unsigned int final_size;
CFF2SubTableOffsets offsets;
unsigned int orig_fdcount;
unsigned int subset_fdcount;
unsigned int subset_fdselect_format;
hb_vector_t<code_pair> subset_fdselect_ranges;
Remap fdmap;
StrBuffArray subset_charstrings;
StrBuffArray subset_globalsubrs;
hb_vector_t<StrBuffArray> subset_localsubrs;
hb_vector_t<TableInfo> privateDictInfos;
bool drop_hints;
bool desubroutinize;
CFF2SubrSubsetter subr_subsetter;
};
static inline bool _write_cff2 (const cff2_subset_plan &plan,
const OT::cff2::accelerator_subset_t &acc,
const hb_vector_t<hb_codepoint_t>& glyphs,
unsigned int dest_sz,
void *dest)
{
hb_serialize_context_t c (dest, dest_sz);
OT::cff2 *cff2 = c.start_serialize<OT::cff2> ();
if (unlikely (!c.extend_min (*cff2)))
return false;
/* header */
cff2->version.major.set (0x02);
cff2->version.minor.set (0x00);
cff2->topDict.set (OT::cff2::static_size);
/* top dict */
{
assert (cff2->topDict == c.head - c.start);
cff2->topDictSize.set (plan.offsets.topDictInfo.size);
TopDict &dict = cff2 + cff2->topDict;
CFF2TopDict_OpSerializer topSzr;
if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict");
return false;
}
}
/* global subrs */
{
assert (cff2->topDict + plan.offsets.topDictInfo.size == c.head - c.start);
CFF2Subrs *dest = c.start_embed <CFF2Subrs> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines");
return false;
}
}
/* variation store */
if (acc.varStore != &Null(CFF2VariationStore))
{
assert (plan.offsets.varStoreOffset == c.head - c.start);
CFF2VariationStore *dest = c.start_embed<CFF2VariationStore> ();
if (unlikely (!dest->serialize (&c, acc.varStore)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Variation Store");
return false;
}
}
/* FDSelect */
if (acc.fdSelect != &Null(CFF2FDSelect))
{
assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs.len, *(const FDSelect *)acc.fdSelect, acc.fdArray->count,
plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size,
plan.subset_fdselect_ranges)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 subset FDSelect");
return false;
}
}
/* FDArray (FD Index) */
{
assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
CFF2FDArray *fda = c.start_embed<CFF2FDArray> ();
if (unlikely (fda == nullptr)) return false;
CFFFontDict_OpSerializer fontSzr;
if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
acc.fontDicts, plan.subset_fdcount, plan.fdmap,
fontSzr, plan.privateDictInfos)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray");
return false;
}
}
/* CharStrings */
{
assert (plan.offsets.charStringsInfo.offset == c.head - c.start);
CFF2CharStrings *cs = c.start_embed<CFF2CharStrings> ();
if (unlikely (cs == nullptr)) return false;
if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings");
return false;
}
}
/* private dicts & local subrs */
assert (plan.offsets.privateDictsOffset == c.head - c.start);
for (unsigned int i = 0; i < acc.privateDicts.len; i++)
{
if (plan.fdmap.includes (i))
{
PrivateDict *pd = c.start_embed<PrivateDict> ();
if (unlikely (pd == nullptr)) return false;
unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
bool result;
CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints);
/* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
unsigned int subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0;
result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset);
if (unlikely (!result))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
return false;
}
if (plan.offsets.localSubrsInfos[i].size > 0)
{
CFF2Subrs *dest = c.start_embed <CFF2Subrs> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i])))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines");
return false;
}
}
}
}
assert (c.head == c.end);
c.end_serialize ();
return true;
}
static bool
_hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
const char *data,
hb_subset_plan_t *plan,
hb_blob_t **prime /* OUT */)
{
cff2_subset_plan cff2_plan;
if (unlikely (!cff2_plan.create (acc, plan)))
{
DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan.");
return false;
}
unsigned int cff2_prime_size = cff2_plan.get_final_size ();
char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs,
cff2_prime_size, cff2_prime_data))) {
DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
free (cff2_prime_data);
return false;
}
*prime = hb_blob_create (cff2_prime_data,
cff2_prime_size,
HB_MEMORY_MODE_READONLY,
cff2_prime_data,
free);
return true;
}
/**
* hb_subset_cff2:
* Subsets the CFF2 table according to a provided plan.
*
* Return value: subsetted cff2 table.
**/
bool
hb_subset_cff2 (hb_subset_plan_t *plan,
hb_blob_t **prime /* OUT */)
{
hb_blob_t *cff2_blob = hb_sanitize_context_t().reference_table<CFF::cff2> (plan->source);
const char *data = hb_blob_get_data(cff2_blob, nullptr);
OT::cff2::accelerator_subset_t acc;
acc.init(plan->source);
bool result = likely (acc.is_valid ()) &&
_hb_subset_cff2 (acc, data, plan, prime);
hb_blob_destroy (cff2_blob);
acc.fini ();
return result;
}

38
src/hb-subset-cff2.hh Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_SUBSET_CFF2_HH
#define HB_SUBSET_CFF2_HH
#include "hb.hh"
#include "hb-subset-plan.hh"
HB_INTERNAL bool
hb_subset_cff2 (hb_subset_plan_t *plan,
hb_blob_t **cff2_prime /* OUT */);
#endif /* HB_SUBSET_CFF2_HH */

View File

@ -131,3 +131,16 @@ hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input)
{
return subset_input->drop_layout;
}
HB_EXTERN void
hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
hb_bool_t desubroutinize)
{
subset_input->desubroutinize = desubroutinize;
}
HB_EXTERN hb_bool_t
hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input)
{
return subset_input->desubroutinize;
}

View File

@ -43,6 +43,7 @@ struct hb_subset_input_t
bool drop_hints : 1;
bool drop_layout : 1;
bool desubroutinize : 1;
/* TODO
*
* features

View File

@ -30,6 +30,7 @@
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-cff1-table.hh"
static void
_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
_gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain)
{
@ -89,8 +103,10 @@ _populate_gids_to_retain (hb_face_t *face,
{
OT::cmap::accelerator_t cmap;
OT::glyf::accelerator_t glyf;
OT::cff1::accelerator_t cff;
cmap.init (face);
glyf.init (face);
cff.init (face);
hb_set_t *initial_gids_to_retain = hb_set_create ();
initial_gids_to_retain->add (0); // Not-def
@ -120,6 +136,8 @@ _populate_gids_to_retain (hb_face_t *face,
while (initial_gids_to_retain->next (&gid))
{
_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);
@ -130,6 +148,7 @@ _populate_gids_to_retain (hb_face_t *face,
while (all_gids_to_retain->next (&gid))
glyphs->push (gid);
cff.fini ();
glyf.fini ();
cmap.fini ();
@ -163,6 +182,7 @@ hb_subset_plan_create (hb_face_t *face,
plan->drop_hints = input->drop_hints;
plan->drop_layout = input->drop_layout;
plan->desubroutinize = input->desubroutinize;
plan->unicodes = hb_set_create();
plan->glyphs.init();
plan->source = hb_face_reference (face);

View File

@ -40,6 +40,7 @@ struct hb_subset_plan_t
bool drop_hints : 1;
bool drop_layout : 1;
bool desubroutinize : 1;
// For each cp that we'd like to retain maps to the corresponding gid.
hb_set_t *unicodes;

View File

@ -40,6 +40,9 @@
#include "hb-ot-maxp-table.hh"
#include "hb-ot-os2-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-vorg-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"
@ -177,7 +180,15 @@ _subset_table (hb_subset_plan_t *plan,
case HB_OT_TAG_post:
result = _subset<const OT::post> (plan);
break;
case HB_OT_TAG_cff1:
result = _subset<const OT::cff1> (plan);
break;
case HB_OT_TAG_cff2:
result = _subset<const OT::cff2> (plan);
break;
case HB_OT_TAG_VORG:
result = _subset<const OT::VORG> (plan);
break;
case HB_OT_TAG_GSUB:
result = _subset2<const OT::GSUB> (plan);
break;

View File

@ -66,6 +66,11 @@ hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input,
HB_EXTERN hb_bool_t
hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input);
HB_EXTERN void
hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
hb_bool_t desubroutinize);
HB_EXTERN hb_bool_t
hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input);
/* hb_subset () */
HB_EXTERN hb_face_t *

View File

@ -48,6 +48,8 @@ TEST_PROGS = \
test-subset-os2 \
test-subset-post \
test-subset-vmtx \
test-subset-cff1 \
test-subset-cff2 \
test-unicode \
test-version \
$(NULL)
@ -61,6 +63,8 @@ test_subset_hmtx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_os2_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_post_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_vmtx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_cff1_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_cff2_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_unicode_CPPFLAGS = \
$(AM_CPPFLAGS) \
@ -78,6 +82,7 @@ TEST_PROGS += \
test-ot-ligature-carets \
test-ot-name \
test-ot-tag \
test-ot-extents-cff \
$(NULL)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,199 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#include "hb-test.h"
#include <hb-ot.h>
/* Unit tests for CFF/CFF2 glyph extents */
static void
test_extents_cff1 (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.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, 1, &extents);
g_assert (result);
g_assert_cmpint (extents.x_bearing, ==, 52);
g_assert_cmpint (extents.y_bearing, ==, 498);
g_assert_cmpint (extents.width, ==, 381);
g_assert_cmpint (extents.height, ==, -510);
hb_font_destroy (font);
hb_face_t *face_j = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
g_assert (face_j);
hb_font_t *font_j = hb_font_create (face_j);
hb_face_destroy (face_j);
g_assert (font_j);
hb_ot_font_set_funcs (font_j);
hb_bool_t result_j = hb_font_get_glyph_extents (font_j, 3, &extents);
g_assert (result_j);
g_assert_cmpint (extents.x_bearing, ==, 34);
g_assert_cmpint (extents.y_bearing, ==, 840);
g_assert_cmpint (extents.width, ==, 920);
g_assert_cmpint (extents.height, ==, -907);
hb_font_destroy (font_j);
}
static void
test_extents_cff1_flex (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/cff1_flex.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, 1, &extents);
g_assert (result);
g_assert_cmpint (extents.x_bearing, ==, -20);
g_assert_cmpint (extents.y_bearing, ==, 520);
g_assert_cmpint (extents.width, ==, 540);
g_assert_cmpint (extents.height, ==, -540);
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
test_extents_cff2 (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.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, 1, &extents);
g_assert (result);
g_assert_cmpint (extents.x_bearing, ==, 46);
g_assert_cmpint (extents.y_bearing, ==, 487);
g_assert_cmpint (extents.width, ==, 455);
g_assert_cmpint (extents.height, ==, -500);
float coords[2] = { 600.0f, 50.0f };
hb_font_set_var_coords_design (font, coords, 2);
result = hb_font_get_glyph_extents (font, 1, &extents);
g_assert (result);
g_assert_cmpint (extents.x_bearing, ==, 38);
g_assert_cmpint (extents.y_bearing, ==, 493);
g_assert_cmpint (extents.width, ==, 481);
g_assert_cmpint (extents.height, ==, -508);
hb_font_destroy (font);
}
static void
test_extents_cff2_vsindex (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/AdobeVFPrototype_vsindex.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;
float coords[2] = { 800.0f, 50.0f };
hb_font_set_var_coords_design (font, coords, 2);
hb_bool_t result = hb_font_get_glyph_extents (font, 1, &extents);
g_assert (result);
g_assert_cmpint (extents.x_bearing, ==, 11);
g_assert_cmpint (extents.y_bearing, ==, 656);
g_assert_cmpint (extents.width, ==, 653);
g_assert_cmpint (extents.height, ==, -656);
result = hb_font_get_glyph_extents (font, 2, &extents);
g_assert (result);
g_assert_cmpint (extents.x_bearing, ==, 7);
g_assert_cmpint (extents.y_bearing, ==, 669);
g_assert_cmpint (extents.width, ==, 650);
g_assert_cmpint (extents.height, ==, -669);
hb_font_destroy (font);
}
int
main (int argc, char **argv)
{
hb_test_init (&argc, &argv);
hb_test_add (test_extents_cff1);
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_vsindex);
return hb_test_run ();
}

308
test/api/test-subset-cff1.c Normal file
View File

@ -0,0 +1,308 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#include "hb-test.h"
#include "hb-subset-test.h"
/* Unit tests for CFF subsetting */
static void
test_subset_cff1_noop (void)
{
hb_face_t *face_abc = hb_test_open_font_file("fonts/SourceSansPro-Regular.abc.otf");
hb_set_t *codepoints = hb_set_create ();
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'b');
hb_set_add (codepoints, 'c');
face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
hb_set_destroy (codepoints);
hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('C','F','F',' '));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
}
static void
test_subset_cff1 (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.otf");
hb_set_t *codepoints = hb_set_create ();
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C','F','F',' '));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff1_strip_hints (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nohints.otf");
hb_set_t *codepoints = hb_set_create ();
hb_subset_input_t *input;
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_drop_hints (input, true);
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', ' '));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff1_desubr (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nosubrs.otf");
hb_set_t *codepoints = hb_set_create ();
hb_subset_input_t *input;
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_desubroutinize (input, true);
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C','F','F',' '));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff1_desubr_strip_hints (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf");
hb_set_t *codepoints = hb_set_create ();
hb_subset_input_t *input;
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_drop_hints (input, true);
hb_subset_input_set_desubroutinize (input, true);
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', ' '));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff1_j (void)
{
hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.otf");
hb_set_t *codepoints = hb_set_create ();
hb_face_t *face_41_3041_4c2e_subset;
hb_set_add (codepoints, 0x41);
hb_set_add (codepoints, 0x4C2E);
face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, hb_subset_test_create_input (codepoints));
hb_set_destroy (codepoints);
hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' '));
hb_face_destroy (face_41_3041_4c2e_subset);
hb_face_destroy (face_41_3041_4c2e);
hb_face_destroy (face_41_4c2e);
}
static void
test_subset_cff1_j_strip_hints (void)
{
hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nohints.otf");
hb_set_t *codepoints = hb_set_create ();
hb_face_t *face_41_3041_4c2e_subset;
hb_subset_input_t *input;
hb_set_add (codepoints, 0x41);
hb_set_add (codepoints, 0x4C2E);
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_drop_hints (input, true);
face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' '));
hb_face_destroy (face_41_3041_4c2e_subset);
hb_face_destroy (face_41_3041_4c2e);
hb_face_destroy (face_41_4c2e);
}
static void
test_subset_cff1_j_desubr (void)
{
hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf");
hb_set_t *codepoints = hb_set_create ();
hb_face_t *face_41_3041_4c2e_subset;
hb_subset_input_t *input;
hb_set_add (codepoints, 0x41);
hb_set_add (codepoints, 0x4C2E);
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_desubroutinize (input, true);
face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' '));
hb_face_destroy (face_41_3041_4c2e_subset);
hb_face_destroy (face_41_3041_4c2e);
hb_face_destroy (face_41_4c2e);
}
static void
test_subset_cff1_j_desubr_strip_hints (void)
{
hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf");
hb_set_t *codepoints = hb_set_create ();
hb_face_t *face_41_3041_4c2e_subset;
hb_subset_input_t *input;
hb_set_add (codepoints, 0x41);
hb_set_add (codepoints, 0x4C2E);
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_drop_hints (input, true);
hb_subset_input_set_desubroutinize (input, true);
face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' '));
hb_face_destroy (face_41_3041_4c2e_subset);
hb_face_destroy (face_41_3041_4c2e);
hb_face_destroy (face_41_4c2e);
}
static void
test_subset_cff1_expert (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/cff1_expert.otf");
hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_expert.2D,F6E9,FB00.otf");
hb_set_t *codepoints = hb_set_create ();
hb_set_add (codepoints, 0x2D);
hb_set_add (codepoints, 0xF6E9);
hb_set_add (codepoints, 0xFB00);
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);
}
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);
}
static void
test_subset_cff1_dotsection (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/cff1_dotsect.otf");
hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_dotsect.nohints.otf");
hb_set_t *codepoints = hb_set_create ();
hb_set_add (codepoints, 0x69); /* i */
hb_subset_input_t *input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_drop_hints (input, true);
hb_face_t *face_test = hb_subset_test_create_subset (face, input);
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
main (int argc, char **argv)
{
hb_test_init (&argc, &argv);
hb_test_add (test_subset_cff1_noop);
hb_test_add (test_subset_cff1);
hb_test_add (test_subset_cff1_strip_hints);
hb_test_add (test_subset_cff1_desubr);
hb_test_add (test_subset_cff1_desubr_strip_hints);
hb_test_add (test_subset_cff1_j);
hb_test_add (test_subset_cff1_j_strip_hints);
hb_test_add (test_subset_cff1_j_desubr);
hb_test_add (test_subset_cff1_j_desubr_strip_hints);
hb_test_add (test_subset_cff1_expert);
hb_test_add (test_subset_cff1_seac);
hb_test_add (test_subset_cff1_dotsection);
return hb_test_run ();
}

153
test/api/test-subset-cff2.c Normal file
View File

@ -0,0 +1,153 @@
/*
* Copyright © 2018 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#include "hb-test.h"
#include "hb-subset-test.h"
/* Unit tests for CFF2 subsetting */
static void
test_subset_cff2_noop (void)
{
hb_face_t *face_abc = hb_test_open_font_file("fonts/AdobeVFPrototype.abc.otf");
hb_set_t *codepoints = hb_set_create ();
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'b');
hb_set_add (codepoints, 'c');
face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
hb_set_destroy (codepoints);
hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('C','F','F','2'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
}
static void
test_subset_cff2 (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.otf");
hb_set_t *codepoints = hb_set_create ();
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C','F','F','2'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff2_strip_hints (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nohints.otf");
hb_set_t *codepoints = hb_set_create ();
hb_subset_input_t *input;
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_drop_hints (input, true);
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff2_desubr (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nosubrs.otf");
hb_set_t *codepoints = hb_set_create ();
hb_subset_input_t *input;
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_desubroutinize (input, true);
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff2_desubr_strip_hints (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf");
hb_set_t *codepoints = hb_set_create ();
hb_subset_input_t *input;
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_desubroutinize (input, true);
hb_subset_input_set_drop_hints (input, true);
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
int
main (int argc, char **argv)
{
hb_test_init (&argc, &argv);
hb_test_add (test_subset_cff2_noop);
hb_test_add (test_subset_cff2);
hb_test_add (test_subset_cff2_strip_hints);
hb_test_add (test_subset_cff2_desubr);
hb_test_add (test_subset_cff2_desubr_strip_hints);
return hb_test_run ();
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
--desubroutinize

View File

@ -0,0 +1,2 @@
--no-hinting
--desubroutinize

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