diff options
Diffstat (limited to 'Build/source/libs/graphite/engine-2.3.1/src')
57 files changed, 42124 insertions, 0 deletions
diff --git a/Build/source/libs/graphite/engine-2.3.1/src/Makefile.am b/Build/source/libs/graphite/engine-2.3.1/src/Makefile.am new file mode 100644 index 00000000000..7d55c002a23 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/Makefile.am @@ -0,0 +1,22 @@ +AUTOMAKE_OPTIONS = 1.6 + +AM_CPPFLAGS = -I$(top_srcdir)/include/graphite -I$(top_srcdir)/src/generic +AM_CPPFLAGS += -I$(top_srcdir)/src/font -I$(top_srcdir)/src/segment +#AM_CPPFLAGS += $(FT_CFLAGS) $(XFT_CFLAGS) +#AM_CPPFLAGS += -D_UNICODE -DUNICODE -DNO_ASM + +lib_LTLIBRARIES = libgraphite.la + +libgraphite_la_LDFLAGS = -version-info 3:0:0 + +#EXTRA_DIST=textsource +EXTRA_DIST= +graphiteinclude_HEADERS = +graphiteincludedir = $(includedir)/graphite +include ../include/Makefile.am + +libgraphite_la_SOURCES = +include generic/Makefile.am +include font/Makefile.am +include segment/Makefile.am +include painter/Makefile.am diff --git a/Build/source/libs/graphite/engine-2.3.1/src/Makefile.in b/Build/source/libs/graphite/engine-2.3.1/src/Makefile.in new file mode 100644 index 00000000000..487f21dc19c --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/Makefile.in @@ -0,0 +1,781 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +DIST_COMMON = $(graphiteinclude_HEADERS) \ + $(srcdir)/../include/Makefile.am $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/font/Makefile.am \ + $(srcdir)/generic/Makefile.am $(srcdir)/painter/Makefile.am \ + $(srcdir)/segment/Makefile.am +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(graphiteincludedir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +libgraphite_la_LIBADD = +am_libgraphite_la_OBJECTS = Debug.lo Util.lo Font.lo TtfUtil.lo \ + FileFont.lo FileInput.lo FontCache.lo FontFace.lo \ + GrCharStream.lo GrClassTable.lo GrEngine.lo GrFeature.lo \ + GrFSM.lo GrGlyphTable.lo GrPassActionCode.lo GrPass.lo \ + GrSlotState.lo GrSlotStream.lo GrTableManager.lo \ + MemoryUsage.lo Platform.lo SegmentAux.lo Segment.lo TestFSM.lo \ + TestPasses.lo TransductionLog.lo SegmentPainter.lo +libgraphite_la_OBJECTS = $(am_libgraphite_la_OBJECTS) +libgraphite_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(libgraphite_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libgraphite_la_SOURCES) +DIST_SOURCES = $(libgraphite_la_SOURCES) +graphiteincludeHEADERS_INSTALL = $(INSTALL_HEADER) +HEADERS = $(graphiteinclude_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LSB_RELEASE = @LSB_RELEASE@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +REL_CODENAME = @REL_CODENAME@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIZEOF_WCHAR_T = @SIZEOF_WCHAR_T@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = 1.6 +AM_CPPFLAGS = -I$(top_srcdir)/include/graphite \ + -I$(top_srcdir)/src/generic -I$(top_srcdir)/src/font \ + -I$(top_srcdir)/src/segment +#AM_CPPFLAGS += $(FT_CFLAGS) $(XFT_CFLAGS) +#AM_CPPFLAGS += -D_UNICODE -DUNICODE -DNO_ASM +lib_LTLIBRARIES = libgraphite.la +libgraphite_la_LDFLAGS = -version-info 3:0:0 + +#EXTRA_DIST=textsource +EXTRA_DIST = $(top_srcdir)/include/graphite/BinTree.h \ + $(top_srcdir)/include/graphite/GenericResource.h \ + $(top_srcdir)/include/graphite/HashMap.h $(genericdir)/*.h \ + $(genericdir)/BinTree_i.cpp $(genericdir)/HashMap_i.cpp \ + $(fontdir)/*.h $(segmentdir)/*.h +graphiteinclude_HEADERS = $(top_srcdir)/include/graphite/FileFont.h \ + $(top_srcdir)/include/graphite/Font.h \ + $(top_srcdir)/include/graphite/GraphiteProcess.h \ + $(top_srcdir)/include/graphite/GrAppData.h \ + $(top_srcdir)/include/graphite/GrClient.h \ + $(top_srcdir)/include/graphite/GrCommon.h \ + $(top_srcdir)/include/graphite/GrConstants.h \ + $(top_srcdir)/include/graphite/GrData.h \ + $(top_srcdir)/include/graphite/GrDebug.h \ + $(top_srcdir)/include/graphite/GrExt.h \ + $(top_srcdir)/include/graphite/GrFeature.h \ + $(top_srcdir)/include/graphite/GrMstypes.h \ + $(top_srcdir)/include/graphite/GrPlatform.h \ + $(top_srcdir)/include/graphite/GrResult.h \ + $(top_srcdir)/include/graphite/GrStructs.h \ + $(top_srcdir)/include/graphite/GrWrappers.h \ + $(top_srcdir)/include/graphite/IGraphiteProcess.h \ + $(top_srcdir)/include/graphite/IGrEngine.h \ + $(top_srcdir)/include/graphite/IGrJustifier.h \ + $(top_srcdir)/include/graphite/ITextSource.h \ + $(top_srcdir)/include/graphite/MemoryUsage.h \ + $(top_srcdir)/include/graphite/SegmentAux.h \ + $(top_srcdir)/include/graphite/Segment.h \ + $(top_srcdir)/include/graphite/SegmentPainter.h +graphiteincludedir = $(includedir)/graphite +libgraphite_la_SOURCES = $(genericdir)/Debug.cpp \ + $(genericdir)/Util.cpp $(fontdir)/Font.cpp \ + $(fontdir)/TtfUtil.cpp $(fontdir)/FileFont.cpp \ + $(segmentdir)/FileInput.cpp $(segmentdir)/FontCache.cpp \ + $(segmentdir)/FontFace.cpp $(segmentdir)/GrCharStream.cpp \ + $(segmentdir)/GrClassTable.cpp $(segmentdir)/GrEngine.cpp \ + $(segmentdir)/GrFeature.cpp $(segmentdir)/GrFSM.cpp \ + $(segmentdir)/GrGlyphTable.cpp \ + $(segmentdir)/GrPassActionCode.cpp $(segmentdir)/GrPass.cpp \ + $(segmentdir)/GrSlotState.cpp $(segmentdir)/GrSlotStream.cpp \ + $(segmentdir)/GrTableManager.cpp $(segmentdir)/MemoryUsage.cpp \ + $(segmentdir)/Platform.cpp $(segmentdir)/SegmentAux.cpp \ + $(segmentdir)/Segment.cpp $(segmentdir)/TestFSM.cpp \ + $(segmentdir)/TestPasses.cpp $(segmentdir)/TransductionLog.cpp \ + $(painterdir)/SegmentPainter.cpp +genericdir = $(top_srcdir)/src/generic +fontdir = $(top_srcdir)/src/font +segmentdir = $(top_srcdir)/src/segment +painterdir = $(top_srcdir)/src/painter +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/../include/Makefile.am $(srcdir)/generic/Makefile.am $(srcdir)/font/Makefile.am $(srcdir)/segment/Makefile.am $(srcdir)/painter/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libgraphite.la: $(libgraphite_la_OBJECTS) $(libgraphite_la_DEPENDENCIES) + $(libgraphite_la_LINK) -rpath $(libdir) $(libgraphite_la_OBJECTS) $(libgraphite_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Debug.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileFont.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileInput.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Font.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FontCache.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FontFace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrCharStream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrClassTable.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrEngine.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrFSM.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrFeature.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrGlyphTable.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrPass.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrPassActionCode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrSlotState.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrSlotStream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrTableManager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MemoryUsage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Platform.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Segment.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SegmentAux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SegmentPainter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TestFSM.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TestPasses.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TransductionLog.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TtfUtil.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +Debug.lo: $(genericdir)/Debug.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Debug.lo -MD -MP -MF $(DEPDIR)/Debug.Tpo -c -o Debug.lo `test -f '$(genericdir)/Debug.cpp' || echo '$(srcdir)/'`$(genericdir)/Debug.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/Debug.Tpo $(DEPDIR)/Debug.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(genericdir)/Debug.cpp' object='Debug.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Debug.lo `test -f '$(genericdir)/Debug.cpp' || echo '$(srcdir)/'`$(genericdir)/Debug.cpp + +Util.lo: $(genericdir)/Util.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Util.lo -MD -MP -MF $(DEPDIR)/Util.Tpo -c -o Util.lo `test -f '$(genericdir)/Util.cpp' || echo '$(srcdir)/'`$(genericdir)/Util.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/Util.Tpo $(DEPDIR)/Util.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(genericdir)/Util.cpp' object='Util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Util.lo `test -f '$(genericdir)/Util.cpp' || echo '$(srcdir)/'`$(genericdir)/Util.cpp + +Font.lo: $(fontdir)/Font.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Font.lo -MD -MP -MF $(DEPDIR)/Font.Tpo -c -o Font.lo `test -f '$(fontdir)/Font.cpp' || echo '$(srcdir)/'`$(fontdir)/Font.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/Font.Tpo $(DEPDIR)/Font.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(fontdir)/Font.cpp' object='Font.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Font.lo `test -f '$(fontdir)/Font.cpp' || echo '$(srcdir)/'`$(fontdir)/Font.cpp + +TtfUtil.lo: $(fontdir)/TtfUtil.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT TtfUtil.lo -MD -MP -MF $(DEPDIR)/TtfUtil.Tpo -c -o TtfUtil.lo `test -f '$(fontdir)/TtfUtil.cpp' || echo '$(srcdir)/'`$(fontdir)/TtfUtil.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/TtfUtil.Tpo $(DEPDIR)/TtfUtil.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(fontdir)/TtfUtil.cpp' object='TtfUtil.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o TtfUtil.lo `test -f '$(fontdir)/TtfUtil.cpp' || echo '$(srcdir)/'`$(fontdir)/TtfUtil.cpp + +FileFont.lo: $(fontdir)/FileFont.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT FileFont.lo -MD -MP -MF $(DEPDIR)/FileFont.Tpo -c -o FileFont.lo `test -f '$(fontdir)/FileFont.cpp' || echo '$(srcdir)/'`$(fontdir)/FileFont.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/FileFont.Tpo $(DEPDIR)/FileFont.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(fontdir)/FileFont.cpp' object='FileFont.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o FileFont.lo `test -f '$(fontdir)/FileFont.cpp' || echo '$(srcdir)/'`$(fontdir)/FileFont.cpp + +FileInput.lo: $(segmentdir)/FileInput.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT FileInput.lo -MD -MP -MF $(DEPDIR)/FileInput.Tpo -c -o FileInput.lo `test -f '$(segmentdir)/FileInput.cpp' || echo '$(srcdir)/'`$(segmentdir)/FileInput.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/FileInput.Tpo $(DEPDIR)/FileInput.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/FileInput.cpp' object='FileInput.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o FileInput.lo `test -f '$(segmentdir)/FileInput.cpp' || echo '$(srcdir)/'`$(segmentdir)/FileInput.cpp + +FontCache.lo: $(segmentdir)/FontCache.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT FontCache.lo -MD -MP -MF $(DEPDIR)/FontCache.Tpo -c -o FontCache.lo `test -f '$(segmentdir)/FontCache.cpp' || echo '$(srcdir)/'`$(segmentdir)/FontCache.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/FontCache.Tpo $(DEPDIR)/FontCache.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/FontCache.cpp' object='FontCache.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o FontCache.lo `test -f '$(segmentdir)/FontCache.cpp' || echo '$(srcdir)/'`$(segmentdir)/FontCache.cpp + +FontFace.lo: $(segmentdir)/FontFace.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT FontFace.lo -MD -MP -MF $(DEPDIR)/FontFace.Tpo -c -o FontFace.lo `test -f '$(segmentdir)/FontFace.cpp' || echo '$(srcdir)/'`$(segmentdir)/FontFace.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/FontFace.Tpo $(DEPDIR)/FontFace.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/FontFace.cpp' object='FontFace.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o FontFace.lo `test -f '$(segmentdir)/FontFace.cpp' || echo '$(srcdir)/'`$(segmentdir)/FontFace.cpp + +GrCharStream.lo: $(segmentdir)/GrCharStream.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrCharStream.lo -MD -MP -MF $(DEPDIR)/GrCharStream.Tpo -c -o GrCharStream.lo `test -f '$(segmentdir)/GrCharStream.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrCharStream.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrCharStream.Tpo $(DEPDIR)/GrCharStream.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrCharStream.cpp' object='GrCharStream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrCharStream.lo `test -f '$(segmentdir)/GrCharStream.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrCharStream.cpp + +GrClassTable.lo: $(segmentdir)/GrClassTable.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrClassTable.lo -MD -MP -MF $(DEPDIR)/GrClassTable.Tpo -c -o GrClassTable.lo `test -f '$(segmentdir)/GrClassTable.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrClassTable.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrClassTable.Tpo $(DEPDIR)/GrClassTable.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrClassTable.cpp' object='GrClassTable.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrClassTable.lo `test -f '$(segmentdir)/GrClassTable.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrClassTable.cpp + +GrEngine.lo: $(segmentdir)/GrEngine.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrEngine.lo -MD -MP -MF $(DEPDIR)/GrEngine.Tpo -c -o GrEngine.lo `test -f '$(segmentdir)/GrEngine.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrEngine.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrEngine.Tpo $(DEPDIR)/GrEngine.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrEngine.cpp' object='GrEngine.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrEngine.lo `test -f '$(segmentdir)/GrEngine.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrEngine.cpp + +GrFeature.lo: $(segmentdir)/GrFeature.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrFeature.lo -MD -MP -MF $(DEPDIR)/GrFeature.Tpo -c -o GrFeature.lo `test -f '$(segmentdir)/GrFeature.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrFeature.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrFeature.Tpo $(DEPDIR)/GrFeature.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrFeature.cpp' object='GrFeature.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrFeature.lo `test -f '$(segmentdir)/GrFeature.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrFeature.cpp + +GrFSM.lo: $(segmentdir)/GrFSM.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrFSM.lo -MD -MP -MF $(DEPDIR)/GrFSM.Tpo -c -o GrFSM.lo `test -f '$(segmentdir)/GrFSM.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrFSM.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrFSM.Tpo $(DEPDIR)/GrFSM.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrFSM.cpp' object='GrFSM.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrFSM.lo `test -f '$(segmentdir)/GrFSM.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrFSM.cpp + +GrGlyphTable.lo: $(segmentdir)/GrGlyphTable.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrGlyphTable.lo -MD -MP -MF $(DEPDIR)/GrGlyphTable.Tpo -c -o GrGlyphTable.lo `test -f '$(segmentdir)/GrGlyphTable.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrGlyphTable.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrGlyphTable.Tpo $(DEPDIR)/GrGlyphTable.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrGlyphTable.cpp' object='GrGlyphTable.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrGlyphTable.lo `test -f '$(segmentdir)/GrGlyphTable.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrGlyphTable.cpp + +GrPassActionCode.lo: $(segmentdir)/GrPassActionCode.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrPassActionCode.lo -MD -MP -MF $(DEPDIR)/GrPassActionCode.Tpo -c -o GrPassActionCode.lo `test -f '$(segmentdir)/GrPassActionCode.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrPassActionCode.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrPassActionCode.Tpo $(DEPDIR)/GrPassActionCode.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrPassActionCode.cpp' object='GrPassActionCode.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrPassActionCode.lo `test -f '$(segmentdir)/GrPassActionCode.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrPassActionCode.cpp + +GrPass.lo: $(segmentdir)/GrPass.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrPass.lo -MD -MP -MF $(DEPDIR)/GrPass.Tpo -c -o GrPass.lo `test -f '$(segmentdir)/GrPass.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrPass.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrPass.Tpo $(DEPDIR)/GrPass.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrPass.cpp' object='GrPass.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrPass.lo `test -f '$(segmentdir)/GrPass.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrPass.cpp + +GrSlotState.lo: $(segmentdir)/GrSlotState.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrSlotState.lo -MD -MP -MF $(DEPDIR)/GrSlotState.Tpo -c -o GrSlotState.lo `test -f '$(segmentdir)/GrSlotState.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrSlotState.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrSlotState.Tpo $(DEPDIR)/GrSlotState.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrSlotState.cpp' object='GrSlotState.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrSlotState.lo `test -f '$(segmentdir)/GrSlotState.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrSlotState.cpp + +GrSlotStream.lo: $(segmentdir)/GrSlotStream.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrSlotStream.lo -MD -MP -MF $(DEPDIR)/GrSlotStream.Tpo -c -o GrSlotStream.lo `test -f '$(segmentdir)/GrSlotStream.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrSlotStream.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrSlotStream.Tpo $(DEPDIR)/GrSlotStream.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrSlotStream.cpp' object='GrSlotStream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrSlotStream.lo `test -f '$(segmentdir)/GrSlotStream.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrSlotStream.cpp + +GrTableManager.lo: $(segmentdir)/GrTableManager.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT GrTableManager.lo -MD -MP -MF $(DEPDIR)/GrTableManager.Tpo -c -o GrTableManager.lo `test -f '$(segmentdir)/GrTableManager.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrTableManager.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/GrTableManager.Tpo $(DEPDIR)/GrTableManager.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/GrTableManager.cpp' object='GrTableManager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o GrTableManager.lo `test -f '$(segmentdir)/GrTableManager.cpp' || echo '$(srcdir)/'`$(segmentdir)/GrTableManager.cpp + +MemoryUsage.lo: $(segmentdir)/MemoryUsage.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT MemoryUsage.lo -MD -MP -MF $(DEPDIR)/MemoryUsage.Tpo -c -o MemoryUsage.lo `test -f '$(segmentdir)/MemoryUsage.cpp' || echo '$(srcdir)/'`$(segmentdir)/MemoryUsage.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/MemoryUsage.Tpo $(DEPDIR)/MemoryUsage.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/MemoryUsage.cpp' object='MemoryUsage.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o MemoryUsage.lo `test -f '$(segmentdir)/MemoryUsage.cpp' || echo '$(srcdir)/'`$(segmentdir)/MemoryUsage.cpp + +Platform.lo: $(segmentdir)/Platform.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Platform.lo -MD -MP -MF $(DEPDIR)/Platform.Tpo -c -o Platform.lo `test -f '$(segmentdir)/Platform.cpp' || echo '$(srcdir)/'`$(segmentdir)/Platform.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/Platform.Tpo $(DEPDIR)/Platform.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/Platform.cpp' object='Platform.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Platform.lo `test -f '$(segmentdir)/Platform.cpp' || echo '$(srcdir)/'`$(segmentdir)/Platform.cpp + +SegmentAux.lo: $(segmentdir)/SegmentAux.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT SegmentAux.lo -MD -MP -MF $(DEPDIR)/SegmentAux.Tpo -c -o SegmentAux.lo `test -f '$(segmentdir)/SegmentAux.cpp' || echo '$(srcdir)/'`$(segmentdir)/SegmentAux.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/SegmentAux.Tpo $(DEPDIR)/SegmentAux.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/SegmentAux.cpp' object='SegmentAux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o SegmentAux.lo `test -f '$(segmentdir)/SegmentAux.cpp' || echo '$(srcdir)/'`$(segmentdir)/SegmentAux.cpp + +Segment.lo: $(segmentdir)/Segment.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Segment.lo -MD -MP -MF $(DEPDIR)/Segment.Tpo -c -o Segment.lo `test -f '$(segmentdir)/Segment.cpp' || echo '$(srcdir)/'`$(segmentdir)/Segment.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/Segment.Tpo $(DEPDIR)/Segment.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/Segment.cpp' object='Segment.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Segment.lo `test -f '$(segmentdir)/Segment.cpp' || echo '$(srcdir)/'`$(segmentdir)/Segment.cpp + +TestFSM.lo: $(segmentdir)/TestFSM.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT TestFSM.lo -MD -MP -MF $(DEPDIR)/TestFSM.Tpo -c -o TestFSM.lo `test -f '$(segmentdir)/TestFSM.cpp' || echo '$(srcdir)/'`$(segmentdir)/TestFSM.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/TestFSM.Tpo $(DEPDIR)/TestFSM.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/TestFSM.cpp' object='TestFSM.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o TestFSM.lo `test -f '$(segmentdir)/TestFSM.cpp' || echo '$(srcdir)/'`$(segmentdir)/TestFSM.cpp + +TestPasses.lo: $(segmentdir)/TestPasses.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT TestPasses.lo -MD -MP -MF $(DEPDIR)/TestPasses.Tpo -c -o TestPasses.lo `test -f '$(segmentdir)/TestPasses.cpp' || echo '$(srcdir)/'`$(segmentdir)/TestPasses.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/TestPasses.Tpo $(DEPDIR)/TestPasses.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/TestPasses.cpp' object='TestPasses.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o TestPasses.lo `test -f '$(segmentdir)/TestPasses.cpp' || echo '$(srcdir)/'`$(segmentdir)/TestPasses.cpp + +TransductionLog.lo: $(segmentdir)/TransductionLog.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT TransductionLog.lo -MD -MP -MF $(DEPDIR)/TransductionLog.Tpo -c -o TransductionLog.lo `test -f '$(segmentdir)/TransductionLog.cpp' || echo '$(srcdir)/'`$(segmentdir)/TransductionLog.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/TransductionLog.Tpo $(DEPDIR)/TransductionLog.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(segmentdir)/TransductionLog.cpp' object='TransductionLog.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o TransductionLog.lo `test -f '$(segmentdir)/TransductionLog.cpp' || echo '$(srcdir)/'`$(segmentdir)/TransductionLog.cpp + +SegmentPainter.lo: $(painterdir)/SegmentPainter.cpp +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT SegmentPainter.lo -MD -MP -MF $(DEPDIR)/SegmentPainter.Tpo -c -o SegmentPainter.lo `test -f '$(painterdir)/SegmentPainter.cpp' || echo '$(srcdir)/'`$(painterdir)/SegmentPainter.cpp +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/SegmentPainter.Tpo $(DEPDIR)/SegmentPainter.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$(painterdir)/SegmentPainter.cpp' object='SegmentPainter.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o SegmentPainter.lo `test -f '$(painterdir)/SegmentPainter.cpp' || echo '$(srcdir)/'`$(painterdir)/SegmentPainter.cpp + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-graphiteincludeHEADERS: $(graphiteinclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(graphiteincludedir)" || $(MKDIR_P) "$(DESTDIR)$(graphiteincludedir)" + @list='$(graphiteinclude_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(graphiteincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(graphiteincludedir)/$$f'"; \ + $(graphiteincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(graphiteincludedir)/$$f"; \ + done + +uninstall-graphiteincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(graphiteinclude_HEADERS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(graphiteincludedir)/$$f'"; \ + rm -f "$(DESTDIR)$(graphiteincludedir)/$$f"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(graphiteincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-graphiteincludeHEADERS + +install-dvi: install-dvi-am + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-graphiteincludeHEADERS \ + uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am \ + install-graphiteincludeHEADERS install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-graphiteincludeHEADERS \ + uninstall-libLTLIBRARIES + + +#EXTRA_DIST += $(painterdir)/*.h +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Build/source/libs/graphite/engine-2.3.1/src/font/FileFont.cpp b/Build/source/libs/graphite/engine-2.3.1/src/font/FileFont.cpp new file mode 100644 index 00000000000..4600b788ed2 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/font/FileFont.cpp @@ -0,0 +1,429 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999 - 2008 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FileFont.cpp +Responsibility: Keith Stribley +Last reviewed: Not yet. + +Description: + A Font is an object that represents a font-family + bold + italic setting, that contains + Graphite tables. +----------------------------------------------------------------------------------------------*/ + +#include "Main.h" + +#include "FontTableCache.h" +#include "FileFont.h" +#include <stdio.h> + + +// TBD do this properly +#define FUDGE_FACTOR 72 +#define fix26_6(x) (x >> 6) + (x & 32 ? (x > 0 ? 1 : 0) : (x < 0 ? -1 : 0)) + +namespace gr +{ + + +FileFont::FileFont() + : Font(), + m_pfile(NULL), + m_pTableCache(NULL), + m_mAscent(0), + m_mDescent(0), + m_mEmSquare(0), + m_pointSize(0), + m_dpiX(FUDGE_FACTOR), + m_dpiY(FUDGE_FACTOR), + m_fIsValid(false), + m_pHeader(NULL), + m_pTableDir(NULL), + m_xScale(1.0f), + m_yScale(1.0f) +{ + m_fItalic = false; + m_fBold = false; + // colors are not used + m_clrFore = (unsigned long)kclrBlack; + m_clrBack = (unsigned long)kclrTransparent; +} + +FileFont::FileFont(FILE * pfile, float pointSize, + unsigned int dpiX, unsigned int dpiY) + : Font(), + m_pfile(pfile), + m_pTableCache(NULL), + m_mAscent(0), + m_mDescent(0), + m_mEmSquare(0), + m_pointSize(pointSize), + m_dpiX(dpiX), + m_dpiY(dpiY), + m_fIsValid(false), + m_pHeader(NULL), + m_pTableDir(NULL), + m_xScale(1.0f), + m_yScale(1.0f) +{ + Assert(m_pfile); // shouldn't be null + initializeFromFace(); + +} + +FileFont::FileFont(char * fileName, float pointSize, + unsigned int dpiX, unsigned int dpiY) + : Font(), + m_pfile(NULL), + m_pTableCache(NULL), + m_mAscent(0), + m_mDescent(0), + m_mEmSquare(0), + m_pointSize(pointSize), + m_dpiX(dpiX), + m_dpiY(dpiY), + m_fIsValid(false), + m_pHeader(NULL), + m_pTableDir(NULL), + m_xScale(1.0f), + m_yScale(1.0f) +{ + Assert(fileName); // shouldn't be null but we play safe anyway + m_pfile = fopen(fileName, "rb"); + + initializeFromFace(); +} + +FileFont::FileFont(std::string fileName, float pointSize, + unsigned int dpiX, unsigned int dpiY) + : Font(), + m_pfile(NULL), + m_pTableCache(NULL), + m_mAscent(0), + m_mDescent(0), + m_mEmSquare(0), + m_pointSize(pointSize), + m_dpiX(dpiX), + m_dpiY(dpiY), + m_fIsValid(false), + m_pHeader(NULL), + m_pTableDir(NULL), + m_xScale(1.0f), + m_yScale(1.0f) +{ + Assert(fileName.length()); // shouldn't be null but we play safe anyway + m_pfile = fopen(fileName.c_str(), "rb"); + + initializeFromFace(); +} + + +void +FileFont::initializeFromFace() +{ + if (m_dpiY == 0) m_dpiY = m_dpiX; + m_fItalic = false; + m_fBold = false; + // colors are not used + m_clrFore = (unsigned long)kclrBlack; + m_clrBack = (unsigned long)kclrTransparent; + + if (m_pfile) + { + size_t lOffset, lSize; + TtfUtil::GetHeaderInfo(lOffset, lSize); + m_pHeader = new byte [lSize]; + m_fIsValid = true; + if (!m_pHeader) + { + m_fIsValid = false; + return; + } + m_fIsValid = (fseek(m_pfile, lOffset, SEEK_SET) == 0); + size_t bytesRead = fread(m_pHeader, 1, lSize, m_pfile); + Assert(bytesRead == lSize); + m_fIsValid = TtfUtil::CheckHeader(m_pHeader); + + if (!m_fIsValid) return; + m_fIsValid = TtfUtil::GetTableDirInfo(m_pHeader, lOffset, lSize); + m_pTableDir = new byte[lSize]; + if (!m_pTableDir) + { + m_fIsValid = false; + return; + } + // if lOffset hasn't changed this isn't needed + fseek(m_pfile, lOffset, SEEK_SET); + bytesRead = fread(m_pTableDir, 1, lSize, m_pfile); + Assert(bytesRead == lSize); + + // now read head tables + m_fIsValid = TtfUtil::GetTableInfo(ktiOs2, m_pHeader, m_pTableDir, + lOffset, lSize); + if (!m_fIsValid) + { + return; + } + byte * pTable = readTable(ktiOs2, lSize); + // get ascent, descent, style while we have the OS2 table loaded + if (!m_fIsValid || !pTable) + { + return; + } + m_fIsValid = TtfUtil::FontOs2Style(pTable, m_fBold, m_fItalic); + m_mAscent = static_cast<float>(TtfUtil::FontAscent(pTable)); + m_mDescent = static_cast<float>(TtfUtil::FontDescent(pTable)); + + pTable = readTable(ktiName, lSize); + if (!m_fIsValid || !pTable) + { + return; + } + if (!TtfUtil::Get31EngFamilyInfo(pTable, lOffset, lSize)) + { + // not English name + m_fIsValid = false; + return; + } + Assert(lSize %2 == 0);// should be utf16 + utf16 rgchwFace[128]; + int cchw = (lSize / isizeof(utf16)) + 1; + cchw = min(cchw, 128); + utf16 * pTable16 = reinterpret_cast<utf16*>(pTable + lOffset); + std::copy(pTable16, pTable16 + cchw - 1, rgchwFace); + rgchwFace[cchw - 1] = 0; // zero terminate + TtfUtil::SwapWString(rgchwFace, cchw - 1); +// We could use something like "if (sizeof(std::wstring::value_type) == 4)" here, +// but a compile-time switch is preferable. +#if SIZEOF_WCHAR_T == 4 + for (int cch16 = 0; cch16 < cchw; ) + { + int cch16Used = 0; + utf32 ch32 = GrCharStream::Utf16ToUtf32(&(rgchwFace[cch16]), + cchw - cch16, &cch16Used); + m_stuFaceName.push_back(ch32); + cch16 += cch16Used; + } +// } +#else + m_stuFaceName.assign(rgchwFace); + // VS 2005 needs this: + //for (int cch16 = 0; cch16 < cchw; cch16++) + // m_stuFaceName.push_back(rgchwFace[cch16]); +#endif + pTable = readTable(ktiHead, lSize); + if (!m_fIsValid || !pTable) + { + return; + } + m_mEmSquare = static_cast<float>(TtfUtil::DesignUnits(pTable)); + // can now set the scale + m_xScale = scaleFromDpi(m_dpiX); + m_yScale = scaleFromDpi(m_dpiY); + } + else + { + m_fIsValid = false; + } +} + +byte * +FileFont::readTable(int /*TableId*/ tid, size_t & size) +{ + const TableId tableId = TableId(tid); + bool isValid = true; + size_t lOffset = 0, lSize = 0; + if (!m_pTableCache) + { + m_pTableCache = new FontTableCache(); + } + if (!m_pTableCache) return NULL; + + byte * pTable = m_pTableCache->getTable(tableId); + size = m_pTableCache->getTableSize(tableId); + // check whether it is already in the cache + if (pTable) return pTable; + isValid &= TtfUtil::GetTableInfo(tableId, m_pHeader, m_pTableDir, + lOffset, lSize); + if (!isValid) + return NULL; + fseek(m_pfile, lOffset, SEEK_SET); + // only allocate if needed + pTable = m_pTableCache->allocateTable(tableId, lSize); + + if (!pTable) + { + isValid = false; + return NULL; + } + size_t bytesRead = fread(pTable, 1, lSize, m_pfile); + isValid = bytesRead == lSize; + if (isValid) + { + isValid &= TtfUtil::CheckTable(tableId, pTable, lSize); + } + if (!isValid) + { + return 0; + } + size = lSize; + return pTable; +} + + +FileFont::~FileFont() +{ + + // if this is the last copy of the font sharing these tables + // delete them + if (m_pTableCache) + { + m_pTableCache->decrementFontCount(); + if (m_pTableCache->getFontCount() == 0) + { + delete [] m_pHeader; + delete [] m_pTableDir; + delete m_pTableCache; + m_pTableCache = NULL; + if (m_pfile) + fclose(m_pfile); + } + } + else + { + delete [] m_pHeader; + delete [] m_pTableDir; + if (m_pfile) + fclose(m_pfile); + } + // note the DecFontCount(); is done in the Font base class +} + +Font * FileFont::copyThis() +{ + FileFont * copy = new FileFont(*this); + return copy; +} + +/** +* A copy constructor that allows a change of point size or dpi +* The underlying table cache will be shared between the fonts, so +* this should have a low overhead. +*/ +FileFont::FileFont(const FileFont & font, float pointSize, + unsigned int dpiX, unsigned int dpiY) +: Font(font), + m_pfile(font.m_pfile), + m_mAscent(font.m_mAscent), + m_mDescent(font.m_mDescent), + m_mEmSquare(font.m_mEmSquare), + m_pointSize(font.m_pointSize), + m_dpiX(font.m_dpiX), + m_dpiY(font.m_dpiY), + m_fIsValid(font.m_fIsValid), + m_pHeader(font.m_pHeader), + m_pTableDir(font.m_pTableDir), + m_xScale(font.m_xScale), + m_yScale(font.m_yScale) +{ + if (pointSize > 0) + { + m_pointSize = pointSize; + if (dpiX > 0) + { + m_dpiX = dpiX; + if (dpiY > 0) m_dpiY = dpiY; + else dpiY = dpiX; + } + m_xScale = scaleFromDpi(m_dpiX); + m_yScale = scaleFromDpi(m_dpiY); + } + m_fItalic = font.m_fItalic; + m_fBold = font.m_fBold; + // colors are not used + m_clrFore = font.m_clrFore; + m_clrBack = font.m_clrBack; + m_stuFaceName.assign(font.m_stuFaceName); + + m_pTableCache = font.m_pTableCache; + // use the same table cache between instances + if (m_pTableCache) + m_pTableCache->incrementFontCount(); + + + // I dont' see why we need to reget the face, but WinFont does + //m_pfface = FontFace::GetFontFace(this, m_fpropDef.szFaceName, + // m_fpropDef.fBold, m_fpropDef.fItalic); +} + + + + //virtual FontErrorCode isValidForGraphite(int * pnVersion = NULL, int * pnSubVersion = NULL); + +/*---------------------------------------------------------------------------------------------- + Read a table from the font. +----------------------------------------------------------------------------------------------*/ +const void * +FileFont::getTable(fontTableId32 tableID, size_t * pcbSize) +{ + *pcbSize = 0; + // use a cache to reduce the number of times tables have to be reloaded + if (m_pTableCache == NULL) + { + // constructor automatically sets the font count to 1 + m_pTableCache = new FontTableCache(); + } + TableId tid; + for (int i = 0; i<ktiLast; i++) + { + tid = static_cast<TableId>(i); + if (tableID == TtfUtil::TableIdTag(tid)) + { + if (m_pTableCache->getTable(tid)) + { + *pcbSize = m_pTableCache->getTableSize(tid); + return m_pTableCache->getTable(tid); + } + break; + } + } + Assert(tid < ktiLast); + size_t tableSize = 0; + void * pTable = readTable(tid, tableSize); + *pcbSize = static_cast<int>(tableSize); + return pTable; +} + + +void FileFont::getFontMetrics(float * pAscent, float * pDescent, + float * pEmSquare) +{ + if (pEmSquare) *pEmSquare = m_mEmSquare * m_xScale; + if (pAscent) *pAscent = m_mAscent * m_yScale; + if (pDescent) *pDescent = m_mDescent * m_yScale; +} + + +bool FileFont::FontHasGraphiteTables(FILE * file) +{ + FileFont testFont(file, 1.0f, FUDGE_FACTOR); + return testFont.fontHasGraphiteTables(); +} + +bool FileFont::FontHasGraphiteTables(char * fileName) +{ + FileFont testFont(fileName, 1.0f, FUDGE_FACTOR); + return testFont.fontHasGraphiteTables(); +} + +bool FileFont::fontHasGraphiteTables() +{ + size_t tableSize; + bool isGraphiteFont = m_fIsValid; + isGraphiteFont &= (readTable(ktiSilf, tableSize) != NULL); + return isGraphiteFont; +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/font/Font.cpp b/Build/source/libs/graphite/engine-2.3.1/src/font/Font.cpp new file mode 100644 index 00000000000..02a5769d809 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/font/Font.cpp @@ -0,0 +1,949 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999 - 2008 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: Font.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + A font is an object that knows how to read tables from a (platform-specific) font resource. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + + +namespace gr +{ + +//:>******************************************************************************************** +//:> Font methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Constructor. +----------------------------------------------------------------------------------------------*/ +Font::Font(const Font & fontSrc) : m_pfface(fontSrc.m_pfface), m_fTablesCached(false) +{ + if (m_pfface) + m_pfface->IncFontCount(); +} + +/*---------------------------------------------------------------------------------------------- + Destructor. +----------------------------------------------------------------------------------------------*/ +Font::~Font() +{ + if (m_pfface) + m_pfface->DecFontCount(); +} + +/*---------------------------------------------------------------------------------------------- + Mark the font-cache as to whether it should be deleted when all the font instances go away. +----------------------------------------------------------------------------------------------*/ +void Font::SetFlushMode(int flush) +{ + FontFace::SetFlushMode(flush); +} + +/*---------------------------------------------------------------------------------------------- + Cache the common tables if necessary. +----------------------------------------------------------------------------------------------*/ +void Font::EnsureTablesCached() +{ + if (m_fTablesCached) + return; + + size_t cbBogus; + m_pHead = getTable(TtfUtil::TableIdTag(ktiHead), &cbBogus); + m_pHmtx = getTable(TtfUtil::TableIdTag(ktiHmtx), &m_cbHmtxSize); + m_pGlyf = getTable(TtfUtil::TableIdTag(ktiGlyf), &cbBogus); + m_pLoca = getTable(TtfUtil::TableIdTag(ktiLoca), &m_cbLocaSize); + + // getTable can return 0 for the lengths if it is hard to calculate them, but it should + // never do that for hmtx or loca. + Assert(m_cbHmtxSize > 0); + Assert(m_cbLocaSize > 0); + + m_fTablesCached = true; +} + +/*---------------------------------------------------------------------------------------------- + Return the flush mode of the font-cache. +----------------------------------------------------------------------------------------------*/ +int Font::GetFlushMode() +{ + return FontFace::GetFlushMode(); +} + +/*---------------------------------------------------------------------------------------------- + Return the internal object representing the font-face. This is the object that is stored + in Graphite's internal cache. +----------------------------------------------------------------------------------------------*/ +inline FontFace & Font::fontFace(bool fDumbFallback) +{ + if (m_pfface == 0) + initialiseFontFace(fDumbFallback); + return *m_pfface; +} + +void Font::initialiseFontFace(bool fDumbFallback) +{ + std::wstring stuFaceName; + bool fBold, fItalic; + + UniqueCacheInfo(stuFaceName, fBold, fItalic); + + m_pfface = FontFace::GetFontFace(this, stuFaceName, fBold, fItalic, fDumbFallback); + + Assert(m_pfface != 0); + + m_pfface->IncFontCount(); + + FontException fexptn; + fexptn.version = -1; + fexptn.subVersion = -1; + if (m_pfface->BadFont(&fexptn.errorCode) + || (!fDumbFallback && m_pfface->DumbFallback(&fexptn.errorCode))) + { + throw fexptn; + } +} + +/*---------------------------------------------------------------------------------------------- + Return uniquely identifying information that will be used as the key for this font + in the font cache. This includes the font face name and the bold and italic flags. +----------------------------------------------------------------------------------------------*/ +void Font::UniqueCacheInfo(std::wstring & stuFace, bool & fBold, bool & fItalic) +{ + size_t cbSize; + const byte * pNameTbl = static_cast<const byte *>(getTable(TtfUtil::TableIdTag(ktiName), &cbSize)); + size_t lOffset, lSize; + if (!TtfUtil::Get31EngFamilyInfo(pNameTbl, lOffset, lSize)) + { + // TODO: try to find any name in any arbitrary language. + Assert(false); + return; + } + // byte * pvName = (byte *)pNameTbl + lOffset; + utf16 rgchwFace[128]; + const size_t cchw = min(long(lSize / sizeof(utf16)), long(sizeof rgchwFace - 1)); + const utf16 *src_start = reinterpret_cast<const utf16 *>(pNameTbl+ lOffset); + std::copy(src_start, src_start + cchw, rgchwFace); + rgchwFace[cchw] = 0; // zero terminate + TtfUtil::SwapWString(rgchwFace, cchw); + for (size_t ich16 = 0; ich16 < cchw; ich16++) // to handle situation where wchar_t is 4 bytes + stuFace.push_back(wchar_t(rgchwFace[ich16])); + ///stuFace.assign(rgchwFace, rgchwFace + cchw); + + const void * pOs2Tbl = getTable(TtfUtil::TableIdTag(ktiOs2), &cbSize); + TtfUtil::FontOs2Style(pOs2Tbl, fBold, fItalic); + // Do we need to compare the results from the OS2 table with the italic flag in the + // head table? (There is no requirement that they be consistent!) +} + + +/*---------------------------------------------------------------------------------------------- + A default unhinted implmentation of getGlyphPoint(..) +----------------------------------------------------------------------------------------------*/ +void Font::getGlyphPoint(gid16 glyphID, unsigned int pointNum, Point & pointReturn) +{ + EnsureTablesCached(); + + // Default values + pointReturn.x = 0; + pointReturn.y = 0; + + if (m_pGlyf == 0) return; + if (m_pHead == 0) return; + if (m_pLoca == 0) return; + + size_t cContours; + size_t cPoints; + + const size_t CONTOUR_BUF_SIZE = 16; + const size_t POINT_BUF_SIZE = 64; + // Fixed-size buffers that will be used in most cases: + bool rgfOnCurveFixedBuf[POINT_BUF_SIZE], *rgfOnCurveHeapBuf=0; + int rgnEndPtFixedBuf[CONTOUR_BUF_SIZE], *rgnEndPtHeapBuf=0, + rgnXFixedBuf[POINT_BUF_SIZE], *rgnXHeapBuf=0, + rgnYFixedBuf[POINT_BUF_SIZE], *rgnYHeapBuf=0; + + if (!TtfUtil::GlyfContourCount(glyphID, m_pGlyf, m_pLoca, m_cbLocaSize, m_pHead, cContours)) + return; // can't figure it out + + int * prgnEndPt = (cContours > CONTOUR_BUF_SIZE) ? + rgnEndPtHeapBuf = new int[cContours] : + rgnEndPtFixedBuf; + + if (!TtfUtil::GlyfContourEndPoints(glyphID, m_pGlyf, m_pLoca, m_cbLocaSize, m_pHead, + prgnEndPt, cContours)) + { + return; // should have been caught above + } + cPoints = prgnEndPt[cContours - 1] + 1; + + bool* prgfOnCurve = (cPoints > POINT_BUF_SIZE) ? rgfOnCurveHeapBuf= new bool[cPoints] : rgfOnCurveFixedBuf; + int * prgnX = (cPoints > POINT_BUF_SIZE) ? rgnXHeapBuf= new int[cPoints] : rgnXFixedBuf; + int * prgnY = (cPoints > POINT_BUF_SIZE) ? rgnYHeapBuf= new int[cPoints] : rgnYFixedBuf; + + if (TtfUtil::GlyfPoints(glyphID, m_pGlyf, m_pLoca, m_cbLocaSize, m_pHead, 0, 0, + prgnX, prgnY, prgfOnCurve, cPoints)) + { + float nPixEmSquare; + getFontMetrics(0, 0, &nPixEmSquare); + + const float nDesignUnitsPerPixel = float(TtfUtil::DesignUnits(m_pHead)) / nPixEmSquare; + pointReturn.x = prgnX[pointNum] / nDesignUnitsPerPixel; + pointReturn.y = prgnY[pointNum] / nDesignUnitsPerPixel; + } + + delete[] rgnEndPtHeapBuf; + delete[] rgfOnCurveHeapBuf; + delete[] rgnXHeapBuf; + delete[] rgnYHeapBuf; +} + + +/*---------------------------------------------------------------------------------------------- + A default unhinted implmentation of getGlyphMetrics(..) +----------------------------------------------------------------------------------------------*/ +void Font::getGlyphMetrics(gid16 glyphID, gr::Rect & boundingBox, gr::Point & advances) +{ + EnsureTablesCached(); + + // Setup default return values in case of failiure. + boundingBox.left = 0; + boundingBox.right = 0; + boundingBox.bottom = 0; + boundingBox.top = 0; + advances.x = 0; + advances.y = 0; + + if (m_pHead == 0) return; + + // Calculate the number of design units per pixel. + float pixelEmSquare; + getFontMetrics(0, 0, &pixelEmSquare); + const float pixelsPerDesignUnit = + pixelEmSquare / float(TtfUtil::DesignUnits(m_pHead)); + + // getTable can return 0 for the lengths if it is hard to calculate them, but it should + // never do that for hmtx or loca. + Assert(m_cbHmtxSize > 0); + + // Use the Hmtx and Head tables to find the glyph advances. + int lsb; + unsigned int advance = 0; + if (TtfUtil::HorMetrics(glyphID, m_pHmtx, m_cbHmtxSize, m_pHead, + lsb, advance)) + { + advances.x = (advance * pixelsPerDesignUnit); + advances.y = 0.0f; + } + + if (m_pGlyf == 0) return; + if (m_pLoca == 0) return; + Assert(m_cbLocaSize > 0); + + // Fetch the glyph bounding box. GlyphBox may return false for a whitespace glyph. + // Note that using GlyfBox here allows attachment points (ie, points lying outside + // the glyph's outline) to affect the bounding box, which might not be what we want. + int xMin, xMax, yMin, yMax; + if (TtfUtil::GlyfBox(glyphID, m_pGlyf, m_pLoca, m_cbLocaSize, m_pHead, + xMin, yMin, xMax, yMax)) + { + boundingBox.left = (xMin * pixelsPerDesignUnit); + boundingBox.bottom = (yMin * pixelsPerDesignUnit); + boundingBox.right = (xMax * pixelsPerDesignUnit); + boundingBox.top = (yMax * pixelsPerDesignUnit); + } +} + + +/*---------------------------------------------------------------------------------------------- + Return a pair of iterators over a sequence of features defined for the font. +----------------------------------------------------------------------------------------------*/ +std::pair<FeatureIterator, FeatureIterator> Font::getFeatures() +{ + std::pair<FeatureIterator, FeatureIterator> pairRet; + pairRet.first = BeginFeature(); + pairRet.second = EndFeature(); + return pairRet; +} + +/*---------------------------------------------------------------------------------------------- + Returns an iterator indicating the feature with the given ID. If no such feature, + returns the end iterator. +----------------------------------------------------------------------------------------------*/ +FeatureIterator Font::featureWithID(featid id) +{ + FeatureIterator fit = BeginFeature(); + FeatureIterator fitEnd = EndFeature(); + for ( ; fit != fitEnd ; ++fit) + { + if (*fit == id) + return fit; + } + return fitEnd; // invalid +} + +/*---------------------------------------------------------------------------------------------- + Returns the UI label for the indicated feature. Return false if there is no label for the + language, or it is empty. +----------------------------------------------------------------------------------------------*/ +bool Font::getFeatureLabel(FeatureIterator fit, lgid nLanguage, utf16 * label) +{ + return GetFeatureLabel(fit.m_ifeat, nLanguage, label); +} + +/*---------------------------------------------------------------------------------------------- + Returns an iterator pointing at the default value for the feature. +----------------------------------------------------------------------------------------------*/ +FeatureSettingIterator Font::getDefaultFeatureValue(FeatureIterator fit) +{ + size_t ifset = GetFeatureDefault(fit.m_ifeat); + FeatureSettingIterator fsit = fit.BeginSetting(); + fsit += ifset; + return fsit; +} + +/*---------------------------------------------------------------------------------------------- + Returns a pair of iterators spanning the defined settings for the feature. +----------------------------------------------------------------------------------------------*/ +std::pair<FeatureSettingIterator, FeatureSettingIterator> + Font::getFeatureSettings(FeatureIterator fit) +{ + std::pair<FeatureSettingIterator, FeatureSettingIterator> pairRet; + pairRet.first = fit.BeginSetting(); + pairRet.second = fit.EndSetting(); + return pairRet; +} + +/*---------------------------------------------------------------------------------------------- + Returns the UI label for the indicated feature. Return false if there is no label for the + language, or it is empty. +----------------------------------------------------------------------------------------------*/ +bool Font::getFeatureSettingLabel(FeatureSettingIterator fsit, lgid nLanguage, utf16 * label) +{ + //FeatureIterator * pfit = static_cast<FeatureIterator *>(fsit.m_pfit); + return GetFeatureSettingLabel(fsit.m_fit.m_ifeat, fsit.m_ifset, nLanguage, label); +} + +/*---------------------------------------------------------------------------------------------- + Private methods for feature and language access. +----------------------------------------------------------------------------------------------*/ +size_t Font::NumberOfFeatures() +{ + try { + return fontFace().NumberOfFeatures(); + } + catch (...) { return 0; } +} +featid Font::FeatureID(size_t ifeat) +{ + try { + return fontFace().FeatureID(ifeat); + } + catch (...) { return 0; } +} +size_t Font::FeatureWithID(featid id) +{ + try { + return fontFace().FeatureWithID(id); + } + catch (...) { return 0; } +} +bool Font::GetFeatureLabel(size_t ifeat, lgid language, utf16 * label) +{ + try { + return fontFace().GetFeatureLabel(ifeat, language, label); + } + catch (...) + { + *label = 0; + return false; + } +} +int Font::GetFeatureDefault(size_t ifeat) // index of default setting +{ + try { + return fontFace().GetFeatureDefault(ifeat); + } + catch (...) { return 0; } +} +size_t Font::NumberOfSettings(size_t ifeat) +{ + try { + return fontFace().NumberOfSettings(ifeat); + } + catch (...) { return 0; } +} +int Font::GetFeatureSettingValue(size_t ifeat, size_t ifset) +{ + try { + return fontFace().GetFeatureSettingValue(ifeat, ifset); + } + catch (...) { return 0; } +} +bool Font::GetFeatureSettingLabel(size_t ifeat, size_t ifset, lgid language, utf16 * label) +{ + try { + return fontFace().GetFeatureSettingLabel(ifeat, ifset, language, label); + } + catch (...) + { + *label = 0; + return false; + } +} +size_t Font::NumberOfFeatLangs() +{ + try { + return fontFace().NumberOfFeatLangs(); + } + catch (...) { return 0; } +} +short Font::FeatLabelLang(size_t ilang) +{ + try { + return fontFace().FeatLabelLang(ilang); + } + catch (...) { return 0; } +} +size_t Font::NumberOfLanguages() +{ + try { + return fontFace().NumberOfLanguages(); + } + catch (...) { return 0; } +} +isocode Font::LanguageCode(size_t ilang) +{ + try { + return fontFace().LanguageCode(ilang); + } + catch (...) + { + isocode ret; + std::fill_n(ret.rgch, sizeof(ret.rgch), '\0'); + return ret; + } +} + +/*---------------------------------------------------------------------------------------------- + Return a pair of iterators over a sequence of feature-label languages for this font. +----------------------------------------------------------------------------------------------*/ +std::pair<FeatLabelLangIterator, FeatLabelLangIterator> Font::getFeatureLabelLanguages() +{ + std::pair<FeatLabelLangIterator, FeatLabelLangIterator> pairRet; + pairRet.first = BeginFeatLang(); + pairRet.second = EndFeatLang(); + return pairRet; +} + +/*---------------------------------------------------------------------------------------------- + Return a pair of iterators over a sequence of languages defined for the font, that is, + those that have features specified for them. +----------------------------------------------------------------------------------------------*/ +std::pair<LanguageIterator, LanguageIterator> Font::getSupportedLanguages() +{ + std::pair<LanguageIterator, LanguageIterator> pairRet; + pairRet.first = BeginLanguage(); + pairRet.second = EndLanguage(); + return pairRet; +} + +/*---------------------------------------------------------------------------------------------- + Return a bitmap indicating which directions are supported by the font. +----------------------------------------------------------------------------------------------*/ +ScriptDirCode Font::getSupportedScriptDirections() const throw() +{ + try { + Font * pfontThis = const_cast<Font *>(this); + FontFace & fface = pfontThis->fontFace(); + return fface.ScriptDirection(); + } + catch (...) { + return kfsdcHorizLtr; + } +} + +/*---------------------------------------------------------------------------------------------- + For segment creation. +----------------------------------------------------------------------------------------------*/ +void Font::RenderLineFillSegment(Segment * pseg, ITextSource * pts, LayoutEnvironment & layout, + toffset ichStart, toffset ichStop, float xsMaxWidth, bool fBacktracking) +{ + fontFace(layout.dumbFallback()).RenderLineFillSegment(pseg, this, pts, layout, + ichStart, ichStop, xsMaxWidth, fBacktracking); +} + +void Font::RenderRangeSegment(Segment * pseg, ITextSource * pts, LayoutEnvironment & layout, + toffset ichStart, toffset ichStop) +{ + fontFace(layout.dumbFallback()).RenderRangeSegment(pseg, this, pts, layout, ichStart, ichStop); +} + +void Font::RenderJustifiedSegment(Segment * pseg, ITextSource * pts, + LayoutEnvironment & layout, toffset ichStart, toffset ichStop, + float xsCurrentWidth, float xsDesiredWidth) +{ + fontFace(layout.dumbFallback()).RenderJustifiedSegment(pseg, this, pts, layout, + ichStart, ichStop, xsCurrentWidth, xsDesiredWidth); +} + +/*---------------------------------------------------------------------------------------------- + Feature iterators. +----------------------------------------------------------------------------------------------*/ +FeatureIterator Font::BeginFeature() +{ + FeatureIterator fit(this, 0, NumberOfFeatures()); + return fit; +} + +FeatureIterator Font::EndFeature() +{ + int cfeat = NumberOfFeatures(); + FeatureIterator fit (this, cfeat, cfeat); + return fit; +} + +/*---------------------------------------------------------------------------------------------- + Feature-label language iterators. +----------------------------------------------------------------------------------------------*/ +FeatLabelLangIterator Font::BeginFeatLang() +{ + FeatLabelLangIterator lgit(this, 0, NumberOfFeatLangs()); + return lgit; +} + +FeatLabelLangIterator Font::EndFeatLang() +{ + int clang = NumberOfFeatLangs(); + FeatLabelLangIterator lgit(this, clang, clang); + return lgit; +} + + +/*---------------------------------------------------------------------------------------------- + Language iterators. +----------------------------------------------------------------------------------------------*/ +LanguageIterator Font::BeginLanguage() +{ + LanguageIterator lgit(this, 0, NumberOfLanguages()); + return lgit; +} + +LanguageIterator Font::EndLanguage() +{ + int clang = NumberOfLanguages(); + LanguageIterator lgit(this, clang, clang); + return lgit; +} + + +/*---------------------------------------------------------------------------------------------- + Debugging. +----------------------------------------------------------------------------------------------*/ +//bool Font::DbgCheckFontCache() +//{ +// return FontFace::DbgCheckFontCache(); +//} + + +//:>******************************************************************************************** +//:> FeatureIterator methods +//:>******************************************************************************************** + +//FeatureIterator::FeatureIterator(FeatureIterator & fitToCopy) +//{ +// m_pfont = fitToCopy.m_pfont; +// m_ifeat = fitToCopy.m_ifeat; +// m_cfeat = fitToCopy.m_cfeat; +//} + +/*---------------------------------------------------------------------------------------------- + Dereference the iterator, returning a feature ID. +----------------------------------------------------------------------------------------------*/ +featid FeatureIterator::operator*() +{ + if (m_ifeat >= m_cfeat) + { + Assert(false); + return (unsigned int)kInvalid; + } + + return m_pfont->FeatureID(m_ifeat); +} + +/*---------------------------------------------------------------------------------------------- + Increment the iterator. +----------------------------------------------------------------------------------------------*/ +FeatureIterator FeatureIterator::operator++() +{ + if (m_ifeat >= m_cfeat) + { + // Can't increment. + Assert(false); + } + else + m_ifeat++; + + return *this; +} + +/*---------------------------------------------------------------------------------------------- + Increment the iterator by the given value. +----------------------------------------------------------------------------------------------*/ +FeatureIterator FeatureIterator::operator+=(int n) +{ + if (m_ifeat + n >= m_cfeat) + { + // Can't increment. + Assert(false); + m_ifeat = m_cfeat; + } + else if (m_ifeat + n < 0) + { + // Can't decrement. + Assert(false); + m_ifeat = 0; + } + else + m_ifeat += m_cfeat; + + return *this; +} + +/*---------------------------------------------------------------------------------------------- + Test whether the two iterators are equal. +----------------------------------------------------------------------------------------------*/ +bool FeatureIterator::operator==(FeatureIterator & fit) +{ + return (fit.m_ifeat == m_ifeat && fit.m_pfont == m_pfont); +} + +bool FeatureIterator::operator!=(FeatureIterator & fit) +{ + return (fit.m_ifeat != m_ifeat || fit.m_pfont != m_pfont); +} + +/*---------------------------------------------------------------------------------------------- + Return the number of items represented by the range of the two iterators. +----------------------------------------------------------------------------------------------*/ +int FeatureIterator::operator-(FeatureIterator & fit) +{ + if (m_pfont != fit.m_pfont) + { + throw; + } + return (m_ifeat - fit.m_ifeat); +} + + +/*---------------------------------------------------------------------------------------------- + Iterators. +----------------------------------------------------------------------------------------------*/ +FeatureSettingIterator FeatureIterator::BeginSetting() +{ + int cfset = m_pfont->NumberOfSettings(m_ifeat); + FeatureSettingIterator fsit(*this, 0, cfset); + return fsit; +} + +FeatureSettingIterator FeatureIterator::EndSetting() +{ + int cfset = m_pfont->NumberOfSettings(m_ifeat); + FeatureSettingIterator fsit(*this, cfset, cfset); + return fsit; +} + + +//:>******************************************************************************************** +//:> FeatureSettingIterator methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Dereference the iterator, returning a feature value. +----------------------------------------------------------------------------------------------*/ +int FeatureSettingIterator::operator*() +{ + if (m_ifset >= m_cfset) + { + Assert(false); + return kInvalid; + } + + return m_fit.m_pfont->GetFeatureSettingValue(m_fit.m_ifeat, m_ifset); +} + +/*---------------------------------------------------------------------------------------------- + Increment the iterator. +----------------------------------------------------------------------------------------------*/ +FeatureSettingIterator FeatureSettingIterator::operator++() +{ + if (m_ifset >= m_cfset) + { + // Can't increment. + Assert(false); + } + else + m_ifset++; + + return *this; +} + +/*---------------------------------------------------------------------------------------------- + Increment the iterator by the given value. +----------------------------------------------------------------------------------------------*/ +FeatureSettingIterator FeatureSettingIterator::operator+=(int n) +{ + if (m_ifset + n >= m_cfset) + { + // Can't increment. + Assert(false); + m_ifset = m_cfset; + } + if (m_ifset + n < 0) + { + // Can't decrement. + Assert(false); + m_ifset = 0; + } + else + m_ifset += n; + + return *this; +} + +/*---------------------------------------------------------------------------------------------- + Test whether the two iterators are equal. +----------------------------------------------------------------------------------------------*/ +bool FeatureSettingIterator::operator==(FeatureSettingIterator & fsit) +{ + //FeatureIterator * pfit = static_cast<FeatureIterator *>(m_pfit); + //FeatureIterator * pfitOther = static_cast<FeatureIterator *>(fsit.m_pfit); + //return (m_ifset == fsit.m_ifset && pfit == pfitOther); + return (m_ifset == fsit.m_ifset && m_fit == fsit.m_fit); +} + +bool FeatureSettingIterator::operator!=(FeatureSettingIterator & fsit) +{ + //FeatureIterator * pfit = static_cast<FeatureIterator *>(m_pfit); + //FeatureIterator * pfitOther = static_cast<FeatureIterator *>(fsit.m_pfit); + //return (m_ifset != fsit.m_ifset || pfit != pfitOther); + + return (m_ifset != fsit.m_ifset || m_fit != fsit.m_fit); +} + +/*---------------------------------------------------------------------------------------------- + Return the number of items represented by the range of the two iterators. +----------------------------------------------------------------------------------------------*/ +int FeatureSettingIterator::operator-(FeatureSettingIterator fsit) +{ + //FeatureIterator * pfit = static_cast<FeatureIterator *>(m_pfit); + //FeatureIterator * pfitOther = static_cast<FeatureIterator *>(fsit.m_pfit); + //if (pfit != pfitOther) + + if (m_fit != fsit.m_fit) + { + throw; + } + return (m_ifset - fsit.m_ifset); +} + +//:>******************************************************************************************** +//:> FeatLabelLangIterator methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Dereference the iterator, returning a language LCID. +----------------------------------------------------------------------------------------------*/ +data16 FeatLabelLangIterator::operator*() +{ + if (m_ilang >= m_clang) + { + Assert(false); + return 0; + } + + return m_pfont->FeatLabelLang(m_ilang); +} + +/*---------------------------------------------------------------------------------------------- + Increment the iterator. +----------------------------------------------------------------------------------------------*/ +FeatLabelLangIterator FeatLabelLangIterator::operator++() +{ + if (m_ilang >= m_clang) + { + // Can't increment. + Assert(false); + } + else + m_ilang++; + + return *this; +} + +/*---------------------------------------------------------------------------------------------- + Increment the iterator by the given value. +----------------------------------------------------------------------------------------------*/ +FeatLabelLangIterator FeatLabelLangIterator::operator+=(int n) +{ + if (m_ilang + n >= m_clang) + { + // Can't increment. + Assert(false); + m_ilang = m_clang; + } + else if (m_ilang + n < 0) + { + // Can't decrement. + Assert(false); + m_ilang = 0; + } + else + m_ilang += m_clang; + + return *this; +} + +/*---------------------------------------------------------------------------------------------- + Test whether the two iterators are equal. +----------------------------------------------------------------------------------------------*/ +bool FeatLabelLangIterator::operator==(FeatLabelLangIterator & fllit) +{ + return (fllit.m_ilang == m_ilang && fllit.m_pfont == m_pfont); +} + +bool FeatLabelLangIterator::operator!=(FeatLabelLangIterator & fllit) +{ + return (fllit.m_ilang != m_ilang || fllit.m_pfont != m_pfont); +} + +/*---------------------------------------------------------------------------------------------- + Return the number of items represented by the range of the two iterators. +----------------------------------------------------------------------------------------------*/ +int FeatLabelLangIterator::operator-(FeatLabelLangIterator & fllit) +{ + if (m_pfont != fllit.m_pfont) + { + throw; + } + return (m_ilang - fllit.m_ilang); +} + + +//:>******************************************************************************************** +//:> LanguageIterator methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Dereference the iterator, returning a language code. +----------------------------------------------------------------------------------------------*/ +isocode LanguageIterator::operator*() +{ + if (m_ilang >= m_clang) + { + Assert(false); + isocode codeRet; + codeRet.rgch[0] = '?'; codeRet.rgch[1] = '?'; + codeRet.rgch[2] = '?'; codeRet.rgch[3] = 0; + return codeRet; + } + + return m_pfont->LanguageCode(m_ilang); +} + +/*---------------------------------------------------------------------------------------------- + Increment the iterator. +----------------------------------------------------------------------------------------------*/ +LanguageIterator LanguageIterator::operator++() +{ + if (m_ilang >= m_clang) + { + // Can't increment. + Assert(false); + } + else + m_ilang++; + + return *this; +} + +/*---------------------------------------------------------------------------------------------- + Increment the iterator by the given value. +----------------------------------------------------------------------------------------------*/ +LanguageIterator LanguageIterator::operator+=(int n) +{ + if (m_ilang + n >= m_clang) + { + // Can't increment. + Assert(false); + m_ilang = m_clang; + } + else if (m_ilang + n < 0) + { + // Can't decrement. + Assert(false); + m_ilang = 0; + } + else + m_ilang += m_clang; + + return *this; +} + +/*---------------------------------------------------------------------------------------------- + Test whether the two iterators are equal. +----------------------------------------------------------------------------------------------*/ +bool LanguageIterator::operator==(LanguageIterator & lgit) +{ + return (lgit.m_ilang == m_ilang && lgit.m_pfont == m_pfont); +} + +bool LanguageIterator::operator!=(LanguageIterator & lgit) +{ + return (lgit.m_ilang != m_ilang || lgit.m_pfont != m_pfont); +} + +/*---------------------------------------------------------------------------------------------- + Return the number of items represented by the range of the two iterators. +----------------------------------------------------------------------------------------------*/ +int LanguageIterator::operator-(LanguageIterator & lgit) +{ + if (m_pfont != lgit.m_pfont) + { + throw; + } + return (m_ilang - lgit.m_ilang); +} + + +} // namespace gr + + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/font/FontTableCache.h b/Build/source/libs/graphite/engine-2.3.1/src/font/FontTableCache.h new file mode 100644 index 00000000000..461b00d745f --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/font/FontTableCache.h @@ -0,0 +1,52 @@ + +#include "TtfUtil.h" + +namespace gr +{ + + +// used to allow table sharing among font copies +// TBD: should this be a real class or is it acceptable to +// make the attributes public +class FontTableCache +{ +public: + FontTableCache() + : m_fontCount(1) + { + for (int i = 0; i<ktiLast; i++) + { + m_pTable[i] = NULL; + } + } + ~FontTableCache() + { + Assert(m_fontCount == 0); + for (int i = 0; i<ktiLast; i++) + { + if (m_pTable[i]) delete[] m_pTable[i]; + } + } + const int & getFontCount() { return m_fontCount; } + void incrementFontCount() { m_fontCount++; } + void decrementFontCount() { m_fontCount--; Assert(m_fontCount > -1); } + byte * getTable(TableId id) const { Assert(id < ktiLast); return m_pTable[id]; } + const long & getTableSize(TableId id) const + { + Assert(id < ktiLast); + return m_tableSize[id]; + } + byte * allocateTable(TableId id, long size) + { + m_pTable[id] = new byte[size]; + m_tableSize[id] = size; + return m_pTable[id]; + } + +private: + int m_fontCount; + byte * m_pTable[ktiLast]; + long m_tableSize[ktiLast]; +}; + +} diff --git a/Build/source/libs/graphite/engine-2.3.1/src/font/Makefile.am b/Build/source/libs/graphite/engine-2.3.1/src/font/Makefile.am new file mode 100644 index 00000000000..461af621b49 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/font/Makefile.am @@ -0,0 +1,7 @@ +fontdir = $(top_srcdir)/src/font + +libgraphite_la_SOURCES += $(fontdir)/Font.cpp +libgraphite_la_SOURCES += $(fontdir)/TtfUtil.cpp +libgraphite_la_SOURCES += $(fontdir)/FileFont.cpp + +EXTRA_DIST += $(fontdir)/*.h diff --git a/Build/source/libs/graphite/engine-2.3.1/src/font/TtfTypes.h b/Build/source/libs/graphite/engine-2.3.1/src/font/TtfTypes.h new file mode 100644 index 00000000000..da8d90f1f26 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/font/TtfTypes.h @@ -0,0 +1,388 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2007 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TtfTypes.h +Responsibility: Tim Eves +Last reviewed: Not yet. + +Description: +Provides types required to represent the TTF basic types. +-------------------------------------------------------------------------------*//*:End Ignore*/ + + +//********************************************************************************************** +// Include files +//********************************************************************************************** +#include "GrPlatform.h" + +namespace TtfUtil +{ +//********************************************************************************************** +// Forward declarations +//********************************************************************************************** + + +//********************************************************************************************** +// Type declarations +//********************************************************************************************** +typedef gr::data8 uint8; +typedef gr::sdata8 int8; +typedef gr::data16 uint16; +typedef gr::sdata16 int16; +typedef gr::data32 uint32; +typedef gr::sdata32 int32; + +typedef int16 short_frac; +typedef int32 fixed; +typedef int16 fword; +typedef uint16 ufword; +typedef int16 f2dot14; +typedef uint32 long_date_time[2]; + +//********************************************************************************************** +// Constants and enum types +//**********************************************************************************************/ +enum +{ + OneFix = 1<<16 +}; + +//********************************************************************************************** +// Table declarations +//********************************************************************************************** +namespace Sfnt +{ +#pragma pack(1) // We need this or the structure members aren't alligned + // correctly. Fortunately this form of pragma is supposed + // to be recongnised by VS C++ too (at least according to + // MSDN). + + struct OffsetSubTable + { + uint32 scaler_type; + uint16 num_tables, + search_range, + entry_selector, + range_shift; + struct Entry + { + uint32 tag, + checksum, + offset, + length; + } table_directory[1]; + + enum ScalerType + { + TrueTypeMac = 0x74727565U, + TrueTypeWin = 0x00010000U, + Type1 = 0x74797031U + }; + }; + + + + + struct CharacterCodeMap + { + uint16 version, + num_subtables; + struct + { + uint16 platform_id, + platform_specific_id; + uint32 offset; + } encoding[1]; + }; + + struct CmapSubTable + { + uint16 format, + length, + language; + }; + + struct CmapSubTableFormat4 : CmapSubTable + { + uint16 seg_count_x2, + search_range, + entry_selector, + range_shift, + end_code[1]; + // There are arrarys after this which need their + // start positions calculated since end_code is + // seg_count uint16s long. + }; + + struct CmapSubTableFormat12 + { + fixed format; + uint32 length, + language, + num_groups; + struct + { + uint32 start_char_code, + end_char_code, + start_glyph_id; + } group[1]; + }; + + + + struct FontHeader + { + fixed version, + font_revision; + uint32 check_sum_adjustment, + magic_number; + uint16 flags, + units_per_em; + long_date_time created, + modified; + fword x_min, + y_min, + x_max, + y_max; + uint16 mac_style, + lowest_rec_ppem; + int16 font_direction_hint, + index_to_loc_format, + glyph_data_format; + enum + { + MagicNumber = 0x5F0F3CF5, + GlypDataFormat = 0 + }; + enum {ShortIndexLocFormat, LongIndexLocFormat}; + }; + + + + + struct PostScriptGlyphName + { + fixed format, + italic_angle; + fword underline_position, + underline_thickness; + uint32 is_fixed_pitch, + min_mem_type42, + max_mem_type42, + min_mem_type1, + max_mem_type1; + enum + { + Format1 = 0x10000, + Format2 = 0x20000, + Format25 = 0x28000, + Format3 = 0x30000, + Format4 = 0x40000 + }; + }; + + struct PostScriptGlyphName2 : PostScriptGlyphName + { + uint16 number_of_glyphs, + glyph_name_index[1]; + }; + + struct PostScriptGlyphName25 : PostScriptGlyphName + { + uint16 number_of_glyphs; + int8 offset[1]; + }; + + struct PostScriptGlyphName3 : PostScriptGlyphName {}; + + struct PostScriptGlyphName4 : PostScriptGlyphName + { + uint16 glyph_to_char_map[1]; + }; + + + struct HorizontalHeader + { + fixed version; + fword ascent, + descent, + line_gap; + ufword advance_width_max; + fword min_left_side_bearing, + max_left_side_bearing, + x_max_element; + int16 caret_slope_rise, + caret_slope_run; + fword caret_offset; + int16 reserved[4], + metric_data_format; + uint16 num_long_hor_metrics; + }; + + struct MaximumProfile + { + fixed version; + uint16 num_glyphs, + max_points, + max_contours, + max_component_points, + max_component_contours, + max_zones, + max_twilight_points, + max_storage, + max_function_defs, + max_instruction_defs, + max_stack_elements, + max_size_of_instructions, + max_component_elements, + max_component_depth; + }; + + + typedef gr::byte Panose[10]; + + struct Compatibility0 + { + uint16 version; + int16 x_avg_char_width; + uint16 weight_class, + width_class; + int16 fs_type, + y_subscript_x_size, + y_subscript_y_size, + y_subscript_x_offset, + y_subscript_y_offset, + y_superscript_x_size, + y_superscript_y_size, + y_superscript_x_offset, + y_superscript_y_offset, + y_strikeout_size, + y_strikeout_position, + family_class; + Panose panose; + uint32 unicode_range[4]; + int8 ach_vend_id[4]; + uint16 fs_selection, + fs_first_char_index, + fs_last_char_index, // Acording to Apple's spec this is where v0 should end + typo_ascender, + typo_descender, + type_linegap, + win_ascent, + win_descent; + + enum + { + Italic =0x01, + Underscore=0x02, + Negative =0x04, + Outlined =0x08, + StrikeOut =0x10, + Bold =0x20 + }; + }; + + struct Compatibility1 : Compatibility0 + { + uint32 codepage_range[2]; + }; + + struct Compatibility2 : Compatibility1 + { + int16 x_height, + cap_height; + uint16 default_char, + break_char, + max_context; + }; + + struct Compatibility3 : Compatibility2 {}; + + typedef Compatibility3 Compatibility; + + + struct NameRecord + { + uint16 platform_id, + platform_specific_id, + language_id, + name_id, + length, + offset; + enum {Unicode, Mactintosh, Reserved, Microsoft}; + enum + { + Copyright, Family, Subfamily, UniqueSubfamily, + Fullname, Version, PostScript + }; + }; + + struct FontNames + { + uint16 format, + count, + string_offset; + NameRecord name_record[1]; + }; + + + struct HorizontalMetric + { + uint16 advance_width; + int16 left_side_bearing; + }; + + + struct Glyph + { + int16 number_of_contours; + fword x_min, + y_min, + x_max, + y_max; + }; + + struct SimpleGlyph : Glyph + { + uint16 end_pts_of_contours[1]; + enum + { + OnCurve = 0x01, + XShort = 0x02, + YShort = 0x04, + Repeat = 0x08, + XIsSame = 0x10, + XIsPos = 0x10, + YIsSame = 0x20, + YIsPos = 0x20 + }; + }; + + struct CompoundGlyph : Glyph + { + uint16 flags, + glyph_index; + enum + { + Arg1Arg2Words = 0x01, + ArgsAreXYValues = 0x02, + RoundXYToGrid = 0x04, + HaveScale = 0x08, + MoreComponents = 0x20, + HaveXAndYScale = 0x40, + HaveTwoByTwo = 0x80, + HaveInstructions = 0x100, + UseMyMetrics = 0x200, + OverlapCompund = 0x400, + ScaledOffset = 0x800, + UnscaledOffset = 0x1000 + }; + }; + +#pragma pack() +} // end of namespace Sfnt + +} // end of namespace TtfUtil diff --git a/Build/source/libs/graphite/engine-2.3.1/src/font/TtfUtil.cpp b/Build/source/libs/graphite/engine-2.3.1/src/font/TtfUtil.cpp new file mode 100644 index 00000000000..4fe98adadb2 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/font/TtfUtil.cpp @@ -0,0 +1,1935 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2000, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TtfUtil.cpp +Responsibility: Alan Ward +Last reviewed: Not yet. + +Description: + Implements the methods for TtfUtil class. This file should remain portable to any C++ + environment by only using standard C++ and the TTF structurs defined in Tt.h. +-------------------------------------------------------------------------------*//*:End Ignore*/ + + +/*********************************************************************************************** + Include files +***********************************************************************************************/ +// Language headers +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstring> +#include <climits> +#include <stdexcept> +// Platform headers +// Module headers +#include "TtfUtil.h" +#include "TtfTypes.h" + +/*********************************************************************************************** + Forward declarations +***********************************************************************************************/ + +/*********************************************************************************************** + Local Constants and static variables +***********************************************************************************************/ +namespace +{ + // max number of components allowed in composite glyphs + const int kMaxGlyphComponents = 8; + + // These are basic byte order swapping functions + template<typename T> inline T rev16(const T d) { + T r = (d & 0xff) << 8; r |= (d & 0xff00) >> 8; + return r; + } + + template<typename T> inline T rev32(const T d) { + T r = (d & 0xff) << 24; r |= (d & 0xff00) << 8; + r |= (d & 0xff0000) >> 8; r |= (d & 0xff000000) >> 24; + return r; + } + + // This is the generic read function which does the swapping + template<typename T> inline T read(const T d) { + return d; + } + +#if !defined WORDS_BIGENDIAN || defined PC_OS + template<> inline TtfUtil::uint16 read(const TtfUtil::uint16 d) { + return rev16(d); + } + + template<> inline TtfUtil::int16 read(const TtfUtil::int16 d) { + return rev16(d); + } + + template<> inline TtfUtil::uint32 read(const TtfUtil::uint32 d) { + return rev32(d); + } + + template<> inline TtfUtil::int32 read(const TtfUtil::int32 d) { + return rev32(d); + } +#endif + + template <int R, typename T> + inline float fixed_to_float(const T f) { + return float(f)/float(2^R); + } + +#define MAKE_TAG(a,b,c,d) ((a << 24UL) + (b << 16UL) + (c << 8UL) + (d)) + const gr::fontTableId32 mapIdToTag[] = { + MAKE_TAG('c','m','a','p'), + MAKE_TAG('c','v','t',' '), + MAKE_TAG('c','r','y','p'), + MAKE_TAG('h','e','a','d'), + MAKE_TAG('f','p','g','m'), + MAKE_TAG('g','d','i','r'), + MAKE_TAG('g','l','y','f'), + MAKE_TAG('h','d','m','x'), + MAKE_TAG('h','h','e','a'), + MAKE_TAG('h','m','t','x'), + MAKE_TAG('l','o','c','a'), + MAKE_TAG('k','e','r','n'), + MAKE_TAG('L','T','S','H'), + MAKE_TAG('m','a','x','p'), + MAKE_TAG('n','a','m','e'), + MAKE_TAG('O','S','/','2'), + MAKE_TAG('p','o','s','t'), + MAKE_TAG('p','r','e','p'), + MAKE_TAG('F','e','a','t'), + MAKE_TAG('G','l','a','t'), + MAKE_TAG('G','l','o','c'), + MAKE_TAG('S','i','l','f'), + MAKE_TAG('S','i','l','e'), + MAKE_TAG('S','i','l','l') + }; + + +/*---------------------------------------------------------------------------------------------- + Table of standard Postscript glyph names. From Martin Hosken. Disagress with ttfdump.exe +---------------------------------------------------------------------------------------------*/ + const int kcPostNames = 258; + + const char * rgPostName[kcPostNames] = { + ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quotesingle", "parenleft", + "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", + "nine", "colon", "semicolon", "less", "equal", "greater", "question", + "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "bracketleft", "backslash", "bracketright", "asciicircum", + "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", + "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", + "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", + "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", + "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", + "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", + "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", + "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", + "section", "bullet", "paragraph", "germandbls", "registered", + "copyright", "trademark", "acute", "dieresis", "notequal", "AE", + "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", + "mu", "partialdiff", "summation", "product", "pi", "integral", + "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", + "exclamdown", "logicalnot", "radical", "florin", "approxequal", + "Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace", + "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", + "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", + "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", + "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", + "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", + "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", + "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", + "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", + "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", + "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", + "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", + "Yacute", "yacute", "Thorn", "thorn", "minus", "multiply", + "onesuperior", "twosuperior", "threesuperior", "onehalf", "onequarter", + "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla", + "scedilla", "Cacute", "cacute", "Ccaron", "ccaron", + "dcroat" }; + +} // end of namespace + +/*********************************************************************************************** + Methods +***********************************************************************************************/ + +/* Note on error processing: The code guards against bad glyph ids being used to look up data +in open ended tables (loca, hmtx). If the glyph id comes from a cmap this shouldn't happen +but it seems prudent to check for user errors here. The code does assume that data obtained +from the TTF file is valid otherwise (though the CheckTable method seeks to check for +obvious problems that might accompany a change in table versions). For example an invalid +offset in the loca table which could exceed the size of the glyf table is NOT trapped. +Likewise if numberOf_LongHorMetrics in the hhea table is wrong, this will NOT be trapped, +which could cause a lookup in the hmtx table to exceed the table length. Of course, TTF tables +that are completely corrupt will cause unpredictable results. */ + +/* Note on composite glyphs: Glyphs that have components that are themselves composites +are not supported. IsDeepComposite can be used to test for this. False is returned from many +of the methods in this cases. It is unclear how to build composite glyphs in some cases, +so this code represents my best guess until test cases can be found. See notes on the high- +level GlyfPoints method. */ + +namespace TtfUtil +{ + + +/*---------------------------------------------------------------------------------------------- + Get offset and size of the offset table needed to find table directory + Return true if success, false otherwise + lSize excludes any table directory entries +----------------------------------------------------------------------------------------------*/ +bool GetHeaderInfo(size_t & lOffset, size_t & lSize) +{ + lOffset = 0; + lSize = offsetof(Sfnt::OffsetSubTable, table_directory); + assert(sizeof(uint32) + 4*sizeof (uint16) == lSize); + return true; +} + +/*---------------------------------------------------------------------------------------------- + Check the offset table for expected data + Return true if success, false otherwise +----------------------------------------------------------------------------------------------*/ +bool CheckHeader(const void * pHdr) +{ + const Sfnt::OffsetSubTable * pOffsetTable + = reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr); + + return read(pOffsetTable->scaler_type) == Sfnt::OffsetSubTable::TrueTypeWin; +} + +/*---------------------------------------------------------------------------------------------- + Get offset and size of the table directory + Return true if successful, false otherwise +----------------------------------------------------------------------------------------------*/ +bool GetTableDirInfo(const void * pHdr, size_t & lOffset, size_t & lSize) +{ + const Sfnt::OffsetSubTable * pOffsetTable + = reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr); + + lOffset = offsetof(Sfnt::OffsetSubTable, table_directory); + lSize = read(pOffsetTable->num_tables) + * sizeof(Sfnt::OffsetSubTable::Entry); + + return true; +} + + +/*---------------------------------------------------------------------------------------------- + Get offset and size of the specified table + Return true if successful, false otherwise. On false, offset and size will be 0 +----------------------------------------------------------------------------------------------*/ +bool GetTableInfo(TableId ktiTableId, const void * pHdr, const void * pTableDir, + size_t & lOffset, size_t & lSize) +{ + gr::fontTableId32 lTableTag = TableIdTag(ktiTableId); + if (!lTableTag) + { + lOffset = 0; + lSize = 0; + return false; + } + + const Sfnt::OffsetSubTable * pOffsetTable + = reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr); + const Sfnt::OffsetSubTable::Entry + * entry_itr = reinterpret_cast<const Sfnt::OffsetSubTable::Entry *>( + pTableDir), + * const dir_end = entry_itr + read(pOffsetTable->num_tables); + + assert(read(pOffsetTable->num_tables) < 40); + if (read(pOffsetTable->num_tables) > 40) + return false; + + for (;entry_itr != dir_end; ++entry_itr) // 40 - safe guard + { + if (read(entry_itr->tag) == lTableTag) + { + lOffset = read(entry_itr->offset); + lSize = read(entry_itr->length); + return true; + } + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Check the specified table. Tests depend on the table type. + Return true if successful, false otherwise +----------------------------------------------------------------------------------------------*/ +bool CheckTable(TableId ktiTableId, const void * pTable, size_t lTableSize) +{ + using namespace Sfnt; + + switch(ktiTableId) + { + case ktiCmap: // cmap + { + const Sfnt::CharacterCodeMap * const pCmap + = reinterpret_cast<const Sfnt::CharacterCodeMap *>(pTable); + assert(read(pCmap->version) == 0); + return read(pCmap->version) == 0; + } + + case ktiHead: // head + { + const Sfnt::FontHeader * const pHead + = reinterpret_cast<const Sfnt::FontHeader *>(pTable); + bool r = read(pHead->version) == OneFix + && read(pHead->magic_number) == FontHeader::MagicNumber + && read(pHead->glyph_data_format) + == FontHeader::GlypDataFormat + && (read(pHead->index_to_loc_format) + == FontHeader::ShortIndexLocFormat + || read(pHead->index_to_loc_format) + == FontHeader::LongIndexLocFormat) + && sizeof(FontHeader) <= lTableSize; + assert(r); return r; + } + + case ktiPost: // post + { + const Sfnt::PostScriptGlyphName * const pPost + = reinterpret_cast<const Sfnt::PostScriptGlyphName *>(pTable); + const fixed format = read(pPost->format); + bool r = format == PostScriptGlyphName::Format1 + || format == PostScriptGlyphName::Format2 + || format == PostScriptGlyphName::Format3 + || format == PostScriptGlyphName::Format25; + assert(r); return r; + } + + case ktiHhea: // hhea + { + const Sfnt::HorizontalHeader * pHhea = + reinterpret_cast<const Sfnt::HorizontalHeader *>(pTable); + bool r = read(pHhea->version) == OneFix + && read(pHhea->metric_data_format) == 0 + && sizeof (Sfnt::HorizontalHeader) <= lTableSize; + assert(r); return r; + } + + case ktiMaxp: // maxp + { + const Sfnt::MaximumProfile * pMaxp = + reinterpret_cast<const Sfnt::MaximumProfile *>(pTable); + bool r = read(pMaxp->version) == OneFix + && sizeof(Sfnt::MaximumProfile) <= lTableSize; + assert(r); return r; + } + + case ktiOs2: // OS/2 + { + const Sfnt::Compatibility * pOs2 + = reinterpret_cast<const Sfnt::Compatibility *>(pTable); + if (read(pOs2->version) == 0) + { // OS/2 table version 1 size +// if (sizeof(Sfnt::Compatibility) +// - sizeof(uint32)*2 - sizeof(int16)*2 +// - sizeof(uint16)*3 <= lTableSize) + if (sizeof(Sfnt::Compatibility0) <= lTableSize) + return true; + } + else if (read(pOs2->version) == 1) + { // OS/2 table version 2 size +// if (sizeof(Sfnt::Compatibility) +// - sizeof(int16) *2 +// - sizeof(uint16)*3 <= lTableSize) + if (sizeof(Sfnt::Compatibility1) <= lTableSize) + return true; + } + else if (read(pOs2->version) == 2) + { // OS/2 table version 3 size + if (sizeof(Sfnt::Compatibility2) <= lTableSize) + return true; + } + else if (read(pOs2->version) == 3) + { // OS/2 table version 4 size - version 4 changed the meaning of some fields which we don't use + if (sizeof(Sfnt::Compatibility3) <= lTableSize) + return true; + } + else + return false; + } + + case ktiName: + { + const Sfnt::FontNames * pName + = reinterpret_cast<const Sfnt::FontNames *>(pTable); + assert(read(pName->format) == 0); + return read(pName->format) == 0; + } + + default: + break; + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return the number of glyphs in the font. Should never be less than zero. +----------------------------------------------------------------------------------------------*/ +size_t GlyphCount(const void * pMaxp) +{ + const Sfnt::MaximumProfile * pTable = + reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp); + return read(pTable->num_glyphs); +} + +/*---------------------------------------------------------------------------------------------- + Return the maximum number of components for any composite glyph in the font +----------------------------------------------------------------------------------------------*/ +size_t MaxCompositeComponentCount(const void * pMaxp) +{ + const Sfnt::MaximumProfile * pTable = + reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp); + return read(pTable->max_component_elements); +} + +/*---------------------------------------------------------------------------------------------- + Composite glyphs can be composed of glyphs that are themselves composites. + This method returns the maximum number of levels like this for any glyph in the font. + A non-composite glyph has a level of 1 +----------------------------------------------------------------------------------------------*/ +size_t MaxCompositeLevelCount(const void * pMaxp) +{ + const Sfnt::MaximumProfile * pTable = + reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp); + return read(pTable->max_component_depth); +} + +/*---------------------------------------------------------------------------------------------- + Return the number of glyphs in the font according to a differt source. + Should never be less than zero. Return -1 on failure. +----------------------------------------------------------------------------------------------*/ +size_t LocaGlyphCount(size_t lLocaSize, const void * pHead) throw(std::domain_error) +{ + + const Sfnt::FontHeader * pTable + = reinterpret_cast<const Sfnt::FontHeader *>(pHead); + + if (read(pTable->index_to_loc_format) + == Sfnt::FontHeader::ShortIndexLocFormat) + // loca entries are two bytes and have been divided by two + return (lLocaSize >> 1) - 1; + + if (read(pTable->index_to_loc_format) + == Sfnt::FontHeader::LongIndexLocFormat) + // loca entries are four bytes + return (lLocaSize >> 2) - 1; + + //return -1; + throw std::domain_error("head table in inconsistent state. The font may be corrupted"); +} + +/*---------------------------------------------------------------------------------------------- + Return the design units the font is designed with +----------------------------------------------------------------------------------------------*/ +int DesignUnits(const void * pHead) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast<const Sfnt::FontHeader *>(pHead); + + return read(pTable->units_per_em); +} + +/*---------------------------------------------------------------------------------------------- + Return the checksum from the head table, which serves as a unique identifer for the font. +----------------------------------------------------------------------------------------------*/ +int HeadTableCheckSum(const void * pHead) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast<const Sfnt::FontHeader *>(pHead); + + return read(pTable->check_sum_adjustment); +} + +/*---------------------------------------------------------------------------------------------- + Return the create time from the head table. This consists of a 64-bit integer, which + we return here as two 32-bit integers. +----------------------------------------------------------------------------------------------*/ +void HeadTableCreateTime(const void * pHead, + unsigned int * pnDateBC, unsigned int * pnDateAD) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast<const Sfnt::FontHeader *>(pHead); + + *pnDateBC = read(pTable->created[0]); + *pnDateAD = read(pTable->created[1]); +} + +/*---------------------------------------------------------------------------------------------- + Return the modify time from the head table.This consists of a 64-bit integer, which + we return here as two 32-bit integers. +----------------------------------------------------------------------------------------------*/ +void HeadTableModifyTime(const void * pHead, + unsigned int * pnDateBC, unsigned int *pnDateAD) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast<const Sfnt::FontHeader *>(pHead); + + *pnDateBC = read(pTable->modified[0]); + *pnDateAD = read(pTable->modified[1]); +} + +/*---------------------------------------------------------------------------------------------- + Return true if the font is italic. +----------------------------------------------------------------------------------------------*/ +bool IsItalic(const void * pHead) +{ + const Sfnt::FontHeader * pTable = + reinterpret_cast<const Sfnt::FontHeader *>(pHead); + + return read(((pTable->mac_style) & 0x00000002) != 0); +} + +/*---------------------------------------------------------------------------------------------- + Return the ascent for the font +----------------------------------------------------------------------------------------------*/ +int FontAscent(const void * pOs2) +{ + const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2); + + return read(pTable->win_ascent); +} + +/*---------------------------------------------------------------------------------------------- + Return the descent for the font +----------------------------------------------------------------------------------------------*/ +int FontDescent(const void * pOs2) +{ + const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2); + + return read(pTable->win_descent); +} + +/*---------------------------------------------------------------------------------------------- + Get the bold and italic style bits. + Return true if successful. false otherwise. + In addition to checking the OS/2 table, one could also check + the head table's macStyle field (overridden by the OS/2 table on Win) + the sub-family name in the name table (though this can contain oblique, dark, etc too) +----------------------------------------------------------------------------------------------*/ +bool FontOs2Style(const void *pOs2, bool & fBold, bool & fItalic) +{ + const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2); + + fBold = (read(pTable->fs_selection) & Sfnt::Compatibility::Bold) != 0; + fItalic = (read(pTable->fs_selection) & Sfnt::Compatibility::Italic) != 0; + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Method for searching name table. +----------------------------------------------------------------------------------------------*/ +bool GetNameInfo(const void * pName, int nPlatformId, int nEncodingId, + int nLangId, int nNameId, size_t & lOffset, size_t & lSize) +{ + lOffset = 0; + lSize = 0; + + const Sfnt::FontNames * pTable = reinterpret_cast<const Sfnt::FontNames *>(pName); + uint16 cRecord = read(pTable->count); + uint16 nRecordOffset = read(pTable->string_offset); + const Sfnt::NameRecord * pRecord = reinterpret_cast<const Sfnt::NameRecord *>(pTable + 1); + + for (int i = 0; i < cRecord; ++i) + { + if (read(pRecord->platform_id) == nPlatformId && + read(pRecord->platform_specific_id) == nEncodingId && + read(pRecord->language_id) == nLangId && + read(pRecord->name_id) == nNameId) + { + lOffset = read(pRecord->offset) + nRecordOffset; + lSize = read(pRecord->length); + return true; + } + pRecord++; + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Return all the lang-IDs that have data for the given name-IDs. Assume that there is room + in the return array (langIdList) for 128 items. The purpose of this method is to return + a list of all possible lang-IDs. +----------------------------------------------------------------------------------------------*/ +int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId, + int * nameIdList, int cNameIds, short * langIdList) +{ + const Sfnt::FontNames * pTable = reinterpret_cast<const Sfnt::FontNames *>(pName); + uint16 cRecord = read(pTable->count); + //uint16 nRecordOffset = swapw(pTable->stringOffset); + const Sfnt::NameRecord * pRecord = reinterpret_cast<const Sfnt::NameRecord *>(pTable + 1); + + int cLangIds = 0; + for (int i = 0; i < cRecord; ++i) + { + if (read(pRecord->platform_id) == nPlatformId && + read(pRecord->platform_specific_id) == nEncodingId) + { + bool fNameFound = false; + int nLangId = read(pRecord->language_id); + int nNameId = read(pRecord->name_id); + for (int j = 0; j < cNameIds; j++) + { + if (nNameId == nameIdList[j]) + { + fNameFound = true; + break; + } + } + if (fNameFound) + { + // Add it if it's not there. + int ilang; + for (ilang = 0; ilang < cLangIds; ilang++) + if (langIdList[ilang] == nLangId) + break; + if (ilang >= cLangIds) + { + langIdList[cLangIds] = short(nLangId); + cLangIds++; + } + if (cLangIds == 128) + return cLangIds; + } + } + pRecord++; + } + + return cLangIds; +} + +/*---------------------------------------------------------------------------------------------- + Get the offset and size of the font family name in English for the MS Platform with Unicode + writing system. The offset is within the pName data. The string is double byte with MSB first. +----------------------------------------------------------------------------------------------*/ +bool Get31EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize) +{ + return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 1, 1033, + Sfnt::NameRecord::Family, lOffset, lSize); +} + +/*---------------------------------------------------------------------------------------------- + Get the offset and size of the full font name in English for the MS Platform with Unicode + writing system. The offset is within the pName data. The string is double byte with MSB first. +----------------------------------------------------------------------------------------------*/ +bool Get31EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize) +{ + return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 1, 1033, + Sfnt::NameRecord::Fullname, lOffset, lSize); +} + +/*---------------------------------------------------------------------------------------------- + Get the offset and size of the font family name in English for the MS Platform with Symbol + writing system. The offset is within the pName data. The string is double byte with MSB first. +----------------------------------------------------------------------------------------------*/ +bool Get30EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize) +{ + return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 0, 1033, + Sfnt::NameRecord::Family, lOffset, lSize); +} + +/*---------------------------------------------------------------------------------------------- + Get the offset and size of the full font name in English for the MS Platform with Symbol + writing system. The offset is within the pName data. The string is double byte with MSB first. +----------------------------------------------------------------------------------------------*/ +bool Get30EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize) +{ + return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 0, 1033, + Sfnt::NameRecord::Fullname, lOffset, lSize); +} + +/*---------------------------------------------------------------------------------------------- + Return the Glyph ID for a given Postscript name. This method finds the first glyph which + matches the requested Postscript name. Ideally every glyph should have a unique Postscript + name (except for special names such as .notdef), but this is not always true. + On failure return value less than zero. + -1 - table search failed + -2 - format 3 table (no Postscript glyph info) + -3 - other failures +----------------------------------------------------------------------------------------------*/ +int PostLookup(const void * pPost, size_t lPostSize, const void * pMaxp, + const char * pPostName) +{ + using namespace Sfnt; + + const Sfnt::PostScriptGlyphName * pTable + = reinterpret_cast<const Sfnt::PostScriptGlyphName *>(pPost); + fixed format = read(pTable->format); + + if (format == PostScriptGlyphName::Format3) + { // format 3 - no Postscript glyph info in font + return -2; + } + + // search for given Postscript name among the standard names + int iPostName = -1; // index in standard names + for (int i = 0; i < kcPostNames; i++) + { + if (!strcmp(pPostName, rgPostName[i])) + { + iPostName = i; + break; + } + } + + if (format == PostScriptGlyphName::Format1) + { // format 1 - use standard Postscript names + return iPostName; + } + + if (format == PostScriptGlyphName::Format25) + { + if (iPostName == -1) + return -1; + + const PostScriptGlyphName25 * pTable25 + = static_cast<const PostScriptGlyphName25 *>(pTable); + int cnGlyphs = GlyphCount(pMaxp); + for (gr::gid16 nGlyphId = 0; nGlyphId < cnGlyphs && nGlyphId < kcPostNames; + nGlyphId++) + { // glyph_name_index25 contains bytes so no byte swapping needed + // search for first glyph id that uses the standard name + if (nGlyphId + pTable25->offset[nGlyphId] == iPostName) + return nGlyphId; + } + } + + if (format == PostScriptGlyphName::Format2) + { // format 2 + const PostScriptGlyphName2 * pTable2 + = static_cast<const PostScriptGlyphName2 *>(pTable); + + int cnGlyphs = read(pTable2->number_of_glyphs); + + if (iPostName != -1) + { // did match a standard name, look for first glyph id mapped to that name + for (gr::gid16 nGlyphId = 0; nGlyphId < cnGlyphs; nGlyphId++) + { + if (read(pTable2->glyph_name_index[nGlyphId]) == iPostName) + return nGlyphId; + } + return -1; // no glyph with this standard name + } + + else + { // did not match a standard name, search font specific names + size_t nStrSizeGoal = strlen(pPostName); + const char * pFirstGlyphName = reinterpret_cast<const char *>( + &pTable2->glyph_name_index[0] + cnGlyphs); + const char * pGlyphName = pFirstGlyphName; + int iInNames = 0; // index in font specific names + bool fFound = false; + const char * const endOfTable + = reinterpret_cast<const char *>(pTable2) + lPostSize; + while (pGlyphName < endOfTable && !fFound) + { // search Pascal strings for first matching name + size_t nStringSize = size_t(*pGlyphName); + if (nStrSizeGoal != nStringSize || + strncmp(pGlyphName + 1, pPostName, nStringSize)) + { // did not match + ++iInNames; + pGlyphName += nStringSize + 1; + } + else + { // did match + fFound = true; + } + } + if (!fFound) + return -1; // no font specific name matches request + + iInNames += kcPostNames; + for (gr::gid16 nGlyphId = 0; nGlyphId < cnGlyphs; nGlyphId++) + { // search for first glyph id that maps to the found string index + if (read(pTable2->glyph_name_index[nGlyphId]) == iInNames) + return nGlyphId; + } + return -1; // no glyph mapped to this index (very strange) + } + } + + return -3; +} + +/*---------------------------------------------------------------------------------------------- + Convert a Unicode character string from big endian (MSB first, Motorola) format to little + endian (LSB first, Intel) format. + nSize is the number of Unicode characters in the string. It should not include any + terminating null. If nSize is 0, it is assumed the string is null terminated. nSize + defaults to 0. + Return true if successful, false otherwise. +----------------------------------------------------------------------------------------------*/ +void SwapWString(void * pWStr, size_t nSize /* = 0 */) throw (std::invalid_argument) +{ + if (pWStr == 0) + throw std::invalid_argument("null pointer given"); + + uint16 * pStr = reinterpret_cast<uint16 *>(pWStr); + uint16 * const pStrEnd = pStr + (nSize == 0 ? gr::utf16len(pStr) : nSize); + + std::transform(pStr, pStrEnd, pStr, read<uint16>); + +// for (int i = 0; i < nSize; i++) +// { // swap the wide characters in the string +// pStr[i] = gr::utf16(read(uint16(pStr[i]))); +// } +} + +/*---------------------------------------------------------------------------------------------- + Get the left-side bearing and and advance width based on the given tables and Glyph ID + Return true if successful, false otherwise. On false, one or both value could be INT_MIN +----------------------------------------------------------------------------------------------*/ +bool HorMetrics(gr::gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, const void * pHhea, + int & nLsb, unsigned int & nAdvWid) +{ + const Sfnt::HorizontalMetric * phmtx = + reinterpret_cast<const Sfnt::HorizontalMetric *>(pHmtx); + + const Sfnt::HorizontalHeader * phhea = + reinterpret_cast<const Sfnt::HorizontalHeader *>(pHhea); + + size_t cLongHorMetrics = read(phhea->num_long_hor_metrics); + if (nGlyphId < cLongHorMetrics) + { // glyph id is acceptable + nAdvWid = read(phmtx[nGlyphId].advance_width); + nLsb = read(phmtx[nGlyphId].left_side_bearing); + } + else + { + nAdvWid = read(phmtx[cLongHorMetrics - 1].advance_width); + + // guard against bad glyph id + size_t lLsbOffset = sizeof(Sfnt::HorizontalMetric) * cLongHorMetrics + + sizeof(int16) * (nGlyphId - cLongHorMetrics); // offset in bytes + if (lLsbOffset + 1 >= lHmtxSize) // + 1 because entries are two bytes wide + { + nLsb = 0; + return false; + } + const int16 * pLsb = reinterpret_cast<const int16 *>(phmtx) + + lLsbOffset / sizeof(int16); + nLsb = read(*pLsb); + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return a pointer to the requested cmap subtable. By default find the Microsoft Unicode + subtable. Pass nEncoding as -1 to find first table that matches only nPlatformId. + Return NULL if the subtable cannot be found. +----------------------------------------------------------------------------------------------*/ +void * FindCmapSubtable(const void * pCmap, + int nPlatformId, /* =3 */ + int nEncodingId) /* = 1 */ +{ + const Sfnt::CharacterCodeMap * pTable = + reinterpret_cast<const Sfnt::CharacterCodeMap *>(pCmap); + + uint16 csuPlatforms = read(pTable->num_subtables); + for (int i = 0; i < csuPlatforms; i++) + { + if (read(pTable->encoding[i].platform_id) == nPlatformId && + (nEncodingId == -1 || read(pTable->encoding[i].platform_specific_id) == nEncodingId)) + { + const void * pRtn = reinterpret_cast<const uint8 *>(pCmap) + + read(pTable->encoding[i].offset); + return const_cast<void *>(pRtn); + } + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Check the Microsoft Unicode subtable for expected values +----------------------------------------------------------------------------------------------*/ +bool CheckCmap31Subtable(const void * pCmap31) +{ + const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmap31); + // Bob H says ome freeware TT fonts have version 1 (eg, CALIGULA.TTF) + // so don't check subtable version. 21 Mar 2002 spec changes version to language. + + return read(pTable->format) == 4; +} + +/*---------------------------------------------------------------------------------------------- + Return the Glyph ID for the given Unicode ID in the Microsoft Unicode subtable. + (Actually this code only depends on subtable being format 4.) + Return 0 if the Unicode ID is not in the subtable. +----------------------------------------------------------------------------------------------*/ +gr::gid16 Cmap31Lookup(const void * pCmap31, int nUnicodeId) +{ + const Sfnt::CmapSubTableFormat4 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmap31); + + uint16 nSeg = read(pTable->seg_count_x2) >> 1; + + uint16 n; + const uint16 * pLeft, * pMid; + uint16 cMid, chStart, chEnd; + + // Binary search of the endCode[] array + pLeft = &(pTable->end_code[0]); + n = nSeg; + while (n > 0) + { + cMid = n >> 1; // Pick an element in the middle + pMid = pLeft + cMid; + chEnd = read(*pMid); + if (nUnicodeId <= chEnd) + { + if (cMid == 0 || nUnicodeId > read(pMid[-1])) + break; // Must be this seg or none! + n = cMid; // Continue on left side, omitting mid point + } + else + { + pLeft = pMid + 1; // Continue on right side, omitting mid point + n -= (cMid + 1); + } + } + + if (!n) + return 0; + + // Ok, we're down to one segment and pMid points to the endCode element + // Either this is it or none is. + + chStart = read(*(pMid += nSeg + 1)); + if (chEnd >= nUnicodeId && nUnicodeId >= chStart) + { + // Found correct segment. Find Glyph Id + int16 idDelta = read(*(pMid += nSeg)); + uint16 idRangeOffset = read(*(pMid += nSeg)); + + if (idRangeOffset == 0) + return (uint16)(idDelta + nUnicodeId); // must use modulus 2^16 + + // Look up value in glyphIdArray + gr::gid16 nGlyphId = read(*(pMid + (nUnicodeId - chStart) + (idRangeOffset >> 1))); + // If this value is 0, return 0. Else add the idDelta + return nGlyphId ? nGlyphId + idDelta : 0; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the next Unicode value in the cmap. Pass 0 to obtain the first item. + Returns 0xFFFF as the last item. + pRangeKey is an optional key that is used to optimize the search; its value is the range + in which the character is found. +----------------------------------------------------------------------------------------------*/ +unsigned int Cmap31NextCodepoint(const void *pCmap31, unsigned int nUnicodeId, int * pRangeKey) +{ + const Sfnt::CmapSubTableFormat4 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmap31); + + uint16 nRange = read(pTable->seg_count_x2) >> 1; + + uint32 nUnicodePrev = (uint32)nUnicodeId; + + const uint16 * pStartCode = &(pTable->end_code[0]) + + nRange // length of end code array + + 1; // reserved word + + if (nUnicodePrev == 0) + { + // return the first codepoint. + if (pRangeKey) + *pRangeKey = 0; + return read(pStartCode[0]); + } + else if (nUnicodePrev >= 0xFFFF) + { + if (pRangeKey) + *pRangeKey = nRange - 1; + return 0xFFFF; + } + + int iRange = (pRangeKey) ? *pRangeKey : 0; + // Just in case we have a bad key: + while (iRange > 0 && read(pStartCode[iRange]) > nUnicodePrev) + iRange--; + while (read(pTable->end_code[iRange]) < nUnicodePrev) + iRange++; + + // Now iRange is the range containing nUnicodePrev. + unsigned int nStartCode = read(pStartCode[iRange]); + unsigned int nEndCode = read(pTable->end_code[iRange]); + + if (nStartCode > nUnicodePrev) + // Oops, nUnicodePrev is not in the cmap! Adjust so we get a reasonable + // answer this time around. + nUnicodePrev = nStartCode - 1; + + if (nEndCode > nUnicodePrev) + { + // Next is in the same range; it is the next successive codepoint. + if (pRangeKey) + *pRangeKey = iRange; + return nUnicodePrev + 1; + } + + // Otherwise the next codepoint is the first one in the next range. + // There is guaranteed to be a next range because there must be one that + // ends with 0xFFFF. + if (pRangeKey) + *pRangeKey = iRange + 1; + return read(pStartCode[iRange + 1]); +} + +/*---------------------------------------------------------------------------------------------- + Check the Microsoft UCS-4 subtable for expected values +----------------------------------------------------------------------------------------------*/ +bool CheckCmap310Subtable(const void *pCmap310) +{ + const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmap310); + return read(pTable->format) == 12; +} + +/*---------------------------------------------------------------------------------------------- + Return the Glyph ID for the given Unicode ID in the Microsoft UCS-4 subtable. + (Actually this code only depends on subtable being format 12.) + Return 0 if the Unicode ID is not in the subtable. +----------------------------------------------------------------------------------------------*/ +gr::gid16 Cmap310Lookup(const void * pCmap310, unsigned int uUnicodeId) +{ + const Sfnt::CmapSubTableFormat12 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmap310); + + //uint32 uLength = read(pTable->length); //could use to test for premature end of table + uint32 ucGroups = read(pTable->num_groups); + + for (unsigned int i = 0; i < ucGroups; i++) + { + uint32 uStartCode = read(pTable->group[i].start_char_code); + uint32 uEndCode = read(pTable->group[i].end_char_code); + if (uUnicodeId >= uStartCode && uUnicodeId <= uEndCode) + { + uint32 uDiff = uUnicodeId - uStartCode; + uint32 uStartGid = read(pTable->group[i].start_glyph_id); + return static_cast<gr::gid16>(uStartGid + uDiff); + } + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the next Unicode value in the cmap. Pass 0 to obtain the first item. + Returns 0x10FFFF as the last item. + pRangeKey is an optional key that is used to optimize the search; its value is the range + in which the character is found. +----------------------------------------------------------------------------------------------*/ +unsigned int Cmap310NextCodepoint(const void *pCmap310, unsigned int nUnicodeId, int * pRangeKey) +{ + const Sfnt::CmapSubTableFormat12 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmap310); + + int nRange = read(pTable->num_groups); + + uint32 nUnicodePrev = (uint32)nUnicodeId; + + if (nUnicodePrev == 0) + { + // return the first codepoint. + if (pRangeKey) + *pRangeKey = 0; + return read(pTable->group[0].start_char_code); + } + else if (nUnicodePrev >= 0x10FFFF) + { + if (pRangeKey) + *pRangeKey = nRange; + return 0x10FFFF; + } + + int iRange = (pRangeKey) ? *pRangeKey : 0; + // Just in case we have a bad key: + while (iRange > 0 && read(pTable->group[iRange].start_char_code) > nUnicodePrev) + iRange--; + while (read(pTable->group[iRange].end_char_code) < nUnicodePrev) + iRange++; + + // Now iRange is the range containing nUnicodePrev. + + unsigned int nStartCode = read(pTable->group[iRange].start_char_code); + unsigned int nEndCode = read(pTable->group[iRange].end_char_code); + + if (nStartCode > nUnicodePrev) + // Oops, nUnicodePrev is not in the cmap! Adjust so we get a reasonable + // answer this time around. + nUnicodePrev = nStartCode - 1; + + if (nEndCode > nUnicodePrev) + { + // Next is in the same range; it is the next successive codepoint. + if (pRangeKey) + *pRangeKey = iRange; + return nUnicodePrev + 1; + } + + // Otherwise the next codepoint is the first one in the next range, or 10FFFF if we're done. + if (pRangeKey) + *pRangeKey = iRange + 1; + return (iRange + 1 >= nRange) ? 0x10FFFF : read(pTable->group[iRange + 1].start_char_code); +} + +/*---------------------------------------------------------------------------------------------- + Return the offset stored in the loca table for the given Glyph ID. + (This offset is into the glyf table.) + Return -1 if the lookup failed. + Technically this method should return an unsigned long but it is unlikely the offset will + exceed 2^31. +----------------------------------------------------------------------------------------------*/ +size_t LocaLookup(gr::gid16 nGlyphId, + const void * pLoca, size_t lLocaSize, + const void * pHead) throw (std::out_of_range) +{ + const Sfnt::FontHeader * pTable = reinterpret_cast<const Sfnt::FontHeader *>(pHead); + + // CheckTable verifies the index_to_loc_format is valid + if (read(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat) + { // loca entries are two bytes and have been divided by two + if (nGlyphId <= (lLocaSize >> 1) - 1) // allow sentinel value to be accessed + { + const uint16 * pTable = reinterpret_cast<const uint16 *>(pLoca); + return (read(pTable[nGlyphId]) << 1); + } + } + + if (read(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat) + { // loca entries are four bytes + if (nGlyphId <= (lLocaSize >> 2) - 1) + { + const uint32 * pTable = reinterpret_cast<const uint32 *>(pLoca); + return read(pTable[nGlyphId]); + } + } + + // only get here if glyph id was bad + //return -1; + throw std::out_of_range("glyph id out of range for font"); +} + +/*---------------------------------------------------------------------------------------------- + Return a pointer into the glyf table based on the given offset (from LocaLookup). + Return NULL on error. +----------------------------------------------------------------------------------------------*/ +void * GlyfLookup(const void * pGlyf, size_t nGlyfOffset) +{ + const uint8 * pByte = reinterpret_cast<const uint8 *>(pGlyf); + return const_cast<uint8 *>(pByte + nGlyfOffset); +} + +/*---------------------------------------------------------------------------------------------- + Get the bounding box coordinates for a simple glyf entry (non-composite) + Return true if successful, false otherwise +----------------------------------------------------------------------------------------------*/ +bool GlyfBox(const void * pSimpleGlyf, int & xMin, int & yMin, + int & xMax, int & yMax) +{ + const Sfnt::Glyph * pGlyph = reinterpret_cast<const Sfnt::Glyph *>(pSimpleGlyf); + + xMin = read(pGlyph->x_min); + yMin = read(pGlyph->y_min); + xMax = read(pGlyph->x_max); + yMax = read(pGlyph->y_max); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return the number of contours for a simple glyf entry (non-composite) + Returning -1 means this is a composite glyph +----------------------------------------------------------------------------------------------*/ +int GlyfContourCount(const void * pSimpleGlyf) +{ + const Sfnt::Glyph * pGlyph = reinterpret_cast<const Sfnt::Glyph *>(pSimpleGlyf); + return read(pGlyph->number_of_contours); // -1 means composite glyph +} + +/*---------------------------------------------------------------------------------------------- + Get the point numbers for the end points of the glyph contours for a simple + glyf entry (non-composite). + cnPointsTotal - count of contours from GlyfContourCount(); (same as number of end points) + prgnContourEndPoints - should point to a buffer large enough to hold cnPoints integers + cnPoints - count of points placed in above range + Return true if successful, false otherwise. + False could indicate a multi-level composite glyphs. +----------------------------------------------------------------------------------------------*/ +bool GlyfContourEndPoints(const void * pSimpleGlyf, int * prgnContourEndPoint, + int cnPointsTotal, int & cnPoints) +{ + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf); + + int cContours = read(pGlyph->number_of_contours); + if (cContours < 0) + return false; // this method isn't supposed handle composite glyphs + + for (int i = 0; i < cContours && i < cnPointsTotal; i++) + { + prgnContourEndPoint[i] = read(pGlyph->end_pts_of_contours[i]); + } + + cnPoints = cContours; + return true; +} + +/*---------------------------------------------------------------------------------------------- + Get the points for a simple glyf entry (non-composite) + cnPointsTotal - count of points from largest end point obtained from GlyfContourEndPoints + prgnX & prgnY - should point to buffers large enough to hold cnPointsTotal integers + The ranges are parallel so that coordinates for point(n) are found at offset n in both + ranges. This is raw point data with relative coordinates. + prgbFlag - should point to a buffer a large enough to hold cnPointsTotal bytes + This range is parallel to the prgnX & prgnY + cnPoints - count of points placed in above ranges + Return true if successful, false otherwise. + False could indicate a composite glyph + TODO: implement +----------------------------------------------------------------------------------------------*/ +bool GlyfPoints(const void * pSimpleGlyf, int * prgnX, int * prgnY, + char * prgbFlag, int cnPointsTotal, int & cnPoints) +{ + using namespace Sfnt; + + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf); + int cContours = read(pGlyph->number_of_contours); + // return false for composite glyph + if (cContours <= 0) + return false; + int cPts = read(pGlyph->end_pts_of_contours[cContours - 1]) + 1; + if (cPts > cnPointsTotal) + return false; + + // skip over bounding box data & point to byte count of instructions (hints) + const uint8 * pbGlyph = reinterpret_cast<const uint8 *> + (&pGlyph->end_pts_of_contours[cContours]); + + // skip over hints & point to first flag + int cbHints = read(*(const uint16 *)pbGlyph); + pbGlyph += sizeof(uint16); + pbGlyph += cbHints; + + // load flags & point to first x coordinate + int iFlag = 0; + while (iFlag < cPts) + { + if (!(*pbGlyph & SimpleGlyph::Repeat)) + { // flag isn't repeated + prgbFlag[iFlag] = (char)*pbGlyph; + pbGlyph++; + iFlag++; + } + else + { // flag is repeated; count specified by next byte + char chFlag = (char)*pbGlyph; + pbGlyph++; + int cFlags = (int)*pbGlyph; + pbGlyph++; + prgbFlag[iFlag] = chFlag; + iFlag++; + for (int i = 0; i < cFlags; i++) + { + prgbFlag[iFlag + i] = chFlag; + } + iFlag += cFlags; + } + } + if (iFlag != cPts) + return false; + + // load x coordinates + iFlag = 0; + while (iFlag < cPts) + { + if (prgbFlag[iFlag] & SimpleGlyph::XShort) + { + prgnX[iFlag] = *pbGlyph; + if (!(prgbFlag[iFlag] & SimpleGlyph::XIsPos)) + { + prgnX[iFlag] = -prgnX[iFlag]; + } + pbGlyph++; + } + else + { + if (prgbFlag[iFlag] & SimpleGlyph::XIsSame) + { + prgnX[iFlag] = 0; + // do NOT increment pbGlyph + } + else + { + prgnX[iFlag] = read(*(const int16 *)pbGlyph); + pbGlyph += sizeof(int16); + } + } + iFlag++; + } + + // load y coordinates + iFlag = 0; + while (iFlag < cPts) + { + if (prgbFlag[iFlag] & SimpleGlyph::YShort) + { + prgnY[iFlag] = *pbGlyph; + if (!(prgbFlag[iFlag] & SimpleGlyph::YIsPos)) + { + prgnY[iFlag] = -prgnY[iFlag]; + } + pbGlyph++; + } + else + { + if (prgbFlag[iFlag] & SimpleGlyph::YIsSame) + { + prgnY[iFlag] = 0; + // do NOT increment pbGlyph + } + else + { + prgnY[iFlag] = read(*(const int16 *)pbGlyph); + pbGlyph += sizeof(int16); + } + } + iFlag++; + } + + cnPoints = cPts; + return true; +} + +/*---------------------------------------------------------------------------------------------- + Fill prgnCompId with the component Glyph IDs from pSimpleGlyf. + Client must allocate space before calling. + pSimpleGlyf - assumed to point to a composite glyph + cCompIdTotal - the number of elements in prgnCompId + cCompId - the total number of Glyph IDs stored in prgnCompId + Return true if successful, false otherwise + False could indicate a non-composite glyph or the input array was not big enough +----------------------------------------------------------------------------------------------*/ +bool GetComponentGlyphIds(const void * pSimpleGlyf, int * prgnCompId, + size_t cnCompIdTotal, size_t & cnCompId) +{ + using namespace Sfnt; + + if (GlyfContourCount(pSimpleGlyf) >= 0) + return false; + + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf); + // for a composite glyph, the special data begins here + const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]); + + uint16 GlyphFlags; + size_t iCurrentComp = 0; + do + { + GlyphFlags = read(*((const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + prgnCompId[iCurrentComp++] = read(*((const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + if (iCurrentComp >= cnCompIdTotal) + return false; + int nOffset = 0; + nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; + nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0; + pbGlyph += nOffset; + } while (GlyphFlags & CompoundGlyph::MoreComponents); + + cnCompId = iCurrentComp; + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return info on how a component glyph is to be placed + pSimpleGlyph - assumed to point to a composite glyph + nCompId - glyph id for component of interest + bOffset - if true, a & b are the x & y offsets for this component + if false, b is the point on this component that is attaching to point a on the + preceding glyph + Return true if successful, false otherwise + False could indicate a non-composite glyph or that component wasn't found +----------------------------------------------------------------------------------------------*/ +bool GetComponentPlacement(const void * pSimpleGlyf, int nCompId, + bool fOffset, int & a, int & b) +{ + using namespace Sfnt; + + if (GlyfContourCount(pSimpleGlyf) >= 0) + return false; + + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf); + // for a composite glyph, the special data begins here + const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]); + + uint16 GlyphFlags; + do + { + GlyphFlags = read(*((const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + if (read(*((const uint16 *)pbGlyph)) == nCompId) + { + pbGlyph += sizeof(uint16); // skip over glyph id of component + fOffset = (GlyphFlags & CompoundGlyph::ArgsAreXYValues) == CompoundGlyph::ArgsAreXYValues; + + if (GlyphFlags & CompoundGlyph::Arg1Arg2Words ) + { + a = read(*(const int16 *)pbGlyph); + pbGlyph += sizeof(int16); + b = read(*(const int16 *)pbGlyph); + pbGlyph += sizeof(int16); + } + else + { // args are signed bytes + a = *pbGlyph++; + b = *pbGlyph++; + } + return true; + } + pbGlyph += sizeof(uint16); // skip over glyph id of component + int nOffset = 0; + nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; + nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0; + pbGlyph += nOffset; + } while (GlyphFlags & CompoundGlyph::MoreComponents); + + // didn't find requested component + fOffset = true; + a = 0; + b = 0; + return false; +} + +/*---------------------------------------------------------------------------------------------- + Return info on how a component glyph is to be transformed + pSimpleGlyph - assumed to point to a composite glyph + nCompId - glyph id for component of interest + flt11, flt11, flt11, flt11 - a 2x2 matrix giving the transform + bTransOffset - whether to transform the offset from above method + The spec is unclear about the meaning of this flag + Currently - initialize to true for MS rasterizer and false for Mac rasterizer, then + on return it will indicate whether transform should apply to offset (MSDN CD 10/99) + Return true if successful, false otherwise + False could indicate a non-composite glyph or that component wasn't found +----------------------------------------------------------------------------------------------*/ +bool GetComponentTransform(const void * pSimpleGlyf, int nCompId, + float & flt11, float & flt12, float & flt21, float & flt22, + bool & fTransOffset) +{ + using namespace Sfnt; + + if (GlyfContourCount(pSimpleGlyf) >= 0) + return false; + + const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf); + // for a composite glyph, the special data begins here + const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]); + + uint16 GlyphFlags; + do + { + GlyphFlags = read(*((const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + if (read(*((const uint16 *)pbGlyph)) == nCompId) + { + pbGlyph += sizeof(uint16); // skip over glyph id of component + pbGlyph += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; // skip over placement data + + if (fTransOffset) // MS rasterizer + fTransOffset = !(GlyphFlags & CompoundGlyph::UnscaledOffset); + else // Apple rasterizer + fTransOffset = (GlyphFlags & CompoundGlyph::ScaledOffset) != 0; + + if (GlyphFlags & CompoundGlyph::HaveScale) + { + flt11 = fixed_to_float<14>(read(*(const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt12 = 0; + flt21 = 0; + flt22 = flt11; + } + else if (GlyphFlags & CompoundGlyph::HaveXAndYScale) + { + flt11 = fixed_to_float<14>(read(*(const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt12 = 0; + flt21 = 0; + flt22 = fixed_to_float<14>(read(*(const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + } + else if (GlyphFlags & CompoundGlyph::HaveTwoByTwo) + { + flt11 = fixed_to_float<14>(read(*(const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt12 = fixed_to_float<14>(read(*(const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt21 = fixed_to_float<14>(read(*(const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + flt22 = fixed_to_float<14>(read(*(const uint16 *)pbGlyph)); + pbGlyph += sizeof(uint16); + } + else + { // identity transform + flt11 = 1.0; + flt12 = 0.0; + flt21 = 0.0; + flt22 = 1.0; + } + return true; + } + pbGlyph += sizeof(uint16); // skip over glyph id of component + int nOffset = 0; + nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; + nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0; + nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0; + pbGlyph += nOffset; + } while (GlyphFlags & CompoundGlyph::MoreComponents); + + // didn't find requested component + fTransOffset = false; + flt11 = 1; + flt12 = 0; + flt21 = 0; + flt22 = 1; + return false; +} + +/*---------------------------------------------------------------------------------------------- + Return a pointer into the glyf table based on the given tables and Glyph ID + Since this method doesn't check for spaces, it is good to call IsSpace before using it. + Return NULL on error +----------------------------------------------------------------------------------------------*/ +void * GlyfLookup(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void * pHead) +{ + // test for valid glyph id + // CheckTable verifies the index_to_loc_format is valid + + const Sfnt::FontHeader * pTable + = reinterpret_cast<const Sfnt::FontHeader *>(pHead); + + if (read(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat) + { // loca entries are two bytes (and have been divided by two) + if (nGlyphId >= (lLocaSize >> 1) - 1) // don't allow nGlyphId to access sentinel + throw std::out_of_range("glyph id out of range for font"); + } + if (read(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat) + { // loca entries are four bytes + if (nGlyphId >= (lLocaSize >> 2) - 1) + throw std::out_of_range("glyph id out of range for font"); + } + + long lGlyfOffset = LocaLookup(nGlyphId, pLoca, lLocaSize, pHead); + void * pSimpleGlyf = GlyfLookup(pGlyf, lGlyfOffset); // invalid loca offset returns null + return pSimpleGlyf; +} + +/*---------------------------------------------------------------------------------------------- + Determine if a particular Glyph ID has any data in the glyf table. If it is white space, + there will be no glyf data, though there will be metric data in hmtx, etc. +----------------------------------------------------------------------------------------------*/ +bool IsSpace(gr::gid16 nGlyphId, const void * pLoca, size_t lLocaSize, const void * pHead) +{ + size_t lGlyfOffset = LocaLookup(nGlyphId, pLoca, lLocaSize, pHead); + + // the +1 should always work because there is a sentinel value at the end of the loca table + size_t lNextGlyfOffset = LocaLookup(nGlyphId + 1, pLoca, lLocaSize, pHead); + + return (lNextGlyfOffset - lGlyfOffset) == 0; +} + +/*---------------------------------------------------------------------------------------------- + Determine if a particular Glyph ID is a multi-level composite. +----------------------------------------------------------------------------------------------*/ +bool IsDeepComposite(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + long lLocaSize, const void * pHead) +{ + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;} + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lLocaSize, pHead); + if (pSimpleGlyf == NULL) + return false; // no way to really indicate an error occured here + + if (GlyfContourCount(pSimpleGlyf) >= 0) + return false; + + int rgnCompId[kMaxGlyphComponents]; // assumes only a limited number of glyph components + size_t cCompIdTotal = kMaxGlyphComponents; + size_t cCompId = 0; + + if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId)) + return false; + + for (size_t i = 0; i < cCompId; i++) + { + void * pSimpleGlyf = GlyfLookup(static_cast<gr::gid16>(rgnCompId[i]), + pGlyf, pLoca, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + + if (GlyfContourCount(pSimpleGlyf) < 0) + return true; + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Get the bounding box coordinates based on the given tables and Glyph ID + Handles both simple and composite glyphs. + Return true if successful, false otherwise. On false, all point values will be INT_MIN + False may indicate a white space glyph +----------------------------------------------------------------------------------------------*/ +bool GlyfBox(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void * pHead, int & xMin, int & yMin, int & xMax, int & yMax) +{ + xMin = yMin = xMax = yMax = INT_MIN; + + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;} + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + + return GlyfBox(pSimpleGlyf, xMin, yMin, xMax, yMax); +} + +/*---------------------------------------------------------------------------------------------- + Get the number of contours based on the given tables and Glyph ID + Handles both simple and composite glyphs. + Return true if successful, false otherwise. On false, cnContours will be INT_MIN + False may indicate a white space glyph or a multi-level composite glyph. +----------------------------------------------------------------------------------------------*/ +bool GlyfContourCount(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void * pHead, size_t & cnContours) +{ + cnContours = static_cast<size_t>(INT_MIN); + + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;} + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + + int cRtnContours = GlyfContourCount(pSimpleGlyf); + if (cRtnContours >= 0) + { + cnContours = size_t(cRtnContours); + return true; + } + + //handle composite glyphs + + int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components + size_t cCompIdTotal = kMaxGlyphComponents; + size_t cCompId = 0; + + if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId)) + return false; + + cRtnContours = 0; + int cTmp = 0; + for (size_t i = 0; i < cCompId; i++) + { + if (IsSpace(static_cast<gr::gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;} + pSimpleGlyf = GlyfLookup(static_cast<gr::gid16>(rgnCompId[i]), + pGlyf, pLoca, lLocaSize, pHead); + if (pSimpleGlyf == 0) {return false;} + // return false on multi-level composite + if ((cTmp = GlyfContourCount(pSimpleGlyf)) < 0) + return false; + cRtnContours += cTmp; + } + + cnContours = size_t(cRtnContours); + return true; +} + +/*---------------------------------------------------------------------------------------------- + Get the point numbers for the end points of the glyph contours based on the given tables + and Glyph ID + Handles both simple and composite glyphs. + cnPoints - count of contours from GlyfContourCount (same as number of end points) + prgnContourEndPoints - should point to a buffer large enough to hold cnPoints integers + Return true if successful, false otherwise. On false, all end points are INT_MIN + False may indicate a white space glyph or a multi-level composite glyph. +----------------------------------------------------------------------------------------------*/ +bool GlyfContourEndPoints(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void * pHead, + int * prgnContourEndPoint, size_t cnPoints) +{ + std::fill_n(prgnContourEndPoint, cnPoints, INT_MIN); + + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;} + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + + int cContours = GlyfContourCount(pSimpleGlyf); + int cActualPts = 0; + if (cContours > 0) + return GlyfContourEndPoints(pSimpleGlyf, prgnContourEndPoint, cnPoints, cActualPts); + + // handle composite glyphs + + int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components + size_t cCompIdTotal = kMaxGlyphComponents; + size_t cCompId = 0; + + if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId)) + return false; + + int * prgnCurrentEndPoint = prgnContourEndPoint; + int cCurrentPoints = cnPoints; + int nPrevPt = 0; + for (size_t i = 0; i < cCompId; i++) + { + if (IsSpace(static_cast<gr::gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;} + pSimpleGlyf = GlyfLookup(static_cast<gr::gid16>(rgnCompId[i]), pGlyf, pLoca, lLocaSize, pHead); + if (pSimpleGlyf == NULL) {return false;} + // returns false on multi-level composite + if (!GlyfContourEndPoints(pSimpleGlyf, prgnCurrentEndPoint, cCurrentPoints, cActualPts)) + return false; + // points in composite are numbered sequentially as components are added + // must adjust end point numbers for new point numbers + for (int j = 0; j < cActualPts; j++) + prgnCurrentEndPoint[j] += nPrevPt; + nPrevPt = prgnCurrentEndPoint[cActualPts - 1] + 1; + + prgnCurrentEndPoint += cActualPts; + cCurrentPoints -= cActualPts; + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Get the points for a glyph based on the given tables and Glyph ID + Handles both simple and composite glyphs. + cnPoints - count of points from largest end point obtained from GlyfContourEndPoints + prgnX & prgnY - should point to buffers large enough to hold cnPoints integers + The ranges are parallel so that coordinates for point(n) are found at offset n in + both ranges. These points are in absolute coordinates. + prgfOnCurve - should point to a buffer a large enough to hold cnPoints bytes (bool) + This range is parallel to the prgnX & prgnY + Return true if successful, false otherwise. On false, all points may be INT_MIN + False may indicate a white space glyph, a multi-level composite, or a corrupt font + // TODO: doesn't support composite glyphs whose components are themselves components + It's not clear from the TTF spec when the transforms should be applied. Should the + transform be done before or after attachment point calcs? (current code - before) + Should the transform be applied to other offsets? (currently - no; however commented + out code is in place so that if CompoundGlyph::UnscaledOffset on the MS rasterizer is + clear (typical) then yes, and if CompoundGlyph::ScaledOffset on the Apple rasterizer is + clear (typical?) then no). See GetComponentTransform. + It's also unclear where point numbering with attachment poinst starts + (currently - first point number is relative to whole glyph, second point number is + relative to current glyph). +----------------------------------------------------------------------------------------------*/ +bool GlyfPoints(gr::gid16 nGlyphId, const void * pGlyf, + const void * pLoca, size_t lLocaSize, const void * pHead, + const int * prgnContourEndPoint, size_t cnEndPoints, + int * prgnX, int * prgnY, bool * prgfOnCurve, size_t cnPoints) +{ + std::fill_n(prgnX, cnPoints, INT_MAX); + std::fill_n(prgnY, cnPoints, INT_MAX); + + if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) + return false; + + void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lLocaSize, pHead); + if (pSimpleGlyf == NULL) + return false; + + int cContours = GlyfContourCount(pSimpleGlyf); + int cActualPts; + if (cContours > 0) + { + if (!GlyfPoints(pSimpleGlyf, prgnX, prgnY, (char *)prgfOnCurve, cnPoints, cActualPts)) + return false; + CalcAbsolutePoints(prgnX, prgnY, cnPoints); + SimplifyFlags((char *)prgfOnCurve, cnPoints); + return true; + } + + // handle composite glyphs + int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components + size_t cCompIdTotal = kMaxGlyphComponents; + size_t cCompId = 0; + + // this will fail if there are more components than there is room for + if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId)) + return false; + + int * prgnCurrentX = prgnX; + int * prgnCurrentY = prgnY; + char * prgbCurrentFlag = (char *)prgfOnCurve; // converting bool to char should be safe + int cCurrentPoints = cnPoints; + bool fOffset = true, fTransOff = true; + int a, b; + float flt11, flt12, flt21, flt22; + // int * prgnPrevX = prgnX; // in case first att pt number relative to preceding glyph + // int * prgnPrevY = prgnY; + for (size_t i = 0; i < cCompId; i++) + { + if (IsSpace(static_cast<gr::gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;} + void * pCompGlyf = GlyfLookup(static_cast<gr::gid16>(rgnCompId[i]), pGlyf, pLoca, lLocaSize, pHead); + if (pCompGlyf == NULL) {return false;} + // returns false on multi-level composite + if (!GlyfPoints(pCompGlyf, prgnCurrentX, prgnCurrentY, prgbCurrentFlag, + cCurrentPoints, cActualPts)) + return false; + if (!GetComponentPlacement(pSimpleGlyf, rgnCompId[i], fOffset, a, b)) + return false; + if (!GetComponentTransform(pSimpleGlyf, rgnCompId[i], + flt11, flt12, flt21, flt22, fTransOff)) + return false; + bool fIdTrans = flt11 == 1.0 && flt12 == 0.0 && flt21 == 0.0 && flt22 == 1.0; + + // convert points to absolute coordinates + // do before transform and attachment point placement are applied + CalcAbsolutePoints(prgnCurrentX, prgnCurrentY, cActualPts); + + // apply transform - see main method note above + // do before attachment point calcs + if (!fIdTrans) + for (int j = 0; j < cActualPts; j++) + { + int x = prgnCurrentX[j]; // store before transform applied + int y = prgnCurrentY[j]; + prgnCurrentX[j] = (int)(x * flt11 + y * flt12); + prgnCurrentY[j] = (int)(x * flt21 + y * flt22); + } + + // apply placement - see main method note above + int nXOff, nYOff; + if (fOffset) // explicit x & y offsets + { + /* ignore fTransOff for now + if (fTransOff && !fIdTrans) + { // transform x & y offsets + nXOff = (int)(a * flt11 + b * flt12); + nYOff = (int)(a * flt21 + b * flt22); + } + else */ + { // don't transform offset + nXOff = a; + nYOff = b; + } + } + else // attachment points + { // in case first point is relative to preceding glyph and second relative to current + // nXOff = prgnPrevX[a] - prgnCurrentX[b]; + // nYOff = prgnPrevY[a] - prgnCurrentY[b]; + // first point number relative to whole composite, second relative to current glyph + nXOff = prgnX[a] - prgnCurrentX[b]; + nYOff = prgnY[a] - prgnCurrentY[b]; + } + for (int j = 0; j < cActualPts; j++) + { + prgnCurrentX[j] += nXOff; + prgnCurrentY[j] += nYOff; + } + + // prgnPrevX = prgnCurrentX; + // prgnPrevY = prgnCurrentY; + prgnCurrentX += cActualPts; + prgnCurrentY += cActualPts; + prgbCurrentFlag += cActualPts; + cCurrentPoints -= cActualPts; + } + + SimplifyFlags((char *)prgfOnCurve, cnPoints); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Simplify the meaning of flags to just indicate whether point is on-curve or off-curve +---------------------------------------------------------------------------------------------*/ +bool SimplifyFlags(char * prgbFlags, int cnPoints) +{ + for (int i = 0; i < cnPoints; i++) + prgbFlags[i] = static_cast<char>(prgbFlags[i] & Sfnt::SimpleGlyph::OnCurve); + return true; +} + +/*---------------------------------------------------------------------------------------------- + Convert relative point coordinates to absolute coordinates + Points are stored in the font such that they are offsets from one another except for the + first point of a glyph. +---------------------------------------------------------------------------------------------*/ +bool CalcAbsolutePoints(int * prgnX, int * prgnY, int cnPoints) +{ + int nX = prgnX[0]; + int nY = prgnY[0]; + for (int i = 1; i < cnPoints; i++) + { + prgnX[i] += nX; + nX = prgnX[i]; + prgnY[i] += nY; + nY = prgnY[i]; + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Convert a numerical table ID to a string containing the actual name of the table. + Returns native order unsigned long. +---------------------------------------------------------------------------------------------*/ +gr::fontTableId32 TableIdTag(const TableId tid) +{ + assert(sizeof(mapIdToTag) == sizeof(gr::fontTableId32) * ktiLast); + assert(tid < ktiLast); + + return mapIdToTag[tid]; +} + +/*---------------------------------------------------------------------------------------------- + Return the length of the 'name' table in bytes. + Currently used. +---------------------------------------------------------------------------------------------*/ +#if 0 +size_t NameTableLength(const gr::byte * pTable) +{ + gr::byte * pb = (const_cast<gr::byte *>(pTable)) + 2; // skip format + size_t cRecords = *pb++ << 8; cRecords += *pb++; + int dbStringOffset0 = (*pb++) << 8; dbStringOffset0 += *pb++; + int dbMaxStringOffset = 0; + for (size_t irec = 0; irec < cRecords; irec++) + { + int nPlatform = (*pb++) << 8; nPlatform += *pb++; + int nEncoding = (*pb++) << 8; nEncoding += *pb++; + int nLanguage = (*pb++) << 8; nLanguage += *pb++; + int nName = (*pb++) << 8; nName += *pb++; + int cbStringLen = (*pb++) << 8; cbStringLen += *pb++; + int dbStringOffset = (*pb++) << 8; dbStringOffset += *pb++; + if (dbMaxStringOffset < dbStringOffset + cbStringLen) + dbMaxStringOffset = dbStringOffset + cbStringLen; + } + return dbStringOffset0 + dbMaxStringOffset; +} +#endif + +} // end of namespace TtfUtil diff --git a/Build/source/libs/graphite/engine-2.3.1/src/font/TtfUtil.h b/Build/source/libs/graphite/engine-2.3.1/src/font/TtfUtil.h new file mode 100644 index 00000000000..bf437ff3475 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/font/TtfUtil.h @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2000, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TtfUtil.h +Responsibility: Alan Ward +Last reviewed: Not yet. + +Description: + Utility class for handling TrueType font files. +----------------------------------------------------------------------------------------------*/ + +#ifdef _MSC_VER +#pragma once +#endif +#ifndef TTFUTIL_H +#define TTFUTIL_H + +#include <cstddef> +#include <stdexcept> +#include "GrPlatform.h" + +// Enumeration used to specify a table in a TTF file +enum TableId +{ + ktiCmap, ktiCvt, ktiCryp, ktiHead, ktiFpgm, ktiGdir, ktiGlyf, + ktiHdmx, ktiHhea, ktiHmtx, ktiLoca, ktiKern, ktiLtsh, ktiMaxp, + ktiName, ktiOs2, ktiPost, ktiPrep, ktiFeat, ktiGlat, ktiGloc, + ktiSilf, ktiSile, ktiSill, + ktiLast /*This gives the enum length - it is not a real table*/ +}; + +/*---------------------------------------------------------------------------------------------- + Class providing utility methods to parse a TrueType font file (TTF). + Callling application handles all file input and memory allocation. + Assumes minimal knowledge of TTF file format. +----------------------------------------------------------------------------------------------*/ +namespace TtfUtil +{ + ////////////////////////////////// tools to find & check TTF tables + bool GetHeaderInfo(size_t & lOffset, size_t & lSize); + bool CheckHeader(const void * pHdr); + bool GetTableDirInfo(const void * pHdr, size_t & lOffset, size_t & lSize); + bool GetTableInfo(TableId ktiTableId, const void * pHdr, const void * pTableDir, + size_t & lOffset, size_t & lSize); + bool CheckTable(TableId ktiTableId, const void * pTable, size_t lTableSize); + + ////////////////////////////////// simple font wide info + size_t GlyphCount(const void * pMaxp); + size_t MaxCompositeComponentCount(const void * pMaxp); + size_t MaxCompositeLevelCount(const void * pMaxp); + size_t LocaGlyphCount(size_t lLocaSize, const void * pHead) throw (std::domain_error); + int DesignUnits(const void * pHead); + int HeadTableCheckSum(const void * pHead); + void HeadTableCreateTime(const void * pHead, unsigned int * pnDateBC, unsigned int * pnDateAD); + void HeadTableModifyTime(const void * pHead, unsigned int * pnDateBC, unsigned int * pnDateAD); + bool IsItalic(const void * pHead); + int FontAscent(const void * pOs2); + int FontDescent(const void * pOs2); + bool FontOs2Style(const void *pOs2, bool & fBold, bool & fItalic); + bool Get31EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize); + bool Get31EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize); + bool Get30EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize); + bool Get30EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize); + int PostLookup(const void * pPost, size_t lPostSize, const void * pMaxp, + const char * pPostName); + + ////////////////////////////////// utility methods helpful for name table + bool GetNameInfo(const void * pName, int nPlatformId, int nEncodingId, + int nLangId, int nNameId, size_t & lOffset, size_t & lSize); + //size_t NameTableLength(const gr::byte * pTable); + int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId, + int *nameIdList, int cNameIds, short *langIdList); + void SwapWString(void * pWStr, size_t nSize = 0) throw (std::invalid_argument); + + ////////////////////////////////// cmap lookup tools + void * FindCmapSubtable(const void * pCmap, int nPlatformId = 3, + int nEncodingId = 1); + bool CheckCmap31Subtable(const void * pCmap31); + gr::gid16 Cmap31Lookup(const void * pCmap31, int nUnicodeId); + unsigned int Cmap31NextCodepoint(const void *pCmap31, unsigned int nUnicodeId, + int * pRangeKey = 0); + bool CheckCmap310Subtable(const void *pCmap310); + gr::gid16 Cmap310Lookup(const void * pCmap310, unsigned int uUnicodeId); + unsigned int Cmap310NextCodepoint(const void *pCmap310, unsigned int nUnicodeId, + int * pRangeKey = 0); + + ///////////////////////////////// horizontal metric data for a glyph + bool HorMetrics(gr::gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, + const void * pHhea, int & nLsb, unsigned int & nAdvWid); + + ///////////////////////////////// convert our TableId enum to standard TTF tags + gr::fontTableId32 TableIdTag(const TableId); + + ////////////////////////////////// primitives for loca and glyf lookup + size_t LocaLookup(gr::gid16 nGlyphId, const void * pLoca, size_t lLocaSize, + const void * pHead) throw (std::out_of_range); + void * GlyfLookup(const void * pGlyf, size_t lGlyfOffset); + + ////////////////////////////////// primitves for simple glyph data + bool GlyfBox(const void * pSimpleGlyf, int & xMin, int & yMin, + int & xMax, int & yMax); + + int GlyfContourCount(const void * pSimpleGlyf); + bool GlyfContourEndPoints(const void * pSimpleGlyf, int * prgnContourEndPoint, + int cnPointsTotal, size_t & cnPoints); + bool GlyfPoints(const void * pSimpleGlyf, int * prgnX, int * prgnY, + char * prgbFlag, int cnPointsTotal, int & cnPoints); + + // primitive to find the glyph ids in a composite glyph + bool GetComponentGlyphIds(const void * pSimpleGlyf, int * prgnCompId, + size_t cnCompIdTotal, size_t & cnCompId); + // primitive to find the placement data for a component in a composite glyph + bool GetComponentPlacement(const void * pSimpleGlyf, int nCompId, + bool fOffset, int & a, int & b); + // primitive to find the transform data for a component in a composite glyph + bool GetComponentTransform(const void * pSimpleGlyf, int nCompId, + float & flt11, float & flt12, float & flt21, float & flt22, bool & fTransOffset); + + ////////////////////////////////// operate on composite or simple glyph (auto glyf lookup) + void * GlyfLookup(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void * pHead); // primitive used by below methods + + // below are primary user methods for handling glyf data + bool IsSpace(gr::gid16 nGlyphId, const void * pLoca, size_t lLocaSize, const void * pHead); + bool IsDeepComposite(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void * pHead); + + bool GlyfBox(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, size_t lLocaSize, + const void * pHead, int & xMin, int & yMin, int & xMax, int & yMax); + bool GlyfContourCount(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void *pHead, size_t & cnContours); + bool GlyfContourEndPoints(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void * pHead, int * prgnContourEndPoint, size_t cnPoints); + bool GlyfPoints(gr::gid16 nGlyphId, const void * pGlyf, const void * pLoca, + size_t lLocaSize, const void * pHead, const int * prgnContourEndPoint, size_t cnEndPoints, + int * prgnX, int * prgnY, bool * prgfOnCurve, size_t cnPoints); + + // utitily method used by high-level GlyfPoints + bool SimplifyFlags(char * prgbFlags, int cnPoints); + bool CalcAbsolutePoints(int * prgnX, int * prgnY, int cnPoints); + +} // end of namespace TtfUtil + +#endif diff --git a/Build/source/libs/graphite/engine-2.3.1/src/generic/BinTree.h b/Build/source/libs/graphite/engine-2.3.1/src/generic/BinTree.h new file mode 100644 index 00000000000..a487df03770 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/generic/BinTree.h @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: BinTree.h +Responsibility: Darrell Zook +Last reviewed: 9/8/99 + +Description: + This file provides declarations of binary tree class(es). +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef BINTREE_H_INCLUDED +#define BINTREE_H_INCLUDED + +/*---------------------------------------------------------------------------------------------- + This template class implements a balanced tree, which is a binary tree where the height of + the two children of any node in the tree can only be different by +/- 1 or 0. + + The class of objects to be stored in the tree should be derived from BalTreeBase, because + the constructor of BalTreeBase adds an object to the tree in sorted order without + allocating any memory. + + Classes that derive from BalTreeBase must have a CompareKey member function that compares + a given key with this->key to determine which should be first when sorting the tree. + The declaration of CompareKey should be as follows: + int CompareKey(const Key & key); + and it should return: + <0 if key is less than the key in the current node (this->key) + 0 if key is equal to the key in the current node (this->key) + >0 if key is greater than the key in the current node (this->key) + + CAUTION: Classes that use this template should never be dynamically instantiated (by using + local variables or new/NewObj). This class is meant to be used only for global + instances of a class. The reason for this restriction is that the tree is never + destroyed, so using dynamic instantiations of objects would result in stale pointers + in the tree. + + Hungarian: btb +----------------------------------------------------------------------------------------------*/ + +template<typename Obj, typename Key> class BalTreeBase +{ +protected: + // Pointers to the left and right subtrees. + Obj * m_rgpobj[2]; + + // The difference between the height of the right sub tree and the height of the left + // sub tree. This should be -1, 0, or +1. Insert may temporarily set it to -2 or +2. + short m_dht; + + // Which way we went from this node when inserting the new node. + short m_ipobj; + + // Inserts this in the balanced tree. + BalTreeBase(Obj ** ppobjRoot, const Key & key); + + // Returns a node with the given key from the tree rooted at pobjRoot. + static Obj * Find(Obj * pobjRoot, const Key & key); + +private: + // Inserts a node in the tree rooted at *ppobjRoot, sorting it based on key. + void Insert(Obj ** ppobjRoot, const Key & key); +}; + +#endif // !BINTREE_H_INCLUDED + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/generic/BinTree_i.cpp b/Build/source/libs/graphite/engine-2.3.1/src/generic/BinTree_i.cpp new file mode 100644 index 00000000000..5929bf0bab1 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/generic/BinTree_i.cpp @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: BinTree_i.cpp +Responsibility: Darrell Zook +Last reviewed: 9/8/99 + +Description: + This file provides implementations of binary tree class(es). + It is used as an #include file in any file which derives from one of the classes. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef BINTREE_I_CPP_INCLUDED +#define BINTREE_I_CPP_INCLUDED + +#include "BinTree.h" +////#include <algorithm> +////#include "GrPlatform.h" +#include "GrCommon.h" + + +//using namespace gr; + +/*********************************************************************************************** + BalTreeBase<Obj, Key> methods +***********************************************************************************************/ + +/*---------------------------------------------------------------------------------------------- + Constructor. + This calls Insert to add this node to the tree in the correct (sorted) position. + + Arguments: + [in] ppobjRoot -> The address of the pointer to the root node of the tree. + [in] key -> References the key of the node to insert. +----------------------------------------------------------------------------------------------*/ +template<typename Obj, typename Key> + BalTreeBase<Obj, Key>::BalTreeBase(Obj ** ppobjRoot, const Key & key) +{ + AssertPtr(ppobjRoot); + AssertPtrN(*ppobjRoot); + + m_rgpobj[0] = NULL; + m_rgpobj[1] = NULL; + m_dht = 0; + + Insert(ppobjRoot, key); +} + +/*---------------------------------------------------------------------------------------------- + This method finds a node with the given key within the tree. If the key is not found in + the tree, NULL is returned. + + Arguments: + [in] pobjRoot -> Points to the root node of the tree. + [in] key -> References the key of the node to search for. +----------------------------------------------------------------------------------------------*/ +template<typename Obj, typename Key> + Obj * BalTreeBase<Obj, Key>::Find(Obj * pobjRoot, const Key & key) +{ + AssertPtrN(pobjRoot); + + for (Obj * pobj = pobjRoot; pobj; ) + { + int nT = pobj->CompareKey(key); + if (0 == nT) + return pobj; + pobj = pobj->m_rgpobj[nT > 0]; + } + return NULL; +} + +/*---------------------------------------------------------------------------------------------- + This method inserts a node within the tree, sorting it based on key. It does not allocate + any memory. + + Arguments: + [in] ppobjRoot -> The address of the pointer to the root node of the tree. + [in] key -> References the key of the node to insert into the tree. + Note: "this" is the node that is being inserted into the tree. +----------------------------------------------------------------------------------------------*/ +template<typename Obj, typename Key> + void BalTreeBase<Obj, Key>::Insert(Obj ** ppobjRoot, const Key & key) +{ + AssertPtr(ppobjRoot); + AssertPtrN(*ppobjRoot); + + Assert(m_dht == 0); + Assert(!m_rgpobj[0]); + Assert(!m_rgpobj[1]); + + Obj * pobjThis = static_cast<Obj *>(this); + + // If the tree is empty, set the root of the tree to this and return. + if (!*ppobjRoot) + { + *ppobjRoot = pobjThis; + return; + } + + Obj ** ppobjRebal = ppobjRoot; + Obj * pobjCur; + + // Place this in the tree. Mark each node indicating which branch we took. + // Remember the lowest node with non-zero m_dht in ppobjRebal. If we have to do + // rebalancing, this is where we'll do it. + for (pobjCur = *ppobjRoot; ; ) + { + AssertPtr(pobjCur); + + int nT = pobjCur->CompareKey(key); + Assert(nT != 0); + + // if nT is positive go right, otherwise go left. + pobjCur->m_ipobj = nT > 0; + + Obj ** ppobjNext = &pobjCur->m_rgpobj[pobjCur->m_ipobj]; + if (!*ppobjNext) + { + // Place the node and break. + *ppobjNext = pobjThis; + break; + } + + // Remember this location if m_dht is non-zero. + if ((*ppobjNext)->m_dht != 0) + ppobjRebal = ppobjNext; + + // Move on down. + pobjCur = *ppobjNext; + } + + // Adjust m_dht from *ppobjRebal to pobjThis. + for (pobjCur = *ppobjRebal; pobjCur != pobjThis; ) + { + AssertPtr(pobjCur); + Assert((unsigned int)pobjCur->m_ipobj < (unsigned int)2); + + // Recall that ppobjRebal is the lowest node on the path which has non-zero m_dht. + Assert(pobjCur->m_dht == 0 || pobjCur == *ppobjRebal); + + // If we went left (m_ipobj == 0) we need to subtract one. If we went right + // (m_ipobj == 1) we need to add one. + pobjCur->m_dht = (short)(pobjCur->m_dht + (pobjCur->m_ipobj << 1) - 1); + pobjCur = pobjCur->m_rgpobj[pobjCur->m_ipobj]; + } + + if ((unsigned int)((*ppobjRebal)->m_dht + 1) <= (unsigned int)2) + { + // No need to rebalance since the new m_dht for *ppobjRebal is within -1 to 1. + return; + } + + // We need to rebalance. + Obj * pobjRebal = *ppobjRebal; + Assert(pobjRebal->m_dht == -2 || pobjRebal->m_dht == 2); + + // Get the direction from pobjRebal and the next lower node. + int ipobj = pobjRebal->m_ipobj; + Obj * pobjChd = pobjRebal->m_rgpobj[ipobj]; + + if (pobjChd->m_ipobj == ipobj) + { + // We branched the same way at pobjRebal and pobjChd. + // Do a single rotation: make pobjChd the parent of pobjRebal. + Assert(pobjChd->m_dht << 1 == pobjRebal->m_dht); + pobjRebal->m_rgpobj[ipobj] = pobjChd->m_rgpobj[1 - ipobj]; + pobjChd->m_rgpobj[1 - ipobj] = pobjRebal; + *ppobjRebal = pobjChd; + + // Fix the m_dht values. + pobjChd->m_dht = 0; + pobjRebal->m_dht = 0; + return; + } + + // We branched opposite ways at pobjRebal and pobjChd. + // Do a double rotation. + Assert(pobjChd->m_ipobj == 1 - ipobj); + pobjCur = pobjChd->m_rgpobj[1 - ipobj]; + AssertPtr(pobjCur); + + // Let (A own[ipobj] B) mean that A->m_rgpobj[ipobj] == B. + // Currently, (pobjRebal own[ipobj] pobjChd) and (pobjChd own[1 - ipobj] pobjCur). + // Make (pobjCur own[ipobj] pobjChd) and (pobjCur own[1 - ipobj] pobjRebal). + + // Rotate pobjChd and pobjCur. + Assert(pobjCur == pobjChd->m_rgpobj[1 - ipobj]); + pobjChd->m_rgpobj[1 - ipobj] = pobjCur->m_rgpobj[ipobj]; + pobjCur->m_rgpobj[ipobj] = pobjChd; + + // Rotate pobjRebal and pobjCur. + Assert(pobjRebal->m_rgpobj[ipobj] == pobjChd); + pobjRebal->m_rgpobj[ipobj] = pobjCur->m_rgpobj[1 - ipobj]; + pobjCur->m_rgpobj[1 - ipobj] = pobjRebal; + + // Fix *ppobjRebal. + *ppobjRebal = pobjCur; + + // Fix the m_dht values. Note that we don't compare with pobjRebal->m_dht since it + // currently has absolute value 2. + Assert(pobjChd->m_dht); + if (pobjCur->m_dht == pobjChd->m_dht) + { + pobjRebal->m_dht = 0; + pobjChd->m_dht = (short)-pobjChd->m_dht; + } + else if (pobjCur->m_dht == -pobjChd->m_dht) + { + pobjRebal->m_dht = pobjChd->m_dht; + pobjChd->m_dht = 0; + } + else + { + pobjRebal->m_dht = 0; + pobjChd->m_dht = 0; + } + pobjCur->m_dht = 0; +} + +#endif // !BINTREE_I_CPP_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/generic/Debug.cpp b/Build/source/libs/graphite/engine-2.3.1/src/generic/Debug.cpp new file mode 100644 index 00000000000..d1f2a67e364 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/generic/Debug.cpp @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: Debug.cpp +Responsibility: Darrell Zook +Last reviewed: Not yet. + +Description: + This file provides the standard debug error functions. +----------------------------------------------------------------------------------------------*/ + +#ifdef _WIN32 +#include <windows.h> +#endif +//#include "common.h" +//#include "Main.h" +#include "GrDebug.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//#include "ModuleEntry.h" +#include <stdio.h> +#ifdef _MSC_VER +#include <crtdbg.h> +#endif + + +#if 0 +//#ifdef DEBUG + +extern HMODULE Win32DllGetModuleHandle(); + +void WINAPI AssertProcLocal(const char * pszExp, const char * pszFile, int nLine, + bool fCritical) +{ + AssertProc(pszExp, pszFile, nLine, fCritical, Win32DllGetModuleHandle()); +} + +void WINAPI WarnProcLocal(const char * pszExp, const char * pszFile, int nLine, + bool fCritical) +{ + WarnProc(pszExp, pszFile, nLine, fCritical, Win32DllGetModuleHandle()); +} + +void WINAPI WarnHrProc(HRESULT hr, const char * pszFile, int nLine, bool fCritical) +{ + char szBuffer[MAX_PATH + 25]; + sprintf(szBuffer, "HRESULT[0x%08x]--", hr); + int cch = lstrlen(szBuffer); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, szBuffer + cch, + sizeof(szBuffer) - cch, NULL); + szBuffer[max(lstrlen(szBuffer) - 2, 0)] = 0; + WarnProcLocal(szBuffer, pszFile, nLine, fCritical); +} + + +OLECHAR * DebugWatch::WatchNV() +{ + // JT: we need this, otherwise if dealing with a deleted object or null pointer + // we may try to call a virtual function where there is no valid VTABLE pointer. + // In the debugger we can't be sure of not dereferencing a bad pointer. + if (!::_CrtIsValidPointer(this, isizeof(this), TRUE )) + return L"A very bad object pointer"; + // We could also be referencing memory that is trashed (e.g., the object has + // been deleted, or deleted and replaced by something else). + if (!dynamic_cast<DebugWatch *>(this)) + return L"A bad object pointer"; + return Watch(); +} + +void DebugWatch::Output (char *fmt, ...) // LIMITED to 10 pointer-sized arguments +{ + struct args { void * pv[10]; }; + _CrtDbgReport (_CRT_WARN, NULL, NULL, __FILE__, fmt, *(args*)(&fmt+1)); +} + +#endif // DEBUG diff --git a/Build/source/libs/graphite/engine-2.3.1/src/generic/GenericResource.h b/Build/source/libs/graphite/engine-2.3.1/src/generic/GenericResource.h new file mode 100644 index 00000000000..119c6317bcd --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/generic/GenericResource.h @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GenericResource.h +Responsibility: John Thomson +Last reviewed: + + Resources needed by certain generic components (but must be created in app's or AppCore's + resource file). +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GenericResource_H +#define GenericResource_H 1 +// Create suitable resources for these if your app uses StackDumper. +// For example +// STRINGTABLE DISCARDABLE +// BEGIN +// "A programming error (%s) has been detected in module %s.\n Please report this to the developers", kstidInternalError +// "Out of memory. To attempt Save, close other apps and click OK. To quit without saving click Cancel", kstidOutOfMemory +// END + + +// Date qualifier string IDs: +#define kstidDateQualBefore 24200 +#define kstidDateQualOn 24201 +#define kstidDateQualAbout 24202 +#define kstidDateQualAfter 24203 +#define kstidDateQualAbt 24204 +#define kstidDateQualBC 24205 +#define kstidDateQualAD 24206 +#define kstidDateBlank 24207 +#define kstidDateBlankM 24208 +#define kstidDateBlankD 24209 + + +#define kstidInternalError 25900 +#define khcidHelpOutOfMemory 25901 +#define khcidNoHelpAvailable 25902 // default help module when there is no specific one. +#define kstidOutOfMemory 25903 +#define kstidUndoFrame 25904 +#define kstidRedoFrame 25905 + +#define kstidFileErrUnknown 25920 +#define kstidFileErrNotFound 25921 +#define kstidFileErrPathNotFound 25922 +#define kstidFileErrTooManyFiles 25923 +#define kstidFileErrAccDenied 25924 +#define kstidFileErrBadHandle 25925 +#define kstidFileErrBadDrive 25926 +#define kstidFileErrWriteProtect 25927 +#define kstidFileErrBadUnit 25928 +#define kstidFileErrNotReady 25929 +#define kstidFileErrSeek 25930 +#define kstidFileErrNotDosDisk 25931 +#define kstidFileErrBadSector 25932 +#define kstidFileErrWriteFault 25933 +#define kstidFileErrReadFault 25934 +#define kstidFileErrGeneral 25935 +#define kstidFileErrSharing 25936 +#define kstidFileErrLock 25937 +#define kstidFileErrEof 25938 +#define kstidFileErrHandleDiskFull 25939 +#define kstidFileErrBadNetPath 25940 +#define kstidFileErrNetworkBusy 25941 +#define kstidFileErrNoDevice 25942 +#define kstidFileErrNoNetAccess 25943 +#define kstidFileErrBadDevice 25944 +#define kstidFileErrBadNetName 25945 +#define kstidFileErrExists 25946 +#define kstidFileErrCantMake 25947 +#define kstidFileErrBadPassword 25948 +#define kstidFileErrNetWriteFault 25949 +#define kstidFileErrDriveLocked 25950 +#define kstidFileErrOpenFailed 25951 +#define kstidFileErrBufOverflow 25952 +#define kstidFileErrDiskFull 25953 +#define kstidFileErrBadName 25954 +#define kstidFileErrNoVolLabel 25955 +#define kstidFileErrAlreadyExists 25956 + +#endif // !GenericResource_H diff --git a/Build/source/libs/graphite/engine-2.3.1/src/generic/HashMap.h b/Build/source/libs/graphite/engine-2.3.1/src/generic/HashMap.h new file mode 100644 index 00000000000..dc2de9d0af7 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/generic/HashMap.h @@ -0,0 +1,328 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: HashMap.h +Responsibility: Steve McConnel +Last reviewed: Not yet. + +Description: + This provides a set of template collection classes to replace std::map. Their primary + reason to exist is to allow explicit checking for internal memory allocation failures. +----------------------------------------------------------------------------------------------*/ +#pragma once +#ifndef HASHMAP_H_INCLUDED +#define HASHMAP_H_INCLUDED +//:End Ignore + +/*---------------------------------------------------------------------------------------------- + Functor class for computing a hash value from an arbitrary object. + + Hungarian: hsho +----------------------------------------------------------------------------------------------*/ +class HashObj +{ +public: + int operator () (void * pKey, int cbKey); +}; + +/*---------------------------------------------------------------------------------------------- + Functor class for comparing two arbitrary objects (of the same class) for equality. + + Hungarian: eqlo +----------------------------------------------------------------------------------------------*/ +class EqlObj +{ +public: + bool operator () (void * pKey1, void * pKey2, int cbKey); +}; + +/*---------------------------------------------------------------------------------------------- + Hash map template collection class whose keys are objects of an arbitrary class. + + Hungarian: hm[K][T] +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H = HashObj, class Eq = EqlObj> class HashMap +{ +public: + //:> Member classes + + /*------------------------------------------------------------------------------------------ + This is the basic data structure for storing one key-value pair in a hash map. In + order to handle hash collisions, this structure is a member of a linked list. + Hungarian: hsnd + ------------------------------------------------------------------------------------------*/ + class HashNode + { + public: + //:> Constructors/destructors/etc. + + HashNode(void) + : m_key(K()), m_value(T()), m_nHash(0), m_ihsndNext(0) + { + } + HashNode(K & key, T & value, int nHash, int ihsndNext = -1) + : m_key(key), m_value(value), m_nHash(nHash), m_ihsndNext(ihsndNext) + { + } + ~HashNode() + { + } + + //:> Member variable access + + void PutKey(K & key) + { + m_key = key; + } + K & GetKey() + { + return m_key; + } + void PutValue(T & value) + { + m_value = value; + } + T & GetValue() + { + return m_value; + } + void PutHash(int nHash) + { + m_nHash = nHash; + } + int GetHash() + { + return m_nHash; + } + void PutNext(int ihsndNext) + { + m_ihsndNext = ihsndNext; + } + int GetNext() + { + return m_ihsndNext; + } + + /*-------------------------------------------------------------------------------------- + Check whether the given HashNode is being used. + --------------------------------------------------------------------------------------*/ + bool InUse() + { + return m_ihsndNext >= -1; + } + + protected: + //:> Member variables + + K m_key; + T m_value; + int m_nHash; + int m_ihsndNext; // -1 means end of list, -(ihsnd + 3) for free list members + }; + + /*------------------------------------------------------------------------------------------ + This provides an iterator for stepping through all HashNodes stored in the hash map. + This is useful primarily for saving the contents of a hash map to a file. + + Hungarian: ithm[K][T] + ------------------------------------------------------------------------------------------*/ + class iterator + { + public: + // Constructors/destructors/etc. + + iterator() : m_phmParent(NULL), m_ihsnd(0) + { + } + iterator(HashMap<K,T,H,Eq> * phm, int ihsnd) : m_phmParent(phm), m_ihsnd(ihsnd) + { + } + iterator(const iterator & v) : m_phmParent(v.m_phmParent), m_ihsnd(v.m_ihsnd) + { + } + ~iterator() + { + } + + // Other public methods + + iterator & operator = (const iterator & ithm) + { + m_phmParent = ithm.m_phmParent; + m_ihsnd = ithm.m_ihsnd; + return *this; + } + T & operator * (void) + { + Assert(m_phmParent); + Assert(m_phmParent->m_prghsnd); + Assert(m_ihsnd < m_phmParent->m_ihsndLim); + return m_phmParent->m_prghsnd[m_ihsnd].GetValue(); + } + HashNode * operator -> (void) + { + Assert(m_phmParent); + Assert(m_phmParent->m_prghsnd); + Assert(m_ihsnd < m_phmParent->m_ihsndLim); + return &m_phmParent->m_prghsnd[m_ihsnd]; + } + iterator & operator ++ (void) + { + Assert(m_phmParent); + ++m_ihsnd; + // make sure that this new HashNode is actually in use + while (m_ihsnd < m_phmParent->m_ihsndLim) + { + if (m_phmParent->m_prghsnd[m_ihsnd].InUse()) + return *this; + // skip to the next one and check it + ++m_ihsnd; + } + if (m_ihsnd > m_phmParent->m_ihsndLim) + m_ihsnd = m_phmParent->m_ihsndLim; + return *this; + } + bool operator == (const iterator & ithm) + { + return (m_phmParent == ithm.m_phmParent) && (m_ihsnd == ithm.m_ihsnd); + } + bool operator != (const iterator & ithm) + { + return (m_phmParent != ithm.m_phmParent) || (m_ihsnd != ithm.m_ihsnd); + } + T & GetValue(void) + { + Assert(m_phmParent); + Assert(m_phmParent->m_prghsnd); + Assert(m_ihsnd < m_phmParent->m_ihsndLim); + Assert(m_phmParent->m_prghsnd[m_ihsnd].InUse()); + return m_phmParent->m_prghsnd[m_ihsnd].GetValue(); + } + K & GetKey(void) + { + Assert(m_phmParent); + Assert(m_phmParent->m_prghsnd); + Assert(m_ihsnd < m_phmParent->m_ihsndLim); + Assert(m_phmParent->m_prghsnd[m_ihsnd].InUse()); + return m_phmParent->m_prghsnd[m_ihsnd].GetKey(); + } + int GetHash() + { + Assert(m_phmParent); + Assert(m_phmParent->m_prghsnd); + Assert(m_ihsnd < m_phmParent->m_ihsndLim); + Assert(m_phmParent->m_prghsnd[m_ihsnd].InUse()); + return m_phmParent->m_prghsnd[m_ihsnd].GetHash(); + } + int GetIndex() + { + Assert(m_phmParent); + Assert(m_phmParent->m_prghsnd); + Assert(m_ihsnd < m_phmParent->m_ihsndLim); + Assert(m_phmParent->m_prghsnd[m_ihsnd].InUse()); + return m_ihsnd; + } + + protected: + //:> Member variables + + HashMap<K,T,H,Eq> * m_phmParent; + int m_ihsnd; + }; + friend class iterator; + + //:> Constructors/destructors/etc. + + HashMap(); + ~HashMap(); + HashMap(HashMap<K,T,H,Eq> & hm); + + //:> Other public methods + + iterator Begin(); + iterator End(); + void Insert(K & key, T & value, bool fOverwrite = false, int * pihsndOut = NULL); + bool Retrieve(K & key, T * pvalueRet); + bool Delete(K & key); + void Clear(); + void CopyTo(HashMap<K,T,H,Eq> & hmKT); + void CopyTo(HashMap<K,T,H,Eq> * phmKT); + + bool GetIndex(K & key, int * pihsndRet); + bool IndexKey(int ihsnd, K * pkeyRet); + bool IndexValue(int ihsnd, T * pvalueRet); + + int Size(); + + /*------------------------------------------------------------------------------------------ + The assignment operator allows an entire hashmap to be assigned as the value of another + hashmap. It throws an error if it runs out of memory. + + @return a reference to this hashmap. (That is how the assignment operator is defined!) + + @param hm is a reference to the other hashmap. + ------------------------------------------------------------------------------------------*/ + HashMap<K,T,H,Eq> & operator = (HashMap<K,T,H,Eq> & hm) + { + hm.CopyTo(this); + return *this; + } + + //:Ignore +#ifdef DEBUG + int _BucketCount(); + int _EmptyBuckets(); + int _BucketsUsed(); + int _FullestBucket(); + bool AssertValid() + { + AssertPtrN(m_prgihsndBuckets); + Assert(m_prgihsndBuckets || !m_cBuckets); + Assert(!m_prgihsndBuckets || m_cBuckets); + AssertArray(m_prgihsndBuckets, m_cBuckets); + AssertPtrN(m_prghsnd); + Assert(m_prghsnd || !m_ihsndMax); + Assert(!m_prghsnd || m_ihsndMax); + AssertArray(m_prghsnd, m_ihsndMax); + Assert(0 <= m_ihsndLim && m_ihsndLim <= m_ihsndMax); + Assert(-1 <= FreeListIdx(m_ihsndFirstFree)); + Assert(FreeListIdx(m_ihsndFirstFree) < m_ihsndLim); + return true; + } +#endif + //:End Ignore + +protected: + //:> Member variables + + int * m_prgihsndBuckets; + int m_cBuckets; + HashNode * m_prghsnd; + int m_ihsndLim; + int m_ihsndMax; + int m_ihsndFirstFree; // stores -(ihsnd + 3) + + //:> Protected methods + //:Ignore + + /*------------------------------------------------------------------------------------------ + Map between real index and "free list" index. Note that this mapping is bidirectional. + ------------------------------------------------------------------------------------------*/ + int FreeListIdx(int ihsnd) + { + return -(ihsnd + 3); + } + //:End Ignore +}; + + +// Local Variables: +// mode:C++ +// c-file-style:"cellar" +// tab-width:4 +// End: + +#endif /*HASHMAP_H_INCLUDED*/ diff --git a/Build/source/libs/graphite/engine-2.3.1/src/generic/HashMap_i.cpp b/Build/source/libs/graphite/engine-2.3.1/src/generic/HashMap_i.cpp new file mode 100644 index 00000000000..baebe427a4e --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/generic/HashMap_i.cpp @@ -0,0 +1,561 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: HashMap_i.cpp +Responsibility: Steve McConnel +Last reviewed: Not yet. + +Description: + This file provides the implementations of methods for the HashMap template collection + classes. It is used as an #include file in any file which explicitly instantiates any + particular type of HashMap<K,T>, HashMapStrUni<T>, or HashMapChars<T>. +----------------------------------------------------------------------------------------------*/ +#pragma once +#ifndef HASHMAP_I_C_INCLUDED +#define HASHMAP_I_C_INCLUDED + +#include "HashMap.h" +////#include <algorithm> +////#include "GrPlatform.h" +#include "GrCommon.h" + +/*********************************************************************************************** + Methods +***********************************************************************************************/ +//:End Ignore + +/*---------------------------------------------------------------------------------------------- + Constructor. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + HashMap<K,T,H,Eq>::HashMap() +{ + m_prgihsndBuckets = NULL; + m_cBuckets = 0; + m_prghsnd = NULL; + m_ihsndLim = 0; + m_ihsndMax = 0; + m_ihsndFirstFree = FreeListIdx(-1); +} + +/*---------------------------------------------------------------------------------------------- + Copy constructor. It throws an error if it runs out of memory. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + HashMap<K,T,H,Eq>::HashMap(HashMap<K,T,H,Eq> & hm) +{ + m_prgihsndBuckets = NULL; + m_cBuckets= 0; + m_prghsnd = NULL; + m_ihsndLim = 0; + m_ihsndMax = 0; + m_ihsndFirstFree = FreeListIdx(-1); + hm.CopyTo(this); +} + +/*---------------------------------------------------------------------------------------------- + Destructor. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + HashMap<K,T,H,Eq>::~HashMap() +{ + Clear(); +} + +/*---------------------------------------------------------------------------------------------- + Return an iterator that references the first key and value stored in the HashMap. + If the hash map is empty, Begin returns the same value as End. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + typename HashMap<K,T,H,Eq>::iterator HashMap<K,T,H,Eq>::Begin() +{ + AssertObj(this); + int ihsnd; + for (ihsnd = 0; ihsnd < m_ihsndLim; ++ihsnd) + { + if (m_prghsnd[ihsnd].InUse()) + { + iterator ithm(this, ihsnd); + return ithm; + } + } + return End(); +} + +/*---------------------------------------------------------------------------------------------- + Return an iterator that marks the end of the set of keys and values stored in the HashMap. + If the HashMap is empty, End returns the same value as Begin. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + typename HashMap<K,T,H,Eq>::iterator HashMap<K,T,H,Eq>::End() +{ + AssertObj(this); + iterator ithm(this, m_ihsndLim); + return ithm; +} + +/*---------------------------------------------------------------------------------------------- + Add one key and value to the HashMap. Insert potentially invalidates existing iterators + for this HashMap. An exception is thrown if there are any errors. + + @param key Reference to the key object. An internal copy is made of this object. + @param value Reference to the object associated with the key. An internal copy is + made of this object. + @param fOverwrite Optional flag (defaults to false) to allow a value already associated + with this key to be replaced by this value. + @param pihsndOut Optional pointer to an integer for returning the internal index where the + key-value pair is stored. + + @exception E_INVALIDARG if fOverwrite is not true and the key already is stored with a value + in this HashMap. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + void HashMap<K,T,H,Eq>::Insert(K & key, T & value, bool fOverwrite, int * pihsndOut) +{ + AssertObj(this); + // Check for initial allocation of memory. + if (!m_cBuckets) + { + int cBuckets = GetPrimeNear(10); + m_prgihsndBuckets = (int *)malloc(cBuckets * isizeof(int)); + if (!m_prgihsndBuckets) + ThrowHr(WarnHr(E_OUTOFMEMORY)); + std::fill_n(m_prgihsndBuckets, cBuckets, -1); + m_cBuckets = cBuckets; + } + if (!m_ihsndMax) + { + int iMax = 32; + m_prghsnd = (HashNode *)malloc(iMax * isizeof(HashNode)); + if (!m_prghsnd) + ThrowHr(WarnHr(E_OUTOFMEMORY)); + std::fill_n(m_prghsnd, iMax, 0); + m_ihsndLim = 0; + m_ihsndMax = iMax; + m_ihsndFirstFree = FreeListIdx(-1); + } + // Check whether this key is already used. + // If it is, store the value if overwriting is allowed, otherwise return an error value. + H hasher; + Eq equal; + int ihsnd; + int nHash = hasher(&key, isizeof(K)); + int ie = (unsigned)nHash % m_cBuckets; + for (ihsnd = m_prgihsndBuckets[ie]; ihsnd != -1; ihsnd = m_prghsnd[ihsnd].GetNext()) + { + if ((nHash == m_prghsnd[ihsnd].GetHash()) && + equal(&key, &m_prghsnd[ihsnd].GetKey(), isizeof(K))) + { + if (fOverwrite) + { + m_prghsnd[ihsnd].PutValue(value); + if (pihsndOut) + *pihsndOut = ihsnd; + AssertObj(this); + return; + } + else + { + ThrowHr(WarnHr(E_INVALIDARG)); + } + } + } + // Check whether to increase the number of buckets to redistribute the wealth. + // Calculate the average depth of hash collection chains. + // If greater than or equal to two, increase the number of buckets. + int chsndFree = 0; + int i; + for (i = m_ihsndFirstFree; i != FreeListIdx(-1); i = m_prghsnd[FreeListIdx(i)].GetNext()) + ++chsndFree; + int chsndAvgDepth = (m_ihsndLim - chsndFree) / m_cBuckets; + if (chsndAvgDepth > 2) + { + int cNewBuckets = GetPrimeNear(4 * m_cBuckets); + if (cNewBuckets && cNewBuckets > m_cBuckets) + { + int * pNewBuckets = (int *)realloc(m_prgihsndBuckets, cNewBuckets * isizeof(int)); + if (pNewBuckets) + { + std::fill_n(pNewBuckets, cNewBuckets, -1); + m_cBuckets = cNewBuckets; + m_prgihsndBuckets = pNewBuckets; + for (ihsnd = 0; ihsnd < m_ihsndLim; ++ihsnd) + { + if (m_prghsnd[ihsnd].InUse()) + { + ie = (unsigned)m_prghsnd[ihsnd].GetHash() % m_cBuckets; + m_prghsnd[ihsnd].PutNext(m_prgihsndBuckets[ie]); + m_prgihsndBuckets[ie] = ihsnd; + } + } + // Recompute the new entry's slot so that it can be stored properly. + ie = (unsigned)nHash % m_cBuckets; + } + } + } + if (m_ihsndLim < m_ihsndMax) + { + ihsnd = m_ihsndLim; + ++m_ihsndLim; + } + else if (m_ihsndFirstFree != FreeListIdx(-1)) + { + ihsnd = FreeListIdx(m_ihsndFirstFree); + m_ihsndFirstFree = m_prghsnd[ihsnd].GetNext(); + } + else + { + int iNewMax = (!m_ihsndMax) ? 32 : 2 * m_ihsndMax; + HashNode * pNewNodes = (HashNode *)realloc(m_prghsnd, iNewMax * isizeof(HashNode)); + if (!pNewNodes && iNewMax > 32) + { + iNewMax = m_ihsndMax + (m_ihsndMax / 2); + pNewNodes = (HashNode *)realloc(m_prghsnd, iNewMax * isizeof(HashNode)); + if (!pNewNodes) + ThrowHr(WarnHr(E_OUTOFMEMORY)); + } + m_prghsnd = pNewNodes; + m_ihsndMax = iNewMax; + Assert(m_ihsndLim < m_ihsndMax); + ihsnd = m_ihsndLim; + ++m_ihsndLim; + } + // Call constructor on previously allocated memory. + new((void *)&m_prghsnd[ihsnd]) HashNode(key, value, nHash, m_prgihsndBuckets[ie]); + m_prgihsndBuckets[ie] = ihsnd; + if (pihsndOut) + *pihsndOut = ihsnd; + AssertObj(this); +} + +/*---------------------------------------------------------------------------------------------- + Search the HashMap for the given key, and return true if the key is found or false if the + key is not found. If the key is found and the given pointer is not NULL, copy the + associated value to that memory location. + + @param key Reference to a key object. + @param pvalueRet Pointer to an empty object for storing a copy of the value associated with + the key, if one exists. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + bool HashMap<K,T,H,Eq>::Retrieve(K & key, T * pvalueRet) +{ + AssertObj(this); + if (!m_prgihsndBuckets) + return false; + H hasher; + Eq equal; + int nHash = hasher(&key, isizeof(K)); + int ie = (unsigned)nHash % m_cBuckets; + int ihsnd; + for (ihsnd = m_prgihsndBuckets[ie]; ihsnd != -1; ihsnd = m_prghsnd[ihsnd].GetNext()) + { + if ((nHash == m_prghsnd[ihsnd].GetHash()) && + equal(&key, &m_prghsnd[ihsnd].GetKey(), isizeof(K))) + { + if (pvalueRet) + *pvalueRet = m_prghsnd[ihsnd].GetValue(); + return true; + } + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + Remove the element with the given key from the stored HashMap. This potentially + invalidates existing iterators for this HashMap. If the key is not found in the + HashMap, then nothing is deleted. + + @param key Reference to a key object. + + @return True if the key is found, and something is actually deleted; otherwise, false. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + bool HashMap<K,T,H,Eq>::Delete(K & key) +{ + AssertObj(this); + if (!m_prgihsndBuckets) + return false; + H hasher; + Eq equal; + int nHash = hasher(&key, isizeof(K)); + int ie = (unsigned)nHash % m_cBuckets; + int ihsnd; + int ihsndPrev = -1; + for (ihsnd = m_prgihsndBuckets[ie]; ihsnd != -1; ihsnd = m_prghsnd[ihsnd].GetNext()) + { + if ((nHash == m_prghsnd[ihsnd].GetHash()) && + equal(&key, &m_prghsnd[ihsnd].GetKey(), isizeof(K))) + { + if (ihsndPrev == -1) + m_prgihsndBuckets[ie] = m_prghsnd[ihsnd].GetNext(); + else + m_prghsnd[ihsndPrev].PutNext(m_prghsnd[ihsnd].GetNext()); + m_prghsnd[ihsnd].~HashNode(); // Ensure member destructors are called. + memset(&m_prghsnd[ihsnd], 0, isizeof(HashNode)); + m_prghsnd[ihsnd].PutNext(m_ihsndFirstFree); + m_ihsndFirstFree = FreeListIdx(ihsnd); + AssertObj(this); + return true; + } + ihsndPrev = ihsnd; + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + Free all the memory used by the HashMap. When done, only the minimum amount of + bookkeeping memory is still taking up space, and any internal pointers all been set + to NULL. The appropriate destructor is called for all key and value objects stored + in the HashMap before the memory space is freed. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + void HashMap<K,T,H,Eq>::Clear() +{ + AssertObj(this); + if (!m_prgihsndBuckets) + return; + int ihsnd; + for (ihsnd = 0; ihsnd < m_ihsndLim; ++ihsnd) + { + if (m_prghsnd[ihsnd].InUse()) + m_prghsnd[ihsnd].~HashNode(); // Ensure member destructors are called. + } + free(m_prgihsndBuckets); + free(m_prghsnd); + m_prgihsndBuckets = NULL; + m_cBuckets = 0; + m_prghsnd = NULL; + m_ihsndLim = 0; + m_ihsndMax = 0; + m_ihsndFirstFree = FreeListIdx(-1); + AssertObj(this); +} + +/*---------------------------------------------------------------------------------------------- + Copy the content of one HashMap to another. An exception is thrown if there are any errors. + + @param hmKT Reference to the other HashMap. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + void HashMap<K,T,H,Eq>::CopyTo(HashMap<K,T,H,Eq> & hmKT) +{ + AssertObj(this); + AssertObj(&hmKT); + hmKT.Clear(); + iterator itmm; + for (itmm = Begin(); itmm != End(); ++itmm) + hmKT.Insert(itmm->GetKey(), itmm->GetValue()); +} + +/*---------------------------------------------------------------------------------------------- + Copy the content of one HashMap to another. An exception is thrown if there are any errors. + + @param phmKT Pointer to the other HashMap. + + @exception E_POINTER if phmKT is NULL. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + void HashMap<K,T,H,Eq>::CopyTo(HashMap<K,T,H,Eq> * phmKT) +{ + if (!phmKT) + ThrowHr(WarnHr(E_POINTER)); + CopyTo(*phmKT); +} + +/*---------------------------------------------------------------------------------------------- + If the given key is found in the HashMap, return true, and if the provided index pointer + is not NULL, also store the internal index value in the indicated memory location. + If the given key is NOT found in the HashMap, return false and ignore the provided index + pointer. + + @param key Reference to a key object. + @param pihsndRet Pointer to an integer for returning the internal index where the + key-value pair is stored. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + bool HashMap<K,T,H,Eq>::GetIndex(K & key, int * pihsndRet) +{ + AssertObj(this); + if (!m_prgihsndBuckets) + return false; + H hasher; + Eq equal; + int nHash = hasher(&key, isizeof(K)); + int ie = (unsigned)nHash % m_cBuckets; + int ihsnd; + for (ihsnd = m_prgihsndBuckets[ie]; ihsnd != -1; ihsnd = m_prghsnd[ihsnd].GetNext()) + { + if ((nHash == m_prghsnd[ihsnd].GetHash()) && + equal(&key, &m_prghsnd[ihsnd].GetKey(), isizeof(K))) + { + if (pihsndRet) + *pihsndRet = ihsnd; + return true; + } + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + If the given internal HashMap index is valid, return true, and if the provided pointer to a + key object is not NULL, also copy the indexed key to the indicated memory location. + If the given internal index is NOT valid, return false, and ignore the provided key + object pointer. + + @param ihsnd Internal index value returned earlier by GetIndex or Insert. + @param pkeyRet Pointer to an empty key object for storing a copy of the key found at the + indexed location. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + bool HashMap<K,T,H,Eq>::IndexKey(int ihsnd, K * pkeyRet) +{ + AssertObj(this); + if ((ihsnd < 0) || (ihsnd >= m_ihsndLim)) + return false; + if (m_prghsnd[ihsnd].InUse()) + { + if (pkeyRet) + *pkeyRet = m_prghsnd[ihsnd].GetKey(); + return true; + } + else + { + return false; + } +} + +/*---------------------------------------------------------------------------------------------- + If the given internal HashMap index is valid, return true, and if the provided pointer to an + object is not NULL, also copy the indexed value to the indicated memory location. + If the given internal index is NOT valid, return false, and ignore the provided + object pointer. + + @param ihsnd Internal index value returned earlier by GetIndex or Insert. + @param pvalueRet Pointer to an empty object for storing a copy of the value found at the + indexed location. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + bool HashMap<K,T,H,Eq>::IndexValue(int ihsnd, T * pvalueRet) +{ + AssertObj(this); + if (ihsnd < 0 || ihsnd >= m_ihsndLim) + return false; + if (m_prghsnd[ihsnd].InUse()) + { + if (pvalueRet) + *pvalueRet = m_prghsnd[ihsnd].GetValue(); + return true; + } + else + { + return false; + } +} + +/*---------------------------------------------------------------------------------------------- + Return the number of items (key-value pairs) stored in the HashMap. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + int HashMap<K,T,H,Eq>::Size() +{ + AssertObj(this); + if (!m_prgihsndBuckets) + return 0; + int chsndFree = 0; + int ihsnd; + for (ihsnd = m_ihsndFirstFree; + ihsnd != FreeListIdx(-1); + ihsnd = m_prghsnd[FreeListIdx(ihsnd)].GetNext()) + { + ++chsndFree; + } + return m_ihsndLim - chsndFree; +} + +//:Ignore +#ifdef DEBUG +/*---------------------------------------------------------------------------------------------- + Return the number of buckets (hash slots) currently allocated for the hash map. This is + useful only for debugging the hash map mechanism itself. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + int HashMap<K,T,H,Eq>::_BucketCount() +{ + AssertObj(this); + return m_cBuckets; +} + +/*---------------------------------------------------------------------------------------------- + Return the number of buckets (hash slots) that do not point to a list of HashNode objects. + This is useful only for debugging the hash map mechanism itself. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + int HashMap<K,T,H,Eq>::_EmptyBuckets() +{ + AssertObj(this); + int ceUnused = 0; + int ie; + for (ie = 0; ie < m_cBuckets; ++ie) + { + if (m_prgihsndBuckets[ie] == -1) + ++ceUnused; + } + return ceUnused; +} + +/*---------------------------------------------------------------------------------------------- + Return the number of buckets (hash slots) that currently point to a list of HashNode + objects in the hash map. This is useful only for debugging the hash map mechanism itself. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + int HashMap<K,T,H,Eq>::_BucketsUsed() +{ + AssertObj(this); + int ceUsed = 0; + int ie; + for (ie = 0; ie < m_cBuckets; ++ie) + { + if (m_prgihsndBuckets[ie] != -1) + ++ceUsed; + } + return ceUsed; +} + +/*---------------------------------------------------------------------------------------------- + Return the length of the longest list of HashNode objects stored in any bucket (hash slot) + of the hash map. This is useful only for debugging the hash map mechanism itself. +----------------------------------------------------------------------------------------------*/ +template<class K, class T, class H, class Eq> + int HashMap<K,T,H,Eq>::_FullestBucket() +{ + AssertObj(this); + int chsndMax = 0; + int chsnd; + int ie; + int ihsnd; + for (ie = 0; ie < m_cBuckets; ++ie) + { + chsnd = 0; + for (ihsnd = m_prgihsndBuckets[ie]; ihsnd != -1; ihsnd = m_prghsnd[ihsnd].GetNext()) + ++chsnd; + if (chsndMax < chsnd) + chsndMax = chsnd; + } + return chsndMax; +} +#endif +//:End Ignore + + + +// Local Variables: +// mode:C++ +// c-file-style:"cellar" +// tab-width:4 +// End: + +#endif /*HASHMAP_I_C_INCLUDED*/ diff --git a/Build/source/libs/graphite/engine-2.3.1/src/generic/Makefile.am b/Build/source/libs/graphite/engine-2.3.1/src/generic/Makefile.am new file mode 100644 index 00000000000..ec60b09be3a --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/generic/Makefile.am @@ -0,0 +1,8 @@ +genericdir = $(top_srcdir)/src/generic + +libgraphite_la_SOURCES += $(genericdir)/Debug.cpp +libgraphite_la_SOURCES += $(genericdir)/Util.cpp + +EXTRA_DIST += $(genericdir)/*.h +EXTRA_DIST += $(genericdir)/BinTree_i.cpp +EXTRA_DIST += $(genericdir)/HashMap_i.cpp diff --git a/Build/source/libs/graphite/engine-2.3.1/src/generic/Util.cpp b/Build/source/libs/graphite/engine-2.3.1/src/generic/Util.cpp new file mode 100644 index 00000000000..aabd6563d3e --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/generic/Util.cpp @@ -0,0 +1,261 @@ + +#include "GrCommon.h" +#include "GrDebug.h" + + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + Swap two blocks of size cb starting at pv1 and pv2. Doesn't handle overlapping blocks. +----------------------------------------------------------------------------------------------*/ +void SwapBytes(void * pv1, void * pv2, int cb) +{ + Assert((byte *)pv1 + cb <= (byte *)pv2 || (byte *)pv2 + cb <= (byte *)pv1); + AssertPtrSize(pv1, cb); + AssertPtrSize(pv2, cb); + +#ifdef NO_ASM + + byte *pb1 = (byte *)pv1; + byte *pb2 = (byte *)pv2; + byte b; + + while (--cb >= 0) + { + b = *pb1; + *pb1++ = *pb2; + *pb2++ = b; + } + +#else // !NO_ASM + + __asm + { + // edi -> memory to swap, first pointer + // esi -> memory to swap, second pointer + + mov edi,pv1 + mov esi,pv2 + + mov ecx,cb + shr ecx,2 + jz LBytes + +LIntLoop: + mov eax,[edi] + mov ebx,[esi] + mov [edi],ebx + mov [esi],eax + + add edi,4 + add esi,4 + dec ecx + jnz LIntLoop; + +LBytes: + mov ecx,cb + and ecx,3 + jz LDone + +LByteLoop: + mov al,[edi] + mov bl,[esi] + mov [edi],bl + mov [esi],al + inc edi + inc esi + dec ecx + jnz LByteLoop + +LDone: + } + +#endif //!NO_ASM +} + +/*---------------------------------------------------------------------------------------------- + Fills a block of memory with the given short value. +----------------------------------------------------------------------------------------------*/ +void FillShorts(void * pv, short sn, int csn) +{ + AssertPtrSize(pv, csn * isizeof(short)); + +#ifdef NO_ASM + + short * psn = (short *)pv; + short * psnLim = psn + csn; + + while (psn < psnLim) + *psn++ = sn; + +#else // !NO_ASM + + __asm + { + // Setup the registers for using REP STOS instruction to set memory. + // NOTE: Alignment does not effect the speed of STOS. + // + // edi -> memory to set + // eax = value to store in destination + // direction flag is clear for auto-increment + + mov edi,pv + mov ax,sn + mov ecx,csn + + mov edx,ecx + and edx,1 + jz LInts + + // set 1 short + stosw + +LInts: + shr ecx,1 + jz LDone + + mov ebx,eax + shl eax,16 + mov ax,bx + + rep stosd +LDone: + } + +#endif //!NO_ASM +} + +/*********************************************************************************************** + Integer utilities. +***********************************************************************************************/ + +/* + * table of powers of 2, and largest prime smaller than each power of 2 + * n 2**n prime diff + * --- ---------- ---------- ---- + * 2: 4 3 ( -1) + * 3: 8 7 ( -1) + * 4: 16 13 ( -3) + * 5: 32 31 ( -1) + * 6: 64 61 ( -3) + * 7: 128 127 ( -1) + * 8: 256 251 ( -5) + * 9: 512 509 ( -3) + * 10: 1024 1021 ( -3) + * 11: 2048 2039 ( -9) + * 12: 4096 4093 ( -3) + * 13: 8192 8191 ( -1) + * 14: 16384 16381 ( -3) + * 15: 32768 32749 (-19) + * 16: 65536 65521 (-15) + * 17: 131072 131071 ( -1) + * 18: 262144 262139 ( -5) + * 19: 524288 524287 ( -1) + * 20: 1048576 1048573 ( -3) + * 21: 2097152 2097143 ( -9) + * 22: 4194304 4194301 ( -3) + * 23: 8388608 8388593 (-15) + * 24: 16777216 16777213 ( -3) + * 25: 33554432 33554393 (-39) + * 26: 67108864 67108859 ( -5) + * 27: 134217728 134217689 (-39) + * 28: 268435456 268435399 (-57) + * 29: 536870912 536870909 ( -3) + * 30: 1073741824 1073741789 (-35) + * 31: 2147483648 2147483647 ( -1) + * 32: 4294967296 4294967291 ( -5) + */ +const static unsigned int g_rguPrimes[] = { + 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, + 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, + 134217689, 268435399, 536870909, 1073741789, 2147483647, 4294967291U +}; + + +/*---------------------------------------------------------------------------------------------- + Returns the prime in g_rguPrimes that is closest to u. +----------------------------------------------------------------------------------------------*/ +unsigned int GetPrimeNear(unsigned int u) +{ + int cu = isizeof(g_rguPrimes) / isizeof(unsigned int); + int iuMin; + int iuLim; + int iu; + + for (iuMin = 0, iuLim = cu; iuMin < iuLim; ) + { + iu = (iuMin + iuLim) / 2; + if (u > g_rguPrimes[iu]) + iuMin = iu + 1; + else + iuLim = iu; + } + Assert(iuMin == cu || iuMin < cu && u <= g_rguPrimes[iuMin]); + Assert(iuMin == 0 || iuMin > 0 && u > g_rguPrimes[iuMin - 1]); + + if (!iuMin) + return g_rguPrimes[0]; + if (iuMin == cu) + return g_rguPrimes[cu - 1]; + if (g_rguPrimes[iuMin] - u < u - g_rguPrimes[iuMin - 1]) + return g_rguPrimes[iuMin]; + return g_rguPrimes[iuMin - 1]; +} + + +/*---------------------------------------------------------------------------------------------- + Returns the prime in g_rguPrimes that is larger than u or is the largest in the list. +----------------------------------------------------------------------------------------------*/ +unsigned int GetLargerPrime(unsigned int u) +{ + int cu = isizeof(g_rguPrimes) / isizeof(unsigned int); + int iuMin; + int iuLim; + int iu; + + for (iuMin = 0, iuLim = cu; iuMin < iuLim; ) + { + iu = (iuMin + iuLim) / 2; + if (u >= g_rguPrimes[iu]) + iuMin = iu + 1; + else + iuLim = iu; + } + Assert(iuMin == cu || iuMin < cu && u < g_rguPrimes[iuMin]); + Assert(iuMin == 0 || iuMin > 0 && u >= g_rguPrimes[iuMin - 1]); + + if (iuMin == cu) + return g_rguPrimes[cu - 1]; + return g_rguPrimes[iuMin]; +} + + +/*---------------------------------------------------------------------------------------------- + Returns the prime in g_rguPrimes that is smaller than u or is the smallest in the list. +----------------------------------------------------------------------------------------------*/ +unsigned int GetSmallerPrime(unsigned int u) +{ + int cu = isizeof(g_rguPrimes) / isizeof(unsigned int); + int iuMin; + int iuLim; + int iu; + + for (iuMin = 0, iuLim = cu; iuMin < iuLim; ) + { + iu = (iuMin + iuLim) / 2; + if (u > g_rguPrimes[iu]) + iuMin = iu + 1; + else + iuLim = iu; + } + Assert(iuMin == cu || iuMin < cu && u <= g_rguPrimes[iuMin]); + Assert(iuMin == 0 || iuMin > 0 && u > g_rguPrimes[iuMin - 1]); + + if (!iuMin) + return g_rguPrimes[0]; + return g_rguPrimes[iuMin - 1]; +} + +} + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/painter/Makefile.am b/Build/source/libs/graphite/engine-2.3.1/src/painter/Makefile.am new file mode 100644 index 00000000000..497d31bd018 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/painter/Makefile.am @@ -0,0 +1,5 @@ +painterdir = $(top_srcdir)/src/painter + +libgraphite_la_SOURCES += $(painterdir)/SegmentPainter.cpp + +#EXTRA_DIST += $(painterdir)/*.h diff --git a/Build/source/libs/graphite/engine-2.3.1/src/painter/SegmentPainter.cpp b/Build/source/libs/graphite/engine-2.3.1/src/painter/SegmentPainter.cpp new file mode 100644 index 00000000000..149366eb4b3 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/painter/SegmentPainter.cpp @@ -0,0 +1,3004 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: SegmentPainter.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the object that handles drawing, mouse clicks, and other UI-related behavior + for a Graphite segment. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) +#include <math.h> +#ifndef _WIN32 +#include <stdlib.h> +#endif + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** +namespace +{ + +static bool g_fDrawing; + +} // namespace + + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Constructors. +----------------------------------------------------------------------------------------------*/ +//SegmentPainter::SegmentPainter(Segment * pseg) +//{ +// m_pseg = pseg; +// +// m_xsOrigin = 0; +// m_ysOrigin = 0; +// m_xdPosition = 0; +// m_ydPosition = 0; +// m_xFactor = 1.0; +// m_yFactor = 1.0; +//} + +SegmentPainter::SegmentPainter(Segment * pseg, float xsOrigin, float ysOrigin) +{ + m_pseg = pseg; + + m_xsOrigin = xsOrigin; + m_ysOrigin = ysOrigin; + m_xdPosition = m_xsOrigin; + m_ydPosition = m_ysOrigin; + m_xFactor = 1.0; + m_yFactor = 1.0; + + //m_xsOriginInt = GrEngine::RoundFloat(xsOrigin); + //m_xdPositionInt = m_xsOriginInt; + //m_xFactor64 = 64; +} + +/*---------------------------------------------------------------------------------------------- + Destructor. +----------------------------------------------------------------------------------------------*/ +SegmentPainter::~SegmentPainter() +{ +} + +/*---------------------------------------------------------------------------------------------- + Initialization. + + The correspondence with the old rectangle approach is: + m_xsOrigin = sourceRect.left; + m_ysOrigin = sourceRect.top; + m_xdPosition = destRect.left; + m_ydPosition = destRect.top; + + m_xFactor = (destRect.right - destRect.left) / (sourceRect.right - sourceRect.left); + m_yFactor = (destRect.top - destRect.bottom) / (sourceRect.top - sourceRect.bottom); + +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::setOrigin(float xsOrigin, float ysOrigin) +{ + m_xsOrigin = xsOrigin; + m_ysOrigin = ysOrigin; + + //m_xsOriginInt = GrEngine::RoundFloat(xsOrigin); +} + +void SegmentPainter::setPosition(float xdPosition, float ydPosition) +{ + m_xdPosition = xdPosition; + m_ydPosition = ydPosition; + + //m_xdPositionInt = GrEngine::RoundFloat(xdPosition); +} + +void SegmentPainter::setScalingFactors(float xFactor, float yFactor) +{ + m_xFactor = xFactor; + m_yFactor = yFactor; + + //int temp = GrEngine::RoundFloat(xFactor); + //m_xFactor64 = GrEngine::RoundFloat(xFactor * 64); +} + + +//:>******************************************************************************************** +//:> SegmentPainter interface methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Draw the text of the segment at the specified point in the graphics context. + This method must be implemented by a concrete subclass for painters that really need + to paint. +----------------------------------------------------------------------------------------------*/ +//void SegmentPainter::paint() +//{ +// Assert(false); +//} + +/*---------------------------------------------------------------------------------------------- + Indicate if the character position is a valid place for an insertion point. + + @oaram ich - position to test + @param pipvr - answer: kipvrOK = IP here is valid; + kipvrBad = IP here no good; + kipvrUnknown = this seg can't decide, ask the next +----------------------------------------------------------------------------------------------*/ +LgIpValidResult SegmentPainter::isValidInsertionPoint(int ichw) +{ + Assert(m_pseg->m_dxsWidth >= 0); + Assert(m_pseg->m_dysAscent >= 0); + + int ichwSegOffset = ichw - m_pseg->m_ichwMin; + if (ichwSegOffset < m_pseg->m_ichwAssocsMin || ichwSegOffset >= m_pseg->m_ichwAssocsLim) + { + // If we're pointing at a hard line break character (Unicode 2028), then we know + // that it's a good insertion point in spite of missing the other conditions. + utf16 ch; + m_pseg->m_pgts->fetch(ichw, 1, &ch); + if (ch != knLineSep) + return kipvrUnknown; // Definitely in a different segment of the paragraph. + return kipvrOK; + } + + int isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin]; + if (isloutLig != kNegInfinity) + { + // Ligature. + int isloutLigAfter = m_pseg->m_prgisloutLigature[ichwSegOffset - 1 - m_pseg->m_ichwAssocsMin]; + if (isloutLig == isloutLigAfter) + { + int icompBefore = m_pseg->m_prgiComponent[ichwSegOffset - m_pseg->m_ichwAssocsMin]; + int icompAfter = m_pseg->m_prgiComponent[ichwSegOffset - 1 - m_pseg->m_ichwAssocsMin]; + if (icompBefore == icompAfter) + // If inserting before this character yields the same visible component + // as inserting after the previous, then this character is invisible, or has + // been merged with the previous, and it doesn't make sense to put an insertion + // point here. + return kipvrBad; + } + return kipvrOK; + } + + // TODO: do we need to test ALL the associated glyphs? Here we just test the first. + + LgIpValidResult ipvrRet; + + int isloutBefore = m_pseg->UnderlyingToLogicalSurface(ichw, true); // leading edge + int isloutAfter = m_pseg->UnderlyingToLogicalSurface(ichw - 1, false); // trailing edge of previous + int isloutTest = m_pseg->UnderlyingToLogicalInThisSeg(ichw); + if (isloutBefore == isloutAfter) + // If inserting before this character yields the same visible glyph as + // inserting after the previous, then this character is invisible, or has + // been merged with the previous, and it doesn't make sense to put an insertion + // point here. + ipvrRet = kipvrBad; + else if (isloutTest == kNegInfinity || isloutTest == kPosInfinity) + ipvrRet = kipvrBad; // can't figure it out + else if (!m_pseg->OutputSlot(isloutTest)->InsertBefore()) + ipvrRet = kipvrBad; + else if (m_pseg->SameSurfaceGlyphs(ichw - 1, ichw)) + // The insertion point would be between two characters that map to the same + // set of glyphs. Not a good place. + ipvrRet = kipvrBad; + else + ipvrRet = kipvrOK; + + return ipvrRet; +} + +/*---------------------------------------------------------------------------------------------- + Answer whether the logical and physical boundaries of the string coincide. + This method is called by the application to handle writing system boundaries, and by + other segments in a writing-system chain to handle line-breaks. + + @param fBoundaryEnd - asking about the logical end boundary? + @param fBoundaryRight - asking about the physical right boundary? + @param pfResult - return value +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::doBoundariesCoincide(bool fBoundaryEnd, bool fBoundaryRight) +{ + float xsBefore, ysBeforeTop, ysBeforeBottom; + float xsAfter, ysAfterTop, ysAfterBottom; + bool fRtlBefore, fRtlAfter; + + if (m_pseg->m_cginf == 0) + { + // Empty segment. + return true; + } + + //SetUpGraphics(ichwBase, pgg, true); + + Assert(m_pseg->m_dxsWidth >= 0); + Assert(m_pseg->m_dysAscent >= 0); + + if (fBoundaryEnd) + { + CalcIP(m_pseg->m_ichwMin + m_pseg->m_dichwLim, true, + &xsBefore, &ysBeforeTop, &ysBeforeBottom, &fRtlBefore); + CalcIP(m_pseg->m_ichwMin + m_pseg->m_dichwLim, false, + &xsAfter, &ysAfterTop, &ysAfterBottom, &fRtlAfter); + } + else + { + CalcIP(m_pseg->m_ichwMin, true, + &xsBefore, &ysBeforeTop, &ysBeforeBottom, &fRtlBefore); + CalcIP(m_pseg->m_ichwMin, false, + &xsAfter, &ysAfterTop, &ysAfterBottom, &fRtlAfter); + } + + // We allow for rounding errors which might cause differences between the expected + // and the actual values, hence the test for a difference of <= 1. + bool fRet; + if (fBoundaryRight) + { + // right edge + if (fBoundaryEnd) // logical end + fRet = (!fRtlAfter && fabsf(xsAfter - (m_pseg->m_dxsTotalWidth + m_pseg->m_dxsStretch)) <= 1); + else // logical start + fRet = (fRtlBefore && fabsf(xsBefore - (m_pseg->m_dxsTotalWidth + m_pseg->m_dxsStretch)) <= 1); + } + else + { + // left edge + if (fBoundaryEnd) // logical end + fRet = (fRtlAfter && fabsf(xsAfter) <= 1); + else // logical start + fRet = (!fRtlBefore && fabsf(xsBefore) <= 1); + } + + return fRet; +} + +/*---------------------------------------------------------------------------------------------- + Draw an insertion point at an appropriate position. + + @param twx, twy - same origin used to Draw segment + @param ich - character position; must be valid + @param fAssocPrev - associated with previous character? + @param bOn - turning on or off? Caller should alternate, first turning on + (ignored in this implementation) + @param dm - draw mode: + kdmNormal = draw complete insertion pt (I-beam or split cursor); + kdmPrimary = only draw primary half of split cursor; + kdmSecondary = only draw secondary half of split cursor +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::drawInsertionPoint(int ichwIP, bool fAssocPrev, + bool bOn, bool fForceSplit) // LgIPDrawMode dm) +{ + if (g_fDrawing) + return; + + g_fDrawing = true; + + //SetUpGraphics(ichwBase, pgg, true); + + Assert(m_pseg->m_dxsWidth >= 0); + Assert(m_pseg->m_dysAscent >= 0); + + CalcOrDrawInsertionPoint(ichwIP, + fAssocPrev, bOn, fForceSplit, + NULL, NULL); // passing NULLs here means we want to draw + + //RestoreFont(pgg); + + g_fDrawing = false; +} + +/*---------------------------------------------------------------------------------------------- + Fill in bounding rectangles for the (possibly 2-part) IP, in destination device coordinates. + Return flags indicating which of the 2 parts where rendered here. + + @param rs,rd - source/destination coordinates, for scaling + @param ichwIP - insertion point + @param fAssocPrev - is the IP "leaning" backward? + @param dm - draw mode: + kdmNormal = draw complete insertion pt (I-beam or split cursor); + kdmPrimary = only draw primary half of split cursor; + kdmSecondary = only draw secondary half of split cursor + @param prdPrimary - return location of primary selection, in dest coords + @param prdSecondary - return location of secondary selection, in dest coords +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::positionsOfIP( + int ichwIP, bool fAssocPrev, bool fForceSplit, + Rect * prdPrimary, Rect * prdSecondary) +{ + ChkGrArgPtr(prdPrimary); + ChkGrArgPtr(prdSecondary); + + //SetUpGraphics(ichwBase, pgg, true); + + Assert(m_pseg->m_dxsWidth >= 0); + Assert(m_pseg->m_dysAscent >= 0); + + CalcOrDrawInsertionPoint(ichwIP, fAssocPrev, true, fForceSplit, + prdPrimary, prdSecondary); + + //RestoreFont(pgg); +} + +/*---------------------------------------------------------------------------------------------- + Highlight a range of text. + + @param ichwAnchor/End - selected range + @param ydLineTop/Bottom - top/bottom of area to highlight if whole line height; + includes half of inter-line spacing. + @param bOn - true if we are turning on (ignored in this implementation) +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::drawSelectionRange(int ichwAnchor, int ichwEnd, + float ydLineTop, float ydLineBottom, bool bOn) +{ + if (g_fDrawing) + return true; + + g_fDrawing = true; + + //SetUpGraphics(ichwBase, pgg, true); + + Assert(m_pseg->m_dxsWidth >= 0); + Assert(m_pseg->m_dysAscent >= 0); + + float xsSegLeft = 0; + float ysSegTop = 0; + + float xdSegRight = ScaleXToDest(m_pseg->m_dxsTotalWidth); + + int ichwMinSel = min(ichwAnchor, ichwEnd); + int ichwLimSel = max(ichwAnchor, ichwEnd); + + int ichwMinSeg = max(ichwMinSel, m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin); + int ichwLimSeg = min(ichwLimSel, m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim); + + std::vector<Rect> vrs; + std::vector<bool> vfEntireHt; + + bool * prgfAllCompsSelected = new bool[m_pseg->m_dichwLim]; + std::fill_n(prgfAllCompsSelected, m_pseg->m_dichwLim, false); + CalcPartialLigatures(prgfAllCompsSelected, ichwMinSeg, ichwLimSeg, + ichwMinSel, ichwLimSel); + + // Array to keep track of which glyphs were highlighted. + int cginf = m_pseg->m_cginf; + bool * prgfHighlighted = new bool[cginf]; + std::fill_n(prgfHighlighted, cginf, false); + + for (int ichwLp = ichwMinSeg; ichwLp < ichwLimSeg; ichwLp++) + { + CalcHighlightRect(ichwLp, vrs, vfEntireHt, + !prgfAllCompsSelected[ichwLp - m_pseg->m_ichwMin], prgfHighlighted, false); + } + + // If all the glyphs of a given cluster were highlighted, highlight the whole cluster + // as a unit. + for (int islout = 0; islout < m_pseg->m_cslout; islout++) + { + CalcCompleteCluster(islout, vrs, vfEntireHt, prgfHighlighted); + } + + // Convert to destination coordinates; also extend full-line rectangles to the top + // and bottom of the line height. + std::vector<Rect> vrd; + for (size_t irs = 0; irs < vrs.size(); irs++) + { + Rect rsTmp = vrs[irs]; + Rect rdTmp; + if (vfEntireHt[irs]) + { + rdTmp.top = ydLineTop; + rdTmp.bottom = ydLineBottom; + } + else + { + float ydTmp = ScaleYToDest(rsTmp.top + ysSegTop); + rdTmp.top = min(ydLineBottom, max(ydLineTop, ydTmp)); + ydTmp = ScaleYToDest(rsTmp.bottom + ysSegTop); + rdTmp.bottom = max(ydLineTop, min(ydLineBottom, ydTmp)); + } + float xdTmp = ScaleXToDest(rsTmp.left + xsSegLeft); + rdTmp.left = min(xdSegRight, max(float(0), xdTmp)); + xdTmp = ScaleXToDest(rsTmp.right + xsSegLeft); + rdTmp.right = max(float(0), min(xdSegRight, xdTmp)); + if (rdTmp.left > rdTmp.right) + { + // I think this can happen due to rounding errors. + float tmp = rdTmp.left; + rdTmp.left = rdTmp.right; + rdTmp.right = tmp; + } + vrd.push_back(rdTmp); + } + + // Create a list of rectangles with no overlaps, so the selection draws cleanly. + std::vector<Rect> vrdNoOverlaps; + size_t irect; + for (irect = 0; irect < vrd.size(); irect++) + AddRectWithoutOverlaps(vrdNoOverlaps, vrd[irect]); + + AssertNoOverlaps(vrdNoOverlaps); + + // Draw the highlight. + for (irect = 0; irect < vrdNoOverlaps.size(); irect++) + { + Rect rdTmp = vrdNoOverlaps[irect]; + InvertRect(rdTmp.left, rdTmp.top, rdTmp.right, rdTmp.bottom); + } + + delete[] prgfAllCompsSelected; + delete[] prgfHighlighted; + + //RestoreFont(pgg); + + g_fDrawing = false; + + return (vrdNoOverlaps.size() > 0); +} + +/*---------------------------------------------------------------------------------------------- + Get a bounding rectangle that will contain the area highlighted by this segment + when drawing the specified range. + + @param ichwAnchor/End - selected range + @param ydLineTop/Bottom - top/bottom of area to highlight if whole line height; + includes half of inter-line spacing. + @param prdBounds - return location of range + @return - true if the return rectange has a meaningful value +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::positionsOfRange(int ichwAnchor, int ichwEnd, + float ydLineTop, float ydLineBottom, Rect * prdBounds) +{ + ChkGrArgPtr(prdBounds); + + std::vector<Rect> vrs; + std::vector<bool> vfEntireHt; + + //SetUpGraphics(ichwBase, pgg, true); + + Assert(m_pseg->m_dxsWidth >= 0); + Assert(m_pseg->m_dysAscent >= 0); + + int ichwMin = min(ichwAnchor, ichwEnd); + int ichwLim = max(ichwAnchor, ichwEnd); + + for (int ichwLp = ichwMin; ichwLp < ichwLim; ichwLp++) + { + CalcHighlightRect(ichwLp, vrs, vfEntireHt, true, NULL, false); + } + + if (vrs.size() == 0) + { + return false; + } + + Rect * prsBounds = &(vrs[0]); + for (size_t irect = 1; irect < vrs.size(); irect++) + { + Rect * prs = &(vrs[irect]); + prsBounds->left = min(prsBounds->left, prs->left); + prsBounds->right = max(prsBounds->right, prs->right); + } + + prdBounds->top = ydLineTop; + prdBounds->bottom = ydLineBottom; + prdBounds->left = ScaleXToDest(prsBounds->left); + prdBounds->right = ScaleXToDest(prsBounds->right); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Convert a click position to a character position. + + @param zptdClickPosition - relative to the segment draw origin + @param pichw - return character clicked before + @param pfAssocPrev - return true if they clicked on the trailing half of + the previous char, false if click was on the + leading half of the following +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::pointToChar(Point zptdClickPosition, int * pichw, bool * pfAssocPrev) +{ + ChkGrOutPtr(pichw); + ChkGrOutPtr(pfAssocPrev); + + //SetUpGraphics(ichwBase, pgg, true); + + bool fRtl = m_pseg->rightToLeft(); + + if (m_pseg->m_dysAscent < 0 || m_pseg->m_dxsWidth < 0) + m_pseg->ComputeDimensions(); + + if (m_pseg->m_dichwLim == 0 || m_pseg->m_cginf == 0) + { + *pichw = m_pseg->m_ichwMin; + *pfAssocPrev = false; + //RestoreFont(pgg); + return; + } + + float xsClick = ScaleXToSource(zptdClickPosition.x); + float ysClick = ScaleYToSource(zptdClickPosition.y); + ysClick = m_pseg->m_dysAscent - ysClick; // make relative to common base line + + float xsTry = xsClick; + float ysTry = ysClick; + + float xysInc = 1.0; + //res = pgg->LogUnitsFromXPixels(1, &xysInc); + //if (ResultFailed(res)) xysInc = 20; + int cTries = 1; + int nAdjustDir = 0; + + while (true) + { + float xsGlyphOffset; + float xsGlyphWidth; + int iginfLoc = PointToCharAux(xsTry, ysTry, &xsGlyphOffset, &xsGlyphWidth); + + bool fClickedAfter; + int ichwCharClicked; + if (xsTry < m_pseg->m_prgginf[m_pseg->LeftMostGlyph()].bb().left) + { + fClickedAfter = fRtl; + ichwCharClicked = (fClickedAfter) ? + m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim - 1 : + m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin; + } + else if (xsTry > m_pseg->m_prgginf[m_pseg->RightMostGlyph()].bb().right) + { + fClickedAfter = !fRtl; + ichwCharClicked = (fClickedAfter) ? + m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim - 1 : + m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin; + } + else + { + ichwCharClicked = m_pseg->PhysicalSurfaceToUnderlying(iginfLoc, + xsTry - xsGlyphOffset, m_pseg->m_dysAscent - ysClick, + xsGlyphWidth, m_pseg->m_dysHeight, &fClickedAfter); // handles ligature components + } + + if (fClickedAfter) + { + *pichw = ichwCharClicked + 1; + while (!GrCharStream::AtUnicodeCharBoundary(m_pseg->GetString(), *pichw)) + *pichw = *pichw + 1; + *pfAssocPrev = true; + } + else + { + *pichw = ichwCharClicked; + *pfAssocPrev = false; + } + + if (*pichw < m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin) + { + //RestoreFont(pgg); + return; + } + else if (*pichw >= m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim) + { + //RestoreFont(pgg); + return; + } + + //int islout = UnderlyingToLogicalInThisSeg(ichwBase, ichwCharClicked); + int isloutBefore = m_pseg->UnderlyingToLogicalInThisSeg(*pichw); + if (isloutBefore == kNegInfinity || isloutBefore == kPosInfinity) + { } + else + { + Assert(0 <= isloutBefore && isloutBefore < m_pseg->m_cslout); + if (m_pseg->OutputSlot(isloutBefore)->InsertBefore()) + { + // Legal insertion point + //RestoreFont(pgg); + return; + } + } + + // Gradually adjust the click position in various directions and try again + // until we find a legal insertion point. + switch (nAdjustDir) + { + case 0: // Adjust to the left + case 4: + xsTry = xsClick - (xysInc * cTries); + ysTry = ysClick; + nAdjustDir = 0; + break; + case 1: // Adjust to the right + xsTry = xsClick + (xysInc * cTries); + ysTry = ysClick; + break; + case 2: // Adjust up + xsTry = xsClick; + ysTry = ysClick - (xysInc * cTries); + break; + case 3: // Adjust down + xsTry = xsClick; + ysTry = ysClick + (xysInc * cTries); + cTries++; + break; + default: + Assert(false); + nAdjustDir = 0; + cTries++; + } + nAdjustDir++; + + if (cTries > 50) + { // Safety net + *pichw = 0; + *pfAssocPrev = false; + THROW(kresFail); + } + else if (cTries > 20) + xysInc = xysInc * 2; + } + + //RestoreFont(pgg); +} + +/*---------------------------------------------------------------------------------------------- + Convert a click position to a glyph bounding-box position. +----------------------------------------------------------------------------------------------*/ +int SegmentPainter::PointToCharAux(float xsTry, float ysTry, + float * pxsGlyphOffset, float * pxsGlyphWidth) +{ + int iginfLoc = m_pseg->GlyphHit(xsTry, ysTry); + + GlyphInfo * pginf = m_pseg->m_prgginf + iginfLoc; + *pxsGlyphOffset = pginf->bb().left; + *pxsGlyphWidth = pginf->bb().right - *pxsGlyphOffset; + + return iginfLoc; +} + +// Review: may need a way to do this and subsequent methods with one result per method? +/*---------------------------------------------------------------------------------------------- + Returns a value what logical position an arrow key should move the IP to. + + @param ichwIP - initial position + @param pfAssocPrev - is the IP "leaning" backwards? input and output + @param fRight - direction of desired movement (physical or logical?) + @param pfInThisSeg - initially true if ichwIP is in the current segment; + return value is true if there is a legitimate output +----------------------------------------------------------------------------------------------*/ +int SegmentPainter::arrowKeyPosition(int ichwIP, bool * pfAssocPrev, + bool fRight, bool * pfInThisSeg) +//GrResult SegmentPainter::arrowKeyPosition( +// int * pichwIP, bool * pfAssocPrev, +// bool fRight, bool fMovingIn, +// bool * pfResult) +{ + ChkGrArgPtr(pfAssocPrev); + //ChkGrOutPtr(pfInThisSeg); + + bool fMovingIn = !*pfInThisSeg; + int ichwTmp = ichwIP; + bool fResult; + ArrowKeyPositionAux(&ichwTmp, pfAssocPrev, fRight, fMovingIn, false, false, &fResult); + *pfInThisSeg = fResult; + return ichwTmp; + + //return ArrowKeyPositionAux(pichwIP, pfAssocPrev, + // fRight, fMovingIn, false, false, pfResult); +} + +/*---------------------------------------------------------------------------------------------- + Indicate what logical position a shift-arrow-key combination should move the + end of the selection to. + + @param pichw - initial endpoint and also adjusted result + @param fAssocPrevMatch - true if we need a certain orientation for the result; + should be false when *pichw = 0 and we are moving within the + same segment + @param fAssocPrevNeeded - association of the end-point, ie, true if it follows the anchor + @param ichAnchor - -1 if anchor is in a different segment + @param fRight - direction of desired movement + @param fMovingIn - true if we are moving in to this segment; + if so, initial pichw meaningless + @param pfRet - if false try next seg or string +----------------------------------------------------------------------------------------------*/ +int SegmentPainter::extendSelectionPosition(int ichwEnd, bool fAssocPrevMatch, bool fAssocPrevNeeded, + int ichwAnchor, bool fRight, bool * pfInThisSeg) +//GrResult SegmentPainter::extendSelectionPosition( +// int * pichw, bool fAssocPrevMatch, bool fAssocPrevNeeded, int ichwAnchor, +// bool fRight, bool fMovingIn, +// bool* pfRet) +{ + //ChkGrArgPtr(pichw); + //ChkGrOutPtr(pfRet); + + // Make the same adjustment as for an insertion point. + bool fAssocPrevCopy = fAssocPrevMatch; + bool fMovingIn = !*pfInThisSeg; + int ichwOld = ichwEnd; + int ichwNew = ichwEnd; + bool fResult; + ArrowKeyPositionAux(&ichwNew, &fAssocPrevCopy, + fRight, fMovingIn, fAssocPrevMatch, fAssocPrevNeeded, &fResult); + *pfInThisSeg = fResult; + + //int ichwNew = *pichw; + //bool fAssocPrevCopy = fAssocPrevMatch; + //// TODO: adjust fAssocPrevCopy based on assoc-prev value of IP + //*pfRet = false; + //int ichwOld = *pichw; + //GrResult res = ArrowKeyPositionAux(&ichwNew, &fAssocPrevCopy, + // fRight, fMovingIn, + // fAssocPrevMatch, fAssocPrevNeeded, + // pfRet); + +// if (*pfRet && ichwNew == *pichw) +// { +// // The selection wasn't actually moved, just switched the direction it leans. +// // Try one more time. +// res = ArrowKeyPositionAux(&ichwNew, &fAssocPrevCopy, +// fRight, fMovingIn, +// (ichwAnchor != *pichw && fAssocPrevNeeded), fAssocPrevNeeded, +// pfRet); +// } + if (ichwAnchor != -1 && fResult && + ((ichwNew < ichwAnchor && ichwAnchor < ichwOld) || + (ichwOld < ichwAnchor && ichwAnchor < ichwNew))) + { + // Don't switch directions in one operation--force to an insertion point. + ichwNew = ichwAnchor; + } + return ichwNew; + //*pichw = ichwNew; + //ReturnResult(res); +} + +/*---------------------------------------------------------------------------------------------- + Handle arrow key movement for both plain and shifted arrows. + + @param pichw - initial endpoint and also adjusted result + @param pfAssocPrev - association of the end-point, ie, true if it follows the anchor + @param fRight - direction of desired movement + @param fMovingIn - true if we are moving in to this segment; + if so, initial pichw meaningless + @param fAssocPrevMatch - true if the direction of the result should + match the requested orientation, to avoid flipping around and + infinite loops + @param fAssocPrevNeeded - what is needed for the result, if fAssocPrevMatch == true + @param pfRet - if false try next segment or string +----------------------------------------------------------------------------------------------*/ +GrResult SegmentPainter::ArrowKeyPositionAux( + int * pichwIP, bool * pfAssocPrev, + bool fRight, bool fMovingIn, + bool fAssocPrevMatch, bool fAssocPrevNeeded, + bool * pfResult) +{ + ChkGrArgPtr(pfResult); + + //bool fAssocPrevTmp = *pfAssocPrev; + + Assert(m_pseg->m_dxsWidth >= 0); + Assert(m_pseg->m_dysAscent >= 0); + + int nNextOrPrevSeg; + if (!fMovingIn) + { + *pfResult = ArrowKeyPositionInternal(pichwIP, pfAssocPrev, fRight, + fAssocPrevMatch, fAssocPrevNeeded, &nNextOrPrevSeg); + ReturnResult(kresOk); + } + else + { + int ichw; + bool fAssocPrevNew; + if (fRight) + { + // Moving right: make selection at left edge. + int iginf = m_pseg->LeftMostGlyph(); + bool fBefore = !m_pseg->rightToLeft(); + ichw = m_pseg->PhysicalSurfaceToUnderlying(iginf, fBefore) + + ((fBefore) ? 0 : 1); + fAssocPrevNew = !fBefore; // Review: should it be based on the char dir? + } + else + { + // Moving left: make selection at right edge; + int iginf = m_pseg->RightMostGlyph(); + bool fBefore = m_pseg->rightToLeft(); + ichw = m_pseg->PhysicalSurfaceToUnderlying(iginf, fBefore) + + ((fBefore) ? 0 : 1); + fAssocPrevNew = !fBefore; // Review: should it be based on the char dir? + } + + // Okay, now we have a selection at the edge of the segment. Try moving inside by + // one glyph. + *pfResult = ArrowKeyPositionInternal(&ichw, &fAssocPrevNew, fRight, + fAssocPrevMatch, fAssocPrevNeeded, &nNextOrPrevSeg); + if (*pfResult) + { + *pichwIP = ichw; + *pfAssocPrev = fAssocPrevNew; + } + + } + + ReturnResult(kresOk); +} + +/*---------------------------------------------------------------------------------------------- + Used to find out where underlines should be drawn. + + @param ichwMin/Lim - range of text of interest + @param rs, rd - source/destination coordinates, for scaling + @param fSkipSpace - true if white space should not be underlined; some renderers may + ignore this + @param cxdMax - number of ranges allowed + @param prgxdLefts/Rights/UnderTops + - arrays of corresponding values indicating where an underline + should be drawn, in logical order + + TODO: fix the bug where if you have trailing spaces in different segments (eg, + at different sizes), the spaces all but the last segment will be erroneously included + in the underline result. +----------------------------------------------------------------------------------------------*/ +size_t SegmentPainter::getUnderlinePlacement(int ichwMin, int ichwLim, + bool fSkipSpace, size_t cxdMax, + float * prgxdLefts, float * prgxdRights, float * prgydUnderTops) +{ + ChkGrArrayArg(prgxdLefts, cxdMax); + ChkGrArrayArg(prgxdRights, cxdMax); + //ChkGrArrayArg(prgydUnderTops, cxdMax); + + GrResult res = kresOk; + + //SetUpGraphics(ichwBase, pgg, true); + + Assert(m_pseg->m_dxsWidth >= 0); + Assert(m_pseg->m_dysAscent >= 0); + + float xsSegLeft = m_pseg->m_dxsTotalWidth; + for (int iginf = 0; iginf < m_pseg->m_cginf; iginf++) + { + xsSegLeft = min(xsSegLeft, m_pseg->GlyphLeftEdge(iginf)); + } + + float xdSegLeft = this->ScaleXToDest(xsSegLeft); + float xdSegRight = xdSegLeft + m_pseg->m_dxsTotalWidth; + + int ichwMinSel = min(ichwMin, ichwLim); + int ichwLimSel = max(ichwMin, ichwLim); + + int ichwMinSeg = max(ichwMinSel, m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin); + int ichwLimSeg = min(ichwLimSel, m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim); + + std::vector<Rect> vrs; + std::vector<bool> vfEntireHt; + + bool * prgfAllCompsSelected = new bool[m_pseg->m_dichwLim]; + std::fill_n(prgfAllCompsSelected, m_pseg->m_dichwLim, false); + CalcPartialLigatures(prgfAllCompsSelected, ichwMinSeg, ichwLimSeg, + ichwMinSel, ichwLimSel); + + // Array to keep track of which glyphs were highlighted: + int cginf = m_pseg->m_cginf; + bool * prgfHighlighted = new bool[cginf]; + std::fill_n(prgfHighlighted, cginf, false); + + for (int ichwLp = ichwMinSeg; ichwLp < ichwLimSeg; ichwLp++) + { + CalcHighlightRect(ichwLp, vrs, vfEntireHt, + !prgfAllCompsSelected[ichwLp - m_pseg->m_ichwMin], prgfHighlighted, + (m_pseg->m_fEndLine && fSkipSpace)); + } + + // If all the glyphs of a given cluster were highlighted, highlight the whole cluster + // as a unit. + for (int islout = 0; islout < m_pseg->m_cslout; islout++) + { + CalcCompleteCluster(islout, vrs, vfEntireHt, prgfHighlighted); + } + + // Convert rectangles in source coordinates to line segments in destination + // coordinates. + std::vector<LineSeg> vlsd; + for (size_t irs = 0; irs < vrs.size(); irs++) + { + Rect rsTmp = vrs[irs]; + LineSeg lsdTmp; + lsdTmp.left = min(xdSegRight, max(xdSegLeft, ScaleXToDest(rsTmp.left))); + lsdTmp.right = max(xdSegLeft, min(xdSegRight, ScaleXToDest(rsTmp.right))); + vlsd.push_back(lsdTmp); + } + + // Create a list of line segments with no overlaps. + std::vector<LineSeg> vlsdNoOverlaps; + size_t ils; + for (ils = 0; ils < vlsd.size(); ils++) + AddLineSegWithoutOverlaps(vlsdNoOverlaps, vlsd[ils]); + + AssertNoOverlaps(vlsdNoOverlaps); + + // The gap between the baseline and underlining is one pixel, on a normal screen + // at standard magnification, and as near as we can get to the equivalent in any + // special case. + //float dydGap = rdHeight / 96; + //float dydSubscript = max(float(0), (-1 * GrEngine::GrFloatMulDiv(m_pseg->m_dysOffset, rsHeight, rdHeight))); + float dydGap = 1; + float dydSubscript = max(float(0), (-1 * (m_pseg->m_dysOffset * m_yFactor))); + float ydBottom = ScaleYToDest(m_pseg->m_dysAscent) + dydGap + dydSubscript; + + // Put the results in the output buffers. + size_t cxdRet = vlsdNoOverlaps.size(); + for (ils = 0; ils < min(cxdRet, size_t(cxdMax)); ils++) + { + prgxdLefts[ils] = vlsdNoOverlaps[ils].left; + prgxdRights[ils] = vlsdNoOverlaps[ils].right; + if (prgydUnderTops) + prgydUnderTops[ils] = ydBottom; + } + delete[] prgfAllCompsSelected; + delete[] prgfHighlighted; + + //RestoreFont(pgg); + + if (vlsdNoOverlaps.size() > cxdMax) + res = kresFail; + + return cxdRet; +} + + +//:>******************************************************************************************** +//:> Other methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Handle either drawing an insertion point or returning the position at which it + would be drawn. + + @param ichwIP - character position + @param fAssocPrev - true if selection is associated with previous character + @param bOn - turning on or off? (ignored in this implementation) + @param fForceSplit - force a split insertion point + @param prdPrimary - if NULL, do drawing; otherwise, return location of primary IP + in destination device coordinates + @param prdSecondary - return location of secondary IP in dest device coordinates + @param pfPrimaryHere - true if primary selection is rendered in this segment + @param pfSecHere - true if (distinct) secondary selection is rendered in this segment; + false if it is in a different segment, or we are rendering + an I-beam (std selection) +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::CalcOrDrawInsertionPoint( + int ichwIP, bool fAssocPrev, bool bOn, bool fForceSplit, + Rect * prdPrimary, Rect * prdSecondary) +{ + GrResult res = kresOk; + + float xsSegLeft = 0; + float ysSegTop = 0; + + if (prdPrimary) + { + prdPrimary->top = 0; + prdPrimary->bottom = 0; + prdPrimary->left = 0; + prdPrimary->right = 0; + } + if (prdSecondary) + { + prdSecondary->top = 0; + prdSecondary->bottom = 0; + prdSecondary->left = 0; + prdSecondary->right = 0; + } + + int ichwLim = m_pseg->m_ichwMin + m_pseg->m_dichwLim; + bool fRtl = m_pseg->rightToLeft(); + + if (m_pseg->m_dxsWidth < 0) + m_pseg->ComputeDimensions(); + + // Get the locations for the before and after selections. + + float xsBefore, ysBeforeTop, ysBeforeBottom; + float xsAfter, ysAfterTop, ysAfterBottom; + bool fRtlBefore, fRtlAfter; + CalcIP(ichwIP, true, &xsBefore, &ysBeforeTop, &ysBeforeBottom, &fRtlBefore); + CalcIP(ichwIP, false, &xsAfter, &ysAfterTop, &ysAfterBottom, &fRtlAfter); + + if (m_pseg->m_cginf == 0 && ichwIP == ichwLim) + { + Assert(kPosInfFloat == xsBefore && kPosInfFloat == xsAfter); + xsBefore = 0; + xsAfter = 0; + } + else if ((kPosInfFloat == xsBefore || kNegInfFloat == xsBefore) && + (kPosInfFloat == xsAfter || kNegInfFloat == xsAfter)) + { + return; // quick exit for nothing-to-draw case + } + + bool fPrimary = true; + if (fForceSplit) + { + // Supposedly we can't find both halves of the IP in this segment. + Assert(xsBefore == kPosInfFloat || xsBefore == kNegInfFloat + || xsAfter == kPosInfFloat || xsAfter == kNegInfFloat); + if (xsBefore == kPosInfFloat || xsBefore == kNegInfFloat) + { + // Can't find the char the IP is before. + fPrimary = fAssocPrev; + } + else // if (xsAfter == kPosInfinity || xsAfter == kNegInfinity) + { + // Can't find the char the IP is after. + fPrimary = !fAssocPrev; + } + ////Assert((dm == kdmSplitPrimary) == fPrimary); + } + + if (xsBefore == kPosInfinity || xsBefore == kNegInfinity) + { + ysBeforeTop = ysSegTop; + ysBeforeBottom = ysSegTop + m_pseg->m_dysHeight; + } + else + { + xsBefore += xsSegLeft; + ysBeforeTop += ysSegTop; + ysBeforeBottom += ysSegTop; + } + if (xsAfter == kNegInfinity || xsAfter == kPosInfinity) + { + ysAfterTop = ysSegTop; + ysAfterBottom = ysSegTop + m_pseg->m_dysHeight; + } + else + { + xsAfter += xsSegLeft; + ysAfterTop += ysSegTop; + ysAfterBottom += ysSegTop; + } + + float xsLeft = xsSegLeft + + ((fRtl && m_pseg->m_fEndLine) ? (m_pseg->m_dxsVisibleWidth - m_pseg->m_dxsTotalWidth) :0); + float xsRight = xsSegLeft + + ((fRtl && m_pseg->m_fEndLine) ? m_pseg->m_dxsVisibleWidth : m_pseg->m_dxsTotalWidth) + + m_pseg->m_dxsStretch; + float dysMinSplitHeight = m_pseg->m_dysHeight / 4; + + if (!fForceSplit) // kdmNormal == dm) + { + // Standard I-beam, or both halves of split IP. + + bool fIBeam = false; + + if (xsBefore == xsAfter) + // Positions coincide within this segment: make an I-beam. + fIBeam = true; + + else if (fabsf(xsBefore - xsLeft) <= 1 && ichwIP == m_pseg->m_ichwMin) + { + // Logical start is at a left edge: allow I-beam if previous segment has one + // at the end (or there is no previous segment). + //Assert(!fRtlBefore); + //if (m_pseg->m_psegPrev) + //{ + // SegmentPainter segpPrev(m_pseg->m_psegPrev); + // fIBeam = segpPrev.doBoundariesCoincide(true, true); + //} + //else + + fIBeam = true; + } + else if (fabsf(xsBefore - xsRight) <= 1 && ichwIP == m_pseg->m_ichwMin) + { + // Logical start is at a right edge: allow I-beam if previous segment has one + // at the end (or there is no previous segment). + //Assert(fRtlBefore); + //if (m_pseg->m_psegPrev) + //{ + // SegmentPainter segpPrev(m_pseg->m_psegPrev); + // fIBeam = segpPrev.doBoundariesCoincide(true, false); + //} + //else + + fIBeam = true; + } + else if (fabsf(xsAfter - xsLeft) <= 1 && ichwIP == ichwLim) + { + // Logical end is at a left edge: allow I-beam if next segment has one + // at the beginning (or there is no next segment). + //Assert(fRtlAfter); + //if (m_pseg->m_psegNext == NULL) + //{ + // SegmentPainter segpPrev(m_pseg->m_psegPrev); + // fIBeam = segpPrev.doBoundariesCoincide(false, true); + //} + //else + + fIBeam = true; + } + else if (fabsf(xsAfter - xsRight) <= 1 && ichwIP == ichwLim) + { + // Logical end is at a right edge: allow I-beam if next segment has one + // at the beginning (or there is no next segment). + //Assert(!fRtlAfter); + //if (m_pseg->m_psegNext == NULL) + //{ + // SegmentPainter segpPrev(m_pseg->m_psegPrev); + // fIBeam = segpPrev.doBoundariesCoincide(false, false); + //} + //else + + fIBeam = true; + } + else + { + // If the horizontal positions of the two halves are sufficiently close, + // make an I-beam. + fIBeam = CloseIPPositions( + &xsBefore, ysBeforeTop, ysBeforeBottom, + &xsAfter, ysAfterTop, ysAfterBottom); + } + if (ResultFailed(res)) + THROW(WARN(res)); + + if (fIBeam && + ((ichwIP == m_pseg->m_ichwMin && fAssocPrev) || (ichwIP == ichwLim && !fAssocPrev))) + { + // Adjacent segment will show the I-beam, so don't show it here. + if (m_pseg->m_cginf == 0) + { + // Empty segment--assume no adjacent segment. + } + else + return; + } + + if (fIBeam) + { // I-beam caret + + float ysTop = min(ysBeforeTop, ysAfterTop); + float ysBottom = max(ysBeforeBottom, ysAfterBottom); + float xsMid = (xsBefore == kPosInfFloat) ? xsAfter : xsBefore; + InvertIBeam(xsMid, ysTop, ysBottom, fAssocPrev, prdPrimary); + } + else + { // split caret + if (fAssocPrev) + { // xsAfter is primary, xsBefore is secondary + if (xsAfter != kNegInfinity) + { + InvertSplitIP(xsAfter, ysAfterTop, ysAfterBottom, + true, fRtlAfter, false, dysMinSplitHeight, prdPrimary); + } + if (xsBefore != kPosInfFloat) + { + InvertSplitIP(xsBefore, ysBeforeTop, ysBeforeBottom, + false, !fRtlBefore, true, dysMinSplitHeight, prdSecondary); + } + } + else + { // xsBefore is primary, xsAfter is secondary + if (xsBefore != kPosInfinity) + { + InvertSplitIP(xsBefore, ysBeforeTop, ysBeforeBottom, + false, !fRtlBefore, false, dysMinSplitHeight, prdPrimary); + } + if (xsAfter != kNegInfinity) + { + InvertSplitIP(xsAfter, ysAfterTop, ysAfterBottom, + true, fRtlAfter, true, dysMinSplitHeight, prdSecondary); + } + } + //*pfSecHere = true; + } + //*pfPrimaryHere = true; + } + else if (fForceSplit && fPrimary) // kdmSplitPrimary == dm) + { + // Draw just the primary caret. + if (fAssocPrev) + { // xsAfter is primary + if (xsAfter == kNegInfinity) + {} // no primary + else + InvertSplitIP(xsAfter, ysAfterTop, ysAfterBottom, + true, fRtlAfter, false, dysMinSplitHeight, prdPrimary); + } + else + { // xsBefore is primary + if (xsBefore == kPosInfinity) + {} // no primary + else + InvertSplitIP(xsBefore, ysBeforeTop, ysBeforeBottom, + false, !fRtlBefore, false, dysMinSplitHeight, prdPrimary); + } + } + else // fForceSplit && !fPrimary + { + // Draw just the secondary caret. + //Assert(kdmSplitSecondary == dm); + if (fAssocPrev) + { // xsBefore is secondary + if (xsBefore == kPosInfinity) + {} // no secondary + else + InvertSplitIP(xsBefore, ysBeforeTop, ysBeforeBottom, + false, !fRtlBefore, true, dysMinSplitHeight, prdSecondary); + } + else + { // xsAfter is secondary + if (xsAfter == kNegInfinity) + {} // no secondary + else + InvertSplitIP(xsAfter, ysAfterTop, ysAfterBottom, + true, fRtlAfter, true, dysMinSplitHeight, prdSecondary); + } + } +} + +/*---------------------------------------------------------------------------------------------- + Draw an I-beam cursor, or return the location at which it would be drawn. + + Note that currently we do not draw a true I-beam, but rather just a vertical line. + + @param xs - horizontal center of IP + @param ysTop - top of glyph (or ligature component) box + @param ysBottom - bottom of glyph (or ligature component) box + @param fAssocRight - true if the IP is associated with the text to the right + @param rs, rd - source/destination device coordinates + @param prdRet - return location to be drawn in dest device coords; + if NULL, do the drawing +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::InvertIBeam(float xs, float ysTop, float ysBottom, + bool fAssocPrev, Rect * prdRet) +{ + float xd = ScaleXToDest(xs); + float ydTop = ScaleYToDest(ysTop); + float ydBottom = ScaleYToDest(ysBottom); + + // For now, assume that destination coordinates are in terms of pixels. + float dxdTwoXPixels = 2; + + float dxdLeftAdjust = dxdTwoXPixels / 2; + float dxdRightAdjust = dxdTwoXPixels - dxdLeftAdjust; + +// int dysFontAscent; +// pgg->GetFontAscentSourceUnits(pgg, &dysFontAscent); +// dysFontAscent += m_dysXAscent; +// if (dysFontAscent < 20) +// { +// dxdRightAdjust--; +// dxdRightHook = 1; +// } + + if (prdRet) + { + prdRet->left = xd - dxdLeftAdjust; + prdRet->right = xd + dxdRightAdjust; + prdRet->top = ydTop; + prdRet->bottom = ydBottom; + } + else + { + InvertRect(xd-dxdLeftAdjust, ydTop, + xd+dxdRightAdjust, ydBottom); + + // To make a true I-beam: +// InvertRect(xd-dxdTwoXPixels, ydTop-dydOneYPixel, +// xd+dxdRightHook, ydTop); +// InvertRect(xd-dxdTwoXPixels, ydBottom, +// xd+dxdRightHook, ydBottom+dydOneYPixel); + } +} + +/*---------------------------------------------------------------------------------------------- + Draw a split insertion point, or return the location at which it would be drawn. + + @param xs - horizontal center of IP + @param ysTop - top of glyph (or ligature component) box + @param ysBottom - bottom of glyph (or ligature component) box + @param fTop - true if the IP is to be drawn at the top of the line of text + @param fAssocRight - true if the IP is associated with the text to the right + @param fSecondary - true if the IP is secondary, false if it is primary + @param dysMinSplitHeight + - minimum split cursor height + @param rs, rd - source/destination device coordinates + @param prdRet - return location to be drawn in dest device coords; + if NULL, do the drawing +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::InvertSplitIP(float xs, float ysTop, float ysBottom, + bool fTop, bool fAssocRight, bool fSecondary, float dysMinSplitHeight, + Rect * prdRet) +{ + if (prdRet == NULL && ysTop == ysBottom) + return; // invisible IP + + float xd = ScaleXToDest(xs); + float ydTop = ScaleYToDest(ysTop); + float ydBottom = ScaleYToDest(ysBottom); + float dydMinSplitHeight = dysMinSplitHeight * m_yFactor; + + float ydMid = (ydTop + ydBottom) / 2; + + // For now, assume that destination coordinates are in terms of pixels. + float dxdOneXPixel = 1; + float dydOneYPixel = 1; + float dxdTwoXPixels = 2; + + float xdLeft, xdRight, dxdHookLen; + if (fSecondary) + { + xdLeft = xd - (dxdOneXPixel / 2); + xdRight = xdLeft + 1; + dxdHookLen = 2; + } + else + { + xdLeft = xd - (dxdTwoXPixels / 2); + xdRight = xdLeft + 2; + dxdHookLen = 3; + } + + float yd1, yd2; + if (fTop) + { + yd1 = ydTop; + if (ydMid - ydTop < dydMinSplitHeight) + ydMid = ydTop + dydMinSplitHeight; + yd2 = (fSecondary) ? ydMid : ydMid + 3; + } + else + { + yd2 = ydBottom; + if (ydBottom - ydMid < dydMinSplitHeight) + ydMid = ydBottom - dydMinSplitHeight; + yd1 = (fSecondary) ? ydMid : ydMid - 3; + } + + if (prdRet) + { + prdRet->left = xdLeft; + prdRet->right = xdRight; + // The most common reason for returning the positions of the IP is to scroll it into + // view, in which case we want to return the entire line height, not just the + // height of the IP itself. + prdRet->top = ydTop; // yd1; + prdRet->bottom = ydBottom; // yd2; + } + else + { + InvertRect(xdLeft, yd1, xdRight, yd2); + } + + if (fAssocRight) + { + if (prdRet) + prdRet->right = xdRight + dxdHookLen; + else + { + if (fTop) + InvertRect(xdRight, ydTop, + xdRight + dxdHookLen, ydTop + dydOneYPixel); + else + InvertRect(xdRight, ydBottom-dydOneYPixel, + xdRight + dxdHookLen, ydBottom); + } + } + else + { + if (prdRet) + prdRet->left = xdLeft - dxdHookLen; + else + { + if (fTop) + InvertRect(xdLeft - dxdHookLen, ydTop, + xdLeft, ydTop + dydOneYPixel); + else + InvertRect(xdLeft - dxdHookLen, ydBottom - dydOneYPixel, + xdLeft, ydBottom); + } + } +} + +/*---------------------------------------------------------------------------------------------- + Given a character position (relative to the beginning of the string), return the + position for the insertion point, relative to the top-left of the segment. + Notice that these are before/after positions, not left/right positions. + Return an infinity value to indicate no appropriate IP. + + @param ichwSel - selection position, relative to beginning of string + @param fBefore - true if we are interested in the character the IP is before. + @param pxs, pysTop, pysBottom + - return locations for drawing the IP (top and bottom are not + adjusted for half-selections) + @param pfRtl - return true if the character is right-to-left + + Review: it might be useful to figure out if the two surface glyphs are physically adjacent + in such a way that their IPs can be combined into one I-beam. For now we use a simple + rule that assumes that any glyph or ligature component is at least 10% as wide as the + height of the font (see CloseIPPositions). +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::CalcIP(int ichwSel, bool fBefore, + float * pxs, float * pysTop, float * pysBottom, bool * pfRtl) +{ + Font & font = this->m_pseg->getFont(); + + int ichw = (fBefore) ? ichwSel : ichwSel - 1; + + int iginfOutput; + + *pfRtl = m_pseg->CharIsRightToLeft(ichw, fBefore); + + GrEngine * pgreng = m_pseg->EngineImpl(); + GrGlyphTable * pgtbl = (pgreng) ? pgreng->GlyphTable() : NULL; + + float dysFontAscent = m_pseg->m_dysFontAscent; + ////GrResult res = GetFontAscentSourceUnits(pgg, &dysFontAscent); + ////if (ResultFailed(res)) + //// THROW(WARN(res)); + dysFontAscent += m_pseg->m_dysXAscent; + + *pysTop = 0; + *pysBottom = m_pseg->m_dysHeight; + + int ichwSegOffset = ichw - m_pseg->m_ichwMin; + int isloutLig; + if (ichwSegOffset < m_pseg->m_ichwAssocsMin || ichwSegOffset >= m_pseg->m_ichwAssocsLim) + isloutLig = kNegInfinity; + else + isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin]; + bool fOkLig = (isloutLig != kNegInfinity); + if (pgtbl && fOkLig) + { + // Ligature component; place the IP at the boundary + // of the component box. + + GrSlotOutput * pslout = m_pseg->OutputSlot(isloutLig); + int icomp = m_pseg->m_prgiComponent[ichwSegOffset - m_pseg->m_ichwAssocsMin]; + + iginfOutput = m_pseg->LogicalToPhysicalSurface(isloutLig); + + float xsGlyphOffset = m_pseg->GlyphLeftEdge(iginfOutput); + + float xsLeft, xsRight, ysTop, ysBottom; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + fOkLig = pgtbl->ComponentBoxLogUnits(m_pseg->m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_pseg->m_mFontEmUnits, m_pseg->m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom); + if (fOkLig) + { + if (fBefore == *pfRtl) + // Position of interest is to the right. + *pxs = xsGlyphOffset + xsRight; + else + // Position of interest is to the left. + *pxs = xsGlyphOffset + xsLeft; + + GlyphInfo * pginf = m_pseg->m_prgginf + iginfOutput; + float ysGlyph = pginf->yOffset(); + + *pysTop = ysTop + (m_pseg->m_dysAscent - dysFontAscent) - ysGlyph; + *pysBottom = ysBottom + (m_pseg->m_dysAscent - dysFontAscent) - ysGlyph; + } + } + + if (!fOkLig) + { + // Not a ligature component. + + int islout = m_pseg->UnderlyingToLogicalSurface(ichw, fBefore); + GrSlotOutput * psloutTmp = m_pseg->OutputSlot(islout); + iginfOutput = m_pseg->LogicalToPhysicalSurface(islout); + + if (kPosInfinity == iginfOutput || kNegInfinity == iginfOutput) + *pxs = kPosInfFloat; + + else + { + bool fCluster = (psloutTmp->IsPartOfCluster()); + bool fHighlightBB = fCluster; + // Test below should match the one in CalcHighlightRect: + if ((psloutTmp->ClusterAdvance() == 0 + || psloutTmp->GlyphMetricLogUnits(&font, kgmetAdvWidth) == 0) + && !psloutTmp->IsSpace()) + { + fHighlightBB = true; // use bounding box for zero-advance glyph + } + if (fHighlightBB) + { + // Cluster member or zero-width glyph. + + if (fCluster && AtEdgeOfCluster(psloutTmp, islout, fBefore) && + !CanInsertIntoCluster(psloutTmp, islout)) + { + // Place the IP at the edge of the whole cluster. + + int isloutBase = psloutTmp->ClusterBase(); + GrSlotOutput * psloutBase = m_pseg->OutputSlot(isloutBase); + //int igbbBase = psloutBase->GlyphBbIndex(); + int iginfBase = m_pseg->LogicalToPhysicalSurface(isloutBase); + + float xsBase = m_pseg->GlyphLeftEdge(iginfBase); + + float xsLeft = xsBase + psloutBase->ClusterXOffset(); + float xsRight = xsBase + psloutBase->ClusterAdvance(); + + if (fBefore == *pfRtl) + // Position of interest is to the right + *pxs = xsRight; + else + // Position of interest is to the left. + *pxs = xsLeft; + } + else + { + // Place the IP on the perimeter of the bounding box of the cluster member. + + float xsGlyphStart = m_pseg->GlyphLeftEdge(iginfOutput); + +// float dysFontAscent; +// res = GetFontAscentSourceUnits(pgg, &dysFontAscent); +// if (ResultFailed(res)) THROW(WARN(res)); +// dysFontAscent += m_dysXAscent; + + float ysBbTop = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbTop); + float ysBbBottom = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbBottom); + float xsBbLeft = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbLeft); + float xsBbRight = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbRight); + + int twoPixels = 2; // assume dest coords are in pixels + + if (fBefore == *pfRtl) + // Position of interest is to the right + *pxs = xsGlyphStart + xsBbRight + twoPixels; + else + // Position of interest is to the left. + *pxs = xsGlyphStart + xsBbLeft - twoPixels; + + GlyphInfo * pginf = m_pseg->m_prgginf + iginfOutput; + float ysGlyph = pginf->yOffset(); + float ysGlyphBaseline = m_pseg->m_dysAscent - ysGlyph; // relative to top + + *pysTop = ysGlyphBaseline - ysBbTop - twoPixels; + *pysBottom = ysGlyphBaseline - ysBbBottom + twoPixels; + } + } + else + { + // Insertion point goes at origin or advance width of glyph. + + *pxs = m_pseg->GlyphLeftEdge(iginfOutput); + if (fBefore == *pfRtl) + { + // Position of interest is to the right + //*pxs += psloutTmp->GlyphMetricLogUnits(&font, kgmetAdvWidth); + *pxs += psloutTmp->ClusterAdvance(); + } + // else position of interest is to the left + } + } + } +} + +/*---------------------------------------------------------------------------------------------- + If the two positions for the insertion point are close enough, merge them into + one insertion point, and return true to indicate that we want an I-beam. + + @param pxsBefore, pxsAfter - for comparing, and also adjusted results + @param ysBeforeTop/Bottom, ysAfterTop/Bottom - for comparing +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::CloseIPPositions(float * pxsBefore, float ysBeforeTop, float ysBeforeBottom, + float * pxsAfter, float ysAfterTop, float ysAfterBottom) +{ + // No adjustment if vertical positions are disjoint. + if (ysBeforeBottom <= ysAfterTop) + return false; + if (ysAfterBottom <= ysBeforeTop) + return false; + + float dysHt = max(ysBeforeBottom, ysAfterBottom) - min(ysBeforeTop, ysAfterTop); + float dxsDiff = fabsf(*pxsBefore - *pxsAfter); + + // If the difference in the x positions is less than 10% of the height, + // we want an I-beam--use the average. + if (dxsDiff * 10 < dysHt) + { + *pxsBefore = (*pxsBefore + *pxsAfter) / 2; + *pxsAfter = *pxsBefore; + return true; + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the slot in question is the first or last slot of its cluster, + or if it is not part of a cluster at all. Return false if the specified side + of the slot is interior to a cluster. + + @param fBefore - if true, we are examining the slot the insertion point is before, + so we are interested in whether this slot is at the + leading edge of the cluster. +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::AtEdgeOfCluster(GrSlotOutput * pslout, int islout, bool fBefore) +{ + if (!pslout->IsPartOfCluster()) + return true; + + if (fBefore && (islout == 0)) + return true; + if (!fBefore && (m_pseg->m_cslout == islout + 1)) + return true; + + // If the adjacent slot in whatever direction we're interested in has the same + // cluster base, this slot is not on the edge of the cluster. + // -- NO, because you could have an intervening glyph that is not part of the + // cluster. + //int isloutAdjacentBase = (fBefore) ? + // m_pseg->OutputSlot(islout - 1)->ClusterBase() : + // m_pseg->OutputSlot(islout + 1)->ClusterBase(); + //return (isloutAdjacentBase != m_pseg->OutputSlot(islout)->ClusterBase()); + + // Ask the cluster base. + return AtEdgeOfCluster(m_pseg->OutputSlot(pslout->ClusterBase()), pslout->ClusterBase(), + pslout, islout, fBefore); +} + +bool SegmentPainter::AtEdgeOfCluster(GrSlotOutput * psloutBase, int isloutBase, + GrSlotOutput * pslout, int islout, bool fBefore) +{ + // Compare pslout to all the members of the cluster. If it is the minimum or maximum, it + // is at an edge. + if (fBefore && isloutBase < islout) + return false; + if (!fBefore && isloutBase > islout) + return false; + + std::vector<int> visloutCluster; + m_pseg->ClusterMembersForGlyph(isloutBase, psloutBase->ClusterRange(), visloutCluster); + + for (size_t iislout = 0; iislout < visloutCluster.size(); iislout++) + { + int isloutMember = visloutCluster[iislout]; + if (fBefore && isloutMember < islout) + return false; + if (!fBefore && isloutMember > islout) + return false; + } + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return true if it is permitted to put an insertion point somewhere in the middle of + the given slot's cluster. +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::CanInsertIntoCluster(GrSlotOutput * pslout, int islout) +{ + if (!pslout->IsPartOfCluster()) + return false; + + int isloutBase = pslout->ClusterBase(); + if (islout != isloutBase) + { + // Ask the cluster base. + Assert(pslout->ClusterRange() == 0); + return CanInsertIntoCluster(m_pseg->OutputSlot(isloutBase), isloutBase); + } + + if (!AtEdgeOfCluster(pslout, islout, true) && pslout->InsertBefore()) + return true; + + std::vector<int> visloutCluster; + m_pseg->ClusterMembersForGlyph(isloutBase, pslout->ClusterRange(), visloutCluster); + + for (size_t iislout = 0; iislout < visloutCluster.size(); iislout++) + { + int isloutMember = visloutCluster[iislout]; + if (AtEdgeOfCluster(m_pseg->OutputSlot(isloutMember), isloutMember, true)) + // Inserting before the first member of the cluster doesn't count. + continue; + + if (m_pseg->OutputSlot(isloutMember)->InsertBefore()) + return true; + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Determine which characters in the selected range are part of ligatures that are fully + selected vs. those that are only partially selected. Those that are only partially selected + must have their components highlighted individually, but those that are fully selected + should be highlighted fully, to avoid blank spots in the case that the components do not + fully cover the surface of the glyph. + + @param prgfAllSelected - array of flags to fill in with results + @param ichwMin/LimSeg - range of characters to examine + @param ichwMin/LimSel - range of characters that are selected +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::CalcPartialLigatures(bool * prgfAllSelected, + int ichwMinSeg, int ichwLimSeg, + int ichwMinSel, int ichwLimSel) +{ + GrEngine * pgreng = m_pseg->EngineImpl(); + GrGlyphTable * pgtbl = (pgreng) ? pgreng->GlyphTable() : NULL; + + for (int ichw = ichwMinSeg; ichw < ichwLimSeg; ichw++) + { + int ichwSegOffset = ichw - m_pseg->m_ichwMin; + int isloutLig; + if (ichwSegOffset < m_pseg->m_ichwAssocsMin || ichwSegOffset >= m_pseg->m_ichwAssocsLim) + isloutLig = kNegInfinity; + else + isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin]; + bool fLigComp = (pgtbl && isloutLig != kNegInfinity); + if (fLigComp && !prgfAllSelected[ichwSegOffset]) + { + GrSlotOutput * pslout = m_pseg->OutputSlot(isloutLig); + bool fAll = true; + int icomp; + for (icomp = 0; icomp < pslout->NumberOfComponents(); icomp++) + { + int ichwUnderlying = pslout->FirstUnderlyingComponent(icomp); // is it possible to select one char of a component and not another? + if (ichwUnderlying < ichwMinSel - m_pseg->m_ichwMin || + ichwUnderlying >= ichwLimSel - m_pseg->m_ichwMin) + { + fAll = false; // found a component that is not selected + } + } + for (icomp = 0; icomp < pslout->NumberOfComponents(); icomp++) + { + for (int ichw = pslout->FirstUnderlyingComponent(icomp) ; + ichw <= pslout->LastUnderlyingComponent(icomp) ; + ichw++) + { + if (m_pseg->m_prgiComponent[ichw - m_pseg->m_ichwAssocsMin] == icomp) + prgfAllSelected[ichw] = fAll; + } + } + } + } +} + +/*---------------------------------------------------------------------------------------------- + The given character is part of the selected range. Add to the vector a rectangle (or + eventually, several) indicating the corresponding screen area(s) of the character. + The rectangle is relative to the top left of the segment. + + @param ichw - selected char, relative to beginning of string + @param vrs - list of rectangles to highlight, in source coords, relative to + segment draw position (top-left) + @param vfEntireHt - for each of the above rectangles, true if the rect should be + extended to include the entire line + @param fJustComponent - true if we want to highlight just the single ligature component + this character maps to; false if the entire ligature is selected + @param rgfHighlighted - for each glyph in this segment, whether it is being highlighted + by this operation + @param fSkipTrSpace - should we skip trailing white space? +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::CalcHighlightRect(int ichw, + std::vector<Rect> & vrs, std::vector<bool> & vfEntireHt, + bool fJustComponent, bool * rgfHighlighted, bool fSkipTrSpace) +{ + GrResult res; + + GrEngine * pgreng = m_pseg->EngineImpl(); + GrGlyphTable * pgtbl = (pgreng) ? pgreng->GlyphTable() : NULL; + //bool fRtl = m_pseg->rightToLeft(); + + Font & font = m_pseg->getFont(); + + if (!GrCharStream::AtUnicodeCharBoundary(m_pseg->GetString(), ichw)) + // Second half of a surrogate pair, or UTF-8: ignore. + return; + + // Calculate the location for the glyph that this IP is before. + + int ichwSegOffset = ichw - m_pseg->m_ichwMin; + int isloutLig; + if (ichwSegOffset < m_pseg->m_ichwAssocsMin || ichwSegOffset >= m_pseg->m_ichwAssocsLim) + isloutLig = kNegInfinity; + else + isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin]; + bool fLigComp = (pgtbl && isloutLig != kNegInfinity); + if (fLigComp && fJustComponent) + { + // Character is a ligature component. + + GrSlotOutput * pslout = m_pseg->OutputSlot(isloutLig); + int icomp = m_pseg->m_prgiComponent[ichwSegOffset - m_pseg->m_ichwAssocsMin]; + int iginfOutput = m_pseg->LogicalToPhysicalSurface(isloutLig); + + float xsGlyphStart = m_pseg->GlyphLeftEdge(iginfOutput); + + float xsLeft, xsRight, ysTop, ysBottom; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + pgtbl->ComponentBoxLogUnits(m_pseg->m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_pseg->m_mFontEmUnits, m_pseg->m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom); + + float dysFontAscent = m_pseg->m_dysFontAscent; + ////GetFontAscentSourceUnits(pgg, &dysFontAscent); + dysFontAscent += m_pseg->m_dysXAscent; + GlyphInfo * pginf = m_pseg->m_prgginf + iginfOutput; + float ysForGlyph = pginf->yOffset(); + float dysGlyphTop = (m_pseg->m_dysAscent - dysFontAscent) - ysForGlyph; // rel. to common baseline + + Rect rsNew; + rsNew.top = ysTop + dysGlyphTop; + rsNew.bottom = ysBottom + dysGlyphTop; + rsNew.left = xsGlyphStart + xsLeft; + rsNew.right = xsGlyphStart + xsRight; + vrs.push_back(rsNew); + + vfEntireHt.push_back(false); + } + else + { + std::vector<int> vislout = m_pseg->UnderlyingToLogicalAssocs(ichw); + if (vislout.size() == 0) + return; + + float xsGlyphLeft, xsGlyphRight; + + res = kresOk; + + for (size_t iislout = 0; iislout < vislout.size(); iislout++) + { + int islout = vislout[iislout]; + if (islout == kNegInfinity || islout == kPosInfinity) + continue; + int iginfTmp = m_pseg->LogicalToPhysicalSurface(islout); + Assert(iginfTmp != kNegInfinity); + Assert(iginfTmp != kPosInfinity); + + if (fSkipTrSpace && islout >= m_pseg->m_isloutVisLim) + continue; + + Rect rsNew; + GrSlotOutput * psloutTmp = m_pseg->OutputSlot(islout); + bool fHighlightBB = psloutTmp->IsPartOfCluster(); + // Test below should match the one in CalcIP-- + // If the glyph has no advance width but does have a real bounding box, + // highlight the bounding box. Review: do we want this??? + if ((psloutTmp->ClusterAdvance() == 0 + || psloutTmp->GlyphMetricLogUnits(&font, kgmetAdvWidth) == 0) + && !psloutTmp->IsSpace()) + { + fHighlightBB = true; + } + xsGlyphLeft = m_pseg->GlyphLeftEdge(iginfTmp); + if (fHighlightBB) + { + // For now, highlight just the bounding box of this glyph. After we finish + // highlighting all glyphs individually, we'll go back and highlight + // as one unit any clusters that are completely selected. + +// int dysFontAscent; +// res = GetFontAscentSourceUnits(pgg, &dysFontAscent); // if we restore this code, add in ExtraAscent +// if (ResultFailed(res)) THROW(WARN(res)); +// dysFontAscent += m_dysXAscent; + + GlyphInfo * pginf = m_pseg->m_prgginf + iginfTmp; + float ysForGlyph = pginf->yOffset(); + float ysGlyphBaseline = m_pseg->m_dysAscent - ysForGlyph; // relative to top + + float ysBbTop = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbTop); + float ysBbBottom = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbBottom); + float xsBbLeft = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbLeft); + float xsBbRight = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbRight); + + rsNew.top = ysGlyphBaseline - ysBbTop - 2; + rsNew.bottom = ysGlyphBaseline - ysBbBottom + 2; + rsNew.left = xsGlyphLeft + xsBbLeft - 2; + rsNew.right = xsGlyphLeft + xsBbRight + 2; + + vfEntireHt.push_back(false); + } + else + { + // Highlight entire width of the glyph from the top of the line to the bottom. + // Horizontally, highlight from the origin of the glyph to the advance width. + + //xsGlyphRight = xsGlyphLeft + psloutTmp->GlyphMetricLogUnits(&font, kgmetAdvWidth); + xsGlyphRight = xsGlyphLeft + psloutTmp->ClusterAdvance(); + + if (xsGlyphLeft > xsGlyphRight) + std::swap(xsGlyphLeft, xsGlyphRight); + + rsNew.top = 0; + rsNew.bottom = m_pseg->m_dysHeight; + rsNew.left = xsGlyphLeft; + rsNew.right = xsGlyphRight; + + vfEntireHt.push_back(true); + } + vrs.push_back(rsNew); + + if (rgfHighlighted) + rgfHighlighted[iginfTmp] = true; + } + } +} + +/*---------------------------------------------------------------------------------------------- + If the output slot is a cluster base and all the members of the cluster are highlighted, + simply highlight the cluster as a single rectangle, to avoid unslightly gaps + between glyphs. (Note that this does not remove the rectangles that were already included + to handle the individual members.) + The rectangles are relative to the top left of the segment. + + @param islout - output slot index + @param vrect - list of rectangles to highlight; add result here + @param prgfHighlighted - flags indicating which glyphs have been highlighted, indexed by + physical surface position +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::CalcCompleteCluster(int islout, + std::vector<Rect> & vrs, std::vector<bool> & vfEntireHt, bool * prgfHighlighted) +{ + //GrEngine * pgreng = m_pseg->EngineImpl(); + + GrSlotOutput * pslout = m_pseg->OutputSlot(islout); + if (pslout->ClusterRange() == 0) + return; + + int iginfDraw = m_pseg->LogicalToPhysicalSurface(islout); + if (!prgfHighlighted[iginfDraw]) + return; + + bool fAllHighlighted = true; + + std::vector<int> visloutCluster; + m_pseg->ClusterMembersForGlyph(islout, pslout->ClusterRange(), visloutCluster); + for (size_t i = 0; i < visloutCluster.size(); i++) + { + int iginfMemberDraw = m_pseg->LogicalToPhysicalSurface(visloutCluster[i]); + if (!prgfHighlighted[iginfMemberDraw]) + { + fAllHighlighted = false; + break; + } + } + + if (!fAllHighlighted) + return; + + // All the members of this cluster are highlighted. + + float xsGlyphStart = m_pseg->GlyphLeftEdge(iginfDraw); + + Rect rsNew; + rsNew.top = 0; + rsNew.bottom = m_pseg->m_dysHeight; + rsNew.left = xsGlyphStart + pslout->ClusterXOffset(); + rsNew.right = xsGlyphStart + pslout->ClusterAdvance(); + vrs.push_back(rsNew); + + vfEntireHt.push_back(true); +} + +/*---------------------------------------------------------------------------------------------- + Add the given rectangle to the vector, eliminating any overlapping areas. + + Note this method can be recursive: if we have to break the current rectangle into + two, we'll call the method again the for new +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::AddRectWithoutOverlaps(std::vector<Rect> & vrect, Rect rectToAdd) +{ + std::vector<Rect> vrectMoreToAdd; + + bool fAnythingToAdd = AnyArea(&rectToAdd); + + size_t irect; + for (irect = 0; irect < vrect.size(); irect++) + { + bool f = AdjustRectsToNotOverlap(vrect, irect, &rectToAdd, vrectMoreToAdd); + if (!AnyArea(&vrect[irect])) + { + vrect.erase(vrect.begin() + irect); + irect--; + } + if (!f) + { + fAnythingToAdd = false; + break; + } + } + + // Now rectToAdd is in a state that it does not intersect with any other rectangles + // in the list. + + if (fAnythingToAdd) + vrect.push_back(rectToAdd); + + // Add in any extra rectangles that are needed to handle overlaps. + + for (irect = 0; irect < vrectMoreToAdd.size(); irect++) + AddRectWithoutOverlaps(vrect, vrectMoreToAdd[irect]); +} + +/*---------------------------------------------------------------------------------------------- + Return true if the rectangle has an area greater than 0. +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::AnyArea(Rect * prect) +{ + Assert(prect->left <= prect->right); + Assert(prect->top <= prect->bottom); + + return (prect->left < prect->right && prect->top < prect->bottom); +} + +/*---------------------------------------------------------------------------------------------- + Adjust the rectangle prect2 to avoid overlaps with the given one in the vector. + Return false if the rectangle does not need to be added after all. + + If we have to split this rectangle into two, add the second one to vrectMoreToAdd, so + we can do the same process on it. + + If we can merge the two rectangles, add the result to vrectMoreToAdd (so it can be properly + checked for further overlaps), and adjust the rectangle in the vector so it will be + deleted. + + @param vrect - current list of valid rectangles + @param irect - item in vrect we're comparing with prect2 + @param prect2 - rectangle to add + @param vrectMoreToAdd - list of accumulated rectangles that need to be added + (caused by splitting one into two) + + @return True if we want to add prect2 (possibly adjusted) to the list; + false it it is completely subsumed by vrect[irect], or has been merged with it, + or has no area. +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::AdjustRectsToNotOverlap(std::vector<Rect> & vrect, int irect, Rect * prect2, + std::vector<Rect> & vrectMoreToAdd) +{ + Rect * prect1 = &(vrect[irect]); + + float xLeft1 = prect1->left; + float xRight1 = prect1->right; + float yTop1 = prect1->top; + float yBottom1 = prect1->bottom; + Assert(xLeft1 < xRight1); + Assert(yTop1 < yBottom1); + + float xLeft2 = prect2->left; + float xRight2 = prect2->right; + float yTop2 = prect2->top; + float yBottom2 = prect2->bottom; + Assert(xLeft2 <= xRight2); + Assert(yTop2 <= yBottom2); + + if (!AnyArea(prect2)) + return false; + + // No overlap: keep both as they are. + if (yBottom2 < yTop1 || yBottom1 < yTop2) + return true; + if (xRight2 < xLeft1 || xRight1 < xLeft2) + return true; + + // Present rect subsumes new: ignore new + if (xLeft1 <= xLeft2 && xRight2 <= xRight1 && yTop1 <= yTop2 && yBottom2 <= yBottom1) + return false; + + // New rect subsumes present: replace. + if (xLeft2 <= xLeft1 && xRight1 <= xRight2 && yTop2 <= yTop1 && yBottom1 <= yBottom2) + { + prect1->bottom = prect1->top; // delete + return true; // keep this one + } + + // Abutting rectangles of same height or width: merge. + if (yTop1 == yTop2 && yBottom1 == yBottom2) + { + if (xRight1 == xLeft2) + { + // prect2 is to the right + prect2->left = prect1->left; + vrectMoreToAdd.push_back(*prect2); + prect1->bottom = prect1->top; // delete previous + return false; + } + else if (xRight2 == xLeft1) + { + // prect2 is to the left + prect2->right = prect1->right; + vrectMoreToAdd.push_back(*prect2); + prect1->bottom = prect1->top; // delete + return false; + } + } + if (xLeft1 == xLeft2 && xRight1 == xRight2) + { + if (yBottom1 == yTop2) + { + // prect2 is below + prect2->top = prect1->top; + vrectMoreToAdd.push_back(*prect2); + prect1->bottom = prect1->top; // delete + return false; + } + else if (yBottom2 == yTop1) + { + // prect2 is above + prect2->bottom = prect1->bottom; + vrectMoreToAdd.push_back(*prect2); + prect1->bottom = prect1->top; // delete + return false; + } + } + + // Abutting rectangles of different heights or widths: leave as they are. + if (xRight1 == xLeft2 || xRight2 == xLeft1) + return true; + if (yBottom1 == yTop2 || yBottom2 == yTop1) + return true; + + // Overlapping on one axis: adjust smaller rectangle + if (yTop1 <= yTop2 && yBottom2 <= yBottom1) + { + if (xLeft1 <= xLeft2 && xLeft2 < xRight1) + { + Assert(xRight1 < xRight2); // otherwise rect2 is subsumed + prect2->left = prect1->right; + return true; + } + else if (xLeft1 < xRight2 && xRight2 <= xRight1) + { + Assert(xLeft2 < xLeft1); // otherwise rect2 is subsumed + prect2->right = prect1->left; + return true; + } + } + else if (yTop2 <= yTop1 && yBottom1 <= yBottom2) + { + if (xLeft2 <= xLeft1 && xLeft1 < xRight2) + { + Assert(xRight2 < xRight1); // otherwise rect1 is subsumed + prect1->left = prect2->right; + return true; + } + else if (xLeft2 < xRight1 && xRight1 <= xRight2) + { + Assert(xLeft1 < xLeft2); // otherwise rect1 is subsumed + prect1->right = prect2->left; + return true; + } + } + + if (xLeft1 <= xLeft2 && xRight2 <= xRight1) + { + if (yTop1 <= yTop2 && yTop2 < yBottom1) + { + Assert(yBottom1 < yBottom2); // otherwise rect2 is subsumed + prect2->top = prect1->bottom; + return true; + } + else if (yTop1 < yBottom2 && yBottom2 <= yBottom1) + { + Assert(yTop2 < yTop1); // otherwise rect2 is subsumed + prect2->bottom = prect1->top; + return true; + } + } + else if (xLeft2 <= xLeft1 && xRight1 <= xRight2) + { + if (yTop2 <= yTop1 && yTop1 < yBottom2) + { + Assert(yBottom2 < yBottom1); // otherwise rect1 is subsumed + prect1->top = prect2->bottom; + return true; + } + else if (yTop2 < yBottom1 && yBottom1 <= yBottom2) + { + Assert(yTop1 < yTop2); // otherwise rect1 is subsumed + prect1->bottom = prect2->top; + return true; + } + } + + // Centers overlap: break prect2 into two disjoint rectangles. + if (xLeft1 < xLeft2 && xRight2 < xRight1 && yTop2 < yTop1 && yBottom1 < yBottom2) + { + // create a new rectangle for the bottom of prect2 that is below prect1 + Rect rectNew = *prect2; + rectNew.top = prect1->bottom; + prect2->bottom = prect1->top; + vrectMoreToAdd.push_back(rectNew); + return true; + } + else if (xLeft2 < xLeft1 && xRight1 < xRight2 && yTop1 < yTop2 && yBottom2 < yBottom1) + { + // create a new rectangle for the right of prect2 that is to the right of prect1 + Rect rectNew = *prect2; + rectNew.left = prect1->right; + prect2->right = prect1->left; + vrectMoreToAdd.push_back(rectNew); + return true; + } + + // Corners overlap: break prect2 into two adjacent rectangles. + if (xLeft1 < xLeft2 && xRight1 < xRight2) + { + // prect2 is partially to the right + if (yTop1 < yTop2 && yBottom1 < yBottom2) + { + // prect2 is partially below + Rect rectNew = *prect2; + rectNew.left = prect1->right; + prect2->top = prect1->bottom; + vrectMoreToAdd.push_back(rectNew); + return true; + } + else if (yTop2 < yTop1 && yBottom2 < yBottom1) + { + // prect2 is partially above + Rect rectNew = *prect2; + rectNew.left = prect1->right; + prect2->bottom = prect1->top; + vrectMoreToAdd.push_back(rectNew); + return true; + } + } + else if (xLeft2 < xLeft1 && xRight2 < xRight1) + { + // prect2 is partially to the left + if (yTop1 < yTop2 && yBottom1 < yBottom2) + { + // prect2 is partially below + Rect rectNew = *prect2; + rectNew.right = prect1->left; + prect2->top = prect1->bottom; + vrectMoreToAdd.push_back(rectNew); + return true; + } + else if (yTop2 < yTop1 && yBottom2 < yBottom1) + { + // prect2 is partially above + Rect rectNew = *prect2; + rectNew.right = prect1->left; + prect2->bottom = prect1->top; + vrectMoreToAdd.push_back(rectNew); + return true; + } + } + + // No overlap. + return true; +} + +/*---------------------------------------------------------------------------------------------- + Assert that there are no overlaps among all the rectangles in the array, which should + be the case if AdjustRectsToNotOverlap is working properly. +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::AssertNoOverlaps(std::vector<Rect> & vrect) +{ +#ifdef _DEBUG + for (int irect1 = 0; irect1 < signed(vrect.size() - 1); irect1++) + { + for (int irect2 = irect1 + 1; irect2 < signed(vrect.size()); irect2++) + { + float xLeft1 = vrect[irect1].left; + float xRight1 = vrect[irect1].right; + float yTop1 = vrect[irect1].top; + float yBottom1 = vrect[irect1].bottom; + Assert(xLeft1 < xRight1); + Assert(yTop1 < yBottom1); + + float xLeft2 = vrect[irect2].left; + float xRight2 = vrect[irect2].right; + float yTop2 = vrect[irect2].top; + float yBottom2 = vrect[irect2].bottom; + Assert(xLeft2 <= xRight2); + Assert(yTop2 <= yBottom2); + + Assert(xRight1 <= xLeft2 || xRight2 <= xLeft1 || + yBottom1 <= yTop2 || yBottom2 <= yTop1); + } + } +#endif +} + +/*---------------------------------------------------------------------------------------------- + Add the given line segment to the vector, eliminating any overlapping areas. +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::AddLineSegWithoutOverlaps(std::vector<LineSeg> & vls, LineSeg lsToAdd) +{ + std::vector<LineSeg> vlsMoreToAdd; + + bool fAnythingToAdd = AnyLength(&lsToAdd); + + size_t ils; + for (ils = 0; ils < vls.size(); ils++) + { + bool f = AdjustLineSegsToNotOverlap(vls, ils, &lsToAdd, vlsMoreToAdd); + if (!AnyLength(&vls[ils])) + { + vls.erase(vls.begin() + ils); + ils--; + } + if (!f) + { + fAnythingToAdd = false; + break; + } + } + + // Now lsToAdd is in a state that it does not overlap with any other line segments + // in the list. + + if (fAnythingToAdd) + vls.push_back(lsToAdd); + + // Add in any extra line segments that are needed to handle overlaps. + + for (ils = 0; ils < vlsMoreToAdd.size(); ils++) + AddLineSegWithoutOverlaps(vls, vlsMoreToAdd[ils]); +} + +/*---------------------------------------------------------------------------------------------- + Return true if the rectangle has an area greater than 0. +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::AnyLength(LineSeg * pls) +{ + Assert(pls->left <= pls->right); + return (pls->left < pls->right); +} + +/*---------------------------------------------------------------------------------------------- + Adjust the line segment pls2 to avoid overlaps with the given one in the vector. + Return false if the line segment does not need to be added after all. + + If we can merge the two line segments, add the result to vrectMoreToAdd (so it can be properly + checked for further overlaps), and adjust the rectangle in the vector so it will be + deleted. + + @param vls - current list of valid line segments + @param ils - item in vrect we're comparing with pls2 + @param pls2 - line segment to add + @param vlsMoreToAdd - list of accumulated line segments that need to be added + (caused by splitting one into two) + + @return True if we want to add pls2 (possibly adjusted) to the list; + false it it is completely subsumed by vls[ils], or has been merged with it, + or has no length. +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::AdjustLineSegsToNotOverlap(std::vector<LineSeg> & vls, int ils, LineSeg * pls2, + std::vector<LineSeg> & vlsMoreToAdd) +{ + LineSeg * pls1 = &(vls[ils]); + + float xLeft1 = pls1->left; + float xRight1 = pls1->right; + Assert(xLeft1 < xRight1); + + float xLeft2 = pls2->left; + float xRight2 = pls2->right; + Assert(xLeft2 <= xRight2); + + if (!AnyLength(pls2)) + return false; + + // No overlap: keep both as they are. + if (xRight2 < xLeft1 || xRight1 < xLeft2) + return true; + + // Present line segment subsumes new: ignore new + if (xLeft1 <= xLeft2 && xRight2 <= xRight1) + return false; + + // New line segment subsumes present: replace. + if (xLeft2 <= xLeft1 && xRight1 <= xRight2) + { + pls1->left = pls1->right; // delete previous + return true; // keep this one + } + + // Adjacent or partially overlapping line segments: merge. + if (xLeft1 <= xLeft2 && xLeft2 <= xRight1 && xRight1 <= xRight2) + { + pls2->left = xLeft1; + vlsMoreToAdd.push_back(*pls2); + pls1->left = pls1->right; // delete previous + return false; + } + else if (xLeft2 <= xLeft1 && xLeft1 <= xRight2 && xRight2 <= xRight1) + { + pls2->right = xRight1; + vlsMoreToAdd.push_back(*pls2); + pls1->left = pls1->right; // delete previous + return false; + } + else + Assert(false); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Assert that there are no overlaps among all the rectangles in the array, which should + be the case if AdjustRectsToNotOverlap is working properly. +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::AssertNoOverlaps(std::vector<LineSeg> & vls) +{ +#ifdef _DEBUG + for (int ils1 = 0; ils1 < (int)vls.size() - 1; ils1++) + { + for (int ils2 = ils1 + 1; ils2 < (int)vls.size(); ils2++) + { + float xLeft1 = vls[ils1].left; + float xRight1 = vls[ils1].right; + Assert(xLeft1 < xRight1); + + float xLeft2 = vls[ils2].left; + float xRight2 = vls[ils2].right; + Assert(xLeft2 <= xRight2); + + Assert(xRight1 <= xLeft2 || xRight2 <= xLeft1); + } + } +#endif +} + +/*---------------------------------------------------------------------------------------------- + Find the next physical location for the IP. Return true if we found one. + + @param pichw - current insertion point, and adjusted result to return + @param pfAssocPrev - is the IP "leaning" backwards? adjust to return + @param fRight - true if we are moving right + @param fAssocPrevMatch - the assoc-prev of the result must match the original selection. + This is used when extending range selections; the orientation + of the resulting end-point must be the same as previously. + This helps avoids circular loops when dealing with reordering. + @param fAssocPrevNeeded - value needed for result; ignored if fAssocPrevMatch == false + @param pnNextOrPrevSeg - return 1 if we need to try the following segment, -1 if we + need to try the previous - CURRENTLY NOT USED for anything + practical +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::ArrowKeyPositionInternal( + int * pichw, bool * pfAssocPrev, + bool fRight, bool fAssocPrevMatch, bool fAssocPrevNeeded, int * pnNextOrPrevSeg) +{ + int kBefore = true; + int kAfter = false; + + bool fResult = false; + + // Handle ligatures. + + fResult = AdjacentLigComponent(pichw, pfAssocPrev, fRight, true); + if (fResult) + return true; + + // Not within a ligature, or on the outside edge of one. + + int iginfStart; + bool fCharRtl; + if (*pfAssocPrev) + { + iginfStart = m_pseg->UnderlyingToPhysicalSurface(*pichw - 1, kAfter); + fCharRtl = m_pseg->CharIsRightToLeft(*pichw - 1, kAfter); + } + else + { + iginfStart = m_pseg->UnderlyingToPhysicalSurface(*pichw, kBefore); + fCharRtl = m_pseg->CharIsRightToLeft(*pichw, kBefore); + } + if (kNegInfinity == iginfStart || kPosInfinity == iginfStart) + { + *pnNextOrPrevSeg = 0; + return false; + } + + //if (*pfAssocPrev) + // fCharRtl ? iginfStart : iginfStart++; + //else + // fCharRtl ? iginfStart++ : iginfStart; + + if (*pfAssocPrev) + iginfStart++; + + // At this point iginfStart indicates the index of the glyph we are before. + + int ichwNewIndex; + bool fAPNew; + + bool fRtl = m_pseg->rightToLeft(); + bool fForward = (fRtl) ? !fRight : fRight; + int dir = (fForward) ? 1 : -1; + int diginf = 0; + + // When moving forward, we first move to the opposite side of the current glyph, + // so initially diginf should be zero. + if (fForward) + diginf = -1; + + while (true) + { + diginf++; + int iginfTry = iginfStart + (dir * diginf); + + if (iginfTry < 0) + { + *pnNextOrPrevSeg = -1; + return false; + } + if (iginfTry > m_pseg->m_cginf) + { + *pnNextOrPrevSeg = 1; + return false; + } + if (iginfTry == m_pseg->m_cginf && fForward) + { + *pnNextOrPrevSeg = 1; + return false; + } + + bool fAfter; + int ichwTryChar; + if (iginfTry == m_pseg->m_cginf) + { + ichwTryChar = m_pseg->PhysicalSurfaceToUnderlying(iginfTry - 1, + kPosInfFloat, kPosInfFloat, 0, 0, &fAfter); + } + else + { + ichwTryChar = m_pseg->PhysicalSurfaceToUnderlying(iginfTry, + (fRight) ? kPosInfFloat : kNegInfFloat, + (fRight) ? kPosInfFloat : kNegInfFloat, + 0, 0, &fAfter); + } + + if (fAfter) + { + ichwNewIndex = ichwTryChar + 1; + fAPNew = true; + } + else + { + ichwNewIndex = ichwTryChar; + fAPNew = false; + } + + if (ichwNewIndex == *pichw) + continue; // didn't make any progress + + LgIpValidResult ipvr = isValidInsertionPoint(ichwNewIndex); + if (ipvr == kipvrBad) + continue; // bad insertion point + + if (fAssocPrevMatch && (fAssocPrevNeeded != fAPNew)) + { + // Try switching fAPNew and see if that gives the right result + bool fAPAlt = !fAPNew; + int iginfAlt = m_pseg->UnderlyingToPhysicalSurface( + fAPAlt ? ichwTryChar - 1 : ichwTryChar + 1, + fAPAlt ? kAfter : kBefore); + fCharRtl = m_pseg->CharIsRightToLeft( + fAPAlt ? ichwTryChar - 1 : ichwTryChar + 1, + fAPAlt ? kAfter : kBefore); + + if (fAPAlt) + fCharRtl ? iginfAlt : iginfAlt++; + else + fCharRtl ? iginfAlt++ : iginfAlt; + + if ((fForward && iginfAlt <= iginfStart) + || (!fForward && iginfAlt >= iginfStart)) + { + // Switching fAPNew would cause us to move in the opposite direction, so + // that's not a good solution. Keep going. + if ((iginfStart == 0 && !fForward) || (iginfStart == m_pseg->m_cginf && fForward)) + { + // Can't go any further, so go with this even though AP is wrong. + } + else + continue; + } + fAPNew = fAPAlt; + } + break; + } + + // If we have a selection within a ligature, adjust to either the right-most or the + // left-most. + AdjacentLigComponent(&ichwNewIndex, &fAPNew, fRight, false); + + *pichw = ichwNewIndex; + *pfAssocPrev = fAPNew; + return true; +} + +/****** OLD VERSION +bool Segment::ArrowKeyPositionInternal( + int * pichw, bool * pfAssocPrev, + bool fRight, bool fAssocPrevMatch, bool fAssocPrevNeeded, int * pnNextOrPrevSeg) +{ + int kBefore = true; + int kAfter = false; + + bool fResult = false; + + // Handle ligatures. + + fResult = AdjacentLigComponent(pichw, pfAssocPrev, fRight, true); + if (fResult) + return true; + + // Not within a ligature, or on the outside edge of one. + + int igbbOutput; + bool fCharRtl; + if (*pfAssocPrev) + { + igbbOutput = UnderlyingToPhysicalSurface(*pichw - 1, kAfter); + fCharRtl = CharIsRightToLeft(*pichw - 1, kAfter); + } + else + { + igbbOutput = UnderlyingToPhysicalSurface(*pichw, kBefore); + fCharRtl = CharIsRightToLeft(*pichw, kBefore); + } + if (kNegInfinity == igbbOutput || kPosInfinity == igbbOutput) + { + *pnNextOrPrevSeg = 0; + return false; + } + + if (*pfAssocPrev) + fCharRtl ? igbbOutput : igbbOutput++; + else + fCharRtl ? igbbOutput++ : igbbOutput; + + bool fBackward = (fRight == m_fWsRtl); + + // Try to find an underlying position that would produce an IP in the expected place. + // First look in the same direction in the underlying text, then, if necessary, try the + // opposite direction (which may be necessary when reordering occurred). + + int igbbNeed = (fRight) ? igbbOutput + 1 : igbbOutput - 1; + int ichwNew; + bool fAssocPrevNew; + + while (0 <= igbbNeed && igbbNeed <= NumGbb()) // include end-points--we may need to + // be before or after them. + { + int dichwInc = fBackward ? -1 : 1; + int dichwLim = m_ichwMin + (fBackward ? 0 : m_dichwLim); + + int nDirectionsTried = 0; +// while (nDirectionsTried < 2) -- Nope, for now we are being very strict above moving + while (nDirectionsTried < 1) // only in the desired direction + { + for (int ichwTry = *pichw; + ((dichwInc == 1 && ichwTry <= dichwLim) || + (dichwInc == -1 && ichwTry >= dichwLim)); + ichwTry += dichwInc) + { + int igbbTry = UnderlyingToPhysicalSurface(ichwTry, kBefore); + fCharRtl = CharIsRightToLeft(ichwTry, kBefore); + igbbTry = fCharRtl ? igbbTry + 1 : igbbTry; + if (igbbTry == igbbNeed && + (ichwTry != *pichw || *pfAssocPrev) && + (!fAssocPrevMatch || !fAssocPrevNeeded)) + { + ichwNew = ichwTry; + fAssocPrevNew = false; + goto LFixLigatures; + } + + igbbTry = UnderlyingToPhysicalSurface(ichwTry - 1, kAfter); + fCharRtl = CharIsRightToLeft(ichwTry - 1, kAfter); + igbbTry = fCharRtl ? igbbTry : igbbTry + 1; + if (igbbTry == igbbNeed && + (ichwTry != *pichw || !*pfAssocPrev) && + (!fAssocPrevMatch || fAssocPrevNeeded)) + { + ichwNew = ichwTry; + fAssocPrevNew = true; + goto LFixLigatures; + } + } + + nDirectionsTried++; + + // Didn't find an answer going the expected way; try the opposite direction. + dichwInc = (fRight) ? -1 : 1; + dichwLim = m_ichwMin + ((fRight) ? 0 : m_dichwLim); + } + + // Didn't find an underlying position that would produce an IP in the next glyph; + // try the glyph after that. + + igbbNeed = (fRight) ? igbbNeed + 1 : igbbNeed - 1; + } + + if (igbbNeed >= NumGbb()) + *pnNextOrPrevSeg = 1; // try next segment + else if (igbbNeed < 0) + *pnNextOrPrevSeg = -1; // try previous segment + else + { + Assert(false); + *pnNextOrPrevSeg = 0; + } + + return false; + +LFixLigatures: + + // If we have a selection within a ligature, adjust to either the right-most or the + // left-most. + AdjacentLigComponent(&ichwNew, &fAssocPrevNew, fRight, false); + + *pichw = ichwNew; + *pfAssocPrev = fAssocPrevNew; + return true; +} +******************/ + +/*---------------------------------------------------------------------------------------------- + Return position for the physically next or previous ligature component, or the right-most + or left-most. Return true if this routine handled it. + + @param pichw - starting underlying position + @param pfAssocPrev - is selection leaning backwards? + @param fMovingRight - true if right arrow key was pressed, false if left + @param fMove - if true, we want to find the next one over from *pichw; + if false, we want to find either the right-most + (fMovingRight = false) + or the left-most (fMovingRight = true) +----------------------------------------------------------------------------------------------*/ +bool SegmentPainter::AdjacentLigComponent(int * pichw, bool * pfAssocPrev, + bool fMovingRight, bool fMove) +{ + int kBefore = true; + int kAfter = false; + + GrEngine * pgreng = m_pseg->EngineImpl(); + GrGlyphTable * pgtbl = (pgreng) ? pgreng->GlyphTable() : NULL; + + if (!pgtbl) + { + return false; + } + + // Find the slot containing the ligature glyph. + + int ichwSegOffset = *pichw - m_pseg->m_ichwMin; + int isloutLig = kNegInfinity; + if (*pfAssocPrev) + { + if (ichwSegOffset - 1 >= m_pseg->m_ichwAssocsMin && ichwSegOffset - 1 < m_pseg->m_ichwAssocsLim) + isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - 1 - m_pseg->m_ichwAssocsMin]; + } + else + { + if (ichwSegOffset >= m_pseg->m_ichwAssocsMin && ichwSegOffset < m_pseg->m_ichwAssocsLim) + isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin]; + } + + if (isloutLig == kNegInfinity) + { + return false; + } + + if (*pfAssocPrev) + ichwSegOffset--; + + bool fGlyphRtl = (*pfAssocPrev) ? + m_pseg->CharIsRightToLeft(*pichw - 1, kAfter) : + m_pseg->CharIsRightToLeft(*pichw, kBefore); + + // Comparing right-sides. + bool fIPOnRight = (fGlyphRtl != *pfAssocPrev); + + // When two ligatures are horizontally equal, take vertical position into account. + // In left-to-right writing systems, moving right means moving down. + bool fMovingDown = (fMovingRight != m_pseg->m_fWsRtl); + + // Get the coordinates for all the ligatures. + + std::vector<float> vysTops; + std::vector<float> vysBottoms; + std::vector<float> vxsLefts; + std::vector<float> vxsRights; + + float ysTopCurr, ysBottomCurr, xsLeftCurr, xsRightCurr; + int icompCurr = -1; + if (fMovingRight) + ysTopCurr = ysBottomCurr = xsLeftCurr = xsRightCurr = kNegInfFloat; + else + ysTopCurr = ysBottomCurr = xsLeftCurr = xsRightCurr = kPosInfFloat; + + GrSlotOutput * pslout = m_pseg->OutputSlot(isloutLig); + //int iginfOutput = m_pseg->LogicalToPhysicalSurface(isloutLig); + + int icomp; + for (icomp = 0; icomp < pslout->NumberOfComponents(); icomp++) + { + float xsLeft, ysTop, xsRight, ysBottom; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + pgtbl->ComponentBoxLogUnits(m_pseg->m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_pseg->m_mFontEmUnits, m_pseg->m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom); + + vysTops.push_back(ysTop); + vysBottoms.push_back(ysBottom); + vxsLefts.push_back(xsLeft); + vxsRights.push_back(xsRight); + + // If we're moving, record this component as the one we're moving relative to. + if (fMove && icomp == m_pseg->m_prgiComponent[ichwSegOffset - m_pseg->m_ichwAssocsMin]) + { + icompCurr = icomp; + ysTopCurr = ysTop; + ysBottomCurr = ysBottom; + xsLeftCurr = xsLeft; + xsRightCurr = xsRight; + } + } + + float xsHorizCurr = (fIPOnRight) ? xsRightCurr : xsLeftCurr; + float xsHorizNext; + + // Find the first ligature that is in the proper direction. + int icompNext; + for (icompNext = 0; icompNext < pslout->NumberOfComponents(); icompNext++) + { + if (icompNext != icompCurr) + { + float xsHorizNext = (fIPOnRight) ? vxsRights[icompNext] : vxsLefts[icompNext]; + if (fMovingRight && xsHorizNext > xsHorizCurr) + break; + else if (!fMovingRight && xsHorizNext < xsHorizCurr) + break; + } + } + if (icompNext == pslout->NumberOfComponents()) + { + bool fResult; + // No component in the proper direction. Can we switch to the opposite edge of the + // current component? + if (fMovingRight && !fIPOnRight) + { + if (fGlyphRtl) + --(*pichw); + else + ++(*pichw); + *pfAssocPrev = !*pfAssocPrev; + fResult = true; + } + else if (!fMovingRight && fIPOnRight) + { + if (fGlyphRtl) + ++(*pichw); + else + --(*pichw); + + *pfAssocPrev = !*pfAssocPrev; + fResult = true; + } + else + fResult = false; + return fResult; + } + + xsHorizNext = (fIPOnRight) ? vxsRights[icompNext] : vxsLefts[icompNext]; + + // Now find the closest one in the proper direction. + for (icomp = icompNext + 1; icomp < pslout->NumberOfComponents(); icomp++) + { + if (icomp != icompCurr) + { + float xsHorizTry = (fIPOnRight) ? vxsRights[icomp] : vxsLefts[icomp]; + + if (fMovingRight && xsHorizTry > xsHorizCurr && + (xsHorizTry < xsHorizNext || + (xsHorizTry == xsHorizNext && + (vysTops[icomp] > vysTops[icompNext]) == fMovingDown))) + { + icompNext = icomp; + xsHorizNext = xsHorizTry; + } + else if (!fMovingRight && xsHorizTry < xsHorizCurr && + (xsHorizTry > xsHorizNext || + (xsHorizTry == xsHorizNext && + (vysTops[icomp] > vysTops[icompNext]) == fMovingDown))) + { + icompNext = icomp; + xsHorizNext = xsHorizTry; + } + } + } + + *pichw = pslout->FirstUnderlyingComponent(icompNext) + m_pseg->m_ichwMin; // TODO: handle multiple components per ligature + if (*pfAssocPrev) + ++(*pichw); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Scale the x-coordinate by the scaling factors for this painter. +----------------------------------------------------------------------------------------------*/ +float SegmentPainter::ScaleX(float xs, Rect rs, Rect rd) +{ + //return rs.MapXTo(xs, rd); + + float dxs = rs.right - rs.left; + float dxd = rd.right - rd.left; + Assert(dxs > 0); + if (dxs == dxd) + return xs + rd.left - rs.left; + + float xd = rd.left + GrEngine::GrFloatMulDiv((xs - rs.left), dxd, dxs); + return xd; +} + +/*---------------------------------------------------------------------------------------------- + Scale the y-coordinate by the scaling factors for this painter. +----------------------------------------------------------------------------------------------*/ +float SegmentPainter::ScaleY(float ys, Rect rs, Rect rd) +{ + //return rs.MapYTo(ys, rd); + + float dys = rs.bottom - rs.top; + float dyd = rd.bottom - rd.top; + Assert(dys > 0); + if (dys == dyd) + return ys + rd.top - rs.top; + float yd = rd.top + GrEngine::GrFloatMulDiv((ys - rs.top), dyd, dys); + return yd; +} + + +//:>******************************************************************************************** +//:> Low-level platform-specific drawing methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Make sure the font is set to use the character properties required by this segment. +----------------------------------------------------------------------------------------------*/ +void SegmentPainter::SetFontProps(unsigned long clrFore, unsigned long clrBack) +{ + return; +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/FileInput.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/FileInput.cpp new file mode 100644 index 00000000000..69be6b408c3 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/FileInput.cpp @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FileInput.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Contains the functions for reading from a TT file. (These are functions, not methods + associated with a class.) +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** + +#include "GrCommon.h" +#include "GrData.h" +#ifndef _WIN32 +#include "GrMstypes.h" +#endif +#include "GrDebug.h" +#include <fstream> +#include <iostream> +#include <string> +#include "FileInput.h" +#include "GrResult.h" + +//#ifndef _MSC_VER +//#include "config.h" +//#endif + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods of GrBufferIStream +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Constructor. +----------------------------------------------------------------------------------------------*/ +GrBufferIStream::GrBufferIStream() +{ + m_pbStart = NULL; + m_pbNext = NULL; + m_pbLim = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Destructor. +----------------------------------------------------------------------------------------------*/ +GrBufferIStream::~GrBufferIStream() +{ + Close(); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the stream. +----------------------------------------------------------------------------------------------*/ +#ifdef GR_FW +bool GrBufferIStream::Open(std::wstring stuFileName, int kMode) +#else +bool GrBufferIStream::Open(const char * pcFileName, std::ios::openmode kMode) +#endif +{ + Assert(false); // use OpenBuffer + return false; +} + +/*---------------------------------------------------------------------------------------------- + Initialize the stream to a buffer. +----------------------------------------------------------------------------------------------*/ +bool GrBufferIStream::OpenBuffer(const byte * pbBuffer, int cb) +{ + Assert(m_pbStart == NULL); + Assert(m_pbNext == NULL); + Assert(m_pbLim == NULL); + + m_pbStart = pbBuffer; + m_pbNext = pbBuffer; + if (cb > 0) + m_pbLim = m_pbStart + cb; + // otherwise we don't know the length + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Close the stream. +----------------------------------------------------------------------------------------------*/ +void GrBufferIStream::Close() +{ + m_pbStart = NULL; + m_pbNext = NULL; + m_pbLim = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Read a byte from the stream. +----------------------------------------------------------------------------------------------*/ +byte GrBufferIStream::ReadByteFromFont() +{ + byte bInput = *m_pbNext; + m_pbNext += isizeof(byte); + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); + return bInput; +} + +/*---------------------------------------------------------------------------------------------- + Read a short (signed 16-bit) word from the stream. Switch the bytes from big-endian + to little-endian format. +----------------------------------------------------------------------------------------------*/ +short GrBufferIStream::ReadShortFromFont() +{ + short snInput = *(const short *)m_pbNext; + m_pbNext += isizeof(short); + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); + snInput = lsbf(snInput); + return snInput; +} + +/*---------------------------------------------------------------------------------------------- + Read a wide character (unsigned 16-bit word) from the stream. + Switch the bytes from big-endian to little-endian format. +----------------------------------------------------------------------------------------------*/ +utf16 GrBufferIStream::ReadUShortFromFont() +{ + utf16 chwInput = *(const utf16 *)m_pbNext; + m_pbNext += isizeof(utf16); + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); + chwInput = lsbf(chwInput); + return chwInput; +} + +/*---------------------------------------------------------------------------------------------- + Read a standard (32-bit) word from the stream. Switch the bytes from big-endian + to little-endian format. +----------------------------------------------------------------------------------------------*/ +int GrBufferIStream::ReadIntFromFont() +{ + int nInput = *(const int *)m_pbNext; + m_pbNext += isizeof(int); + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); + nInput = lsbf(nInput); + return nInput; +} + +/*---------------------------------------------------------------------------------------------- + Read a block of data from the stream. DON'T switch the bytes from big-endian + to little-endian format. +----------------------------------------------------------------------------------------------*/ +void GrBufferIStream::ReadBlockFromFont(void * pvInput, int cb) +{ + std::copy(m_pbNext, m_pbNext + cb, reinterpret_cast<byte*>(pvInput)); + m_pbNext += cb; + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); +} + +/*---------------------------------------------------------------------------------------------- + Get the absolute position of the font-file stream (relative to the beginning of + the file). For buffers, we just return the byte position in the buffer. +----------------------------------------------------------------------------------------------*/ +void GrBufferIStream::GetPositionInFont(long * plPos) +{ + *plPos = (m_pbNext - m_pbStart); +} + +/*---------------------------------------------------------------------------------------------- + Set the position of the font-file stream to the given absolute position (relative + to the beginning of the file). For buffers, assume the position is relative to the + beginning of the buffer. +----------------------------------------------------------------------------------------------*/ +void GrBufferIStream::SetPositionInFont(long lPos) +{ + m_pbNext = m_pbStart + lPos; + if (m_pbLim && m_pbNext > m_pbLim) + THROW(kresReadFault); +} + + +//:>******************************************************************************************** +//:> Swap byte order. +//:>******************************************************************************************** +int swapb(int nArg) +{ +#if WORDS_BIGENDIAN +return nArg; +#else + int b1, b2, b3, b4; + b1 = ((nArg & 0xFF000000) >> 24) & 0x000000FF; // remove sign extension + b2 = ((nArg & 0x00FF0000) >> 8); // & 0x0000FF00; + b3 = ((nArg & 0x0000FF00) << 8); // & 0x00FF0000; + b4 = ((nArg & 0x000000FF) << 24); // & 0xFF000000; + int nRet = b1 | b2 | b3 | b4; + return nRet; +#endif +} + +unsigned int swapb(unsigned int nArg) +{ +#if WORDS_BIGENDIAN +return nArg; +#else + int b1, b2, b3, b4; + b1 = ((nArg & 0xFF000000) >> 24) & 0x000000FF; // remove sign extension + b2 = ((nArg & 0x00FF0000) >> 8); // & 0x0000FF00; + b3 = ((nArg & 0x0000FF00) << 8); // & 0x00FF0000; + b4 = ((nArg & 0x000000FF) << 24); // & 0xFF000000; + int nRet = b1 | b2 | b3 | b4; + return nRet; +#endif +} + +utf16 swapb(utf16 chwArg) +{ +#if WORDS_BIGENDIAN +return chwArg; +#else + utf16 b1, b2; + b1 = ((chwArg & 0xFF00) >> 8) & 0x00FF; // remove sign extension + b2 = ((chwArg & 0x00FF) << 8); // & 0xFF00; + utf16 chwRet = b1 | b2; + return chwRet; +#endif +} + +short swapb(short snArg) +{ +#if WORDS_BIGENDIAN +return snArg; +#else + short b1, b2; + b1 = ((snArg & 0xFF00) >> 8) & 0x00FF; // remove sign extension + b2 = ((snArg & 0x00FF) << 8); // & 0xFF00; + short snRet = b1 | b2; + return snRet; +#endif +} + +} //namespace gr + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/FileInput.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/FileInput.h new file mode 100644 index 00000000000..022206b06a4 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/FileInput.h @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FileInput.h +Responsibility: Sharon Correll +Last reviewed: not yet + +Description: + Defines utility functions for reading from a font file. +----------------------------------------------------------------------------------------------*/ + +#ifdef _MSC_VER +#pragma once +#endif +#ifndef FILEINPUT_INCLUDED +#define FILEINPUT_INCLUDED + +//:End Ignore + +namespace gr +{ + +int swapb(int nArg); +unsigned int swapb(unsigned int nArg); +utf16 swapb(utf16 chwArg); +short swapb(short snArg); + +// Most significant byte first (converting from least-sig-first): +inline int msbf(int nArg) { return swapb(nArg); } +inline unsigned int msbf(unsigned int nArg) { return swapb(nArg); } +inline utf16 msbf(utf16 chwArg) { return swapb(chwArg); } +inline short msbf(short chwArg) { return swapb(chwArg); } + +// Least significant byte first (converting from most-sig first): +inline int lsbf(int nArg) { return swapb(nArg); } +inline unsigned int lsbf(unsigned int nArg) { return swapb(nArg); }; +inline utf16 lsbf(utf16 chwArg) { return swapb(chwArg); } +inline short lsbf(short chwArg) { return swapb(chwArg); } + +class GrIStream +{ +public: + virtual void Close() = 0; + + virtual byte ReadByteFromFont() = 0; + virtual short ReadShortFromFont() = 0; + virtual utf16 ReadUShortFromFont() = 0; + virtual int ReadIntFromFont() = 0; + virtual void ReadBlockFromFont(void * pvInput, int cb) = 0; + + virtual void GetPositionInFont(long * plPos) = 0; + virtual void SetPositionInFont(long lPos) = 0; + + virtual bool OpenBuffer(const byte * pbBuffer, int cb) = 0; + virtual void CloseBuffer() = 0; + +protected: + virtual ~GrIStream() {} +}; + + +/*---------------------------------------------------------------------------------------------- + A stream that reads from a buffer rather than a file. +----------------------------------------------------------------------------------------------*/ +class GrBufferIStream : public GrIStream +{ +public: + GrBufferIStream(); + ~GrBufferIStream(); + + #ifdef GR_FW + virtual bool Open(std::wstring stuFileName, std::ios::openmode kMode); + #else + virtual bool Open(const char * pcFileName, std::ios::openmode kMode); + #endif + virtual void Close(); + + virtual byte ReadByteFromFont(); + virtual short ReadShortFromFont(); + virtual utf16 ReadUShortFromFont(); + virtual int ReadIntFromFont(); + virtual void ReadBlockFromFont(void * pvInput, int cb); + + virtual void GetPositionInFont(long * plPos); + virtual void SetPositionInFont(long lPos); + + virtual bool OpenBuffer(const byte * pbBuffer, int cb); + virtual void CloseBuffer() + { + Close(); + } + +protected: + const byte * m_pbStart; + const byte * m_pbNext; + const byte * m_pbLim; +}; + +} // namespace gr + +#endif // !FILEINPUT_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/FontCache.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/FontCache.cpp new file mode 100644 index 00000000000..f0540463e3b --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/FontCache.cpp @@ -0,0 +1,308 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FontCache.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + A cache of all the font-face objects. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + Store the given font-face in the cache. Overwrite whatever was there before. + Return NULL if there is nothing appropriate there. +----------------------------------------------------------------------------------------------*/ +void FontCache::GetFontFace(std::wstring strFaceName, bool fBold, bool fItalic, + FontFace ** ppfface) +{ + int ifci = FindCacheItem(strFaceName); + if (ifci < 0) // no fonts of that family present + { + *ppfface = NULL; + return; + } + + CacheItem * pfci = m_prgfci + ifci; + if (fBold) + { + if (fItalic) + *ppfface = pfci->pffaceBI; + else + *ppfface = pfci->pffaceBold; + } + else + { + if (fItalic) + *ppfface = pfci->pffaceItalic; + else + *ppfface = pfci->pffaceRegular; + } +} + +/*---------------------------------------------------------------------------------------------- + Store the given font-face in the cache. Overwrite whatever was there before. +----------------------------------------------------------------------------------------------*/ +void FontCache::CacheFontFace(std::wstring strFaceName, bool fBold, bool fItalic, + FontFace * pfface) +{ + if (m_prgfci == NULL) + Initialize(); + + int ifciIns = FindCacheItem(strFaceName); + int ifci = ifciIns; + if (ifciIns < 0) + { + ifci = (ifciIns + 1) * -1; + InsertCacheItem(ifci); + std::copy(strFaceName.c_str(), strFaceName.c_str() + + (strFaceName.size() + 1), m_prgfci[ifci].szFaceName); + } + + CacheItem * pfci = m_prgfci + ifci; + + bool fPrevNull; + if (fBold) + { + if (fItalic) + { + fPrevNull = (pfci->pffaceBI == NULL); + pfci->pffaceBI = pfface; + } + else + { + fPrevNull = (pfci->pffaceBold == NULL); + pfci->pffaceBold = pfface; + } + } + else + { + if (fItalic) + { + fPrevNull = (pfci->pffaceItalic == NULL); + pfci->pffaceItalic = pfface; + } + else + { + fPrevNull = (pfci->pffaceRegular == NULL); + pfci->pffaceRegular = pfface; + } + } + + if (fPrevNull && (pfface != NULL)) + m_cfface++; +} + +/*---------------------------------------------------------------------------------------------- + Remove the given font-face from the cache. Return false if it couldn't be found. +----------------------------------------------------------------------------------------------*/ +bool FontCache::RemoveFontFace(std::wstring strFaceName, bool fBold, bool fItalic, + bool fZapCache) +{ + int ifci = FindCacheItem(strFaceName); + if (ifci < 0) + { + return false; + } + + bool fPrevVal; + CacheItem * pfci = m_prgfci + ifci; + if (fBold) + { + if (fItalic) + { + fPrevVal = (pfci->pffaceBI != NULL); + pfci->pffaceBI = NULL; + } + else + { + fPrevVal = (pfci->pffaceBold != NULL); + pfci->pffaceBold = NULL; + } + } + else + { + if (fItalic) + { + fPrevVal = (pfci->pffaceItalic != NULL); + pfci->pffaceItalic = NULL; + } + else + { + fPrevVal = (pfci->pffaceRegular != NULL); + pfci->pffaceRegular = NULL; + } + } + + if (fPrevVal) + m_cfface--; + + Assert(m_cfface >= 0); + + if (m_flush == kflushAuto && fZapCache) + { + DeleteIfEmpty(); + } + + return fPrevVal; +} + +/*---------------------------------------------------------------------------------------------- + Search for the font-family in the cache and return its index. + If not present, return a negative number indicating where it was expected. +----------------------------------------------------------------------------------------------*/ +int FontCache::FindCacheItem(std::wstring strFaceName) +{ + if (m_cfci == 0) + return -1; + + Assert(m_prgfci); + + // Use a binary-chop search. + + int ifciLow = 0; + int ifciHigh = m_cfci; + while (true) + { + int ifciMid = (ifciHigh + ifciLow) >> 1; // divide by 2 + CacheItem * pfci = m_prgfci + ifciMid; + int tst = wcscmp(strFaceName.c_str(), (const wchar_t *)pfci->szFaceName); + if (tst == 0) + return ifciMid; // found it + if (ifciLow + 1 == ifciHigh) + { + // not there; return where expected: 0 -> -1, 1 -> -2 + Assert(ifciMid == ifciLow); + if (tst < 0) + return (ifciLow * -1) - 1; + else + return (ifciHigh * -1) - 1; + } + + // Keep looking. + if (tst < 0) + ifciHigh = ifciMid; + else + ifciLow = ifciMid; + } +} + +/*---------------------------------------------------------------------------------------------- + Insert space in the cache at location ifci. +----------------------------------------------------------------------------------------------*/ +void FontCache::InsertCacheItem(int ifci) +{ + if (m_cfci == m_cfciMax) + { + // Cache is full; double the space. + CacheItem * m_prgfciOld = m_prgfci; + m_prgfci = new CacheItem[m_cfciMax * 2]; + std::copy(m_prgfciOld, m_prgfciOld + m_cfciMax, m_prgfci); + delete[] m_prgfciOld; + m_cfciMax *= 2; + } + + // This copy involves overlapping ranges, so we need copy_backward not copy + // to satisfy the preconditions + std::copy_backward(m_prgfci + ifci, m_prgfci + m_cfci, m_prgfci + m_cfci + 1); + m_cfci++; + + // Initialize inserted item. + CacheItem * pfci = m_prgfci + ifci; + pfci->pffaceRegular = NULL; + pfci->pffaceBold = NULL; + pfci->pffaceItalic = NULL; + pfci->pffaceBI = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Delete the cache if it is empty. +----------------------------------------------------------------------------------------------*/ +void FontCache::DeleteIfEmpty() +{ + if (m_cfface <= 0) + // All items are NULL; delete this cache. Probably this is because + // the program is exiting. + FontFace::ZapFontCache(); +} + +/*---------------------------------------------------------------------------------------------- + Check that the cache is indeed empty. +----------------------------------------------------------------------------------------------*/ +void FontCache::AssertEmpty() +{ +#ifdef _DEBUG + for (int ifci = 0; ifci < m_cfci; ifci++) + { + CacheItem * pfci = m_prgfci + ifci; + Assert(pfci->pffaceRegular == NULL); + Assert(pfci->pffaceBold == NULL); + Assert(pfci->pffaceItalic == NULL); + Assert(pfci->pffaceBI == NULL); + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Set the flush mode on the cache to indicate whether or not it should be deleted when + it becomes empty. +----------------------------------------------------------------------------------------------*/ +void FontCache::SetFlushMode(int flush) +{ + m_flush = flush; + + if (m_flush == kflushAuto) + { + // Delete any font faces that have no remaining corresponding fonts. + // Work backwards so as we remove items the loop still works. + for (int ifci = m_cfci; --ifci >= 0; ) + { + CacheItem * pfci = m_prgfci + ifci; + if (pfci->pffaceRegular && pfci->pffaceRegular->NoFonts()) + RemoveFontFace(pfci->szFaceName, false, false, false); + if (pfci->pffaceBold && pfci->pffaceBold->NoFonts()) + RemoveFontFace(pfci->szFaceName, true, false, false); + if (pfci->pffaceItalic && pfci->pffaceItalic->NoFonts()) + RemoveFontFace(pfci->szFaceName, false, true, false); + if (pfci->pffaceBI && pfci->pffaceBI->NoFonts()) + RemoveFontFace(pfci->szFaceName, true, true, false); + } + + if (m_cfface <= 0) + FontFace::ZapFontCache(); + } +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/FontCache.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/FontCache.h new file mode 100644 index 00000000000..56da5ab26b8 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/FontCache.h @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FontCache.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + A cache of all the font-face objects known to mankind. There is exactly one instance + of a FontCache. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef FONTCACHE_INCLUDED +#define FONTCACHE_INCLUDED + +//:End Ignore + +namespace gr +{ + +class FontFace; +class FontMemoryUsage; + +/*---------------------------------------------------------------------------------------------- + TODO: change from a sorted list to a hash table, if performance so requires. +----------------------------------------------------------------------------------------------*/ +class FontCache { + friend class FontMemoryUsage; + +public: + FontCache() + { + m_cfci = 0; + m_prgfci = NULL; + m_cfciMax = 0; + m_cfface = 0; + m_flush = kflushAuto; + } + + ~FontCache() + { + delete[] m_prgfci; + m_prgfci = NULL; + m_cfci = 0; + m_cfciMax = 0; + m_cfface = 0; + } + + void Initialize() + { + m_cfci = 0; + m_prgfci = new CacheItem[12]; + m_cfciMax = 12; + m_cfface = 0; + } + + struct CacheItem + { + wchar_t szFaceName[32]; // type should match std::wstring + FontFace * pffaceRegular; + FontFace * pffaceBold; + FontFace * pffaceItalic; + FontFace * pffaceBI; + }; + + void GetFontFace(std::wstring strFaceName, bool fBold, bool fItalic, FontFace ** ppfface); + void CacheFontFace(std::wstring strFaceName, bool fBold, bool fItalic, FontFace * pfface); + bool RemoveFontFace(std::wstring strFaceName, bool fBold, bool fItalic, bool fZapCache = true); + void DeleteIfEmpty(); + void AssertEmpty(); + + int GetFlushMode() + { + return m_flush; + } + void SetFlushMode(int); + + // Debugging: + //bool DbgCheckFontCache(); + + void calculateMemoryUsage(FontMemoryUsage & fmu); + +protected: + int FindCacheItem(std::wstring strFaceName); + void InsertCacheItem(int ifci); + +protected: + // member variables; + int m_cfci; // number of items (font-families) + int m_cfciMax; // amount of space available + int m_cfface; // number of font-faces + CacheItem * m_prgfci; + + int m_flush; +}; + +} // namespace gr + + +#endif // !FONTCACHE_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/FontFace.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/FontFace.cpp new file mode 100644 index 00000000000..8750f20c9b4 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/FontFace.cpp @@ -0,0 +1,598 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrEngine.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: Contains the implementation of the FontFace class. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#include <functional> +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +namespace gr +{ + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +FontCache * FontFace::s_pFontCache = 0; + +//:>******************************************************************************************** +//:> New interface +//:>******************************************************************************************** +/*---------------------------------------------------------------------------------------------- + Return the appropriate FontFace, initialized with Graphite tables. + Called from the font constructor. +----------------------------------------------------------------------------------------------*/ +FontFace * FontFace::GetFontFace(Font * pfont, + std::wstring strFaceName, bool fBold, bool fItalic, + bool fDumbFallback) +{ + if (s_pFontCache == NULL) + s_pFontCache = new FontCache; + + FontFace * pfface; + s_pFontCache->GetFontFace(strFaceName, fBold, fItalic, &pfface); + if (pfface) + return pfface; + + // Create a new font face. + pfface = new FontFace(); + pfface->InitFontFace(pfont, strFaceName, fBold, fItalic, fDumbFallback); + return pfface; +} + +/*---------------------------------------------------------------------------------------------- + Initialize the engine using the given Graphite font. +----------------------------------------------------------------------------------------------*/ +GrResult FontFace::InitFontFace(Font * pfont, + std::wstring stuFaceName, bool fBold, bool fItalic, + bool fDumbFallback) +{ + AssertPtrN(pfont); + + m_pgreng = new GrEngine; + m_pgreng->m_pfface = this; + + m_pgreng->m_nScriptTag = 0; // not currently used, but set it to something + + std::wstring stuFeatures; + + //stuFeatures = stuFeaturesArg; + + if (wcscmp(stuFaceName.c_str(), m_pgreng->m_stuFaceName.c_str()) != 0) + { + s_pFontCache->RemoveFontFace(m_pgreng->FaceName(), m_pgreng->Bold(), m_pgreng->Italic()); + + m_pgreng->DestroyEverything(); // nothing we've cached is safe or useful. + m_pgreng->m_stuFaceName = stuFaceName; // if this is buggy, using assign might fix it + } + + //GrResult res = kresFail; +//#ifdef GR_FW +// // Read from the control file indicated by the registry, if any. +// std::wstring stuFaceNameTmp; +// stuFaceNameTmp.assign(rgchFaceName, wcslen(rgchFaceName)); +// res = InitFromControlFile(pfont, stuFaceName.c_str(), fBold, fItalic); +//#endif // GR_FW + + // Read directly from the font. + m_pgreng->DestroyContents(); + GrResult res = kresUnexpected; + try { + res = m_pgreng->ReadFontTables(pfont, fItalic); + } + catch (...) + { + if (fDumbFallback && m_pgreng->DumbFallback() && !m_pgreng->BadFont()) + { + // If we have a basically good font but can't do Graphite, + // just go with empty tables, which should be set up. + res = m_pgreng->m_resFontRead; + } + else + { + if (m_cfonts == 0) + { + //s_pFontCache->DeleteIfEmpty(); + //Assert(s_pFontCache == 0); + delete this; // will also delete GrEngine + } + else + { + delete m_pgreng; + } + m_pgreng = NULL; + throw; // throw original exception + } + } + + m_pgreng->m_resFontValid = res; + + m_pgreng->m_fBold = fBold; + m_pgreng->m_fItalic = fItalic; + + s_pFontCache->CacheFontFace(m_pgreng->FaceName(), fBold, fItalic, this); + + return m_pgreng->m_resFontValid; +} + +/*---------------------------------------------------------------------------------------------- + Read the cmap and Graphite tables from the font that is selected into the graphics + device. + + @return A GrResult indicating whether we were successful in loading the Graphite font: + - kresOk means success + - kresFail means the Graphite font could not be found or the basic font tables + (head, name, cmap) could not be loaded + - kresUnexpected means the Graphite tables could not be loaded + - kresFalse means it is not a Graphite font at all (has no Silf table). +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::ReadFontTables(Font * pfont, bool fItalic) +{ + GrResult res = kresOk; + m_ferr = kferrOkay; + GrBufferIStream grstrm; + + FontException fexptn; + fexptn.version = -1; + fexptn.subVersion = -1; + + bool fOk = false; + + const void * pHeadTbl; const void * pSileTbl; const void * pSilfTbl; + const void * pFeatTbl; const void * pGlatTbl; const void * pGlocTbl; const void * pSillTbl; + //const void * pCmapTbl; const void * pNameTbl; + size_t cbHeadSz, cbSilfSz, cbFeatSz, cbGlatSz, cbGlocSz, cbSillSz; + //size_t cbCmapSz, cbSileSz, cbNameSz; + + m_fFakeItalic = false; + + bool fBasicTables = false; + bool fSilf = false; // does the font have some sort of Silf table? + int nCheckSum = 0; + + bool fBadBase = false, + fMismatchedBase = false, + fFontIsItalic = false; + + // First read the head table. This gives us the checksum that we are using as a + // unique identifer. If it is the same as the one stored, and things appear set up, + // don't reload the tables. + res = (pHeadTbl = pfont->getTable(TtfUtil::TableIdTag(ktiHead), &cbHeadSz)) ? kresOk : kresFail; + fOk = pHeadTbl && (cbHeadSz == 0 || TtfUtil::CheckTable(ktiHead, pHeadTbl, cbHeadSz)); + if (res == kresFail) + { + m_stuInitError = L"could not locate head table for Graphite rendering"; + m_ferr = kferrFindHeadTable; + goto LUnexpected; + } + if (!fOk) + { + m_stuInitError = L"could not read design units for Graphite rendering"; + m_ferr = kferrReadDesignUnits; + goto LUnexpected; + } + m_mFontEmUnits = TtfUtil::DesignUnits(pHeadTbl); + nCheckSum = TtfUtil::HeadTableCheckSum(pHeadTbl); + fFontIsItalic = TtfUtil::IsItalic(pHeadTbl); + + if (m_nFontCheckSum == nCheckSum && m_ptman) + return m_resFontRead; // already initialized in some form + + DestroyContents(); + + Assert(!m_ptman); + Assert(!m_pctbl); + Assert(!m_pgtbl); + Assert(!m_prgpsd); + m_prgpsd = NULL; + m_cpsd = 0; + + m_ptman = new GrTableManager(this); + + m_fFakeItalic = (fItalic && !fFontIsItalic); + + // Look for an Sile table. If there is one, this is an extension font containing only + // the Graphite tables. Read the base font name out of the Sile table, and use it to + // read the cmap. Then replace the original font name to read the Graphite tables. + + m_fUseSepBase = false; + m_stuBaseFaceName.erase(); + + // TODO: rework the handling of the Sile table. + pSileTbl = NULL; + //pSileTbl = pfont->getTable(TtfUtil::TableIdTag(ktiSile)); + //if (pSileTbl) + //{ + // pgg->get_FontCharProperties(&chrpOriginal); + // grstrm.OpenBuffer(pSileTbl, sile_tbl.size()); + // m_fUseSepBase = ReadSileTable(pgg, grstrm, 0, &m_mFontEmUnits, &fMismatchedBase); + // grstrm.Close(); + + // if (!m_fUseSepBase) + // { + // SwitchGraphicsFont(pgg, false); // back to Graphite table font + // m_stuBaseFaceName.erase(); + // fBadBase = true; + // m_fUseSepBase = false; + // } + // // Otherwise leave the GrGraphics in a state to read from the base font. + //} + + // We don't need the offset table, and there's no way to get it anyway + // without a font file. + + // cmap and name + Assert(!m_fCmapTblCopy); + Assert(!m_fNameTblCopy); + fOk = SetCmapAndNameTables(pfont); + if (!fOk) + { + goto LUnexpected; + } + + fBasicTables = true; + + // If we have a bad base file, don't do Graphite stuff. + if (fBadBase || fMismatchedBase) + goto LUnexpected; + + /**** + Obtain font name from InitNew() now instead of reading from font file. InitNew should + should have a correct font name passed to it since it should come from a font registered + by GrFontInst.exe. This commented code could be use to verify name in font file matches. + NOTE: if we ever use this code again, make sure we're using the base font name table, + not the Graphite wrapper font name table. + // find the font family name + if (!TtfUtil::Get31EngFamilyInfo(vbName.Begin(), lnNameOff, lnNameSz)) + { // use Name table which is Symbol encode instead + // this could cause problems if a real Symbol writing system is used in the name table + // however normally real Unicode values are used instead a Symbol writing system + if (!TtfUtil::Get30EngFamilyInfo(vbName.Begin(), lnNameOff, lnNameSz)) + { + ReturnResult(kresFail); + } + // test for Symbol writing system. first byte of Unicode id should be 0xF0 + if (vbName[lnNameOff + 1] == (unsigned char)0xF0) // 1 - Unicode id is big endian + ReturnResult(kresFail); + } + if (!TtfUtil::SwapWString(vbName.Begin() + lnNameOff, lnNameSz / isizeof(utf16))) + ReturnResult(kresFail); + + m_stuFaceName = std::wstring((utf16 *)(vbName.begin() + lnNameOff), lnNameSz / isizeof(utf16)); + ****/ + + // Silf + res = (pSilfTbl = pfont->getTable(TtfUtil::TableIdTag(ktiSilf), &cbSilfSz)) ? kresOk : kresFail; + fOk = pSilfTbl && (cbSilfSz == 0 || TtfUtil::CheckTable(ktiSilf, pSilfTbl, cbSilfSz)); + if (!fOk) + { + m_stuInitError = L"could not load Silf table for Graphite rendering"; + m_ferr = kferrLoadSilfTable; + goto LUnexpected; + } + + // Feat + res = (pFeatTbl = pfont->getTable(TtfUtil::TableIdTag(ktiFeat), &cbFeatSz)) ? kresOk : kresFail; + fOk = pFeatTbl && (cbFeatSz == 0 || TtfUtil::CheckTable(ktiFeat, pFeatTbl, cbFeatSz)); + if (!fOk) + { + // TODO: just create an empty set of features, since this is not disastrous. + m_stuInitError = L"could not load Feat table for Graphite rendering"; + m_ferr = kferrLoadFeatTable; + goto LUnexpected; + } + + // Glat + res = (pGlatTbl = pfont->getTable(TtfUtil::TableIdTag(ktiGlat), &cbGlatSz)) ? kresOk : kresFail; + fOk = pGlatTbl && (cbGlatSz == 0 || TtfUtil::CheckTable(ktiGlat, pGlatTbl, cbGlatSz)); + if (!fOk) + { + m_stuInitError = L"could not load Glat table for Graphite rendering"; + m_ferr = kferrLoadGlatTable; + goto LUnexpected; + } + + // Gloc + res = (pGlocTbl = pfont->getTable(TtfUtil::TableIdTag(ktiGloc), &cbGlocSz)) ? kresOk : kresFail; + fOk = pGlocTbl && (cbGlocSz == 0 || TtfUtil::CheckTable(ktiGloc, pGlocTbl, cbGlocSz)); + if (!fOk) + { + m_stuInitError = L"could not load Gloc table for Graphite rendering"; + m_ferr = kferrLoadGlocTable; + goto LUnexpected; + } + + // Sill + try { + res = (pSillTbl = pfont->getTable(TtfUtil::TableIdTag(ktiSill), &cbSillSz)) ? kresOk : kresFail; + fOk = pSillTbl && (cbSillSz == 0 || TtfUtil::CheckTable(ktiSill, pSillTbl, cbSillSz)); + } + catch (...) + { + fOk = true; + pSillTbl = NULL; + } + // if table couldn't be loaded, this is not disastrous. + +// ibGlocStart = cbGlatTbl; + fOk = CheckTableVersions(&grstrm, + (const byte *)pSilfTbl, 0, + (const byte *)pGlocTbl, 0, + (const byte *)pFeatTbl, 0, + &m_fxdBadVersion); + if (!fOk) + { +// wchar_t rgch1[50]; +// wchar_t rgch2[50]; +//#if defined(_WIN32) +// // This version does not work in Windows, in spite of being documented: +// // swprintf(rgch, 50, L"%d", (m_fxdBadVersion >> 16)); +// // Removing the size argument make it work. +// swprintf(rgch1, L"%d", (m_fxdBadVersion >> 16)); +// swprintf(rgch2, L"%d", (m_fxdBadVersion & 0x0000FFFF)); +//#else +// swprintf(rgch1, 50, L"%d", (m_fxdBadVersion >> 16)); +// swprintf(rgch2, 50, L"%d", (m_fxdBadVersion & 0x0000FFFF)); +//#endif + char rgch[50]; // more than enough space to print two 16-bit ints + char *pch = &rgch[0]; + sprintf(rgch, "%d.%d", (m_fxdBadVersion >> 16), (m_fxdBadVersion & 0x0000FFFF)); + std::wstring stu = L"unsupported version ("; + //stu.append(rgch1); + //stu.append(L"."); + //stu.append(rgch2); + while (*pch != 0) + stu.push_back((wchar_t)*pch++); + stu.append(L") of Graphite tables"); + m_stuInitError.assign(stu.c_str()); + m_ferr = kferrBadVersion; + goto LUnexpected; + } + + try + { + // Parse the "Silf" table. + grstrm.OpenBuffer((const byte*)pSilfTbl, cbSilfSz); + int chwGlyphIDMax, fxdVersion; + bool f = ReadSilfTable(grstrm, 0, 0, &chwGlyphIDMax, &fxdVersion); + grstrm.Close(); + if (!f) + { + m_ferr = kferrReadSilfTable; + fexptn.errorCode = m_ferr; + throw fexptn; + } + + // Parse the "Gloc" and "Glat" tables. + { + GrBufferIStream grstrmGlat; + + grstrm.OpenBuffer((const byte *)pGlocTbl, cbGlocSz); + grstrmGlat.OpenBuffer((const byte *)pGlatTbl, cbGlatSz); + f = ReadGlocAndGlatTables(grstrm, 0, grstrmGlat, 0, chwGlyphIDMax, fxdVersion); + grstrm.Close(); + grstrmGlat.Close(); + if (!f) + { + m_ferr = kferrReadGlocGlatTable; + fexptn.errorCode = m_ferr; + throw fexptn; + } + } + + // Parse the "Feat" table. + grstrm.OpenBuffer((const byte *)pFeatTbl, cbFeatSz); + f = ReadFeatTable(grstrm, 0); + grstrm.Close(); + if (!f) + { + m_ferr = kferrReadFeatTable; + fexptn.errorCode = m_ferr; + throw fexptn; + } + + // Parse the "Sill" table. + if (pSillTbl) + { + grstrm.OpenBuffer((const byte *)pSillTbl, cbFeatSz); + f = ReadSillTable(grstrm, 0); + grstrm.Close(); + if (!f) + { + m_ferr = kferrReadSillTable; + fexptn.errorCode = m_ferr; + throw fexptn; + } + } + else + m_langtbl.CreateEmpty(); + } + catch (...) + { + fSilf = false; + m_resFontRead = kresUnexpected; + try { + DestroyContents(false); + } + catch (...) + {} + goto LUnexpected; + } + + m_stuErrCtrlFile.erase(); + m_nFontCheckSum = nCheckSum; + m_resFontRead = kresOk; + m_ferr = kferrOkay; + ReturnResult(kresOk); + +LUnexpected: + // Don't do this, because it is possible to use a base font with an empty Graphite + // engine: + //if (m_fUseSepBase || m_stuBaseFaceName.Length() > 0) + //{ + // SwitchGraphicsFont(pgg, false); // back to Graphite table font + // m_stuBaseFaceName.erase(); + // m_fUseSepBase = false; + //} + + CreateEmpty(); + m_nFontCheckSum = nCheckSum; + if (!fBasicTables) + m_resFontRead = kresFail; // bad font + else if (!fSilf) + m_resFontRead = kresFalse; // no Silf table--not a Graphite font + else + m_resFontRead = kresUnexpected; // couldn't read the Graphite tables + + fexptn.errorCode = m_ferr; + fexptn.version = m_fxdBadVersion >> 16; + fexptn.subVersion = m_fxdBadVersion & 0x0000FFFF; + throw fexptn; + + ReturnResult(m_resFontRead); +} + +/*---------------------------------------------------------------------------------------------- + Read the cmap and name tables. + + This is called from two places. One is when we first initialize the engine. Also, if the + font tables have not been actually copied from the font, they may have been deleted + when the font was deleted. So when reusing the engine, set them to something valid based + on the current font. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::SetCmapAndNameTables(Font * pfont) +{ + GrResult res = kresOk; + bool fOk; + const void * pCmapTbl; + const void * pNameTbl; + size_t cbCmapSz, cbNameSz; + + // cmap + if (!m_fCmapTblCopy) + { + res = (pCmapTbl = pfont->getTable(TtfUtil::TableIdTag(ktiCmap), &cbCmapSz)) ? kresOk : kresFail; + fOk = pCmapTbl && (cbCmapSz == 0 || TtfUtil::CheckTable(ktiCmap, pCmapTbl, cbCmapSz)); + if (!fOk) + { + m_stuInitError = L"could not locate cmap table"; + m_ferr = kferrFindCmapTable; + return false; + } + + if (pCmapTbl && cbCmapSz > 0) + { + // Make a private copy of the cmap for the engine's use. + m_pCmapTbl = new byte[cbCmapSz]; + std::copy(reinterpret_cast<const byte*>(pCmapTbl), + reinterpret_cast<const byte*>(pCmapTbl) + cbCmapSz, m_pCmapTbl); + m_fCmapTblCopy = true; + m_cbCmapTbl = cbCmapSz; + } + else + { + m_pCmapTbl = const_cast<byte*>(reinterpret_cast<const byte*>(pCmapTbl)); + m_fCmapTblCopy = false; + } + + // MS Unicode cmap + m_pCmap_3_1 = TtfUtil::FindCmapSubtable(m_pCmapTbl, 3, 1); + m_pCmap_3_10 = TtfUtil::FindCmapSubtable(m_pCmapTbl, 3, 10); + if (!m_pCmap_3_1) + m_pCmap_3_1 = TtfUtil::FindCmapSubtable(m_pCmapTbl, 3, 0); + if (!m_pCmap_3_1) + { + m_stuInitError = L"failure to load cmap subtable"; + m_ferr = kferrLoadCmapSubtable; + return false; + } + if (!TtfUtil::CheckCmap31Subtable(m_pCmap_3_1)) + { + m_stuInitError = L"checking cmap subtable failed"; + m_ferr = kferrCheckCmapSubtable; + return false; + } + } + else + { + Assert(m_pCmapTbl); + } + + // name table - eventually need feature label strings + + // Currently the only stuff we're getting from the name table are our feature names, + // so use the version from the Graphite font (not the base font if any). + //////if (m_fUseSepBase) + ////// pgg->SetupGraphics(&chrpOriginal); + + if (!m_fNameTblCopy) + { + res = (pNameTbl = (const byte *)pfont->getTable(TtfUtil::TableIdTag(ktiName), &cbNameSz)) ? kresOk : kresFail; + fOk = pNameTbl && (cbNameSz == 0 || TtfUtil::CheckTable(ktiName, pNameTbl, cbNameSz)); + if (!fOk) + { + m_stuInitError = L"could not locate name table"; + m_ferr = kferrFindNameTable; + return false; + } + + if (pNameTbl && cbNameSz > 0) + { + // Make a private copy of the name table for the engine's use. + m_pNameTbl = new byte[cbNameSz]; + std::copy(reinterpret_cast<const byte*>(pNameTbl), + reinterpret_cast<const byte*>(pNameTbl) + cbNameSz, m_pNameTbl); + m_fNameTblCopy = true; + m_cbNameTbl = cbNameSz; + } + else + { + m_pNameTbl = const_cast<byte*>(reinterpret_cast<const byte*>(pNameTbl)); + m_fNameTblCopy = false; + } + } + else + { + Assert(m_pNameTbl); + } + + return true; +} + + +//void FontFace::DbgCheckFontFace() +//{ +// Assert(m_cfonts < 5000); +// Assert(m_cfonts >= 0); +// wchar_t chw0 = m_pgreng->m_stuFaceName[0]; +// Assert(chw0 >= 0x0040); // A +// Assert(chw0 <= 0x007A); // z +//} + +} // namespace gr + +//:End Ignore diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/FontFace.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/FontFace.h new file mode 100644 index 00000000000..7f2684a5908 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/FontFace.h @@ -0,0 +1,246 @@ +/*-------------------------------------------------------------------- +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: FontFace.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Contains the definition of the FontFace class. + TODO: Merge with GrEngine. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef FONTFACE_INCLUDED +#define FONTFACE_INCLUDED +#include <limits> + +#ifdef _MSC_VER +#include <crtdbg.h> +#endif + +namespace gr +{ + +class IGrJustifier; +class Segment; + + +/*---------------------------------------------------------------------------------------------- + The GrEngine serves as the top level object that knows how to run Graphite tables + and generate Graphite segments. + + Primarily, this class implements IRenderEngine, which allows it to serve as a FW + rendering engine. It also implements ISimpleInit, a general interface for initializing + using a string. Finally, it implements ITraceControl, a very simple interface which + allows a client to flip a flag indicating whether or not we want to output a log of + the Graphite transduction process. + + Hungarian: greng +----------------------------------------------------------------------------------------------*/ +class FontFace +{ + friend class gr::FontMemoryUsage; + +public: + FontFace() + { + m_cfonts = 0; + } + + ~FontFace() + { + if (s_pFontCache) + s_pFontCache->RemoveFontFace(m_pgreng->FaceName(), m_pgreng->Bold(), m_pgreng->Italic()); + delete m_pgreng; + } + + void IncFontCount() + { + m_cfonts++; + } + void DecFontCount() + { + m_cfonts--; + if (m_cfonts <= 0 && (s_pFontCache == NULL || s_pFontCache->GetFlushMode() == kflushAuto)) + delete this; + } + + bool NoFonts() + { + return (m_cfonts <= 0); + } + + static FontFace * GetFontFace(Font * pfont, + std::wstring strFaceName, bool fBold, bool fItalic, + bool fDumbFallback = false); + + GrResult InitFontFace(Font * pfont, + std::wstring stuFaceName, bool fBold, bool fItalic, + bool fDumbLayout); + + FontErrorCode IsValidForGraphite(int * pnVersion, int * pnSubVersion) + { + return m_pgreng->IsValidForGraphite(pnVersion, pnSubVersion); + } + + static void ZapFontCache() + { + if (s_pFontCache) + { + s_pFontCache->AssertEmpty(); + delete s_pFontCache; + } + s_pFontCache = NULL; + } + + static void SetFlushMode(int flush) + { + if (s_pFontCache == NULL) + s_pFontCache = new FontCache; + s_pFontCache->SetFlushMode(flush); + } + static int GetFlushMode() + { + if (s_pFontCache == NULL) + s_pFontCache = new FontCache; + return s_pFontCache->GetFlushMode(); + } + + // Temporary, until interface gets thoroughly reworked: + GrEngine * GraphiteEngine() + { + return m_pgreng; + } + + // Feature access: + size_t NumberOfFeatures() + { + return m_pgreng->NumberOfFeatures_ff(); + } + featid FeatureID(size_t ifeat) + { + return m_pgreng->FeatureID_ff(ifeat); + } + size_t FeatureWithID(featid id) + { + return m_pgreng->FeatureWithID_ff(id); + } + bool GetFeatureLabel(size_t ifeat, lgid language, utf16 * label) + { + return m_pgreng->GetFeatureLabel_ff(ifeat, language, label); + } + int GetFeatureDefault(size_t ifeat) // index of default setting + { + return m_pgreng->GetFeatureDefault_ff(ifeat); + } + size_t NumberOfSettings(size_t ifeat) + { + return m_pgreng->NumberOfSettings_ff(ifeat); + } + int GetFeatureSettingValue(size_t ifeat, size_t ifset) + { + return m_pgreng->GetFeatureSettingValue_ff(ifeat, ifset); + } + bool GetFeatureSettingLabel(size_t ifeat, size_t ifset, lgid language, utf16 * label) + { + return m_pgreng->GetFeatureSettingLabel_ff(ifeat, ifset, language, label); + } + // Feature-label language access: + size_t NumberOfFeatLangs() + { + return m_pgreng->NumberOfFeatLangs_ff(); + } + short FeatLabelLang(int ilang) + { + return m_pgreng->GetFeatLabelLang_ff(ilang); + } + // Language access: + size_t NumberOfLanguages() + { + return m_pgreng->NumberOfLanguages_ff(); + } + isocode LanguageCode(size_t ilang) + { + return m_pgreng->GetLanguageCode_ff(ilang); + } + + // Script Direction access: + ScriptDirCode ScriptDirection() const throw() + { + unsigned int script_dirs = 0; + OLECHAR err_dummy = 0; + m_pgreng->get_ScriptDirection(&script_dirs, &err_dummy, 1); + return ScriptDirCode(script_dirs); + } + + bool BadFont(FontErrorCode * pferr = NULL) + { + return m_pgreng->BadFont(pferr); + } + bool DumbFallback(FontErrorCode * pferr = NULL) + { + return m_pgreng->DumbFallback(pferr); + } + +public: + // For use in segment creation: + void RenderLineFillSegment(Segment * pseg, Font * pfont, ITextSource * pts, + LayoutEnvironment & layout, + toffset ichStart, toffset ichStop, float xsMaxWidth, bool fBacktracking) + { + m_pgreng->MakeSegment(pseg, pfont, pts, NULL, layout, + ichStart, ichStop, xsMaxWidth, fBacktracking, false, 0, kestMoreLines); + } + void RenderRangeSegment(Segment * pseg, Font * pfont, + ITextSource * pts, LayoutEnvironment & layout, + toffset ichStart, toffset ichStop) + { + m_pgreng->MakeSegment(pseg, pfont, pts, NULL, layout, + ichStart, ichStop, kPosInfFloat, false, false, 0, kestMoreLines); + } + void RenderJustifiedSegment(Segment * pseg, Font * pfont, + ITextSource * pts, LayoutEnvironment & layout, + toffset ichStart, toffset ichStop, float xsCurrentWidth, float xsDesiredWidth) + { + m_pgreng->MakeSegment(pseg, pfont, pts, NULL, layout, + ichStart, ichStop, xsCurrentWidth, false, true, xsDesiredWidth, kestMoreLines); + } + + // Debugging. + //static bool DbgCheckFontCache() + //{ + // if (s_pFontCache) + // return s_pFontCache->DbgCheckFontCache(); + // else + // return true; + //} + //void DbgCheckFontFace(); + + static void calculateAllMemoryUsage(FontMemoryUsage & fmu); + void calculateMemoryUsage(FontMemoryUsage & fmu); + +protected: + // Number of fonts in existence that use this face; when it goes to zero, delete. + int m_cfonts; + + // Static variable: + static FontCache * s_pFontCache; + + // Member variables: + GrEngine * m_pgreng; +}; + +} // namespace gr + + +#if defined(GR_NO_NAMESPACE) +using namespace gr; +#endif + + +#endif // !FONTFACE_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrCharStream.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrCharStream.cpp new file mode 100644 index 00000000000..20b5a46ab76 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrCharStream.cpp @@ -0,0 +1,652 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrCharStream.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the GrCharStream class. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Constructor. +----------------------------------------------------------------------------------------------*/ +GrCharStream::GrCharStream(ITextSource * pgts, int ichrMin, int ichrLim, + bool fStartLine, bool fEndLine) + : m_pgts(pgts), + m_ichrMin(ichrMin), + m_ichrLim(ichrLim), + m_ichrPos(ichrMin), + m_fStartLine(fStartLine), + m_fEndLine(fEndLine), + m_cchrBackedUp(0), + m_ichrRunMin(0), + m_ichrRunLim(0), + m_ichlRunOffset(kPosInfinity), + m_ichrRunOffset(kPosInfinity) +{ + #if 0 // ??? surely m_cnRunMax and m_prgnRunText are undefined at this point + if (m_cchlRunMax > -1) + delete[] m_prgchlRunText; + #endif + m_cchlRunMax = -1; + m_prgchlRunText = NULL; + + m_vislotNextChunkMap.clear(); + + m_utf = m_pgts->utfEncodingForm(); +} + +/*---------------------------------------------------------------------------------------------- + Restart the stream when an error has a occurred and we need to fall back to dumb rendering. +----------------------------------------------------------------------------------------------*/ +void GrCharStream::Restart() +{ + if (m_cchlRunMax > -1) + delete[] m_prgchlRunText; + + m_ichrPos = m_ichrMin; + m_cchrBackedUp = 0; + m_ichrRunMin = 0; + m_ichrRunLim = 0; + m_ichlRunOffset = kPosInfinity; + m_ichrRunOffset = kPosInfinity; + + m_cchlRunMax = -1; + m_prgchlRunText = NULL; + + m_vislotNextChunkMap.clear(); +} + +/*---------------------------------------------------------------------------------------------- + Get the next character from the stream. + + @param pichwSegOffset - index of this character from the beginning of the segment + @param pcchRaw - number of raw (UTF-16 or UTF-8) chars consumed to get one complete + Unicode codepoint. +----------------------------------------------------------------------------------------------*/ +int GrCharStream::NextGet(GrTableManager * ptman, + GrFeatureValues * pfval, int * pichrSegOffset, int * pcchr) +{ + if (AtEnd()) + { + Assert(m_ichrPos == m_ichrLim); + return 0; + } + + int m_ichrMinBackedUp = m_ichrMin - m_cchrBackedUp; + + if ((m_ichrRunOffset + m_ichrRunMin) >= m_ichrRunLim) + { + // Get the next run of characters + + std::pair<toffset, toffset> pairRange = m_pgts->propertyRange(m_ichrPos); + m_ichrRunMin = pairRange.first; + m_ichrRunLim = pairRange.second; + + // The only time the run extends before what we want is on the first run. + Assert((m_ichrRunMin == m_ichrPos) // m_ichwPos is beginning of run + || (m_ichrPos == m_ichrMinBackedUp)); // first run + // Pretend like the run starts where we need it: + m_ichrRunMin = max(m_ichrRunMin, m_ichrMinBackedUp); + Assert(m_ichrRunMin == m_ichrPos); + + if (m_cchlRunMax != -1 && m_cchlRunMax < m_ichrRunLim - m_ichrRunMin) + { + // Text buffer is too small: delete it and create a new one. + delete[] m_prgchlRunText; + m_cchlRunMax = -1; + } + + if (m_cchlRunMax == -1) + { + // Note that combining surrogates or UTF-8 will always result in the same or fewer + // number of characters needed in m_prgchlRunText, so the following is safe: + m_cchlRunMax = m_ichrRunLim - m_ichrRunMin; + m_prgchlRunText = new int[m_cchlRunMax]; + } + + utf32 * prgchlRunText32 = NULL; + utf16 * prgchwRunText16 = NULL; + utf8 * prgchsRunText8 = NULL; + + int cchGot; + switch (m_utf) + { + case kutf8: + prgchsRunText8 = new utf8[m_ichrRunLim - m_ichrRunMin]; + cchGot = m_pgts->fetch(m_ichrRunMin, m_ichrRunLim - m_ichrRunMin, prgchsRunText8); + break; + case kutf16: + prgchwRunText16 = new utf16[m_ichrRunLim - m_ichrRunMin]; + cchGot = m_pgts->fetch(m_ichrRunMin, m_ichrRunLim - m_ichrRunMin, prgchwRunText16); + break; + default: + Assert(false); + // Assume UTF-32: fall through + case kutf32: + prgchlRunText32 = new utf32[m_ichrRunLim - m_ichrRunMin]; + cchGot = m_pgts->fetch(m_ichrRunMin, m_ichrRunLim - m_ichrRunMin, prgchlRunText32); + break; + } + + int ichlUtf32 = 0; + for (int ichr = 0; ichr < m_ichrRunLim - m_ichrRunMin; ichr++) + { + if (ichr == m_ichrPos - m_ichrRunMin) + { + m_ichrRunOffset = ichr; + m_ichlRunOffset = ichlUtf32; + } + Assert(m_ichrRunOffset != kPosInfinity || ichr < m_ichrPos - m_ichrRunMin); + + int cchrUsed; + switch (m_utf) + { + case kutf8: + m_prgchlRunText[ichlUtf32] = Utf8ToUtf32(prgchsRunText8 + ichr, + m_ichrRunLim - m_ichrRunMin - ichr, &cchrUsed); + break; + case kutf16: + m_prgchlRunText[ichlUtf32] = Utf16ToUtf32(prgchwRunText16 + ichr, + m_ichrRunLim - m_ichrRunMin - ichr, &cchrUsed); + break; + default: + Assert(false); + // Assume UTF-32: fall through + case kutf32: + m_prgchlRunText[ichlUtf32] = prgchlRunText32[ichlUtf32]; + cchrUsed = 1; + break; + } + + m_vislotNextChunkMap.push_back(ichlUtf32); + for (int i = 1; i < cchrUsed; i++) + m_vislotNextChunkMap.push_back(-1); + ichr += cchrUsed - 1; + ichlUtf32++; + } + delete[] prgchlRunText32; + delete[] prgchwRunText16; + delete[] prgchsRunText8; + + while (m_ichrLim - m_ichrRunMin < signed(m_vislotNextChunkMap.size()) + && m_vislotNextChunkMap[m_ichrLim - m_ichrRunMin] == -1) + { + // Invalid surrogate boundary; this is really a bug in the application program, + // but adjust here to avoid a crash! + m_ichrLim--; + } + + SetUpFeatureValues(ptman, m_ichrPos); + + Assert(m_ichrRunOffset >= 0); + Assert(m_ichrRunOffset < m_ichrRunLim - m_ichrRunMin); + Assert(m_vislotNextChunkMap.size() == (unsigned) m_ichrRunLim - m_ichrMinBackedUp); + } + + int chlRet = m_prgchlRunText[m_ichlRunOffset]; + *pfval = m_fvalRunFeats; + *pichrSegOffset = m_ichrPos - m_ichrMin; // offset from the official start of the segment + // of the first 16-bit char + int ichrPosPrev = m_ichrPos; + ++m_ichlRunOffset; + do { + ++m_ichrPos; + ++m_ichrRunOffset; + } while (m_ichrPos - m_ichrMinBackedUp < signed(m_vislotNextChunkMap.size()) + && m_vislotNextChunkMap[m_ichrPos - m_ichrMinBackedUp] == -1); + + *pcchr = m_ichrPos - ichrPosPrev; // number of raw chars consumed + Assert(m_utf != kutf8 || *pcchr <= 6); + Assert(m_utf != kutf16 || *pcchr <= 2); + + Assert(m_ichrPos == (m_ichrRunMin + m_ichrRunOffset)); + Assert(m_ichrPos <= m_ichrLim); + + /* KLUDGE for debugging + if (m_ichrPos == 1) + nRet = 0x1018; + else if (m_ichrPos == 2) + nRet = 0x1039; + else if (m_ichrPos == 3) + nRet = 0x101b; + else if (m_ichrPos == 4) + nRet = 0x1032; + else if (m_ichrPos == 5) + nRet = 0x1037; + else if (m_ichrPos == 6) + nRet = 0x1037; + */ + + return chlRet; +} + +/*---------------------------------------------------------------------------------------------- + Convert UTF-8 to UTF-32--a single character's worth. Also pass back the number of 8-bit + items consumed. +----------------------------------------------------------------------------------------------*/ +utf32 GrCharStream::Utf8ToUtf32(utf8 * prgchs, int cchs, int * pcchsUsed) +{ + if (cchs <= 0) + { + *pcchsUsed = 0; + return 0; + } + + long chlRet = DecodeUtf8(prgchs, cchs, pcchsUsed); + Assert(chlRet > 0); + if (chlRet == -1) + { + // Some error occurred. Just treat the UTF-8 as UTF-32. + *pcchsUsed = 1; + return (utf32)prgchs[0]; + } + else + return chlRet; +} + +/*---------------------------------------------------------------------------------------------- + Convert UTF-16 to UTF-32--a single character's worth. Also pass back the number of 16-bit + items consumed. +----------------------------------------------------------------------------------------------*/ +utf32 GrCharStream::Utf16ToUtf32(utf16 * prgchw, int cchw, int * pcchwUsed) +{ + if (cchw <= 0) + { + *pcchwUsed = 0; + return 0; + } + + unsigned int nUtf32; + bool fSurrogate = FromSurrogatePair(prgchw[0], ((cchw < 2) ? 0 : prgchw[1]), &nUtf32); + *pcchwUsed = (fSurrogate) ? 2 : 1; + return (int)nUtf32; + +/* + // For testing: + int nCharRet; + if (cchw >= 2 && prgchw[0] == '^' && prgchw[1] == 'a') + { + nCharRet = 'A'; + *pcchwUsed = 2; + } + else if (cchw >= 3 && prgchw[0] == '#' && prgchw[1] == '#' && + prgchw[2] == 'B') + { + nCharRet = 'b'; + *pcchwUsed = 3; + } + else if (cchw >= 1) + { + nCharRet = prgchw[0]; + *pcchwUsed = 1; + } + else + { + nCharRet = 0; + *pcchwUsed = 0; + } + return nCharRet; +*/ +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given position is at the boundary of a Unicode character; return false + if it is between two parts of pair of surrogates. +----------------------------------------------------------------------------------------------*/ +bool GrCharStream::AtUnicodeCharBoundary(ITextSource * pgts, int ichr) +{ + int cchr = pgts->getLength(); + if (ichr <= 0 || ichr >= cchr) + return true; + + UtfType utf = pgts->utfEncodingForm(); + + // Note that we never need more than a handful of characters. + int ichrMinGet = max(0, ichr - 1); + int ichrLimGet = ichr + 1; + utf16 rgchwText16[3]; + utf8 rgchsText8[3]; + bool fRet; + switch (utf) + { + case kutf8: + pgts->fetch(ichrMinGet, ichrLimGet - ichrMinGet, rgchsText8); + fRet = AtUnicodeCharBoundary(rgchsText8, ichrLimGet - ichrMinGet, + ichr - ichrMinGet, utf); + break; + case kutf16: + pgts->fetch(ichrMinGet, ichrLimGet - ichrMinGet, rgchwText16); + fRet = AtUnicodeCharBoundary(rgchwText16, ichrLimGet - ichrMinGet, + ichr - ichrMinGet, utf); + break; + default: + Assert(false); + case kutf32: + fRet = true; + break; + } + + return fRet; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given position is at the boundary of a Unicode character; return false + if it is between two parts of pair of surrogates. +----------------------------------------------------------------------------------------------*/ +bool GrCharStream::AtUnicodeCharBoundary(utf16 * prgchw, int cchw, int ichr, UtfType utf) +{ + Assert(ichr >= 0); + Assert(ichr <= cchw); + + if (ichr == 0) + return true; + if (ichr >= cchw) + return true; + + switch (utf) + { + case kutf16: + { + unsigned int nUtf32; + bool fMiddleOfPair = FromSurrogatePair(prgchw[ichr - 1], prgchw[ichr], &nUtf32); + return !fMiddleOfPair; + } + case kutf8: + { + // 16-bit buffer being treated like UTF-8. + utf8 rgchs[2]; + rgchs[1] = (utf8)prgchw[ichr]; + return AtUnicodeCharBoundary(rgchs, 2, 1, kutf8); + } + case kutf32: + default: + return true; + } + +/* + // temp stuff for debugging: + if (ichr > 0 && cchw >= 1 && prgchw[ichr-1] == '^' && prgchw[ichr] == 'a') + { + return false; + } + if (ichr > 0 && cchw >= 2 && prgchw[ichr-1] == '#' && prgchw[ichr] == '#' && + prgchw[ichr+1] == 'B') + { + return false; + } + if (ichr > 1 && cchw >= 1 && prgchw[ichr-2] == '#' && prgchw[ichr-1] == '#' && + prgchw[ichr] == 'B') + { + return false; + } + + return true; +*/ +} + +bool GrCharStream::AtUnicodeCharBoundary(utf8 * prgchs, int cchs, int ichs, UtfType utf) +{ + Assert(ichs >= 0); + Assert(ichs <= cchs); + + Assert(utf == kutf8); + + if (ichs == 0) + return true; + if (ichs >= cchs) + return true; + + if (cchs == 0) + return true; + + // A bit pattern of 10xxxxxx indicates a continuation of a sequence + // (11xxxxxx means the first byte of a sequence, 0xxxxxxx means a single character). + + utf8 chsTest = prgchs[ichs] & 0xC0; + return (chsTest != 0x80); +} + +/*---------------------------------------------------------------------------------------------- + Convert the given pair of characters into a single 32-bit Unicode character. Return + true if they are a legitimate surrogate pair. If not, just return the first of the + two 16-bit characters. +----------------------------------------------------------------------------------------------*/ +bool GrCharStream::FromSurrogatePair(utf16 chwIn1, utf16 chwIn2, unsigned int * pchlOut) +{ + if ((chwIn1 < kzUtf16HighFirst) || (chwIn1 > kzUtf16HighLast) + || (chwIn2 < kzUtf16LowFirst) || (chwIn2 > kzUtf16LowLast)) + { + // Not a surrogate + *pchlOut = (unsigned int)chwIn1; + return false; + } + else + { + *pchlOut = ((chwIn1 - kzUtf16HighFirst) << kzUtf16Shift) + chwIn2 + kzUtf16Inc; + return true; + } +} + +/*---------------------------------------------------------------------------------------------- + Decode 1-6 bytes in the character string from UTF-8 format to Unicode (UCS-4). + As a side-effect, cbOut is set to the number of UTF-8 bytes consumed. + + @param rgchUtf8 Pointer to a a character array containing UTF-8 data. + @param cchUtf8 Number of characters in the array. + @param cbOut Reference to an integer for holding the number of input (8-bit) characters + consumed to produce the single output Unicode character. + + @return A single Unicode (UCS-4) character. If an error occurs, return -1. +----------------------------------------------------------------------------------------------*/ +long GrCharStream::DecodeUtf8(const utf8 * rgchUtf8, int cchUtf8, int * pcbOut) +{ + // check for valid input + AssertArray(rgchUtf8, cchUtf8); + if ((cchUtf8 == 0) || (rgchUtf8[0] == '\0')) + { + *pcbOut = (cchUtf8) ? 1 : 0; + return 0; + } + // + // decode the first byte of the UTF-8 sequence + // + long lnUnicode; + int cbExtra; + int chsUtf8 = *rgchUtf8++ & 0xFF; + if (chsUtf8 >= kzUtf8Flag6) // 0xFC + { + lnUnicode = chsUtf8 & kzUtf8Mask6; + cbExtra = 5; + } + else if (chsUtf8 >= kzUtf8Flag5) // 0xF8 + { + lnUnicode = chsUtf8 & kzUtf8Mask5; + cbExtra = 4; + } + else if (chsUtf8 >= kzUtf8Flag4) // 0xF0 + { + lnUnicode = chsUtf8 & kzUtf8Mask4; + cbExtra = 3; + } + else if (chsUtf8 >= kzUtf8Flag3) // 0xE0 + { + lnUnicode = chsUtf8 & kzUtf8Mask3; + cbExtra = 2; + } + else if (chsUtf8 >= kzUtf8Flag2) // 0xC0 + { + lnUnicode = chsUtf8 & kzUtf8Mask2; + cbExtra = 1; + } + else // 0x00 + { + lnUnicode = chsUtf8; + cbExtra = 0; + } + if (cbExtra >= cchUtf8) + { + return -1; + } + + switch (cbExtra) + { + case 5: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + // fall through + case 4: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + // fall through + case 3: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + // fall through + case 2: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + // fall through + case 1: + lnUnicode <<= kzUtf8ByteShift; + chsUtf8 = *rgchUtf8++ & 0xFF; + if ((chsUtf8 & ~kzByteMask) != 0x80) + return -1; + lnUnicode += chsUtf8 & kzByteMask; + break; + case 0: + // already handled + break; + default: + Assert(false); + } + if ((unsigned long)lnUnicode > kzUnicodeMax) + { + return -1; + } + *pcbOut = cbExtra + 1; + return lnUnicode; +} + +/*---------------------------------------------------------------------------------------------- + Get the current feature values and character properties from the stream. +----------------------------------------------------------------------------------------------*/ +void GrCharStream::CurrentFeatures(GrTableManager * ptman, GrFeatureValues * pfval) +{ + if (m_ichrRunOffset == kPosInfinity) + { + // Not yet set up. + if (AtEnd()) + { + // Empty stream; no valid values. + Assert(false); + return; + } + + // Get the settings from the first character. + int ichrSegOffset; + int ichrPosSave = m_ichrPos; + int ichrRunOffsetSave = m_ichrRunOffset; + int ichlRunOffsetSave = m_ichlRunOffset; + int cMapSize = m_vislotNextChunkMap.size(); + int cchrConsumed; + NextGet(ptman, pfval, &ichrSegOffset, &cchrConsumed); + // Put the character back. + m_ichrPos = ichrPosSave; + m_ichrRunOffset = ichrRunOffsetSave; + m_ichlRunOffset = ichlRunOffsetSave; + while (signed(m_vislotNextChunkMap.size()) > cMapSize) + m_vislotNextChunkMap.pop_back(); + } + else + { + *pfval = m_fvalRunFeats; + //*ppchrp = &m_chrpRun; + } +} + +/*---------------------------------------------------------------------------------------------- + Read the feature settings from the text properties. + + Eventually, handle style index here? +----------------------------------------------------------------------------------------------*/ +void GrCharStream::SetUpFeatureValues(GrTableManager * ptman, int ichr) +{ + // Set all the features to their default values. + for (int i = 0; i < kMaxFeatures; ++i) + { + m_fvalRunFeats.m_rgnFValues[i] = ptman->DefaultForFeatureAt(i); + } + m_fvalRunFeats.m_nStyleIndex = 0; + + // Add in the defaults for the language of the text. + std::vector<featid> vnFeats; + std::vector<int> vnValues; + isocode lgcode = m_pgts->getLanguage(ichr); + ptman->DefaultsForLanguage(lgcode, vnFeats, vnValues); + size_t ifeatLp; + int ifeat; + for (ifeatLp = 0; ifeatLp < vnFeats.size(); ifeatLp++) + { + ptman->FeatureWithID(vnFeats[ifeatLp], &ifeat); + m_fvalRunFeats.m_rgnFValues[ifeat] = vnValues[ifeatLp]; + } + + // Add in the explicit feature settings. + FeatureSetting rgfset[kMaxFeatures]; + int cfeat = m_pgts->getFontFeatures(ichr, rgfset); + + for (ifeatLp = 0; ifeatLp < (size_t)cfeat; ifeatLp++) + { + ptman->FeatureWithID(rgfset[ifeatLp].id, &ifeat); + if (ifeat >= 0) + m_fvalRunFeats.m_rgnFValues[ifeat] = rgfset[ifeatLp].value; + } +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrCharStream.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrCharStream.h new file mode 100644 index 00000000000..bb91cf54eba --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrCharStream.h @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrCharStream.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + + +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_CHARSTREAM_INCLUDED +#define GR_CHARSTREAM_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + Makes a stream out of a text string that is serving as input to the process. + + Hungarian: chstrm +----------------------------------------------------------------------------------------------*/ +class GrCharStream +{ +public: + // Constructor and destructor: + GrCharStream(ITextSource * pgts, int ichrMin, int ichrLim, + bool fStartLine, bool fEndLine); + + ~GrCharStream() + { + if (m_cchlRunMax >= 0) + delete[] m_prgchlRunText; + } + + // Getters: + ITextSource * TextSrc() { return m_pgts; } + int Min() { return m_ichrMin; } + int Pos() { return m_ichrPos; } + int Lim() { return m_ichrLim; } + bool StartLine() { return m_fStartLine; } + bool EndLine() { return m_fEndLine; } + + // offset from the beginning of the segment + int SegOffset() { return m_ichrPos - m_ichrMin; } + int SegOffset(int ichr) { return ichr - m_ichrMin; } + + bool AtEnd() + { + return (m_ichrPos >= m_ichrLim); + } + + // The input string is empty. + bool IsEmpty() + { + return (m_ichrLim - m_ichrMin == 0); + } + + // When we are restarting in order to create a segment other than the first, + // back up the position in the stream slightly in order to reprocess a few characters + // before the line-break. + void Backup(int cchrToReprocess) + { + Assert(m_ichrPos >= cchrToReprocess); + m_ichrPos -= cchrToReprocess; + m_ichrRunOffset = kPosInfinity; + m_cchrBackedUp = cchrToReprocess; + } + + // When we found a hard-break in the input stream, we need to put it back, and since + // we're done, we set the end of the stream to that position. + void HitHardBreak() + { + m_ichrPos -= 1; + m_ichrRunOffset -= 1; + m_ichrLim = m_ichrPos; + } + + void Restart(); + + int NextGet(GrTableManager *, GrFeatureValues * pfval, int * ichrSegOffset, int * pcchr); + + void CurrentFeatures(GrTableManager * ptman, GrFeatureValues * pfval); + + // For transduction logging: + int GetLogData(GrTableManager * ptman, int * rgnChars, bool * rgfNewRun, + GrFeatureValues * rgfval, int cchwBackup, + int * pcchwMaxRaw); + void GetLogDataRaw(GrTableManager * ptman, int cchw, int cchwBackup, + int cchwMax16bit, int * prgnChars, + utf16 * prgchw2, utf16 * prgchw3, utf16 * prgchw4, utf16 * prgchw5, utf16 * prgchw6, + int * prgichwRaw); + +protected: + void SetUpFeatureValues(GrTableManager * ptman, int ichr); +public: + static utf32 Utf8ToUtf32(utf8 * prgchs8bit, int cchs, int * pcchsUsed); + static utf32 Utf16ToUtf32(utf16 * prgchw16bit, int cchw, int * pcchwUsed); + bool AtUnicodeCharBoundary(int cchr) + { + return AtUnicodeCharBoundary(m_pgts, cchr); + } + static bool AtUnicodeCharBoundary(ITextSource * pgts, int ichr); + static bool AtUnicodeCharBoundary(utf8 * prgchs, int cchs, int ichs, UtfType utf); + static bool AtUnicodeCharBoundary(utf16 * prgchw, int cchw, int ichr, UtfType utf); + static bool FromSurrogatePair(utf16 chwIn1, utf16 chwIn2, unsigned int * pch32Out); + static long DecodeUtf8(const utf8 * rgchUtf8, int cchUtf8, int * pcbOut); + +protected: + // Instance variables: + // Hungarian note: chr = raw characters, chl = long characters (UTF-32) + // chs = short characters (UTF-8), chw = wide characters (UTF-16) + ITextSource * m_pgts; // string to render + UtfType m_utf; // what encoding form the text-source uses + int m_ichrMin; // official start of segment relative to beginning of string + int m_ichrLim; // end of stream (potential end of seg) relative to beginning of string + int m_ichrPos; // stream position (0 = start of string) + bool m_fStartLine; // true if a line-break character should be prepended + bool m_fEndLine; // true if a line-break character should be appended + int m_cchrBackedUp; // number of characters backed up before beginning of segment + + // We read a run's worth of data at a time and cache it in the following variables: + int m_cchlRunMax; // size of buffer allocated + int * m_prgchlRunText; // buffer containing current run + int m_ichrRunMin; // start of run relative to beginning of string + int m_ichrRunLim; // end of run relative to beginning of string + int m_ichlRunOffset; // index into m_prgchlRunText; kPosInfinity if nothing set up + int m_ichrRunOffset; // corresponding index into text source + GrFeatureValues m_fvalRunFeats; + + std::vector<int> m_vislotNextChunkMap; // maps from 16-bit chars to 32-bit chars & glyphs + + enum + { + kzUtf8Mask1 = 0x7F, + kzUtf8Mask2 = 0x1F, + kzUtf8Mask3 = 0x0F, + kzUtf8Mask4 = 0x07, + kzUtf8Mask5 = 0x03, + kzUtf8Mask6 = 0x01 + }; + + enum + { + kzUtf8Flag1 = 0x00, + kzUtf8Flag2 = 0xC0, + kzUtf8Flag3 = 0xE0, + kzUtf8Flag4 = 0xF0, + kzUtf8Flag5 = 0xF8, + kzUtf8Flag6 = 0xFC + }; + + enum + { + kzByteMask = 0x3F, + kzByteMark = 0x80, + kzUtf8ByteShift = 6, + kzUnicodeMax = 0x7FFFFFFF + }; + + enum + { + kzUtf16Shift = 10, + kzUtf16Inc = 0x2400, + kzUtf16HighFirst = 0xD800, + kzUtf16HighLast = 0xDBFF, + kzUtf16LowFirst = 0xDC00, + kzUtf16LowLast = 0xDFFF + }; +}; + +} // namespace gr + +#endif // !GR_CHARSTREAM_INCLUDED + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrClassTable.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrClassTable.cpp new file mode 100644 index 00000000000..43316fdc04a --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrClassTable.cpp @@ -0,0 +1,382 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrClassTable.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the GrClassTable class and related classes. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Fill in by reading from the file stream. +----------------------------------------------------------------------------------------------*/ +bool GrClassTable::ReadFromFont(GrIStream & grstrm, int fxdVersion) +{ + long lClassMapStart; + grstrm.GetPositionInFont(&lClassMapStart); + + // number of classes + m_ccls = grstrm.ReadUShortFromFont(); + if (m_ccls >= kMaxReplcmtClasses) + return false; // bad table + if (fxdVersion < 0x00030000 && m_ccls > kMaxReplcmtClassesV1_2) + return false; // bad table + + // number of linear classes + m_cclsLinear = grstrm.ReadUShortFromFont(); + if (m_cclsLinear > m_ccls) + return false; // bad table + + // class offsets + m_prgichwOffsets = new data16[m_ccls + 1]; + gAssert(m_prgichwOffsets); + if (!m_prgichwOffsets) + return false; // bad table + + data16 * pchw = m_prgichwOffsets; + int icls; + for (icls = 0; icls <= m_ccls; icls++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // Offsets are relative to the start class map; make them relative to the class list + // itself, and in terms of utf16s, not bytes. + long lClassesPos; + grstrm.GetPositionInFont(&lClassesPos); + int cbDiff = lClassesPos - lClassMapStart; + for (icls = 0; icls <= m_ccls; icls++) + { + m_prgichwOffsets[icls] = data16(int(m_prgichwOffsets[icls]) - cbDiff); + gAssert((m_prgichwOffsets[icls] & 0x00000001) == 0); + if ((m_prgichwOffsets[icls] & 0x00000001) != 0) + return false; // bad table + m_prgichwOffsets[icls] >>= 1; // divide by 2 + } + + // classes - slurp entire block (data remains in big-endian format) + m_prgchwBIGGlyphList = new data16[m_prgichwOffsets[m_ccls]]; + gAssert(m_prgchwBIGGlyphList); + if (!m_prgchwBIGGlyphList) + return false; // bad table + grstrm.ReadBlockFromFont(m_prgchwBIGGlyphList, + m_prgichwOffsets[m_ccls] * isizeof(data16)); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Set up an empty class table. +----------------------------------------------------------------------------------------------*/ +void GrClassTable::CreateEmpty() +{ + // number of classes + m_ccls = 0; + // number of linear classes + m_cclsLinear = 0; +} + +/*---------------------------------------------------------------------------------------------- + Search for the glyph ID using a fast binary search, and return the matching index. +----------------------------------------------------------------------------------------------*/ +int GrInputClass::FindIndex(gid16 gid) +{ + int digixInit = InitialSearchRange(); + int igixStart = StartSearch(); +#ifdef _DEBUG + int cgix = NumberOfGlyphs(); + int nPowerOf2 = 1; + while (nPowerOf2 <= cgix) + nPowerOf2 <<= 1; + nPowerOf2 >>= 1; + // Now nPowerOf2 is the max power of 2 <= cgix + gAssert((1 << LoopCount()) == nPowerOf2); // LoopCount() == log2(nPowerOf2) + gAssert(digixInit == nPowerOf2); + gAssert(igixStart == cgix - digixInit); +#endif // _DEBUG + + int digixCurr = digixInit; + + GrGlyphIndexPair * pgixCurr = m_pgixFirst + igixStart; + while (digixCurr > 0) + { + int nTest; + if (pgixCurr < m_pgixFirst) + nTest = -1; + else + nTest = pgixCurr->GlyphID() - gid; + + if (nTest == 0) + return pgixCurr->Index(); + + digixCurr >>= 1; // divide by 2 + if (nTest < 0) + pgixCurr += digixCurr; + else // (nTest > 0) + pgixCurr -= digixCurr; + } + + return -1; +} + +/*---------------------------------------------------------------------------------------------- + Return the selector index for the given glyph ID. +----------------------------------------------------------------------------------------------*/ +int GrClassTable::FindIndex(int icls, gid16 chwGlyphID) +{ + if (icls >= m_ccls) + { + gAssert(false); // bad compiler problem + return 0; + } + + if (icls < m_cclsLinear) + { + // The class is an output class--uses linear format--and is being used as an + // input class. Shouldn't happen if the compiler is working right. + gAssert(false); // comment out for test procedures + + // Do a slow linear search to find the glyph ID. + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + int cchw = ichwLim - ichwMin; + + for (int ichw = 0; ichw < cchw; ichw++) + { + if (GlyphAt(ichwMin + ichw) == chwGlyphID) + return ichw; + } + return -1; + } + + // Slurp the class into an instance so we can see what's there. + GrInputClass clsin; + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + clsin.CopyFrom(GlyphListLoc(ichwMin), (ichwLim - ichwMin)); + + // Do a fast binary search to find our glyph. + int iRet = clsin.FindIndex(chwGlyphID); + return iRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the glyph ID at the selector index. +----------------------------------------------------------------------------------------------*/ +gid16 GrClassTable::GetGlyphID(int icls, int ichw) +{ + if (ichw < 0) + { + gAssert(false); + return 0; + } + + if (icls >= m_cclsLinear) + { + // The class is an input class--sorted by glyph ID--and is being used as an + // output class. Shouldn't happen if the compiler is working right. + gAssert(false); // comment out for test procedures + + if (icls >= m_ccls) // bad compiler problem + return 0; + + // Do a slow linear search to find the index and answer the matching glyph. + // Slurp the class into an instance so we can see what's there. + GrInputClass clsin; + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + clsin.CopyFrom(GlyphListLoc(ichwMin), (ichwLim - ichwMin)); + + int cgix = clsin.NumberOfGlyphs(); + for (int igix = 0; igix < cgix; igix++) + { + GrGlyphIndexPair * m_pgix = clsin.m_pgixFirst + igix; + if (m_pgix->Index() == ichw) + return m_pgix->GlyphID(); + } + return 0; + } + + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + + if (ichw >= ichwLim - ichwMin) + return 0; + else + return GlyphAt(ichwMin + ichw); +} + +/*---------------------------------------------------------------------------------------------- + Return the number of glyphs in the class. +----------------------------------------------------------------------------------------------*/ +int GrClassTable::NumberOfGlyphsInClass(int icls) +{ + int ichwMin = m_prgichwOffsets[icls]; + int ichwLim = m_prgichwOffsets[icls+1]; + + if (icls >= m_cclsLinear) + { + if (icls >= m_ccls) // bad compiler problem + return 0; + + GrInputClass clsin; + clsin.CopyFrom(GlyphListLoc(ichwMin), (ichwLim - ichwMin)); + + int cgix = clsin.NumberOfGlyphs(); + return cgix; + } + else + { + gAssert(false); // this method should not be used for output classes. + return ichwLim - ichwMin; + } +} + +//:>******************************************************************************************** +//:> For test procedures +//:>******************************************************************************************** + +//:Ignore + +#ifdef OLD_TEST_STUFF +/*---------------------------------------------------------------------------------------------- + General test of class table. +----------------------------------------------------------------------------------------------*/ +void GrClassTable::SetUpTestData() +{ + m_ccls = 7; // number of classes + m_cclsLinear = 4; // number of classes in linear format + + m_prgchwBIGGlyphList = new gid16[100]; + + m_prgichwOffsets = new data16[7+1]; + + gid16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: uppercase consonants B - H + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(gid16(66)); *pchw++ = msbf(utf16(67)); *pchw++ = msbf(utf16(68)); + *pchw++ = msbf(gid16(70)); *pchw++ = msbf(utf16(71)); *pchw++ = msbf(utf16(72)); + + // Output class 1: grave vowels + m_prgichwOffsets[1] = 6; + *pchw++ = msbf(gid16(192)); // A + *pchw++ = msbf(gid16(224)); // a + *pchw++ = msbf(gid16(200)); // E + *pchw++ = msbf(gid16(232)); // e + *pchw++ = msbf(gid16(204)); // I + *pchw++ = msbf(gid16(236)); // i + *pchw++ = msbf(gid16(210)); // O + *pchw++ = msbf(gid16(243)); // o + *pchw++ = msbf(gid16(217)); // U + *pchw++ = msbf(gid16(249)); // u + + // Output class 2: circumflex vowels + m_prgichwOffsets[2] = 6 + 10; + *pchw++ = msbf(gid16(194)); // A + *pchw++ = msbf(gid16(226)); // a + *pchw++ = msbf(gid16(202)); // E + *pchw++ = msbf(gid16(234)); // e + *pchw++ = msbf(gid16(206)); // I + *pchw++ = msbf(gid16(238)); // i + *pchw++ = msbf(gid16(212)); // O + *pchw++ = msbf(gid16(244)); // o + *pchw++ = msbf(gid16(219)); // U + *pchw++ = msbf(gid16(251)); // u + + // Output class 3: diaeresis vowels, uppercase + m_prgichwOffsets[3] = 16 + 10; + *pchw++ = msbf(gid16(196)); // A + *pchw++ = msbf(gid16(196)); // A + *pchw++ = msbf(gid16(203)); // E + *pchw++ = msbf(gid16(203)); // E + *pchw++ = msbf(gid16(207)); // I + *pchw++ = msbf(gid16(207)); // I + *pchw++ = msbf(gid16(214)); // O + *pchw++ = msbf(gid16(214)); // O + *pchw++ = msbf(gid16(220)); // U + *pchw++ = msbf(gid16(220)); // U + + // Input class 4: lowercase consonants b - h + m_prgichwOffsets[4] = 26 + 10; // = 36 + *pchw++ = msbf(gid16(6)); + *pchw++ = msbf(gid16(4)); *pchw++ = msbf(gid16(2)); *pchw++ = msbf(gid16(6-4)); + *pchw++ = msbf(gid16(98)); *pchw++ = msbf(gid16(0)); + *pchw++ = msbf(gid16(99)); *pchw++ = msbf(gid16(1)); + *pchw++ = msbf(gid16(100)); *pchw++ = msbf(gid16(2)); + *pchw++ = msbf(gid16(102)); *pchw++ = msbf(gid16(3)); + *pchw++ = msbf(gid16(103)); *pchw++ = msbf(gid16(4)); + *pchw++ = msbf(gid16(104)); *pchw++ = msbf(gid16(5)); + + // Input class 5: vowels + m_prgichwOffsets[5] = 36 + 4 + 6*2; // = 52 + *pchw++ = msbf(gid16(10)); + *pchw++ = msbf(gid16(8)); *pchw++ = msbf(utf16(3)); *pchw++ = msbf(utf16(10-8)); + *pchw++ = msbf(gid16(65)); *pchw++ = msbf(gid16(0)); // A + *pchw++ = msbf(gid16(69)); *pchw++ = msbf(gid16(2)); // E + *pchw++ = msbf(gid16(73)); *pchw++ = msbf(gid16(4)); // I + *pchw++ = msbf(gid16(79)); *pchw++ = msbf(gid16(6)); // O + *pchw++ = msbf(gid16(85)); *pchw++ = msbf(gid16(8)); // U + *pchw++ = msbf(gid16(97)); *pchw++ = msbf(gid16(1)); // a + *pchw++ = msbf(gid16(101)); *pchw++ = msbf(gid16(3)); // e + *pchw++ = msbf(gid16(105)); *pchw++ = msbf(gid16(5)); // i + *pchw++ = msbf(gid16(111)); *pchw++ = msbf(gid16(7)); // o + *pchw++ = msbf(gid16(117)); *pchw++ = msbf(gid16(9)); // u + + // Input class 6: acute vowels + m_prgichwOffsets[6] = 52 + 4 + 10*2; // = 76 + *pchw++ = msbf(gid16(10)); + *pchw++ = msbf(gid16(8)); *pchw++ = msbf(gid16(3)); *pchw++ = msbf(gid16(10-8)); + *pchw++ = msbf(gid16(193)); *pchw++ = msbf(gid16(0)); // A + *pchw++ = msbf(gid16(201)); *pchw++ = msbf(gid16(2)); // E + *pchw++ = msbf(gid16(205)); *pchw++ = msbf(gid16(4)); // I + *pchw++ = msbf(gid16(211)); *pchw++ = msbf(gid16(6)); // O + *pchw++ = msbf(gid16(218)); *pchw++ = msbf(gid16(8)); // U + *pchw++ = msbf(gid16(225)); *pchw++ = msbf(gid16(1)); // a + *pchw++ = msbf(gid16(233)); *pchw++ = msbf(gid16(3)); // e + *pchw++ = msbf(gid16(237)); *pchw++ = msbf(gid16(5)); // i + *pchw++ = msbf(gid16(243)); *pchw++ = msbf(gid16(7)); // o + *pchw++ = msbf(gid16(250)); *pchw++ = msbf(gid16(9)); // u + + m_prgichwOffsets[7] = 76 + 4 + 10*2; // = 100 +}; + +#endif // OLD_TEST_STUFF + +} // namespace gr + +//:End Ignore + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrClassTable.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrClassTable.h new file mode 100644 index 00000000000..67d6940862c --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrClassTable.h @@ -0,0 +1,188 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrClassTable.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrClassTable and related classes that store the classes that are used in substitutions. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_CTABLE_INCLUDED +#define GR_CTABLE_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + A glyph ID / index pair, a member of the GrInputClass where the items are sorted + by glyph ID. + + Hungarian: gix +----------------------------------------------------------------------------------------------*/ +class GrGlyphIndexPair +{ + friend class GrInputClass; + friend class GrClassTable; + + gid16 GlyphID() { return lsbf(m_gidBIG); } + data16 Index() { return lsbf(m_nBIGIndex); } + + gid16 m_gidBIG; + data16 m_nBIGIndex; +}; + +/*---------------------------------------------------------------------------------------------- + A class consisting of a mapping from glyph ID to index, used for classes that + function as input classes (eg, occurrng in the left-hand side of a rule). + + Hungarian: clsin +----------------------------------------------------------------------------------------------*/ +class GrInputClass +{ + friend class GrClassTable; + +protected: + /*------------------------------------------------------------------------------------------ + Copy the raw memory into the instance. + ------------------------------------------------------------------------------------------*/ + void CopyFrom(data16 * pchwStart, int cchw) + { + m_cgixBIG = pchwStart[0]; + m_digixBIGInit = pchwStart[1]; + m_cBIGLoop = pchwStart[2]; + m_igixBIGStart = pchwStart[3]; + + int cgix = NumberOfGlyphs(); + m_pgixFirst = m_prggixBuffer; + if (cgix > 64) + { + m_vgix.resize(cgix); + m_pgixFirst = &m_vgix[0]; + } + gAssert((4 + (cgix * 2)) == cchw); + #ifdef _DEBUG + memset(m_pgixFirst, 0, cgix * isizeof(GrGlyphIndexPair)); + #endif + Assert(sizeof(GrGlyphIndexPair) == sizeof(gid16) + sizeof(data16)); + GrGlyphIndexPair * pgixStart = reinterpret_cast<GrGlyphIndexPair*>(pchwStart + 4); + std::copy(pgixStart, pgixStart + cgix, m_pgixFirst); + } + + int NumberOfGlyphs() { return lsbf(m_cgixBIG); } + int LoopCount() { return lsbf(m_cBIGLoop); } + int InitialSearchRange() { return lsbf(m_digixBIGInit); } + int StartSearch() { return lsbf(m_igixBIGStart); } + + int FindIndex(gid16 gid); + +protected: + // Instance variables: + data16 m_cgixBIG; // number of glyphs in the class + + // constants for fast binary search + data16 m_digixBIGInit; // (max power of 2 <= m_cgix); + // size of initial range to consider + data16 m_cBIGLoop; // log2(max power of 2 <= m_cgix); + // indicates how many iterations are necessary + data16 m_igixBIGStart; // m_cgix - m_digixInit; + // where to start search + + GrGlyphIndexPair m_prggixBuffer[64]; + std::vector<GrGlyphIndexPair> m_vgix; + GrGlyphIndexPair * m_pgixFirst; +}; + + +/*---------------------------------------------------------------------------------------------- + Contains all the classes used for substitution rules. + + Hungarian: ctbl +----------------------------------------------------------------------------------------------*/ + +class GrClassTable +{ + friend class FontMemoryUsage; + +public: + // Constructor & destructor: + GrClassTable() + : m_prgichwOffsets(NULL), + m_prgchwBIGGlyphList(NULL) + { + } + + ~GrClassTable() + { + delete[] m_prgichwOffsets; + delete[] m_prgchwBIGGlyphList; + } + + int NumberOfClasses() { return m_ccls; } + int NumberOfInputClasses() { return m_ccls - m_cclsLinear; } + int NumberOfOutputClasses() { return m_cclsLinear; } + + bool ReadFromFont(GrIStream & grstrm, int fxdVersion); + void CreateEmpty(); + + int FindIndex(int icls, gid16 chwGlyphID); + gid16 GetGlyphID(int icls, int ichw); + int NumberOfGlyphsInClass(int icls); + + gid16 GlyphAt(int ichw) + { + return lsbf(m_prgchwBIGGlyphList[ichw]); + } + data16 * GlyphListLoc(int ichw) + { + return m_prgchwBIGGlyphList + ichw; + } + +protected: + // Instance variables: + int m_ccls; // number of classes + int m_cclsLinear; // number of classes in linear format + + data16 * m_prgichwOffsets; + + // Two formats are included in the following array: the first section consists of + // flat ordered lists of glyphs, used for the "output" classes that use linear format. + // This provides an index-to-glyph mapping. + // The second section contains data in the format of GrInputClasses, used for "input" + // classes that need a binary-search format. This provides a glyph-to-index mapping. + // We don't create instances of GrInputClasses, because that would make reading + // from the ECF file slow. Instead we just set up a single instance at the time + // we're interested in it. + // NOTE that all this data has been slurped directly from the ECF file and therefore + // uses BIG-ENDIAN format. + gid16 * m_prgchwBIGGlyphList; + +//:Ignore +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + void SetUpTestData(); + void SetUpRuleActionTest(); + void SetUpRuleAction2Test(); + void SetUpAssocTest(); + void SetUpAssoc2Test(); + void SetUpDefaultAssocTest(); + void SetUpFeatureTest(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF +//:End Ignore +}; + +} // namespace gr + +#endif // !GR_CTABLE_INCLUDED + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrEngine.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrEngine.cpp new file mode 100644 index 00000000000..91db0dfb80a --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrEngine.cpp @@ -0,0 +1,2031 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrEngine.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: Contains the implementation of the GrEngine class. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#include <functional> +#include <cstring> +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +namespace gr +{ + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + + +//:>******************************************************************************************** +//:> General functions +//:>******************************************************************************************** + +bool compareCmap(const byte *lhs, const byte *rhs) +{ + using namespace gr; + + typedef const struct index {data16 version; data16 numberSubtables;} *const IndexPtr; + typedef const struct enc {data16 platID; data16 platSpecID; data32 offset;} *const EncPtr; + + // Calculate the size of the cmap. + + IndexPtr rhsIndex = IndexPtr(rhs); + size_t numSubTbl = lsbf(rhsIndex->numberSubtables); + size_t cbCmapSz = sizeof(index) + sizeof(enc) * numSubTbl; + EncPtr encTbl = EncPtr(rhs + sizeof(index)); + + for (size_t iSubTbl = 0; iSubTbl < numSubTbl; iSubTbl++) + //for (int iSubTbl = numSubTbl - 1; iSubTbl >= 0; --iSubTbl) + { + const data16 *const mapTbl = reinterpret_cast<const data16 *>(rhs + lsbf((int)encTbl[iSubTbl].offset)); + int format = lsbf(*mapTbl); + switch (format) + { + // 16-bit lengths + case 0: + case 2: + case 4: + case 6: + cbCmapSz += lsbf((data16)unsigned(mapTbl[1])); + break; + // 32-bit lengths + case 8: + case 10: + case 12: + cbCmapSz += lsbf((int)unsigned(reinterpret_cast<const data32 *>(mapTbl)[1])); + break; + default: + Assert(false); + } + } + + // Do the comparison. + return (memcmp(lhs, rhs, cbCmapSz) ? false : true); +} + + +//:>******************************************************************************************** +//:> GrEngine class. +//:>******************************************************************************************** + +GrEngine::GrEngine() +{ + /////////////_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF); //////////////// + + + m_cref = 1; + BasicInit(); +} + +void GrEngine::BasicInit() +{ + m_pfface = NULL; + + m_ptman = NULL; + m_pctbl = NULL; + m_pgtbl = NULL; + m_prgpsd = NULL; + m_cpsd = 0; + m_cfeat = 0; + m_mFontEmUnits = -1; + m_dysOffset = 0; + + ////m_tbufCmap.reset(); + m_pCmap_3_1 = NULL; + m_pCmap_3_10 = NULL; + ////m_tbufNameTbl.reset(); + // new interface stuff: + m_pCmapTbl = NULL; + m_pNameTbl = NULL; + m_fCmapTblCopy = false; + m_fNameTblCopy = false; + + m_fLogXductn = false; + + m_resFontValid = kresInvalidArg; // not yet initialized + m_ferr = kferrUninitialized; + m_fxdBadVersion = 0; + + m_fSmartReg = false; + m_fSmartBold = false; + m_fSmartItalic = false; + m_fSmartBI = false; + m_fFakeItalicCache = false; + m_fFakeBICache = false; + m_strCtrlFileReg.erase(); + m_strCtrlFileBold.erase(); + m_strCtrlFileItalic.erase(); + m_strCtrlFileBI.erase(); + m_fFakeItalic = false; + m_stuCtrlFile.erase(); + m_stuInitError.erase(); + m_stuErrCtrlFile.erase(); + m_stuFaceName.erase(); + m_stuBaseFaceName.erase(); + m_fUseSepBase = false; + m_stuFeatures.erase(); + + m_nFontCheckSum = 0; + + m_fInErrorState = false; + + m_rglcidFeatLabelLangs = NULL; // initialize lazily, when needed + m_clcidFeatLabelLangs = 0; +} + +GrEngine::~GrEngine() +{ + DestroyEverything(); + #ifdef _MSC_VER + if (!_CrtCheckMemory()) + { + OutputDebugString(L"bad memory"); + } + #endif +} + +/*---------------------------------------------------------------------------------------------- + Clean everything out (destructor or reinitializing with a different face name). +----------------------------------------------------------------------------------------------*/ +void GrEngine::DestroyEverything() +{ + DestroyContents(); + #ifdef _MSC_VER + if (!_CrtCheckMemory()) + { + OutputDebugString(L"bad memory"); + } + #endif + + m_strCtrlFileReg.erase(); + m_strCtrlFileBold.erase(); + m_strCtrlFileItalic.erase(); + m_strCtrlFileBI.erase(); + m_fSmartReg = false; + m_fSmartBold = false; + m_fSmartItalic = false; + m_fSmartBI = false; + m_fFakeItalicCache = false; + m_fFakeBICache = false; + m_stuBaseFaceName.erase(); + + //m_stuFaceName.erase(); + //m_stuFaceName = L"this is not a usable or valid string"; + m_fUseSepBase = false; + m_stuFeatures.erase(); +} + +void GrEngine::DestroyContents(bool fDestroyCmap) +{ + if (fDestroyCmap) + { + ////m_tbufCmap.reset(); + m_pCmap_3_1 = NULL; + m_pCmap_3_10 = NULL; + + if (m_fCmapTblCopy) + delete[] m_pCmapTbl; + if (m_fNameTblCopy) + delete[] m_pNameTbl; + m_pCmapTbl = NULL; + m_pNameTbl = NULL; + m_fCmapTblCopy = false; + m_fNameTblCopy = false; + } + + delete m_ptman; + delete m_pctbl; + delete m_pgtbl; + delete[] m_prgpsd; + m_ptman = NULL; + m_pctbl = NULL; + m_pgtbl = NULL; + m_prgpsd = NULL; + + ////m_tbufNameTbl.reset(); + + m_stuCtrlFile.erase(); + m_stuInitError.erase(); + + m_resFontValid = kresInvalidArg; + m_ferr = kferrUninitialized; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the number of features. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::NumberOfFeatures_ff() +{ + return (size_t)m_cfeat; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the feature ID for the ifeat-th feature. +----------------------------------------------------------------------------------------------*/ +featid GrEngine::FeatureID_ff(size_t ifeat) +{ + return (featid)m_rgfeat[ifeat].ID(); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the index of the feature with the given ID. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::FeatureWithID_ff(featid fid) +{ + int ifeatRet; + /* GrFeature * pfeat = */ + FeatureWithID(fid, &ifeatRet); + return (size_t)ifeatRet; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the label for the ifeat-th feature. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::GetFeatureLabel_ff(size_t ifeat, lgid nLanguage, utf16 * rgchwLabel) +{ + std::wstring stu = m_rgfeat[ifeat].Label(this, nLanguage); + + int cch = stu.size(); + cch = min(cch, 127); // 1 char for zero-termination + std::copy(stu.data(), stu.data() + cch, rgchwLabel); + rgchwLabel[cch] = 0; + + return (cch > 0); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the index of the default setting. Return -1 if it cannot be found. +----------------------------------------------------------------------------------------------*/ +int GrEngine::GetFeatureDefault_ff(size_t ifeat) +{ + GrFeature * pfeat = m_rgfeat + ifeat; + int defaultValue = pfeat->DefaultValue(); + int cfset = pfeat->NumberOfSettings(); + int rgnSettings[100]; + pfeat->Settings(100, rgnSettings); + Assert(cfset < 100); // TODO: improve this + for (int ifset = 0; ifset < cfset; ifset++) + { + if (rgnSettings[ifset] == defaultValue) + return ifset; + } + return -1; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the number of settings for the ifeat-th feature. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::NumberOfSettings_ff(size_t ifeat) +{ + return m_rgfeat[ifeat].NumberOfSettings(); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the value of the ifset-th setting for the ifeat-th feature. +----------------------------------------------------------------------------------------------*/ +int GrEngine::GetFeatureSettingValue_ff(size_t ifeat, size_t ifset) +{ + return m_rgfeat[ifeat].NthSetting(ifset); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the UI label for the given feature setting. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::GetFeatureSettingLabel_ff(size_t ifeat, size_t ifset, lgid language, + utf16 * rgchwLabel) +{ + std::wstring stu = m_rgfeat[ifeat].NthSettingLabel(this, ifset, language); + + int cch = stu.size(); + cch = min(cch, 127); // 1 char for zero-termination + // Note: the wchar_t label was originally assigned from utf16 data, so although wchar_t is + // utf32 on some platforms the conversion back to utf16 should still give correct results. + std::copy(stu.data(), stu.data() + cch, rgchwLabel); + rgchwLabel[cch] = 0; + + return (cch > 0); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the number of languages that are possible among the feature labels. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::NumberOfFeatLangs_ff() +{ + SetUpFeatLangList(); + return m_clcidFeatLabelLangs; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the language LCID for the feature-label language with the given index. +----------------------------------------------------------------------------------------------*/ +short GrEngine::GetFeatLabelLang_ff(size_t ilang) +{ + SetUpFeatLangList(); + return m_rglcidFeatLabelLangs[ilang]; +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the number of supported languages. +----------------------------------------------------------------------------------------------*/ +size_t GrEngine::NumberOfLanguages_ff() +{ + return (size_t)m_langtbl.NumberOfLanguages(); +} + +/*---------------------------------------------------------------------------------------------- + For FontFace: return the language code for the language with the given index. +----------------------------------------------------------------------------------------------*/ +isocode GrEngine::GetLanguageCode_ff(size_t ilang) +{ + return m_langtbl.LanguageCode(ilang); +} + +/*---------------------------------------------------------------------------------------------- + Set up the list of all the languages that are present in the feature labels. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpFeatLangList() +{ + if (m_rglcidFeatLabelLangs) + return; + + int rgnNameIDs[kMaxFeatures]; + for (int ifeat = 0; ifeat < m_cfeat; ifeat++) + rgnNameIDs[ifeat] = m_rgfeat[ifeat].NameId(); + short rglcid[128]; // 128 is the number expected by TtfUtil::GetLangsForNames + m_clcidFeatLabelLangs = TtfUtil::GetLangsForNames(m_pNameTbl, 3, 1, rgnNameIDs, m_cfeat, rglcid); + m_rglcidFeatLabelLangs = new short[m_clcidFeatLabelLangs]; + memcpy(m_rglcidFeatLabelLangs, rglcid, sizeof(short) * m_clcidFeatLabelLangs); +} + +/*---------------------------------------------------------------------------------------------- + Return the maximum size needed for the block of data to pass between segments, that is, + to reinitialize the engine based on the results of the previously generated segment. + This value must match what is in GrTableManager::InitializeStreams() and + InitializeForNextSeg(). 256 is an absolute maximum imposed by the interface. + + In Graphite, what this block of data will contain is information about cross-line + contextualization and some directionality information. + + Assumes InitNew() has already been called to set the font name. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::get_SegDatMaxLength(int * pcb) +{ + ChkGrOutPtr(pcb); + + if (m_resFontValid == kresInvalidArg) + ReturnResult(kresUnexpected); // engine not initialized + + GrResult res = m_resFontValid; + if (m_resFontValid == kresFail || m_resFontValid == kresUnexpected || m_resFontValid == kresFalse) + res = kresOk; // invalid font + if (ResultFailed(res)) + ReturnResult(res); + + Assert(m_ptman); + if (!m_ptman) + *pcb = 256; + else + *pcb = m_ptman->NumberOfPasses() + 4; + + ReturnResult(res); +} + +/*---------------------------------------------------------------------------------------------- + @return The supported script direction(s). If more than one, the application is + responsible for choosing the most appropriate. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::get_ScriptDirection(unsigned int * pgrfsdc, OLECHAR * prgchwErrMsg, int cchMaxErrMsg) +{ + ChkGrOutPtr(pgrfsdc); + ChkGrArrayArg(prgchwErrMsg, cchMaxErrMsg); + + if (m_resFontValid == kresInvalidArg) + ReturnResult(kresUnexpected); // engine not initialized + + GrResult res = m_resFontValid; + + *pgrfsdc = m_grfsdc; + +// ClearFontError(prgchwErrMsg, cchMaxErrMsg); +// try +// { +// if (m_resFontValid == kresFail || m_resFontValid == kresUnexpected || m_resFontValid == kresFalse) +// { +// RecordFontLoadError(prgchwErrMsg, cchMaxErrMsg); +// res = kresOk; +// } +// *pgrfsdc = m_grfsdc; +// } +// catch (Throwable & thr) +// { +// res = (GrResult)thr.Error(); +// *pgrfsdc = kfsdcHorizLtr; +// } +// catch (...) +// { +// res = WARN(kresFail); +// *pgrfsdc = kfsdcHorizLtr; +// } + + ReturnResult(res); +} + +/*---------------------------------------------------------------------------------------------- + Get an glyph attribute from the engine that will help the GrJustifier in its work. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::getGlyphAttribute(int iGlyph, int jgat, int nLevel, float * pValueRet) +{ + return m_ptman->State()->GetGlyphAttrForJustification(iGlyph, jgat, nLevel, pValueRet); +} + +GrResult GrEngine::getGlyphAttribute(int iGlyph, int jgat, int nLevel, int * pValueRet) +{ + return m_ptman->State()->GetGlyphAttrForJustification(iGlyph, jgat, nLevel, pValueRet); +} + +/*---------------------------------------------------------------------------------------------- + Set an glyph attribute in the engine as a result of the decisions made by the + GrJustifier. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::setGlyphAttribute(int iGlyph, int jgat, int nLevel, float value) +{ + return m_ptman->State()->SetGlyphAttrForJustification(iGlyph, jgat, nLevel, value); +} + +GrResult GrEngine::setGlyphAttribute(int iGlyph, int jgat, int nLevel, int value) +{ + return m_ptman->State()->SetGlyphAttrForJustification(iGlyph, jgat, nLevel, value); +} + +//:>******************************************************************************************** +//:> Non-FieldWorks interface methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Make a complete segment that can be used to measure and do line-breaking. + + OBSOLETE - delete +----------------------------------------------------------------------------------------------*/ +/* +GrResult GrEngine::MakeMeasuredSegment( + Font * pfont, ITextSource * pgts, + int ichMin, int ichLim, + bool fParaRtl, + Segment ** ppsegRet, SegEnd * pest, + std::ostream * pstrmLog, + OLECHAR * prgchwErrMsg, int cchMaxErrMsg) +{ + int dichContext, dichLimSeg; + float dxWidth; + return MakeSegment(ksegmodeMsfr, + pfont, pgts, NULL, ichMin, ichLim, false, false, + 100000, klbClipBreak, ktwshAll, // not used + fParaRtl, + ppsegRet, &dxWidth, + 0, NULL, 0, NULL, NULL, &dichContext, // not used + pstrmLog, + prgchwErrMsg, cchMaxErrMsg, + -1, false, false, klbClipBreak, // not used + &dichLimSeg, pest, + false, 100000, kestMoreLines); // not used +} +*/ + +/*---------------------------------------------------------------------------------------------- + Make a segment from the given range, regardless of the kind of line-break that results. + + OBSOLETE - delete +----------------------------------------------------------------------------------------------*/ +/* +GrResult GrEngine::MakeSegmentFromRange( + Font * pfont, ITextSource * pgts, IGrJustifier * pgj, + int ichMin, int ichLim, + bool fStartLine, bool fEndLine, + TrWsHandling twsh, bool fParaRtl, + Segment ** ppsegRet, float * pdxWidth, SegEnd * pest, + int cbPrev, byte * pbPrevSegDat, int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + int * pdichwContext, + std::ostream * pstrmLog, + OLECHAR * prgchwErrMsg, int cchwMaxErrMsg) +{ + int dichLimSegBogus; + return MakeSegment(ksegmodeMsfr, pfont, pgts, pgj, + ichMin, ichLim, + fStartLine, fEndLine, + 10000, klbWordBreak, // not used + twsh, fParaRtl, + ppsegRet, pdxWidth, + //cbPrev, pbPrevSegDat, cbNextMax, pbNextSegDat, pcbNextSegDat, pdichwContext, + 0, NULL, 0, NULL, NULL, NULL, + pstrmLog, + prgchwErrMsg, cchwMaxErrMsg, + ichLim, false, false, klbClipBreak, // not used + &dichLimSegBogus, pest, + 10000, kestMoreLines); // not used +} +*/ + + +//:>******************************************************************************************** +//:> Other methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Create a segment. +----------------------------------------------------------------------------------------------*/ +void GrEngine::MakeSegment( + // Common parameters + Segment * psegNew, + Font * pfont, ITextSource * pgts, IGrJustifier * pjus, + LayoutEnvironment & layout, + int ichMin, int ichLim, + // for finding a break point + float dxMaxWidth, + bool fBacktracking, + // for justification + bool fJust, float dxJustifiedWidthJ, SegEnd estJ) +{ + ChkGrArgPtr(pgts); + ChkGrArgPtrN(pjus); + ChkGrOutPtr(psegNew); + Assert(layout.bestBreak() <= layout.worstBreak()); + + if (pjus == NULL) + pjus = layout.justifier(); + + if (m_resFontValid == kresInvalidArg) + // Uninitialized. + return; // ReturnResult(kresUnexpected); + + SetCmapAndNameTables(pfont); + + int segmode = ksegmodeRange; // segment from range + if (fJust) + segmode = ksegmodeJust; // justified segment + else if (dxMaxWidth < kPosInfFloat) + segmode = ksegmodeBreak; // find break point + + // Temporary: + if (!this->m_ptman) + { + Warn("No Graphite tables"); + m_ptman = new GrTableManager(this); + CreateEmpty(); + } + + // GrResult hr = kresOk; + m_fInErrorState = false; + + bool fBold, fItalic; + GetStyles(pfont, ichMin, &fBold, &fItalic); + + GetWritingSystemDirection(pgts, ichMin); + + // Find the end of the range to render with the current font. + int nDirDepth = 0; + int ichFontLim = FindFontLim(pgts, ichMin, &nDirDepth); + int ichStrmLim = 0; + int ichSegLim = 0; + switch (segmode) + { + case ksegmodeBreak: // find break point + // Here ichLim should be the end of the text-source, unless we're backtracking. + ichStrmLim = min(ichFontLim, ichLim); + ichSegLim = -1; // unknown + break; + case ksegmodeJust: // stretching an existing segment to achieve justification + ichStrmLim = ichFontLim; + ichSegLim = ichLim; + break; + //case ksegmodeMms: // MakeMeasuredSegment + // ichLim = ichFontLim; + // ichStrmLim = ichFontLim; + // ichSegLim = -1; // process to the end of the stream + // break; + case ksegmodeRange: // segment from range + ichLim = min(ichLim, ichFontLim); + ichStrmLim = ichFontLim; + ichSegLim = (ichLim == kPosInfinity) ? -1 : ichLim; + break; + default: + Assert(false); + } + + // Initialize the graphics object with the font and character properties. + // SILE + ////if (m_fUseSepBase) + ////{ + //// Assert(m_stuBaseFaceName.size() > 0); + //// SetUpGraphics(pgg, pgts, ichMin, m_stuBaseFaceName); + ////} + ////else + //// SetUpGraphics(pgg, pgts, ichMin, m_stuFaceName); + + GrResult res = kresOk; + + Assert(m_ptman); + m_ptman->State()->SetFont(pfont); + + // Create a character stream on the text source. + GrCharStream * pchstrm = new GrCharStream(pgts, ichMin, ichStrmLim, + layout.startOfLine(), layout.endOfLine()); + + // TODO: change vertical offset to work on a character-by-character basis + m_dysOffset = (pgts->getVerticalOffset(ichMin) * pfont->getDPIy()) / 72; + + // Run the tables and get back a segment. + + bool fMoreText = false; + int ichCallerBtLim = -1; + bool fInfiniteWidth = false; + switch (segmode) + { + //case ksegmodeMms: + case ksegmodeBreak: + case ksegmodeRange: + if (segmode == ksegmodeBreak) + { + fMoreText = ((ichFontLim < ichLim) || fBacktracking); + ichCallerBtLim = (fBacktracking) ? ichLim : -1; + fInfiniteWidth = false; + } + //else if (segmode == ksegmodeMms) + //{ + // fMoreText = false; + // ichCallerBtLim = -1; + // fInfiniteWidth = true; + // Assert(twsh == ktwshAll); + //} + else if (segmode == ksegmodeRange) + { + int ichTextLim; + ichTextLim = pgts->getLength(); + fMoreText = (ichLim < ichTextLim); + ichCallerBtLim = -1; + fInfiniteWidth = true; + } + try { + m_ptman->Run(psegNew, pfont, pchstrm, pjus, + ((pjus) ? kjmodiCanShrink : kjmodiNormal), + layout, + ichSegLim, dxMaxWidth, 0, + fBacktracking, fMoreText, ichFontLim, fInfiniteWidth, false, + ichCallerBtLim, + nDirDepth, estJ); + } + catch (...) + { + // Disastrous problem in rendering. + if (!layout.dumbFallback()) + throw; // throws original exception + + // Try dumb rendering. + if (m_fUseSepBase) + SwitchGraphicsFont(true); // use the base font, which has real glyphs + + m_fInErrorState = true; + res = RunUsingEmpty(psegNew, pfont, pchstrm, layout, + ichSegLim, + dxMaxWidth, + fBacktracking, fMoreText, fInfiniteWidth, + ichCallerBtLim, + nDirDepth, estJ); + } + break; + + case ksegmodeJust: // justified segment + { + m_ptman->Run(psegNew, pfont, pchstrm, pjus, kjmodiJustify, layout, + ichSegLim, + dxJustifiedWidthJ, dxMaxWidth, + false, false, // not used + ichFontLim, + true, // infinite width + false, // kludge + -1, + nDirDepth, estJ); + //&dxWidth, &est); + Assert((ichLim - ichMin) == (psegNew->stopCharacter() - psegNew->startCharacter())); + break; + } + + default: + Assert(false); + } + + delete pchstrm; + + if (m_fUseSepBase) + SwitchGraphicsFont(false); +} + +/*---------------------------------------------------------------------------------------------- + Return an indication of whether an error happened in trying to load Graphite tables. +----------------------------------------------------------------------------------------------*/ +FontErrorCode GrEngine::IsValidForGraphite(int * pnVersion, int * pnSubVersion) +{ + if (pnVersion) + *pnVersion = m_fxdBadVersion >> 16; + if (pnSubVersion) + *pnSubVersion = m_fxdBadVersion & 0x0000FFFF; + + return m_ferr; +} + +/*---------------------------------------------------------------------------------------------- + Record a system error indicating that the font could not be loaded properly. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void GrEngine::RecordFontLoadError(OLECHAR * prgchwErrMsg, int cchMax) +{ + if (prgchwErrMsg == NULL || cchMax == 0) + return; + + std::wstring stuMessage = L"Error in initializing Graphite font \""; + stuMessage.append(m_stuFaceName); + if (m_stuErrCtrlFile.size()) + { + stuMessage.append(L"\" ("); + stuMessage.append(m_stuErrCtrlFile); + stuMessage.append(L")"); + } + else + stuMessage.append(L"\""); + if (m_stuInitError.size()) + { + stuMessage.append(L"--\n"); + stuMessage.append(m_stuInitError); + } + + std::fill_n(prgchwErrMsg, cchMax, 0); + std::copy(stuMessage.data(), stuMessage.data() + min(cchMax - 1, signed(stuMessage.size())), + prgchwErrMsg); +} + +/*---------------------------------------------------------------------------------------------- + Record a system error indicating a bad error in rendering using a supposedly valid font. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void GrEngine::RecordFontRunError(OLECHAR * prgchwErrMsg, int cchMax, GrResult res) +{ + if (prgchwErrMsg == NULL || cchMax == 0) + return; + + std::wstring stuMessage = L"Error in rendering using Graphite font \""; + stuMessage.append(m_stuFaceName); + if (m_stuErrCtrlFile.size()) + { + stuMessage.append(L"\" ("); + stuMessage.append(m_stuErrCtrlFile); + stuMessage.append(L")"); + } + else + stuMessage.append(L"\""); + + std::fill_n(prgchwErrMsg, cchMax, 0); + std::copy(stuMessage.data(), stuMessage.data() + min(cchMax - 1, signed(stuMessage.size())), + prgchwErrMsg); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the font error message buffer to zero. This way we can be sure to recognize + an error condition by the fact that the buffer is non-zero. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void GrEngine::ClearFontError(OLECHAR * prgchwErrMsg, int cchMaxErrMsg) +{ + std::fill_n(prgchwErrMsg, cchMaxErrMsg, 0); +} + +/*---------------------------------------------------------------------------------------------- + Set the overall direction of the writing system based on the text properties. +----------------------------------------------------------------------------------------------*/ +void GrEngine::GetWritingSystemDirection(ITextSource * pgts, int ichwMin) +{ +#ifdef OLD_TEST_STUFF + // for test procedures: + if (m_stuInitialize == "RightToLeftLayout") + { + m_fRightToLeft = true; + return; + } +#endif // OLD_TEST_STUFF + + m_fRightToLeft = pgts->getRightToLeft(ichwMin); +} + +/*---------------------------------------------------------------------------------------------- + Find the end of the range that uses the current font. + + @param pgts - contains text to render + @param ichwMinFont - beginning of range to render with this font + @param pnDirDepth - return direction depth: 0=LTR, 1=RTL + + @return The lim of the range that could be rendered by this font. Also the direction depth. +----------------------------------------------------------------------------------------------*/ +int GrEngine::FindFontLim(ITextSource * pgts, int ichwMinFont, int * pnDirDepth) +{ + int ichwTextLen = (int)pgts->getLength(); + + int ichwLimRange; // range that can be rendered without breaking contextualization. + int ichwMinNext = ichwMinFont; + + while (true) + { + std::pair<toffset, toffset> pairRange = pgts->propertyRange(ichwMinNext); + // int ichwMinRun = pairRange.first; + int ichwLimRun = pairRange.second; + *pnDirDepth = pgts->getDirectionDepth(ichwMinNext); + + //if (ichwLim > -1 && ichwLim < ichwLimRun) + //{ + // // Stopping in the middle of a run + // return ichwLim; + //} + + // We can at least go to the end of this run. + ichwLimRange = ichwLimRun; + if (ichwLimRun >= ichwTextLen) + { + // Hit the end of the text-source + return ichwLimRange; + } + else + { + // If the following run is the same as this run except for color and underlining, + // we don't have to break here. + if (!pgts->sameSegment(ichwMinNext, ichwLimRun)) + return ichwLimRange; + } + ichwMinNext = ichwLimRun; + } + + return ichwLimRange; +} + +/*---------------------------------------------------------------------------------------------- + Read the version number from the tables, and return false if any are a version this + implementation of the engine can't handle. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::CheckTableVersions(GrIStream * pgrstrm, + const byte *pSilfTbl, int lSilfStart, + const byte *pGlobTbl, int lGlocStart, + const byte *pFeatTbl, int lFeatStart, + int * pfxdBadVersion) +{ + pgrstrm->OpenBuffer(pSilfTbl, isizeof(int)); + pgrstrm->SetPositionInFont(lSilfStart); + *pfxdBadVersion = ReadVersion(*pgrstrm); + pgrstrm->CloseBuffer(); + if (*pfxdBadVersion > kSilfVersion) + return false; + + pgrstrm->OpenBuffer(pGlobTbl, lGlocStart + isizeof(int)); + pgrstrm->SetPositionInFont(lGlocStart); + *pfxdBadVersion = ReadVersion(*pgrstrm); + pgrstrm->CloseBuffer(); + if (*pfxdBadVersion > kGlocVersion) + return false; + + pgrstrm->OpenBuffer(pFeatTbl, isizeof(int)); + pgrstrm->SetPositionInFont(lFeatStart); + *pfxdBadVersion = ReadVersion(*pgrstrm); + pgrstrm->CloseBuffer(); + if (*pfxdBadVersion > kFeatVersion) + return false; + + *pfxdBadVersion = 0; + return true; +} + +/*---------------------------------------------------------------------------------------------- + Reinterpret the version number from a font table. +----------------------------------------------------------------------------------------------*/ +int GrEngine::ReadVersion(GrIStream & grstrm) +{ + int fxdVersion = grstrm.ReadIntFromFont(); + + if (fxdVersion < 0x00010000) + fxdVersion = 0x00010000; // kludge for bug with which some fonts were generated + + return fxdVersion; +} + +/*---------------------------------------------------------------------------------------------- + Initialize the engine from the test file. Used for test procedures. +----------------------------------------------------------------------------------------------*/ +//:Ignore +#ifdef OLD_TEST_STUFF +void GrEngine::InitFromControlFileTest() +{ + IStreamPtr qstrm; + FileStream::Create(m_stuCtrlFile, STGM_READ, &qstrm); + + int nSilfOffset, nGlocOffset, nGlatOffset, nFeatOffset; + + nSilfOffset = ReadIntFromFont(qstrm); + nGlocOffset = ReadIntFromFont(qstrm); + nGlatOffset = ReadIntFromFont(qstrm); + nFeatOffset = ReadIntFromFont(qstrm); + nSill + + // Read the "Silf" table. + int chwMaxGlyphID; + int fxdVersion; + ReadSilfTable(qstrm, nSilOffset, 0, &chwMaxGlyphID, &fxdVersion); + + // Read the "Gloc" and "Glat" tables. + ReadGlocAndGlatTables(qstrm, nGlocOffset, qstrm, nGlatOffset, chwMaxGlyphID, fxdVersion); + + // Read the "Feat" table. + ReadFeatTable(qstrm, nFeatOffset); +} +#endif // OLD_TEST_STUFF +//:End Ignore + + +/*---------------------------------------------------------------------------------------------- + Return whether the text is asking for bold and/or italic text. +----------------------------------------------------------------------------------------------*/ +void GrEngine::GetStyles(Font * pfont, int ichwMin, bool * pfBold, bool * pfItalic) +{ + *pfBold = pfont->bold(); + *pfItalic = pfont->italic(); +} + +/*---------------------------------------------------------------------------------------------- + Switch the Graphics object between the Graphite table file and the base font. + Should only be called when we know we are using a base font, or when we are reading + the base font to see if it is valid. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SwitchGraphicsFont(bool fBase) +{ + Assert(!fBase || m_stuBaseFaceName.size() > 0); + + //LgCharRenderProps chrp; + //pgg->get_FontCharProperties(&chrp); + //if (fBase) + //{ + // wcsncpy(chrp.szFaceName, m_stuBaseFaceName.data(), m_stuBaseFaceName.size() + 1); + // chrp.szFaceName[31] = 0; + //} + //else + //{ + // wcsncpy(chrp.szFaceName, m_stuFaceName.data(), m_stuFaceName.size() + 1); + // chrp.szFaceName[31] = 0; + //} + //pgg->SetupGraphics(&chrp); +} + +/*---------------------------------------------------------------------------------------------- + Read the contents of the "Silf" table from the stream, which is on an extended + TrueType font file. Specifically, read the iSubTable-th sub-table (for now there is + only one). + + WARNING: any changes to this method must be accompanied by equivalent changes to + CreateEmpty(). +----------------------------------------------------------------------------------------------*/ +bool GrEngine::ReadSilfTable(GrIStream & grstrm, long lTableStart, int iSubTable, + int * pchwMaxGlyphID, int * pfxdSilfVersion) +{ + grstrm.SetPositionInFont(lTableStart); + + // version + *pfxdSilfVersion = ReadVersion(grstrm); + if (*pfxdSilfVersion > kSilfVersion) + // Version we don't know how to handle. + return false; + + if (*pfxdSilfVersion >= 0x00030000) + // compiler version + grstrm.ReadIntFromFont(); + + // number of tables + unsigned short cSubTables = grstrm.ReadUShortFromFont(); + Assert(cSubTables == 1); // for now + Assert(cSubTables <= kMaxSubTablesInFont); + if (cSubTables != 1 || cSubTables > kMaxSubTablesInFont) + return false; + + if (*pfxdSilfVersion >= 0x00020000) + // reserved + grstrm.ReadShortFromFont(); + + // subtable offsets + int nSubTableOffsets[kMaxSubTablesInFont]; + int i; + for (i = 0; i < cSubTables; i++) + { + nSubTableOffsets[i] = grstrm.ReadIntFromFont(); + } + + grstrm.SetPositionInFont(lTableStart + nSubTableOffsets[iSubTable]); + + // Now we are at the beginning of the desired sub-table. + + // Get the position of the start of the table. + long lSubTableStart; + grstrm.GetPositionInFont(&lSubTableStart); + + // rule version + int fxdRuleVersion = (*pfxdSilfVersion >= 0x00030000) ? + ReadVersion(grstrm) : + *pfxdSilfVersion; + + long lPassBlockPos = -1; + long lPseudosPos = -1; + if (*pfxdSilfVersion >= 0x00030000) + { + lPassBlockPos = grstrm.ReadUShortFromFont() + lSubTableStart; + lPseudosPos = grstrm.ReadUShortFromFont() + lSubTableStart; + } + + // maximum glyph ID + data16 chwTmp; + *pchwMaxGlyphID = grstrm.ReadUShortFromFont(); + + // extra ascent and descent + m_mXAscent = grstrm.ReadShortFromFont(); + m_mXDescent = grstrm.ReadShortFromFont(); + + // TODO: decide whether we want these: + m_mXAscent = 0; + m_mXDescent = 0; + + // number of passes + byte cPasses = grstrm.ReadByteFromFont(); + // index of first substitution pass + byte ipassSub1 = grstrm.ReadByteFromFont(); + // index of first positioning pass + byte ipassPos1 = grstrm.ReadByteFromFont(); + // index of first justification pass + byte ipassJust1 = grstrm.ReadByteFromFont(); + // index of first reordered pass, or 0xFF if no reordering + byte ipassReordered1 = grstrm.ReadByteFromFont(); + if (*pfxdSilfVersion < 0x00020000) + { + Assert(ipassJust1 == cPasses || ipassJust1 == ipassPos1); + ipassJust1 = ipassPos1; + } + + // Sanity checks. + if (cPasses > kMaxPasses || ipassSub1 > cPasses || ipassPos1 > cPasses) + return false; // bad table + + // line-break flag + int nLineBreak = grstrm.ReadByteFromFont(); + if (nLineBreak != 0 && nLineBreak != 1) + return false; // bad table + m_fLineBreak = (bool)nLineBreak; + + // range of possible cross-line-boundary contextualization + m_cchwPreXlbContext = grstrm.ReadByteFromFont(); + m_cchwPostXlbContext = grstrm.ReadByteFromFont(); + + // actual glyph ID for pseudo-glyph (ID of bogus attribute) + byte bTmp; // unsigned + bTmp = grstrm.ReadByteFromFont(); + m_chwPseudoAttr = bTmp; + // breakweight + bTmp = grstrm.ReadByteFromFont(); + m_chwBWAttr = bTmp; + // directionality + bTmp = grstrm.ReadByteFromFont(); + m_chwDirAttr = bTmp; + + // Sanity checks--don't bother with these, I don't seem to be able to know what are reasonable values. + //if (m_chwPseudoAttr > 200 || m_chwBWAttr > 200 || m_chwDirAttr > 200) + // return false; // bad table + + if (*pfxdSilfVersion >= 0x00020000) + { + // reserved + grstrm.ReadByteFromFont(); + grstrm.ReadByteFromFont(); + + // justification levels + m_cJLevels = grstrm.ReadByteFromFont(); + if (m_cJLevels > kMaxJLevels) + return false; // bad table + m_fBasicJust = (m_cJLevels == 0); + m_chwJStretch0 = 0xffff; // if no justification + m_chwJShrink0 = 0xffff; + m_chwJStep0 = 0xffff; + m_chwJWeight0 = 0xffff; + for (int i = 0; i < m_cJLevels; i++) + { + // justification glyph attribute IDs + bTmp = grstrm.ReadByteFromFont(); + if (i == 0) + m_chwJStretch0 = bTmp; + bTmp = grstrm.ReadByteFromFont(); + if (i == 0) + m_chwJShrink0 = bTmp; + bTmp = grstrm.ReadByteFromFont(); + if (i == 0) + m_chwJStep0 = bTmp; + bTmp = grstrm.ReadByteFromFont(); + if (i == 0) + m_chwJWeight0 = bTmp; + bTmp = grstrm.ReadByteFromFont(); // runto + // reserved + grstrm.ReadByteFromFont(); + grstrm.ReadByteFromFont(); + grstrm.ReadByteFromFont(); + } + } + else + { + m_cJLevels = 0; + m_fBasicJust = true; + m_chwJStretch0 = 0xffff; + m_chwJShrink0 = 0xffff; + m_chwJStep0 = 0xffff; + m_chwJWeight0 = 0xffff; + } + + // number of component attributes + chwTmp = grstrm.ReadUShortFromFont(); + m_cComponents = chwTmp; + + // number of user-defined slot attributes + m_cnUserDefn = grstrm.ReadByteFromFont(); + if (m_cnUserDefn > kMaxUserDefinableSlotAttrs) + return false; // bad table + + // max number of ligature components per glyph + m_cnCompPerLig = grstrm.ReadByteFromFont(); + if (m_cnCompPerLig > 16) + return false; // bad table + + // directions supported + bTmp = grstrm.ReadByteFromFont(); + m_grfsdc = bTmp; + if (m_grfsdc > kfsdcHorizLtr + kfsdcHorizRtl + kfsdcVertFromLeft + kfsdcVertFromRight) + return false; // bad table + + // reserved + bTmp = grstrm.ReadByteFromFont(); + bTmp = grstrm.ReadByteFromFont(); + bTmp = grstrm.ReadByteFromFont(); + + // critical features + int cCriticalFeatures; + if (*pfxdSilfVersion >= 0x00020000) + { + // reserved + bTmp = grstrm.ReadByteFromFont(); + + cCriticalFeatures = grstrm.ReadByteFromFont(); + Assert(cCriticalFeatures == 0); + if (cCriticalFeatures != 0) + return false; // bad table + + // reserved + bTmp = grstrm.ReadByteFromFont(); + } + + // rendering behaviors--ignore for now + byte cBehaviors = grstrm.ReadByteFromFont(); + unsigned int nBehaviors[kMaxRenderingBehavior]; + for (i = 0; i < cBehaviors; i++) + { + nBehaviors[i] = unsigned(grstrm.ReadIntFromFont()); + } + + // linebreak glyph ID + m_chwLBGlyphID = grstrm.ReadUShortFromFont(); + + // Jump to the beginning of the pass offset block, if we have this information. + if (*pfxdSilfVersion >= 0x00030000) + grstrm.SetPositionInFont(lPassBlockPos); + else + // Otherwise assume that's where we are! + Assert(lPassBlockPos == -1); + + // offsets to passes, relative to the start of this subtable; + // note that we read (cPasses + 1) of these + int nPassOffsets[kMaxPasses]; + for (i = 0; i <= cPasses; i++) + { + nPassOffsets[i] = grstrm.ReadIntFromFont(); + } + + // Jump to the beginning of the pseudo-glyph info block, if we have this information. + if (*pfxdSilfVersion >= 0x00030000) + grstrm.SetPositionInFont(lPseudosPos); + else + // Otherwise assume that's where we are! + Assert(lPseudosPos == -1); + + // number of pseudo-glyphs and search constants + short snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_cpsd = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_dipsdInit = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_cPsdLoop = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_ipsdStart = snTmp; + + // unicode-to-pseudo map + m_prgpsd = new GrPseudoMap[m_cpsd]; + for (i = 0; i < m_cpsd; i++) + { + if (*pfxdSilfVersion <= 0x00010000) + { + utf16 chwUnicode = grstrm.ReadUShortFromFont(); + m_prgpsd[i].SetUnicode(chwUnicode); + } + else + { + int nUnicode = grstrm.ReadIntFromFont(); + m_prgpsd[i].SetUnicode(nUnicode); + } + gid16 chwPseudo = grstrm.ReadUShortFromFont(); + m_prgpsd[i].SetPseudoGlyph(chwPseudo); + } + + // class table + m_pctbl = new GrClassTable(); + if (!m_pctbl->ReadFromFont(grstrm, *pfxdSilfVersion)) + return false; + + // passes + return m_ptman->CreateAndReadPasses(grstrm, *pfxdSilfVersion, fxdRuleVersion, + cPasses, lSubTableStart, nPassOffsets, + ipassSub1, ipassPos1, ipassJust1, ipassReordered1); +} + +/*---------------------------------------------------------------------------------------------- + Set up the engine with no Graphite smarts at all, just dummy tables. +----------------------------------------------------------------------------------------------*/ +void GrEngine::CreateEmpty() +{ + // Silf table + + m_mXAscent = 0; + m_mXDescent = 0; + + m_fLineBreak = false; + + // range of possible cross-line-boundary contextualization + m_cchwPreXlbContext = 0; + m_cchwPostXlbContext = 0; + + // Bogus attribute IDs: + m_chwPseudoAttr = 1; // actual glyph ID for pseudo-glyph (ID of bogus attribute) + m_chwBWAttr = 2; // breakweight + m_chwDirAttr = 3; // directionality + + m_cComponents = 0; // number of component attributes + + m_cnUserDefn = 0; // number of user-defined slot attributes + m_cnCompPerLig = 0; // max number of ligature components + + m_grfsdc = kfsdcHorizLtr; // supported script direction + + // linebreak glyph ID + m_chwLBGlyphID = 0xFFFE; + + // number of pseudo-glyphs and search constants + m_cpsd = 0; + m_dipsdInit = 0; + m_cPsdLoop = 0; + m_ipsdStart = 0; + + // class table + m_pctbl = new GrClassTable(); + m_pctbl->CreateEmpty(); + + // passes + if (m_ptman != 0) + m_ptman->CreateEmpty(); + + // Gloc and Glat tables + m_pgtbl = new GrGlyphTable(); + m_pgtbl->SetNumberOfGlyphs(0); + m_pgtbl->SetNumberOfComponents(0); + m_pgtbl->SetNumberOfStyles(1); // for now + + m_pgtbl->CreateEmpty(); + + // Feat table + m_cfeat = 0; + m_rglcidFeatLabelLangs = NULL; + m_clcidFeatLabelLangs = 0; + + // Language table + m_langtbl.CreateEmpty(); +} + +/*---------------------------------------------------------------------------------------------- + Create the glyph table and fill it in from the font stream. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::ReadGlocAndGlatTables(GrIStream & grstrmGloc, long lGlocStart, + GrIStream & grstrmGlat, long lGlatStart, + int chwGlyphIDMax, int fxdSilfVersion) +{ + // Determine the highest used glyph ID number. +// int chwGlyphIDMax = m_chwLBGlyphID; +// for (int ipsd = 0; ipsd < m_cpsd; ipsd++) +// { +// if (m_prgpsd[ipsd].PseudoGlyph() > chwGlyphIDMax) +// chwGlyphIDMax = m_prgpsd[ipsd].PseudoGlyph(); +// } + + // Create the glyph table. + m_pgtbl = new GrGlyphTable(); + m_pgtbl->SetNumberOfGlyphs(chwGlyphIDMax + 1); + m_pgtbl->SetNumberOfComponents(m_cComponents); + m_pgtbl->SetNumberOfStyles(1); // for now + + return m_pgtbl->ReadFromFont(grstrmGloc, lGlocStart, grstrmGlat, lGlatStart, + m_chwBWAttr, m_chwJStretch0, m_cJLevels, m_cnCompPerLig, fxdSilfVersion); +} + +/*---------------------------------------------------------------------------------------------- + Read the contents of the "Feat" table from the stream, which is on an extended + TrueType font file. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::ReadFeatTable(GrIStream & grstrm, long lTableStart) +{ + short snTmp; + data16 chwTmp; + int nTmp; + + grstrm.SetPositionInFont(lTableStart); + + // version + int fxdVersion = ReadVersion(grstrm); + if (fxdVersion > kFeatVersion) + return false; + + // number of features + int cfeat; + cfeat = grstrm.ReadUShortFromFont(); + if (cfeat > kMaxFeatures) + return false; // bad table; + + // reserved + chwTmp = grstrm.ReadUShortFromFont(); + nTmp = grstrm.ReadIntFromFont(); + + std::vector<featid> vnIDs; // unsigned ints + std::vector<int> vnOffsets; + std::vector<int> vcfset; + + m_cfeat = 0; + int ifeat; + for (ifeat = 0; ifeat < cfeat; ifeat++) + { + // ID + featid nID; + if (fxdVersion >= 0x00020000) + nID = (unsigned int)grstrm.ReadIntFromFont(); + else + nID = grstrm.ReadUShortFromFont(); + vnIDs.push_back(nID); + // number of settings + data16 cfset = grstrm.ReadUShortFromFont(); + vcfset.push_back(cfset); + if (fxdVersion >= 0x00020000) + grstrm.ReadShortFromFont(); // pad bytes + // offset to settings list + nTmp = grstrm.ReadIntFromFont(); + vnOffsets.push_back(nTmp); + // flags + chwTmp = grstrm.ReadUShortFromFont(); + Assert(chwTmp == 0x8000); // mutually exclusive + // index into name table of UI label + int nNameId = grstrm.ReadShortFromFont(); + + if (fxdVersion <= 0x00020000 && nID == GrFeature::knLangFeatV2) + { + // Ignore the obsolete "lang" feature which has ID = 1 + vnIDs.pop_back(); + vcfset.pop_back(); + vnOffsets.pop_back(); + continue; + } + AddFeature(nID, nNameId, cfset); + } + + // default feature setting value and name strings; + for (ifeat = 0; ifeat < m_cfeat; ifeat++) + { + GrFeature * pfeat = m_rgfeat + ifeat; + Assert(pfeat->ID() == vnIDs[ifeat]); + + grstrm.SetPositionInFont(lTableStart + vnOffsets[ifeat]); + + // Each feature has been initialized with blank spots for the number of settings + // it has. + int cfset = vcfset[ifeat]; + Assert(pfeat->NumberOfSettings() == cfset); + for (int ifset = 0; ifset < cfset; ifset++) + { + snTmp = grstrm.ReadShortFromFont(); // yes, signed (TT feat table spec says unsigned) + short snTmp2 = grstrm.ReadShortFromFont(); + pfeat->AddSetting(snTmp, snTmp2); + + if (ifset == 0) + { + // default setting value--first in list + pfeat->SetDefault(snTmp); + } + } + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Read the contents of the "Sill" table from the stream, which is on an extended + TrueType font file. +----------------------------------------------------------------------------------------------*/ +bool GrEngine::ReadSillTable(GrIStream & grstrm, long lTableStart) +{ + grstrm.SetPositionInFont(lTableStart); + + // version + int fxdVersion = ReadVersion(grstrm); + if (fxdVersion > kSillVersion) + return false; + + return m_langtbl.ReadFromFont(&grstrm, fxdVersion); +} + +/*---------------------------------------------------------------------------------------------- + An error happened in running the tables. Set up temporary dummy tables, and try running + with dumb rendering. + Return kresUnexpected if even running the empty tables crashed, kresFail otherwise. +----------------------------------------------------------------------------------------------*/ +GrResult GrEngine::RunUsingEmpty(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm, LayoutEnvironment & layout, + int ichStop, + float dxMaxWidth, + bool fNeedFinalBreak, bool fMoreText, bool fInfiniteWidth, + int ichwCallerBtLim, + int nDirDepth, SegEnd estJ) +{ + // Save the current state of the engine. + int mXAscentSave = m_mXAscent; + int mXDescentSave = m_mXDescent; + bool fLineBreakSave = m_fLineBreak; + int cchwPreXlbContextSave = m_cchwPreXlbContext; + int cchwPostXlbContextSave = m_cchwPostXlbContext; + data16 chwPseudoAttrSave = m_chwPseudoAttr; + data16 chwBWAttrSave = m_chwBWAttr; + data16 chwDirAttrSave = m_chwDirAttr; + int cComponentsSave = m_cComponents; + int cnUserDefnSave = m_cnUserDefn; + int cnCompPerLigSave = m_cnCompPerLig; + unsigned int grfsdcSave = m_grfsdc; + gid16 chwLBGlyphIDSave = m_chwLBGlyphID; + int cpsdSave = m_cpsd; + int dipsdInitSave = m_dipsdInit; + int cPsdLoopSave = m_cPsdLoop; + int ipsdStartSave = m_ipsdStart; + GrTableManager * ptmanSave = m_ptman; + GrClassTable * pctblSave = m_pctbl; + GrGlyphTable * pgtblSave = m_pgtbl; + int cfeatSave = m_cfeat; + GrResult resFontReadSave = m_resFontRead; + + GrResult res = kresFail; + + pchstrm->Restart(); + + // Create dummy versions of the tables and run them. + m_pctbl = NULL; + m_pgtbl = NULL; + m_ptman = new GrTableManager(this); + m_ptman->State()->SetFont(pfont); + CreateEmpty(); + + try + { + m_ptman->Run(psegNew, pfont, pchstrm, NULL, kjmodiNormal, layout, + ichStop, dxMaxWidth, 0, + fNeedFinalBreak, fMoreText, -1, fInfiniteWidth, false, + ichwCallerBtLim, + nDirDepth, estJ); + } + catch(...) + { + // Still didn't work. Oh, well, not much we can do. + res = kresUnexpected; + } + + psegNew->SetErroneous(true); + + if (m_ptman) + delete m_ptman; + if (m_pctbl) + delete m_pctbl; + if (m_pgtbl) + delete m_pgtbl; + + // Put everything back the way it was. + m_mXAscent = mXAscentSave; + m_mXDescent = mXDescentSave; + m_fLineBreak = fLineBreakSave; + m_cchwPreXlbContext = cchwPreXlbContextSave; + m_cchwPostXlbContext = cchwPostXlbContextSave; + m_chwPseudoAttr = chwPseudoAttrSave; + m_chwBWAttr = chwBWAttrSave; + m_chwDirAttr = chwDirAttrSave; + m_cComponents = cComponentsSave; + m_cnUserDefn = cnUserDefnSave; + m_cnCompPerLig = cnCompPerLigSave; + m_grfsdc = grfsdcSave; + m_chwLBGlyphID = chwLBGlyphIDSave; + m_cpsd = cpsdSave; + m_dipsdInit = dipsdInitSave; + m_cPsdLoop = cPsdLoopSave; + m_ipsdStart = ipsdStartSave; + m_ptman = ptmanSave; + m_pctbl = pctblSave; + m_pgtbl = pgtblSave; + m_cfeat = cfeatSave; + m_resFontRead = resFontReadSave; + + return res; +} + +/*---------------------------------------------------------------------------------------------- + Return the glyph ID for the given Unicode value. +----------------------------------------------------------------------------------------------*/ +gid16 GrEngine::GetGlyphIDFromUnicode(int nUnicode) +{ + gid16 chwGlyphID = MapToPseudo(nUnicode); + if (chwGlyphID != 0) + return chwGlyphID; // return a pseudo-glyph + +#ifdef OLD_TEST_STUFF + if (!m_pCmap31) + // test procedures + return chwUnicode; +#endif // OLD_TEST_STUFF + + // Otherwise, get the glyph ID from the font's cmap. + + if (m_pCmap_3_10) + return gid16(TtfUtil::Cmap310Lookup(m_pCmap_3_10, nUnicode)); + else if (m_pCmap_3_1) + return gid16(TtfUtil::Cmap31Lookup(m_pCmap_3_1, nUnicode)); + else + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the pseudo-glyph corresponding to the given Unicode input, or 0 if none. +----------------------------------------------------------------------------------------------*/ +gid16 GrEngine::MapToPseudo(int nUnicode) +{ + if (m_cpsd == 0) + return 0; + +#ifdef _DEBUG + int nPowerOf2 = 1; + while (nPowerOf2 <= m_cpsd) + nPowerOf2 <<= 1; + nPowerOf2 >>= 1; + // Now nPowerOf2 is the max power of 2 <= m_cpds + Assert((1 << m_cPsdLoop) == nPowerOf2); // m_cPsdLoop == log2(nPowerOf2) + Assert(m_dipsdInit == nPowerOf2); + Assert(m_ipsdStart == m_cpsd - m_dipsdInit); +#endif // _DEBUG + + int dipsdCurr = m_dipsdInit; + + GrPseudoMap * ppsdCurr = m_prgpsd + m_ipsdStart; + while (dipsdCurr > 0) + { + int nTest; + if (ppsdCurr < m_prgpsd) + nTest = -1; + else + nTest = ppsdCurr->Unicode() - nUnicode; + + if (nTest == 0) + return ppsdCurr->PseudoGlyph(); + + dipsdCurr >>= 1; // divide by 2 + if (nTest < 0) + ppsdCurr += dipsdCurr; + else // (nTest > 0) + ppsdCurr -= dipsdCurr; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the actual glyph ID to use for glyph metrics and output. For most glyphs, this + is just the glyph ID we're already working with, but for pseudo-glyphs it will something + different. +----------------------------------------------------------------------------------------------*/ +gid16 GrEngine::ActualGlyphForOutput(gid16 chwGlyphID) +{ + gid16 chwActual = gid16(GlyphAttrValue(chwGlyphID, m_chwPseudoAttr)); + if (chwActual == 0) + return chwGlyphID; // not a pseudo, we're already working with the actual glyph ID + else + return chwActual; +} + +/*---------------------------------------------------------------------------------------------- + Look up name based on lang id and name id in name table. + Currently used to look up feature names. +----------------------------------------------------------------------------------------------*/ +std::wstring GrEngine::StringFromNameTable(int nLangID, int nNameID) +{ + std::wstring stuName; + stuName.erase(); + size_t lOffset = 0; + size_t lSize = 0; + + // The Graphite compiler stores our names in either + // the MS (platform id = 3) Unicode (writing system id = 1) table + // or the MS Symbol (writing system id = 0) table. Try MS Unicode first. + // lOffset & lSize are in bytes. + // new interface: + if (!TtfUtil::GetNameInfo(m_pNameTbl, 3, 1, nLangID, nNameID, lOffset, lSize)) + { + if (!TtfUtil::GetNameInfo(m_pNameTbl, 3, 0, nLangID, nNameID, lOffset, lSize)) + { + return stuName; + } + } + + size_t cchw = (unsigned(lSize) / sizeof(utf16)); + utf16 * pchwName = new utf16[cchw+1]; // lSize - byte count for Uni str + const utf16 *pchwSrcName = reinterpret_cast<const utf16*>(m_pNameTbl + lOffset); + std::transform(pchwSrcName, pchwSrcName + cchw, pchwName, std::ptr_fun<utf16,utf16>(lsbf)); + pchwName[cchw] = 0; // zero terminate + #ifdef _WIN32 + stuName.assign((const wchar_t*)pchwName, cchw); + #else + wchar_t * pchwName32 = new wchar_t[cchw+1]; // lSize - byte count for Uni str + for (int i = 0; i <= signed(cchw); i++) { + pchwName32[i] = pchwName[i]; + } + stuName.assign(pchwName32, cchw); + delete [] pchwName32; + #endif + + delete [] pchwName; + return stuName; +} + +/*---------------------------------------------------------------------------------------------- + Add a feature (eg, when loading from the font). +----------------------------------------------------------------------------------------------*/ +void GrEngine::AddFeature(featid nID, int nNameId, int cfset, int nDefault) +{ + if (m_cfeat >= kMaxFeatures) + return; + + int ifeat = m_cfeat; + m_rgfeat[ifeat].Initialize(nID, nNameId, cfset, nDefault); +// m_hmnifeat.Insert(nID, ifeat); + m_cfeat++; +} + +/*---------------------------------------------------------------------------------------------- + Return the feature with the given ID, or NULL if none. +----------------------------------------------------------------------------------------------*/ +GrFeature * GrEngine::FeatureWithID(featid nID, int * pifeat) +{ + for (int ifeat = 0; ifeat < m_cfeat; ifeat++) + { + if (m_rgfeat[ifeat].ID() == nID) + { + *pifeat = ifeat; + return m_rgfeat + ifeat; + } + } + *pifeat = -1; + return NULL; + + + // Alternate implementation if we end up with so many features that we need to use + // a hash map: +// int ifeat; +// if (m_hmnifeat.Retrieve(nID, &ifeat)) +// { +// Assert(ifeat < kMaxFeatures); +// return m_rgfeat + ifeat; // may be NULL +// } +// else +// return NULL; +} + +/*---------------------------------------------------------------------------------------------- + Return the default for the feature at the given index in the array. +----------------------------------------------------------------------------------------------*/ +int GrEngine::DefaultForFeatureAt(int ifeat) +{ + Assert(ifeat < kMaxFeatures); + if (ifeat >= m_cfeat || ifeat < 0) + return 0; // undefined feature + + return m_rgfeat[ifeat].DefaultValue(); +} + +/*---------------------------------------------------------------------------------------------- + Set the default for the feature at the given index in the array. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetDefaultForFeatureAt(int ifeat, int nValue) +{ + Assert(ifeat < kMaxFeatures); + Assert(ifeat < m_cfeat); + if (ifeat >= 0) + m_rgfeat[ifeat].SetDefault(nValue); +} + +/*---------------------------------------------------------------------------------------------- + Return the default feature settings for the given language. +----------------------------------------------------------------------------------------------*/ +void GrEngine::DefaultsForLanguage(isocode lgcode, + std::vector<featid> & vnFeats, std::vector<int> & vnValues) +{ + m_langtbl.LanguageFeatureSettings(lgcode, vnFeats, vnValues); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the directionality and breakweight attributes of a new slot based on its + glyph ID. + REVIEW: we could optimize by not initializing from the glyph table right away, + but waiting until we need the value. +----------------------------------------------------------------------------------------------*/ +void GrEngine::InitSlot(GrSlotState * pslot, int nUnicode) +{ + gid16 chwGlyphID = pslot->GlyphID(); + + if (m_ptman->InternalJustificationMode() != kjmodiNormal) + { + if (m_cJLevels > 0) + { + Assert(m_cJLevels == 1); // for now + // TODO: do this at level 0. + pslot->SetJStretch(m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwJStretch0)); + pslot->SetJShrink( m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwJShrink0)); + pslot->SetJStep( m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwJStep0)); + pslot->SetJWeight( m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwJWeight0)); + } + else if (nUnicode == knSpace) + { + // Basic stretch/shrink for whitespace. + // TODO: do this at level 0. + Assert(m_fBasicJust); + int mAdv = pslot->AdvanceX(m_ptman); + pslot->SetJStretch(mAdv * 100); // stretch up to 100 times natural width + pslot->SetJShrink(mAdv / 4); // shrink to 75% + pslot->SetJWeight(1); + } + } + + if (m_pgtbl && !m_pgtbl->IsEmpty()) + { + // Initialize from glyph table. + pslot->SetBreakWeight(m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwBWAttr)); + + DirCode dirc = DirCode(m_pgtbl->GlyphAttrValue(chwGlyphID, m_chwDirAttr)); + if (BidiCode(nUnicode) && (chwGlyphID == 0 || dirc == kdircNeutral)) + // A built-in character for the bidi algorithm that is not defined in the font but + // must be given a special directionality: + goto LDefaults; + + pslot->SetDirectionality(dirc); + + return; + } + +LDefaults: + // Otherwise, in fill defaults for some basic values. + if (pslot->BreakWeight() == GrSlotState::kNotYetSet8) + { + switch (nUnicode) + { + case knSpace: + pslot->SetBreakWeight(klbWordBreak); + break; + case knHyphen: + pslot->SetBreakWeight(klbHyphenBreak); + break; + default: + pslot->SetBreakWeight(klbLetterBreak); + } + } + + if ((int)pslot->Directionality() == GrSlotState::kNotYetSet8) + { + switch (nUnicode) + { + case knSpace: + pslot->SetDirectionality(kdircWhiteSpace); + break; + case knLRM: + pslot->SetDirectionality(kdircL); + break; + case knRLM: + pslot->SetDirectionality(kdircR); + break; + case knLRO: + pslot->SetDirectionality(kdircLRO); + break; + case knRLO: + pslot->SetDirectionality(kdircRLO); + break; + case knLRE: + pslot->SetDirectionality(kdircLRE); + break; + case knRLE: + pslot->SetDirectionality(kdircRLE); + break; + case knPDF: + pslot->SetDirectionality(kdircPDF); + break; + default: + if (chwGlyphID == LBGlyphID()) + pslot->SetDirectionality(kdircNeutral); + else + pslot->SetDirectionality(kdircL); + } + } + else + Assert(false); // currently no way to set directionality ahead of time +} + +/*---------------------------------------------------------------------------------------------- + We have just changed this slot's glyph ID. Change the directionality and breakweight + attributes of a new slot based on its glyph ID. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetSlotAttrsFromGlyphAttrs(GrSlotState * pslot) +{ + InitSlot(pslot); +} + +/*---------------------------------------------------------------------------------------------- + Create a new segment. +----------------------------------------------------------------------------------------------*/ +void GrEngine::NewSegment(Segment ** ppseg) +{ + *ppseg = new Segment; +} + +/*---------------------------------------------------------------------------------------------- + Methods to pass on to the tables. +----------------------------------------------------------------------------------------------*/ +gid16 GrEngine::GetClassGlyphIDAt(int nClass, int nIndex) +{ + if (nIndex < 0) + return 0; + if (nClass < 0) + return 0; + + try { + return m_pctbl->GetGlyphID(nClass, nIndex); + } + catch (...) + { + return 0; + } +} + +int GrEngine::GetIndexInGlyphClass(int nClass, gid16 chwGlyphID) +{ + try { + return m_pctbl->FindIndex(nClass, chwGlyphID); + } + catch (...) + { + return -1; + } +} + +size_t GrEngine::NumberOfGlyphsInClass(int nClass) +{ + try { + return m_pctbl->NumberOfGlyphsInClass(nClass); + } + catch (...) + { + return 0; + } +} + +int GrEngine::GlyphAttrValue(gid16 chwGlyphID, int nAttrID) +{ +#ifdef OLD_TEST_STUFF + if (!m_pgtbl) // test procedures + return 0; +#endif // OLD_TEST_STUFF + + return m_pgtbl->GlyphAttrValue(chwGlyphID, nAttrID); +} + +int GrEngine::ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID) +{ +#ifdef OLD_TEST_STUFF + if (!m_pgtbl) // test procedures + return -1; +#endif // OLD_TEST_STUFF + + return m_pgtbl->ComponentIndexForGlyph(chwGlyphID, nCompID); +} + + +//:>******************************************************************************************** +//:> For test procedures +//:>******************************************************************************************** + +//:Ignore + +#ifdef OLD_TEST_STUFF + +void GrEngine::SetUpPseudoGlyphTest() +{ + m_cpsd = 5; + + m_prgpsd = new GrPseudoMap[m_cpsd]; + + m_prgpsd[0].SetUnicode(65); + m_prgpsd[0].SetPseudoGlyph(1065); + + m_prgpsd[1].SetUnicode(66); + m_prgpsd[1].SetPseudoGlyph(1066); + + m_prgpsd[2].SetUnicode(67); + m_prgpsd[2].SetPseudoGlyph(1067); + + m_prgpsd[3].SetUnicode(68); + m_prgpsd[3].SetPseudoGlyph(1068); + + m_prgpsd[4].SetUnicode(69); + m_prgpsd[4].SetPseudoGlyph(1069); + + m_dipsdInit = 4; + m_cPsdLoop = 2; + m_ipsdStart = 1; +} + +#endif // OLD_TEST_STUFF + + +//:>******************************************************************************************** +//:> New interface +//:>******************************************************************************************** +/*---------------------------------------------------------------------------------------------- + Store the default features values as specified by the client. +----------------------------------------------------------------------------------------------*/ +void GrEngine::AssignDefaultFeatures(int cfeat, FeatureSetting * prgfset) +{ + for (int ifeatIn = 0; ifeatIn < cfeat; ifeatIn++) + { + int ifeat; + FeatureWithID(prgfset[ifeatIn].id, &ifeat); + if (ifeat >= 0 ) + SetDefaultForFeatureAt(ifeat, prgfset[ifeatIn].value); + } +} + +bool GrEngine::LoggingTransduction() +{ + Assert(false); + //return m_pfface->getLogging(); + //return m_fLogXductn; + return true; +} + +} // namespace gr + +//:End Ignore + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrEngine.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrEngine.h new file mode 100644 index 00000000000..6dc270a38af --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrEngine.h @@ -0,0 +1,622 @@ +/*-------------------------------------------------------------------- +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrEngine.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Contains the definition of the GrEngine class. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_ENGINE_INCLUDED +#define GR_ENGINE_INCLUDED +#include <limits> + +#ifdef _MSC_VER +#include <crtdbg.h> +#endif + +namespace gr +{ +class IGrJustifier; +class Segment; + + +/*---------------------------------------------------------------------------------------------- + The GrEngine serves as the top level object that knows how to run Graphite tables + and generate Graphite segments. + + Primarily, this class implements IRenderEngine, which allows it to serve as a FW + rendering engine. It also implements ISimpleInit, a general interface for initializing + using a string. Finally, it implements ITraceControl, a very simple interface which + allows a client to flip a flag indicating whether or not we want to output a log of + the Graphite transduction process. + + Hungarian: greng +----------------------------------------------------------------------------------------------*/ +class GrEngine : public GraphiteProcess +{ + friend class FontFace; + friend class FontMemoryUsage; + +public: + // Constructors & destructors: + GrEngine(); + void BasicInit(); + virtual ~GrEngine(); + void DestroyContents(bool fDestroyCmap = true); + + // ISimpleInit methods: + ////GrResult InitNew(const byte * prgb, int cb); -- obsolete + ////GrResult get_InitializationData(int cchMax, OLECHAR * rgchw, int * pcch); -- obsolete + + // ITraceControl methods: + //GrResult SetTracing(int n); + //GrResult GetTracing(int * pnOptions); + + // IRenderingFeatures methods: + //GrResult GetFeatureIDs(int cMax, int * prgFids, int * pcfid); + //GrResult GetFeatureLabel(int fid, int nLanguage, int cchMax, OLECHAR * rgchw, int * pcch); + //GrResult GetFeatureValues(int fid, int cfvalMax, + // int * prgfval, int * pcfval, int * pfvalDefault); + //GrResult GetFeatureValueLabel(int fid, int fval, int nLanguage, + // int cchMax, OLECHAR * rgchw, int * pcch); + + // IRenderEngine methods: + ////GrResult InitRenderer(IGrGraphics * pgg, OLECHAR * prgchData, int cchData); -- obsolete + ////GrResult FontIsValid(OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + GrResult get_SegDatMaxLength(int * pcb); + + //GrResult FindBreakPoint(Font * pfont, ITextSource * pgts, DELETE + // int ichMin, int ichLim, int ichLimBacktrack, + // bool fNeedFinalBreak, + // bool fStartLine, + // float dxMaxWidth, + // LineBrk lbPref, LineBrk lbMax, + // TrWsHandling twsh, bool fParaRtoL, + // Segment ** ppsegRet, + // int * pdichwLimSeg, float * pdxWidth, SegEnd * pest, + // int cbPrev, byte * pbPrevSegDat, + // int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + // int * pdichwContext, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + //GrResult FindBreakPointWJust(Font * pfont, ITextSource * pgts, IGrJustifier * pgjus, + // int ichMin, int ichLim, int ichLimBacktrack, DELETE + // bool fNeedFinalBreak, + // bool fStartLine, + // float dxMaxWidth, + // LineBrk lbPref, LineBrk lbMax, + // TrWsHandling twsh, bool fParaRtoL, + // Segment ** ppsegRet, + // int * pdichwLimSeg, float * pdxWidth, SegEnd * pest, + // int cbPrev, byte * pbPrevSegDat, + // int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + // int * pdichwContext, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + GrResult get_ScriptDirection(unsigned int * pgrfsdc, OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + // DELETE + //GrResult MakeJustifiedSegment(Font * pfont, ITextSource * pgts, IGrJustifier * pgj, + // int ichMin, int ichLim, + // bool fStartLine, bool fEndLine, + // float dxUnjustifiedWidth, float dxJustifiedWidth, + // TrWsHandling twsh, SegEnd est, LineBrk lb, + // bool fParaRtl, + // Segment ** ppsegRet, float * pdxWidth, + // //int cbPrev, byte * pbPrevSegDat, int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, int * pdichwContext, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchwMaxErrMsg); + + // IJustifyingRenderer methods: + // These methods return kresInvalidArg if the attribute ID is invalid or inappropriate; + // kresFail if the engine is not in an appropriate state to return the information. + virtual GrResult getGlyphAttribute(int iGlyph, int jgat, int nLevel, float * pValueRet); + virtual GrResult getGlyphAttribute(int iGlyph, int jgat, int nLevel, int * pValueRet); + virtual GrResult setGlyphAttribute(int iGlyph, int jgat, int nLevel, float value); + virtual GrResult setGlyphAttribute(int iGlyph, int jgat, int nLevel, int value); + + // Interface methods not used by FW: + + ////GrResult IsGraphiteFont(IGrGraphics * pgg); -- obsolete + + ////GrResult FontAscentAndDescent(IGrGraphics * pgg, int * pysAscent, int * pysDescent); -- obsolete + + //GrResult MakeMeasuredSegment( + // Font * pfont, ITextSource * pgts, + // int ichMin, int ichLim, + // bool fParaRtl, + // Segment ** ppsegRet, SegEnd * pest, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + //GrResult MakeSegmentFromRange(Font * pfont, ITextSource * pgts, IGrJustifier * pgj, + // int ichMin, int ichLim, + // bool fStartLine, bool fEndLine, + // TrWsHandling twsh, bool fParaRtl, + // Segment ** ppsegRet, float * pdxWidth, SegEnd * pest, + // int cbPrev, byte * pbPrevSegDat, int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + // int * pdichwContext, + // std::ostream * pstrmLog, + // OLECHAR * prgchwErrMsg, int cchwMaxErrMsg); + + // NEW interface + + GrResult ReadFontTables(Font * pfont, bool fItalic); + + FontErrorCode IsValidForGraphite(int * pnVersion, int * pnSubVersion); + + // Other public methods + + GrResult FindBreakPointAux(Font * pfont, ITextSource * pgts, IGrJustifier * pgjus, + int ichwMin, int ichwLim, int ichwLimBacktrack, + bool fNeedFinalBreakArg, bool fStartLine, bool fEndLine, + float dxMaxWidth, bool fWidthIsCharCount, + LineBrk lbPref, LineBrk lbMax, TrWsHandling twsh, bool fParaRtl, + Segment ** ppsegRet, + int * pdichwLimSeg, + float * pdxWidth, SegEnd * pest, + int cbPrev, byte * pbPrevSegDat, + int cbNextMax, byte * pbNextSegDat, int * pcbNextSegDat, + int * pdichwContext, + std::ostream * pstrmLog, + OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + + gid16 GetGlyphIDFromUnicode(int nUnicode); + gid16 ActualGlyphForOutput(utf16 chwGlyphID); + + int GetFontEmUnits() + { + return m_mFontEmUnits; + } + + gid16 LBGlyphID() + { + return m_chwLBGlyphID; + } + + gid16 GetClassGlyphIDAt(int nClass, int nIndex); + int GetIndexInGlyphClass(int nClass, gid16 chwGlyphID); + size_t NumberOfGlyphsInClass(int nClass); + int GlyphAttrValue(gid16 chwGlyphID, int nAttrID); + int ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID); + + ////void InitFontName(std::wstring stuInitialize, std::wstring & stuFaceName, std::wstring & stuFeatures); -- obsolete + void RecordFontLoadError(OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + void RecordFontRunError(OLECHAR * prgchwErrMsg, int cchMaxErrMsg, GrResult res); + void ClearFontError(OLECHAR * prgchwErrMsg, int cchMaxErrMsg); + void GetWritingSystemDirection(ITextSource * pgts, int ichwMin); + ////GrResult SetUp(IGrGraphics * pgg, std::wstring stuFeatures); -- obsolete + ////GrResult SetUp(IGrGraphics * pgg, std::wstring stuFeaturesArg, bool fBold, bool fItalic); -- obsolete + + GrResult RunUsingEmpty(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm, LayoutEnvironment & layout, + int ichStop, + float dxMaxWidth, + bool fNeedFinalBreak, bool fMoreText, bool fInfiniteWidth, + int ichwCallerBtLim, + int nDirDepth, SegEnd estJ); + bool InErrorState() + { + return m_fInErrorState; + } + + std::wstring FaceName() + { + return m_stuFaceName; + } + std::wstring BaseFaceName() + { + Assert(m_stuBaseFaceName.size() == 0 || m_fUseSepBase); + return m_stuBaseFaceName; // empty if not using separate base font + } + bool Bold() + { + return m_fBold; + } + bool Italic() + { + return m_fItalic; + } + + void GetStyles(Font * pfont, int ichwMin, bool * pfBold, bool * pfItalic); + void SwitchGraphicsFont(bool fBase); + + void AddFeature(featid nID, int nNameId, int cfset, int nDefault = 0); + GrFeature * FeatureWithID(featid nID, int * pifeat); + int DefaultForFeatureAt(int ifeat); + void SetDefaultForFeatureAt(int ifeat, int nValue); + GrFeature * Feature(int ifeat) + { + if (ifeat >= kMaxFeatures) + return NULL; + return m_rgfeat + ifeat; + } + void DefaultsForLanguage(isocode lgcode, + std::vector<featid> & vnFeats, std::vector<int> & vnValues); + std::wstring StringFromNameTable(int nNameId, int nLang); + + void InitSlot(GrSlotState *, int nUnicode = -1); + void SetSlotAttrsFromGlyphAttrs(GrSlotState *); + + bool RightToLeft() + { + return m_fRightToLeft; + } + int TopDirectionLevel() + { + return (m_fRightToLeft) ? 1 : 0; + } + + GrGlyphTable * GlyphTable() + { + return m_pgtbl; + } + + GrClassTable * ClassTable() + { + return m_pctbl; + } + + int NumFeat() + { + return m_cfeat; + } + + bool LineBreakFlag() + { + return m_fLineBreak; + } + + int PreXlbContext() + { + return m_cchwPreXlbContext; + } + + int PostXlbContext() + { + return m_cchwPostXlbContext; + } + + int ExtraAscent() + { + return m_mXAscent; + } + + int ExtraDescent() + { + return m_mXDescent; + } + + float VerticalOffset() + { + return m_dysOffset; + } + + int NumUserDefn() + { + return m_cnUserDefn; + } + + int NumCompPerLig() + { + return m_cnCompPerLig; + } + + bool BasicJustification() + { + return m_fBasicJust; + } + + bool LoggingTransduction(); + //{ + // return m_pfface->getLogging(); + // //return m_fLogXductn; + //} + + bool FakeItalic() + { + return m_fFakeItalic; + } + + void NewSegment(Segment ** ppseg); + + static int ReadVersion(GrIStream & grstrm); + + /*---------------------------------------------------------------------------------------------- + Function to convert between coordinate systems. + This is identical to the built-in MulDiv function except that it rounds with better + precision. + + TODO: move these out of GrEngine to someplace more general. + ----------------------------------------------------------------------------------------------*/ + inline static int GrIntMulDiv(const int v, const int n, const int d) + { + return int(n < 0 ? + (double(v * n)/double(d)) - 0.5 : + (double(v * n)/double(d)) + 0.5); + } + inline static float GrFloatMulDiv(const float v, const float n, const float d) + { + return v * n / d; + } + inline static float GrIFIMulDiv(const int v, const float n, const int d) + { + return float(double(v) * n / double(d)); + } + inline static int GrFIFMulDiv(const float v, const int n, const float d) + { + return int(n < 0 ? + (v * double(n) / double(d)) - 0.5 : + (v * double(n) / double(d)) + 0.5); + } + inline static int RoundFloat(const float n) + { + return int(n < 0 ? n - 0.5 : n + 0.5); + } + +protected: + // Member variables: + + long m_cref; + + //////////// Font information //////////// + + bool m_fBold; + bool m_fItalic; + + // do we have the capacity to perform smart rendering for the various styles? + bool m_fSmartReg; + bool m_fSmartBold; + bool m_fSmartItalic; + bool m_fSmartBI; + // are the italics faked using a slant? + bool m_fFakeItalicCache; + bool m_fFakeBICache; + // control files for styled text; empty means not yet determined + std::wstring m_strCtrlFileReg; // regular + std::wstring m_strCtrlFileBold; // bold + std::wstring m_strCtrlFileItalic; // italic + std::wstring m_strCtrlFileBI; // bold-italic + + bool m_fFakeItalic; // true if we are simulating an italic slant in positioning + + std::wstring m_stuCtrlFile; // control file (.ttf file) from which we got + // currently loaded files + + std::wstring m_stuInitError; // description of any error that happened while initializing + // the engine + std::wstring m_stuErrCtrlFile; // file for which the error occurred + + std::wstring m_stuFaceName; // eg, "Times New Roman" + std::wstring m_stuFeatures; + + bool m_fUseSepBase; + std::wstring m_stuBaseFaceName; // from Sile table; empty if we are not using a separate + // control file + + GrResult m_resFontRead; // kresOk if the Graphite font was loaded successfully; + // kresFail if the font could not be found or basic tables couldn't be read; + // kresFalse if the Silf table is not present; + // kresUnexpected if the Graphite tables could not be loaded + + GrResult m_resFontValid; // kresInvalidArg if the engine is not yet initialized + // Originally: + // kresOk if Graphite font & dc font match cmaps + // kresUnexpected/kresFalse if dumb rendering + // kresFail if dumb rendering with no cmap + // But now it is apparently just = to m_resFontRead otherwise. + FontErrorCode m_ferr; + int m_fxdBadVersion; + + int m_nFontCheckSum; // when loading a font using the GrGraphics, remember the + // check sum as a unique identifer + + int m_nScriptTag; // from the pertinent writing system; currently not used + unsigned int m_grfsdc; // supported script directions + + bool m_fRightToLeft; // overall writing-system direction + + int m_mXAscent; // extra ascent, in font's em-units + int m_mXDescent; // extra descent, in font's em_units + + float m_dysOffset; // vertical offset, in logical units (eg, for super/subscript) + + bool m_fBasicJust; // true if there are no complex justification tables/rules in the + // font, so we use basic whitespace justification + int m_cJLevels; // number of justification levels + + GrTableManager * m_ptman; + GrClassTable * m_pctbl; + GrGlyphTable * m_pgtbl; + + FontFace * m_pfface; + + GrFeature m_rgfeat[kMaxFeatures]; +// HashMap<int, int> m_hmnifeat; // maps from feature IDs to indices in the array +// // Review: do we need this, or is a linear search adequate? + int m_cfeat; // number of features present + + short * m_rglcidFeatLabelLangs; // LCIDs (language IDs) for all the labels in the feature table + size_t m_clcidFeatLabelLangs; + + GrLangTable m_langtbl; + + // are line-breaks relevant at all? + bool m_fLineBreak; + // ranges for possible cross-line-boundary contextualization + int m_cchwPreXlbContext; + int m_cchwPostXlbContext; + + // magic glyph attribute numbers + data16 m_chwPseudoAttr; // actual-for-pseudo fake glyph attribute + data16 m_chwBWAttr; // break-weight + data16 m_chwDirAttr; // directionality + data16 m_chwJStretch0; // justify.0.stretch + data16 m_chwJShrink0; // justify.0.shrink + data16 m_chwJStep0; // justify.0.step + data16 m_chwJWeight0; // justify.0.weight + + gid16 m_chwLBGlyphID; // magic line-break glyph ID + + int m_cComponents; // number of glyph attributes at the beginning of the glyph table + // that actually represent ligature components + + int m_cnUserDefn; // number of user-defined slot attributes + int m_cnCompPerLig; // max number of components needed per ligature + + int m_mFontEmUnits; // number of design units in the Em square for the font + + // for pseudo-glyph mappings + int m_cpsd; // number of psuedo-glyphs + GrPseudoMap * m_prgpsd; + int m_dipsdInit; // (max power of 2 <= m_cpsd); + // size of initial range to consider + int m_cPsdLoop; // log2(max power of 2 <= m_cpsd); + // indicates how many iterations are necessary + int m_ipsdStart; // m_cpsd - m_dipsdInit; + // where to start search + + // for Unicode to glyph ID mapping + ////TableBuffer m_tbufCmap; // hold the full cmap table + void * m_pCmap_3_1; // point to the MS Unicode cmap subtable + // uses platform 3 writing system 1 or 0, preferably 1 + // use for Unicode to Glyph ID conversion + void * m_pCmap_3_10; + byte * m_pCmapTbl; + bool m_fCmapTblCopy; + int m_cbCmapTbl; // needed only for memory instrumentation + + // for feature names and maybe other strings from font later + ////TableBuffer m_tbufNameTbl; // hold full name table; use Name() method to access + byte * m_pNameTbl; + bool m_fNameTblCopy; + int m_cbNameTbl; // needed only for memory instrumentation + + bool m_fLogXductn; // true if we want to log the transduction process + + bool m_fInErrorState; // true if we are handling a rendering error + + // Other protected methods: + + enum { + ksegmodeBreak = 0, // FindBreakPoint + ksegmodeJust, // MakeJustifiedSegment + //ksegmodeMms, // MakeMeasuredSegment + ksegmodeRange // MakeSegmentFromRange + }; + + void MakeSegment( + // Common parameters + Segment * psegRet, + Font * pfont, ITextSource * pgts, IGrJustifier * pjus, + LayoutEnvironment & layout, + int ichMin, int ichLim, + // for finding a line break + float dxMaxWidth, + bool fBacktracking, + // for making a justified segment + bool fJust, float dxJustifiedWidthJ, SegEnd estJ); + + int FindFontLim(ITextSource * pgts, int ichFontMin, int * pnDirDepth); + +#ifdef GR_FW + ////GrResult InitFromControlFile(IGrGraphics * pgg, std::wstring stuFaceName, + //// bool fBold, bool fItalic); -- obsolete + ////GrResult ReadFromControlFile(std::wstring stuFontFile); -- obsolete +// GrResult GetFallBackFont(std::wstring * pstu); +#endif + + ////GrResult ReadFromGraphicsFont(IGrGraphics * pgg, bool fItalic); -- obsolete + void DestroyEverything(); + + bool CheckTableVersions(GrIStream * pgrstrm, + const byte *silf_tbl, int lSilfStart, + const byte *gloc_tbl, int lGlocStart, + const byte *feat_tbl, int lFeatStart, + int * pfxdBadVersion); + //bool ReadSileTable(GrIStream & grstrm, long lTableSTart, + // int * pmFontEmUnits, bool * pfMismatchedBase); + bool ReadSilfTable(GrIStream & grstrm, long lTableStart, int iSubTable, + int * pchwMaxGlyphID, int * pfxdVersion); + bool ReadGlocAndGlatTables(GrIStream & grstrm, long lGlocStart, GrIStream & glat_strm, long lGlatStart, + int chwMaxGlyphID, int fxdSilfVersion); + bool ReadFeatTable(GrIStream & grstrm, long lTableStart); + bool ReadSillTable(GrIStream & grstrm, long lTableStart); + bool SetCmapAndNameTables(Font * pfont); + + void CreateEmpty(); + + bool BadFont(FontErrorCode * pferr = NULL) + { + if (pferr) + *pferr = m_ferr; + return (m_resFontValid == kresFail || m_resFontRead == kresFail); + } + bool DumbFallback(FontErrorCode * pferr = NULL) + { + if (pferr) + *pferr = m_ferr; + return (m_resFontValid != kresOk || m_resFontRead != kresOk); + } + + gid16 MapToPseudo(int nUnicode); + + // NEW interface + void AssignDefaultFeatures(int cfeat, FeatureSetting * prgfset); + + // Feature access for FontFace: + size_t NumberOfFeatures_ff(); + featid FeatureID_ff(size_t ifeat); + size_t FeatureWithID_ff(featid id); + bool GetFeatureLabel_ff(size_t ifeat, lgid language, utf16 * label); + int GetFeatureDefault_ff(size_t ifeat); + size_t NumberOfSettings_ff(size_t ifeat); + int GetFeatureSettingValue_ff(size_t ifeat, size_t ifset); + bool GetFeatureSettingLabel_ff(size_t ifeat, size_t ifset, lgid language, utf16 * label); + // Feature-label languages: + size_t NumberOfFeatLangs_ff(); + short GetFeatLabelLang_ff(size_t ilang); + // Language access for FontFace: + size_t NumberOfLanguages_ff(); + isocode GetLanguageCode_ff(size_t ilang); + + void SetUpFeatLangList(); + +//:Ignore +#ifdef OLD_TEST_STUFF + // For test procedures: + void SetUpCrossLineContextTest(); + void SetUpPseudoGlyphTest(); + void SetUpRuleActionTest(); + void SetUpRuleAction2Test(); + void SetUpAssocTest(); + void SetUpAssoc2Test(); + void SetUpDefaultAssocTest(); + void SetUpFeatureTest(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); + + void InitFromControlFileTest(); +#endif // OLD_TEST_STUFF +//:End Ignore +}; + +} // namespace gr + +#if defined(GR_NO_NAMESPACE) +using namespace gr; +#endif + + +#endif // !GR_ENGINE_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFSM.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFSM.cpp new file mode 100644 index 00000000000..53182edab0d --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFSM.cpp @@ -0,0 +1,526 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrFSM.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the finite state machine. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#include <cstring> +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Fill in the FSM by reading from the font stream. + Assumes the stream is in the correct position. +----------------------------------------------------------------------------------------------*/ +bool GrFSM::ReadFromFont(GrIStream & grstrm, int fxdVersion) +{ + short snTmp; + + // number of FSM states + snTmp = grstrm.ReadShortFromFont(); + m_crow = snTmp; + // number of transitional states + snTmp = grstrm.ReadShortFromFont(); + int crowTransitional = snTmp; + // number of success states + snTmp = grstrm.ReadShortFromFont(); + int crowSuccess = snTmp; + m_crowFinal = m_crow - crowTransitional; + m_crowNonAcpt = m_crow - crowSuccess; + m_rowFinalMin = crowTransitional; + // number of columns + snTmp = grstrm.ReadShortFromFont(); + m_ccol = snTmp; + + // Sanity checks. + if (crowTransitional > m_crow || crowSuccess > m_crow) + return false; // bad table + // TODO: add a sanity check for m_ccol. + + // number of FSM glyph ranges and search constants + snTmp = grstrm.ReadShortFromFont(); + m_cmcr = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_dimcrInit = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_cLoop = snTmp; + snTmp = grstrm.ReadShortFromFont(); + m_imcrStart = snTmp; + + m_prgmcr = new GrFSMClassRange[m_cmcr]; + for (int imcr = 0; imcr < m_cmcr; imcr++) + { + m_prgmcr[imcr].m_chwFirst = grstrm.ReadUShortFromFont(); + m_prgmcr[imcr].m_chwLast = grstrm.ReadUShortFromFont(); + m_prgmcr[imcr].m_col = grstrm.ReadUShortFromFont(); + } + + // rule map and offsets (extra item at end gives final offset, ie, total) + m_prgirulnMin = new data16[crowSuccess + 1]; + data16 * pchw = m_prgirulnMin; + int i; + for (i = 0; i < (crowSuccess + 1); i++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // Last offset functions as the total length of the rule list. + // Note that the number of rules in the map is not necessarily equal to the number of + // rules in the pass; some rules may be listed multiply, if they are matched by more + // than one state. + int crulInMap = m_prgirulnMin[crowSuccess]; + m_prgrulnMatched = new data16[crulInMap]; + m_crulnMatched = crulInMap; + pchw = m_prgrulnMatched; + for (i = 0; i < crulInMap; i++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // min rule pre-context number of items + byte bTmp = grstrm.ReadByteFromFont(); + m_critMinRulePreContext = bTmp; + // max rule pre-context number of items + bTmp = grstrm.ReadByteFromFont(); + m_critMaxRulePreContext = bTmp; + + if (m_critMinRulePreContext > kMaxSlotsPerRule || m_critMaxRulePreContext > kMaxSlotsPerRule) + return false; // bad table + + int cStartStates = m_critMaxRulePreContext - m_critMinRulePreContext + 1; + + // start states + m_prgrowStartStates = new short[cStartStates]; + short * psn = m_prgrowStartStates; + for (int ic = 0; ic < cStartStates; ic++, psn++) + { + *psn = grstrm.ReadShortFromFont(); + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Fill in the FSM's state table by reading from the font stream. + Assumes the stream is in the correct position. +----------------------------------------------------------------------------------------------*/ +bool GrFSM::ReadStateTableFromFont(GrIStream & grstrm, int fxdVersion) +{ + int cCells = ((m_crow - m_crowFinal) * m_ccol); + m_prgrowTransitions = new short[cCells]; + short * psn = m_prgrowTransitions; + for (int iCell = 0; iCell < cCells; iCell++, psn++) + { + *psn = grstrm.ReadShortFromFont(); + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Use the transition table to find a rule or rules that match; then run the contraint + code to see if the rule is active. Return the rule number of the rule to be applied. + + @param ppass - pass, which knows how to run constraint code + @param psstrmIn - input stream + @param psstrmOut - output stream (from which to get recently output slots) +----------------------------------------------------------------------------------------------*/ +int GrFSM::GetRuleToApply(GrTableManager * ptman, GrPass * ppass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // List of accepting states and corresponding count of slots that were + // matched by each. + int prgrowAccepting[kMaxSlotsPerRule]; + int prgcslotMatched[kMaxSlotsPerRule]; + + // Run the transition table until it jams. + int crowAccepting = RunTransitionTable(ppass, psstrmIn, psstrmOut, + prgrowAccepting, prgcslotMatched); + + // Nothing matched; fail quickly. + if (crowAccepting == 0) + return -1; + + int * prow = prgrowAccepting + crowAccepting - 1; + int * pcslot = prgcslotMatched + crowAccepting - 1; // # of slots matched AFTER the curr pos + + // Quick test for common case of exactly one rule matched. + if (crowAccepting == 1 && + (m_prgirulnMin[*prow - m_crowNonAcpt + 1] - m_prgirulnMin[*prow - m_crowNonAcpt]) == 1) + { + int iruln = m_prgirulnMin[*prow - m_crowNonAcpt]; + int ruln = m_prgrulnMatched[iruln]; + if (RunConstraintAndRecordLog(ptman, ppass, ruln, psstrmIn, psstrmOut, + ppass->PreModContextForRule(ruln), *pcslot)) + { + return ruln; + } + else + return -1; + } + + // Several rules were matched. We need to sort them and then run the constraints + // for each rule, accepting the first one whose constraints succeed. + + // Count up the number of rules that matched and allocate a buffer of the right size. + int crulMatched = 0; + while (prow >= prgrowAccepting) + { + crulMatched += + m_prgirulnMin[*prow - m_crowNonAcpt + 1] - m_prgirulnMin[*prow - m_crowNonAcpt]; + --prow; + } + MatchedRule * prgmr = new MatchedRule[crulMatched]; + + prow = prgrowAccepting + crowAccepting - 1; + + // Do a dumb insertion sort, ordering primarily by sort key (largest first) + // and secondarily by the order in the original file (rule number). + // Review: is this kind of sort fast enough? We assuming that we usually only + // have a handful of matched rules. + int cmr = 0; + while (prow >= prgrowAccepting) + { + int iruln = m_prgirulnMin[*prow - m_crowNonAcpt]; + // Get all the rules matches by a single state. + while (iruln < m_prgirulnMin[*prow - m_crowNonAcpt + 1]) + { + int ruln = m_prgrulnMatched[iruln]; + int nSortKey = ppass->SortKeyForRule(ruln); + int cslot = *pcslot; + int imr; + for (imr = 0; imr < cmr; imr++) + { + if (nSortKey > prgmr[imr].nSortKey || + (nSortKey == prgmr[imr].nSortKey && ruln < prgmr[imr].ruln)) + { + // Insert here. + memmove(prgmr+imr+1, prgmr+imr, (isizeof(MatchedRule) * (cmr - imr))); + prgmr[imr].ruln = ruln; + prgmr[imr].nSortKey = nSortKey; + prgmr[imr].cslot = cslot; + break; + } + } + if (imr >= cmr) + { + prgmr[cmr].ruln = ruln; + prgmr[cmr].nSortKey = nSortKey; + prgmr[cmr].cslot = cslot; + } + cmr++; + + iruln++; + } + --prow; + --pcslot; + } + + + for (int imr = 0; imr < cmr; imr++) + { + int ruln = prgmr[imr].ruln; + if (RunConstraintAndRecordLog(ptman, ppass, ruln, psstrmIn, psstrmOut, + ppass->PreModContextForRule(ruln), prgmr[imr].cslot)) + { + // Success! + delete[] prgmr; + return ruln; + } + } + + // No rule matched or the constraints failed. + delete[] prgmr; + return -1; +} + +/*---------------------------------------------------------------------------------------------- + Run the transition table, generating a list of rows corresponding to accepting states. + + @param psstrmIn - input stream + @param prgrowAccepting - array to fill in with accepting rows + @param prgcslotMatched - corresponding array with number of slots matched +----------------------------------------------------------------------------------------------*/ +int GrFSM::RunTransitionTable(GrPass * ppass, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int * prgrowAccepting, int * prgcslotMatched) +{ + int ipass = ppass->PassNumber(); + + // Remember we're reading the pre-context items from the output stream, not the input. + int cslotPreContextAvail = psstrmOut->WritePos(); + + if (cslotPreContextAvail < m_critMinRulePreContext) + return 0; // not enough input to match any rule + + // Look backwards by the number of pre-context items for this pass, but not earlier + // than the beginning of the stream. + int cslot = -1 * min(cslotPreContextAvail, m_critMaxRulePreContext); + + // Normally the start state is state 0. But if we are near the beginning of the stream, + // we may need to adjust due to the fact that we don't have enough input yet to + // match the pre-context items in all the rules. So in effect we skip some states + // corresponding to the earlier items that aren't present. + int row = m_prgrowStartStates[max(0, m_critMaxRulePreContext - cslotPreContextAvail)]; + + int * prowTop = prgrowAccepting; + int * pcslotTop = prgcslotMatched; + while (true) + { + if (row >= m_rowFinalMin) + // final state--jammed + return (prowTop - prgrowAccepting); + + if (psstrmIn->SlotsPendingInContext() <= cslot) + // no more input + return (prowTop - prgrowAccepting); + + // Figure out what column the input glyph belong in. + // Since this is a time-critical routine, we cache the results for the current pass. + GrSlotState * pslot = (cslot < 0) ? + psstrmOut->PeekBack(cslot) : + psstrmIn->Peek(cslot); + int col; + if (pslot->PassNumberForColumn() == ipass) + { + col = pslot->FsmColumn(); + } + else + { + gid16 chwGlyphID = (cslot < 0) ? + psstrmOut->PeekBack(cslot)->GlyphID() : + psstrmIn->Peek(cslot)->GlyphID(); + col = FindColumn(chwGlyphID); + pslot->CacheFsmColumn(ipass, col); + } + int rowNext; + if (col < 0) + rowNext = 0; + else + rowNext = m_prgrowTransitions[(row * m_ccol) + col]; + + if (rowNext == 0) // jammed + return (prowTop - prgrowAccepting); + + cslot++; + if (rowNext >= m_crowNonAcpt) // or (rowNext < 0) + { + // Accepting state--push it on the stack. + //rowNext *= -1; + gAssert((prowTop - prgrowAccepting) < kMaxSlotsPerRule); + *prowTop++ = rowNext; + *pcslotTop++ = cslot; + } + row = rowNext; + + gAssert((prowTop - prgrowAccepting) == (pcslotTop - prgcslotMatched)); + } +} + +/*---------------------------------------------------------------------------------------------- + Run the transition table, generating a list of rows corresponding to accepting states. + Optimized to avoid matrix arithmetic for non-accepting states. + + Review: do we need to use this version? + + @param psstrmIn - input stream + @param prgrowAccepting - array to fill in with accepting rows + @param prgcslotMatched - corresponding array with number of slots matched +----------------------------------------------------------------------------------------------*/ +#if 0 + +int GrFSM::RunTransitionTableOptimized(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int * prgrowAccepting, int * prgcslotMatched) +{ + int cslot = 0; + int ichwRowOffset = 0; + int ichwRowOffsetLim = m_crowFinal * m_ccol; // number of cells + int * prowTop = prgrowAccepting; + int * pcslotTop = prgcslotMatched; + while (true) + { + if (ichwRowOffset >= ichwRowOffsetLim) + // final state--jammed + return (prowTop - prgrowAccepting); + + if (psstrmIn->SlotsPendingInContext() < cslot) + // no more input + return (prowTop - prgrowAccepting); + + gid16 chwGlyphID = psstrmIn->Peek(cslot)->GlyphID(); + int col = FindColumn(chwGlyphID); + short cellValue = *(m_prgprgrowXitions + ichwRowOffset + col); + + if (cellValue == 0) // jammed + return (prowTop - prgrowAccepting); + + cslot++; + if (cellValue < 0) + { + // Accepting state--push it on the stack. + int rowNext = cellValue * -1; + gAssert((prowTop - prgrowAccepting) < kMaxSlotsPerRule); + *prowTop++ = rowNext; + *pcslotTop++ = cslot; + ichwRowOffset = rowNext * m_ccol; + } + else + ichwRowOffset = cellValue; + + gAssert((prowTop - prgrowAccepting) == (pcslotTop - prgcslotMatched)); + } +} + +#endif // 0 + + +/*---------------------------------------------------------------------------------------------- + Run all the constraints for the rules matched by the state that we have reached. + The rules are ordered as they were in the GDL file. When we hit a constraint that + matches, return the corresponding rule number. Return -1 if no rule passed. + + @param ppass - pass, which knows how to actually run the constraints + @param psstrmIn - input stream + @param cslotMatched - number of slots matched; constraints must be run for each of these +----------------------------------------------------------------------------------------------*/ +//:Ignore +int GrFSM::RunConstraints_Obsolete(GrTableManager * ptman, GrPass * ppass, int row, + GrSlotStream * psstrmIn, int cslotMatched) +{ + int iruln = m_prgirulnMin[row - m_crowNonAcpt]; + while (iruln < m_prgirulnMin[row - m_crowNonAcpt + 1]) + { + int ruln = m_prgrulnMatched[iruln]; + bool fSuccess = ppass->RunConstraint(ptman, ruln, psstrmIn, NULL, 0, cslotMatched); + if (fSuccess) + { + if (ptman->LoggingTransduction()) + ppass->RecordRuleFired(psstrmIn->ReadPosForNextGet(), ruln); + return ruln; + } + else + { + if (ptman->LoggingTransduction()) + ppass->RecordRuleFailed(psstrmIn->ReadPosForNextGet(), ruln); + } + + iruln++; + } + return -1; // no rule succeeded +} +//:End Ignore + +/*---------------------------------------------------------------------------------------------- + Run the constraints for the given rule, and record whether it failed or succeeded in the + transduction log. + + @param ppass - pass, which knows how to actually run the constraints + @param psstrmIn - input stream + @param cslotPreModContext - number of slots before the current position that must be tested + @param islotLimMatched - number of slots matched AFTER the current position + + @return True if the constraints succeeded. +----------------------------------------------------------------------------------------------*/ +bool GrFSM::RunConstraintAndRecordLog(GrTableManager * ptman, GrPass * ppass, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotPreModContext, int islotLimMatched) +{ + bool fSuccess = ppass->RunConstraint(ptman, ruln, psstrmIn, psstrmOut, + cslotPreModContext, islotLimMatched); + + if (ptman->LoggingTransduction()) + { + if (fSuccess) + ppass->RecordRuleFired(psstrmIn->ReadPosForNextGet(), ruln); + else + ppass->RecordRuleFailed(psstrmIn->ReadPosForNextGet(), ruln); + } + + return fSuccess; +} + +/*---------------------------------------------------------------------------------------------- + Search for the class range containing the glyph ID, and return the matching FSM column. + Does a fast binary search that uses only shifts, no division. +----------------------------------------------------------------------------------------------*/ +int GrFSM::FindColumn(gid16 chwGlyphID) +{ + #ifdef _DEBUG + // Check that the pre-calculated constants are correct. + int nPowerOf2 = 1; + while (nPowerOf2 <= m_cmcr) + nPowerOf2 <<= 1; + nPowerOf2 >>= 1; + // Now nPowerOf2 is the max power of 2 <= m_cclsrg. + // m_cLoop is not needed for our purposes, but it is included because it is part of + // the TrueType standard. + gAssert(1 << m_cLoop == nPowerOf2); // m_cloop == log2(nPowerOf2) + gAssert(m_dimcrInit == nPowerOf2); + gAssert(m_imcrStart == m_cmcr - m_dimcrInit); + #endif + + int dimcrCurr = m_dimcrInit; + + GrFSMClassRange * pmcrCurr = m_prgmcr + m_imcrStart; + while (dimcrCurr > 0) + { + int nTest; + if (pmcrCurr < m_prgmcr) + nTest = -1; + else + { + nTest = pmcrCurr->m_chwFirst - chwGlyphID; + if (nTest < 0 && chwGlyphID <= pmcrCurr->m_chwLast) + //nTest = 0; + // This is a tiny bit more efficient: + return pmcrCurr->m_col; + } + + if (nTest == 0) + // found it + return pmcrCurr->m_col; + + dimcrCurr >>= 1; // divide by 2 + if (nTest < 0) + pmcrCurr += dimcrCurr; + else // (nTest > 0) + pmcrCurr -= dimcrCurr; + } + + return -1; +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFSM.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFSM.h new file mode 100644 index 00000000000..b1909553daa --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFSM.h @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrFSM.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrFSM class, which is the mechanism that examines input in a glyph stream and + determines which rule matches and should be run. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef FSM_INCLUDED +#define FSM_INCLUDED + +//:End Ignore + +namespace gr +{ + +class GrPass; + +/*---------------------------------------------------------------------------------------------- + A machine class range consists of a range of contiguous glyph IDs that map to + a single column in the FSM (a machine class). (Note that there might be more than one + range mapping to a given column.) + + Keep in mind that these "classes" are completely different from the classes that + are used for substitution. These are used only for matching by the FSM. + + Hungarian: mcr +----------------------------------------------------------------------------------------------*/ +class GrFSMClassRange +{ + friend class GrFSM; + +protected: + gid16 m_chwFirst; // first glyph ID in range + gid16 m_chwLast; // last glyph ID in range + data16 m_col; // column (machine class) to which this range maps +}; + +/*---------------------------------------------------------------------------------------------- + The finite state machine that is used to match sequences of glyphs and + determine which rule to apply. There is one FSM per pass. + + The states in the FSM are grouped and ordered in the following way: + - non-accepting states (no rule completely matched) + - accepting non-final states (a rule matched, but a longer rule is possible) + - final states (a rule matched and no longer rule is possible) + In other words, we have: + - transition, non-accepting + - transition, accepting + - non-transition, accepting + The transition states have information about the next state to transition to; + the accepting states have information about the rule(s) that matched. + + There are three main data structures that are part of the finite state machine: + + (1) the transition matrix: m_prgprgrowXitions. It contains a row for each transition + state (non-accepting states plus accepting non-final states), and a column for each + machine class. The cell values indicate the next state to transition to for the + matched input. A positive number indicates that the next state is a non-accepting state; + a negative value indicates an accepting state. Zero means there is no next state; + no more matches are possible; machine has "jammed." + + (2) the matched-rule list: m_prgrulnMatched. This is a list of rule numbers, + which are indices into the pass's action- and constraint-code arrays. + It contains the numbers of the rules matched by the first accepting state, + followed by those matched by the second accepting state, etc. For each state, + the rules must be ordered using the same order of the rules in the RDL file. + + (3) the matched-rule-offsets list: m_prgirulnMin. This gives the starting index into + the matched-rule list for each accepting state. (Non-accepting states are not included, + so the first item is for state m_crowNonAcpt.) + + In addition, the FSM interacts with the action- and constraint-code lists in the pass + itself. These lists are indexed by rule number (the values of m_prgrulnMatched). + When some input matches the rule, the constraint-code is run; if it succeeds, + the rule number is returned to the caller and that rule is applied (ie, the action-code + is run). + + Hungarian: fsm + + Other hungarian: + row - row (state) + col - column (machine class) + ruln - rule number + + REVIEW: This is quite a time-critical class, and there are two potential optimizations + that have been proposed for the value of a cell: + + (1) use a negative cell number to indicate an accepting state and a positive number + to indicate a non-accepting state. So this gives a comparison with zero rather than + some arbitrary number + + (2) have the positive numbers--the most common case--be the actual byte offset into the + table rather than the row number. This saves matrix multiplication at each step, + ie, "m_prgsnTransitions[(row * m_ccol) + col]". + + The two versions are implemented in RunTransitionTable and RunTransitionTableOptimized. + Do these seem to be worth the inconvenience of added complexity in understanding + and debugging? +----------------------------------------------------------------------------------------------*/ +class GrFSM +{ + friend class FontMemoryUsage; + +public: + GrFSM() : + m_prgirulnMin(NULL), + m_prgrulnMatched(NULL), + m_prgrowTransitions(NULL), + m_prgibStateDebug(NULL), + m_prgmcr(NULL), + m_prgrowStartStates(NULL) + { + } + + ~GrFSM() + { + delete[] m_prgirulnMin; + delete[] m_prgrulnMatched; + + delete[] m_prgrowTransitions; + + delete[] m_prgibStateDebug; + + delete[] m_prgmcr; + + delete[] m_prgrowStartStates; + } + + bool ReadFromFont(GrIStream & grstrm, int fxdVersion); + bool ReadStateTableFromFont(GrIStream & grstrm, int fxdVersion); + + int GetRuleToApply(GrTableManager *, GrPass * ppass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + + int RunTransitionTable(GrPass * ppass, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int * prgrowAccepting, int * prgcslotMatched); + int RunTransitionTableOptimized(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int * prgrowAccepting, int * prgcslotMatched); + bool RunConstraintAndRecordLog(GrTableManager *, GrPass * ppass, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotPreModContext, int cslotMatched); + + int RunConstraints_Obsolete(GrTableManager *, GrPass * ppass, int row, GrSlotStream * psstrmIn, + int cslotMatched); + + // For sorting matched rules + struct MatchedRule // mr + { + int ruln; + int nSortKey; + int cslot; // number of slots matched AFTER the current stream position + }; + + int MaxRulePreContext() + { + return m_critMaxRulePreContext; + } + +protected: + int FindColumn(gid16 chwGlyphID); +protected: + // Instance variables: + int m_crow; // number of rows (states) + int m_crowFinal; // number of final states; no transitions for these + int m_rowFinalMin; // index of first final row + int m_crowNonAcpt; // number of non-accepting states; no rule indices for these + + int m_ccol; // number of columns (machine classes) + + data16 * m_prgirulnMin; // m_crow-m_crowNonAcpt+1 of these; + // index within m_prgrulnMatched, start of matched + // rules for each accepting state + + data16 * m_prgrulnMatched; // long ordered list of rule indices matched by + // subsequent states; total length is sum of number + // of rules matched for each accepting state + int m_crulnMatched; // needed only for memory instrumentation + + // Transition matrix--for optimized version: +// short ** m_prgprgrowXitions; // ((m_crow-m_crowFinal) * m_ccol) of these; + // positive number indicates + // next state is non-accepting; negative number is + // negative of accepting state. + + // Transition matrix--for current version: + short * m_prgrowTransitions; // ((m_crow-m_crowFinal) * m_ccol) of these + + // debugger string offsets + data16 * m_prgibStateDebug; // for transition states; (m_crow-m_crul+1) of these + + // constants for fast binary search; these are generated by the compiler so that the + // engine doesn't have to take time to do it + data16 m_dimcrInit; // (max power of 2 <= m_cmcr); + // size of initial range to consider + data16 m_cLoop; // log2(max power of 2 <= m_cmcr); + // indicates how many iterations are necessary + data16 m_imcrStart; // m_cmcr - m_dimcrInit; + // where to start search + + int m_cmcr; // number of machine-class-ranges + GrFSMClassRange * m_prgmcr; // array of class ranges; we search these to find the + // mapping to the machine-class-column + + // minimum and maximum number of items in the rule contexts before the first modified + // item. + int m_critMinRulePreContext; + int m_critMaxRulePreContext; + + // start states--row in the FSM to start on depending on how many bogus slots we + // are skipping; (max rule-precontext - min rule-precontext + 1) of these; + // first always = zero + short * m_prgrowStartStates; + +//:Ignore +#if OLD_TEST_STUFF +public: + // For test procedures: + void SetUpSimpleFSMTest(); + void SetUpRuleActionTest(); + void SetUpRuleAction2Test(int); + void SetUpAssocTest(int); + void SetUpAssoc2Test(int); + void SetUpDefaultAssocTest(); + void SetUpFeatureTest(); + void SetUpLigatureTest(int); + void SetUpLigature2Test(int); +#endif // OLD_TEST_STUFF +//:End Ignore + +}; + +} // namespace gr + + +#endif // !FSM_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFeature.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFeature.cpp new file mode 100644 index 00000000000..61970d56949 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFeature.cpp @@ -0,0 +1,321 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrFeature.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the GrFeature class. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#include <cstring> + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> GrFeature Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Initialize the feature. +----------------------------------------------------------------------------------------------*/ +void GrFeature::Initialize(featid nID, int nNameId, int cfset, int nDefault) +{ + m_nID = nID; + m_nNameId = nNameId; + m_nDefault = nDefault; + + m_vnVal.resize(cfset); + for (unsigned int ifset = 0; ifset < m_vnVal.size(); ifset++) + m_vnVal[ifset] = INT_MAX; + m_vnNameId.resize(cfset); +} + +/*---------------------------------------------------------------------------------------------- + Return the feature settings. +----------------------------------------------------------------------------------------------*/ +int GrFeature::Settings(int cMax, int * prgnVal) +{ + int cRet = min(cMax, signed(m_vnVal.size())); + for (int ifset = 0; ifset < cRet; ifset++) + prgnVal[ifset] = m_vnVal[ifset]; + return cRet; +} + +/*---------------------------------------------------------------------------------------------- + Add a feature setting. +----------------------------------------------------------------------------------------------*/ +void GrFeature::AddSetting(int nVal, int nNameId) +{ + Assert(nVal != INT_MAX); + unsigned int ifset; + for (ifset = 0; ifset < m_vnVal.size(); ifset++) + { + if (m_vnVal[ifset] == nVal) + return; // already there + } + for (ifset = 0; ifset < m_vnVal.size(); ifset++) + { + if (m_vnVal[ifset] == INT_MAX) + { + m_vnVal[ifset] = nVal; + m_vnNameId[ifset] = nNameId; + return; + } + } + m_vnVal.push_back(nVal); + m_vnNameId.push_back(nNameId); +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given setting is valid according to the font's feature table. +----------------------------------------------------------------------------------------------*/ +bool GrFeature::IsValidSetting(int nVal) +{ + for (unsigned int ifset = 0; ifset < m_vnVal.size(); ifset++) + { + if (m_vnVal[ifset] == nVal) + return true; + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + Read the feature label from the name table. +----------------------------------------------------------------------------------------------*/ +std::wstring GrFeature::Label(GrEngine * pgreng, int nLang) +{ + std::wstring stu = pgreng->StringFromNameTable(nLang, m_nNameId); + if (stu == L"NoName") + stu.erase(); + return stu; +} + +/*---------------------------------------------------------------------------------------------- + Read the feature value label from the name table. +----------------------------------------------------------------------------------------------*/ +std::wstring GrFeature::SettingLabel(GrEngine * pgreng, int nVal, int nLang) +{ + for (unsigned int ifset = 0; ifset < m_vnVal.size(); ifset++) + { + if (m_vnVal[ifset] == nVal) + { + std::wstring stu = pgreng->StringFromNameTable(nLang, m_vnNameId[ifset]); + if (stu == L"NoName") + stu.erase(); + return stu; + } + } + Assert(false); // setting is not valid + return L""; +} + +/*---------------------------------------------------------------------------------------------- + Return the given setting value. +----------------------------------------------------------------------------------------------*/ +int GrFeature::NthSetting(int ifset) +{ + if (ifset >= (int)m_vnVal.size()) + return -1; + else + return m_vnVal[ifset]; +} + +/*---------------------------------------------------------------------------------------------- + Read the feature value label for the given index. +----------------------------------------------------------------------------------------------*/ +std::wstring GrFeature::NthSettingLabel(GrEngine * pgreng, int ifset, int nLang) +{ + std::wstring stu; + + if (ifset >= (int)m_vnVal.size()) + stu.erase(); + else + { + stu = pgreng->StringFromNameTable(nLang, m_vnNameId[ifset]); + if (stu == L"NoName") + stu.erase(); + } + return stu; +} + +/*---------------------------------------------------------------------------------------------- + Return the feature setting label. +----------------------------------------------------------------------------------------------*/ +// void GrFeature::SetSettingLabel(int nVal, std::wstring stuLabel, int nLang) +// { +// for (int ifset = 0; ifset < m_vfset.size(); ifset++) +// { +// if (m_vfset[ifset].m_nVal == nVal) +// { +// m_vfset[ifset].m_hmnstuLabels.Insert(nLang, stuLabel, true); +// return; +// } +// } +// Assert(false); // setting is not valid +// } + + +//:>******************************************************************************************** +//:> GrLangTable Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Read the languages from the font. +----------------------------------------------------------------------------------------------*/ +bool GrLangTable::ReadFromFont(GrIStream * pgrstrm, int fxdVersion) +{ + GrIStream & grstrm = *pgrstrm; + + // number of languages + m_clang = (size_t)grstrm.ReadUShortFromFont(); + + // search constants + m_dilangInit = grstrm.ReadUShortFromFont(); + m_cLoop = grstrm.ReadUShortFromFont(); + m_ilangStart = grstrm.ReadUShortFromFont(); + + // Slurp the data into the buffers. The "+1" represents a bogus ending entry that quickly + // gives us the size of the feature list. Note that the resulting data is all big-endian. + int cb = (m_clang + 1) * sizeof(LangEntry); + m_prglang = new LangEntry[m_clang + 1]; + grstrm.ReadBlockFromFont(m_prglang, cb); + + m_cbOffset0 = (lsbf)(m_prglang[0].cbOffsetBIG); + + Assert((lsbf)(m_prglang[m_clang].cFeaturesBIG) == 0); // bogus entry has no settings + cb = (lsbf)(m_prglang[m_clang].cbOffsetBIG) - m_cbOffset0; + Assert(cb % sizeof(FeatSet) == 0); // # of bytes fits nicely into FeatSet class + int cfset = cb / sizeof(FeatSet); + m_prgfset = new FeatSet[cfset]; + m_cfset = cfset; + grstrm.ReadBlockFromFont(m_prgfset, cb); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Create an empty language table to use for dumb rendering, or when there is no valid + Sill table. +----------------------------------------------------------------------------------------------*/ +void GrLangTable::CreateEmpty() +{ + m_prglang = NULL; + m_prgfset = NULL; + m_clang = 0; + m_dilangInit = 0; + m_cLoop = 0; + m_ilangStart = 0; +} + +/*---------------------------------------------------------------------------------------------- + Return information about the language with the given ID by filling in the given vectors + with the feature ids and values. Will wipe out any data in the vectors already. +----------------------------------------------------------------------------------------------*/ +void GrLangTable::LanguageFeatureSettings(isocode lgcode, + std::vector<featid> & vnFeatId, std::vector<int> & vnValues) +{ + vnFeatId.clear(); + vnValues.clear(); + + int ilang = FindIndex(lgcode); + if (ilang == -1) + return; // no such language: leave vectors empty + + LangEntry * plang = m_prglang + ilang; + int cbOffset = lsbf(plang->cbOffsetBIG) - m_cbOffset0; + Assert(cbOffset % sizeof(FeatSet) == 0); + byte * pbFeatSet = reinterpret_cast<byte*>(m_prgfset) + cbOffset; + FeatSet * pfset = reinterpret_cast<FeatSet*>(pbFeatSet); + for (int ifset = 0; ifset < lsbf(plang->cFeaturesBIG); ifset++) + { + vnFeatId.push_back(lsbf(pfset[ifset].featidBIG)); + vnValues.push_back(lsbf(pfset[ifset].valueBIG)); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the code for the language at the given index. +----------------------------------------------------------------------------------------------*/ +isocode GrLangTable::LanguageCode(size_t ilang) +{ + isocode lgcodeRet; + if (ilang > m_clang) + { + std::fill_n(lgcodeRet.rgch, 4, '\0'); + return lgcodeRet; + } + LangEntry * plang = m_prglang + ilang; + std::copy(plang->rgchCode, plang->rgchCode + 4, lgcodeRet.rgch); + return lgcodeRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the index of the language in the list. +----------------------------------------------------------------------------------------------*/ +int GrLangTable::FindIndex(isocode lgcode) +{ + if (m_clang == 0) + return -1; +#ifdef _DEBUG + int nPowerOf2 = 1; + while (nPowerOf2 <= signed(m_clang)) + nPowerOf2 <<= 1; + nPowerOf2 >>= 1; + // Now nPowerOf2 is the max power of 2 <= m_clang + gAssert((1 << m_cLoop) == nPowerOf2); // m_cLoop == log2(nPowerOf2) + gAssert(m_dilangInit == nPowerOf2); + gAssert(m_ilangStart == m_clang - m_dilangInit); +#endif // _DEBUG + + int dilangCurr = m_dilangInit; + + int ilangCurr = m_ilangStart; // may become < 0 + while (dilangCurr > 0) + { + int nTest; + if (ilangCurr < 0) + nTest = -1; + else + { + LangEntry * plangCurr = m_prglang + ilangCurr; + nTest = strcmp(plangCurr->rgchCode, lgcode.rgch); + } + + if (nTest == 0) + return ilangCurr; + + dilangCurr >>= 1; // divide by 2 + if (nTest < 0) + ilangCurr += dilangCurr; + else // (nTest > 0) + ilangCurr -= dilangCurr; + } + + return -1; +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFeatureValues.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFeatureValues.h new file mode 100644 index 00000000000..97e6a1fe33c --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrFeatureValues.h @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrFeatureValues.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + + +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_FEATVAL_INCLUDED +#define GR_FEATVAL_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + A convenient way to group together style and feature information that interacts + with the rules. + + Hungarian: fval +----------------------------------------------------------------------------------------------*/ +class GrFeatureValues +{ + friend class GrCharStream; + friend class GrSlotAbstract; + friend class GrSlotState; + +public: + // Standard constructor: + GrFeatureValues() + { + m_nStyleIndex = 0; + std::fill(m_rgnFValues, m_rgnFValues + kMaxFeatures, 0); + } + + // Copy constructor: + GrFeatureValues(const GrFeatureValues & fval) + { + m_nStyleIndex = fval.m_nStyleIndex; + std::copy(fval.m_rgnFValues, fval.m_rgnFValues + kMaxFeatures, m_rgnFValues); + } + + // For transduction logging: +#ifdef TRACING + void WriteXductnLog(GrTableManager * ptman, std::ostream &); +#endif // TRACING + +protected: + int m_nStyleIndex; + int m_rgnFValues[kMaxFeatures]; +}; + +} // namespace gr + + +#endif // !GR_FEATVAL_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrGlyphTable.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrGlyphTable.cpp new file mode 100644 index 00000000000..8643eae8cb8 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrGlyphTable.cpp @@ -0,0 +1,650 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrGlyphTable.cpp +Responsibility: Sharon Correll +Last reviewed: 10Aug99, JT, quick look over + +Description: + Implements the GrGlyphTable class and related classes. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Fill in the glyph table from the font stream. +----------------------------------------------------------------------------------------------*/ +bool GrGlyphTable::ReadFromFont(GrIStream & grstrmGloc, long lGlocStart, + GrIStream & grstrmGlat, long lGlatStart, + data16 chwBWAttr, data16 chwJStrAttr, int cJLevels, int cnCompPerLig, + int fxdSilfVersion) +{ + // Create the glyph sub-table for the single style. + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + + // Gloc table--offsets into Glat table + grstrmGloc.SetPositionInFont(lGlocStart); + + // version + int fxdGlocVersion = GrEngine::ReadVersion(grstrmGloc); + if (fxdGlocVersion > kGlocVersion) + return false; + + // flags + short snFlags = grstrmGloc.ReadShortFromFont(); + + // number of attributes + unsigned short cAttrs = grstrmGloc.ReadUShortFromFont(); + + // Create a single sub-table for the single style. + pgstbl->Initialize(fxdSilfVersion, snFlags, chwBWAttr, chwJStrAttr, data16(chwJStrAttr + cJLevels), + m_cglf, cAttrs, cnCompPerLig); + + SetSubTable(0, pgstbl); + + return pgstbl->ReadFromFont(grstrmGloc, m_cglf, grstrmGlat, lGlatStart); +} + +bool GrGlyphSubTable::ReadFromFont(GrIStream & grstrmGloc, int cGlyphs, + GrIStream & grstrmGlat, long lGlatStart) +{ + // attribute value offsets--slurp + if (m_fGlocShort) + { + grstrmGloc.ReadBlockFromFont(m_prgibBIGAttrValues, ((cGlyphs + 1) * sizeof(data16))); + } + else + { + grstrmGloc.ReadBlockFromFont(m_prgibBIGAttrValues, ((cGlyphs + 1) * sizeof(data32))); + } + + // length of buffer needed for glyph attribute values: + int cbBufLen = GlocLookup(data16(cGlyphs)); + + // TODO: slurp debugger offsets: 'm_prgibBIGGlyphAttrDebug' + + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(m_fxdSilfVersion, cbBufLen); + + // Glat table + grstrmGlat.SetPositionInFont(lGlatStart); + + // version + int fxdGlatVersion = grstrmGlat.ReadIntFromFont(); + if (fxdGlatVersion > kGlatVersion) + return false; + + // Back up over the version number and include it right in the attribute value entry + // buffer, since the offsets take it into account. + grstrmGlat.SetPositionInFont(lGlatStart); + + // Slurp the entire block of entries. + grstrmGlat.ReadBlockFromFont(m_pgatbl->m_prgbBIGEntries, cbBufLen); + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Create an empty glyph table with no glyph attributes. +----------------------------------------------------------------------------------------------*/ +void GrGlyphTable::CreateEmpty() +{ + // Create the glyph sub-table for the single style. + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + + // Create a single sub-table for the single style. + pgstbl->Initialize(0, 0, 0, 0, 0, m_cglf, 0, 0); + + SetSubTable(0, pgstbl); + + pgstbl->CreateEmpty(); +} + +void GrGlyphSubTable::CreateEmpty() +{ + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(0, 0); +} + +/*---------------------------------------------------------------------------------------------- + Initialization. +----------------------------------------------------------------------------------------------*/ +void GrGlyphSubTable::Initialize(int fxdSilfVersion, utf16 chwFlags, + data16 chwBWAttr, data16 chwJStrAttr, data16 chwJStrHWAttr, + int cGlyphs, int cAttrs, int cnCompPerLig) +{ + m_fxdSilfVersion = fxdSilfVersion; + m_fHasDebugStrings = HasAttrNames(chwFlags); + m_nAttrIDLim = cAttrs; + m_chwBWAttr = chwBWAttr; + m_chwJStrAttr = chwJStrAttr; + m_chwJStrHWAttr = chwJStrHWAttr; + m_fGlocShort = !LongFormat(chwFlags); + + if (m_fGlocShort) + { // item # cGlyphs holds length, which is convenient for dealing with last item + m_prgibBIGAttrValues = new byte[(cGlyphs + 1) * sizeof(data16)]; + } + else + { + m_prgibBIGAttrValues = new byte[(cGlyphs + 1) * sizeof(data32)]; + } + + + if (m_fHasDebugStrings) + { + m_prgibBIGGlyphAttrDebug = new data16[cAttrs + 1]; + } + + // Make a cache to hold a list of defined components for each glyph. This is + // calculated lazily, as needed, so we also include (as the first item) a flag to + // indicate whether or not it has been calculated; hence the "+1". + m_cnCompPerLig = cnCompPerLig; + m_prgnDefinedComponents = new int[(m_cnCompPerLig + 1) * cGlyphs]; + std::fill_n(m_prgnDefinedComponents, (m_cnCompPerLig + 1) * cGlyphs, 0); + + // Now the instance is ready to have the locations and the debug strings + // read from the file. +} + +/*---------------------------------------------------------------------------------------------- + Return the attribute value for the given glyph. +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::GlyphAttrValue(gid16 chwGlyphID, int nAttrID) +{ + if (m_nAttrIDLim == 0) + { + // No attributes defined (eg, invalid font) + return 0; + } + if (nAttrID >= m_nAttrIDLim || nAttrID >= 0xFF) + { + gAssert(false); + return 0; + } + int ibMin = GlocLookup(chwGlyphID); // where this glyph's attrs start + int ibLim = GlocLookup(chwGlyphID + 1); // where next glyph starts + + int nRet = m_pgatbl->GlyphAttr16BitValue(ibMin, ibLim, byte(nAttrID)); + if ((data16)nAttrID == m_chwJStrAttr) + { + // For justify.stretch, which can be a 32-bit value, add on the high word. + unsigned int nRetHW = m_pgatbl->GlyphAttr16BitValue(ibMin, ibLim, byte(m_chwJStrHWAttr)); + nRet = (nRetHW << 16) | nRet; + } + return ConvertValueForVersion(nRet, nAttrID); +} + +/*---------------------------------------------------------------------------------------------- + Convert the glyph attribute value from the version of the font table to what is + expected by the engine. +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::ConvertValueForVersion(int nValue, int nAttrID) +{ + return ConvertValueForVersion(nValue, nAttrID, (int)m_chwBWAttr, m_fxdSilfVersion); +} + +int GrGlyphSubTable::ConvertValueForVersion(int nValue, int nAttrID, int nBWAttr, int fxdVersion) +{ + if ((nBWAttr >= 0 && nAttrID == (int)nBWAttr) || + (nBWAttr == -1 && nAttrID == (int)kslatBreak)) + { + if (fxdVersion < 0x00020000) + { + //switch (nValue) + //{ + //case klbv1WordBreak: return klbWordBreak; + //case klbv1HyphenBreak: return klbHyphenBreak; + //case klbv1LetterBreak: return klbLetterBreak; + //case klbv1ClipBreak: return klbClipBreak; + //default: + // break; + //} + //switch (nValue * -1) + //{ + //case klbv1WordBreak: return (klbWordBreak * -1); + //case klbv1HyphenBreak: return (klbHyphenBreak * -1); + //case klbv1LetterBreak: return (klbLetterBreak * -1); + //case klbv1ClipBreak: return (klbClipBreak * -1); + //default: + // break; + //} + + // Breakweight values in version 2 are 10 times what they were in version 1. + if (abs(nValue) <= 7) + return nValue * 10; + } + } + return nValue; +} + +/*---------------------------------------------------------------------------------------------- + Return the attribute value a glyph. Loop through the attribute runs, looking + for the one containing the given attribute. Assume the value is 0 if not found. + + @param ibMin - byte offset of first run for the glyph of interest + @param ibLim - byte offset of first run for the following glyph + @param bAttrID - attribute to find +----------------------------------------------------------------------------------------------*/ +int GrGlyphAttrTable::GlyphAttr16BitValue(int ibMin, int ibLim, byte bAttrID) +{ + GrGlyphAttrRun gatrun; + byte * pbBIGEnt = m_prgbBIGEntries + ibMin; + + while (pbBIGEnt < m_prgbBIGEntries + ibLim) + { + // Suck the run of attribute values into the instance, so we can see + // what's there. + gatrun.CopyFrom(pbBIGEnt); + + if (bAttrID < gatrun.m_bMinAttrID) + // Attribute not found--assume value is 0. + return 0; + else if (gatrun.m_bMinAttrID + gatrun.m_cAttrs > bAttrID) + { + // Found the value in this run. + data16 chw = lsbf(gatrun.m_rgchwBIGValues[bAttrID - gatrun.m_bMinAttrID]); + if ((chw & 0x8000) == 0) + return (int)chw; + else + return 0xFFFF0000 | chw; // sign extension + } + else + // Go to next run. + pbBIGEnt += gatrun.ByteCount(); + } + + // Attribute not found--assume value is 0; + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Given a point on the glyph, determine which component (if any) the point is in, and return + its index relative to the components that are defined for this glyph; ie, the index within + the compRef array. + Return -1 if the click with not inside any component, or if there are no components + defined for this glyph. + CURRENTLY NOT USED - if ever used, need to incorporate floating point +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::ComponentContainingPoint(gid16 chwGlyphID, int x, int y) +{ + int i = CalculateDefinedComponents(chwGlyphID); + for (int inComp = 0; inComp < m_cnCompPerLig; inComp++) + { + // The m_pnComponents list holds a list of the component attributes that are defined + // for this glyph, padded with -1's. + int nCompID = m_prgnDefinedComponents[i + inComp]; + if (nCompID == -1) + break; // hit the end of the list + + // The value of the component attribute is actually the attribute ID for the + // first of the component box fields--the top. There should a contiguous group of + // four fields: top, bottom, left, right. + int nFieldIDMin = GlyphAttrValue(chwGlyphID, nCompID); + gAssert(nFieldIDMin != 0); + int nTop = GlyphAttrValue(chwGlyphID, nFieldIDMin); + int nBottom = GlyphAttrValue(chwGlyphID, nFieldIDMin + 1); + int nLeft = GlyphAttrValue(chwGlyphID, nFieldIDMin + 2); + int nRight = GlyphAttrValue(chwGlyphID, nFieldIDMin + 3); + + if (nLeft <= x && x < nRight && + nBottom <= y && y < nTop) + { + return inComp; + } + } + return -1; +} + +/*---------------------------------------------------------------------------------------------- + Fill in the box with the source-device coordinates corresponding to the given component. + The rectangle is relative to the top-left of the glyph. + Return false if the component is not defined. + + @param xysEmSquare - of the font, in logical units + @param chwGlyphID - the glyph of interest + @param icomp - the component of interest (eg, for the "oe" ligature, + the index for component.o would be 0, + and the index for component.e would be 1); + these is as defined by the glyph, not as used in the rules + @param mFontEmUnits - the number of em-units in the font, for scaling + @param dysFontAscent - ascent of the font, for adjusting coordinate systems + @param pxsLeft, pxsRight, pysTop, pysBottom + - return values, in source device coordinates + @param fTopOrigin - convert values to a coordinate system where the origin is the + top of the line, not the baseline +----------------------------------------------------------------------------------------------*/ +bool GrGlyphSubTable::ComponentBoxLogUnits(float xysEmSquare, + gid16 chwGlyphID, int icomp, int mFontEmUnits, float dysFontAscent, + float * pxsLeft, float * pysTop, float * pxsRight, float * pysBottom, + bool fTopOrigin) +{ + int i = CalculateDefinedComponents(chwGlyphID); + + // The m_pnComponents list holds a list of the component attributes that are defined + // for this glyph, padded with -1's. + int nCompID = m_prgnDefinedComponents[i + icomp]; + if (nCompID == -1) + { + // Ooops, the component is undefined for this glyph. + *pxsLeft = 0; + *pxsRight = 0; + *pysTop = 0; + *pysBottom = 0; + return false; + } + + // The value of the component attribute is actually the attribute ID for the + // first of the component box fields--the top. There should a contiguous group of + // four fields: top, bottom, left, right. + int nFieldIDMin = GlyphAttrValue(chwGlyphID, nCompID); + if (nFieldIDMin == 0) + { + // Component inadequately defined. This can happen when we defined components + // for a glyph and then substituted another glyph for it. + *pxsLeft = 0; + *pxsRight = 0; + *pysTop = 0; + *pysBottom = 0; + return false; + } + int mTop = GlyphAttrValue(chwGlyphID, nFieldIDMin); + int mBottom = GlyphAttrValue(chwGlyphID, nFieldIDMin + 1); + int mLeft = GlyphAttrValue(chwGlyphID, nFieldIDMin + 2); + int mRight = GlyphAttrValue(chwGlyphID, nFieldIDMin + 3); + + ////int xysFontEmUnits; + ////// ysHeight should correspond to the pixels in the font's em square + ////GrResult res = pgg->GetFontEmSquare(&xysFontEmUnits); + ////if (ResultFailed(res)) + //// return false; + + *pxsLeft = GrEngine::GrIFIMulDiv(mLeft, xysEmSquare, mFontEmUnits); + *pxsRight = GrEngine::GrIFIMulDiv(mRight, xysEmSquare, mFontEmUnits); + *pysTop = GrEngine::GrIFIMulDiv(mTop, xysEmSquare, mFontEmUnits); + *pysBottom = GrEngine::GrIFIMulDiv(mBottom, xysEmSquare, mFontEmUnits); + + if (*pxsLeft > *pxsRight) + { + float xsTmp = *pxsLeft; + *pxsLeft = *pxsRight; + *pxsRight = xsTmp; + } + if (*pysTop < *pysBottom) + { + float ysTmp = *pysTop; + *pysTop = *pysBottom; + *pysBottom = ysTmp; + } + + if (fTopOrigin) + { + // Currently top and bottom coordinates are such that 0 is the baseline and positive is + // upwards, while for the box we want to return, 0 is the top of the glyph and positive + // is downwards. So switch systems. + *pysTop = (dysFontAscent - *pysTop); + *pysBottom = (dysFontAscent - *pysBottom); + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Calculate the list of defined components for the given glyph, if any. + Return the index to the first relevant item in the array. + Private. + TODO: With the current implementation that stores the glyph ids in each slot, this + mechanism is redundant and obsolete, and should be removed. +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::CalculateDefinedComponents(gid16 chwGlyphID) +{ + // The first item is a flag indicating whether the list has been calculated. + int iFlag = chwGlyphID * (m_cnCompPerLig + 1); // index of has-been-calculated flag + int iMin = iFlag + 1; // first actual value; +1 in order to skip the flag + if (m_prgnDefinedComponents[iFlag] == 0) + { + int iTmp = iMin; + for (int nCompID = 0; nCompID < m_cComponents; nCompID++) + { + if (ComponentIsDefined(chwGlyphID, nCompID)) + m_prgnDefinedComponents[iTmp++] = nCompID; + if (iTmp - iMin >= m_cnCompPerLig) + break; // ignore any components beyond the maximum allowed + } + // Fill in the rest of the list with -1. + for( ; iTmp < iMin + m_cnCompPerLig; iTmp++) + m_prgnDefinedComponents[iTmp] = -1; + + m_prgnDefinedComponents[iFlag] = 1; // has been calculated + } + return iMin; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given attr ID, which is a component definition attribute, indicates + that that component is defined for the given glyph. + + @param chwGlyphID - the glyph of interest + @param nAttrID - the attribute of interest, assumed to be a component glyph + attribute (eg. component.e.top) +----------------------------------------------------------------------------------------------*/ +bool GrGlyphSubTable::ComponentIsDefined(gid16 chwGlyphID, int nAttrID) +{ + gAssert(nAttrID < m_cComponents); + if (nAttrID >= m_cComponents) + return false; + + return (GlyphAttrValue(chwGlyphID, nAttrID) != 0); +} + +/*---------------------------------------------------------------------------------------------- + Return the index of the given component as defined for the given glyph ID. that is, + convert the global ID for the component to the one within this glyph. Return -1 + if the component is not defined for the given glyph. + For instance, given the glyph ID "oe" (the oe ligature) and the attr ID for component.o, + the result would be 0; for component.e the result would be 1. +----------------------------------------------------------------------------------------------*/ +int GrGlyphSubTable::ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID) +{ + int i = CalculateDefinedComponents(chwGlyphID); + + for (int iLp = 0; iLp < m_cnCompPerLig; iLp++) + { + if (m_prgnDefinedComponents[i + iLp] == nCompID) + return iLp; + } + return -1; +} + +/*---------------------------------------------------------------------------------------------- + Return the ID of the nth defined component attribute for the given glyph. +----------------------------------------------------------------------------------------------*/ +//int GrGlyphSubTable::NthComponentID(gid16 chwGlyphID, int n) +//{ +// int i = CalculateDefinedComponents(chwGlyphID); +// return m_pnComponents[i + 1 + n]; +//} + + +//:>******************************************************************************************** +//:> For test procedures +//:>******************************************************************************************** + +//:Ignore +#ifdef OLD_TEST_STUFF + +void GrGlyphTable::SetUpTestData() +{ + SetNumberOfGlyphs(5); + SetNumberOfStyles(1); + + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + gAssert(pgstbl); + + pgstbl->Initialize(1, 0, 5, 10, 4); + SetSubTable(0, pgstbl); + + pgstbl->SetUpTestData(); +} + +/*********************************************************************************************** + TODO: This method is BROKEN because m_prgibBIGAttrValues has been changed. It is no + longer a utf16 *. The Gloc table can contain 16-bit or 32-bit entries and must be + accessed accordingly. +***********************************************************************************************/ +void GrGlyphSubTable::SetUpTestData() +{ + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(0, 48); + + m_prgibBIGAttrValues[0] = byte(msbf(data16(0))); + m_prgibBIGAttrValues[1] = byte(msbf(data16(6)))); + m_prgibBIGAttrValues[2] = byte(msbf(data16(20))); + m_prgibBIGAttrValues[3] = byte(msbf(data16(20))); + m_prgibBIGAttrValues[4] = byte(msbf(data16(40))); + m_prgibBIGAttrValues[5] = byte(msbf(data16(48))); + + m_pgatbl->SetUpTestData(); +} + +void GrGlyphAttrTable::SetUpTestData() +{ + // Glyph 0 (1 run; offset = 0): + // 1 = 11 = 0x0B + // 2 = 22 = 0x16 + // 0102 000B 0016 + // + // Glyph 1 (2 runs; offset = 3*2 = 6): + // 0 = 5 + // 1 = 6 + // 2 = 7 + // 8 = 8 + // 9 = 9 + // 0003 0005 0006 0007 + // 0802 0008 0009 + // + // Glyph 2 (0 runs; offset = 6 + 7*2 = 20): + // all values = 0 + // + // Glyph 3 (5 runs; offset = 20): + // 1 = 111 = 0x006F + // 3 = 333 = 0x00DE + // 5 = 555 = 0x022B + // 7 = 777 = 0x0309 + // 9 = 999 = 0x03E7 + // 0101 006F + // 0301 00DE + // 0501 022B + // 0701 0309 + // 0901 03E7 + // + // Glyph 4 (1 run; offset = 20 + 10*2 = 40): + // 5 = 4 + // 6 = 4 + // 7 = 4 + // 0503 0004 0004 0004 + // + // Glyph 5 (offset = 40 + 4*2 = 48) + + byte * pbBIG = m_prgbBIGEntries; + + GrGlyphAttrRun gatrun; + + // Glyph 0 + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 2; + gatrun.m_rgchwBIGValues[0] = msbf(data16(11)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(22)); + memcpy(pbBIG, &gatrun, 6); + pbBIG += 6; + + // Glyph 1 + gatrun.m_bMinAttrID = 0; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(5)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(6)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(7)); + memcpy(pbBIG, &gatrun, 8); + pbBIG += 8; + gatrun.m_bMinAttrID = 8; + gatrun.m_cAttrs = 2; + gatrun.m_rgchwBIGValues[0] = msbf(data16(8)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(9)); + memcpy(pbBIG, &gatrun, 6); + pbBIG += 6; + + // Glyph 2: no data + + // Glyph 3 + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(111)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + gatrun.m_bMinAttrID = 3; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(333)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + gatrun.m_bMinAttrID = 5; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(555)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + gatrun.m_bMinAttrID = 7; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(777)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + gatrun.m_bMinAttrID = 9; + gatrun.m_cAttrs = 1; + gatrun.m_rgchwBIGValues[0] = msbf(data16(999)); + memcpy(pbBIG, &gatrun, 4); + pbBIG += 4; + + // Glyph 4 + gatrun.m_bMinAttrID = 5; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(4)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(4)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(4)); + memcpy(pbBIG, &gatrun, 8); + pbBIG += 8; + + gAssert(pbBIG == m_prgbBIGEntries + 48); +} + +#endif // OLD_TEST_STUFF + +} // namespace gr + +//:End Ignore diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrGlyphTable.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrGlyphTable.h new file mode 100644 index 00000000000..bec0e518dee --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrGlyphTable.h @@ -0,0 +1,429 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrGlyphTable.h +Responsibility: Sharon Correll +Last reviewed: 10Aug99, JT, quick look over + +Description: + The GrGlyphTable and related classes that store the values of glyph attributes. + + For each glyph in a TrueType font, Graphite has a list of glyph attributes (which + are defined in the GDL script file). Since most of the values will be zero, we save space + by only storing the non-zero values. + + Eventually it may be possible to have more than one TrueType font compiled into a single + glyph table, hence the need for multiple sub-tables. For now we assume that there is only + one font file and only one sub-table. + + Note that BIG in a variable name means that the data is in big-endian format, or is a range + of bytes that must be interpreted as a run of wide chars or ints in big-endian format. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:Ignore +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_GTABLE_INCLUDED +#define GR_GTABLE_INCLUDED +//:End Ignore + +namespace gr +{ + +class Font; + +/*---------------------------------------------------------------------------------------------- + A run of non-zero attribute values for a single glyph, where the attribute IDs + are contiguous. + + NOTE: it is assumed that the format of this class matches the byte order of the data as it + is read directly from the TTF/ECF file--that is, it is big-endian. + + Hungarian: gatrun +----------------------------------------------------------------------------------------------*/ + +class GrGlyphAttrRun +{ + friend class GrGlyphTable; + friend class GrGlyphSubTable; + friend class GrGlyphAttrTable; + + enum { + kMaxAttrsPerRun = 255 + }; + + /*------------------------------------------------------------------------------------------ + Copy the raw memory (starting at the given byte) into the instance. This method must + reflect the format of the "Glat_entry" as it is stored in the file and slurped into + memory. + ------------------------------------------------------------------------------------------*/ + void CopyFrom(byte * pbBIGEnt) + { + m_bMinAttrID = *pbBIGEnt; + m_cAttrs = *(pbBIGEnt + 1); + if (m_cAttrs >= kMaxAttrsPerRun) + { + gAssert(false); + m_cAttrs = kMaxAttrsPerRun; + } + #ifdef _DEBUG + // Probably not strictly necessary to zero the array, but convenient for debugging. + // Removed from release build for optimization + std::fill(m_rgchwBIGValues, m_rgchwBIGValues + kMaxAttrsPerRun, 0); + #endif + // this is mixing types of data16 and byte pointers! + const data16 * prgchw = reinterpret_cast<const data16*>(pbBIGEnt + 2); + std::copy(prgchw, prgchw + m_cAttrs, m_rgchwBIGValues); + } + + int ByteCount() + { + return (2 + (m_cAttrs * isizeof(data16))); + } + +protected: + byte m_bMinAttrID; // ID of first attribute in the run + byte m_cAttrs; // number of attributes in the run + data16 m_rgchwBIGValues[kMaxAttrsPerRun]; +}; + +/*---------------------------------------------------------------------------------------------- + Contains runs of attribute values for all the glyphs in the font; corresponds to + the "Glat" table in the font. + + Hungarian: gatbl +----------------------------------------------------------------------------------------------*/ + +class GrGlyphAttrTable +{ + friend class GrGlyphTable; + friend class GrGlyphSubTable; + friend class FontMemoryUsage; + +protected: + // Constructor: + GrGlyphAttrTable() + { + m_prgbBIGEntries = NULL; + } + + // Destructor: + ~GrGlyphAttrTable() + { + delete[] m_prgbBIGEntries; + } + + // Initialization: + void Initialize(int fxdSilfVersion, int cbBufLen) + { + m_fxdSilfVersion = fxdSilfVersion; + m_cbEntryBufLen = cbBufLen; + m_prgbBIGEntries = new byte[cbBufLen]; + // Now the instance is ready to have all the glyph attr entries slurped into + // the byte array from the file. + } + + int GlyphAttr16BitValue(int ibMin, int ibLim, byte bAttrID); + +protected: + int m_fxdSilfVersion; // version number of the Silf table, which is used + // to interpret the attribute values + + // Block of variable-length glyph attribute runs, matching the format of + // GrGlyphAttrRun. We don't store instances of that class here because they + // are variable length, and it would be too slow to read them individually from the + // font. Instead, we set up a single instance in the method that accesses the values; + // having this instance will help debugging. Eventually we may want to do without it, + // if it would help efficiency. + int m_cbEntryBufLen; // needed only for memory instrumentation + byte * m_prgbBIGEntries; + +//:Ignore +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + void SetUpTestData(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF + +//:End Ignore + +}; + +/*---------------------------------------------------------------------------------------------- + One glyph table per font (style) file; corresponds to the "Gloc" table in the font. + Currently there is only considered to be one style per file, so there is only one of + these. It holds the (non-zero) glyph attribute values for every glyph in the font. + + Hungarian: gstbl + + Review: Eventually we may need to make a subclass that uses 32-bit values for the offsets, + or just use a separate array pointer in this class. Which would be preferable? +----------------------------------------------------------------------------------------------*/ + +class GrGlyphSubTable +{ + friend class GrGlyphTable; + friend class FontMemoryUsage; + +public: + // Constructor: + GrGlyphSubTable() : + m_pgatbl(NULL), + m_prgibBIGAttrValues(NULL), + m_prgibBIGGlyphAttrDebug(NULL), + m_prgnDefinedComponents(NULL) + { + } + // Destructor: + ~GrGlyphSubTable() + { + delete m_pgatbl; + delete[] m_prgibBIGAttrValues; + if (m_fHasDebugStrings) + delete[] m_prgibBIGGlyphAttrDebug; + delete[] m_prgnDefinedComponents; + } + + // Initialization: + bool ReadFromFont(GrIStream & gloc_strm, int cGlyphs, + GrIStream & glat_strm, long lGlatStart); + void Initialize(int fxdSilfVersion, data16 chwFlags, + data16 chwBWAttr, data16 chwJStrAttr, data16 chwJStrHWAttr, + int cGlyphs, int cAttrs, int cnCompPerLig); + + void CreateEmpty(); + + void SetNumberOfComponents(int c) + { + m_cComponents = c; + } + + // General: + int NumberOfGlyphAttrs() { return m_nAttrIDLim; } // 0..(m_nAttrIDLim-1) + + int GlyphAttrValue(gid16 chwGlyphID, int nAttrID); + + // Ligatures: + int ComponentContainingPoint(gid16 chwGlyphID, int x, int y); + bool ComponentBoxLogUnits(float xysEmSquare, gid16 chwGlyphID, int icomp, + int mFontEmUnits, float dysAscent, + float * pxsLeft, float * pysTop, float * pxsRight, float * pysBottom, + bool fTopOrigin = true); +protected: + int CalculateDefinedComponents(gid16 chwGlyphID); + bool ComponentIsDefined(gid16 chwGlyphID, int nAttrID); + int ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID); +// int NthComponentID(gid16 chwGlyphID, int n); + int ConvertValueForVersion(int nRet, int nAttrID); + +public: + static int ConvertValueForVersion(int nRet, int nAttrID, int nBWAttr, int fxdVersion); + + // Flags: + bool LongFormat(data16 chwFlags) // 32-bit values? + { + return ((chwFlags & 0x01) == 0x01); // bit 0 + } + bool HasAttrNames(data16 chwFlags) + { + return ((chwFlags & 0x02) == 0x02); // bit 1 + } + int GlocLookup(data16 chwGlyphId) + { + if (m_fGlocShort) + { + unsigned short su = ((unsigned short *)m_prgibBIGAttrValues)[chwGlyphId]; + return lsbf(su); + } + else + { unsigned int u = ((unsigned int *)m_prgibBIGAttrValues)[chwGlyphId]; + return lsbf((int)u); + } + } + +protected: + int m_fxdSilfVersion; // version number of the Silf table, which is used + // to interpret the attribute values + bool m_fHasDebugStrings; // are debugging strings loaded into memory? + + int m_nAttrIDLim; // number of glyph attributes + int m_cComponents; // number of initial glyph attributes that + // represent ligature components + int m_cnCompPerLig; + + GrGlyphAttrTable * m_pgatbl; + byte * m_prgibBIGAttrValues; // byte offsets for glyph attr values - BIG endian + bool m_fGlocShort; // flag for Gloc table format + data16 * m_prgibBIGGlyphAttrDebug; // byte offsets for glyph attr debug strings - BIG endian + + data16 m_chwBWAttr; // breakweight attr ID; needed for converting + // between versions + + // Attr IDs for justify.0.stretch and justify.0.stretchHW; these must always be looked + // up in tandem. + data16 m_chwJStrAttr; + data16 m_chwJStrHWAttr; + + int * m_prgnDefinedComponents; // for each glyph, cache list of component attributes that + // are defined + +//:Ignore +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + void SetUpTestData(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF + +//:End Ignore +}; + +/*---------------------------------------------------------------------------------------------- + Holds all the information about glyph attributes. + + Hungarian: gtbl +----------------------------------------------------------------------------------------------*/ + +class GrGlyphTable +{ + friend class GrGlyphSubTable; + friend class FontMemoryUsage; + +public: + // Constructor: + GrGlyphTable() + { + m_cglf = 0; + m_cComponents = 0; + m_cgstbl = 0; + m_vpgstbl.clear(); + } + + // Destructor: + ~GrGlyphTable() + { + for (int i = 0; i < m_cgstbl; ++i) + delete m_vpgstbl[i]; + } + + bool ReadFromFont(GrIStream & gloc_strm, long lGlocStart, + GrIStream & glat_strm, long lGlatStart, + data16 chwBWAttr, data16 chwJStrAttr, int cJLevels, int cnCompPerLig, + int fxdSilfVersion); + + void CreateEmpty(); + + // Setters: + void SetNumberOfGlyphs(int c) + { + m_cglf = c; + } + void SetNumberOfStyles(int c) + { + m_cgstbl = c; + m_vpgstbl.resize(c); + } + void SetNumberOfComponents(int c) + { + m_cComponents = c; + for (unsigned int ipgstbl = 0; ipgstbl < m_vpgstbl.size(); ipgstbl++) + m_vpgstbl[ipgstbl]->SetNumberOfComponents(c); + } + void SetSubTable(int i, GrGlyphSubTable * pgstbl) + { + if (signed(m_vpgstbl.size()) <= i) + { + gAssert(false); + m_vpgstbl.resize(i+1); + } + m_vpgstbl[i] = pgstbl; + m_vpgstbl[i]->SetNumberOfComponents(m_cComponents); + } + + // Getters: + int NumberOfGlyphs() { return m_cglf; } + int NumberOfStyles() { return m_cgstbl; } + + int NumberOfGlyphAttrs() + { + // All of the sub-tables should have the same number of glyph attributes, + // so just ask the first. + return m_vpgstbl[0]->NumberOfGlyphAttrs(); + } + + int GlyphAttrValue(gid16 chwGlyphID, int nAttrID) + { + gAssert(m_cgstbl == 1); + return m_vpgstbl[0]->GlyphAttrValue(chwGlyphID, nAttrID); + } + // Eventually: + //int GlyphAttrValue(gid16 chwGlyphID, int nAttrID, int nStyleIndex) + //{ + // return m_vpgstbl[nStyleIndex]->GlyphAttrValue(chwGlyphID, nAttrID); + //} + + int ComponentContainingPoint(gid16 chwGlyphID, int x, int y) + { + gAssert(m_cgstbl == 1); + return m_vpgstbl[0]->ComponentContainingPoint(chwGlyphID, x, y); + } + // Eventually: + //int ComponentContainingPoint(gid16 chwGlyphID, int nStyleIndex, int x, int y) + //{ + // gAssert(m_cgstbl == 1); + // return m_vpgstbl[nStyleIndex]->ComponentContainingPoint(chwGlyphID, nAttrID); + //} + + bool ComponentBoxLogUnits(float xysEmSquare, gid16 chwGlyphID, int icomp, + int mFontEmUnits, float dysAscent, + float * pxsLeft, float * pysTop, float * pxsRight, float * pysBottom, + bool fTopOrigin = true) + { + gAssert(m_cgstbl == 1); + return m_vpgstbl[0]->ComponentBoxLogUnits(xysEmSquare, + chwGlyphID, icomp, mFontEmUnits, dysAscent, + pxsLeft, pysTop, pxsRight, pysBottom, + fTopOrigin); + } + + int ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID) + { + gAssert(m_cgstbl == 1); + return m_vpgstbl[0]->ComponentIndexForGlyph(chwGlyphID, nCompID); + } + + bool IsEmpty() + { + return (m_cglf == 0); + } + +protected: + int m_cglf; // number of glyphs + int m_cComponents; // number of defined components + int m_cgstbl; // number of sub-tables, corresponding to font files; + // for now there is only one + + std::vector<GrGlyphSubTable *> m_vpgstbl; + +//:Ignore +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + void SetUpTestData(); + void SetUpLigatureTest(); + void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF +//:End Ignore + +}; + +} // namespace gr + +#endif // !GR_GTABLE_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPass.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPass.cpp new file mode 100644 index 00000000000..ce8094744eb --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPass.cpp @@ -0,0 +1,2030 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrPass.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements the GrPass class and subclasses. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** +/*---------------------------------------------------------------------------------------------- + Constructors and initializers +----------------------------------------------------------------------------------------------*/ +GrPass::GrPass(int i) + : m_ipass(i), + m_fxdVersion(0), + m_nMaxRuleContext(0), + m_pfsm(NULL), + m_nMaxRuleLoop(0), + m_nMaxBackup(0), + m_crul(0), + m_prgchwRuleSortKeys(NULL), + m_prgcritRulePreModContext(NULL), + m_cbPassConstraint(0), + m_prgibConstraintStart(NULL), + m_prgibActionStart(NULL), + m_prgbPConstraintBlock(NULL), + m_prgbConstraintBlock(NULL), + m_prgbActionBlock(NULL), + m_cbConstraints(0), + m_cbActions(0), + m_prgibConstraintDebug(NULL), + m_prgibRuleDebug(NULL), + m_fCheckRules(false), + m_prgfRuleOkay(NULL), + m_vnStack(128), + m_pzpst(NULL) +{ +} + +void PassState::InitForNewSegment(int ipass, int nMaxChunk) +{ + m_ipass = ipass; + m_nRulesSinceAdvance = 0; + m_nMaxChunk = nMaxChunk; + m_cslotSkipToResync = 0; + m_fDidResyncSkip = false; + InitializeLogInfo(); +} + +void PassState::InitializeLogInfo() +{ + m_crulrec = 0; + std::fill_n(m_rgcslotDeletions, 128, 0); + std::fill_n(m_rgfInsertion, 128, false); +} + +/*---------------------------------------------------------------------------------------------- + Destructors +----------------------------------------------------------------------------------------------*/ +GrPass::~GrPass() +{ + delete m_pfsm; + + delete[] m_prgchwRuleSortKeys; + + delete[] m_prgcritRulePreModContext; + + delete[] m_prgibConstraintStart; + delete[] m_prgibActionStart; + + delete[] m_prgbPConstraintBlock; + delete[] m_prgbConstraintBlock; + delete[] m_prgbActionBlock; + + delete[] m_prgfRuleOkay; + + delete[] m_prgibConstraintDebug; + delete[] m_prgibRuleDebug; +} + +/*---------------------------------------------------------------------------------------------- + Fill in the pass by reading from the font stream. +----------------------------------------------------------------------------------------------*/ +bool GrPass::ReadFromFont(GrIStream & grstrm, int fxdSilfVersion, int fxdRuleVersion, + int nOffset) +{ + long lPassInfoStart; + grstrm.GetPositionInFont(&lPassInfoStart); + + m_fxdVersion = fxdSilfVersion; + + m_fCheckRules = (fxdRuleVersion > kRuleVersion); + +// Assert(nOffset == lPassInfoStart); + if (lPassInfoStart != nOffset) + { + grstrm.SetPositionInFont(nOffset); + } + + // flags - ignore for now + //byte bTmp = grstrm.ReadByteFromFont(); + grstrm.ReadByteFromFont(); + + // MaxRuleLoop + m_nMaxRuleLoop = grstrm.ReadByteFromFont(); + + // max rule context + m_nMaxRuleContext = grstrm.ReadByteFromFont(); + + // MaxBackup + m_nMaxBackup = grstrm.ReadByteFromFont(); + + // number of rules + m_crul = grstrm.ReadShortFromFont(); + // TODO: add a sanity check for the number of rules. + + // offset to pass constraint code, relative to start of subtable + int nPConstraintOffset = 0; + long lFsmPos = -1; + if (fxdSilfVersion >= 0x00020000) + { + if (fxdSilfVersion >= 0x00030000) + lFsmPos = grstrm.ReadUShortFromFont() + nOffset; // offset to row info + else + grstrm.ReadShortFromFont(); // pad bytes + nPConstraintOffset = grstrm.ReadIntFromFont(); + } + // offset to rule constraint code, relative to start of subtable + //int nConstraintOffset = grstrm.ReadIntFromFont(); + grstrm.ReadIntFromFont(); + // offset to action code, relative to start of subtable + //int nActionOffset = grstrm.ReadIntFromFont(); + grstrm.ReadIntFromFont(); + // offset to debug strings; 0 if stripped + //int nDebugOffset = grstrm.ReadIntFromFont(); + grstrm.ReadIntFromFont(); + + // Jump to beginning of FSM, if we have this information. + if (fxdSilfVersion >= 0x00030000) + grstrm.SetPositionInFont(lFsmPos); + else + // Otherwise assume that's where we are! + Assert(lFsmPos == -1); + + m_pfsm = new GrFSM(); + + m_pfsm->ReadFromFont(grstrm, fxdSilfVersion); + + // rule sort keys + m_prgchwRuleSortKeys = new data16[m_crul]; + data16 * pchw = m_prgchwRuleSortKeys; + int irul; + for (irul = 0; irul < m_crul; irul++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // rule pre-mod-context item counts + m_prgcritRulePreModContext = new byte[m_crul]; + byte * pb = m_prgcritRulePreModContext; + for (irul = 0; irul < m_crul; irul++, pb++) + { + *pb = grstrm.ReadByteFromFont(); + } + + // constraint offset for pass-level constraints + if (fxdSilfVersion >= 0x00020000) + { + // reserved - pad byte + grstrm.ReadByteFromFont(); + // Note: pass constraints have not been fully implemented. + m_cbPassConstraint = grstrm.ReadUShortFromFont(); + } + else + m_cbPassConstraint = 0; + + // constraint and action offsets for rules + m_prgibConstraintStart = new data16[m_crul + 1]; + pchw = m_prgibConstraintStart; + for (irul = 0; irul <= m_crul; irul++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + m_prgibActionStart = new data16[m_crul + 1]; + pchw = m_prgibActionStart; + for (irul = 0; irul <= m_crul; irul++, pchw++) + { + *pchw = grstrm.ReadUShortFromFont(); + } + + // FSM state table + m_pfsm->ReadStateTableFromFont(grstrm, fxdSilfVersion); + + if (fxdSilfVersion >= 0x00020000) + // reserved - pad byte + grstrm.ReadByteFromFont(); + + // Constraint and action blocks + int cb = m_cbPassConstraint; + m_prgbPConstraintBlock = new byte[cb]; + grstrm.ReadBlockFromFont(m_prgbPConstraintBlock, cb); + m_cbConstraints = cb; + + cb = m_prgibConstraintStart[m_crul]; + m_prgbConstraintBlock = new byte[cb]; + grstrm.ReadBlockFromFont(m_prgbConstraintBlock, cb); + m_cbConstraints += cb; + + cb = m_prgibActionStart[m_crul]; + m_prgbActionBlock = new byte[cb]; + grstrm.ReadBlockFromFont(m_prgbActionBlock, cb); + m_cbActions = cb; + + // Rule-validity flags + m_prgfRuleOkay = new bool[m_crul]; + for (irul = 0; irul < m_crul; irul++) + m_prgfRuleOkay[irul] = !m_fCheckRules; + + // TODO SharonC/AlanW: debuggers + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Initialize a bogus pass with no rules. Currently this is used to make a single positioning + pass if there was none in the font. +----------------------------------------------------------------------------------------------*/ +void GrPass::InitializeWithNoRules() +{ + m_crul = 0; + m_nMaxRuleContext = 1; + m_nMaxRuleLoop = 1; + m_nMaxBackup = 0; + m_pfsm = NULL; + m_prgchwRuleSortKeys = NULL; + m_prgcritRulePreModContext = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Extend the output stream by the given number of characters. + (Overridden by GrBidiPass.) + + @param ptman - table manager + @param psstrmIn/Out - streams being processed + @param cslotNeededByNext - the number of slots being requested by the following pass + @param twsh - how we are handling trailing white-space + @param pnRet - return value + @param pcslotGot - return the number of slots gotten + @param pislotFinalBreak - return the index of the final slot, when we are removing + the trailing white-space and so the end of the segment + will be before the any actual line-break slot + + @return kNextPass if we were able to generated the number requested, or processing is + complete; otherwise return the number of slots needed from the previous pass. +----------------------------------------------------------------------------------------------*/ +void GrPass::ExtendOutput(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotNeededByNext, TrWsHandling twsh, + int * pnRet, int * pcslotGot, int * pislotFinalBreak) +{ + // Kludge to generate an error in rendering: + //int z = 50; + //int t = ((5 * 10) / z) - 1; + //if (this->m_ipass == 4) + //{ + // int x; x = z / t; + //} + + int islotInitReadPos = psstrmIn->ReadPos(); + int islotInitWritePos = psstrmOut->WritePos(); + + int cslotToGet = max(cslotNeededByNext, + m_pzpst->NeededToResync() - psstrmOut->WritePos()); + int cslotGot = 0; + + gid16 chwLB = ptman->LBGlyphID(); + + // While we haven't got the number of slot we've been asked for, and there is enough + // available in the input, run rules, filling up the output. + + while ((cslotToGet > 0 && cslotGot < cslotToGet) + // Don't leave this pass until we've got the index offset. This is especially + // needed for input to the positioning pass, but it doesn't hurt to make it a + // general rule. + || !psstrmOut->GotIndexOffset() + // Don't leave this pass until all the slots to reprocess have been + // completely handled. + || (psstrmIn->SlotsToReprocess() > 0) + // Don't leave this pass until there is a complete cluster in the output + || (islotInitWritePos == psstrmOut->WritePos()) + || (psstrmOut->MaxClusterSlot(islotInitWritePos, psstrmOut->WritePos()) > 0)) + { + int cslotAvailable = psstrmIn->TotalSlotsPending(); + int cslotNeedMore = MaxRuleContext() - cslotAvailable + ptman->PrevPassMaxBackup(m_ipass); + + // This test should not be necessary, but just in case. + if (psstrmIn->PastEndOfPositioning(false)) + cslotNeedMore = 0; // the input stream is done + + if (// Not enough available in the input: + (cslotNeedMore > 0 && !psstrmIn->FullyWritten()) + + // && psstrmIn->SegLimIfKnown() == -1) && -- no, because substitution passes may need + // to consider slots beyond the seg limit + + // Positioning passes need to know where they are in relation to the initial + // line-break. + || (IsPosPass() && !psstrmIn->GotIndexOffset()) + + // The following can happen on backtracking, when we undo stuff before the + // beginning of the segment: + || !ptman->Pass(m_ipass - 1)->DidResyncSkip()) + { + // Ask previous pass for more input. + *pnRet = max(cslotNeedMore, 1); + *pnRet = max(*pnRet, cslotNeededByNext - cslotGot); + *pcslotGot = cslotGot; + return; + } + + Assert(ptman->Pass(m_ipass - 1)->DidResyncSkip()); + + bool fDoneThisPass = (cslotAvailable == 0); + if (psstrmIn->SlotsToReprocess() == 0) + { + fDoneThisPass = fDoneThisPass || + psstrmIn->PastEndOfPositioning(false) || + psstrmOut->PastEndOfPositioning(true); + } + + if (fDoneThisPass) + { + // No more input to process--this pass is done. + Assert(psstrmIn->SlotsToReprocess() == 0); + psstrmIn->ClearReprocBuffer(); + + if (twsh == ktwshNoWs && m_ipass == ptman->NumberOfLbPasses()) + { + *pnRet = RemoveTrailingWhiteSpace(ptman, psstrmOut, twsh, pislotFinalBreak); + if (*pnRet == kBacktrack) // entire segment was white-space: + return; // backtrack, which will fail + } + DoResyncSkip(psstrmOut); + DoCleanUpSegMin(ptman, psstrmIn, islotInitReadPos, psstrmOut); + DoCleanUpSegLim(ptman, psstrmOut, twsh); + psstrmOut->MarkFullyWritten(); + *pnRet = kNextPass; + *pcslotGot = cslotGot; + return; + } + + if (twsh == ktwshOnlyWs && m_ipass == ptman->NumberOfLbPasses() + 1) + { + // Note that this is the first pass after the linebreak pass, so psstrmInput + // is the output of the linebreak pass. + GrSlotState * pslotNext = psstrmIn->Peek(); + if (!pslotNext->IsLineBreak(chwLB) && + pslotNext->Directionality() != kdircWhiteSpace && + psstrmIn->SegMin() > -1 && psstrmIn->SegMin() <= psstrmIn->ReadPos()) + { + // We are only allowing white-space in this segment and we hit + // something else. Don't process any further. + if (psstrmIn->SegLimIfKnown() > -1 && + psstrmIn->SegLimIfKnown() <= psstrmIn->ReadPos()) + { + // Already inserted a line-break; we're done. + DoResyncSkip(psstrmOut); + psstrmOut->MarkFullyWritten(); + *pnRet = kNextPass; + *pcslotGot = cslotGot; + return; + } + while (psstrmIn->SlotsToReprocess() > 0) + { + psstrmOut->CopyOneSlotFrom(psstrmIn); + psstrmOut->SetPosForNextRule(0, psstrmIn, IsPosPass()); + } + psstrmIn->ClearReprocBuffer(); + *pnRet = kBacktrack; + return; + } + } + + // Otherwise, we have enough input to run a rule. + + psstrmIn->SetRuleStartReadPos(); + psstrmOut->SetRuleStartWritePos(); + + int ruln = -1; + if (m_pfsm) + ruln = m_pfsm->GetRuleToApply(ptman, this, psstrmIn, psstrmOut); + ruln = CheckRuleValidity(ruln); + RunRule(ptman, ruln, psstrmIn, psstrmOut); + + cslotGot = psstrmOut->WritePos() - islotInitWritePos; + psstrmOut->CalcIndexOffset(ptman); + } + + DoResyncSkip(psstrmOut); + DoCleanUpSegMin(ptman, psstrmIn, islotInitReadPos, psstrmOut); + + // We're past the point where we care about anything in the reprocessing buffer. + Assert(psstrmIn->NoReproc()); + psstrmIn->ClearReprocBuffer(); + + if (psstrmOut->PastEndOfPositioning(true)) + { + DoCleanUpSegLim(ptman, psstrmOut, twsh); + psstrmOut->MarkFullyWritten(); + } + + *pnRet = kNextPass; + *pcslotGot = cslotGot; +} + +/*---------------------------------------------------------------------------------------------- + Extend the output stream by the given number of characters. + For a GrBidiPass, if we are at a direction change, get the entire + range of upstream glyphs, reverse them, and treat them as one chunk. + Otherwise just pass one slot through. + + @param ptman - table manager + @param psstrmInput/Output - streams being processed + @param cslotNeededByNext - the number of slots being requested by the following pass + @param twsh - how we are handling trailing white-space + @param pnRet - return value + @param pcslotGot - return the number of slots gotten + @param pislotFinalBreak - not used in this version + + @return 1 if we need more glyphs from the previous pass +----------------------------------------------------------------------------------------------*/ +void GrBidiPass::ExtendOutput(GrTableManager * ptman, + GrSlotStream* psstrmIn, GrSlotStream* psstrmOut, + int cslotNeededByNext, TrWsHandling twsh, + int * pnRet, int * pcslotGot, int * pislotFinalBreak) +{ + Assert(psstrmIn->SlotsToReprocess() == 0); + + int islotInitReadPos = psstrmIn->ReadPos(); + int islotInitWritePos = psstrmOut->WritePos(); + + Assert(m_pzpst->NeededToResync() == 0); + + int cslotToGet = max(cslotNeededByNext, + m_pzpst->NeededToResync() - psstrmOut->WritePos()); + int cslotGot = 0; + int nTopDir; + if (twsh == ktwshOnlyWs) + nTopDir = (ptman->State()->ParaRightToLeft()) ? 1 : 0; + else + nTopDir = ptman->TopDirectionLevel(); + + while (cslotToGet > 0 && cslotGot < cslotToGet) + { + int islotChunkO = psstrmOut->WritePos(); + int islotChunkI = psstrmIn->ReadPos(); + + // Need at least one character to test. + if (psstrmIn->SlotsPending() < 1 || !ptman->Pass(m_ipass-1)->DidResyncSkip()) + { + if (!psstrmIn->FullyWritten()) + { + // Ask previous pass for more input. + *pnRet = max(cslotToGet - psstrmIn->SlotsPending(), 1); + *pnRet = max(*pnRet, cslotNeededByNext - cslotGot); + *pcslotGot = cslotGot; + return; + } + else + { + Assert(ptman->Pass(m_ipass-1)->DidResyncSkip()); + Assert(psstrmIn->SlotsToReprocess() == 0); + psstrmIn->ClearReprocBuffer(); + DoResyncSkip(psstrmOut); + DoCleanUpSegMin(ptman, psstrmIn, islotInitReadPos, psstrmOut); + DoCleanUpSegLim(ptman, psstrmOut, twsh); + psstrmOut->MarkFullyWritten(); + *pnRet = kNextPass; + *pcslotGot = cslotGot; + return; + } + } + + std::vector<int> vislotStarts; + std::vector<int> vislotStops; + int islotReverseLim = psstrmIn->DirLevelRange(ptman->State(), + psstrmIn->ReadPos(), nTopDir, + vislotStarts, vislotStops); + //int islotReverseLim = psstrmIn->OldDirLevelRange(pengst, psstrmIn->ReadPos(), nTopDir); + if (islotReverseLim == -1) + { + // We haven't got the full range of reversed text yet-- + // ask for more input. + *pnRet = max(1, cslotNeededByNext - cslotGot); + *pcslotGot = cslotGot; + return; + } + + // Okay, we have enough input to do the reversal, if any. + + int cslotToReverse = islotReverseLim - psstrmIn->ReadPos(); + + // Never reverse the final linebreak; leave it at the end. + if (cslotToReverse > 0 && islotReverseLim > 0) + { + GrSlotState * pslotLast = psstrmIn->SlotAt(islotReverseLim - 1); + if (pslotLast->IsFinalLineBreak(ptman->LBGlyphID())) + { + for (size_t i = 0; i < vislotStops.size(); i++) + if (vislotStops.back() == islotReverseLim - 1) + vislotStops.back() = islotReverseLim - 2; + islotReverseLim--; + cslotToReverse--; + } + } + + psstrmIn->SetRuleStartReadPos(); + psstrmOut->SetRuleStartWritePos(); + + if (cslotToReverse == 0) + { + psstrmOut->CopyOneSlotFrom(psstrmIn); + } + else + { + int islotNextWritePos = psstrmOut->WritePos() + cslotToReverse; + int islotNextReadPos = psstrmIn->ReadPos() + cslotToReverse; + if (vislotStarts.size() == 0) + { + Assert(false); // this should have been done by DirLevelRange + vislotStarts.push_back(psstrmIn->ReadPos()); + vislotStops.push_back(islotNextReadPos - 1); + } + Assert(vislotStarts.back() == psstrmIn->ReadPos()); + Assert(vislotStops.back() == islotNextReadPos - 1); + int cslotNotCopied = Reverse(ptman, psstrmIn, psstrmOut, vislotStarts, vislotStops); + //Reverse(nTopDir + 1, + // psstrmIn, psstrmIn->ReadPos(), islotReverseLim, + // psstrmOut, psstrmOut->WritePos() + cslotToReverse - 1, psstrmOut->WritePos()-1); + + islotNextWritePos -= cslotNotCopied; // bidi markers that are not passed through + + psstrmIn->SetReadPos(islotNextReadPos); + psstrmOut->SetWritePos(islotNextWritePos); + + // It's quite strange to have the segment start or end in the middle of stuff to + // reverse (because the LB forms a natural terminator), but at any rate, + // if that happens, record the segment lim at the corresponding place in the + // output stream. + int islotSegMinIn = psstrmIn->SegMin(); + if (islotSegMinIn > -1 && + psstrmIn->ReadPos() - cslotToReverse <= islotSegMinIn && + islotSegMinIn < psstrmIn->ReadPos()) + { + Assert(islotSegMinIn == psstrmIn->ReadPos() - cslotToReverse); // normal situation + psstrmOut->SetSegMin( + psstrmOut->WritePos() - (psstrmIn->ReadPos() - islotSegMinIn)); + } + int islotSegLimIn = psstrmIn->SegLimIfKnown(); + if (islotSegLimIn > -1 && + psstrmIn->ReadPos() - cslotToReverse <= islotSegLimIn && + islotSegLimIn < psstrmIn->ReadPos()) + { + Assert(islotSegLimIn == psstrmIn->ReadPos() - cslotToReverse); // normal situation + psstrmOut->SetSegLim( + psstrmOut->WritePos() - (psstrmIn->ReadPos() - islotSegLimIn)); + } + } + + psstrmOut->SetPosForNextRule(0, psstrmIn, false); + + // Record the chunk mappings: + MapChunks(psstrmIn, psstrmOut, islotChunkI, islotChunkO, 0); + + cslotGot = psstrmOut->WritePos() - islotInitWritePos; + psstrmOut->CalcIndexOffset(ptman); + } + + DoResyncSkip(psstrmOut); + DoCleanUpSegMin(ptman, psstrmIn, islotInitReadPos, psstrmOut); + + // We're past the point where we care about anything in the reprocessing buffer. + Assert(psstrmIn->NoReproc()); + psstrmIn->ClearReprocBuffer(); + + Assert(psstrmIn->SlotsToReprocess() == 0); + + if (psstrmOut->PastEndOfPositioning(true)) + { + DoCleanUpSegLim(ptman, psstrmOut, twsh); + psstrmOut->MarkFullyWritten(); + } + + *pnRet = kNextPass; + *pcslotGot = cslotGot; +} + +/*---------------------------------------------------------------------------------------------- + Generate slots containing glyph IDs for the underlying character data, + incrementing the input pointer as we go. + + @param ptman - table manager + @param pchstrm - input character stream + @param psstrmOutput - output slot stream + @param ichSegLim - known end of segment, index into the text; or -1; + the lim of the of char-stream itself represents + the end of the text-source; this lim is the + known desired end of the segment + @param cchwPostXlbContext - number of characters that may be needed from the following line; + valid when ichSegLim > -1 + @param lb - breakweight to use for inserted LB + @param cslotToGet - the number of slots being requested by the following pass + @param fNeedFinalBreak - true if the end of the segment needs to be a valid break point + @param pislotFinalBreak - the end of this segment +----------------------------------------------------------------------------------------------*/ +int GrPass::ExtendGlyphIDOutput(GrTableManager * ptman, + GrCharStream * pchstrm, GrSlotStream * psstrmOut, int ichSegLim, int cchwPostXlbContext, + LineBrk lb, int cslotToGet, bool fNeedFinalBreak, TrWsHandling twsh, + int * pislotFinalBreak) +{ + EngineState * pengst = ptman->State(); + + // This pass should be the glyph-generation pass. + Assert(dynamic_cast<GrGlyphGenPass*>(this)); + Assert(m_pzpst->m_cslotSkipToResync == 0); + m_pzpst->m_fDidResyncSkip = true; + + for (int islot = 0; islot < cslotToGet; ++islot) + { + int islotChunkO = psstrmOut->WritePos(); + int islotChunkI = pchstrm->SegOffset(); + + if (pchstrm->AtEnd() + || (ichSegLim > -1 && pchstrm->Pos() == ichSegLim)) + { + if (psstrmOut->SegLimIfKnown() > -1 && + psstrmOut->SegLimIfKnown() <= psstrmOut->WritePos()) + { + // Already found the end of this stream. + } + else + { + if (pchstrm->EndLine() && !fNeedFinalBreak) + // (if we need a good final break, don't just append an LB; + // make it backtrack and find a valid break point) + { + // Only need to get a good break when we're backtracking; otherwise we + // already know it. + Assert(ichSegLim == -1 || !fNeedFinalBreak); + // Notice that we're cheating here: we're putting the LB in the zeroth + // stream instead of in the output of the linebreak table. + psstrmOut->AppendLineBreak(ptman, pchstrm, + (pchstrm->AtEnd() ? klbWordBreak : lb), + ((ptman->RightToLeft()) ? kdircRlb : kdircLlb), -1, false, + pchstrm->SegOffset()); + if (pchstrm->AtEnd()) + pengst->SetFinalLB(); + else + pengst->SetInsertedLB(true); + } + else + { + // Don't actually insert a line-break glyph. + psstrmOut->SetSegLimToWritePos(); + } + *pislotFinalBreak = psstrmOut->WritePos() - 1; + if (ptman->NumberOfLbPasses() > 0 && pengst->HasInitialLB()) + { + // Because we cheated above: the output stream of the linekbreak table + // has an initial LB, which this stream doesn't have. Adjust the position + // of the final break to match what it will be in the output of the + // lb table. + *pislotFinalBreak += 1; + } + } + + if (twsh == ktwshNoWs && m_ipass == ptman->NumberOfLbPasses()) + { + int nRet = RemoveTrailingWhiteSpace(ptman, psstrmOut, twsh, pislotFinalBreak); + if (nRet == kBacktrack) // entire segment was white-space: + return kBacktrack; // backtrack, which will fail + } + if (pchstrm->AtEnd()) + { + psstrmOut->MarkFullyWritten(); + return kNextPass; + } + // otherwise we may need a few more characters for line-boundary contextualization. + } + + int ichwSegOffset; // offset from the official start of the segment + int cchw; // number of 16-bit chars consumed + GrFeatureValues fval; + int nUnicode = pchstrm->NextGet(ptman, &fval, &ichwSegOffset, &cchw); + gid16 chwGlyphID = ptman->GetGlyphIDFromUnicode(nUnicode); + + if (nUnicode == knCR || nUnicode == knLF || nUnicode == knLineSep || nUnicode == knParaSep || + nUnicode == knORC) + { + // Hard line-break: we're done. + // Note that we don't include the hard-break character in this segment. + pchstrm->HitHardBreak(); + pengst->SetHitHardBreak(); + psstrmOut->MarkFullyWritten(); + return kNextPass; + } + + GrSlotState * pslotNew; + ptman->State()->NewSlot(chwGlyphID, fval, 0, ichwSegOffset, nUnicode, &pslotNew); + + psstrmOut->NextPut(pslotNew); + psstrmOut->MapInputChunk(islotChunkI, islotChunkO, pchstrm->SegOffset(), false, false); + // Mapping the output chunks of the char stream has already been handled by the + // char stream. + + } + + psstrmOut->CalcIndexOffset(ptman); + + if (psstrmOut->PastEndOfPositioning(true) + || (ichSegLim > -1 && pchstrm->Pos() > ichSegLim + cchwPostXlbContext)) + { + // We have enough for this segment. + psstrmOut->MarkFullyWritten(); + } + + return kNextPass; +} + +/*---------------------------------------------------------------------------------------------- + Extend the output stream until it is using up the allotted amount of + physical space. Return kNextPass when all data has been processed successfully. + Return kBacktrack when space overflows, indicating that we need to backtrack + and find a break point. Otherwise, return the number of slots we + need from the previous pass. + + @param fWidthIsCharCount - kludge for test procedures + @param fInfiniteWidth - don't test for more space + @param fMustBacktrack - true if we need a good final break and haven't found one yet + @param lbMax - max allowed for the final slot + @param twsh - how we are handling trailing white-space +----------------------------------------------------------------------------------------------*/ +int GrPass::ExtendFinalOutput(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput, + float xsSpaceAllotted, bool fWidthIsCharCount, bool fInfiniteWidth, + bool fHaveLineBreak, bool fMustBacktrack, LineBrk lbMax, TrWsHandling twsh, + int * pislotLB, float * pxsWidth) +{ + EngineState * pengst = ptman->State(); + + // This pass should be the final positioning pass. + Assert(dynamic_cast<GrPosPass*>(this)); + + // It would be very strange to be positioning based on something that happened in + // previous line. -- not true any more, since this will include the max-precontext-length. +// Assert(m_cslotSkipToResync == 0); + + int islotOutputLB = -1; + //int islotInitWritePos = psstrmOutput->WritePos(); + int islotNoLbUpTo = psstrmOutput->WritePos();; + + while (true) + { + // Do this right up front, so we are only measuring actual output. + if (m_pzpst->CanResyncSkip(psstrmOutput)) + m_pzpst->DoResyncSkip(psstrmOutput); + + bool fMoreSpace; + if (!m_pzpst->DidResyncSkip()) + fMoreSpace = true; + else if (fInfiniteWidth) + fMoreSpace = true; + else + fMoreSpace = psstrmOutput->MoreSpace(ptman, + xsSpaceAllotted, fWidthIsCharCount, + true, // always ignore trailing white space when we are first making the segment + twsh, + pxsWidth); + if (!fMoreSpace) + { + // Overflowed available space; backtrack and find a line break. + while (psstrmInput->SlotsToReprocess() > 0) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmOutput->SetPosForNextRule(0, psstrmInput, IsPosPass()); + } + psstrmInput->ClearReprocBuffer(); + + *pislotLB = -1; + pengst->SetExceededSpace(); + pengst->SetHitHardBreak(false); + return kBacktrack; + } + + if (islotOutputLB != -1 && psstrmInput->SlotsToReprocess() == 0) + { + // Hit the inserted line break--we're done. + psstrmInput->ClearReprocBuffer(); + *pislotLB = islotOutputLB; + return kNextPass; + } + + int nslotAvailable = psstrmInput->SlotsPending(); + if ((nslotAvailable - ptman->PrevPassMaxBackup(m_ipass) + < MaxRuleContext() && !psstrmInput->FullyWritten()) + || !ptman->Pass(m_ipass-1)->DidResyncSkip()) + { + // Not enough available in the input--ask previous pass for more input. + // Ten is an arbitrary value--we ask for more that we really need to cut down + // on the number of times we loop between passes. + return max(MaxRuleContext() - (nslotAvailable - 10), 1); + } + + Assert(ptman->Pass(m_ipass - 1)->DidResyncSkip()); + + if (nslotAvailable == 0) + { + // No more input to process. If we have a valid line-break, or we don't care, + // we're done. Otherwise backtrack to find a valid break point. + Assert(psstrmInput->SlotsToReprocess() == 0); + psstrmInput->ClearReprocBuffer(); + if (fMustBacktrack) + { + *pislotLB = -1; + return kBacktrack; + } + else + { + psstrmOutput->MarkFullyWritten(); + return kNextPass; + } + } + + // Otherwise, we have enough input to run a rule. + + psstrmInput->SetRuleStartReadPos(); + psstrmOutput->SetRuleStartWritePos(); + + int ruln = -1; + if (m_pfsm) + ruln = m_pfsm->GetRuleToApply(ptman, this, psstrmInput, psstrmOutput); + ruln = CheckRuleValidity(ruln); + RunRule(ptman, ruln, psstrmInput, psstrmOutput); + + if (fHaveLineBreak) + { + islotOutputLB = + psstrmOutput->FindFinalLineBreak(ptman->LBGlyphID(), + islotNoLbUpTo, psstrmOutput->WritePos()); + islotNoLbUpTo = psstrmOutput->WritePos(); + } + + psstrmOutput->CalcIndexOffset(ptman); + } + + Assert(false); + + psstrmInput->ClearReprocBuffer(); + *pislotLB = -1; + return kNextPass; +} + +/*---------------------------------------------------------------------------------------------- + Remove undesirable trailing white-space. +----------------------------------------------------------------------------------------------*/ +int GrPass::RemoveTrailingWhiteSpace(GrTableManager * ptman, GrSlotStream * psstrmOut, + TrWsHandling twsh, int * pislotFinalBreak) +{ + EngineState * pengst = ptman->State(); + + Assert(twsh == ktwshNoWs); + Assert(m_ipass == ptman->NumberOfLbPasses()); // output of (final) lb pass + + int islotTmp = psstrmOut->FinalSegLim(); + if (islotTmp <= 0) + return kNextPass; + + GrSlotState * pslotLast = psstrmOut->SlotAt(islotTmp-1); + if (islotTmp > 0 && pslotLast->IsFinalLineBreak(ptman->LBGlyphID())) + { + islotTmp--; + pslotLast = (islotTmp > 0) ? psstrmOut->SlotAt(islotTmp-1) : NULL; + } + bool fRemovedWs = false; + while (islotTmp > 0 && pslotLast->Directionality() == kdircWhiteSpace) + { + islotTmp--; + pslotLast = (islotTmp > 0) ? psstrmOut->SlotAt(islotTmp-1) : NULL; + fRemovedWs = true; + } + if (fRemovedWs) + { + if (islotTmp <= 0) + { + // Entire segment was white-space: backtrack, which will fail. + return kBacktrack; + } + psstrmOut->SetSegLim(islotTmp); + *pislotFinalBreak = islotTmp - 1; + pengst->SetFinalLB(false); + pengst->SetRemovedTrWhiteSpace(); + ptman->UnwindAndReinit(islotTmp - 1); + } + + return kNextPass; +} + +/*---------------------------------------------------------------------------------------------- + Keep track of whether we're advancing through the input satisfactorily; + if not, forcibly advance. This is a safety net to avoid infinite loops; it + should never be necessary if they've set up their tables right. +----------------------------------------------------------------------------------------------*/ +void GrPass::CheckInputProgress(GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput, + int islotOrigInput) +{ + int islotInput = psstrmInput->ReadPosForNextGet(); +// Assert(islotInput >= islotOrigInput); -- no longer true now that we can back up + + if (islotInput <= psstrmInput->ReadPosMax()) + { + // Didn't advance. + if (m_pzpst->m_nRulesSinceAdvance >= m_nMaxRuleLoop) + { + bool fAdvanced = false; + // Forcibly advance. First try to advance to where we backed up from. + while (!psstrmInput->AtEnd() && + psstrmInput->ReadPosForNextGet() < psstrmInput->ReadPosMax()) + { + RecordHitMaxRuleLoop(psstrmInput->ReadPosForNextGet()); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + fAdvanced = true; + } + // If that didn't do anything productive, just advance one slot. + if (!fAdvanced && !psstrmInput->AtEndOfContext()) + { + RecordHitMaxRuleLoop(psstrmInput->ReadPosForNextGet()); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + } + + m_pzpst->m_nRulesSinceAdvance = 0; + } + else m_pzpst->m_nRulesSinceAdvance++; + } + else m_pzpst->m_nRulesSinceAdvance = 0; + + psstrmInput->SetReadPosMax(islotInput); +} + +/*---------------------------------------------------------------------------------------------- + Record the chunks in the streams' chunk maps. + + psstrmIn/Out - streams being processed + islotChunkIn/Out - start of chunks + cslotReprocessed - number of slots to reprocess before the rule was run +----------------------------------------------------------------------------------------------*/ +void GrPass::MapChunks(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int islotChunkIn, int islotChunkOut, int cslotReprocessed) +{ + if (islotChunkOut > psstrmOut->WritePos()) + { + // backing up + int islotReadPosTmp = psstrmIn->ReadPosForNextGet(); +/// Assert((islotChunkIn - islotReadPosTmp) == (islotChunkOut - psstrmOut->WritePos())); +// psstrmIn->MapOutputChunk(psstrmOut->WritePos() - 1, islotReadPosTmp - 1, +// islotChunkOut, true, 0, true); +// psstrmOut->MapInputChunk(islotReadPosTmp - 1, psstrmOut->WritePos() - 1, +// islotChunkIn, true, true); + + // Resync. + if (psstrmOut->WritePos() == 0) + { + // Clear all the chunks. + psstrmIn->MapOutputChunk(-1, -1, 0, true, 0, true); + psstrmOut->MapInputChunk(-1, -1, islotReadPosTmp, true, true); + psstrmIn->AssertChunkMapsValid(psstrmOut); + return; + } + else if (islotReadPosTmp == 0) + { + // Clear all the chunks. + psstrmIn->MapOutputChunk(-1, -1, psstrmOut->WritePos(), true, 0, true); + psstrmOut->MapInputChunk(-1, -1, 0, true, true); + psstrmIn->AssertChunkMapsValid(psstrmOut); + return; + } + // Find the beginning of the current chunk. + int islotChunkOutAdj = min(islotChunkOut, psstrmOut->WritePos() - 1); + int islotChunkInAdj = psstrmOut->ChunkInPrev(islotChunkOutAdj); + while (islotChunkInAdj == -1 && islotChunkOutAdj > 0) + islotChunkInAdj = psstrmOut->ChunkInPrev(--islotChunkOutAdj); + + if (islotChunkInAdj == -1) + { + // Couldn't find the beginning of any chunk; zap them all. + psstrmIn->MapOutputChunk(-1, -1, psstrmOut->WritePos(), true, 0, true); + psstrmOut->MapInputChunk(-1, -1, psstrmOut->ReadPos(), true, true); + psstrmIn->AssertChunkMapsValid(psstrmOut); + return; + } + + if (psstrmIn->ChunkInNext(islotChunkInAdj) != islotChunkOutAdj) + { + islotChunkOutAdj = psstrmIn->ChunkInNext(islotChunkInAdj); + while (islotChunkOutAdj == -1 && islotChunkInAdj > 0) + islotChunkOutAdj = psstrmIn->ChunkInNext(--islotChunkInAdj); + } + + psstrmIn->MapOutputChunk(islotChunkOutAdj, islotChunkInAdj, + psstrmOut->WritePos(), false, 0, true); + psstrmOut->MapInputChunk(islotChunkInAdj, islotChunkOutAdj, + psstrmIn->ReadPos(), false, true); + } + else if (islotChunkOut == psstrmOut->WritePos()) + // no output generated--continue the previous chunk + ; + else if (islotChunkIn == psstrmIn->ReadPos()) + // no input consumed + ; + else + { + psstrmIn->MapOutputChunk(islotChunkOut, islotChunkIn, psstrmOut->WritePos(), + (cslotReprocessed > 0), cslotReprocessed, false); + psstrmOut->MapInputChunk(islotChunkIn, islotChunkOut, psstrmIn->ReadPos(), + (cslotReprocessed > 0), false); + } + + psstrmIn->AssertChunkMapsValid(psstrmOut); + + m_pzpst->m_nMaxChunk = max(m_pzpst->m_nMaxChunk, psstrmIn->LastNextChunkLength()); +} + +/*---------------------------------------------------------------------------------------------- + This method is something of a kludge. During the course of running the rules we try to + keep track of the seg-min location, but sometimes that gets confused due to insertions + and deletions at the segment boundaries. +----------------------------------------------------------------------------------------------*/ +void GrSubPass::DoCleanUpSegMin(GrTableManager * ptman, + GrSlotStream * psstrmIn, int islotInitReadPos, GrSlotStream * psstrmOut) +{ + int islotSegMinIn = psstrmIn->SegMin(); + if (islotSegMinIn == -1) + return; // input doesn't even know it + if (islotInitReadPos > islotSegMinIn) + return; // should already have figured it out + + // Otherwise this batch of processing likely set the seg-min on the output stream, + // so check it out. + + // First, if the seg-min of the input stream is zero, it should be zero on the output. + if (islotSegMinIn == 0) + { + psstrmOut->SetSegMin(0, true); + return; + } + + // If there is an initial line-break, the seg-min should be just before it. + int islot; + if (ptman->State()->HasInitialLB()) + { + gid16 chwLB = ptman->LBGlyphID(); + + // Unfortunately, the seg-min from the previous segment can get off, too. :-( + // Fix it, while we're at it. + if (!psstrmIn->SlotAt(islotSegMinIn)->IsInitialLineBreak(chwLB)) + { + for (islot = 0; islot < psstrmIn->ReadPos(); islot++) + if (psstrmIn->SlotAt(islot)->IsInitialLineBreak(chwLB)) + { + psstrmIn->SetSegMin(islot, true); + break; + } + } + if (psstrmOut->SegMin() > -1 + && psstrmOut->SlotAt(psstrmOut->SegMin())->IsInitialLineBreak(chwLB)) + { + return; // already okay + } + for (islot = 0; islot < psstrmOut->WritePos(); islot++) + if (psstrmOut->SlotAt(islot)->IsInitialLineBreak(chwLB)) + { + psstrmOut->SetSegMin(islot, true); + return; + } + Assert(false); // couldn't find it + } + + // Otherwise, figure it out using the associations. First observe that the seg-min + // will be in the corresponding chunk to the seg-min of the previous pass. + int islotChunkMinIn = psstrmIn->ChunkInNextMin(islotSegMinIn); + int islotChunkLimIn = psstrmIn->ChunkInNextLim(islotSegMinIn); + if (islotChunkMinIn == -1) islotChunkMinIn = 0; + if (islotChunkLimIn == -1) islotChunkLimIn = 1; + + int islotChunkMinOut = psstrmIn->ChunkInNext(islotChunkMinIn); + int islotChunkLimOut = psstrmIn->ChunkInNext(islotChunkLimIn); + if (islotChunkMinOut == -1) islotChunkMinOut = 0; + if (islotChunkLimOut == -1) islotChunkLimOut = 1; + + int islotSegMinOut = psstrmOut->SegMin(); + if (islotSegMinOut == -1) + { + // No reasonable guess; try to figure it out. + for (islot = islotChunkMinOut; islot < islotChunkLimOut; islot++) + { +// GrSlotState * pslotOut = psstrmOut->SlotAt(islot); + if (psstrmOut->SlotAt(islot)->BeforeAssoc() == 0) + { + islotSegMinOut = islot; + break; + } + } + if (islotSegMinOut == -1) + { + // Ick, couldn't figure it out. Let's hope we set it to something reasonable earlier. + Assert(psstrmOut->SegMin() > -1); + return; + } + } + // else we have a reasonable guess + + // islotSegMinOut is the best or at leastfirst slot in the chunk that maps into the segment. + // But if there is an adjacent slot inserted before it and it is in the chunk, + // we want to include that also. + while (islotSegMinOut > islotChunkMinOut + && psstrmOut->SlotAt(islotSegMinOut-1)->BeforeAssoc() >= 0) + { + islotSegMinOut--; + } + psstrmOut->SetSegMin(islotSegMinOut, true); +} + +/*---------------------------------------------------------------------------------------------- + This method is something of a kludge. We try to put inserted glyphs after the final + line-break, but if we don't have line break, they should go inside the segment. + This method is only called after the pass is fully written. +----------------------------------------------------------------------------------------------*/ +void GrSubPass::DoCleanUpSegLim(GrTableManager * ptman, GrSlotStream * psstrmOut, + TrWsHandling twsh) +{ + int islotSegLimOut = psstrmOut->SegLimIfKnown(); + if (islotSegLimOut == -1) + return; // output hasn't set it; nothing to fix + + if (twsh == ktwshNoWs) + return; // we just stripped off the whitespace; don't put it back! + + //gid16 chwLB = ptman->LBGlyphID(); + // If there is a LB glyph, it should be at islotSegLimOut-- + // unless we backtracked an inserted one earlier. + if (ptman->State()->HasInsertedLB()) // && psstrmOut->SlotAt(islotSegLimOut)->IsFinalLineBreak(chwLB)) + return; + + // Okay, no LB; make sure all output glyphs are included in this segment. + psstrmOut->SetSegLimToWritePos(true); +} + +/*---------------------------------------------------------------------------------------------- + If we are in a state where we are supposed to skip some of the initial output, + do so. The purpose of this is to resync when restarting for a segment other than the + first. Caller should ensure that there are enough slots to skip. +----------------------------------------------------------------------------------------------*/ +int PassState::DoResyncSkip(GrSlotStream * psstrmOutput) +{ + if (m_fDidResyncSkip) + return 0; + + if (m_cslotSkipToResync == 0) + { + m_fDidResyncSkip = true; + return 0; + } + + if (!CanResyncSkip(psstrmOutput)) + { + Assert(false); // caller makes sure this doesn't happen + return (m_cslotSkipToResync - psstrmOutput->WritePos()); + } + + psstrmOutput->ResyncSkip(m_cslotSkipToResync); + m_fDidResyncSkip = true; + + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Run a constraint for a single rule. + + @param ruln - rule to test + @param psstrmIn - input stream + @param psstrmOut - for accessing items in the pre-context + @param cslotPreModContext - the number of items that need to be tested prior to the + current stream position + @param cslotMatched - the number of items matched after the current stream position +----------------------------------------------------------------------------------------------*/ +bool GrPass::RunConstraint(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotPreModContext, int cslotMatched) +{ + int nRet = 0; + + if (m_prgibConstraintStart == NULL) + return true; + + int biStart = int(m_prgibConstraintStart[ruln]); + if (biStart == 0) + return true; // no constraints + + for (int islot = -cslotPreModContext; islot < cslotMatched; islot++) + { + nRet = RunCommandCode(ptman, m_prgbConstraintBlock + biStart, true, + psstrmIn, psstrmOut, islot); + + if (nRet == 0) // one slot failed + return false; + } + + return (nRet != 0); +} + +/*---------------------------------------------------------------------------------------------- + Check that we can interpret all the commands in the rule. If not, return -1 indicating + that we don't want to run a rule after all. This can happen when the version of the compiler + that generated the font is later than this engine. +----------------------------------------------------------------------------------------------*/ +int GrPass::CheckRuleValidity(int ruln) +{ + if (ruln == -1) + return -1; + + if (m_prgfRuleOkay[ruln]) + return ruln; + + int biStart = m_prgibActionStart[ruln]; + byte * pbNext = m_prgbActionBlock + biStart; + + // General purpose variables: +// int arg1, arg2, arg3, arg4; + int nSlotRef; + int nInputClass; + int nOutputClass; + int c; + int islotArg; + int nIndex; + int nGlyphAttr; + int nAttLevel; + int nFeat; + int nPState; +// SlotAttrName slat; + + int i; + +// int nRet = 0; + + while (true) // exit by encountering PopRet or RetTrue or RetZero + { + ActionCommand op = ActionCommand(*pbNext++); + + switch (op) + { + case kopNop: + break; + + case kopPushByte: + pbNext++; + break; + case kopPushByteU: + pbNext++; + break; + case kopPushShort: + pbNext++; + pbNext++; + break; + case kopPushShortU: + pbNext++; + pbNext++; + break; + case kopPushLong: + pbNext++; + pbNext++; + pbNext++; + pbNext++; + break; + + case kopNeg: + case kopTrunc8: case kopTrunc16: + case kopNot: + break; + + case kopAdd: case kopSub: + case kopMul: case kopDiv: + case kopMin: case kopMax: + case kopAnd: case kopOr: + case kopEqual: case kopNotEq: + case kopLess: case kopGtr: + case kopLessEq: case kopGtrEq: + break; + + case kopCond: + break; + + case kopNext: + break; + case kopNextN: + c = *pbNext; pbNext++; // count + break; + case kopPutGlyph8bitObs: + nOutputClass = *pbNext++; + break; + case kopPutGlyph: + pbNext += 2; + break; + case kopPutCopy: + nSlotRef = *pbNext++; + break; + case kopPutSubs8bitObs: + nSlotRef = *pbNext++; + nInputClass = *pbNext++; + nOutputClass = *pbNext++; + break; + case kopPutSubs: + nSlotRef = *pbNext++; + pbNext += 4; // 2 bytes each for classes + break; + case kopCopyNext: + break; + case kopInsert: + break; + case kopDelete: + break; + case kopAssoc: + c = *pbNext; pbNext++; + for (i = 0; i < c; i++) + pbNext++; + break; + case kopCntxtItem: + islotArg = *pbNext++; + c = *pbNext++; + break; + + case kopAttrSet: + case kopAttrAdd: + case kopAttrSub: + case kopAttrSetSlot: + pbNext++; // slot attribute ID + break; + case kopIAttrSet: + case kopIAttrAdd: + case kopIAttrSub: + case kopIAttrSetSlot: + pbNext++; // slot attribute ID + nIndex = *pbNext++; // index; eg, global ID for component + break; + + case kopPushSlotAttr: + pbNext++; + nSlotRef = *pbNext++; + break; + case kopPushISlotAttr: + pbNext++; + nSlotRef = *pbNext++; + nIndex = *pbNext++; + break; + + case kopPushGlyphAttrObs: + case kopPushAttToGAttrObs: + nGlyphAttr = *pbNext++; + nSlotRef = *pbNext++; + break; + case kopPushGlyphAttr: + case kopPushAttToGlyphAttr: + *pbNext += 3; + break; + case kopPushGlyphMetric: + case kopPushAttToGlyphMetric: + nGlyphAttr = *pbNext++; + nSlotRef = *pbNext++; + nAttLevel = *pbNext++; + break; + case kopPushFeat: + nFeat = *pbNext++; + nSlotRef = *pbNext++; + break; + + case kopPushProcState: + nPState = *pbNext++; + break; + case kopPushVersion: + break; + + case kopPopRet: + case kopRetZero: + case kopRetTrue: + m_prgfRuleOkay[ruln] = true; + return ruln; + + default: + // Uninterpretable command: + return -1; + } + } + + return ruln; // to keep compiler from griping +} + +/*---------------------------------------------------------------------------------------------- + Runs the rule, if any, otherwise just passes one character through. + The rule does nothing but set the line-break weights for the slots. + + @param ptman - the table manager that allocates slots + @param ruln - number of rule to run; -1 if none applies + @param psstrmIn/Out - input/output stream +----------------------------------------------------------------------------------------------*/ +void GrLineBreakPass::RunRule(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Remember the beginnings of the new chunk: + int islotIn = psstrmIn->ReadPos(); + int islotOut = psstrmOut->WritePos(); + int cslotReprocessed = psstrmIn->SlotsToReprocess(); + + if (ruln == -1) + { +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + // Just pass one glyph through. + { + psstrmOut->CopyOneSlotFrom(psstrmIn); + psstrmOut->SetPosForNextRule(0, psstrmIn, false); + } + } + else + { + // Run the rule. +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + { + int biStart = m_prgibActionStart[ruln]; + int iResult = RunCommandCode(ptman, m_prgbActionBlock + biStart, false, + psstrmIn, psstrmOut, 0); + psstrmOut->SetPosForNextRule(iResult, psstrmIn, false); + } + } + + CheckInputProgress(psstrmIn, psstrmOut, islotIn); + + MapChunks(psstrmIn, psstrmOut, islotIn, islotOut, cslotReprocessed); +} + +/*---------------------------------------------------------------------------------------------- + Runs the rule, if any, otherwise just passes one character through. + Keeps track of the association information in the slots and chunk + mappings for backtracking. + + @param ptman - the table manager that allocates slots + @param ruln - number of rule to run; -1 if none applies + @param psstrmIn/Out - input / output streams +----------------------------------------------------------------------------------------------*/ +void GrSubPass::RunRule(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Remember the beginnings of the new chunk: + int islotIn = psstrmIn->ReadPosForNextGet(); + int islotOut = psstrmOut->WritePos(); + int cslotReprocessed = psstrmIn->SlotsToReprocess(); + + if (ruln == -1) + { +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + // Just pass one glyph through. + { + psstrmOut->CopyOneSlotFrom(psstrmIn); + psstrmOut->SetPosForNextRule(0, psstrmIn, false); + } + } + else + { + // Run the rule. +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + /****************************/ + { + int biStart = m_prgibActionStart[ruln]; + int iResult = RunCommandCode(ptman, m_prgbActionBlock + biStart, false, + psstrmIn, psstrmOut, 0); + psstrmOut->SetPosForNextRule(iResult, psstrmIn, false); + + // Restore if we need line-boundary contextualization based on physical locations + ////psstrmOut->SetLBContextFlag(ptman, islotOut); + } + } + + CheckInputProgress(psstrmIn, psstrmOut, islotIn); + + MapChunks(psstrmIn, psstrmOut, islotIn, islotOut, cslotReprocessed); +} + +/*---------------------------------------------------------------------------------------------- + Runs the rule, if any, otherwise just positions one character in the standard way. + Keeps track of (among other things) the overall physical size of the output + and chunk mappings. + + @param ptman - the table manager that allocates slots + @param ruln - number of rule to run; -1 if none applies + @param psstrmIn / Out - input / output streams +----------------------------------------------------------------------------------------------*/ +void GrPosPass::RunRule(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Remember the beginnings of the new chunk: + int islotIn = psstrmIn->ReadPosForNextGet(); + int islotOut = psstrmOut->WritePos(); + int cslotReprocessed = psstrmIn->SlotsToReprocess(); + + int iResult = 0; + + Assert(psstrmIn->GotIndexOffset()); + // Don't do any positioning in anything being processed from the previous line. + if (psstrmIn->ReadPosForNextGet() < psstrmIn->IndexOffset()) + ruln = -1; + + if (ruln == -1) + { + // Just position one glyph in the standard way. + psstrmOut->CopyOneSlotFrom(psstrmIn); + iResult = 0; + } + else + { + // Run the rule. +#ifdef OLD_TEST_STUFF + if (RunTestRules(ptman, ruln, psstrmIn, psstrmOut)) +#else + if (false) +#endif // OLD_TEST_STUFF + {} + else + { + int biStart = m_prgibActionStart[ruln]; + iResult = RunCommandCode(ptman, m_prgbActionBlock + biStart, false, + psstrmIn, psstrmOut, 0); + } + } + + // Make sure an entire cluster is present in the output stream. Actually there might be + // interleaved clusters, which is the purpose for the loop. + int dislotClusterMax = 0; + do { + dislotClusterMax = psstrmIn->MaxClusterSlot(islotIn, psstrmIn->ReadPos()); + Assert(dislotClusterMax >= 0); + for (int islot = 0; islot < dislotClusterMax; islot++) + psstrmOut->CopyOneSlotFrom(psstrmIn); + iResult -= dislotClusterMax; + } while (dislotClusterMax > 0); + + psstrmOut->SetPosForNextRule(iResult, psstrmIn, true); + + // Restore if we need line-boundary contextualization based on physical locations + ////psstrmOut->SetLBContextFlag(ptman, islotOut); + + if (ruln > -1) + { + psstrmOut->CalcIndexOffset(ptman); + // Update the attachment trees and cluster metrics for slots modified by this rule. + // Note that this loop assumes that the reprocess buffer points to the identical + // slot objects as the output stream (not copies of the slots). + for (int islot = islotIn - psstrmIn->SlotsSkippedToResync(); + islot < psstrmOut->WritePos() + psstrmIn->SlotsToReprocess(); + islot++) + { + psstrmOut->SlotAt(islot)->HandleModifiedPosition(ptman, psstrmIn, psstrmOut, islot); + } + } + + CheckInputProgress(psstrmIn, psstrmOut, islotIn); + + MapChunks(psstrmIn, psstrmOut, islotIn, islotOut, cslotReprocessed); + + psstrmOut->AssertStreamIndicesValid(psstrmIn); +} + +/*---------------------------------------------------------------------------------------------- + Perform the reversal for internal bidirectionality. + Caller is responsible for updating the read- and write-positions. + Private. + + OBSOLETE + + @param nCurrDirection - direction level of range of reverse + @param psstrmIn, psstrmOut - streams + @param islotInMin, islotInLim - range to reverse + @param islotOutMin,islotOutLim - corresponding output positions; note that islotOutMin + will be greater than islotOutLim if we are reversing + reversing the slots (direction is odd) +----------------------------------------------------------------------------------------------*/ +#if 0 +void GrBidiPass::OldReverse(int nCurrDirection, + GrSlotStream * psstrmIn, int islotInMin, int islotInLim, + GrSlotStream * psstrmOut, int islotOutMin, int islotOutLim) +{ + int oInc = (islotOutLim > islotOutMin)? 1: -1; // which direction output is moving in + // buffer (if dir is even, oInc == 1; + // if odd, oInc == -1) + + int islotITmp = islotInMin; + int islotOTmp = islotOutMin; + + while (islotITmp != islotInLim) { + // Find end of sub-range at current level to reverse. + int islotSubLim = psstrmIn->OldDirLevelRange(NULL, islotITmp, nCurrDirection); + Assert(islotSubLim >= 0); + + if (islotSubLim == islotITmp) { + // Current slot is at this level--just copy it. + psstrmOut->SimpleCopyFrom(psstrmIn, islotITmp, islotOTmp); + islotOTmp += oInc; + islotITmp++; + } + else { + // There is a sub-range that needs to be reversed. + int islotONext = islotOTmp + ((islotSubLim-islotITmp) * oInc); + OldReverse(nCurrDirection+1, + psstrmIn, islotITmp, islotSubLim, + psstrmOut, islotONext - oInc, islotOTmp - oInc); + islotOTmp = islotONext; + islotITmp = islotSubLim; + } + } +} +#endif + +/*---------------------------------------------------------------------------------------------- + Perform the reversal for internal bidirectionality. + Caller is responsible for updating the read- and write-positions. + Return the number of slots NOT copied because they were markers. + Private. + + @param psstrmIn, psstrmOut - streams + @param vislotStarts, vislotStops - indices of ranges to reverse, from inner to outer +----------------------------------------------------------------------------------------------*/ +int GrBidiPass::Reverse(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + std::vector<int> & vislotStarts, std::vector<int> & vislotStops) +{ + Assert(vislotStarts.size() == vislotStops.size()); + + std::vector<int> vislotMap; // list of slot indices in the order they should be copied + // into the output + + int islotOuterStart = vislotStarts.back(); + int islotOuterStop = vislotStops.back(); + + // Initialize map as if nothing is reversed; eg for [2, 9]: (2 3 4 5 6 7 8 9) + int islot; + for (islot = 0; islot <= (islotOuterStop - islotOuterStart); islot++) + vislotMap.push_back(islot + islotOuterStart); + + // Do the inner reversals first, followed by the outer reversals. + size_t iislot; + for (iislot = 0; iislot < vislotStarts.size(); iislot++) + { + // Reverse the run. + int islotStart = vislotStarts[iislot] - islotOuterStart; + int islotStop = vislotStops[iislot] - islotOuterStart; + int islot1, islot2; + for (islot1 = islotStart, islot2 = islotStop; islot1 < islot2; islot1++, islot2--) + { + int islotTmp = vislotMap[islot1]; + vislotMap[islot1] = vislotMap[islot2]; + vislotMap[islot2] = islotTmp; + } + } + + // With vislotStarts = [7, 5, 2] and vislotStops = [8, 8, 9], we get + // first: (2 3 4 5 6 8 7 9) + // then: (2 3 4 7 8 5 6 9) + // and finally: (9 6 5 8 7 4 3 2) + + // Now copy the slots. + + int islotOutStart = psstrmOut->WritePos(); + int cslotNotCopied = 0; + for (iislot = 0; iislot < vislotMap.size(); iislot++) + { + GrSlotState * pslot = psstrmIn->SlotAt(vislotMap[iislot]); + // Don't copy bidi markers unless they have been set to use a specific glyph. + if (pslot->IsBidiMarker() && pslot->ActualGlyphForOutput(ptman) == 0) + cslotNotCopied++; + else + psstrmOut->SimpleCopyFrom(psstrmIn, vislotMap[iislot], + iislot + islotOutStart - cslotNotCopied); + } + return cslotNotCopied; +} + +/*---------------------------------------------------------------------------------------------- + Unwind the output for this pass, given that a change was assumed to have occurred + in the input at the given position. Return the position to which we unwound. +----------------------------------------------------------------------------------------------*/ +int GrPass::Unwind(GrTableManager * ptman, + int islotChanged, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + bool fFirst) +{ + // Back up the number of slots required for the longest rule context, + // but if we land in the middle of a chunk, go forward to its boundary. + int islotIn = max(islotChanged - m_pzpst->MaxChunk(), 0); + if (!psstrmIn->NoReproc()) + islotIn = min(islotIn, psstrmIn->ReprocMin()); + psstrmIn->ClearReprocBuffer(); + int islotOut = 0; + if (islotIn < psstrmIn->SlotsSkippedToResync() || + islotIn == 0 || + psstrmIn->ReadPos() == 0) + { + // The next-chunk-map is invalid. The beginning of the chunk is 0. + islotIn = 0; + islotOut = 0; + } + else if (islotIn >= psstrmIn->ReadPos()) + { + // No need to unwind. + Assert(psstrmIn->NoReproc()); + return psstrmOut->WritePos(); + } + else + { + Assert(islotIn < psstrmIn->ReadPos()); + islotIn = min(islotIn, psstrmIn->ReadPos() - 1); + while (islotIn < psstrmIn->ReadPos() && + (islotOut = psstrmIn->ChunkInNext(islotIn)) == -1) + { + ++islotIn; + } + if (islotIn == psstrmIn->ReadPos()) + islotOut = psstrmOut->WritePos(); + } + + Assert(islotOut != -2); // because the chunk size must be <= than the max-chunk, + // so we should never hit the end of the output stream + + // Now we've found a chunk boundary. + +// if (fFirst) +// Unattach(psstrmIn, islotIn, psstrmOut, islotOut, islotChanged); +// else +// Unattach(psstrmIn, islotIn, psstrmOut, islotOut, -1); + + psstrmIn->UnwindInput(islotIn, PreBidiPass()); + psstrmOut->UnwindOutput(islotOut, IsPosPass()); + + if (psstrmIn->ReadPos() < psstrmIn->SlotsSkippedToResync()) + { + ptman->Pass(m_ipass - 1)->UndoResyncSkip(); + psstrmIn->ClearSlotsSkippedToResync(); + } + if (psstrmOut->WritePos() < psstrmOut->SlotsSkippedToResync()) + { + Assert(psstrmOut->SlotsSkippedToResync() == m_pzpst->m_cslotSkipToResync); + UndoResyncSkip(); + psstrmOut->ClearSlotsSkippedToResync(); + } + + if (ptman->LoggingTransduction()) + m_pzpst->UnwindLogInfo(islotIn, islotOut); + + return islotOut; +} + +/*---------------------------------------------------------------------------------------------- + For a GrBidiPass, just unwind to the beginning of the reordered chunk. +----------------------------------------------------------------------------------------------*/ +int GrBidiPass::Unwind(GrTableManager * ptman, + int islotChanged, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + bool fFirst) +{ + int islotIn; + int islotOut; + + if (islotChanged == 0) + { + islotIn = 0; + islotOut = 0; + } + else { + islotIn = min(islotChanged, psstrmIn->ReadPos()); + islotIn = max(islotIn - 1, 0); + while (islotIn > 0 && !StrongDir(psstrmIn->SlotAt(islotIn)->Directionality())) + { + // Slightly redundant with ZapCalculatedDirLevels, but possibly needed for slots + // that have been modified by the substitution passes. + psstrmIn->SlotAt(islotIn)->ZapDirLevel(); + islotIn--; + } + Assert(islotIn == 0 || psstrmIn->SlotAt(islotIn)->DirHasBeenProcessed()); + islotOut = 0; + while (islotIn > 0 && (islotOut = psstrmIn->ChunkInNext(islotIn)) == -1) + --islotIn; + } + + if (islotOut == -1 || islotOut == -2) + islotOut = 0; + + // Now we've found a chunk boundary; islotI and islotO are the positions in the + // input and output streams, respectively. + + psstrmIn->UnwindInput(islotIn, false); + psstrmOut->UnwindOutput(islotOut, false); + + if (psstrmOut->WritePos() < m_pzpst->m_cslotSkipToResync) + { + Assert(false); // shouldn't be any of this mess for the Bidi pass + m_pzpst->m_fDidResyncSkip = false; + } + + if (ptman->LoggingTransduction()) + m_pzpst->UnwindLogInfo(islotIn, islotOut); + + return islotOut; +} + +/*---------------------------------------------------------------------------------------------- + Reinitialize part of the transduction logging information when we unwind the pass. +----------------------------------------------------------------------------------------------*/ +void PassState::UnwindLogInfo(int islotIn, int islotOut) +{ + while (m_crulrec > 0 && m_rgrulrec[m_crulrec-1].m_islot >= islotIn) + { + m_crulrec--; + m_rgrulrec[m_crulrec].m_islot = 0; + m_rgrulrec[m_crulrec].m_irul = 0; + m_rgrulrec[m_crulrec].m_fFired = false; + } + + for (int islot = islotOut; islot < 128; islot++) + { + m_rgcslotDeletions[islot] = 0; + m_rgfInsertion[islot] = false; + } +} + +/*---------------------------------------------------------------------------------------------- + Undo the side effects of attachments, ie, fix up the attachment trees as we unwind. + + @param islotLB the index of the line-break that was just inserted, or -1 if this is + not the first pass to include line-breaks; we want to skip this slot + in the processing in this method + + OBSOLETE +----------------------------------------------------------------------------------------------*/ +//:Ignore +void GrPosPass::Unattach(GrSlotStream * psstrmIn, int islotIn, + GrSlotStream * psstrmOut, int islotOut, int islotLB) +{ + // Because this is a positioning pass, there is a one-to-one correspondence between + // the slots in the input and the slots in the output. Thus we can make simplifying + // assumptions. Specifically, the chunk sizes are equal, except for the possiblity + // of a LB slot that we just inserted in the input stream. +#if 0 + +#ifdef _DEBUG + int islotDiff = (psstrmIn->ReadPos() - islotIn) - (psstrmOut->WritePos() - islotOut); + if (islotLB == -1) + Assert(islotDiff == 0); + else + Assert(islotDiff == 1); +#endif // _DEBUG + + int islotInLp = psstrmIn->ReadPos(); + int islotOutLp = psstrmOut->WritePos(); + for ( ; islotInLp-- > islotIn && islotOutLp-- > islotOut; ) + { + if (islotLB == islotInLp) + { + GrSlotState * pslotTmp = psstrmIn->SlotAt(islotInLp); + islotInLp--; + } + + GrSlotState * pslotIn = psstrmIn->SlotAt(islotInLp); + GrSlotState * pslotOut = psstrmOut->SlotAt(islotOutLp); + + if (pslotIn != pslotOut) + { + GrSlotState * pslotInRoot = pslotIn->AttachRoot(); + GrSlotState * pslotOutRoot = pslotOut->AttachRoot(); + + if (pslotOutRoot) + pslotOutRoot->RemoveLeaf(pslotOut); + if (pslotInRoot) + pslotInRoot->AddLeaf(pslotIn); + } + } +#endif // 0 +} +//:End Ignore + + +/*---------------------------------------------------------------------------------------------- + Record the fact that the given rule's constraint failed, for the purpose of logging + the transduction process. +----------------------------------------------------------------------------------------------*/ +void GrPass::RecordRuleFailed(int islot, int irul) +{ + m_pzpst->RecordRule(islot, irul, false); +} + + +/*---------------------------------------------------------------------------------------------- + Record the fact that the given rule fired, for the purpose of logging + the transduction process. +----------------------------------------------------------------------------------------------*/ +void GrPass::RecordRuleFired(int islot, int irul) +{ + m_pzpst->RecordRule(islot, irul, true); +} + + +/*---------------------------------------------------------------------------------------------- + Record the fact that we forcibly advanced due to the max-rule-loop. +----------------------------------------------------------------------------------------------*/ +void GrPass::RecordHitMaxRuleLoop(int islot) +{ + m_pzpst->RecordRule(islot, PassState::kHitMaxRuleLoop); +} + + +/*---------------------------------------------------------------------------------------------- + Record the fact that we forcibly advanced due to the max-backup. + + In theory it should be possible to call this, but in reality, it is pretty hard to be + able to say that there is a rule that would have fired except it was prevent by + MaxBackup. So currently this method is not called. +----------------------------------------------------------------------------------------------*/ +void GrPass::RecordHitMaxBackup(int islot) +{ + m_pzpst->RecordRule(islot, PassState::kHitMaxBackup); +} + + +/*---------------------------------------------------------------------------------------------- + Record some rule-related activity on a slot. +----------------------------------------------------------------------------------------------*/ +void PassState::RecordRule(int islot, int irul, bool fFired) +{ + if (m_crulrec >= 128) + return; + + m_rgrulrec[m_crulrec].m_irul = irul; + m_rgrulrec[m_crulrec].m_islot = islot; + m_rgrulrec[m_crulrec].m_fFired = true; + m_rgrulrec[m_crulrec].m_fFired = fFired; + m_crulrec++; +} + + +/*---------------------------------------------------------------------------------------------- + Record the fact that a slot was deleted before the given slot (in the output). +----------------------------------------------------------------------------------------------*/ +void PassState::RecordDeletionBefore(int islot) +{ + if (islot >= 128) + return; + + (m_rgcslotDeletions[islot])++; +} + +/*---------------------------------------------------------------------------------------------- + Record the fact that the given slot was inserted into the output. +----------------------------------------------------------------------------------------------*/ +void PassState::RecordInsertionAt(int islot) +{ + if (islot >= 128) + return; + + m_rgfInsertion[islot] = true; +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPass.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPass.h new file mode 100644 index 00000000000..446bfa1fb83 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPass.h @@ -0,0 +1,747 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrPass.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrPass class and subclasses. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef PASS_INCLUDED +#define PASS_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + This class stores the state of processing in a single pass. + + Hungarian: zpst +----------------------------------------------------------------------------------------------*/ + +class PassState { + friend class GrPass; + friend class GrGlyphGenPass; + friend class GrLineBreakPass; + friend class GrSubPass; + friend class GrBidiPass; + friend class GrPosPass; + friend class FontMemoryUsage; + +public: + PassState() + { + } + + void InitForNewSegment(int ipass, int nMaxChunk); + void InitializeLogInfo(); + + int MaxChunk() + { + return m_nMaxChunk; + } + + void SetResyncSkip(int c) + { + m_cslotSkipToResync = c; + m_fDidResyncSkip = false; + } + int NeededToResync() + { + if (m_fDidResyncSkip) + return 0; + return m_cslotSkipToResync; + } + bool DidResyncSkip() + { + return m_fDidResyncSkip; + } + bool CanResyncSkip(GrSlotStream * psstrmOutput) + { + if (m_fDidResyncSkip) + return true; // already did it + // Are we in the position to do the resync-skip? + return m_cslotSkipToResync <= psstrmOutput->WritePos(); + } + int DoResyncSkip(GrSlotStream * psstrmOutput); + void UndoResyncSkip() + { + m_fDidResyncSkip = false; + } + + void UnwindLogInfo(int islotIn, int islotOut); + void RecordRule(int islot, int irul, bool fFired = false); + void RecordDeletionBefore(int islot); + void RecordInsertionAt(int islot); +#ifdef TRACING + void LogRulesFiredAndFailed(std::ostream & strmOut, GrSlotStream * psstrmIn); + void LogInsertionsAndDeletions(std::ostream & strmOut, GrSlotStream * psstrmOut); +#endif // TRACING + +protected: + int m_ipass; + + // Used to avoid infinite loops. If m_nRulesSinceAdvance > m_nMaxRuleLoop, + // and we have not passed the stream's ReadPosMax, forcibly advance. + int m_nRulesSinceAdvance; + + // Maximum length of chunk, used when unwinding the streams during backtracking. This + // must be initialized to m_nMaxRuleContext, but that is not always adequate, because + // when there is reprocessing going on the chunks may be longer. + int m_nMaxChunk; + + // Indicates how much of the pass's initial output should be automatically + // skipped over in order to resync with a chunk boundary. This handles the situation + // where we are reprocessing a little of the previous segment in order to handle + // cross-line-boundary contextuals. The goal is to skip over the output until we hit + // what we know (from processing the previous segment) is a chunk boundary. + // + // In most cases (and always when this is the first segment of the + // writing system) this number is 0. + int m_cslotSkipToResync; + bool m_fDidResyncSkip; + + enum { + kHitMaxBackup = -1, + kHitMaxRuleLoop = -2 + }; + + // For logging transduction process: record of one rule matched or fired + struct RuleRecord + { + int m_irul; // rule index, or kHitMaxBackup or kHitMaxRuleLoop + int m_islot; // slot index of input stream + bool m_fFired; + }; + RuleRecord m_rgrulrec[128]; + int m_crulrec; + + int m_rgcslotDeletions[128]; // number of deletions before slot i in output + bool m_rgfInsertion[128]; // true if slot i in output was inserted +}; + +/*---------------------------------------------------------------------------------------------- + This class handles finding and applying rules from the pass and storing + the results in the appropriate stream. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrPass { + friend class FontMemoryUsage; + +protected: + // Action command codes; these MUST match the corresponding definitions in the compiler: + enum ActionCommand { + kopNop = 0, + + kopPushByte, kopPushByteU, kopPushShort, kopPushShortU, kopPushLong, + + kopAdd, kopSub, kopMul, kopDiv, + kopMin, kopMax, + kopNeg, + kopTrunc8, kopTrunc16, + + kopCond, + + kopAnd, kopOr, kopNot, + kopEqual, kopNotEq, + kopLess, kopGtr, kopLessEq, kopGtrEq, + + kopNext, kopNextN, kopCopyNext, + kopPutGlyph8bitObs, kopPutSubs8bitObs, kopPutCopy, + kopInsert, kopDelete, + kopAssoc, + kopCntxtItem, + + kopAttrSet, kopAttrAdd, kopAttrSub, + kopAttrSetSlot, + kopIAttrSetSlot, + kopPushSlotAttr, kopPushGlyphAttrObs,kopPushGlyphMetric, kopPushFeat, + kopPushAttToGAttrObs, kopPushAttToGlyphMetric, + kopPushISlotAttr, + + kopPushIGlyphAttr, // not implemented + + kopPopRet, kopRetZero, kopRetTrue, + kopIAttrSet, kopIAttrAdd, kopIAttrSub, + kopPushProcState, kopPushVersion, + kopPutSubs, kopPutSubs2, kopPutSubs3, + kopPutGlyph, kopPushGlyphAttr, kopPushAttToGlyphAttr + + }; + + enum StackMachineFlag { + ksmfDone = 0, + ksmfContinue, + ksmfUnderflow, + ksmfStackNotEmptied + }; + +public: + // Constructor: + GrPass(int i); + // Destructor: + virtual ~GrPass(); + + int PassNumber() { return m_ipass; } + + bool ReadFromFont(GrIStream & grstrm, int fxdSilfVersion, int fxdRuleVersion, int nOffset); + void InitializeWithNoRules(); + + virtual void SetTopDirLevel(int n) + { // only GrBidiPass does anything interesting + } + + void SetPassState(PassState * pzpst) + { + m_pzpst = pzpst; + } + + virtual void ExtendOutput(GrTableManager *, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput, int nslotToGet, + TrWsHandling twsh, + int * pnRet, int * pcslotGot, int * pislotFinalBreak); + + int ExtendGlyphIDOutput(GrTableManager *, GrCharStream *, + GrSlotStream *, int ichSegLim, int cchwPostXlbContext, + LineBrk lb, int cslotToGet, bool fNeedFinalBreak, + TrWsHandling twsh, int * pislotFinalBreak); + + int ExtendFinalOutput(GrTableManager *, GrSlotStream * psstrmInput, + GrSlotStream * psstrmOutput, + float xsSpaceAllotted, bool fWidthIsCharCount, bool fInfiniteWidth, + bool fHaveLineBreak, bool fMustBacktrack, + LineBrk lbMax, TrWsHandling twsh, + int * pislotLB, float * pxsWidth); + + int RemoveTrailingWhiteSpace(GrTableManager * ptman, GrSlotStream * psstrmOut, + TrWsHandling twsh, int * pislotFinalBreak); + + virtual int Unwind(GrTableManager * ptman, + int islotChanged, GrSlotStream *psstrmInput, GrSlotStream * psstrmOutput, + bool fFirst); + + int MaxBackup() + { + return m_nMaxBackup; + } + + bool DidResyncSkip() + { + return m_pzpst->DidResyncSkip(); + } + int DoResyncSkip(GrSlotStream * psstrmOutput) + { + return m_pzpst->DoResyncSkip(psstrmOutput); + } + + void UndoResyncSkip() + { + m_pzpst->UndoResyncSkip(); + } + void SetResyncSkip(int n) + { + m_pzpst->SetResyncSkip(n); + } + + virtual void DoCleanUpSegMin(GrTableManager * ptman, + GrSlotStream * psstrmIn, int islotInitReadPos, GrSlotStream * psstrmOut) + { + } + + virtual void DoCleanUpSegLim(GrTableManager * ptman, GrSlotStream * psstrmOut, + TrWsHandling twsh) + { + } + + virtual bool IsPosPass() + { + return false; + } + + virtual bool PreBidiPass() = 0; + + bool RunConstraint(GrTableManager *, int ruln, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int cslotPreModContext, int cslotMatched); + + int RunCommandCode(GrTableManager * ptman, + byte * pbStart, bool fConstraints, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islot); + +protected: + int RunOneCommand(GrTableManager * ptman, bool fConstraints, + ActionCommand op, byte ** ppbArg, bool * pfMustGet, bool * pfInserting, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islot, + std::vector<int> & vnStack, StackMachineFlag * psmf); + void DoStackArithmetic2Args(ActionCommand op, std::vector<int> & vnStack, + StackMachineFlag * psmf); + void DoStackArithmetic1Arg(ActionCommand op, std::vector<int> & vnStack, + StackMachineFlag * psmf); + void DoConditional(std::vector<int> & vnStack, StackMachineFlag * psmf); + StackMachineFlag CheckStack(std::vector<int> & vnStack, int cn); + void DoNext(GrTableManager * ptman, + int cslot, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutGlyph(GrTableManager * ptman, bool fInserting, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutCopy(GrTableManager * ptman, bool fInserting, int cslotCopyFrom, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutSubs(GrTableManager * ptman, bool fInserting, + int cslotSel, int nSelClass, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutSubs2(GrTableManager * ptman, bool fInserting, + int cslotSel1, int nSelClass1, int cslotSel2, int nSelClass2, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutSubs3(GrTableManager * ptman, bool fInserting, + int cslotSel1, int nSelClass1, int cslotSel2, int nSelClass2, int cslotSel3, int nSelClass3, + int nReplacementClass, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPutSubsInit(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, bool fInserting, + GrSlotState ** ppslotNextPut, bool * pfAtSegMin, bool * pfAtSegLim); + void DoPutSubsAux(GrTableManager * ptman, bool fInserting, gid16 nGlyphReplacement, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, GrSlotState * pslotNextInput, + bool fAtSegMin, bool fAtSegLim); + void SetNeutralAssocs(GrSlotState * pslotNew, GrSlotStream * psstrmIn); + void DoDelete(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoAssoc(int cnAssocs, std::vector<int> & vnAssocs, bool fInserting, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoSetAttr(GrTableManager * ptman, + ActionCommand op, bool fInserrting, + SlotAttrName slat, int slati, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushSlotAttr(GrTableManager * ptman, + int nSlotRef, bool fInserting, + SlotAttrName slat, int slati, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushGlyphAttr(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushAttToGlyphAttr(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushGlyphMetric(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, int nAttLevel, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushAttToGlyphMetric(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, int nAttLevel, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushFeatValue(GrTableManager * ptman, int islot, bool fInsering, + int nFeat, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + void DoPushProcState(GrTableManager * ptman, int nPState, std::vector<int> & vnStack); +protected: + void DoPushGlyphMetricAux(GrTableManager * ptman, + GrSlotState * pslot, int nGlyphAttr, int nAttLevel, + std::vector<int> & vnStack, GrSlotStream * psstrmIn); +public: + int SortKeyForRule(int ruln) + { + Assert(ruln < m_crul); + return m_prgchwRuleSortKeys[ruln]; + } + int PreModContextForRule(int ruln) + { + Assert(ruln < m_crul); + return m_prgcritRulePreModContext[ruln]; + } + + int MaxRulePreContext() + { + if (m_pfsm) + return m_pfsm->MaxRulePreContext(); + else + return 0; + } + + void RecordRuleFailed(int islot, int irul); + void RecordRuleFired(int islot, int irul); + void RecordHitMaxRuleLoop(int islot); + void RecordHitMaxBackup(int islot); +#ifdef TRACING + void LogRulesFiredAndFailed(std::ostream & strmOut, GrSlotStream * psstrmIn); + void LogInsertionsAndDeletions(std::ostream & strmOut, GrSlotStream * psstrmOut); +#endif // TRACING + +protected: + int CheckRuleValidity(int ruln); + + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) = 0; + + void CheckInputProgress(GrSlotStream* input, GrSlotStream* psstrmOutput, + int islotOrigInput); + + void MapChunks(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + int islotChunkI, int islotChunkO, int cslotReprocessed); + + virtual void Unattach(GrSlotStream * psstrmIn, int islotIn, // GrPosPass overrides + GrSlotStream * psstrmOut, int islotOut, int islotLB) + { + } + + friend class EngineState; // let it call the method below + virtual int MaxRuleContext() // GrBidiPass overrides + { + return m_nMaxRuleContext; + } + +protected: + // Instance variables: + + int m_ipass; // index of pass + + int m_fxdVersion; + + // number of items required from previous pass; don't access directly, use the getter + // method, because GrBidiPass overrides to always use 1. + int m_nMaxRuleContext; + + GrFSM * m_pfsm; // Finite State Machine to give next rule + + int m_nMaxRuleLoop; // maximum number of rules to process before forcibly + // advancing input position + + int m_nMaxBackup; + + int m_crul; // number of rules + + // rule sort keys, indicating precedence of rules; m_crul of these + data16 * m_prgchwRuleSortKeys; + + // for each rule, the number of items in the context before the first modified item + // that the constraints need to be tested on + byte * m_prgcritRulePreModContext; + + // offset for pass-level constraints; just 1 of these + data16 m_cbPassConstraint; + // offsets for constraint and action code; m_crul+1 of each of these (the last + // indicates the total byte count) + data16 * m_prgibConstraintStart; + data16 * m_prgibActionStart; + + // blocks of constraint and action code + byte * m_prgbPConstraintBlock; // pass-level constraints + byte * m_prgbConstraintBlock; // rule constraints + byte * m_prgbActionBlock; + + int m_cbConstraints; // needed for memory instrumentation only + int m_cbActions; // needed for memory instrumentation only + + bool m_fHasDebugStrings; + data16 * m_prgibConstraintDebug; // m_crul+1 of these + data16 * m_prgibRuleDebug; // m_crul+1 of these + + bool m_fCheckRules; + bool * m_prgfRuleOkay; + + std::vector<int> m_vnStack; // for stack machine processing (more efficient than creating the + // vector each time) + + // state of process for this pass + PassState * m_pzpst; + +public: +#ifdef OLD_TEST_STUFF + // For test procedures: + StrAnsi m_staBehavior; + + virtual void SetUpTestData(); + + // overridden on appropriate subclasses: + virtual void SetUpReverseNumbersTest() { Assert(false); } + virtual void SetUpBidiNumbersTest() { Assert(false); } + + virtual void SetUpCrossLineContextTest() { Assert(false); } + virtual void SetUpReprocessTest() { Assert(false); } + virtual void SetUpLineEdgeContextTest(int ipass) { Assert(false); } + virtual void SetUpBidiAlgorithmTest() { Assert(false); } + virtual void SetUpPseudoGlyphsTest() { Assert(false); } + virtual void SetUpSimpleFSMTest() { Assert(false); } + virtual void SetUpRuleActionTest() { Assert(false); } + virtual void SetUpRuleAction2Test() { Assert(false); } + virtual void SetUpAssocTest() { Assert(false); } + virtual void SetUpAssoc2Test() { Assert(false); } + virtual void SetUpDefaultAssocTest() { Assert(false); } + virtual void SetUpFeatureTest() { Assert(false); } + virtual void SetUpLigatureTest() { Assert(false); } + virtual void SetUpLigature2Test() { Assert(false); } +#endif // OLD_TEST_STUFF + +}; // end of class GrPass + + +/*---------------------------------------------------------------------------------------------- + The initial pass that generates glyph IDs from Unicode input. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrGlyphGenPass : public GrPass +{ + friend class FontMemoryUsage; + +public: + GrGlyphGenPass(int ipass) : GrPass(ipass) + { + } + + virtual bool PreBidiPass() + { + return true; + } + +protected: + // Irrelevant when generating glyphs. + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) + { + Assert(false); + } + +}; // end of class GrGlyphGenPass + + +/*---------------------------------------------------------------------------------------------- + A pass containing rules that set the break weight values. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrLineBreakPass : public GrPass +{ + friend class FontMemoryUsage; + +public: + GrLineBreakPass(int ipass) : GrPass(ipass) + { + } + + virtual bool PreBidiPass() + { + return true; + } + +protected: + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput); + +public: +#ifdef OLD_TEST_STUFF + // For test procedures: + bool RunTestRules(GrTableManager *, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + bool RunTestRules(GrTableManager *, int ruln, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpCrossLineContextTest(); + bool RunCrossLineContextTest(GrTableManager*, GrSlotStream* psstrmIn, + GrSlotStream* psstrmOut); + + virtual void SetUpReprocessTest(); + bool RunReprocessTest(GrTableManager *, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut); + + virtual void SetUpAssocTest(); + virtual void SetUpLigatureTest(); +#endif // OLD_TEST_STUFF + +}; // end of class GrLineBreakPass + + +/*---------------------------------------------------------------------------------------------- + A pass containing substitution rules. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrSubPass : public GrPass +{ + friend class FontMemoryUsage; + +public: + GrSubPass(int ipass) : GrPass(ipass) + { + } + + virtual bool PreBidiPass() + { + return true; + } + +protected: + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput); + + virtual void DoCleanUpSegMin(GrTableManager * ptman, + GrSlotStream * psstrmIn, int islotInitReadPos, GrSlotStream * psstrmOut); + virtual void DoCleanUpSegLim(GrTableManager * ptman, GrSlotStream * psstrmOut, + TrWsHandling twsh); + +public: +#ifdef OLD_TEST_STUFF + // For test procedures: + bool RunTestRules(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + bool RunTestRules(GrTableManager *, int ruln, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpReverseNumbersTest(); + bool RunReverseNumbersTest(GrTableManager*, GrSlotStream* psstrmIn, + GrSlotStream* psstrmOut); + + virtual void SetUpBidiNumbersTest(); + bool RunBidiNumbersTest(GrTableManager*, GrSlotStream* psstrmIn, + GrSlotStream* psstrmOut); + + virtual void SetUpCrossLineContextTest(); + bool RunCrossLineContextTest(GrTableManager*, GrSlotStream* psstrmIn, + GrSlotStream* psstrmOut); + + virtual void SetUpReprocessTest(); + bool RunReprocessTest(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpLineEdgeContextTest(int ipass); + bool RunLineEdgeContextTest(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpBidiAlgorithmTest(); + bool RunBidiAlgorithmTest(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpPseudoGlyphsTest(); + bool RunPseudoGlyphsTest(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + + virtual void SetUpSimpleFSMTest(); + bool RunSimpleFSMTest(GrTableManager *, int ruln, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + virtual void SetUpRuleActionTest(); + virtual void SetUpRuleAction2Test(); + virtual void SetUpAssocTest(); + virtual void SetUpAssoc2Test(); + virtual void SetUpDefaultAssocTest(); + virtual void SetUpFeatureTest(); + virtual void SetUpLigatureTest(); + virtual void SetUpLigature2Test(); +#endif // OLD_TEST_STUFF + +}; // end of class GrSubPass + + +/*---------------------------------------------------------------------------------------------- + This class is the one the knows how to handle bidi reordering, including backtracking. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrBidiPass : public GrSubPass +{ + friend class FontMemoryUsage; + +public: + // Constructor: + GrBidiPass(int ipass) + : GrSubPass(ipass), + m_nTopDirection(0) + { + } + + virtual void SetTopDirLevel(int n) + { + m_nTopDirection = n; + } + + virtual bool PreBidiPass() + { + return false; + } + + virtual void ExtendOutput(GrTableManager *, + GrSlotStream * psstrmI, GrSlotStream * psstrmO, int nslotToGet, + TrWsHandling twsh, + int * pnRet, int * pcslotGot, int * pislotFinalBreak); + + virtual int Unwind(GrTableManager * ptman, + int islotChanged, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + bool fFirst); +protected: + virtual int MaxRuleContext() + { + return 1; + } + +private: + //void OldReverse(int nCurrDirection, + // GrSlotStream * psstrmI, int islotI1, int islotI2, + // GrSlotStream * psstrmO, int islotO1, int islotO2); + + int Reverse(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, + std::vector<int> & vislotStarts, std::vector<int> & vislotStops); + +protected: + // Instance variables: + int m_nTopDirection; // 0 for LTR, 1 for RTL -- CURRENTLY NOT USED; need to + // initialize it when we set the writing system direction + +#ifdef OLD_TEST_STUFF +public: + // For test procedures: + ////virtual void SetUpTestData(); + virtual void SetUpBidiNumbersTest(); + virtual void SetUpBidiAlgorithmTest(); +#endif // OLD_TEST_STUFF + +}; // end of class GrBidiPass + + +/*---------------------------------------------------------------------------------------------- + A pass containing positioning rules. + + Hungarian: pass +----------------------------------------------------------------------------------------------*/ +class GrPosPass : public GrPass +{ + friend class FontMemoryUsage; + +public: + GrPosPass(int ipass) : GrPass(ipass) + { + } + + virtual bool IsPosPass() + { + return true; + } + + virtual bool PreBidiPass() + { + return false; + } + +protected: + virtual void RunRule(GrTableManager *, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput); + + virtual void Unattach(GrSlotStream * psstrmIn, int islotIn, + GrSlotStream * psstrmOut, int islotOut, int islotLB); + +public: +#ifdef OLD_TEST_STUFF + // For test procedures: + bool RunTestRules(GrTableManager *, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); + bool RunTestRules(GrTableManager *, int ruln, GrSlotStream * psstrmIn, + GrSlotStream * psstrmOut); +#endif // OLD_TEST_STUFF + +}; // end of class GrPosPass + +} // namespace gr + + +#endif // PASS_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPassActionCode.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPassActionCode.cpp new file mode 100644 index 00000000000..38868e52477 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPassActionCode.cpp @@ -0,0 +1,1490 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrPassActionCode.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + A continution of the GrPass file, including functions to run the action commands. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local type definitions +//:>******************************************************************************************** + +typedef signed char Int8; // signed value +typedef unsigned char Uint8; // unsigned value + + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + The main function to run a block of action or constraint code and return a result. + + @param ptman - table manager, for generating new slots; NULL when + running constraints + @param pbStart - address of the first command + @param fConstraints - true if we are running constraint tests; + false if we are running rule actions + @param psstrmIn - input stream + @param psstrmOut - output stream; when running constraints, used to access items + in the precontext + @param islot - slot being processed relative to the start of the rule; + used only for constraints; must be 0 for rule actions +----------------------------------------------------------------------------------------------*/ +int GrPass::RunCommandCode(GrTableManager * ptman, + byte * pbStart, bool fConstraints, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islot) +{ + byte * pbNext = pbStart; + +// std::vector<int> vnStack; +// vnStack.EnsureSpace(128); // make it nice and big + m_vnStack.clear(); + + gAssert(fConstraints || islot == 0); + + // There are two states: fMustGet = true means we need to get a slot to work on from + // the input; fMustGet = false means we have a slot and we need to do a Next before + // we can get the next one. + bool fMustGet = !fConstraints; + + bool fInserting = false; // set to true by an Insert command + + int nRet = 0; + + while (true) + { + ActionCommand op = ActionCommand(*pbNext++); + StackMachineFlag smf; + nRet = RunOneCommand(ptman, fConstraints, op, + &pbNext, &fMustGet, &fInserting, + psstrmIn, psstrmOut, islot, + m_vnStack, &smf); + + if (smf == ksmfDone) + return nRet; + + if (smf == ksmfUnderflow) + { + Warn("Underflow in Graphite stack machine"); + gAssert(false); + FontException fexptn; + fexptn.version = -1; + fexptn.subVersion = -1; + fexptn.errorCode = kferrUnknown; + throw fexptn; // disastrous error in rendering; fall back to dumb rendering + } + } + return nRet; // not needed, but to avoid compiler warning +} + +/*---------------------------------------------------------------------------------------------- + Perform a single command. Specifically, read the arguments from the byte buffer and the + run the appropriate functions. + + @param ptman - table manager, for generating new slots; or NULL + @param fConstraints - true if we are running constraints; false if we are running + rule actions + @param op - command operator + @param ppbArg - pointer to first argument + @param pfMustGet - state: getting input or processing it; when running constraints, + always false + @param pfInserting - true if current item is to be inserted, or was inserted + @param psstrmIn - input stream + @param psstrmOut - output stream + @param islot - the slot being processed relative to the beginning of the rule; + only relevant for constraint testing, must be 0 for rule actions + @param vnStack - the stack of values being manipulated + @param psmf - error code, done flag, or continue flag +----------------------------------------------------------------------------------------------*/ +int GrPass::RunOneCommand(GrTableManager * ptman, bool fConstraints, + ActionCommand op, byte ** ppbArg, bool * pfMustGet, bool * pfInserting, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islot, + std::vector<int> & vnStack, StackMachineFlag * psmf) +{ + *psmf = ksmfContinue; + + // General purpose variables: + int arg1, arg2, arg3, arg4; + int nSlotRef; + int nInputClass; + int nOutputClass; + int c; + int islotArg; + int nIndex; + int nGlyphAttr; + int nAttLevel; + int nFeat; + int nPState; + SlotAttrName slat; + + int i; + std::vector<int> vnTmp; + + byte * pbNext = *ppbArg; + + int nRet = 0; + + switch (op) + { + case kopNop: + break; + + case kopPushByte: + arg1 = Int8(*pbNext++); + vnStack.push_back(arg1); + break; + case kopPushByteU: + arg1 = Uint8(*pbNext++); + vnStack.push_back(arg1); + break; + case kopPushShort: + arg1 = Int8(*pbNext++); + arg2 = Uint8(*pbNext++); + vnStack.push_back((arg1 << 8) + arg2); + break; + case kopPushShortU: + arg1 = Uint8(*pbNext++); + arg2 = Uint8(*pbNext++); + vnStack.push_back((arg1 << 8) + arg2); + break; + case kopPushLong: + arg1 = Int8(*pbNext++); + arg2 = Uint8(*pbNext++); + arg3 = Uint8(*pbNext++); + arg4 = Uint8(*pbNext++); + vnStack.push_back((arg1 << 24) + (arg2 << 16) + (arg3 << 8) + arg4); + break; + + case kopNeg: + case kopTrunc8: case kopTrunc16: + case kopNot: + DoStackArithmetic1Arg(op, vnStack, psmf); + if (*psmf != ksmfContinue) + return 0; + break; + + case kopAdd: case kopSub: + case kopMul: case kopDiv: + case kopMin: case kopMax: + case kopAnd: case kopOr: + case kopEqual: case kopNotEq: + case kopLess: case kopGtr: + case kopLessEq: case kopGtrEq: + DoStackArithmetic2Args(op, vnStack, psmf); + if (*psmf != ksmfContinue) + return 0; + break; + + case kopCond: + DoConditional(vnStack, psmf); + if (*psmf != ksmfContinue) + return 0; + break; + + case kopNext: + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(!*pfMustGet); + DoNext(ptman, 1, psstrmIn, psstrmOut); + *pfMustGet = true; + *pfInserting = false; + } + break; + case kopNextN: + c = Int8(*pbNext++); + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(false); // for now we don't allow anything other than moving one slot + gAssert(!*pfMustGet); // forward at a time + DoNext(ptman, c, psstrmIn, psstrmOut); + *pfMustGet = true; + } + break; + case kopPutGlyph8bitObs: + case kopPutGlyph: + if (op == kopPutGlyph8bitObs) + nOutputClass = Uint8(*pbNext++); + else + { + nOutputClass = (Uint8(*pbNext++) << 8); + nOutputClass += Uint8(*pbNext++); + } + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + DoPutGlyph(ptman, *pfInserting, nOutputClass, psstrmIn, psstrmOut); + *pfMustGet = false; + } + break; + case kopPutCopy: + nSlotRef = Int8(*pbNext++); + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + DoPutCopy(ptman, *pfInserting, nSlotRef, psstrmIn, psstrmOut); + *pfMustGet = false; + } + break; + case kopPutSubs8bitObs: + case kopPutSubs: + nSlotRef = Int8(*pbNext++); + if (op == kopPutSubs8bitObs) + { + nInputClass = Uint8(*pbNext++); + nOutputClass = Uint8(*pbNext++); + } + else + { + nInputClass = (Uint8(*pbNext++) << 8); + nInputClass += Uint8(*pbNext++); + nOutputClass = (Uint8(*pbNext++) << 8); + nOutputClass += Uint8(*pbNext++); + } + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + DoPutSubs(ptman, *pfInserting, nSlotRef, nInputClass, nOutputClass, + psstrmIn, psstrmOut); + *pfMustGet = false; + } + break; + case kopCopyNext: + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + gAssert(!*pfInserting); // 0 means copy this slot + DoPutCopy(ptman, *pfInserting, 0, psstrmIn, psstrmOut); + DoNext(ptman, 1, psstrmIn, psstrmOut); + // *pfMustGet still = true + } + break; + case kopInsert: + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + *pfInserting = true; + // *pfMustGet still = true + + if (ptman->LoggingTransduction()) + m_pzpst->RecordInsertionAt(psstrmOut->WritePos()); + } + break; + case kopDelete: + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(*pfMustGet); + DoDelete(ptman, psstrmIn, psstrmOut); + *pfMustGet = false; + } + break; + case kopAssoc: + c = Uint8(*pbNext++); + // std::vector<int> vnTmp; + for (i = 0; i < c; i++) + vnTmp.push_back(Int8(*pbNext++)); + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(!*pfMustGet); + DoAssoc(c, vnTmp, *pfInserting, psstrmIn, psstrmOut); + } + break; + case kopCntxtItem: + islotArg = Int8(*pbNext++); + c = Uint8(*pbNext++); + if (fConstraints) + { + // Old approach: + // If this is NOT the relevant item, push TRUE, which causes the subsequent tests + // (which are eventually ORed with it) to become irrelevant. + //vnStack.Push((int)(islotArg != islot)); + + // If this is not the relevant item, skip the specified number of bytes. + if (islotArg != islot) + { + pbNext += c; + vnStack.push_back(1); + } + } + else + { + gAssert(false); + } + break; + + case kopAttrSet: + case kopAttrAdd: + case kopAttrSub: + case kopAttrSetSlot: + slat = SlotAttrName(Uint8(*pbNext++)); // slot attribute ID + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(!*pfMustGet); + DoSetAttr(ptman, op, *pfInserting, slat, -1, vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + } + break; + case kopIAttrSet: + case kopIAttrAdd: + case kopIAttrSub: + case kopIAttrSetSlot: + slat = SlotAttrName(Uint8(*pbNext++)); // slot attribute ID + nIndex = Uint8(*pbNext++); // index; eg, global ID for component + if (fConstraints) + { + gAssert(false); + } + else + { + gAssert(!*pfMustGet); + DoSetAttr(ptman, op, *pfInserting, slat, nIndex, vnStack, + psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + } + break; + + case kopPushSlotAttr: + slat = SlotAttrName(Uint8(*pbNext++)); + nSlotRef = Int8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet + DoPushSlotAttr(ptman, nSlotRef, *pfInserting, slat, -1, vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + case kopPushISlotAttr: + slat = SlotAttrName(Uint8(*pbNext++)); + nSlotRef = Int8(*pbNext++); + nIndex = Uint8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet + DoPushSlotAttr(ptman, nSlotRef, *pfInserting, slat, nIndex, vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + + case kopPushGlyphAttrObs: + case kopPushAttToGAttrObs: + case kopPushGlyphAttr: + case kopPushAttToGlyphAttr: + if (op == kopPushGlyphAttrObs || op == kopPushAttToGAttrObs) + nGlyphAttr = Uint8(*pbNext++); + else { + nGlyphAttr = (Uint8(*pbNext++) << 8); + nGlyphAttr += Uint8(*pbNext++); + } + nSlotRef = Int8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet + if (op == kopPushAttToGlyphAttr || op == kopPushAttToGAttrObs) + DoPushAttToGlyphAttr(ptman, nSlotRef, *pfInserting, nGlyphAttr, + vnStack, psstrmIn, psstrmOut); + else + DoPushGlyphAttr(ptman, nSlotRef, *pfInserting, nGlyphAttr, vnStack, + psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + case kopPushGlyphMetric: + case kopPushAttToGlyphMetric: + nGlyphAttr = Uint8(*pbNext++); + nSlotRef = Int8(*pbNext++); + nAttLevel = Int8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet + if (op == kopPushAttToGlyphMetric) + DoPushAttToGlyphMetric(ptman, nSlotRef, *pfInserting, nGlyphAttr, nAttLevel, + vnStack, psstrmIn, psstrmOut); + else + DoPushGlyphMetric(ptman, nSlotRef, *pfInserting, nGlyphAttr, nAttLevel, + vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + case kopPushFeat: + nFeat = Uint8(*pbNext++); + nSlotRef = Int8(*pbNext++); + if (fConstraints) + nSlotRef += islot + 1; // +1 to peek ahead to a slot we haven't "got" yet +// else +// nSlotRef = 0; + DoPushFeatValue(ptman, nSlotRef, *pfInserting, nFeat, vnStack, psstrmIn, psstrmOut); + if (*psmf != ksmfContinue) + return 0; + break; + case kopPushProcState: + nPState = Uint8(*pbNext++); + gAssert(fConstraints); + DoPushProcState(ptman, nPState, vnStack); + break; + case kopPushVersion: + vnStack.push_back(kRuleVersion); + break; + case kopPopRet: + if ((*psmf = CheckStack(vnStack, 1)) != ksmfContinue) + return 0; + nRet = vnStack.back(); + vnStack.pop_back(); + if (vnStack.size() != 0) + { + gAssert(false); + *psmf = ksmfStackNotEmptied; + } + else + *psmf = ksmfDone; + break; + case kopRetZero: + nRet = 0; + if (vnStack.size() != 0) + { + gAssert(false); + *psmf = ksmfStackNotEmptied; + } + else + *psmf = ksmfDone; + break; + case kopRetTrue: + nRet = 1; + if (vnStack.size() != 0) + { + gAssert(false); + *psmf = ksmfStackNotEmptied; + } + else + *psmf = ksmfDone; + break; + + default: + gAssert(false); + } + + *ppbArg = pbNext; + return nRet; +} + +/*---------------------------------------------------------------------------------------------- + Perform arithmetic functions that take two arguments. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoStackArithmetic2Args(ActionCommand op, std::vector<int> & vnStack, + StackMachineFlag * psmf) +{ + if ((*psmf = CheckStack(vnStack, 2)) != ksmfContinue) + return; + + int nArg2 = vnStack.back(); + vnStack.pop_back(); + int nArg1 = vnStack.back(); + vnStack.pop_back(); + + int nResult; + + switch (op) + { + case kopAdd: nResult = nArg1 + nArg2; break; + case kopSub: nResult = nArg1 - nArg2; break; + case kopMul: nResult = nArg1 * nArg2; break; + case kopDiv: nResult = nArg1 / nArg2; break; + case kopMin: nResult = min(nArg1, nArg2); break; + case kopMax: nResult = max(nArg1, nArg2); break; + case kopAnd: nResult = (nArg1 != 0 && nArg2 != 0)? 1: 0; break; + case kopOr: nResult = (nArg1 != 0 || nArg2 != 0)? 1: 0; break; + case kopEqual: nResult = (nArg1 == nArg2)? 1: 0; break; + case kopNotEq: nResult = (nArg1 != nArg2)? 1: 0; break; + case kopLess: nResult = (nArg1 < nArg2)? 1: 0; break; + case kopLessEq: nResult = (nArg1 <= nArg2)? 1: 0; break; + case kopGtr: nResult = (nArg1 > nArg2)? 1: 0; break; + case kopGtrEq: nResult = (nArg1 >= nArg2)? 1: 0; break; + default: + Assert(false); + } + + vnStack.push_back(nResult); +} + +/*---------------------------------------------------------------------------------------------- + Perform arithmetic functions that take one argument. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoStackArithmetic1Arg(ActionCommand op, std::vector<int> & vnStack, + StackMachineFlag * psmf) +{ + if ((*psmf = CheckStack(vnStack, 1)) != ksmfContinue) + return; + + int nArg = vnStack.back(); + vnStack.pop_back(); + int nResult; + + switch (op) + { + case kopNeg: nResult = nArg * -1; break; + case kopTrunc8: nResult = nArg & 0xFF; break; + case kopTrunc16: nResult = nArg & 0xFFFF; break; + case kopNot: nResult = (nArg == 0)? 1: 0; break; + default: + Assert(false); + } + + vnStack.push_back(nResult); +} + +/*---------------------------------------------------------------------------------------------- + Perform the conditional statement. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoConditional(std::vector<int> & vnStack, StackMachineFlag * psmf) +{ + if ((*psmf = CheckStack(vnStack, 3)) != ksmfContinue) + return; + + int nArg3 = vnStack.back(); + vnStack.pop_back(); + int nArg2 = vnStack.back(); + vnStack.pop_back(); + int nArg1 = vnStack.back(); + vnStack.pop_back(); + + if (nArg1 == 0) + vnStack.push_back(nArg3); + else + vnStack.push_back(nArg2); +} + +/*---------------------------------------------------------------------------------------------- + Check to make sure the stack has the appropriate number of items on it; if not, + return a stack error flag. +----------------------------------------------------------------------------------------------*/ +GrPass::StackMachineFlag GrPass::CheckStack(std::vector<int> & vnStack, int cn) +{ + if (signed(vnStack.size()) < cn) + return ksmfUnderflow; + else + return ksmfContinue; +} + +/*---------------------------------------------------------------------------------------------- + We are finished processing a slot; go on to the next slot, or possibly go backwards. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoNext(GrTableManager * ptman, + int cslot, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + gAssert(cslot == 1); // for now anyway +} + +/*---------------------------------------------------------------------------------------------- + Set the next glyph to be the given class. There should only be one member of the class, + but if there happens to be more than one, just use the first member. + All the slot attributes remain unchanged from the current input slot; associations are + neutralized. + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param nReplacementClass - class from which to take replacement glyph (corresponding to + selector slot's glyph's index in selector class) + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutGlyph(GrTableManager * ptman, bool fInserting, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + EngineState * pengst = ptman->State(); + + // If we are exactly at the segment boundary, pass the information on to the output stream. + // Note that inserted items always go outside the segment, if they are at the boundary + // (you DON'T want them between the segment boundary and the LB slot!). + bool fSetSegMin = psstrmIn->AtSegMin() && !fInserting; + bool fSetSegLim = psstrmIn->AtSegLim(); + + // Slot to copy features and text properties from: + GrSlotState * pslotNextInput; + if (psstrmIn->AtEndOfContext()) + { + gAssert(fInserting); + pslotNextInput = psstrmIn->RuleInputSlot(0, psstrmOut); + } + else + pslotNextInput = (fInserting)? psstrmIn->Peek(): psstrmIn->NextGet(); + + gid16 nGlyphReplacement = ptman->GetClassGlyphIDAt(nReplacementClass, 0); + + GrSlotState * pslotNew; + if (fInserting) + { + pengst->NewSlot(nGlyphReplacement, pslotNextInput, m_ipass, &pslotNew); + // leave associations empty; eventually they will be "neutralized" + } + else + { + pengst->NewSlotCopy(pslotNextInput, m_ipass, &pslotNew); + pslotNew->SetGlyphID(nGlyphReplacement); + ptman->SetSlotAttrsFromGlyphAttrs(pslotNew); + } + + if (fSetSegMin) + psstrmOut->SetSegMinToWritePos(false); + if (fSetSegLim) + psstrmOut->SetSegLimToWritePos(false); + psstrmOut->NextPut(pslotNew); +} + +/*---------------------------------------------------------------------------------------------- + Copy a glyph from the given slot in the input to the output. All the associations and slot + attributes are copied from the specified slot as well. Consume a slot from the input + (which may or may not be the slot we're copying). + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param cslotCopyFrom - slot to copy from + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutCopy(GrTableManager * ptman, bool fInserting, int cslotCopyFrom, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + EngineState * pengst = ptman->State(); + + // If we are exactly at the segment boundary, pass the information on to the output stream. + // Note that inserted items always go outside the segment, if they are at the boundary. + bool fSetSegMin = psstrmIn->AtSegMin() && !fInserting; + bool fSetSegLim = psstrmIn->AtSegLim(); + + if (!fInserting) + { + gAssert(!psstrmIn->AtEndOfContext()); + // Absorb next slot + psstrmIn->NextGet(); + } + + GrSlotState * pslotCopyFrom = psstrmIn->RuleInputSlot(cslotCopyFrom, psstrmOut); + + GrSlotState * pslotNew; + pengst->NewSlotCopy(pslotCopyFrom, m_ipass, &pslotNew); + + if (fSetSegMin) + psstrmOut->SetSegMinToWritePos(false); + if (fSetSegLim) + psstrmOut->SetSegLimToWritePos(false); + psstrmOut->NextPut(pslotNew); +} + +/*---------------------------------------------------------------------------------------------- + Copy the current slot from the input to the output, substituting the corresponding glyph + in the output class. All the associations and slot attributes remain unchanged from the + current input slot. + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param cslotSel - selector slot, relative to current slot + @param nSelectorClass - class of selector slot + @param nReplacementClass - class from which to take replacement glyph (corresponding to + selector slot's glyph's index in selector class) + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubs(GrTableManager * ptman, bool fInserting, + int cslotSel, int nSelClass, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + bool fAtSegMin, fAtSegLim; + GrSlotState * pslotNextInput; + DoPutSubsInit(psstrmIn, psstrmOut, fInserting, &pslotNextInput, &fAtSegMin, &fAtSegLim); + + GrSlotState * pslotInSelector = psstrmIn->RuleInputSlot(cslotSel, psstrmOut); + + gid16 gidSelector = pslotInSelector->GlyphID(); + int nSelIndex = ptman->GetIndexInGlyphClass(nSelClass, gidSelector); + gAssert(nSelIndex != -1); + gid16 gidReplacement = (nSelIndex == -1)? + gidSelector: + ptman->GetClassGlyphIDAt(nReplacementClass, nSelIndex); + + DoPutSubsAux(ptman, fInserting, gidReplacement, psstrmIn, psstrmOut, pslotNextInput, + fAtSegMin, fAtSegLim); +} + +/*---------------------------------------------------------------------------------------------- + Copy the current slot from the input to the output, substituting the corresponding glyph + in the output class based on 2 input classes. All the associations and slot attributes + remain unchanged from the current input slot. + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param cslotSel - selector slot, relative to current slot + @param nSelectorClass - class of selector slot + @param nReplacementClass - class from which to take replacement glyph (corresponding to + selector slot's glyph's index in selector class) + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubs2(GrTableManager * ptman, bool fInserting, + int cslotSel1, int nSelClass1, int cslotSel2, int nSelClass2, int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + bool fAtSegMin, fAtSegLim; + GrSlotState * pslotNextInput; + DoPutSubsInit(psstrmIn, psstrmOut, fInserting, &pslotNextInput, &fAtSegMin, &fAtSegLim); + + GrSlotState * pslotInSelector1 = psstrmIn->RuleInputSlot(cslotSel1, psstrmOut); + gid16 gidSelector1 = pslotInSelector1->GlyphID(); + int nSelIndex1 = ptman->GetIndexInGlyphClass(nSelClass1, gidSelector1); + gAssert(nSelIndex1 != -1); + //size_t cClassLen1 = ptman->NumberOfGlyphsInClass(nSelClass1); + + GrSlotState * pslotInSelector2 = psstrmIn->RuleInputSlot(cslotSel2, psstrmOut); + gid16 gidSelector2 = pslotInSelector2->GlyphID(); + int nSelIndex2 = ptman->GetIndexInGlyphClass(nSelClass2, gidSelector2); + gAssert(nSelIndex2 != -1); + size_t cClassLen2 = ptman->NumberOfGlyphsInClass(nSelClass2); + + int nSelIndex = (nSelIndex1 == -1 || nSelIndex2 == -1) ? + -1 : + (nSelIndex1 * static_cast<int>(cClassLen2)) + nSelIndex2; + + gid16 gidReplacement = (nSelIndex == -1)? + gidSelector1: + ptman->GetClassGlyphIDAt(nReplacementClass, nSelIndex); + + DoPutSubsAux(ptman, fInserting, gidReplacement, psstrmIn, psstrmOut, pslotNextInput, + fAtSegMin, fAtSegLim); +} + +/*---------------------------------------------------------------------------------------------- + Copy the current slot from the input to the output, substituting the corresponding glyph + in the output class based on 2 input classes. All the associations and slot attributes + remain unchanged from the current input slot. + + @param ptman - table manager, for generating new slots + @param fInserting - true if this slot is being inserted, not copied + @param cslotSel - selector slot, relative to current slot + @param nSelectorClass - class of selector slot + @param nReplacementClass - class from which to take replacement glyph (corresponding to + selector slot's glyph's index in selector class) + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubs3(GrTableManager * ptman, bool fInserting, + int cslotSel1, int nSelClass1, int cslotSel2, int nSelClass2, int cslotSel3, int nSelClass3, + int nReplacementClass, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + bool fAtSegMin, fAtSegLim; + GrSlotState * pslotNextInput; + DoPutSubsInit(psstrmIn, psstrmOut, fInserting, &pslotNextInput, &fAtSegMin, &fAtSegLim); + + GrSlotState * pslotInSelector1 = psstrmIn->RuleInputSlot(cslotSel1, psstrmOut); + gid16 gidSelector1 = pslotInSelector1->GlyphID(); + int nSelIndex1 = ptman->GetIndexInGlyphClass(nSelClass1, gidSelector1); + gAssert(nSelIndex1 != -1); + //size_t cClassLen1 = ptman->NumberOfGlyphsInClass(nSelClass1); + + GrSlotState * pslotInSelector2 = psstrmIn->RuleInputSlot(cslotSel2, psstrmOut); + gid16 gidSelector2 = pslotInSelector2->GlyphID(); + int nSelIndex2 = ptman->GetIndexInGlyphClass(nSelClass2, gidSelector2); + gAssert(nSelIndex2 != -1); + size_t cClassLen2 = ptman->NumberOfGlyphsInClass(nSelClass2); + +// GrSlotState * pslotInSelector3 = psstrmIn->RuleInputSlot(cslotSel3, psstrmOut); +// gid16 gidSelector3 = pslotInSelector3->GlyphID(); + int nSelIndex3 = ptman->GetIndexInGlyphClass(nSelClass3, gidSelector2); + gAssert(nSelIndex3 != -1); + size_t cClassLen3 = ptman->NumberOfGlyphsInClass(nSelClass3); + + int nSelIndex = (nSelIndex1 == -1 || nSelIndex2 == -1 || nSelIndex3 == -1) ? + -1 : + (((nSelIndex1 * static_cast<int>(cClassLen2)) + nSelIndex2) * static_cast<int>(cClassLen3)) + nSelIndex3; + + gid16 gidReplacement = (nSelIndex == -1)? + gidSelector1: + ptman->GetClassGlyphIDAt(nReplacementClass, nSelIndex); + + DoPutSubsAux(ptman, fInserting, gidReplacement, psstrmIn, psstrmOut, pslotNextInput, + fAtSegMin, fAtSegLim); +} + + +/*---------------------------------------------------------------------------------------------- + Initial common part of all the DoPutSubs... methods. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubsInit(GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, bool fInserting, + GrSlotState ** ppslotNextInput, bool * pfAtSegMin, bool * pfAtSegLim) +{ + // Do this before reading from stream. + *pfAtSegMin = psstrmIn->AtSegMin(); + *pfAtSegLim = psstrmIn->AtSegLim(); + + // Slot to copy features and text properties from: + if (psstrmIn->AtEndOfContext()) + { + gAssert(fInserting); + *ppslotNextInput = psstrmIn->RuleInputSlot(0, psstrmOut); + } + else + *ppslotNextInput = (fInserting)? psstrmIn->Peek(): psstrmIn->NextGet(); +} + +/*---------------------------------------------------------------------------------------------- + Common part of all the DoPutSubs... methods. +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPutSubsAux(GrTableManager * ptman, bool fInserting, gid16 nGlyphReplacement, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, GrSlotState * pslotNextInput, + bool fAtSegMin, bool fAtSegLim) +{ + EngineState * pengst = ptman->State(); + + // If we are exactly at the segment boundary, pass the information on to the output stream. + // Note that inserted items always go outside the segment, if they are at the boundary. + bool fSetSegMin = fAtSegMin && !fInserting; + bool fSetSegLim = fAtSegLim; + + GrSlotState * pslotNew; + if (fInserting) + { + pengst->NewSlot(nGlyphReplacement, pslotNextInput, m_ipass, &pslotNew); + // leave associations empty; eventually they will be "neutralized" + } + else + { + pengst->NewSlotCopy(pslotNextInput, m_ipass, &pslotNew); + pslotNew->SetGlyphID(nGlyphReplacement); + ptman->SetSlotAttrsFromGlyphAttrs(pslotNew); + } + + if (fSetSegMin) + psstrmOut->SetSegMinToWritePos(false); + if (fSetSegLim) + psstrmOut->SetSegLimToWritePos(false); + psstrmOut->NextPut(pslotNew); +} + +/*---------------------------------------------------------------------------------------------- + A slot has just been inserted. Clear all the associations; eventually (unless we go on + to set the associations explicitly) we will set its before-assoc to the slot after it + and its after-assoc to the slot before it. This makes it basically unselectable. + OBSOLETE - handled by slot initialization code +----------------------------------------------------------------------------------------------*/ +void GrPass::SetNeutralAssocs(GrSlotState * pslotNew, GrSlotStream * psstrmIn) +{ + pslotNew->ClearAssocs(); +} + +/*---------------------------------------------------------------------------------------------- + Delete the next slot in the input. + + @param psstrmIn - input stream + @param psstrmOut - output stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoDelete(GrTableManager * ptman, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + if (psstrmIn->AtSegMin()) + psstrmOut->SetSegMinToWritePos(); + if (psstrmIn->AtSegLim()) + psstrmOut->SetSegLimToWritePos(); + + GrSlotState * pslot = psstrmIn->NextGet(); + pslot->MarkDeleted(); + + if (ptman->LoggingTransduction()) + m_pzpst->RecordDeletionBefore(psstrmOut->WritePos()); +} + +/*---------------------------------------------------------------------------------------------- + Set the associations for the current slot. Any previous associations will be overwritten. + + @param cn - number of associations + @param vnAssoc - list of associations + @param fInserting - whether current slot was inserted + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoAssoc(int cnAssocs, std::vector<int> & vnAssocs, bool fInserting, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + gAssert((unsigned)cnAssocs == vnAssocs.size()); + + // Sort the list of associations. It's okay to use a simple bubble sort + // since we expect the list to be very short. Ideally we should remove duplicates + // too but I don't think duplicates actually hurt anything. + for (int i1 = 0; i1 < cnAssocs - 1; i1++) + { + for (int i2 = i1 + 1; i2 < cnAssocs; i2++) + { + if (vnAssocs[i2] < vnAssocs[i1]) + { + int nTmp = vnAssocs[i2]; + vnAssocs[i2] = vnAssocs[i1]; + vnAssocs[i1] = nTmp; + } + } + } + + std::vector<GrSlotState *> vpslotAssocs; + vpslotAssocs.resize(cnAssocs); + for (int i = 0; i < cnAssocs; i++) + vpslotAssocs[i] = psstrmIn->RuleInputSlot(vnAssocs[i], psstrmOut); + + GrSlotState * pslot = psstrmOut->RuleOutputSlot(); + pslot->Associate(vpslotAssocs); +} + +/*---------------------------------------------------------------------------------------------- + Set a slot attribute for the current slot; the value is on the stack. + + @param op - command + @param fInserting - whether current slot was inserted + @param slat - slot attribute to set + @param slati - slot attribute index, or -1 for non-indexed attribute + @param vnStack - stack to read value from + @param psstrmIn / Out - input/output streams +----------------------------------------------------------------------------------------------*/ +void GrPass::DoSetAttr(GrTableManager * ptman, ActionCommand op, bool fInserting, + SlotAttrName slat, int slati, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + int nVal = vnStack.back(); + vnStack.pop_back(); + + if (slat == kslatUserDefnV1) + slat = kslatUserDefn; + + if (slati != -1 && slat != kslatCompRef && slat != kslatUserDefn) + { + // Invalid slot attribute index. + gAssert(false); + slati = -1; + } + + if (slati == -1 && (slat == kslatCompRef || slat == kslatUserDefn)) + { + // Missing slot attribute index. + gAssert(false); + slati = 0; + } + + if (slat == kslatPosX || slat == kslatPosY) + { + // Doesn't make sense to set the pos attribute. + gAssert(false); + return; + } + + GrSlotState * pslotIn = psstrmIn->RuleInputSlot(0, psstrmOut); + + GrSlotState * pslotComp; + + int nOldVal; + + if (op == kopAttrAdd || op == kopAttrSub || op == kopIAttrAdd || op == kopIAttrSub) + { + // Incrementing or decrementing. + + gAssert(!fInserting); + + switch (slat) + { + case kslatAdvX: nOldVal = pslotIn->AdvanceX(ptman); break; + case kslatAdvY: nOldVal = pslotIn->AdvanceY(ptman); break; + case kslatShiftX: nOldVal = pslotIn->ShiftX(); break; + case kslatShiftY: nOldVal = pslotIn->ShiftY(); break; + + case kslatAttAtX: nOldVal = pslotIn->AttachAtX(ptman, psstrmIn); break; + case kslatAttAtY: nOldVal = pslotIn->AttachAtY(); break; + case kslatAttAtXoff: nOldVal = pslotIn->AttachAtXOffset(); break; + case kslatAttAtYoff: nOldVal = pslotIn->AttachAtYOffset(); break; + + case kslatAttWithX: nOldVal = pslotIn->AttachWithX(ptman, psstrmIn); break; + case kslatAttWithY: nOldVal = pslotIn->AttachWithY(); break; + case kslatAttWithXoff: nOldVal = pslotIn->AttachWithXOffset(); break; + case kslatAttWithYoff: nOldVal = pslotIn->AttachWithYOffset(); break; + + case kslatUserDefn: nOldVal = pslotIn->UserDefn(slati); break; + + case kslatMeasureSol: nOldVal = pslotIn->MeasureSol(); break; + case kslatMeasureEol: nOldVal = pslotIn->MeasureEol(); break; + + case kslatJStretch: nOldVal = pslotIn->JStretch(); break; + case kslatJShrink: nOldVal = pslotIn->JShrink(); break; + case kslatJStep: nOldVal = pslotIn->JStep(); break; + case kslatJWeight: nOldVal = pslotIn->JWeight(); break; + case kslatJWidth: nOldVal = pslotIn->JWidth(); break; + + // These are kind of odd, but maybe. + case kslatAttLevel: nOldVal = pslotIn->AttachLevel(); break; + case kslatBreak: nOldVal = pslotIn->BreakWeight(); break; + case kslatDir: nOldVal = pslotIn->Directionality(); break; + + default: + // Kind of attribute that it makes no sense to increment. + gAssert(false); + return; + } + + if (op == kopAttrAdd || op == kopIAttrAdd) + nVal = nOldVal + nVal; + else + { + gAssert(op == kopAttrSub || op == kopIAttrSub); + nVal = nOldVal - nVal; + } + } + + GrSlotState * pslotOut = psstrmOut->RuleOutputSlot(); + + if (pslotOut->IsLineBreak(ptman->LBGlyphID())) + { + switch (slat) + { + case kslatAdvX: + case kslatAdvY: + case kslatShiftX: + case kslatShiftY: + + case kslatAttTo: + case kslatAttLevel: + case kslatAttAtX: + case kslatAttAtY: + case kslatAttAtGpt: + case kslatAttAtXoff: + case kslatAttAtYoff: + case kslatAttWithX: + case kslatAttWithY: + case kslatAttWithGpt: + case kslatAttWithXoff: + case kslatAttWithYoff: + + case kslatMeasureSol: + case kslatMeasureEol: + + case kslatJStretch: + case kslatJShrink: + case kslatJStep: + case kslatJWeight: + case kslatJWidth: + slat = kslatNoEffect; + break; + default: + ; // okay, do nothing + } + } + + nVal = GrGlyphSubTable::ConvertValueForVersion(nVal, slat, -1, m_fxdVersion); + + switch (slat) + { + case kslatAdvX: pslotOut->SetAdvanceX(nVal); break; + case kslatAdvY: pslotOut->SetAdvanceY(nVal); break; + case kslatShiftX: pslotOut->SetShiftX(nVal); break; + case kslatShiftY: pslotOut->SetShiftY(nVal); break; + + case kslatAttTo: pslotOut->SetAttachTo(nVal); break; + case kslatAttLevel: pslotOut->SetAttachLevel(nVal); break; + case kslatAttAtX: pslotOut->SetAttachAtX(nVal); break; + case kslatAttAtY: pslotOut->SetAttachAtY(nVal); break; + case kslatAttAtGpt: pslotOut->SetAttachAtGpoint(nVal); break; + case kslatAttAtXoff: pslotOut->SetAttachAtXOffset(nVal); break; + case kslatAttAtYoff: pslotOut->SetAttachAtYOffset(nVal); break; + case kslatAttWithX: pslotOut->SetAttachWithX(nVal); break; + case kslatAttWithY: pslotOut->SetAttachWithY(nVal); break; + case kslatAttWithGpt: pslotOut->SetAttachWithGpoint(nVal); break; + case kslatAttWithXoff: pslotOut->SetAttachWithXOffset(nVal); break; + case kslatAttWithYoff: pslotOut->SetAttachWithYOffset(nVal); break; + + case kslatUserDefn: pslotOut->SetUserDefn(slati, nVal); break; + + case kslatCompRef: + gAssert(nVal - 1 >= psstrmIn->RuleStartReadPos() - psstrmIn->ReadPosForNextGet()); + pslotComp = psstrmIn->RuleInputSlot(nVal, psstrmOut); + pslotOut->SetCompRefSlot(ptman, slati, pslotComp); + break; + + case kslatMeasureSol: pslotOut->SetMeasureSol(nVal); break; + case kslatMeasureEol: pslotOut->SetMeasureEol(nVal); break; + + case kslatJStretch: pslotOut->SetJStretch(nVal); break; + case kslatJShrink: pslotOut->SetJShrink(nVal); break; + case kslatJStep: pslotOut->SetJStep(nVal); break; + case kslatJWeight: pslotOut->SetJWeight(nVal); break; + case kslatJWidth: pslotOut->SetJWidth(nVal); break; + + case kslatBreak: pslotOut->SetBreakWeight(nVal); break; + case kslatDir: pslotOut->SetDirectionality(DirCode(nVal)); break; + case kslatInsert: pslotOut->SetInsertBefore(bool(nVal)); break; + + case kslatNoEffect: + // Do nothing. + break; + + default: + // Kind of attribute that it makes no sense to set. + gAssert(false); + return; + } +} + +/*---------------------------------------------------------------------------------------------- + Push value of a slot attribute onto the stack. + + @param nSlotRef - slot whose attribute we are interested in + @param fInserting - whether current slot was inserted + @param slat - slot attribute to get + @param slati - slot attribute index, or -1 for non-indexed attribute + @param vnStack - stack to push onto + @param psstrmIn - input stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushSlotAttr(GrTableManager * ptman, + int nSlotRef, bool fInserting, + SlotAttrName slat, int slati, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef, psstrmOut, true); + if (pslot == NULL) + { + // Non-existent pre-context item. + vnStack.push_back(0); + return; + } + + int nVal; + int xyBogus; + + if (slat == kslatUserDefnV1) + slat = kslatUserDefn; + + if (slati != -1 && slat != kslatCompRef && slat != kslatUserDefn) + { + // Invalid slot attribute index. + gAssert(false); + slati = -1; + } + + if (slati == -1 && (slat == kslatCompRef || slat == kslatUserDefn)) + { + // Missing slot attribute index. + gAssert(false); + slati = 0; + } + + switch (slat) + { + case kslatAdvX: nVal = pslot->AdvanceX(ptman); break; + case kslatAdvY: nVal = pslot->AdvanceY(ptman); break; + case kslatShiftX: nVal = pslot->ShiftX(); break; + case kslatShiftY: nVal = pslot->ShiftY(); break; + + case kslatPosX: pslot->Position(ptman, psstrmIn, &nVal, &xyBogus); break; + case kslatPosY: pslot->Position(ptman, psstrmIn, &xyBogus, &nVal); break; + + case kslatAttTo: nVal = pslot->AttachTo(); break; + case kslatAttLevel: nVal = pslot->AttachLevel(); break; + case kslatAttAtX: nVal = pslot->AttachAtX(ptman, psstrmIn); break; + case kslatAttAtY: nVal = pslot->AttachAtY(); break; + case kslatAttAtGpt: nVal = pslot->AttachAtGpoint(); break; + case kslatAttAtXoff: nVal = pslot->AttachAtXOffset(); break; + case kslatAttAtYoff: nVal = pslot->AttachAtYOffset(); break; + case kslatAttWithX: nVal = pslot->AttachWithX(ptman, psstrmIn); break; + case kslatAttWithY: nVal = pslot->AttachWithY(); break; + case kslatAttWithGpt: nVal = pslot->AttachWithGpoint(); break; + case kslatAttWithXoff: nVal = pslot->AttachWithXOffset(); break; + case kslatAttWithYoff: nVal = pslot->AttachWithYOffset(); break; + + case kslatMeasureSol: nVal = pslot->MeasureSol(); break; + case kslatMeasureEol: nVal = pslot->MeasureEol(); break; + + case kslatJStretch: nVal = pslot->JStretch(); break; + case kslatJShrink: nVal = pslot->JShrink(); break; + case kslatJStep: nVal = pslot->JStep(); break; + case kslatJWeight: nVal = pslot->JWeight(); break; + case kslatJWidth: nVal = pslot->JWidth(); break; + + case kslatBreak: nVal = pslot->BreakWeight(); break; + case kslatDir: nVal = pslot->Directionality(); break; + case kslatInsert: nVal = pslot->InsertBefore(); break; + + case kslatUserDefn: nVal = pslot->UserDefn(slati); break; + + // Currently no way to look up component.X.ref. + + default: + // Invalid attribute. + gAssert(false); + nVal = 0; + } + + vnStack.push_back(nVal); +} + +/*---------------------------------------------------------------------------------------------- + Push value of a glyph attribute onto the stack. + + @param nSlotRef - slot whose attribute we are interested in + @param fInserting - whether current slot was inserted + @param nGlyphAttr - glyph attribute to get + @param vnStack - stack to push onto + @param psstrmIn - input stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushGlyphAttr(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, + std::vector<int> & vnStack, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef, psstrmOut, true); + if (pslot == NULL) + { + // Non-existent pre-context item. + vnStack.push_back(0); + return; + } + int nVal = pslot->GlyphAttrValueEmUnits(ptman, nGlyphAttr); + vnStack.push_back(nVal); +} + +/*---------------------------------------------------------------------------------------------- + Push value of a glyph attribute onto the stack. The slot of interest is the slot to + which the current slot is attached. + + @param nSlotRef - leaf slot + @param fInserting - whether current slot was inserted + @param nGlyphAttr - glyph attribute to get + @param vnStack - stack to push onto + @param psstrmIn - input stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushAttToGlyphAttr(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Note that it is the slot in the output stream that has + // the relevant attach.to attribute set. + GrSlotState * pslotLeaf = psstrmOut->RuleOutputSlot(0); + int srAttTo = pslotLeaf->AttachTo(); + if (srAttTo == 0) + { + gAssert(false); + vnStack.push_back(0); + return; + } + + GrSlotState * pslotRoot = psstrmIn->RuleInputSlot(nSlotRef + srAttTo, psstrmOut); + int nVal = pslotRoot->GlyphAttrValueEmUnits(ptman, nGlyphAttr); + vnStack.push_back(nVal); +} + +/*---------------------------------------------------------------------------------------------- + Push value of a glyph metric onto the stack. + + @param nSlotRef - slot whose attribute we are interested in + @param fInserting - whether current slot was inserted + @param nGlyphAttr - glyph attribute to get + @param nAttLevel - for accessing composite metrics + @param vnStack - stack to push onto + @param psstrmIn - input stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushGlyphMetric(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, int nAttLevel, + std::vector<int> & vnStack, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef, psstrmOut, true); + if (pslot == NULL) + { + // Non-existent pre-context item. + vnStack.push_back(0); + return; + } + + DoPushGlyphMetricAux(ptman, pslot, nGlyphAttr, nAttLevel, vnStack, psstrmIn); +} + +/*---------------------------------------------------------------------------------------------- + Push value of a glyph metric onto the stack. The slot of interest is the slot to + which the current slot is attached. + + @param nSlotRef - slot whose attribute we are interested in + @param fInserting - whether current slot was inserted + @param nGlyphAttr - glyph attribute to get + @param nAttLevel - for accessing composite metrics + @param vnStack - stack to push onto + @param psstrmIn - input stream + @param psstrmOut - output stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushAttToGlyphMetric(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nGlyphAttr, int nAttLevel, + std::vector<int> & vnStack, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + // Note that it is the slot in the output stream that has + // the relevant attach.to attribute set. + GrSlotState * pslotLeaf = psstrmOut->RuleOutputSlot(0); + int srAttTo = pslotLeaf->AttachTo(); + if (srAttTo == 0) + { + gAssert(false); + vnStack.push_back(0); + return; + } + + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef + srAttTo, psstrmOut); + + DoPushGlyphMetricAux(ptman, pslot, nGlyphAttr, nAttLevel, vnStack, psstrmIn); +} + +/*--------------------------------------------------------------------------------------------*/ + +void GrPass::DoPushGlyphMetricAux(GrTableManager * ptman, + GrSlotState * pslot, int nGlyphAttr, int nAttLevel, + std::vector<int> & vnStack, GrSlotStream * psstrmIn) +{ + int mVal; + GlyphMetric gmet = GlyphMetric(nGlyphAttr); + if (nAttLevel == 0 || gmet == kgmetAscent || gmet == kgmetDescent) + { + mVal = pslot->GlyphMetricEmUnits(ptman, nGlyphAttr); + } + else + { + pslot->CalcCompositeMetrics(ptman, psstrmIn, nAttLevel, true); + + float xy; + switch (gmet) + { + case kgmetLsb: xy = pslot->ClusterLsb(psstrmIn); break; + case kgmetRsb: xy = pslot->ClusterRsb(psstrmIn); break; + case kgmetBbTop: xy = pslot->ClusterBbTop(psstrmIn); break; + case kgmetBbBottom: xy = pslot->ClusterBbBottom(psstrmIn); break; + case kgmetBbLeft: xy = pslot->ClusterBbLeft(psstrmIn); break; + case kgmetBbRight: xy = pslot->ClusterBbRight(psstrmIn); break; + case kgmetBbHeight: xy = pslot->ClusterBbHeight(psstrmIn); break; + case kgmetBbWidth: xy = pslot->ClusterBbWidth(psstrmIn); break; +// case kgmetAdvHeight: xy = pslot->ClusterAdvHeight(psstrmIn); break; + case kgmetAdvWidth: xy = pslot->ClusterAdvWidth(psstrmIn); break; + default: + gAssert(false); + xy = 0; + } + mVal = ptman->LogToEmUnits(xy); + } + + vnStack.push_back(mVal); +} + +/*---------------------------------------------------------------------------------------------- + Push a feature value onto the stack. For rule actions, the slot of interest is the + current slot. + + @param nSlotRef - slot being examined + @param fInserting - whether current slot was inserted + @param nFeat - feature of interest + @param vnStack - stack to push onto + @param psstrmIn - input stream + @param psstrmOut - output stream +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushFeatValue(GrTableManager * ptman, int nSlotRef, bool fInserting, + int nFeat, std::vector<int> & vnStack, GrSlotStream * psstrmIn, GrSlotStream * psstrmOut) +{ + gAssert(!fInserting); + + GrSlotState * pslot = psstrmIn->RuleInputSlot(nSlotRef, psstrmOut, true); + if (pslot == NULL) + { + // Non-existent pre-context item. + vnStack.push_back(0); + return; + } + + int nVal = pslot->FeatureValue(nFeat); + vnStack.push_back(nVal); +} + +/*---------------------------------------------------------------------------------------------- + Push a processing-state value onto the stack. + + @param nFeat - feature of interest + @param vnStack - stack to push onto +----------------------------------------------------------------------------------------------*/ +void GrPass::DoPushProcState(GrTableManager * ptman, int nPState, std::vector<int> & vnStack) +{ + int nValue; + if (nPState == kpstatJustifyMode) + { + // Convert from internal modes to user modes. + int jmodi = ptman->InternalJustificationMode(); + switch (jmodi) + { + case kjmodiNormal: + case kjmodiCanShrink: + nValue = kjmoduNormal; + break; + case kjmodiMeasure: + nValue = kjmoduMeasure; + break; + case kjmodiJustify: + nValue = kjmoduJustify; + break; + default: + gAssert(false); + nValue = kjmoduNormal; + } + } + else if (nPState == kpstatJustifyLevel) + nValue = 1; // TODO: get justify level from ptman + vnStack.push_back(nValue); +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPseudoMap.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPseudoMap.h new file mode 100644 index 00000000000..c6fec210808 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrPseudoMap.h @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrPseudoMap.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrPseudoMap class. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_PSEUDOMAP_INCLUDED +#define GR_PSEUDOMAP_INCLUDED + +//:End Ignore + +namespace gr +{ + +/*---------------------------------------------------------------------------------------------- + A mapping between a Unicode value and a pseudo-glyph. + + Hungarian: psd +----------------------------------------------------------------------------------------------*/ + +class GrPseudoMap +{ +public: + unsigned int Unicode() { return m_nUnicode; } + gid16 PseudoGlyph() { return m_chwPseudo; } + + void SetUnicode(int n) { m_nUnicode = n; } + void SetPseudoGlyph(gid16 chw) { m_chwPseudo = chw; } + +protected: + // Instance variables: + unsigned int m_nUnicode; + gid16 m_chwPseudo; +}; + +} // namespace gr + +#endif // !GR_PSEUDOMAP_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotState.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotState.cpp new file mode 100644 index 00000000000..9879bc0516f --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotState.cpp @@ -0,0 +1,1540 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrSlotState.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + GrSlotState class implementation. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Initialize slots. +----------------------------------------------------------------------------------------------*/ +// standard for pass 0 slots +void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng, + GrFeatureValues fval, int ipass, int ichwSegOffset, int nUnicode) +{ + Assert(ipass == 0); + m_chwGlyphID = chw; + m_chwActual = kInvalidGlyph; + m_xysGlyphWidth = kNegInfFloat; + m_bStyleIndex = byte(fval.m_nStyleIndex); + u_intslot nullSlot; + nullSlot.pslot = NULL; + std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot); + std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot); + std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot); + u_intslot * pFeatBuf = PFeatureBuf(); + for (size_t i = 0; i < m_cnFeat; i++) + pFeatBuf[i].nValue = fval.m_rgnFValues[i]; + m_ipassFsmCol = -1; + m_colFsm = -1; + + m_ipassModified = ipass; + m_ichwSegOffset = ichwSegOffset; + m_nUnicode = nUnicode; + m_vpslotAssoc.clear(); + pgreng->InitSlot(this, nUnicode); + + switch (nUnicode) + { + case knLRM: m_spsl = kspslLRM; break; + case knRLM: m_spsl = kspslRLM; break; + case knLRO: m_spsl = kspslLRO; break; + case knRLO: m_spsl = kspslRLO; break; + case knLRE: m_spsl = kspslLRE; break; + case knRLE: m_spsl = kspslRLE; break; + case knPDF: m_spsl = kspslPDF; break; + default: + Assert(m_spsl == kspslNone); + m_spsl = kspslNone; + break; + } +} + +// line-break slots +void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng, + GrSlotState * pslotFeat, int ipass, int ichwSegOffset) +{ + m_chwGlyphID = chw; + m_chwActual = kInvalidGlyph; + m_xysGlyphWidth = kNegInfFloat; + u_intslot nullSlot; + nullSlot.pslot = NULL; + std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot); + std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot); + std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot); + CopyFeaturesFrom(pslotFeat); + m_ipassModified = ipass; + m_ichwSegOffset = ichwSegOffset; + m_nUnicode = -1; + m_vpslotAssoc.clear(); + pgreng->InitSlot(this); + // Caller is responsible for setting m_spsl. + m_ipassFsmCol = -1; + m_colFsm = -1; +} + +// for inserting new slots after pass 0 (under-pos and unicode are irrelevant) +void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng, + GrSlotState * pslotFeat, int ipass) +{ + m_chwGlyphID = chw; + m_chwActual = kInvalidGlyph; + m_xysGlyphWidth = kNegInfFloat; + u_intslot nullSlot; + nullSlot.pslot = NULL; + std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot); + std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot); + std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot); + CopyFeaturesFrom(pslotFeat); + m_ipassModified = ipass; + m_ichwSegOffset = kInvalid; + m_nUnicode = kInvalid; + m_vpslotAssoc.clear(); + pgreng->InitSlot(this); + m_spsl = kspslNone; + m_ipassFsmCol = -1; + m_colFsm = -1; +} + +/*---------------------------------------------------------------------------------------------- + The slot has been modified by the given pass and therefore is in a new state; + make a new SlotState initialized from the old one. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::InitializeFrom(GrSlotState * pslotOld, int ipass) +{ + CopyFrom(pslotOld, false); + + m_ipassModified = ipass; + m_pslotPrevState = pslotOld; + m_ichwSegOffset = kInvalid; + m_vpslotAssoc.clear(); + m_vpslotAssoc.push_back(pslotOld); + + m_dircProc = pslotOld->m_dircProc; + m_fDirProcessed = pslotOld->m_fDirProcessed; + + // Since we're going on to a new pass, no point in copying these: + m_ipassFsmCol = -1; + m_colFsm = -1; + + ////FixAttachmentTree(pslotOld); +} + +/*---------------------------------------------------------------------------------------------- + Copy the features and style information from the given slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CopyFeaturesFrom(GrSlotState * pslotSrc) +{ + m_bStyleIndex = pslotSrc->m_bStyleIndex; + Assert(m_cnFeat == pslotSrc->m_cnFeat); + std::copy(pslotSrc->PFeatureBuf(), pslotSrc->PFeatureBuf() + m_cnFeat, PFeatureBuf()); +} + +/*---------------------------------------------------------------------------------------------- + Copy the basic information. + Warning: the functions below will break if GrSlotState and subclasses are given virtual + methods. In that case, we will need to copy from the address of the first variable in + GrSlotAbstract. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CopyFrom(GrSlotState * pslot, bool fCopyEverything) +{ + GrSlotAbstract::CopyAbstractFrom(pslot); + std::copy(pslot->m_prgnVarLenBuf, pslot->m_prgnVarLenBuf + CExtraSpace(), m_prgnVarLenBuf); + + if (fCopyEverything) + { + Assert(false); + m_ipassModified = pslot->m_ipassModified; + m_pslotPrevState = pslot->m_pslotPrevState; + m_ichwSegOffset = pslot->m_ichwSegOffset; + m_colFsm = pslot->m_colFsm; + m_ipassFsmCol = pslot->m_ipassFsmCol; + // TODO: copy m_vpslotAssocs. + m_fNeutralAssocs = pslot->m_fNeutralAssocs; + } + + m_dislotRootFixed = pslot->m_dislotRootFixed; + + m_vdislotAttLeaves.resize(pslot->m_vdislotAttLeaves.size()); + for (size_t i = 0; i < pslot->m_vdislotAttLeaves.size(); i++) + m_vdislotAttLeaves[i] = pslot->m_vdislotAttLeaves[i]; + + m_islotPosPass = pslot->m_islotPosPass; + m_nUnicode = pslot->m_nUnicode; + m_dircProc = pslot->m_dircProc; + m_fDirProcessed = pslot->m_fDirProcessed; + m_cnUserDefn = pslot->m_cnUserDefn; + m_cnFeat = pslot->m_cnFeat; + m_bStyleIndex = pslot->m_bStyleIndex; + m_mAdvanceX = pslot->m_mAdvanceX; + m_mAdvanceY = pslot->m_mAdvanceY; + m_mShiftX = pslot->m_mShiftX; + m_mShiftY = pslot->m_mShiftY; + m_srAttachTo = pslot->m_srAttachTo; + m_nAttachLevel = pslot->m_nAttachLevel; + m_mAttachAtX = pslot->m_mAttachAtX; + m_mAttachAtY = pslot->m_mAttachAtY; + m_mAttachAtXOffset = pslot->m_mAttachAtXOffset; + m_mAttachAtYOffset = pslot->m_mAttachAtYOffset; + m_mAttachWithX = pslot->m_mAttachWithX; + m_mAttachWithY = pslot->m_mAttachWithY; + m_mAttachWithXOffset = pslot->m_mAttachWithXOffset; + m_mAttachWithYOffset = pslot->m_mAttachWithYOffset; + m_nAttachAtGpoint = pslot->m_nAttachAtGpoint; + m_nAttachWithGpoint = pslot->m_nAttachWithGpoint; + m_fAttachMod = pslot->m_fAttachMod; + m_fShiftMod = pslot->m_fShiftMod; + m_fIgnoreAdvance = pslot->m_fIgnoreAdvance; + + m_nCompositeLevel = kNegInfinity; // uncalculated, so don't need to copy the positions?? +} + +void GrSlotAbstract::CopyAbstractFrom(GrSlotState * pslot) +{ + u_intslot * pnBufSave = m_prgnVarLenBuf; + *this = *pslot; + m_prgnVarLenBuf = pnBufSave; + Assert(m_prgnVarLenBuf); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the output slot from the one use within the passes, with the basic information. + Warning: this function will break if GrSlotState and subclasses are given virtual + methods. In that case, we will need to copy from the address of the first variable in + GrSlotAbstract. +----------------------------------------------------------------------------------------------*/ +void GrSlotOutput::InitializeOutputFrom(GrSlotState * pslot) +{ + CopyAbstractFrom(pslot); + + // Copy just the component information, which is of length (m_cnCompPerLig * 2) + //std::copy(pslot->PCompRefBuf(), + // pslot->PCompRefBuf() + (m_cnCompPerLig * 2), + // this->PCompRefBufSlout()); +} + +/*---------------------------------------------------------------------------------------------- + Return the ID of the actual glyph that will be used for output and metrics. This is the + same for most glyphs, but will be different for pseudo-glyphs. +----------------------------------------------------------------------------------------------*/ +gid16 GrSlotAbstract::ActualGlyphForOutput(GrTableManager * ptman) +{ + if (m_chwActual == kInvalidGlyph) + m_chwActual = ptman->ActualGlyphForOutput(m_chwGlyphID); + return m_chwActual; +} +/*---------------------------------------------------------------------------------------------- + We are replacing the old slot with the recipient. Replace the pointers in any attachment + root or attached leaf slots. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void GrSlotState::FixAttachmentTree(GrSlotState * pslotOld) +{ +#if 0 + pslotOld->m_vpslotAttLeaves.CopyTo(m_vpslotAttLeaves); + for (int islot = 0; islot < m_vpslotAttLeaves.Size(); islot++) + { + Assert(m_vpslotAttLeaves[islot]->m_pslotAttRoot == pslotOld); + m_vpslotAttLeaves[islot]->m_pslotAttRoot = this; + } + + m_pslotAttRoot = pslotOld->m_pslotAttRoot; + if (m_pslotAttRoot) + { + for (int islot = 0; islot < m_pslotAttRoot->m_vpslotAttLeaves.Size(); islot++) + { + if (m_pslotAttRoot->m_vpslotAttLeaves[islot] == pslotOld) + { + m_pslotAttRoot->m_vpslotAttLeaves.Delete(islot); + m_pslotAttRoot->m_vpslotAttLeaves.Push(this); + return; + } + } + Assert(false); // didn't find old slot in the list + } +#endif // 0 +} + +/*---------------------------------------------------------------------------------------------- + Make sure all the values are cached that are needed to be copied to the output slots +----------------------------------------------------------------------------------------------*/ +void GrSlotState::EnsureCacheForOutput(GrTableManager * ptman) +{ + // Make sure the actual glyph ID is set. + gid16 gidActual = ActualGlyphForOutput(ptman); + + // Make sure the glyph metrics are stored. + Font * pfont = ptman->State()->GetFont(); + if (IsLineBreak(ptman->LBGlyphID())) + { + GetGlyphMetric(pfont, kgmetAscent, 0); + GetGlyphMetric(pfont, kgmetDescent, 0); + m_xysGlyphX = 0; + m_xysGlyphY = 0; + m_xysGlyphHeight = 0; + m_xysGlyphWidth = 0; + m_xysAdvX = 0; + m_xysAdvY = 0; + m_bIsSpace = true; + } + else + { + //IsSpace(ptman); // cache this flag--doing bb-top below will do it + + GetGlyphMetric(pfont, kgmetAscent, gidActual); + GetGlyphMetric(pfont, kgmetDescent, gidActual); + GetGlyphMetric(pfont, kgmetBbTop, gidActual); + // call above will also cache all the values below + //GetGlyphMetric(pfont, kgmetBbBottom, gidActual); + //GetGlyphMetric(pfont, kgmetBbLeft, gidActual); + //GetGlyphMetric(pfont, kgmetBbRight, gidActual); + //GetGlyphMetric(pfont, kgmetAdvWidth, gidActual); + //GetGlyphMetric(pfont, kgmetAdvHeight, gidActual); + } +} + +/*---------------------------------------------------------------------------------------------- + Set the associations for the slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::Associate(GrSlotState * pslot) +{ + m_vpslotAssoc.clear(); + m_vpslotAssoc.push_back(pslot); +} + +void GrSlotState::Associate(GrSlotState * pslotBefore, GrSlotState * pslotAfter) +{ + m_vpslotAssoc.clear(); + m_vpslotAssoc.push_back(pslotBefore); + m_vpslotAssoc.push_back(pslotAfter); +} + +void GrSlotState::Associate(std::vector<GrSlotState*> & vpslot) +{ + m_vpslotAssoc.clear(); + ///vpslot.CopyTo(m_vpslotAssoc); -- bug in CopyTo, so we do it ourselves: + for (size_t islot = 0; islot < vpslot.size(); ++islot) + { + m_vpslotAssoc.push_back(vpslot[islot]); + } + + // Set its character styles and features from the associated slot. + if (vpslot.size() > 0) // && !m_pslotPrevState + { + std::copy(m_vpslotAssoc[0]->PFeatureBuf(), + m_vpslotAssoc[0]->PFeatureBuf() + m_cnFeat, PFeatureBuf()); + } +} + +/*---------------------------------------------------------------------------------------------- + Clear the associations for the slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::ClearAssocs() +{ + m_vpslotAssoc.clear(); +} + +/*---------------------------------------------------------------------------------------------- + Return a list of (ie, add into the vector) all the underlying associations, relative + to the official beginning of the segment. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AllAssocs(std::vector<int> & vichw) +{ + if (PassModified() == 0) + { + Assert(m_ichwSegOffset != kInvalid); + vichw.push_back(m_ichwSegOffset); + } + else + { + for (size_t i = 0; i < m_vpslotAssoc.size(); ++i) + m_vpslotAssoc[i]->AllAssocs(vichw); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the underlying position of the before-association, relative to the official + beginning of the segment. May be -1, if this slot maps to a character in the previous + segment, or >= the length of the segment, if it maps to a character in the following + segment. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::BeforeAssoc() +{ + GrSlotState * pslot = this; + while (pslot->PassModified() > 0) + { + pslot = pslot->RawBeforeAssocSlot(); + if (pslot == NULL) + { + return kPosInfinity; + } + } + Assert(pslot->m_ichwSegOffset != kInvalid); + return pslot->m_ichwSegOffset; +} + +/*---------------------------------------------------------------------------------------------- + Return the underlying position of the after-association, relative to the official + beginning of the segment. May be -1, if this slot maps to a character in the previous + segment, or >= the length of the segment, if it maps to a character in the following + segment. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::AfterAssoc() +{ + GrSlotState * pslot = this; + while (pslot->PassModified() > 0) + { + pslot = pslot->RawAfterAssocSlot(); + if (pslot == NULL) + { + return kNegInfinity; + } + } + Assert(pslot->m_ichwSegOffset != kInvalid); + return pslot->m_ichwSegOffset; +} + +/*---------------------------------------------------------------------------------------------- + It is possible to get into a state where we are associated with an invalid state. + For instance, slot C may be associated with slots B1 and B2, but slot B1 is not associated + with any earlier slot, in which case slot C should remove the association with B1 and just + be associated with B2. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CleanUpAssocs() +{ + for (size_t i = 0; i < m_vpslotAssoc.size(); i++) + m_vpslotAssoc[i]->CleanUpAssocs(); + + GrSlotState * pslot; + + pslot = RawBeforeAssocSlot(); + while (pslot && BeforeAssoc() == kPosInfinity) + { + // The before association is bogus--delete it. + m_vpslotAssoc.erase(m_vpslotAssoc.begin()); + pslot = RawBeforeAssocSlot(); + } + + pslot = RawAfterAssocSlot(); + while (pslot && AfterAssoc() == kNegInfinity) + { + // The after association is bogus--delete it. + m_vpslotAssoc.pop_back(); + pslot = RawAfterAssocSlot(); + } +} + +/*---------------------------------------------------------------------------------------------- + Fill in the array of the given output slot with the underlying positions of the + ligature components, relative to the official beginning of the segment. + Positions may be < 0, if this slot maps to a character in the previous segment, + or >= the length of the segment, if it maps to a character in the following segment. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::SetComponentRefsFor(GrSlotOutput * pslout, int slatiArg) +{ + if (PassModified() > 0) + { + GrSlotState * pslot; + int slati; + if (HasComponents()) + { + for (int iComponent = 0; iComponent < m_cnCompPerLig; iComponent++) + { + pslot = CompRefSlot(iComponent); + slati = Slati(iComponent); + if (pslot) + { + Assert(slati != -1); + Assert(PassModified() >= pslot->PassModified()); + pslot->SetComponentRefsFor(pslout, slati); + } + } + } + else + { + // Follow the chain back through the passes. + for (int islot = 0; islot < AssocsSize(); islot++) + { + pslot = m_vpslotAssoc[islot]; + if (pslot) + { + Assert(PassModified() >= pslot->PassModified()); + // Passing slati here is definitely needed for our Arabic font: for instance when + // you type something like "mla", so the l is transformed into a temporary glyph + // before creating the ligature. However, this seems to have broken something, + // which I will probably find eventually. :-( + pslot->SetComponentRefsFor(pslout, slatiArg); + //pslot->SetComponentRefsFor(pslout, -1); + } + } + } + } + else + { + Assert(m_ichwSegOffset != kInvalid); + pslout->AddComponentReference(m_ichwSegOffset, slatiArg); + } +} + +/*---------------------------------------------------------------------------------------------- + Fill in the vector with the underlying positions of the + ligature components, relative to the official beginning of the segment. + Positions may be < 0, if this slot maps to a character in the previous segment, + or >= the length of the segment, if it maps to a character in the following segment. + ENHANCE: merge with method above. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AllComponentRefs(std::vector<int> & vichw, std::vector<int> & vicomp, + int iComponent) +{ + if (PassModified() > 0) + { + GrSlotState * pslot; + if (HasComponents()) + { + for (int iComponent = 0; iComponent < m_cnCompPerLig; iComponent++) + { + pslot = CompRefSlot(iComponent); + if (pslot) + { + Assert(PassModified() >= pslot->PassModified()); + pslot->AllComponentRefs(vichw, vicomp, iComponent); + } + } + } + else + { + for (int islot = 0; islot < AssocsSize(); islot++) + { + m_vpslotAssoc[islot]->AllComponentRefs(vichw, vicomp, iComponent); + } + } + } + else + { + Assert(m_ichwSegOffset != kInvalid); + vichw.push_back(m_ichwSegOffset); + vicomp.push_back(iComponent); + } +} + +/*---------------------------------------------------------------------------------------------- + Get the value of the component.???.ref attribute for the slot. + Note that 'i' is the local index for the component as defined for this glyph. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotState::CompRefSlot(int i) +{ + Assert(i < m_cnCompPerLig); + if (i < m_cnCompPerLig) + return CompRef(i); + else + return NULL; +} + +/*---------------------------------------------------------------------------------------------- + Set the value of the component.???.ref attribute for the slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::SetCompRefSlot(GrTableManager * ptman, int slati, GrSlotState * pslotComp) +{ + // Convert the global identifer for the component to the index for this glyph. + int icomp = ptman->ComponentIndexForGlyph(GlyphID(), slati); + Assert(icomp != -1); + if (icomp == -1) + // Component not defined for this glyph--ignore. + return; + Assert(icomp < m_cnCompPerLig); + + if (m_fHasComponents == false) + { + // None have been set yet; initialize them. + for (int iLoop = 0; iLoop < m_cnCompPerLig; iLoop++) + { + SetCompRef(iLoop, NULL); + SetSlati(iLoop, -1); + } + } + + m_fHasComponents = true; + int iLoop; + for (iLoop = 0; iLoop < m_cnCompPerLig; iLoop++) + { + if (Slati(iLoop) == slati) + break; + if (Slati(iLoop) == -1) + break; + } + //if (icomp < m_cnCompPerLig) + if (iLoop < m_cnCompPerLig) + { + //SetCompRef(icomp, pslotComp); + SetCompRef(iLoop, pslotComp); + SetSlati(iLoop, slati); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the underlying position for the slot, relative to the official beginning of the + segment. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::SegOffset() +{ + if (m_ichwSegOffset == kInvalid) + { + Assert(m_pslotPrevState); + Assert(m_ipassModified > 0); + return m_pslotPrevState->SegOffset(); + } + else + { + Assert(m_pslotPrevState == NULL); + Assert(m_ipassModified == 0); + return m_ichwSegOffset; + } +} + +/*---------------------------------------------------------------------------------------------- + Return the value of the glyph attribute (in design units, if this is a measurement). +----------------------------------------------------------------------------------------------*/ +int GrSlotState::GlyphAttrValueEmUnits(GrTableManager * ptman, int nAttrID) +{ + return ptman->GlyphAttrValue(m_chwGlyphID, nAttrID); +} + +/*---------------------------------------------------------------------------------------------- + Return the value of the glyph metric, in design coordinates (ie, based on the font's + em-square). +----------------------------------------------------------------------------------------------*/ +int GrSlotState::GlyphMetricEmUnits(GrTableManager * ptman, int nMetricID) +{ + int mValue; + if (ptman->State()->GetFont()) + { + // Get the actual metric, possibly adjusted for hinting, then convert back to + // design units. + float xysValue = GlyphMetricLogUnits(ptman, nMetricID); + mValue = ptman->LogToEmUnits(xysValue); + } + else + // Ask the font directly. + ////mValue = ??? + mValue = 0; + + return mValue; +} + +/*---------------------------------------------------------------------------------------------- + Return the value of the glyph attribute, converted to source device coordinates. + The attribute is assumed to be one whose value is a measurement. +----------------------------------------------------------------------------------------------*/ +float GrSlotState::GlyphAttrValueLogUnits(GrTableManager * ptman, int nAttrID) +{ + int mValue = GlyphAttrValueEmUnits(ptman, nAttrID); + float xysRet = ptman->EmToLogUnits(mValue); + return xysRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the value of the glyph metric, in the source device's logical units. +----------------------------------------------------------------------------------------------*/ +float GrSlotState::GlyphMetricLogUnits(GrTableManager * ptman, int nMetricID) +{ +#ifdef OLD_TEST_STUFF + if (ptman->GlyphTable() == NULL) + return 0; +#endif // OLD_TEST_STUFF + +#ifdef _DEBUG + if (IsLineBreak(ptman->LBGlyphID())) + { + Warn("Getting metrics of line-break slot"); + } +#endif // _DEBUG + if (IsLineBreak(ptman->LBGlyphID())) + { + return 0; + } + + return GetGlyphMetric(ptman->State()->GetFont(), nMetricID, + ActualGlyphForOutput(ptman)); +} + + +float GrSlotOutput::GlyphMetricLogUnits(Font * pfont, int nMetricID) +{ + Assert(m_chwActual != kInvalidGlyph); + if (m_chwActual == kInvalidGlyph) + return 0; + + return GetGlyphMetric(pfont, nMetricID, m_chwActual); +} + +//float GrSlotOutput::GlyphMetricLogUnits(int gmet) -- obsolete: no longer caching these values +//{ +// // When the font is not passed as an argument, the values better be cached! +// +// switch (gmet) +// { // There may be off by one errors below, depending on what width and height mean +// case kgmetLsb: +// return m_xysGlyphX; +// case kgmetRsb: +// return (m_xysAdvX - m_xysGlyphX - m_xysGlyphWidth); +// case kgmetBbTop: +// return m_xysGlyphY; +// case kgmetBbBottom: +// return (m_xysGlyphY - m_xysGlyphHeight); +// case kgmetBbLeft: +// return m_xysGlyphX; +// case kgmetBbRight: +// return (m_xysGlyphX + m_xysGlyphWidth); +// case kgmetBbHeight: +// return m_xysGlyphHeight; +// case kgmetBbWidth: +// return m_xysGlyphWidth; +// case kgmetAdvWidth: +// return m_xysAdvX; +// case kgmetAdvHeight: +// return m_xysAdvY; +// default: +// Warn("GetGlyphMetric was asked for an illegal metric."); +// }; +// +// return 0; +//} + +float GrSlotState::GetGlyphMetric(Font * pfont, int nMetricID, gid16 chwGlyphID) +{ + GlyphMetric gmet = GlyphMetric(nMetricID); + switch (gmet) + { + case kgmetAscent: + case kgmetDescent: + if (m_xysFontAscent == kNegInfFloat) + { + m_xysFontAscent = GrSlotAbstract::GetGlyphMetric(pfont, kgmetAscent, chwGlyphID); + m_xysFontDescent = GrSlotAbstract::GetGlyphMetric(pfont, kgmetDescent, chwGlyphID); + } + break; + default: + if (m_xysGlyphWidth == kNegInfFloat) + { + GetGlyphMetricAux(pfont, chwGlyphID, m_xysGlyphX, m_xysGlyphY, m_xysGlyphWidth, + m_xysGlyphHeight, m_xysAdvX, m_xysAdvY, m_bIsSpace); + } + break; + } + + switch (gmet) + { // There may be off-by-one errors below, depending on what width and height mean + case kgmetAscent: + return m_xysFontAscent; + case kgmetDescent: + return m_xysFontDescent; + case kgmetLsb: + return m_xysGlyphX; + case kgmetRsb: + return (m_xysAdvX - m_xysGlyphX - m_xysGlyphWidth); + case kgmetBbTop: + return m_xysGlyphY; + case kgmetBbBottom: + return (m_xysGlyphY - m_xysGlyphHeight); + case kgmetBbLeft: + return m_xysGlyphX; + case kgmetBbRight: + return (m_xysGlyphX + m_xysGlyphWidth); + case kgmetBbHeight: + return m_xysGlyphHeight; + case kgmetBbWidth: + return m_xysGlyphWidth; + case kgmetAdvWidth: + return m_xysAdvX; + case kgmetAdvHeight: + return m_xysAdvY; + default: + Warn("GetGlyphMetric was asked for an illegal metric."); + }; + + return 0; +} + +float GrSlotAbstract::GetGlyphMetric(Font * pfont, int nMetricID, gid16 chwGlyphID) +{ + GlyphMetric gmet = GlyphMetric(nMetricID); + + float yAscent, yDescent; + + if (kgmetAscent == gmet) + { + //if (m_xysFontAscent != -1) + // return m_xysFontAscent; + pfont->getFontMetrics(&yAscent); + //m_xysFontAscent = xysRet; + //if (pfont) + // m_xysFontAscent = yAscent; + return yAscent; + } + + if (kgmetDescent == gmet) + { + //if (m_xysFontDescent != -1) + // return m_xysFontDescent; + pfont->getFontMetrics(NULL, &yDescent); + //m_xysFontDescent = xysRet; + //if (pfont) + // m_xysFontDescent = yDescent; + return yDescent; + } + + float xysGlyphX, xysGlyphY, xysGlyphWidth, xysGlyphHeight, xysAdvX, xysAdvY; + GetGlyphMetricAux(pfont, chwGlyphID, xysGlyphX, xysGlyphY, xysGlyphWidth, + xysGlyphHeight, xysAdvX, xysAdvY, m_bIsSpace); + + switch (gmet) + { // There may be off-by-one errors below, depending on what width and height mean + case kgmetLsb: + return xysGlyphX; + case kgmetRsb: + return (xysAdvX - xysGlyphX - xysGlyphWidth); + case kgmetBbTop: + return xysGlyphY; + case kgmetBbBottom: + return (xysGlyphY - xysGlyphHeight); + case kgmetBbLeft: + return xysGlyphX; + case kgmetBbRight: + return (xysGlyphX + xysGlyphWidth); + case kgmetBbHeight: + return xysGlyphHeight; + case kgmetBbWidth: + return xysGlyphWidth; + case kgmetAdvWidth: + return xysAdvX; + case kgmetAdvHeight: + return xysAdvY; + default: + Warn("GetGlyphMetric was asked for an illegal metric."); + }; + + return 0; +} + +void GrSlotAbstract::GetGlyphMetricAux(Font * pfont, gid16 chwGlyphID, + float & xysGlyphX, float & xysGlyphY, + float & xysGlyphWidth, float & xysGlyphHeight, float & xysAdvX, float & xysAdvY, sdata8 & bIsSpace) +{ + gr::Point ptAdvances; + gr::Rect rectBb; + pfont->getGlyphMetrics(chwGlyphID, rectBb, ptAdvances); + + xysGlyphX = rectBb.left; + xysGlyphY = rectBb.top; + xysGlyphWidth = (rectBb.right - rectBb.left); + xysGlyphHeight = (rectBb.top - rectBb.bottom); + xysAdvX = ptAdvances.x; + xysAdvY = ptAdvances.y; + + bIsSpace = (0 == xysGlyphX && 0 == xysGlyphY); // should agree with test done in IsSpace() below + + if (bIsSpace == 1) + { + // White space glyph - only case where nGlyphX == nGlyphY == 0 + // nGlyphWidth & nGlyphHeight are always set to 16 for unknown reasons, so correct. + xysGlyphWidth = xysGlyphHeight = 0; + } +} + +/*---------------------------------------------------------------------------------------------- + Test the glyph id to see if it is a white space glyph. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::IsSpace(GrTableManager * ptman) +{ + gid16 gidActual = ActualGlyphForOutput(ptman); + + ////if (m_xysGlyphWidth == -1) + ////{ + //// res = ptman->State()->GraphicsObject()->GetGlyphMetrics(gidActual, + //// &m_xysGlyphWidth, &m_xysGlyphHeight, + //// &m_xysGlyphX, &m_xysGlyphY, &m_xysAdvX, &m_xysAdvY); + //// if (ResultFailed(res)) + //// { + //// WARN(res); + //// return 2; // will test as true but can be distinguished by separate value + //// } + + //// gr::Point ptAdvances; + //// gr::Rect rectBb; + //// ptman->State()->Font()->getGlyphMetrics(gidActual, rectBb, ptAdvances); + ////} + + if (m_bIsSpace == -1) + GetGlyphMetric(ptman->State()->GetFont(), kgmetBbLeft, gidActual); + // One call is enough to cache the information. + //GetGlyphMetric(ptman->State()->Font(), kgmetBbBottom, gidActual); + + // should agree with test done in GetGlyphMetric() above + //////m_bIsSpace = (0 == m_xysGlyphX && 0 == m_xysGlyphY); + + Assert(m_bIsSpace == 0 || m_bIsSpace == 1); + + return m_bIsSpace; +} + +bool GrSlotOutput::IsSpace() +{ + Assert(m_bIsSpace == 0 || m_bIsSpace == 1); + return m_bIsSpace; +} + +/*---------------------------------------------------------------------------------------------- + If any of the attach attributes have been modified, fix things up. Set up the attachment + tree, and set any needed default positions. Zap the cached positions of any following + glyphs in the stream. + + @param psstrm - the stream in which the modifications were made + @param islotThis - the index of the attached (leaf) slot in the stream; + -1 if we don't know +----------------------------------------------------------------------------------------------*/ +void GrSlotState::HandleModifiedPosition(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis) +{ + if (!m_fAttachMod && !m_fShiftMod) + return; + + if (islotThis == -1) + { + for (int islot = 0; islot < psstrmOut->WritePos(); islot++) + { + if (psstrmOut->SlotAt(islot) == this) + { + islotThis = islot; + break; + } + } + Assert(islotThis > -1); + } + + if (m_fAttachMod) + { + // Attachments. + + // Note that it doesn't make sense to attach a slot to itself, so if the value of + // the attach.to attribute is 0, that means clear the attachment. + GrSlotState * pslotNewRoot = AttachRoot(psstrmOut); + + AttachToRoot(ptman, psstrmOut, pslotNewRoot); + + if (pslotNewRoot) + { + // If this is an attachment with no positions, attach the two glyphs side by side + // in the appropriate order. + bool fRtl = ((pslotNewRoot->PostBidiDirLevel(ptman) % 2) != 0); + if ((fRtl && m_srAttachTo < 0) || (!fRtl && m_srAttachTo > 0)) + { + // The root is on the right, the leaf is on the left. + if (m_mAttachAtX == kNotYetSet && m_nAttachAtGpoint == kNotYetSet) + m_mAttachAtX = 0; + if (m_mAttachWithX == kNotYetSet && m_nAttachWithGpoint == kNotYetSet) + m_mAttachWithX = short(AdvanceX(ptman)); + } + else + { + // The root is on the left, the leaf is on the right. + if (m_mAttachAtX == kNotYetSet && m_nAttachAtGpoint == kNotYetSet) + m_mAttachAtX = short(pslotNewRoot->AdvanceX(ptman)); + if (m_mAttachWithX == kNotYetSet && m_nAttachWithGpoint == kNotYetSet) + m_mAttachWithX = 0; + } + } + } + else + { + // Shifting, or changing the advance width. + Assert(m_fShiftMod); + EnsureLocalAttachmentTree(ptman, psstrmIn, psstrmOut, islotThis); + ZapMetricsAndPositionDownToBase(psstrmOut); + ZapMetricsOfLeaves(psstrmOut); + } + + if (psstrmOut->m_ipass == ptman->NumberOfPasses() - 1) + ptman->InitPosCache(); // cached position is most likely no longer valid + + // Invalidate the positions of this and any following glyphs. + for (int islot = islotThis + 1; islot < psstrmOut->WritePos(); islot++) + psstrmOut->SlotAt(islot)->ZapPosition(); + + m_fAttachMod = false; + m_fShiftMod = false; +} + +/*---------------------------------------------------------------------------------------------- + The shift attribute of a slot has been modified. Make sure any slots that are part of the + same attachment cluster are local to this stream. The reason for this is so that the + position calculations stay consistent within the stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::EnsureLocalAttachmentTree(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis) +{ + if (m_dislotRootFixed) + { + GrSlotState * pslotRoot = SlotAtOffset(psstrmOut, m_dislotRootFixed); + psstrmOut->EnsureLocalCopy(ptman, pslotRoot, psstrmIn); + pslotRoot = SlotAtOffset(psstrmOut, m_dislotRootFixed); // get the new one! + pslotRoot->EnsureLocalAttachmentTree(ptman, psstrmIn, psstrmOut, islotThis + m_dislotRootFixed); + } + for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++) + { + GrSlotState * pslotLeaf = SlotAtOffset(psstrmOut, m_vdislotAttLeaves[islot]); + psstrmOut->EnsureLocalCopy(ptman, pslotLeaf, psstrmIn); + } +} + +/*---------------------------------------------------------------------------------------------- + The recipient slot is being attached to the argument slot. + NOTE: the caller is responsible to zap the cached positions of following glyphs + in the stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AttachToRoot(GrTableManager * ptman, GrSlotStream * psstrm, + GrSlotState * pslotNewRoot) +{ + GrSlotState * pslotOldRoot = (m_dislotRootFixed == 0) ? + NULL : + SlotAtOffset(psstrm, m_dislotRootFixed); + + if (pslotOldRoot) + { + if (pslotOldRoot != pslotNewRoot) + pslotOldRoot->RemoveLeaf(m_dislotRootFixed); + pslotOldRoot->ZapMetricsAndPositionDownToBase(psstrm); + pslotOldRoot->ZapMetricsOfLeaves(psstrm); + } + + ZapCompositeMetrics(); + + if (pslotNewRoot && pslotNewRoot != pslotOldRoot) + { + pslotNewRoot->AddLeaf(m_srAttachTo); + pslotNewRoot->ZapMetricsAndPositionDownToBase(psstrm); + pslotNewRoot->ZapMetricsOfLeaves(psstrm); + } + + m_dislotRootFixed = m_srAttachTo; +} + +/*---------------------------------------------------------------------------------------------- + Return the absolute position of the glyph, relative to the start of the segment, + in font design units. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::Position(GrTableManager * ptman, + GrSlotStream * psstrmOut, int * pmXPos, int * pmYPos) +{ + Assert(!m_fAttachMod); // the attachment tree should be set up + Assert(!m_fShiftMod); + + float xsWidth, xsVisWidth; + if (m_xsPositionX == kNegInfFloat || m_ysPositionY == kNegInfFloat) + ptman->CalcPositionsUpTo(psstrmOut->m_ipass, this, &xsWidth, &xsVisWidth); + + *pmXPos = ptman->LogToEmUnits(m_xsPositionX); + *pmYPos = ptman->LogToEmUnits(m_ysPositionY); +} + +/*---------------------------------------------------------------------------------------------- + Recalculate the metrics for this slot, which has attachments on it (or possibly + did in the past). +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AdjustRootMetrics(GrTableManager * ptman, GrSlotStream * psstrm) +{ + Assert(m_dislotRootFixed == m_srAttachTo); + GrSlotState * pslotRoot = AttachRoot(psstrm); + CalcRootMetrics(ptman, psstrm, kPosInfinity); + if (pslotRoot) + pslotRoot->AdjustRootMetrics(ptman, psstrm); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the composite metrics for this slot. + + @param psstrm - stream for which we are calculating it + @param nLevel - attachment level we are asking for; kPosInifinity means all levels + @param fThorough - true: do a thorough recalculation; false: don't recalculate + metrics for leaves (are they assumed to be accurate???) + --currently not used +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CalcCompositeMetrics(GrTableManager * ptman, GrSlotStream * psstrm, + int nLevel, bool fThorough) +{ + if (m_nCompositeLevel == nLevel) + return; + + if (fThorough) + { + Assert(m_dislotRootFixed == m_srAttachTo); + GrSlotState * pslotRoot = AttachRoot(psstrm); + + InitMetrics(ptman, pslotRoot); + + for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++) + { + GrSlotState * pslotLeaf = SlotAtOffset(psstrm, m_vdislotAttLeaves[islot]); + if (pslotLeaf->AttachLevel() <= nLevel) + pslotLeaf->CalcCompositeMetrics(ptman, psstrm, nLevel, fThorough); + else + // this slot will be ignored in the composite metrics + pslotLeaf->ZapRootMetrics(); + } + CalcRootMetrics(ptman, psstrm, nLevel); + + m_nCompositeLevel = nLevel; + } + else + { + Assert(false); // for now + + // Don't bother with the leaves. + InitRootMetrics(ptman); + } +} + +/*---------------------------------------------------------------------------------------------- + Calculate the metrics for this node and all its leaf nodes. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::CalcRootMetrics(GrTableManager * ptman, GrSlotStream * psstrm, int nLevel) +{ + for (size_t idislot = 0; idislot < m_vdislotAttLeaves.size(); idislot++) + { + GrSlotState * pslot = SlotAtOffset(psstrm, m_vdislotAttLeaves[idislot]); + if (pslot->AttachLevel() > nLevel) + continue; + + m_xsClusterXOffset = min(m_xsClusterXOffset, pslot->m_xsClusterXOffset); + if (!pslot->m_fIgnoreAdvance) + { + m_xsClusterAdv = max( + m_xsClusterAdv, + pslot->m_xsClusterAdv + m_xsRootShiftX); + } + m_xsClusterBbLeft = min(m_xsClusterBbLeft, pslot->m_xsClusterBbLeft); + m_xsClusterBbRight = max(m_xsClusterBbRight, pslot->m_xsClusterBbRight); + m_ysClusterBbTop = max(m_ysClusterBbTop, pslot->m_ysClusterBbTop); + m_ysClusterBbBottom = min(m_ysClusterBbBottom, pslot->m_ysClusterBbBottom); + } +} + +/*---------------------------------------------------------------------------------------------- + Reset the cluster metrics of this slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::InitMetrics(GrTableManager * ptman, GrSlotState * pslotRoot) +{ + InitLeafMetrics(ptman, pslotRoot); + InitRootMetrics(ptman); +} + +/*---------------------------------------------------------------------------------------------- + Initialize the variables that store the offsets of just this node (ignoring any of its + leaves) relative to the cluster base. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::InitLeafMetrics(GrTableManager * ptman, GrSlotState * pslotRoot) +{ + float xsShiftX = ptman->EmToLogUnits(ShiftX()); + float ysShiftY = ptman->EmToLogUnits(ShiftY()); + + if (ptman->RightToLeft()) + xsShiftX *= -1; + + if (IsBase()) + { + // The x-value below has to be zero because the shift is incorporated into + // m_xsRootShiftX. (m_ysRootShiftY, on the other hand, isn't used anywhere.) + m_xsOffsetX = 0; + m_ysOffsetY = ysShiftY; + + m_xsRootShiftX = xsShiftX; + m_ysRootShiftY = ysShiftY; + Assert(!IsLineBreak(ptman->LBGlyphID()) + || (m_xsRootShiftX == 0 && m_ysRootShiftY == 0)); + return; + } + + Assert(!IsLineBreak(ptman->LBGlyphID())); + + // Hint-adjusted logical coordinates equivalent to attach.at and attach.with attributes. + // If attach.at or attach.with attributes were unset, the defaults for a side-by-side + // attachment should have been supplied in HandleModifiedPosition(). + float xsAttAtX, ysAttAtY, xsAttWithX, ysAttWithY; + AttachLogUnits(ptman, pslotRoot, &xsAttAtX, &ysAttAtY, &xsAttWithX, &ysAttWithY); + + m_xsOffsetX = xsAttAtX - xsAttWithX; + m_xsOffsetX += pslotRoot->m_xsOffsetX; + m_xsOffsetX += xsShiftX; + + m_ysOffsetY = ysAttAtY - ysAttWithY; + m_ysOffsetY += pslotRoot->m_ysOffsetY; + m_ysOffsetY += ysShiftY; + + // Cumulative effect of shifts on this and all base nodes: + m_xsRootShiftX = pslotRoot->m_xsRootShiftX + xsShiftX; + m_ysRootShiftY = pslotRoot->m_ysRootShiftY + ysShiftY; +} + +/*---------------------------------------------------------------------------------------------- + Initialize the variables that store the offsets of this node taking into account its + leaves; these are relative to the cluster base. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::InitRootMetrics(GrTableManager * ptman) +{ + if (IsLineBreak(ptman->LBGlyphID())) + { + m_fIgnoreAdvance = true; + m_xsClusterXOffset = 0; + m_xsClusterAdv = 0; + m_xsClusterBbLeft = 0; + m_xsClusterBbRight = 0; + m_ysClusterBbTop = 0; + m_ysClusterBbBottom = 0; + return; + } + + float xsAdvanceX = ptman->EmToLogUnits(AdvanceX(ptman)); + + // If the glyph's advance width is zero, then we NEVER want it to have any affect, + // even if the glyph is attached way out to the right of its base's advance. + m_fIgnoreAdvance = (xsAdvanceX == 0); + + float xsBbLeft = GlyphMetricLogUnits(ptman, kgmetBbLeft); + float xsBbRight = GlyphMetricLogUnits(ptman, kgmetBbRight); + float ysBbTop = GlyphMetricLogUnits(ptman, kgmetBbTop); + float ysBbBottom = GlyphMetricLogUnits(ptman, kgmetBbBottom); + + // Any shifts should be ignored for the sake of calculating actual position or width, + // hence we subtract the cumulative effect of the shifts. + m_xsClusterXOffset = m_xsOffsetX - m_xsRootShiftX; + m_xsClusterAdv = m_xsOffsetX + xsAdvanceX - m_xsRootShiftX; + + m_xsClusterBbLeft = m_xsOffsetX + xsBbLeft; + m_xsClusterBbRight = m_xsOffsetX + xsBbRight; + m_ysClusterBbTop = m_ysOffsetY + ysBbTop; + m_ysClusterBbBottom = m_ysOffsetY + ysBbBottom; +} + +/*---------------------------------------------------------------------------------------------- + X-offset of a single glyph relative to the previous advance position. +----------------------------------------------------------------------------------------------*/ +float GrSlotState::GlyphXOffset(GrSlotStream * psstrm, float fakeItalicRatio) +{ + float xsRet = Base(psstrm)->ClusterRootOffset() + m_xsOffsetX; + + // fake an italic slant + xsRet += m_ysOffsetY * fakeItalicRatio; + + return xsRet; +} + +/*---------------------------------------------------------------------------------------------- + Y-offsets of a single glyph relative to the previous advance position. +----------------------------------------------------------------------------------------------*/ +float GrSlotState::GlyphYOffset(GrSlotStream * psstrm) +{ + return m_ysOffsetY; +} + +/*---------------------------------------------------------------------------------------------- + Return the offset of the last leaf of the cluster, relative to this slot, which is + the base. Return kNegInfinity if there are not enough slots in the stream to tell. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::LastLeafOffset(GrSlotStream * psstrm) +{ + int islotRet = 0; + for (size_t idislot = 0; idislot < m_vdislotAttLeaves.size(); idislot++) + { + int dislot = m_vdislotAttLeaves[idislot]; + Assert(dislot != 0); + if (!psstrm->HasSlotAtPosPassIndex(PosPassIndex() + dislot)) + return kNegInfinity; + GrSlotState * pslotLeaf = SlotAtOffset(psstrm, dislot); + int islotTmp = pslotLeaf->LastLeafOffset(psstrm); + if (islotTmp == kNegInfinity) + return kNegInfinity; + islotRet = max(islotRet, (dislot + islotTmp)); + } + return islotRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the attach positions in the device context's logical units, + adjusted for hinting if possible. + + Note that m_nAttachAt/WithGpoint = 0 is always an invalid value (resulting from a + glyph attribute that was defined in terms of x/y coordinates that didn't map to an + actual on-curve point). Therefore the x/y coordinates should be used to do the + attachment. In the case where we actually want point #0 on the curve, + m_nAttachAt/WithGpoint will have the special value 'kGpointZero' (the glyph attribute + is defined this way). +----------------------------------------------------------------------------------------------*/ +void GrSlotState::AttachLogUnits(GrTableManager * ptman, GrSlotState * pslotRoot, + float * pxsAttAtX, float * pysAttAtY, + float * pxsAttWithX, float * pysAttWithY) +{ + if (m_nAttachAtGpoint == kNotYetSet || m_nAttachAtGpoint == 0) + { + // Use x- and y-coordinates; no adjustment for hinting is done. + int mX = m_mAttachAtX + m_mAttachAtXOffset; + int mY = m_mAttachAtY + m_mAttachAtYOffset; + + *pxsAttAtX = ptman->EmToLogUnits(mX); + *pysAttAtY = ptman->EmToLogUnits(mY); + } + else + { + // Look up the actual on-curve point. + int nGpoint = m_nAttachAtGpoint; + if (nGpoint == kGpointZero) + nGpoint = 0; + bool fImpl = ptman->GPointToXY(pslotRoot->GlyphID(), nGpoint, pxsAttAtX, pysAttAtY); + + // Debuggers: + //int mXTmpAt = m_mAttachAtX + m_mAttachAtXOffset; + //int mYTmpAt = m_mAttachAtY + m_mAttachAtYOffset; + //int xsAttAtXTmp = ptman->EmToLogUnits(mXTmpAt); + //int ysAttAtYTmp = ptman->EmToLogUnits(mYTmpAt); + //fImpl = false; + + if (!fImpl) + { + // Fall back to using x- and y-coordinates; no adjustment for hinting. + int mX = m_mAttachAtX + m_mAttachAtXOffset; + int mY = m_mAttachAtY + m_mAttachAtYOffset; + *pxsAttAtX = ptman->EmToLogUnits(mX); + *pysAttAtY = ptman->EmToLogUnits(mY); + } + else + { + // Adjust by offsets. + *pxsAttAtX += ptman->EmToLogUnits(m_mAttachAtXOffset); + *pysAttAtY += ptman->EmToLogUnits(m_mAttachAtYOffset); + } + } + + if (m_nAttachWithGpoint == kNotYetSet || m_nAttachWithGpoint == 0) + { + // Use x- and y-coordinates; no adjustment for hinting is done. + int mX = m_mAttachWithX + m_mAttachWithXOffset; + int mY = m_mAttachWithY + m_mAttachWithYOffset; + + *pxsAttWithX = ptman->EmToLogUnits(mX); + *pysAttWithY = ptman->EmToLogUnits(mY); + } + else + { + // Look up the actual on-curve point. + int nGpoint = m_nAttachWithGpoint; + if (nGpoint == kGpointZero) + nGpoint = 0; + bool fImpl = ptman->GPointToXY(m_chwGlyphID, nGpoint, pxsAttWithX, pysAttWithY); + + // Debuggers: + //int mXTmpWith = m_mAttachWithX + m_mAttachWithXOffset; + //int mYTmpWith = m_mAttachWithY + m_mAttachWithYOffset; + //int xsAttWithXTmp = ptman->EmToLogUnits(mXTmpWith); + //int ysAttWithYTmp = ptman->EmToLogUnits(mYTmpWith); + //fImpl = false; + + if (!fImpl) + { + // Fall back to using x- and y-coordinates; no adjustment for hinting. + int mX = m_mAttachWithX + m_mAttachWithXOffset; + int mY = m_mAttachWithY + m_mAttachWithYOffset; + *pxsAttWithX = ptman->EmToLogUnits(mX); + *pysAttWithY = ptman->EmToLogUnits(mY); + } + else + { + // Adjust by offsets. + *pxsAttWithX += ptman->EmToLogUnits(m_mAttachWithXOffset); + *pysAttWithY += ptman->EmToLogUnits(m_mAttachWithYOffset); + } + } +} + +/*---------------------------------------------------------------------------------------------- + Return the slot that is 'dislot' slots away from this slot in the given stream. + Only valid for streams that are the input to or output of positioning passes. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotState::SlotAtOffset(GrSlotStream * psstrm, int dislot) +{ + Assert(psstrm->m_fUsedByPosPass); + return psstrm->SlotAtPosPassIndex(PosPassIndex() + dislot); +} + +/*---------------------------------------------------------------------------------------------- + If the direction level has not been calculated at this point, assume it is the + top direction. This should only happen if there was no bidi pass to set it. + This method should only be called by the positioning passes; it assumes any bidi + pass has been run. +----------------------------------------------------------------------------------------------*/ +int GrSlotState::PostBidiDirLevel(GrTableManager * ptman) +{ + if (m_nDirLevel == -1) + { + Assert(!ptman->HasBidiPass()); + return ptman->TopDirectionLevel(); + } + return m_nDirLevel; +} + +/*---------------------------------------------------------------------------------------------- + Return true if this glyph represents a LRM code. +----------------------------------------------------------------------------------------------*/ +bool GrSlotState::IsLrm() +{ + if (PassModified() == 0) + return (m_nUnicode == knLRM); + return m_pslotPrevState->IsLrm(); +} + +/*---------------------------------------------------------------------------------------------- + Return true if this glyph represents a RLM code. +----------------------------------------------------------------------------------------------*/ +bool GrSlotState::IsRlm() +{ + if (PassModified() == 0) + return (m_nUnicode == knRLM); + return m_pslotPrevState->IsRlm(); +} + + +/*---------------------------------------------------------------------------------------------- + Used by GlyphInfo +----------------------------------------------------------------------------------------------*/ +//float GrSlotOutput::AdvanceX(Segment * pseg) +//{ +// return pseg->EmToLogUnits(m_mAdvanceX); +//} +//float GrSlotOutput::AdvanceY(Segment * pseg) +//{ +// return pseg->EmToLogUnits(m_mAdvanceY); +//} +float GrSlotOutput::MaxStretch(Segment * pseg, int level) +{ + Assert(level == 0); + return (level == 0) ? pseg->EmToLogUnits(m_mJStretch0) : 0; +} +float GrSlotOutput::MaxShrink(Segment * pseg, int level) +{ + Assert(level == 0); + return (level == 0) ? pseg->EmToLogUnits(m_mJShrink0) : 0; +} +float GrSlotOutput::StretchStep(Segment * pseg, int level) +{ + Assert(level == 0); + return (level == 0) ? pseg->EmToLogUnits(m_mJStep0) : 0; +} +int GrSlotOutput::JustWeight(int level) +{ + Assert(level == 0); + return (level == 0) ? m_nJWeight0 : 0; +} +float GrSlotOutput::JustWidth(Segment * pseg, int level) +{ + Assert(level == 0); + return (level == 0) ? pseg->EmToLogUnits(m_mJWidth0) : 0; +} +float GrSlotOutput::MeasureSolLogUnits(Segment * pseg) +{ + return pseg->EmToLogUnits(m_mMeasureSol); +} +float GrSlotOutput::MeasureEolLogUnits(Segment * pseg) +{ + return pseg->EmToLogUnits(m_mMeasureEol); +} + +/*---------------------------------------------------------------------------------------------- + Make a copy of the GrSlotOutput. This is used in making an identical copy of the segment. + Warning: this function will break if GrSlotState and subclasses are given virtual + methods. In that case, we will need to copy from the address of the first variable in + GrSlotAbstract. +----------------------------------------------------------------------------------------------*/ +void GrSlotOutput::ExactCopyFrom(GrSlotOutput * pslout, u_intslot * pnVarLenBuf, int cnExtraPerSlot) +{ + // The chunk of the object from GrSlotAbstract can be copied exactly, + // except for the variable-length buffer. + *this = *pslout; + m_prgnVarLenBuf = pnVarLenBuf; + std::copy(pslout->m_prgnVarLenBuf, pslout->m_prgnVarLenBuf + cnExtraPerSlot, + m_prgnVarLenBuf); + + // Now copy the stuff specific to GrSlotOutput. + m_ichwBeforeAssoc = pslout->m_ichwBeforeAssoc; + m_ichwAfterAssoc = pslout->m_ichwAfterAssoc; + m_cComponents = pslout->m_cComponents; + + m_isloutClusterBase = pslout->m_isloutClusterBase; + m_disloutCluster = pslout->m_disloutCluster; + m_xsClusterXOffset = pslout->m_xsClusterXOffset; + m_xsClusterAdvance = pslout->m_xsClusterAdvance; + m_igbb = pslout->m_igbb; + m_xsAdvanceX = pslout->m_xsAdvanceX; +// m_ysAdvanceY = pslout->m_ysAdvanceY; +// m_rectBB = pslout->m_rectBB; +} + +/*---------------------------------------------------------------------------------------------- + Shift the glyph to the opposite end of the segment. This is needed for white-space-only + segments whose direction is being changed. +----------------------------------------------------------------------------------------------*/ +void GrSlotOutput::ShiftForDirDepthChange(float dxsSegWidth) +{ + float dxsShift = dxsSegWidth - m_xsAdvanceX - (2 * m_xsPositionX); + int tmp; tmp = (int) dxsShift; + m_xsPositionX += dxsShift; +// m_rectBB.left += dxsShift; +// m_rectBB.right += dxsShift; +} + +/*---------------------------------------------------------------------------------------------- + Return the indices of all the glyphs attached to this cluster, in logical order. + Return an empty vector if this glyph is not the base glyph or if there are no + attached glyphs. + This method return glyph indices, not slot indices. +----------------------------------------------------------------------------------------------*/ +void GrSlotOutput::ClusterMembers(Segment * pseg, int isloutThis, std::vector<int> & visloutRet) +{ + if (m_isloutClusterBase == -1 || m_isloutClusterBase == isloutThis) // yes, a base + pseg->ClusterMembersForGlyph(isloutThis, m_disloutCluster, visloutRet); + else + { + Assert(visloutRet.size() == 0); + } +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotState.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotState.h new file mode 100644 index 00000000000..83a21bedea4 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotState.h @@ -0,0 +1,1315 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrSlotState.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Copyright (C) 1999 by SIL International. All rights reserved. + +Description: + Classes GrSlotAbstract, GrSlotState, and GrSlotOutput +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef SLOTSTATE_INCLUDED +#define SLOTSTATE_INCLUDED + +namespace gr +{ + +class GrSlotAbstract; +class GrSlotState; +class GrSlotOutput; +class GrSlotStream; +class Font; + +//:End Ignore + + +/*---------------------------------------------------------------------------------------------- + Subsumes GrSlotState and GrSlotOutput, both of which represent a single glyph + and its slot attributes and features. + + Hungarian: slab +----------------------------------------------------------------------------------------------*/ +class GrSlotAbstract +{ + friend class GrSlotStream; + friend class GlyphInfo; + +public: + // Constructor: + GrSlotAbstract() + { + } + + virtual ~GrSlotAbstract() + { + // the table manager is responsible for destroying the contents of m_prgnVarLenBuf + } + + void BasicInitializeAbstract(int cnCompPerLig, u_intslot * pnBuf) + { + m_dirc = kNotYetSet8; + m_lb = kNotYetSet8; + m_fInsertBefore = true; + m_nDirLevel = -1; + + m_bIsSpace = -1; // unknown + + m_fInsertBefore = true; + + m_mMeasureSol = 0; + m_mMeasureEol = 0; + + m_mJStretch0 = 0; + m_mJShrink0 = 0; + m_mJStep0 = 0; + m_nJWeight0 = 0; + m_mJWidth0 = 0; + + m_cnCompPerLig = byte(cnCompPerLig); + m_prgnVarLenBuf = pnBuf; + } + + void CopyAbstractFrom(GrSlotState * pslot); + + void SetBufferPtr(u_intslot * pn) + { + m_prgnVarLenBuf = pn; + } + + gid16 GlyphID() { return m_chwGlyphID; } + gid16 RawActualGlyph() { return m_chwActual; } + virtual float GetGlyphMetric(Font * pfont, int nGlyphMetricID, gid16 chwGlyphID); + +protected: + void GetGlyphMetricAux(Font * pfont, gid16 chwGlyphID, + float & xysGlyphX, float & xysGlyphY, + float & xysGlyphWidth, float & xysGlyphHeight, float & xysAdvX, float & xysAdvY, + sdata8 & bIsSpace); + +public: + + //GrSlotState * CompRefSlot(int i); + + int BreakWeight() + { + return (int)m_lb; + } + DirCode Directionality() + { + return DirCode(int(m_dirc)); + } + int InsertBefore() + { + return m_fInsertBefore; + } + int MeasureSol() + { + return m_mMeasureSol; + } + int MeasureEol() + { + return m_mMeasureEol; + } + + int DirLevel() + { + return m_nDirLevel; + } + + int SpecialSlotFlag() + { + return m_spsl; + } + void SetSpecialSlotFlag(int spsl) + { + m_spsl = sdata8(spsl); + } + + enum { + kNotYetSet = 0x7FFF, + kNotYetSet8 = 0x7F, + kInvalidGlyph = 0xFFFF + }; + + gid16 ActualGlyphForOutput(GrTableManager * ptman); + + // Needed for GlyphInfo: + float XPosition() + { + return m_xsPositionX; + } + float YPosition() + { + return m_ysPositionY; // relative to baseline (positive is up) + } + +protected: + gid16 m_chwGlyphID; + gid16 m_chwActual; // actual glyph to output (which is a different glyph for pseudos) + + sdata8 m_spsl; // special slot flag: LB, bidi marker + +// bool m_fInitialLB; // for LB slots: true if this is the initial LB; + // false if it is the terminating LB + // TODO: remove + + sdata8 m_dirc; // default = kNotYetSet8 (read from glyph attr) + sdata8 m_lb; // default = kNotYetSet8 (read from glyph attr) + + sdata8 m_nDirLevel; + + // Slot attributes that are used by GrSlotOutput: + short m_mMeasureSol; + short m_mMeasureEol; + + unsigned short m_mJStretch0; + unsigned short m_mJShrink0; + unsigned short m_mJStep0; + int m_mJWidth0; + byte m_nJWeight0; + + bool m_fInsertBefore; // default = true + + sdata8 m_bIsSpace; // 0 = false, 1 = true, -1 = unknown + + byte m_cnCompPerLig; + // There is a large block managed by either the GrTableManager (for GrSlotState) + // or the segment (for GrSlotOutput); this variable points at the sub-buffer for this + // particular slot: + u_intslot * m_prgnVarLenBuf; + + float m_xsPositionX; + float m_ysPositionY; + +}; // end of class GrSlotAbstract + + +/*---------------------------------------------------------------------------------------------- + A GrSlotState represents one slot as modified by a pass in the table. + Each time a slot state is modified, a new instance is created, copying the relevant + information. + + Hungarian: slot + + Other hungarian: + m - integer indicating glyph design units ("em-units") + sr - integer indicating slot reference +----------------------------------------------------------------------------------------------*/ +class GrSlotState : public GrSlotAbstract +{ + + friend class GrSlotStream; + friend class FontMemoryUsage; + +public: + enum { kNeutral = 99 }; + + // Constructors: + GrSlotState() + : GrSlotAbstract(), + m_pslotPrevState(NULL) + { + m_vpslotAssoc.clear(); + //m_fInitialLB = false; // TODO: remove + m_spsl = kspslNone; + m_fNeutralAssocs = false; + m_dircProc = kdircUnknown; + m_fDirProcessed = false; + + m_vdislotAttLeaves.clear(); + m_fAttachMod = false; + m_fShiftMod = false; + m_dislotRootFixed = 0; + ZapCompositeMetrics(); + } + + virtual ~GrSlotState() + { + } + + void BasicInitialize(int cnUserDefn, int cnCompPerLig, int cnFeat, u_intslot * pnBuf) + { + BasicInitializeAbstract(cnCompPerLig, pnBuf); + + m_mAdvanceX = kNotYetSet; + m_mAdvanceY = kNotYetSet; + m_mShiftX = 0; + m_mShiftY = 0; + + m_fAdvXSet = false; // for transduction logging + m_fAdvYSet = false; + + m_srAttachTo = 0; + m_nAttachLevel = 0; + + m_mAttachAtX = kNotYetSet; + m_mAttachAtY = 0; + m_nAttachAtGpoint = kNotYetSet; + m_mAttachAtXOffset = 0; + m_mAttachAtYOffset = 0; + m_mAttachWithX = kNotYetSet; + m_mAttachWithY = 0; + m_nAttachWithGpoint = kNotYetSet; + m_mAttachWithXOffset = 0; + m_mAttachWithYOffset = 0; + + m_islotPosPass = kNotYetSet; + + m_cnUserDefn = byte(cnUserDefn); + m_cnFeat = byte(cnFeat); + + m_fHasComponents = false; + + m_xysFontAscent = kNegInfFloat; + m_xysFontDescent = kNegInfFloat; + m_xysGlyphWidth = kNegInfFloat; + m_xysGlyphHeight = kNegInfFloat; + m_xysGlyphX = kNegInfFloat; + m_xysGlyphY = kNegInfFloat; + m_xysAdvX = kNegInfFloat; + m_xysAdvY = kNegInfFloat; + } + + void Initialize(gid16 chw, GrEngine *, GrFeatureValues fval, + int ipass, int ichwSegOffset, int nUnicode = -1); + void Initialize(gid16 chw, GrEngine *, GrSlotState * pslotFeat, + int ipass, int ichwSegOffset); + void Initialize(gid16 chw, GrEngine *, GrSlotState * pslotFeat, + int ipass); + + void InitializeFrom(GrSlotState * pslot, int ipass); + void CopyFeaturesFrom(GrSlotState * pslotSrc); + void FixAttachmentTree(GrSlotState * pslotOld); + + void CopyFrom(GrSlotState * pslot, bool fCopyEverything = true); + + virtual float GetGlyphMetric(Font * pfont, int nGlyphMetricID, gid16 chwGlyphID); + + // General: + int RawSegOffset() { return m_ichwSegOffset; } + + void SetGlyphID(gid16 chw) + { + m_chwGlyphID = chw; + m_chwActual = kInvalidGlyph; + m_xysGlyphWidth = kNegInfFloat; // indicate glyph metrics are invalid + m_ipassFsmCol = -1; + m_colFsm = -1; + } + int PosPassIndex() + { + return m_islotPosPass; + } + void SetPosPassIndex(int islot, bool fInputToPosPass1) + { + // If we're resetting it, it should be to the same value as before: + Assert(fInputToPosPass1 || m_islotPosPass == kNotYetSet || m_islotPosPass == islot); + m_islotPosPass = islot; + } + void IncPosPassIndex() + { + m_islotPosPass++; + } + void ZapPosPassIndex() + { + m_islotPosPass = kNotYetSet; + } + + int StyleIndex() { return m_bStyleIndex; } + + int AttachTo() { return m_srAttachTo; } + int AttachLevel() { return m_nAttachLevel; } + + int RawAttachAtX() { return m_mAttachAtX; } + int AttachAtY() { return m_mAttachAtY; } + int AttachAtGpoint() { return m_nAttachAtGpoint; } + int AttachAtXOffset() { return m_mAttachAtXOffset; } + int AttachAtYOffset() { return m_mAttachAtYOffset; } + + int RawAttachWithX() { return m_mAttachWithX; } + int AttachWithY() { return m_mAttachWithY; } + int AttachWithGpoint() { return m_nAttachWithGpoint; } + int AttachWithXOffset() { return m_mAttachWithXOffset; } + int AttachWithYOffset() { return m_mAttachWithYOffset; } + + void Associate(GrSlotState *); + void Associate(GrSlotState *, GrSlotState *); + void Associate(std::vector<GrSlotState*> &); + void ClearAssocs(); + + int AssocsSize() { return m_vpslotAssoc.size(); } + GrSlotState * RawBeforeAssocSlot() + { + if (m_vpslotAssoc.size() == 0) + return NULL; + return m_vpslotAssoc[0]; + } + GrSlotState * RawAfterAssocSlot() + { + if (m_vpslotAssoc.size() == 0) + return NULL; + return m_vpslotAssoc.back(); + } + GrSlotState * AssocSlot(int i) { return m_vpslotAssoc[i]; } + + void AllAssocs(std::vector<int> & vichw); + int BeforeAssoc(); + int AfterAssoc(); + + void CleanUpAssocs(); + + void SetComponentRefsFor(GrSlotOutput *, int iComp = -1); + void AllComponentRefs(std::vector<int> & vichw, std::vector<int> & vicomp, int iComponent = -1); + + int PassModified() { return m_ipassModified; } + int SegOffset(); + int UnderlyingPos(); + GrSlotState * PrevState() { return m_pslotPrevState; } + + void MarkDeleted() // for now, do nothing + { + } + + int IsSpace(GrTableManager * ptman); + + // TODO: remove argument from these methods; it is no longer needed. + bool IsLineBreak(gid16 chwLB) + { + return (IsInitialLineBreak(chwLB) || IsFinalLineBreak(chwLB)); + //return (m_chwGlyphID == chwLB); // TODO: remove + } + bool IsInitialLineBreak(gid16 chwLB) + { + return (m_spsl == kspslLbInitial); + //return (IsLineBreak(chwLB) && m_fInitialLB == true); // TODO: remove + } + bool IsFinalLineBreak(gid16 chwLB) + { + return (m_spsl == kspslLbFinal); + //return (IsLineBreak(chwLB) && m_fInitialLB == false); // TODO: remove + } + + bool IsBidiMarker() + { + switch (m_spsl) + { + case kspslLRM: + case kspslRLM: + case kspslLRO: + case kspslRLO: + case kspslLRE: + case kspslRLE: + case kspslPDF: + return true; + default: + return false; + } + return false; + } + + bool HasComponents() { return m_fHasComponents; } + + // Directionality as determined by the bidi algorithm + DirCode DirProcessed() // return the value + { + Assert(m_dirc != kNotYetSet8); + if (m_dircProc == kdircUnknown) + m_dircProc = DirCode(m_dirc); + return m_dircProc; + } + void SetDirProcessed(DirCode dirc) // set the directionality + { + m_dircProc = dirc; + } + bool DirHasBeenProcessed() // has this slot been fully processed? + { + return m_fDirProcessed; + } + void MarkDirProcessed() // this slot has been fully processed + { + m_fDirProcessed = true; + } + + int RawAdvanceX() { return m_mAdvanceX; } + int RawAdvanceY() { return m_mAdvanceY; } + int ShiftX() { return m_mShiftX; } + int ShiftY() { return m_mShiftY; } + + // Slot attributes that must be calculated: + + int AdvanceX(GrTableManager * ptman) + { + if (m_mAdvanceX == kNotYetSet) + // Initialize it from the glyph metric (adjusted for hinting). + m_mAdvanceX = short(GlyphMetricEmUnits(ptman, kgmetAdvWidth)); + return m_mAdvanceX; + } + + int AdvanceY(GrTableManager * ptman) + { + if (m_mAdvanceY == kNotYetSet) + // Initialize it from the glyph metric (adjusted for hinting). + m_mAdvanceY = short(GlyphMetricEmUnits(ptman, kgmetAdvHeight)); + return m_mAdvanceY; + } + + int AttachAtX(GrTableManager * ptman, GrSlotStream * psstrm) + { + if (m_mAttachAtX == kNotYetSet) + { + Assert(false); // Should have already been set in HandleModifiedPosition, + // but just in case... + if (m_srAttachTo == 0) + return 0; + else + m_mAttachAtX = short(AttachRoot(psstrm)->AdvanceX(ptman)); // attach on the right + } + return m_mAttachAtX; + } + + int AttachWithX(GrTableManager * ptman, GrSlotStream * psstrm) + { + if (m_mAttachAtX == kNotYetSet) + { + Assert(false); // Should have already been set in HandleModifiedPosition, + // but just in case. + if (!m_srAttachTo == 0) + return 0; + else + m_mAttachAtX = 0; // attach on the right + } + return m_mAttachAtX; + } + + int JStretch() + { + return m_mJStretch0; + } + int JShrink() + { + return m_mJShrink0; + } + int JStep() + { + return m_mJStep0; + } + int JWeight() + { + return m_nJWeight0; + } + int JWidth() + { + return m_mJWidth0; + } + + // Slot attribute setters: + + void SetAdvanceX(int mVal) + { + Assert(mVal < 0xFFFF); m_mAdvanceX = short(mVal & 0xFFFF); m_fShiftMod = true; + m_fAdvXSet = true; // for transduction logging + } + void SetAdvanceY(int mVal) + { + Assert(mVal < 0xFFFF); m_mAdvanceY = short(mVal & 0xFFFF); m_fShiftMod = true; + m_fAdvYSet = true; // for transduction logging + } + void SetShiftX(int mVal) + { + Assert(mVal < 0xFFFF); m_mShiftX = short(mVal & 0xFFFF); + m_fShiftMod = true; + } + void SetShiftY(int mVal) + { + Assert(mVal < 0xFFFF); m_mShiftY = short(mVal & 0xFFFF); + m_fShiftMod = true; + } + + void SetAttachTo(int srVal) + { + Assert(srVal < 0xFFFF); + m_srAttachTo = short(srVal & 0xFFFF); + m_fAttachMod = true; + } + void SetAttachLevel(int nVal) { + Assert(nVal < 0xFFFF); m_nAttachLevel = short(nVal & 0xFFFF); m_fAttachMod = true; } + + void SetAttachAtX(int mVal) + { + Assert(mVal < 0xFFFF); + m_mAttachAtX = short(mVal) & 0xFFFF; + m_fAttachMod = true; + } + void SetAttachAtY(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachAtY = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachAtGpoint(int nVal) { + m_nAttachAtGpoint = short(nVal); m_fAttachMod = true; } + void SetAttachAtXOffset(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachAtXOffset = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachAtYOffset(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachAtYOffset = short(mVal & 0xFFFF); m_fAttachMod = true; } + + void SetAttachWithX(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachWithX = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachWithY(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachWithY = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachWithGpoint(int nVal) { + m_nAttachWithGpoint = short(nVal); m_fAttachMod = true; } + void SetAttachWithXOffset(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachWithXOffset = short(mVal & 0xFFFF); m_fAttachMod = true; } + void SetAttachWithYOffset(int mVal) { + Assert(mVal < 0xFFFF); m_mAttachWithYOffset = short(mVal & 0xFFFF); m_fAttachMod = true; } + + void SetCompRefSlot(GrTableManager * ptman, int i, GrSlotState * pslotComp); + + void SetBreakWeight(int lb) { m_lb = sdata8(lb); } + void SetInsertBefore(bool f) { m_fInsertBefore = f; } + + void SetDirectionality(DirCode dirc) { m_dirc = sdata8(dirc); } + void SetDirLevel(int n) { m_nDirLevel = sdata8(n); } + + void SetMeasureSol(int mVal) { m_mMeasureSol = short(mVal); } + void SetMeasureEol(int mVal) { m_mMeasureEol = short(mVal); } + + void SetJStretch(int mVal) { m_mJStretch0 = short(mVal); } + void SetJShrink(int mVal) { m_mJShrink0 = short(mVal); } + void SetJStep(int mVal) { m_mJStep0 = short(mVal); } + void SetJWeight(int nVal) { m_nJWeight0 = byte(nVal); } + void SetJWidth(int mVal) { m_mJWidth0 = mVal; } + void AddJWidthToAdvance(GrTableManager * ptman) + { + // Don't change m_fShiftMod. + m_mAdvanceX = short(m_mJWidth0 + AdvanceX(ptman)); // make sure it is calculated + m_mJWidth0 = 0; + m_fAdvXSet = true; // for transduction logging + } + + int PostBidiDirLevel(GrTableManager * ptman); + + bool BaseEarlierInStream(); + + void ZapDirLevel() + { + m_nDirLevel = -1; + m_dircProc = kdircUnknown; + m_fDirProcessed = false; + } + + int GlyphAttrValueEmUnits(GrTableManager * ptman, int nAttrID); + int GlyphMetricEmUnits(GrTableManager * ptman, int nGlyphMetricID); + + float GlyphAttrValueLogUnits(GrTableManager * ptman, int nAttrID); + float GlyphMetricLogUnits(GrTableManager * ptman, int nGlyphMetricID); + +// void HandleModifiedCluster(GrTableManager * ptman, +// GrSlotStream * psstrm, int islotThis); + void HandleModifiedPosition(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis); + + void CalcCompositeMetrics(GrTableManager * ptman, GrSlotStream * psstrm, + int nLevel, bool fThorough = false); + + void Position(GrTableManager * ptman, + GrSlotStream * psstrmOut, int * pmXPos, int * pmYPos); + + void ZapPosition() + { + m_xsPositionX = kNegInfFloat; + m_ysPositionY = kNegInfFloat; + } + + void SetXPos(float xs) + { + m_xsPositionX = xs; + } + void SetYPos(float ys) + { + m_ysPositionY = ys; // relative to baseline (positive is up) + } + + float ClusterRootOffset() { return -m_xsClusterXOffset; } // the offset of the root + // relative to the whole cluster + + float ClusterAdvWidthFrom(float xs) { return xs + m_xsClusterAdv; } + float ClusterBbLeftFrom(float xs) { return xs + m_xsClusterBbLeft; } + float ClusterBbRightFrom(float xs) { return xs + m_xsClusterBbRight; } + + float ClusterLsb(GrSlotStream * psstrm, float xs) + { + return ClusterBbLeft(psstrm) + xs; + } + float ClusterRsb(GrSlotStream * psstrm, float xs) + { + return ClusterAdvWidthFrom(xs) - ClusterBbRightFrom(xs); + } + + float ClusterAdvWidth(GrSlotStream * psstrm) + { + return ClusterAdvWidthFrom(Base(psstrm)->ClusterRootOffset()); + } +// int ClusterAdvHeight(GrSlotStream * psstrm) +// { +// Assert(m_mAdvanceY != kNotYetSet); +// return EmUnitsToTwips(m_mAdvanceY); +// } + float ClusterBbLeft(GrSlotStream * psstrm) + { + return ClusterBbLeftFrom(Base(psstrm)->ClusterRootOffset()); + } + float ClusterBbRight(GrSlotStream * psstrm) + { + return ClusterBbRightFrom(Base(psstrm)->ClusterRootOffset()); + } + float ClusterBbTop(GrSlotStream * psstrm) + { + return m_ysClusterBbTop; + } + float ClusterBbBottom(GrSlotStream * psstrm) + { + return m_ysClusterBbBottom; + } + float ClusterBbWidth(GrSlotStream * psstrm) + { + return m_xsClusterBbRight - m_xsClusterBbLeft + 1; + } + float ClusterBbHeight(GrSlotStream * psstrm) + { + return m_ysClusterBbTop - m_ysClusterBbBottom + 1; + } + float ClusterLsb(GrSlotStream * psstrm) + { + return ClusterBbLeft(psstrm); + } + float ClusterRsb(GrSlotStream * psstrm) + { + return ClusterAdvWidth(psstrm) - ClusterBbRight(psstrm); + } + + float GlyphXOffset(GrSlotStream * psstrm, float fakeItalicRatio = 0); + float GlyphYOffset(GrSlotStream * psstrm = NULL); + + bool IsBase() + { + Assert(m_dislotRootFixed == m_srAttachTo); + return (m_dislotRootFixed == 0); + } + + bool HasClusterMembers() + { + return (m_vdislotAttLeaves.size() > 0); + } + + GrSlotState * AttachRoot(GrSlotStream * psstrm) + { + if (m_srAttachTo == 0) + return NULL; + else + return SlotAtOffset(psstrm, m_srAttachTo); + } + + int AttachRootPosPassIndex() + { + return PosPassIndex() + m_srAttachTo; + } + + GrSlotState * Base(GrSlotStream * psstrm) + { + GrSlotState * pslotRoot = AttachRoot(psstrm); + if (!pslotRoot) + return this; + else + return pslotRoot->Base(psstrm); + } + + int LastLeafOffset(GrSlotStream * psstrm); + + void AddLeaf(int dislot) + { + m_vdislotAttLeaves.push_back(dislot * -1); + } + void RemoveLeaf(int dislot) + { + for (size_t iislot = 0; iislot < m_vdislotAttLeaves.size(); iislot++) + { + if (m_vdislotAttLeaves[iislot] == dislot * -1) + { + m_vdislotAttLeaves.erase(m_vdislotAttLeaves.begin() + iislot); + return; + } + } + Assert(false); + } + + bool HasAsRoot(GrSlotStream * psstrm, GrSlotState * pslot) + { + GrSlotState * pslotRoot = AttachRoot(psstrm); + if (pslotRoot == pslot) + return true; + else if (pslotRoot == NULL) + return false; + else + return pslotRoot->HasAsRoot(psstrm, pslot); + } + + bool HasAsPreviousState(GrSlotState * pslot) + { + if (this == pslot) + return true; + else + return m_pslotPrevState->HasAsPreviousState(pslot); + } + + void EnsureCacheForOutput(GrTableManager * ptman); + + bool IsLrm(); + bool IsRlm(); + + // Cache of FSM column information for the most recent pass: + int FsmColumn() { return m_colFsm; } + int PassNumberForColumn() { return m_ipassFsmCol; } + void CacheFsmColumn(int ipass, int col) + { + m_colFsm = col; + m_ipassFsmCol = ipass; + } + + // Variable-length buffer--includes four sub-buffers: + // * user-defined variables + // * component.???.ref assignments + // * mapping from components used to global attribute IDs for components + // * feature values + + // user-defined slot attributes + int UserDefn(int slati) + { + Assert(slati < m_cnUserDefn); + return m_prgnVarLenBuf[slati].nValue; + } + void SetUserDefn(int slati, int nVal) + { + Assert(slati < m_cnUserDefn); + m_prgnVarLenBuf[slati].nValue = nVal; + } + u_intslot * PUserDefnBuf() + { + return m_prgnVarLenBuf; + } + + // pointer to the associated slot which is the value of the comp.ref attribute + GrSlotState * CompRef(int slati) + { + Assert(slati < m_cnCompPerLig); + return m_prgnVarLenBuf[m_cnUserDefn + slati].pslot; + } + void SetCompRef(int slati, GrSlotState * pvSlot) + { + Assert(slati < m_cnCompPerLig); + m_prgnVarLenBuf[m_cnUserDefn + slati].pslot = pvSlot; + } + u_intslot * PCompRefBuf() + { + return m_prgnVarLenBuf + m_cnUserDefn; + } + + // global component identifier + int Slati(int i) + { + Assert(i < m_cnCompPerLig); + return m_prgnVarLenBuf[m_cnUserDefn + m_cnCompPerLig + i].nValue; + } + void SetSlati(int i, int n) + { + Assert(i < m_cnCompPerLig); + m_prgnVarLenBuf[m_cnUserDefn + m_cnCompPerLig + i].nValue = n; + } + u_intslot * PSlatiBuf() + { + return m_prgnVarLenBuf + m_cnUserDefn + m_cnCompPerLig; + } + + // feature settings + int FeatureValue(int i) + { + Assert(i < m_cnFeat); + return m_prgnVarLenBuf[m_cnUserDefn + (m_cnCompPerLig * 2) + i].nValue; + } + + u_intslot * PFeatureBuf() + { + return m_prgnVarLenBuf + m_cnUserDefn + (m_cnCompPerLig * 2); + } + + int CExtraSpace() + { + return m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat; + } + + GrSlotState * CompRefSlot(int i); + + void GetFeatureValues(GrFeatureValues * pfval) + { + pfval->m_nStyleIndex = m_bStyleIndex; + std::fill(pfval->m_rgnFValues, pfval->m_rgnFValues + kMaxFeatures, 0); + for (size_t i = 0; i < m_cnFeat; i++) + pfval->m_rgnFValues[i] = PFeatureBuf()[i].nValue; + } + +// For transduction logging: +#ifdef TRACING + void SlotAttrsModified(bool * rgfMods, bool fPreJust, int * pccomp, int * pcassoc); + void LogSlotAttribute(GrTableManager *, std::ostream &, int ipass, int slat, int icomp, + bool fPreJust, bool fPostJust); + void LogAssociation(GrTableManager * ptman, + std::ostream & strmOut, int ipass, int iassoc, bool fBoth, bool fAfter); + int m_islotTmpIn; // for use by transduction log; index of slot in input stream + int m_islotTmpOut; // ditto; index of slot in output stream +#endif // TRACING + +protected: + + // Instance variables: + int m_ipassModified; // pass in which this slot was modified + + GrSlotState * m_pslotPrevState; + + int m_ichwSegOffset; // for original (pass 0) slot states: position in + // underlying text relative to the official + // beginning of the segment; + // should == kInvalid for other slot states + int m_islotPosPass; // index of slot in positioning streams, relative to first official + // slot in the segment (possibly the LB slot) + + int m_colFsm; // which FSM column this glyph corresponds to... + int m_ipassFsmCol; // ...for the most recent pass + + std::vector<GrSlotState*> m_vpslotAssoc; // association mappings + + bool m_fNeutralAssocs; // true if we've set the associations to some neutral + // default, rather than them being set explicitly + // within a rule + + int m_nUnicode; // for debugging + + DirCode m_dircProc; // directionality as processed in bidi algorithm + bool m_fDirProcessed; + + // affects length of variable-length buffer + byte m_cnUserDefn; + byte m_cnFeat; + + byte m_bStyleIndex; + + // Slot attributes: + short m_mAdvanceX; + short m_mAdvanceY; + short m_mShiftX; + short m_mShiftY; + + short m_srAttachTo; + short m_nAttachLevel; + + short m_mAttachAtX; + short m_mAttachAtY; + short m_mAttachAtXOffset; + short m_mAttachAtYOffset; + short m_mAttachWithX; + short m_mAttachWithY; + short m_mAttachWithXOffset; + short m_mAttachWithYOffset; + + short m_nAttachAtGpoint; + short m_nAttachWithGpoint; + + // Raw glyph metrics (directly from font) + float m_xysFontAscent; + float m_xysFontDescent; + float m_xysGlyphWidth; + float m_xysGlyphHeight; + float m_xysGlyphX; + float m_xysGlyphY; + float m_xysAdvX; + float m_xysAdvY; + + // Attachment and metrics + + // This is a flag that is set whenever we change the value of any of the attach + // slot attributes. Then when we want to find out something about the attachments, + // if it is set, we have some work to do in updating the pointers and metrics. + bool m_fAttachMod; + + // This is a flag that is set whenever we change the value of any of the shift or + // advance slot attributes. It forces us to zap the metrics. + bool m_fShiftMod; + + // This glyph (and all its leaves, if any) have a zero advance width; + // never allow it to affect the advance width of a cluster it is part of. + bool m_fIgnoreAdvance; + + int m_dislotRootFixed; // the offset of the slot (relative to this one) that considers + // this slot to be one of its leaves + std::vector<int> m_vdislotAttLeaves; + + // The following are used by the CalcCompositeMetrics() method and depend on + // the cluster level that was passed as an argument. + + int m_nCompositeLevel; // cluster level last used to calculate composite metrics; + // kNegInfinity if uncalculated + + // offsets for this node only, relative to cluster base + float m_xsOffsetX; + float m_ysOffsetY; // relative to baseline (positive is up) + + // offsets for this node and its leaves, relative to cluster base (y-coords are + // relative to baseline) + float m_xsClusterXOffset; + float m_xsClusterAdv; + float m_xsClusterBbLeft; + float m_xsClusterBbRight; + float m_ysClusterBbTop; + float m_ysClusterBbBottom; + + // cumulative total of shifts for this node and roots; advance needs to ignore these + float m_xsRootShiftX; + float m_ysRootShiftY; + + bool m_fHasComponents; // default = false + + // Private methods: + + void CopyAbstractFrom(GrSlotState * pslot); + + void AdjustRootMetrics(GrTableManager * ptman, GrSlotStream *); + void InitMetrics(GrTableManager * ptman, GrSlotState * pslotRoot); + void InitLeafMetrics(GrTableManager * ptman, GrSlotState * pslotRoot); + void InitRootMetrics(GrTableManager * ptman); + void CalcRootMetrics(GrTableManager * ptman, GrSlotStream *, int nLevel); + void AttachToRoot(GrTableManager * ptman, GrSlotStream *, GrSlotState * pslotNewRoot); + void AttachLogUnits(GrTableManager * ptman, + GrSlotState * pslotRoot, + float * pxsAttAtX, float * pysAttAtY, + float * pxsAttWithX, float * pysAttWithY); + + GrSlotState * SlotAtOffset(GrSlotStream * psstrm, int dislot); // ENHANCE SharonC: inline? + + void EnsureLocalAttachmentTree(GrTableManager * ptman, + GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis); + + void ZapCompositeMetrics() + { + m_nCompositeLevel = kNegInfinity; + m_xsPositionX = kNegInfFloat; + m_ysPositionY = kNegInfFloat; + m_xsOffsetX = 0; + m_ysOffsetY = 0; + m_xsRootShiftX = 0; + m_ysRootShiftY = 0; + ZapRootMetrics(); + } + + void ZapRootMetrics() + { + m_xsClusterXOffset = 0; + m_xsClusterAdv = 0; + m_xsClusterBbLeft = 0; + m_xsClusterBbRight = 0; + m_ysClusterBbTop = 0; + m_ysClusterBbBottom = 0; + m_fIgnoreAdvance = false; + } + + void ZapMetricsAndPositionDownToBase(GrSlotStream * psstrm) + { + ZapCompositeMetrics(); + if (m_dislotRootFixed) + SlotAtOffset(psstrm, m_dislotRootFixed)->ZapMetricsAndPositionDownToBase(psstrm); + } + + void ZapMetricsOfLeaves(GrSlotStream * psstrm, bool fThis = false) + { + if (fThis) + ZapCompositeMetrics(); + for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++) + { + SlotAtOffset(psstrm, m_vdislotAttLeaves[islot])->ZapMetricsOfLeaves(psstrm, true); + } + } + +public: // for transduction logging + bool m_fAdvXSet; + bool m_fAdvYSet; + +}; // end of class GrSlotState + + +/*---------------------------------------------------------------------------------------------- + A GrSlotOutput represents one slot as the final output of the final pass. These + are recorded in the segment. + + Hungarian: slout +----------------------------------------------------------------------------------------------*/ +class GrSlotOutput : public GrSlotAbstract +{ + friend class GlyphInfo; + friend class Segment; + friend class SegmentMemoryUsage; + +public: + GrSlotOutput() + { + m_cComponents = 0; + m_isloutClusterBase = -1; // not part of any cluster + m_disloutCluster = 0; + m_igbb = -1; + } + + void ExactCopyFrom(GrSlotOutput * pslout, u_intslot * pnVarLenBuf, int cnExtraPerSlot); + + void InitializeOutputFrom(GrSlotState * pslot); + + int BeforeAssoc() + { + return m_ichwBeforeAssoc; // relative to the official beginning of the segment + } + int AfterAssoc() + { + return m_ichwAfterAssoc; // relative to the official beginning of the segment + } + + void SetBeforeAssoc(int ichw) + { + m_ichwBeforeAssoc = ichw; + } + void SetAfterAssoc(int ichw) + { + m_ichwAfterAssoc = ichw; + } + + //u_intslot * PCompRefBufSlout() + //{ + // return m_prgnVarLenBuf; + //} + int CExtraSpaceSlout() + { + return (m_cnCompPerLig * 2); + } + + int IbufUnderlyingCompOffset() + { + return 0; + } + int IbufCompIdOffset() + { + return m_cnCompPerLig; + } + + void AddComponentReference(int ichw, int slati) + { + short ichw16 = short(ichw); + if (m_cComponents >= m_cnCompPerLig) + { + Assert(false); // too many--ignore the requested component ref + } + else + { + int iComp; + for (iComp = 0; iComp < m_cComponents; iComp++) + { + if (ComponentId(iComp) == slati) + { + // We already have at least one character for this component. Add another. + if (m_prgnVarLenBuf[IbufUnderlyingCompOffset() + iComp].smallint[0] > ichw16) + m_prgnVarLenBuf[IbufUnderlyingCompOffset() + iComp].smallint[0] = ichw16; // min + if (m_prgnVarLenBuf[IbufUnderlyingCompOffset() + iComp].smallint[1] < ichw16) + m_prgnVarLenBuf[IbufUnderlyingCompOffset() + iComp].smallint[1] = ichw16; // max + return; + } + } + + iComp = m_cComponents; + + m_prgnVarLenBuf[IbufUnderlyingCompOffset() + iComp].smallint[0] = ichw16; // min + m_prgnVarLenBuf[IbufUnderlyingCompOffset() + iComp].smallint[1] = ichw16; // max + + // OBSOLETE comment: + // Maps the used components to the defined components. Normally this will be + // one-to-one and the buffer will hold [0,1,2...]. But possibly we may + // have defined components a, b, and c, but only mapped a and c to actual + // characters. This buffer will then hold [0,2]. + + m_prgnVarLenBuf[IbufCompIdOffset() + iComp].nValue = slati; + +// Assert(iComp >= m_cComponents); // because we process them in order they are defined + // in, but we could have skipped some that are + // defined + m_cComponents++; + } + } + int NumberOfComponents() // the number used by the rules + { + return m_cComponents; + } + // Index of ligature components, relative to the beginning of the segment. + // iComp is index of components USED in this glyph. + int FirstUnderlyingComponent(int iComp) + { + Assert(iComp < m_cnCompPerLig); + int ichw = m_prgnVarLenBuf[IbufUnderlyingCompOffset() + iComp].smallint[0]; + return ichw; + } + int LastUnderlyingComponent(int iComp) + { + Assert(iComp < m_cnCompPerLig); + int ichw = m_prgnVarLenBuf[IbufUnderlyingCompOffset() + iComp].smallint[1]; + return ichw; + } + int ComponentId(int iComp) + { + return m_prgnVarLenBuf[IbufCompIdOffset() + iComp].nValue; + } + + void SetClusterBase(int islout) + { + m_isloutClusterBase = islout; + } + + int ClusterBase() + { + return m_isloutClusterBase; + } + + bool IsPartOfCluster() + { + return (m_isloutClusterBase > -1); + } + + //int NumClusterMembers() + //{ + // return m_visloutClusterMembers.size(); + //} + //int ClusterMember(int iislout) + //{ + // return m_visloutClusterMembers[iislout]; + //} + + void AddClusterMember(int isloutThis, int isloutAttached) + { + m_disloutCluster = sdata8(max(int(m_disloutCluster), abs(isloutThis - isloutAttached))); + } + + void ClusterMembers(Segment * pseg, int islout, std::vector<int> & visloutRet); + + int ClusterRange() + { + return m_disloutCluster; + } + + float ClusterXOffset() { return m_xsClusterXOffset; } + float ClusterAdvance() { return m_xsClusterAdvance; } + void SetClusterXOffset(float xs) { m_xsClusterXOffset = xs; } + void SetClusterAdvance(float xs) { m_xsClusterAdvance = xs; } + + float GlyphMetricLogUnits(Font * pfont, int nGlyphMetric); + //float GlyphMetricLogUnits(int nMetricID); + + int GlyphBbIndex() { return m_igbb; } + void SetGlyphBbIndex (int i) { m_igbb = i; } + + Rect BoundingBox(Font & font) + { + Rect rectBB; + rectBB.left = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetBbLeft); + if (IsSpace()) + rectBB.right = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetAdvWidth); + else + rectBB.right = m_xsPositionX + GlyphMetricLogUnits(&font, kgmetBbRight); + rectBB.top = m_ysPositionY + GlyphMetricLogUnits(&font, kgmetBbTop); + rectBB.bottom = m_ysPositionY + GlyphMetricLogUnits(&font, kgmetBbBottom); + return rectBB; + } + + bool IsLineBreak() + { + return (IsInitialLineBreak() || IsFinalLineBreak()); + } + bool IsInitialLineBreak() + { + return (m_spsl == kspslLbInitial); + } + bool IsFinalLineBreak() + { + return (m_spsl == kspslLbFinal); + } + + //float AdvanceXMetric() + //{ + // return m_xysAdvX; + //} + + bool IsSpace(); + + void AdjustPosXBy(float dxs) + { + m_xsPositionX += dxs; + //m_rectBB.left += dxs; + //m_rectBB.right += dxs; + } + + void ShiftForDirDepthChange(float dxsSegWidth); + + // Used by GlyphInfo + int IndexAttachedTo(); + //inline float AdvanceX(Segment * pseg); + //inline float AdvanceY(Segment * pseg); + float MaxStretch(Segment * pseg, int level); + float MaxShrink(Segment * pseg, int level); + float StretchStep(Segment * pseg, int level); + int JustWeight(int level); + float JustWidth(Segment * pseg, int level); + float MeasureSolLogUnits(Segment * pseg); + float MeasureEolLogUnits(Segment * pseg); + Rect ComponentRect(Segment * pseg, int icomp); + +protected: + // Instance variables: + + sdata8 m_cComponents; + + data8 m_disloutCluster; // how far to search on either side of this glyph to find + // other members of the cluster; + // 0 means there are no cluster members or this glyph is + // attached to some other base + + int m_isloutClusterBase; // the index of the slot that serves as the base for the + // cluster this slot is a part of; -1 if not part of cluster + + int m_ichwBeforeAssoc; // index of associated character(s) in the string + int m_ichwAfterAssoc; // (relative to the official beginning of the segment) + // char might possibly not be officially in this segment, + // in which case value is infinity + + // Measurements for highlighting an entire cluster, relative to origin of this slot, + // which is the cluster base. + float m_xsClusterXOffset; + float m_xsClusterAdvance; // for single non-cluster glyphs, advance width of positioned glyph + + // Index into m_prggbb in Segment; -1 indicates a line break slot that is not rendered: + int m_igbb; + + float m_xsAdvanceX; +// float m_ysAdvanceY; -- not used +// Rect m_rectBB; + +}; // end of class GrSlotOutput + +} // namespace gr + + +#endif // !SLOTSTATE_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotStream.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotStream.cpp new file mode 100644 index 00000000000..df1a73e5e3e --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotStream.cpp @@ -0,0 +1,2381 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrSlotStream.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + GrSlotStream class implementation. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#include "GrConstants.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Answer true if there is nothing valid available to read from the stream. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::AtEnd() +{ + Assert(AssertValid()); + + if (m_islotReprocPos > -1 && m_islotReprocPos < signed(m_vpslotReproc.size())) + return false; + + if (m_islotSegLim > -1 && m_islotReadPos >= m_islotSegLim) + // We've determined the segment limit, and we're at or past it. + return true; + + return (m_islotReadPos == m_islotWritePos); +} + +/*---------------------------------------------------------------------------------------------- + Answer true if there is nothing at all available to read from the stream. + For substitution passes, we need to consider slots that may be beyond what we know + will be the official end of the segment. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::AtEndOfContext() +{ + if (m_fUsedByPosPass) + return AtEnd(); + + if (m_islotReprocPos > -1 && m_islotReprocPos < signed(m_vpslotReproc.size())) + return false; + + return (m_islotReadPos == m_islotWritePos); +} + +/*---------------------------------------------------------------------------------------------- + Append a slot to the end of the stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::NextPut(GrSlotState * pslot) +{ + Assert(AssertValid()); + + if (m_islotWritePos < signed(m_vpslot.size())) + { + m_vpslot[m_islotWritePos] = pslot; + } + else + { + m_vpslot.push_back(pslot); + m_vislotPrevChunkMap.push_back(-1); + m_vislotNextChunkMap.push_back(-1); + } + + if (m_fUsedByPosPass && GotIndexOffset()) + pslot->SetPosPassIndex(m_islotWritePos - m_cslotPreSeg, m_fInputToPosPass1); + + m_islotWritePos++; +} + +/*---------------------------------------------------------------------------------------------- + Get the next slot from the stream. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::NextGet() +{ + Assert(AssertValid()); + Assert(!AtEndOfContext()); + + GrSlotState * pslotRet; + + if (m_islotReprocPos > -1) + { + if (m_islotReprocPos >= signed(m_vpslotReproc.size())) + { + // Finished reprocessing. + Assert((unsigned)m_islotReprocPos == m_vpslotReproc.size()); + m_islotReprocPos = -1; + // But leave the m_vpslotReproc array in place so that earlier slots can + // be accessed by the rules. + pslotRet = m_vpslot[m_islotReadPos]; + m_islotReadPos++; + } + else + { + // Read from the reprocess buffer. + pslotRet = m_vpslotReproc[m_islotReprocPos]; + m_islotReprocPos++; + } + } + else + { + // Read normally. + pslotRet = m_vpslot[m_islotReadPos]; + m_islotReadPos++; + } + + Assert(!m_fUsedByPosPass || pslotRet->PosPassIndex() != GrSlotAbstract::kNotYetSet); + + return pslotRet; +} + +/*--------------------------------------------------------------------------------------------- + Peek at the next slot from the stream (dislot = 0 means the very next slot). + Only used for peeking forward. RuleInputSlot is used for accessing any possible + slot in the rule's context. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::Peek(int dislot) +{ + Assert(AssertValid()); + Assert(!AtEndOfContext()); + Assert(dislot >= 0); + + GrSlotState * pslotRet; + + if (m_islotReprocPos > -1) + { + int cslotReproc = m_vpslotReproc.size() - m_islotReprocPos; // num left in reproc buffer + if (dislot >= cslotReproc) + { + // Not quite enough slots in the reprocess buffer. + pslotRet = m_vpslot[m_islotReadPos + dislot - cslotReproc]; + } + else if (m_islotReprocPos + dislot < 0) + { + // Reading prior to the reprocess buffer + pslotRet = m_vpslot[m_islotReadPos + dislot - cslotReproc]; + } + else + { + // Read from the reprocess buffer. + pslotRet = m_vpslotReproc[m_islotReprocPos + dislot]; + } + } + else + { + // Read normally. + Assert(m_islotWritePos - m_islotReadPos > dislot); + pslotRet = m_vpslot[m_islotReadPos + dislot]; + } + + return pslotRet; +} + +/*--------------------------------------------------------------------------------------------- + Treat the stream, which is really the output stream, as an input stream, + retrieving a recently output slot. This happens when we are reading before the + current stream position when the rule is being matched, or before the original + stream position when the rule is being run. + + @param dislot - how far back to peek before the write position + when the rule started; a negative number + (NOTE: the current write position is irrelevant) + @param fNullOkay - true if it's okay to return NULL in the situation where we're asking + for something before the beginning of the stream +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::PeekBack(int dislot, bool fNullOkay) +{ + Assert(dislot < 0); + if (dislot < m_islotRuleStartWrite * -1) + { + Assert(fNullOkay); + return NULL; + } + GrSlotState * pslotRet; + if (m_islotReprocPos > -1) + { + if (dislot < m_islotReprocLim - m_islotRuleStartWrite && + dislot >= m_islotReprocLim - m_islotRuleStartWrite - signed(m_vpslotReproc.size())) + { + // Read from the reprocess buffer. + // Review: this is weird; can it ever happen? + pslotRet = m_vpslotReproc[m_vpslotReproc.size() + - (m_islotReprocLim - m_islotRuleStartWrite) + dislot]; + } + else + pslotRet = m_vpslot[m_islotRuleStartWrite + dislot]; + } + else + pslotRet = m_vpslot[m_islotRuleStartWrite + dislot]; + + return pslotRet; +} + +/*--------------------------------------------------------------------------------------------- + Skip the given number of slots in the stream. + ENHANCE: implement more efficiently +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::Skip(int dislot) +{ + for (int islot = 0; islot < dislot; ++islot) + NextGet(); +} + +/*---------------------------------------------------------------------------------------------- + Perform assertions to ensure that the stream is in a valid state. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::AssertValid() +{ + Assert(m_islotWritePos <= signed(m_vpslot.size())); + Assert(m_islotReadPos <= m_islotWritePos); + // Streams used by the positioning passes (input or output) should not be working + // with slots beyond the segment lim + Assert(m_islotSegLim == -1 || !m_fUsedByPosPass || m_islotReadPos <= m_islotSegLim); + Assert(m_vpslot.size() == m_vislotPrevChunkMap.size()); + Assert(m_vpslot.size() == m_vislotNextChunkMap.size()); + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return the number of slots that have been completely output by the previous pass but not + yet processed by the following pass. ("Completely" output means that they do not need + to be reprocessed by the same pass, only then are they available as valid input.) + Include any slots that are present by virtue of needing to be reprocessed by the + corresponding output stream. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::SlotsPending() +{ + Assert(AssertValid()); + if (m_islotSegLim > -1) + return (m_islotSegLim - m_islotReadPos + SlotsToReprocess()); + else + return (m_islotWritePos - m_islotReadPos + SlotsToReprocess()); +} + +int GrSlotStream::SlotsPendingInContext() +{ + Assert(AssertValid()); + if (m_fUsedByPosPass) + return SlotsPending(); + else + return (m_islotWritePos - m_islotReadPos + SlotsToReprocess()); +} + +int GrSlotStream::TotalSlotsPending() +{ + int cslot = SlotsPendingInContext(); + if (m_fUsedByPosPass && m_islotSegLim > -1) + { + int ctmp = m_islotWritePos - m_islotReadPos + SlotsToReprocess(); + if (ctmp > cslot) + { + int x; x = 3; + } + cslot = max(cslot, ctmp); + } + return cslot; +} + +/*---------------------------------------------------------------------------------------------- + Return true if this pass is input or output to a positioning pass and we're past the end + of the segment. So this stream is virtually finished. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::PastEndOfPositioning(bool fOutput) +{ + if (m_fUsedByPosPass && m_islotSegLim > -1) + { + if (fOutput) // this stream is functioning as an output stream + return (m_islotWritePos >= m_islotSegLim); + else // this stream is functioning as an input stream + return (m_islotReadPos >= m_islotSegLim); + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + Mark the stream as fully written, so that the output pass doesn't keep trying to ask + for more input. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::MarkFullyWritten() +{ + m_fFullyWritten = true; +} + +/*---------------------------------------------------------------------------------------------- + Return the position of the first glyph at or following the given position + that is in the top direction, or -1 if we do not have a complete range. + + @param ptman + @param islotStart + @nTopDirLevel - top direction: 0 = left-to-right, 1 = right-to-left +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::OldDirLevelRange(EngineState * pengst, int islotStart, int nTopDirLevel) +{ + Assert(m_islotReadPos <= islotStart); + Assert(islotStart < m_islotWritePos); + + int islot = islotStart; + int nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nTopDirLevel, + kdircUnknown, kdircNeutral); + if (nSlotDirLevel == -1) + return -1; + while (nSlotDirLevel > nTopDirLevel) + { + islot++; + if (islot >= m_islotWritePos) + { + if (m_fFullyWritten) + return islot; + else + return -1; + } + else if (m_islotSegLim > -1 && islot >= m_islotSegLim) + { + return islot; + } + + nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nTopDirLevel, + kdircUnknown, kdircNeutral); + if (nSlotDirLevel == -1) + return -1; + } + return islot; +} + +/*---------------------------------------------------------------------------------------------- + Return the position of the first glyph at or following the given position + that is in the top direction, or -1 if we do not have a complete range. + + @param ptman + @param islotStart + @nTopDirLevel - top direction: 0 = left-to-right, 1 = right-to-left +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::DirLevelRange(EngineState * pengst, int islotStart, int nTopDirLevel, + std::vector<int> & vislotStarts, std::vector<int> & vislotStops) +{ + Assert(m_islotReadPos <= islotStart); + Assert(islotStart < m_islotWritePos); + + std::vector<int> vislotStartStack; // indices of starts of ranges to reverse + + // Stack corresponding to embedding and override markers: + std::vector<DirCode> vdircMarkerStack; + std::vector<DirCode> vdircOverrideStack; + std::vector<int> vnLevelStack; + + vislotStarts.clear(); + vislotStops.clear(); + + //vislotStartStack.Push(islot); + + int islotSegLim = (FullyWritten() || m_islotSegLim > -1) ? + FinalSegLim() : + WritePos() + 100; // arbitrarily large number + + if (islotStart > islotSegLim) + { + // Don't do anything fancy with stuff beyond the end of the segment. + return 0; + } + + DirCode dircMarker = kdircNeutral; // none so far + int nCurrLevel = nTopDirLevel; + int nMarkerLevel = nTopDirLevel; + //bool fHitMark = false; + DirCode dircOverride = kdircNeutral; + int islot = islotStart; + Assert(islot <= m_islotWritePos); + + while (true) + { + GrSlotState * pslot = m_vpslot[islot]; + DirCode dirc = pslot->DirProcessed(); + bool fIncludeLast = false; + int nSlotDirLevel; + if (dirc == kdircLRO || dirc == kdircLRE) // steps X1, X3 in the Unicode bidi algorithm + { + vdircMarkerStack.push_back(dircMarker); + vdircOverrideStack.push_back(dircOverride); + vnLevelStack.push_back(nCurrLevel); + dircMarker = dirc; + dircOverride = (dirc == kdircLRO) ? kdircL : kdircNeutral; + nSlotDirLevel = (nCurrLevel % 2) ? + nCurrLevel + 1 : nCurrLevel + 2; // next highest LTR level + nMarkerLevel = nSlotDirLevel; + + pslot->SetDirProcessed(dircMarker); + pslot->SetDirLevel(nMarkerLevel); + pslot->MarkDirProcessed(); + } + else if (dirc == kdircRLO || dirc == kdircRLE) // steps X2, X4 + { + vdircMarkerStack.push_back(dircMarker); + vdircOverrideStack.push_back(dircOverride); + vnLevelStack.push_back(nCurrLevel); + dircMarker = dirc; + dircOverride = (dirc == kdircRLO) ? kdircR : kdircNeutral; + nSlotDirLevel = (nCurrLevel % 2) ? + nCurrLevel + 2 : nCurrLevel + 1; // next highest RTL level + nMarkerLevel = nSlotDirLevel; + + pslot->SetDirProcessed(dircMarker); + pslot->SetDirLevel(nMarkerLevel); + pslot->MarkDirProcessed(); + } + else if (dirc == kdircPDF) + { + if (dircMarker == kdircNeutral) + pslot->SetDirProcessed((nCurrLevel % 2) ? kdircR : kdircL); + else + pslot->SetDirProcessed(RightToLeftDir(dircMarker) ? kdircPdfR : kdircPdfL); + pslot->SetDirLevel(nMarkerLevel); + pslot->MarkDirProcessed(); + + if (vnLevelStack.size()) + { + dircMarker = vdircMarkerStack.back(); + vdircMarkerStack.pop_back(); + dircOverride = vdircOverrideStack.back(); + vdircOverrideStack.pop_back(); + nMarkerLevel = vnLevelStack.back(); + vnLevelStack.pop_back(); + nSlotDirLevel = nMarkerLevel; + fIncludeLast = true; + } + else // ignore + nSlotDirLevel = nCurrLevel; + } + else + { + nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nMarkerLevel, + dircMarker, dircOverride); + if (nSlotDirLevel == -1) + return -1; // not enough slots + } + + Assert(pengst->WhiteSpaceOnly() || nCurrLevel >= nTopDirLevel); + while (nSlotDirLevel > nCurrLevel) + { + vislotStartStack.push_back(islot); + nCurrLevel++; + } + // otherwise... + while (nTopDirLevel <= nCurrLevel && nSlotDirLevel < nCurrLevel) + { + if (vislotStartStack.size()) + { + int islotStartTmp = vislotStartStack.back(); + vislotStartStack.pop_back(); + vislotStarts.push_back(islotStartTmp); + vislotStops.push_back(islot - 1); // previous character was the end of the range + } + else + Assert(pengst->WhiteSpaceOnly()); // the space is higher than the segment itself + nCurrLevel--; + } + // In the case of white-space-only segments, the space may be bumped up to a level + // higher than the segment itself. + Assert(pengst->WhiteSpaceOnly() || vislotStartStack.size() + nTopDirLevel == (unsigned)nCurrLevel); + Assert(pengst->WhiteSpaceOnly() || vnLevelStack.size() == vdircMarkerStack.size()); + Assert(pengst->WhiteSpaceOnly() || vdircOverrideStack.size() == vdircMarkerStack.size()); + + if (nCurrLevel <= nTopDirLevel) + { + if (fIncludeLast) + { + // Include in the range to reverse the PDF or the char after the LRM/RLM. + // (Note: don't use a size_t below: we are decrementing below zero.) + for (int i = signed(vislotStops.size()) - 1; i >= 0 && vislotStops[i] == islot-1; i--) + { + vislotStops[i] = islot; + } + islot++; + } + break; // reached end of reversible range + } + + islot++; + if (islot >= m_islotWritePos) + { + if (m_fFullyWritten) + break; // end of input + else + return -1; // not enough slots + } + else if (islot >= islotSegLim) + { + break; // end of input + } + } + + // Reached the end of a reversable range. + while (vislotStartStack.size() > 0) + { + int islotStartTmp = vislotStartStack.back(); + vislotStartStack.pop_back(); + vislotStarts.push_back(islotStartTmp); + //vislotStops.push_back(min(islot, ReadPos() - 1)); + //vislotStops.push_back(min(islot, max(ReadPos() - 1, islotStartTmp))); + vislotStops.push_back(min(islot - 1, islotSegLim - 1)); + nCurrLevel--; + } + Assert(pengst->WhiteSpaceOnly() || nCurrLevel == nTopDirLevel); + Assert(vislotStarts.size() == vislotStops.size()); + + // The start- and stop-lists were generated in the order that we hit the stops, in other + // words, from inner to outer. Which is the order in which we need to do the reversals, too. + + return islot; +} + + +/*---------------------------------------------------------------------------------------------- + Return the direction level of the glyph at the given position, according to the + Unicode bidi algorithm. Return -1 if there aren't enough slots in the stream to + figure it out. + + ???? How should this handle the reprocessing sub-stream???? + + @param ptman + @param islot - slot for which information is requested + @param nOuterLevel - 0 = left-to-right, 1 = right-to-left + @param nCurrLevel + @param dircMarker - most recent override or embedding marker, if any + (for interpreting PDF codes) + @param dircOverride - override imposed by LRO or RLO +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::GetSlotDirLevel(EngineState * pengst, int islot, + int nOuterLevel, int nCurrLevel, DirCode dircMarker, DirCode dircOverride) +{ +// Assert(m_psstrmReprocess == NULL); + + GrSlotState * pslot = m_vpslot[islot]; + + int nDirLevel = pslot->DirLevel(); + if (nDirLevel > -1) + // already figured out + return nDirLevel; + + if (pengst->WhiteSpaceOnly()) + { + // White-space-only segments should not have any reordering. + nDirLevel = pengst->ParaRightToLeft(); + pslot->SetDirLevel(nDirLevel); + pslot->MarkDirProcessed(); + return nDirLevel; + } + + DirCode dirc = pslot->DirProcessed(); + + if (!pslot->DirHasBeenProcessed() && (WeakDir(dirc) || NeutralDir(dirc)) ) + { + // Resolve weak and neutral types; process an entire run of weak types at once, + // in several passes. + + int islotWeakRunLim = islot + 1; + while (true) + { + if (islotWeakRunLim >= m_islotWritePos) + { + if (!m_fFullyWritten) + return -1; // get more slots + else + break; + } + else if (m_islotSegLim > -1 && islotWeakRunLim >= SegLimIfKnown()) + { + break; + } + if (StrongDir(m_vpslot[islotWeakRunLim]->DirProcessed())) + // ignore the line-break; we need the next strong dir after that + //// && !m_vpslot[islotWeakRunLim]->IsFinalLineBreak(ptman->LBGlyphID())) + { + break; + } + if (m_vpslot[islotWeakRunLim]->DirProcessed() == kdircPDF && + dircMarker != kdircUnknown) + { + // A valid PDF is a strong code. TODO: replace kdircPDF with kdircPdfL/R + break; + } + + islotWeakRunLim++; + } + + int islotLp; + + // X6. Handle directional overrides: LRO, RLO. + if (dircOverride != kdircNeutral) + { + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + m_vpslot[islotLp]->SetDirProcessed(dircOverride); + m_vpslot[islotLp]->MarkDirProcessed(); + } + goto LSetDir; + } + + DirCode dircLp; + //DirCode dircTmp; + + // W1. Non-spacing marks get the type of the previous slot. + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + Assert(!m_vpslot[islotLp]->DirHasBeenProcessed()); + + if (m_vpslot[islotLp]->DirProcessed() == kdircNSM) + { +#ifdef _DEBUG + if (islotLp <= 0) + { + Warn("Pathological case in bidi algorithm"); + } +#endif // _DEBUG + dircLp = AdjacentNonBndNeutralCode(pengst, islotLp - 1, -1, dircMarker); + + m_vpslot[islotLp]->SetDirProcessed(dircLp); + } + } + + // W2. European numbers are changed to Arabic numbers if the previous strong type + // was an Arabic letter. + DirCode dircPrevStrong = AdjacentStrongCode(pengst, islot - 1, -1, dircMarker); + if (dircPrevStrong == kdircRArab) + { + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = m_vpslot[islotLp]->DirProcessed(); + if (dircLp == kdircEuroNum) + m_vpslot[islotLp]->SetDirProcessed(kdircArabNum); + } + } + + // W4. A single European separator between two Latin numbers changes to a Latin number. + // A single common separator between two numbers of the same type changes to that + // type. + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = m_vpslot[islotLp]->DirProcessed(); + if ((dircLp == kdircEuroSep || dircLp == kdircComSep) && + islotLp + 1 < m_islotWritePos) // don't test last slot in stream + { + DirCode dircPrev = AdjacentNonBndNeutralCode(pengst, islotLp - 1, -1, dircMarker); + DirCode dircNext = AdjacentNonBndNeutralCode(pengst, islotLp + 1, 1, dircMarker); + if (dircPrev == dircNext) + { + if (dircLp == kdircEuroSep && dircPrev == kdircEuroNum) + { + m_vpslot[islotLp]->SetDirProcessed(kdircEuroNum); + } + else if (dircLp == kdircComSep && + (dircPrev == kdircEuroNum || dircPrev == kdircArabNum)) + { + m_vpslot[islotLp]->SetDirProcessed(dircPrev); + } + } + } + } + + // W5, W6. A sequence of European number terminators adjacent to Latin numbers changes + // to Latin numbers. + // This appears to be an ambiguity in the official algorithm: do we search backward + // first, or forward? For our purposes, it is easier to search backward first. + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = m_vpslot[islotLp]->DirProcessed(); + if (dircLp == kdircEuroTerm || dircLp == kdircBndNeutral) + { + if (TerminatorSequence(pengst, islotLp - 1, -1, dircMarker) == kdircEuroNum) + dircLp = kdircEuroNum; + else + { + if (TerminatorSequence(pengst, islotLp + 1, 1, dircMarker) == kdircEuroNum) + dircLp = kdircEuroNum; + // else could possibly be unknown if we are at the end of the stream + } + } + + // W6. Separators and terminators change to neutral. (Don't change + // boundary neutrals at this point, because that would confuse step Pre-L1.) + if (dircLp == kdircEuroSep || dircLp == kdircEuroTerm || dircLp == kdircComSep) + { + dircLp = kdircNeutral; + } + m_vpslot[islotLp]->SetDirProcessed(dircLp); + } + + // W7. European numbers are changed to plain left-to-right if the previous strong type + // was left-to-right. + dircPrevStrong = AdjacentStrongCode(pengst, islot - 1, -1, dircMarker); + if (dircPrevStrong == kdircL) + { + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = (m_vpslot[islotLp]->DirProcessed()); + if (dircLp == kdircEuroNum) + m_vpslot[islotLp]->SetDirProcessed(kdircL); + } + } + + // Pre-L1. Make a list of all the trailing whitespace characters that need to take + // on the direction of the final line break (ie, the top direction). We can't actually + // change them yet, because that would confuse the loop below. But we have to + // record them now because they might get changed in the loop below. + std::vector<int> vislotTrailingWS; + int islotFinalLB = -1; + for (islotLp = islotWeakRunLim; islotLp-- > islot; ) + { + if (m_vpslot[islotLp]->IsFinalLineBreak(pengst->TableManager()->LBGlyphID())) + { + islotFinalLB = islotLp; + for (int islotLp2 = islotLp; islotLp2-- > islot; ) + { + dircLp = m_vpslot[islotLp2]->DirProcessed(); + if (dircLp == kdircWhiteSpace || dircLp == kdircBndNeutral) + vislotTrailingWS.push_back(islotLp2); + else + break; // hit something other than whitespace or neutral + } + break; + } + } + + // N1. A sequence of neutrals takes the direction of the strong surrounding text if + // the text on both sides has the same direction. Latin and Arabic numbers are treated + // as if they are right-to-left. + // (If they are not the same, leave the type as neutral.) + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + { + dircLp = m_vpslot[islotLp]->DirProcessed(); + + // Change Boundary Neutrals to plain neutrals (must happen after Pre-L1). + if (dircLp == kdircBndNeutral) + { + m_vpslot[islotLp]->SetDirProcessed(kdircNeutral); + dircLp = kdircNeutral; + } + + if (NeutralDir(dircLp)) + { + // First, look for either a strong code or a number. If we get at least + // one strong code and a number with the same direction, use that strong + // code. + DirCode dircPrev = AdjacentStrongCode(pengst, islotLp - 1, -1, dircMarker, true); + DirCode dircNext = AdjacentStrongCode(pengst, islotLp + 1, 1, dircMarker, true); + + if (dircNext == kdircUnknown) + return -1; + + if (dircPrev == kdircUnknown) + dircPrev = dircNext; + + DirCode dircResult = kdircUnknown; + if (dircNext == kdircNeutral) + {} + else if (RightToLeftDir(dircPrev) == RightToLeftDir(dircNext) && + (StrongDir(dircNext) || StrongDir(dircPrev))) + { + // Got a strong matching direction. + dircResult = (StrongDir(dircPrev)) ? dircPrev : dircNext; + } + else if (dircNext == kdircPdfL || dircNext == kdircPdfR) + { + // A following PDF flag wins, because it represents the edge of a run. + dircResult = dircNext; + } + else if (StrongDir(dircNext) && StrongDir(dircPrev)) + { // We must have text in different directions. + } + else + { + // Try again, insisting on strong codes, no numbers. + if (!StrongDir(dircPrev)) + dircPrev = AdjacentStrongCode(pengst, islotLp - 1, -1, dircMarker, false); + if (!StrongDir(dircNext)) + dircNext = AdjacentStrongCode(pengst, islotLp + 1, 1, dircMarker, false); + if (dircPrev == kdircUnknown) + dircPrev = dircNext; + + if (RightToLeftDir(dircPrev) == RightToLeftDir(dircNext) && + (StrongDir(dircNext) || StrongDir(dircPrev))) + { + // Got a strong matching direction. + dircResult = (StrongDir(dircPrev)) ? dircPrev : dircNext; + } + } + + if (dircResult != kdircUnknown) + { + dircResult = (RightToLeftDir(dircResult)) ? kdircR : kdircL; + m_vpslot[islotLp]->SetDirProcessed(dircResult); + } + // else leave as neutral + } + } + + // L1. Now change trailing neutrals recorded above to the directionality of + // the immediately following line-break, if any. + if (islotFinalLB != -1) + { + DirCode dircLB = m_vpslot[islotFinalLB]->DirProcessed(); + dircLB = (dircLB == kdircLlb) ? kdircL : kdircR; + for (int iislot = 0; iislot < signed(vislotTrailingWS.size()); iislot++) + { + int islotTmp = vislotTrailingWS[iislot]; + m_vpslot[islotTmp]->SetDirProcessed(dircLB); + } + } + + // Mark all the slots processed. + for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) + m_vpslot[islotLp]->MarkDirProcessed(); + } + else + m_vpslot[islot]->MarkDirProcessed(); + +LSetDir: + + // Final resolution. + + dirc = pslot->DirProcessed(); + + if (dirc != kdircLlb && dirc != kdircRlb && dircOverride != kdircNeutral) + { + // Enforce whatever is imposed by the override. + m_vpslot[islot]->SetDirProcessed(dircOverride); + nDirLevel = nCurrLevel; + } + else if ((nCurrLevel % 2) == 0) // current level is even (left-to-right) + { + switch (dirc) + { + case kdircNeutral: + case kdircWhiteSpace: + nDirLevel = nCurrLevel; + break; + + case kdircL: + nDirLevel = nCurrLevel; + break; + + case kdircLlb: + case kdircRlb: // all line-breaks have the direction of the paragraph + nDirLevel = nOuterLevel; + break; + + case kdircR: + case kdircRArab: + nDirLevel = nCurrLevel + 1; + break; + + case kdircEuroNum: + case kdircArabNum: + nDirLevel = nCurrLevel + 2; + break; + + default: + Assert(false); + nDirLevel = nCurrLevel; + } + } + else // current level is odd (right-to-left) + { + switch (dirc) + { + case kdircNeutral: + case kdircWhiteSpace: + nDirLevel = nCurrLevel; + break; + + case kdircR: + //case kdircRlb: + case kdircRArab: + nDirLevel = nCurrLevel; + break; + + case kdircL: + //case kdircLlb: + case kdircEuroNum: + case kdircArabNum: + nDirLevel = nCurrLevel + 1; + break; + + case kdircLlb: + case kdircRlb: // all line-breaks have the direction of the paragraph + nDirLevel = nOuterLevel; + break; + + default: + Assert(false); + nDirLevel = nCurrLevel; + } + } + pslot->SetDirLevel(nDirLevel); + + return nDirLevel; +} + +/*---------------------------------------------------------------------------------------------- + Return the next or previous strong code, kdircNeutral if we don't find one, + or kdircUnknown if we need more slots to process. + + @param islot - index of slot of interest + @param nInc - +1 to search forward, -1 to search backward + @param fNumbersAreStrong - true if we want to treat numbers as having strong + directionality + @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) +----------------------------------------------------------------------------------------------*/ +DirCode GrSlotStream::AdjacentStrongCode(EngineState * pengst, int islot, int nInc, + DirCode dircPDF, bool fNumbersAreStrong) +{ + if (islot < 0) + return pengst->InitialStrongDir(); + + else if (islot >= m_islotWritePos) + { + if (m_fFullyWritten || (m_islotSegLim > -1 && islot >= SegLimIfKnown())) + return kdircNeutral; + else + return kdircUnknown; + } + + DirCode dirc = m_vpslot[islot]->DirProcessed(); + + if (dirc == kdircPDF && StrongDir(dircPDF)) + return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; + if (StrongDir(dirc)) + return dirc; + if (fNumbersAreStrong && (dirc == kdircEuroNum || dirc == kdircArabNum)) + return dirc; + + return AdjacentStrongCode(pengst, islot + nInc, nInc, dircPDF, fNumbersAreStrong); +} + +/*---------------------------------------------------------------------------------------------- + Return kdircEuroNum if there is a terminator sequence starting or ending with a + European number. Return kdircUnknown if we need more slots to process. + Return kdircNeutral otherwise. + + @param islot - index of slot of interest + @param nInc - +1 to search forward, -1 to search backward + @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) +----------------------------------------------------------------------------------------------*/ +DirCode GrSlotStream::TerminatorSequence(EngineState * pengst, int islot, int nInc, + DirCode dircPDF) +{ + if (islot < 0) + return pengst->InitialTermDir(); + + else if (islot >= m_islotWritePos) + { + if (m_fFullyWritten) + return kdircNeutral; + else + return kdircUnknown; + } + + DirCode dirc = m_vpslot[islot]->DirProcessed(); + + if (dirc == kdircPDF) + return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; + else if (dirc == kdircEuroNum) + // number found at end of terminator sequence + return kdircEuroNum; + else if (dirc == kdircEuroTerm) + // yes, this slot is a terminator; are there more? + return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); + else if (dirc == kdircLlb || dirc == kdircRlb) + // line-break slot--ignore and keep going + return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); + else if (dirc == kdircBndNeutral) + // boundary neutral--ignore and keep going + return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); + else + // no number found + return kdircNeutral; +} + +/*---------------------------------------------------------------------------------------------- + Return the previous code that is not a Boundary Neutral. Return Other Neutral if there + is no such. + + @param islot - index of slot of interest + @param nInc - +1 to search forward, -1 to search backward + @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) +----------------------------------------------------------------------------------------------*/ +DirCode GrSlotStream::AdjacentNonBndNeutralCode(EngineState * pengst, int islot, int nInc, + DirCode dircPDF) +{ + Assert(islot < m_islotWritePos); + if (islot < 0) + return kdircNeutral; // or should we use: ptman->InitialStrongDir() ?? + + else if (islot >= m_islotWritePos) + { + if (m_fFullyWritten) + return kdircNeutral; + else + return kdircUnknown; + } + + DirCode dirc = m_vpslot[islot]->DirProcessed(); + + if (dirc == kdircBndNeutral) + return AdjacentNonBndNeutralCode(pengst, islot + nInc, nInc, dircPDF); + else if (dirc == kdircPDF) + return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; + else + return dirc; +} + +/*---------------------------------------------------------------------------------------------- + Directionality type functions. +----------------------------------------------------------------------------------------------*/ +bool StrongDir(DirCode dirc) +{ + return (dirc == kdircL || dirc == kdircR || dirc == kdircRArab || + dirc == kdircLRO || dirc == kdircRLO || dirc == kdircLRE || dirc == kdircRLE || + dirc == kdircPdfL || dirc == kdircPdfR); +} + +bool WeakDir(DirCode dirc) +{ + return (dirc == kdircEuroNum || dirc == kdircEuroSep || + dirc == kdircEuroTerm || dirc == kdircArabNum || + dirc == kdircComSep || dirc == kdircBndNeutral || dirc == kdircNSM); +} + +bool NeutralDir(DirCode dirc) +{ + return (dirc == kdircWhiteSpace || dirc == kdircNeutral); +} + +/*---------------------------------------------------------------------------------------------- + Special values used by the bidi algorithm. +----------------------------------------------------------------------------------------------*/ +bool BidiCode(int nUnicode) +{ + switch (nUnicode) + { + case knLRM: + case knRLM: + case knLRO: + case knRLO: + case knLRE: + case knRLE: + case knPDF: + return true; + default: + break; + } + return false; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the directionality should be treated as right-to-left. + Used for the step of resolving neutrals (N1). +----------------------------------------------------------------------------------------------*/ +bool RightToLeftDir(DirCode dirc) +{ + switch (dirc) + { + case kdircL: + case kdircLRO: + case kdircLRE: + case kdircPdfL: + case kdircLlb: + return false; + + case kdircR: + case kdircRArab: + case kdircArabNum: + case kdircEuroNum: + case kdircRLO: + case kdircRLE: + case kdircPdfR: + return true; + + case kdircNeutral: + case kdircWhiteSpace: + case kdircComSep: + case kdircEuroSep: + case kdircEuroTerm: + case kdircBndNeutral: + case kdircNSM: + case kdircPDF: + return false; + + default: + Assert(false); + return false; + } +} + +/*---------------------------------------------------------------------------------------------- + Copy a slot from prevStream, incrementing the positions. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::CopyOneSlotFrom(GrSlotStream * psstrmPrev) +{ +#ifdef _DEBUG + gid16 chw; chw = psstrmPrev->Peek()->GlyphID(); +#endif + + // If we are exactly at the segment boundary, pass the information on to this stream. + if (psstrmPrev->AtSegMin()) + SetSegMinToWritePos(); + if (psstrmPrev->AtSegLim()) + SetSegLimToWritePos(); + + NextPut(psstrmPrev->NextGet()); + + AssertValid(); + psstrmPrev->AssertValid(); +} + +/*---------------------------------------------------------------------------------------------- + Copy one slot from the input stream to the recipient, which is the output stream. + Used when reversing ranges of glyphs; caller is responsible for adjusting the read- + and write-positions of the streams. + + REVIEW: How should this handle the reprocessing sub-stream? Currently this is not a problem + because this function is only used by the bidi pass, which never has intra-pass + reprocessing. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::SimpleCopyFrom(GrSlotStream * psstrmI, int islotInput, int islotOutput) +{ + if (signed(m_vpslot.size()) < islotOutput + 1) + { + m_vpslot.resize(islotOutput + 1); + m_vislotPrevChunkMap.resize(islotOutput + 1); + m_vislotNextChunkMap.resize(islotOutput + 1); + } + + m_vpslot[islotOutput] = psstrmI->m_vpslot[islotInput]; + m_vislotPrevChunkMap[islotOutput] = -1; + m_vislotNextChunkMap[islotOutput] = -1; + + Assert(m_fUsedByPosPass); + if (m_fUsedByPosPass && GotIndexOffset()) + m_vpslot[islotOutput]->SetPosPassIndex(islotOutput - m_cslotPreSeg, m_fInputToPosPass1); + +} + +/*---------------------------------------------------------------------------------------------- + Return true if there is physical space to add more glyphs, false if the positioning + has caused them to use all the alotted space. Recipient is assumed to be output + stream from the final pass. + + @param ptman - table manager, for handling the positioning + @param pgg - graphics device + @param xsSpaceAllotted - how much space is available + @param fWidthIsCharCount - kludge for test procedures + @param fIgnoreTrailingWS - true if we are ignoring trailing white-space (currently + always true) + @param twsh - how the segment is handling trailing white-space + @param pxsWidth - return width of stuff so far +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::MoreSpace(GrTableManager * ptman, + float xsSpaceAllotted, bool fWidthIsCharCount, + bool fIgnoreTrailingWS, TrWsHandling twsh, + float * pxsWidth) +{ + Assert(ptman->NumberOfPasses() - 1 == m_ipass); + + if (fWidthIsCharCount) + { + // Used in test procedures + *pxsWidth = (float)m_islotWritePos; + return (m_islotWritePos < xsSpaceAllotted); + } + else + { + *pxsWidth = -1; + if (!GotIndexOffset()) + return true; + if (WritePos() <= IndexOffset()) + return true; + int dislot = MaxClusterSlot(WritePos()); + if (dislot == kNegInfinity || dislot > 0) + // We're in the middle of processing a cluster--we have to keep processing, + // so for now assume there is more space. + return true; + + float xsWidth, xsVisWidth; + ptman->CalcPositionsUpTo(m_ipass, NULL, &xsWidth, &xsVisWidth); + + *pxsWidth = (fIgnoreTrailingWS || twsh == ktwshOnlyWs) ? xsVisWidth : xsWidth; + return (*pxsWidth < xsSpaceAllotted); + } +} + +/*---------------------------------------------------------------------------------------------- + Work backwards from the prevLineBreak position (or if none, the readPos) to find the + previous line-break of the given weight or less. Return the position of the inserted + line-break slot, or -1 if no appropriate break point was found. Recipient should be + the output of the final line break pass, or if none, the output of the + glyph-generation pass. + + @param ptman - table manager, for supplying new slots + @param islotPrevBreak - position of previous inserted line break + @param fInsertedLB - did we actually insert a line-break glyph before? + @param islotStartTry - where to start looking, or -1 for the end of the stream + @param lb - (max) break weight to allow + @param twsh - how we are handling trailing white-space + @param islotMin - first slot that is officially part of the segment (after initial LB) + @param plbNextToTry - the best we found +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::InsertLineBreak(GrTableManager * ptman, + int islotPrevBreak, bool fInsertedLB, int islotStartTry, + LineBrk lb, TrWsHandling twsh, int islotMin, + LineBrk * plbNextToTry) +{ + EngineState * pengst = ptman->State(); + + Assert(!fInsertedLB || islotPrevBreak > -1); + + int ichwSegOffset; + int islot; + LineBrk lbFound; + if (!FindSegmentEnd(ptman, islotStartTry, lb, twsh, islotMin, + &islot, &ichwSegOffset, &lbFound, plbNextToTry)) + { + return -1; // didn't find a legal break + } + + GrSlotState * pslotCopyFeat = pengst->AnAdjacentSlot(m_ipass, islot + 1); + Assert(pslotCopyFeat); + + if (islotPrevBreak > -1 && fInsertedLB) + { + //GrSlotState * pslotOld = m_vpslot[islotPrevBreak]; + + m_vpslot.erase(m_vpslot.begin() + islotPrevBreak); + m_vislotPrevChunkMap.erase(m_vislotPrevChunkMap.begin() + islotPrevBreak); + m_vislotNextChunkMap.erase(m_vislotNextChunkMap.begin() + islotPrevBreak); + AdjustPrevStreamNextChunkMap(ptman, islotPrevBreak + 1, -1); + } + else + { + m_islotReadPos++; + m_islotWritePos++; + } + + GrSlotState * pslotNew; + pengst->NewSlot(ptman->LBGlyphID(), pslotCopyFeat, 0, ichwSegOffset, &pslotNew); + + pslotNew->SetSpecialSlotFlag(kspslLbFinal); + pslotNew->SetBreakWeight(lbFound); + pslotNew->SetDirectionality(ptman->RightToLeft() ? kdircRlb : kdircLlb); + + int islotLB = islot + 1; + m_vpslot.insert(m_vpslot.begin() + islotLB, pslotNew); + // Inserting -1 makes it part of a chunk with the previous char/glyph + m_vislotPrevChunkMap.insert(m_vislotPrevChunkMap.begin() + islotLB, -1); + m_vislotNextChunkMap.insert(m_vislotNextChunkMap.begin() + islotLB, -1); + AdjustPrevStreamNextChunkMap(ptman, islotLB, 1); + // We don't need to adjust the following stream because we are going to unwind it anyway. + + if (m_fUsedByPosPass && GotIndexOffset()) + { + pslotNew->SetPosPassIndex(islot - m_cslotPreSeg, m_fInputToPosPass1); + // Increment the stream index for the following slots. + for (int islotTmp = islotLB; + islotTmp < ((islotPrevBreak == -1) ? m_islotWritePos : islotPrevBreak + 1); + islotTmp++) + { + SlotAt(islotTmp)->IncPosPassIndex(); + } + } + + m_islotSegLim = islotLB + 1; // after the line-break glyph + + return islotLB; +} + +/*---------------------------------------------------------------------------------------------- + Make a segment break that does not correspond to a line break; ie, don't insert + a line-break glyph. + + Work backwards from the prevLineBreak position (or if none, the readPos) to find the + previous line-break of the given weight or less. Return the position of the final slot + in the segment, or -1 if no appropriate break point was found. Recipient should be + the output of the final line break pass, if any, or the glyph-generation pass. + + @param ptman - table manager, for supplying new slots + @param islotPrevBreak - position of previous inserted line break + @param fInsertedLB - did we actually insert a line-break glyph previously; currently + ignored + @param islotStartTry - where to start looking, or -1 for the end of the stream + @param lb - (max) break weight to allow + @param twsh - how we are handling trailing white-space + @param islotMin - first slot that is officially part of the segment (after initial LB) +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::MakeSegmentBreak(GrTableManager * ptman, + int islotPrevBreak, bool fInsertedLB, int islotStartTry, + LineBrk lb, TrWsHandling twsh, int islotMin, + LineBrk * plbNextToTry) +{ + int ichwSegOffset; + int islot; + LineBrk lbFound; + if (!FindSegmentEnd(ptman, islotStartTry, lb, twsh, islotMin, + &islot, &ichwSegOffset, &lbFound, plbNextToTry)) + { + return -1; // didn't find a legal break + } + + // Review: do we need to delete any previously-inserted line-break glyph from the stream? + // Doesn't seem like this has been a problem so far. + + m_islotSegLim = islot + 1; // after the final glyph of this segment + + return islot; +} + +/*---------------------------------------------------------------------------------------------- + Work backwards from the prev line break position (or if none, the readPos) to find an + earlier line-break of the given weight or less. + Recipient should be the output of the final line break pass, if any, or the output + of the glyph-generation pass. + + @param ptman - table manager, for supplying new slots + @param islotStartTry - where to start looking, or -1 for the end of the stream + @param lb - (max) break weight to allow + @param twsh - how we are handling trailing white-space + @param islotMin - first slot that is officially part of the segment (after initial LB) + @param pislot - slot AFTER which the break will go (not the lim of the segment) + @param pichwSegOffset - offset relative to the segment + @param plbFound - weight of break to be created + @param plbNextToTry - best found, in case we didn't find one at the requested level + + @return Whether or not an appropriate break was found. +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::FindSegmentEnd(GrTableManager * ptman, + int islotStartTry, LineBrk lb, TrWsHandling twsh, int islotMin, + int * pislot, int * pichwSegOffset, LineBrk * plbFound, + LineBrk * plbNextToTry) +{ + Assert(AssertValid()); + Assert(m_ipass == ptman->NumberOfLbPasses()); + + if (islotStartTry < 0) + return false; + + *pislot = islotStartTry; + GrSlotState * pslot = m_vpslot[*pislot]; + *plbNextToTry = klbClipBreak; // worst break + + ptman->State()->SetRemovedTrWhiteSpace(false); + + while (twsh != ktwshOnlyWs) // in white-space only case, ignore lb values + { + if (*pislot < islotMin) + { + return false; + } + *plbFound = LineBrk(pslot->m_lb); + // Sanity check + if (abs(*plbFound) > klbClipBreak) + { +// Assert(false); + Warn("Unusually large breakweight"); + *plbFound = (LineBrk)((*plbFound < 0) ? -klbClipBreak : klbClipBreak); + } + *plbNextToTry = (LineBrk)(min(static_cast<int>(*plbNextToTry), abs(*plbFound))); + *pichwSegOffset = pslot->SegOffset(); + + if (int(*plbFound) >= 0 && *plbFound <= lb) + break; + + --(*pislot); + if (*pislot < 0) + { + return false; + } + pslot = m_vpslot[*pislot]; + + if (int(*plbFound) <= 0 && ((int)*plbFound * -1) <= lb) + { + *plbFound = LineBrk(int(*plbFound) * -1); + break; + } + } + + // Found a good break. + + if (twsh == ktwshOnlyWs) + { + // white-space-only segment + while (pslot->Directionality() != kdircWhiteSpace) + { + --(*pislot); + if (*pislot < 0) + return false; + pslot = m_vpslot[*pislot]; + *pichwSegOffset = pslot->SegOffset(); + } + } + else if (twsh == ktwshNoWs) + { + // no trailing white-space: remove it + while (pslot->Directionality() == kdircWhiteSpace) + { + --(*pislot); + if (*pislot < 0) + return false; + pslot = m_vpslot[*pislot]; + *pichwSegOffset = pslot->SegOffset(); + ptman->State()->SetRemovedTrWhiteSpace(true); + } + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the stream (which should be the output of the last linebreak pass) has + a better break at some earlier point than the one we found. + + @param islotBreak - position of inserted or final break point in stream, or index + of last valid slot +----------------------------------------------------------------------------------------------*/ +bool GrSlotStream::HasEarlierBetterBreak(int islotBreak, LineBrk lbFound, gid16 chwLB) +{ + Assert(AssertValid()); + + int islot = islotBreak; + if (SlotAt(islot)->IsFinalLineBreak(chwLB)) + islot--; + + if (lbFound == klbNoBreak) + lbFound = (LineBrk)SlotAt(islot)->BreakWeight(); + + while (islot >= 0) + { + if (SlotAt(islot)->IsInitialLineBreak(chwLB)) + return false; + if (SlotAt(islot)->BreakWeight() < lbFound) + return true; + islot--; + } + + return false; +} + +/*---------------------------------------------------------------------------------------------- + Make sure the stream contains a local instance of the given slot, not a copy used by + a previous pass. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::EnsureLocalCopy(GrTableManager * ptman, GrSlotState * pslot, + GrSlotStream * psstrmIn) +{ + //Assert(m_vpslot[islot + m_cslotPreSeg] == pslot); + Assert(pslot->PassModified() <= m_ipass); + + if (pslot->PassModified() < m_ipass) + { + int islot = pslot->PosPassIndex(); + Assert(SlotAtPosPassIndex(islot) == pslot); + GrSlotState * pslotNew; + ptman->State()->NewSlotCopy(pslot, m_ipass, &pslotNew); + m_vpslot[islot + m_cslotPreSeg] = pslotNew; + + psstrmIn->ReplaceSlotInReprocessBuffer(pslot, pslotNew); + } +} + +/*---------------------------------------------------------------------------------------------- + A slot has been replace within the coorsponding output stream. Make sure the reprocess + buffer of this stream constains the same slot. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::ReplaceSlotInReprocessBuffer(GrSlotState * pslotOld, GrSlotState * pslotNew) +{ + if (m_islotReprocPos > -1) + { + for (size_t islot = 0; islot < m_vpslotReproc.size(); islot++) + { + if (m_vpslotReproc[islot] == pslotOld) + m_vpslotReproc[islot] = pslotNew; + } + } +} + +/*---------------------------------------------------------------------------------------------- + Inserting a line break can potentially alter the directionality of preceeding glyphs. + Zap the information that has been calculated. + + @param islotLB - index of the inserted final line-break glyph +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::ZapCalculatedDirLevels(int islotLB) +{ + int islot; + for (islot = m_islotWritePos; islot-- > islotLB; ) + m_vpslot[islot]->ZapDirLevel(); + + for (islot = islotLB; islot-- > 0; ) + { + if (StrongDir(m_vpslot[islot]->Directionality())) + // We've found a strong direction code; don't need to zap beyond this point. + return; + + m_vpslot[islot]->ZapDirLevel(); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the slot index of the last slot in the cluster for the just-processed chunk, + relative to the last slot in the chunk. + + Return kNegInfinity if there are not enough slots in the stream to tell. This can + happen while we are in the middle of processing a cluster. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::MaxClusterSlot(int islotChunkMin, int islotChunkLim) +{ + Assert(islotChunkLim > islotChunkMin); + + if (!m_fUsedByPosPass) + return 0; // no clusters yet + + int islotRet = SlotAt(islotChunkLim - 1)->PosPassIndex(); + + for (int islot = islotChunkMin; islot < islotChunkLim; islot++) + { + GrSlotState * pslot = SlotAt(islot); + + if (m_cslotPreSeg == -1) + return kNegInfinity; + if (!HasSlotAtPosPassIndex(pslot->AttachRootPosPassIndex())) + return kNegInfinity; + + GrSlotState * pslotBase = pslot->Base(this); + + int dislotOffset = pslotBase->LastLeafOffset(this); + if (dislotOffset == kNegInfinity) + return kNegInfinity; + islotRet = max(islotRet, pslotBase->PosPassIndex() + dislotOffset); + } + + // Make it relative to the last slot in the chunk. + islotRet -= SlotAt(islotChunkLim - 1)->PosPassIndex(); + + return islotRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the break weight of the given slot, which should be a line-break. + OBSOLETE?? +----------------------------------------------------------------------------------------------*/ +LineBrk GrSlotStream::BreakWeightAt(gid16 chwLB, int islot) +{ + GrSlotState * pslot = GetSlotAt(islot); + Assert(pslot->IsLineBreak(chwLB)); + return LineBrk(pslot->m_lb); +} + +/*---------------------------------------------------------------------------------------------- + Append an initial or final line break of the given weight to the stream. + + @param ptman - table manager, for generating new slot + @param lb - break weight for the line-break glyph + @param dirc - directionality code for the line-break glyph + @param islotLB - where to insert, or -1 if at the end + @param fInitial - is this an initial line-break, or a final break? + @param ichwSegOffset - offset relative to the beginning of the segment +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AppendLineBreak(GrTableManager * ptman, GrCharStream * pchstrm, + LineBrk lb, DirCode dirc, int islotLB, bool fInitial, int ichwSegOffset) +{ + EngineState * pengst = ptman->State(); + + Assert(AssertValid()); +/// Assert(m_islotReprocPos == -1); + + Assert(islotLB == -1 || fInitial); + if (islotLB == -1) + islotLB = m_islotWritePos; + +// int islot = m_islotWritePos; + + GrSlotState * pslotCopyFeat = pengst->AnAdjacentSlot(m_ipass, islotLB); + GrSlotState * pslotNew; + if (pslotCopyFeat) + pengst->NewSlot(ptman->LBGlyphID(), pslotCopyFeat, 0, ichwSegOffset, &pslotNew); + else + { + GrFeatureValues fval; + pchstrm->CurrentFeatures(ptman, &fval); + pengst->NewSlot(ptman->LBGlyphID(), fval, 0, ichwSegOffset, -1, &pslotNew); + } + + pslotNew->m_lb = sdata8(lb); + pslotNew->SetSpecialSlotFlag(fInitial ? kspslLbInitial : kspslLbFinal); + //pslotNew->m_fInitialLB = fInitial; // TODO: remove + pslotNew->SetDirectionality(dirc); + + m_vpslot.insert(m_vpslot.begin() + islotLB, pslotNew); + // Make it part of a chunk with previous char/glyph + m_vislotPrevChunkMap.insert(m_vislotPrevChunkMap.begin() + islotLB, -1); + m_vislotNextChunkMap.insert(m_vislotNextChunkMap.begin() + islotLB, -1); + if (m_ipass > 0) + AdjustPrevStreamNextChunkMap(ptman, islotLB, 1); + + m_islotWritePos++; + + if (m_fUsedByPosPass && GotIndexOffset()) + { + pslotNew->SetPosPassIndex(islotLB - m_cslotPreSeg, m_fInputToPosPass1); + // Increment the stream index for the following slots. + for (int islotTmp = islotLB + 1; islotTmp < m_islotWritePos; islotTmp++) + { + SlotAt(islotTmp)->IncPosPassIndex(); + } + } + + if (fInitial) + m_islotSegMin = islotLB; // just before the LB + else + m_islotSegLim = islotLB + 1; // just after the LB +} + +/*---------------------------------------------------------------------------------------------- + Return the index of the line break glyph within the specified range, or -1 if none. + Recipient is functioning as the output stream. + + @param chwLB - glyph ID being used for line-break glyphs + @param islotMin/Lim - range to search +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::FindFinalLineBreak(gid16 chwLB, int islotMin, int islotLim) +{ + for (int islot = islotMin; islot < islotLim; ++islot) + { + if (m_vpslot[islot]->IsFinalLineBreak(chwLB)) + return islot; + } + return -1; +} + +/*---------------------------------------------------------------------------------------------- + A slot has been inserted (nInc == 1) or deleted (nInc == -1). Adjust the next-chunk map + of the previous stream to match. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AdjustPrevStreamNextChunkMap(GrTableManager * ptman, int islotMod, int nInc) +{ + if (m_ipass == 0) + return; // no previous stream to adjust + + int islotTmp = max(islotMod - 5, 0); + while (islotTmp > 0 && m_vislotPrevChunkMap[islotTmp] == -1) + islotTmp--; + + ptman->InputStream(m_ipass)->AdjustNextChunkMap( + ((islotTmp <= 0) ? 0 : m_vislotPrevChunkMap[islotTmp]), + islotMod, nInc); +} + +/*---------------------------------------------------------------------------------------------- + An insertion or deletion has occurred at the given location in the following stream + (specifically of an line-break glyph). Adjust the next-chunk-map accordingly so + the indices still match. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AdjustNextChunkMap(int islotMin, int islotInsOrDel, int nInc) +{ + for (int islot = islotMin; islot < m_islotWritePos; islot++) + { + if (m_vislotNextChunkMap[islot] != -1 && m_vislotNextChunkMap[islot] >= islotInsOrDel) + m_vislotNextChunkMap[islot] += nInc; + } +} + +/*---------------------------------------------------------------------------------------------- + Unwind the read position of the stream, so that the following pass will begin + reading from the new position. The recipient is functioning as the input stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::UnwindInput(int islotNewPos, bool fPreBidiPass) +{ + Assert(m_islotReprocPos == -1); + Assert(islotNewPos <= m_islotReadPos); + + int islot; + for (islot = islotNewPos; islot < m_islotReadPos; ++islot) + m_vislotNextChunkMap[islot] = -1; + + m_islotReadPos = islotNewPos; + m_islotReadPosMax = m_islotReadPos; + + Assert(m_islotReadPos <= m_islotWritePos); + if (fPreBidiPass) + { + for (islot = m_islotReadPos; islot < m_islotWritePos; ++islot) + SlotAt(islot)->ZapDirLevel(); + } +} + +/*---------------------------------------------------------------------------------------------- + Unwind the write position of the stream, so that the pass will begin writing + at the new position. The recipient is functioning as the output stream +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::UnwindOutput(int islotNewPos, bool fOutputOfPosPass) +{ + Assert(islotNewPos <= m_islotWritePos); + + int islot; + for (islot = islotNewPos; islot < m_islotWritePos; ++islot) + { + m_vislotPrevChunkMap[islot] = -1; + + if (!fOutputOfPosPass && m_fUsedByPosPass) + // This stream is the input to the first positioning pass, ie, the output + // of the last sub pass or the bidi pass. Zap the stream indices, + // since unwinding this pass could cause them to be invalid due to a later + // reordering. + SlotAt(islot)->ZapPosPassIndex(); + } + + m_islotWritePos = islotNewPos; + + m_fFullyWritten = false; + + if (m_islotSegMin > m_islotWritePos) + m_islotSegMin = -1; + if (m_islotSegLim > m_islotWritePos) + m_islotSegLim = -1; +} + +/*---------------------------------------------------------------------------------------------- + We've just created a new chunk, with the recipient as the output stream. The beginning + of the chunk is passed as 'islotOutput' and the end of the chunk is assumed to be the + write-position of the recipient. Store the mappings into the corresponding + input position. + + @param islotInputMin - beginning of chunk in input stream + @param islotOutputMin - beginning of chunk in output stream (recipient) + @param islotInputLim - end of chunk in input stream (currently not used) + @param fSkipChunkStart - if true, either we have reprocessed part of a + previously created chunk, + or there was no input consumed; + in either case, don't record the beginning of the chunk, + just make this chunk be part of the previous + @param fBackingUp - this chunk results in the stream position moving backwards, + so clear anything we're backing over +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::MapInputChunk(int islotInputMin, int islotOutputMin, int islotInputLim, + bool fSkipChunkStart, bool fBackingUp) +{ + Assert(AssertValid()); + Assert(islotOutputMin >= -1); + + if (!fSkipChunkStart) + { + Assert(islotOutputMin >= 0); + if (islotOutputMin >= 0) // just in case an invalid value was passed + m_vislotPrevChunkMap[islotOutputMin] = islotInputMin; + } + + int islot; + for (islot = max(0, islotOutputMin + 1); islot < m_islotWritePos; ++islot) + m_vislotPrevChunkMap[islot] = -1; // not a chunk boundary + +// if (fBackingUp && m_islotWritePos < m_vislotPrevChunkMap.Size()) +// m_vislotPrevChunkMap[m_islotWritePos] = -1; + + if (fBackingUp) + for (islot = m_islotWritePos; islot < signed(m_vislotPrevChunkMap.size()); islot++) + m_vislotPrevChunkMap[islot] = -1; + +// m_vislotPrevChunkMap[m_islotWritePos] = islotInputLim; +} + +/*---------------------------------------------------------------------------------------------- + Return the length of the final chunk, where the recipient is the input stream. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::LastNextChunkLength() +{ + int cslotRet = 1; + for (int islot = m_islotReadPos; islot-- > 0; ) + { + if (m_vislotNextChunkMap[islot] != -1) + return cslotRet; + cslotRet++; + } + return cslotRet + 1; +} + +/*---------------------------------------------------------------------------------------------- + We've just created a new chunk, with the recipient as the input stream. The beginning of + the chunk is passed as 'islotInputMin' and the end of the chunk is assumed to be the + read-position of the recipient. Store the mappings into the corresponding + output position. + + @param islotOutputMin - beginning of chunk in output stream + @param islotInputMin - beginning of chunk in input stream (recipient) + @param islotOutputLim - end of chunk in output stream (currently not used) + @param fSkipChunkStart - if true, either we have reprocessed part of a + previously created chunk, + or there was no output generated; + in either case, don't record the beginning of the chunk, + just make this chunk be part of the previous + @param fBackingUp - this chunk results in the stream position moving backwards, + so clear anything we're backing over +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::MapOutputChunk(int islotOutputMin, int islotInputMin, int islotOutputLim, + bool fSkipChunkStart, int cslotReprocess, bool fBackingUp) +{ + Assert(AssertValid()); + + // Note: islotInputMin can be less than -1 if there is a large reprocess buffer near the + // beginning of the input stream. In which case, fSkipChunk start should be true and + // cslotReprocess large enough to keep any of the stuff below from having any problems. + //Assert(islotInputMin >= -1); + + if (!fSkipChunkStart) + { + Assert(islotInputMin >= 0); + if (islotInputMin >= 0) // just in case an invalid value was passed + m_vislotNextChunkMap[islotInputMin] = islotOutputMin; + } + + int islot; + for (islot = max(0, islotInputMin + 1 + cslotReprocess); islot < m_islotReadPos; ++islot) + m_vislotNextChunkMap[islot] = -1; // not a chunk boundary + +// if (fBackingUp && m_islotReadPos < m_vislotNextChunkMap.Size()) +// m_vislotNextChunkMap[m_islotReadPos] = -1; + + if (fBackingUp) + for (islot = m_islotReadPos; islot < signed(m_vislotNextChunkMap.size()); islot++) + m_vislotNextChunkMap[islot] = -1; + +// m_vislotNextChunkMap[m_islotReadPos] = islotOutputLim; +} + +/*---------------------------------------------------------------------------------------------- + Ensure that the chunk maps for a pair of streams match properly. The recipient is + the input stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AssertChunkMapsValid(GrSlotStream * psstrmOut) +{ +#ifdef _DEBUG + GrSlotStream * psstrmIn = this; + + Assert(m_ipass == 0 || (m_ipass + 1 == psstrmOut->m_ipass)); + + int islotMapNext; + int islotMapPrev; + int islot; + for (islot = 0; islot < m_islotReadPos; ++islot) + { + islotMapNext = psstrmIn->ChunkInNext(islot); + Assert(islotMapNext != -2); + if (islotMapNext != -1) + { + islotMapPrev = psstrmOut->ChunkInPrev(islotMapNext); + if (islotMapPrev == -2) + { + Assert(islot == psstrmOut->WritePos()); + } + else + { + Assert(islot == islotMapPrev); + } + } + } + + for (islot = 0; islot < psstrmOut->m_islotWritePos; ++islot) + { + islotMapPrev = psstrmOut->ChunkInPrev(islot); + Assert(islotMapPrev != -2); + if (islotMapPrev != -1) + { + islotMapNext = psstrmIn->ChunkInNext(islotMapPrev); + if (islotMapNext == -2) + { + Assert(islot == psstrmIn->ReadPos()); + } + else + { + Assert(islot == islotMapNext); + } + } + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Ensure that corresponding items in the streams of a positioning pass have matching + stream indices. The recipient is the output stream. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AssertStreamIndicesValid(GrSlotStream * psstrmIn) +{ +#ifdef _DEBUG + if (!GotIndexOffset()) + return; + + Assert(m_fUsedByPosPass); + + for (int islot = 0; islot < WritePos(); islot++) + { + GrSlotState * pslotOut = SlotAt(islot); + GrSlotState * pslotIn = + psstrmIn->SlotAt(islot + psstrmIn->IndexOffset() - IndexOffset()); + Assert(pslotOut->PosPassIndex() == pslotIn->PosPassIndex()); + Assert(pslotOut->HasAsPreviousState(pslotIn)); + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Ensure that the roots of all attachments made in this chunk are present + in the output stream. (Currently the compiler ensures this by making it an error + to write rules that don't do this.) +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::AssertAttachmentsInOutput(int islotMin, int islotLim) +{ +#ifdef _DEBUG + for (int islot = islotMin; islot < islotLim; islot++) + { + GrSlotState * pslotOut = SlotAt(islot); + int dislot = pslotOut->AttachTo(); + Assert(islotMin <= islot + dislot); + Assert(islot + dislot < islotLim); + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Answer the slot index corresponding to the start of the chunk (as mapped to the following + stream). + ENHANCE: make more robust. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::ChunkInNextMin(int islot) +{ + int islotRet = islot; + while (m_vislotNextChunkMap[islotRet] == -1 && islotRet > 0) + --islotRet; + return islotRet; +} + +/*---------------------------------------------------------------------------------------------- + Answer the slot index corresponding to the end of the chunk (as mapped to the following + stream). + ENHANCE: make more robust. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::ChunkInNextLim(int islot) +{ + int islotRet = islot + 1; + if (islotRet == m_islotReadPos) + return islot; + while (m_vislotNextChunkMap[islotRet] == -1 && islotRet < m_islotReadPos) + { + ++islotRet; + //Assert(islotRet < signed(m_vislotNextChunkMap.size())); + if (islotRet >= signed(m_vislotNextChunkMap.size())) + { + islotRet = m_vislotNextChunkMap.size() - 1; + break; + } + } + return islotRet; +} + +/*---------------------------------------------------------------------------------------------- + Skip over the given number slots. This is used to resync the streams when we + are restarting a new segment. Specifically the output stream informs the + input stream of the number of slots to skip to get the boundary for a chunk that + the output finds interesting. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::ResyncSkip(int cslot) +{ + Assert(AssertValid()); + m_islotReadPos += cslot; + m_cslotSkippedForResync = cslot; + // Should never skip past the beginning of the line. + Assert(m_islotSegMin == -1 || m_islotReadPos <= m_islotSegMin); +} + +/*---------------------------------------------------------------------------------------------- + Record the number of slots in the stream that are previous to the official start of the + segment. +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::CalcIndexOffset(GrTableManager * ptman) +{ + if (GotIndexOffset()) + return; // already figured it + + if (m_islotSegMin > -1) + { + m_cslotPreSeg = m_islotSegMin; + if (m_fUsedByPosPass) + { + for (int islotPre = 0; islotPre < WritePos(); islotPre++) + { + SlotAt(islotPre)->SetPosPassIndex(islotPre - m_cslotPreSeg, + m_fInputToPosPass1); + } + } + } + // else can't figure it out yet + + // Old code: +#if 0 + gid16 chwLB = ptman->LBGlyphID(); + + for (int islot = 0; islot < WritePos(); islot++) + { + if (SlotAt(islot)->IsLineBreak(chwLB)) + { + if (SlotAt(islot)->IsInitialLineBreak(chwLB)) + { + m_cslotPreSeg = islot; + } + else + { + // Otherwise, we hit the final line break, which is kind of strange, because + // in this case there is no initial line break, and we should have set + // m_cslotPreSeg to 0 immediately when it was initialized. + Assert(false); + m_cslotPreSeg = 0; + } + + if (m_fUsedByPosPass) + { + for (int islotPre = 0; islotPre < WritePos(); islotPre++) + { + SlotAt(islotPre)->SetPosPassIndex(islotPre - m_cslotPreSeg, + m_fInputToPosPass1); + } + } + return; + } + + } + // No line breaks yet, so no way to tell. +#endif +} + +/*---------------------------------------------------------------------------------------------- + Set the positions in the streams for the next rule. The recipient is the output stream. + If we are moving backwards, copy part of the output stream back to (a temporary buffer + in) the input stream, so that we can reprocess it. + + @param cslotArg - how far to skip forward or back; most commonly zero +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::SetPosForNextRule(int cslotArg, GrSlotStream * psstrmIn, + bool fOutputOfPosPass) +{ + Assert(AssertValid()); + + int cslot = cslotArg; + // Can't reprocess what has already been read by the next pass. MaxBackup should allow + // the flexibility needed, but if not, we fix it here. + if (m_islotWritePos + cslot < m_islotReadPos) + cslot = m_islotReadPos - m_islotWritePos; + + Assert(psstrmIn->m_ipass == m_ipass-1); + + if (cslot >= 0) + { + // Skipping forward. + Assert(cslot <= psstrmIn->SlotsPendingInContext()); + for (int islot = 0; islot < cslot; islot++) + CopyOneSlotFrom(psstrmIn); + } + else + { + // Reprocessing. + + // Save corresponding positions before doing the back-up. + int islotReadPosInSave = psstrmIn->ReadPosForNextGet(); + int islotWritePosOutSave = this->WritePos(); + + std::vector<GrSlotState*> vpslotTmp; + int islot; + if (psstrmIn->SlotsToReprocess() > 0) + { + // Stick any slots still to reprocess in a temporary buffer. + for (islot = psstrmIn->m_islotReprocPos; + islot < signed(psstrmIn->m_vpslotReproc.size()); + islot++) + { + vpslotTmp.push_back(psstrmIn->m_vpslotReproc[islot]); + } + } + + psstrmIn->ClearReprocBuffer(); + psstrmIn->m_islotReprocLim = psstrmIn->m_islotReadPos; + + for (islot = cslot; islot < 0; islot++) + psstrmIn->m_vpslotReproc.push_back(m_vpslot[m_islotWritePos+islot]); + for (islot = 0; islot < signed(vpslotTmp.size()); islot++) + psstrmIn->m_vpslotReproc.push_back(vpslotTmp[islot]); + psstrmIn->m_islotReprocPos = 0; + + if (!fOutputOfPosPass && m_fUsedByPosPass) + { + // Last substitution pass; zap the stream indices, since they may + // now be invalid. + for (islot = 0; islot < signed(psstrmIn->m_vpslotReproc.size()); islot++) + psstrmIn->m_vpslotReproc[islot]->ZapPosPassIndex(); + } + + // If either the min or lim is in the middle of the reprocess buffer, + // adjust as necessary to match where it is in the output stream. + int islotSegMinIn = psstrmIn->SegMin(); + if (islotSegMinIn > -1 && + psstrmIn->ReadPosForNextGet() <= islotSegMinIn && + islotSegMinIn < islotReadPosInSave) + { + Assert(this->SegMin() > -1); + int dislotIn = islotReadPosInSave - islotSegMinIn; + int dislotOut = islotWritePosOutSave - SegMin(); + psstrmIn->SetSegMin(islotSegMinIn + dislotIn - dislotOut, true); + if (psstrmIn->m_cslotPreSeg > psstrmIn->SegMin()) + psstrmIn->m_cslotPreSeg = -1; + } + int islotSegLimIn = psstrmIn->SegLimIfKnown(); + if (islotSegLimIn > -1 && + psstrmIn->ReadPosForNextGet() <= islotSegLimIn && + islotSegLimIn < islotReadPosInSave) + { + int islotSegLimOut = this->SegLimIfKnown(); + Assert(islotSegLimOut > -1); + int dislotIn = islotReadPosInSave - islotSegLimIn; + int dislotOut = islotWritePosOutSave - islotSegLimOut; + psstrmIn->SetSegLim(islotSegLimIn + dislotIn - dislotOut); + } + + m_islotWritePos += cslot; + + if (m_islotSegMin >= m_islotWritePos) + m_islotSegMin = -1; + if (m_islotSegLim >= m_islotWritePos) + m_islotSegLim = -1; + } +} + +/*---------------------------------------------------------------------------------------------- + If a rule was run over a LB glyph, set the appropriate flag in the table manager. + CURRENTLY NOT USED +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::SetLBContextFlag(GrTableManager * ptman, int islotStart) +{ + gid16 chwLB = ptman->LBGlyphID(); + for (int islot = islotStart; islot < WritePos(); islot++) + { + GrSlotState * pslot = SlotAt(islot); + if (pslot->IsInitialLineBreak(chwLB)) + ptman->State()->SetStartLineContext(true); + if (pslot->IsFinalLineBreak(chwLB)) + ptman->State()->SetEndLineContext(true); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the number of slots that have been output by the current pass but must be + reprocessed by this same pass. The recipient is serving as the input stream. +----------------------------------------------------------------------------------------------*/ +int GrSlotStream::SlotsToReprocess() +{ + Assert(AssertValid()); + + if (m_islotReprocPos == -1) + return 0; + return (m_vpslotReproc.size() - m_islotReprocPos); +} + +/*---------------------------------------------------------------------------------------------- + Get the input at the given slot, that is, the original input, ignoring the issue of + reprocessing. +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::GetSlotAt(int islot) +{ + return m_vpslot[islot]; +} + +/*---------------------------------------------------------------------------------------------- + Return the "current" input item from the rule's perspective, ie, the last slot read. + So dislotOffset = 0 means not the slot at the read position but one slot earlier. + + If dislotOffset is less the the original read position that was in effect at the + beginning of the rule, we need to read from the output stream, which has the + up-to-date values. See the class comment for more details. + + @param dislotOffset - offset from current stream position + @param fNullOkay - true if it's okay to return NULL in the situation where we're asking + for something before the beginning of the stream +----------------------------------------------------------------------------------------------*/ + +GrSlotState * GrSlotStream::RuleInputSlot(int dislotOffset, GrSlotStream * psstrmOutput, + bool fNullOkay) +{ + Assert(m_islotRuleStartRead <= m_islotReadPos); + + if (dislotOffset > 0) + return Peek(dislotOffset - 1); + + int cslotOffsetBack = dislotOffset * -1; + + if (m_islotReprocLim > -1) + { + // There is a reprocess buffer. + + // Position when starting the rule should never be earlier than the + // beginning of the reprocess buffer. + Assert(m_islotRuleStartRead >= m_islotReprocLim - signed(m_vpslotReproc.size())); + + // number of items following the reprocess buffer: + int cslotPostReproc = m_islotReadPos - m_islotReprocLim; + + if (cslotOffsetBack >= cslotPostReproc) + { + // number of items in the reprocess buffer that were valid when the rule + // was started: + int cslotValidReproc = m_islotReprocLim - m_islotRuleStartRead; + + if (cslotOffsetBack >= cslotPostReproc + cslotValidReproc) + { + // Read from the output stream. + int dislotTmp = dislotOffset - 1 + cslotPostReproc + cslotValidReproc; + Assert(dislotTmp < 0); + return psstrmOutput->PeekBack(dislotTmp); + } + else + { + if (m_islotReprocPos > -1) + { + // Current read pos is inside reprocess buffer. + Assert(cslotPostReproc == 0); + int islotStartReadReprocBuf = m_vpslotReproc.size() - cslotValidReproc; + Assert(islotStartReadReprocBuf >= 0); + int islotInReprocBuf = m_islotReprocPos - cslotOffsetBack - 1; + if (islotInReprocBuf < islotStartReadReprocBuf) + { + // Return a slot from before the reprocess buffer, from the + // output stream (which is serving as our input stream for precontext + // items). + return psstrmOutput->PeekBack(islotInReprocBuf - islotStartReadReprocBuf); + } + else if (islotInReprocBuf < 0) + { + Assert(false); + //int islotReprocMin = m_islotReprocLim - m_vpslotReproc.Size(); + //return m_vpslot[islotReprocMin + islotInReprocBuf]; + } + else + // Read from the reprocess buffer. + return m_vpslotReproc[islotInReprocBuf]; + } + else + // Looking backwards into the reprocess buffer. + return m_vpslotReproc[ + m_vpslotReproc.size() - cslotOffsetBack + cslotPostReproc - 1]; + } + } + } + + // There is no reprocess buffer, or it is not a factor. + + if (m_islotReadPos + dislotOffset - 1 < m_islotRuleStartRead) + { + // Read from the output stream. + return psstrmOutput->PeekBack(m_islotReadPos + dislotOffset - 1 - m_islotRuleStartRead, + fNullOkay); + } + else + { + // Read normally from the input stream. + return m_vpslot[m_islotReadPos - cslotOffsetBack - 1]; + } +} + +/*---------------------------------------------------------------------------------------------- + Return the "current" output item from the rule's perspective, ie, the last slot written. + So dislotOffset = 0 means not the slot at the write position but one slot earlier. +----------------------------------------------------------------------------------------------*/ + +GrSlotState * GrSlotStream::RuleOutputSlot(int dislotOffset) +{ + return m_vpslot[m_islotWritePos - 1 + dislotOffset]; +} + +/*---------------------------------------------------------------------------------------------- + For any slot in the final output that has no associations, set the "before" pointer to + the following slot and the "after" pointer to the preceding slot. The idea of this is + that it will make it impossible to select a the glyph separately. + + Note that if the slot is the first on the line, it will not be associated with + a slot in the previous segment. + + @param chwLB - the glyph IB for the line-break glyphs +----------------------------------------------------------------------------------------------*/ +void GrSlotStream::SetNeutralAssociations(gid16 chwLB) +{ + for (int islot = 0; islot < m_islotWritePos; ++islot) + { + GrSlotState * pslot = SlotAt(islot); + if (pslot->PassModified() > 0 && + (pslot->BeforeAssoc() == kPosInfinity || pslot->AfterAssoc() == kNegInfinity)) + { + pslot->CleanUpAssocs(); + if (pslot->BeforeAssoc() != kPosInfinity && pslot->AfterAssoc() != kNegInfinity) + continue; + + Assert(pslot->BeforeAssoc() == kPosInfinity && pslot->AfterAssoc() == kNegInfinity); + + GrSlotState * pslotBefore = FindAssociatedSlot(islot, 1, chwLB); + GrSlotState * pslotAfter = FindAssociatedSlot(islot, -1, chwLB); + + if (pslotBefore && pslotAfter) + pslot->Associate(pslotBefore, pslotAfter); + else if (pslotBefore) + pslot->Associate(pslotBefore); + else if (pslotAfter) + pslot->Associate(pslotAfter); + else + // Weird, but can happen with an empty segment. + Warn("No assocations"); + +// Assert(pslot->m_vpslotAssoc.Size() > 0); + pslot->m_fNeutralAssocs = true; + } + } +} + +/*---------------------------------------------------------------------------------------------- + Find the next or previous slot in the final output that has explicit associations, or + return NULL if none. + + @param islot - starting slot + @param nInc - +1 if we want to find the next slot, -1 if we want the previous slot + @param chwLB - the glyph IB for the line-break glyphs +----------------------------------------------------------------------------------------------*/ +GrSlotState * GrSlotStream::FindAssociatedSlot(int islot, int nInc, gid16 chwLB) +{ + int islotNext = islot + nInc; + + while (islotNext >= 0 && islotNext < m_islotWritePos) + { + GrSlotState * pslotRet = SlotAt(islotNext); + if (pslotRet->IsLineBreak(chwLB)) + { } + else if (pslotRet->PassModified() == 0 || + (pslotRet->m_vpslotAssoc.size() > 0 && pslotRet->m_fNeutralAssocs == false)) + { + return pslotRet; + } + islotNext += nInc; + } + return NULL; +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotStream.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotStream.h new file mode 100644 index 00000000000..006191252dc --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrSlotStream.h @@ -0,0 +1,577 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrSlotStream.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Classes GrSlotStream and GrSlotState. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef SLOTSTREAM_INCLUDED +#define SLOTSTREAM_INCLUDED + +//:End Ignore + +namespace gr +{ + +class EngineState; +class Font; + +/*---------------------------------------------------------------------------------------------- + Each GrSlotStream represents the output of one pass, which also serves as the input + of the following pass. Hence it has two positions, a write position for when it is + serving as an output stream, and a read position for when it is serving as an input + stream. The TableManager holds a list of the streams. + + The reading of slot streams is complicated by two factors. First, it may be necessary + to access the "pre-context" items of a rule, which are the slots before the current + stream position at the point when the rule is matched. These items will not be modified + by the rule, but may have been modified by a previous rule in the same pass, Therefore + we need to read these slots from the output stream, rather than the input stream, + because the output stream has the current slot attribute values that we want to work with. + In order make this happen, we keep track of the read and write positions of + the streams at the time the current rule was matched (m_islotRuleStartRead and + m_islotRuleStartWrite) and whenever we try to read a slot before m_islotRuleStartRead, + we read the corresponding slot from the output stream instead. + + Second, a rule may explicitly set the advance position so that some items are + actually reprocessed (and potentially modified) by the same pass. When this happens, + we actually copy the slots to reprocess from the output stream back + into a temporary buffer (m_vpslotReproc) in the input stream, and set up the stream so + that the next read operations will read from the buffer. + + It gets tricky because these two mechanisms can overlap with each other, so care + must be taken to read from the right place: the input stream in the normal way, + the reprocess buffer, or the output stream. See the RuleInputSlot method. + + Another messy bit of code has to do with maintaining chunks. These are used for + backtracking and unwinding after inserting a line-break. Adjacent streams have maps + showing how many slots were processed by a single rule; the maps must be consisent + between adjacent rules. The following is an example of a pair of valid chunk maps: + + Input stream Output stream + Pos next-map prev-map + 0 0 0 + 1 -1 -1 + 2 2 2 + 3 4 -1 + 4 -1 3 + 5 -1 -1 + 6 7 -1 + 7 -1 6 + 8 -1 -1 + + A -1 means that slot is not at the beginning of a chunk. Otherwise, there must be a + corresponding value in the parallel map of the adjacent stream--eg, the input stream's + next-map(3) == 4 and the output stream's prev-map(4) == 3. Assertions are run + regularly to ensure that the chunk maps stay valid. + + So above, the first chunk corresponds to slots 0-1 in the input and 0-1 in the output; + two slots were processed by the rule. The second chunk corresponds to slot 2 + in the input and slots 2-3 in the output (a slot was inserted). + The third corresponds to slots 3-5 in the input and 4-6 in the output (three slots + processed). The fourth chunk covers slots 6-8 in the input and 7-8 in the output + (one slot was deleted). + + ENHANCE SharonC: explain the effect of skipped slots on the chunk maps. + + Hungarian: sstrm +----------------------------------------------------------------------------------------------*/ +class GrSlotStream { + + friend class GrSlotState; + friend class FontMemoryUsage; + +public: + // Constructor: + GrSlotStream(int ipass) + { + m_ipass = ipass; + m_vpslot.clear(); + m_vislotPrevChunkMap.clear(); + m_vislotNextChunkMap.clear(); + m_vpslotReproc.clear(); + } + + // Destructor: + ~GrSlotStream() + { + ReleaseSlots(0, m_vpslot.size()); + } + + void ReleaseSlots(int islotMin, int islotLim) + { + // A slot stream is responsible for deleting the slot states that it created, + // that is, the ones whose modified tag equals this stream's pass index. + // NO LONGER TRUE - there is a master list of them managed by the GrTableManager. +// for (int islot = islotMin; islot < islotLim; ++islot) +// { +// if (m_vpslot[islot]->m_ipassModified == m_ipass) +// delete m_vpslot[islot]; +// } + } + + // Getters: + int WritePos() { return m_islotWritePos; } + int ReadPos() { return m_islotReadPos; } + bool FullyWritten() { return m_fFullyWritten; } + + int SlotsPresent() { return m_vpslot.size(); } + + // Setters: + void SetWritePos(int islot) + { + m_islotWritePos = islot; + } + void SetReadPos(int islot) + { + m_islotReadPos = islot; + } + + void Initialize(int ipassPos1, bool fAnythingPrevious) + { + m_islotWritePos = 0; + m_islotReadPos = 0; + m_islotReprocPos = -1; + m_islotReprocLim = -1; + m_islotRuleStartRead = 0; + m_islotRuleStartWrite = 0; + m_islotReadPosMax = 0; + m_cslotSkippedForResync = 0; + m_cslotPreSeg = (fAnythingPrevious) ? -1 : 0; + m_fFullyWritten = false; + m_islotSegMin = -1; + m_islotSegLim = -1; + m_fUsedByPosPass = (m_ipass + 1 >= ipassPos1); + m_fInputToPosPass1 = (m_ipass + 1 == ipassPos1); + m_vpslot.clear(); // not responsible for destroying the slots + m_vislotPrevChunkMap.clear(); + m_vislotNextChunkMap.clear(); + } + +public: + bool AtEnd(); + bool AtEndOfContext(); + void NextPut(GrSlotState* pslot); + GrSlotState * NextGet(); + GrSlotState * Peek(int dislot = 0); + GrSlotState * PeekBack(int dislot, bool fNullOkay = false); + void Skip(int = 1); + GrSlotState * SlotAt(int islot) + { + return m_vpslot[islot]; + } + + int OutputOfPass() + { + return m_ipass; + } + + // This function is intended to be used within positioning passes where there is a + // one-to-one correspondence beween the slots in consecutive streams. + GrSlotState * OutputSlotAt(int islot) + { + return Peek(islot - ReadPosForNextGet()); + } + + int ReadPosForNextGet() + { + return ReadPos() - SlotsToReprocess(); + } + + // Only valid for streams that are the input or output to a positioning pass. + GrSlotState * SlotAtPosPassIndex(int islot) + { + Assert(GotIndexOffset()); + Assert(m_fUsedByPosPass); + return m_vpslot[islot + m_cslotPreSeg]; + } + + // Only valid for streams that are the input or output to a positioning pass. + bool HasSlotAtPosPassIndex(int islot) + { + Assert(GotIndexOffset()); + Assert(m_fUsedByPosPass); + return (signed(m_vpslot.size()) > (islot + m_cslotPreSeg)); + } + + bool AssertValid(); + void AssertChunkMapsValid(GrSlotStream * psstrmOut); + void AssertStreamIndicesValid(GrSlotStream * psstrmIn); + void AssertAttachmentsInOutput(int islotMin, int islotLim); + bool NoReproc() + { + return (m_islotReprocPos == -1 || (m_islotReprocPos >= signed(m_vpslotReproc.size()))); + } + + int SlotsPending(); + int SlotsPendingInContext(); + int TotalSlotsPending(); + bool PastEndOfPositioning(bool fOutput); + + void ClearReprocBuffer() + { + m_islotReprocPos = -1; + m_islotReprocLim = -1; + m_vpslotReproc.clear(); + } + + int ReprocLim() + { + return m_islotReprocLim; + } + + int ReprocMin() + { + Assert(m_islotReprocPos > -1); + return m_islotReprocLim - m_vpslot.size(); + } + + int RuleStartReadPos() + { + return m_islotRuleStartRead; + } + + void SetRuleStartReadPos() + { + if (m_islotReprocPos > -1) + m_islotRuleStartRead = m_islotReprocLim - SlotsToReprocess(); + else + m_islotRuleStartRead = m_islotReadPos; + } + + int RuleStartWritePos() + { + return m_islotRuleStartWrite; + } + + void SetRuleStartWritePos() + { + m_islotRuleStartWrite = m_islotWritePos; + } + + void MarkFullyWritten(); + + void SetSegMin(int islot, bool fAdjusting = false) + { + Assert(fAdjusting || m_islotSegMin == -1 || m_islotSegMin == islot); + m_islotSegMin = islot; + } + void SetSegMinToWritePos(bool fMod = true) + { + if (m_islotSegMin == -1) + m_islotSegMin = m_islotWritePos; + else + Assert(m_islotSegMin <= m_islotWritePos); // eg, already set to before the initial LB + } + int SegMin() + { + return m_islotSegMin; + } + void SetSegLim(int islot) + { + m_islotSegLim = islot; + } + void SetSegLimToWritePos(bool fMod = true) + { + if (m_islotSegLim > -1 && !fMod) + { + Assert(m_islotSegLim <= m_islotWritePos); + return; + } + m_islotSegLim = m_islotWritePos; + } + int SegLimIfKnown() + { + return m_islotSegLim; + } + int FinalSegLim() + { + if (m_islotSegLim > -1) + return m_islotSegLim; + else + return m_islotWritePos; + } + + // Return true if we are exactly at the segment min. + bool AtSegMin() + { + if (m_islotSegMin == -1) + return false; + return (m_islotSegMin == ReadPosForNextGet()); + } + + // Return true if we are exactly at the segment lim. + bool AtSegLim() + { + if (m_islotSegLim == -1) + return false; + return (m_islotSegLim == ReadPosForNextGet()); + } + + int ReadPosMax() + { + return m_islotReadPosMax; + } + void SetReadPosMax(int islot) + { + m_islotReadPosMax = std::max(m_islotReadPosMax, islot); + } + + int OldDirLevelRange(EngineState * pengst, int islotStart, int nTopDirection); + int DirLevelRange(EngineState * pengst, int islotStart, int nTopDirection, + std::vector<int> & vislotStarts, std::vector<int> & vislotStops); + int GetSlotDirLevel(EngineState * pengst, int islot, + int nOuterLevel, int nCurrLevel, DirCode dircMarker, DirCode dircOverride); + DirCode AdjacentStrongCode(EngineState * pengst, int islot, int nInc, + DirCode dircPDF, bool fNumbersAreStrong = false); + DirCode TerminatorSequence(EngineState * pengst, int islot, int nInc, DirCode dircPDF); + DirCode AdjacentNonBndNeutralCode(EngineState * pengst, int islot, int nInc, + DirCode dircPDF); + + void CopyOneSlotFrom(GrSlotStream * psstrmPrev); + void SimpleCopyFrom(GrSlotStream * psstrmI, int islotInput, int islotOutput); + + bool MoreSpace(GrTableManager * ptman, + float xsSpaceAllotted, bool fWidthIsCharCount, + bool fIgnoreTrailingWS, TrWsHandling twsh, + float * pxsWidth); + + int InsertLineBreak(GrTableManager * ptman, + int islotPrevBreak, bool fInsertedLB, int islotStartTry, + LineBrk lb, TrWsHandling twsh, int islotMin, LineBrk * plbNextToTry); + int MakeSegmentBreak(GrTableManager * ptman, + int islotPrevBreak, bool fInsertedLB, int islotStartTry, + LineBrk lb, TrWsHandling twsh, int islotMin, LineBrk * plbNextToTry); + bool FindSegmentEnd(GrTableManager * ptman, + int islotStartTry, LineBrk lb, TrWsHandling twsh, int islotMin, + int * pislot, int * pichwSegOffset, + LineBrk * plbFound, LineBrk * plbNextToTry); + bool HasEarlierBetterBreak(int islotBreak, LineBrk lbFound, gid16 chwLB); + LineBrk BreakWeightAt(gid16 chwLB, int islot); + void AppendLineBreak(GrTableManager *, GrCharStream * pchstrm, + LineBrk, DirCode dirc, int islot, bool fInitial, int ichwSegOffset); + int FindFinalLineBreak(gid16 chwLB, int islotMin, int islotLim); + void AdjustPrevStreamNextChunkMap(GrTableManager * ptman, int islotMod, int nInc); + void AdjustNextChunkMap(int islotMin, int islotMod, int nInc); + + void EnsureLocalCopy(GrTableManager * ptman, GrSlotState * pslot, GrSlotStream * psstrmIn); + void ReplaceSlotInReprocessBuffer(GrSlotState * pslotOld, GrSlotState * pslotNew); + + void ZapCalculatedDirLevels(int islotLB); + + int MaxClusterSlot(int islotChunkLim) + { + return MaxClusterSlot(islotChunkLim - 1, islotChunkLim); + } + int MaxClusterSlot(int islotChunkMin, int islotChunkLim); + + void UnwindInput( int islot, bool fPreBidiPass); + void UnwindOutput(int islot, bool fOutputOfPosPass); + + void MapInputChunk( int islotIn, int islotOut, int islotInLim, + bool fReprocessing, bool fBackingUp); + void MapOutputChunk(int islotOut, int islotIn, int islotOutLim, + bool fReprocessing, int cslotReprocess, bool fBackingUp); + + int LastNextChunkLength(); + + int ChunkInPrev(int i) + { + Assert(i >= 0); + Assert(i < signed(m_vislotPrevChunkMap.size())); + Assert(i < m_islotWritePos); + return m_vislotPrevChunkMap[i]; + } + int ChunkInNext(int i) + { + Assert(i >= 0); + Assert(i < signed(m_vislotNextChunkMap.size())); + Assert(i < m_islotReadPos); + return m_vislotNextChunkMap[i]; + } + + int ChunkInNextMin(int islot); + int ChunkInNextLim(int islot); + + void ResyncSkip(int nslot); + int SlotsSkippedToResync() + { + return m_cslotSkippedForResync; + } + void ClearSlotsSkippedToResync() + { + m_cslotSkippedForResync = 0; + } + + int IndexOffset() + { + Assert(GotIndexOffset()); // should have been calculated + return m_cslotPreSeg; + } + void SetIndexOffset(int c) + { + m_cslotPreSeg = c; + } + bool GotIndexOffset() + { + return (m_cslotPreSeg >= 0); + } + void CalcIndexOffset(GrTableManager * ptman); + + void SetPosForNextRule(int nslot, GrSlotStream * psstrmInput, bool fOutputOfPosPass); + void SetLBContextFlag(GrTableManager * ptman, int islotStart); + int SlotsToReprocess(); + + void SetNeutralAssociations(gid16 chwLB); +protected: + GrSlotState * FindAssociatedSlot(int islot, int nInc, gid16 chwLB); + + GrSlotState * GetSlotAt(int islot); + +public: + // Used by the action code: + void BackupReadPos(int cslot = 1) + { + for (int islot = 0; islot < cslot; islot++) + { + if (m_islotReprocLim > -1 && m_islotReprocLim == m_islotReadPos) + { + if (m_islotReprocPos == -1) + m_islotReprocPos = m_vpslotReproc.size() - 1; + else + --m_islotReprocPos; + Assert(m_islotReprocPos >= 0); + } + else + --m_islotReadPos; + } + } + + GrSlotState * RuleInputSlot(int dislot = 0, GrSlotStream * psstrmOut = NULL, + bool fNullOkay = false); + GrSlotState * RuleOutputSlot(int dislot = 0); + + void PutSlotAt(GrSlotState * pslot, int islot) + { + // Currently only used for the final pass, when applying justify.width to + // the advance width. + Assert(signed(m_vpslot.size()) > islot); + Assert(WritePos() > islot); + Assert(ReadPos() <= islot); + Assert(SlotsToReprocess() == 0); + m_vpslot[islot] = pslot; + } + +protected: + // Instance variables: + int m_ipass; // which pass this stream is serving as OUTPUT of + + std::vector<GrSlotState*> m_vpslot; // the slots being processed + + // Chunking information: + std::vector<int> m_vislotPrevChunkMap; // chunk mapping into previous stream; + // -1 if not at a chunk boundary + // (this information is needed for + // reinitialization after inserting a line break) + + std::vector<int> m_vislotNextChunkMap; // chunk mapping into next stream, or -1 + // (this information is needed for backtracking) + + // Since most streams serve as both input and output, there are two positions. + // Note that readPos <= writePos always. + int m_islotWritePos; // where previous pass should put next output + int m_islotReadPos; // where following pass should start processing from + + // When we are reprocessing some of the output from the next stream: + std::vector<GrSlotState*> m_vpslotReproc; // temporary buffer + int m_islotReprocLim; // the read position corresponding to the end of the reprocess + // buffer; -1 if buffer is invalid + int m_islotReprocPos; // index into m_vpslotReproc from where we should read next; + // -1 if we are not reprocessing + + int m_islotRuleStartRead; // read position when the rule was started; any requests before + // this point should access the output stream, because it has + // the most up-to-date values. + int m_islotRuleStartWrite; // write position when the rule started + + bool m_fFullyWritten; // set to true when we can't get any more from the + // previous pass; initially false. + + int m_islotSegMin; // the official min of the segment; -1 if not yet set + int m_islotSegLim; // the official lim of the segment; -1 if not yet set + // (only set for the output of the final line-break pass + // and those beyond) + + int m_cslotSkippedForResync; // number of slots skipped to resync; + // specifically, the number of slots' worth of the + // next-chunk-map that are invalid, not mapped + // to any slots in the following stream + + int m_cslotPreSeg; // number of slots previous to the official beginning of the segment; + // -1 if we haven't figured it out yet. Used for keeping streams + // in the positioning pass in sync; not particularly useful for + // substitution passes, and will possibly be left uncalculated. + + bool m_fUsedByPosPass; // true if this is input to or output from a positioning pass + bool m_fInputToPosPass1; // true if this is the input to the first positioning pass + + // Max value of m_islotReadPos; used for recognizing infinite loops and forcibly + // advancing (see GrPass::CheckInputProgress). + int m_islotReadPosMax; + +public: + // For transduction logging: +#ifdef TRACING +// void LogSlotGlyphs(std::ofstream & strmOut, gid16 chwLB); +#endif // TRACING + + // For test procedures: + int NumberOfSlots() { return m_vpslot.size(); } + GrSlotState * LastSlotWritten() + { + if (m_islotWritePos == 0) + return NULL; + else + return m_vpslot[m_islotWritePos-1]; + } + GrSlotState * LastSlotRead() + { + if (m_islotReadPos == 0) + return NULL; + else + return m_vpslot[m_islotReadPos-1]; + } + + gid16 GlyphIDAt(int islot) + { + return m_vpslot[islot]->m_chwGlyphID; + } + + int PreChunk() // answer the number of prepended items that are not in any chunk; + // should be obsolete now + { + int islot; + for (islot = 0; islot < m_islotReadPos; ++islot) + { + if (m_vislotNextChunkMap[islot] != -1) + return islot; + } + Assert(false); + return islot; + } + +}; // end of class GrSlotStream + +} // namespace gr + + +#endif // !SLOTSTREAM_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrTableManager.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrTableManager.cpp new file mode 100644 index 00000000000..ff675adb0ad --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrTableManager.cpp @@ -0,0 +1,2694 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrTableManager.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + GrTableManager, which handles the interactions between passes, including the demand-pull + algorithm. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE +#ifndef _WIN32 +#include <stdlib.h> +#include <math.h> +#endif + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Constructors and initializers +----------------------------------------------------------------------------------------------*/ +EngineState::EngineState() + : m_ipassJustCalled(-1), + m_fStartLineContext(false), + m_fEndLineContext(false), + ////m_pgg(NULL), + m_cslotPreSeg(0), + m_islotblkCurr(-1), + m_islotNext(kSlotBlockSize), + m_fInitialLB(false), + m_fFinalLB(false), + m_fInsertedLB(false), + m_fExceededSpace(false), + m_fHitHardBreak(false), + m_fRemovedWhtsp(false), + m_dxsShrinkPossible(GrSlotState::kNotYetSet), + m_islotPosNext(-1), + m_xsPosXNext(0), + m_ysPosYNext(0), + m_dxsTotWidthSoFar(0), + m_dxsVisWidthSoFar(0), + m_prgzpst(NULL), + m_prgpsstrm(NULL) +{ + m_vslotblk.clear(); + m_vprgnSlotVarLenBufs.clear(); +} + +void EngineState::Initialize(GrEngine * pgreng, GrTableManager * ptman) +{ + m_ptman = ptman; + m_cpass = ptman->NumberOfPasses(); // duplicate for convenience + + m_cUserDefn = pgreng->NumUserDefn(); + m_cFeat = pgreng->NumFeat(); + m_cCompPerLig = pgreng->NumCompPerLig(); + + if (m_prgzpst) + delete[] m_prgzpst; + m_prgzpst = new PassState[m_cpass]; + ptman->StorePassStates(m_prgzpst); +} + +/*---------------------------------------------------------------------------------------------- + Destructors +----------------------------------------------------------------------------------------------*/ +GrTableManager::~GrTableManager() +{ + for (int ipass = 0; ipass < m_cpass; ++ipass) + delete Pass(ipass); + + delete[] m_prgppass; +} + +EngineState::~EngineState() +{ + DestroySlotBlocks(); + + delete[] m_prgzpst; + + if (m_prgpsstrm) + { + for (int isstrm = 0; isstrm < m_cpass; ++isstrm) + delete OutputStream(isstrm); + + delete[] m_prgpsstrm; + } +} + +/*---------------------------------------------------------------------------------------------- + Delete all the slots. +----------------------------------------------------------------------------------------------*/ +void EngineState::DestroySlotBlocks() +{ + Assert(m_vslotblk.size() == m_vprgnSlotVarLenBufs.size()); + for (size_t islotblk = 0; islotblk < m_vslotblk.size(); ++islotblk) + { + delete[] m_vslotblk[islotblk]; + delete[] m_vprgnSlotVarLenBufs[islotblk]; + } + m_vslotblk.clear(); + m_vprgnSlotVarLenBufs.clear(); +} + +/*---------------------------------------------------------------------------------------------- + Create the passes, and them fill them in by reading from the file stream. +----------------------------------------------------------------------------------------------*/ +bool GrTableManager::CreateAndReadPasses(GrIStream & grstrm, + int fxdSilfVersion, int fxdRuleVersion, + int cpassFont, long lSubTableStart, int * rgnPassOffsets, + int ipassSub1Font, int ipassPos1Font, int ipassJust1Font, byte ipassPostBidiFont) +{ + Assert(ipassSub1Font <= ipassJust1Font); + Assert(ipassJust1Font <= ipassPos1Font); + Assert(ipassPos1Font <= cpassFont); + if (ipassSub1Font > ipassJust1Font || ipassJust1Font > ipassPos1Font + || ipassPos1Font > cpassFont) + { + return false; // bad table + } + + // Adjusted indices based on the fact that we have a glyph generation pass that is + // pass 0, and possibly a bidi pass: + m_cpass = cpassFont + 1; + int ipassLB1, ipassSub1, ipassBidi, ipassPos1, ipassJust1; + ipassLB1 = 1; + ipassSub1 = ipassSub1Font + 1; + if (ipassPostBidiFont == 0xFF) + { + // No bidi pass. + m_fBidi = false; + // Add 1 below to account for the glyph-generation pass. + ipassPos1 = ipassPos1Font + 1; + ipassJust1 = ipassJust1Font + 1; + ipassBidi = ipassJust1; + } + else + { + m_fBidi = true; + m_cpass++; + Assert(ipassPostBidiFont == ipassJust1Font); + ipassJust1 = ipassJust1Font + 2; + ipassPos1 = ipassPos1Font + 2; + ipassBidi = ipassJust1 - 1; + } + + // Always make at least one positioning pass, even if there are no rules. + if (ipassPos1 == m_cpass) + { + m_cpass++; + } + + m_prgppass = new GrPass*[m_cpass + 1]; + m_prgppass[0] = new GrGlyphGenPass(0); + + int ipass = 1; + int ipassFont = 0; + m_cpassLB = 0; + m_ipassJust1 = 1; + m_ipassPos1 = 1; + for ( ; ipass < m_cpass; ipass++, ipassFont++) + { + if (ipass < ipassSub1) + { + m_prgppass[ipass] = new GrLineBreakPass(ipass); + m_prgppass[ipass]->ReadFromFont(grstrm, fxdSilfVersion, fxdRuleVersion, + lSubTableStart + rgnPassOffsets[ipassFont]); + m_cpassLB++; + m_ipassJust1++; + m_ipassPos1++; + } + else if (ipassSub1 <= ipass && ipass < ipassBidi) + { + m_prgppass[ipass] = new GrSubPass(ipass); + m_prgppass[ipass]->ReadFromFont(grstrm, fxdSilfVersion, fxdRuleVersion, + lSubTableStart + rgnPassOffsets[ipassFont]); + m_ipassJust1++; + m_ipassPos1++; + } + else if (ipassBidi == ipass && ipass < ipassJust1) + { + m_prgppass[ipass] = new GrBidiPass(ipass); + m_prgppass[ipass]->SetTopDirLevel(TopDirectionLevel()); + ipassFont--; // no corresponding pass in font + m_ipassJust1++; + m_ipassPos1++; + } + else if (ipassJust1 <= ipass && ipass < ipassPos1) + { + // A justification pass is, in essence, a substitution pass. + m_prgppass[ipass] = new GrSubPass(ipass); + m_prgppass[ipass]->ReadFromFont(grstrm, fxdSilfVersion, fxdRuleVersion, + lSubTableStart + rgnPassOffsets[ipassFont]); + m_ipassPos1++; + } + else if (ipassPos1 <= ipass) + { + m_prgppass[ipass] = new GrPosPass(ipass); + if (ipassFont < cpassFont) + { + m_prgppass[ipass]->ReadFromFont(grstrm, fxdSilfVersion, fxdRuleVersion, + lSubTableStart + rgnPassOffsets[ipassFont]); + } + else + { + // No positioning pass in font: create a bogus one. + m_prgppass[ipass]->InitializeWithNoRules(); + } + } + else + { + Assert(false); + return false; // bad table + } + } + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Create passes corresponding to an invalid font. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::CreateEmpty() +{ + m_cpass = 2; + + m_prgppass = new GrPass*[2]; + m_prgppass[0] = new GrGlyphGenPass(0); + + m_prgppass[1] = new GrPosPass(1); + m_prgppass[1]->InitializeWithNoRules(); + + m_ipassPos1 = 1; + m_cpassLB = 0; + m_fBidi = false; + + m_engst.CreateEmpty(); +} + +void EngineState::CreateEmpty() +{ + m_fStartLineContext = false; + m_fEndLineContext = false; +} + +/*---------------------------------------------------------------------------------------------- + Create the slot streams corresponding to the passes. +----------------------------------------------------------------------------------------------*/ +void EngineState::CreateSlotStreams() +{ + if (m_prgpsstrm) + return; + + m_prgpsstrm = new GrSlotStream * [m_cpass]; + for (int ipass = 0; ipass < m_cpass; ++ipass) + { + m_prgpsstrm[ipass] = new GrSlotStream(ipass); + } +} + +/*---------------------------------------------------------------------------------------------- + Store information about the pass-states in the pass objects. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::StorePassStates(PassState * rgzpst) +{ + for (int ipass = 0; ipass < m_cpass; ipass++) + Pass(ipass)->SetPassState(rgzpst + ipass); +} + +/*---------------------------------------------------------------------------------------------- + Demand-driven loop: with goal of filling up final slot stream, loop backward and forward + filling up slot streams until final stream has reached physical capacity. + + @param pgjus - justification agent; NULL if justification is of no interest + @param jmodi - (internal) justification mode + @param fStartLine, fEndLine + @param ichSegLim - known end of segment; -1 when using backtracking + @param dxWidthRequested - available width; when justifying, exact width desired + @param dxUnjustifiedWidth - when justifying, what the width would be normally + @param fNeedFinalBreak - true if the end of the segment needs to be a valid break point + @param fMoreText - true if char stream doesn't go up to text-stream lim + @param ichFontLim - end of the range that could be rendered by this font + @param fInfiniteWidth - used for "measured" segments + @param fWidthIsCharCount - kludge for test procedures: the width is a character count, + not a pixel count + @param lbPref - try for longest segment ending with this breakweight + - when justifying, lb to assume + @param lbMax - max (last resort) breakweight if no preferred break possible + @param ichwCallerBtLim - caller's backtrack lim; -1 if caller is not backtracking + @param twsh - how to handle trailing white-space + @param fParaRtl - overall paragraph direction + @param nDirDepth - direction depth of this segment +----------------------------------------------------------------------------------------------*/ +void GrTableManager::Run(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm, IGrJustifier * pgjus, int jmodi, + LayoutEnvironment & layout, + int ichSegLim, + float dxWidthRequested, float dxUnjustifiedWidth, + bool fNeedFinalBreak, bool fMoreText, int ichFontLim, + bool fInfiniteWidth, bool fWidthIsCharCount, + int ichwCallerBtLim, + int nDirDepth, SegEnd estJ) +{ + bool fStartLine = layout.startOfLine(); + bool fEndLine = layout.endOfLine(); + LineBrk lbPref = layout.bestBreak(); + LineBrk lbMax = layout.worstBreak(); + TrWsHandling twsh = layout.trailingWs(); + bool fParaRtl = layout.rightToLeft(); + Segment * psegPrev = layout.prevSegment(); + Segment * psegInit = layout.segmentForInit(); + std::ostream * pstrmLog = layout.loggingStream(); + + m_engst.Initialize(Engine(), this); // do this after the tables have been fully read + m_engst.m_jmodi = jmodi; + m_engst.m_ipassJustCalled = -1; + m_engst.m_twsh = twsh; + m_engst.m_fParaRtl = fParaRtl; + m_engst.m_dxsShrinkPossible = GrSlotState::kNotYetSet; + m_fLogging = (pstrmLog != NULL); + + int cbPrev; + byte * pbPrevSegDat = NULL; + if (psegPrev) + cbPrev = psegPrev->NextSegInitBuffer(&pbPrevSegDat); + else if (psegInit) + cbPrev = psegInit->ThisSegInitBuffer(&pbPrevSegDat); + else + cbPrev = 0; + + if (pchstrm->IsEmpty()) + { + m_engst.m_lbPrevEnd = klbNoBreak; + InitSegmentAsEmpty(psegNew, pfont, pchstrm, fStartLine, fEndLine); + psegNew->SetWidths(0, 0); + psegNew->SetUpOutputArrays(pfont, this, NULL, 0, 0, 0, twsh, + fParaRtl, nDirDepth, true); + psegNew->SetLayout(layout); + return; + } + + int islotUnderBreak = -1; // position of inserted or final break point in underlying + // data; none so far (if an actual LB has been inserted, + // this is the index of that slot; otherwise it is the + // index of the last valid slot) + int islotSurfaceBreak = -1; // position of line break in surface stream; none so far + LineBrk lbBestToTry = lbPref; + LineBrk lbFound = klbNoBreak; + + float dxMaxWidth = dxWidthRequested; + + if (m_engst.m_jmodi == kjmodiJustify) + { + Assert(fInfiniteWidth); + lbBestToTry = klbClipBreak; + lbFound = lbPref; + } + + float dxWidth; + + std::vector<int> vnslotNeeded; + vnslotNeeded.resize(m_cpass); + + m_engst.InitializeStreams(this, pchstrm, cbPrev, pbPrevSegDat, fNeedFinalBreak, &islotUnderBreak); + + int ipassCurr; + int nNeedInput; // kNextPass: go on to the next pass + // kBacktrack: need to insert a line break + // > 0: need more input from previous pass + + int cchwPostXlbContext = m_pgreng->PostXlbContext(); + if (ichSegLim > -1) + { + // When we know exactly where the end of the segment will be, get as much as + // we need from the character stream, and then fill up the remaining streams. + // (3 below is an arbitrary number; we just want to get slightly more than + // we really need.) + vnslotNeeded[0] = ichSegLim - pchstrm->Min() + cchwPostXlbContext + 3; + for (int ipass = 1; ipass < m_cpass; ipass++) + vnslotNeeded[ipass] = 10000; + // Start at pass 0. + ipassCurr = 0; + } + else + { + // When we're doing line-breaking to find the end of the segment, use the + // demand-driven approach that tries to fill up the available space. + vnslotNeeded[m_cpass - 1] = 1; + + // Start at the last pass. (It might be more efficient to start at the first pass and + // generate a small arbitrary number of glyphs.) + ipassCurr = m_cpass - 1; + } + + while (ipassCurr < m_cpass) + { + if (ipassCurr == 0) + { + int nNeedNow = vnslotNeeded[0]; + // Zeroth pass: generate glyph IDs from the underlying input. + nNeedInput = Pass(0)->ExtendGlyphIDOutput(this, + pchstrm, OutputStream(0), ichSegLim, cchwPostXlbContext, lbPref, + nNeedNow, fNeedFinalBreak, m_engst.m_twsh, &islotUnderBreak); + Assert(nNeedInput == kNextPass || nNeedInput == kBacktrack); + vnslotNeeded[0] -= nNeedNow; + } + else if (ipassCurr < m_cpass - 1) + { + // Middle pass. + int cslotNeedNow = vnslotNeeded[ipassCurr]; + cslotNeedNow = max(1, cslotNeedNow); + int cslotGot; + Pass(ipassCurr)->ExtendOutput(this, + InputStream(ipassCurr), OutputStream(ipassCurr), cslotNeedNow, twsh, + &nNeedInput, &cslotGot, &islotUnderBreak); + vnslotNeeded[ipassCurr] -= cslotGot; + //if (nNeedInput != kNextPass && nNeedInput != kBacktrack) + // nNeedInput = max(nNeedInput, cslotNeedNow - cslotGot); + } + else // (ipassCurr == m_cpass - 1) + { + // Final pass: position and test allotted space. + float dxWidthWShrink = dxMaxWidth; + if (m_engst.m_dxsShrinkPossible != GrSlotState::kNotYetSet) + dxWidthWShrink += (int)m_engst.m_dxsShrinkPossible; + nNeedInput = Pass(ipassCurr)->ExtendFinalOutput(this, + InputStream(ipassCurr), OutputStream(ipassCurr), + dxWidthWShrink, fWidthIsCharCount, fInfiniteWidth, + (islotUnderBreak > -1), (fNeedFinalBreak && lbFound == klbNoBreak), + lbMax, twsh, + &islotSurfaceBreak, &dxWidth); + } + + if (m_engst.m_jmodi == kjmodiJustify) + { + if (ipassCurr < m_ipassJust1 && !OutputStream(ipassCurr)->FullyWritten()) + { + // When justifying, fill up all the streams up to the point where we need + // to interact with the justification routine. + // I think this code is obsolete. + int nStillNeed = 1000; + if ((ipassCurr == 1 && nNeedInput != kNextPass) || ipassCurr == 0) + nStillNeed = ichSegLim - (pchstrm->Pos() - pchstrm->Min()) + + cchwPostXlbContext + 3; + if (nStillNeed > 0) + { + if (nNeedInput == kNextPass) + ipassCurr++; // repeat this pass + nNeedInput = nStillNeed; + } + } + else if (nNeedInput == kNextPass && ipassCurr + 1 == m_ipassJust1) + { + CallJustifier(pgjus, ipassCurr, dxUnjustifiedWidth, dxWidthRequested, fEndLine); + } + } + DetermineShrink(pgjus, ipassCurr); + + if (nNeedInput == kBacktrack) + { + // Final output has overflowed its space--find (or adjust) the break point. + + bool fFoundBreak = Backtrack(&islotUnderBreak, + &lbBestToTry, lbMax, twsh, fMoreText, ichwCallerBtLim, fEndLine, &lbFound); + if (!fFoundBreak) + { + // Nothing will fit. Initialize the new segment just enough so that + // we can delete it. + InitSegmentToDelete(psegNew, pfont, pchstrm); + return; + } + } + else if (nNeedInput == kNextPass) + { + // This pass is sufficiently full--return to following pass. + ipassCurr++; + } + else + { + // Get more input from previous pass. + ipassCurr--; + vnslotNeeded[ipassCurr] = nNeedInput; + } + } + + // At this point we have fully transduced all the text for this segment. + + // Figure out why we broke the segment and what the caller is supposed to do about it. + SegEnd est; + if (m_engst.m_jmodi == kjmodiJustify) + { + // Don't change what was passed in. + est = estJ; + } + else if (m_engst.m_fRemovedWhtsp) // (islotUnderBreak > -1 && twsh == ktwshNoWs) + { + Assert(!m_engst.m_fInsertedLB); + est = kestMoreWhtsp; + } + + else if (m_engst.m_fHitHardBreak) + { + est = kestHardBreak; + } + + else if (!m_engst.m_fExceededSpace && !fMoreText) + // Rendered to the end of the original requested range. + est = kestNoMore; + +// else if (!m_fExceededSpace && !fNextIsSameWs && fMoreText) +// // Writing system break; may be more room on the line. But see tweak below. +// *pest = kestWsBreak; + + else if (m_engst.m_fExceededSpace) + { + // Exceeded available space, found a legal break. + if (twsh == ktwshNoWs && m_engst.m_fRemovedWhtsp) + est = kestMoreWhtsp; + else + est = kestMoreLines; + } + + else if (twsh == ktwshOnlyWs) + { + est = kestOkayBreak; // but see tweak below + } + + else + { + // Broke because of something like a font change. Determine whether this is a legal + // break or not. + // TODO SharonC: handle the situation where it is legal to break BEFORE the first + // character of the following segment. + Assert(fMoreText); + //Assert(fNextIsSameWs); + if (ichwCallerBtLim > -1) + { + Assert(m_engst.m_fInsertedLB); + } + else if (ichSegLim > -1) + { + // We stopped where caller said to. + Assert(fInfiniteWidth); + } + else + { + Assert(!m_engst.m_fInsertedLB); + Assert(islotUnderBreak == -1 || m_engst.m_fFinalLB); + } + int islotTmp = OutputStream(m_cpass - 1)->WritePos(); + GrSlotState * pslotTmp; + if (m_engst.m_fFinalLB || m_engst.m_fInsertedLB) + pslotTmp = OutputStream(m_cpass-1)->SlotAt(islotTmp - 2); + else + pslotTmp = OutputStream(m_cpass-1)->SlotAt(islotTmp - 1); + + if (abs(pslotTmp->BreakWeight()) <= lbPref) + est = kestOkayBreak; + + else if (abs(pslotTmp->BreakWeight()) > lbMax) + est = kestBadBreak; + + else if (OutputStream(m_cpassLB)->HasEarlierBetterBreak(islotUnderBreak, lbFound, + this->LBGlyphID())) + { + est = kestBadBreak; + } + else + est = kestOkayBreak; + + // We no longer have the ability to know whether or not the next segment will use + // the same writing system. + //if (est == kestBadBreak && m_engst.m_fExceededSpace && !fNextIsSameWs && fMoreText) + // est = kestWsBreak; + } + + // Make a segment out of it and return it. + int dichSegLen; + InitNewSegment(psegNew, pfont, pchstrm, pgjus, + islotUnderBreak, islotSurfaceBreak, fStartLine, fEndLine, ichFontLim, lbFound, est, + &dichSegLen); + if (psegNew->segmentTermination() == kestNothingFit) + { + return; + } + else if (psegNew->segmentTermination() == kestHardBreak && + psegNew->startCharacter() == psegNew->stopCharacter()) + { + // Empty segment caused by a hard-break. + m_engst.m_lbPrevEnd = klbNoBreak; + psegNew->SetWidths(0, 0); + psegNew->SetUpOutputArrays(pfont, this, NULL, 0, 0, 0, twsh, + fParaRtl, nDirDepth, true); + psegNew->SetLayout(layout); + return; + } + + psegNew->RecordInitializationForThisSeg(cbPrev, pbPrevSegDat); + + if ((est == kestNoMore || est == kestWsBreak) && twsh == ktwshOnlyWs && + dichSegLen < pchstrm->Lim() - pchstrm->Min()) + { + // If we are asking for white-space only, and we didn't use up everything in the font + // range, we could possibly have gotten an initial white-space only segment + // (as opposed to a writing system break). + est = kestOkayBreak; + psegNew->FixTermination(est); + } + + // Do this before fixing up the associations below. + //if (m_pgreng->LoggingTransduction()) + // WriteTransductionLog(pchstrm, *ppsegRet, + // cbPrev, pbPrevSegDat, pbNextSegDat, pcbNextSegDat); + WriteTransductionLog(pstrmLog, pchstrm, psegNew, cbPrev, pbPrevSegDat); + + RecordAssocsAndOutput(pfont, psegNew, fWidthIsCharCount, twsh, fParaRtl, nDirDepth); + + // Do this after fixing up the associations. + //if (m_pgreng->LoggingTransduction()) + // WriteAssociationLog(pchstrm, *ppsegRet); + WriteAssociationLog(pstrmLog, pchstrm, psegNew); + + psegNew->SetLayout(layout); +} + +/*---------------------------------------------------------------------------------------------- + Create a new segment to return. + + @param pchstrm - input stream + @param pgjus - justification agent, in case this segment needs to be + stretched or shrunk + @param islotLBStreamBreak - position of inserted line break slot (which should be equal + to the position in the glyph generation stream); + -1 if no line-break was inserted (we rendered to the + end of the range) + @param islotSurfaceBreak - position of line break slot in final (surface) stream + @param fStartLine - does this segment start a line? + @param fEndLine - does this segment end a line? + @param ichFontLim - end of range that could be rendered by this font + @param lbEndFound - line break found; possibly adjusted here + @param est - why did the segment terminate +----------------------------------------------------------------------------------------------*/ +void GrTableManager::InitNewSegment(Segment * psegNew, + Font * pfont, GrCharStream * pchstrm, IGrJustifier * pgjus, + int islotLBStreamBreak, int islotSurfaceBreak, bool fStartLine, bool fEndLine, int ichFontLim, + LineBrk lbEndFound, SegEnd est, int * pdichSegLen) +{ + LineBrk lbStart = m_engst.m_lbPrevEnd; + LineBrk lbEnd = lbEndFound; + if (est == kestBadBreak) + lbEnd = klbLetterBreak; + + int ichwMin = pchstrm->Min(); + int ichwLim; + +// int cslotPreSegStream0 = m_cslotPreSeg; +// if (m_fInitialLB && m_cpassLB > 0) +// { +// // Initial LB is included in m_cslotPreSeg, but is not in stream 0. +// // --Not really needed, because both islotLBStreamBreak and m_cslotPreSeg take +// // the initial LB into account if it is there. +// cslotPreSegStream0--; +// } + + // No longer have to subtract this, because the chunk map takes it into + // consideration. + //int cslotPreInitialLB = m_cslotPreSeg - (m_fInitialLB ? 1 : 0); + + //int ichwOldTmp; + + if (m_engst.m_fInsertedLB) + { + Assert(islotLBStreamBreak > -1); + int ichwSegLim = m_engst.LbSlotToSegLim(islotLBStreamBreak, pchstrm, m_cpassLB); + ichwLim = ichwSegLim + pchstrm->Min(); // - cslotPreInitialLB; + + // Old result, for debugging: + //ichwOldTmp = islotLBStreamBreak + pchstrm->Min() - m_cslotPreSeg; + } + else if (m_engst.m_fFinalLB || islotLBStreamBreak == -1) + ichwLim = pchstrm->Lim(); + else + { + int ichwSegLim = m_engst.LbSlotToSegLim(islotLBStreamBreak, pchstrm, m_cpassLB); + ichwLim = ichwSegLim + pchstrm->Min(); // - cslotPreInitialLB; + + // Old result, for debugging: + // islotLBStreamBreak is the last slot in the segment, not the LB glyph; hence +1. + //ichwOldTmp = islotLBStreamBreak + pchstrm->Min() - m_cslotPreSeg + 1; + } + + *pdichSegLen = ichwLim - ichwMin; + + if (ichwMin >= ichwLim) + { + if (est == kestHardBreak) + { + // Empty segment cause by a hard-break. + InitSegmentAsEmpty(psegNew, pfont, pchstrm, fStartLine, fEndLine); + psegNew->FixTermination(est); + } + else + { + // Invalid empty segment. + Assert(ichwMin == ichwLim); + InitSegmentToDelete(psegNew, pfont, pchstrm); + } + return; + } + + Assert(psegNew); + + psegNew->Initialize(pchstrm->TextSrc(), + ichwMin, ichwLim, + lbStart, lbEnd, est, fStartLine, fEndLine, m_pgreng->RightToLeft()); + + psegNew->SetEngine(m_pgreng); + psegNew->SetFont(pfont); + psegNew->SetJustifier(pgjus); + psegNew->SetFaceName(m_pgreng->FaceName(), m_pgreng->BaseFaceName()); + + bool fNextSegNeedsContext = + !(est == kestNoMore || est == kestWsBreak + || ichwLim >= pchstrm->Lim() || ichwLim >= ichFontLim); + + InitializeForNextSeg(psegNew, islotLBStreamBreak, islotSurfaceBreak, lbEnd, + fNextSegNeedsContext, pchstrm); + + psegNew->SetPreContext(0 - m_pgreng->PreXlbContext()); +} + +/*---------------------------------------------------------------------------------------------- + The newly created segment is invalid, for instance, because we were backtracking and + couldn't find a valid break point. Initialize it just enough so that it can be + deleted by the client. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::InitSegmentToDelete(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm) +{ + psegNew->Initialize(pchstrm->TextSrc(), 0, 0, + klbClipBreak, klbClipBreak, kestNothingFit, false, false, + m_pgreng->RightToLeft()); + + psegNew->SetEngine(m_pgreng); + psegNew->SetFont(pfont); + psegNew->SetJustifier(NULL); // can't justify an empty segment + psegNew->SetFaceName(m_pgreng->FaceName(), m_pgreng->BaseFaceName()); + psegNew->SetPreContext(0); +} + +/*---------------------------------------------------------------------------------------------- + Map from the given slot (which is generally the final LB slot) to the end of the segment, + taking into account the fact that a single slot may represent two 16-bit surrogates. + + There are several approaches that seem reasonable but do NOT work. (I tried them!) + You can't calculate it directly from the length of the streams, because that doesn't take + into account the possible presence of surrogate pairs in the character stream. You can't + use the chunk map at the point of the LB, because if a rule spanned a LB the chunk won't + have ended there and so there won't be any valid information. + + What seems to work is to figure out the slot(s) in the zeroth stream using the associations + and then use the segment offset for this slot. This should be reliable because there is + always a one-to-one correspondence between glyphs in the line-break passes (ie, no + insertions or deletions), so the associations should be dependable and straightforward. +----------------------------------------------------------------------------------------------*/ +int EngineState::LbSlotToSegLim(int islotLB, GrCharStream * pchstrm, int cpassLB) +{ + Assert(islotLB < OutputStream(cpassLB)->WritePos()); + + // Figure out the corresponding characters in the zeroth stream. These should be straight- + // forward to figure out because no substitutions are permitted in the LB passes. + // I do the slots before and after the LB, if possible, as a sanity check. + GrSlotStream * psstrmLB = OutputStream(cpassLB); + GrSlotState * pslotPreLB = (m_fFinalLB || m_fInsertedLB) ? + psstrmLB->SlotAt(islotLB - 1) : + psstrmLB->SlotAt(islotLB); + GrSlotState * pslotPostLB = (psstrmLB->WritePos() > islotLB + 1) ? + psstrmLB->SlotAt(islotLB + 1) : + NULL; + + int ichwPreLB = pslotPreLB->AfterAssoc(); + + int ichwSegLim; + if (pslotPostLB) + { + ichwSegLim = pslotPostLB->BeforeAssoc(); + Assert(ichwPreLB + 1 == ichwSegLim + || ichwPreLB + 2 == ichwSegLim); // in case of a surrogate + } + else + { + ichwSegLim = ichwPreLB + 1; + while (!pchstrm->AtUnicodeCharBoundary(ichwSegLim)) + ichwSegLim++; + Assert(pchstrm->AtUnicodeCharBoundary(ichwSegLim)); + } + + // Old buggy routine that doesn't handle the case where a rule (and hence the chunk map) did + // not end at the LB character: + // Map backwards through the LB pass streams, if any. + //int islotAdjusted = islotLB + 1; // after the LB + //for (int ipass = m_cpassLB; ipass > 0; ipass--) + //{ + // islotAdjusted = ChunkInPrev(ipass, islotAdjusted, pchstrm); + //} + // + //int ichwSegLim = ChunkInPrev(0, islotAdjusted, pchstrm); + + // Mapping from 32-bit to 16-bit should only increase the index. Except that there + // may be a LB in the slot stream that is not in the character stream, so it could be + // one less. + Assert(ichwSegLim + m_cslotPreSeg >= islotLB - 1); + + return ichwSegLim; +} + +/*---------------------------------------------------------------------------------------------- + Return the chunk mapping into the previous stream, taking into account the fact that it + is not recorded for the write-position. +----------------------------------------------------------------------------------------------*/ +int GrTableManager::ChunkInPrev(int ipass, int islot, GrCharStream * pchstrm) +{ + GrSlotStream * psstrmOut = OutputStream(ipass); + GrSlotStream * psstrmIn = (ipass == 0) ? NULL : InputStream(ipass); + + int islotTmp = islot; + int islotAdjusted; + do { + if (islot >= psstrmOut->WritePos()) + { + Assert(islot == psstrmOut->WritePos()); + islotAdjusted = (ipass == 0) ? pchstrm->SegOffset() : psstrmIn->ReadPos(); + Assert(islotAdjusted > -1); + } + else + islotAdjusted = psstrmOut->ChunkInPrev(islotTmp); + islotTmp--; + } while (islotAdjusted == -1); + + return islotAdjusted; +} + +/*---------------------------------------------------------------------------------------------- + Create a new segment corresponding to an empty string. + + @param pchstrm - input stream + @param fStartLine - does this segment start a line? + @param fEndLine - does this segment end a line? +----------------------------------------------------------------------------------------------*/ +void GrTableManager::InitSegmentAsEmpty(Segment * psegNew, Font * pfont, + GrCharStream * pchstrm, bool fStartLine, bool fEndLine) +{ + LineBrk lbStart = m_engst.m_lbPrevEnd; + LineBrk lbEnd = klbNoBreak; + + int ichwMin = 0; + int ichwLim = 0; + + Assert(psegNew); + + psegNew->Initialize(pchstrm->TextSrc(), ichwMin, ichwLim, + lbStart, lbEnd, kestNoMore, fStartLine, fEndLine, m_pgreng->RightToLeft()); + + psegNew->SetEngine(m_pgreng); + psegNew->SetFont(pfont); + psegNew->SetJustifier(NULL); // can't justify an empty segment + psegNew->SetFaceName(m_pgreng->FaceName(), m_pgreng->BaseFaceName()); + + byte pbNextSegDat[256]; + int cbNextSegDat; + int * pcbNextSegDat = &cbNextSegDat; + + // Initialization for (theoretical) next segment. + byte * pb = pbNextSegDat; + *pb++ = byte(lbEnd); + *pb++ = kdircNeutral; + *pb++ = kdircNeutral; + *pb++ = 0; + for (int ipass = 0; ipass < m_cpass; ipass++) + *pb++ = 0; + *pcbNextSegDat = 0; + psegNew->RecordInitializationForNextSeg(*pcbNextSegDat, pbNextSegDat); + + psegNew->SetPreContext(0); +} + +/*---------------------------------------------------------------------------------------------- + Find a line-break and unwind the passes. Return true if we were able to + backtrack successfully (which may have meant using a less-than-optimal line-break). + Return false if no line-break could be found (because maxBreakWeight did not allow + letter breaks or clipping--generally because there was already something on this + line). + + @param pislotPrevBreak - position of previously-created break, -1 if none; + this is the index of the line-break glyph if any, + or the index of the last slot in the segment; + adjusted to contain the position of the newly + found break + @param plbMin - minimum (best) line break weight to try + @param lbMax - maximum (worst possible) line break weight + @param twsh - how to handle trailing white-space + @param fMoreText - true if char stream doesn't go up to original lim + @param ichwCallerBtLim - caller's backtrack lim; -1 if caller is not backtracking + @param plbFound - kind of line-break created +----------------------------------------------------------------------------------------------*/ +bool GrTableManager::Backtrack(int * pislotPrevBreak, + LineBrk * plbMin, LineBrk lbMax, TrWsHandling twsh, bool fMoreText, + int ichwCallerBtLim, bool fEndLine, + LineBrk * plbFound) +{ + int islotStartTry; + //if (*pislotPrevBreak == ichwCallerBtLim - 1) + //{ + // islotStartTry = *pislotPrevBreak; + //} + //else + if (*pislotPrevBreak == -1) + { + // If no line break has been found so far, figure out where to start trying based + // on where the final stream choked. + GrSlotStream * psstrmFinal = OutputStream(m_cpass-1); + if ((islotStartTry + = m_engst.TraceStreamZeroPos(psstrmFinal->WritePos()-1, TopDirectionLevel())) + == -1) + { + // Just start looking at the end of what's been generated. + islotStartTry = OutputStream(m_cpassLB)->ReadPos() - 1; + } + } + else + { + // Start just before previous line break. + if (m_engst.m_fInsertedLB || m_engst.m_fFinalLB) + islotStartTry = *pislotPrevBreak - 2; // skip inserted LB and previous slot + else + islotStartTry = *pislotPrevBreak - 1; // skip previous slot + } + if (ichwCallerBtLim > -1 && islotStartTry > ichwCallerBtLim - 1) + { + islotStartTry = ichwCallerBtLim - 1; + } + + // Determine if we want to insert an actual line-break "glyph". Don't do that if + // we are omitting trailing white space or we are at a writing-system or font break. + bool fInsertLB; + if (twsh == ktwshNoWs) + // omitting trailing white-space + fInsertLB = false; + + // These rules seem more complicated than what is necessary... :-/ + //else if (twsh == ktwshOnlyWs) + // // trailing white-space segment + // fInsertLB = true; + //else if (ichwCallerBtLim > -1) + // // backtracking + // fInsertLB = true; + //else if (*pislotPrevBreak > -1) + // // no longer at the edge of the writing system or font + // fInsertLB = true; + //else if (!fMoreText) + // // at final edge of total range to render + // fInsertLB = true; + //else + // fInsertLB = false; + + else if (!fEndLine) + fInsertLB = false; + else + fInsertLB = true; + + // Try to insert a line-break in the output of the (final) line-break pass (if any, or + // the output of the glyph-generation pass). First try the preferred (strictest) + // break weight and gradually relax. + LineBrk lb = *plbMin; + Assert(*plbMin <= lbMax); + GrSlotStream * psstrmLB = OutputStream(m_cpassLB); + islotStartTry = min(islotStartTry, psstrmLB->WritePos() - 1); + int islotNewBreak = -1; + while (lb <= lbMax) + { + LineBrk lbNextToTry; + if (fInsertLB) + { + islotNewBreak = psstrmLB->InsertLineBreak(this, + *pislotPrevBreak, m_engst.m_fInsertedLB, islotStartTry, + lb, twsh, m_engst.m_cslotPreSeg, &lbNextToTry); + } + else + { + islotNewBreak = psstrmLB->MakeSegmentBreak(this, + *pislotPrevBreak, m_engst.m_fInsertedLB, islotStartTry, + lb, twsh, m_engst.m_cslotPreSeg, &lbNextToTry); + } + if (islotNewBreak > -1) + break; + if (lb >= lbMax) + break; + lb = IncLineBreak(lb); // relax the break weight + } + + if (islotNewBreak == -1) + { + return false; + } + + // We've successfully inserted a line break. + + if (fInsertLB) + m_engst.m_fInsertedLB = true; + + m_engst.m_fFinalLB = false; + + UnwindAndReinit(islotNewBreak); + + *pislotPrevBreak = islotNewBreak; + *plbMin = lb; // return the best break we were able to find, so we don't keep trying + // for a better one when we know now that we can't find it + *plbFound = lb; + + return true; +} + +/*---------------------------------------------------------------------------------------------- + This method is called after backtracking or removing trailing white-space. + Unwind the following slot streams as necessary depending on where the change was made. + + @param islotNewBreak - in output of (final) line-break pass +----------------------------------------------------------------------------------------------*/ +void GrTableManager::UnwindAndReinit(int islotNewBreak) +{ + OutputStream(m_cpassLB)->ZapCalculatedDirLevels(islotNewBreak); + + // Mark the passes before the line-break pass as fully written, so that if there + // isn't a line-break glyph to intercept, we don't keep trying to get more input + // from them. + int ipass; + for (ipass = 1; ipass < m_cpassLB + 1; ++ipass) + { + InputStream(ipass)->MarkFullyWritten(); + } + + // Unwind the passes to the changed positions. + // Note that we don't need to unwind pass 0, since all it does is generate glyph IDs. + // Also we don't need to unwind the line-break passes, because conceptually they happen + // before the break is inserted. + + int islotChgPos = islotNewBreak; + bool fFirst = true; + for (ipass = m_cpassLB + 1 ; ipass < m_cpass ; ++ipass) + { + islotChgPos = + Pass(ipass)->Unwind(this, islotChgPos, InputStream(ipass), OutputStream(ipass), + fFirst); + fFirst = false; + } + // For anything that may have been skipped in the final output: + OutputStream(m_cpass - 1)->SetReadPos(0); + OutputStream(m_cpass - 1)->SetReadPosMax(0); + Pass(m_cpass - 1)->UndoResyncSkip(); + OutputStream(m_cpass - 1)->ClearSlotsSkippedToResync(); + + m_engst.InitPosCache(); + + m_engst.m_dxsShrinkPossible = GrSlotState::kNotYetSet; // recalculate it +} + +/*---------------------------------------------------------------------------------------------- + Store information in the newly created segment that will allow us to reinitalize + the streams in order to process the next segment, appropriately handling any + cross-line-boundary contextuals. + + The information required is the number of glyphs in the first stream that must + be reprocessed, and a skip-offset between each stream indicating the beginning + of chunk boundaries. + + In most cases, where there are no cross-line-boundary contextuals, all these numbers + will be zero. + + The buffer generated is of the following format (all are 1 byte, unsigned): + end breakweight + previous strong directionality code + previous terminator dir code + restart backup + skip offsets for each pass + This format must match what is generated by InitializeStreams. + + @param pseg - newly created segment + @param islotUnderBreak - position of line break inserted in underlying stream (output + of glyph-generation pass--stream 0); -1 if we rendered to + the end of the range + @param islotSurfaceBreak - position of line break in surface stream; + -1 if we rendered to the end of the range + @param lbEnd - kind of line-break at the end of this segment + @param fNextSegNeedsContext - true if we need to remember the context for the next seg + @param pchstrm - character stream input for this segment + @param cbNextMax - amount of space available in the buffer + @param pbNextSegDat - buffer for initializing next segment + @param pcbNextSegDat - amount of space used in buffer +----------------------------------------------------------------------------------------------*/ +void GrTableManager::InitializeForNextSeg(Segment * pseg, + int islotUnderBreak, int islotSurfaceBreak, LineBrk lbEnd, + bool fNextSegNeedsContext, GrCharStream * pchstrm) +{ + byte pbNextSegDat[256]; + int cbNextSegDat; + int * pcbNextSegDat = &cbNextSegDat; + + std::vector<int> vcslotSkipOffsets; + vcslotSkipOffsets.resize(m_cpass); + + byte * pb = pbNextSegDat; + + if (!fNextSegNeedsContext) + { + // No contextualization between this segment and the next, whatever kind of renderer + // it might use. + *pcbNextSegDat = 0; + return; + } + + gid16 chwLB = LBGlyphID(); + + int islotUnderLim = islotUnderBreak; + if (!m_engst.m_fInsertedLB && !m_engst.m_fFinalLB) + // Then islotUnderBreak is the position of the last slot in the segment; increment + // to get the lim in stream zero. + islotUnderLim++; + + int islotSurfaceLim = islotSurfaceBreak; + if (islotSurfaceLim == -1) + islotSurfaceLim = OutputStream(m_cpass - 1)->FinalSegLim(); + + *pb++ = byte(lbEnd); + + // Find previous strong and terminator directionality codes. + + GrSlotStream * psstrm = OutputStream(m_cpass - 1); + DirCode dircStrong = kdircNeutral; + DirCode dircTerm = kdircNeutral; + int islot; + for (islot = islotSurfaceLim; islot-- > 0; ) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + DirCode dirc = pslot->Directionality(); + if (dircTerm == kdircNeutral && dirc == kdircEuroTerm) + dircTerm = dirc; + if (StrongDir(dirc)) + { + dircStrong = dirc; + break; + } + } + *pb++ = byte(dircStrong); + *pb++ = byte(dircTerm); + + // Record how much of each pass to redo in the next segment. + // The basic algorithm is described in the "WR Data Transform Engine" document. + // But here we are doing a variation on what is described there. We are calculating + // the backup locations for BOTH the rule start locations and the necessary pre-contexts. + // It is the precontext values that are recorded for use by the following segment. But we + // also calculate the positions for the rule-start locations to make sure there is enough + // pre-context at each pass. + + int islotRS = islotSurfaceLim; // rule-start positions + int islotPC = islotRS; // to handle pre-context positions + + // If line-breaks are irrelevant to this font, we don't need to bother. + bool fGiveUp = !m_pgreng->LineBreakFlag(); + + int ipass; + for (ipass = m_cpass - 1; ipass >= 0 && !fGiveUp; --ipass) + { + GrSlotStream * psstrmIn = (ipass == 0) ? NULL : InputStream(ipass); + GrSlotStream * psstrmOut = OutputStream(ipass); + int islotBackupMin = (ipass == 0) ? 0 : psstrmOut->SegMin(); + if (ipass >= m_cpassLB && m_engst.m_fInitialLB) + islotBackupMin++; // how far we can back up--not before the start of this segment + + int islotPrevRS = 0; // corresponding slot in the previous stream for rule-start position + int islotPrevPC = 0; // same for pre-context position + + // Initially we have to include at least this many prior slots: + int cslotSkipOffset = 0; + + if (islotRS == psstrmOut->WritePos()) + { + islotPrevRS = (ipass == 0) ? pchstrm->Pos() : psstrmIn->ReadPos(); + } + else + { + // Ignore the final line-break, because it is explicitly inserted during + // the (final) line-break pass. +// if (ipass == m_cpassLB && psstrm->SlotAt(islotRS)->IsFinalLineBreak(chwLB)) +// { +// Assert(islotRS > 0); +// islotRS--; +// } + // Ignore the chunk in the pass that generated the line break that corresponds + // to the inserted line break being chunked with the previous glyph--we don't + // want to treat that as a cross-line-boundary contextual! + if (ipass == m_cpassLB && psstrmOut->SlotAt(islotRS)->IsFinalLineBreak(chwLB)) + { +// Assert(psstrm->ChunkInPrev(islotRS) == -1); + if (islotRS + 1 == psstrmOut->WritePos()) + islotPrevRS = (ipass == 0) ? pchstrm->SegOffset() : psstrmIn->ReadPos(); + else + islotPrevRS = psstrmOut->ChunkInPrev(islotRS + 1); + } + else + { + // Back up to the beginning of a chunk. + while (islotRS >= islotBackupMin && + (islotPrevRS = psstrmOut->ChunkInPrev(islotRS)) == -1) + { + --islotRS; + //++cslotSkipOffset; + } + } + } + if (islotPrevRS == -1 || islotRS < islotBackupMin) // hit the start of the segment + { + // In order to get enough context to be sure to run the same rules again, + // we'd have to back up into the previous segment. Too complicated, so give up. + fGiveUp = true; + break; + } + + int cslotPreContext = Pass(ipass)->MaxRulePreContext(); +LBackupPC: + if (islotPC == psstrmOut->WritePos()) + { + islotPrevPC = (ipass == 0) ? pchstrm->Pos() : psstrmIn->ReadPos(); + } + else + { + if (ipass == m_cpassLB && psstrmOut->SlotAt(islotPC)->IsFinalLineBreak(chwLB)) + { + if (islotPC+1 == psstrmOut->WritePos()) + islotPrevPC = (ipass == 0) ? pchstrm->SegOffset() : psstrmIn->ReadPos(); + else + islotPrevPC = psstrmOut->ChunkInPrev(islotPC+1); + } + else + { + // Back up to the beginning of a chunk; also make sure there are enough slots + // to handle the required pre-context for this pass. + while (islotPC >= islotBackupMin && + ((islotPrevPC = psstrmOut->ChunkInPrev(islotPC)) == -1 || + islotPrevRS - islotPrevPC < cslotPreContext)) + { + --islotPC; + ++cslotSkipOffset; + } + } + } + if (islotPC >= islotBackupMin && islotPrevRS - islotPrevPC < cslotPreContext) + { + --islotPC; + ++cslotSkipOffset; + goto LBackupPC; + } + if (islotPrevPC == -1 || islotPC < islotBackupMin) // hit the start of the segment + { + fGiveUp = true; + break; + } + + vcslotSkipOffsets[ipass] = cslotSkipOffset; + + islotRS = islotPrevRS; + islotPC = islotPrevPC; + + if (ipass == 0) + { + // Convert from underlying chars to stream-zero slots. + islotRS += m_engst.m_cslotPreSeg; + islotPC += m_engst.m_cslotPreSeg; + } + } + + // The following should be the case unless they tried to position contextually across + // the line break boundary, which is a very strange thing to do! - no longer true, + // because the pre-context is now included in this count, and it normal to have + // a pre-context in the final positioning pass. + //Assert(vcslotSkipOffsets[m_cpass-1] == 0); + + // Also no adjustments are made by the glyph-generation pass, so this should always + // be true: + Assert(vcslotSkipOffsets[0] == 0); + + // The number of slots in the underlying input previous to the line-break + // that must be reprocessed: + int cslotPreLB = islotUnderLim - islotPC; + + if (fGiveUp) + { + // We can't reasonably provide enough context, so don't try to do it at all. + cslotPreLB = 0; + for (ipass = 0; ipass < m_cpass; ipass++) + vcslotSkipOffsets[ipass] = 0; + } + + Assert(cslotPreLB >= 0); + Assert(cslotPreLB < 0xFF); + + *pb++ = byte(cslotPreLB); + for (ipass = 0; ipass < m_cpass; ipass++) + *pb++ = byte(vcslotSkipOffsets[ipass]); + + *pcbNextSegDat = 4 + m_cpass; + + pseg->RecordInitializationForNextSeg(*pcbNextSegDat, pbNextSegDat); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the associations, and record the output slots in the segment. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::RecordAssocsAndOutput(Font * pfont, + Segment * pseg, bool fWidthIsCharCount, + TrWsHandling twsh, bool fParaRtl, int nDirDepth) +{ + int cchwUnderlying = pseg->stopCharacter() - pseg->startCharacter(); + + GrSlotStream * psstrmFinal = OutputStream(m_cpass-1); + int csloutSurface = psstrmFinal->WritePos() - psstrmFinal->IndexOffset(); // # of output slots + + psstrmFinal->SetNeutralAssociations(LBGlyphID()); + + float xsTotalWidth, xsVisWidth; + +#ifdef OLD_TEST_STUFF + if (fWidthIsCharCount) + xsTotalWidth = xsVisWidth = 0; + else +#endif // OLD_TEST_STUFF + + // Make sure the final positions are set for every glyph. + CalcPositionsUpTo(m_cpass-1, reinterpret_cast<GrSlotState *>(NULL), + &xsTotalWidth, &xsVisWidth); + pseg->SetWidths(xsVisWidth, xsTotalWidth); + + pseg->SetUpOutputArrays(pfont, this, psstrmFinal, cchwUnderlying, csloutSurface, LBGlyphID(), + twsh, fParaRtl, nDirDepth); + + // Set underlying-to-surface assocations in the segment. + CalculateAssociations(pseg, csloutSurface); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the underlying-to-surface associations and ligature mappings. + Assumes the arrays have been properly initialized. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::CalculateAssociations(Segment * pseg, int csloutSurface) +{ + GrSlotStream * psstrmFinal = OutputStream(m_cpass-1); + + std::vector<int> vichwAssocs; + std::vector<int> vichwComponents; + std::vector<int> vicomp; + + for (int islot = psstrmFinal->IndexOffset(); islot < psstrmFinal->WritePos(); islot++) + { + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + int islout = islot - psstrmFinal->IndexOffset(); + if (!pslot->IsLineBreak(LBGlyphID())) + { + vichwAssocs.clear(); + pslot->AllAssocs(vichwAssocs); + + size_t iichw; + for (iichw = 0; iichw < vichwAssocs.size(); ++iichw) + { + pseg->RecordSurfaceAssoc(vichwAssocs[iichw], islout, 0); + } + + vichwComponents.clear(); + vicomp.clear(); + if (pslot->HasComponents()) + pslot->AllComponentRefs(vichwComponents, vicomp); + + for (iichw = 0; iichw < vichwComponents.size(); iichw++) + { + pseg->RecordLigature(vichwComponents[iichw], islout, vicomp[iichw]); + } + } + } + + AdjustAssocsForOverlaps(pseg); + pseg->CleanUpAssocsVectors(); + +// pseg->AdjustForOverlapsWithPrevSeg(); // for any characters officially in the + // previous segment but rendered in this one, + // or vice versa + + // TODO SharonC: for all characters that are right-to-left in the underlying stream, + // reverse the before and after flags in the segment. +} + +/*---------------------------------------------------------------------------------------------- + For any slots associated with characters in the new segment but not rendered in the + segment, adjust the underlying-to-surface mappings accordingly. Ie, set the before + mapping to kNegInfinity for slots at the beginning of the streams, and set the + after mapping to kPosInfinity for slots at the end of the streams. + + TODO SharonC: Rework this method more carefully to handle each slot exactly once. The current + approach could possibly, for instance, process a slot that is inserted in one pass + and deleted in the next. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::AdjustAssocsForOverlaps(Segment * pseg) +{ + if (!m_engst.m_fInitialLB && !m_engst.m_fFinalLB && !m_engst.m_fInsertedLB) + // no cross-line contextualization possible + return; + + gid16 chwLB = LBGlyphID(); + std::vector<int> vichwAssocs; + + // Process any slots output by any substitution pass (or later) on either side of the + // line-breaks. These are the relevant slots that are rendered in the previous and + // following segments. + for (int ipass = m_cpass; ipass-- > m_cpassLB + 1; ) + { + GrSlotStream * psstrm = OutputStream(ipass); + int islotMin = (ipass == m_cpass - 1) ? psstrm->IndexOffset() : 0; + + if (m_engst.m_fInitialLB) + { + for (int islot = islotMin; ; islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsInitialLineBreak(chwLB)) + break; + Assert(!pslot->IsFinalLineBreak(chwLB)); + if (pslot->PassModified() != ipass) + continue; + + vichwAssocs.clear(); + pslot->AllAssocs(vichwAssocs); + for (size_t iichw = 0; iichw < vichwAssocs.size(); ++iichw) + pseg->MarkSlotInPrevSeg(vichwAssocs[iichw], islot); + } + } + + if ((m_engst.m_fFinalLB || m_engst.m_fInsertedLB) && ipass > m_cpassLB) + { + for (int islot = psstrm->WritePos(); islot-- > islotMin ; ) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsFinalLineBreak(chwLB)) + break; + Assert(!pslot->IsInitialLineBreak(chwLB)); + if (pslot->PassModified() != ipass) + continue; + + vichwAssocs.clear(); + pslot->AllAssocs(vichwAssocs); + for (size_t iichw = 0; iichw < vichwAssocs.size(); ++iichw) + pseg->MarkSlotInNextSeg(vichwAssocs[iichw], islot); + } + } + } +} + +/*---------------------------------------------------------------------------------------------- + Initialize all the streams. In particular, process the information about the + skip-offsets to handle segments other than the first. + + @param cbPrev - number of bytes in the pbPrevSegDat buffer + @param pbPrevSegDat - buffer in the following format (all are 1 byte, unsigned): + prev seg's end breakweight + prev seg's previous strong directionality code + prev seg's previous terminator dir code + restart backup + skip offsets for each pass + This format must match what is generated by InitializeForNextSeg. +----------------------------------------------------------------------------------------------*/ +void EngineState::InitializeStreams(GrTableManager * ptman, + GrCharStream * pchstrm, + int cbPrev, byte * pbPrevSegDat, bool fNeedFinalBreak, int * pislotFinalBreak) +{ + int cpassLB = ptman->NumberOfLbPasses(); + int ipassPos1 = ptman->FirstPosPass(); + + CreateSlotStreams(); + + int cslotBackup = 0; + + InitForNewSegment(ptman); + + if (cbPrev == 0) + { + m_lbPrevEnd = klbNoBreak; + m_dircInitialStrongDir = kdircNeutral; + m_dircInitialTermDir = kdircNeutral; + for (int ipass = 0; ipass < m_cpass; ++ipass) + { + OutputStream(ipass)->Initialize(ipassPos1, false); + m_prgzpst[ipass].SetResyncSkip(0); + + if (ptman->LoggingTransduction()) + m_prgzpst[ipass].InitializeLogInfo(); + } + } + else + { + Assert(cbPrev >= 4); + byte * pb = pbPrevSegDat; + + m_lbPrevEnd = LineBrk(*pb++); + + // Directionality codes from the previous segment. + m_dircInitialStrongDir = DirCode((int)*pb++); + m_dircInitialTermDir = DirCode((int)*pb++); + + cslotBackup = (int)*pb++; + Assert((cbPrev == 4 || cslotBackup == 0) || cbPrev == m_cpass + 4); + + for (int ipass = 0; ipass < m_cpass; ++ipass) + { + OutputStream(ipass)->Initialize(ipassPos1, true); + if (cbPrev == 4) + m_prgzpst[ipass].SetResyncSkip(0); // different writing system + else + m_prgzpst[ipass].SetResyncSkip(*pb++); + + if (ptman->LoggingTransduction()) + m_prgzpst[ipass].InitializeLogInfo(); + } + + pchstrm->Backup(cslotBackup); + } + + if (cbPrev > 0 || pchstrm->StartLine()) + { + // Go ahead and run the initial line-break passes (if any), and then insert + // a line-break at the appropriate place where the new segment will begin. + int cslotToGet = cslotBackup; +LGetMore: + ptman->Pass(0)->ExtendGlyphIDOutput(ptman, pchstrm, OutputStream(0), + -1, 0, klbWordBreak, + cslotToGet, fNeedFinalBreak, m_twsh, pislotFinalBreak); + OutputStream(0)->SetSegMin(cslotBackup); + for (int ipass = 1; ipass <= cpassLB; ipass++) + { + if (cslotToGet > 0) + { + int nRet = kNextPass; + int cslotGot; + ptman->Pass(ipass)->ExtendOutput(ptman, + InputStream(ipass), OutputStream(ipass), + cslotToGet, m_twsh, &nRet, &cslotGot, pislotFinalBreak); + if (nRet != kNextPass) + { + cslotToGet = 1; + goto LGetMore; + } + } + OutputStream(ipass)->SetSegMin(cslotBackup); + } + Assert(OutputStream(cpassLB)->WritePos() >= cslotBackup); + m_cslotPreSeg = cslotBackup; + if (pchstrm->StartLine()) + { + OutputStream(cpassLB)->AppendLineBreak(ptman, pchstrm, m_lbPrevEnd, + (ptman->RightToLeft() ? kdircRlb : kdircLlb), cslotBackup, true, -1); + SetInitialLB(); + m_cslotPreSeg++; + if (cpassLB > 0 && *pislotFinalBreak > -1) + // See corresponding kludge in GrPass::ExtendGlyphIDOutput. + *pislotFinalBreak += 1; + } + else + { + OutputStream(cpassLB)->SetSegMin(cslotBackup); + } + OutputStream(cpassLB)->CalcIndexOffset(ptman); + } + + else + { + Assert(cslotBackup == 0); + OutputStream(0)->SetSegMin(0); + m_cslotPreSeg = 0; + } +} + +/*---------------------------------------------------------------------------------------------- + Reinitialize as we start to generate a new segment. +----------------------------------------------------------------------------------------------*/ +void EngineState::InitForNewSegment(GrTableManager * ptman) +{ + // Set up a fresh batch of slots. + DestroySlotBlocks(); + m_islotblkCurr = -1; + m_islotNext = kSlotBlockSize; + + m_fInitialLB = false; + m_fFinalLB = false; + m_fInsertedLB = false; + m_fExceededSpace = false; + m_fHitHardBreak = false; + m_fRemovedWhtsp = false; + InitPosCache(); + //////m_pgg = pgg; + + for (int ipass = 0; ipass < m_cpass; ipass++) + m_prgzpst[ipass].InitForNewSegment(ipass, ptman->Pass(ipass)->MaxRuleContext()); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the position in stream zero of the given slot in the final stream, + based on the associations. (This will be the same as the position at the end of the + line-break passes, which is really what we want.) + + @param islotFinal - index of the final slot in the final stream +----------------------------------------------------------------------------------------------*/ +int EngineState::TraceStreamZeroPos(int islotFinal, int nTopDir) +{ + GrSlotStream * psstrmFinal = OutputStream(m_cpass - 1); + if (psstrmFinal->WritePos() == 0) + return -1; + + GrSlotState * pslot = psstrmFinal->SlotAt(islotFinal); + + // If we're in the middle of reordered bidi stuff, just use the end of the stream. + if (pslot->DirLevel() > nTopDir) + return -1; + + int islot = pslot->BeforeAssoc(); + if (islot == kPosInfinity || islot < 0) + return -1; + return islot + m_cslotPreSeg; +} + +/*---------------------------------------------------------------------------------------------- + Return the next higher (more relaxed, less desirable) line break weight. +----------------------------------------------------------------------------------------------*/ +LineBrk GrTableManager::IncLineBreak(LineBrk lb) +{ + Assert(lb != klbClipBreak); + return (LineBrk)((int)lb + 1); +} + +/*---------------------------------------------------------------------------------------------- + Return a pointer to a slot that is near the current position in the given stream. + Return NULL if no slots have been output at all. + This is used for initializing features of line-break slots. + @param ipassArg - pass at which to start looking + @param islot - slot to look for; -1 if considering the current position +----------------------------------------------------------------------------------------------*/ +GrSlotState * EngineState::AnAdjacentSlot(int ipassArg, int islot) +{ + int ipass = ipassArg; + while (ipass >= 0) + { + GrSlotStream * psstrm = OutputStream(ipass); + if (psstrm->NumberOfSlots() > 0) + { + if (islot == -1) + { + if (psstrm->AtEnd()) + return psstrm->SlotAt(psstrm->WritePos() - 1); + else + return psstrm->Peek(); + } + else + { + if (psstrm->WritePos() <= islot) + return psstrm->SlotAt(psstrm->WritePos() - 1); + else + return psstrm->SlotAt(islot); + } + } + ipass--; + } + return NULL; +} + +/*---------------------------------------------------------------------------------------------- + Simple access methods. +----------------------------------------------------------------------------------------------*/ +GrEngine * GrTableManager::Engine() +{ + return m_pgreng; +} + +EngineState * GrTableManager::State() +{ + return &m_engst; +} + +GrEngine * EngineState::Engine() +{ + return m_ptman->Engine(); +} + +GrTableManager * EngineState::TableManager() +{ + return m_ptman; +} + +/*---------------------------------------------------------------------------------------------- + Memory management for slots +----------------------------------------------------------------------------------------------*/ + +// standard for pass 0 slots +void EngineState::NewSlot( + gid16 gID, GrFeatureValues fval, int ipass, int ichwSegOffset, int nUnicode, + GrSlotState ** ppslotRet) +{ + NextSlot(ppslotRet); + (*ppslotRet)->Initialize(gID, Engine(), fval, ipass, ichwSegOffset, nUnicode); +} + +// line-break slots +void EngineState::NewSlot( + gid16 gID, GrSlotState * pslotFeat, int ipass, int ichwSegOffset, + GrSlotState ** ppslotRet) +{ + NextSlot(ppslotRet); + (*ppslotRet)->Initialize(gID, Engine(), pslotFeat, ipass, ichwSegOffset); +} + +// for inserting new slots after pass 0 (under-pos and unicode are irrelevant) +void EngineState::NewSlot( + gid16 gID, GrSlotState * pslotFeat, int ipass, + GrSlotState ** ppslotRet) +{ + NextSlot(ppslotRet); + (*ppslotRet)->Initialize(gID, Engine(), pslotFeat, ipass); +} + +// for making a new version of the slot initialized from the old +void EngineState::NewSlotCopy(GrSlotState * pslotCopyFrom, int ipass, + GrSlotState ** ppslotRet) +{ + NextSlot(ppslotRet); + (*ppslotRet)->InitializeFrom(pslotCopyFrom, ipass); +} + +void EngineState::NextSlot(GrSlotState ** ppslotRet) +{ + int cnExtraPerSlot = m_cUserDefn + (m_cCompPerLig * 2) + m_cFeat; + if (m_islotNext >= kSlotBlockSize) + { + // Allocate a new block of slots + GrSlotState * slotblkNew = new GrSlotState[kSlotBlockSize]; + u_intslot * prgnSlotBuf = new u_intslot[kSlotBlockSize * cnExtraPerSlot]; + + m_vslotblk.push_back(slotblkNew); + m_vprgnSlotVarLenBufs.push_back(prgnSlotBuf); + m_islotblkCurr++; + m_islotNext = 0; + Assert((unsigned)m_islotblkCurr == m_vslotblk.size()-1); + } + + *ppslotRet = m_vslotblk[m_islotblkCurr] + m_islotNext; + (*ppslotRet)->BasicInitialize(m_cUserDefn, m_cCompPerLig, m_cFeat, + (m_vprgnSlotVarLenBufs[m_islotblkCurr] + (m_islotNext * cnExtraPerSlot))); + m_islotNext++; +} + +/*---------------------------------------------------------------------------------------------- + Convert the number of font design units into source device logical units. +----------------------------------------------------------------------------------------------*/ +float GrTableManager::EmToLogUnits(int m) +{ + return m_engst.EmToLogUnits(m); +} + +float EngineState::EmToLogUnits(int m) +{ + if (m == 0) + return 0; + + float xysFontEmSquare; + m_pfont->getFontMetrics(NULL, NULL, &xysFontEmSquare); + + // mEmUnits should be equal to the design units in the font's em square + int mEmUnits = Engine()->GetFontEmUnits(); + if (mEmUnits <= 0) + { + Warn("Failed to obtain font em-units"); + return (float)m; + } + + return GrEngine::GrIFIMulDiv(m, xysFontEmSquare, mEmUnits); +} + +/*---------------------------------------------------------------------------------------------- + Convert the source device coordinates to font design units. +----------------------------------------------------------------------------------------------*/ +int GrTableManager::LogToEmUnits(float xys) +{ + return m_engst.LogToEmUnits(xys); +} + +int EngineState::LogToEmUnits(float xys) +{ + if (xys == 0) + return 0; + + float xysFontEmSquare; + m_pfont->getFontMetrics(NULL, NULL, &xysFontEmSquare); + + // mEmUnits should be equal to the design units in the font's em square + int mEmUnits = Engine()->GetFontEmUnits(); + if (mEmUnits < 0) + { + Warn("Failed to obtain font em-units"); + return (int)xys; + } + + return GrEngine::GrFIFMulDiv(xys, mEmUnits, xysFontEmSquare); +} + +/*---------------------------------------------------------------------------------------------- + Find the coordinates for a given glyph point. + Return true on success, false otherwise. +----------------------------------------------------------------------------------------------*/ +bool GrTableManager::GPointToXY(gid16 chwGlyphID, int nGPoint, float * pxs, float * pys) +{ + return m_engst.GPointToXY(chwGlyphID, nGPoint, pxs, pys); +} + +bool EngineState::GPointToXY(gid16 chwGlyphID, int nGPoint, float * pxs, float * pys) +{ + *pxs = INT_MIN; + *pys = INT_MIN; + Point pointRet; + m_pfont->getGlyphPoint(chwGlyphID, nGPoint, pointRet); + *pxs = pointRet.x; + *pys = pointRet.y; + + return true; +} + +/*---------------------------------------------------------------------------------------------- + Call the justification routines. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::CallJustifier(IGrJustifier * pgjus, int ipassCurr, + float dxUnjustified, float dxJustified, bool fEndLine) +{ + // Indicates the stream the justification routines will read and modify: + m_engst.m_ipassJustCalled = ipassCurr; + + int iGlyphMin = OutputStream(ipassCurr)->SegMin(); + int iGlyphLim = OutputStream(ipassCurr)->SegLimIfKnown(); + if (iGlyphLim == -1) + iGlyphLim = OutputStream(ipassCurr)->WritePos(); + + GrSlotStream * psstrm = OutputStream(ipassCurr); + if (m_pgreng->BasicJustification() && fEndLine) + UnstretchTrailingWs(psstrm, iGlyphLim); + + pgjus->adjustGlyphWidths(m_pgreng, iGlyphMin, iGlyphLim, dxUnjustified, dxJustified); + + // After returning, reading and modifying slot attributes is not permitted: + m_engst.m_ipassJustCalled = -1; +} + +/*---------------------------------------------------------------------------------------------- + For the basic justification approach, remove stretch from the trailing whitespace. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::UnstretchTrailingWs(GrSlotStream * psstrm, int iGlyphLim) +{ + int islot = iGlyphLim - 1; + while (islot >= 0) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsLineBreak(LBGlyphID())) + { } // keep looking for the trailing space + else if (pslot->IsSpace(this)) + pslot->SetJStretch(0); // TODO: do this at level 0 + else + // something other than space--quit + return; + islot--; + } +} + +/*---------------------------------------------------------------------------------------------- + Determine how much shrink is permissible. + + This method is really used only for testing the shrink mechanism, since at this time + we don't allow low-end justification to do any shrinking. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::DetermineShrink(IGrJustifier * pgjus, int ipassCurr) +{ + if (m_engst.m_dxsShrinkPossible != GrSlotState::kNotYetSet) + return; + + if (m_engst.m_jmodi != kjmodiCanShrink || pgjus == NULL) + { + m_engst.m_dxsShrinkPossible = 0; + return; + } + + if (ipassCurr != m_ipassJust1 - 1) + return; + + GrSlotStream * psstrm = OutputStream(ipassCurr); + if (!psstrm->FullyWritten()) + return; + + // Normally just answer 0. To test shrinking, turn on the code below. + m_engst.m_dxsShrinkPossible = 0; + +#if TEST_SHRINK + int mTotal = 0; + int mShrink = 0; + int islotLim = psstrm->SegLimIfKnown(); + if (islotLim == -1) + islotLim = psstrm->WritePos(); + for ( ; islotLim >= 0; islotLim--) + { + // Trailing white space is not shrinkable. + GrSlotState * pslot = psstrm->SlotAt(islotLim - 1); + if (pslot->IsLineBreak(LBGlyphID())) + continue; + if (!pslot->IsSpace(this)) + break; + } + int islot; + for (islot = psstrm->SegMin(); islot < islotLim; islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsLineBreak(LBGlyphID())) + continue; + mTotal += pslot->AdvanceX(this); + mShrink += pslot->JShrink(); + } + + // Say that we are allowed to shrink up to 10% of the width of the segment. + mShrink = min(mShrink, mTotal/10); + m_engst.m_dxsShrinkPossible = EmToLogUnits(mShrink); +#endif // TEST_SHRINK +} + +/*---------------------------------------------------------------------------------------------- + Return the attribute of the given slot, back to the justification routine. Return + logical units. +----------------------------------------------------------------------------------------------*/ +GrResult EngineState::GetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, + float * pValueRet) +{ + GrResult res; + int valueRetInt = 0; + switch(jgat) + { + case kjgatWeight: + case kjgatStretchInSteps: + case kjgatBreak: + res = GetGlyphAttrForJustification(iGlyph, jgat, nLevel, &valueRetInt); + *pValueRet = (float)valueRetInt; + return res; + default: + break; + } + + if (m_ipassJustCalled == -1) + return kresUnexpected; + + if (nLevel != 1) + return kresInvalidArg; + + GrSlotStream * psstrm = OutputStream(m_ipassJustCalled); + + if (iGlyph < -1 || iGlyph >= psstrm->WritePos()) + return kresInvalidArg; + + GrSlotState * pslot = psstrm->SlotAt(iGlyph); + + // bool fConvertUnits = true; + + switch(jgat) + { + case kjgatStretch: + *pValueRet = EmToLogUnits(pslot->JStretch()); + break; + case kjgatShrink: + *pValueRet = EmToLogUnits(pslot->JShrink()); + break; + case kjgatStep: + *pValueRet = EmToLogUnits(pslot->JStep()); + break; + default: + return kresNotImpl; + } + return kresOk; +} + +GrResult EngineState::GetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, + int * pValueRet) +{ + GrResult res; + float valueRetFloat; + switch(jgat) + { + case kjgatStretch: + case kjgatShrink: + case kjgatStep: + res = GetGlyphAttrForJustification(iGlyph, jgat, nLevel, &valueRetFloat); + *pValueRet = GrEngine::RoundFloat(valueRetFloat); + return res; + default: + break; + } + + if (m_ipassJustCalled == -1) + return kresUnexpected; + + if (nLevel != 1) + return kresInvalidArg; + + GrSlotStream * psstrm = OutputStream(m_ipassJustCalled); + + if (iGlyph < -1 || iGlyph >= psstrm->WritePos()) + return kresInvalidArg; + + GrSlotState * pslot = psstrm->SlotAt(iGlyph); + + int mStretch, mStep; + //float xsStretch, xsStep; + + switch(jgat) + { + case kjgatWeight: + *pValueRet = pslot->JWeight(); + break; + case kjgatStretchInSteps: + // ???? should this be calculated in terms of logical units?? + // Probably doesn't matter, since the logical units are floating point, so the + // level of precision is about the same. + mStretch = pslot->JStretch(); + mStep = pslot->JStep(); + if (mStep == 0) + return kresUnexpected; + *pValueRet = int(mStretch / mStep); // round down + break; + //case kjgatStretchInSteps: + // xsStretch = EmToLogUnits(pslot->JStretch()); + // xsStep = EmToLogUnits(pslot->JStep()); + // if (xsStep == 0) + // return kresUnexpected; + // *pValueRet = int(xsStretch / xsStep); // round down + // break; + case kjgatBreak: + *pValueRet = pslot->BreakWeight(); + break; + default: + return kresNotImpl; + } + return kresOk; +} + +/*---------------------------------------------------------------------------------------------- + Set the attribute of the given slot, for the justification routine. +----------------------------------------------------------------------------------------------*/ +GrResult EngineState::SetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, + float value) +{ + GrResult res; + int valueInt; + switch(jgat) + { + case kjgatWeight: + case kjgatStretchInSteps: + case kjgatBreak: + valueInt = (int)value; + res = SetGlyphAttrForJustification(iGlyph, jgat, nLevel, valueInt); + return res; + default: + break; + } + + if (m_ipassJustCalled == -1) + return kresUnexpected; + + if (nLevel != 1) + return kresInvalidArg; + + GrSlotStream * psstrm = OutputStream(m_ipassJustCalled); + + if (iGlyph < -1 || iGlyph >= psstrm->WritePos()) + return kresInvalidArg; + + GrSlotState * pslot = psstrm->SlotAt(iGlyph); + + int mValue = LogToEmUnits(value); + mValue = min(mValue, 0xFFFF); // truncate to an unsigned short + + // Review: do we really want to allow them to set stretch/shrink/step/weight? The normal + // thing for them to do is set width. + switch(jgat) + { + case kjgatStretch: + pslot->SetJStretch(mValue); + break; + case kjgatShrink: + pslot->SetJShrink(mValue); + break; + case kjgatStep: + pslot->SetJStep(mValue); + break; + case kjgatWidth: + pslot->SetJWidth(mValue); + break; + default: + return kresNotImpl; + } + return kresOk; +} + +GrResult EngineState::SetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, + int value) +{ + GrResult res; + float valueFloat; + switch(jgat) + { + case kjgatStretch: + case kjgatShrink: + case kjgatStep: + case kjgatWidth: + valueFloat = (float)value; + res = SetGlyphAttrForJustification(iGlyph, jgat, nLevel, valueFloat); + return res; + default: + break; + } + + if (m_ipassJustCalled == -1) + return kresUnexpected; + + if (nLevel != 1) + return kresInvalidArg; + + GrSlotStream * psstrm = OutputStream(m_ipassJustCalled); + + if (iGlyph < -1 || iGlyph >= psstrm->WritePos()) + return kresInvalidArg; + + GrSlotState * pslot = psstrm->SlotAt(iGlyph); + + int mValue, cSteps; + switch(jgat) + { + case kjgatWeight: + pslot->SetJWeight(value); + break; + case kjgatWidthInSteps: + cSteps = value; + mValue = pslot->JStep(); + if (mValue == 0) + return kresUnexpected; + pslot->SetJWidth(mValue * cSteps); + break; + default: + return kresNotImpl; + } + return kresOk; +} + +//:>-------------------------------------------------------------------------------------------- +//:> Functions to forward to the engine. +//:>------------------------------------------------------------------------------------------*/ +gid16 GrTableManager::GetGlyphIDFromUnicode(int nUnicode) +{ + return m_pgreng->GetGlyphIDFromUnicode(nUnicode); +} + +/*--------------------------------------------------------------------------------------------*/ + +gid16 GrTableManager::ActualGlyphForOutput(utf16 chw) +{ + return m_pgreng->ActualGlyphForOutput(chw); +} + +/*--------------------------------------------------------------------------------------------*/ + +GrGlyphTable * GrTableManager::GlyphTable() +{ + return m_pgreng->GlyphTable(); +} + +/*--------------------------------------------------------------------------------------------*/ + +gid16 GrTableManager::LBGlyphID() +{ + return m_pgreng->LBGlyphID(); +} + +/*--------------------------------------------------------------------------------------------*/ + +gid16 GrTableManager::GetClassGlyphIDAt(int nClass, int nIndex) +{ + return m_pgreng->GetClassGlyphIDAt(nClass, nIndex); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::GetIndexInGlyphClass(int nClass, gid16 chwGlyphID) +{ + return m_pgreng->GetIndexInGlyphClass(nClass, chwGlyphID); +} + +/*--------------------------------------------------------------------------------------------*/ + +size_t GrTableManager::NumberOfGlyphsInClass(int nClass) +{ + return m_pgreng->NumberOfGlyphsInClass(nClass); +} + +/*--------------------------------------------------------------------------------------------*/ + +void GrTableManager::SetSlotAttrsFromGlyphAttrs(GrSlotState * pslot) +{ + m_pgreng->SetSlotAttrsFromGlyphAttrs(pslot); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::NumFeat() +{ + return m_pgreng->NumFeat(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::DefaultForFeatureAt(int ifeat) +{ + return m_pgreng->DefaultForFeatureAt(ifeat); +} + +/*--------------------------------------------------------------------------------------------*/ + +void GrTableManager::DefaultsForLanguage(isocode lgcode, + std::vector<featid> & vnFeats, std::vector<int> & vnValues) +{ + return m_pgreng->DefaultsForLanguage(lgcode, vnFeats, vnValues); +} + +/*--------------------------------------------------------------------------------------------*/ + +GrFeature * GrTableManager::FeatureWithID(featid nID, int * pifeat) +{ + return m_pgreng->FeatureWithID(nID, pifeat); +} + +/*--------------------------------------------------------------------------------------------*/ + +GrFeature * GrTableManager::Feature(int ifeat) +{ + return m_pgreng->Feature(ifeat); +} + +/*--------------------------------------------------------------------------------------------*/ + +bool GrTableManager::RightToLeft() +{ + if (m_engst.WhiteSpaceOnly()) + return m_engst.m_fParaRtl; + else + return m_pgreng->RightToLeft(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::TopDirectionLevel() +{ + return m_pgreng->TopDirectionLevel(); +} + +/*--------------------------------------------------------------------------------------------*/ + +float GrTableManager::VerticalOffset() +{ + return m_pgreng->VerticalOffset(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::NumUserDefn() +{ + return m_pgreng->NumUserDefn(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::NumCompPerLig() +{ + return m_pgreng->NumCompPerLig(); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID) +{ + return m_pgreng->ComponentIndexForGlyph(chwGlyphID, nCompID); +} + +/*--------------------------------------------------------------------------------------------*/ + +int GrTableManager::GlyphAttrValue(gid16 chwGlyphID, int nAttrID) +{ + return m_pgreng->GlyphAttrValue(chwGlyphID, nAttrID); +} + +/*--------------------------------------------------------------------------------------------*/ + +bool GrTableManager::LoggingTransduction() +{ + return m_fLogging; + //return m_pgreng->LoggingTransduction(); +} + +/*---------------------------------------------------------------------------------------------- + Calculate the positions of the glyphs up to the given slot, within the output of the given + pass. If the slot is NULL, go all the way to the end of what has been generated. + + @param ipass - index of pass needing positioning; normally this will be the + final pass, but it could be another if positions are + requested by the rules themselves + @param pslotLast - last slot that needs to be positioned, or NULL + @param pxsWidth - return the total width used so far + @param psxVisibleWidth - return the visible width so far + + MOVE to EngineState +----------------------------------------------------------------------------------------------*/ +void GrTableManager::CalcPositionsUpTo(int ipass, GrSlotState * pslotLast, + float * pxsWidth, float * pxsVisibleWidth) +{ + Assert(ipass >= m_ipassPos1 - 1); + + int isstrm = ipass; + GrSlotStream * psstrm = OutputStream(isstrm); + Assert(psstrm->GotIndexOffset()); + if (psstrm->WritePos() <= psstrm->IndexOffset()) + { + Assert(psstrm->WritePos() == 0); + *pxsWidth = 0; + *pxsVisibleWidth = 0; + return; + } + if (!pslotLast) + { + pslotLast = psstrm->SlotAt(psstrm->WritePos() - 1); + } + Assert(pslotLast); + + // First set positions of all base slots. + + int islot = psstrm->IndexOffset(); + GrSlotState * pslot; + float xs = 0; + float ys = VerticalOffset(); + *pxsWidth = 0; + *pxsVisibleWidth = 0; + + bool fLast = false; + bool fLastBase = false; + + bool fFakeItalic = m_pgreng->FakeItalic(); + float fakeItalicRatio = 0; + if (fFakeItalic) + fakeItalicRatio = State()->GetFont()->fakeItalicRatio(); + bool fBasicJust = m_pgreng->BasicJustification(); + + // Figure out how to know when to stop. + // We need to calculate up to the base of the last leaf slot, if it happens + // to be later in the stream than the last actual slot passed in. + if (!psstrm->HasSlotAtPosPassIndex(pslotLast->AttachRootPosPassIndex())) + return; + GrSlotState * pslotLastBase = pslotLast->Base(psstrm); + + if (ipass == m_cpass - 1 && m_engst.m_islotPosNext > -1) + { + // For final pass, initialize from cache of previous calculations. + islot = m_engst.m_islotPosNext; + xs = m_engst.m_xsPosXNext; + ys = m_engst.m_ysPosYNext; + *pxsWidth = m_engst.m_dxsTotWidthSoFar; + *pxsVisibleWidth = m_engst.m_dxsVisWidthSoFar; + + if (psstrm->SlotsPresent() <= islot) + return; + + if (pslotLastBase->PosPassIndex() == GrSlotAbstract::kNotYetSet) + { + Assert(false); // I think this case is handled above + return; // can't position this slot yet; its base has not been processed + } + + if (pslotLastBase->PosPassIndex() + psstrm->IndexOffset() < islot) + { + fLastBase = true; + if (pslotLast == pslotLastBase) + fLast = true; + } + } + + std::vector<GrSlotState *> vpslotAttached; + + bool fRtl = RightToLeft(); + + while (!fLast || !fLastBase) + { + Assert(islot < psstrm->SlotsPresent()); + + pslot = (isstrm == ipass) ? psstrm->SlotAt(islot) : psstrm->OutputSlotAt(islot); + + if (!pslot->IsBase()) + { + // This slot is attached to another; it will be positioned strictly + // relative to that one. This happens in the loop below. + vpslotAttached.push_back(pslot); + } + else + { + // Base character. + + bool fLB = pslot->IsLineBreak(LBGlyphID()); + if (InternalJustificationMode() == kjmodiJustify && fBasicJust + && ipass == m_cpass - 1 && !fLB) + { + m_engst.AddJWidthToAdvance(psstrm, &pslot, islot, &pslotLast, &pslotLastBase); + } + + // Make sure the metrics are the complete ones. + pslot->CalcCompositeMetrics(this, psstrm, kPosInfinity, true); + + float xsInc = pslot->GlyphXOffset(psstrm, fakeItalicRatio); + float ysInc = pslot->GlyphYOffset(psstrm); + float xsAdvX = pslot->ClusterAdvWidth(psstrm); + float ysAdvY = (fLB) ? + 0 : + EmToLogUnits(pslot->AdvanceY(this)); + + if (fRtl) + { + xs -= xsAdvX; + ys -= ysAdvY; + pslot->SetXPos(xs + xsInc); + pslot->SetYPos(ys + ysInc); + } + else + { + pslot->SetXPos(xs + xsInc); + pslot->SetYPos(ys + ysInc); + xs += xsAdvX; + ys += ysAdvY; + } + + *pxsWidth = max(*pxsWidth, fabsf(xs)); + if (!IsWhiteSpace(pslot)) + *pxsVisibleWidth = *pxsWidth; + + if (isstrm == m_cpass - 1) + { + // For the final output pass, cache the results of the calculation so far. + // Only do this for slots that have actually been processed by the final + // pass, because others may have intermediate values that may be changed + // later. + m_engst.m_islotPosNext = pslot->PosPassIndex() + psstrm->IndexOffset() + 1; + m_engst.m_xsPosXNext = xs; + m_engst.m_ysPosYNext = ys; + m_engst.m_dxsTotWidthSoFar = *pxsWidth; + m_engst.m_dxsVisWidthSoFar = *pxsVisibleWidth; + } + } + + // Need to have calculated both the last leaf and the last base (if they happen to + // be different); if so, we're done. + if (pslot == pslotLast) + fLast = true; + if (pslot == pslotLastBase) + fLastBase = true; + + islot++; + } + + Assert(fLast); + Assert(fLastBase); + + // Now set positions of non-base slots, relative to their bases. + + for (size_t ipslot = 0; ipslot < vpslotAttached.size(); ipslot++) + { + GrSlotState * pslot = vpslotAttached[ipslot]; + GrSlotState * pslotBase = pslot->Base(psstrm); + if (pslotBase->XPosition() == kNegInfinity || pslotBase->YPosition() == kNegInfinity) + { + Assert(false); + continue; + } + float xsCluster = pslotBase->XPosition() - pslotBase->GlyphXOffset(psstrm, fakeItalicRatio); + float ysCluster = pslotBase->YPosition() - pslotBase->GlyphYOffset(psstrm); + float xsInc = pslot->GlyphXOffset(psstrm, fakeItalicRatio); + float ysInc = pslot->GlyphYOffset(psstrm); + pslot->SetXPos(xsCluster + xsInc); + pslot->SetYPos(ysCluster + ysInc); + + // My theory is that we don't need to adjust *pxsWidth here, because the width of + // any non-base slots should be factored into the advance width of their cluster + // base, which was handled above. + } +} + +/*---------------------------------------------------------------------------------------------- + Return true if the given slot is white space. +----------------------------------------------------------------------------------------------*/ +bool GrTableManager::IsWhiteSpace(GrSlotState * pslot) +{ + if (pslot->IsLineBreak(LBGlyphID())) + return true; + return pslot->IsSpace(this); +} + +/*---------------------------------------------------------------------------------------------- + When doing basic justification, apply the value of justify.width to the advance width. + This happens during the final positioning routine. +----------------------------------------------------------------------------------------------*/ +void EngineState::AddJWidthToAdvance(GrSlotStream * psstrm, GrSlotState ** ppslot, int islot, + GrSlotState ** ppslotLast, GrSlotState ** ppslotLastBase) +{ + Assert(psstrm->OutputOfPass() == m_cpass - 1); // only used for final stream + if ((*ppslot)->JWidth() > 0) + { + if ((*ppslot)->PassModified() != m_cpass - 1) + { + // Replace the slot with a new slot, so it gets marked properly with the + // pass in which it was modified. (Otherwise the debug log gets screwed up.) + GrSlotState * pslotNew; + NewSlotCopy((*ppslot), m_cpass - 1, &pslotNew); + psstrm->PutSlotAt(pslotNew, islot); + if (*ppslot == *ppslotLast) + *ppslotLast = pslotNew; + if (*ppslot == *ppslotLastBase) + *ppslotLastBase = pslotNew; + *ppslot = pslotNew; + } + (*ppslot)->AddJWidthToAdvance(TableManager()); + } +} + + +//:>******************************************************************************************** +//:> Debuggers and utilities +//:>******************************************************************************************** + +//:Ignore +#if 0 +void GrTableManager::TempDebug() +{ + for (int ipass = 0; ipass < m_cpass; ++ipass) + { + int wpos = OutputStream(ipass)->WritePos(); + int rpos = OutputStream(ipass)->ReadPos(); + GrSlotState * wslot = OutputStream(ipass)->LastSlotWritten(); + GrSlotState * rslot = OutputStream(ipass)->LastSlotRead(); + + } +} +#endif +/*---------------------------------------------------------------------------------------------- + Given a character index in the original string, locate the corresponding line-break slot + in the final surface stream. The character index must be one before which there is a + line-break (seg lim), or the beginning or the end of the string. + TODO SharonC: fix the bugs +----------------------------------------------------------------------------------------------*/ +int GrTableManager::SurfaceLineBreakSlot(int ichw, GrCharStream * pchstrm, bool fInitial) +{ + if (ichw == 0) + { + Assert(fInitial); + return -1; // no initial line break + } + if (ichw == pchstrm->Lim()) + { + Assert(!fInitial); + return -1; // no terminating line break + } + + int islotIn = pchstrm->SegOffset(ichw); + Assert(islotIn >= 0); + islotIn += (fInitial? m_engst.m_cslotPreSeg - 1: m_engst.m_cslotPreSeg); + + gid16 chwLBGlyphID = LBGlyphID(); + + // Now islotIn is the position in stream 0; + + for (int ipass = 1; ipass < m_cpass; ++ipass) + { + int islotChunkMin; + int islotChunkLim; + GrSlotStream * psstrmOut = OutputStream(ipass); + + GrSlotStream * psstrmIn = InputStream(ipass); + if (fInitial) + islotIn = max(islotIn, psstrmIn->PreChunk()); + int islotT = psstrmIn->ChunkInNextMin(islotIn); + islotChunkMin = psstrmIn->ChunkInNext(islotT); + islotChunkMin = (islotChunkMin == -1)? 0: islotChunkMin; + + islotT = psstrmIn->ChunkInNextLim(islotIn); + Assert(islotT <= psstrmIn->ReadPos()); + if (islotT == psstrmIn->ReadPos()) + islotChunkLim = psstrmOut->WritePos(); + else + islotChunkLim = psstrmIn->ChunkInNext(islotT); + + int islotOut; + for (islotOut = islotChunkMin; islotOut < islotChunkLim; ++islotOut) + { + gid16 chw = psstrmOut->GlyphIDAt(islotOut); + if (chw == chwLBGlyphID) + break; + } + Assert(psstrmOut->GlyphIDAt(islotOut) == chwLBGlyphID); + + islotIn = islotOut; + } + return islotIn; +} + +/*---------------------------------------------------------------------------------------------- + Generate a debugger string showing the chunk boundaries for the given stream. +----------------------------------------------------------------------------------------------*/ +std::wstring GrTableManager::ChunkDebugString(int ipass) +{ + GrSlotStream * psstrm = OutputStream(ipass); + std::wstring stuRet; + //utf16 chwN = '\\'; + //utf16 chwR = '^'; + + gid16 chwLBGlyphID = LBGlyphID(); + + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + if (psstrm->ChunkInPrev(islot) != -1) + { + if (psstrm->ChunkInNext(islot) != -1) + stuRet.append(L">"); + else + stuRet.append(L"\\"); + } + else if (psstrm->ChunkInNext(islot) != -1) + stuRet.append(L"/"); + else + stuRet.append(L" "); + +// if (islot == psstrm->ReadPos()) +// stuRet.Append(&chwR, 1); + + wchar_t chw = psstrm->SlotAt(islot)->GlyphID(); + if (chw == chwLBGlyphID) + stuRet.append(L"#"); + else + stuRet.append(&chw, 1); + } + return stuRet; +} + +} // namespace gr + +//:End Ignore diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/GrTableManager.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrTableManager.h new file mode 100644 index 00000000000..b75034479e4 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/GrTableManager.h @@ -0,0 +1,557 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: GrTableManager.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + The GrTableManager class. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GR_TABLEMAN_INCLUDED +#define GR_TABLEMAN_INCLUDED + +#include <cassert> +//:End Ignore + +namespace gr +{ + +class Font; + +/*---------------------------------------------------------------------------------------------- + There is a single instance of Engine State that keeps track of the state of the processing + for a single segment generation. + + Hungarian: engst +----------------------------------------------------------------------------------------------*/ +class EngineState +{ + friend class GrTableManager; + friend class FontMemoryUsage; + +public: + EngineState(); + void Initialize(GrEngine *, GrTableManager *); + + ~EngineState(); + void DestroySlotBlocks(); + + void InitForNewSegment(GrTableManager * ptman); + + void CreateEmpty(); + + GrEngine * Engine(); + GrTableManager * TableManager(); + + void CreateSlotStreams(); + + GrSlotStream * InputStream(int ipass) + { + Assert(ipass > 0); // the char stream serves as input to the zeroth pass + return m_prgpsstrm[ipass - 1]; + } + + // Return the stream that serves as output to the given pass. + GrSlotStream * OutputStream(int ipass) + { + Assert(ipass < m_cpass); + return m_prgpsstrm[ipass]; + } + + GrSlotState * AnAdjacentSlot(int ipassArg, int islotArg); + + GrResult GetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, float * pxysValueRet); + GrResult GetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, int * pxysValueRet); + GrResult SetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, float xysValue); + GrResult SetGlyphAttrForJustification(int iGlyph, int jgat, int nLevel, int xysValue); + + enum { kSlotBlockSize = 50 }; // number of slots to allocate in a block + + void InitPosCache() + { + m_islotPosNext = -1; + m_xsPosXNext = 0; + m_ysPosYNext = 0; + m_dxsTotWidthSoFar = 0; + m_dxsVisWidthSoFar = 0; + } + + void SetFont(Font * pfont) + { + m_pfont = pfont; + } + Font * GetFont() + { + return m_pfont; + } + + bool StartLineContext() + { + return m_fStartLineContext; + } + bool EndLineContext() + { + return m_fEndLineContext; + } + void SetStartLineContext(bool f) + { + m_fStartLineContext = f; + } + void SetEndLineContext(bool f) + { + m_fEndLineContext = f; + } + + void SetInitialLB(bool f = true) + { + m_fInitialLB = f; + } + + void SetFinalLB(bool f = true) + { + m_fFinalLB = f; + } + + void SetInsertedLB(bool f = true) + { + m_fInsertedLB = f; + } + + bool HasInitialLB() + { + return m_fInitialLB; + } + bool HasInsertedLB() + { + return m_fInsertedLB; + } + bool HasFinalLB() + { + return m_fFinalLB; + } + + void SetExceededSpace(bool f = true) + { + m_fExceededSpace = f; + } + + bool ExceededSpace() + { + return m_fExceededSpace; + } + + void SetHitHardBreak(bool f = true) + { + m_fHitHardBreak = f; + } + + bool HitHardBreak() + { + return m_fHitHardBreak; + } + + void SetRemovedTrWhiteSpace(bool f = true) + { + m_fRemovedWhtsp = f; + } + + bool RemovedTrWhiteSpace() + { + return m_fRemovedWhtsp; + } + + bool WhiteSpaceOnly() + { + return (m_twsh == ktwshOnlyWs); + } + + bool ParaRightToLeft() + { + return m_fParaRtl; + } + + //int PrevPassMaxBackup(int ipass) + //{ + // if (ipass == 0) + // return 0; + // return Pass(ipass - 1)->MaxBackup(); + //} + + DirCode InitialStrongDir() + { + return m_dircInitialStrongDir; + } + DirCode InitialTermDir() + { + return m_dircInitialTermDir; + } + + void InitializeStreams(GrTableManager * ptman, GrCharStream * pchstrm, + int cbPrev, byte * pbPrevSegDat, bool fNeedFinalBreak, int * pislotFinalBreak); + int LbSlotToSegLim(int islot, GrCharStream * pchstrm, int cpassLB); + int TraceStreamZeroPos(int islotFinal, int nTopDir); + + float EmToLogUnits(int m); + int LogToEmUnits(float xys); + bool GPointToXY(gid16 chwGlyphID, int nGPoint, float * xs, float * ys); + + void AddJWidthToAdvance(GrSlotStream * psstrm, GrSlotState ** ppslot, int islot, + GrSlotState ** ppslotLast, GrSlotState ** ppslotLastBase); + + // Memory management for slots: + void NewSlot(gid16 gID, GrFeatureValues fval, int ipass, int ichwSegOffset, int nUnicode, + GrSlotState **); + void NewSlot(gid16 gID, GrSlotState * pslotFeat, int ipass, int ichwSegOffset, + GrSlotState **); + void NewSlot(gid16 gID, GrSlotState * pslotFeat, int ipass, GrSlotState **); + void NewSlotCopy(GrSlotState * pslotCopyFrom, int ipass, + GrSlotState ** pslotRet); +protected: + void NextSlot(GrSlotState ** ppslot); + +protected: + GrTableManager * m_ptman; + + int m_cFeat; + int m_cCompPerLig; + int m_cUserDefn; + + int m_jmodi; // justification mode + int m_ipassJustCalled; // pass after which justification routine was called; + // -1 if we are not in the midst of interacting with the + // GrJustifier + + // Pointer to font + Font * m_pfont; + + bool m_fStartLineContext; // true if there was a rule that ran over the initial LB + bool m_fEndLineContext; // true if there was a rule that ran over the final LB + + // the number of slots inserted in the first stream before the official beginning + // of the segment, including any initial line break + int m_cslotPreSeg; + + // List of allocated GrSlotStates; hungarian slotblk = prgslot + std::vector<GrSlotState *> m_vslotblk; + std::vector<u_intslot *> m_vprgnSlotVarLenBufs; // corresponding var-length blocks + int m_islotblkCurr; + int m_islotNext; + + // Directionality codes from previous segment; these are used by the bidi algorithm + // at the initial line-break character. + DirCode m_dircInitialStrongDir; + DirCode m_dircInitialTermDir; + + LineBrk m_lbPrevEnd; + + // All of the following 3 are true only when there is an actual LB glyph in the stream + bool m_fInitialLB; // is there is an initial LB (an actual LB glyph--in all streams) + bool m_fFinalLB; // is there is a final LB (in all streams--true when we've hit + // the end of the input and end-of-line is true) + bool m_fInsertedLB; // is there is a final LB inserted in the output of LB pass + // (true when we've backtracked) + + bool m_fExceededSpace; // true if we backtracked due to exceeding space; false if we + // haven't backtracked, or we did so for some other reason + // (eg, omitting trailing whitespace) + + bool m_fHitHardBreak; // true if we encountered a hard-break in the input stream + // (before the natural end of the segment) + + bool m_fRemovedWhtsp; // true if we've hit trailing whitespace characters that we've + // removed + + TrWsHandling m_twsh; // white-space handling + bool m_fParaRtl; // paragraph direction + + float m_dxsShrinkPossible; + + // The final positioning pass is where we are going to do most of the positioning. + // Cache the intermediate results so we don't have to calculate from the beginning of + // the segment every time. + int m_islotPosNext; // where to continue calculating positions in final output stream + // (one past the last base that was calculated) + float m_xsPosXNext; + float m_ysPosYNext; + float m_dxsTotWidthSoFar; + float m_dxsVisWidthSoFar; + + int m_cpass; // number of passes + + // array of pass-state objects + PassState * m_prgzpst; + + // array of slot streams which are output of passes (input of zeroth + // pass is the character stream, not a slot stream): + GrSlotStream ** m_prgpsstrm; + // For instance, a simple set of passes might look like this: + // CharStream + // GlyphGenPass pass 0 + // SlotStream stream 0 + // SubPass pass 1 + // SlotStream stream 1 + // PosPass pass 2 + // SlotStream stream 2 + // + // m_cpass = 3 + +}; // end of class EngineState + +/*---------------------------------------------------------------------------------------------- + There is a single instance of GrTableManager that handle the interactions between + passes, including the demand-pull mechanism. + + Hungarian: tman +----------------------------------------------------------------------------------------------*/ + +class GrTableManager { + friend class FontMemoryUsage; + +public: + // Constructor & destructor: + GrTableManager(GrEngine * pgreng) + : m_cpass(0), + m_fBidi(false), + m_prgppass(NULL), + m_pgreng(pgreng) + { + Assert(pgreng); + } + + ~GrTableManager(); + + bool CreateAndReadPasses(GrIStream & grstrm, int fxdSilfVersion, int fxdRuleVersion, + int cpassFont, long lSubTableStart, int * rgnPassOffsets, + int ipassSub1Font, int ipassPos1Font, int ipassJust1Font, byte ipassPostBidiFont); + + void CreateEmpty(); + + GrEngine * Engine(); + EngineState * State(); + + void Run(Segment * psegNew, Font * pfont, GrCharStream * pchstrm, + IGrJustifier * pgjus, int jmodi, + LayoutEnvironment & layout, + int ichStop, float dxWidth, float dxUnjustified, + bool fNeedFinalBreak, bool fMoreText, int ichFontLim, + bool fInfiniteWidth, bool fWidthIsCharCount, + int ichwCallerBtLim, + int nDirDepth, SegEnd estJ); + + GrPass* Pass(int ipass) + { + Assert(ipass >= 0); + return m_prgppass[ipass]; + } + + int NumberOfPasses() + { + return m_cpass; + } + + int NumberOfLbPasses() + { + return m_cpassLB; + } + + bool HasBidiPass() + { + return m_fBidi; + } + + int FirstPosPass() + { + return m_ipassPos1; + } + + void StorePassStates(PassState * rgzpst); + + // Return the stream that serves as input to the given pass. + GrSlotStream * InputStream(int ipass) + { + return m_engst.InputStream(ipass); + } + GrSlotStream * OutputStream(int ipass) + { + return m_engst.OutputStream(ipass); + } + + void UnwindAndReinit(int islotNewBreak); + +public: + int InternalJustificationMode() + { + return m_engst.m_jmodi; + } + + bool ShouldLogJustification() + { + return (m_engst.m_jmodi != kjmodiNormal); + } + + int PrevPassMaxBackup(int ipass) + { + if (ipass == 0) + return 0; + return Pass(ipass - 1)->MaxBackup(); + } + + // Forward to the engine itself + gid16 GetGlyphIDFromUnicode(int nUnicode); + gid16 ActualGlyphForOutput(utf16 chwGlyphID); + GrGlyphTable * GlyphTable(); + + gid16 LBGlyphID(); + + gid16 GetClassGlyphIDAt(int nClass, int nIndex); + int GetIndexInGlyphClass(int nClass, gid16 chwGlyphID); + size_t NumberOfGlyphsInClass(int nClass); + + void SetSlotAttrsFromGlyphAttrs(GrSlotState * pslot); + + int NumFeat(); + int DefaultForFeatureAt(int ifeat); + GrFeature * FeatureWithID(featid nID, int * pifeat); + GrFeature * Feature(int ifeat); + void DefaultsForLanguage(isocode lgcode, + std::vector<featid> & vnFeats, std::vector<int> & vnValues); + + bool RightToLeft(); + int TopDirectionLevel(); + + float VerticalOffset(); + + int NumUserDefn(); + int NumCompPerLig(); + + int ComponentIndexForGlyph(gid16 chwGlyphID, int nCompID); + int GlyphAttrValue(gid16 chwGlyphID, int nAttrID); + + // --- + + bool LoggingTransduction(); + + float EmToLogUnits(int m); + int LogToEmUnits(float xys); + bool GPointToXY(gid16 chwGlyphID, int nGPoint, float * xs, float * ys); + + void CalcPositionsUpTo(int ipass, GrSlotState * pslotLast, + float * pxsWidth, float * pxsVisibleWidth); + + void InitPosCache() + { + m_engst.InitPosCache(); + } + + bool IsWhiteSpace(GrSlotState * pslot); + + // For transduction logging: + //bool WriteTransductionLog(GrCharStream * pchstrm, Segment * psegRet, + // int cbPrev, byte * pbPrevSegDat, byte * pbNextSegDat, int * pcbNextSegDat); + //bool WriteAssociationLog(GrCharStream * pchstrm, Segment * psegRet); + bool WriteTransductionLog(std::ostream * pstrmLog, + GrCharStream * pchstrm, Segment * psegRet, int cbPrevSetDat, byte * pbPrevSegDat); + bool WriteAssociationLog(std::ostream * pstrmLog, + GrCharStream * pchstrm, Segment * psegRet); +#ifdef TRACING + void WriteXductnLog(std::ostream & strmOut, GrCharStream * pchstrm, Segment * psegRet, + int cbPrevSegDat, byte * pbPrevSegDat); + //bool LogFileName(std::string & staFile); + void LogUnderlying(std::ostream & strmOut, GrCharStream * pchstrm, int cchwBackup); + void LogPass1Input(std::ostream & strmOut); + void LogPassOutput(std::ostream & strmOut, int ipass, int cslotSkipped); + void LogAttributes(std::ostream & strmOut, int ipass, bool fJustWidths = false); + void LogFinalPositions(std::ostream & strmOut); + void LogUnderlyingHeader(std::ostream & strmOut, int ichwMin, + int ichwLim, int cchwBackup, int * prgichw16bit); + void LogSlotHeader(std::ostream & strmOut, int cslot, + int cspPerSlot, int cspLeading, int islotMin = 0); + void LogSlotGlyphs(std::ostream & strmOut, GrSlotStream * psstrm); + void SlotAttrsModified(int ipass, bool * rgfMods, bool fPreJust, int * pccomp, int *pcassoc); + void LogInTable(std::ostream & strmOut, int n); + void LogInTable(std::ostream & strmOut, float n); + void LogHexInTable(std::ostream & strmOut, gid16 chw, bool fPlus = false); + void LogDirCodeInTable(std::ostream & strmOut, int dirc); + void LogBreakWeightInTable(std::ostream & strmOut, int lb); +#endif // TRACING + +protected: + void InitNewSegment(Segment * psegNew, Font * pfont, GrCharStream * pchstrm, IGrJustifier * pgjus, + int islotStream0Break, int islotSurfaceBreak, bool fStartLine, bool fEndLine, int ichFontLim, + LineBrk lbEnd, SegEnd est, int * dichSegLen); + + void InitSegmentAsEmpty(Segment * psegNew, Font * pfont, GrCharStream * pchstrm, + bool fStartLine, bool fEndLine); + + void InitSegmentToDelete(Segment * psegNew, Font * pfont, GrCharStream * pchstrm); + + int ChunkInPrev(int ipass, int islot, GrCharStream * pchstrm); + + bool Backtrack(int * islotPrevLB, + LineBrk * plbPref, LineBrk lbMax, TrWsHandling, bool fMoreText, + int ichwCallerBtLim, bool fEndLine, LineBrk * plbFound); + + LineBrk IncLineBreak(LineBrk lb); + + void InitializeForNextSeg(Segment* pseg, + int islotStream0Break, int islotSurfaceBreak, LineBrk lbEnd, + bool fNextSegNeedsContext, GrCharStream * pchstrm); + + void RecordAssocsAndOutput(Font * pfont, + Segment * pseg, bool fWidthIsCharCount, + TrWsHandling twsh, bool fParaRtl, int nDirDepth); + + void CalculateAssociations(Segment * pseg, int csloutSurface); + void AdjustAssocsForOverlaps(Segment * pseg); + + void UnstretchTrailingWs(GrSlotStream * psstrm, int iGlyphLim); + void CallJustifier(IGrJustifier * pgjus, int ipassCurr, float dxUnjustified, float dxJustified, + bool fEndLine); + + void DetermineShrink(IGrJustifier * pgjus, int ipass); + +protected: + // Instance variables: + + int m_cpass; // number of passes; zero-th pass is Unicode character -> glyph ID mapping; + // Note thatwe always have one positioning pass to measure things even + // if there are no tables to run. + + int m_cpassLB; // number of line-break passes + int m_ipassPos1; // first positioning pass + int m_ipassJust1; // first justification pass (== m_ipassPos1 if none) + + bool m_fBidi; // is there a bidi pass? + + // array of passes: + GrPass ** m_prgppass; + + // Pointer back to Graphite engine, to give access to global constants and tables. + GrEngine * m_pgreng; + + // State of processing. + EngineState m_engst; + + bool m_fLogging; + +public: + // For test procedures: + void SetUpTest(std::wstring); + +protected: + void TempDebug(); + int SurfaceLineBreakSlot(int ichw, GrCharStream * pchstrm, bool fInitial); + std::wstring ChunkDebugString(int ipass); +}; // end of class GrTableManager + +} // namespace gr + + +#endif // !GR_TABLEMAN_INCLUDED diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/Main.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/Main.h new file mode 100644 index 00000000000..e3f971bfdbb --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/Main.h @@ -0,0 +1,182 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: Main.h +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Main.header file for the Graphite engine. +----------------------------------------------------------------------------------------------*/ +#ifdef _MSC_VER +#pragma once +#endif +#ifndef GRAPHITE_H +#define GRAPHITE_H 1 + +//:End Ignore + +#define NO_EXCEPTIONS 1 + +// It's okay to use functions that were declared deprecated by VS 2005: +#ifdef _MSC_VER +#define _CRT_SECURE_NO_DEPRECATE +#pragma warning(disable: 4996) // warning: function was declared deprecated +#pragma warning(disable: 4702) // unreachable code +#endif + +#include "GrCommon.h" + +//:>******************************************************************************************** +//:> Interfaces. +//:>******************************************************************************************** +#include "GrData.h" + +//:>******************************************************************************************** +//:> Implementations. +//:>******************************************************************************************** + +#ifndef _WIN32 +#include "GrMstypes.h" +#ifndef HAVE_FABSF +float fabsf(float x); +#endif +#endif +#include "GrDebug.h" + +// For reading the font (FieldWorks's approach) and transduction logging: +#include <fstream> +#include <iostream> + +#include <vector> +////#include <algorithm> +#include <string> +#include <cstring> + +// gAssert should be used for any kind of assertions that can be caused by a corrupted font, +// particularly those that won't be caught when loading the tables. +#define gAssert(x) Assert(x) +// When testing the error mechanism (because the process of bringing up the assertion dialog +// interferes with FW repainting the invalid window: +////#define gAssert(x) if (!(x)) Warn("corrupted font?") + +// Internal headers. +#include "FileInput.h" + +// Public headers. +#include "GrResult.h" +//////#include "IGrGraphics.h" +#include "ITextSource.h" +#include "IGrJustifier.h" +#include "IGrEngine.h" +#include "GrConstants.h" +#include "GrFeature.h" + +// External helper classes. +#include "GrExt.h" + + +// Forward declarations. +namespace gr +{ + class GrTableManager; + class Segment; + class GrEngine; +} + +// Define after GrExt.h to avoid conflict with FW Rect class. +namespace gr +{ +/* +struct Point +{ + float x; + float y; + + Point() + { + x = y = 0; + } + + Point(POINT & p) + { + x = (float)p.x; + y = (float)p.y; + } +}; + + +struct Rect +{ + float top; + float bottom; + float left; + float right; + + Rect() + { + top = bottom = left = right = 0; + }; + + Rect(RECT & r) + { + top = (float)r.top; + bottom = (float)r.bottom; + left = (float)r.left; + right = (float)r.right; + }; +}; +*/ +}; // namespace gr + +#include "GrFeatureValues.h" +#include "GrSlotState.h" + +#include "SegmentAux.h" + +// Internal headers +#include "GrCharStream.h" +#include "GrGlyphTable.h" +#include "GrClassTable.h" +#include "GrPseudoMap.h" +#include "GrSlotStream.h" +#include "GrFSM.h" +#include "GrPass.h" +#include "GrTableManager.h" +#include "FontCache.h" + + +// Public headers +#include "Font.h" +#include "GraphiteProcess.h" +#include "GrEngine.h" +#include "FontFace.h" +#include "Segment.h" +#include "SegmentPainter.h" + +//#ifdef _WIN32 +//#include <hash_map> +//#include "WinFont.h" +//#include "WinSegmentPainter.h" +//#endif + +// Internal headers +#include "TtfUtil.h" +//#include "GrUtil.h" + +#include "GrWrappers.h" + +//#include "IGrDebug.h" +//#include "GrSegmentDebug.h" +//#include "GrEngineDebug.h" + +/////<<<<<<< .mine +// clashes with non gr stuff +// using namespace gr; +///////>>>>>>> .r98 + +#endif // GRAPHITE_H + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/Makefile.am b/Build/source/libs/graphite/engine-2.3.1/src/segment/Makefile.am new file mode 100644 index 00000000000..59113aa924b --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/Makefile.am @@ -0,0 +1,26 @@ +segmentdir = $(top_srcdir)/src/segment + +libgraphite_la_SOURCES += $(segmentdir)/FileInput.cpp +libgraphite_la_SOURCES += $(segmentdir)/FontCache.cpp +libgraphite_la_SOURCES += $(segmentdir)/FontFace.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrCharStream.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrClassTable.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrEngine.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrFeature.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrFSM.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrGlyphTable.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrPassActionCode.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrPass.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrSlotState.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrSlotStream.cpp +libgraphite_la_SOURCES += $(segmentdir)/GrTableManager.cpp +libgraphite_la_SOURCES += $(segmentdir)/MemoryUsage.cpp +libgraphite_la_SOURCES += $(segmentdir)/Platform.cpp +libgraphite_la_SOURCES += $(segmentdir)/SegmentAux.cpp +libgraphite_la_SOURCES += $(segmentdir)/Segment.cpp +libgraphite_la_SOURCES += $(segmentdir)/TestFSM.cpp +libgraphite_la_SOURCES += $(segmentdir)/TestPasses.cpp +libgraphite_la_SOURCES += $(segmentdir)/TransductionLog.cpp + +EXTRA_DIST += $(segmentdir)/*.h + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/MemoryUsage.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/MemoryUsage.cpp new file mode 100644 index 00000000000..1fb3fc25282 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/MemoryUsage.cpp @@ -0,0 +1,781 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: MemoryUsage.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Calculates memory usage for the engine and segments. +----------------------------------------------------------------------------------------------*/ + +#pragma warning(disable: 4244) // conversion from wchar_t to char +#pragma warning(disable: 4702) // unreachable code + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** + +#include "GrCommon.h" +#include "GrData.h" +#ifndef _WIN32 +#include "GrMstypes.h" +#endif +#include "GrDebug.h" +//#include <fstream> +#include <iostream> +#include <cstring> +//#include <string> +// Forward declarations. + +#include "Main.h" + +//namespace gr +//{ +// class GrTableManager; +// class Segment; +// class GrEngine; +//} +// +//#include "GrTableManager.h" +//#include "GrFeatureValues.h" +//#include "GrSlotState.h" +//#include "SegmentAux.h" +//#include "Segment.h" +#include "MemoryUsage.h" + +//#ifndef _MSC_VER +//#include "config.h" +//#endif + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +const int stringOverhead = (3 * sizeof(int)) + (sizeof(void *)) + (8 * sizeof(short)); +const int vectorOverhead = 3 * sizeof(void *); + +namespace gr +{ + +//:>******************************************************************************************** +//:> Font/engine +//:>******************************************************************************************** + +void FontMemoryUsage::initialize() +{ + font = 0; + fontCache = 0; + fontFace = 0; + + eng_count = 0; + eng_overhead = 0; + eng_scalars = 0; + eng_strings = 0; + eng_pointers = 0; + eng_cmap = 0; + eng_nameTable = 0; + + pseudoMap = 0; + + clstbl_counters = 0; + clstbl_offsets = 0; + clstbl_glyphList = 0; + + glftbl_general = 0; + glftbl_compDefns = 0; + glftbl_attrTable = 0; + glftbl_attrOffsets = 0; + + lngtbl_general = 0; + lngtbl_entries = 0; + lngtbl_featureSets = 0; + + tman_general = 0; + + pass_count = 0; + pass_general = 0; + pass_fsm = 0; + pass_ruleExtras = 0; + pass_constraintOffsets = 0; + pass_constraintCode = 0; + pass_actionOffsets = 0; + pass_actionCode = 0; + + engst_general = 0; + engst_passState = 0; + + sstrm_count = 0; + sstrm_general = 0; + sstrm_chunkMapsUsed = 0; + sstrm_chunkMapsAlloc = 0; + sstrm_reprocBuf = 0; + + slot_count = 0; + slot_general = 0; + slot_abstract = 0; + slot_varLenBuf = 0; + slot_assocsUsed = 0; + slot_assocsAlloc = 0; + slot_attachUsed = 0; + slot_attachAlloc = 0; +} + +/*---------------------------------------------------------------------------------------------- + Return the total byte count. +----------------------------------------------------------------------------------------------*/ +int FontMemoryUsage::total() +{ + int grandTotal = 0; + grandTotal += font + fontCache + fontFace; + grandTotal += eng_overhead + eng_scalars + eng_strings + eng_pointers + eng_cmap + eng_nameTable; + grandTotal += pseudoMap; + grandTotal += clstbl_counters + clstbl_offsets + clstbl_glyphList; + grandTotal += glftbl_general + glftbl_compDefns + glftbl_attrTable + glftbl_attrOffsets; + grandTotal += lngtbl_general + lngtbl_entries + lngtbl_featureSets; + grandTotal += tman_general; + grandTotal += pass_general + pass_fsm + pass_ruleExtras + + pass_constraintOffsets + pass_constraintCode + pass_actionOffsets + pass_actionCode; + grandTotal += engst_general + engst_passState; + grandTotal += sstrm_general + sstrm_chunkMapsAlloc + sstrm_reprocBuf; + grandTotal += slot_general + slot_abstract + slot_varLenBuf + slot_assocsAlloc + slot_attachAlloc; + + return grandTotal; +} + +/*---------------------------------------------------------------------------------------------- + Add the results of two runs together. +----------------------------------------------------------------------------------------------*/ +void FontMemoryUsage::add(FontMemoryUsage & fmu) +{ + font += fmu.font; + fontCache += fmu.fontCache; + fontFace += fmu.fontFace; + + eng_count += fmu.eng_count; + eng_overhead += fmu.eng_overhead; + eng_scalars += fmu.eng_scalars; + eng_strings += fmu.eng_strings; + eng_pointers += fmu.eng_pointers; + eng_cmap += fmu.eng_cmap; + eng_nameTable += fmu.eng_nameTable; + + pseudoMap += fmu.pseudoMap; + + clstbl_counters += fmu.clstbl_counters; + clstbl_offsets += fmu.clstbl_offsets; + clstbl_glyphList += fmu.clstbl_glyphList; + + glftbl_general += fmu.glftbl_general; + glftbl_compDefns += fmu.glftbl_compDefns; + glftbl_attrTable += fmu.glftbl_attrTable; + glftbl_attrOffsets += fmu.glftbl_attrOffsets; + + lngtbl_general += fmu.lngtbl_general; + lngtbl_entries += fmu.lngtbl_entries; + lngtbl_featureSets += fmu.lngtbl_featureSets; + + tman_general += fmu.tman_general; + + pass_count += fmu.pass_count; + pass_general += fmu.pass_general; + pass_fsm += fmu.pass_fsm; + pass_ruleExtras += fmu.pass_ruleExtras; + pass_constraintOffsets += fmu.pass_constraintOffsets; + pass_constraintCode += fmu.pass_constraintCode; + pass_actionOffsets += fmu.pass_actionOffsets; + pass_actionCode += fmu.pass_actionCode; + + engst_general += fmu.engst_general; + engst_passState += fmu.engst_passState; + + sstrm_count += fmu.sstrm_count; + sstrm_general += fmu.sstrm_general; + sstrm_chunkMapsUsed += fmu.sstrm_chunkMapsUsed; + sstrm_chunkMapsAlloc += fmu.sstrm_chunkMapsAlloc; + sstrm_reprocBuf += fmu.sstrm_reprocBuf; + + slot_count += fmu.slot_count; + slot_general += fmu.slot_general; + slot_abstract += fmu.slot_abstract; + slot_varLenBuf += fmu.slot_varLenBuf; + slot_assocsUsed += fmu.slot_assocsUsed; + slot_assocsAlloc += fmu.slot_assocsAlloc; + slot_attachUsed += fmu.slot_attachUsed; + slot_attachAlloc += fmu.slot_attachAlloc; +} + +/*---------------------------------------------------------------------------------------------- + Add up the memory used by all the engines in the cache. +----------------------------------------------------------------------------------------------*/ +FontMemoryUsage Font::calculateMemoryUsage() +{ + FontMemoryUsage fmu; + FontFace::calculateAllMemoryUsage(fmu); + return fmu; +} + +void FontFace::calculateAllMemoryUsage(FontMemoryUsage & fmu) +{ + s_pFontCache->calculateMemoryUsage(fmu); +} + +void FontCache::calculateMemoryUsage(FontMemoryUsage & fmuTotal) +{ + fmuTotal.fontCache += sizeof(int) * 4; // m_cfci, m_cfciMax, m_cfface, m_flush + fmuTotal.fontCache += sizeof(void*); // m_prgfci; + + // Iterate through all the font faces in the cache. + for (int ifci = 0; ifci < m_cfci; ifci++) + { + CacheItem * pfci = m_prgfci + ifci; + char rgchFontName[32]; + memset(rgchFontName, 0, 32 * sizeof(char)); + std::copy(pfci->szFaceName, pfci->szFaceName + 32, rgchFontName); + std::string strFontName(rgchFontName); + fmuTotal.vstrFontNames.push_back(strFontName); + int cface = 0; + if (pfci->pffaceRegular) + { + FontMemoryUsage fmu; + pfci->pffaceRegular->calculateMemoryUsage(fmu); + fmuTotal.add(fmu); + fmuTotal.vFontTotalsReg.push_back(fmu.total()); + cface++; + } + else + fmuTotal.vFontTotalsReg.push_back(0); + + if (pfci->pffaceBold) + { + FontMemoryUsage fmu; + pfci->pffaceBold->calculateMemoryUsage(fmu); + fmuTotal.add(fmu); + fmuTotal.vFontTotalsBold.push_back(fmu.total()); + cface++; + } + else + fmuTotal.vFontTotalsBold.push_back(0); + + if (pfci->pffaceItalic) + { + FontMemoryUsage fmu; + pfci->pffaceItalic->calculateMemoryUsage(fmu); + fmuTotal.add(fmu); + fmuTotal.vFontTotalsItalic.push_back(fmu.total()); + cface++; + } + else + fmuTotal.vFontTotalsItalic.push_back(0); + + if (pfci->pffaceBI) + { + FontMemoryUsage fmu; + pfci->pffaceBI->calculateMemoryUsage(fmu); + fmuTotal.add(fmu); + fmuTotal.vFontTotalsBI.push_back(fmu.total()); + cface++; + } + else + fmuTotal.vFontTotalsBI.push_back(0); + + fmuTotal.vFaceCount.push_back(cface); + } +} + +void FontFace::calculateMemoryUsage(FontMemoryUsage & fmu) +{ + fmu.fontFace += sizeof(int); // m_cfonts + fmu.fontFace += sizeof(void *) * 2; // s_pFontCache, m_pgreng + + fmu.font += m_cfonts * sizeof(void*); // m_pfface + + fmu.addEngine(m_pgreng); +} + +void FontMemoryUsage::addEngine(GrEngine * pgreng) +{ + eng_count++; + + eng_overhead += sizeof(long); // m_cref + eng_scalars += sizeof(bool) * 6; // m_fBold, m_fItalic, m_fSmart... + eng_scalars += sizeof(bool) * 2; // m_fFakeItalicCache, m_fFakeBICache; + eng_strings += stringOverhead * 4; // m_strCtrlFileReg/Bold/Italic/BI + eng_strings += sizeof(wchar_t) * pgreng->m_strCtrlFileReg.length(); + eng_strings += sizeof(wchar_t) * pgreng->m_strCtrlFileBold.length(); + eng_strings += sizeof(wchar_t) * pgreng->m_strCtrlFileItalic.length(); + eng_strings += sizeof(wchar_t) * pgreng->m_strCtrlFileBI.length(); + eng_scalars += sizeof(bool); + eng_strings += stringOverhead * 6; // m_stuCtrlFile etc. + eng_strings += sizeof(wchar_t) * pgreng->m_stuCtrlFile.length(); + eng_strings += sizeof(wchar_t) * pgreng->m_stuInitError.length(); + eng_strings += sizeof(wchar_t) * pgreng->m_stuFaceName.length(); + eng_strings += sizeof(wchar_t) * pgreng->m_stuFeatures.length(); + eng_strings += sizeof(wchar_t) * pgreng->m_stuBaseFaceName.length(); + eng_scalars += sizeof(bool); // m_fUseSepBase + eng_scalars += sizeof(GrResult) * 2; // m_resFontRead, m_resFontValid + eng_scalars += sizeof(FontErrorCode); + eng_scalars += sizeof(int) * 3; // m_fxdBadVersion, m_nFontCheckSum, m_nScriptTag, m_grfsdc + eng_scalars += sizeof(bool); // m_fRightToLeft + eng_scalars += sizeof(int) * 2; // m_mXAscent, m_mXDescent + eng_scalars += sizeof(float); // m_dysOffset + eng_scalars += sizeof(bool); // m_fBasicJust + eng_scalars += sizeof(int); // m_cJLevels + eng_pointers += sizeof(void*) * 4; // m_ptman, m_pctbl, m_pgtbl, m_pfface + eng_scalars += sizeof(GrFeature) * kMaxFeatures; + eng_scalars += sizeof(int); // m_cfeat + eng_scalars += sizeof(size_t); // m_clcidFeatLabelLangs + eng_scalars += sizeof(short) * pgreng->m_clcidFeatLabelLangs; + // m_langtbl + eng_scalars += sizeof(bool); // m_fLineBreak + eng_scalars += sizeof(int) * 2; // m_cchwPreXlbContext, m_cchwPostXlbContext + eng_scalars += sizeof(data16) * 7; // m_chwPseudoAttr, etc. + eng_scalars += sizeof(gid16); // m_chwLBGlyphID + eng_scalars += sizeof(int) * 4; // m_cComponents, m_cnUserDefn, m_cnCompPerLig, m_mFontEmUnits + + pseudoMap += sizeof(int) * 3; + pseudoMap += sizeof(GrPseudoMap) * pgreng->m_cpsd; + + eng_cmap += sizeof(void*) * 2; + eng_cmap += sizeof(byte*); + eng_cmap += sizeof(bool); + if (pgreng->m_fCmapTblCopy) + eng_cmap += pgreng->m_cbCmapTbl; + + eng_nameTable += sizeof(byte *); + eng_nameTable += sizeof(bool); + if (pgreng->m_fNameTblCopy) + eng_nameTable += pgreng->m_cbNameTbl; + + eng_scalars += sizeof(bool) * 2; // m_fLogXductn, m_fInErrorState + + clstbl_counters += sizeof(int) * 2; // m_ccls, m_cclsLinear + clstbl_offsets += sizeof(data16) * (pgreng->m_pctbl->m_ccls + 1); + clstbl_glyphList += sizeof(data16) * (pgreng->m_pctbl->m_prgichwOffsets[pgreng->m_pctbl->m_ccls]); + + glftbl_general += sizeof(int) * 3; // m_cglf, m_cComponents, m_cgstbl + glftbl_general += vectorOverhead; // m_vpgstbl + GrGlyphSubTable * pgstbl = pgreng->m_pgtbl->m_vpgstbl[0]; + glftbl_general += sizeof(int) * 4; // m_fxdSilfVersion, m_nAttrIDLim, m_cComponents, m_cnCompPerLig + glftbl_general += sizeof(bool) * 2; // m_fHasDebugStrings, m_fGlocShort + glftbl_general += sizeof(data16) * 3; // m_chw...Attr + glftbl_compDefns += sizeof(int) * (pgstbl->m_cnCompPerLig + 1) * pgreng->m_pgtbl->m_cglf; + glftbl_attrTable += sizeof(int) * 2; // m_fxdSilfVersion, m_cbEntryBufLen + glftbl_attrTable += sizeof(byte) * pgstbl->m_pgatbl->m_cbEntryBufLen; + glftbl_attrOffsets += + (pgreng->m_pgtbl->m_cglf + 1) * ((pgstbl->m_fGlocShort) ? sizeof(data16) : sizeof(data32)); + + lngtbl_general += sizeof(size_t); // m_clang + lngtbl_general += sizeof(data16) * 3; // m_dilangInit, m_cLoop, m_ilangStart + lngtbl_general += sizeof(void*) * 2; // m_prglang, m_prgfset + lngtbl_general += sizeof(int); // m_cbOffset0 + lngtbl_entries += sizeof(GrLangTable::LangEntry) * pgreng->m_langtbl.m_clang; + lngtbl_featureSets += sizeof(GrLangTable::FeatSet) * pgreng->m_langtbl.m_cfset; + + GrTableManager * ptman = pgreng->m_ptman; + tman_general += sizeof(int) * 4; // m_cpass, m_cpassLB, m_ipassPos1, m_ipassJust1 + tman_general += sizeof(bool); // m_fBidi + tman_general += sizeof(void*) * 2; // m_prgppass, m_pgreng + tman_general += sizeof(void*) * ptman->m_cpass; // m_prgppass + tman_general += sizeof(bool); // m_fLogging + + pass_count += ptman->m_cpass; + for (int ipass = 0; ipass < ptman->m_cpass; ipass++) + { + GrPass * ppass = ptman->m_prgppass[ipass]; + pass_general = sizeof(GrPass); + + GrFSM * pfsm = ppass->m_pfsm; + if (pfsm) + { + pass_fsm += sizeof(int) * 5; // m_crow, m_crowFinal, m_rowFinalMin, m_crowNonAcpt, m_ccol + pass_fsm += sizeof(void*) * 3; // m_prgirulnMin, m_prgrulnMatched, m_prgrowTransitions + int crowSuccess = pfsm->m_crow - pfsm->m_crowNonAcpt; + pass_fsm += sizeof(data16) * (crowSuccess + 1); // m_prgirulnMin + pass_fsm += sizeof(data16) * (pfsm->m_crulnMatched); // m_prgrulnMatched + pass_fsm += sizeof(short) * ((pfsm->m_crow - pfsm->m_crowFinal) * pfsm->m_ccol); + pass_fsm += sizeof(data16); // m_dimcrInit, m_cLoop, m_imcrStart + pass_fsm += sizeof(int); // m_cmcr + pass_fsm += sizeof(void*); // m_prgmcr + pass_fsm += sizeof(GrFSMClassRange) * pfsm->m_cmcr; + pass_fsm += sizeof(int) * 2; // m_critMinRulePreContext, m_critMaxRulePreContext + pass_fsm += sizeof(void*); // m_prgrowStartStates + pass_fsm += sizeof(short) * (pfsm->m_critMaxRulePreContext - pfsm->m_critMinRulePreContext + 1); + } + + pass_ruleExtras = sizeof(data16) * ppass->m_crul; // m_prgchwRuleSortKeys + pass_ruleExtras = sizeof(byte*) * ppass->m_crul; // m_prgcritRulePreModContext + + pass_constraintOffsets += sizeof(data16) * (ppass->m_crul + 1); + pass_actionOffsets =+ sizeof(data16) * (ppass->m_crul + 1); + + pass_constraintCode += sizeof(byte) * ppass->m_cbConstraints; + pass_actionCode += sizeof(byte) * ppass->m_cbActions; + + pass_general += sizeof(bool) * ppass->m_crul; // m_prgfRuleOkay + pass_general += vectorOverhead; // m_vnStack + pass_general += sizeof(int) * ppass->m_vnStack.capacity(); + } + + engst_general += sizeof(EngineState); + engst_general += sizeof(void *) * ptman->m_engst.m_vslotblk.capacity(); + engst_general += sizeof(void *) * ptman->m_engst.m_vprgnSlotVarLenBufs.capacity(); + engst_general += sizeof(void *) * ptman->m_engst.m_cpass; // m_prgpsstrm + + engst_passState += sizeof(PassState) * ptman->m_engst.m_cpass; + + for (int isstrm = 0; isstrm < ptman->m_engst.m_cpass; isstrm++) + { + sstrm_count++; + GrSlotStream * psstrm = ptman->m_engst.m_prgpsstrm[isstrm]; + sstrm_general += sizeof(GrSlotStream); + sstrm_chunkMapsUsed += psstrm->m_vislotPrevChunkMap.size(); + sstrm_chunkMapsUsed += psstrm->m_vislotPrevChunkMap.size(); + sstrm_chunkMapsAlloc += psstrm->m_vislotPrevChunkMap.capacity(); + sstrm_chunkMapsAlloc += psstrm->m_vislotNextChunkMap.capacity(); + sstrm_reprocBuf += psstrm->m_vpslotReproc.capacity(); + sstrm_reprocBuf += sizeof(int) * 2; + sstrm_general -= sizeof(int) * 2; + } + + slot_general += ptman->m_engst.m_vslotblk.size() * EngineState::kSlotBlockSize + * (sizeof(GrSlotState) - sizeof(GrSlotAbstract)); + slot_abstract += ptman->m_engst.m_vslotblk.size() * EngineState::kSlotBlockSize + * sizeof(GrSlotAbstract); + int cnExtraPerSlot = ptman->m_engst.m_cUserDefn + (ptman->m_engst.m_cCompPerLig * 2) + + ptman->m_engst.m_cFeat; + slot_varLenBuf += EngineState::kSlotBlockSize * cnExtraPerSlot * sizeof(u_intslot); + for (size_t islotblk = 0; islotblk < ptman->m_engst.m_vslotblk.size(); islotblk++) + { + for (int islot = 0; islot < EngineState::kSlotBlockSize; islot++) + { + slot_count++; + GrSlotState * pslot = ptman->m_engst.m_vslotblk[islotblk] + islot; + slot_assocsUsed += pslot->m_vpslotAssoc.size(); + slot_assocsAlloc += pslot->m_vpslotAssoc.capacity(); + slot_attachUsed += pslot->m_vdislotAttLeaves.size(); + slot_attachAlloc += pslot->m_vdislotAttLeaves.capacity(); + } + } + +} + +/*---------------------------------------------------------------------------------------------- + Write out the memory usage onto a stream. +----------------------------------------------------------------------------------------------*/ +void FontMemoryUsage::prettyPrint(std::ostream & strm) +{ + int totalBytes = total(); + + strm << "Number of engines: " << eng_count << "\n"; + strm << "Number of passes: " << pass_count << "\n\n"; + + strm << "Number of slot streams: " << sstrm_count << "\n"; + strm << "Number of slots: " << slot_count << "\n\n"; + + int classTableTotal = clstbl_counters + clstbl_offsets + clstbl_glyphList; + int glyphTableTotal = glftbl_general + glftbl_compDefns + glftbl_attrTable + glftbl_attrOffsets; + int langTableTotal = lngtbl_general + lngtbl_entries + lngtbl_featureSets; + int passTotal = pass_general + pass_fsm + pass_ruleExtras + pass_constraintOffsets + pass_constraintCode + + pass_actionOffsets + pass_actionCode; + int engineTotal = eng_overhead + eng_scalars + eng_strings + eng_pointers + eng_cmap + eng_nameTable + + pseudoMap + classTableTotal + glyphTableTotal + langTableTotal + tman_general + + passTotal; + + strm << "BYTE COUNT TOTALS\n"; + strm << "Font: " << font << "\n"; + strm << "Font cache: " << fontCache << "\n"; + strm << "Font face: " << fontFace << "\n"; + strm << "Engine " << engineTotal << "\n"; + strm << " Overhead: " << eng_overhead << "\n"; + strm << " Scalars: " << eng_scalars << "\n"; + strm << " Strings: " << eng_strings << "\n"; + strm << " Pointers: " << eng_pointers << "\n"; + strm << " Cmap table: " << eng_cmap << "\n"; + strm << " Name table: " << eng_nameTable << "\n"; + strm << " Pseudo map: " << pseudoMap << "\n"; + strm << " Class table: " << classTableTotal << "\n"; + strm << " Counters: " << clstbl_counters << "\n"; + strm << " Offsets: " << clstbl_offsets << "\n"; + strm << " Glyph list: " << clstbl_glyphList << "\n"; + strm << " Glyph table: " << glyphTableTotal << "\n"; + strm << " General: " << glftbl_general << "\n"; + strm << " Component defns: " << glftbl_compDefns << "\n"; + strm << " Attr table: " << glftbl_attrTable << "\n"; + strm << " Attr offsets: " << glftbl_attrOffsets << "\n"; + strm << " Language table: " << langTableTotal << "\n"; + strm << " General: " << lngtbl_general << "\n"; + strm << " Entries: " << lngtbl_entries << "\n"; + strm << " Feature sets: " << lngtbl_featureSets << "\n"; + strm << " Table manager: " << tman_general << "\n"; + strm << " Passes: " << passTotal << "\n"; + strm << " General: " << pass_general << "\n"; + strm << " FSM: " << pass_fsm << "\n"; + strm << " Rule extras: " << pass_ruleExtras << "\n"; + strm << " Constraint offsets: " << pass_constraintOffsets << "\n\n"; + strm << " Constraint code: " << pass_constraintCode << "\n"; + strm << " Action offsets: " << pass_actionOffsets << "\n"; + strm << " Action code: " << pass_actionCode << "\n\n"; + + int slotTotal = slot_general + slot_abstract + slot_varLenBuf + slot_assocsAlloc + slot_attachAlloc; + int streamTotal = sstrm_general + sstrm_chunkMapsAlloc + sstrm_reprocBuf + slotTotal; + int engineStateTotal = engst_general + engst_passState + streamTotal; + + strm << " Engine State: " << engineStateTotal << "\n"; + strm << " General: " << engst_general << "\n"; + strm << " Pass states: " << engst_passState << "\n"; + strm << " Slot streams: " << streamTotal << "\n"; + strm << " General: " << sstrm_general << "\n"; + strm << " Chunk maps: " << sstrm_chunkMapsAlloc << " (" << sstrm_chunkMapsUsed << " used)\n"; + strm << " Reprocess buffer: " << sstrm_reprocBuf << "\n"; + strm << " Slots: " << slotTotal << "\n"; + strm << " General: " << slot_general << "\n"; + strm << " Abstract: " << slot_abstract << "\n"; + strm << " Var-length buf: " << slot_varLenBuf << "\n"; + strm << " Associations: " << slot_assocsAlloc << " (" << slot_assocsUsed << " used)\n"; + strm << " Attachments: " << slot_attachAlloc << " (" << slot_attachUsed << " used)\n\n"; + + strm << "Total bytes used: " << totalBytes << "\n\n"; + + strm << "TOTALS PER FONT\n"; + for (size_t ifont = 0; ifont < vstrFontNames.size(); ifont++) + { + strm << vstrFontNames[ifont].c_str() << "\n"; + strm << " Regular: " << vFontTotalsReg[ifont] << "\n"; + strm << " Bold: " << vFontTotalsBold[ifont] << "\n"; + strm << " Italic: " << vFontTotalsItalic[ifont] << "\n"; + strm << " Bold-italic: " << vFontTotalsBI[ifont] << "\n\n"; + } +} + + +//:>******************************************************************************************** +//:> Segment +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Initialize the data structure. +----------------------------------------------------------------------------------------------*/ +void SegmentMemoryUsage::initialize() +{ + seg_count = 0; + overhead = 0; + pointers = 0; + scalars = 0; + strings = 0; + metrics = 0; + associations = 0; + init = 0; + obsolete = 0; + + slot_count = 0; + slot_abstract = 0; + slot_varLenBuf = 0; + slot_scalars = 0; + slot_clusterMembers = 0; + + glyphInfo_count = 0; + glyphInfo = 0; + + wastedVector = 0; +} + +/*---------------------------------------------------------------------------------------------- + Add the memory for the segemtn to the SegmentMemoryUsage data structure. +----------------------------------------------------------------------------------------------*/ +void Segment::calculateMemoryUsage(SegmentMemoryUsage & smu) +{ + smu.addSegment(*this); +} + + +void SegmentMemoryUsage::addSegment(Segment & seg) +{ + seg_count++; + + overhead += sizeof(long); // m_cref + pointers += sizeof(void*); // m_pgts + scalars += sizeof(int); // m_dichwLim + scalars += sizeof(int); // m_ichwMin + pointers += sizeof(void*); // m_pfont + pointers += sizeof(void*); // m_preneng + scalars += sizeof(bool); // m_fErroneous + pointers += sizeof(void*); // m_pgjus + scalars += sizeof(LayoutEnvironment); + scalars += sizeof(bool); // m_fWsRtl + scalars += sizeof(bool); // m_fParaRtl + scalars += sizeof(TrWsHandling); + scalars += sizeof(int); // m_nDirDepth + init += sizeof(byte*); // m_prgbNextSegDat + init += sizeof(int); // m_cbNextSegDat + init += sizeof(byte) * seg.m_cbNextSegDat; + init += sizeof(byte*); // m_prgInitDat + init += sizeof(int); // m_cbInitDat + init += sizeof(byte) * seg.m_cbInitDat; + init += sizeof(int); // m_dichPreContext +// obsolete += sizeof(void *); // m_psegPrev +// obsolete += sizeof(void *); // m_psegNext +// strings += stringOverhead * 2; +// strings += sizeof(wchar_t) * seg.m_stuFaceName.length(); +// strings += sizeof(wchar_t) * seg.m_stuBaseFaceName.length(); +// scalars += sizeof(bool); // m_fUseSepBase; +// scalars += sizeof(float); // m_pixHeight +// scalars += sizeof(bool); // m_fBold +// scalars += sizeof(bool); // m_fItalic + scalars += sizeof(LineBrk); // m_lbStart + scalars += sizeof(LineBrk); // m_lbEnd + scalars += sizeof(bool); // m_fStartLine + scalars += sizeof(bool); // m_fEndLine + scalars += sizeof(SegEnd); // m_est + metrics += sizeof(int); // m_mFontEmUnits + metrics += sizeof(float) * 17; // was 19, removed m_x/ysDPI + associations += sizeof(int); // m_ichwAssocsMin + associations += sizeof(int); // m_ichwAssocsLim + int dichw = seg.m_ichwAssocsLim - seg.m_ichwAssocsMin; + associations += sizeof(int*); // m_prgisloutBefore + associations += sizeof(int) * dichw; + associations += sizeof(int*); // m_prgisloutAfter + associations += sizeof(int) * dichw; + associations += sizeof(void*); // m_prgpvisloutAssocs + associations += sizeof(void*) * dichw; + for (int idichw = 0; idichw < dichw; idichw++) + { + std::vector<int> * pvisloutAssocs = seg.m_prgpvisloutAssocs[idichw]; + if (pvisloutAssocs) + { + associations += vectorOverhead; + associations += sizeof(int) * pvisloutAssocs->capacity(); + wastedVector += sizeof(int) * (pvisloutAssocs->capacity() - pvisloutAssocs->size()); + } + } + associations += sizeof(int*); // m_prgisloutLigature + associations += sizeof(int) * dichw; + associations += sizeof(sdata8*); // m_prgiComponent + associations += sizeof(sdata8) * dichw; +// obsolete += sizeof(void*); // m_psstrm + scalars += sizeof(int); // m_cslout + slot_count += seg.m_cslout; + for (int islout = 0; islout < seg.m_cslout; islout++) + { + GrSlotOutput * pslout = seg.m_prgslout + islout; + slot_abstract += sizeof(gid16) * 2; // m_chwGlyphID, m_chwActual + slot_abstract += sizeof(sdata8) * 4; // m_spsl, m_dirc, m_lb, m_nDirLevel +// slot_abstract += sizeof(float) * 8; // font/glyph metrics + slot_abstract += sizeof(short) * 5; // slot attributes - was 21 + slot_abstract += sizeof(int); // m_mJWidth0; + slot_abstract += sizeof(byte); // m_nJWeight0; + slot_abstract += sizeof(bool); // m_fInsertBefore + slot_abstract += sizeof(sdata8); // m_bIsSpace + slot_abstract += sizeof(byte); // m_cnCompPerLig + slot_abstract += sizeof(void*); // m_prgnVarLenBuf + slot_varLenBuf += pslout->CExtraSpaceSlout() * sizeof(u_intslot); + slot_abstract += sizeof(float); // m_xsPositionX, m_ysPositionY +// slot_abstract += sizeof(bool); // m_fAdvXSet, m_fAdvYSet + slot_scalars += sizeof(int) * 2; // m_ichwBefore/AfterAssoc + slot_scalars += sizeof(sdata8); // m_cComponents + slot_scalars += sizeof(int); // m_isloutClusterBase + slot_scalars += sizeof(sdata8); // m_disloutCluster + slot_scalars += sizeof(float) * 3; // m_xsClusterXOffset, m_xsClusterAdvance, m_xsAdvanceX + slot_scalars += sizeof(int); // m_igbb +// slot_scalars += sizeof(Rect); // m_rectBB + } + scalars += sizeof(int); // m_cnUserDefn + scalars += sizeof(int); // m_cnCompPerLig + scalars += sizeof(int); // m_cnFeat + glyphInfo_count += seg.m_cginf; + scalars += sizeof(int); // m_cginf; + scalars += sizeof(int); // m_isloutGinf0 + for (int iginf = 0; iginf < seg.m_cginf; iginf++) + { + ///GlyphInfo * pginf = seg.m_prgginf + iginf; + glyphInfo += sizeof(void *); // m_pseg + glyphInfo += sizeof(void *); // m_pslout + glyphInfo += sizeof(int); // m_islout + } + init += sizeof(int); // m_cslotRestartBackup + init += vectorOverhead; + init += sizeof(sdata8) * seg.m_vnSkipOffsets.capacity(); + wastedVector += sizeof(sdata8) * (seg.m_vnSkipOffsets.capacity() - seg.m_vnSkipOffsets.size()); + init += sizeof(DirCode); // m_dircPrevStrong + init += sizeof(DirCode); // m_dircPrevTerm + + //int grandTotal; + //grandTotal = overhead + pointers + scalars + strings + metrics + associations + // + init + obsolete + slot_count + slot_abstract + slot_varLenBuf + slot_scalars + // + slot_clusterMembers + glyphInfo_count + glyphInfo; + + //int totalPerSeg = grandTotal / seg_count; +} + +/*---------------------------------------------------------------------------------------------- + Write out the memory usage onto a stream. +----------------------------------------------------------------------------------------------*/ +void SegmentMemoryUsage::prettyPrint(std::ostream & strm) +{ + int totalBytes = overhead + pointers + scalars + strings + metrics + associations + + init + obsolete + slot_count + slot_abstract + slot_varLenBuf + slot_scalars + + slot_clusterMembers + glyphInfo_count + glyphInfo; + + int slotTotal = slot_abstract + slot_varLenBuf + slot_scalars + slot_clusterMembers; + + strm << "Number of segments: " << seg_count << "\n\n"; + + strm << "TOTALS\n"; + strm << "Overhead: " << overhead << "\n"; + strm << "Pointers: " << pointers << "\n"; + strm << "Scalars: " << scalars << "\n"; + strm << "Strings: " << strings << "\n"; + strm << "Metrics: " << metrics << "\n"; + strm << "Associations: " << associations << "\n"; + strm << "Obsolete: " << obsolete << "\n"; + strm << "Slot data: " << slotTotal << "\n"; + strm << " Abstract: " << slot_abstract << "\n"; + strm << " Var-length buffer: " << slot_varLenBuf << "\n"; + strm << " Scalars: " << slot_scalars << "\n"; + strm << " Cluster members: " << slot_clusterMembers << "\n"; + strm << "Glyph info: " << glyphInfo << "\n\n"; + + strm << "Wasted in vectors: " << wastedVector << "\n\n"; + + strm << "Total bytes used: " << totalBytes << "\n\n"; + + if (seg_count > 0) + { + strm << "AVERAGES\n"; + strm << "Overhead: " << overhead/seg_count << "\n"; + strm << "Pointers: " << pointers/seg_count << "\n"; + strm << "Scalars: " << scalars/seg_count << "\n"; + strm << "Strings: " << strings/seg_count << "\n"; + strm << "Metrics: " << metrics/seg_count << "\n"; + strm << "Associations: " << associations/seg_count << "\n"; + strm << "Obsolete: " << obsolete/seg_count << "\n"; + strm << "Slot data: " << slotTotal/seg_count << "\n"; + strm << " Abstract: " << slot_abstract/seg_count << "\n"; + strm << " Var-length buffer: " << slot_varLenBuf/seg_count << "\n"; + strm << " Scalars: " << slot_scalars/seg_count << "\n"; + strm << " Cluster members: " << slot_clusterMembers/seg_count << "\n"; + strm << "Glyph info: " << glyphInfo/seg_count << "\n\n"; + + strm << "Avg. bytes per segment: " << totalBytes / seg_count << "\n\n"; + + strm << "Avg. # of slots per segment: " << slot_count / seg_count << "\n\n"; + } +} + +} //namespace gr + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/Platform.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/Platform.cpp new file mode 100644 index 00000000000..30ec3f4bf1f --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/Platform.cpp @@ -0,0 +1,113 @@ +#include "GrPlatform.h" + +#ifdef _WIN32 +#include "windows.h" + +namespace gr +{ + +size_t Platform_UnicodeToANSI(const utf16 * prgchwSrc, size_t cchwSrc, char * prgchsDst, size_t cchsDst) +{ + return ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)prgchwSrc, cchwSrc, prgchsDst, cchsDst, NULL, NULL); +} + +size_t Platform_AnsiToUnicode(const char * prgchsSrc, size_t cchsSrc, utf16 * prgchwDst, size_t cchwDst) +{ + return ::MultiByteToWideChar(CP_ACP, 0, prgchsSrc, cchsSrc, (LPWSTR)prgchwDst, cchwDst); +} + +size_t Platform_8bitToUnicode(int nCodePage, const char * prgchsSrc, int cchsSrc, + utf16 * prgchwDst, int cchwDst) +{ + return ::MultiByteToWideChar(nCodePage, 0, prgchsSrc, cchsSrc, (LPWSTR)prgchwDst, cchwDst); +} + +size_t utf8len(const char *s) +{ + return mbstowcs(NULL,s,0); +} + +utf16 *utf16cpy(utf16 *dest, const utf16 *src) +{ + return (utf16*)wcscpy((wchar_t*)dest, (const wchar_t*)src); +} + +utf16 *utf16ncpy(utf16 *dest, const utf16 *src, size_t n) +{ + return (utf16*)wcsncpy((wchar_t*)dest, (const wchar_t*)src, n); +} + +utf16 *utf16ncpy(utf16 *dest, const char *src, size_t n) +{ + #ifdef UTF16DEBUG + std::cerr << "utf16ncpy8: " << src << std::endl; + #endif + Platform_AnsiToUnicode(src, strlen(src), dest, n); + return dest; +} + + +size_t utf16len(const utf16 *s) +{ + return wcslen((const wchar_t*)s); +} + +int utf16cmp(const utf16 *s1, const utf16 *s2) +{ +return wcscmp((const wchar_t*)s1, (const wchar_t*)s2); +} + +int utf16ncmp(const utf16 *s1, const utf16 *s2, size_t n) +{ + return wcsncmp((const wchar_t*)s1, (const wchar_t*)s2, n); +} + +int utf16cmp(const utf16 *s1, const char *s2) +{ + while (*s1 && s2) + { + if (*s1 < *s2) + { + return -1; + } + if (*s1 > *s2) + { + return 1; + } + *s1++; + *s2++; + } + if (*s1) return -1; + else if (*s2) return 1; + return 0; +} + +} +#else // not _WIN32 + +#include <iostream> +#include <stdlib.h> +#include <string.h> + +#ifndef HAVE_FABSF +float fabsf(float x) +{ + return (x < 0.0f) ? -x : x; +} +#endif + +namespace gr +{ + +size_t utf16len(const utf16 *s) +{ + // assumes NULL terminated strings + const utf16 *start = s; + for (; *s; ++s); + + return s - start; +} + +} // namespace gr +#endif // _WIN32 + diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/Segment.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/Segment.cpp new file mode 100644 index 00000000000..669864780a6 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/Segment.cpp @@ -0,0 +1,3558 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: Segment.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements a Graphite segment--a range of rendered text in one writing system, that can be + rendered with a single font, and that fits on a single line. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) +#include <math.h> +#ifndef _WIN32 +#include <stdlib.h> +#endif + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** +#if 0 // TODO remove +namespace +{ + +static bool g_fDrawing; + +} // namespace +#endif + + +static int g_csegTotal = 0; + +namespace gr +{ + +//:>******************************************************************************************** +//:> Segment methods +//:>******************************************************************************************** + + +int Segment::GetSegmentCount() /// TEMP +{ + return g_csegTotal; +} + + +/*---------------------------------------------------------------------------------------------- + Constructor: basic initialization. +----------------------------------------------------------------------------------------------*/ +Segment::Segment() : + m_pgts(NULL), + m_pfont(NULL), + m_preneng(NULL), + m_fErroneous(false), + m_pgjus(NULL), + m_prgbNextSegDat(NULL), + m_cbNextSegDat(0), + m_prgInitDat(NULL), + m_cbInitDat(0), +// m_fUseSepBase(0), + //m_psegAltEndLine(NULL), + m_prgisloutBefore(NULL), + m_prgisloutAfter(NULL), + m_prgpvisloutAssocs(NULL), + m_prgisloutLigature(NULL), + m_prgiComponent(NULL), +// m_psstrm(NULL), + m_cslout(0), + m_prgslout(NULL), + m_prgnSlotVarLenBuf(NULL), +// m_cnUserDefn(0), + m_cnCompPerLig(0), +// m_cnFeat(0), + m_cginf(0), + m_prgginf(NULL) +{ + m_cref = 1; + + g_csegTotal++; + +// m_stuFaceName.erase(); +// m_stuBaseFaceName.erase(); + m_vnSkipOffsets.clear(); +} + +/*---------------------------------------------------------------------------------------------- + Reference counting. +----------------------------------------------------------------------------------------------*/ + +long Segment::IncRefCount(void) +{ + AssertPtr(this); + return InterlockedIncrement(&m_cref); +} + +long Segment::DecRefCount(void) +{ + AssertPtr(this); + long cref = InterlockedDecrement(&m_cref); + if (cref == 0) { + m_cref = 1; + delete this; + } + return cref; +} + +/*---------------------------------------------------------------------------------------------- + Constructor to fit as much of the text on the line as possible, + finding a reasonable break point if necessary. +----------------------------------------------------------------------------------------------*/ +LineFillSegment::LineFillSegment( + Font * pfont, + ITextSource * pts, + LayoutEnvironment * playoutArg, + toffset ichStart, + toffset ichStop, + float xsMaxWidth, + bool fBacktracking) + : Segment() +{ + if (!pfont) + throw; + if (!pts) + throw; + + LayoutEnvironment layoutDefault; + LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg; + + pfont->RenderLineFillSegment(this, pts, *playout, ichStart, ichStop, xsMaxWidth, + fBacktracking); +} + +/*---------------------------------------------------------------------------------------------- + Constructor to create a segment representing the given range of characters. +----------------------------------------------------------------------------------------------*/ +RangeSegment::RangeSegment( + Font * pfont, + ITextSource * pts, + LayoutEnvironment * playoutArg, + toffset ichStart, + toffset ichStop, + Segment * psegInitLike) + : Segment() +{ + if (!pfont) + throw; + if (!pts) + throw; + + LayoutEnvironment layoutDefault; + LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg; + playout->setSegmentForInit(psegInitLike); + + pfont->RenderRangeSegment(this, pts, *playout, ichStart, ichStop); + + playout->setSegmentForInit(NULL); +} + +/*---------------------------------------------------------------------------------------------- + Constructor to create a justified segment. Private. +----------------------------------------------------------------------------------------------*/ +JustSegmentAux::JustSegmentAux( + Font * pfont, + ITextSource * pts, + LayoutEnvironment * playoutArg, + toffset ichStart, + toffset ichStop, + float xsNaturalWidth, + float xsJustifiedWidth, + Segment * psegInitLike) + : Segment() +{ + if (!pfont) + throw; + if (!pts) + throw; + + LayoutEnvironment layoutDefault; + LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg; + playout->setSegmentForInit(psegInitLike); + + pfont->RenderJustifiedSegment(this, pts, *playout, ichStart, ichStop, + xsNaturalWidth, xsJustifiedWidth); + + playout->setSegmentForInit(NULL); +} + +//Segment::Segment(ITextSource * pgts, int ichwMin, int ichwLim, +// LineBrk lbStart, LineBrk lbEnd, +// bool fStartLine, bool fEndLine, bool fWsRtl) +//{ +// m_cref = 1; +// Initialize(pgts, ichwMin, ichwLim, lbStart, lbEnd, fStartLine, fEndLine, fWsRtl); +//} + +void Segment::Initialize(ITextSource * pgts, int ichwMin, int ichwLim, + LineBrk lbStart, LineBrk lbEnd, SegEnd est, + bool fStartLine, bool fEndLine, bool fWsRtl) +{ + AssertPtrN(pgts); + Assert(ichwMin >= 0); + Assert(ichwLim >= 0); + Assert(ichwMin <= ichwLim); + + //pgts->TextSrcObject(&m_pgts); // create a permanent wrapper, if necessary + // m_pgts->AddRef(); no, smart pointer + m_pgts = pgts; + + m_ichwMin = ichwMin; + m_dichwLim = ichwLim - ichwMin; + m_lbStart = lbStart; + m_lbEnd = lbEnd; + m_est = est; + m_dxsStretch = 0; + m_dxsWidth = -1; + m_dysAscent = -1; // to properly init, ComputeDimensions must be called + m_dysHeight = -1; + m_dysXAscent = -1; + m_dysXDescent = -1; + m_dysAscentOverhang = -1; + m_dysDescentOverhang = -1; + m_dxsLeftOverhang = -1; + m_dxsRightOverhang = -1; + m_dysOffset = 0; + m_fStartLine = fStartLine; + m_fEndLine = fEndLine; + m_fWsRtl = fWsRtl; + + m_dxsVisibleWidth = -1; + m_dxsTotalWidth = -1; + +// m_psstrm = NULL; + m_prgslout = NULL; + m_prgnSlotVarLenBuf = NULL; + m_prgisloutBefore = NULL; + m_prgisloutAfter = NULL; + m_prgpvisloutAssocs = NULL; + m_prgisloutLigature = NULL; + m_prgiComponent = NULL; + + m_prgbNextSegDat = NULL; + m_cbNextSegDat = 0; + + //m_psegAltEndLine = NULL; + +// m_stuFaceName.erase(); +// m_stuBaseFaceName.erase(); +// m_fUseSepBase = false; + +// m_psegPrev = NULL; // obsolete +// m_psegNext = NULL; + + //InitializePlatform(); +} + +/*---------------------------------------------------------------------------------------------- + Destructor. +----------------------------------------------------------------------------------------------*/ +Segment::~Segment() +{ + g_csegTotal--; + + DestroyContents(); +} + +void Segment::DestroyContents() +{ + delete m_pfont; + m_pfont = NULL; + + //if (m_pgts) + // // A specially-created wrapper object has to be deleted. + // m_pgts->DeleteTextSrcPtr(); + m_pgts = NULL; + + //if (m_pgjus) + // // A specially-created wrapper object has to be deleted. + // m_pgjus->DeleteJustifierPtr(); + m_pgjus = NULL; + + //if (m_psegAltEndLine) + //{ + // if (m_psegAltEndLine->m_psegAltEndLine == this) + // m_psegAltEndLine->m_psegAltEndLine = NULL; + // delete m_psegAltEndLine; + //} + + //for (int iginf = 0; iginf < m_cginf; iginf++) // DELETE + // delete m_prgginf[iginf].components; + + Assert((m_prgslout && m_prgnSlotVarLenBuf) || (!m_prgslout && !m_prgnSlotVarLenBuf)); + delete[] m_prgslout; + delete[] m_prgnSlotVarLenBuf; + delete[] m_prgisloutBefore; + delete[] m_prgisloutAfter; + for (int ichw = 0; ichw < m_ichwAssocsLim - m_ichwAssocsMin; ichw++) + { + if (m_prgpvisloutAssocs && m_prgpvisloutAssocs[ichw]) + delete m_prgpvisloutAssocs[ichw]; + } + delete[] m_prgpvisloutAssocs; + delete[] m_prgisloutLigature; + delete[] m_prgiComponent; + delete[] m_prgbNextSegDat; + delete[] m_prgInitDat; + delete[] m_prgginf; + m_cginf = 0; + + //DestroyContentsPlatform(); + + // ReleaseObj(m_pgts); automatic because smart pointer +} + +/*---------------------------------------------------------------------------------------------- + Factory method to create a new version of an existing segment with different + line-boundary flags. +----------------------------------------------------------------------------------------------*/ +Segment * Segment::LineContextSegment(Segment & seg, bool fStartLine, bool fEndLine) +{ + if (!seg.hasLineBoundaryContext()) + { + // Make an identical segment and just change the flags. + Segment * psegNew = new Segment(seg); + psegNew->InitLineContextSegment(fStartLine, fEndLine); + return psegNew; + } + else + { + // Rerun the processing. + LayoutEnvironment layout = seg.Layout(); + layout.setStartOfLine(fStartLine); + layout.setEndOfLine(fEndLine); + ITextSource & gts = seg.getText(); + Font & font = seg.getFont(); + return new RangeSegment(&font, >s, &layout, + seg.startCharacter(), seg.stopCharacter(), &seg); + } +} + +void Segment::InitLineContextSegment(bool fStartLine, bool fEndLine) +{ + // For RTL end-of-line segments, trailing white space hangs off the left of + // the origin of the segment. So when changing that flag + // shift appropriately. + bool fShift = ((m_nDirDepth % 2) && (m_fEndLine != fEndLine)); + + m_fStartLine = fStartLine; + m_fEndLine = fEndLine; + + m_layout.setStartOfLine(fStartLine); + m_layout.setEndOfLine(fEndLine); + + if (fShift) + { + (m_fEndLine) ? + ShiftGlyphs(m_dxsVisibleWidth - m_dxsTotalWidth) : // shift left + ShiftGlyphs(m_dxsTotalWidth - m_dxsVisibleWidth); // shift right + } + + m_dxsWidth = -1; +} + +/*---------------------------------------------------------------------------------------------- + A factory method to create a justified version of an existing segment. +----------------------------------------------------------------------------------------------*/ +Segment * Segment::JustifiedSegment(Segment & seg, float xsNewWidth) +{ + LayoutEnvironment layout(seg.Layout()); + ITextSource & gts = seg.getText(); + + // Why do we have to do this? + layout.setJustifier(seg.Justifier()); + Font & font = seg.getFont(); + return new JustSegmentAux(&font, >s, &layout, + seg.startCharacter(), seg.stopCharacter(), + seg.advanceWidth(), xsNewWidth, &seg); +} + +/*---------------------------------------------------------------------------------------------- + A factory method to create a new version of an existing trailing white-space segment + with a different direction. +----------------------------------------------------------------------------------------------*/ +Segment * Segment::WhiteSpaceSegment(Segment & seg, int nNewDepth) +{ + Segment * psegNew = new Segment(seg); + psegNew->InitWhiteSpaceSegment(nNewDepth); + return psegNew; +} + +void Segment::InitWhiteSpaceSegment(int nNewDepth) +{ + if (nNewDepth == m_nDirDepth) + { + return; + } + else if ((nNewDepth % 2) == (m_nDirDepth % 2)) + { + // Same direction, not much to change. + m_nDirDepth = nNewDepth; + return; + } + else if (m_twsh != ktwshOnlyWs) + return; // couldn't change it, oh, well + + // Otherwise, do the hard stuff: reverse the positions of the glyphs. + for (int islout = 0; islout < m_cslout; islout++) + { + OutputSlot(islout)->ShiftForDirDepthChange(m_dxsTotalWidth); + } + m_nDirDepth = nNewDepth; + + //SetDirectionDepthPlatform(nNewDepth); +} + +/*---------------------------------------------------------------------------------------------- + Basic copy method. +----------------------------------------------------------------------------------------------*/ +Segment::Segment(Segment & seg) +{ + int islout; + + m_pgts = seg.m_pgts; + m_dichwLim = seg.m_dichwLim; + m_ichwMin = seg.m_ichwMin; + m_pfont = seg.m_pfont->copyThis(); + m_preneng = seg.m_preneng; + m_fErroneous = seg.m_fErroneous; + m_pgjus = seg.m_pgjus; + m_fWsRtl = seg.m_fWsRtl; + m_fParaRtl = seg.m_fParaRtl; + m_twsh = seg.m_twsh; + m_nDirDepth = seg.m_nDirDepth; + m_cbNextSegDat = seg.m_cbNextSegDat; + m_prgbNextSegDat = new byte[m_cbNextSegDat]; + std::copy(seg.m_prgbNextSegDat, seg.m_prgbNextSegDat + m_cbNextSegDat, m_prgbNextSegDat); +// m_psegPrev = seg.m_psegPrev; +// m_psegNext = seg.m_psegNext; +// m_stuFaceName = seg.m_stuFaceName; +// m_stuBaseFaceName = seg.m_stuBaseFaceName; +// m_fUseSepBase = seg.m_fUseSepBase; +// m_pixHeight = seg.m_pixHeight; +// m_fBold = seg.m_fBold; +// m_fItalic = seg.m_fItalic; + m_lbStart = seg.m_lbStart; + m_lbEnd = seg.m_lbEnd; + m_fStartLine = seg.m_fStartLine; + m_fEndLine = seg.m_fEndLine; + m_est = seg.m_est; + m_mFontEmUnits = seg.m_mFontEmUnits; + m_dysFontAscent = seg.m_dysFontAscent; + m_dysFontDescent = seg.m_dysFontDescent; + m_xysEmSquare = seg.m_xysEmSquare; +// m_xsDPI = seg.m_xsDPI; +// m_ysDPI = seg.m_ysDPI; + m_dxsStretch = seg.m_dxsStretch; + m_dxsWidth = seg.m_dxsWidth; + m_dysHeight = seg.m_dysHeight; + m_dysAscent = seg.m_dysAscent; + m_dysXAscent = seg.m_dysXAscent; + m_dysXDescent = seg.m_dysXDescent; + m_dysAscentOverhang = seg.m_dysAscentOverhang; + m_dysDescentOverhang = seg.m_dysDescentOverhang; + m_dxsLeftOverhang = seg.m_dxsLeftOverhang; + m_dxsRightOverhang = seg.m_dxsRightOverhang; + m_dxsVisibleWidth = seg.m_dxsVisibleWidth; + m_dxsTotalWidth = seg.m_dxsTotalWidth; + m_isloutVisLim = seg.m_isloutVisLim; + m_dysOffset = seg.m_dysOffset; + + m_ichwAssocsMin = seg.m_ichwAssocsMin; + m_ichwAssocsLim = seg.m_ichwAssocsLim; + + int dichw = m_ichwAssocsLim - m_ichwAssocsMin; + m_prgisloutBefore = new int[dichw]; + std::copy(seg.m_prgisloutBefore, seg.m_prgisloutBefore + dichw, m_prgisloutBefore); + m_prgisloutAfter = new int[dichw]; + std::copy(seg.m_prgisloutAfter, seg.m_prgisloutAfter + dichw, m_prgisloutAfter); + m_prgpvisloutAssocs = new std::vector<int> * [dichw]; + for (int ivislout = 0; ivislout < dichw; ivislout++) + { + std::vector<int> * pvislout = seg.m_prgpvisloutAssocs[ivislout]; + if (pvislout) + { + std::vector<int> * pvisloutNew = new std::vector<int>; + m_prgpvisloutAssocs[ivislout] = pvisloutNew; + *pvisloutNew = *pvislout; // copy the vector + } + } + m_prgisloutLigature = new int[dichw]; + std::copy(seg.m_prgisloutLigature, seg.m_prgisloutLigature + dichw, m_prgisloutLigature); + m_prgiComponent = new sdata8[dichw]; + std::copy(seg.m_prgiComponent, seg.m_prgiComponent + dichw, m_prgiComponent); + + m_cslout = seg.m_cslout; + int cnExtraPerSlot = (m_cslout > 0) ? seg.m_prgslout[0].CExtraSpaceSlout() : 0; + m_prgnSlotVarLenBuf = new u_intslot[m_cslout * cnExtraPerSlot]; + m_prgslout = new GrSlotOutput[m_cslout]; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutOrig = seg.m_prgslout + islout; + GrSlotOutput * psloutThis = m_prgslout + islout; + psloutThis->ExactCopyFrom(psloutOrig, + m_prgnSlotVarLenBuf + (islout * cnExtraPerSlot), // var-length buffer location + cnExtraPerSlot); + } + +// m_cnUserDefn = seg.m_cnUserDefn; + m_cnCompPerLig = seg.m_cnCompPerLig; +// m_cnFeat = seg.m_cnFeat; + m_cslotRestartBackup = seg.m_cslotRestartBackup; + std::copy(seg.m_prgnSlotVarLenBuf, seg.m_prgnSlotVarLenBuf + + (m_cslout * cnExtraPerSlot), m_prgnSlotVarLenBuf); + + m_cginf = seg.m_cginf; + m_isloutGinf0 = seg.m_isloutGinf0; + m_prgginf = new GlyphInfo[m_cginf]; + std::copy(seg.m_prgginf, seg.m_prgginf + m_cginf, m_prgginf); + for (int iginf = 0; iginf < m_cginf; iginf++) + { + m_prgginf[iginf].m_pseg = this; + m_prgginf[iginf].m_pslout = m_prgslout + m_prgginf[iginf].m_islout; + } + + // Platform-specific; remove eventually: + //m_cgstrm = seg.m_cgstrm; + //m_prggstrm = new GlyphStrm[m_cgstrm]; + //for (int igstrm = 0; igstrm < m_cgstrm; igstrm++) + //{ + // m_prggstrm[igstrm].gsk.ys = seg.m_prggstrm[igstrm].gsk.ys; + // m_prggstrm[igstrm].gsk.clrFore = seg.m_prggstrm[igstrm].gsk.clrFore; + // m_prggstrm[igstrm].gsk.clrBack = seg.m_prggstrm[igstrm].gsk.clrBack; + // m_prggstrm[igstrm].vchwGlyphId = seg.m_prggstrm[igstrm].vchwGlyphId; + // m_prggstrm[igstrm].vdxs = seg.m_prggstrm[igstrm].vdxs; + // m_prggstrm[igstrm].vigbb = seg.m_prggstrm[igstrm].vigbb; + // m_prggstrm[igstrm].xsStart = seg.m_prggstrm[igstrm].xsStart; + // m_prggstrm[igstrm].xsStartInt = seg.m_prggstrm[igstrm].xsStartInt; + //} + //m_cgbb = seg.m_cgbb; + //m_prggbb = new GlyphBb[m_cgbb]; + //memcpy(m_prggbb, seg.m_prggbb, m_cgbb * sizeof(GlyphBb)); + //------------------------------------- + + m_vnSkipOffsets = seg.m_vnSkipOffsets; + m_dircPrevStrong = seg.m_dircPrevStrong; + m_dircPrevTerm = seg.m_dircPrevTerm; + m_cbInitDat = seg.m_cbInitDat; + m_prgInitDat = new byte[m_cbInitDat]; + std::copy(seg.m_prgInitDat, seg.m_prgInitDat + m_cbInitDat, m_prgInitDat); +} + +/*---------------------------------------------------------------------------------------------- + Swap the guts of the two segments. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::SwapWith(Segment * pgrseg) +{ + int crefThis = m_cref; + int crefOther = pgrseg->m_cref; + + std::swap(*this, *pgrseg); + + m_cref = crefThis; + pgrseg->m_cref = crefOther; +} + +/*---------------------------------------------------------------------------------------------- + Delete the pointer to the alternate-end-of-line segment. +----------------------------------------------------------------------------------------------*/ +void Segment::ClearAltEndLineSeg() +{ + //if (m_psegAltEndLine) + //{ + // if (m_psegAltEndLine->m_psegAltEndLine == this) + // m_psegAltEndLine->m_psegAltEndLine = NULL; // so we don't get into a loop + // delete m_psegAltEndLine; + // m_psegAltEndLine = NULL; + //} +} + + +//:>******************************************************************************************** +//:> Interface methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Return the font that was used to create this segment. + Note that in some implementations, the font may be invalid; eg, on Windows the font + may not contain a valid device context. +----------------------------------------------------------------------------------------------*/ +Font & Segment::getFont() +{ + return *m_pfont; +} + +/*---------------------------------------------------------------------------------------------- + Return the text-source that the segment is representing. +----------------------------------------------------------------------------------------------*/ +ITextSource & Segment::getText() +{ + return *m_pgts; +} + +/*---------------------------------------------------------------------------------------------- + The distance the drawing point should advance after drawing this segment. + Always positive, even for RtoL segments. +----------------------------------------------------------------------------------------------*/ +float Segment::advanceWidth() +{ + if (m_dxsWidth < 0) + { + ////SetUpGraphics(ichwBase, pgg, true); + ComputeDimensions(); + } + return m_dxsWidth; +} + +/*---------------------------------------------------------------------------------------------- + Compute the rectangle in destination coords which contains all the pixels drawn by + this segment. This should be a sufficient rectangle to invalidate if the segment is + about to be discarded. +----------------------------------------------------------------------------------------------*/ +Rect Segment::boundingRect() +{ + if (m_dxsWidth < 0) + { + ////SetUpGraphics(ichwBase, pgg, true); + ComputeDimensions(); + } + + Rect rectRet; + rectRet.top = m_dysAscent + m_dysAscentOverhang; + rectRet.bottom = (m_dysAscent - m_dysHeight) - m_dysDescentOverhang; + rectRet.left = m_dxsLeftOverhang; + rectRet.right = m_dxsVisibleWidth + m_dxsRightOverhang; + + return rectRet; +} + +/*---------------------------------------------------------------------------------------------- + Answer whether the primary direction of the segment is right-to-left. This is based + on the direction of the writing system, except for white-space-only segments, in + which case it is based on the main paragraph direction. +----------------------------------------------------------------------------------------------*/ +bool Segment::rightToLeft() +{ + if (m_twsh == ktwshOnlyWs) + { + // White-space-only segment: use main paragraph direction. + return (m_nDirDepth % 2); + } + + GrEngine * pgreng = EngineImpl(); + if (pgreng) + { + return pgreng->RightToLeft(); + } + return (m_pgts->getRightToLeft(m_ichwMin)); +} + +/*---------------------------------------------------------------------------------------------- + Get the depth of direction embedding used by this segment. It is presumably the same + for all runs of the segment, otherwise, some of them would use a different writing + system and therefore be part of a different segment. So just use the first. +----------------------------------------------------------------------------------------------*/ +int Segment::directionDepth(bool * pfWeak) +{ + if (pfWeak) + *pfWeak = (m_twsh == ktwshOnlyWs); + +// *pnDepth = m_pgts->getDirectionDepth(m_ichwLim); +// Assert(*pnDepth == m_nDirDepth); + + return m_nDirDepth; +} + +/*---------------------------------------------------------------------------------------------- + Change the direction of the segment. This is needed specifically for white-space-only + segments, which are initially created to be in the direction of the paragraph, but then + later are discovered to not be line-end after all, and need to be changed to use the + directionality of the writing system. + + @return kresFail for segments that do not have weak directionality and therefore + cannot be changed. +----------------------------------------------------------------------------------------------*/ +bool Segment::setDirectionDepth(int nNewDepth) +{ + Assert(false); + + if (nNewDepth == m_nDirDepth) + { + return true; + } + else if ((nNewDepth % 2) == (m_nDirDepth % 2)) + { + // Same direction, not much to change. + m_nDirDepth = nNewDepth; + return true; + } + else if (m_twsh != ktwshOnlyWs) + return false; + + // Otherwise, do the hard stuff: reverse the positions of the glyphs. + for (int islout = 0; islout < m_cslout; islout++) + { + OutputSlot(islout)->ShiftForDirDepthChange(m_dxsTotalWidth); + } + + //SetDirectionDepthPlatform(nNewDepth); + + return true; + +} + +/*---------------------------------------------------------------------------------------------- + The logical range of characters covered by the segment, relative to the beginning of + the segment. The stop character is the index of the first character beyond the end + of the segment. + + These values should be exact at a writing system or string boundary, + but may be somewhat fuzzy at a line-break, since characters may be re-ordered + across such boundaries. The renderer is free to apply any definition it likes of + where a line-break occurs. This should always be the same value obtained from the + renderer as pdichLimSeg. +----------------------------------------------------------------------------------------------*/ +int Segment::startCharacter() +{ + return m_ichwMin; +} + +int Segment::stopCharacter() +{ + return m_ichwMin + m_dichwLim; +} + +/*---------------------------------------------------------------------------------------------- + Returns an indication of why the segment ended. +----------------------------------------------------------------------------------------------*/ +bool Segment::startOfLine() +{ + return m_fStartLine; +} + +/*---------------------------------------------------------------------------------------------- + Returns an indication of why the segment ended. +----------------------------------------------------------------------------------------------*/ +bool Segment::endOfLine() +{ + return m_fEndLine; +} + +/*---------------------------------------------------------------------------------------------- + Returns true if this segment has something special happening at line boundaries. +----------------------------------------------------------------------------------------------*/ +bool Segment::hasLineBoundaryContext() +{ + return (EngineImpl()->LineBreakFlag()); +} + +/*---------------------------------------------------------------------------------------------- + Returns an indication of why the segment ended. +----------------------------------------------------------------------------------------------*/ +SegEnd Segment::segmentTermination() +{ + return m_est; +} + +/*---------------------------------------------------------------------------------------------- + Indicates the last character of interest to the segment, relative to the beginning + of the segment. The meaning of this is that no behavior of this segment will be + affected if characters beyond that change. This does not necessarily mean that + a different line break could not have been obtained by the renderer if characters + beyond that change, just that a segment with the boundaries of this one would + not behave differently. +----------------------------------------------------------------------------------------------*/ +//GrResult Segment::get_LimInterest(int ichwBase, int * pdichw) +//{ +// ChkGrOutPtr(pdichw); +// +// GrEngine * pgreng = EngineImpl(); +// Assert(pgreng); +// *pdichw = m_dichwLim + pgreng->PostXlbContext(); +// ReturnResult(kresOk); +//} + +/*---------------------------------------------------------------------------------------------- + Changes the end-of-line status of the segment. This is used after making the last segment + of a string if we want to attempt to put some more text after it (e.g., in another + writing system), or if we have reordered due to bidirectionality. + + ENHANCE: the current version handles switching back and forth when we are trying to + decide whether we can put more stuff on a line. So it assumes we are at the end of + the contextual run (ie, the next batch of stuff will be in a different ows). + This will not correctly handle the way this function could or possibly + should be used for bidirectional reordering. +----------------------------------------------------------------------------------------------*/ +/* +GrResult Segment::changeLineEnds(bool fNewStart, bool fNewEnd) +{ + if (m_fStartLine != fNewStart || m_fEndLine != fNewEnd) + { + if (EngineImpl()->LineBreakFlag()) + { + if (!m_psegAltEndLine) + { + int dichwLimSeg, dichwContext; + float dxWidth; + SegEnd est; + int cbNextSegDat; + byte rgbNextSegDat[256]; + OLECHAR rgchErrMsg[256]; + Segment * pseg; + + EngineImpl()->FindBreakPointAux(m_pfont, m_pgts, m_pgjus, + m_ichwMin, m_ichwMin + m_dichwLim, m_ichwMin + m_dichwLim, + false, fNewStart, fNewEnd, kPosInfFloat, false, + klbNoBreak, klbNoBreak, m_twsh, m_fParaRtl, + &pseg, + &dichwLimSeg, &dxWidth, &est, + m_cbInitDat, m_prgInitDat, 256, rgbNextSegDat, &cbNextSegDat, + &dichwContext, + NULL, + rgchErrMsg, 256); + // TODO: do something with the error message. This shouldn't really happen, + // since to get here we already created a segment with this font. + if (!pseg) + { + // Because we provide INT_MAX as the amount of space, there should + // always be enough room to generate the alternate segment. + Assert(false); + THROW(kresUnexpected); + } + Assert(cbNextSegDat == 0); + m_psegAltEndLine = pseg; + } + Assert(m_psegAltEndLine->m_fEndLine == fNewEnd); + Segment * pseg = m_psegAltEndLine; // save the value + SwapWith(m_psegAltEndLine); + m_psegAltEndLine = pseg; // reset it after swap + pseg->m_psegAltEndLine = this; + } + else + { + bool fShift = ((m_nDirDepth % 2) && (m_fEndLine != fNewEnd)); + m_fStartLine = fNewStart; + m_fEndLine = fNewEnd; + if (fShift) + { + (m_fEndLine) ? + ShiftGlyphs(m_dxsVisibleWidth - m_dxsTotalWidth) : // shift left + ShiftGlyphs(m_dxsTotalWidth - m_dxsVisibleWidth); // shift right + } + m_dxsWidth = -1; + } + } + + ReturnResult(kresOk); +} +*/ + +/*---------------------------------------------------------------------------------------------- + Get the type of break that occurs at the logical start of the segment. +----------------------------------------------------------------------------------------------*/ +LineBrk Segment::startBreakWeight() +{ + return m_lbStart; +} + +/*---------------------------------------------------------------------------------------------- + Get the type of break that occurs at the logical end of the segment. +----------------------------------------------------------------------------------------------*/ +LineBrk Segment::endBreakWeight() +{ + return m_lbEnd; +} + +/*---------------------------------------------------------------------------------------------- + Read the amount of stretch, that is, the difference between the actual and natural width. + If the segment has been shrunk, returns a negative number. + TODO: implement properly. +----------------------------------------------------------------------------------------------*/ +float Segment::stretch() +{ + return (float)m_dxsStretch; +} + +/*---------------------------------------------------------------------------------------------- + Return the maximum stretch possible. + TODO: implement properly. +----------------------------------------------------------------------------------------------*/ +float Segment::maxStretch() +{ + return 1000; +} + +/*---------------------------------------------------------------------------------------------- + Return the maximum shrink possible. + TODO: implement properly. +----------------------------------------------------------------------------------------------*/ +float Segment::maxShrink() +{ + return 0; +} + +/*---------------------------------------------------------------------------------------------- + Return iterators representing the complete range of glyphs in the segment. +----------------------------------------------------------------------------------------------*/ +std::pair<GlyphIterator, GlyphIterator> Segment::glyphs() +{ + return std::make_pair(GlyphIterator(*this, 0), GlyphIterator(*this, m_cginf)); +} + +/*---------------------------------------------------------------------------------------------- + Return iterators representing the set of glyphs for the given character. +----------------------------------------------------------------------------------------------*/ +std::pair<GlyphSetIterator, GlyphSetIterator> Segment::charToGlyphs(toffset ich) +{ + std::vector<int> vislout = UnderlyingToLogicalAssocs(ich); + + if (vislout.size() == 0) + return std::make_pair(GlyphSetIterator(), GlyphSetIterator()); + else + { + // Note that BOTH instances of GlyphSetIterator must use the same underlying vector, + // so that comparisons make sense. + RcVector * qvislout = new RcVector(vislout); + return std::make_pair( + GlyphSetIterator(*this, 0, qvislout), + GlyphSetIterator(*this, vislout.size(), qvislout)); + } +} + +/*---------------------------------------------------------------------------------------------- + Return the rendered glyphs and their x- and y-coordinates, in the order generated by + the final positioning pass, relative to the top-left of the segment. They are always + returned in (roughly) left-to-right order. Note: any of the arrays may be null. +----------------------------------------------------------------------------------------------*/ +/* +GrResult Segment::GetGlyphsAndPositions( + Rect rs, Rect rd, int cchMax, int * pcchRet, + utf16 * prgchGlyphs, float * prgxd, float * prgyd, float * prgdxdAdv) +{ + ChkGrOutPtr(pcchRet); + if (prgchGlyphs) + ChkGrArrayArg(prgchGlyphs, cchMax); + if (prgxd) + ChkGrArrayArg(prgxd, cchMax); + if (prgyd) + ChkGrArrayArg(prgyd, cchMax); + if (prgdxdAdv) + ChkGrArrayArg(prgdxdAdv, cchMax); + + GrResult res; + + //SetUpGraphics(ichwBase, pgg, true); + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pcchRet = m_cginf; + if (cchMax < m_cginf) + { + res = (cchMax == 0) ? kresFalse : kresInvalidArg; + //RestoreFont(pgg); + ReturnResult(res); + } + + if (m_dxsWidth < 0) + ComputeDimensions(); + + float dysFontAscent = m_dysFontAscent; + //////res = GetFontAscentSourceUnits(pgg, &dysFontAscent); + //////if (ResultFailed(res)) + //////{ + ////// RestoreFont(pgg); + ////// ReturnResult(res); + //////} + + float xs = 0; + // Top of text that sits on the baseline, relative to top of segment: + float ys = m_dysAscent - dysFontAscent; + + float * prgxdLocal; + if (prgxd == NULL) + prgxdLocal = new float[cchMax]; + else + prgxdLocal = prgxd; + + res = GetGlyphsAndPositionsPlatform(xs, ys, rs, rd, + prgchGlyphs, prgxdLocal, prgyd, prgdxdAdv); + + if (!prgxd) + delete[] prgxdLocal; + + //RestoreFont(pgg); + + ReturnResult(res); +} +*/ + +/*---------------------------------------------------------------------------------------------- + For debugging. Return the characters in this segment. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +GrResult Segment::GetCharData(int cchMax, utf16 * prgch, int * pcchRet) +{ + ChkGrArgPtr(prgch); + ChkGrOutPtr(pcchRet); + + int ichLimTmp = m_ichwMin + min(m_dichwLim, cchMax); + m_pgts->fetch(m_ichwMin, ichLimTmp - m_ichwMin, prgch); + *pcchRet = ichLimTmp - m_ichwMin; + ReturnResult(kresOk); +} + +/*---------------------------------------------------------------------------------------------- + Return either the first or last character that the the glyph corresponds to. The + index of the glyph matches what was returned by GetGlyphsAndPositions. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +GrResult Segment::GlyphToChar(int iginf, bool fFirst, int * pich) +{ + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pich = PhysicalSurfaceToUnderlying(iginf, fFirst); + ReturnResult(kresOk); +} + +/*---------------------------------------------------------------------------------------------- + Return all the characters that the glyph corresponds to. Most commonly this will + be only one char, but ligatures, for instance, will have several. The index of the + glyph matches what was returned by GetGlyphsAndPositions. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +GrResult Segment::GlyphToAllChars(int iginf, + int cichMax, int * prgich, int * pcichRet) +{ + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + std::vector<int> vich; + int ichFirst = PhysicalSurfaceToUnderlying(iginf, true); + int ichLast = PhysicalSurfaceToUnderlying(iginf, false); + Assert(ichFirst <= ichLast); + + if (ichFirst < ichLast) + { + // Loop over the range of characters that might be associated with this glyph + // and add any that actually are. + // TODO: we should probably use a method that gets us all the physical associations, + // but this should get us 99.5% of the way there for now. + for (int ich = ichFirst; ich <= ichLast; ich++) + { + int iginfFirst = UnderlyingToPhysicalSurface(ich, true); + int iginfLast = UnderlyingToPhysicalSurface(ich, false); + if (iginfFirst == iginf || iginfLast == iginf) + vich.push_back(ich); + } + } + else + vich.push_back(ichFirst); + + *pcichRet = vich.size(); + if (cichMax < signed(vich.size())) + { + GrResult res = (cichMax == 0 ? kresFalse : kresInvalidArg); + ReturnResult(res); + } + int * pi = prgich; + for (size_t i = 0; i < vich.size(); i++) + *pi++ = vich[i]; + + ReturnResult(kresOk); +} + +/*---------------------------------------------------------------------------------------------- + Return either the first or last glyph that corresponds to the character. The + index of the glyph matches what was returned by GetGlyphsAndPositions. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +GrResult Segment::CharToGlyph(int ich, bool fFirst, int * pigbb) +{ + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pigbb = UnderlyingToPhysicalSurface(ich, fFirst); + ReturnResult(kresOk); +} + + +//:>******************************************************************************************** +//:> Non-FieldWorks interface methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Return the breakweight for the given character. Returns the best value if the character is + mapped to glyphs with different values. +----------------------------------------------------------------------------------------------*/ +LineBrk Segment::getBreakWeight(int ich, bool fBreakBefore) +{ + LineBrk lbFirst, lbLast; + + int isloutFirst = UnderlyingToLogicalSurface(ich, true); + if (isloutFirst == kPosInfinity || isloutFirst == kNegInfinity) + return klbClipBreak; + GrSlotOutput * psloutFirst = m_prgslout + isloutFirst; + bool fNotFirst = false; + if ((psloutFirst->NumberOfComponents() > 0) + && psloutFirst->FirstUnderlyingComponent(0) != ich) + { + // Not the first component of a ligature. + lbFirst = (LineBrk) ((int)klbClipBreak * -1); + fNotFirst = true; + } + else + { + lbFirst = (LineBrk) psloutFirst->BreakWeight(); + } + + int isloutLast = UnderlyingToLogicalSurface(ich, false); + if (isloutLast == kPosInfinity || isloutLast == kNegInfinity) + return klbClipBreak; + GrSlotOutput * psloutLast = m_prgslout + isloutLast; + bool fNotLast = false; + int icompLast = psloutLast->NumberOfComponents() - 1; + if ((psloutLast->NumberOfComponents() > 0) + && psloutLast->LastUnderlyingComponent(icompLast) != ich) + { + // Not the last component of a ligature. + lbLast = klbClipBreak; + fNotLast = true; + } + else + { + lbLast = (LineBrk) psloutLast->BreakWeight(); + } + + LineBrk lbRet; + if (fNotLast && fNotFirst) + // middle of a ligature + lbRet = klbClipBreak; + else if (fNotLast) + lbRet = (fBreakBefore) ? lbFirst : klbClipBreak; + else if (fNotFirst) + lbRet = (fBreakBefore) ? klbClipBreak : lbLast; + else + lbRet = (fBreakBefore) ? lbFirst : lbLast; + + return lbRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the width of the range of characters. Line-boundary contextualization is handled + by the measure attributes on the glyphs. The width returned is equivalent to the sum of + the widths of any line-segments that would be needed to underline all the characters. + + Since this method is intended to be used on "measured" segments that include the entire + paragraph, it is not very smart about handling cross-line-boundary contextualization. + It basically ignores any glyphs that are rendered by this segment but not officially + part of the segment. +----------------------------------------------------------------------------------------------*/ +float Segment::getRangeWidth(int ichMin, int ichLim, + bool fStartLine, bool fEndLine, bool fSkipSpace) +{ + if (m_dxsWidth < 0) + { + ////SetUpGraphics(ichwBase, pgg, true); + ComputeDimensions(); + } + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + //float xsSegRight = m_dxsTotalWidth; + + int ichMinRange = min(ichMin, ichLim); + int ichLimRange = max(ichMin, ichLim); + + //int ichMinSeg = max(ichMinRange, m_ichwMin + m_ichwAssocsMin); + //int ichLimSeg = min(ichLimRange, m_ichwMin + m_ichwAssocsLim); + int ichMinSeg = max(ichMinRange, m_ichwMin); + int ichLimSeg = min(ichLimRange, m_ichwMin + m_dichwLim); + + if (ichLimSeg < m_ichwMin) // not + m_ichwAssocsMin + return 0; + if (ichMinSeg >= m_ichwMin + m_dichwLim) // not m_ichwAssocsLim + return 0; + + if (fSkipSpace) + { + int islout = UnderlyingToLogicalSurface(ichLimSeg - 1, true); + GrSlotOutput * pslout = (islout == kNegInfinity || islout == kPosInfinity) ? + NULL : + OutputSlot(islout); + while (pslout && pslout->IsSpace()) + { + ichLimSeg--; + islout = UnderlyingToLogicalSurface(ichLimSeg - 1, true); +// pslout = OutputSlot(islout); + pslout = (islout == kNegInfinity || islout == kPosInfinity) ? + NULL : OutputSlot(islout); + } + } + + float xsWidth = 0; + SegmentNonPainter segp(this); // doesn't really need to paint + float rgxdLefts[100]; + float rgxdRights[100]; + size_t cxd = 0; + if (ichMinRange < ichLimRange) + cxd = segp.getUnderlinePlacement(ichMinSeg, ichLimSeg, fSkipSpace, + 100, rgxdLefts, rgxdRights, NULL); + for (size_t i = 0; i < cxd; i++) + xsWidth += rgxdRights[i] - rgxdLefts[i]; + + // Add in the line-boundary contextualization. + int isloutFirst = UnderlyingToLogicalSurface(ichMin, true); + int isloutLast = UnderlyingToLogicalSurface(ichLim - 1, false); + int mSol = 0; + int mEol = 0; + if (0 <= isloutFirst && isloutFirst < m_cslout) + mSol = m_prgslout[isloutFirst].MeasureSol(); + if (0 <= isloutLast && isloutLast < m_cslout) + mEol = m_prgslout[isloutLast].MeasureEol(); + float dxsSol = GrEngine::GrIFIMulDiv(mSol, m_xysEmSquare, m_mFontEmUnits); + float dxsEol = GrEngine::GrIFIMulDiv(mEol, m_xysEmSquare, m_mFontEmUnits); + xsWidth += dxsSol; + xsWidth += dxsEol; + + //RestoreFont(pgg); + + return xsWidth; +} + +/*---------------------------------------------------------------------------------------------- + Return the next reasonable breakpoint after ichStart resulting in range that fits in + the specified width. +----------------------------------------------------------------------------------------------*/ +int Segment::findNextBreakPoint(int ichStart, + LineBrk lbPref, LineBrk lbWorst, float dxMaxWidth, + float * pdxBreakWidth, bool fStartLine, bool fEndLine) +{ + ChkGrOutPtr(pdxBreakWidth); + + int ichBreak; + + // First make a rough estimation of how much will fit, using a binary chop approach. + int iginfStart = UnderlyingToPhysicalSurface(ichStart, !m_fWsRtl); + int iginfEnd = UnderlyingToPhysicalSurface(m_ichwMin + m_dichwLim - 1, m_fWsRtl); + int iginfLeft = min(iginfStart, iginfEnd); + int iginfRight= max(iginfStart, iginfEnd); + + int iginfMin = iginfLeft; + int iginfLim = iginfRight; + int ichFit; + if (m_fWsRtl) // right-to-left + { + float xRight = GlyphLeftEdge(iginfRight + 1); + float xLeftMost = xRight - dxMaxWidth; + while (GlyphLeftEdge(iginfLim) < xLeftMost) + { + if (iginfLim - iginfMin <= 1) + break; + int iginfMid = (iginfLim + iginfMin) >> 1; // divide by 2 + if (GlyphLeftEdge(iginfMid) < xLeftMost) + iginfLim = iginfMid; + else + iginfMin = iginfMid; + } + ichFit = PhysicalSurfaceToUnderlying(iginfLim, false); + } + else // left-to-right + { + float xLeft = GlyphLeftEdge(iginfLeft); + float xRightMost = dxMaxWidth - xLeft; // QUESTION: shouldn't this be +, not -? + iginfLim++; + while (GlyphLeftEdge(iginfLim) > xRightMost) + { + if (iginfLim - iginfMin <= 1) + break; + int iginfMid = (iginfLim + iginfMin) >> 1; // divide by 2 + if (GlyphLeftEdge(iginfMid) > xRightMost) + iginfLim = iginfMid; + else + iginfMin = iginfMid; + } + Assert(iginfLim <= m_cginf); + if (iginfLim >= m_cginf) iginfLim = m_cginf - 1; + ichFit = PhysicalSurfaceToUnderlying(iginfLim, false); + } + + // Breaking after ichFit should pretty much fit in the space. But make sure. + float dxWidthFit; + while (ichFit > ichStart) + { + dxWidthFit = getRangeWidth(ichStart, ichFit, fStartLine, fEndLine); + if (dxWidthFit <= dxMaxWidth) + break; + ichFit--; + } + + if (ichFit <= ichStart) + { + // Nothing will fit. + return ichStart - 1; + } + + // Now look for a good break. First look forward until the range doesn't fit anymore. + int ichTry = ichFit; + int nExtra = 2; + LineBrk lbBest = klbClipBreak; + int ichBestBreak = -1; + while (nExtra > 0 && ichTry < m_ichwMin + m_dichwLim) + { + float dxWTmp; + dxWTmp = getRangeWidth(ichStart, ichTry, fStartLine, fEndLine); + if (dxWTmp <= dxMaxWidth) + { + LineBrk lbTmp; + lbTmp = getBreakWeight(ichTry - 1, false); + if (lbTmp > 0 && lbTmp < lbWorst && max(lbTmp, lbPref) <= lbBest) + { + ichBestBreak = ichTry; + lbBest = max(lbTmp, lbPref); // any value better than lbPref is insignificant + } + else + { + lbTmp = getBreakWeight(ichTry - 1, true); + if (lbTmp < 0) + { + lbTmp = LineBrk(max(lbTmp * -1L, long(lbPref))); + if (lbTmp < lbWorst && lbTmp <= lbBest) + { + ichBestBreak = ichTry - 1; + lbBest = lbTmp; + } + } + } + } + else + { + // Didn't fit. But it is theoretically possible that actually adding another + // character will! Try a few more. + nExtra--; + } + ichTry++; + } + + if (ichBestBreak > -1) + { + if (ichTry >= m_ichwMin + m_dichwLim) + { + // Break at the end of the segment. + ichBreak = m_ichwMin + m_dichwLim; + *pdxBreakWidth = getRangeWidth(ichStart, m_dichwLim, fStartLine, fEndLine); + return ichBreak; + } + + if (lbBest <= lbPref) + { + // Found a reasonable break by looking forward; return it. + ichBreak = ichBestBreak;// + m_ichwMin; + *pdxBreakWidth = getRangeWidth(ichStart, ichBestBreak, fStartLine, fEndLine); + return ichBreak; + } + } + + // Didn't find a really good break point looking forward from our rough guess. + // Now look backward. + + ichTry = ichFit; + while (ichTry > ichStart) + { + LineBrk lbTmp; + lbTmp = getBreakWeight(ichTry - 1, false); + if (lbTmp > 0 && lbTmp < lbWorst && max(lbTmp, lbPref) < lbBest) + { + ichBestBreak = ichTry; + lbBest = max(lbTmp, lbPref); // any value better than lbPref is insignificant + } + else + { + lbTmp = getBreakWeight(ichTry - 1, true); + if (lbTmp < 0) + { + lbTmp = LineBrk(max(lbTmp * -1L, long(lbPref))); + if (lbTmp < lbWorst && lbTmp < lbBest) + { + ichBestBreak = ichTry - 1; + lbBest = lbTmp; + } + } + } + if (lbBest <= lbPref) + break; + + ichTry--; + } + + if (ichBestBreak > -1 && ichBestBreak > ichStart) + { + // Found a reasonable break by looking backward; return it. + ichBreak = ichBestBreak;// + m_ichwMin; + *pdxBreakWidth = getRangeWidth(ichStart, ichBestBreak, fStartLine, fEndLine); + return ichBreak; + } + + // No reasonable break. + return ichStart - 1; +} + +/*---------------------------------------------------------------------------------------------- + Private; used by findNextBreakPoint(). +----------------------------------------------------------------------------------------------*/ +float Segment::GlyphLeftEdge(int iginf) +{ + if (iginf >= m_cginf) + return m_prgginf[m_cginf - 1].origin() + m_prgginf[m_cginf - 1].advanceWidth(); +// return m_prgginf[m_cginf].origin() + m_prgginf[m_cginf].advanceWidth(); + else + return m_prgginf[iginf].origin(); +} + + +//:>******************************************************************************************** +//:> Other methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + Get the embedded string. For temporary use; AddRef is not automatically called. + Do not release unless you AddRef. +----------------------------------------------------------------------------------------------*/ +ITextSource * Segment::GetString() +{ + return m_pgts; +} + +/*---------------------------------------------------------------------------------------------- + Compute the ascent, height, width, and overhangs of the segment. + The ascent is the ascent of the font (distance from the baseline to the top) + plus any extra ascent height specified by the font. + The descent is distance from the baseline to the bottom of font, plus any extra descent + specified by the font (ExtraDescent). + The ascent overhang is the distance from the top of the font to the top of the highest + glyph, or zero if all the glyphs fit within the ascent. + The descent overhang is the distance from the bottom of the font to the bottom of the lowest + glyph, or zero if all the glyphs fit within the descent. + The height is the sum of the ascent and descent. +----------------------------------------------------------------------------------------------*/ +void Segment::ComputeDimensions() +{ + if (m_fEndLine) + m_dxsWidth = m_dxsVisibleWidth; // don't include trailing white space. + else + m_dxsWidth = m_dxsTotalWidth; + + if (m_dxsWidth == -1) + { + for (int iginf = 0; iginf < m_cginf; iginf++) + { + GlyphInfo * pginf = m_prgginf + iginf; + if (pginf->isSpace()) + continue; + m_dxsWidth = max(m_dxsWidth, (pginf->origin() + pginf->advanceWidth())); + } + } + + int dysNegFontDescent = (int)m_dysFontDescent * -1; + + // if there were no glyphs in the segment then there is sometimes no EngineImpl + if (!EngineImpl()) + { + Assert(m_cslout == 0); + Assert(m_cginf == 0); + m_dxsWidth = 0; + m_dysAscent = 0; // max(0.0, m_dysFontAscent); + m_dysHeight = 0; // max(0.0, m_dysAscent - dysNegFontDescent); + m_dysAscentOverhang = 0; + m_dysDescentOverhang = 0; + m_dxsLeftOverhang = 0; + m_dxsRightOverhang = 0; + return; + } + + // Calculate the extra ascent and descent. + int mXAscent = EngineImpl()->ExtraAscent(); + int mXDescent = EngineImpl()->ExtraDescent(); + + m_dysXAscent = GrEngine::GrIFIMulDiv(mXAscent, m_xysEmSquare, m_mFontEmUnits); + m_dysXDescent = GrEngine::GrIFIMulDiv(mXDescent, m_xysEmSquare, m_mFontEmUnits); + + m_dysAscent = m_dysFontAscent + m_dysXAscent; + float dysNegDescent = dysNegFontDescent - m_dysXDescent; // dysNegDescent < 0 + m_dysHeight = m_dysAscent - dysNegDescent; + + // Calculate the overhangs. + float dysVisAscent = m_dysAscent; + float dysNegVisDescent = dysNegDescent; + float dxsVisLeft = 0; + float dxsVisRight = m_dxsTotalWidth; + ComputeOverhangs(&dysVisAscent, &dysNegVisDescent, &dxsVisLeft, &dxsVisRight); + m_dysAscentOverhang = max(float(0), (dysVisAscent - m_dysAscent)); + m_dysDescentOverhang = max(float(0), (dysNegDescent - dysNegVisDescent)); + m_dxsLeftOverhang = min(float(0), dxsVisLeft); + m_dxsRightOverhang = max(float(0), dxsVisRight - m_dxsTotalWidth); + + m_dysOffset = EngineImpl()->VerticalOffset(); +} + +/*---------------------------------------------------------------------------------------------- + Compute the visible ascent and descent, based on how much the bounding box of any + glyph may extend beyond the ascent or descent of the font. + Also compute the left- and right-overhangs. +----------------------------------------------------------------------------------------------*/ +void Segment::ComputeOverhangs(float * pdysVisAscent, float * pdysNegVisDescent, + float * pdxsVisLeft, float * pdxsVisRight) +{ + for (int iginf = 0; iginf < m_cginf; iginf++) + { + *pdysVisAscent = max(*pdysVisAscent, m_prgginf[iginf].bb().top); + *pdysNegVisDescent = min(*pdysNegVisDescent, m_prgginf[iginf].bb().bottom); + + *pdxsVisLeft = min(*pdxsVisLeft, m_prgginf[iginf].bb().left); + *pdxsVisRight = max(*pdxsVisRight, m_prgginf[iginf].bb().right); + } +} + +/*---------------------------------------------------------------------------------------------- + Create the arrays to store the final output and association information. +----------------------------------------------------------------------------------------------*/ +void Segment::SetUpOutputArrays(Font * pfont, GrTableManager * ptman, + GrSlotStream * psstrmFinal, + int cchwInThisSeg, int csloutSurface, gid16 chwLB, + TrWsHandling twsh, bool fParaRtl, int nDirDepth, bool fEmpty) +{ + m_mFontEmUnits = EngineImpl()->GetFontEmUnits(); + + pfont->getFontMetrics(&m_dysFontAscent, &m_dysFontDescent, &m_xysEmSquare); +// m_xsDPI = (float)pfont->getDPIx(); +// m_ysDPI = (float)pfont->getDPIy(); + + // Note that storing both of these values is redundant; they should be the same. +// Assert(m_xysEmSquare == m_pixHeight); + + m_twsh = twsh; + m_fParaRtl = fParaRtl; + if (m_twsh == ktwshOnlyWs) + m_nDirDepth = (int)fParaRtl; + else if (fParaRtl && nDirDepth == 0) + m_nDirDepth = 2; + else + m_nDirDepth = nDirDepth; + + // Create association mappings and set up final versions of slots. + + Assert((psstrmFinal == NULL) == fEmpty); + + m_ichwAssocsMin = 0; + m_ichwAssocsLim = cchwInThisSeg; + + m_prgisloutBefore = new int[cchwInThisSeg]; + + m_prgisloutAfter = new int[cchwInThisSeg]; + + m_prgpvisloutAssocs = new std::vector<int> * [cchwInThisSeg]; + + m_prgisloutLigature = new int[cchwInThisSeg]; + + m_prgiComponent = new sdata8[cchwInThisSeg]; + + int cslot = 0; + //m_psstrm = psstrmFinal; // TODO: make a local variable + if (psstrmFinal) + cslot = psstrmFinal->FinalSegLim(); + else + Assert(fEmpty); + + float xsMin = 0; + int islot; + int islotMin = (psstrmFinal) ? psstrmFinal->IndexOffset() : 0; + for (islot = islotMin; islot < cslot; islot++) + xsMin = min(xsMin, psstrmFinal->SlotAt(islot)->XPosition()); + + // For right-to-left segments, the draw origin is at the left side of the visible + // portion of the text. So if necessary, scoot everything left so that the invisible + // trailing white-space is to the left of the draw position. + Assert(m_dxsTotalWidth > -1); + Assert(m_dxsVisibleWidth > -1); + float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0; + for (islot = islotMin; ((m_nDirDepth % 2) && (islot < cslot)); islot++) + { + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + // int islout = islot - islotMin; + if (pslot->GlyphID() == chwLB) + continue; // skip over linebreak markers + if (m_nDirDepth % 2) // redundant test + { + // RTL will have descending neg values, adjust to be positive. Also make sure + // invisible trailing white space is to the left of the draw origin. + // Review: will changing this value have side affects? + pslot->SetXPos(pslot->XPosition() - xsMin - dxsInvisible); + } + } + + Assert(kPosInfinity > 0); + Assert(kNegInfinity < 0); + for (int ichw = 0; ichw < cchwInThisSeg; ++ichw) + { + m_prgisloutBefore[ichw] = kPosInfinity; + m_prgisloutAfter[ichw] = kNegInfinity; + m_prgpvisloutAssocs[ichw] = new std::vector<int>; + m_prgisloutLigature[ichw] = kNegInfinity; + m_prgiComponent[ichw] = 0; + } + + m_cslout = csloutSurface; + if (ptman->NumUserDefn() > 0 && ptman->NumCompPerLig() > 0) + { + int x; x = 3; + } + +// m_cnUserDefn = ptman->NumUserDefn(); + m_cnCompPerLig = ptman->NumCompPerLig(); +// m_cnFeat = ptman->NumFeat(); + // Normal buffers, plus: + // - underlying indices of ligature components + // - map from used components to defined components +// int cnExtraPerSlot = m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat + (m_cnCompPerLig * 2); + + // We don't need to store the user-defined slot attributes or the features in the segment itself. + // What we need is: (1) component.ref attr settings, (2) slot-attribute indices, + // (3) underlying indices of ligature components, and (4) map from used components + // to defined components + // 18Jul08 - it looks like all we need are underlying chars for ligature components and + // map from components to defined components. + int cnExtraPerSlot = m_cnCompPerLig * 2; + m_prgslout = new GrSlotOutput[m_cslout]; + m_prgnSlotVarLenBuf = new u_intslot[m_cslout * cnExtraPerSlot]; + + m_isloutVisLim = 0; + if (psstrmFinal) + { + for (islot = islotMin; islot < psstrmFinal->FinalSegLim(); ++islot) + { + int isloutRel = islot - islotMin; + + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + pslot->SetPosPassIndex(isloutRel, false); + pslot->EnsureCacheForOutput(ptman); + GrSlotOutput * pslout = OutputSlot(isloutRel); + pslout->SetBufferPtr(m_prgnSlotVarLenBuf + (isloutRel * cnExtraPerSlot)); + pslout->InitializeOutputFrom(pslot); + pslout->SetBeforeAssoc(pslot->BeforeAssoc()); + pslout->SetAfterAssoc(pslot->AfterAssoc()); + if (pslot->HasComponents()) + pslot->SetComponentRefsFor(pslout); + Assert(pslot->HasComponents() == (pslout->NumberOfComponents() > 0)); + //if (pslot->HasClusterMembers()) + //{ + //Assert(pslot->HasClusterMembers() || pslot->ClusterRootOffset() == 0); + pslout->SetClusterXOffset(pslot->ClusterRootOffset() * -1); + pslout->SetClusterAdvance(pslot->ClusterAdvWidthFrom(0)); + + //} + + if (!ptman->IsWhiteSpace(pslot)) + m_isloutVisLim = islot + 1; + } + } + + // Set up the clusters. + for (islot = islotMin; islot < m_cslout; islot++) + { + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + int isloutAdj = islot - islotMin; + int isloutBaseIndex = pslot->Base(psstrmFinal)->PosPassIndex(); + if (!pslot->IsBase()) + { + OutputSlot(isloutBaseIndex)->AddClusterMember(isloutBaseIndex, pslot->PosPassIndex()); + OutputSlot(isloutAdj)->SetClusterBase(isloutBaseIndex); + } + else if (pslot->HasClusterMembers() && pslot->IsBase()) + { + Assert(isloutBaseIndex == islot - islotMin); + OutputSlot(isloutAdj)->SetClusterBase(isloutBaseIndex); + } + } + + ///AssertValidClusters(psstrmFinal); + +#ifdef OLD_TEST_STUFF + if (ptman->GlyphTable() == NULL) + return; // test procedures +#endif // OLD_TEST_STUFF + + // CalcPositionsUpTo() called on final pass already from Table Mgr + + // Final output for draw routines. + + SetUpGlyphInfo(ptman, psstrmFinal, chwLB, nDirDepth, islotMin, cslot); + + //SetUpOutputArraysPlatform(ptman, chwLB, nDirDepth, islotMin, cslot); + +} + +/*---------------------------------------------------------------------------------------------- + Set up the data structures that represent the actual rendered glyphs for the new segment. +----------------------------------------------------------------------------------------------*/ +void Segment::SetUpGlyphInfo(GrTableManager * ptman, GrSlotStream * psstrmFinal, + gid16 chwLB, int nDirDepth, int islotMin, int cslot) +{ + //int paraDirLevel = (ptman->State()->ParaRightToLeft()) ? 1 : 0; + + m_cginf = 0; + + int islot; + for (islot = islotMin; islot < cslot; islot++) + { + if (psstrmFinal->SlotAt(islot)->GlyphID() != chwLB) + { + m_cginf++; + } + } + + // For right-to-left segments, the draw origin will be at the left side of the visible + // portion of the text. So if necessary, scoot everything left so that the invisible + // trailing white-space is to the left of the draw position. + Assert(m_dxsTotalWidth > -1); + Assert(m_dxsVisibleWidth > -1); + //float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0; + + m_prgginf = new GlyphInfo [m_cginf]; + + m_isloutGinf0 = -1; + int iginf = 0; + for (int islot = islotMin; islot < cslot; islot++) + { + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + + if (pslot->GlyphID() == chwLB) + { + continue; // skip over linebreak markers + } + + int islout = islot - islotMin; + GrSlotOutput * pslout = OutputSlot(islout); + + if (m_isloutGinf0 == -1) + m_isloutGinf0 = islout; + + m_prgginf[iginf].m_pslout = pslout; + m_prgginf[iginf].m_islout = islout; + m_prgginf[iginf].m_pseg = this; + + // Fill in stuff in the output slot that is needed by the GlyphInfo object. + ////pslout->m_xsAdvanceX = pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth); // wrong + pslout->m_xsAdvanceX = ptman->EmToLogUnits(pslot->AdvanceX(ptman)); + //pslout->m_ysAdvanceY = ptman->EmToLogUnits(pslot->AdvanceY(ptman)); + //pslout->m_rectBB.top + // = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbTop); + //pslout->m_rectBB.bottom + // = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbBottom); + //pslout->m_rectBB.left + // = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbLeft); + //if (pslot->IsSpace(ptman)) + // pslout->m_rectBB.right + // = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth); + //else + // pslout->m_rectBB.right + // = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbRight); + + iginf++; + } + + if (cslot - islotMin == 0) + m_isloutGinf0 = 0; + if (m_isloutGinf0 == -1) + { + Assert(m_cginf == 0); + m_isloutGinf0 = ((OutputSlot(0)->IsInitialLineBreak()) ? 1 : 0); + } + + Assert(m_isloutGinf0 == 0 || m_isloutGinf0 == 1); +} + +/* +// OLD VERSION +void Segment::SetUpGlyphInfo(GrTableManager * ptman, GrSlotStream * psstrmFinal, gid16 chwLB, int nDirDepth, + int islotMin, int cslot) +{ + int paraDirLevel = (ptman->State()->ParaRightToLeft()) ? 1 : 0; + + m_cginf = 0; + int islot; + for (islot = islotMin; islot < cslot; islot++) + { + if (psstrmFinal->SlotAt(islot)->GlyphID() != chwLB) + m_cginf++; + } + + // For right-to-left segments, the draw origin will be at the left side of the visible + // portion of the text. So if necessary, scoot everything left so that the invisible + // trailing white-space is to the left of the draw position. + Assert(m_dxsTotalWidth > -1); + Assert(m_dxsVisibleWidth > -1); + float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0; + + m_prgginf = new GlyphInfo [m_cginf]; + + // Fill in glyph information structures. + m_isloutGinf0 = -1; + for (islot = islotMin; islot < cslot; islot++) + { + GrSlotState * pslot = psstrmFinal->SlotAt(islot); + + int islout = islot - islotMin; + if (pslot->GlyphID() == chwLB) + { + continue; // skip over linebreak markers + } + if (m_isloutGinf0 == -1) + m_isloutGinf0 = islout; + + GrSlotOutput * pslout = OutputSlot(islout); + + int iginf = islout - m_isloutGinf0; + GlyphInfo & ginf = m_prgginf[iginf]; + + ginf.glyphID = pslot->ActualGlyphForOutput(ptman); + ginf.pseudoGlyphID = pslot->GlyphID(); + if (ginf.glyphID == ginf.pseudoGlyphID) + ginf.pseudoGlyphID = 0; + + ginf.isSpace = pslot->IsSpace(ptman); + + ginf.origin = pslot->XPosition(); + ginf.yOffset = pslot->YPosition(); + + ginf.bbLeft = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbLeft); + ginf.bbTop = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbTop); + if (ginf.isSpace) + { + ginf.bbRight = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth); + } + else + { + ginf.bbRight = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbRight); + } + ginf.bbBottom = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbBottom); + + ginf.advanceWidth = pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth); + ginf.advanceHeight = 0; + + if (pslot->AttachTo() == 0) // not attached + ginf.attachedTo = -1; + else + ginf.attachedTo = pslot->AttachTo() + islot; + + ginf.directionality = byte(pslot->Directionality()); + if (pslot->DirLevel() == -1) + ginf.directionLevel = paraDirLevel; + else + ginf.directionLevel = byte(pslot->DirLevel()); + + //ginf.firstChar = LogicalSurfaceToUnderlying(islout, true); + //ginf.lastChar = LogicalSurfaceToUnderlying(islout, false); + + if (pslot->HasComponents()) + { + SetUpLigInfo(ptman, ginf, pslout); + } + else + { + ginf.numberOfComponents = 0; + ginf.components = NULL; + } + + ginf.insertBefore = pslot->InsertBefore(); + + ginf.maxStretch[0] = ptman->EmToLogUnits(pslot->JStretch()); + ginf.maxShrink[0] = ptman->EmToLogUnits(pslot->JShrink()); + ginf.stretchStep[0]= ptman->EmToLogUnits(pslot->JStep()); + ginf.justWidth[0] = ptman->EmToLogUnits(pslot->JWidth()); + ginf.justWeight[0] = pslot->JWeight(); + + ginf.measureStartOfLine = pslout->MeasureSolLogUnits(); + ginf.measureEndOfLine = pslout->MeasureEolLogUnits(); + } +} +*/ + +/*---------------------------------------------------------------------------------------------- + Set up the data structures that represent the components of a ligature glyph. +----------------------------------------------------------------------------------------------*/ +/* +void Segment::SetUpLigInfo(GrTableManager * ptman, GlyphInfo & ginf, GrSlotOutput * pslout) +{ + GrGlyphTable * pgtbl = ptman->GlyphTable(); + + int ccomp = pslout->NumberOfComponents(); + ginf.numberOfComponents = ccomp; + ginf.components = new ComponentBox[ccomp]; + for (int icomp = 0; icomp < ccomp; icomp++) + { + ComponentBox & cb = ginf.components[icomp]; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + float xsLeft, xsRight, ysTop, ysBottom; + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_mFontEmUnits, m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom, false); + + cb.firstChar = pslout->UnderlyingComponent(icomp); + cb.lastChar = cb.firstChar; // TODO: is this supported? + cb.left = xsLeft; + cb.right = xsRight; + cb.top = ysTop; + cb.bottom = ysBottom; + } +} +*/ + + +// --------- OBSOLETE - replaced by GrTableManager::AdjustAssocsForOverlaps() --------------- +#if 0 + +/*---------------------------------------------------------------------------------------------- + Now that this segment is finished and has its associations set, handle any overlaps in + the associations between the previous segment and this one. + + Review: Think about whether we need to distinguish between before==kPosInfinity meaning + that the glyph is in the next segment vs. there is no association at all. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::AdjustForOverlapsWithPrevSeg() +{ + if (m_psegPrev) + { + AdjustBeforeArrayFromPrevSeg(); + m_psegPrev->AdjustAfterArrayFromNextSeg(); + } +} + +/*---------------------------------------------------------------------------------------------- + For any characters that are officially in this segment but are rendered in + the previous segment, initialize this segment's before array appropriately. Specifically, + set this segment's before value to something invalid--negative infinity, which will + never be overwritten. + OBSOLETE - replaced by AdjustAfterArrayFromNextSeg +----------------------------------------------------------------------------------------------*/ +void Segment::InitBeforeArrayFromPrevSeg() +{ + Assert(m_psegPrev); + + int ichwPrevMin = m_psegPrev->m_ichwMin; + int ichwPrevLim = m_psegPrev->m_ichwLim; +// int ichwPrevContextLim = +// m_psegPrev->m_ichwAssocsLim - m_psegPrev->m_ichwAssocsMin + m_psegPrev->m_ichwMin; + int cchwPrevAssocsMin = m_psegPrev->m_ichwAssocsMin; + int cchwPrevAssocsLim = m_psegPrev->m_ichwAssocsLim; + + // Loop over associations appended to the end of the previous segment; + // ichwPrev and ichwThis are relative to the start of their respective segments. + for (int ichwPrev = ichwPrevLim - ichwPrevMin; ichwPrev < cchwPrevAssocsLim; ++ichwPrev) + { + int ichwThis = ichwPrev - (ichwPrevLim - ichwPrevMin); // relative to start of this seg + if (m_psegPrev->m_prgisloutBefore[ichwPrev - cchwPrevAssocsMin] < kPosInfinity) + // The "before" value is in the previous segment, so make this segment's + // before value invalid. + m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity; + } +} + +/*---------------------------------------------------------------------------------------------- + For any associations that overlap between the previous segment and this, adjust + this segment's before array appropriately. Specifically, for any characters that + are rendered in both, set this segment's before value to something invalid. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::AdjustBeforeArrayFromPrevSeg() +{ + Assert(m_psegPrev); + + int ichwPrevMin = m_psegPrev->m_ichwMin; + int ichwPrevLim = m_psegPrev->m_ichwLim; + int ichwPrevAssocsMin = m_psegPrev->m_ichwAssocsMin; + int ichwPrevAssocsLim = m_psegPrev->m_ichwAssocsLim; + + // Loop over associations appended to the end of the previous segment + // (ie, rendered in the previous segment but officially in this one); + // ichwPrev and ichwThis are relative to the start of their respective segments. + for (int ichwPrev = ichwPrevLim - ichwPrevMin; ichwPrev < ichwPrevAssocsLim; ++ichwPrev) + { + int ichwThis = ichwPrev - (ichwPrevLim - ichwPrevMin); // relative to start of this seg + if (m_psegPrev->m_prgisloutBefore[ichwPrev - ichwPrevAssocsMin] < kPosInfinity) + // The "before" value is in the previous segment, so make this segment's + // before value invalid. + m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity; + } + // Loop over associations prepended to the start of this segment + // (ie, rendered in this segment but officially in the previous one); + // ichwPrev and ichwThis are relative to the start of their respective segments. + for (int ichwThis = m_ichwAssocsMin; ichwThis < 0; ++ichwThis) + { + int ichwPrev = ichwThis + (ichwPrevLim - ichwPrevMin); + if (m_psegPrev->m_prgisloutBefore[ichwPrev - ichwPrevAssocsMin] < kPosInfinity) + // The "before" value is in the previous segment, so make this segment's + // before value invalid. + m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity; + } +} + +/*---------------------------------------------------------------------------------------------- + For any associations that overlap between this segment and the next, adjust + this segment's after array appropriately. Specifically, for any characters that + are rendered in both, set this segment's after value to something invalid. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::AdjustAfterArrayFromNextSeg() +{ + Assert(m_psegNext); + + int ichwNextMin = m_psegNext->m_ichwMin; + int ichwNextContextMin = ichwNextMin + m_psegNext->m_ichwAssocsMin; + int ichwNextAssocsMin = m_psegNext->m_ichwAssocsMin; + + // Loop over associations prepended to the beginning of the following segment + // (ie, rendered in the next segment but officially in this one); + // ichwThis and ichwNext are relative to the start of their respective segments. + for (int ichwNext = cchwNextAssocsMin; ichwNext < 0; ++ichwNext) + { + int ichwThis = ichwNext + (m_ichwLim - m_ichwMin); + if (m_psegNext->m_prgisloutAfter[ichwNext - ichwNextAssocsMin] > kNegInfinity) + // The "after" value is in the next segment, so make this segment's + // after value invalid. + m_prgisloutAfter[ichwThis - m_ichwAssocsMin] = kPosInfinity; + } + + // Loop over associations appended to the end of this segment + // (ie, rendered in this segment but officially in the next one); + // ichwThis and ichwNext are relative to the start of their respective segments. + for (int ichwThis = m_ichwLim - m_ichwMin; ichwThis < m_cchwAssocsLim; ++ichwThis) + { + int ichwNext = ichwThis - (m_ichwLim - m_ichwMin); + if (m_psegNext->m_prgisloutAfter[ichwNext - ichwNextAssocsMin] > kNegInfinity) + // The "after" value is in the next segment, so make this segment's + // after value invalid. + m_prgisloutAfter[ichwThis - m_ichwAssocsMin] = kPosInfinity; + } +} + +#endif // -------------------------- END OF OBSOLETE CODE ---------------------------------- + + +/*---------------------------------------------------------------------------------------------- + Generate a list of all the glyphs that are attached to the base with the given index. + Note that we want to generate glyph indices, not slot indices. + + @param disloutCluster - indicates how far on either side of the base to look. +----------------------------------------------------------------------------------------------*/ +void Segment::ClusterMembersForGlyph(int isloutBase, int disloutCluster, + std::vector<int> & visloutRet) +{ + for (int islout = max(0, isloutBase - disloutCluster); + islout < min(m_cslout, isloutBase + disloutCluster + 1); + islout++) + { + if (islout == isloutBase) + continue; // don't include the base itself + GrSlotOutput * pslout = m_prgslout + islout; + if (pslout->ClusterBase() == isloutBase) + { + visloutRet.push_back(islout); + } + } +} + +/*---------------------------------------------------------------------------------------------- + Record an underlying-to-surface association mapping, based on the fact that there is + a corresponding surface-to-underlying association in the streams. + + @param ichwUnder - character index relative to the official beginning of the segment + @param islotSurface - surface glyph it maps to +----------------------------------------------------------------------------------------------*/ +void Segment::RecordSurfaceAssoc(int ichwUnder, int islotSurface, int nDir) +{ + // If we are recording an association across the line boundary, make sure there + // is space for it. + EnsureSpaceAtLineBoundaries(ichwUnder); + + // For left-to-right chars, the before-value is the minimum of the previous + // and new values, and the after-value is the maximum. + if (nDir % 2 == 0) + { + // left-to-right + m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] = + min(m_prgisloutBefore[ichwUnder - m_ichwAssocsMin], islotSurface); + m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin] = + max(m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin], islotSurface); + } + else + { + // right-to-left: the before-value is the max and the after-value is the min + m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] = + max(m_prgisloutBefore[ichwUnder - m_ichwAssocsMin], islotSurface); + m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin] = + min(m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin], islotSurface); + } + + m_prgpvisloutAssocs[ichwUnder - m_ichwAssocsMin]->push_back(islotSurface); +} + +/*---------------------------------------------------------------------------------------------- + Record an underlying-to-surface ligature mapping, based on the fact that there is + a component.ref attribute in the surface stream. Note that if there was a previous + ligature value there, it will be overwritten. + + @param ichwUnder - character index relative to the official beginning of the segment + @param islotSurface - surface glyph it maps to + @param iComponent - which component this glyph represents +----------------------------------------------------------------------------------------------*/ +void Segment::RecordLigature(int ichwUnder, int islotSurface, int iComponent) +{ + // If we are recording a mapping across the line boundary, make sure there + // is space for it. + EnsureSpaceAtLineBoundaries(ichwUnder); + + Assert(m_prgisloutLigature[ichwUnder - m_ichwAssocsMin] == kNegInfinity); + + m_prgisloutLigature[ichwUnder - m_ichwAssocsMin] = islotSurface; + Assert(iComponent < kMaxComponentsPerGlyph); + m_prgiComponent[ ichwUnder - m_ichwAssocsMin] = sdata8(iComponent); +} + +/*---------------------------------------------------------------------------------------------- + The given slot is one that is rendered in the previous segment. + If it is associated with a character officially in this segment, mark the + before-association kNegInfinity. + + @param ichwUnder - character index relative to the official beginning of the segment + @param islot - processed glyph it maps to +----------------------------------------------------------------------------------------------*/ +void Segment::MarkSlotInPrevSeg(int ichwUnder, int islot) +{ + if (ichwUnder >= m_ichwAssocsMin) + m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] = kNegInfinity; +} + +/*---------------------------------------------------------------------------------------------- + The given slot is one that is rendered in the following segment. + If it is associated with a character officially in this segment, mark the + after-association kPosInfinity. + + @param ichwUnder - character index relative to the official beginning of the segment + @param islot - processed glyph it maps to +----------------------------------------------------------------------------------------------*/ +void Segment::MarkSlotInNextSeg(int ichwUnder, int islot) +{ + if (ichwUnder < m_ichwAssocsLim) + m_prgisloutAfter[ichwUnder - m_ichwAssocsMin] = kPosInfinity; +} + +/*---------------------------------------------------------------------------------------------- + If we are recording an association across the line boundary (in either direction), + make sure there is space for it. This involves adding space to the beginning or + the end of the association arrays, and adjusting the min and lim indicators. +----------------------------------------------------------------------------------------------*/ +void Segment::EnsureSpaceAtLineBoundaries(int ichwUnder) +{ + int cchwNewMin = min(ichwUnder, m_ichwAssocsMin); + int cchwNewLim = max(ichwUnder+1, m_ichwAssocsLim); + if (cchwNewMin < m_ichwAssocsMin || cchwNewLim > m_ichwAssocsLim) + { + // Make space either at the beginning or the end of the arrays. + int cchwPreAdd = m_ichwAssocsMin - cchwNewMin; + int cchwPostAdd = cchwNewLim - m_ichwAssocsLim; + + int * prgisloutTmp = m_prgisloutBefore; + m_prgisloutBefore = new int[cchwNewLim - cchwNewMin]; + std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin), + m_prgisloutBefore + cchwPreAdd); + delete[] prgisloutTmp; + + prgisloutTmp = m_prgisloutAfter; + m_prgisloutAfter = new int[cchwNewLim - cchwNewMin]; + std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin), + m_prgisloutAfter + cchwPreAdd); + delete[] prgisloutTmp; + + std::vector<int> ** ppvisloutTmp = m_prgpvisloutAssocs; + m_prgpvisloutAssocs = new std::vector<int> * [cchwNewLim - cchwNewMin]; + std::swap_ranges(m_prgpvisloutAssocs + cchwPreAdd, + m_prgpvisloutAssocs + cchwPreAdd + + (m_ichwAssocsLim - m_ichwAssocsMin), ppvisloutTmp); + delete[] ppvisloutTmp; + + prgisloutTmp = m_prgisloutLigature; + m_prgisloutLigature = new int[cchwNewLim - cchwNewMin]; + std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin), + m_prgisloutLigature + cchwPreAdd); + delete[] prgisloutTmp; + + sdata8 * prgiCompTmp = m_prgiComponent; + m_prgiComponent = new sdata8[cchwNewLim - cchwNewMin]; + std::copy(prgiCompTmp, prgiCompTmp + (m_ichwAssocsLim - m_ichwAssocsMin), + m_prgiComponent + cchwPreAdd); + delete[] prgiCompTmp; + + // Initialize new slots. + int i; + for (i = 0; i < cchwPreAdd; ++i) + { + m_prgisloutBefore[i] = kPosInfinity; + m_prgisloutAfter[i] = kNegInfinity; + m_prgpvisloutAssocs[i] = new std::vector<int>; + m_prgisloutLigature[i] = kNegInfinity; + m_prgiComponent[i] = 0; + } + for (i = m_ichwAssocsLim - m_ichwAssocsMin + cchwPreAdd; + i < m_ichwAssocsLim - m_ichwAssocsMin + cchwPreAdd + cchwPostAdd; + ++i) + { + m_prgisloutBefore[i] = kPosInfinity; + m_prgisloutAfter[i] = kNegInfinity; + m_prgpvisloutAssocs[i] = new std::vector<int>; + m_prgisloutLigature[i] = kNegInfinity; + m_prgiComponent[i] = 0; + } + m_ichwAssocsMin = cchwNewMin; + m_ichwAssocsLim = cchwNewLim; + } +} + +/*---------------------------------------------------------------------------------------------- + Return the given output slot. +----------------------------------------------------------------------------------------------*/ +GrSlotOutput * Segment::OutputSlot(int islout) +{ + return m_prgslout + islout; +} + +/*---------------------------------------------------------------------------------------------- + Set the rendering engine to the current one. +----------------------------------------------------------------------------------------------*/ +void Segment::SetEngine(GrEngine * pgreng) +{ + //if (m_preneng) + // m_preneng->DecRefCount(); + m_preneng = pgreng; + //if (m_preneng) + // m_preneng->IncRefCount(); +} + +/*---------------------------------------------------------------------------------------------- + Set the font. +----------------------------------------------------------------------------------------------*/ +void Segment::SetFont(Font * pfont) +{ + m_pfont = pfont->copyThis(); + +// m_fBold = pfont->bold(); +// m_fItalic = pfont->italic(); + // Note that we store the character height (which does not include the internal leading), + // not the actual font height, ie, ascent + descent. This is what is used in the LOGFONT. + pfont->getFontMetrics(NULL, NULL, &m_xysEmSquare); // m_xysEmSquare = m_pixHeight +} + +/*---------------------------------------------------------------------------------------------- + Switch the font out. + ??? Do we need to change the member variables too? +----------------------------------------------------------------------------------------------*/ +void Segment::SwitchFont(Font * pfont) +{ + m_pfont = pfont; +} + +/*---------------------------------------------------------------------------------------------- + Return the engine implementation if it is a proper GrEngine, or NULL otherwise. +----------------------------------------------------------------------------------------------*/ +GrEngine * Segment::EngineImpl() +{ + return m_preneng; +} + +/*---------------------------------------------------------------------------------------------- + Set the justification agent, in case this segment will ever need to be stretched + or shrunk. +----------------------------------------------------------------------------------------------*/ +void Segment::SetJustifier(IGrJustifier * pgjus) +{ + m_pgjus = pgjus; + //if (pgjus) + // pgjus->JustifierObject(&m_pgjus); + //else + // m_pgjus = NULL; +} + +/*---------------------------------------------------------------------------------------------- + Set the graphics object to use the base font. +----------------------------------------------------------------------------------------------*/ +//void Segment::SetToBaseFont(IGrGraphics * pgg) +//{ +// if (m_fUseSepBase) +// EngineImpl()->SwitchGraphicsFont(pgg, true); +//} + +/*---------------------------------------------------------------------------------------------- + Restore the graphics object to use the font that it originally had when passed in to + the interface method. +----------------------------------------------------------------------------------------------*/ +//void Segment::RestoreFont(IGrGraphics * pgg) +//{ +// if (m_fUseSepBase) +// EngineImpl()->SwitchGraphicsFont(pgg, false); +//} + +/*---------------------------------------------------------------------------------------------- + Given a logical surface location, return the underlying position (relative to the + beginning of the string). NOTE that we are returning the index of the closest charater + (plus an indication of which side the click was on), not a position between characters. + + @param islout - output slot + @param xsOffset - x coordinate of physical click, relative to left of glyph; + kPosInfinity means all the way to the right; + kNegInfinity means all the way to the left + @param ysOffset - y coordinate of physical click, relative to TOP of segment (NOT + upwards from baseline); kPosInfinity means all the way at + the top; kNegInfinity means all the way at the bottom + @param dxsGlyphWidth - width of glyph clicked on, or 0 if irrelevant + @param dysGlyphHeight - actually line height -- CURRENTLY NOT USED + @param pfAfter - return true if they clicked on trailing side; possibly NULL +----------------------------------------------------------------------------------------------*/ +int Segment::LogicalSurfaceToUnderlying(int islout, float xsOffset, float ysClick, + float dxsGlyphWidth, float dysGlyphHeight, bool * pfAfter) +{ + Assert(islout >= 0); + Assert(islout < m_cslout); + + GrSlotOutput * pslout = OutputSlot(islout); + bool fGlyphRtl = SlotIsRightToLeft(pslout); + + int dichw; + bool fAfter, fRight; + + float ysForGlyph = pslout->YPosition(); + + float dysFontAscent = m_dysFontAscent; + ////GrResult res = GetFontAscentSourceUnits(pgg, &dysFontAscent); + ////if (ResultFailed(res)) + //// THROW(WARN(res)); + dysFontAscent += m_dysXAscent; + + float dysOffset = ysClick - (m_dysAscent - dysFontAscent); // relative to top of 0-baseline text + + // If the slot has components, see if the offset falls within one of them. + + if (pslout->NumberOfComponents() > 0) + { + GrEngine * pgreng = EngineImpl(); + if (!pgreng) + goto LNotLigature; + + GrGlyphTable * pgtbl = pgreng->GlyphTable(); + + for (int icomp = 0; icomp < pslout->NumberOfComponents(); icomp++) + { + float xsLeft, xsRight, ysTop, ysBottom; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + if (!pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_mFontEmUnits, m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom)) + { + continue; // component not defined + } + Assert(xsLeft <= xsRight); + Assert(ysTop <= ysBottom); + if (xsOffset < xsLeft || xsOffset > xsRight) + continue; + if ((dysOffset - ysForGlyph) < ysTop || (dysOffset - ysForGlyph) > ysBottom) + continue; + + // Click was within the component's box. + + fRight = (xsOffset - xsLeft > xsRight - xsOffset); + fAfter = (fGlyphRtl) ? !fRight : fRight; + if (pfAfter) + *pfAfter = fAfter; + + if (fAfter) + dichw = pslout->LastUnderlyingComponent(icomp); + else + dichw = pslout->FirstUnderlyingComponent(icomp); + Assert(m_ichwMin + dichw >= 0); + Assert(dichw < m_dichwLim); + + Assert(GrCharStream::AtUnicodeCharBoundary(m_pgts, m_ichwMin + dichw)); + + return m_ichwMin + dichw; + } + } + +LNotLigature: + + // No relevant ligature component. + // If the x-offset is less than half way across, + // answer the before-association; otherwise answer the after-association. + + if (xsOffset == kPosInfinity) + fRight = true; + else if (xsOffset == kNegInfinity) + fRight = false; + else + fRight = (xsOffset > (dxsGlyphWidth / 2)); + + fAfter = (fGlyphRtl) ? !fRight : fRight; + + int diCharAdj = 0; + if (fAfter) + { + dichw = pslout->AfterAssoc(); + if (pfAfter) + *pfAfter = true; + // The following should not be necessary because of the way associations are set up: + //while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichwBase + dichw + diCharAdj)) + // diCharAdj++; + } + else + { + dichw = pslout->BeforeAssoc(); + if (pfAfter) + *pfAfter = false; + // The following should not be necessary because of the way associations are set up: + //while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichwBase + dichw + diCharAdj)) + // diCharAdj--; + } + + if (dichw == kNegInfinity || dichw == kPosInfinity) + return dichw; + + // This should be true because of the way the associations are set up: + Assert(GrCharStream::AtUnicodeCharBoundary(m_pgts, m_ichwMin + dichw)); // + diCharAdj)); + + return m_ichwMin + dichw + diCharAdj; +} + +/*--------------------------------------------------------------------------------------------*/ + +int Segment::LogicalSurfaceToUnderlying(int islout, bool fBefore) +{ + if (fBefore) + return LogicalSurfaceToUnderlying(islout, kNegInfFloat, kNegInfFloat); + else + return LogicalSurfaceToUnderlying(islout, kPosInfFloat, kPosInfFloat); +} + +/*---------------------------------------------------------------------------------------------- + Given a physical surface location (an index into the glyph boxes), + return the underlying position, relative to the beginning of the string. + + @param igbb - index of glyph + @param xsOffset - relative to left side of glyph + @param ysOffset - downwards from top of segment (not upwards from baseline) +----------------------------------------------------------------------------------------------*/ +int Segment::PhysicalSurfaceToUnderlying( + int iginf, float xsOffset, float ysOffset, + float dxsGlyphWidth, float dysGlyphHeight, bool * pfAfter) +{ + int islout = iginf + m_isloutGinf0; + return LogicalSurfaceToUnderlying(islout, xsOffset, ysOffset, + dxsGlyphWidth, dysGlyphHeight, pfAfter); +} + +/*--------------------------------------------------------------------------------------------*/ + +int Segment::PhysicalSurfaceToUnderlying(int iginf, bool fBefore) +{ + int islout = iginf + m_isloutGinf0; + bool fGlyphRtl = SlotIsRightToLeft(OutputSlot(islout)); + if (fBefore == fGlyphRtl) + return PhysicalSurfaceToUnderlying(iginf, kPosInfFloat, kPosInfFloat); + else + return PhysicalSurfaceToUnderlying(iginf, kNegInfFloat, kNegInfFloat); +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying position (relative to the beginning of the string), + return the logical surface location (ignoring right-to-left). + NOTE that ichw indicates a CHARACTER, not a location between characters. So ichw = 3, + fBefore = false indicates a selection between characters 3 and 4. + For surrogates, it indicates the FIRST of the pair of 16-bit chars. +----------------------------------------------------------------------------------------------*/ +int Segment::UnderlyingToLogicalSurface(int ichw, bool fBefore) +{ + int ichwSegOffset = ichw - m_ichwMin; + + if (ichwSegOffset < m_ichwAssocsMin) + return kNegInfinity; // probably rendered in previous segment + + + // if the buffers aren't even allocated, then it probably means there + // wasn't room for any glyphs, so the following segment is most likely +// else if (ichwSegOffset >= m_ichwAssocsLim) + else if (ichwSegOffset >= m_ichwAssocsLim || !m_prgisloutBefore || !m_prgisloutAfter) + return kPosInfinity; // probably rendered in following segment + + else if (fBefore) + { + int isloutRet; + int ichw = ichwSegOffset; + // If no association has been made, loop forward to the next slot + // we are before. As a last resort, answer kPosInfinity, meaning we + // aren't before anything. + do + { + isloutRet = m_prgisloutBefore[ichw - m_ichwAssocsMin]; + do { ++ichw; } + while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichw)); + } while (isloutRet == kPosInfinity && ichw < m_ichwAssocsLim); + return isloutRet; + } + else + { + int isloutRet; + int ichw = ichwSegOffset; + // If no association has been made, loop backward to the previous slot + // we are after. As a last resort, answer kNegInfinity, meaning we + // aren't after anything. + do + { + isloutRet = m_prgisloutAfter[ichw - m_ichwAssocsMin]; + do { --ichw; } + while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichw)); + } while (isloutRet == kNegInfinity && ichw >= 0); + return isloutRet; + } + Assert(false); // should never reach here +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying position (relative to the beginning of the string), + return the physical surface location, the index into m_prgginf. +----------------------------------------------------------------------------------------------*/ +int Segment::UnderlyingToPhysicalSurface(int ichw, bool fBefore) +{ + int islout = UnderlyingToLogicalSurface(ichw, fBefore); + return LogicalToPhysicalSurface(islout); +} + +/*---------------------------------------------------------------------------------------------- + Given a logical position (index into output slots), return the physical position, + the index into the list of glyphs (m_prgginf). +----------------------------------------------------------------------------------------------*/ +int Segment::LogicalToPhysicalSurface(int islout) +{ + if (islout == kNegInfinity || islout == kPosInfinity) + return islout; + + Assert(m_prgginf[islout - m_isloutGinf0].logicalIndex() == (unsigned)(islout - m_isloutGinf0)); + + return islout - m_isloutGinf0; +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying position (relative to the beginning of the string), + return the physical surface locations of all the associated surface glyphs. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +#if 0 +void Segment::UnderlyingToPhysicalAssocs(int ichw, std::vector<int> & viginf) +{ + int ichwSegOffset = ichw - m_ichwMin; + + if (ichwSegOffset < m_ichwAssocsMin) + return; // possibly rendered in previous segment + + else if (ichwSegOffset >= m_ichwAssocsLim) + return; // possibly rendered in following segment + + else + { + std::vector<int> * pvisloutTmp = &(m_prgvisloutAssocs[ichwSegOffset - m_ichwAssocsMin]); + + for (size_t i = 0; i < pvisloutTmp->size(); i++) + { + int islout = (*pvisloutTmp)[i]; + Assert(islout != kNegInfinity); + Assert(islout != kPosInfinity); + viginf.push_back(LogicalToPhysicalSurface(islout)); + } + } +} +#endif + +/*---------------------------------------------------------------------------------------------- + Given an underlying character position (relative to the beginning of the string), + return a pointer to the vector containing the logical surface locations of + all the associated surface glyphs. + + Returns an empty vector (not something containing infinities) if the character is + "invisible." +----------------------------------------------------------------------------------------------*/ +std::vector<int> Segment::UnderlyingToLogicalAssocs(int ichw) +{ + std::vector<int> vnEmpty; + vnEmpty.clear(); + Assert(vnEmpty.size() == 0); + + int ichwSegOffset = ichw - m_ichwMin; + + if (ichwSegOffset < m_ichwAssocsMin) + return vnEmpty; // probably rendered in previous segment + + else if (ichwSegOffset >= m_ichwAssocsLim) + return vnEmpty; // probably rendered in following segment + + else if (m_prgpvisloutAssocs[ichwSegOffset - m_ichwAssocsMin] == NULL) + { + // Create a vector using the before and after values. + std::vector<int> visloutRet; + int isloutBefore = m_prgisloutBefore[ichwSegOffset - m_ichwAssocsMin]; + int isloutAfter = m_prgisloutAfter[ichwSegOffset - m_ichwAssocsMin]; + if (isloutBefore != kPosInfinity && isloutBefore != kNegInfinity) + visloutRet.push_back(isloutBefore); + if (isloutAfter != kPosInfinity && isloutAfter != kNegInfinity && isloutBefore != isloutAfter) + { + visloutRet.push_back(isloutAfter); + } + return visloutRet; + } + else + { + std::vector<int> * pvisloutRet = m_prgpvisloutAssocs[ichwSegOffset - m_ichwAssocsMin]; + return *pvisloutRet; + } +} + +/*---------------------------------------------------------------------------------------------- + Given an underlying character position (relative to the beginning of the string), + return an index of a slot in this segment that can used to test things like + InsertBefore. Don't return kNegInfinity or kPosInfinity except as a last resort. +----------------------------------------------------------------------------------------------*/ +int Segment::UnderlyingToLogicalInThisSeg(int ichw) +{ + int isloutTest = kNegInfinity; + std::vector<int> vislout = UnderlyingToLogicalAssocs(ichw); + for (size_t iislout = 0; iislout < vislout.size(); iislout++) + { + isloutTest = vislout[iislout]; + if (isloutTest != kNegInfinity && isloutTest != kPosInfinity) + return isloutTest; + } + return isloutTest; +} + +/*---------------------------------------------------------------------------------------------- + Return true if the two characters map to exactly the same set of glyphs. +----------------------------------------------------------------------------------------------*/ +bool Segment::SameSurfaceGlyphs(int ichw1, int ichw2) +{ + std::vector<int> vislout1 = UnderlyingToLogicalAssocs(ichw1); + std::vector<int> vislout2 = UnderlyingToLogicalAssocs(ichw2); + + bool fRet = true; + if (vislout1.size() == 0 || vislout2.size() == 0) + fRet = false; + else if (vislout1.size() != vislout2.size()) + fRet = false; + else + { + for (size_t islout = 0; islout < vislout1.size(); islout++) + { + if (vislout1[islout] != vislout2[islout]) + { + fRet = false; + break; + } + } + } + return fRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the direction level of the given character. +----------------------------------------------------------------------------------------------*/ +int Segment::DirLevelOfChar(int ichw, bool fBefore) +{ + if (m_twsh == ktwshOnlyWs) + return m_nDirDepth; + int islot = UnderlyingToLogicalSurface(ichw, fBefore); + if (islot == kNegInfinity || islot == kPosInfinity) + return 0; + int nDir = OutputSlot(islot)->DirLevel(); + if (nDir == -1) + // Not calculated: assume to be the top direction level. + nDir = TopDirLevel(); + + return nDir; +} + +/*---------------------------------------------------------------------------------------------- + Return whether the given character is right-to-left. +----------------------------------------------------------------------------------------------*/ +int Segment::CharIsRightToLeft(int ichw, bool fBefore) +{ + int nDir = DirLevelOfChar(ichw, fBefore); + return ((nDir % 2) != 0); +} + +/*---------------------------------------------------------------------------------------------- + Return whether the given slot is right-to-left. +----------------------------------------------------------------------------------------------*/ +int Segment::SlotIsRightToLeft(GrSlotOutput * pslout) +{ + if (m_twsh == ktwshOnlyWs) + return (m_nDirDepth % 2); + int nDir = pslout->DirLevel(); + if (nDir == -1) + nDir = TopDirLevel(); + return (nDir % 2); +} + +/*---------------------------------------------------------------------------------------------- + Check to make sure that the attachments are valid. Specifically, for every glyph that + is part of a cluster, all glyphs between that glyph and its root must (ultimately) have + that root glyph as one of its roots. + OBSOLETE +----------------------------------------------------------------------------------------------*/ +void Segment::AssertValidClusters(GrSlotStream * psstrm) +{ +#ifdef _DEBUG + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslotThis = psstrm->SlotAt(islot); + GrSlotState * pslotRoot = pslotThis->AttachRoot(psstrm); + if (!pslotRoot) + continue; + Assert(pslotRoot != pslotThis); + + int inc = (pslotThis->PosPassIndex() > pslotRoot->PosPassIndex()) ? -1 : 1; + + for (int islot2 = pslotThis->PosPassIndex() + inc; + islot2 != pslotRoot->PosPassIndex(); + islot2 += inc) + { + GrSlotState * pslotMid = psstrm->SlotAt(islot2); + Assert(pslotMid->HasAsRoot(psstrm, pslotRoot)); + } + } +#endif // _DEBUG +} + +/*---------------------------------------------------------------------------------------------- + Return information about (editing) clusters as appropriate for Uniscribe, ie, consistent + with the ScriptShape funciton. + A cluster includes all attachments, all clumps of characters created by insert = false, + and clumps of reordered glyphs. + + prgiginfFirstOfCluster returns the first glyph of the cluster for each CHARACTER + (glyph indices returned are zero-based and refer to physical order--left-to-right, etc.). + pfClusterStart returns a flag for each GLYPH indicating whether it is the first (left-most) + of a cluster. + Both arrays are optional. + CORRECTION: for now we use strictly logical order. It's not clear whether physical + order is needed for right-to-left text or not. See note below. + + TODO: convert from GlyphInfo objects which are RTL. +----------------------------------------------------------------------------------------------*/ +GrResult Segment::getUniscribeClusters( + int * prgiginfFirstOfCluster, int cchMax, int * pcch, + bool * pfClusterStart, int cfMax, int * pcf) +{ + ChkGrOutPtr(pcch); + if (prgiginfFirstOfCluster) + ChkGrArrayArg(prgiginfFirstOfCluster, cchMax); + ChkGrOutPtr(pcf); + if (pfClusterStart) + ChkGrArrayArg(pfClusterStart, cfMax); + + GrResult res = kresOk; + + //SetUpGraphics(ichwBase, pgg, true); + + if (m_dxsWidth == -1) + ComputeDimensions(); + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + if (pcch) + *pcch = m_dichwLim; + if (pcf) + *pcf = m_cginf; + if (cchMax < m_dichwLim) + { + if (cchMax == 0 && cfMax == 0) + res = kresFalse; // just asking for size information + else if (prgiginfFirstOfCluster || cchMax > 0) + res = kresInvalidArg; // not enough space + } + if (cfMax < m_cginf) + { + if (cchMax == 0 && cfMax == 0) + res = kresFalse; + else if (pfClusterStart || cfMax > 0) + res = kresInvalidArg; + } + if (res != kresOk) + { + //RestoreFont(pgg); + ReturnResult(res); + } + + // Generate a pair of arrays, indicating the first and last (logical) slots + // of the cluster for each character. Initially clusters are based just on + // character-to-glyph associations, but they expand as we process + // insert=false settings, attachments, and reordering. + + std::vector<int> visloutBefore; + std::vector<int> visloutAfter; + visloutBefore.resize(m_dichwLim); + visloutAfter.resize(m_dichwLim); + + int ich; + for (ich = 0; ich < m_dichwLim; ich++) + { + visloutBefore[ich] = m_prgisloutBefore[ich - m_ichwAssocsMin]; + visloutAfter[ich] = m_prgisloutAfter[ich - m_ichwAssocsMin]; + } + + if (m_dichwLim == 1) + { + // Only one character, therefore only one cluster. + visloutBefore[0] = m_isloutGinf0; + visloutAfter[0] = max(m_cginf - 1, 0) + m_isloutGinf0; + } + else + { + // Make all glyphs attached to each other part of a cluster, or glyphs where + // insertion is not allowed. + for (ich = 0; ich < m_dichwLim; ich++) + { + std::vector<int> vislout = UnderlyingToLogicalAssocs(ich + m_ichwMin); + + if (vislout.size() == 0) + { + // No glyphs represent this character. Merge it with the previous + // character. + visloutBefore[ich] = (ich == 0) ? visloutBefore[ich + 1] : visloutBefore[ich - 1]; + visloutAfter[ich] = (ich == 0) ? visloutAfter[ich + 1] : visloutAfter[ich - 1]; + continue; + } + + size_t iislout; + for (iislout = 0; iislout < vislout.size(); iislout++) + { + int islout = vislout[iislout]; + if (!m_prgslout[islout].InsertBefore()) + { + // This glyph does not allow insertion before it; make its character part + // of the same cluster as the previous character. + MergeUniscribeCluster(visloutBefore, visloutAfter, ich - 1, ich); +/* ++ // Keith Stribley's fix: ++ // NOTE: this is NOT always the same as the previous character, so ++ // check whether the next character actually has a glyph before this ++ // one ++ if ((ich == m_dichwLim - 1) || ++ (std::min(visloutBefore[ich + 1], visloutAfter[ich + 1]) > ++ std::max(visloutBefore[ich], visloutAfter[ich]))) ++ { ++ MergeUniscribeCluster(visloutBefore, visloutAfter, ich - 1, ich); ++ } ++ else ++ MergeUniscribeCluster(visloutBefore, visloutAfter, ich, ich + 1); +*/ + } + else if (m_prgslout[islout].ClusterBase() != -1 + && m_prgslout[islout].ClusterBase() != islout) + { + // This glyph is attached to something; make them part of the same cluster. + int isloutBase = m_prgslout[islout].ClusterBase(); + int ichBase = LogicalSurfaceToUnderlying(isloutBase, false) - m_ichwMin; + MergeUniscribeCluster(visloutBefore, visloutAfter, + ich, ichBase); + int ichBase1 = LogicalSurfaceToUnderlying(isloutBase, false) - m_ichwMin; + if (ichBase1 != ichBase) + MergeUniscribeCluster(visloutBefore, visloutAfter, + ich, ichBase1); + } + } + } + + // Handle reordering. Whenever the islout values get out of order, we need to + // merge into a single cluster. + for (ich = 0; ich < m_dichwLim - 1; ich++) + { + Assert(visloutBefore[ich] <= visloutAfter[ich]); + Assert(visloutBefore[ich] != kPosInfinity); + Assert(visloutBefore[ich] != kNegInfinity); + Assert(visloutAfter[ich] != kPosInfinity); + Assert(visloutAfter[ich] != kNegInfinity); + const int b1 = visloutBefore[ich]; + const int a1 = visloutAfter[ich]; + const int b2 = visloutBefore[ich + 1]; + const int a2 = visloutAfter[ich + 1]; + if (b1 == b2 && a1 == a2) + continue; // already a cluster + + if (std::min(b2, a2) <= std::max(b1, a1)) + { + MergeUniscribeCluster(visloutBefore, visloutAfter, ich, ich + 1); + } + } + } + + if (pfClusterStart) + { + // Initialize. + int iginf; + for (iginf = 0; iginf < m_cginf; iginf++) + pfClusterStart[iginf] = false; + } + + // To skip line-break slots; remember that the number of output slots equals the + // number of gbbs plus initial and/or final line-break slots if any. + // In this case the beginning of the first cluster indicates the first real glyph. + int isloutFirstReal = (m_dichwLim) ? ((m_fWsRtl)? visloutAfter[0] : visloutBefore[0]) : 0; + + // Kludge to make assertions below easier: + if (m_dichwLim > 0) + { + visloutBefore.push_back(visloutAfter.back() + 1); + visloutAfter.push_back(visloutAfter.back() + 1); + } + + // Convert (logical) slots to actual glyphs. + for (ich = 0; ich < m_dichwLim; ich++) + { + // NOTE: to treat the first of the cluster as the left-most, use visloutAfter for + // right-to-left situations. + + if (prgiginfFirstOfCluster) + prgiginfFirstOfCluster[ich] + = LogicalToPhysicalSurface((m_fWsRtl) ? visloutAfter[ich] : visloutBefore[ich]); + int clusterStartPos = ((m_fWsRtl) ? visloutAfter[ich] : visloutBefore[ich]) - isloutFirstReal; + if (pfClusterStart) + pfClusterStart[clusterStartPos] = true; + + Assert((visloutBefore[ich] == visloutBefore[ich + 1] && + visloutAfter[ich] == visloutAfter[ich + 1]) + || (visloutBefore[ich] <= visloutAfter[ich] && + visloutAfter[ich] < visloutBefore[ich + 1])); + } + + //RestoreFont(pgg); + + return kresOk; +} + + +// suppress GCC 4.3 warning for optimized min()/max() when called with (ich, ich+1) or similar +#pragma GCC diagnostic ignored "-Wstrict-overflow" + +/*---------------------------------------------------------------------------------------------- + Merge the given characters into the same Uniscribe cluster. This means merging any + intervening characters as well. +----------------------------------------------------------------------------------------------*/ +void Segment::MergeUniscribeCluster( + std::vector<int> & visloutBefore, std::vector<int> & visloutAfter, + int ich1, int ich2) +{ + int ichStartOrig = min(ich1, ich2); + int ichStopOrig = max(ich1, ich2); + + int isloutMin = visloutBefore[ichStopOrig]; + int isloutMax = visloutAfter[ichStartOrig]; + + int ichStart = ichStopOrig; + int ichStop = ichStartOrig; + // Find the beginning of the cluster. + while (ichStart > 0 && + (ichStart > ichStartOrig + || visloutBefore[ichStart - 1] >= visloutBefore[ichStopOrig] + || visloutBefore[ichStart - 1] == visloutBefore[ichStart])) + { + ichStart--; + isloutMin = std::min(isloutMin, visloutBefore[ichStart]); + isloutMax = std::max(isloutMax, visloutAfter[ichStart]); + } + // Find end of cluster. + while (ichStop < m_dichwLim - 1 && + (ichStop < ichStopOrig + || visloutAfter[ichStop + 1] <= visloutAfter[ichStartOrig] + || visloutAfter[ichStop + 1] == visloutAfter[ichStop])) + { + ichStop++; + isloutMin = std::min(isloutMin, visloutBefore[ichStop]); + isloutMax = std::max(isloutMax, visloutAfter[ichStop]); + } + + int ich; + for (ich = ichStart; ich <= ichStop; ich++) + { + visloutBefore[ich] = isloutMin; + visloutAfter[ich] = isloutMax; + } +} + +/*---------------------------------------------------------------------------------------------- + Return information about the positioning of glyphs as appropriate for Uniscribe. + The advance widths returned are the natural ones (what would be applied if no smart + rendering were happening), and the x-offsets are the difference between the + natural location of the glyph and what is produced by Graphite. The y-offset + is the same as GetGlyphsAndPositions. + + This is an experimental method. Its behavior may change signficantly in the future. +----------------------------------------------------------------------------------------------*/ +/* +GrResult Segment::GetUniscribePositions( + Rect rs, Rect rd, int cgidMax, int *pcgidRet, + float * prgxd, float * prgyd, float * prgdxdAdv) +{ + ChkGrOutPtr(pcgidRet); + ChkGrArrayArg(prgxd, cgidMax); + ChkGrArrayArg(prgyd, cgidMax); + ChkGrArrayArg(prgdxdAdv, cgidMax); + + GrResult res; + + //SetUpGraphics(ichBase, pgg, true); + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pcgidRet = m_cginf; + if (cgidMax < m_cginf) + { + res = (cgidMax == 0) ? kresFalse : kresInvalidArg; + //RestoreFont(pgg); + ReturnResult(res); + } + + if (m_dxsWidth < 0) + ComputeDimensions(); + + float * rgxdTmp = new float[cgidMax]; + float * rgdxdAdvTmp = new float[cgidMax]; + res = GetGlyphsAndPositions(rs, rd, cgidMax, pcgidRet, + NULL, rgxdTmp, prgyd, rgdxdAdvTmp); + if (ResultFailed(res)) + { + delete[] rgxdTmp; + delete[] rgdxdAdvTmp; + //RestoreFont(pgg); + ReturnResult(res); + } + + float xdLeft = SegmentPainter::ScaleX(0, rs, rd); + float xdXPosNatural = xdLeft; + for (int iginf = 0; iginf < *pcgidRet; iginf++) + { + int islout = iginf + m_isloutGinf0; + GrSlotOutput * pslout = OutputSlot(islout); + float dxsAdvNatural = pslout->AdvanceXMetric(); + float dxdAdvNatural = SegmentPainter::ScaleX(dxsAdvNatural, rs, rd) - xdLeft; + prgdxdAdv[iginf] = dxdAdvNatural; + prgxd[iginf] = rgxdTmp[iginf] - xdXPosNatural; + + xdXPosNatural += dxdAdvNatural; + } + + delete[] rgxdTmp; + delete[] rgdxdAdvTmp; + + //RestoreFont(pgg); + + ReturnResult(res); + +} +*/ + +/*---------------------------------------------------------------------------------------------- + Return information about the positioning of glyphs as appropriate for Uniscribe. + The advance widths returned are the natural ones (what would be applied if no smart + rendering were happening), and the x-offsets are the difference between the + natural location of the glyph and what is produced by Graphite. The y-offset + is the same as GetGlyphsAndPositions. + + This is an experimental method. Its behavior may change signficantly in the future. +----------------------------------------------------------------------------------------------*/ +/* +GrResult Segment::GetUniscribeGlyphsAndPositions( + Rect rs, Rect rd, int cgidMax, int *pcgidRet, utf16 * prgchGlyphs, + float * prgxd, float * prgyd, float * prgdxdAdv) +{ + ChkGrOutPtr(pcgidRet); + ChkGrArrayArg(prgxd, cgidMax); + ChkGrArrayArg(prgyd, cgidMax); + ChkGrArrayArg(prgdxdAdv, cgidMax); + + GrResult res; + + //SetUpGraphics(ichBase, pgg, true); + + Assert(m_dxsWidth >= 0); + Assert(m_dysAscent >= 0); + + *pcgidRet = m_cginf; + if (cgidMax < m_cginf) + { + res = (cgidMax == 0) ? kresFalse : kresInvalidArg; + //RestoreFont(pgg); + ReturnResult(res); + } + + if (m_dxsWidth < 0) + ComputeDimensions(); + + float * rgxdTmp = new float[cgidMax]; + float * rgdxdAdvTmp = new float[cgidMax]; + res = GetGlyphsAndPositions(rs, rd, cgidMax, pcgidRet, + prgchGlyphs, rgxdTmp, prgyd, rgdxdAdvTmp); + if (ResultFailed(res)) + { + delete[] rgxdTmp; + delete[] rgdxdAdvTmp; + //RestoreFont(pgg); + ReturnResult(res); + } + + float xdLeft = SegmentPainter::ScaleX(0, rs, rd); + float xdXPosNatural = xdLeft; + for (int iginf = 0; iginf < *pcgidRet; iginf++) + { + int islout = iginf + m_isloutGinf0; + GrSlotOutput * pslout = OutputSlot(islout); + float dxsAdvNatural = pslout->AdvanceXMetric(); + float dxdAdvNatural = SegmentPainter::ScaleX(dxsAdvNatural, rs, rd) - xdLeft; + prgdxdAdv[iginf] = dxdAdvNatural; + prgxd[iginf] = rgxdTmp[iginf] - xdXPosNatural; + + xdXPosNatural += dxdAdvNatural; + } + + delete[] rgxdTmp; + delete[] rgdxdAdvTmp; + + //RestoreFont(pgg); + + ReturnResult(res); +} +*/ + +/*---------------------------------------------------------------------------------------------- + Return the visual box for the given ligature component. Return (0,0,0,0) if the component + is not defined. +----------------------------------------------------------------------------------------------*/ +Rect Segment::ComponentRect(GrSlotOutput * pslout, int icomp) +{ + Rect rectRet; + rectRet.top = rectRet.bottom = rectRet.left = rectRet.right = 0; + if (icomp < 0 || icomp > pslout->NumberOfComponents()) + return rectRet; + + GrEngine * pgreng = EngineImpl(); + if (!pgreng) + return rectRet; + GrGlyphTable * pgtbl = pgreng->GlyphTable(); + if (!pgtbl) + return rectRet; + + float xsLeft, xsRight, ysTop, ysBottom; + + // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph. + int slati = pslout->ComponentId(icomp); + int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati); + if (!pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph, + m_mFontEmUnits, m_dysAscent, + &xsLeft, &ysTop, &xsRight, &ysBottom, + false)) // origin is baseline + { + return rectRet; + } + rectRet.top = ysTop; + rectRet.bottom = ysBottom; + rectRet.left = xsLeft; + rectRet.right = xsRight; + + return rectRet; +} + +/*---------------------------------------------------------------------------------------------- + Shift the glyphs physically by the given amount. This happens when a right-to-left + segment is changed from being end-of-line to not, so we have to account for the fact + that the trailing white-space was previous invisible but is now visible. +----------------------------------------------------------------------------------------------*/ +void Segment::ShiftGlyphs(float dxsShift) +{ + for (int islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * pslout = OutputSlot(islout); + pslout->AdjustPosXBy(dxsShift); + } + + //ShiftGlyphsPlatform(dxsShift); +} + +/*---------------------------------------------------------------------------------------------- + Find the glyph that best matches the given click location. + + @param xsClick - relative to start of segment + @param ysClick - relative to segment baseline +----------------------------------------------------------------------------------------------*/ +int Segment::GlyphHit(float xsClick, float ysClick) +{ + float xsLeft = kPosInfFloat; + float xsRight = kNegInfFloat; + + int iginf; + for (iginf = 0; iginf < m_cginf; iginf++) + { + GlyphInfo * pginf = m_prgginf + iginf; + xsLeft = min(xsLeft, pginf->origin()); + xsRight = max(xsRight, pginf->origin() + pginf->advanceWidth()); + } + + if (xsClick < xsLeft) + return m_fWsRtl ? m_cginf - 1 : 0; + else if (xsClick > xsRight) + return m_fWsRtl ? 0 : m_cginf - 1; + + int iginfHit; + std::vector<int> viginfNear; + std::vector<int> viginfHit; + std::vector<int> viginfInside; + + // Find glyph which might contain click point. + + for (iginfHit = m_cginf; iginfHit-- > 0; ) + { + if (m_fWsRtl) + { + if (m_prgginf[iginfHit].bb().right >= xsClick) + break; + } + else + { + if (m_prgginf[iginfHit].bb().left <= xsClick) + break; + } + } + if (iginfHit < 0) // click within lsb of first glyph + { + return 0; + } + + // Find all BB that OVERLAP the click point (bb.left < XClick < bb.right). + for (iginf = iginfHit; iginf >= 0; iginf--) + { + Rect rectBB = m_prgginf[iginf].bb(); + if (rectBB.left <= xsClick && xsClick <= rectBB.right) + { + viginfNear.push_back(iginf); + } + } + + // Find all BB that CONTAIN the click point (considering the click's Y coor too). + int iiginf; + for (iiginf = 0; iiginf < (int)viginfNear.size(); iiginf++) + { + Rect rectBB = m_prgginf[viginfNear[iiginf]].bb(); + if (rectBB.bottom <= ysClick && ysClick <= rectBB.top) + { + viginfHit.push_back(viginfNear[iiginf]); + } + } + + if (viginfNear.size() > 2 && viginfHit.size() == 0) + { + // No hit along y-axis; find the closest thing. + float dy = float(10000000.0); + for (int iiginf2 = 0; iiginf2 < (int)viginfNear.size(); iiginf2++) + { + Rect rectBB = m_prgginf[viginfNear[iiginf2]].bb(); + if (fabsf(rectBB.top - ysClick) < dy) + { + dy = fabsf(rectBB.top - ysClick); + viginfHit.clear(); + viginfHit.push_back(viginfNear[iiginf2]); + } + if (fabsf(rectBB.bottom - ysClick) < dy) + { + dy = fabsf(rectBB.bottom - ysClick); + viginfHit.clear(); + viginfHit.push_back(viginfNear[iiginf2]); + } + } + } + + // Find all BB for which the click is within their advance width, if any. + for (iiginf = 0; iiginf < (int)viginfHit.size(); iiginf++) + { + //Rect rectBB = m_prgginf[viginfHit[iiginf]].bb(); -- ?? this tests the bounding box again :-/ + //if (rectBB.left <= xsClick && xsClick <= rectBB.right) + gr::GlyphInfo * pginf = m_prgginf + viginfHit[iiginf]; + if (pginf->advanceWidth() == 0 // advance width is irrelevant + || (pginf->origin() < xsClick && xsClick <= pginf->origin() + pginf->advanceWidth())) + { + viginfInside.push_back(viginfHit[iiginf]); + } + } + + // If no BB coincides in any way with the click point, take the BB whose leading edge + // is closest to the click point. + // There could be more than one BB with the same leading x coor. + if (viginfHit.size() == 0 && viginfNear.size() == 0 && viginfInside.size() == 0) + { + float xsLeading; + if (iginfHit + 1 == m_cginf) + { + viginfNear.push_back(iginfHit); + } + else if (!m_fWsRtl) + { + // Left-to-right: + xsLeading = m_prgginf[iginfHit + 1].bb().left; + for (iginf = iginfHit + 1; iginf < m_cginf; iginf++) + { + if (m_prgginf[iginf].bb().left == xsLeading) + viginfNear.push_back(iginf); + } + } + else + { + // Right-to-left: + xsLeading = m_prgginf[iginfHit].bb().right; + for (iginf = iginfHit; iginf < m_cginf; iginf++) + { + if (m_prgginf[iginf].bb().right == xsLeading) + viginfNear.push_back(iginf); + } + } + } + + Assert(viginfNear.size()); + + int iginfLoc; + + // Select the BB based on its leading edge, offset from baseline, position in final pass. + if (viginfInside.size()) + iginfLoc = SelectBb(viginfInside, m_fWsRtl); + else if (viginfHit.size()) + iginfLoc = SelectBb(viginfHit, m_fWsRtl); + else + iginfLoc = SelectBb(viginfNear, m_fWsRtl); + + return iginfLoc; +} + +/*---------------------------------------------------------------------------------------------- + Select the bounding box we will use for a mouse hit. + First select any glyph with a significantly smaller bounding box. + Then, select the BB with the left-most (LTR) or right-most (RTL) edge. + If more than one BB has same edge of interest, select the one that is closest to the + the baseline. + If that is a tie, select the one with the lowest islout. + + @param vigbb - list of indexes into m_prggbb + @param fRTL - writing system direction +----------------------------------------------------------------------------------------------*/ +int Segment::SelectBb(std::vector<int> & viginf, bool fRTL) +{ + if (!viginf.size()) + { + Assert(false); + return -1; + } + + if (viginf.size() == 1) + return viginf[0]; + + // Find a glyph that has a signficantly smaller bounding box. + // The idea is that it is going to be harder to hit than the bigger glyph. + float smallestArea = float(1000000000.0); + float largestArea = 0.0; + size_t iiginfSmallest = 0; + size_t iiginf; + for (iiginf = 0; iiginf < viginf.size(); iiginf++) + { + gr::GlyphInfo * pginf = m_prgginf + viginf[iiginf]; + Rect rectBB = pginf->bb(); + float thisArea = (rectBB.right - rectBB.left) * (rectBB.top - rectBB.bottom); + if (smallestArea > thisArea) + { + smallestArea = thisArea; + iiginfSmallest = iiginf; + } + largestArea = max(largestArea, thisArea); + } + if (smallestArea * 2.0 < largestArea) + return viginf[iiginfSmallest]; + + // Find appropriate x coor of leading edge. + float xsLeading; + if (!fRTL) + xsLeading = m_prgginf[viginf[0]].bb().left; + else + xsLeading = m_prgginf[viginf[0]].bb().right; + + for (iiginf = 1; iiginf < viginf.size(); iiginf++) + { + Rect rectBB = m_prgginf[viginf[iiginf]].bb(); + if (!fRTL) + xsLeading = min(xsLeading, rectBB.left); + else + xsLeading = max(xsLeading, rectBB.right); + } + + // Find BBs with the leading edge. + std::vector<int> viginfSameEdge; + for (iiginf = 0; iiginf < viginf.size(); iiginf++) + { + Rect rectBB = m_prgginf[viginf[iiginf]].bb(); + if (!fRTL) + { + if (rectBB.left == xsLeading) + viginfSameEdge.push_back(viginf[iiginf]); + } + else + { + if (rectBB.right == xsLeading) + viginfSameEdge.push_back(viginf[iiginf]); + } + } + + if (viginfSameEdge.size() == 1) + return viginfSameEdge[0]; + + // Find minimum distance from baseline. + float ysMin = fabsf(m_prgginf[viginfSameEdge[0]].yOffset()); + for (iiginf = 1; iiginf < viginfSameEdge.size(); iiginf++) + { + float ysThis = fabsf(m_prgginf[viginfSameEdge[iiginf]].yOffset()); + ysMin = min(ysMin, ysThis); + } + + // Find BBs with minimum distance from baseline. + std::vector<int> viginfSameY; + for (iiginf = 0; iiginf < viginfSameEdge.size(); iiginf++) + { + if (fabsf(m_prgginf[viginfSameEdge[iiginf]].yOffset()) == ysMin) + viginfSameY.push_back(viginfSameEdge[iiginf]); + } + + if (viginfSameY.size() == 1) + return viginfSameY[0]; + + // Find minimum index of current candidates. + int islout = m_prgginf[viginfSameY[0]].logicalIndex(); + for (iiginf = 1; iiginf < viginfSameY.size(); iiginf++) + { + int isloutThis = m_prgginf[viginfSameY[iiginf]].logicalIndex(); + islout = min(islout, isloutThis); + } + + // Find BB with minimum islout - islout should be unique for all glyphs. + for (iiginf = 0; iiginf < viginfSameY.size(); iiginf++) + { + if (m_prgginf[viginfSameY[iiginf]].logicalIndex() == (unsigned)islout) + return viginfSameY[iiginf]; + } + + Assert(false); + return -1; // needed to stop compiler warning +} + +/*---------------------------------------------------------------------------------------------- + Return the right-most glyph on the physical surface. If there are ties, it will + prefer the one that is logically closest to the edge. +----------------------------------------------------------------------------------------------*/ +int Segment::RightMostGlyph() +{ + float xsRight = kNegInfFloat; + int iginfRet = -1; + for (int iginf = 0; iginf < m_cginf; iginf++) + { + float xsThis = m_prgginf[iginf].bb().right; + // For RTL, we prefer the glyph that is logically first; for LTR we prefer logically last. + if (xsThis > xsRight || (!m_fWsRtl && xsRight == xsThis)) + { + xsRight = xsThis; + iginfRet = iginf; + } + } + return iginfRet; +} + +/*---------------------------------------------------------------------------------------------- + Return the left-most glyph on the physical surface. If there are ties, it will + prefer the one that is logically closest to the edge. +----------------------------------------------------------------------------------------------*/ +int Segment::LeftMostGlyph() +{ + float xsLeft = kPosInfFloat; + int iginfRet = -1; + for (int iginf = 0; iginf < m_cginf; iginf++) + { + float xsThis = m_prgginf[iginf].bb().left; + // For RTL, we prefer the glyph that is logically last; for LTR we prefer logically first. + if (xsThis < xsLeft || (m_fWsRtl && xsLeft == xsThis)) + { + xsLeft = xsThis; + iginfRet = iginf; + } + } + return iginfRet; +} + +/*---------------------------------------------------------------------------------------------- + Convert from em-units to logical units. +----------------------------------------------------------------------------------------------*/ +float Segment::EmToLogUnits(int m) +{ + return GrEngine::GrIFIMulDiv(m, m_xysEmSquare, m_mFontEmUnits); +} + +/*---------------------------------------------------------------------------------------------- + Return the line-break pseudo glyph ID. +----------------------------------------------------------------------------------------------*/ +gid16 Segment::LBGlyphID() +{ + return m_preneng->LBGlyphID(); +} + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/SegmentAux.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/SegmentAux.cpp new file mode 100644 index 00000000000..2d475f9114f --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/SegmentAux.cpp @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2005 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: SegmentAux.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Implements auxiliary class that work with Segment: + - GlyphInfo + - GlyphIterator + - LayoutEnvironment +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif +// any other headers (not precompiled) +#ifndef _WIN32 +#include <stdlib.h> +#endif + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> GlyphInfo methods +//:>******************************************************************************************** + +gid16 GlyphInfo::glyphID() +{ + return m_pslout->m_chwActual; +} + +gid16 GlyphInfo::pseudoGlyphID() +{ + if (m_pslout->m_chwActual == m_pslout->m_chwGlyphID) + return 0; // not a pseudo-glyph + return m_pslout->m_chwGlyphID; +} + +bool GlyphInfo::isAttached() const throw() +{ + return m_pslout->IsPartOfCluster(); +} + +gr::GlyphIterator GlyphInfo::attachedClusterBase() const throw() +{ + const int islout = m_pslout->ClusterBase(); + + // Since we are not passing an array, the constructor expects a ginf index, not a slout index. + return GlyphIterator(*m_pseg, ((islout >= 0) ? islout : m_islout) - m_pseg->m_isloutGinf0); +} + +float GlyphInfo::attachedClusterAdvance() const throw() +{ + return m_pslout->ClusterAdvance(); +} + +std::pair<gr::GlyphSetIterator, gr::GlyphSetIterator> GlyphInfo::attachedClusterGlyphs() const +{ + std::vector<int> visloutClusterMembers; + m_pslout->ClusterMembers(m_pseg, m_islout, visloutClusterMembers); + RcVector * qvislout = new RcVector(visloutClusterMembers); + return std::make_pair( + GlyphSetIterator(*m_pseg, 0, qvislout), + GlyphSetIterator(*m_pseg, visloutClusterMembers.size(), qvislout)); +} + +size_t GlyphInfo::logicalIndex() +{ + if (m_pseg->OutputSlot(0)->IsLineBreak()) + return m_islout - 1; + else + return m_islout; +} + +float GlyphInfo::origin() +{ + return m_pslout->XPosition(); +} + +float GlyphInfo::advanceWidth() // logical units +{ + return m_pslout->m_xsAdvanceX; +} + +float GlyphInfo::advanceHeight() // logical units; zero for horizontal fonts +{ + Font & font = m_pseg->getFont(); + return m_pslout->GlyphMetricLogUnits(&font, kgmetAdvHeight); +} + +float GlyphInfo::yOffset() +{ + return m_pslout->YPosition(); +} + +Rect GlyphInfo::bb() // logical units +{ + return m_pslout->BoundingBox(m_pseg->getFont()); +} + +bool GlyphInfo::isSpace() +{ + return m_pslout->IsSpace(); +} + +// first char associated with this glyph, relative to start of the text-source +toffset GlyphInfo::firstChar() +{ + return m_pseg->LogicalSurfaceToUnderlying(m_islout, true); +} + +// last char associated with this glyph, relative to start of the text-source +toffset GlyphInfo::lastChar() +{ + return m_pseg->LogicalSurfaceToUnderlying(m_islout, false); +} + +unsigned int GlyphInfo::directionality() +{ + return m_pslout->Directionality(); +} + +// Embedding depth +unsigned int GlyphInfo::directionLevel() +{ + return m_pslout->DirLevel(); +} + +bool GlyphInfo::insertBefore() +{ + return m_pslout->InsertBefore(); +} + +int GlyphInfo::breakweight() +{ + return m_pslout->BreakWeight(); +} + +float GlyphInfo::maxStretch(size_t level) +{ + return m_pslout->MaxStretch(m_pseg, (int)level); +} + +float GlyphInfo::maxShrink(size_t level) +{ + return m_pslout->MaxShrink(m_pseg, (int)level); +} + +float GlyphInfo::stretchStep(size_t level) +{ + return m_pslout->StretchStep(m_pseg, (int)level); +} + +byte GlyphInfo::justWeight(size_t level) +{ + return byte(m_pslout->JustWeight((int)level)); +} + +float GlyphInfo::justWidth(size_t level) +{ + return m_pslout->JustWidth(m_pseg, (int)level); +} + +float GlyphInfo::measureStartOfLine() +{ + return m_pslout->MeasureSolLogUnits(m_pseg); +} + +float GlyphInfo::measureEndOfLine() +{ + return m_pslout->MeasureEolLogUnits(m_pseg); +} + +size_t GlyphInfo::numberOfComponents() +{ + return m_pslout->NumberOfComponents(); +} + +Rect GlyphInfo::componentBox(size_t icomp) +{ + return m_pseg->ComponentRect(m_pslout, icomp); +} + +toffset GlyphInfo::componentFirstChar(size_t icomp) +{ + Assert((int)icomp < m_pslout->NumberOfComponents()); + return m_pslout->FirstUnderlyingComponent(icomp) + m_pseg->startCharacter(); +} +toffset GlyphInfo::componentLastChar(size_t icomp) +{ + Assert((int)icomp < m_pslout->NumberOfComponents()); + return m_pslout->LastUnderlyingComponent(icomp) + m_pseg->startCharacter(); +} + +bool GlyphInfo::erroneous() +{ + return m_pseg->Erroneous(); +} + + +//:>******************************************************************************************** +//:> GlyphIterator methods +//:>******************************************************************************************** + +// Constructor +GlyphIterator::GlyphIterator(Segment & seg, size_t iginf) +: m_pginf(seg.m_prgginf + iginf) +{} + +// Copy constructor. +// Check if the incoming iterator is null, as we cannot dereference +// it to get the segment, and create a null GlyphIterator. +// Technically this might better be an assert as converting a null +// GlyphSetIterator into a null GlyphIterator is pointless and suggests +// some kind of error. +GlyphIterator::GlyphIterator(const GlyphSetIterator & sit) +: m_pginf((sit == GlyphSetIterator()) + ? 0 + : sit->segment().m_prgginf + sit->logicalIndex()) +{} + +/*---------------------------------------------------------------------------------------------- + Dereference the iterator, returning a GlyphInfo object. +----------------------------------------------------------------------------------------------*/ + +GlyphSetIterator::reference GlyphSetIterator::operator*() const +{ + assert(m_pseg != 0); + assert(m_vit != std::vector<int>::const_iterator()); + // in the case of a non-contiguous list + return m_pseg->m_prgginf[(*m_vit) - m_pseg->m_isloutGinf0]; +} + + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/TestFSM.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/TestFSM.cpp new file mode 100644 index 00000000000..6dad1dc9dc8 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/TestFSM.cpp @@ -0,0 +1,3416 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TestFSM.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Hard-coded FSMs for test procedures. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:Ignore + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +#ifdef OLD_TEST_STUFF + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +#ifndef _DEBUG + +bool GrLineBreakPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrSubPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrPosPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} +#endif // !_DEBUG + + +#ifdef _DEBUG + +/*---------------------------------------------------------------------------------------------- + Call the appropriate test function. +----------------------------------------------------------------------------------------------*/ + +bool GrLineBreakPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrSubPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + if (m_staBehavior == "SimpleFSM") + return RunSimpleFSMTest(ptman, ruln, psstrmInput, psstrmOutput); + else if (m_staBehavior == "RuleAction") + return false; + else if (m_staBehavior == "RuleAction2") + return false; + else if (m_staBehavior == "Assoc") + return false; + else + return false; +} + +bool GrPosPass::RunTestRules(GrTableManager * ptman, int ruln, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + + +/*---------------------------------------------------------------------------------------------- + A simple FSM. + + Rules: + r1. clsCons clsVowel clsDiacrAcute + r2. clsCons clsVowelAE clsDiacr + r3. clsCons clsVowelI + r4. clsCons clsVowelI clsDiacrGrave + r5. clsCons clsVowelI clsDiacr + + Machine classes: + mcCons: b,c,d / f,g,h / j,k,l,m,n / p,q,r,s,t / v,w,x,y,z + mcVae: a 97/ e 101 + mcVi: i 105 + mcVou: o 111 / u 117 + mcDa: acute '/' 47 + mcDg: grave '\' 92 + mcDct: other diacritics-circumflex '^' 94 / tilde '~' 126 + + + FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcCons mcVae mcVi mcVou mcDa mcDg mcDct + --------------------------------------------------------------------------- + T NA s0 | s1 + | - - - - - - - + T NA s1 | s2 s4 s3 + | - - - - - - - + T NA s2 | s5 s6 s6 + | - - - - - - - + T NA s3 | s10 + | - - - - - - - + T A s4 | s7 s8 s9 + | - - - - - - - + F A s5 | + | - - - - - - - + F A s6 | + | - - - - - - - + F A s7 | + | - - - - - - - + F A s8 | + | - - - - - - - + F A s9 | + | - - - - - - - + F A s10 | + | - - - - - - - + + Rules Matched: + s4: r3 + s5: r1, r2 + s6: r2 + s7: r1, r5 + s8: r4, r5 + s9: r5 + s10: r1 + +----------------------------------------------------------------------------------------------*/ + +void GrSubPass::SetUpSimpleFSMTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "SimpleFSM"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpSimpleFSMTest(); +} + +void GrFSM::SetUpSimpleFSMTest() +{ + // Create machine class ranges. + m_cmcr = 14; + m_prgmcr = new GrFSMClassRange[14]; + m_prgmcr[0].m_chwFirst = 47; // forward slash (acute): mcDa + m_prgmcr[0].m_chwLast = 47; + m_prgmcr[0].m_col = 4; + + m_prgmcr[1].m_chwFirst = 92; // backslash: mcDg + m_prgmcr[1].m_chwLast = 92; + m_prgmcr[1].m_col = 5; + + m_prgmcr[2].m_chwFirst = 94; // caret: mcDct + m_prgmcr[2].m_chwLast = 94; + m_prgmcr[2].m_col = 6; + + m_prgmcr[3].m_chwFirst = 97; // a: mcVae + m_prgmcr[3].m_chwLast = 97; + m_prgmcr[3].m_col = 1; + + m_prgmcr[4].m_chwFirst = 98; // b - d: mcCons + m_prgmcr[4].m_chwLast = 100; + m_prgmcr[4].m_col = 0; + + m_prgmcr[5].m_chwFirst = 101; // e: mcVae + m_prgmcr[5].m_chwLast = 101; + m_prgmcr[5].m_col = 1; + + m_prgmcr[6].m_chwFirst = 102; // f - h: mcCons + m_prgmcr[6].m_chwLast = 104; + m_prgmcr[6].m_col = 0; + + m_prgmcr[7].m_chwFirst = 105; // i: mcVi + m_prgmcr[7].m_chwLast = 105; + m_prgmcr[7].m_col = 2; + + m_prgmcr[8].m_chwFirst = 106; // j - n: mcCons + m_prgmcr[8].m_chwLast = 110; + m_prgmcr[8].m_col = 0; + + m_prgmcr[9].m_chwFirst = 111; // o: mcVou + m_prgmcr[9].m_chwLast = 111; + m_prgmcr[9].m_col = 3; + + m_prgmcr[10].m_chwFirst = 112; // p - t: mcCons + m_prgmcr[10].m_chwLast = 116; + m_prgmcr[10].m_col = 0; + + m_prgmcr[11].m_chwFirst = 117; // u: mcVou + m_prgmcr[11].m_chwLast = 117; + m_prgmcr[11].m_col = 3; + + m_prgmcr[12].m_chwFirst = 118; // v - z: mcCons + m_prgmcr[12].m_chwLast = 122; + m_prgmcr[12].m_col = 0; + + m_prgmcr[13].m_chwFirst = 126; // tilde: mcDct + m_prgmcr[13].m_chwLast = 126; + m_prgmcr[13].m_col = 6; + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 11; + m_crowNonAcpt = 4; + m_crowFinal = 6; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 7; + + // Set up transition table. + m_prgrowTransitions = new short[35]; // 35 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 2; *psn++ = 4; *psn++ = 3; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 5; *psn++ = 6; *psn++ = 6; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =10; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 8; *psn++ = 9; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[10]; // 10 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[7+1]; // 7 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s4: r3 + m_prgrulnMatched[0] = 3; + + m_prgirulnMin[1] = 1; // s5: r1, r2 + m_prgrulnMatched[1] = 1; + m_prgrulnMatched[2] = 2; + + m_prgirulnMin[2] = 3; // s6: r2 + m_prgrulnMatched[3] = 2; + + m_prgirulnMin[3] = 4; // s7: r1, r5 + m_prgrulnMatched[4] = 1; + m_prgrulnMatched[5] = 5; + + m_prgirulnMin[4] = 6; // s8: r4, r5 + m_prgrulnMatched[6] = 4; + m_prgrulnMatched[7] = 5; + + m_prgirulnMin[5] = 8; // s9: r5 + m_prgrulnMatched[8] = 5; + + m_prgirulnMin[6] = 9; // s10: r1 + m_prgrulnMatched[9] = 1; + + m_prgirulnMin[7] = 10; +} + + +/*---------------------------------------------------------------------------------------------- + The "rule" does something silly: just inserts *'s--the number equals the number of the + rule. +----------------------------------------------------------------------------------------------*/ +bool GrSubPass::RunSimpleFSMTest(GrTableManager * ptman, int ruln, GrSlotStream * psstrmInput, + GrSlotStream * psstrmOutput) +{ + int cslotMatched = (ruln == 3)? 2: 3; + + GrSlotState * pslot0 = psstrmInput->Peek(0); + + for (int islot = 0; islot < cslotMatched; islot++) + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + for (islot = 0; islot < ruln; islot++) + { + GrSlotState * pslot; + ptman->NewSlot('*', pslot0, NULL, m_ipass, &pslot); + pslot->Associate(pslot0); + psstrmOutput->NextPut(pslot); + } + + return true; +} + + +/*---------------------------------------------------------------------------------------------- + A simple set of rules with actions. + + Rules: + r0. clsDigit gSlash clsDigit _ > @3 _ @1 gTilde; // tilde = 126 + r1. _ clsDigit gBackSlash clsDigit > gDollar @4 _ @2; // dollar sign = 36 + + Machine classes: + mcDigit: '0' - '9' 48 - 57 + mcSlash: '/' 47 + mcBSlash: '\' 92 + + + FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | clsDig mcSlash mcBSlash + ------------------------------------------------------ + T NA s0 | s1 + | - - - + T NA s1 | s2 s3 + | - - - + T NA s2 | s4 + | - - - + T NA s3 | s5 + | - - - + F A s4 | + | - - - + F A s5 | + | - - - + + Rules Matched: + s4: r0 + s5: r1 + +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpRuleActionTest() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpRuleActionTest(); +} + +void GrClassTable::SetUpRuleActionTest() +{ + m_ccls = 2; // number of classes + m_cclsLinear = 2; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[100]; + + m_prgichwOffsets = new data16[2+1]; + + gid16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: tilde + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(126)); // '~' + + // Output class 1: dollar + m_prgichwOffsets[1] = 1; + *pchw++ = msbf(data16(36)); // '$' + + m_prgichwOffsets[2] = 2; +} + +void GrSubPass::SetUpRuleActionTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "RuleAction"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpRuleActionTest(); + + m_crul = 2; + + // Set up constraint code--succeed trivially. + m_prgbConstraintBlock = new byte[3]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopPushByte; *pb++ = 1; // Push true + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[30]; + pb = m_prgbActionBlock; + + // Rule 1: clsDigit gSlash clsDigit _ > @3 _ @1 gTilde; + + *pb++ = kopPutCopy; *pb++ = 2; // PutCopy 2 + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = -2; // PutCopy -2 + *pb++ = kopNext; // Next + *pb++ = kopInsert; // Insert + *pb++ = kopPutGlyph8bitObs; *pb++ = 0; // PutGlyph 0 ('~' = 126) + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopPopRet; // PopAndRet + + // Rule 2: _ clsDigit gBackSlash clsDigit > gDollar @4 _ @2; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutGlyph8bitObs; *pb++ = 1; // PutGlyph 1 ('$' = 36) + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = 2; // PutCopy 2 + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = -2; // PutCopy -2 + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopPopRet; // PopAndRet + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 15; +} + +void GrFSM::SetUpRuleActionTest() +{ + // Create machine class ranges. + m_cmcr = 3; + m_prgmcr = new GrFSMClassRange[3]; + m_prgmcr[0].m_chwFirst = 47; // forward slash: mcSlash + m_prgmcr[0].m_chwLast = 47; + m_prgmcr[0].m_col = 1; + + m_prgmcr[1].m_chwFirst = 48; // 0 - 9: mcDigit + m_prgmcr[1].m_chwLast = 57; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 92; // backslash + m_prgmcr[2].m_chwLast = 92; + m_prgmcr[2].m_col = 2; + + + m_dimcrInit = 2; // (max power of 2 <= m_cmcr); + m_cLoop = 1; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 6; + m_crowNonAcpt = 4; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 3; + + // Set up transition table. + m_prgrowTransitions = new short[12]; // 12 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 2; *psn++ = 3; + *psn++ = 4; *psn++ = 0; *psn++ = 0; + *psn++ = 5; *psn++ = 0; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[2+1]; // 2 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s4: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s5: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; +} + +/*---------------------------------------------------------------------------------------------- + A more complicated set of rules with actions. + + PASS 1: + r0. clsDigit > @1 {dir = DIR_RIGHT} / ^ _ {dir == DIR_LEFT}; + r1. clsSpecial > @2 {dir = DIR_ARABNUMBER} / clsDigit {dir != DIR_LEFT} ^ _; + r2. clsSpecial > @2 {dir = @1.dir} / clsSpecial ^ _; + + Pass 1 machine classes: + mcDigit: '0' - '9' 48 - 57 + mcSpecial: '-', '_', '=', '~' 45, 95, 61, 126 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcDigit mcSpecial + ------------------------------------------ + T NA s0 | s2 s1 + | - _ + T NA s1 | s4 + | - - + T A s2 | s3 + | - - + F A s3 | + | - - + F A s4 | + | - - + + Rules Matched: + s2: r0 + s3: r1 + s4: r2 + + PASS 2: + r0. clsVowel gCaret > clsCirumVowel _; + r1. clsVowel gSlash > _ clsAcuteVowel$1; + + Pass 2 machine classes: + mcVowel + mcCaret: '^' 94 + mcSlash: '/' 47 + + + Pass 2 FSM: + | mcVowel mcCaret mcSlash + ----------------------------------------------------- + T NA s0 | s1 + | - _ + T NA s1 | s2 s3 + | - - + F A s2 | + | - - + F A s3 | + | - - + + Rules matched: + s2: r0 + s3: r1 + + + PASS 3: + r0. clsLeft > clsRight {dir = @1.dir} / _ clsDir {dir==DIR_RIGHT}; + r1. clsRight > clsLeft {dir = @1.dir} / clsDir {dir==DIR_RIGHT} _; + + Pass 3 machine classes: + mcDir: (clsDigit, clsSpecial) + mcLeft: '(', '[', '{' 40, 91, 123 + mcRight: ')', ']', '}' 41, 93, 125 + + Pass 3 FSM: + | mcDir mcLeft mcRight + ----------------------------------------------------- + T NA s0 | s2 s1 + | - _ + T NA s1 | s3 + | - - + T NA s2 | s4 + | - - + F A s3 | + | - - + F A s4 | + | - - + + Rules matched: + s3: r0 + s4: r1 + + +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpRuleAction2Test() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpRuleAction2Test(); +} + +void GrClassTable::SetUpRuleAction2Test() +{ + m_ccls = 7; // number of classes + m_cclsLinear = 4; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[70]; + + m_prgichwOffsets = new data16[7+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: clsCirumVowel + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(226)); // a + *pchw++ = msbf(data16(234)); // e + *pchw++ = msbf(data16(238)); // i + *pchw++ = msbf(data16(244)); // o + *pchw++ = msbf(data16(251)); // u + *pchw++ = msbf(data16(194)); // A + *pchw++ = msbf(data16(202)); // E + *pchw++ = msbf(data16(206)); // I + *pchw++ = msbf(data16(212)); // O + *pchw++ = msbf(data16(219)); // U + + // Output class 1: clsAcuteVowel + m_prgichwOffsets[1] = 10; + *pchw++ = msbf(data16(225)); // a + *pchw++ = msbf(data16(233)); // e + *pchw++ = msbf(data16(237)); // i + *pchw++ = msbf(data16(243)); // o + *pchw++ = msbf(data16(250)); // u + *pchw++ = msbf(data16(193)); // A + *pchw++ = msbf(data16(201)); // E + *pchw++ = msbf(data16(205)); // I + *pchw++ = msbf(data16(211)); // O + *pchw++ = msbf(data16(218)); // U + + // Output class 2: clsLeft + m_prgichwOffsets[2] = 10 + 10; + *pchw++ = msbf(data16(40)); // ( + *pchw++ = msbf(data16(91)); // [ + *pchw++ = msbf(data16(123)); // { + + // Output class 3: clsRight + m_prgichwOffsets[3] = 20 + 3; + *pchw++ = msbf(data16(41)); // ) + *pchw++ = msbf(data16(93)); // ] + *pchw++ = msbf(data16(125)); // } + + // Input class 4: clsVowel + m_prgichwOffsets[4] = 23 + 3; // 26 + *pchw++ = msbf(data16(10)); + *pchw++ = msbf(data16(8)); *pchw++ = msbf(data16(3)); *pchw++ = msbf(data16(10-8)); + *pchw++ = msbf(data16(65)); *pchw++ = msbf(data16(5)); // A + *pchw++ = msbf(data16(69)); *pchw++ = msbf(data16(6)); // E + *pchw++ = msbf(data16(73)); *pchw++ = msbf(data16(7)); // I + *pchw++ = msbf(data16(79)); *pchw++ = msbf(data16(8)); // O + *pchw++ = msbf(data16(85)); *pchw++ = msbf(data16(9)); // U + *pchw++ = msbf(data16(97)); *pchw++ = msbf(data16(0)); // a + *pchw++ = msbf(data16(101)); *pchw++ = msbf(data16(1)); // e + *pchw++ = msbf(data16(105)); *pchw++ = msbf(data16(2)); // i + *pchw++ = msbf(data16(111)); *pchw++ = msbf(data16(3)); // o + *pchw++ = msbf(data16(117)); *pchw++ = msbf(data16(4)); // u + + // Input class 5: clsLeft + m_prgichwOffsets[5] = 26 + 4 + 10*2; // 50 + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(40)); *pchw++ = msbf(data16(0)); // ( + *pchw++ = msbf(data16(91)); *pchw++ = msbf(data16(1)); // [ + *pchw++ = msbf(data16(123)); *pchw++ = msbf(data16(2)); // { + + // Input class 6: clsRight + m_prgichwOffsets[6] = 50 + 4 + 3*2; // 60 + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(41)); *pchw++ = msbf(data16(0)); // ) + *pchw++ = msbf(data16(93)); *pchw++ = msbf(data16(1)); // ] + *pchw++ = msbf(data16(125)); *pchw++ = msbf(data16(2)); // } + + m_prgichwOffsets[7] = 60 + 4 + 3*2; // 70 +} + +void GrSubPass::SetUpRuleAction2Test() +{ + if (m_ipass == 1) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 5; + m_staBehavior = "RuleAction2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpRuleAction2Test(m_ipass); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 0; // ContextItem 0 + *pb++ = kopPushSlotAttr; *pb++= kslatDir; // PushSlotAttr dir 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = kdircL; // Push DIR_LEFT + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + *pb++ = kopCntxtItem; *pb++ = 0; // ContextItem 0 + *pb++ = kopPushSlotAttr; *pb++= kslatDir; // PushSlotAttr dir 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = kdircL; // Push DIR_LEFT + *pb++ = kopNotEq; // NotEqual + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + m_prgibConstraintStart[1] = 11; + m_prgibConstraintStart[2] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[10 + 11 + 12]; + pb = m_prgbActionBlock; + + // Rule 0: clsDigit > @1 {dir = DIR_RIGHT} / ^ _ {dir == DIR_LEFT}; + + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushByte; *pb++ = kdircR; // PushByte DIR_RIGHT + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopPopRet; // PopAndRet + + // Rule 1: clsSpecial > @2 {dir = DIR_ARABNUMBER} / clsDigit {dir != DIR_LEFT} ^ _; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushByte; *pb++ = kdircArabNum; // Push DIR_ARABNUMBER + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopPopRet; // PopAndRet + + // Rule 2: clsSpecial > @2 {dir = @1.dir} / clsSpecial ^ _; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir -1 + *pb++ = -1; + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopPopRet; // PopAndRet + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 10; + m_prgibActionStart[2] = 10 + 11; + } + else if (m_ipass == 2) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 3; + m_staBehavior = "RuleAction2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpRuleAction2Test(m_ipass); + + m_crul = 2; + + // Set up constraint code--succeed trivially. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[16]; + pb = m_prgbActionBlock; + + // Rule 0: clsVowel gCaret > clsCirumVowel _; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsVowel clsCircumVowel + *pb++ = 4; *pb++ = 0; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsVowel gSlash > _ clsAcuteVowel$1; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutSubs8bitObs; *pb++ = -1; // PutSubs -1 clsVowel clsAcuteVowel + *pb++ = 4; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 8; + } + else if (m_ipass == 3) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 3; + m_staBehavior = "RuleAction2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpRuleAction2Test(m_ipass); + + m_crul = 2; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 1; // ContextItem 1 + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = kdircR; // Push DIR_RIGHT + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + *pb++ = kopCntxtItem; *pb++ = 0; // ContextItem 0 + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = kdircR; // Push DIR_RIGHT + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + m_prgibConstraintStart[1] = 1 + 10; + + // Set up rule action code. + m_prgbActionBlock = new byte[23]; + pb = m_prgbActionBlock; + + // Rule 0: clsLeft > clsRight {dir = @1.dir} / _ clsDir {dir == DIR_RIGHT}; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsLeft clsRight + *pb++ = 5; *pb++ = 3; + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir 1 + *pb++ = 1; + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsRight > clsLeft {dir = @1.dir} / clsDir {dir == DIR_RIGHT} _; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsRight clsLeft + *pb++ = 6; *pb++ = 2; + *pb++ = kopPushSlotAttr;*pb++ = kslatDir; // PushSlotAttr dir -1 + *pb++ = -1; + *pb++ = kopAttrSet; *pb++ = kslatDir; // AttrSet dir + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 11; + } +} + + +void GrFSM::SetUpRuleAction2Test(int ipass) +{ + if (ipass == 1) + { + // Create machine class ranges. + m_cmcr = 5; + m_prgmcr = new GrFSMClassRange[5]; + m_prgmcr[0].m_chwFirst = 45; // hyphen + m_prgmcr[0].m_chwLast = 45; + m_prgmcr[0].m_col = 1; + + m_prgmcr[1].m_chwFirst = 48; // 0 - 9: mcDigit + m_prgmcr[1].m_chwLast = 57; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 61; // equals sign + m_prgmcr[2].m_chwLast = 61; + m_prgmcr[2].m_col = 1; + + m_prgmcr[3].m_chwFirst = 95; // underscore + m_prgmcr[3].m_chwLast = 95; + m_prgmcr[3].m_col = 1; + + m_prgmcr[4].m_chwFirst = 126; // tilde + m_prgmcr[4].m_chwLast = 126; + m_prgmcr[4].m_col = 1; + + m_dimcrInit = 4; // (max power of 2 <= m_cmcr); + m_cLoop = 2; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 5; + m_crowNonAcpt = 2; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 2; + + // Set up transition table. + m_prgrowTransitions = new short[6]; // 6 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 2; *psn++ = 1; + *psn++ = 0; *psn++ = 4; + *psn++ = 0; *psn++ = 3; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[3+1]; // 3 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s2: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s3: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; // s3: r2 + m_prgrulnMatched[2] = 2; + + m_prgirulnMin[3] = 3; + } + else if (ipass == 2) + { + // Create machine class ranges. + m_cmcr = 12; + m_prgmcr = new GrFSMClassRange[12]; + m_prgmcr[0].m_chwFirst = 47; // slash + m_prgmcr[0].m_chwLast = 47; + m_prgmcr[0].m_col = 2; + + m_prgmcr[1].m_chwFirst = 65; // A + m_prgmcr[1].m_chwLast = 65; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 69; // E + m_prgmcr[2].m_chwLast = 69; + m_prgmcr[2].m_col = 0; + + m_prgmcr[3].m_chwFirst = 73; // I + m_prgmcr[3].m_chwLast = 73; + m_prgmcr[3].m_col = 0; + + m_prgmcr[4].m_chwFirst = 79; // O + m_prgmcr[4].m_chwLast = 79; + m_prgmcr[4].m_col = 0; + + m_prgmcr[5].m_chwFirst = 85; // U + m_prgmcr[5].m_chwLast = 85; + m_prgmcr[5].m_col = 0; + + m_prgmcr[6].m_chwFirst = 94; // caret + m_prgmcr[6].m_chwLast = 94; + m_prgmcr[6].m_col = 1; + + m_prgmcr[7].m_chwFirst = 97; // a + m_prgmcr[7].m_chwLast = 97; + m_prgmcr[7].m_col = 0; + + m_prgmcr[8].m_chwFirst = 101; // e + m_prgmcr[8].m_chwLast = 101; + m_prgmcr[8].m_col = 0; + + m_prgmcr[9].m_chwFirst = 105; // i + m_prgmcr[9].m_chwLast = 105; + m_prgmcr[9].m_col = 0; + + m_prgmcr[10].m_chwFirst = 111; // o + m_prgmcr[10].m_chwLast = 111; + m_prgmcr[10].m_col = 0; + + m_prgmcr[11].m_chwFirst = 117; // u + m_prgmcr[11].m_chwLast = 117; + m_prgmcr[11].m_col = 0; + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 4; + m_crowNonAcpt = 2; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 3; + + // Set up transition table. + m_prgrowTransitions = new short[6]; // 6 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 2; *psn++ = 3; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[2+1]; // 2 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s1: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s2: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; + } + else if (ipass == 3) + { + // Create machine class ranges. + m_cmcr = 11; + m_prgmcr = new GrFSMClassRange[11]; + m_prgmcr[0].m_chwFirst = 40; // ( + m_prgmcr[0].m_chwLast = 40; + m_prgmcr[0].m_col = 1; + + m_prgmcr[1].m_chwFirst = 41; // ) + m_prgmcr[1].m_chwLast = 41; + m_prgmcr[1].m_col = 2; + + m_prgmcr[2].m_chwFirst = 45; // hyphen + m_prgmcr[2].m_chwLast = 45; + m_prgmcr[2].m_col = 0; + + m_prgmcr[3].m_chwFirst = 48; // 0 - 9: mcDigit + m_prgmcr[3].m_chwLast = 57; + m_prgmcr[3].m_col = 0; + + m_prgmcr[4].m_chwFirst = 61; // = + m_prgmcr[4].m_chwLast = 61; + m_prgmcr[4].m_col = 0; + + m_prgmcr[5].m_chwFirst = 91; // [ + m_prgmcr[5].m_chwLast = 91; + m_prgmcr[5].m_col = 1; + + m_prgmcr[6].m_chwFirst = 93; // ] + m_prgmcr[6].m_chwLast = 93; + m_prgmcr[6].m_col = 2; + + m_prgmcr[7].m_chwFirst = 95; // underscore + m_prgmcr[7].m_chwLast = 95; + m_prgmcr[7].m_col = 0; + + m_prgmcr[8].m_chwFirst = 123; // { + m_prgmcr[8].m_chwLast = 123; + m_prgmcr[8].m_col = 1; + + m_prgmcr[9].m_chwFirst = 125; // } + m_prgmcr[9].m_chwLast = 125; + m_prgmcr[9].m_col = 2; + + m_prgmcr[10].m_chwFirst = 126; // tilde + m_prgmcr[10].m_chwLast = 126; + m_prgmcr[10].m_col = 0; + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 5; + m_crowNonAcpt = 3; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 3; + + // Set up transition table. + m_prgrowTransitions = new short[9]; // 9 = (m_crow-m_crowFinal) * m_ccol + short * psn = m_prgrowTransitions; + *psn++ = 2; *psn++ = 1; *psn++ = 0; + *psn++ = 3; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 4; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[2+1]; // 2 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s3: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s4: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; + } +} + + +/*---------------------------------------------------------------------------------------------- + Tests of various kinds of assocations. + + PASS 1 (line-break): + r0. gX / # _ ^ ; // do nothing + r1. gX {break = -2}; + + Pass 1 machine classes: + mcX: 'x' 120 + mcLB: 35 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | gX gLB + -------------------------------- + T NA s0 | s2 s1 + | + T NA s1 | s3 + | + F A s2 | + | + F A s3 | + + + Rules Matched: + s2: r1 + s3: r0 + + + + PASS 2: + r0. clsOA clsE > clsELig:(1 2) _; + r1. clsLC _ _ gX > @1 @5 @1 @5 / _ _ # {break==2} _ _ ; + r2. _ clsFraction _ > clsNumerator:2 gSlash:2 clsDenom:2; + + Pass 2 machine classes: + mcOA: O A 79 65 + mcOAlc: o a 111 97 + mcE E e 69 101 + mcFraction: 188 189 190 + mcX: x 120 + mcLCother + mcLB: 35 + + + Pass 2 FSM: mcLC + | mcOA mcOAlc mcE mcElc other mcX mcFractn mcLB + ----------------------------------------------------------------------------------------- + T NA s0 | s1 s2 s4 s3 s3 s6 + | - - - - - - - + T NA s1 | s7 + | - - - - - - - + T NA s2 | s7 s5 + | - - - - - - - + T NA s3 | s5 + | - - - - - - - + T NA s4 | s5 + | - - - - - - - + T NA s5 | s8 + | - - - - - - - + F A s6 | + | - - - - - - + F A s7 | + | - - - - - - + F A s8 | + | - - - - - - + + Rules matched: + s6: r2 + s7: r0 + s8: r1 + + + PASS 3: + r0. _ > gHyphen / _ # {break == 2} ^; + + Pass 3 machine classes: + mcLB: 35 + + Pass 3 FSM: + + | gLB + -------------------------------- + T NA s0 | s1 + | + F A s1 | + | + + Rules Matched: + s1: r0 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpAssocTest() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpAssocTest(); + + m_fLineBreak = true; + m_cchwPreXlbContext = 1; + m_cchwPostXlbContext = 1; +} + +void GrClassTable::SetUpAssocTest() +{ + m_ccls = 11; // number of classes + m_cclsLinear = 5; // number of classes in linear format (output classes) + + m_prgchwBIGGlyphList = new data16[143]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: clsELig + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(140)); // OE + *pchw++ = msbf(data16(198)); // AE + *pchw++ = msbf(data16(156)); // oe + *pchw++ = msbf(data16(230)); // ae + + // Output class 1: clsNumerator + m_prgichwOffsets[1] = 4; + *pchw++ = msbf(data16(49)); // 1 + *pchw++ = msbf(data16(49)); // 1 + *pchw++ = msbf(data16(51)); // 3 + + // Output class 2: clsDenom + m_prgichwOffsets[2] = 4 + 3; + *pchw++ = msbf(data16(52)); // 4 + *pchw++ = msbf(data16(50)); // 2 + *pchw++ = msbf(data16(52)); // 4 + + // Output class 3: gSlash + m_prgichwOffsets[3] = 7 + 3; + *pchw++ = msbf(data16(47)); // '/' + + // Output class 4: gHyphen + m_prgichwOffsets[4] = 10 + 1; + *pchw++ = msbf(data16(45)); // '-' + + // Input class 5: clsOA + m_prgichwOffsets[5] = 11 + 1; + *pchw++ = msbf(data16(4)); + *pchw++ = msbf(data16(4)); *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(0)); + *pchw++ = msbf(data16(65)); *pchw++ = msbf(data16(1)); // A + *pchw++ = msbf(data16(79)); *pchw++ = msbf(data16(0)); // O + *pchw++ = msbf(data16(97)); *pchw++ = msbf(data16(3)); // a + *pchw++ = msbf(data16(111)); *pchw++ = msbf(data16(2)); // o + + // Input class 6: clsE + m_prgichwOffsets[6] = 12 + 4 + 4*2; // 24 + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(40)); *pchw++ = msbf(data16(0)); // E + *pchw++ = msbf(data16(91)); *pchw++ = msbf(data16(1)); // e + + // Input class 7: clsLC + m_prgichwOffsets[7] = 24 + 4 + 2*2; // 32 + *pchw++ = msbf(data16(26)); + *pchw++ = msbf(data16(16)); *pchw++ = msbf(data16(4)); *pchw++ = msbf(data16(26-16)); + for (int i = 0; i < 26; ++i) + { + *pchw++ = msbf(data16(97+i)); *pchw++ = msbf(data16(i)); + } + + // Input class 8: gX + m_prgichwOffsets[8] = 32 + 4 + 26*2; // 88 + *pchw++ = msbf(data16(1)); + *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(0)); *pchw++ = msbf(data16(0)); + *pchw++ = msbf(data16(120)); *pchw++ = msbf(data16(0)); + + // Input class 9: clsFraction + m_prgichwOffsets[9] = 88 + 4 + 1*2; // 127 + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(188)); *pchw++ = msbf(data16(0)); // 1/4 + *pchw++ = msbf(data16(189)); *pchw++ = msbf(data16(1)); // 1/2 + *pchw++ = msbf(data16(190)); *pchw++ = msbf(data16(2)); // 3/4 + + // Input class 10: # + m_prgichwOffsets[10] = 127 + 4 + 3*2; // 137 + *pchw++ = msbf(data16(1)); + *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(0)); *pchw++ = msbf(data16(0)); + *pchw++ = msbf(data16(35)); *pchw++ = msbf(data16(0)); // 1/4 + + m_prgichwOffsets[11] = 137 + 4 + 1*2; // 143 +} + +void GrLineBreakPass::SetUpAssocTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 5; + m_staBehavior = "Assoc"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpAssocTest(m_ipass); + + m_crul = 2; + + // Set up constraint code--succeed trivially. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[7 + 8]; + pb = m_prgbActionBlock; + + // Rule 0: gX / # _ ^ ; // do nothing + + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: gX {break = -2}; + + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushByte; *pb++ = -2; // PushByte -2 + *pb++ = kopAttrSet; *pb++ = kslatBreak; // AttrSet breakweight + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 7; +} + +void GrSubPass::SetUpAssocTest() +{ + if (m_ipass == 2) + { + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 5; + m_staBehavior = "Assoc"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpAssocTest(m_ipass); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 1; // ContextItem 1 + *pb++ = kopPushSlotAttr;*pb++ = kslatBreak; // PushSlotAttr break 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 2; // Push 2 + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 1; + m_prgibConstraintStart[2] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[12 + 12 + 22]; + pb = m_prgbActionBlock; + + // Rule 0: clsOA clsE > clsELig:(1 2) _ ; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsOA clsELig + *pb++ = 5; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 2; // Assoc 2 0 1 + *pb++ = 0; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsLC _ _ gX > @1 @5 @1 @5 / _ _ # {break==2} _ _ ; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = 2; // PutCopy 2 + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext - line break + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = -1; // PutCopy -1 + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopRetZero; // RetZero + + // Rule 2: _ clsFraction _ > clsNumerator:2 gSlash:2 clsDenom:2; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = 1; // PutSubs 1 clsFraction clsNumerator + *pb++ = 9; *pb++ = 1; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 1 + *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopPutGlyph8bitObs; *pb++ = 3; // PutGlyph gSlash + *pb++ = kopNext; // Next + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 clsFraction clsDenom + *pb++ = 9; *pb++ = 2; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 0 + *pb++ = 0; + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 12; + m_prgibActionStart[2] = 12 + 12; + } + else if (m_ipass == 3) + { + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 3; + m_staBehavior = "Assoc"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpAssocTest(m_ipass); + + m_crul = 1; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 0; // ContextItem 0 + *pb++ = kopPushSlotAttr;*pb++ = kslatBreak; // PushSlotAttr break 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 2; // Push 2 + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + + m_prgbActionBlock = new byte[7]; + pb = m_prgbActionBlock; + + // Rule 0: _ > gHyphen / _ # {break == 2} ^; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutGlyph8bitObs; *pb++ = 4; // PutGlyph 4 ('-' = 45) + *pb++ = kopNext; // Next + *pb++ = kopPushByte; *pb++ = 1; // Push 1 + *pb++ = kopPopRet; // PopAndRet + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + } + else + Assert(false); +} + + +void GrFSM::SetUpAssocTest(int ipass) +{ + if (ipass == 1) + { + // Create machine class ranges. + m_cmcr = 2; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 35; // # + m_prgmcr[0].m_chwLast = 35; + m_prgmcr[0].m_col = 1; + + m_prgmcr[1].m_chwFirst = 120; // x + m_prgmcr[1].m_chwLast = 120; + m_prgmcr[1].m_col = 0; + + m_dimcrInit = 2; // (max power of 2 <= m_cmcr); + m_cLoop = 1; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 4; + m_crowNonAcpt = 2; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 2; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 4 + short * psn = m_prgrowTransitions; + *psn++ = 2; *psn++ = 1; + *psn++ = 3; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s2: r1 + m_prgrulnMatched[0] = 1; + + m_prgirulnMin[1] = 1; // s3: r0 + m_prgrulnMatched[1] = 0; + + m_prgirulnMin[2] = 2; + } + else if (ipass == 2) + { + // Create machine class ranges. + m_cmcr = 13; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 35; // # + m_prgmcr[0].m_chwLast = 35; + m_prgmcr[0].m_col = 7; + + m_prgmcr[1].m_chwFirst = 65; // A + m_prgmcr[1].m_chwLast = 65; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 69; // E + m_prgmcr[2].m_chwLast = 69; + m_prgmcr[2].m_col = 2; + + m_prgmcr[3].m_chwFirst = 79; // O + m_prgmcr[3].m_chwLast = 79; + m_prgmcr[3].m_col = 0; + + m_prgmcr[4].m_chwFirst = 97; // a + m_prgmcr[4].m_chwLast = 97; + m_prgmcr[4].m_col = 1; + + m_prgmcr[5].m_chwFirst = 98; // b - d + m_prgmcr[5].m_chwLast = 100; + m_prgmcr[5].m_col = 4; + + m_prgmcr[6].m_chwFirst = 101; // e + m_prgmcr[6].m_chwLast = 101; + m_prgmcr[6].m_col = 3; + + m_prgmcr[7].m_chwFirst = 102; // f - n + m_prgmcr[7].m_chwLast = 110; + m_prgmcr[7].m_col = 4; + + m_prgmcr[8].m_chwFirst = 111; // o + m_prgmcr[8].m_chwLast = 111; + m_prgmcr[8].m_col = 1; + + m_prgmcr[9].m_chwFirst = 112; // p - w + m_prgmcr[9].m_chwLast = 119; + m_prgmcr[9].m_col = 4; + + m_prgmcr[10].m_chwFirst = 120; // x + m_prgmcr[10].m_chwLast = 120; + m_prgmcr[10].m_col = 5; + + m_prgmcr[11].m_chwFirst = 121; // y - z + m_prgmcr[11].m_chwLast = 122; + m_prgmcr[11].m_col = 4; + + m_prgmcr[12].m_chwFirst = 188; // 1/4, 1/2, 3/4 + m_prgmcr[12].m_chwLast = 190; + m_prgmcr[12].m_col = 6; + + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 9; + m_crowNonAcpt = 6; + m_crowFinal = 3; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 8; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 48 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 2; *psn++ = 0; *psn++ = 4; *psn++ = 3; *psn++ = 3; *psn++ = 6; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 5; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 5; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 5; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 8; *psn++ = 0; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[3+1]; // 3 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s6: r2 + m_prgrulnMatched[0] = 2; + + m_prgirulnMin[1] = 1; // s7: r0 + m_prgrulnMatched[1] = 0; + + m_prgirulnMin[2] = 2; // s8: r1 + m_prgrulnMatched[2] = 1; + + m_prgirulnMin[3] = 3; + } + else if (ipass == 3) + { + // Create machine class ranges. + m_cmcr = 1; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 35; // # + m_prgmcr[0].m_chwLast = 35; + m_prgmcr[0].m_col = 0; + + m_dimcrInit = 1; // (max power of 2 <= m_cmcr); + m_cLoop = 0; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 2; + m_crowNonAcpt = 1; + m_crowFinal = 1; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 1; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 1 + short * psn = m_prgrowTransitions; + *psn++ = 1; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[1]; // 1 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s1: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; + } +} + + +/*---------------------------------------------------------------------------------------------- + Second batch of association tests. + + PASS 1: + r0. clsSymbol _ > _ @1 / _ gNumber gHyphen # gNumber _; + r1. clsSymbol _ > _ @1 / _ gHyphen # _; + r2. _ clsSymbol > @6 _ / _ gNumber gHyphen # gNumber _; + r3. _ clsSymbol > @4 _ / _ gHyphen # _; + r4. clsLeft _ > @1 clsRight$1:1 / _ clsLetter gHyphen # _ gSpace; + r5. clsLeft _ > @1 clsRight$1:1 / _ clsLetter gHyphen # clsLetter _ gSpace; + r6. clsLeft _ > @1 clsRight$1:1 / _ clsLetter _ gSpace; + r7. clsLeft _ > @1 clsRight$1:1 / _ clsLetter clsLetter _ gSpace; + + Pass 1 machine classes: + mcSym: @, $, %, & 64, 36, 37, 38 + mcNum: 48-57 + mcHyphen: 45 + mcSpace: 32 + mcLet: 97-122 + mcLeft: ( [ { 40, 91, 123 + mcRight: ) ] } 41, 93, 125 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcSym mcNum mcHyph mcSpace mcLet mcLeft mcLB + ---------------------------------------------------------------------------------- + T NA s0 | s1 s2 s3 s4 + | + T NA s1 | s5 s6 + | + T NA s2 | s7 + | + T NA s3 | s8 + | + T NA s4 | s9 + | + T NA s5 | s10 + | + T NA s6 | s18 + | + T NA s7 | s11 + | + T NA s8 | s19 + | + T NA s9 | s12 s20 s13 + | + T NA s10 | s14 + | + T NA s11 | s15 + | + T NA s12 | s16 + | + T NA s13 | s21 + | + T NA s14 | s22 + | + T NA s15 | s23 + | + T NA s16 | s24 s17 + | + T NA s17 | s25 + | + F A s18 | + | + F A s19 | + | + F A s20 | + | + F A s21 | + | + F A s22 | + | + F A s23 | + | + F A s24 | + | + F A s25 | + + + Rules Matched: + s18: r1 + s19: r3 + s20: r6 + s21: r7 + s22: r0 + s23: r2 + s24: r4 + s25: r5 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpAssoc2Test() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpAssoc2Test(); + + m_fLineBreak = true; + m_cchwPreXlbContext = 3; + m_cchwPostXlbContext = 2; +} + +void GrClassTable::SetUpAssoc2Test() +{ + m_ccls = 2; // number of classes + m_cclsLinear = 1; // number of classes in linear format (output classes) + + m_prgchwBIGGlyphList = new data16[13]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: clsRight + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(41)); // ) + *pchw++ = msbf(data16(93)); // ] + *pchw++ = msbf(data16(125)); // } + + // Input class 1: clsLeft + m_prgichwOffsets[1] = 3; + *pchw++ = msbf(data16(3)); + *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(1)); *pchw++ = msbf(data16(3-2)); + *pchw++ = msbf(data16(40)); *pchw++ = msbf(data16(0)); // ( + *pchw++ = msbf(data16(91)); *pchw++ = msbf(data16(1)); // [ + *pchw++ = msbf(data16(123)); *pchw++ = msbf(data16(2)); // { + + m_prgichwOffsets[2] = 3 + 4 + 3*2; // 13 +} + +void GrSubPass::SetUpAssoc2Test() +{ + m_nMaxRuleContext = m_nMaxChunk = 6; + m_nMaxRuleLoop = 5; + m_staBehavior = "Assoc2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpAssoc2Test(m_ipass); + + m_crul = 8; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + m_prgibConstraintStart[2] = 0; + m_prgibConstraintStart[3] = 0; + m_prgibConstraintStart[4] = 0; + m_prgibConstraintStart[5] = 0; + m_prgibConstraintStart[6] = 0; + m_prgibConstraintStart[7] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[11 + 9 + 11 + 9 + 14 + 15 + 12 + 13]; + pb = m_prgbActionBlock; + + // Rule 0: clsSymbol _ > _ @1 / _ gNumber gHyphen # gNumber _; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = -4; // PutCopy -4 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsSymbol _ > _ @1 / _ gHyphen # _; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = -2; // PutCopy -2 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 2: _ clsSymbol > @6 _ / _ gNumber gHyphen # gNumber _; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = 5; // PutCopy 5 + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 3: _ clsSymbol > @4 _ / _ gHyphen # _; + + *pb++ = kopInsert; // Insert + *pb++ = kopPutCopy; *pb++ = 3; // PutCopy 3 + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 4: clsLeft _ > @1 clsRight$1 / _ clsLetter gHyphen # _ gSpace; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = -3; // PutSubs -3 1 0 + *pb++ = 1; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 -3 + *pb++ = -3; + *pb++ = kopNext; // Next; + *pb++ = kopRetZero; // RetZero + + // Rule 5: clsLeft _ > @1 clsRight$1 / _ clsLetter gHyphen # clsLetter _ gSpace; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = -4; // PutSubs -4 1 0 + *pb++ = 1; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 -4 + *pb++ = -4; + *pb++ = kopNext; // Next; + *pb++ = kopRetZero; // RetZero + + // Rule 6: clsLeft _ > @1 clsRight$1 / _ clsLetter _ gSpace; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = -1; // PutSubs -1 1 0 + *pb++ = 1; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 -1 + *pb++ = -1; + *pb++ = kopNext; // Next; + *pb++ = kopRetZero; // RetZero + + // Rule 7: clsLeft _ > @1 clsRight$1 / _ clsLetter clsLetter _ gSpace; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutSubs8bitObs; *pb++ = -2; // PutSubs -2 1 0 + *pb++ = 1; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 1; // Assoc 1 -2 + *pb++ = -2; + *pb++ = kopNext; // Next; + *pb++ = kopRetZero; // RetZero + + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 11; + m_prgibActionStart[2] = 11 + 9; + m_prgibActionStart[3] = 20 + 11; + m_prgibActionStart[4] = 31 + 9; + m_prgibActionStart[5] = 40 + 14; + m_prgibActionStart[6] = 54 + 15; + m_prgibActionStart[7] = 69 + 12; +} + + +void GrFSM::SetUpAssoc2Test(int ipass) +{ + // Create machine class ranges. + m_cmcr = 10; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 32; // space + m_prgmcr[0].m_chwLast = 32; + m_prgmcr[0].m_col = 3; + + m_prgmcr[1].m_chwFirst = 35; // # + m_prgmcr[1].m_chwLast = 35; + m_prgmcr[1].m_col = 6; + + m_prgmcr[2].m_chwFirst = 36; // $, %, & + m_prgmcr[2].m_chwLast = 38; + m_prgmcr[2].m_col = 0; + + m_prgmcr[3].m_chwFirst = 40; // ( + m_prgmcr[3].m_chwLast = 40; + m_prgmcr[3].m_col = 5; + + m_prgmcr[4].m_chwFirst = 45; // - + m_prgmcr[4].m_chwLast = 45; + m_prgmcr[4].m_col = 2; + + m_prgmcr[5].m_chwFirst = 48; // 0 - 9 + m_prgmcr[5].m_chwLast = 57; + m_prgmcr[5].m_col = 1; + + m_prgmcr[6].m_chwFirst = 64; // @ + m_prgmcr[6].m_chwLast = 64; + m_prgmcr[6].m_col = 0; + + m_prgmcr[7].m_chwFirst = 91; // [ + m_prgmcr[7].m_chwLast = 91; + m_prgmcr[7].m_col = 5; + + m_prgmcr[8].m_chwFirst = 97; // a - z + m_prgmcr[8].m_chwLast = 122; + m_prgmcr[8].m_col = 4; + + m_prgmcr[9].m_chwFirst = 123; // { + m_prgmcr[9].m_chwLast = 123; + m_prgmcr[9].m_col = 5; + + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 26; + m_crowNonAcpt = 18; + m_crowFinal = 8; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 7; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 126 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 2; *psn++ = 3; *psn++ = 0; *psn++ = 0; *psn++ = 4; *psn++ = 0; + *psn++ = 0; *psn++ = 5; *psn++ = 6; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 8; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 9; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ =10; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =18; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =11; + *psn++ =19; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ =12; *psn++ =20; *psn++ =13; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =14; + *psn++ = 0; *psn++ =15; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =16; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =21; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ =22; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ =23; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =24; *psn++ =17; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ =25; *psn++ = 0; *psn++ = 0; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[8]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s18: r1 + m_prgrulnMatched[0] = 1; + + m_prgirulnMin[1] = 1; // s19: r3 + m_prgrulnMatched[1] = 3; + + m_prgirulnMin[2] = 2; // s20: r6 + m_prgrulnMatched[2] = 6; + + m_prgirulnMin[3] = 3; // s21: r7 + m_prgrulnMatched[3] = 7; + + m_prgirulnMin[4] = 4; // s22: r0 + m_prgrulnMatched[4] = 0; + + m_prgirulnMin[5] = 5; // s23: r2 + m_prgrulnMatched[5] = 2; + + m_prgirulnMin[6] = 6; // s24: r4 + m_prgrulnMatched[6] = 4; + + m_prgirulnMin[7] = 7; // s25: r5 + m_prgrulnMatched[7] = 5; + + m_prgirulnMin[8] = 8; +} + +/*---------------------------------------------------------------------------------------------- + Test of default associations + + PASS 1: + r0. gDollar _ > @1 gEquals; + r1. gAtSign > _ ; + + Pass 1 machine classes: + mcDollar: $ 36 + mcAt: @ 64 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcDollar mcAt + -------------------------------------- + T NA s0 | s1 s2 + | + F A s1 | + | + F A s2 | + + + Rules Matched: + s1: r0 + s2: r1 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpDefaultAssocTest() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpDefaultAssocTest(); +} + +void GrClassTable::SetUpDefaultAssocTest() +{ + m_ccls = 1; // number of classes + m_cclsLinear = 1; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[1]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: gEquals + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(61)); + + m_prgichwOffsets[1] = 1; +} + +void GrSubPass::SetUpDefaultAssocTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 5; + m_staBehavior = "DefaultAssoc"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpDefaultAssocTest(); + + m_crul = 2; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[6 + 3]; + pb = m_prgbActionBlock; + + // Rule 0: gDollar _ > @1 gEquals; + + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopInsert; // Insert + *pb++ = kopPutGlyph8bitObs; *pb++ = 0; // PutGlyph 0 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: gAtSign > _ ; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 6; +} + + +void GrFSM::SetUpDefaultAssocTest() +{ + // Create machine class ranges. + m_cmcr = 2; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 36; // $ + m_prgmcr[0].m_chwLast = 36; + m_prgmcr[0].m_col = 0; + + m_prgmcr[1].m_chwFirst = 64; // @ + m_prgmcr[1].m_chwLast = 64; + m_prgmcr[1].m_col = 1; + + + m_dimcrInit = 2; // (max power of 2 <= m_cmcr); + m_cLoop = 1; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 3; + m_crowNonAcpt = 1; + m_crowFinal = 2; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 2; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 2 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 2; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[2]; // 2 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s1: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s2: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; +} + +/*---------------------------------------------------------------------------------------------- + Test of features. The ID of "theFeature" == 234, its index is 0. The default value is 1. + + PASS 1: + if (theFeature == 0) // * -> $ + r0. gAsterisk > gDollar ; + else if (theFeature == 1) // * -> % + r1. gAsterisk > gPercent ; + else // * -> @ + r2. gAsterisk > gAt ; + + Pass 1 machine classes: + mcAsterisk: * 42 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcAsterisk + -------------------------------- + T NA s0 | s1 + | + F A s1 | + + + Rules Matched: + s1: r0, r1, r2 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpFeatureTest() +{ + AddFeature(234, 0, 1, 1); + + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpFeatureTest(); +} + +void GrClassTable::SetUpFeatureTest() +{ + m_ccls = 4; // number of classes + m_cclsLinear = 4; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[4]; + + m_prgichwOffsets = new data16[m_ccls + 1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(36)); // $ + + m_prgichwOffsets[1] = 1; + *pchw++ = msbf(data16(37)); // % + + m_prgichwOffsets[2] = 2; + *pchw++ = msbf(data16(64)); // @ + + m_prgichwOffsets[3] = 3; + *pchw++ = msbf(data16(42)); // * + + m_prgichwOffsets[4] = 4; +} + +void GrSubPass::SetUpFeatureTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 5; + m_staBehavior = "Feature"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpFeatureTest(); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 6 + 12 + 12]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; + + // (theFeature == 0) + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 0; // PushByte 0 + *pb++ = kopEqual; // Equal + *pb++ = kopPopRet; // PopAndRet + + // (theFeature != 0 && theFeature == 1) + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 0; // PushByte 0 + *pb++ = kopNotEq; // NotEq + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 1; // PushByte 1 + *pb++ = kopEqual; // Equal + *pb++ = kopAnd; // And + *pb++ = kopPopRet; // PopAndRet + + // (theFeature != 0 && theFeature != 1) + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 0; // PushByte 0 + *pb++ = kopNotEq; // NotEq + *pb++ = kopPushFeat; *pb++ = 0; // PushFeat 0 + *pb++ = kopPushByte; *pb++ = 1; // PushByte 1 + *pb++ = kopNotEq; // NotEq + *pb++ = kopAnd; // And + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + m_prgibConstraintStart[1] = 1 + 6; + m_prgibConstraintStart[2] = 1 + 6 + 12; + + // Set up rule action code. + m_prgbActionBlock = new byte[4 + 4 + 4]; + pb = m_prgbActionBlock; + + // Rule 0: gAsterisk > gDollar ; + + *pb++ = kopPutGlyph8bitObs; *pb++ = 0; // PutGlyph 0 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: gAsterisk > gPercent ; + + *pb++ = kopPutGlyph8bitObs; *pb++ = 1; // PutGlyph 1 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 2: gAsterisk > gAt ; + + *pb++ = kopPutGlyph8bitObs; *pb++ = 2; // PutGlyph 2 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 4; + m_prgibActionStart[2] = 4 + 4; +} + + +void GrFSM::SetUpFeatureTest() +{ + // Create machine class ranges. + m_cmcr = 1; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 42; // * + m_prgmcr[0].m_chwLast = 42; + m_prgmcr[0].m_col = 0; + + + m_dimcrInit = 1; // (max power of 2 <= m_cmcr); + m_cLoop = 0; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 2; + m_crowNonAcpt = 1; + m_crowFinal = 1; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 1; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 1 + short * psn = m_prgrowTransitions; + *psn++ = 1; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s1: r0, r1, r2 + m_prgrulnMatched[0] = 0; + m_prgrulnMatched[1] = 1; + m_prgrulnMatched[2] = 2; + + m_prgirulnMin[1] = 3; +} + + +/*---------------------------------------------------------------------------------------------- + Tests of ligatures and components. + + PASS 1 (line-break): + r0. clsLetter { break = 2 } + + Pass 1 machine classes: + mcLetter: a - z 97 - 122 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | clsLetter + -------------------------------- + T NA s0 | s1 + | + F A s1 | + + + Rules Matched: + s1: r0 + + + PASS 2 (substitution): + r0. gX gY gZ > gCapX:(1 2 4) { comp {X.ref=@1; Y.ref=@2; Z.ref=@4 }} _ _ + / _ _ # {break==2} _; + r1. gX gY gZ > _ _ gCapZ:(1 3 4) { comp {X.ref=@1; Y.ref=@3; Z.ref=@4 }} + / _ # {break==2} _ _; + r2. gX gY gZ > _ gCapY:(1 2 3) { comp {X.ref=@1; Y.ref=@2; Z.ref=@3 }} gPlus; + + Pass 2 machine classes: + mcX: x 120 + mcY: y 121 + mcZ: z 122 + mcLB: 35 + + + Pass 2 FSM: + | mcX mcY mcZ mcLB + ---------------------------------------------------- + T NA s0 | s1 + | - - - + T NA s1 | s2 s3 + | - - - + T NA s2 | s6 s4 + | - - - + T NA s3 | s5 + | - - - + T NA s4 | s7 + | - - - + T NA s5 | s8 + | - - - + F A s6 | + | - - - + F A s7 | + | - - - + F A s8 | + | - - - + + Rules matched: + s6: r2 + s7: r0 + s8: r1 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpLigatureTest() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpLigatureTest(); + + m_pgtbl = new GrGlyphTable(); + m_pgtbl->SetUpLigatureTest(); + + m_fLineBreak = true; + m_cchwPreXlbContext = 2; + m_cchwPostXlbContext = 2; +} + +void GrClassTable::SetUpLigatureTest() +{ + m_ccls = 7; // number of classes + m_cclsLinear = 7; // number of classes in linear format (all classes are single-item) + + m_prgchwBIGGlyphList = new data16[7]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Class 0: gCapX + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(88)); // X + + // Class 1: gCapY + m_prgichwOffsets[1] = 1; + *pchw++ = msbf(data16(89)); // Y + + // Class 2: gCapZ + m_prgichwOffsets[2] = 2; + *pchw++ = msbf(data16(90)); // Z + + // Class 3: gPlus + m_prgichwOffsets[3] = 3; + *pchw++ = msbf(data16(43)); // + + + // Class 4: gX + m_prgichwOffsets[4] = 4; + *pchw++ = msbf(data16(120)); // x + + // Class 5: gY + m_prgichwOffsets[5] = 5; + *pchw++ = msbf(data16(121)); // y + + // Class 6: gZ + m_prgichwOffsets[6] = 6; + *pchw++ = msbf(data16(122)); // z + + m_prgichwOffsets[7] = 7; +} + +void GrGlyphTable::SetUpLigatureTest() +{ + SetNumberOfGlyphs(128); + SetNumberOfStyles(1); + + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + Assert(pgstbl); + + pgstbl->Initialize(1, 0, 128, 10, 4); + SetSubTable(0, pgstbl); + + SetNumberOfComponents(4); // comp.w, comp.x, comp.y, comp.z + + pgstbl->SetUpLigatureTest(); +} + +/*********************************************************************************************** + TODO: This method is BROKEN because m_prgibBIGAttrValues has been changed. It is no + longer a data16 *. The Gloc table can contain 16-bit or 32-bit entries and must be + accessed accordingly. +***********************************************************************************************/ +void GrGlyphSubTable::SetUpLigatureTest() +{ + m_nAttrIDLim = 20; + + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(0, 102); + + for (int i = 0; i < 88; i++) + m_prgibBIGAttrValues[i] = (byte)msbf(data16(0)); + + m_prgibBIGAttrValues[88] = (byte)msbf(data16(0)); + m_prgibBIGAttrValues[89] = (byte)msbf(data16(34)); + m_prgibBIGAttrValues[90] = (byte)msbf(data16(68)); + + for (i = 91; i < 128; i++) + m_prgibBIGAttrValues[i] = (byte)msbf(data16(102)); + + m_pgatbl->SetUpLigatureTest(); +} + +void GrGlyphAttrTable::SetUpLigatureTest() +{ + // Glyph 88 'X' (2 runs; offset = 0): + // 0 = 0 comp.w undefined + // 1 = 8 comp.x: (0,0, 25,100) + // 2 = 12 comp.y: (25,0, 75,100) + // 3 = 16 comp.z: (75,0, 100,100) + // 4-7 = 0 comp.w box corners + // 8 = 100 comp.x. top + // 9 = 0 bottom + // 10= 0 left + // 11= 25 right + // 12= 100 comp.y. top + // 13= 0 bottom + // 14= 25 left + // 15= 75 right + // 16= 100 comp.z. top + // 17= 0 bottom + // 18= 75 left + // 19= 100 right + // + // Glyph 89 (2 runs; offset = 34): + // 0 = 0 comp.w undefined + // 1 = 8 comp.x: (0,0, 33,100) + // 2 = 12 comp.y: (33,0, 67,100) + // 3 = 16 comp.z: (67,0, 100,100) + // 4-7 = 0 comp.w box corners + // 8 = 100 comp.x. top + // 9 = 0 bottom + // 10= 0 left + // 11= 33 right + // 12= 100 comp.y. top + // 13= 0 bottom + // 14= 33 left + // 15= 67 right + // 16= 100 comp.z. top + // 17= 0 bottom + // 18= 67 left + // 19= 100 right + // + // Glyph 90 (2 runs; offset = 68): + // 0 = 0 comp.w undefined + // 1 = 8 comp.x: (0,0, 10,100) + // 2 = 12 comp.y: (10,0, 90,100) + // 3 = 16 comp.z: (90,0, 100,100) + // 4-7 = 0 comp.w box corners + // 8 = 100 comp.x. top + // 9 = 0 bottom + // 10= 0 left + // 11= 10 right + // 12= 100 comp.y. top + // 13= 0 bottom + // 14= 10 left + // 15= 90 right + // 16= 100 comp.z. top + // 17= 0 bottom + // 18= 90 left + // 19= 100 right + + byte * pbBIG = m_prgbBIGEntries; + + GrGlyphAttrRun gatrun; + + // Glyph 88 'X' + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(8)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(12)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(16)); + memcpy(pbBIG, &gatrun, 8); // 8 = 3*2 + 2 + pbBIG += 8; + gatrun.m_bMinAttrID = 8; // comp.x.top + gatrun.m_cAttrs = 12; + gatrun.m_rgchwBIGValues[0] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[3] = msbf(data16(25)); + gatrun.m_rgchwBIGValues[4] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[5] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[6] = msbf(data16(25)); + gatrun.m_rgchwBIGValues[7] = msbf(data16(75)); + gatrun.m_rgchwBIGValues[8] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[9] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[10] = msbf(data16(75)); + gatrun.m_rgchwBIGValues[11] = msbf(data16(100)); + memcpy(pbBIG, &gatrun, 26); // 26 = 12*2 + 2 + pbBIG += 26; + + // Glyph 89 'Y' + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(8)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(12)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(16)); + memcpy(pbBIG, &gatrun, 8); // 8 = 3*2 + 2 + pbBIG += 8; + gatrun.m_bMinAttrID = 8; // comp.x.top + gatrun.m_cAttrs = 12; + gatrun.m_rgchwBIGValues[0] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[3] = msbf(data16(33)); + gatrun.m_rgchwBIGValues[4] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[5] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[6] = msbf(data16(33)); + gatrun.m_rgchwBIGValues[7] = msbf(data16(67)); + gatrun.m_rgchwBIGValues[8] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[9] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[10] = msbf(data16(67)); + gatrun.m_rgchwBIGValues[11] = msbf(data16(100)); + memcpy(pbBIG, &gatrun, 26); // 26 = 12*2 + 2 + pbBIG += 26; + + // Glyph 90 'Z' + gatrun.m_bMinAttrID = 1; + gatrun.m_cAttrs = 3; + gatrun.m_rgchwBIGValues[0] = msbf(data16(8)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(12)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(16)); + memcpy(pbBIG, &gatrun, 8); // 8 = 3*2 + 2 + pbBIG += 8; + gatrun.m_bMinAttrID = 8; // comp.x.top + gatrun.m_cAttrs = 12; + gatrun.m_rgchwBIGValues[0] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[3] = msbf(data16(10)); + gatrun.m_rgchwBIGValues[4] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[5] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[6] = msbf(data16(10)); + gatrun.m_rgchwBIGValues[7] = msbf(data16(90)); + gatrun.m_rgchwBIGValues[8] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[9] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[10] = msbf(data16(90)); + gatrun.m_rgchwBIGValues[11] = msbf(data16(100)); + memcpy(pbBIG, &gatrun, 26); // 26 = 12*2 + 2 + pbBIG += 26; + + Assert(pbBIG == m_prgbBIGEntries + 102); +} + +void GrLineBreakPass::SetUpLigatureTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 5; + m_staBehavior = "Ligatures"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpLigatureTest(m_ipass); + + m_crul = 1; + + // Set up constraint code--succeed trivially. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[8]; + pb = m_prgbActionBlock; + + // Rule 0: clsLetter { break = 2 } + + *pb++ = kopPutCopy; *pb++ = 0; // PutCopy 0 + *pb++ = kopPushByte; *pb++ = 2; // PushByte 2 + *pb++ = kopAttrSet; *pb++ = kslatBreak; // AttrSet breakweight + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; +} + +void GrSubPass::SetUpLigatureTest() +{ + if (m_ipass == 2) + { + m_nMaxRuleContext = m_nMaxChunk = 4; + m_nMaxRuleLoop = 5; + m_staBehavior = "Ligatures"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpLigatureTest(m_ipass); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1 + 10 + 10]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + *pb++ = kopCntxtItem; *pb++ = 2; // ContextItem 2 + *pb++ = kopPushSlotAttr;*pb++ = kslatBreak; // PushSlotAttr break 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 2; // Push 2 + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + *pb++ = kopCntxtItem; *pb++ = 1; // ContextItem 1 + *pb++ = kopPushSlotAttr;*pb++ = kslatBreak; // PushSlotAttr break 0 + *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 2; // Push 2 + *pb++ = kopEqual; // Equal + *pb++ = kopOr; // Or + *pb++ = kopPopRet; // PopAndRet + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 1; + m_prgibConstraintStart[1] = 11; + m_prgibConstraintStart[2] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[29 + 29 + 29]; + pb = m_prgbActionBlock; + + // Rule 0: gX gY gZ > gCapX:(1 2 4) { comp {X.ref=@1; Y.ref=@2; Z.ref=@4 }} _ _ + // / _ _ # {break==2} _; + + *pb++ = kopPutGlyph8bitObs; *pb++ = 0; // PutGlyph 0 (gCapX) + *pb++ = kopAssoc; *pb++ = 3; // Assoc 3 0 1 3 + *pb++ = 0; *pb++ = 1; *pb++ = 3; + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref x + *pb++ = kslatCompRef; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = 1; // Push 1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref y + *pb++ = kslatCompRef; *pb++ = 2; + *pb++ = kopPushByte; *pb++ = 3; // Push 3 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref z + *pb++ = kslatCompRef; *pb++ = 3; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: gX gY gZ > _ _ gCapZ:(1 3 4) { comp {X.ref=@1; Y.ref=@3; Z.ref=@4 }} + // / _ # {break==2} _ _; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopCopyNext; // CopyNext + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutGlyph8bitObs; *pb++ = 2; // PutGlyph 2 (gCapZ) + *pb++ = kopAssoc; *pb++ = 3; // Assoc 3 -3 -1 0 + *pb++ = -3; *pb++ = -1; *pb++ = 0; + *pb++ = kopPushByte; *pb++ = -3; // Push -3 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref x + *pb++ = kslatCompRef; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref y + *pb++ = kslatCompRef; *pb++ = 2; + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref z + *pb++ = kslatCompRef; *pb++ = 3; + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 2: gX gY gZ > _ gCapY:(1 2 3) { comp {X.ref=@1; Y.ref=@2; Z.ref=@3 }} gPlus; + + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopPutGlyph8bitObs; *pb++ = 1; // PutGlyph 1 (gCapY) + *pb++ = kopAssoc; *pb++ = 3; // Assoc 3 -1 0 1 + *pb++ = -1; *pb++ = 0; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = -1; // Push -1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref x + *pb++ = kslatCompRef; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref y + *pb++ = kslatCompRef; *pb++ = 2; + *pb++ = kopPushByte; *pb++ = 1; // Push 1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot comp_ref z + *pb++ = kslatCompRef; *pb++ = 3; + *pb++ = kopNext; // Next + *pb++ = kopPutGlyph8bitObs; *pb++ = 3; // PutGlyph 3 (gPlus) + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 29; + m_prgibActionStart[2] = 29 + 29; + } + else + Assert(false); +} + + +void GrFSM::SetUpLigatureTest(int ipass) +{ + if (ipass == 1) + { + // Create machine class ranges. + m_cmcr = 1; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 97; // a - z + m_prgmcr[0].m_chwLast = 122; + m_prgmcr[0].m_col = 0; + + m_dimcrInit = 1; // (max power of 2 <= m_cmcr); + m_cLoop = 0; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 2; + m_crowNonAcpt = 1; + m_crowFinal = 1; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 1; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 1 + short * psn = m_prgrowTransitions; + *psn++ = 1; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[1]; // 1 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[m_crow - m_crowNonAcpt + 1]; + + m_prgirulnMin[0] = 0; // s1: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; + } + else if (ipass == 2) + { + // Create machine class ranges. + m_cmcr = 4; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 35; // # + m_prgmcr[0].m_chwLast = 35; + m_prgmcr[0].m_col = 3; + + m_prgmcr[1].m_chwFirst = 120; // x + m_prgmcr[1].m_chwLast = 120; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 121; // y + m_prgmcr[2].m_chwLast = 121; + m_prgmcr[2].m_col = 1; + + m_prgmcr[3].m_chwFirst = 122; // z + m_prgmcr[3].m_chwLast = 123; + m_prgmcr[3].m_col = 2; + + + m_dimcrInit = 4; // (max power of 2 <= m_cmcr); + m_cLoop = 2; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 9; + m_crowNonAcpt = 6; + m_crowFinal = 3; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 4; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 24 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 0; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 2; *psn++ = 0; *psn++ = 3; + *psn++ = 0; *psn++ = 0; *psn++ = 6; *psn++ = 4; + *psn++ = 0; *psn++ = 5; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 7; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 8; *psn++ = 0; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[3+1]; // 3 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s6: r2 + m_prgrulnMatched[0] = 2; + + m_prgirulnMin[1] = 1; // s7: r0 + m_prgrulnMatched[1] = 0; + + m_prgirulnMin[2] = 2; // s8: r1 + m_prgrulnMatched[2] = 1; + + m_prgirulnMin[3] = 3; + } +} + + +/*---------------------------------------------------------------------------------------------- + Tests of ligatures and components where the components are modified glyphs. + + PASS 1: + r0. gX gI > @2 @1; + r1. clsPlainVowel gAcute > clsVowelAcute:(1 2) _; + r2. clsPlainVowel gGrave > clsVowelGrave:(1 2) _; + + Pass 1 machine classes: + mcX: x 120 + mcI: i 105 + mcPV: a e o u 97 101 111 117 + mcAcute: / 47 + mcGrave: \ 92 + + Pass 1 FSM: (T=transition, F=final, A=accepting, NA=non-accepting) + + | mcX mcPV mcI mcAcute mcGrave + --------------------------------------------------------------- + T NA s0 | s1 s2 s2 + | - - - - + T NA s1 | s3 + | - - - - + T NA s2 | s4 s5 + | - - - - + F A s3 | + | - - - - + F A s4 | + | - - - - + F A s5 | + | - - - - + + + Rules Matched: + s3: r0 + s4: r1 + s5: r2 + + + PASS 2: + r0. clsVowel gI > clsCapVowel:(1 2) { comp {v.ref = @1; i.ref = @2}} _; + + Pass 2 machine classes: + mcV: 97 101 111 117 + 224 225 232 233 236 237 242 243 249 250 + mcI: i 105 + + + Pass 2 FSM: + | mcV mcI + ---------------------------------------------------- + T NA s0 | s1 s1 + | - + T NA s1 | s2 + | - + F A s2 | + | - + + Rules matched: + s2: r0 +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpLigature2Test() +{ + if (m_pctbl) + delete m_pctbl; + + m_pctbl = new GrClassTable(); + m_pctbl->SetUpLigature2Test(); + + m_pgtbl = new GrGlyphTable(); + m_pgtbl->SetUpLigature2Test(); +} + +void GrClassTable::SetUpLigature2Test() +{ + m_ccls = 5; // number of classes + m_cclsLinear = 3; // number of classes in linear format + + m_prgchwBIGGlyphList = new data16[73]; + + m_prgichwOffsets = new data16[m_ccls+1]; + + data16 * pchw = m_prgchwBIGGlyphList; + + // Output class 0: clsVowelAcute + m_prgichwOffsets[0] = 0; + *pchw++ = msbf(data16(225)); // a-acute + *pchw++ = msbf(data16(233)); // e-acute + *pchw++ = msbf(data16(237)); // i-acute + *pchw++ = msbf(data16(243)); // o-acute + *pchw++ = msbf(data16(250)); // u-acute + + // Output class 1: clsVowelGrave + m_prgichwOffsets[1] = 5; + *pchw++ = msbf(data16(224)); // a-grave + *pchw++ = msbf(data16(232)); // e-grave + *pchw++ = msbf(data16(236)); // i-grave + *pchw++ = msbf(data16(242)); // o-grave + *pchw++ = msbf(data16(249)); // u-grave + + // Output class 2: clsCapVowel + m_prgichwOffsets[2] = 5 + 5; // 10 + *pchw++ = msbf(data16(65)); // A + *pchw++ = msbf(data16(69)); // E + *pchw++ = msbf(data16(73)); // I + *pchw++ = msbf(data16(79)); // O + *pchw++ = msbf(data16(85)); // U + *pchw++ = msbf(data16(193)); // A-acute + *pchw++ = msbf(data16(201)); // E-acute + *pchw++ = msbf(data16(205)); // I-acute + *pchw++ = msbf(data16(211)); // O-acute + *pchw++ = msbf(data16(218)); // U-acute + *pchw++ = msbf(data16(192)); // A-grave + *pchw++ = msbf(data16(200)); // E-grave + *pchw++ = msbf(data16(204)); // I-grave + *pchw++ = msbf(data16(210)); // O-grave + *pchw++ = msbf(data16(217)); // U-grave + + // Input class 3: clsPlainVowel + m_prgichwOffsets[3] = 10 + 15; // 25 + *pchw++ = msbf(data16(5)); + *pchw++ = msbf(data16(4)); *pchw++ = msbf(data16(2)); *pchw++ = msbf(data16(5-4)); + *pchw++ = msbf(data16(97)); *pchw++ = msbf(data16(0)); // a + *pchw++ = msbf(data16(101)); *pchw++ = msbf(data16(1)); // e + *pchw++ = msbf(data16(105)); *pchw++ = msbf(data16(2)); // i + *pchw++ = msbf(data16(111)); *pchw++ = msbf(data16(3)); // o + *pchw++ = msbf(data16(117)); *pchw++ = msbf(data16(4)); // u + + // Input class 4: clsVowel + m_prgichwOffsets[4] = 25 + 4+(5*2); // 39 + *pchw++ = msbf(data16(15)); + *pchw++ = msbf(data16(8)); *pchw++ = msbf(data16(3)); *pchw++ = msbf(data16(15-8)); + *pchw++ = msbf(data16(97)); *pchw++ = msbf(data16(0)); // a + *pchw++ = msbf(data16(101)); *pchw++ = msbf(data16(1)); // e + *pchw++ = msbf(data16(105)); *pchw++ = msbf(data16(2)); // i + *pchw++ = msbf(data16(111)); *pchw++ = msbf(data16(3)); // o + *pchw++ = msbf(data16(117)); *pchw++ = msbf(data16(4)); // u + *pchw++ = msbf(data16(224)); *pchw++ = msbf(data16(10)); // a-grave + *pchw++ = msbf(data16(225)); *pchw++ = msbf(data16(5)); // a-acute + *pchw++ = msbf(data16(232)); *pchw++ = msbf(data16(11)); // e-grave + *pchw++ = msbf(data16(233)); *pchw++ = msbf(data16(6)); // e-acute + *pchw++ = msbf(data16(236)); *pchw++ = msbf(data16(12)); // i-grave + *pchw++ = msbf(data16(237)); *pchw++ = msbf(data16(7)); // i-acute + *pchw++ = msbf(data16(242)); *pchw++ = msbf(data16(13)); // o-grave + *pchw++ = msbf(data16(243)); *pchw++ = msbf(data16(8)); // o-acute + *pchw++ = msbf(data16(249)); *pchw++ = msbf(data16(14)); // u-grave + *pchw++ = msbf(data16(250)); *pchw++ = msbf(data16(9)); // u-acute + + m_prgichwOffsets[5] = 39 + 4+(15*2); // 73 +} + +void GrGlyphTable::SetUpLigature2Test() +{ + SetNumberOfGlyphs(256); + SetNumberOfStyles(1); + + GrGlyphSubTable * pgstbl = new GrGlyphSubTable(); + Assert(pgstbl); + + pgstbl->Initialize(1, 0, 256, 10, 4); + SetSubTable(0, pgstbl); + + SetNumberOfComponents(2); // comp.base, comp.i + + pgstbl->SetUpLigature2Test(); +} + +/*********************************************************************************************** + TODO: This method is BROKEN because m_prgibBIGAttrValues has been changed. It is no + longer a data16 *. The Gloc table can contain 16-bit or 32-bit entries and must be + accessed accordingly. +***********************************************************************************************/ +void GrGlyphSubTable::SetUpLigature2Test() +{ + m_nAttrIDLim = 10; + + m_pgatbl = new GrGlyphAttrTable(); + m_pgatbl->Initialize(0, 330); + + for (int i = 0; i <= 65; i++) // A + m_prgibBIGAttrValues[i] = (byte)msbf(data16(0)); + + for (i = 66; i <= 69; i++) // E + m_prgibBIGAttrValues[i] = (byte)msbf(data16(22)); + + for (i = 70; i <= 73; i++) // I + m_prgibBIGAttrValues[i] = (byte)msbf(data16(44)); + + for (i = 74; i <= 79; i++) // O + m_prgibBIGAttrValues[i] = (byte)msbf(data16(66)); + + for (i = 80; i <= 85; i++) // U + m_prgibBIGAttrValues[i] = (byte)msbf(data16(88)); + + for (i = 86; i <= 192; i++) // A-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(110)); + + m_prgibBIGAttrValues[193] = (byte)msbf(data16(132)); // A-acute + + for (i = 194; i <= 200; i++) // E-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(154)); + + m_prgibBIGAttrValues[201] = (byte)msbf(data16(176)); // E-acute + + for (i = 202; i <= 204; i++) // I-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(198)); + + m_prgibBIGAttrValues[205] = (byte)msbf(data16(220)); // I-acute + + for (i = 206; i <= 210; i++) // O-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(242)); + + m_prgibBIGAttrValues[211] = (byte)msbf(data16(264)); // O-acute + + for (i = 212; i <= 217; i++) // U-grave + m_prgibBIGAttrValues[i] = (byte)msbf(data16(286)); + + m_prgibBIGAttrValues[218] = (byte)msbf(data16(308)); // O-acute + + for (i = 219; i < 256; i++) + m_prgibBIGAttrValues[i] = (byte)msbf(data16(330)); + + m_pgatbl->SetUpLigature2Test(); +} + +void GrGlyphAttrTable::SetUpLigature2Test() +{ + // All ligatures have the following defined: + // + // 0 = 2 comp.base (0,30, 100,100) + // 1 = 6 comp.i (0,0, 100,30) + // 2= 100 comp.base.top + // 3= 30 bottom + // 4= 0 left + // 5= 100 right + // 6 = 30 comp.i.top + // 7 = 0 bottom + // 8= 0 left + // 9= 100 right + + byte * pbBIG = m_prgbBIGEntries; + + GrGlyphAttrRun gatrun; + + for (int i = 0; i < 15; i++) + { + gatrun.m_bMinAttrID = 0; + gatrun.m_cAttrs = 10; + gatrun.m_rgchwBIGValues[0] = msbf(data16(2)); + gatrun.m_rgchwBIGValues[1] = msbf(data16(6)); + gatrun.m_rgchwBIGValues[2] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[3] = msbf(data16(30)); + gatrun.m_rgchwBIGValues[4] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[5] = msbf(data16(100)); + gatrun.m_rgchwBIGValues[6] = msbf(data16(30)); + gatrun.m_rgchwBIGValues[7] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[8] = msbf(data16(0)); + gatrun.m_rgchwBIGValues[9] = msbf(data16(100)); + memcpy(pbBIG, &gatrun, 22); // 22 = 10*2 + 2 + pbBIG += 22; + } + + Assert(pbBIG == m_prgbBIGEntries + (22 * 15)); +} + +void GrSubPass::SetUpLigature2Test() +{ + if (m_ipass == 1) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 5; + m_staBehavior = "Ligatures2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpLigature2Test(m_ipass); + + m_crul = 3; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + m_prgibConstraintStart[1] = 0; + m_prgibConstraintStart[2] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[7 + 12 + 12]; + pb = m_prgbActionBlock; + + // Rule 0: gX gI > @2 @1; + + *pb++ = kopPutCopy; *pb++ = 1; // PutCopy 1 + *pb++ = kopNext; // Next + *pb++ = kopPutCopy; *pb++ = -1; // PutCopy -1 + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 1: clsPlainVowel gAcute > clsVowelAcute:(1 2) _; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 3 0 + *pb++ = 3; *pb++ = 0; + *pb++ = kopAssoc; *pb++ = 2; // Assoc 2 0 1 + *pb++ = 0; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + // Rule 2: clsPlainVowel gGrave > clsVowelGrave:(1 2) _; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 3 1 + *pb++ = 3; *pb++ = 1; + *pb++ = kopAssoc; *pb++ = 2; // Assoc 2 0 1 + *pb++ = 0; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + m_prgibActionStart[1] = 7; + m_prgibActionStart[2] = 7 + 12; + } + else if (m_ipass == 2) + { + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 5; + m_staBehavior = "Ligatures2"; + + m_pfsm = new GrFSM(); + Assert(m_pfsm); + m_pfsm->SetUpLigature2Test(m_ipass); + + m_crul = 1; + + // Set up constraint code. + m_prgbConstraintBlock = new byte[1]; + byte * pb = m_prgbConstraintBlock; + *pb++ = kopRetTrue; // RetTrue + + m_prgibConstraintStart = new data16[m_crul]; + m_prgibConstraintStart[0] = 0; + + // Set up rule action code. + m_prgbActionBlock = new byte[7 + 12 + 12]; + pb = m_prgbActionBlock; + + // Rule 0: clsVowel gI > clsCapVowel:(1 2) { comp {v.ref = @1; i.ref = @2}} _; + + *pb++ = kopPutSubs8bitObs; *pb++ = 0; // PutSubs 0 4 2 + *pb++ = 4; *pb++ = 2; + *pb++ = kopAssoc; *pb++ = 2; // Assoc 2 0 1 + *pb++ = 0; *pb++ = 1; + *pb++ = kopPushByte; *pb++ = 0; // Push 0 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot compRef v + *pb++ = kslatCompRef; *pb++ = 0; + *pb++ = kopPushByte; *pb++ = 1; // Push 1 + *pb++ = kopIAttrSetSlot; // IAttrSetSlot compRef i + *pb++ = kslatCompRef; *pb++ = 1; + *pb++ = kopNext; // Next + *pb++ = kopDelete; // Delete + *pb++ = kopNext; // Next + *pb++ = kopRetZero; // RetZero + + m_prgibActionStart = new data16[m_crul]; + m_prgibActionStart[0] = 0; + } + else + Assert(false); +} + + +void GrFSM::SetUpLigature2Test(int ipass) +{ + if (ipass == 1) + { + // Create machine class ranges. + m_cmcr = 8; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 47; // acute + m_prgmcr[0].m_chwLast = 47; + m_prgmcr[0].m_col = 3; + + m_prgmcr[1].m_chwFirst = 92; // grave + m_prgmcr[1].m_chwLast = 92; + m_prgmcr[1].m_col = 4; + + m_prgmcr[2].m_chwFirst = 97; // a + m_prgmcr[2].m_chwLast = 97; + m_prgmcr[2].m_col = 1; + + m_prgmcr[3].m_chwFirst = 101; // e + m_prgmcr[3].m_chwLast = 101; + m_prgmcr[3].m_col = 1; + + m_prgmcr[4].m_chwFirst = 105; // i + m_prgmcr[4].m_chwLast = 105; + m_prgmcr[4].m_col = 2; + + m_prgmcr[5].m_chwFirst = 111; // o + m_prgmcr[5].m_chwLast = 111; + m_prgmcr[5].m_col = 1; + + m_prgmcr[6].m_chwFirst = 117; // u + m_prgmcr[6].m_chwLast = 117; + m_prgmcr[6].m_col = 1; + + m_prgmcr[7].m_chwFirst = 120; // x + m_prgmcr[7].m_chwLast = 120; + m_prgmcr[7].m_col = 0; + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 6; + m_crowNonAcpt = 3; + m_crowFinal = 3; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 5; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 15 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 2; *psn++ = 2; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 3; *psn++ = 0; *psn++ = 0; + *psn++ = 0; *psn++ = 0; *psn++ = 0; *psn++ = 4; *psn++ = 5; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[3]; // 3 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[3+1]; // 3 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s3: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; // s4: r1 + m_prgrulnMatched[1] = 1; + + m_prgirulnMin[2] = 2; // s5: r2 + m_prgrulnMatched[2] = 2; + + m_prgirulnMin[3] = 3; + } + else if (ipass == 2) + { + // Create machine class ranges. + m_cmcr = 10; + m_prgmcr = new GrFSMClassRange[m_cmcr]; + m_prgmcr[0].m_chwFirst = 97; // a + m_prgmcr[0].m_chwLast = 97; + m_prgmcr[0].m_col = 0; + + m_prgmcr[1].m_chwFirst = 101; // e + m_prgmcr[1].m_chwLast = 101; + m_prgmcr[1].m_col = 0; + + m_prgmcr[2].m_chwFirst = 105; // i + m_prgmcr[2].m_chwLast = 105; + m_prgmcr[2].m_col = 1; + + m_prgmcr[3].m_chwFirst = 111; // o + m_prgmcr[3].m_chwLast = 111; + m_prgmcr[3].m_col = 0; + + m_prgmcr[4].m_chwFirst = 117; // u + m_prgmcr[4].m_chwLast = 117; + m_prgmcr[4].m_col = 0; + + m_prgmcr[5].m_chwFirst = 224; // a-grave, a-acute + m_prgmcr[5].m_chwLast = 225; + m_prgmcr[5].m_col = 0; + + m_prgmcr[6].m_chwFirst = 232; // e-grave, e-acute + m_prgmcr[6].m_chwLast = 233; + m_prgmcr[6].m_col = 0; + + m_prgmcr[7].m_chwFirst = 236; // i-grave, i-acute + m_prgmcr[7].m_chwLast = 237; + m_prgmcr[7].m_col = 0; + + m_prgmcr[8].m_chwFirst = 242; // o-grave, o-acute + m_prgmcr[8].m_chwLast = 243; + m_prgmcr[8].m_col = 0; + + m_prgmcr[9].m_chwFirst = 249; // u-grave, u-acute + m_prgmcr[9].m_chwLast = 250; + m_prgmcr[9].m_col = 0; + + + m_dimcrInit = 8; // (max power of 2 <= m_cmcr); + m_cLoop = 3; // log2(max power of 2 <= m_cmcr); + m_imcrStart = m_cmcr - m_dimcrInit; + + + m_crow = 3; + m_crowNonAcpt = 2; + m_crowFinal = 1; + m_rowFinalMin = m_crow - m_crowFinal; + m_ccol = 2; + + // Set up transition table. + m_prgrowTransitions = new short[(m_crow-m_crowFinal) * m_ccol]; // 4 + short * psn = m_prgrowTransitions; + *psn++ = 1; *psn++ = 1; + *psn++ = 0; *psn++ = 2; + + // Set up matched-rules tables. + m_prgrulnMatched = new data16[1]; // 1 = sum of rules matched for each accepting state + m_prgirulnMin = new data16[1+1]; // 1 = m_crow - m_crowNonAcpt + + m_prgirulnMin[0] = 0; // s2: r0 + m_prgrulnMatched[0] = 0; + + m_prgirulnMin[1] = 1; + } +} + +#endif // _DEBUG + +} // namespace gr + +#endif // OLD_TEST_STUFF + +//:End Ignore diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/TestPasses.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/TestPasses.cpp new file mode 100644 index 00000000000..a85e6c56207 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/TestPasses.cpp @@ -0,0 +1,1005 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 1999, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TestPasses.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Hard-coded passes for test procedures. +-------------------------------------------------------------------------------*//*:End Ignore*/ + +//:Ignore + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#undef THIS_FILE +DEFINE_THIS_FILE + +#ifdef OLD_TEST_STUFF + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +/*---------------------------------------------------------------------------------------------- + For release version. +----------------------------------------------------------------------------------------------*/ +#ifndef _DEBUG + +bool GrLineBreakPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrSubPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +bool GrPosPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +#endif // !_DEBUG + + +#ifdef _DEBUG + +/*---------------------------------------------------------------------------------------------- + Set up a table manager for testing. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::SetUpTest(std::wstring stuRendBehavior) +{ + if (stuRendBehavior == L"NoRules") + { + m_cpass = 2; + m_cpassLB = 0; + m_ipassPos1 = 1; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrPosPass(1); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpTestData(); + } + else if (stuRendBehavior == L"JustReorder" || stuRendBehavior == "RightToLeftLayout") + { + m_cpass = 4; + m_cpassLB = 0; + m_ipassPos1 = 3; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrBidiPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpReverseNumbersTest(); + m_prgppass[2]->SetUpTestData(); + m_prgppass[3]->SetUpTestData(); + + m_prgppass[2]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"CrossLineContext") + { + m_cpass = 4; + m_cpassLB = 1; + m_ipassPos1 = 3; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrLineBreakPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpCrossLineContextTest(); + m_prgppass[2]->SetUpCrossLineContextTest(); + m_prgppass[3]->SetUpTestData(); + } + else if (stuRendBehavior == L"Reprocess") + { + m_cpass = 6; + m_cpassLB = 1; + m_ipassPos1 = 5; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrLineBreakPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrSubPass(3); + m_prgppass[4] = new GrBidiPass(4); + m_prgppass[5] = new GrPosPass(5); + + m_prgppass[0]->SetUpTestData(); // glyph-gen pass + m_prgppass[1]->SetUpReprocessTest(); // line-break pass + m_prgppass[2]->SetUpReverseNumbersTest(); // sub pass 1 + m_prgppass[3]->SetUpReprocessTest(); // sub pass 2 + m_prgppass[4]->SetUpTestData(); // bidi pass + m_prgppass[5]->SetUpTestData(); // pos pass + + m_prgppass[4]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"LineEdgeContext") + { + m_cpass = 4; + m_cpassLB = 0; + m_ipassPos1 = 3; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpLineEdgeContextTest(1); + m_prgppass[2]->SetUpLineEdgeContextTest(2); + m_prgppass[3]->SetUpTestData(); + } + else if (stuRendBehavior == L"BidiAlgorithm") + { + m_cpass = 4; + m_cpassLB = 0; + m_ipassPos1 = 3; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrBidiPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpBidiAlgorithmTest(); + m_prgppass[2]->SetUpBidiAlgorithmTest(); + m_prgppass[3]->SetUpTestData(); + + m_prgppass[2]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"PseudoGlyphs") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpPseudoGlyphsTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"SimpleFSM") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpSimpleFSMTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"RuleAction") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpRuleActionTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"RuleAction2") + { + m_cpass = 6; + m_cpassLB = 0; + m_ipassPos1 = 5; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrSubPass(3); + m_prgppass[4] = new GrBidiPass(4); + m_prgppass[5] = new GrPosPass(5); + + m_prgppass[0]->SetUpTestData(); // glyph-gen pass + m_prgppass[1]->SetUpRuleAction2Test(); // sub pass 1 + m_prgppass[2]->SetUpRuleAction2Test(); // sub pass 2 + m_prgppass[3]->SetUpRuleAction2Test(); // sub pass 3 + m_prgppass[4]->SetUpTestData(); // bidi pass + m_prgppass[5]->SetUpTestData(); // pos pass + + m_prgppass[4]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"Assoc") + { + m_cpass = 5; + m_cpassLB = 1; + m_ipassPos1 = 4; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrLineBreakPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrSubPass(3); + m_prgppass[4] = new GrPosPass(4); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpAssocTest(); + m_prgppass[2]->SetUpAssocTest(); + m_prgppass[3]->SetUpAssocTest(); + m_prgppass[4]->SetUpTestData(); + } + else if (stuRendBehavior == L"Assoc2") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpAssoc2Test(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"DefaultAssoc") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpDefaultAssocTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"BidiNumbers") + { + m_cpass = 4; + m_cpassLB = 0; + m_ipassPos1 = 3; + m_fBidi = true; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrBidiPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpBidiNumbersTest(); + m_prgppass[2]->SetUpBidiNumbersTest(); + m_prgppass[3]->SetUpTestData(); + + m_prgppass[2]->SetTopDirLevel(TopDirectionLevel()); + } + else if (stuRendBehavior == L"Feature") + { + m_cpass = 3; + m_cpassLB = 0; + m_ipassPos1 = 2; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrPosPass(2); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpFeatureTest(); + m_prgppass[2]->SetUpTestData(); + } + else if (stuRendBehavior == L"Ligatures") + { + m_cpass = 4; + m_cpassLB = 1; + m_ipassPos1 = 3; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrLineBreakPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpLigatureTest(); + m_prgppass[2]->SetUpLigatureTest(); + m_prgppass[3]->SetUpTestData(); + } + else if (stuRendBehavior == L"Ligatures2") + { + m_cpass = 4; + m_cpassLB = 1; + m_ipassPos1 = 3; + m_fBidi = false; + + m_prgppass = new GrPass*[m_cpass]; + m_prgppass[0] = new GrGlyphGenPass(0); + m_prgppass[1] = new GrSubPass(1); + m_prgppass[2] = new GrSubPass(2); + m_prgppass[3] = new GrPosPass(3); + + m_prgppass[0]->SetUpTestData(); + m_prgppass[1]->SetUpLigature2Test(); + m_prgppass[2]->SetUpLigature2Test(); + m_prgppass[3]->SetUpTestData(); + } + else + Assert(false); + + m_engst.m_prgpsstrm = new GrSlotStream*[m_cpass]; + for (int ipass = 0; ipass < m_cpass; ++ipass) + m_engst.m_prgpsstrm[ipass] = new GrSlotStream(ipass); +} + + +/*---------------------------------------------------------------------------------------------- + Set the pass variables that normally will be read from the ECF. +----------------------------------------------------------------------------------------------*/ + +void GrPass::SetUpTestData() +{ + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 2; +} + +//void GrBidiPass::SetUpTestData() +//{ +// m_nMaxRuleContext = m_nMaxChunk = 1; +// m_nMaxRuleLoop = 2; +//} + +/*---------------------------------------------------------------------------------------------- + Call the appropriate test function. +----------------------------------------------------------------------------------------------*/ + +bool GrLineBreakPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + if (m_staBehavior == "Reprocess") + return RunReprocessTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "CrossLineContext") + return RunCrossLineContextTest(ptman, psstrmInput, psstrmOutput); + else + return false; +} + +bool GrSubPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + if (m_staBehavior == "ReverseNumbers") + return RunReverseNumbersTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "CrossLineContext") + return RunCrossLineContextTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "Reprocess") + return RunReprocessTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "LineEdgeContext") + return RunLineEdgeContextTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "BidiAlgorithm") + return RunBidiAlgorithmTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "PseudoGlyphs") + return RunPseudoGlyphsTest(ptman, psstrmInput, psstrmOutput); + else if (m_staBehavior == "BidiNumbers") + return RunBidiNumbersTest(ptman, psstrmInput, psstrmOutput); + else + return false; +} + +bool GrPosPass::RunTestRules(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + return false; +} + +/*---------------------------------------------------------------------------------------------- + A substitution pass that causes numbers to be reversed. Used by both the JustReorder test + and the Reprocess test. +----------------------------------------------------------------------------------------------*/ + +void GrSubPass::SetUpReverseNumbersTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_staBehavior = "ReverseNumbers"; +} + +bool GrSubPass::RunReverseNumbersTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + data16 chw0; + + // clsDigit { dir = DIR_RIGHT }; + if (psstrmInput->SlotsPending() >= 1) + { + bool fRtl = ptman->RightToLeft(); + chw0 = psstrmInput->Peek(0)->GlyphID(); + if ((chw0 >= '0') && (chw0 <= '9')) + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); + + GrSlotState * pslotNew; + ptman->NewSlotCopy(pslot0, m_ipass, &pslotNew); + pslotNew->SetDirectionality(fRtl ? kdircArabNum : kdircR); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + return true; + } + else + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); + + GrSlotState * pslotNew; + ptman->NewSlotCopy(pslot0, m_ipass, &pslotNew); + if (chw0 == 32) + pslotNew->SetDirectionality(kdircWhiteSpace); + else if (chw0 == '.') + pslotNew->SetDirectionality(kdircComSep); + else + pslotNew->SetDirectionality(fRtl ? kdircR : kdircL); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + return true; + } + } + else + return false; +} + +/*---------------------------------------------------------------------------------------------- + A substitution pass that will do some hard-coded actions resulting in cross-line-boundary + contextuals. +----------------------------------------------------------------------------------------------*/ +void GrEngine::SetUpCrossLineContextTest() +{ + m_fLineBreak = true; + m_cchwPreXlbContext = 2; + m_cchwPostXlbContext = 0; +} + +void GrLineBreakPass::SetUpCrossLineContextTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_staBehavior = "CrossLineContext"; +} + +void GrSubPass::SetUpCrossLineContextTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "CrossLineContext"; +} + +bool GrLineBreakPass::RunCrossLineContextTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + // '=' { break = intra }; + if ((psstrmInput->SlotsPending() >= 1) && + (psstrmInput->Peek(0)->GlyphID() == 61)) + { + GrSlotState * pslotNew0; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew0); + pslotNew0->SetBreakWeight(int(klbHyphenBreak)); + psstrmOutput->NextPut(pslotNew0); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + else + return false; +} + +bool GrSubPass::RunCrossLineContextTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + // any '=' _ _ > any '=' '=':2 any:1 / _ _ # _ _ + if ((psstrmInput->SlotsPending() >= 3) && + (psstrmInput->Peek(1)->GlyphID() == 61) && + (psstrmInput->Peek(2)->GlyphID() == ptman->LBGlyphID())) + { + GrSlotState * pslot0 = psstrmInput->Peek(0); + GrSlotState * pslot1 = psstrmInput->Peek(1); + gid16 chw0 = pslot0->GlyphID(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); +// GrFeatureValues fval1; +// pslot1->GetFeatureValues(&fval1); + + // copy the first 3 slots + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + // insert 2 more + GrSlotState * pslot3; + ptman->NewSlot(61, pslot1, NULL, m_ipass, &pslot3); + pslot3->Associate(pslot1); + psstrmOutput->NextPut(pslot3); + + GrSlotState * pslot4; + ptman->NewSlot(chw0, pslot0, NULL, m_ipass, &pslot4); + pslot4->Associate(pslot0); + psstrmOutput->NextPut(pslot4); + + return true; + } + else + return false; +} + +/*---------------------------------------------------------------------------------------------- + Line break and substitution passes that cause intra-pass reprocessing (ie, the ^ mechanism). +----------------------------------------------------------------------------------------------*/ + +void GrLineBreakPass::SetUpReprocessTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 2; + m_nMaxRuleLoop = 2; + m_staBehavior = "Reprocess"; +} + +bool GrLineBreakPass::RunReprocessTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chw; + + // '^' {lb=2} / _ lc + if ((psstrmInput->SlotsPending() >=2) && + (psstrmInput->Peek(0)->GlyphID() == '^') && + (((chw = psstrmInput->Peek(1)->GlyphID()) >= 'a') && + (chw <= 'z'))) + { + GrSlotState * pslotNew0; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew0); + pslotNew0->SetBreakWeight(int(klbHyphenBreak)); + psstrmOutput->NextPut(pslotNew0); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + else + return false; +} + + +void GrSubPass::SetUpReprocessTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "Reprocess"; +} + +bool GrSubPass::RunReprocessTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chw; + gid16 chwLB = ptman->LBGlyphID(); + + // symbol > @2 {dir = DIR_ARABNUMBER} / any {dir != DIR_LEFT} ^ _ + if ((psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(0)->Directionality() != kdircL) && + (((chw = psstrmInput->Peek(1)->GlyphID()) == '@') || + (chw == '@') || (chw == '$') || (chw == '%') || + (chw == '^') || (chw == '&') || (chw == '*'))) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + GrSlotState * pslotNew1; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew1); + pslotNew1->SetDirectionality(kdircArabNum); + psstrmOutput->NextPut(pslotNew1); + + psstrmOutput->SetPosForNextRule(-1, psstrmInput, false); + + return true; + } + + // '^' lc > @1 uc + else if ((psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(0)->GlyphID() == '^') && + (((chw = psstrmInput->Peek(1)->GlyphID()) >= 'a') && + (chw <= 'z'))) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + gid16 chwNew = chw - 'a' + 'A'; + GrSlotState * pslotNew1; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew1); + pslotNew1->SetGlyphID(chwNew); + psstrmOutput->NextPut(pslotNew1); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + + // '^' lc > @1 uc / _ # _ + else if ((psstrmInput->SlotsPending() >= 3) && + (psstrmInput->Peek(0)->GlyphID() == '^') && + (psstrmInput->Peek(1)->IsLineBreak(chwLB)) && + (((chw = psstrmInput->Peek(2)->GlyphID()) >= 'a') && + (chw <= 'z'))) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + gid16 chwNew = chw - 'a' + 'A'; + GrSlotState * pslotNew1; + ptman->NewSlotCopy(psstrmInput->NextGet(), m_ipass, &pslotNew1); + pslotNew1->SetGlyphID(chwNew); + psstrmOutput->NextPut(pslotNew1); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + + else + return false; +} + + +/*---------------------------------------------------------------------------------------------- + Substitution passes that create contextual forms at the line breaks; also a cross-line- + boundary contextual. +----------------------------------------------------------------------------------------------*/ + +void GrSubPass::SetUpLineEdgeContextTest(int ipass) +{ + m_nMaxRuleContext = (ipass == 2)? 5: 2; + m_nMaxChunk = m_nMaxRuleContext; + m_nMaxRuleLoop = 2; + m_staBehavior = "LineEdgeContext"; +} + +bool GrSubPass::RunLineEdgeContextTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chwLB = ptman->LBGlyphID(); + + // any _ _ > @1 '-' '-' / _ _ _ # + if ((m_ipass == 1) && + (psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(1)->IsLineBreak(chwLB))) + { + GrSlotState * pslot0 = psstrmInput->Peek(0); +// GrFeatureValues fval; +// pslot0->GetFeatureValues(&fval); + + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + GrSlotState * pslotNew1; + ptman->NewSlot('-', pslot0, NULL, m_ipass, &pslotNew1); + pslotNew1->Associate(pslot0); + psstrmOutput->NextPut(pslotNew1); + + GrSlotState * pslotNew2; + ptman->NewSlot('-', pslot0, NULL, m_ipass, &pslotNew2); + pslotNew2->Associate(pslot0); + psstrmOutput->NextPut(pslotNew2); + + return true; + } + + // _ _ any > '+' '+' @4 / # _ _ _ + else if ((m_ipass == 1) && + (psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(0)->IsLineBreak(chwLB))) + { + GrSlotState * pslot2 = psstrmInput->Peek(1); +// GrFeatureValues fval; +// pslot2->GetFeatureValues(&fval); + + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + GrSlotState * pslotNew0; + ptman->NewSlot('+', pslot2, NULL, m_ipass, &pslotNew0); + pslotNew0->Associate(pslot2); + psstrmOutput->NextPut(pslotNew0); + + GrSlotState * pslotNew1; + ptman->NewSlot('+', pslot2, NULL, m_ipass, &pslotNew1); + pslotNew1->Associate(pslot2); + psstrmOutput->NextPut(pslotNew1); + + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + return true; + } + + // '-' '-' '+' '+' > @1 _ _ @4 / _ _ # _ _ + else if ((m_ipass == 2) && + (psstrmInput->SlotsPending() >= 5) && + (psstrmInput->Peek(0)->GlyphID() == '-') && + (psstrmInput->Peek(1)->GlyphID() == '-') && + (psstrmInput->Peek(2)->IsLineBreak(chwLB)) && + (psstrmInput->Peek(3)->GlyphID() == '+') && + (psstrmInput->Peek(4)->GlyphID() == '+')) + { + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmInput->Skip(1); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + psstrmInput->Skip(1); + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + return true; + } + + else + return false; +} + +/*---------------------------------------------------------------------------------------------- + Substitution passes that do something special with pseudo glyphs. +----------------------------------------------------------------------------------------------*/ + +void GrSubPass::SetUpPseudoGlyphsTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 3; + m_nMaxRuleLoop = 2; + m_staBehavior = "PseudoGlyphs"; +} + +bool GrSubPass::RunPseudoGlyphsTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chwLB = ptman->LBGlyphID(); + + // pseudo > uppercase / _ ' ' + if ((psstrmInput->SlotsPending() >= 2) && + (psstrmInput->Peek(0)->GlyphID() > 1000) && // pseudo + (psstrmInput->Peek(1)->GlyphID() == 32)) + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval; +// pslot0->GetFeatureValues(&fval); + + GrSlotState * pslotNew; + ptman->NewSlotCopy(pslot0, m_ipass, &pslotNew); + pslotNew->SetGlyphID(pslot0->GlyphID() - 1000); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + + // pseudo > uppercase / _ | + // pseudo > uppercase / _ # + else if ((psstrmInput->SlotsPending() <= 2) && + (psstrmInput->Peek(0)->GlyphID() > 1000) && // pseudo + (psstrmInput->SlotsPending() < 2 || psstrmInput->Peek(1)->GlyphID() == 35)) + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval; +// pslot0->GetFeatureValues(&fval); + + GrSlotState * pslotNew; + ptman->NewSlotCopy(pslot0, m_ipass, &pslotNew); + pslotNew->SetGlyphID(pslot0->GlyphID() - 1000); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + psstrmOutput->SetPosForNextRule(0, psstrmInput, false); + + return true; + } + + // pseudo any _ > lowercase:1 @2 @1 / _ _ ^ _; + else if ((psstrmInput->SlotsPending() >= 3) && + (psstrmInput->Peek(0)->GlyphID() > 1000)) // pseudo + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval; +// pslot0->GetFeatureValues(&fval); + + GrSlotState * pslotNew0; + ptman->NewSlot(pslot0->GlyphID()-1000-65+97, pslot0, NULL, m_ipass, &pslotNew0); + pslotNew0->Associate(pslot0); + psstrmOutput->NextPut(pslotNew0); + + psstrmOutput->CopyOneSlotFrom(psstrmInput); + + GrSlotState * pslotNew2; + ptman->NewSlot(pslot0->GlyphID(), pslot0, NULL, m_ipass, &pslotNew2); + pslotNew2->Associate(pslot0); + psstrmOutput->NextPut(pslotNew2); + + psstrmOutput->SetPosForNextRule(-1, psstrmInput, false); + + return true; + } + else + return false; +} + +/*---------------------------------------------------------------------------------------------- + Substitution pass that tests reversing numbers. +----------------------------------------------------------------------------------------------*/ +void GrSubPass::SetUpBidiNumbersTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_staBehavior = "BidiNumbers"; +} + +bool GrSubPass::RunBidiNumbersTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chw0; + + // clsDigit > clsDigit { dir = DIR_ARABNUMBER }; + if ((psstrmInput->SlotsPending() >= 1) && + (((chw0 = psstrmInput->Peek(0)->GlyphID()) >= '0' && chw0 <= '9') || + (chw0 >= 'A' && chw0 <= 'Z') || (chw0 >= 'a' && chw0 <= 'z'))) + { + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); + + GrSlotState * pslotNew; + ptman->NewSlot(chw0, pslot0, NULL, m_ipass, &pslotNew); + if (chw0 >= '0' && chw0 <= '9') + pslotNew->SetDirectionality(kdircArabNum); + else // a-z and A-Z + pslotNew->SetDirectionality(kdircR); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + return true; + } + else + return false; +} + +void GrBidiPass::SetUpBidiNumbersTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_nTopDirection = 1; +} + +/*---------------------------------------------------------------------------------------------- + Substitution pass that tests the bidi algorithm thoroughly. +----------------------------------------------------------------------------------------------*/ +void GrSubPass::SetUpBidiAlgorithmTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_staBehavior = "BidiAlgorithm"; +} + +bool GrSubPass::RunBidiAlgorithmTest(GrTableManager * ptman, + GrSlotStream * psstrmInput, GrSlotStream * psstrmOutput) +{ + gid16 chw0; + + if (psstrmInput->SlotsPending() >= 1) + { + chw0 = psstrmInput->Peek(0)->GlyphID(); + + DirCode dircNew; + + if (chw0 >= '0' && chw0 <= '9') + dircNew = kdircEuroNum; + else if (chw0 >= 'a' && chw0 <= 'z') + dircNew = kdircL; + else if (chw0 >= 'A' && chw0 <= 'M') + dircNew = kdircR; + else if (chw0 >= 'N' && chw0 <= 'T') + dircNew = kdircRArab; + else if (chw0 >= 'U' && chw0 <= 'Z') + dircNew = kdircArabNum; + else if (chw0 == '$' || chw0 == '%') + dircNew = kdircEuroTerm; + else if (chw0 == '.' || chw0 == '*' || chw0 == '+') + dircNew = kdircEuroSep; + else if (chw0 == '@') + dircNew = kdircComSep; + else if (chw0 == '_' || chw0 == '=') + dircNew = kdircNeutral; + else if (chw0 == ':') + dircNew = kdircNSM; + else + return false; + + GrSlotState * pslot0 = psstrmInput->NextGet(); +// GrFeatureValues fval0; +// pslot0->GetFeatureValues(&fval0); + + GrSlotState * pslotNew; + ptman->NewSlot(chw0, pslot0, NULL, m_ipass, &pslotNew); + pslotNew->SetDirectionality(dircNew); + pslotNew->Associate(pslot0); + psstrmOutput->NextPut(pslotNew); + + return true; + } + else + return false; +} + +void GrBidiPass::SetUpBidiAlgorithmTest() +{ + m_nMaxRuleContext = m_nMaxChunk = 1; + m_nMaxRuleLoop = 2; + m_nTopDirection = 0; +} + + +#endif // _DEBUG + +} // namespace gr + +#endif // OLD_TEST_STUFF + +//:End Ignore diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/TransductionLog.cpp b/Build/source/libs/graphite/engine-2.3.1/src/segment/TransductionLog.cpp new file mode 100644 index 00000000000..9071e3fd462 --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/TransductionLog.cpp @@ -0,0 +1,2129 @@ +/*--------------------------------------------------------------------*//*:Ignore this sentence. +Copyright (C) 2000, 2001 SIL International. All rights reserved. + +Distributable under the terms of either the Common Public License or the +GNU Lesser General Public License, as specified in the LICENSING.txt file. + +File: TransductionLog.cpp +Responsibility: Sharon Correll +Last reviewed: Not yet. + +Description: + Contains the functions for writing a log of the transduction process. +----------------------------------------------------------------------------------------------*/ + +//:>******************************************************************************************** +//:> Include files +//:>******************************************************************************************** +#include "Main.h" + +#ifdef _MSC_VER +#pragma hdrstop +#endif +#include <cstring> +#include <math.h> + +#undef THIS_FILE +DEFINE_THIS_FILE + +//:End Ignore + +//:>******************************************************************************************** +//:> Forward declarations +//:>******************************************************************************************** + +//:>******************************************************************************************** +//:> Local Constants and static variables +//:>******************************************************************************************** + +namespace gr +{ + +//:>******************************************************************************************** +//:> Methods +//:>******************************************************************************************** + +#define SP_PER_SLOT 7 +#define LEADING_SP 15 +#define MAX_SLOTS 128 + +/*---------------------------------------------------------------------------------------------- + Output a file showing a log of the transduction process and the resulting segment. +----------------------------------------------------------------------------------------------*/ +//bool GrTableManager::WriteTransductionLog(GrCharStream * pchstrm, Segment * psegRet, +// int cbPrevSegDat, byte * pbPrevSegDat, byte * pbNextSegDat, int * pcbNextSegDat) +//{ +//#ifdef TRACING +// std::string staFile; +// if (!LogFileName(staFile)) +// return false; +// +// std::ofstream strmOut; +// if (cbPrevSegDat > 0) +// strmOut.open(staFile.c_str(), std::ios::app); // append +// else +// strmOut.open(staFile.c_str()); +// if (strmOut.fail()) +// return false; +// +// WriteXductnLog(strmOut, pchstrm, psegRet, cbPrevSegDat, pbPrevSegDat); +// +// strmOut.close(); +// return true; +//#else +// return false; +//#endif // TRACING +//} + +bool GrTableManager::WriteTransductionLog(std::ostream * pstrmLog, + GrCharStream * pchstrm, Segment * psegRet, int cbPrevSegDat, byte * pbPrevSegDat) +{ +#ifdef TRACING + if (!pstrmLog) + return false; + std::ostream & strmOut = *pstrmLog; + WriteXductnLog(strmOut, pchstrm, psegRet, cbPrevSegDat, pbPrevSegDat); + return true; +#else + return false; +#endif // TRACING +} + +/*---------------------------------------------------------------------------------------------- + Append to the file, showing the surface/underlying mappings. This must be done after + figuring out all the associations. + + Also write the final glyph positions, since they are now determined. +----------------------------------------------------------------------------------------------*/ +//bool GrTableManager::WriteAssociationLog(GrCharStream * pchstrm, Segment * psegRet) +//{ +//#ifdef TRACING +// std::string staFile; +// if (!LogFileName(staFile)) +// return false; +// +// std::ofstream strmOut; +// strmOut.open(staFile.c_str(), std::ios::app); // append +// if (strmOut.fail()) +// return false; +// +// LogFinalPositions(strmOut); +// +// strmOut << "\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n"; +// +// psegRet->LogUnderlyingToSurface(this, strmOut, pchstrm); +// psegRet->LogSurfaceToUnderlying(this, strmOut); +// +// strmOut << "\n\n=======================================================================\n\n"; +// +// strmOut.close(); +// return true; +//#else +// return false; +//#endif // TRACING +//} + + +bool GrTableManager::WriteAssociationLog(std::ostream * pstrmLog, + GrCharStream * pchstrm, Segment * psegRet) +{ +#ifdef TRACING + if (!pstrmLog) + return false; + + std::ostream & strmOut = *pstrmLog; + LogFinalPositions(strmOut); + + strmOut << "\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n"; + + psegRet->LogUnderlyingToSurface(this, strmOut, pchstrm); + psegRet->LogSurfaceToUnderlying(this, strmOut); + + strmOut << "\n\n=======================================================================\n\n"; + + return true; +#else + return false; +#endif // TRACING +} + +#ifdef TRACING + +/*---------------------------------------------------------------------------------------------- + Generate the name of the file where the log will be written. It goes in the directory + defined by either the TEMP or TMP environment variable. + + TODO: remove +----------------------------------------------------------------------------------------------*/ +//bool GrTableManager::LogFileName(std::string & staFile) +//{ +// char * pchTmpEnv = getenv("TEMP"); +// if (pchTmpEnv == 0) +// pchTmpEnv = getenv("TMP"); +// if (pchTmpEnv == 0) +// return false; +// +// staFile.assign(pchTmpEnv); +// if (staFile[staFile.size() - 1] != '\\') +// staFile.append("\\"); +// staFile.append("gr_xductn.log"); +// +// return true; +//} + +/*---------------------------------------------------------------------------------------------- + Output a file showing a log of the transduction process and the resulting segment. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::WriteXductnLog(std::ostream & strmOut, + GrCharStream * pchstrm, Segment * psegRet, + int cbPrevSegDat, byte * pbPrevSegDat) +{ + if (cbPrevSegDat == 0) + LogUnderlying(strmOut, pchstrm, 0); + else + { + Assert(*(pbPrevSegDat + 4) == 0); // skip offset for first pass + LogUnderlying(strmOut, pchstrm, *(pbPrevSegDat + 3)); + } + + LogPass1Input(strmOut); + + for (int ipass = 1; ipass < m_cpass; ipass++) + { + if (cbPrevSegDat == 0) + LogPassOutput(strmOut, ipass, 0); + else + LogPassOutput(strmOut, ipass, *(pbPrevSegDat + 4 + ipass)); + } + + // Do this later, after outputting final positions: + //strmOut << "\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n"; +} + +/*---------------------------------------------------------------------------------------------- + Write out a log of the underlying input. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogUnderlying(std::ostream & strmOut, GrCharStream * pchstrm, + int cchwBackup) +{ + strmOut << "UNDERLYING INPUT\n\n"; + + int rgnChars[MAX_SLOTS]; + bool rgfNewRun[MAX_SLOTS]; + std::fill_n(rgfNewRun, MAX_SLOTS, false); + GrFeatureValues rgfval[MAX_SLOTS]; + int cchwMaxRawChars; + + int cnUtf32 = pchstrm->GetLogData(this, rgnChars, rgfNewRun, rgfval, + cchwBackup, &cchwMaxRawChars); + cnUtf32 = min(cnUtf32, MAX_SLOTS); + + int ichw; + + // For UTF-8 and surrogate representation: + utf16 rgchwChars2[MAX_SLOTS]; + utf16 rgchwChars3[MAX_SLOTS]; + utf16 rgchwChars4[MAX_SLOTS]; + utf16 rgchwChars5[MAX_SLOTS]; + utf16 rgchwChars6[MAX_SLOTS]; + int rgcchwRaw[MAX_SLOTS]; + + if (cchwMaxRawChars > 1) + { + cchwMaxRawChars = min(cchwMaxRawChars, 6); // max of 6 raw (UTF-8 or UTF-16) chars per slot + pchstrm->GetLogDataRaw(this, cnUtf32, cchwBackup, cchwMaxRawChars, + rgnChars, rgchwChars2, rgchwChars3, rgchwChars4, rgchwChars5, rgchwChars6, + rgcchwRaw); + } + else + { + for (ichw = 0; ichw < cnUtf32; ichw++) + { + rgcchwRaw[ichw] = 1; + rgchwChars2[ichw] = 0; + rgchwChars3[ichw] = 0; + rgchwChars4[ichw] = 0; + rgchwChars5[ichw] = 0; + rgchwChars6[ichw] = 0; + } + } + + LogUnderlyingHeader(strmOut, pchstrm->Min(), (pchstrm->Min() + cnUtf32 - cchwBackup), + cchwBackup, rgcchwRaw); + + // Text + strmOut << "Text: "; // 15 spaces + for (ichw = 0; ichw < cnUtf32; ichw++) + { + if (rgnChars[ichw] < 0x0100 && rgchwChars2[ichw] == 0) // ANSI + strmOut << (char)rgnChars[ichw] << " "; // 6 spaces + else if (rgnChars[ichw] == knLRM) + strmOut << "<LRM> "; + else if (rgnChars[ichw] == knRLM) + strmOut << "<RLM> "; + else if (rgnChars[ichw] == knLRO) + strmOut << "<LRO> "; + else if (rgnChars[ichw] == knRLO) + strmOut << "<RLO> "; + else if (rgnChars[ichw] == knLRE) + strmOut << "<LRE> "; + else if (rgnChars[ichw] == knRLE) + strmOut << "<RLE> "; + else if (rgnChars[ichw] == knPDF) + strmOut << "<PDF> "; + else + strmOut << " "; + } + strmOut << "\n"; + + // Unicode + strmOut << "Unicode: "; + for (ichw = 0; ichw < cnUtf32; ichw++) + LogHexInTable(strmOut, utf16(rgnChars[ichw])); + strmOut << "\n"; + for (int icchRaw = 2; icchRaw <= cchwMaxRawChars; icchRaw++) + { + strmOut << " "; + for (ichw = 0; ichw < cnUtf32; ichw++) + { + utf16 chw; + switch (icchRaw) + { + case 2: chw = rgchwChars2[ichw]; break; + case 3: chw = rgchwChars3[ichw]; break; + case 4: chw = rgchwChars4[ichw]; break; + case 5: chw = rgchwChars5[ichw]; break; + case 6: chw = rgchwChars6[ichw]; break; + default: chw = 0; + } + if (chw == 0) + strmOut << " "; + else + LogHexInTable(strmOut, chw); + } + strmOut << "\n"; + } + + // Runs + strmOut << "Runs: "; + int crun = 0; + for (ichw = 0; ichw < cnUtf32; ichw++) + { + if (rgfNewRun[ichw]) + { + crun++; + strmOut << "|" << crun << ((crun < 10) ? " " : " "); + } + else + strmOut << " "; + } + strmOut << "\n"; + + // Features + strmOut << "Features and character properties:\n"; + crun = 0; + for (ichw = 0; ichw < cnUtf32; ichw++) + { + if (rgfNewRun[ichw]) + { + crun++; + strmOut << " Run " << crun << ": "; + rgfval[ichw].WriteXductnLog(this, strmOut); + + } + } +} + + +void GrFeatureValues::WriteXductnLog(GrTableManager * ptman, std::ostream & strmOut) +{ + bool fFirst = true; + for (int i = 0; i < kMaxFeatures; i++) + { + if (m_rgnFValues[i] != 0) + { + GrFeature * pfeat = ptman->Feature(i); + if (!fFirst) + strmOut << ","; + strmOut << pfeat->ID() << "=" << m_rgnFValues[i]; + fFirst = false; + } + } + if (fFirst) + strmOut << "all features=0"; + + strmOut << "\n\n"; +} + +/*---------------------------------------------------------------------------------------------- + Loop through the input stream of underlying characters, storing information in the + arrays for the log. + ENHANCE SharonC: do we need to show the underline properties? +----------------------------------------------------------------------------------------------*/ +int GrCharStream::GetLogData(GrTableManager * ptman, int * rgchl, bool * rgfNewRun, + GrFeatureValues * rgfval, int cchrBackup, int * pcchrMax) +{ + Assert(cchrBackup <= m_cchrBackedUp); + + int ichrPosToStop = m_ichrPos; + int ichrStart = m_ichrMin - max(cchrBackup, m_cchrBackedUp); + + *pcchrMax = 0; + + // Restart the stream. + m_ichrPos = ichrStart; + m_ichrRunMin = 0; + m_ichrRunLim = 0; + m_ichrRunOffset = kPosInfinity; + m_vislotNextChunkMap.clear(); + + int cchrSkipBackup = m_cchrBackedUp - cchrBackup; + + int c = 0; + int cchr = 0; + + while (m_ichrPos < ichrPosToStop) + { + if ((c < MAX_SLOTS) && (m_ichrPos >= m_ichrRunLim)) + { + rgfNewRun[c] = true; + } + + GrFeatureValues fval; + int ichrOffset, cchrThis; + int chl = NextGet(ptman, &fval, &ichrOffset, &cchrThis); + cchr += cchrThis; + if (cchr <= cchrSkipBackup) + {} // ignore - this is before the pre-segment stuff that we want to skip + else if (c < MAX_SLOTS) + { + rgchl[c] = chl; + *pcchrMax = max(*pcchrMax, cchrThis); + if (rgfNewRun[c]) + { + rgfval[c] = fval; + } + c++; + } + } + + ////return (m_ichwPos - (m_ichwMin - cchwBackup)); + return c; +} + +/*---------------------------------------------------------------------------------------------- + If any of the characters are comprised of surrogates or UTF-8, fill in the arrays with + the raw (UTF-16 or UTF-8) chars for display. To do this we get the raw characters + directly from the text source. +----------------------------------------------------------------------------------------------*/ +void GrCharStream::GetLogDataRaw(GrTableManager * ptman, int cchl, int cchrBackup, + int cchrMaxRaw, int * prgchl, + utf16 * prgchw2, utf16 * prgchw3, utf16 * prgchw4, utf16 * prgchw5, utf16 * prgchw6, + int * prgcchr) +{ + for (int i = 0; i < cchl; i++) + { + prgchw2[i] = 0; + } + + int ichrLim = m_ichrPos; + int ichrMin = m_ichrMin - cchrBackup; + + int cchrRange = ichrLim - ichrMin; + int ichr; + utf16 * prgchwRunText = new utf16[cchrRange]; + utf8 * prgchsRunText8 = NULL; + + UtfType utf = m_pgts->utfEncodingForm(); + + switch (utf) + { + case kutf8: + prgchsRunText8 = new utf8[cchrRange]; + m_pgts->fetch(ichrMin, cchrRange, prgchsRunText8); + for (int ichr = 0; ichr < cchrRange; ichr++) + prgchwRunText[ichr] = (utf16)prgchsRunText8[ichr]; // zero-extend into UTF-16 buffer + break; + case kutf16: + m_pgts->fetch(ichrMin, cchrRange, prgchwRunText); + break; + default: + case kutf32: // this method should never have been called + Assert(false); + for (int ichrLp = 0; ichrLp < cchrRange; ichrLp++) + { + prgcchr[ichrLp] = 1; + prgchw2[ichrLp] = 0; + prgchw3[ichrLp] = 0; + prgchw4[ichrLp] = 0; + prgchw5[ichrLp] = 0; + prgchw6[ichrLp] = 0; + } + return; + } + + ichr = ichrMin; + int ichl = 0; + while (ichr < ichrLim) + { + int cchrThis = 1; + // Replace 32-bit char with (first) 16-bit or 8-bit char. + prgchl[ichl] = (int)prgchwRunText[ichr - ichrMin]; + prgcchr[ichr] = cchrThis; + ichr++; + + while (!AtUnicodeCharBoundary(prgchwRunText, cchrRange, ichr - ichrMin, utf)) + { + cchrThis++; + switch (cchrThis) + { + case 2: prgchw2[ichl] = prgchwRunText[ichr - ichrMin]; break; + case 3: prgchw3[ichl] = prgchwRunText[ichr - ichrMin]; break; + case 4: prgchw4[ichl] = prgchwRunText[ichr - ichrMin]; break; + case 5: prgchw5[ichl] = prgchwRunText[ichr - ichrMin]; break; + case 6: prgchw6[ichl] = prgchwRunText[ichr - ichrMin]; break; + default: + break; + } + prgcchr[ichr] = cchrThis; + ichr++; + } + switch (cchrThis) + { + case 1: + prgchw2[ichl] = 0; + // fall through + case 2: + prgchw3[ichl] = 0; + // fall through + case 3: + prgchw4[ichl] = 0; + // fall through + case 4: + prgchw5[ichl] = 0; + // fall through + case 5: + prgchw6[ichl] = 0; + } + ichl++; + } + + delete[] prgchwRunText; + delete[] prgchsRunText8; +} + +/*---------------------------------------------------------------------------------------------- + Output the glyph IDs generated by the glyph generation pass (pass 0). +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogPass1Input(std::ostream & strmOut) +{ + strmOut << "INPUT TO PASS 1\n\n"; + + GrSlotStream * psstrm = OutputStream(0); + + LogSlotHeader(strmOut, psstrm->WritePos(), SP_PER_SLOT, LEADING_SP); + + LogSlotGlyphs(strmOut, psstrm); + + strmOut << "\n"; +} + +/*---------------------------------------------------------------------------------------------- + Output the the results of pass. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogPassOutput(std::ostream & strmOut, int ipass, int cslotSkipped) +{ + strmOut << "\n"; + + GrPass * ppass = Pass(ipass); + GrSlotStream * psstrmIn = InputStream(ipass); + GrSlotStream * psstrmOut = OutputStream(ipass); + + int islot; + // Mark each slot with its index in the input and output streams. + for (islot = 0; islot < psstrmIn->ReadPos(); islot++) + psstrmIn->SlotAt(islot)->m_islotTmpIn = islot; + + for (islot = 0; islot < psstrmOut->WritePos(); islot++) + psstrmOut->SlotAt(islot)->m_islotTmpOut = islot; + + if (!dynamic_cast<GrBidiPass *>(ppass)) + ppass->LogRulesFiredAndFailed(strmOut, psstrmIn); + + strmOut << "\nOUTPUT OF PASS " << ipass; + if (dynamic_cast<GrBidiPass *>(ppass)) + strmOut << " (bidi)"; + else if (dynamic_cast<GrSubPass *>(ppass)) + { + if (ipass >= m_ipassJust1) + strmOut << " (justification)"; + else + strmOut << " (substitution)"; + } + else if (dynamic_cast<GrPosPass *>(ppass)) + strmOut << " (positioning)"; + else if (dynamic_cast<GrLineBreakPass *>(ppass)) + strmOut << " (linebreak)"; + strmOut << "\n"; + + ppass->LogInsertionsAndDeletions(strmOut, psstrmOut); + + LogSlotHeader(strmOut, psstrmOut->WritePos(), SP_PER_SLOT, LEADING_SP); + + LogSlotGlyphs(strmOut, psstrmOut); + + bool fAnyPseudos = false; + if (dynamic_cast<GrPosPass *>(ppass)) + { + for (islot = 0; islot < psstrmOut->WritePos(); islot++) + { + GrSlotState * pslotTmp = psstrmOut->SlotAt(islot); + if (pslotTmp->GlyphID() != pslotTmp->ActualGlyphForOutput(this)) + { + fAnyPseudos = true; + break; + } + } + } + if (fAnyPseudos) + { + strmOut << "Actual glyphs: "; + for (islot = 0; islot < psstrmOut->WritePos(); islot++) + { + GrSlotState * pslotTmp = psstrmOut->SlotAt(islot); + if (pslotTmp->GlyphID() != pslotTmp->ActualGlyphForOutput(this)) + LogHexInTable(strmOut, pslotTmp->ActualGlyphForOutput(this)); + else + strmOut << " "; + } + strmOut << "\n"; + } + + LogAttributes(strmOut, ipass); + + // Do this later, after we're sure the positions have been set: + //if (ipass == m_cpass - 1) + // LogFinalPositions(strmOut); + + if (cslotSkipped > 0) + { + strmOut << "\n "; + for (islot = 0; islot < cslotSkipped; islot++) + strmOut << "SKIP "; + strmOut << "\n"; + } + + // If this was the pass just before the justification routines get run, output a + // special line that just shows the results, ie, the values of justify.width. + // TODO: adjust this when measure mode get functioning. + if (ipass == m_ipassJust1 - 1 && ShouldLogJustification() + && m_engst.m_jmodi == kjmodiJustify) + { + strmOut << "\nJUSTIFICATION\n\n"; + LogSlotHeader(strmOut, psstrmOut->WritePos(), SP_PER_SLOT, LEADING_SP); + LogSlotGlyphs(strmOut, psstrmOut); + LogAttributes(strmOut, ipass, true); + } +} + +/*---------------------------------------------------------------------------------------------- + Write the list of rules fired and failed for a given pass. +----------------------------------------------------------------------------------------------*/ +void GrPass::LogRulesFiredAndFailed(std::ostream & strmOut, GrSlotStream * psstrmIn) +{ + m_pzpst->LogRulesFiredAndFailed(strmOut, psstrmIn); +} + +void PassState::LogRulesFiredAndFailed(std::ostream & strmOut, GrSlotStream * psstrmIn) +{ + + strmOut << "PASS " << m_ipass << "\n\n" << "Rules matched: "; + if (m_crulrec == 0) + strmOut << "none"; + strmOut << "\n"; + + for (int irulrec = 0; irulrec < m_crulrec; irulrec++) + { + if (m_rgrulrec[irulrec].m_fFired) + strmOut << " * "; + else + strmOut << " "; + + strmOut << m_rgrulrec[irulrec].m_islot << ". "; + + if (m_rgrulrec[irulrec].m_irul == PassState::kHitMaxRuleLoop) + strmOut << "hit MaxRuleLoop\n"; + else if (m_rgrulrec[irulrec].m_irul == PassState::kHitMaxBackup) + strmOut << "hit MaxBackup\n"; + else + { + strmOut << "rule " << m_ipass << "." << m_rgrulrec[irulrec].m_irul; + + if (m_rgrulrec[irulrec].m_fFired) + strmOut << " FIRED\n"; + else + strmOut << " failed\n"; + } + } +} + +/*---------------------------------------------------------------------------------------------- + Write out the marks on top of the slot table indicating insertions and deletions. +----------------------------------------------------------------------------------------------*/ +void GrPass::LogInsertionsAndDeletions(std::ostream & strmOut, GrSlotStream * psstrmOut) +{ + m_pzpst->LogInsertionsAndDeletions(strmOut, psstrmOut); +} + +void PassState::LogInsertionsAndDeletions(std::ostream & strmOut, GrSlotStream * psstrmOut) +{ + int cslotDel = m_rgcslotDeletions[0]; + bool fIns; + if (cslotDel > 1) + strmOut << "\n DEL-" << cslotDel; + else if (cslotDel == 1) + strmOut << "\n DEL "; + else + strmOut << "\n "; + + for (int islot = 0; islot < psstrmOut->WritePos(); islot++) + { + cslotDel = (islot + 1 >= MAX_SLOTS) ? 0 : m_rgcslotDeletions[islot + 1]; + fIns = (islot >= MAX_SLOTS) ? 0 : m_rgfInsertion[islot]; + + if (fIns) + { + strmOut << "INS"; + if (cslotDel > 0) + { + if (cslotDel > 1) + strmOut << "/D-" << cslotDel; + else + strmOut << "/DEL"; + } + else + strmOut << " "; + + } + else if (cslotDel > 0) + { + if (cslotDel >= 10) + strmOut << "DEL-" << cslotDel << " "; + else if (cslotDel > 1) + strmOut << " DEL-" << cslotDel << " "; + else + strmOut << " DEL "; + } + else + strmOut << " "; + } + + strmOut << "\n"; +} + +/*---------------------------------------------------------------------------------------------- + Write out a line for each slot attribute that changed during the pass. + + @param fJustWidths - a special pass that writes out just the effects of the justification + routines, ie, the values of justify.width +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogAttributes(std::ostream & strmOut, int ipass, + bool fJustWidths) +{ + // Figure out which slot attributes were modified for some slot during the pass. + bool * prgfMods = new bool[kslatMax + NumUserDefn() - 1]; + + bool fPreJust = (!fJustWidths && ipass == m_ipassJust1 - 1 && ShouldLogJustification()); + bool fPostJust = ((fJustWidths || ipass == m_ipassJust1) && ShouldLogJustification()); + + int ccomp; // max number of components per slot + int cassoc; // max number of associations per slot + SlotAttrsModified(ipass, prgfMods, fPreJust, &ccomp, &cassoc); + + if (fPreJust) + { + // prgfMods[kslatJStretch] = true; + // prgfMods[kslatJShrink] = true; + // prgfMods[kslatJStep] = true; + // prgfMods[kslatWeight] = true; + prgfMods[kslatJWidth] = false; // output j.width in its own line (we call this + // method with fJustWidths == true) + } + else if (fPostJust) + prgfMods[kslatJWidth] = true; + + GrPass * ppass = Pass(ipass); + GrPass * ppassNext = (ipass < m_cpass - 1) ? Pass(ipass + 1) : NULL; + GrSlotStream * psstrm = OutputStream(ipass); + + if (fJustWidths) + {} + else if (dynamic_cast<GrBidiPass *>(ppass)) + { + // Special stuff for Bidi pass: + // Log value of directionality attribute. + strmOut << "directionality "; + int islot; + for (islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + LogDirCodeInTable(strmOut, pslot->DirProcessed()); + } + strmOut << "\n"; + + // Log final direction level for bidi pass. + strmOut << "dir level "; + for (islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + LogInTable(strmOut, pslot->DirLevel()); + } + strmOut << "\n"; + } + else if (ppassNext && dynamic_cast<GrBidiPass *>(ppassNext)) + { + // Next pass is Bidi: log input values of directionality attribute that are input for it. + strmOut << "directionality "; + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + LogDirCodeInTable(strmOut, pslot->Directionality()); + } + strmOut << "\n"; + } + + for (int slat = 0; slat < kslatMax + NumUserDefn() - 1; slat++) + { + int cIndexLim = 1; + if (slat == kslatCompRef) + cIndexLim = ccomp; + else if (slat == kslatUserDefn) + cIndexLim = 1; // kMaxUserDefinedSlotAttributes; + + if (fJustWidths && slat != kslatJWidth) + continue; + + bool fValidAttr = true; + for (int iIndex = 0; iIndex < cIndexLim; iIndex++) + { + if (prgfMods[slat]) + { + switch(slat) + { + case kslatAdvX: strmOut << "advance.x "; break; + case kslatAdvY: strmOut << "advance.y "; break; + case kslatAttTo: strmOut << "att.to "; break; + case kslatAttAtX: strmOut << "att.at.x "; break; + case kslatAttAtY: strmOut << "att.at.y "; break; + case kslatAttAtGpt: strmOut << "att.at.gpt "; break; + case kslatAttAtXoff: strmOut << "att.at.xoff "; break; + case kslatAttAtYoff: strmOut << "att.at.yoff "; break; + case kslatAttWithX: strmOut << "att.with.x "; break; + case kslatAttWithY: strmOut << "att.with.y "; break; + case kslatAttWithGpt: strmOut << "att.with.gpt "; break; + case kslatAttWithXoff: strmOut << "att.with.xoff "; break; + case kslatAttWithYoff: strmOut << "att.with.yoff "; break; + case kslatAttLevel: strmOut << "att.level "; break; + case kslatBreak: strmOut << "breakweight "; break; + case kslatCompRef: strmOut << "component " << iIndex + 1 // 1-based + << " "; break; + case kslatDir: strmOut << "dir "; break; + case kslatInsert: strmOut << "insert "; break; + case kslatMeasureSol: strmOut << "measure.sol "; break; + case kslatMeasureEol: strmOut << "measure.eol "; break; + case kslatJStretch: strmOut << "j.stretch "; break; + case kslatJShrink: strmOut << "j.shrink "; break; + case kslatJStep: strmOut << "j.step "; break; + case kslatJWeight: strmOut << "j.weight "; break; + case kslatJWidth: strmOut << "j.width "; break; + case kslatPosX: + case kslatPosY: + Assert(false); + break; + case kslatShiftX: strmOut << "shift.x "; break; + case kslatShiftY: strmOut << "shift.y "; break; + default: + if (kslatUserDefn <= slat && + slat < kslatUserDefn + NumUserDefn()) + { + strmOut << "user" << (slat - kslatUserDefn + 1) // 1-based + << ((iIndex >= 9) ? " " : " "); + } + else + { + // Invalid attribute: + Warn("bad slot attribute"); + fValidAttr = false; + break; + } + } + + if (!fValidAttr) + break; // out of iIndex loop + + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + pslot->LogSlotAttribute(this, strmOut, ipass, slat, iIndex, + fPreJust, fPostJust); + } + + strmOut << "\n"; + } + } + } + + if (fJustWidths) + goto LDone; + + for (int iassoc = 0; iassoc < cassoc; iassoc++) + { + // Log associations. Put them on one line it that will work; otherwise use several. + bool fBoth = (cassoc <= 2); + bool fAfter = (iassoc == (cassoc - 1)); + if (fBoth) + strmOut << "assocs "; + else if (iassoc == 0) + strmOut << "assocs-before "; + else if (fAfter) + strmOut << " -after "; + else + strmOut << " -other "; + + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + pslot->LogAssociation(this, strmOut, ipass, iassoc, fBoth, fAfter); + } + + strmOut << "\n"; + + if (fBoth) + break; + } + + if (cassoc == 0 && dynamic_cast<GrBidiPass *>(ppass)) + { + strmOut << "assocs "; + + // Log associations for all slots moved during the Bidi pass. + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->m_islotTmpIn != pslot->m_islotTmpOut) + LogInTable(strmOut, pslot->m_islotTmpIn); + else + strmOut << " "; + } + + strmOut << "\n"; + } + +LDone: + + delete[] prgfMods; +} + +/*---------------------------------------------------------------------------------------------- + Write out the final positions. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogFinalPositions(std::ostream & strmOut) +{ + GrSlotStream * psstrm = OutputStream(m_cpass - 1); + + strmOut << "x position "; + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsLineBreak(LBGlyphID())) + { + strmOut << " "; + continue; + } + LogInTable(strmOut, pslot->XPosition()); + } + strmOut << "\n"; + + strmOut << "y position "; + for (int islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslot = psstrm->SlotAt(islot); + if (pslot->IsLineBreak(LBGlyphID())) + { + strmOut << " "; + continue; + } + LogInTable(strmOut, pslot->YPosition()); + } + strmOut << "\n"; +} +#endif // TRACING + +/*---------------------------------------------------------------------------------------------- + Write out the final underlying-to-surface associations. +----------------------------------------------------------------------------------------------*/ +void Segment::LogUnderlyingToSurface(GrTableManager * ptman, std::ostream & strmOut, + GrCharStream * pchstrm) +{ +#ifdef TRACING + strmOut << "\n\nUNDERLYING TO SURFACE MAPPINGS\n\n"; + + size_t cassocs = 0; + int fLigs = false; + int ichw; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (m_prgpvisloutAssocs[ichw]) + cassocs = max(cassocs, m_prgpvisloutAssocs[ichw]->size()); + if (m_prgisloutLigature[ichw] != kNegInfinity) + fLigs = true; + } + +// ptman->LogSlotHeader(strmOut, m_ichwAssocsLim, SP_PER_SLOT, LEADING_SP, m_ichwAssocsMin); + ptman->LogUnderlyingHeader(strmOut, pchstrm->Min(), (pchstrm->Min() + m_ichwAssocsLim), + -m_ichwAssocsMin, NULL); + + int rgnChars[MAX_SLOTS]; + bool rgfNewRun[MAX_SLOTS]; + std::fill_n(rgfNewRun, MAX_SLOTS, false); + GrFeatureValues rgfval[MAX_SLOTS]; + int cchwMaxRawChars; + + int cchw = pchstrm->GetLogData(ptman, rgnChars, rgfNewRun, rgfval, + -m_ichwAssocsMin, &cchwMaxRawChars); + cchw = min(cchw, MAX_SLOTS); + + utf16 rgchwChars2[MAX_SLOTS]; + utf16 rgchwChars3[MAX_SLOTS]; + utf16 rgchwChars4[MAX_SLOTS]; + utf16 rgchwChars5[MAX_SLOTS]; + utf16 rgchwChars6[MAX_SLOTS]; + int rgcchwRaw[MAX_SLOTS]; + if (cchwMaxRawChars > 1) + { + cchwMaxRawChars = min(cchwMaxRawChars, 6); + pchstrm->GetLogDataRaw(ptman, cchw, -m_ichwAssocsMin, cchwMaxRawChars, + rgnChars, rgchwChars2, rgchwChars3, rgchwChars4, rgchwChars5, rgchwChars6, + rgcchwRaw); + } + else + { + for (ichw = 0 ; ichw <(m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + rgcchwRaw[ichw] = 1; + rgchwChars2[ichw] = 0; + rgchwChars3[ichw] = 0; + rgchwChars4[ichw] = 0; + rgchwChars5[ichw] = 0; + rgchwChars6[ichw] = 0; + } + } + + // Text + strmOut << "Text: "; // 15 spaces + int inUtf32 = 0; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + utf16 chw, chwNext; + switch (rgcchwRaw[ichw]) + { + default: + case 1: chw = utf16(rgnChars[inUtf32]); chwNext = rgchwChars2[inUtf32]; break; + case 2: chw = rgchwChars2[inUtf32]; chwNext = rgchwChars3[inUtf32]; break; + case 3: chw = rgchwChars3[inUtf32]; chwNext = rgchwChars4[inUtf32]; break; + case 4: chw = rgchwChars4[inUtf32]; chwNext = rgchwChars5[inUtf32]; break; + case 5: chw = rgchwChars5[inUtf32]; chwNext = rgchwChars6[inUtf32]; break; + case 6: chw = rgchwChars6[inUtf32]; chwNext = 0; break; + } + if (rgcchwRaw[ichw] == 1 && chwNext == 0 && chw < 0x0100) // ANSI + strmOut << (char)chw << " "; // 6 spaces + else + strmOut << " "; // 7 spaces + if (chwNext == 0) + inUtf32++; + } + strmOut << "\n"; + + // Unicode + strmOut << "Unicode: "; + inUtf32 = 0; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + utf16 chw, chwNext; + switch (rgcchwRaw[ichw]) + { + default: + case 1: chw = utf16(rgnChars[inUtf32]); chwNext = rgchwChars2[inUtf32]; break; + case 2: chw = rgchwChars2[inUtf32]; chwNext = rgchwChars3[inUtf32]; break; + case 3: chw = rgchwChars3[inUtf32]; chwNext = rgchwChars4[inUtf32]; break; + case 4: chw = rgchwChars4[inUtf32]; chwNext = rgchwChars5[inUtf32]; break; + case 5: chw = rgchwChars5[inUtf32]; chwNext = rgchwChars6[inUtf32]; break; + case 6: chw = rgchwChars6[inUtf32]; chwNext = 0; break; + } + ptman->LogHexInTable(strmOut, chw, chwNext != 0); + if (chwNext == 0) + inUtf32++; + } + strmOut << "\n"; + + strmOut << "before "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (rgcchwRaw[ichw] > 1) + // continuation of Unicode codepoint + strmOut << " "; + else if (m_prgisloutBefore[ichw] == kNegInfinity) + strmOut << "<-- "; + else if (m_prgisloutBefore[ichw] == kPosInfinity) + strmOut << "--> "; + else + ptman->LogInTable(strmOut, m_prgisloutBefore[ichw]); + } + strmOut <<"\n"; + + for (int ix = 1; ix < signed(cassocs) - 1; ix++) //(cassocs > 2) + { + if (ix == 1) + strmOut << "other "; + else + strmOut << " "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + std::vector<int> * pvislout = m_prgpvisloutAssocs[ichw]; + if (pvislout == NULL) + strmOut << " "; + else if (signed(pvislout->size()) <= ix) + strmOut << " "; + else if ((*pvislout)[ix] != m_prgisloutAfter[ichw]) + ptman->LogInTable(strmOut, (*pvislout)[ix]); + else + strmOut << " "; + } + strmOut << "\n"; + } + + strmOut << "after "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (rgcchwRaw[ichw] > 1) + // continuation of Unicode codepoint + strmOut << " "; + else if (m_prgisloutAfter[ichw] == kNegInfinity) + strmOut << "<-- "; + else if (m_prgisloutAfter[ichw] == kPosInfinity) + strmOut << "--> "; + else + ptman->LogInTable(strmOut, m_prgisloutAfter[ichw]); + } + strmOut <<"\n"; + + if (fLigs) + { + strmOut << "ligature "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (rgcchwRaw[ichw] > 1) + // continuation of Unicode codepoint + strmOut << " "; + else if (m_prgisloutLigature[ichw] != kNegInfinity) + ptman->LogInTable(strmOut, m_prgisloutLigature[ichw]); + else + strmOut << " "; + } + strmOut << "\n"; + + strmOut << "component "; + for (ichw = 0; ichw < (m_ichwAssocsLim - m_ichwAssocsMin); ichw++) + { + if (rgcchwRaw[ichw] > 1) + // continuation of Unicode codepoint + strmOut << " "; + else if (m_prgisloutLigature[ichw] != kNegInfinity) + ptman->LogInTable(strmOut, m_prgiComponent[ichw] + 1); // 1-based + else + strmOut << " "; + } + strmOut << "\n"; + } + + strmOut << "\n"; +#endif +} + +/*---------------------------------------------------------------------------------------------- + Write out the final surface-to-underlying associations. +----------------------------------------------------------------------------------------------*/ +void Segment::LogSurfaceToUnderlying(GrTableManager * ptman, std::ostream & strmOut) +{ +#ifdef TRACING + strmOut << "\nSURFACE TO UNDERLYING MAPPINGS\n\n"; + + ptman->LogSlotHeader(strmOut, m_cslout, SP_PER_SLOT, LEADING_SP); + + int ccomp = 0; + + strmOut << "Glyph IDs: "; + int islout; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->SpecialSlotFlag() == kspslLbInitial || + psloutTmp->SpecialSlotFlag() == kspslLbFinal) + { + strmOut << "# "; + } + else + { + ptman->LogHexInTable(strmOut, psloutTmp->GlyphID()); + ccomp = max(ccomp, psloutTmp->NumberOfComponents()); + } + } + strmOut << "\n"; + + bool fAnyPseudos = false; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->GlyphID() != psloutTmp->ActualGlyphForOutput(ptman)) + { + fAnyPseudos = true; + break; + } + } + if (fAnyPseudos) + { + strmOut << "Actual glyphs: "; + for (int islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->GlyphID() != psloutTmp->ActualGlyphForOutput(ptman)) + ptman->LogHexInTable(strmOut, psloutTmp->ActualGlyphForOutput(ptman)); + else + strmOut << " "; + } + strmOut << "\n"; + } + + strmOut << "before "; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->SpecialSlotFlag() == kspslLbInitial || + psloutTmp->SpecialSlotFlag() == kspslLbFinal) + { + strmOut << " "; + } + else + ptman->LogInTable(strmOut, psloutTmp->BeforeAssoc()); + } + strmOut << "\n"; + + strmOut << "after "; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->SpecialSlotFlag() == kspslLbInitial || + psloutTmp->SpecialSlotFlag() == kspslLbFinal) + { + strmOut << " "; + } + else + ptman->LogInTable(strmOut, psloutTmp->AfterAssoc()); + } + strmOut << "\n"; + + for (int icomp = 0; icomp < ccomp; icomp++) + { + strmOut << "component " << icomp + 1 // 1=based + << " "; + for (islout = 0; islout < m_cslout; islout++) + { + GrSlotOutput * psloutTmp = m_prgslout + islout; + if (psloutTmp->SpecialSlotFlag() == kspslLbInitial || + psloutTmp->SpecialSlotFlag() == kspslLbFinal) + { + strmOut << " "; + } + else if (icomp < psloutTmp->NumberOfComponents()) + { + if (psloutTmp->FirstUnderlyingComponent(icomp) + != psloutTmp->LastUnderlyingComponent(icomp)) // $$$$$ + { + // Multiple characters represented by this component. + char rgch[10]; + std::string strTmp; + sprintf(rgch, "%d", psloutTmp->FirstUnderlyingComponent(icomp)); + strTmp += rgch; + memset(rgch, 0, 10); + sprintf(rgch, "%d", psloutTmp->LastUnderlyingComponent(icomp)); + strTmp += "/"; + strTmp += rgch; + if (strlen(strTmp.c_str()) > SP_PER_SLOT-1) + strmOut << "****** "; + else + { + while (strlen(strTmp.c_str()) < SP_PER_SLOT) // pad with spaces + strTmp += " "; + strmOut << strTmp; + } + } + else + ptman->LogInTable(strmOut, psloutTmp->FirstUnderlyingComponent(icomp)); // $$$$$ + } + else + strmOut << " "; + } + strmOut << "\n"; + } +#endif +} + +#ifdef TRACING +/*---------------------------------------------------------------------------------------------- + Write out the header lines for the underlying data. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogUnderlyingHeader(std::ostream & strmOut, int iMin, + int iLim, int cBackup, int * prgichr) +{ + strmOut << "string "; + int i, iLabel; + int * pichr = prgichr; + for (i = iMin - cBackup, iLabel = i; i < iLim; i++, iLabel++, pichr++) + { + while (prgichr && *pichr > 1) + { + // continuation of upper-plane char + iLabel++; + pichr++; + } + LogInTable(strmOut, iLabel); + } + strmOut << "\n"; + + strmOut << "segment "; + pichr = prgichr; + for (i = 0 - cBackup, iLabel = i; i < (iLim - iMin); i++, iLabel++, pichr++) + { + while (prgichr && *pichr > 1) + { + // continuation of upper-plane char + iLabel++; + pichr++; + } + LogInTable(strmOut, iLabel); + } + strmOut << "\n\n"; +} + +/*---------------------------------------------------------------------------------------------- + Write out the header lines for the slot contents. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogSlotHeader(std::ostream & strmOut, int islotLim, + int cspPerSlot, int cspLeading, int islotMin) +{ + islotLim = min(islotLim, MAX_SLOTS); + + int isp, islot; + + for (isp = 0; isp < cspLeading; isp++) + strmOut << " "; + + for (islot = islotMin; islot < islotLim; islot++) + LogInTable(strmOut, islot); + strmOut << "\n\n"; + +/**** + if (cslot >= 100) + { + for (isp = 0; isp < cspLeading; isp++) + strmOut << " "; + + for (islot = 0; islot < 100; islot++) + { + for (isp = 0; isp < cspPerSlot; isp++) + strmOut << " "; + } + + for (islot = 100; islot < cslot; islot++) + { + strmOut << "1"; + for (isp = 1; isp < cspPerSlot; isp++) + strmOut << " "; + } + } + + if (cslot > 10) + { + for (isp = 0; isp < cspLeading; isp++) + strmOut << " "; + + for (islot = 0; islot < 10; islot++) + { + for (isp = 0; isp < cspPerSlot; isp++) + strmOut << " "; + } + + for (islot = 10; islot < cslot; islot++) + { + if (true) // (islot % 10 == 0) + { + if (islot >= 100) + strmOut << ((islot - 100) / 10); + else + strmOut << (islot / 10); + } + else + strmOut << " "; + + for (isp = 1; isp < cspPerSlot; isp++) + strmOut << " "; + } + strmOut << "\n"; + } + + for (isp = 0; isp < cspLeading; isp++) + strmOut << " "; + + for (islot = 0; islot < cslot; islot++) + { + strmOut << (islot % 10); + for (isp = 1; isp < cspPerSlot; isp++) + strmOut << " "; + + } + strmOut << "\n"; +****/ +} + +/*---------------------------------------------------------------------------------------------- + Write out the glyph numbers for the given stream. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogSlotGlyphs(std::ostream & strmOut, GrSlotStream * psstrm) +{ + strmOut << "Glyph IDs: "; + int islot; + for (islot = 0; islot < psstrm->WritePos(); islot++) + { + GrSlotState * pslotTmp = psstrm->SlotAt(islot); + if (pslotTmp->IsLineBreak(LBGlyphID())) + strmOut << "# "; + else + LogHexInTable(strmOut, pslotTmp->GlyphID()); + } + strmOut << "\n"; +} + +/*---------------------------------------------------------------------------------------------- + Return flags indicating which attributes were modified for some slot during the pass + and therefore need a line in the log file. + + The 'pfMods' array has one flag per slot attribute; the final group are the user- + definable attributes. 'pccomp' returns the maximum number of ligature components + in this pass. Associations are assumed to have changed if anything else for the slot did. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::SlotAttrsModified(int ipass, bool * rgfMods, bool fPreJust, + int * pccomp, int * pcassoc) +{ + // Zero the flags + std::fill_n(rgfMods, kslatMax + NumUserDefn() - 1, false); + *pccomp = 0; + *pcassoc = 0; + + //GrSlotStream * psstrmIn = InputStream(ipass); + GrSlotStream * psstrmOut = OutputStream(ipass); + + for (int islot = 0; islot < psstrmOut->WritePos(); islot++) + { + GrSlotState * pslot = psstrmOut->SlotAt(islot); + Assert(pslot->PassModified() <= ipass); + if (pslot->PassModified() < ipass && !fPreJust) + continue; // not modified during this pass + + pslot->SlotAttrsModified(rgfMods, fPreJust, pccomp, pcassoc); + } +} + +void GrSlotState::SlotAttrsModified(bool * rgfMods, bool fPreJust, int * pccomp, int * pcassoc) +{ + // To handle reprocessing, in which case there may be a chain of slots modified + // in the same pass: + GrSlotState * pslotPrev = m_pslotPrevState; + while (pslotPrev && pslotPrev->PassModified() == PassModified()) + pslotPrev = pslotPrev->m_pslotPrevState; + + if (!pslotPrev) + { + // Inserted slot? + if (m_fAdvXSet) + rgfMods[kslatAdvX] = true; + if (m_fAdvYSet) + rgfMods[kslatAdvY] = true; + + if (m_srAttachTo != 0) + rgfMods[kslatAttTo] = true; + + if (m_mAttachAtX != kNotYetSet || m_mAttachAtY != 0) + { // always do these in pairs + rgfMods[kslatAttAtX] = true; + rgfMods[kslatAttAtY] = true; + } + if (m_nAttachAtGpoint != kNotYetSet) + rgfMods[kslatAttAtGpt] = true; + if (m_mAttachAtXOffset != 0 || m_mAttachAtYOffset != 0) + { // always do these in pairs + rgfMods[kslatAttAtXoff] = true; + rgfMods[kslatAttAtYoff] = true; + } + + if (m_mAttachWithX != kNotYetSet || m_mAttachWithY != 0) + { // always do these in pairs + rgfMods[kslatAttWithX] = true; + rgfMods[kslatAttWithY] = true; + } + if (m_nAttachWithGpoint != kNotYetSet) + rgfMods[kslatAttWithGpt] = true; + if (m_mAttachWithXOffset != 0 || m_mAttachWithYOffset != 0) + { // always do these in pairs + rgfMods[kslatAttWithXoff] = true; + rgfMods[kslatAttWithYoff] = true; + } + + if (m_nAttachLevel != 0) + rgfMods[kslatAttLevel] = true; + + if (m_lb != kNotYetSet8) + rgfMods[kslatBreak] = true; + if (m_dirc != kNotYetSet8) + rgfMods[kslatDir] = true; + if (m_fInsertBefore != true) + rgfMods[kslatInsert] = true; + + if (m_mMeasureSol != kNotYetSet && m_mMeasureSol != 0) + rgfMods[kslatMeasureSol] = true; + if (m_mMeasureEol != kNotYetSet && m_mMeasureEol != 0) + rgfMods[kslatMeasureEol] = true; + + if (m_mJStretch0 != kNotYetSet && m_mJStretch0 != 0) + rgfMods[kslatJStretch] = true; + if (m_mJShrink0 != kNotYetSet && m_mJShrink0 != 0) + rgfMods[kslatJShrink] = true; + if (m_mJStep0 != kNotYetSet && m_mJStep0 != 0) + rgfMods[kslatJStep] = true; + if (m_nJWeight0 != byte(kNotYetSet) && + m_nJWeight0 != 0 && m_nJWeight0 != 1) + rgfMods[kslatJWeight] = true; + if (m_mJWidth0 != kNotYetSet && m_mJWidth0 != 0) + rgfMods[kslatJWidth] = true; + + if (m_mShiftX != 0) + rgfMods[kslatShiftX] = true; + if (m_mShiftY != 0) + rgfMods[kslatShiftY] = true; + + int i; + for (i = 0; i < m_cnCompPerLig; i++) + { + if (CompRef(i) != NULL) + rgfMods[kslatCompRef] = true; + if (CompRef(i)) + *pccomp = max(*pccomp, i + 1); // max number of ligatures in this pass + } + + for (i = 0; i < m_cnUserDefn; i++) + { + if (UserDefn(i) != 0) + rgfMods[kslatUserDefn + i] = true; + } + } + else + { + if (m_fAdvXSet && m_mAdvanceX != pslotPrev->m_mAdvanceX) + rgfMods[kslatAdvX] = true; + if (m_fAdvYSet && m_mAdvanceY != pslotPrev->m_mAdvanceY) + rgfMods[kslatAdvY] = true; + + if (m_srAttachTo != pslotPrev->m_srAttachTo) + rgfMods[kslatAttTo] = true; + + if (m_mAttachAtX != pslotPrev->m_mAttachAtX || m_mAttachAtY != pslotPrev->m_mAttachAtY) + { + rgfMods[kslatAttAtX] = true; + rgfMods[kslatAttAtY] = true; + } + if (m_nAttachAtGpoint != pslotPrev->m_nAttachAtGpoint) + rgfMods[kslatAttAtGpt] = true; + if (m_mAttachAtXOffset != pslotPrev->m_mAttachAtXOffset || + m_mAttachAtYOffset != pslotPrev->m_mAttachAtYOffset) + { + rgfMods[kslatAttAtXoff] = true; + rgfMods[kslatAttAtYoff] = true; + } + + if (m_mAttachWithX != pslotPrev->m_mAttachWithX || + m_mAttachWithY != pslotPrev->m_mAttachWithY) + { + rgfMods[kslatAttWithX] = true; + rgfMods[kslatAttWithY] = true; + } + if (m_nAttachWithGpoint != pslotPrev->m_nAttachWithGpoint) + rgfMods[kslatAttWithGpt] = true; + if (m_mAttachWithXOffset != pslotPrev->m_mAttachWithXOffset || + m_mAttachWithYOffset != pslotPrev->m_mAttachWithYOffset) + { + rgfMods[kslatAttWithXoff] = true; + rgfMods[kslatAttWithYoff] = true; + } + + if (m_nAttachLevel != pslotPrev->m_nAttachLevel) + rgfMods[kslatAttLevel] = true; + + if (m_lb != pslotPrev->m_lb) + rgfMods[kslatBreak] = true; + if (m_dirc != pslotPrev->m_dirc) + rgfMods[kslatDir] = true; + if (m_fInsertBefore != pslotPrev->m_fInsertBefore) + rgfMods[kslatInsert] = true; + + if (m_mMeasureSol != pslotPrev->m_mMeasureSol) + rgfMods[kslatMeasureSol] = true; + if (m_mMeasureEol != pslotPrev->m_mMeasureEol) + rgfMods[kslatMeasureEol] = true; + + if (m_mJStretch0 != pslotPrev->m_mJStretch0 || (fPreJust && m_mJStretch0 != 0)) + rgfMods[kslatJStretch] = true; + if (m_mJShrink0 != pslotPrev->m_mJShrink0 || (fPreJust && m_mJShrink0 != 0)) + rgfMods[kslatJShrink] = true; + if (m_mJStep0 != pslotPrev->m_mJStep0 || (fPreJust && m_mJStep0 != 0)) + rgfMods[kslatJStep] = true; + if (m_nJWeight0 != pslotPrev->m_nJWeight0 || (fPreJust && m_nJWeight0 != 0)) + rgfMods[kslatJWeight] = true; + if (m_mJWidth0 != pslotPrev->m_mJWidth0) + rgfMods[kslatJWidth] = true; + + if (m_mShiftX != pslotPrev->m_mShiftX) + rgfMods[kslatShiftX] = true; + if (m_mShiftY != pslotPrev->m_mShiftY) + rgfMods[kslatShiftY] = true; + + int i; + for (i = 0; i < m_cnCompPerLig; i++) + { + if (CompRef(i) != pslotPrev->CompRef(i)) + rgfMods[kslatCompRef] = true; + if (CompRef(i)) + *pccomp = max(*pccomp, i + 1); // max number of ligatures in this pass + } + + for (i = 0; i < m_cnUserDefn; i++) + { + if (UserDefn(i) != pslotPrev->UserDefn(i)) + rgfMods[kslatUserDefn + i] = true; + } + } + + *pcassoc = max(*pcassoc, AssocsSize()); +} + +/*---------------------------------------------------------------------------------------------- + Log the value of the slot attribute for the given slot, if it changed. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::LogSlotAttribute(GrTableManager * ptman, + std::ostream & strmOut, int ipass, int slat, int iIndex, + bool fPreJust, bool fPostJust) +{ + if (m_ipassModified != ipass && !fPreJust && !fPostJust) + { + strmOut << " "; + return; + } + + // To handle reprocessing, in which case there may be a chain of slots modified + // in the same pass: + GrSlotState * pslotPrev = m_pslotPrevState; + while (pslotPrev && pslotPrev->PassModified() == PassModified()) + pslotPrev = pslotPrev->m_pslotPrevState; + + // General-purpose variables for em-unit attributes: + int mThis = 0; + int mPrev = 0; + switch (slat) + { + case kslatShiftX: + mThis = m_mShiftX; + mPrev = (pslotPrev) ? pslotPrev->m_mShiftX : 0; + break; + case kslatShiftY: + mThis = m_mShiftY; + mPrev = (pslotPrev) ? pslotPrev->m_mShiftY : 0; + break; + case kslatMeasureSol: + mThis = m_mMeasureSol; + mPrev = (pslotPrev) ? pslotPrev->m_mMeasureSol : 0; + break; + case kslatMeasureEol: + mThis = m_mMeasureEol; + mPrev = (pslotPrev) ? pslotPrev->m_mMeasureEol : 0; + break; + case kslatJStretch: + mThis = m_mJStretch0; + mPrev = (pslotPrev) ? pslotPrev->m_mJStretch0 : 0; + mPrev = (fPreJust && mThis > 0) ? -1 : mPrev; // log it even if it didn't change + break; + case kslatJShrink: + mThis = m_mJShrink0; + mPrev = (pslotPrev) ? pslotPrev->m_mJShrink0 : 0; + mPrev = (fPreJust && mThis > 0) ? -1 : mPrev; // log it even if it didn't change + break; + case kslatJStep: + mThis = m_mJStep0; + mPrev = (pslotPrev) ? pslotPrev->m_mJStep0 : 0; + mPrev = (fPreJust && mThis > 1) ? -1 : mPrev; // log it even if it didn't change + break; + case kslatJWeight: + mThis = m_nJWeight0; + mPrev = (pslotPrev) ? pslotPrev->m_nJWeight0 : 0; + mPrev = (fPreJust && mThis > 1) ? -1 : mPrev; // log it even if it didn't change + break; + case kslatJWidth: + mThis = m_mJWidth0; + mPrev = (pslotPrev) ? pslotPrev->m_mJWidth0 : 0; + mPrev = (fPostJust && mThis > 0) ? -1 : mPrev; // log it even if it didn't change + break; + default: + // don't use these variables + break; + } + + switch (slat) + { + case kslatAdvX: + if (m_fAdvXSet && (!pslotPrev || m_mAdvanceX != pslotPrev->m_mAdvanceX)) + { + ptman->LogInTable(strmOut, m_mAdvanceX); + return; + } + break; + case kslatAdvY: + if (m_fAdvYSet && (!pslotPrev || m_mAdvanceY != pslotPrev->m_mAdvanceY)) + { + ptman->LogInTable(strmOut, m_mAdvanceY); + return; + } + break; + + case kslatAttTo: + if (m_srAttachTo != (pslotPrev ? pslotPrev->m_srAttachTo : 0)) + { + ptman->LogInTable(strmOut, m_srAttachTo); + return; + } + break; + + case kslatAttAtX: // always do these in pairs + case kslatAttAtY: + if (m_mAttachAtX != (pslotPrev ? pslotPrev->m_mAttachAtX : kNotYetSet) || + m_mAttachAtY != (pslotPrev ? pslotPrev->m_mAttachAtY : 0)) + { + ptman->LogInTable(strmOut, + ((slat == kslatAttAtX) ? m_mAttachAtX : m_mAttachAtY)); + return; + } + break; + case kslatAttAtGpt: + if (m_nAttachAtGpoint != (pslotPrev ? pslotPrev->m_nAttachAtGpoint : kNotYetSet)) + { + ptman->LogInTable(strmOut, + ((m_nAttachAtGpoint == kGpointZero) ? 0 : m_nAttachAtGpoint)); + return; + } + break; + case kslatAttAtXoff: // always do these in pairs + case kslatAttAtYoff: + if (m_mAttachAtXOffset != (pslotPrev ? pslotPrev->m_mAttachAtXOffset : 0) || + m_mAttachAtYOffset != (pslotPrev ? pslotPrev->m_mAttachAtYOffset : 0)) + { + ptman->LogInTable(strmOut, + ((slat == kslatAttAtXoff) ? m_mAttachAtXOffset : m_mAttachAtYOffset)); + return; + } + break; + + case kslatAttWithX: // always do these in pairs + case kslatAttWithY: + if (m_mAttachWithX != (pslotPrev ? pslotPrev->m_mAttachWithX : kNotYetSet) || + m_mAttachWithY != (pslotPrev ? pslotPrev->m_mAttachWithY : 0)) + { + ptman->LogInTable(strmOut, + ((slat == kslatAttWithX) ? m_mAttachWithX : m_mAttachWithY)); + return; + } + break; + case kslatAttWithGpt: + if (m_nAttachWithGpoint != (pslotPrev ? pslotPrev->m_nAttachWithGpoint : kNotYetSet)) + { + ptman->LogInTable(strmOut, + ((m_nAttachWithGpoint == kGpointZero) ? 0 : m_nAttachWithGpoint)); + return; + } + break; + case kslatAttWithXoff: // always do these in pairs + case kslatAttWithYoff: + if (m_mAttachWithXOffset != (pslotPrev ? pslotPrev->m_mAttachWithXOffset : 0) || + m_mAttachWithYOffset != (pslotPrev ? pslotPrev->m_mAttachWithYOffset : 0)) + { + ptman->LogInTable(strmOut, + ((slat == kslatAttWithXoff) ? m_mAttachWithXOffset : m_mAttachWithYOffset)); + return; + } + break; + + case kslatAttLevel: + if (m_nAttachLevel != (pslotPrev ? pslotPrev->m_nAttachLevel : 0)) + { + ptman->LogInTable(strmOut, m_nAttachLevel); + return; + } + break; + + case kslatBreak: + if (m_lb != (pslotPrev ? pslotPrev->m_lb : kNotYetSet8)) + { + ptman->LogBreakWeightInTable(strmOut, m_lb); + return; + } + break; + case kslatDir: + if (m_dirc != (pslotPrev ? pslotPrev->m_dirc : kNotYetSet8)) + { + ptman->LogDirCodeInTable(strmOut, m_dirc); + return; + } + break; + case kslatInsert: + if (m_fInsertBefore != (pslotPrev ? pslotPrev->m_fInsertBefore : true)) + { + if (m_fInsertBefore) + strmOut << "true "; + else + strmOut << "false "; + return; + } + break; + + case kslatJWeight: + if (m_mJStretch0 == 0 && m_mJShrink0 == 0) + mPrev = mThis; // don't log; weight is irrelevant + // fall through + case kslatShiftX: + case kslatShiftY: + case kslatMeasureSol: + case kslatMeasureEol: + case kslatJStretch: + case kslatJShrink: + case kslatJStep: + case kslatJWidth: + if (mThis != mPrev) + { + ptman->LogInTable(strmOut, mThis); + return; + } + break; + + case kslatCompRef: + if (CompRef(iIndex) != (pslotPrev ? pslotPrev->CompRef(iIndex) : 0)) + { + GrSlotState * pslotComp = reinterpret_cast<GrSlotState *>(CompRef(iIndex)); + ptman->LogInTable(strmOut, pslotComp->m_islotTmpIn); + return; + } + break; + + default: + if (kslatUserDefn <= slat && slat <= kslatUserDefn + m_cnUserDefn) + { + int iTmp = slat - kslatUserDefn; + if (UserDefn(iTmp) != (pslotPrev ? pslotPrev->UserDefn(iTmp) : 0)) + { + ptman->LogInTable(strmOut, UserDefn(iTmp)); + return; + } + } + else + gAssert(false); + } + + strmOut << " "; +} + +/*---------------------------------------------------------------------------------------------- + Log the value of the association, if the slot changed. +----------------------------------------------------------------------------------------------*/ +void GrSlotState::LogAssociation(GrTableManager * ptman, + std::ostream & strmOut, int ipass, int iassoc, bool fBoth, bool fAfter) +{ + if (m_ipassModified != ipass) + { + strmOut << " "; + return; + } + + if (fBoth) + { + GrSlotState * pslotBefore = (m_vpslotAssoc.size()) ? m_vpslotAssoc[0] : NULL; + GrSlotState * pslotAfter = (m_vpslotAssoc.size()) ? m_vpslotAssoc.back() : NULL; + // handle possible reprocessing + while (pslotBefore && pslotBefore->PassModified() == m_ipassModified) + pslotBefore = pslotBefore->m_pslotPrevState; + while (pslotAfter && pslotAfter->PassModified() == m_ipassModified) + pslotAfter = pslotAfter->m_pslotPrevState; + + int nBefore, nAfter; + + + int csp = 4; + if (pslotBefore) + { + nBefore = pslotBefore->m_islotTmpIn; + strmOut << nBefore; + if (nBefore > 99) csp--; + if (nBefore > 9) csp--; + } + else + { + strmOut << "??"; + csp--; + } + if (pslotAfter) + { + nAfter = pslotAfter->m_islotTmpIn; + strmOut << "/" << nAfter; + if (nAfter > 99) csp--; + if (nAfter > 9) csp--; + } + else + { + if (pslotBefore) + { + strmOut << "/" << "??"; + csp--; + } + else + csp = 5; + } + + for (int isp = 0; isp < csp; isp++) + strmOut << " "; + } + + else if (fAfter) + { + Assert(m_vpslotAssoc.size()); + GrSlotState * pslotAfter = m_vpslotAssoc.back(); + // handle possible reprocessing + while (pslotAfter && pslotAfter->PassModified() == m_ipassModified) + pslotAfter = pslotAfter->m_pslotPrevState; + + if (pslotAfter) + { + int nAfter = pslotAfter->m_islotTmpIn; + ptman->LogInTable(strmOut, nAfter); + } + else + strmOut << "?? "; + } + else if (iassoc < signed(m_vpslotAssoc.size())) + { + GrSlotState * pslot = m_vpslotAssoc[iassoc]; + // handle possible reprocessing + while (pslot && pslot->PassModified() == m_ipassModified) + pslot = pslot->m_pslotPrevState; + + if (pslot) + { + int n = pslot->m_islotTmpIn; + ptman->LogInTable(strmOut, n); + } + else + strmOut << "?? "; + } + else + strmOut << " "; +} + +/*---------------------------------------------------------------------------------------------- + Write a hex value (a glyphID or Unicode codepoint) into the table. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogInTable(std::ostream & strmOut, int n) +{ + if (n == kNegInfinity) + { + strmOut << "-inf "; + return; + } + if (n == kPosInfinity) + { + strmOut << "+inf "; + return; + } + if (n > 999999) + { + strmOut << "****** "; + return; + } + if (n < -99999) + { + strmOut << "-***** "; + return; + } + + strmOut << n; + int csp = SP_PER_SLOT - 1; + + if (abs(n) > 99999) csp--; + if (abs(n) > 9999) csp--; + if (abs(n) > 999) csp--; + if (abs(n) > 99) csp--; + if (abs(n) > 9) csp--; + + if (n < 0) csp--; + + for (int isp = 0; isp < csp; isp++) + strmOut << " "; +} + +void GrTableManager::LogInTable(std::ostream & strmOut, float n) +{ + if (n == kNegInfFloat) + { + strmOut << "-inf "; + return; + } + if (n == kPosInfFloat) + { + strmOut << "+inf "; + return; + } + if (n > 9999) + { + strmOut << "****.* "; + return; + } + if (n < -999) + { + strmOut << "-***.* "; + return; + } + + int csp = SP_PER_SLOT - 4; + + int nInt = (int)(fabsf(n)); + int nTenths = (int)fabsf((fabsf(n) - float(nInt) + 0.05f) * 10); + if (nTenths >= 10) + { + nTenths = 0; + nInt++; + } + + if (nInt >= 1000) csp--; + if (nInt >= 100) csp--; + if (nInt >= 10) csp--; + + if (n < 0) csp--; + + for (int isp = 0; isp < csp; isp++) + strmOut << " "; + if (n < 0) + strmOut << "-"; + strmOut << nInt << "." << nTenths << " "; +} + +/*---------------------------------------------------------------------------------------------- + Write a hex value (a glyphID or Unicode codepoint) into the table. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogHexInTable(std::ostream & strmOut, utf16 chw, bool fPlus) +{ + //char rgch[20]; + if (chw <= 0x0fff) strmOut << "0"; + if (chw <= 0x00ff) strmOut << "0"; + if (chw <= 0x000f) strmOut << "0"; + + strmOut << std::hex << chw << std::dec; + + for (int i = 4; i < SP_PER_SLOT - 2; i++) + strmOut << " "; + + if (fPlus) + strmOut << "+ "; + else + strmOut << " "; +} + +/*---------------------------------------------------------------------------------------------- + Write a directionality code to the table. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogDirCodeInTable(std::ostream & strmOut, int dirc) +{ + switch (dirc) + { + case kdircUnknown: strmOut << "??? "; break; + case kdircNeutral: strmOut << "ON "; break; + case kdircL: strmOut << "L "; break; + case kdircR: strmOut << "R "; break; + case kdircRArab: strmOut << "AR "; break; + case kdircEuroNum: strmOut << "EN "; break; + case kdircEuroSep: strmOut << "ES "; break; + case kdircEuroTerm: strmOut << "ET "; break; + case kdircArabNum: strmOut << "AN "; break; + case kdircComSep: strmOut << "CS "; break; + case kdircWhiteSpace: strmOut << "WS "; break; + case kdircBndNeutral: strmOut << "BN "; break; + case kdircNSM: strmOut << "NSM "; break; + case kdircLRO: strmOut << "LRO "; break; + case kdircRLO: strmOut << "RLO "; break; + case kdircLRE: strmOut << "LRE "; break; + case kdircRLE: strmOut << "RLE "; break; + case kdircPDF: strmOut << "PDF "; break; + case kdircPdfL: strmOut << "PDF-L "; break; + case kdircPdfR: strmOut << "PDF-R "; break; + case kdircLlb: strmOut << "L "; break; + case kdircRlb: strmOut << "R "; break; + default: LogInTable(strmOut, dirc); break; + } +} + +/*---------------------------------------------------------------------------------------------- + Write a breakweight code to the table. +----------------------------------------------------------------------------------------------*/ +void GrTableManager::LogBreakWeightInTable(std::ostream & strmOut, int lb) +{ + if (lb < 0) + { + lb = lb * -1; + switch (lb) + { + case klbWsBreak: strmOut << "-ws "; break; + case klbWordBreak: strmOut << "-word "; break; + case klbHyphenBreak: strmOut << "-intra "; break; + case klbLetterBreak: strmOut << "-lettr "; break; + case klbClipBreak: strmOut << "-clip "; break; + default: LogInTable(strmOut, lb); break; + } + } + else + { + switch (lb) + { + case klbNoBreak: strmOut << "none "; break; + case klbWsBreak: strmOut << "ws "; break; + case klbWordBreak: strmOut << "word "; break; + case klbHyphenBreak: strmOut << "intra "; break; + case klbLetterBreak: strmOut << "letter "; break; + case klbClipBreak: strmOut << "clip "; break; + default: LogInTable(strmOut, lb); break; + } + } +} + +#endif // TRACING + +} // namespace gr diff --git a/Build/source/libs/graphite/engine-2.3.1/src/segment/resource.h b/Build/source/libs/graphite/engine-2.3.1/src/segment/resource.h new file mode 100644 index 00000000000..775418f76bf --- /dev/null +++ b/Build/source/libs/graphite/engine-2.3.1/src/segment/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Graphite.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif |